[vm/compiler] Add il_test_helper.h with support for running compilation pipelines for testing

The two existing places where IR is built are changed to use this new
helper. It supports running normal JIT/AOT passes as well as a
user-specified set of passes.

In order to allow vm/cc tests to make assertions about AOT pipeline this CL
enables the DART_PRECOMPILER define in run_vm_tests binary (similar to
gen_snapshot, run_vm_tests has now JIT and AOT support)

Change-Id: Ib51a024a81e0291e89d20860b8b9a2762611426c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98482
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Aart Bik <ajcbik@google.com>
This commit is contained in:
Martin Kustermann 2019-04-04 05:58:29 +00:00 committed by commit-bot@chromium.org
parent 600b2831ef
commit 31d558c81c
12 changed files with 494 additions and 277 deletions

View file

@ -138,6 +138,8 @@ template("build_gen_snapshot") {
}
deps = [ ":generate_abi_version_cc_file" ] + extra_deps
defines = [ "EXCLUDE_CFE_AND_KERNEL_PLATFORM" ]
sources = [
"address_sanitizer.cc",
"builtin.cc",
@ -897,6 +899,10 @@ executable("run_vm_tests") {
"..:dart_os_config",
"..:dart_maybe_product_config",
]
if (dart_target_arch != "ia32") {
configs += [ "..:dart_precompiler_config" ]
}
if (is_fuchsia) {
configs -= [ "//build/config:symbol_visibility_hidden" ]
}
@ -909,7 +915,7 @@ executable("run_vm_tests") {
":generate_abi_version_cc_file",
":libdart_builtin",
":standalone_dart_io",
"..:libdart_jit",
"..:libdart_nosnapshot_with_precompiler",
"//third_party/zlib",
]
include_dirs = [

View file

@ -68,7 +68,13 @@ DFE::DFE()
frontend_filename_(NULL),
application_kernel_buffer_(NULL),
application_kernel_buffer_size_(0) {
#if defined(DART_NO_SNAPSHOT) || defined(DART_PRECOMPILER)
// The run_vm_tests binary has the DART_PRECOMPILER set in order to allow unit
// tests to exercise JIT and AOT pipeline.
//
// Only on X64 do we have kernel-service.dart.snapshot available otherwise we
// need to fall back to the built-in one (if we have it).
#if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) || defined(DART_NO_SNAPSHOT) || \
(defined(DART_PRECOMPILER) && defined(TARGET_ARCH_X64))
kernel_service_dill_ = NULL;
kernel_service_dill_size_ = 0;
platform_strong_dill_ = NULL;

View file

@ -156,6 +156,14 @@ static Dart_Isolate CreateIsolateAndSetup(const char* script_uri,
free(*error);
*error = NULL;
}
// If a test does not actually require the kernel isolate the main thead can
// start calling Dart::Cleanup() while the kernel isolate is booting up.
// This can cause the isolate to be killed early which will return `nullptr`
// here.
if (isolate == nullptr) {
delete isolate_data;
return NULL;
}
}
if (isolate == NULL) {
delete isolate_data;

View file

@ -0,0 +1,122 @@
// Copyright (c) 2019, 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.
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/aot/aot_call_specializer.h"
#include "vm/compiler/backend/block_scheduler.h"
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/inliner.h"
#include "vm/compiler/call_specializer.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/compiler/jit/jit_call_specializer.h"
#include "vm/dart_api_impl.h"
#include "vm/parser.h"
#include "vm/unit_test.h"
namespace dart {
RawLibrary* LoadTestScript(const char* script,
Dart_NativeEntryResolver resolver) {
Dart_Handle api_lib;
{
TransitionVMToNative transition(Thread::Current());
api_lib = TestCase::LoadTestScript(script, resolver);
}
auto& lib = Library::Handle();
lib ^= Api::UnwrapHandle(api_lib);
EXPECT(!lib.IsNull());
return lib.raw();
}
RawFunction* GetFunction(const Library& lib, const char* name) {
Thread* thread = Thread::Current();
const auto& func = Function::Handle(lib.LookupFunctionAllowPrivate(
String::Handle(Symbols::New(thread, name))));
EXPECT(!func.IsNull());
return func.raw();
}
void Invoke(const Library& lib, const char* name) {
Thread* thread = Thread::Current();
Dart_Handle api_lib = Api::NewHandle(thread, lib.raw());
TransitionVMToNative transition(thread);
Dart_Handle result =
Dart_Invoke(api_lib, NewString(name), /*argc=*/0, /*argv=*/nullptr);
EXPECT_VALID(result);
}
FlowGraph* TestPipeline::Run(bool is_aot,
std::initializer_list<CompilerPass::Id> passes) {
auto thread = Thread::Current();
auto zone = thread->zone();
const bool optimized = true;
const intptr_t osr_id = Compiler::kNoOSRDeoptId;
auto pipeline = CompilationPipeline::New(zone, function_);
auto parsed_function = new (zone)
ParsedFunction(thread, Function::ZoneHandle(zone, function_.raw()));
pipeline->ParseFunction(parsed_function);
// Extract type feedback before the graph is built, as the graph
// builder uses it to attach it to nodes.
auto ic_data_array = new (zone) ZoneGrowableArray<const ICData*>();
if (!is_aot) {
function_.RestoreICDataMap(ic_data_array, /*clone_ic_data=*/false);
}
FlowGraph* flow_graph = pipeline->BuildFlowGraph(
zone, parsed_function, ic_data_array, osr_id, optimized);
if (is_aot) {
flow_graph->PopulateWithICData(function_);
}
BlockScheduler block_scheduler(flow_graph);
const bool reorder_blocks =
FlowGraph::ShouldReorderBlocks(function_, optimized);
if (reorder_blocks) {
block_scheduler.AssignEdgeWeights();
}
SpeculativeInliningPolicy speculative_policy(/*enable_blacklist=*/false);
CompilerPassState pass_state(thread, flow_graph, &speculative_policy);
pass_state.block_scheduler = &block_scheduler;
pass_state.reorder_blocks = reorder_blocks;
if (optimized) {
pass_state.inline_id_to_function.Add(&function_);
// We do not add the token position now because we don't know the
// position of the inlined call until later. A side effect of this
// is that the length of |inline_id_to_function| is always larger
// than the length of |inline_id_to_token_pos| by one.
// Top scope function has no caller (-1). We do this because we expect
// all token positions to be at an inlined call.
pass_state.caller_inline_id.Add(-1);
JitCallSpecializer jit_call_specializer(flow_graph, &speculative_policy);
AotCallSpecializer aot_call_specializer(/*precompiler=*/nullptr, flow_graph,
&speculative_policy);
if (is_aot) {
pass_state.call_specializer = &aot_call_specializer;
} else {
pass_state.call_specializer = &jit_call_specializer;
}
const auto mode = is_aot ? CompilerPass::kJIT : CompilerPass::kAOT;
if (passes.size() > 0) {
CompilerPass::RunPipelineWithPasses(&pass_state, passes);
} else {
CompilerPass::RunPipeline(mode, &pass_state);
}
}
return flow_graph;
}
} // namespace dart

View file

@ -0,0 +1,80 @@
// Copyright (c) 2019, 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.
#ifndef RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
#define RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
#include "include/dart_api.h"
#include "platform/allocation.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/compiler_state.h"
// The helpers in this file make it easier to write C++ unit tests which assert
// that Dart code gets turned into certain IR.
//
// Here is an example on how to use it:
//
// ISOLATE_UNIT_TEST_CASE(MyIRTest) {
// const char* script = R"(
// void foo() { ... }
// void main() { foo(); }
// )";
//
// // Load the script and exercise the code once.
// const auto& lib = Library::Handle(LoadTestScript(script);
//
// // Cause the code to be exercised once (to populate ICData).
// Invoke(lib, "main");
//
// // Look up the function.
// const auto& function = Function::Handle(GetFunction(lib, "foo"));
//
// // Run the JIT compilation pipeline with two passes.
// TestPipeline pipeline(function);
// FlowGraph* graph = pipeline.RunJITPasses("ComputeSSA,TypePropagation");
//
// ...
// }
//
namespace dart {
class FlowGraph;
class Function;
class Library;
class RawFunction;
class RawLibrary;
RawLibrary* LoadTestScript(const char* script,
Dart_NativeEntryResolver resolver = nullptr);
RawFunction* GetFunction(const Library& lib, const char* name);
void Invoke(const Library& lib, const char* name);
class TestPipeline {
public:
explicit TestPipeline(const Function& function)
: function_(function),
thread_(Thread::Current()),
compiler_state_(thread_) {}
FlowGraph* RunJITPasses(std::initializer_list<CompilerPass::Id> passes) {
return Run(/*is_aot=*/false, passes);
}
FlowGraph* RunAOTPasses(std::initializer_list<CompilerPass::Id> passes) {
return Run(/*is_aot=*/true, passes);
}
private:
FlowGraph* Run(bool is_aot, std::initializer_list<CompilerPass::Id> passes);
const Function& function_;
Thread* thread_;
CompilerState compiler_state_;
};
} // namespace dart
#endif // RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_

View file

@ -1069,15 +1069,14 @@ class CallSiteInliner : public ValueObject {
if (FLAG_precompiled_mode) {
callee_graph->PopulateWithICData(parsed_function->function());
}
#else
#endif
// If we inline a function which is intrinsified without a fall-through
// to IR code, we will not have any ICData attached, so we do it
// manually here.
if (function.is_intrinsic()) {
if (!FLAG_precompiled_mode && function.is_intrinsic()) {
callee_graph->PopulateWithICData(parsed_function->function());
}
#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_DBC) && \
// !defined(TARGET_ARCH_IA32)
// The parameter stubs are a copy of the actual arguments providing
// concrete information about the values, for example constant values,

View file

@ -9,6 +9,7 @@
#include "vm/compiler/backend/loops.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/backend/inliner.h"
#include "vm/compiler/backend/type_propagator.h"
#include "vm/compiler/compiler_pass.h"
@ -62,43 +63,18 @@ void TestString(BufferFormatter* f,
// Helper method to build CFG and compute induction.
static const char* ComputeInduction(Thread* thread, const char* script_chars) {
// Invoke the script.
Dart_Handle script = TestCase::LoadTestScript(script_chars, NULL);
Dart_Handle result = Dart_Invoke(script, NewString("main"), 0, NULL);
EXPECT_VALID(result);
// Load the script and exercise the code once.
const auto& root_library = Library::Handle(LoadTestScript(script_chars));
Invoke(root_library, "main");
// Find parsed function "foo".
TransitionNativeToVM transition(thread);
Zone* zone = thread->zone();
Library& lib =
Library::ZoneHandle(Library::RawCast(Api::UnwrapHandle(script)));
RawFunction* raw_func =
lib.LookupLocalFunction(String::Handle(Symbols::New(thread, "foo")));
ParsedFunction* parsed_function =
new (zone) ParsedFunction(thread, Function::ZoneHandle(zone, raw_func));
EXPECT(parsed_function != nullptr);
// Build flow graph.
CompilerState state(thread);
ZoneGrowableArray<const ICData*>* ic_data_array =
new (zone) ZoneGrowableArray<const ICData*>();
parsed_function->function().RestoreICDataMap(ic_data_array, true);
kernel::FlowGraphBuilder builder(parsed_function, ic_data_array, nullptr,
nullptr, true, DeoptId::kNone);
FlowGraph* flow_graph = builder.BuildGraph();
EXPECT(flow_graph != nullptr);
// Setup some pass data structures and perform minimum passes.
SpeculativeInliningPolicy speculative_policy(/*enable_blacklist*/ false);
CompilerPassState pass_state(thread, flow_graph, &speculative_policy);
JitCallSpecializer call_specializer(flow_graph, &speculative_policy);
pass_state.call_specializer = &call_specializer;
flow_graph->ComputeSSA(0, nullptr);
FlowGraphTypePropagator::Propagate(flow_graph);
call_specializer.ApplyICData();
flow_graph->SelectRepresentations();
FlowGraphTypePropagator::Propagate(flow_graph);
flow_graph->Canonicalize();
std::initializer_list<CompilerPass::Id> passes = {
CompilerPass::kComputeSSA, CompilerPass::kTypePropagation,
CompilerPass::kApplyICData, CompilerPass::kSelectRepresentations,
CompilerPass::kTypePropagation, CompilerPass::kCanonicalize,
};
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
TestPipeline pipeline(function);
FlowGraph* flow_graph = pipeline.RunJITPasses(passes);
// Build loop hierarchy and find induction.
const LoopHierarchy& hierarchy = flow_graph->GetLoopHierarchy();
@ -116,15 +92,17 @@ static const char* ComputeInduction(Thread* thread, const char* script_chars) {
// Induction tests.
//
TEST_CASE(BasicInductionUp) {
ISOLATE_UNIT_TEST_CASE(BasicInductionUp) {
const char* script_chars =
"foo() {\n"
" for (int i = 0; i < 100; i++) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0; i < 100; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 100\n" // phi
@ -133,15 +111,17 @@ TEST_CASE(BasicInductionUp) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(BasicInductionDown) {
ISOLATE_UNIT_TEST_CASE(BasicInductionDown) {
const char* script_chars =
"foo() {\n"
" for (int i = 100; i > 0; i--) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 100; i > 0; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) 0\n" // phi
@ -150,15 +130,17 @@ TEST_CASE(BasicInductionDown) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(BasicInductionStepUp) {
ISOLATE_UNIT_TEST_CASE(BasicInductionStepUp) {
const char* script_chars =
"foo() {\n"
" for (int i = 10; i < 100; i += 2) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 10; i < 100; i += 2) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(10 + 2 * i)\n" // phi
@ -167,15 +149,17 @@ TEST_CASE(BasicInductionStepUp) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(BasicInductionStepDown) {
ISOLATE_UNIT_TEST_CASE(BasicInductionStepDown) {
const char* script_chars =
"foo() {\n"
" for (int i = 100; i >= 0; i -= 7) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 100; i >= 0; i -= 7) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -7 * i)\n" // phi
@ -184,19 +168,21 @@ TEST_CASE(BasicInductionStepDown) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(BasicInductionLoopNest) {
ISOLATE_UNIT_TEST_CASE(BasicInductionLoopNest) {
const char* script_chars =
"foo() {\n"
" for (int i = 0; i < 100; i++) {\n"
" for (int j = 1; j < 100; j++) {\n"
" for (int k = 2; k < 100; k++) {\n"
" }\n"
" }\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0; i < 100; i++) {
for (int j = 1; j < 100; j++) {
for (int k = 2; k < 100; k++) {
}
}
}
}
main() {
foo();
}
)";
const char* expected =
" [2\n"
" LIN(0 + 1 * i) 100\n" // i
@ -213,18 +199,20 @@ TEST_CASE(BasicInductionLoopNest) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(ChainInduction) {
ISOLATE_UNIT_TEST_CASE(ChainInduction) {
const char* script_chars =
"foo() {\n"
" int j = 1;\n"
" for (int i = 0; i < 100; i++) {\n"
" j += 5;\n"
" j += 7;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
int j = 1;
for (int i = 0; i < 100; i++) {
j += 5;
j += 7;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(1 + 12 * i)\n" // phi (j)
@ -236,21 +224,23 @@ TEST_CASE(ChainInduction) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(TwoWayInduction) {
ISOLATE_UNIT_TEST_CASE(TwoWayInduction) {
const char* script_chars =
"foo() {\n"
" int j = 123;\n"
" for (int i = 0; i < 100; i++) {\n"
" if (i == 10) {\n"
" j += 3;\n"
" } else {\n"
" j += 3;\n"
" }\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
int j = 123;
for (int i = 0; i < 100; i++) {
if (i == 10) {
j += 3;
} else {
j += 3;
}
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(123 + 3 * i)\n" // phi (j)
@ -263,19 +253,21 @@ TEST_CASE(TwoWayInduction) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(DerivedInduction) {
ISOLATE_UNIT_TEST_CASE(DerivedInduction) {
const char* script_chars =
"foo() {\n"
" for (int i = 1; i < 100; i++) {\n"
" int a = i + 3;\n"
" int b = i - 5;\n"
" int c = i * 7;\n"
" int d = - i;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 1; i < 100; i++) {
int a = i + 3;
int b = i - 5;
int c = i * 7;
int d = - i;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(1 + 1 * i) 100\n" // phi
@ -288,21 +280,23 @@ TEST_CASE(DerivedInduction) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(WrapAroundAndDerived) {
ISOLATE_UNIT_TEST_CASE(WrapAroundAndDerived) {
const char* script_chars =
"foo() {\n"
" int w = 99;\n"
" for (int i = 0; i < 100; i++) {\n"
" int a = w + 3;\n"
" int b = w - 5;\n"
" int c = w * 7;\n"
" int d = - w;\n"
" w = i;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
int w = 99;
for (int i = 0; i < 100; i++) {
int a = w + 3;
int b = w - 5;
int c = w * 7;
int d = - w;
w = i;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" WRAP(99, LIN(0 + 1 * i))\n" // phi (w)
@ -316,23 +310,25 @@ TEST_CASE(WrapAroundAndDerived) {
EXPECT_STREQ(ComputeInduction(thread, script_chars), expected);
}
TEST_CASE(PeriodicAndDerived) {
ISOLATE_UNIT_TEST_CASE(PeriodicAndDerived) {
const char* script_chars =
"foo() {\n"
" int p1 = 3;\n"
" int p2 = 5;\n"
" for (int i = 0; i < 100; i++) {\n"
" int a = p1 + 3;\n"
" int b = p1 - 5;\n"
" int c = p1 * 7;\n"
" int d = - p1;\n"
" p1 = - p1;\n"
" p2 = 100 - p2;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
int p1 = 3;
int p2 = 5;
for (int i = 0; i < 100; i++) {
int a = p1 + 3;
int b = p1 - 5;
int c = p1 * 7;
int d = - p1;
p1 = - p1;
p2 = 100 - p2;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" PERIOD(3, -3)\n" // phi(p1)
@ -353,15 +349,17 @@ TEST_CASE(PeriodicAndDerived) {
// Bound specific tests.
//
TEST_CASE(NonStrictConditionUp) {
ISOLATE_UNIT_TEST_CASE(NonStrictConditionUp) {
const char* script_chars =
"foo() {\n"
" for (int i = 0; i <= 100; i++) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0; i <= 100; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 101\n" // phi
@ -371,16 +369,18 @@ TEST_CASE(NonStrictConditionUp) {
}
#ifndef TARGET_ARCH_DBC
TEST_CASE(NonStrictConditionUpWrap) {
ISOLATE_UNIT_TEST_CASE(NonStrictConditionUpWrap) {
const char* script_chars =
"foo() {\n"
" for (int i = 0x7ffffffffffffffe; i <= 0x7fffffffffffffff; i++) {\n"
" if (i < 0) break;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0x7ffffffffffffffe; i <= 0x7fffffffffffffff; i++) {
if (i < 0) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(9223372036854775806 + 1 * i)\n" // phi
@ -394,15 +394,17 @@ TEST_CASE(NonStrictConditionUpWrap) {
}
#endif
TEST_CASE(NonStrictConditionDown) {
ISOLATE_UNIT_TEST_CASE(NonStrictConditionDown) {
const char* script_chars =
"foo() {\n"
" for (int i = 100; i >= 0; i--) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 100; i >= 0; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) -1\n" // phi
@ -412,16 +414,18 @@ TEST_CASE(NonStrictConditionDown) {
}
#ifndef TARGET_ARCH_DBC
TEST_CASE(NonStrictConditionDownWrap) {
ISOLATE_UNIT_TEST_CASE(NonStrictConditionDownWrap) {
const char* script_chars =
"foo() {\n"
" for (int i = 0x8000000000000001; i >= 0x8000000000000000; i--) {\n"
" if (i > 0) break;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0x8000000000000001; i >= 0x8000000000000000; i--) {
if (i > 0) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(-9223372036854775807 + -1 * i)\n" // phi
@ -433,17 +437,20 @@ TEST_CASE(NonStrictConditionDownWrap) {
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
#endif
TEST_CASE(NotEqualConditionUp) {
ISOLATE_UNIT_TEST_CASE(NotEqualConditionUp) {
const char* script_chars =
"foo() {\n"
" for (int i = 10; i != 20; i++) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 10; i != 20; i++) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(10 + 1 * i) 20\n" // phi
@ -452,15 +459,17 @@ TEST_CASE(NotEqualConditionUp) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(NotEqualConditionDown) {
ISOLATE_UNIT_TEST_CASE(NotEqualConditionDown) {
const char* script_chars =
"foo() {\n"
" for (int i = 20; i != 10; i--) {\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 20; i != 10; i--) {
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(20 + -1 * i) 10\n" // phi
@ -469,16 +478,18 @@ TEST_CASE(NotEqualConditionDown) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(SecondExitUp) {
ISOLATE_UNIT_TEST_CASE(SecondExitUp) {
const char* script_chars =
"foo() {\n"
" for (int i = 0; i < 100; i++) {\n"
" if (i >= 50) break;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 0; i < 100; i++) {
if (i >= 50) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(0 + 1 * i) 100 50\n" // phi
@ -487,16 +498,18 @@ TEST_CASE(SecondExitUp) {
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
TEST_CASE(SecondExitDown) {
ISOLATE_UNIT_TEST_CASE(SecondExitDown) {
const char* script_chars =
"foo() {\n"
" for (int i = 100; i > 0; i--) {\n"
" if (i <= 10) break;\n"
" }\n"
"}\n"
"main() {\n"
" foo();\n"
"}\n";
R"(
foo() {
for (int i = 100; i > 0; i--) {
if (i <= 10) break;
}
}
main() {
foo();
}
)";
const char* expected =
" [0\n"
" LIN(100 + -1 * i) 0 10\n" // phi

View file

@ -4,6 +4,7 @@
#include "vm/compiler/backend/redundancy_elimination.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/backend/inliner.h"
#include "vm/compiler/backend/loops.h"
#include "vm/compiler/backend/type_propagator.h"
@ -28,53 +29,6 @@ static Dart_NativeFunction NoopNativeLookup(Dart_Handle name,
return reinterpret_cast<Dart_NativeFunction>(&NoopNative);
}
// Helper method to build CFG and run some preliminary optimizations on it.
// TODO(vegorov) we should consider moving this function into utils to share
// between all compiler tests.
static FlowGraph* BuildOptimizedGraph(Thread* thread,
const char* script_chars) {
// Invoke the script.
Dart_Handle script =
TestCase::LoadTestScript(script_chars, &NoopNativeLookup);
Dart_Handle result = Dart_Invoke(script, NewString("main"), 0, nullptr);
EXPECT_VALID(result);
// Find parsed function "foo".
TransitionNativeToVM transition(thread);
Zone* zone = thread->zone();
Library& lib =
Library::ZoneHandle(Library::RawCast(Api::UnwrapHandle(script)));
RawFunction* raw_func =
lib.LookupLocalFunction(String::Handle(Symbols::New(thread, "foo")));
ParsedFunction* parsed_function =
new (zone) ParsedFunction(thread, Function::ZoneHandle(zone, raw_func));
EXPECT(parsed_function != nullptr);
// Build flow graph.
CompilerState state(thread);
ZoneGrowableArray<const ICData*>* ic_data_array =
new (zone) ZoneGrowableArray<const ICData*>();
parsed_function->function().RestoreICDataMap(ic_data_array, true);
kernel::FlowGraphBuilder builder(parsed_function, ic_data_array, nullptr,
nullptr, true, DeoptId::kNone);
FlowGraph* flow_graph = builder.BuildGraph();
EXPECT(flow_graph != nullptr);
// Setup some pass data structures and perform minimum passes.
SpeculativeInliningPolicy speculative_policy(/*enable_blacklist=*/false);
CompilerPassState pass_state(thread, flow_graph, &speculative_policy);
JitCallSpecializer call_specializer(flow_graph, &speculative_policy);
pass_state.call_specializer = &call_specializer;
flow_graph->ComputeSSA(0, nullptr);
FlowGraphTypePropagator::Propagate(flow_graph);
call_specializer.ApplyICData();
flow_graph->SelectRepresentations();
FlowGraphTypePropagator::Propagate(flow_graph);
flow_graph->Canonicalize();
return flow_graph;
}
// Flatten all non-captured LocalVariables from the given scope and its children
// and siblings into the given array based on their environment index.
static void FlattenScopeIntoEnvironment(FlowGraph* graph,
@ -106,13 +60,25 @@ static void TryCatchOptimizerTest(
Thread* thread,
const char* script_chars,
std::initializer_list<const char*> synchronized) {
FlowGraph* graph = BuildOptimizedGraph(thread, script_chars);
// Load the script and exercise the code once.
const auto& root_library =
Library::Handle(LoadTestScript(script_chars, &NoopNativeLookup));
Invoke(root_library, "main");
// Build the flow graph.
std::initializer_list<CompilerPass::Id> passes = {
CompilerPass::kComputeSSA, CompilerPass::kTypePropagation,
CompilerPass::kApplyICData, CompilerPass::kSelectRepresentations,
CompilerPass::kTypePropagation, CompilerPass::kCanonicalize,
};
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
TestPipeline pipeline(function);
FlowGraph* graph = pipeline.RunJITPasses(passes);
// Finally run TryCatchAnalyzer on the graph (in AOT mode).
OptimizeCatchEntryStates(graph, /*is_aot=*/true);
EXPECT_EQ(1, graph->graph_entry()->catch_entries().length());
auto scope = graph->parsed_function().node_sequence()->scope();
GrowableArray<LocalVariable*> env;
@ -146,7 +112,7 @@ static void TryCatchOptimizerTest(
// Tests for TryCatchOptimizer.
//
TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple1) {
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple1) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
@ -165,7 +131,7 @@ TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple1) {
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{});
}
TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple2) {
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple2) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
@ -185,7 +151,7 @@ TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple2) {
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{"a"});
}
TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic1) {
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic1) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
@ -207,7 +173,7 @@ TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic1) {
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{"a", "i"});
}
TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic2) {
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic2) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {

View file

@ -270,6 +270,14 @@ void CompilerPass::RunPipeline(PipelineMode mode,
INVOKE_PASS(ReorderBlocks);
}
void CompilerPass::RunPipelineWithPasses(
CompilerPassState* state,
std::initializer_list<CompilerPass::Id> passes) {
for (auto pass_id : passes) {
passes_[pass_id]->Run(state);
}
}
COMPILER_PASS(ComputeSSA, {
// Transform to SSA (virtual register 0 and no inlining arguments).
flow_graph->ComputeSSA(0, NULL);

View file

@ -7,6 +7,8 @@
#ifndef DART_PRECOMPILED_RUNTIME
#include <initializer_list>
#include "vm/growable_array.h"
#include "vm/token_position.h"
#include "vm/zone.h"
@ -141,6 +143,10 @@ class CompilerPass {
static void RunPipeline(PipelineMode mode, CompilerPassState* state);
static void RunPipelineWithPasses(
CompilerPassState* state,
std::initializer_list<CompilerPass::Id> passes);
protected:
// This function executes the pass. If it returns true then
// we will run Canonicalize on the graph and execute the pass

View file

@ -155,6 +155,8 @@ compiler_sources_tests = [
"assembler/assembler_x64_test.cc",
"assembler/disassembler_test.cc",
"backend/il_test.cc",
"backend/il_test_helper.h",
"backend/il_test_helper.cc",
"backend/locations_helpers_test.cc",
"backend/loops_test.cc",
"backend/range_analysis_test.cc",

View file

@ -468,9 +468,10 @@ class VMTestSuite extends TestSuite {
var args = configuration.standardOptions.toList();
if (configuration.compilerConfiguration.previewDart2) {
final dfePath = new Path("$buildDir/gen/kernel-service.dart.snapshot")
.absolute
.toNativePath();
final filename = configuration.architecture == Architecture.x64
? '$buildDir/gen/kernel-service.dart.snapshot'
: '$buildDir/gen/kernel_service.dill';
final dfePath = new Path(filename).absolute.toNativePath();
// '--dfe' has to be the first argument for run_vm_test to pick it up.
args.insert(0, '--dfe=$dfePath');
}