Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
kernel_binary_flowgraph_test.cc
Go to the documentation of this file.
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
6
8#include "vm/object.h"
9#include "vm/unit_test.h"
10
11namespace dart {
12
13ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConstFoldStringConcats) {
14 // According to the Dart spec:
15 // "Adjacent strings are implicitly concatenated to form a single string
16 // literal."
17 const char* kScript = R"(
18 test() {
19 var s = 'aaaa'
20 'bbbb'
21 'cccc';
22 return s;
23 }
24 )";
25
26 const auto& root_library = Library::Handle(LoadTestScript(kScript));
27 const auto& function = Function::Handle(GetFunction(root_library, "test"));
28
29 Invoke(root_library, "test");
30
32 FlowGraph* flow_graph = pipeline.RunPasses({
33 CompilerPass::kComputeSSA,
34 });
35
36 auto entry = flow_graph->graph_entry()->normal_entry();
37 EXPECT(entry != nullptr);
38
39 DartReturnInstr* ret = nullptr;
40
41 ILMatcher cursor(flow_graph, entry);
42 // clang-format off
44 kMatchAndMoveFunctionEntry,
45 kMatchAndMoveCheckStackOverflow,
46 kMoveDebugStepChecks,
47 {kMatchDartReturn, &ret},
48 }));
49 // clang-format on
50
51 EXPECT(ret->value()->BindsToConstant());
52 EXPECT(ret->value()->BoundConstant().IsString());
53 const String& ret_str = String::Cast(ret->value()->BoundConstant());
54 EXPECT(ret_str.Equals("aaaabbbbcccc"));
55}
56
57ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_FlattenNestedStringInterp) {
58 // We should collapse nested StringInterpolates:
59 const char* kScript = R"(
60 test(String s) {
61 return '$s' '${'d' 'e'}';
62 }
63 main() => test('u');
64 )";
65
66 const auto& root_library = Library::Handle(LoadTestScript(kScript));
67 const auto& function = Function::Handle(GetFunction(root_library, "test"));
68
69 Invoke(root_library, "main");
70
71 TestPipeline pipeline(function, CompilerPass::kJIT);
72 FlowGraph* flow_graph = pipeline.RunPasses({
73 CompilerPass::kComputeSSA,
74 });
75
76 auto entry = flow_graph->graph_entry()->normal_entry();
77 EXPECT(entry != nullptr);
78
79 StoreIndexedInstr* store1 = nullptr;
80 StoreIndexedInstr* store2 = nullptr;
81
82 ILMatcher cursor(flow_graph, entry);
83 // clang-format off
85 kMatchAndMoveFunctionEntry,
86 kMatchAndMoveCheckStackOverflow,
87 kMoveDebugStepChecks,
88 kMatchAndMoveCreateArray,
89 {kMatchAndMoveStoreIndexed, &store1},
90 {kMatchAndMoveStoreIndexed, &store2},
91 kMatchAndMoveStaticCall,
93 kMatchDartReturn,
94 }));
95 // clang-format on
96
97 // StoreIndexed(tmp_array, 0, s)
98 EXPECT(store1->index()->BindsToConstant());
99 EXPECT(store1->index()->BoundConstant().IsInteger());
100 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
101
102 EXPECT(!store1->value()->BindsToConstant());
103
104 // StoreIndexed(tmp_array, 1, "de")
105 EXPECT(store2->index()->BindsToConstant());
106 EXPECT(store2->index()->BoundConstant().IsInteger());
107 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
108
109 EXPECT(store2->value()->BindsToConstant());
110 EXPECT(store2->value()->BoundConstant().IsString());
111 EXPECT(String::Cast(store2->value()->BoundConstant()).Equals("de"));
112}
113
114ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_DropEmptyStringInterp) {
115 // We should drop empty strings from StringInterpolates:
116 const char* kScript = R"(
117 test(s) {
118 return '' 'a' '$s' '' 'b' '';
119 }
120 main() => test('u');
121 )";
122
123 const auto& root_library = Library::Handle(LoadTestScript(kScript));
124 const auto& function = Function::Handle(GetFunction(root_library, "test"));
125
126 Invoke(root_library, "main");
127
128 TestPipeline pipeline(function, CompilerPass::kJIT);
129 FlowGraph* flow_graph = pipeline.RunPasses({
130 CompilerPass::kComputeSSA,
131 });
132
133 auto entry = flow_graph->graph_entry()->normal_entry();
134 EXPECT(entry != nullptr);
135
136 StoreIndexedInstr* store1 = nullptr;
137 StoreIndexedInstr* store2 = nullptr;
138 StoreIndexedInstr* store3 = nullptr;
139
140 ILMatcher cursor(flow_graph, entry);
141 // clang-format off
142 RELEASE_ASSERT(cursor.TryMatch({
143 kMatchAndMoveFunctionEntry,
144 kMatchAndMoveCheckStackOverflow,
145 kMoveDebugStepChecks,
146 kMatchAndMoveCreateArray,
147 {kMatchAndMoveStoreIndexed, &store1},
148 {kMatchAndMoveStoreIndexed, &store2},
149 {kMatchAndMoveStoreIndexed, &store3},
150 kMatchAndMoveStaticCall,
152 kMatchDartReturn,
153 }));
154 // clang-format on
155
156 // StoreIndexed(tmp_array, 0, "ab")
157 EXPECT(store1->index()->BindsToConstant());
158 EXPECT(store1->index()->BoundConstant().IsInteger());
159 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
160
161 EXPECT(store1->value()->BindsToConstant());
162 EXPECT(store1->value()->BoundConstant().IsString());
163 EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("a"));
164
165 // StoreIndexed(tmp_array, 1, s)
166 EXPECT(store2->index()->BindsToConstant());
167 EXPECT(store2->index()->BoundConstant().IsInteger());
168 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
169
170 EXPECT(!store2->value()->BindsToConstant());
171
172 // StoreIndexed(tmp_array, 2, "b")
173 EXPECT(store3->index()->BindsToConstant());
174 EXPECT(store3->index()->BoundConstant().IsInteger());
175 EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2);
176
177 EXPECT(store3->value()->BindsToConstant());
178 EXPECT(store3->value()->BoundConstant().IsString());
179 EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("b"));
180}
181
182ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConcatStringLits) {
183 // We should drop empty strings from StringInterpolates:
184 const char* kScript = R"(
185 test(s) {
186 return '' 'a' '' 'b' '$s' '' 'c' '' 'd' '';
187 }
188 main() => test('u');
189 )";
190
191 const auto& root_library = Library::Handle(LoadTestScript(kScript));
192 const auto& function = Function::Handle(GetFunction(root_library, "test"));
193
194 Invoke(root_library, "main");
195
196 TestPipeline pipeline(function, CompilerPass::kJIT);
197 FlowGraph* flow_graph = pipeline.RunPasses({
198 CompilerPass::kComputeSSA,
199 });
200
201 auto entry = flow_graph->graph_entry()->normal_entry();
202 EXPECT(entry != nullptr);
203
204 StoreIndexedInstr* store1 = nullptr;
205 StoreIndexedInstr* store2 = nullptr;
206 StoreIndexedInstr* store3 = nullptr;
207
208 ILMatcher cursor(flow_graph, entry);
209 // clang-format off
210 RELEASE_ASSERT(cursor.TryMatch({
211 kMatchAndMoveFunctionEntry,
212 kMatchAndMoveCheckStackOverflow,
213 kMoveDebugStepChecks,
214 kMatchAndMoveCreateArray,
215 {kMatchAndMoveStoreIndexed, &store1},
216 {kMatchAndMoveStoreIndexed, &store2},
217 {kMatchAndMoveStoreIndexed, &store3},
218 kMatchAndMoveStaticCall,
220 kMatchDartReturn,
221 }));
222 // clang-format on
223
224 // StoreIndexed(tmp_array, 0, "ab")
225 EXPECT(store1->index()->BindsToConstant());
226 EXPECT(store1->index()->BoundConstant().IsInteger());
227 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
228
229 EXPECT(store1->value()->BindsToConstant());
230 EXPECT(store1->value()->BoundConstant().IsString());
231 EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("ab"));
232
233 // StoreIndexed(tmp_array, 1, s)
234 EXPECT(store2->index()->BindsToConstant());
235 EXPECT(store2->index()->BoundConstant().IsInteger());
236 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
237
238 EXPECT(!store2->value()->BindsToConstant());
239
240 // StoreIndexed(tmp_array, 2, "cd")
241 EXPECT(store3->index()->BindsToConstant());
242 EXPECT(store3->index()->BoundConstant().IsInteger());
243 EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2);
244
245 EXPECT(store3->value()->BindsToConstant());
246 EXPECT(store3->value()->BoundConstant().IsString());
247 EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("cd"));
248}
249
250ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_InvariantFlagInListLiterals) {
251 const char* kScript = R"(
252 test() {
253 return [...[], 42];
254 }
255 )";
256
257 const auto& root_library = Library::Handle(LoadTestScript(kScript));
258 const auto& function = Function::Handle(GetFunction(root_library, "test"));
259
260 Invoke(root_library, "test");
261
262 TestPipeline pipeline(function, CompilerPass::kJIT);
263 FlowGraph* flow_graph = pipeline.RunPasses({
264 CompilerPass::kComputeSSA,
265 });
266
267 auto entry = flow_graph->graph_entry()->normal_entry();
268 EXPECT(entry != nullptr);
269
270 InstanceCallInstr* call_add = nullptr;
271
272 ILMatcher cursor(flow_graph, entry);
273 // clang-format off
274 RELEASE_ASSERT(cursor.TryMatch({
275 kMatchAndMoveFunctionEntry,
276 kMatchAndMoveCheckStackOverflow,
277 kMoveDebugStepChecks,
278 kMatchAndMoveStaticCall,
279 kMatchAndMoveStaticCall,
280 {kMatchAndMoveInstanceCall, &call_add},
282 kMatchDartReturn,
283 }));
284 // clang-format on
285
286 EXPECT(call_add != nullptr);
287 EXPECT(call_add->function_name().Equals("add"));
288 EXPECT(call_add->entry_kind() == Code::EntryKind::kUnchecked);
289}
290
291ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_TypedClosureCall) {
292 // This test ensures that a typed closure call (i.e. the closure being called
293 // has a real function type as static type) ends up compiling to
294 //
295 // CheckNull()+ClosureCall()
296 //
297 // instead of
298 //
299 // InstanceCall(dyn:call)
300 //
301 // This is a regression test for a case where JIT support uses dynamic calls
302 // instead of typed closure calls if call-site attribute metadata is missing
303 // which is the case if an incremental kernel compiler is used. The
304 // [LoadTestScript] below uses IKG compiler, so this is a regression test for:
305 //
306 // - https://github.com/dart-lang/sdk/issues/46320
307 // - https://github.com/dart-lang/sdk/issues/45421
308 //
309 const char* kScript = R"(
310 int callClosure(int Function(int) fun, int value) => fun(value);
311 test() => callClosure((int a) => a + 1, 10);
312 )";
313
314 const auto& root_library = Library::Handle(LoadTestScript(kScript));
315 Invoke(root_library, "test");
316
317 const auto& callClosureFunction =
318 Function::Handle(GetFunction(root_library, "callClosure"));
319 TestPipeline pipeline(callClosureFunction, CompilerPass::kJIT);
320 FlowGraph* flow_graph = pipeline.RunPasses({
321 CompilerPass::kComputeSSA,
322 });
323
324 auto entry = flow_graph->graph_entry()->normal_entry();
325 EXPECT(entry != nullptr);
326
327 ILMatcher cursor(flow_graph, entry, true);
328 // clang-format off
329 std::initializer_list<MatchCode> expected = {
330 kMatchAndMoveFunctionEntry,
331 kMatchAndMoveCheckStackOverflow,
333 kMatchAndMoveCheckNull,
334 kMatchAndMoveLoadField,
336#if !defined(PRODUCT)
337 kMatchAndMoveRecordCoverage,
338#endif
339 kMatchAndMoveClosureCall,
341 kMatchDartReturn,
342 };
343 RELEASE_ASSERT(cursor.TryMatch(expected));
344 // clang-format on
345}
346
348 StreamingFlowGraphBuilder_StaticGetFinalFieldWithTrivialInitializer) {
349 const char* kScript = R"(
350 final int x = 0xFEEDFEED;
351 test() {
352 return x;
353 }
354 )";
355
356 const auto& root_library = Library::Handle(LoadTestScript(kScript));
357 const auto& function = Function::Handle(GetFunction(root_library, "test"));
358
359 Invoke(root_library, "test");
360
361 TestPipeline pipeline(function, CompilerPass::kJIT);
362 FlowGraph* flow_graph = pipeline.RunPasses({
363 CompilerPass::kComputeSSA,
364 });
365
366 auto entry = flow_graph->graph_entry()->normal_entry();
367 EXPECT(entry != nullptr);
368
369 DartReturnInstr* return_instr = nullptr;
370
371 ILMatcher cursor(flow_graph, entry);
372 RELEASE_ASSERT(cursor.TryMatch({
373 kMatchAndMoveFunctionEntry,
374 kMatchAndMoveCheckStackOverflow,
375 kMoveDebugStepChecks,
376 {kMatchDartReturn, &return_instr},
377 }));
378
379 EXPECT(return_instr != nullptr);
380 ConstantInstr* const_value =
381 return_instr->value()->definition()->AsConstant();
382 EXPECT(const_value != nullptr);
383 EXPECT(const_value->value().IsInteger());
384 EXPECT_EQ(0xFEEDFEED, Integer::Cast(const_value->value()).AsInt64Value());
385}
386
387} // namespace dart
#define EXPECT(type, expectedAlignment, expectedSize)
#define RELEASE_ASSERT(cond)
Definition assert.h:327
GraphEntryInstr * graph_entry() const
Definition flow_graph.h:268
FunctionEntryInstr * normal_entry() const
Definition il.h:1986
bool TryMatch(std::initializer_list< MatchCode > match_codes, MatchOpCode insert_before=kInvalidMatchOpCode)
static Object & Handle()
Definition object.h:407
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
Dart_NativeFunction function
Definition fuchsia.cc:51
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
@ kMoveDebugStepChecks
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64