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