Flutter Engine
The Flutter Engine
type_propagator_test.cc
Go to the documentation of this file.
1// Copyright (c) 2019, 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 <utility>
6
18#include "vm/log.h"
19#include "vm/object.h"
20#include "vm/parser.h"
21#include "vm/symbols.h"
22#include "vm/unit_test.h"
23
24namespace dart {
25
26using compiler::BlockBuilder;
27
28ISOLATE_UNIT_TEST_CASE(TypePropagator_RedefinitionAfterStrictCompareWithNull) {
29 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
30
31 FlowGraphBuilderHelper H(/*num_parameters=*/1);
32 H.AddVariable("v0", AbstractType::ZoneHandle(Type::IntType()));
33
34 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
35
36 // We are going to build the following graph:
37 //
38 // B0[graph_entry]:
39 // B1[function_entry]:
40 // v0 <- Parameter(0)
41 // if v0 == null then B2 else B3
42 // B2:
43 // Return(v0)
44 // B3:
45 // Return(v0)
46
47 Definition* v0;
48 auto b2 = H.TargetEntry();
49 auto b3 = H.TargetEntry();
50
51 {
52 BlockBuilder builder(H.flow_graph(), normal_entry);
53 v0 = builder.AddParameter(0, kTagged);
54 builder.AddBranch(
56 InstructionSource(), Token::kEQ_STRICT, new Value(v0),
57 new Value(H.flow_graph()->GetConstant(Object::Handle())),
58 /*needs_number_check=*/false, S.GetNextDeoptId()),
59 b2, b3);
60 }
61
62 {
63 BlockBuilder builder(H.flow_graph(), b2);
64 builder.AddReturn(new Value(v0));
65 }
66
67 {
68 BlockBuilder builder(H.flow_graph(), b3);
69 builder.AddReturn(new Value(v0));
70 }
71
72 H.FinishGraph();
73
74 FlowGraphTypePropagator::Propagate(H.flow_graph());
75
76 // We expect that v0 is inferred to be nullable int because that is what
77 // static type of an associated variable tells us.
78 EXPECT(v0->Type()->IsNullableInt());
79
80 // In B2 v0 should not have any additional type information so reaching
81 // type should be still nullable int.
82 auto b2_value = b2->last_instruction()->AsDartReturn()->value();
83 EXPECT(b2_value->Type()->IsNullableInt());
84
85 // In B3 v0 is constrained by comparison with null - it should be non-nullable
86 // integer. There should be a Redefinition inserted to prevent LICM past
87 // the branch.
88 auto b3_value = b3->last_instruction()->AsDartReturn()->value();
89 EXPECT(b3_value->Type()->IsInt());
90 EXPECT(b3_value->definition()->IsRedefinition());
91 EXPECT(b3_value->definition()->GetBlock() == b3);
92}
93
95 TypePropagator_RedefinitionAfterStrictCompareWithLoadClassId) {
96 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
97
98 FlowGraphBuilderHelper H(/*num_parameters=*/1);
99 H.AddVariable("v0", AbstractType::ZoneHandle(Type::DynamicType()));
100
101 // We are going to build the following graph:
102 //
103 // B0[graph_entry]:
104 // B1[function_entry]:
105 // v0 <- Parameter(0)
106 // v1 <- LoadClassId(v0)
107 // if v1 == kDoubleCid then B2 else B3
108 // B2:
109 // Return(v0)
110 // B3:
111 // Return(v0)
112
113 Definition* v0;
114 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
115 auto b2 = H.TargetEntry();
116 auto b3 = H.TargetEntry();
117
118 {
119 BlockBuilder builder(H.flow_graph(), b1);
120 v0 = builder.AddParameter(0, kTagged);
121 auto load_cid = builder.AddDefinition(new LoadClassIdInstr(new Value(v0)));
122 builder.AddBranch(
124 InstructionSource(), Token::kEQ_STRICT, new Value(load_cid),
125 new Value(H.IntConstant(kDoubleCid)),
126 /*needs_number_check=*/false, S.GetNextDeoptId()),
127 b2, b3);
128 }
129
130 {
131 BlockBuilder builder(H.flow_graph(), b2);
132 builder.AddReturn(new Value(v0));
133 }
134
135 {
136 BlockBuilder builder(H.flow_graph(), b3);
137 builder.AddReturn(new Value(v0));
138 }
139
140 H.FinishGraph();
141
142 FlowGraphTypePropagator::Propagate(H.flow_graph());
143
144 // There should be no information available about the incoming type of
145 // the parameter either on entry or in B3.
147 auto b3_value = b3->last_instruction()->AsDartReturn()->value();
148 EXPECT(b3_value->Type() == v0->Type());
149
150 // In B3 v0 is constrained by comparison of its cid with kDoubleCid - it
151 // should be non-nullable double. There should be a Redefinition inserted to
152 // prevent LICM past the branch.
153 auto b2_value = b2->last_instruction()->AsDartReturn()->value();
154 EXPECT_PROPERTY(b2_value->Type(), it.IsDouble());
155 EXPECT_PROPERTY(b2_value->definition(), it.IsRedefinition());
156 EXPECT_PROPERTY(b2_value->definition()->GetBlock(), &it == b2);
157}
158
159ISOLATE_UNIT_TEST_CASE(TypePropagator_Refinement) {
160 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
161
162 const Class& object_class =
163 Class::Handle(thread->isolate_group()->object_store()->object_class());
164
166 const Function& target_func = Function::ZoneHandle(Function::New(
167 signature, String::Handle(Symbols::New(thread, "dummy2")),
168 UntaggedFunction::kRegularFunction,
169 /*is_static=*/true,
170 /*is_const=*/false,
171 /*is_abstract=*/false,
172 /*is_external=*/false,
173 /*is_native=*/true, object_class, TokenPosition::kNoSource));
175
176 const Field& field = Field::ZoneHandle(
177 Field::New(String::Handle(Symbols::New(thread, "dummy")),
178 /*is_static=*/true,
179 /*is_final=*/false,
180 /*is_const=*/false,
181 /*is_reflectable=*/true,
182 /*is_late=*/false, object_class, Object::dynamic_type(),
183 TokenPosition::kNoSource, TokenPosition::kNoSource));
184 {
185 SafepointWriteRwLocker locker(thread,
186 thread->isolate_group()->program_lock());
187 thread->isolate_group()->RegisterStaticField(field, Object::Handle());
188 }
189
190 FlowGraphBuilderHelper H(/*num_parameters=*/1);
191 H.AddVariable("v0", AbstractType::ZoneHandle(Type::DynamicType()));
192
193 // We are going to build the following graph:
194 //
195 // B0[graph_entry]
196 // B1[function_entry]:
197 // v0 <- Parameter(0)
198 // v1 <- Constant(0)
199 // if v0 == 1 then B3 else B2
200 // B2:
201 // v2 <- StaticCall(target_func)
202 // goto B4
203 // B3:
204 // goto B4
205 // B4:
206 // v3 <- phi(v1, v2)
207 // return v5
208
209 Definition* v0;
210 Definition* v2;
211 PhiInstr* v3;
212 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
213 auto b2 = H.TargetEntry();
214 auto b3 = H.TargetEntry();
215 auto b4 = H.JoinEntry();
216
217 {
218 BlockBuilder builder(H.flow_graph(), b1);
219 v0 = builder.AddParameter(0, kTagged);
220 builder.AddBranch(new StrictCompareInstr(
221 InstructionSource(), Token::kEQ_STRICT, new Value(v0),
222 new Value(H.IntConstant(1)),
223 /*needs_number_check=*/false, S.GetNextDeoptId()),
224 b2, b3);
225 }
226
227 {
228 BlockBuilder builder(H.flow_graph(), b2);
229 v2 = builder.AddDefinition(
230 new StaticCallInstr(InstructionSource(), target_func,
231 /*type_args_len=*/0,
232 /*argument_names=*/Array::empty_array(),
233 InputsArray(0), S.GetNextDeoptId(),
234 /*call_count=*/0, ICData::RebindRule::kStatic));
235 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
236 }
237
238 {
239 BlockBuilder builder(H.flow_graph(), b3);
240 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
241 }
242
243 {
244 BlockBuilder builder(H.flow_graph(), b4);
245 v3 = H.Phi(b4, {{b2, v2}, {b3, H.IntConstant(0)}});
246 builder.AddPhi(v3);
247 builder.AddReturn(new Value(v3));
248 }
249
250 H.FinishGraph();
251 FlowGraphTypePropagator::Propagate(H.flow_graph());
252
253 EXPECT_PROPERTY(v2->Type(), it.IsNullableInt());
255
256 auto v4 = new LoadStaticFieldInstr(field, InstructionSource());
257 H.flow_graph()->InsertBefore(v2, v4, nullptr, FlowGraph::kValue);
258 v2->ReplaceUsesWith(v4);
259 v2->RemoveFromGraph();
260
261 FlowGraphTypePropagator::Propagate(H.flow_graph());
262
264}
265
266// This test verifies that mutable compile types are not incorrectly cached
267// as reaching types after inference.
268ISOLATE_UNIT_TEST_CASE(TypePropagator_Regress36156) {
269 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
270 FlowGraphBuilderHelper H(/*num_parameters=*/1);
271 H.AddVariable("v0", AbstractType::ZoneHandle(Type::DynamicType()));
272
273 // We are going to build the following graph:
274 //
275 // B0[graph_entry]
276 // B1[function_entry]:
277 // v0 <- Parameter(0)
278 // v1 <- Constant(42)
279 // v2 <- Constant(24)
280 // v4 <- Constant(1.0)
281 // if v0 == 1 then B6 else B2
282 // B2:
283 // if v0 == 2 then B3 else B4
284 // B3:
285 // goto B5
286 // B4:
287 // goto B5
288 // B5:
289 // v3 <- phi(v1, v2)
290 // goto B7
291 // B6:
292 // goto B7
293 // B7:
294 // v5 <- phi(v4, v3)
295 // return v5
296
297 Definition* v0;
298 PhiInstr* v3;
299 PhiInstr* v5;
300 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
301 auto b2 = H.TargetEntry();
302 auto b3 = H.TargetEntry();
303 auto b4 = H.TargetEntry();
304 auto b5 = H.JoinEntry();
305 auto b6 = H.TargetEntry();
306 auto b7 = H.JoinEntry();
307
308 {
309 BlockBuilder builder(H.flow_graph(), b1);
310 v0 = builder.AddParameter(0, kTagged);
311 builder.AddBranch(new StrictCompareInstr(
312 InstructionSource(), Token::kEQ_STRICT, new Value(v0),
313 new Value(H.IntConstant(1)),
314 /*needs_number_check=*/false, S.GetNextDeoptId()),
315 b6, b2);
316 }
317
318 {
319 BlockBuilder builder(H.flow_graph(), b2);
320 builder.AddBranch(new StrictCompareInstr(
321 InstructionSource(), Token::kEQ_STRICT, new Value(v0),
322 new Value(H.IntConstant(2)),
323 /*needs_number_check=*/false, S.GetNextDeoptId()),
324 b3, b4);
325 }
326
327 {
328 BlockBuilder builder(H.flow_graph(), b3);
329 builder.AddInstruction(new GotoInstr(b5, S.GetNextDeoptId()));
330 }
331
332 {
333 BlockBuilder builder(H.flow_graph(), b4);
334 builder.AddInstruction(new GotoInstr(b5, S.GetNextDeoptId()));
335 }
336
337 {
338 BlockBuilder builder(H.flow_graph(), b5);
339 v3 = H.Phi(b5, {{b3, H.IntConstant(42)}, {b4, H.IntConstant(24)}});
340 builder.AddPhi(v3);
341 builder.AddInstruction(new GotoInstr(b7, S.GetNextDeoptId()));
342 }
343
344 {
345 BlockBuilder builder(H.flow_graph(), b6);
346 builder.AddInstruction(new GotoInstr(b7, S.GetNextDeoptId()));
347 }
348
349 {
350 BlockBuilder builder(H.flow_graph(), b7);
351 v5 = H.Phi(b7, {{b5, v3}, {b6, H.DoubleConstant(1.0)}});
352 builder.AddPhi(v5);
353 builder.AddInstruction(new DartReturnInstr(
354 InstructionSource(), new Value(v5), S.GetNextDeoptId()));
355 }
356
357 H.FinishGraph();
358
359 FlowGraphTypePropagator::Propagate(H.flow_graph());
360
361 // We expect that v3 has an integer type, and v5 is either T{Object} or
362 // T{num}.
363 EXPECT_PROPERTY(v3->Type(), it.IsInt());
365 it.IsObjectType() || it.IsNumberType());
366
367 // Now unbox v3 phi by inserting unboxing for both inputs and boxing
368 // for the result.
369 {
370 v3->set_representation(kUnboxedInt64);
371 for (intptr_t i = 0; i < v3->InputCount(); i++) {
372 auto input = v3->InputAt(i);
373 auto unbox =
374 new UnboxInt64Instr(input->CopyWithType(), S.GetNextDeoptId(),
376 H.flow_graph()->InsertBefore(
377 v3->block()->PredecessorAt(i)->last_instruction(), unbox, nullptr,
379 input->BindTo(unbox);
380 }
381
382 auto box = new BoxInt64Instr(new Value(v3));
383 v3->ReplaceUsesWith(box);
384 H.flow_graph()->InsertBefore(b4->last_instruction(), box, nullptr,
386 }
387
388 // Run type propagation again.
389 FlowGraphTypePropagator::Propagate(H.flow_graph());
390
391 // If CompileType of v3 would be cached as a reaching type at its use in
392 // v5 then we will be incorrect type propagation results.
393 // We expect that v3 has an integer type, and v5 is either T{Object} or
394 // T{num}.
395 EXPECT_PROPERTY(v3->Type(), it.IsInt());
397 it.IsObjectType() || it.IsNumberType());
398}
399
400ISOLATE_UNIT_TEST_CASE(CompileType_CanBeSmi) {
401 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
402
403 const char* script_chars = R"(
404import 'dart:async';
405
406class G<T> {}
407
408class C<NoBound,
409 NumBound extends num,
410 ComparableBound extends Comparable,
411 StringBound extends String> {
412 // Simple instantiated types.
413 @pragma('vm-test:can-be-smi') late int t1;
414 @pragma('vm-test:can-be-smi') late num t2;
415 @pragma('vm-test:can-be-smi') late Object t3;
416 late String t4;
417
418 // Type parameters.
419 @pragma('vm-test:can-be-smi') late NoBound tp1;
420 @pragma('vm-test:can-be-smi') late NumBound tp2;
421 @pragma('vm-test:can-be-smi') late ComparableBound tp3;
422 late StringBound tp4;
423
424 // Comparable<T> instantiations.
425 @pragma('vm-test:can-be-smi') late Comparable c1;
426 late Comparable<String> c2;
427 @pragma('vm-test:can-be-smi') late Comparable<num> c3;
428 late Comparable<int> c4; // int is not a subtype of Comparable<int>.
429 @pragma('vm-test:can-be-smi') late Comparable<NoBound> c5;
430 @pragma('vm-test:can-be-smi') late Comparable<NumBound> c6;
431 @pragma('vm-test:can-be-smi') late Comparable<ComparableBound> c7;
432 late Comparable<StringBound> c8;
433
434 // FutureOr<T> instantiations.
435 @pragma('vm-test:can-be-smi') late FutureOr fo1;
436 late FutureOr<String> fo2;
437 @pragma('vm-test:can-be-smi') late FutureOr<num> fo3;
438 @pragma('vm-test:can-be-smi') late FutureOr<int> fo4;
439 @pragma('vm-test:can-be-smi') late FutureOr<NoBound> fo5;
440 @pragma('vm-test:can-be-smi') late FutureOr<NumBound> fo6;
441 @pragma('vm-test:can-be-smi') late FutureOr<ComparableBound> fo7;
442 late FutureOr<StringBound> fo8;
443
444 // Other generic classes.
445 late G<int> g1;
446 late G<NoBound> g2;
447}
448)";
449
450 const auto& lib = Library::Handle(LoadTestScript(script_chars));
451
452 const auto& pragma_can_be_smi =
453 String::Handle(Symbols::New(thread, "vm-test:can-be-smi"));
454 auto expected_can_be_smi = [&](const Field& f) {
455 auto& options = Object::Handle();
456 return lib.FindPragma(thread, /*only_core=*/false, f, pragma_can_be_smi,
457 /*multiple=*/false, &options);
458 };
459
460 const auto& cls = Class::Handle(GetClass(lib, "C"));
461 const auto& err = Error::Handle(cls.EnsureIsFinalized(thread));
462 EXPECT(err.IsNull());
463 const auto& fields = Array::Handle(cls.fields());
464
465 auto& field = Field::Handle();
466 auto& type = AbstractType::Handle();
467 for (intptr_t i = 0; i < fields.Length(); i++) {
468 field ^= fields.At(i);
469 type = field.type();
470
471 auto compile_type = CompileType::FromAbstractType(
473 if (compile_type.CanBeSmi() != expected_can_be_smi(field)) {
474 dart::Expect(__FILE__, __LINE__)
475 .Fail("expected that CanBeSmi() returns %s for compile type %s\n",
476 expected_can_be_smi(field) ? "true" : "false",
477 compile_type.ToCString());
478 }
479 }
480}
481
482// Verifies that Propagate does not crash when running in AOT mode on a graph
483// which contains both AssertAssignable and a CheckClass/Smi after
484// EliminateEnvironments was called.
485ISOLATE_UNIT_TEST_CASE(TypePropagator_RegressFlutter76919) {
486 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
487
488 FlowGraphBuilderHelper H(/*num_parameters=*/1);
489 H.AddVariable("v0", AbstractType::ZoneHandle(Type::DynamicType()));
490
491 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
492
493 // We are going to build the following graph:
494 //
495 // B0[graph_entry]:
496 // B1[function_entry]:
497 // v0 <- Parameter(0)
498 // AssertAssignable(v0, 'int')
499 // CheckSmi(v0)
500 // Return(v0)
501
502 {
503 BlockBuilder builder(H.flow_graph(), normal_entry);
504 Definition* v0 = builder.AddParameter(0, kTagged);
505 auto null_value = builder.AddNullDefinition();
506 builder.AddDefinition(new AssertAssignableInstr(
507 InstructionSource(), new Value(v0),
508 new Value(
509 H.flow_graph()->GetConstant(Type::ZoneHandle(Type::IntType()))),
510 new Value(null_value), new Value(null_value), Symbols::Value(),
511 S.GetNextDeoptId()));
512 builder.AddInstruction(new CheckSmiInstr(new Value(v0), S.GetNextDeoptId(),
514 builder.AddReturn(new Value(v0));
515 }
516
517 H.FinishGraph();
518
519 H.flow_graph()->EliminateEnvironments();
520 FlowGraphTypePropagator::Propagate(H.flow_graph()); // Should not crash.
521}
522
523#if defined(DART_PRECOMPILER)
524
525// This test verifies that LoadStaticField for non-nullable field
526// is non-nullable with sound null safety.
527// Regression test for https://github.com/dart-lang/sdk/issues/47119.
528ISOLATE_UNIT_TEST_CASE(TypePropagator_NonNullableLoadStaticField) {
529 const char* kScript = R"(
530 const y = 0xDEADBEEF;
531 final int x = int.parse('0xFEEDFEED');
532
533 void main(List<String> args) {
534 print(x);
535 print(x + y);
536 }
537 )";
538
539 const auto& root_library = Library::Handle(LoadTestScript(kScript));
540 const auto& function = Function::Handle(GetFunction(root_library, "main"));
541
542 TestPipeline pipeline(function, CompilerPass::kAOT);
543 FlowGraph* flow_graph = pipeline.RunPasses({});
544
545 auto entry = flow_graph->graph_entry()->normal_entry();
546 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
548
549 Instruction* load = nullptr;
550
551 RELEASE_ASSERT(cursor.TryMatch({
552 kMoveGlob,
553 {kMatchAndMoveLoadStaticField, &load},
554 kMatchAndMoveMoveArgument,
555 kMatchAndMoveStaticCall,
556 kMatchAndMoveUnboxInt64,
557 kMatchAndMoveBinaryInt64Op,
558 kMatchAndMoveBoxInt64,
559 kMatchAndMoveMoveArgument,
560 kMatchAndMoveStaticCall,
561 kMatchDartReturn,
562 }));
563
564 EXPECT_PROPERTY(load->AsLoadStaticField()->Type(), !it.is_nullable());
565}
566
567ISOLATE_UNIT_TEST_CASE(TypePropagator_RedefineCanBeSentinelWithCannotBe) {
568 const char* kScript = R"(
569 late final int x;
570 )";
571 Zone* const Z = Thread::Current()->zone();
572 const auto& root_library = Library::CheckedHandle(Z, LoadTestScript(kScript));
573 const auto& toplevel = Class::Handle(Z, root_library.toplevel_class());
574 const auto& field_x = Field::Handle(
575 Z, toplevel.LookupStaticField(String::Handle(Z, String::New("x"))));
576
577 using compiler::BlockBuilder;
578 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
579 FlowGraphBuilderHelper H;
580
581 // We are going to build the following graph:
582 //
583 // B0[graph]:0 {
584 // v2 <- Constant(#3)
585 // }
586 // B1[function entry]:2
587 // v3 <- LoadStaticField:10(x, ThrowIfSentinel)
588 // v5 <- Constant(#sentinel)
589 // Branch if StrictCompare:12(===, v3, v5) goto (2, 3)
590 // B2[target]:4
591 // goto:16 B4
592 // B3[target]:6
593 // v7 <- Redefinition(v3 ^ T{int?})
594 // goto:18 B4
595 // B4[join]:8 pred(B2, B3) {
596 // v9 <- phi(v2, v7) alive
597 // }
598 // Return:20(v9)
599
600 Definition* v2 = H.IntConstant(3);
601 Definition* v3;
602 Definition* v7;
603 PhiInstr* v9;
604 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
605 auto b2 = H.TargetEntry();
606 auto b3 = H.TargetEntry();
607 auto b4 = H.JoinEntry();
608
609 {
610 BlockBuilder builder(H.flow_graph(), b1);
611 v3 = builder.AddDefinition(new LoadStaticFieldInstr(
612 field_x, {},
613 /*calls_initializer=*/false, S.GetNextDeoptId()));
614 auto v5 = builder.AddDefinition(new ConstantInstr(Object::sentinel()));
615 builder.AddBranch(new StrictCompareInstr(
616 {}, Token::kEQ_STRICT, new Value(v3), new Value(v5),
617 /*needs_number_check=*/false, S.GetNextDeoptId()),
618 b2, b3);
619 }
620
621 {
622 BlockBuilder builder(H.flow_graph(), b2);
623 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
624 }
625
626 {
627 BlockBuilder builder(H.flow_graph(), b3);
628 v7 = builder.AddDefinition(new RedefinitionInstr(new Value(v3)));
629 CompileType int_type =
630 CompileType::FromAbstractType(Type::Handle(Type::IntType()),
631 /*can_be_null=*/true,
632 /*can_be_sentinel=*/false);
633 v7->AsRedefinition()->set_constrained_type(new CompileType(int_type));
634 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
635 }
636
637 {
638 BlockBuilder builder(H.flow_graph(), b4);
639 v9 = H.Phi(b4, {{b2, v2}, {b3, v7}});
640 builder.AddPhi(v9);
641 builder.AddReturn(new Value(v9));
642 }
643
644 H.FinishGraph();
645
646 FlowGraphPrinter::PrintGraph("Before TypePropagator", H.flow_graph());
647 FlowGraphTypePropagator::Propagate(H.flow_graph());
648 FlowGraphPrinter::PrintGraph("After TypePropagator", H.flow_graph());
649
650 auto& blocks = H.flow_graph()->reverse_postorder();
651 EXPECT_EQ(5, blocks.length());
652 EXPECT_PROPERTY(blocks[0], it.IsGraphEntry());
653
654 // We expect the following types:
655 //
656 // B1[function entry]:2
657 // v3 <- LoadStaticField:10(x) T{int?~} // T{int~} in null safe mode
658 // v5 <- Constant(#sentinel) T{Sentinel~}
659 // Branch if StrictCompare:12(===, v3, v5) goto (2, 3)
660
661 EXPECT_PROPERTY(blocks[1], it.IsFunctionEntry());
662 EXPECT_PROPERTY(blocks[1]->next(), it.IsLoadStaticField());
663 EXPECT_PROPERTY(blocks[1]->next()->AsLoadStaticField(), it.HasType());
664 EXPECT_PROPERTY(blocks[1]->next()->AsLoadStaticField()->Type(),
665 it.can_be_sentinel());
666
667 // B3[target]:6
668 // v7 <- Redefinition(v3 ^ T{int?}) T{int?} // T{int} in null safe mode
669 // goto:18 B4
670 EXPECT_PROPERTY(blocks[3], it.IsTargetEntry());
671 EXPECT_PROPERTY(blocks[3]->next(), it.IsRedefinition());
672 EXPECT_PROPERTY(blocks[3]->next()->AsRedefinition(), it.HasType());
673 EXPECT_PROPERTY(blocks[3]->next()->AsRedefinition()->Type(),
674 !it.can_be_sentinel());
675
676 // B4[join]:8 pred(B2, B3) {
677 // v9 <- phi(v2, v7) alive T{int?} // T{int} in null safe mode
678 // }
679 // Return:20(v9)
680 EXPECT_PROPERTY(blocks[4], it.IsJoinEntry());
681 EXPECT_PROPERTY(blocks[4], it.AsJoinEntry()->phis() != nullptr);
682 EXPECT_PROPERTY(blocks[4]->AsJoinEntry()->phis()->At(0), it.HasType());
683 EXPECT_PROPERTY(blocks[4]->AsJoinEntry()->phis()->At(0)->Type(),
684 !it.can_be_sentinel());
685}
686
687#endif // defined(DART_PRECOMPILER)
688
689// This test verifies that static type is propagated through a LoadField
690// for a record field.
691ISOLATE_UNIT_TEST_CASE(TypePropagator_RecordFieldAccess) {
692 const char* kScript = R"(
693 @pragma('vm:never-inline')
694 (int, {String foo}) bar() => (42, foo: 'hi');
695 @pragma('vm:never-inline')
696 baz(x) {}
697
698 void main() {
699 final x = bar();
700 baz(x.$1 + 1);
701 baz(x.foo);
702 }
703 )";
704
705 const auto& root_library = Library::Handle(LoadTestScript(kScript));
706 Invoke(root_library, "main");
707
708 const auto& function = Function::Handle(GetFunction(root_library, "main"));
709 TestPipeline pipeline(function, CompilerPass::kJIT);
710 FlowGraph* flow_graph = pipeline.RunPasses({});
711
712 auto entry = flow_graph->graph_entry()->normal_entry();
713 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
714 ParallelMovesHandling::kSkip);
715
716 LoadFieldInstr* load1 = nullptr;
717 LoadFieldInstr* load2 = nullptr;
718
719 RELEASE_ASSERT(cursor.TryMatch({
720 kMoveGlob,
721 kMatchAndMoveStaticCall,
722 {kMatchAndMoveLoadField, &load1},
723 kMatchAndMoveCheckSmi,
724 kMatchAndMoveBinarySmiOp,
725 kMatchAndMoveMoveArgument,
726 kMatchAndMoveStaticCall,
727 {kMatchAndMoveLoadField, &load2},
728 kMatchAndMoveMoveArgument,
729 kMatchAndMoveStaticCall,
730 kMatchDartReturn,
731 }));
732
733 EXPECT_PROPERTY(load1->Type()->ToAbstractType(), it.IsIntType());
734 EXPECT_PROPERTY(load2->Type()->ToAbstractType(), it.IsStringType());
735}
736
737} // namespace dart
const char * options
static float next(float f)
static SkV4 v4(SkV3 v, SkScalar w)
Definition: SkM44.cpp:329
Type
Definition: SortBench.cpp:56
SI T load(const P *ptr)
Definition: Transform_inl.h:98
#define EXPECT(type, expectedAlignment, expectedSize)
Vec2Value v2
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
#define Z
GLenum type
bool IsObjectType() const
Definition: object.h:9201
bool IsDynamicType() const
Definition: object.h:9186
Instruction * last_instruction() const
Definition: il.h:1686
static constexpr bool kCannotBeSentinel
Definition: compile_type.h:49
static constexpr bool kCanBeNull
Definition: compile_type.h:45
static CompileType FromAbstractType(const AbstractType &type, bool can_be_null, bool can_be_sentinel)
const AbstractType * ToAbstractType()
CompileType * Type()
Definition: il.h:2521
void ReplaceUsesWith(Definition *other)
Definition: il.cc:1493
void Fail(const char *format,...) const PRINTF_ATTRIBUTE(2
GraphEntryInstr * graph_entry() const
Definition: flow_graph.h:268
void set_result_type(const AbstractType &value) const
Definition: object.cc:8575
static FunctionTypePtr New(intptr_t num_parent_type_arguments=0, Nullability nullability=Nullability::kNonNullable, Heap::Space space=Heap::kOld)
Definition: object.cc:11631
static FunctionPtr New(const FunctionType &signature, const String &name, UntaggedFunction::Kind kind, bool is_static, bool is_const, bool is_abstract, bool is_external, bool is_native, const Object &owner, TokenPosition token_pos, Heap::Space space=Heap::kOld)
Definition: object.cc:10243
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
virtual BlockEntryInstr * PredecessorAt(intptr_t index) const
Definition: il.h:2066
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
JoinEntryInstr * block() const
Definition: il.h:2817
virtual void set_representation(Representation r)
Definition: il.h:2844
static StringPtr New(Thread *thread, const char *cstr)
Definition: symbols.h:723
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
static TypePtr IntType()
static TypePtr DynamicType()
Definition: il.h:75
intptr_t InputCount() const
Definition: il.h:2794
Value * InputAt(intptr_t i) const
Definition: il.h:2795
#define H
Dart_NativeFunction function
Definition: fuchsia.cc:51
#define EXPECT_PROPERTY(entity, property)
SI void load2(const uint16_t *ptr, U16 *r, U16 *g)
const Type & IntType()
Definition: runtime_api.cc:181
Definition: dart_vm.cc:33
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
GrowableArray< Value * > InputsArray
Definition: il.h:901
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
ClassPtr GetClass(const Library &lib, const char *name)
Definition: SkMD5.cpp:130
#define ISOLATE_UNIT_TEST_CASE(name)
Definition: unit_test.h:64