blob: e26bf4e2f844a6566a3343d8372410cfb901257b [file] [log] [blame]
// Copyright (c) 2020, 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 <vector>
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/call_specializer.h"
#include "vm/compiler/compiler_pass.h"
#include "vm/object.h"
#include "vm/unit_test.h"
namespace dart {
ISOLATE_UNIT_TEST_CASE(ReachabilityFence_Simple) {
// clang-format off
auto kScript =
Utils::CStringUniquePtr(OS::SCreate(nullptr,
R"(
import 'dart:_internal' show reachabilityFence;
int someGlobal = 0;
class A {
int%s a;
}
void someFunction(int arg) {
someGlobal += arg;
}
main() {
final object = A()..a = 10;
someFunction(object.a%s);
reachabilityFence(object);
}
)",
TestCase::NullableTag(), TestCase::NullAssertTag()), std::free);
// clang-format on
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
Invoke(root_library, "main");
const auto& function = Function::Handle(GetFunction(root_library, "main"));
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({});
ASSERT(flow_graph != nullptr);
auto entry = flow_graph->graph_entry()->normal_entry();
EXPECT(entry != nullptr);
// v2 <- AllocateObject(A <not-aliased>) T{A}
// ...
// [use field of object v2]
// ReachabilityFence(v2)
AllocateObjectInstr* allocate_object = nullptr;
ReachabilityFenceInstr* fence = nullptr;
ILMatcher cursor(flow_graph, entry);
RELEASE_ASSERT(cursor.TryMatch({
kMoveGlob,
// Allocate the object.
{kMatchAndMoveAllocateObject, &allocate_object},
kMoveGlob,
// The call.
kMatchAndMoveStoreStaticField,
// The fence should not be moved before the call.
{kMatchAndMoveReachabilityFence, &fence},
}));
EXPECT(fence->value()->definition() == allocate_object);
}
ISOLATE_UNIT_TEST_CASE(ReachabilityFence_Loop) {
// clang-format off
auto kScript =
Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
import 'dart:_internal' show reachabilityFence;
int someGlobal = 0;
class A {
int%s a;
}
@pragma('vm:never-inline')
A makeSomeA() {
return A()..a = 10;
}
void someFunction(int arg) {
someGlobal += arg;
}
main() {
final object = makeSomeA();
for(int i = 0; i < 100000; i++) {
someFunction(object.a%s);
reachabilityFence(object);
}
}
)", TestCase::NullableTag(), TestCase::NullAssertTag()), std::free);
// clang-format on
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
Invoke(root_library, "main");
const auto& function = Function::Handle(GetFunction(root_library, "main"));
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({});
ASSERT(flow_graph != nullptr);
auto entry = flow_graph->graph_entry()->normal_entry();
EXPECT(entry != nullptr);
StaticCallInstr* object = nullptr;
LoadFieldInstr* field_load = nullptr;
ReachabilityFenceInstr* fence = nullptr;
ILMatcher cursor(flow_graph, entry);
RELEASE_ASSERT(cursor.TryMatch(
{
// Get the object from some method
{kMatchAndMoveStaticCall, &object},
// Load the field outside the loop.
{kMatchAndMoveLoadField, &field_load},
// Go into the loop.
kMatchAndMoveBranchTrue,
// The fence should not be moved outside of the loop.
{kMatchAndMoveReachabilityFence, &fence},
},
/*insert_before=*/kMoveGlob));
EXPECT(field_load->instance()->definition() == object);
EXPECT(fence->value()->definition() == object);
}
ISOLATE_UNIT_TEST_CASE(ReachabilityFence_NoCanonicalize) {
// clang-format off
auto kScript =
Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
import 'dart:_internal' show reachabilityFence;
int someGlobal = 0;
class A {
int%s a;
}
@pragma('vm:never-inline')
A makeSomeA() {
return A()..a = 10;
}
void someFunction(int arg) {
someGlobal += arg;
}
main() {
final object = makeSomeA();
reachabilityFence(object);
for(int i = 0; i < 100000; i++) {
someFunction(object.a%s);
reachabilityFence(object);
}
reachabilityFence(object);
reachabilityFence(object);
}
)", TestCase::NullableTag(), TestCase::NullAssertTag()), std::free);
// clang-format on
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
Invoke(root_library, "main");
const auto& function = Function::Handle(GetFunction(root_library, "main"));
TestPipeline pipeline(function, CompilerPass::kJIT);
FlowGraph* flow_graph = pipeline.RunPasses({});
ASSERT(flow_graph != nullptr);
auto entry = flow_graph->graph_entry()->normal_entry();
EXPECT(entry != nullptr);
StaticCallInstr* object = nullptr;
ReachabilityFenceInstr* fence1 = nullptr;
ReachabilityFenceInstr* fence2 = nullptr;
ReachabilityFenceInstr* fence3 = nullptr;
ReachabilityFenceInstr* fence4 = nullptr;
ILMatcher cursor(flow_graph, entry);
RELEASE_ASSERT(cursor.TryMatch(
{
{kMatchAndMoveStaticCall, &object},
{kMatchAndMoveReachabilityFence, &fence1},
kMatchAndMoveBranchTrue,
{kMatchAndMoveReachabilityFence, &fence2},
kMatchAndMoveBranchFalse,
{kMatchAndMoveReachabilityFence, &fence3},
{kMatchAndMoveReachabilityFence, &fence4},
},
/*insert_before=*/kMoveGlob));
EXPECT(fence1->value()->definition() == object);
EXPECT(fence2->value()->definition() == object);
EXPECT(fence3->value()->definition() == object);
EXPECT(fence4->value()->definition() == object);
}
} // namespace dart