Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 kMatchAndMoveRecordCoverage,
92 kMatchAndMoveStaticCall,
94 kMatchDartReturn,
95 }));
96 // clang-format on
97
98 // StoreIndexed(tmp_array, 0, s)
99 EXPECT(store1->index()->BindsToConstant());
100 EXPECT(store1->index()->BoundConstant().IsInteger());
101 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
102
103 EXPECT(!store1->value()->BindsToConstant());
104
105 // StoreIndexed(tmp_array, 1, "de")
106 EXPECT(store2->index()->BindsToConstant());
107 EXPECT(store2->index()->BoundConstant().IsInteger());
108 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
109
110 EXPECT(store2->value()->BindsToConstant());
111 EXPECT(store2->value()->BoundConstant().IsString());
112 EXPECT(String::Cast(store2->value()->BoundConstant()).Equals("de"));
113}
114
115ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_DropEmptyStringInterp) {
116 // We should drop empty strings from StringInterpolates:
117 const char* kScript = R"(
118 test(s) {
119 return '' 'a' '$s' '' 'b' '';
120 }
121 main() => test('u');
122 )";
123
124 const auto& root_library = Library::Handle(LoadTestScript(kScript));
125 const auto& function = Function::Handle(GetFunction(root_library, "test"));
126
127 Invoke(root_library, "main");
128
129 TestPipeline pipeline(function, CompilerPass::kJIT);
130 FlowGraph* flow_graph = pipeline.RunPasses({
131 CompilerPass::kComputeSSA,
132 });
133
134 auto entry = flow_graph->graph_entry()->normal_entry();
135 EXPECT(entry != nullptr);
136
137 StoreIndexedInstr* store1 = nullptr;
138 StoreIndexedInstr* store2 = nullptr;
139 StoreIndexedInstr* store3 = nullptr;
140
141 ILMatcher cursor(flow_graph, entry);
142 // clang-format off
143 RELEASE_ASSERT(cursor.TryMatch({
144 kMatchAndMoveFunctionEntry,
145 kMatchAndMoveCheckStackOverflow,
146 kMoveDebugStepChecks,
147 kMatchAndMoveCreateArray,
148 {kMatchAndMoveStoreIndexed, &store1},
149 {kMatchAndMoveStoreIndexed, &store2},
150 {kMatchAndMoveStoreIndexed, &store3},
151 kMatchAndMoveRecordCoverage,
152 kMatchAndMoveStaticCall,
154 kMatchDartReturn,
155 }));
156 // clang-format on
157
158 // StoreIndexed(tmp_array, 0, "ab")
159 EXPECT(store1->index()->BindsToConstant());
160 EXPECT(store1->index()->BoundConstant().IsInteger());
161 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
162
163 EXPECT(store1->value()->BindsToConstant());
164 EXPECT(store1->value()->BoundConstant().IsString());
165 EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("a"));
166
167 // StoreIndexed(tmp_array, 1, s)
168 EXPECT(store2->index()->BindsToConstant());
169 EXPECT(store2->index()->BoundConstant().IsInteger());
170 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
171
172 EXPECT(!store2->value()->BindsToConstant());
173
174 // StoreIndexed(tmp_array, 2, "b")
175 EXPECT(store3->index()->BindsToConstant());
176 EXPECT(store3->index()->BoundConstant().IsInteger());
177 EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2);
178
179 EXPECT(store3->value()->BindsToConstant());
180 EXPECT(store3->value()->BoundConstant().IsString());
181 EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("b"));
182}
183
184ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConcatStringLits) {
185 // We should drop empty strings from StringInterpolates:
186 const char* kScript = R"(
187 test(s) {
188 return '' 'a' '' 'b' '$s' '' 'c' '' 'd' '';
189 }
190 main() => test('u');
191 )";
192
193 const auto& root_library = Library::Handle(LoadTestScript(kScript));
194 const auto& function = Function::Handle(GetFunction(root_library, "test"));
195
196 Invoke(root_library, "main");
197
198 TestPipeline pipeline(function, CompilerPass::kJIT);
199 FlowGraph* flow_graph = pipeline.RunPasses({
200 CompilerPass::kComputeSSA,
201 });
202
203 auto entry = flow_graph->graph_entry()->normal_entry();
204 EXPECT(entry != nullptr);
205
206 StoreIndexedInstr* store1 = nullptr;
207 StoreIndexedInstr* store2 = nullptr;
208 StoreIndexedInstr* store3 = nullptr;
209
210 ILMatcher cursor(flow_graph, entry);
211 // clang-format off
212 RELEASE_ASSERT(cursor.TryMatch({
213 kMatchAndMoveFunctionEntry,
214 kMatchAndMoveCheckStackOverflow,
215 kMoveDebugStepChecks,
216 kMatchAndMoveCreateArray,
217 {kMatchAndMoveStoreIndexed, &store1},
218 {kMatchAndMoveStoreIndexed, &store2},
219 {kMatchAndMoveStoreIndexed, &store3},
220 kMatchAndMoveRecordCoverage,
221 kMatchAndMoveStaticCall,
223 kMatchDartReturn,
224 }));
225 // clang-format on
226
227 // StoreIndexed(tmp_array, 0, "ab")
228 EXPECT(store1->index()->BindsToConstant());
229 EXPECT(store1->index()->BoundConstant().IsInteger());
230 EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0);
231
232 EXPECT(store1->value()->BindsToConstant());
233 EXPECT(store1->value()->BoundConstant().IsString());
234 EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("ab"));
235
236 // StoreIndexed(tmp_array, 1, s)
237 EXPECT(store2->index()->BindsToConstant());
238 EXPECT(store2->index()->BoundConstant().IsInteger());
239 EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1);
240
241 EXPECT(!store2->value()->BindsToConstant());
242
243 // StoreIndexed(tmp_array, 2, "cd")
244 EXPECT(store3->index()->BindsToConstant());
245 EXPECT(store3->index()->BoundConstant().IsInteger());
246 EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2);
247
248 EXPECT(store3->value()->BindsToConstant());
249 EXPECT(store3->value()->BoundConstant().IsString());
250 EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("cd"));
251}
252
253ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_InvariantFlagInListLiterals) {
254 const char* kScript = R"(
255 test() {
256 return [...[], 42];
257 }
258 )";
259
260 const auto& root_library = Library::Handle(LoadTestScript(kScript));
261 const auto& function = Function::Handle(GetFunction(root_library, "test"));
262
263 Invoke(root_library, "test");
264
265 TestPipeline pipeline(function, CompilerPass::kJIT);
266 FlowGraph* flow_graph = pipeline.RunPasses({
267 CompilerPass::kComputeSSA,
268 });
269
270 auto entry = flow_graph->graph_entry()->normal_entry();
271 EXPECT(entry != nullptr);
272
273 InstanceCallInstr* call_add = nullptr;
274
275 ILMatcher cursor(flow_graph, entry);
276 // clang-format off
277 RELEASE_ASSERT(cursor.TryMatch({
278 kMatchAndMoveFunctionEntry,
279 kMatchAndMoveCheckStackOverflow,
280 kMoveDebugStepChecks,
281 kMatchAndMoveRecordCoverage,
282 kMatchAndMoveStaticCall,
283 kMatchAndMoveRecordCoverage,
284 kMatchAndMoveStaticCall,
285 kMatchAndMoveRecordCoverage,
286 {kMatchAndMoveInstanceCall, &call_add},
288 kMatchDartReturn,
289 }));
290 // clang-format on
291
292 EXPECT(call_add != nullptr);
293 EXPECT(call_add->function_name().Equals("add"));
294 EXPECT(call_add->entry_kind() == Code::EntryKind::kUnchecked);
295}
296
297ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_TypedClosureCall) {
298 // This test ensures that a typed closure call (i.e. the closure being called
299 // has a real function type as static type) ends up compiling to
300 //
301 // CheckNull()+ClosureCall()
302 //
303 // instead of
304 //
305 // InstanceCall(dyn:call)
306 //
307 // This is a regression test for a case where JIT support uses dynamic calls
308 // instead of typed closure calls if call-site attribute metadata is missing
309 // which is the case if an incremental kernel compiler is used. The
310 // [LoadTestScript] below uses IKG compiler, so this is a regression test for:
311 //
312 // - https://github.com/dart-lang/sdk/issues/46320
313 // - https://github.com/dart-lang/sdk/issues/45421
314 //
315 const char* kScript = R"(
316 int callClosure(int Function(int) fun, int value) => fun(value);
317 test() => callClosure((int a) => a + 1, 10);
318 )";
319
320 const auto& root_library = Library::Handle(LoadTestScript(kScript));
321 Invoke(root_library, "test");
322
323 const auto& callClosureFunction =
324 Function::Handle(GetFunction(root_library, "callClosure"));
325 TestPipeline pipeline(callClosureFunction, CompilerPass::kJIT);
326 FlowGraph* flow_graph = pipeline.RunPasses({
327 CompilerPass::kComputeSSA,
328 });
329
330 auto entry = flow_graph->graph_entry()->normal_entry();
331 EXPECT(entry != nullptr);
332
333 ILMatcher cursor(flow_graph, entry, true);
334 // clang-format off
335 std::initializer_list<MatchCode> expected = {
336 kMatchAndMoveFunctionEntry,
337 kMatchAndMoveCheckStackOverflow,
339 kMatchAndMoveCheckNull,
340 kMatchAndMoveLoadField,
342#if !defined(PRODUCT)
343 kMatchAndMoveRecordCoverage,
344#endif
345 kMatchAndMoveClosureCall,
347 kMatchDartReturn,
348 };
349 RELEASE_ASSERT(cursor.TryMatch(expected));
350 // clang-format on
351}
352
354 StreamingFlowGraphBuilder_StaticGetFinalFieldWithTrivialInitializer) {
355 const char* kScript = R"(
356 final int x = 0xFEEDFEED;
357 test() {
358 return x;
359 }
360 )";
361
362 const auto& root_library = Library::Handle(LoadTestScript(kScript));
363 const auto& function = Function::Handle(GetFunction(root_library, "test"));
364
365 Invoke(root_library, "test");
366
367 TestPipeline pipeline(function, CompilerPass::kJIT);
368 FlowGraph* flow_graph = pipeline.RunPasses({
369 CompilerPass::kComputeSSA,
370 });
371
372 auto entry = flow_graph->graph_entry()->normal_entry();
373 EXPECT(entry != nullptr);
374
375 DartReturnInstr* return_instr = nullptr;
376
377 ILMatcher cursor(flow_graph, entry);
378 RELEASE_ASSERT(cursor.TryMatch({
379 kMatchAndMoveFunctionEntry,
380 kMatchAndMoveCheckStackOverflow,
381 kMoveDebugStepChecks,
382 {kMatchDartReturn, &return_instr},
383 }));
384
385 EXPECT(return_instr != nullptr);
386 ConstantInstr* const_value =
387 return_instr->value()->definition()->AsConstant();
388 EXPECT(const_value != nullptr);
389 EXPECT(const_value->value().IsInteger());
390 EXPECT_EQ(0xFEEDFEED, Integer::Cast(const_value->value()).AsInt64Value());
391}
392
393} // 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:2001
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
SI void store2(uint16_t *ptr, U16 r, U16 g)
Definition: dart_vm.cc:33
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)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
#define ISOLATE_UNIT_TEST_CASE(name)
Definition: unit_test.h:64