| # 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`, `dartaotruntime` 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. |
| |
| ```dart |
| 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 |
| |
| ```dart |
| @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(). |
| } |
| ``` |