Flutter Engine
The Flutter Engine
flow_graph_test.cc
Go to the documentation of this file.
1// Copyright (c) 2012, 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
7#include <vector>
8
10#include "platform/utils.h"
16#include "vm/unit_test.h"
17
18namespace dart {
19
20#if defined(TARGET_ARCH_IS_64_BIT)
21ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
22 using compiler::BlockBuilder;
23
24 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
25
26 FlowGraphBuilderHelper H(/*num_parameters=*/1);
27 H.AddVariable("v0", AbstractType::ZoneHandle(Type::IntType()),
28 new CompileType(CompileType::Int()));
29
30 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
31 auto loop_header = H.JoinEntry();
32 auto loop_body = H.TargetEntry();
33 auto loop_exit = H.TargetEntry();
34
35 Definition* v0;
36 PhiInstr* loop_var;
37 Definition* add1;
38
39 {
40 BlockBuilder builder(H.flow_graph(), normal_entry);
41 v0 = builder.AddParameter(0, kTagged);
42 builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
43 }
44
45 {
46 BlockBuilder builder(H.flow_graph(), loop_header);
47 loop_var = H.Phi(loop_header, {{normal_entry, v0}, {loop_body, &add1}});
48 builder.AddPhi(loop_var);
49 builder.AddBranch(new RelationalOpInstr(
50 InstructionSource(), Token::kLT, new Value(loop_var),
51 new Value(H.IntConstant(50)), kMintCid,
52 S.GetNextDeoptId(), Instruction::kNotSpeculative),
53 loop_body, loop_exit);
54 }
55
56 {
57 BlockBuilder builder(H.flow_graph(), loop_body);
58 add1 = builder.AddDefinition(new BinaryInt64OpInstr(
59 Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
60 S.GetNextDeoptId(), Instruction::kNotSpeculative));
61 builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
62 }
63
64 {
65 BlockBuilder builder(H.flow_graph(), loop_exit);
66 builder.AddReturn(new Value(loop_var));
67 }
68
69 H.FinishGraph();
70
71 FlowGraphTypePropagator::Propagate(H.flow_graph());
72 H.flow_graph()->SelectRepresentations();
73
74 EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
75}
76#endif // defined(TARGET_ARCH_IS_64_BIT)
77
78ISOLATE_UNIT_TEST_CASE(FlowGraph_LateVariablePhiUnboxing) {
80
81 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
83
84 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
85 auto loop_header = H.JoinEntry();
86 auto loop_body = H.TargetEntry();
87 auto loop_exit = H.TargetEntry();
88
89 ConstantInstr* sentinel = H.flow_graph()->GetConstant(Object::sentinel());
90
91 PhiInstr* loop_var;
92 PhiInstr* late_var;
93 Definition* add1;
94
95 {
96 BlockBuilder builder(H.flow_graph(), normal_entry);
97 builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
98 }
99
100 {
101 BlockBuilder builder(H.flow_graph(), loop_header);
102 loop_var = H.Phi(loop_header,
103 {{normal_entry, H.IntConstant(0)}, {loop_body, &add1}});
104 builder.AddPhi(loop_var);
105 loop_var->UpdateType(CompileType::Int());
109 late_var =
110 H.Phi(loop_header, {{normal_entry, sentinel}, {loop_body, &add1}});
111 builder.AddPhi(late_var);
112 builder.AddBranch(new RelationalOpInstr(
113 InstructionSource(), Token::kLT, new Value(loop_var),
114 new Value(H.IntConstant(10)), kMintCid,
115 S.GetNextDeoptId(), Instruction::kNotSpeculative),
116 loop_body, loop_exit);
117 }
118
119 {
120 BlockBuilder builder(H.flow_graph(), loop_body);
121 add1 = builder.AddDefinition(new BinaryInt64OpInstr(
122 Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
123 S.GetNextDeoptId(), Instruction::kNotSpeculative));
124 builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
125 }
126
127 {
128 BlockBuilder builder(H.flow_graph(), loop_exit);
129 builder.AddReturn(new Value(late_var));
130 }
131
132 H.FinishGraph();
133
134 FlowGraphTypePropagator::Propagate(H.flow_graph());
135 H.flow_graph()->SelectRepresentations();
136
137#if defined(TARGET_ARCH_IS_64_BIT)
138 EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
139#endif
140 EXPECT_PROPERTY(late_var, it.representation() == kTagged);
141}
142
143ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxedFloatPhi) {
145
146 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
148
149 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
150 auto then_body = H.TargetEntry();
151 auto else_body = H.TargetEntry();
152 auto join_exit = H.JoinEntry();
153
154 PhiInstr* phi;
155 Definition* double_to_float_1;
156 Definition* double_to_float_2;
157
158 {
159 BlockBuilder builder(H.flow_graph(), normal_entry);
160 builder.AddBranch(
162 InstructionSource(), Token::kEQ_STRICT, new Value(H.IntConstant(1)),
163 new Value(H.IntConstant(1)),
164 /*needs_number_check=*/false, S.GetNextDeoptId()),
165 then_body, else_body);
166 }
167
168 {
169 BlockBuilder builder(H.flow_graph(), then_body);
170 double_to_float_1 = builder.AddDefinition(new DoubleToFloatInstr(
171 new Value(H.DoubleConstant(1)), S.GetNextDeoptId(),
173 builder.AddInstruction(new GotoInstr(join_exit, S.GetNextDeoptId()));
174 }
175
176 {
177 BlockBuilder builder(H.flow_graph(), else_body);
178 double_to_float_2 = builder.AddDefinition(new DoubleToFloatInstr(
179 new Value(H.DoubleConstant(2)), S.GetNextDeoptId(),
181 builder.AddInstruction(new GotoInstr(join_exit, S.GetNextDeoptId()));
182 }
183
184 {
185 BlockBuilder builder(H.flow_graph(), join_exit);
186 phi = new PhiInstr(join_exit, 3);
187 phi->SetInputAt(0, new Value(double_to_float_1));
188 phi->SetInputAt(1, new Value(double_to_float_2));
189 phi->SetInputAt(2, new Value(phi));
190 builder.AddPhi(phi);
191 builder.AddReturn(new Value(phi));
192 }
193 H.FinishGraph();
194
195 FlowGraphTypePropagator::Propagate(H.flow_graph());
196 H.flow_graph()->SelectRepresentations();
197
198 EXPECT_PROPERTY(phi, it.representation() == kUnboxedFloat);
199}
200
201void TestLargeFrame(const char* type,
202 const char* zero,
203 const char* one,
204 const char* main) {
205 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 1000);
206 TextBuffer printer(256 * KB);
207
208 intptr_t num_locals = 2000;
209 printer.Printf("import 'dart:typed_data';\n");
210 printer.Printf("@pragma('vm:never-inline')\n");
211 printer.Printf("%s one() { return %s; }\n", type, one);
212 printer.Printf("@pragma('vm:never-inline')\n");
213 printer.Printf("%s largeFrame(int n) {\n", type);
214 for (intptr_t i = 0; i < num_locals; i++) {
215 printer.Printf(" %s local%" Pd " = %s;\n", type, i, zero);
216 }
217 printer.Printf(" for (int i = 0; i < n; i++) {\n");
218 for (intptr_t i = 0; i < num_locals; i++) {
219 printer.Printf(" local%" Pd " += one();\n", i);
220 }
221 printer.Printf(" }\n");
222 printer.Printf(" %s sum = %s;\n", type, zero);
223 for (intptr_t i = 0; i < num_locals; i++) {
224 printer.Printf(" sum += local%" Pd ";\n", i);
225 }
226 printer.Printf(" return sum;\n");
227 printer.Printf("}\n");
228
229 printer.AddString(main);
230
231 const auto& root_library = Library::Handle(LoadTestScript(printer.buffer()));
232 Invoke(root_library, "main");
233}
234
235ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Int) {
236 TestLargeFrame("int", "0", "1",
237 "main() {\n"
238 " for (var i = 0; i < 100; i++) {\n"
239 " var r = largeFrame(1);\n"
240 " if (r != 2000) throw r;\n"
241 " }\n"
242 " return 'Okay';\n"
243 "}\n");
244}
245
246ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Double) {
247 TestLargeFrame("double", "0.0", "1.0",
248 "main() {\n"
249 " for (var i = 0; i < 100; i++) {\n"
250 " var r = largeFrame(1);\n"
251 " if (r != 2000.0) throw r;\n"
252 " }\n"
253 " return 'Okay';\n"
254 "}\n");
255}
256
257ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Int32x4) {
258 TestLargeFrame("Int32x4", "Int32x4(0, 0, 0, 0)", "Int32x4(1, 2, 3, 4)",
259 "main() {\n"
260 " for (var i = 0; i < 100; i++) {\n"
261 " var r = largeFrame(1);\n"
262 " if (r.x != 2000) throw r;\n"
263 " if (r.y != 4000) throw r;\n"
264 " if (r.z != 6000) throw r;\n"
265 " if (r.w != 8000) throw r;\n"
266 " }\n"
267 " return 'Okay';\n"
268 "}\n");
269}
270
271ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Float32x4) {
272 TestLargeFrame("Float32x4", "Float32x4(0.0, 0.0, 0.0, 0.0)",
273 "Float32x4(1.0, 2.0, 3.0, 4.0)",
274 "main() {\n"
275 " for (var i = 0; i < 100; i++) {\n"
276 " var r = largeFrame(1);\n"
277 " if (r.x != 2000.0) throw r;\n"
278 " if (r.y != 4000.0) throw r;\n"
279 " if (r.z != 6000.0) throw r;\n"
280 " if (r.w != 8000.0) throw r;\n"
281 " }\n"
282 " return 'Okay';\n"
283 "}\n");
284}
285
286ISOLATE_UNIT_TEST_CASE(FlowGraph_LargeFrame_Float64x2) {
287 TestLargeFrame("Float64x2", "Float64x2(0.0, 0.0)", "Float64x2(1.0, 2.0)",
288 "main() {\n"
289 " for (var i = 0; i < 100; i++) {\n"
290 " var r = largeFrame(1);\n"
291 " if (r.x != 2000.0) throw r;\n"
292 " if (r.y != 4000.0) throw r;\n"
293 " }\n"
294 " return 'Okay';\n"
295 "}\n");
296}
297
298ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Double) {
299 const char* kScript = R"(
300 double foo(double sum, int n) {
301 if (sum == null) return 0.0;
302 for (int i = 0; i < n; i++) {
303 sum += 1.0;
304 }
305 return sum;
306 }
307 main() {
308 foo(0.0, 10);
309 }
310 )";
311
312 const auto& root_library = Library::Handle(LoadTestScript(kScript));
313 const auto& function = Function::Handle(GetFunction(root_library, "foo"));
314
315 Invoke(root_library, "main");
316
318 FlowGraph* flow_graph = pipeline.RunPasses({});
319
320 auto entry = flow_graph->graph_entry()->normal_entry();
321 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
323
324 RELEASE_ASSERT(cursor.TryMatch({
325 kMatchAndMoveFunctionEntry,
326 }));
327 RELEASE_ASSERT(cursor.TryMatch({
328 kMatchAndMoveUnbox, // outside of loop
329 kMatchAndMoveCheckSmi,
330 kMoveGlob,
331
332 // Loop header
333 kMatchAndMoveJoinEntry,
334 kMatchAndMoveCheckStackOverflow,
335 kMatchAndMoveBranchTrue,
336
337 // Loop body
338 kMatchAndMoveTargetEntry,
339 kMatchAndMoveBinaryDoubleOp,
340 kMatchAndMoveBinarySmiOp,
341 kMatchAndMoveGoto,
342
343 // Loop header, again
344 kMatchAndMoveJoinEntry,
345 kMatchAndMoveCheckStackOverflow,
346 kMatchAndMoveBranchFalse,
347
348 // After loop
349 kMatchAndMoveTargetEntry,
350 kMatchAndMoveBox,
351 kMatchDartReturn,
352 }));
353}
354
355static void TestPhiUnboxingHeuristicSimd(const char* script) {
357 return;
358 }
359
360 const auto& root_library = Library::Handle(LoadTestScript(script));
361 const auto& function = Function::Handle(GetFunction(root_library, "foo"));
362
363 Invoke(root_library, "main");
364
366 FlowGraph* flow_graph = pipeline.RunPasses({});
367
368 auto entry = flow_graph->graph_entry()->normal_entry();
369 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
371
372 RELEASE_ASSERT(cursor.TryMatch({
373 kMatchAndMoveFunctionEntry,
374 }));
375 RELEASE_ASSERT(cursor.TryMatch({
376 kMatchAndMoveUnbox, // outside of loop
377 kMatchAndMoveCheckSmi,
378 kMoveGlob,
379
380 // Loop header
381 kMatchAndMoveJoinEntry,
382 kMatchAndMoveCheckStackOverflow,
383 kMatchAndMoveBranchTrue,
384
385 // Loop body
386 kMatchAndMoveTargetEntry,
387 kMatchAndMoveSimdOp,
388 kMatchAndMoveBinarySmiOp,
389 kMatchAndMoveGoto,
390
391 // Loop header, again
392 kMatchAndMoveJoinEntry,
393 kMatchAndMoveCheckStackOverflow,
394 kMatchAndMoveBranchFalse,
395
396 // After loop
397 kMatchAndMoveTargetEntry,
398 kMatchAndMoveBox,
399 kMatchDartReturn,
400 }));
401}
402
403ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Float32x4) {
404 const char* kScript = R"(
405 import 'dart:typed_data';
406 Float32x4 foo(Float32x4 sum, int n) {
407 if (sum == null) return Float32x4(0.0, 0.0, 0.0, 0.0);
408 for (int i = 0; i < n; i++) {
409 sum += Float32x4(1.0, 2.0, 3.0, 4.0);
410 }
411 return sum;
412 }
413 main() {
414 foo(Float32x4(0.0, 0.0, 0.0, 0.0), 10);
415 }
416 )";
418}
419
420ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Float64x2) {
421 const char* kScript = R"(
422 import 'dart:typed_data';
423 Float64x2 foo(Float64x2 sum, int n) {
424 if (sum == null) return Float64x2(0.0, 0.0);
425 for (int i = 0; i < n; i++) {
426 sum += Float64x2(1.0, 2.0);
427 }
428 return sum;
429 }
430 main() {
431 foo(Float64x2(0.0, 0.0), 10);
432 }
433 )";
435}
436
437ISOLATE_UNIT_TEST_CASE(FlowGraph_PhiUnboxingHeuristic_Int32x4) {
438 const char* kScript = R"(
439 import 'dart:typed_data';
440 Int32x4 foo(Int32x4 sum, int n) {
441 if (sum == null) return Int32x4(0, 0, 0, 0);
442 for (int i = 0; i < n; i++) {
443 sum += Int32x4(1, 2, 3, 4);
444 }
445 return sum;
446 }
447 main() {
448 foo(Int32x4(0, 0, 0, 0), 10);
449 }
450 )";
452}
453
454} // namespace dart
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
GLenum type
void AddString(const char *s)
Definition: text_buffer.cc:263
intptr_t Printf(const char *format,...) PRINTF_ATTRIBUTE(2
Definition: text_buffer.cc:14
char * buffer() const
Definition: text_buffer.h:35
static constexpr bool kCanBeSentinel
Definition: compile_type.h:48
static constexpr bool kCannotBeNull
Definition: compile_type.h:46
static CompileType Int()
static CompileType FromAbstractType(const AbstractType &type, bool can_be_null, bool can_be_sentinel)
PRINT_OPERANDS_TO_SUPPORT PRINT_TO_SUPPORT bool UpdateType(CompileType new_type)
Definition: il.h:2553
static bool SupportsUnboxedSimd128()
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)
@ kNotSpeculative
Definition: il.h:975
void SetInputAt(intptr_t i, Value *value)
Definition: il.h:1014
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
virtual Representation representation() const
Definition: il.h:2835
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
static TypePtr IntType()
Definition: il.h:75
#define H
Dart_NativeFunction function
Definition: fuchsia.cc:51
#define EXPECT_PROPERTY(entity, property)
Definition: dart_vm.cc:33
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
void TestLargeFrame(const char *type, const char *zero, const char *one, const char *main)
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
constexpr intptr_t KB
Definition: globals.h:528
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
static void TestPhiUnboxingHeuristicSimd(const char *script)
Definition: main.py:1
#define Pd
Definition: globals.h:408
Definition: SkMD5.cpp:130