-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[vm/infra] Nascent AOT IL tests infrastructure.
Our current unit testing infrastructure does not make it possible to test AOT compilation pipeline end-to-end, because it does not run TFA when generating Kernel. This makes it challenging to write regression tests for certain issues. Instead we extend test runner with a capability to perform IL matching when running AOT tests. runtime/docs/infa/il_tests.md provides details on how to write such tests. TEST=manually Change-Id: I6f5220b814f4a5d8c053efacd3711df495dea404 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/214961 Commit-Queue: Slava Egorov <vegorov@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
- Loading branch information
Showing
10 changed files
with
412 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
// This is a helper script which performs IL matching for AOT IL tests. | ||
// See runtime/docs/infra/il_tests.md for more information. | ||
|
||
import 'dart:io'; | ||
|
||
void main(List<String> args) { | ||
if (args.length != 2) { | ||
throw 'Usage: compare_il <*_il_test.dart> <output.il>'; | ||
} | ||
|
||
final testFile = args[0]; | ||
final ilFile = args[1]; | ||
|
||
final graphs = _extractGraphs(ilFile); | ||
|
||
final expectations = _extractExpectations(testFile); | ||
|
||
for (var expectation in expectations.entries) { | ||
// Find a graph for this expectation. We expect that function names are | ||
// unique enough to identify a specific graph. | ||
final graph = | ||
graphs.entries.singleWhere((e) => e.key.contains(expectation.key)); | ||
|
||
// Extract the list of opcodes, ignoring irrelevant things like | ||
// ParallelMove. | ||
final gotOpcodesIgnoringMoves = graph.value | ||
.where((instr) => instr.opcode != 'ParallelMove') | ||
.map((instr) => instr.opcode) | ||
.toList(); | ||
|
||
// Check that expectations are the prefix of gotOpcodesIgnoringMoves. | ||
print('Matching ${graph.key}'); | ||
for (var i = 0; i < expectation.value.length; i++) { | ||
final gotOpcode = gotOpcodesIgnoringMoves[i]; | ||
final expectedOpcode = expectation.value[i]; | ||
if (gotOpcode != expectedOpcode) { | ||
throw 'Failed to match graph of ${graph.key} to ' | ||
'expectations for ${expectation.key} at instruction ${i}: ' | ||
'got ${gotOpcode} expected ${expectedOpcode}'; | ||
} | ||
} | ||
print('... ok'); | ||
} | ||
|
||
exit(0); // Success. | ||
} | ||
|
||
// IL instruction extracted from flow graph dump. | ||
class Instruction { | ||
final String raw; | ||
|
||
Instruction(this.raw); | ||
|
||
String get opcode { | ||
final match = instructionPattern.firstMatch(raw)!; | ||
final op = match.namedGroup('opcode')!; | ||
final blockType = match.namedGroup('block_type'); | ||
|
||
// Handle blocks which look like "B%d[%s]". | ||
if (blockType != null) { | ||
return blockTypes[blockType]!; | ||
} | ||
|
||
// Handle parallel moves specially. | ||
if (op.startsWith('ParallelMove')) { | ||
return 'ParallelMove'; | ||
} | ||
|
||
// Handle branches. | ||
if (op.startsWith(branchIfPrefix)) { | ||
return 'Branch(${op.substring(branchIfPrefix.length)})'; | ||
} | ||
|
||
// Normal instruction. | ||
return op; | ||
} | ||
|
||
@override | ||
String toString() => 'Instruction($opcode)'; | ||
|
||
static final instructionPattern = RegExp( | ||
r'^\s*\d+:\s+(v\d+ <- )?(?<opcode>[^:[(]+(?<block_type>\[[\w ]+\])?)'); | ||
|
||
static const blockTypes = { | ||
'[join]': 'JoinEntry', | ||
'[target]': 'TargetEntry', | ||
'[graph]': 'GraphEntry', | ||
'[function entry]': 'FunctionEntry' | ||
}; | ||
|
||
static const branchIfPrefix = 'Branch if '; | ||
} | ||
|
||
Map<String, List<Instruction>> _extractGraphs(String ilFile) { | ||
final graphs = <String, List<Instruction>>{}; | ||
|
||
final reader = LineReader(ilFile); | ||
|
||
var instructions = <Instruction>[]; | ||
while (reader.hasMore) { | ||
if (reader.testNext('*** BEGIN CFG')) { | ||
reader.next(); // Skip phase name. | ||
final functionName = reader.next(); | ||
while (!reader.testNext('*** END CFG')) { | ||
var curr = reader.next(); | ||
|
||
// If instruction line ends with '{' search for a matching '}' (it will | ||
// be on its own line). | ||
if (curr.endsWith('{')) { | ||
do { | ||
curr += '\n' + reader.current; | ||
} while (reader.next() != '}'); | ||
} | ||
|
||
instructions.add(Instruction(curr)); | ||
} | ||
|
||
graphs[functionName] = instructions; | ||
instructions = <Instruction>[]; | ||
} else { | ||
reader.next(); | ||
} | ||
} | ||
|
||
return graphs; | ||
} | ||
|
||
Map<String, List<String>> _extractExpectations(String testFile) { | ||
final expectations = <String, List<String>>{}; | ||
|
||
final reader = LineReader(testFile); | ||
|
||
final matchILPattern = RegExp(r'^// MatchIL\[AOT\]=(?<value>.*)$'); | ||
final matcherPattern = RegExp(r'^// __ (?<value>.*)$'); | ||
|
||
var matchers = <String>[]; | ||
while (reader.hasMore) { | ||
var functionName = reader.matchNext(matchILPattern); | ||
if (functionName != null) { | ||
// Read comment block which follows `// MatchIL[AOT]=...`. | ||
while (reader.hasMore && reader.current.startsWith('//')) { | ||
final match = matcherPattern.firstMatch(reader.next()); | ||
if (match != null) { | ||
matchers.add(match.namedGroup('value')!); | ||
} | ||
} | ||
expectations[functionName] = matchers; | ||
matchers = <String>[]; | ||
} else { | ||
reader.next(); | ||
} | ||
} | ||
|
||
return expectations; | ||
} | ||
|
||
class LineReader { | ||
final List<String> lines; | ||
int lineno = 0; | ||
|
||
LineReader(String path) : lines = File(path).readAsLinesSync(); | ||
|
||
String get current => lines[lineno]; | ||
|
||
bool get hasMore => lineno < lines.length; | ||
|
||
String next() { | ||
final curr = current; | ||
lineno++; | ||
return curr; | ||
} | ||
|
||
bool testNext(String expected) { | ||
if (current == expected) { | ||
next(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
String? matchNext(RegExp pattern) { | ||
final m = pattern.firstMatch(current); | ||
return m?.namedGroup('value'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#!/usr/bin/env bash | ||
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file | ||
# for details. All rights reserved. Use of this source code is governed by a | ||
# BSD-style license that can be found in the LICENSE file. | ||
|
||
# Script for comparing IL generated from IL tests. | ||
|
||
set -e | ||
|
||
function follow_links() { | ||
file="$1" | ||
while [ -h "$file" ]; do | ||
# On Mac OS, readlink -f doesn't work. | ||
file="$(readlink "$file")" | ||
done | ||
echo "$file" | ||
} | ||
|
||
# Unlike $0, $BASH_SOURCE points to the absolute path of this file. | ||
PROG_NAME="$(follow_links "$BASH_SOURCE")" | ||
|
||
# Handle the case where dart-sdk/bin has been symlinked to. | ||
CUR_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" | ||
|
||
SDK_DIR="$CUR_DIR/../../.." | ||
|
||
# TODO(kustermann): For windows as well as for hosts running on arm, our | ||
# checked-in dart binaries must be adjusted. | ||
if [[ `uname` == 'Darwin' ]]; then | ||
DART="$SDK_DIR/tools/sdks/dart-sdk/bin/dart" | ||
else | ||
DART="$SDK_DIR/tools/sdks/dart-sdk/bin/dart" | ||
fi | ||
|
||
exec "$DART" $DART_VM_FLAGS "${SDK_DIR}/pkg/vm/bin/compare_il.dart" $@ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
@echo off | ||
REM Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | ||
REM for details. All rights reserved. Use of this source code is governed by a | ||
REM BSD-style license that can be found in the LICENSE file. | ||
|
||
REM Script for comparing IL generated from IL tests. | ||
|
||
set SCRIPTPATH=%~dp0 | ||
|
||
REM Does the path have a trailing slash? If so, remove it. | ||
if %SCRIPTPATH:~-1%==\ set SCRIPTPATH=%SCRIPTPATH:~0,-1% | ||
|
||
set SDK_DIR=%SCRIPTPATH%/../../../ | ||
|
||
set DART=%SDK_DIR%/tools/sdks/dart-sdk/bin/dart.exe | ||
|
||
"%DART%" %DART_VM_OPTIONS% "%SDK_DIR%/pkg/vm/bin/compare_il.dart" %* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.