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