// 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 #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 ) 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