blob: 21e63d81a67dacc7c0cc75f61cd3322a372d040b [file] [log] [blame]
// Copyright (c) 2018, 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/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"
#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/frontend/kernel_to_il.h"
#include "vm/compiler/jit/jit_call_specializer.h"
#include "vm/log.h"
#include "vm/object.h"
#include "vm/parser.h"
#include "vm/symbols.h"
#include "vm/unit_test.h"
namespace dart {
static void NoopNative(Dart_NativeArguments args) {}
static Dart_NativeFunction NoopNativeLookup(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) {
ASSERT(auto_setup_scope != nullptr);
*auto_setup_scope = false;
return reinterpret_cast<Dart_NativeFunction>(&NoopNative);
}
// 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,
LocalScope* scope,
GrowableArray<LocalVariable*>* env) {
for (intptr_t i = 0; i < scope->num_variables(); i++) {
auto var = scope->VariableAt(i);
if (var->is_captured()) {
continue;
}
auto index = graph->EnvIndex(var);
env->EnsureLength(index + 1, nullptr);
(*env)[index] = var;
}
if (scope->sibling() != nullptr) {
FlattenScopeIntoEnvironment(graph, scope->sibling(), env);
}
if (scope->child() != nullptr) {
FlattenScopeIntoEnvironment(graph, scope->child(), env);
}
}
// Run TryCatchAnalyzer optimization on the function foo from the given script
// and check that the only variables from the given list are synchronized
// on catch entry.
static void TryCatchOptimizerTest(
Thread* thread,
const char* script_chars,
std::initializer_list<const char*> synchronized) {
// 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, CompilerPass::kJIT);
FlowGraph* graph = pipeline.RunPasses(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;
FlattenScopeIntoEnvironment(graph, scope, &env);
for (intptr_t i = 0; i < env.length(); i++) {
bool found = false;
for (auto name : synchronized) {
if (env[i]->name().Equals(name)) {
found = true;
break;
}
}
if (!found) {
env[i] = nullptr;
}
}
CatchBlockEntryInstr* catch_entry = graph->graph_entry()->catch_entries()[0];
// We should only synchronize state for variables from the synchronized list.
for (auto defn : *catch_entry->initial_definitions()) {
if (ParameterInstr* param = defn->AsParameter()) {
EXPECT(0 <= param->index() && param->index() < env.length());
EXPECT(env[param->index()] != nullptr);
}
}
}
//
// Tests for TryCatchOptimizer.
//
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple1) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
var a = blackhole(), b = blackhole();
try {
blackhole([a, b]);
} catch (e) {
// nothing is used
}
}
main() {
foo(42);
}
)";
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{});
}
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Simple2) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
var a = blackhole(), b = blackhole();
try {
blackhole([a, b]);
} catch (e) {
// a should be synchronized
blackhole(a);
}
}
main() {
foo(42);
}
)";
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{"a"});
}
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic1) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
var a = blackhole(), b;
for (var i = 0; i < 42; i++) {
b = blackhole();
try {
blackhole([a, b]);
} catch (e) {
// a and i should be synchronized
}
}
}
main() {
foo(42);
}
)";
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{"a", "i"});
}
ISOLATE_UNIT_TEST_CASE(TryCatchOptimizer_DeadParameterElimination_Cyclic2) {
const char* script_chars = R"(
dynamic blackhole([dynamic val]) native 'BlackholeNative';
foo(int p) {
var a = blackhole(), b = blackhole();
for (var i = 0; i < 42; i++) {
try {
blackhole([a, b]);
} catch (e) {
// a, b and i should be synchronized
}
}
}
main() {
foo(42);
}
)";
TryCatchOptimizerTest(thread, script_chars, /*synchronized=*/{"a", "b", "i"});
}
} // namespace dart