50f1f1459e
Change-Id: Ia49c64bf8461b8acf019c398635e27b11fe6d25e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149666 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
214 lines
6.0 KiB
C++
214 lines
6.0 KiB
C++
// 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
|