1 | // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include <vector> |
6 | |
7 | #include "vm/compiler/backend/il.h" |
8 | #include "vm/compiler/backend/il_printer.h" |
9 | #include "vm/compiler/backend/il_test_helper.h" |
10 | #include "vm/compiler/call_specializer.h" |
11 | #include "vm/compiler/compiler_pass.h" |
12 | #include "vm/object.h" |
13 | #include "vm/unit_test.h" |
14 | |
15 | namespace dart { |
16 | |
17 | ISOLATE_UNIT_TEST_CASE(ReachabilityFence_Simple) { |
18 | // clang-format off |
19 | auto kScript = |
20 | Utils::CStringUniquePtr(OS::SCreate(nullptr, |
21 | R"( |
22 | import 'dart:_internal' show reachabilityFence; |
23 | |
24 | int someGlobal = 0; |
25 | |
26 | class A { |
27 | int%s a; |
28 | } |
29 | |
30 | void someFunction(int arg) { |
31 | someGlobal += arg; |
32 | } |
33 | |
34 | main() { |
35 | final object = A()..a = 10; |
36 | someFunction(object.a%s); |
37 | reachabilityFence(object); |
38 | } |
39 | )" , |
40 | TestCase::NullableTag(), TestCase::NullAssertTag()), std::free); |
41 | // clang-format on |
42 | |
43 | const auto& root_library = Library::Handle(LoadTestScript(kScript.get())); |
44 | |
45 | Invoke(root_library, "main" ); |
46 | |
47 | const auto& function = Function::Handle(GetFunction(root_library, "main" )); |
48 | TestPipeline pipeline(function, CompilerPass::kJIT); |
49 | FlowGraph* flow_graph = pipeline.RunPasses({}); |
50 | ASSERT(flow_graph != nullptr); |
51 | |
52 | auto entry = flow_graph->graph_entry()->normal_entry(); |
53 | EXPECT(entry != nullptr); |
54 | |
55 | // v2 <- AllocateObject(A <not-aliased>) T{A} |
56 | // ... |
57 | // [use field of object v2] |
58 | // ReachabilityFence(v2) |
59 | AllocateObjectInstr* allocate_object = nullptr; |
60 | ReachabilityFenceInstr* fence = nullptr; |
61 | |
62 | ILMatcher cursor(flow_graph, entry); |
63 | RELEASE_ASSERT(cursor.TryMatch({ |
64 | kMoveGlob, |
65 | // Allocate the object. |
66 | {kMatchAndMoveAllocateObject, &allocate_object}, |
67 | kMoveGlob, |
68 | // The call. |
69 | kMatchAndMoveStoreStaticField, |
70 | // The fence should not be moved before the call. |
71 | {kMatchAndMoveReachabilityFence, &fence}, |
72 | })); |
73 | |
74 | EXPECT(fence->value()->definition() == allocate_object); |
75 | } |
76 | |
77 | ISOLATE_UNIT_TEST_CASE(ReachabilityFence_Loop) { |
78 | // clang-format off |
79 | auto kScript = |
80 | Utils::CStringUniquePtr(OS::SCreate(nullptr, R"( |
81 | import 'dart:_internal' show reachabilityFence; |
82 | |
83 | int someGlobal = 0; |
84 | |
85 | class A { |
86 | int%s a; |
87 | } |
88 | |
89 | @pragma('vm:never-inline') |
90 | A makeSomeA() { |
91 | return A()..a = 10; |
92 | } |
93 | |
94 | void someFunction(int arg) { |
95 | someGlobal += arg; |
96 | } |
97 | |
98 | main() { |
99 | final object = makeSomeA(); |
100 | for(int i = 0; i < 100000; i++) { |
101 | someFunction(object.a%s); |
102 | reachabilityFence(object); |
103 | } |
104 | } |
105 | )" , TestCase::NullableTag(), TestCase::NullAssertTag()), std::free); |
106 | // clang-format on |
107 | |
108 | const auto& root_library = Library::Handle(LoadTestScript(kScript.get())); |
109 | |
110 | Invoke(root_library, "main" ); |
111 | |
112 | const auto& function = Function::Handle(GetFunction(root_library, "main" )); |
113 | TestPipeline pipeline(function, CompilerPass::kJIT); |
114 | FlowGraph* flow_graph = pipeline.RunPasses({}); |
115 | ASSERT(flow_graph != nullptr); |
116 | |
117 | auto entry = flow_graph->graph_entry()->normal_entry(); |
118 | EXPECT(entry != nullptr); |
119 | |
120 | StaticCallInstr* object = nullptr; |
121 | LoadFieldInstr* field_load = nullptr; |
122 | ReachabilityFenceInstr* fence = nullptr; |
123 | |
124 | ILMatcher cursor(flow_graph, entry); |
125 | RELEASE_ASSERT(cursor.TryMatch( |
126 | { |
127 | // Get the object from some method |
128 | {kMatchAndMoveStaticCall, &object}, |
129 | // Load the field outside the loop. |
130 | {kMatchAndMoveLoadField, &field_load}, |
131 | // Go into the loop. |
132 | kMatchAndMoveBranchTrue, |
133 | // The fence should not be moved outside of the loop. |
134 | {kMatchAndMoveReachabilityFence, &fence}, |
135 | }, |
136 | /*insert_before=*/kMoveGlob)); |
137 | |
138 | EXPECT(field_load->instance()->definition() == object); |
139 | EXPECT(fence->value()->definition() == object); |
140 | } |
141 | |
142 | ISOLATE_UNIT_TEST_CASE(ReachabilityFence_NoCanonicalize) { |
143 | // clang-format off |
144 | auto kScript = |
145 | Utils::CStringUniquePtr(OS::SCreate(nullptr, R"( |
146 | import 'dart:_internal' show reachabilityFence; |
147 | |
148 | int someGlobal = 0; |
149 | |
150 | class A { |
151 | int%s a; |
152 | } |
153 | |
154 | @pragma('vm:never-inline') |
155 | A makeSomeA() { |
156 | return A()..a = 10; |
157 | } |
158 | |
159 | void someFunction(int arg) { |
160 | someGlobal += arg; |
161 | } |
162 | |
163 | main() { |
164 | final object = makeSomeA(); |
165 | reachabilityFence(object); |
166 | for(int i = 0; i < 100000; i++) { |
167 | someFunction(object.a%s); |
168 | reachabilityFence(object); |
169 | } |
170 | reachabilityFence(object); |
171 | reachabilityFence(object); |
172 | } |
173 | )" , TestCase::NullableTag(), TestCase::NullAssertTag()), std::free); |
174 | // clang-format on |
175 | |
176 | const auto& root_library = Library::Handle(LoadTestScript(kScript.get())); |
177 | |
178 | Invoke(root_library, "main" ); |
179 | |
180 | const auto& function = Function::Handle(GetFunction(root_library, "main" )); |
181 | TestPipeline pipeline(function, CompilerPass::kJIT); |
182 | FlowGraph* flow_graph = pipeline.RunPasses({}); |
183 | ASSERT(flow_graph != nullptr); |
184 | |
185 | auto entry = flow_graph->graph_entry()->normal_entry(); |
186 | EXPECT(entry != nullptr); |
187 | |
188 | StaticCallInstr* object = nullptr; |
189 | ReachabilityFenceInstr* fence1 = nullptr; |
190 | ReachabilityFenceInstr* fence2 = nullptr; |
191 | ReachabilityFenceInstr* fence3 = nullptr; |
192 | ReachabilityFenceInstr* fence4 = nullptr; |
193 | |
194 | ILMatcher cursor(flow_graph, entry); |
195 | RELEASE_ASSERT(cursor.TryMatch( |
196 | { |
197 | {kMatchAndMoveStaticCall, &object}, |
198 | {kMatchAndMoveReachabilityFence, &fence1}, |
199 | kMatchAndMoveBranchTrue, |
200 | {kMatchAndMoveReachabilityFence, &fence2}, |
201 | kMatchAndMoveBranchFalse, |
202 | {kMatchAndMoveReachabilityFence, &fence3}, |
203 | {kMatchAndMoveReachabilityFence, &fence4}, |
204 | }, |
205 | /*insert_before=*/kMoveGlob)); |
206 | |
207 | EXPECT(fence1->value()->definition() == object); |
208 | EXPECT(fence2->value()->definition() == object); |
209 | EXPECT(fence3->value()->definition() == object); |
210 | EXPECT(fence4->value()->definition() == object); |
211 | } |
212 | |
213 | } // namespace dart |
214 | |