mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:59:47 +00:00
[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:
parent
600b2831ef
commit
31d558c81c
|
@ -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 = [
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
122
runtime/vm/compiler/backend/il_test_helper.cc
Normal file
122
runtime/vm/compiler/backend/il_test_helper.cc
Normal 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
|
80
runtime/vm/compiler/backend/il_test_helper.h
Normal file
80
runtime/vm/compiler/backend/il_test_helper.h
Normal 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_
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue