Flutter Engine
The Flutter Engine
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.

To run an IL test you need to use tools/test.py runner with AOT configuration:

# Run against ReleaseX64 AOT compiler
$ tools/test.py -n dartkp-linux-release-x64 $path_to_an_il_test
$ tools/test.py -c dartkp -m release $path_to_an_il_test

Tests require gen_snapshot, dart_precompiled_runtime and vm_platform_strong.dill to be built for the target configuration.

Each IL test should contain one or more of the functions marked with a ‘@pragma('vm:testing:print-flow-graph’[, 'phases filter'])`.

These functions will have their IL dumped at points specified by the phases filter (if present, ]AllocateRegisters by default), which follows the same syntax as --compiler-passes= flag and dumped IL will be compared against the expectations, which are specified programmatically using package:vm/testing/il_matchers.dart helpers. A function named foo has its IL expectations in the function called matchIL$foo in the same file.

import 'package:vm/testing/il_matchers.dart';
@pragma('vm:testing:print-flow-graph')
void foo() {
}
/// Expectations for [foo].
void matchIL$foo(FlowGraph graph) {
graph.match([/* expectations */]);
}

Actual matching is done by the pkg/vm/tool/compare_il script.

In order to test IL of the inner (local) function, use ‘@pragma('vm:testing:match-inner-flow-graph’, 'inner name')`. Specifying a particular phase is not supported for inner closures.

Example

@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int factorial(int value) => value == 1 ? value : value * factorial(value - 1);
void matchIL$factorial(FlowGraph graph) {
// Expected a graph which starts with GraphEntry block followed by a
// FunctionEntry block. FunctionEntry block should contain a Branch()
// instruction, with EqualityCompare as a comparison.
graph.match([
match.block('Graph'),
match.block('Function', [
match.Branch(match.EqualityCompare(match.any, match.any, kind: '==')),
]),
]);
}
@pragma('vm:testing:match-inner-flow-graph', 'bar')
void foo() {
@pragma('vm:testing:print-flow-graph')
bar() {
}
}
void matchIL$foo_bar(FlowGraph graph) {
// Test IL of local bar() in foo().
}