Writing IL tests for AOT compiler

Usually optimized IL strongly depends on TFA results and which makes it difficult to test certain AOT optimizations through run_vm_tests.

In such cases you can attempt to write an IL test instead. In these tests test runner will run full AOT pipeline (TFA + gen_snapshot), will instruct gen_snapshot to dump flow graphs of specific methods and then run pkg/vm/tool/compare_il helper script to compare expectations. Here is how you create an IL test.

IL tests are placed in files ending with _il_test.dart.

Each IL test should contain one or more IL matching blocks, which have the following format:

// MatchIL[AOT]=functionName
//   comment
// __ op
//   comment
// __ op
// __ op
// __ op

Each section starts with a // MatchIL[AOT]=functionName line which contains the name (or a substring of a name) of the function for which IL should be matched.

// MatchIL[AOT]=... line is followed by some number of comment lines //, where lines starting with // __ specify an instruction matcher and the rest are ignored (they just act as normal comments).

gen_snapshot will be instructed (via --print-flow-graph-optimized and --print-flow-graph-filter=functionName,... flags) to dump IL for all functions names specified in IL matching blocks.

After that pkg/vm/tool/compare_il script will be used to compare the dumps to actual expectations: by checking that dumped flow graph starts with the expected sequence of commands (ignoring some instructions like ParallelMove).

Example

// MatchIL[AOT]=factorial
// __ GraphEntry
// __ FunctionEntry
// __ CheckStackOverflow
// __ Branch(EqualityCompare)
@pragma('vm:never-inline')
int factorial(int value) => value == 1 ? value : value * factorial(value - 1);

This test specifies that the graph for factorial should start with a sequence GraphEntry, FunctionEntry, CheckStackOverflow, Branch(EqualityCompare).

If the graph has a different shape the test will fail, e.g. given the graph

*** BEGIN CFG
After AllocateRegisters
==== file:///.../src/dart/sdk/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart_::_factorial (RegularFunction)
  0: B0[graph]:0 {
      v3 <- Constant(#1) [1, 1] T{_Smi}
      v19 <- UnboxedConstant(#1 int64) T{_Smi}
}
  2: B1[function entry]:2 {
      v2 <- Parameter(0) [-9223372036854775808, 9223372036854775807] T{int}
}
  4:     CheckStackOverflow:8(stack=0, loop=0)
  5:     ParallelMove rcx <- S+2
  6:     v17 <- BoxInt64(v2) [-9223372036854775808, 9223372036854775807] T{int}
  7:     ParallelMove rax <- rax
  8:     Branch if StrictCompare(===, v17 T{int}, v3) T{bool} goto (3, 4)

we will get:

Unhandled exception:
Failed to match graph of ==== file:///.../src/dart/sdk/runtime/tests/vm/dart/aot_prefer_equality_comparison_il_test.dart_::_factorial (RegularFunction) to expectations for factorial at instruction 3: got BoxInt64 expected Branch(EqualityCompare)
#0      main (file:///.../src/dart/sdk/pkg/vm/bin/compare_il.dart:37:9)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:285:32)
#2      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:187:12)