Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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* late_tag = TestCase::LateTag();
404 auto script_chars = Utils::CStringUniquePtr(
405 OS::SCreate(nullptr, R"(
406import 'dart:async';
407
408class G<T> {}
409
410class C<NoBound,
411 NumBound extends num,
412 ComparableBound extends Comparable,
413 StringBound extends String> {
414 // Simple instantiated types.
415 @pragma('vm-test:can-be-smi') %s int t1;
416 @pragma('vm-test:can-be-smi') %s num t2;
417 @pragma('vm-test:can-be-smi') %s Object t3;
418 %s String t4;
419
420 // Type parameters.
421 @pragma('vm-test:can-be-smi') %s NoBound tp1;
422 @pragma('vm-test:can-be-smi') %s NumBound tp2;
423 @pragma('vm-test:can-be-smi') %s ComparableBound tp3;
424 %s StringBound tp4;
425
426 // Comparable<T> instantiations.
427 @pragma('vm-test:can-be-smi') %s Comparable c1;
428 %s Comparable<String> c2;
429 @pragma('vm-test:can-be-smi') %s Comparable<num> c3;
430 %s Comparable<int> c4; // int is not a subtype of Comparable<int>.
431 @pragma('vm-test:can-be-smi') %s Comparable<NoBound> c5;
432 @pragma('vm-test:can-be-smi') %s Comparable<NumBound> c6;
433 @pragma('vm-test:can-be-smi') %s Comparable<ComparableBound> c7;
434 %s Comparable<StringBound> c8;
435
436 // FutureOr<T> instantiations.
437 @pragma('vm-test:can-be-smi') %s FutureOr fo1;
438 %s FutureOr<String> fo2;
439 @pragma('vm-test:can-be-smi') %s FutureOr<num> fo3;
440 @pragma('vm-test:can-be-smi') %s FutureOr<int> fo4;
441 @pragma('vm-test:can-be-smi') %s FutureOr<NoBound> fo5;
442 @pragma('vm-test:can-be-smi') %s FutureOr<NumBound> fo6;
443 @pragma('vm-test:can-be-smi') %s FutureOr<ComparableBound> fo7;
444 %s FutureOr<StringBound> fo8;
445
446 // Other generic classes.
447 %s G<int> g1;
448 %s G<NoBound> g2;
449}
450)",
451 late_tag, late_tag, late_tag, late_tag, late_tag, late_tag,
452 late_tag, late_tag, late_tag, late_tag, late_tag, late_tag,
453 late_tag, late_tag, late_tag, late_tag, late_tag, late_tag,
454 late_tag, late_tag, late_tag, late_tag, late_tag, late_tag,
455 late_tag, late_tag),
456 std::free);
457
458 const auto& lib = Library::Handle(LoadTestScript(script_chars.get()));
459
460 const auto& pragma_can_be_smi =
461 String::Handle(Symbols::New(thread, "vm-test:can-be-smi"));
462 auto expected_can_be_smi = [&](const Field& f) {
463 auto& options = Object::Handle();
464 return lib.FindPragma(thread, /*only_core=*/false, f, pragma_can_be_smi,
465 /*multiple=*/false, &options);
466 };
467
468 const auto& cls = Class::Handle(GetClass(lib, "C"));
469 const auto& err = Error::Handle(cls.EnsureIsFinalized(thread));
470 EXPECT(err.IsNull());
471 const auto& fields = Array::Handle(cls.fields());
472
473 auto& field = Field::Handle();
474 auto& type = AbstractType::Handle();
475 for (intptr_t i = 0; i < fields.Length(); i++) {
476 field ^= fields.At(i);
477 type = field.type();
478
479 auto compile_type = CompileType::FromAbstractType(
481 if (compile_type.CanBeSmi() != expected_can_be_smi(field)) {
482 dart::Expect(__FILE__, __LINE__)
483 .Fail("expected that CanBeSmi() returns %s for compile type %s\n",
484 expected_can_be_smi(field) ? "true" : "false",
485 compile_type.ToCString());
486 }
487 }
488}
489
490// Verifies that Propagate does not crash when running in AOT mode on a graph
491// which contains both AssertAssignable and a CheckClass/Smi after
492// EliminateEnvironments was called.
493ISOLATE_UNIT_TEST_CASE(TypePropagator_RegressFlutter76919) {
494 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
495
496 FlowGraphBuilderHelper H(/*num_parameters=*/1);
497 H.AddVariable("v0", AbstractType::ZoneHandle(Type::DynamicType()));
498
499 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
500
501 // We are going to build the following graph:
502 //
503 // B0[graph_entry]:
504 // B1[function_entry]:
505 // v0 <- Parameter(0)
506 // AssertAssignable(v0, 'int')
507 // CheckSmi(v0)
508 // Return(v0)
509
510 {
511 BlockBuilder builder(H.flow_graph(), normal_entry);
512 Definition* v0 = builder.AddParameter(0, kTagged);
513 auto null_value = builder.AddNullDefinition();
514 builder.AddDefinition(new AssertAssignableInstr(
515 InstructionSource(), new Value(v0),
516 new Value(
517 H.flow_graph()->GetConstant(Type::ZoneHandle(Type::IntType()))),
518 new Value(null_value), new Value(null_value), Symbols::Value(),
519 S.GetNextDeoptId()));
520 builder.AddInstruction(new CheckSmiInstr(new Value(v0), S.GetNextDeoptId(),
522 builder.AddReturn(new Value(v0));
523 }
524
525 H.FinishGraph();
526
527 H.flow_graph()->EliminateEnvironments();
528 FlowGraphTypePropagator::Propagate(H.flow_graph()); // Should not crash.
529}
530
531#if defined(DART_PRECOMPILER)
532
533// This test verifies that LoadStaticField for non-nullable field
534// is non-nullable with sound null safety.
535// Regression test for https://github.com/dart-lang/sdk/issues/47119.
536ISOLATE_UNIT_TEST_CASE(TypePropagator_NonNullableLoadStaticField) {
537 const char* kScript = R"(
538 const y = 0xDEADBEEF;
539 final int x = int.parse('0xFEEDFEED');
540
541 void main(List<String> args) {
542 print(x);
543 print(x + y);
544 }
545 )";
546
547 const auto& root_library = Library::Handle(LoadTestScript(kScript));
548 const auto& function = Function::Handle(GetFunction(root_library, "main"));
549
550 TestPipeline pipeline(function, CompilerPass::kAOT);
551 FlowGraph* flow_graph = pipeline.RunPasses({});
552
553 auto entry = flow_graph->graph_entry()->normal_entry();
554 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
556
557 Instruction* load = nullptr;
558
559 RELEASE_ASSERT(cursor.TryMatch({
560 kMoveGlob,
561 {kMatchAndMoveLoadStaticField, &load},
562 kMatchAndMoveMoveArgument,
563 kMatchAndMoveStaticCall,
564 kMatchAndMoveUnboxInt64,
565 kMatchAndMoveBinaryInt64Op,
566 kMatchAndMoveBoxInt64,
567 kMatchAndMoveMoveArgument,
568 kMatchAndMoveStaticCall,
569 kMatchDartReturn,
570 }));
571
572 EXPECT_PROPERTY(load->AsLoadStaticField()->Type(), !it.is_nullable());
573}
574
575ISOLATE_UNIT_TEST_CASE(TypePropagator_RedefineCanBeSentinelWithCannotBe) {
576 const char* kScript = R"(
577 late final int x;
578 )";
579 Zone* const Z = Thread::Current()->zone();
580 const auto& root_library = Library::CheckedHandle(Z, LoadTestScript(kScript));
581 const auto& toplevel = Class::Handle(Z, root_library.toplevel_class());
582 const auto& field_x = Field::Handle(
583 Z, toplevel.LookupStaticField(String::Handle(Z, String::New("x"))));
584
585 using compiler::BlockBuilder;
586 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
587 FlowGraphBuilderHelper H;
588
589 // We are going to build the following graph:
590 //
591 // B0[graph]:0 {
592 // v2 <- Constant(#3)
593 // }
594 // B1[function entry]:2
595 // v3 <- LoadStaticField:10(x, ThrowIfSentinel)
596 // v5 <- Constant(#sentinel)
597 // Branch if StrictCompare:12(===, v3, v5) goto (2, 3)
598 // B2[target]:4
599 // goto:16 B4
600 // B3[target]:6
601 // v7 <- Redefinition(v3 ^ T{int?})
602 // goto:18 B4
603 // B4[join]:8 pred(B2, B3) {
604 // v9 <- phi(v2, v7) alive
605 // }
606 // Return:20(v9)
607
608 Definition* v2 = H.IntConstant(3);
609 Definition* v3;
610 Definition* v7;
611 PhiInstr* v9;
612 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
613 auto b2 = H.TargetEntry();
614 auto b3 = H.TargetEntry();
615 auto b4 = H.JoinEntry();
616
617 {
618 BlockBuilder builder(H.flow_graph(), b1);
619 v3 = builder.AddDefinition(new LoadStaticFieldInstr(
620 field_x, {},
621 /*calls_initializer=*/false, S.GetNextDeoptId()));
622 auto v5 = builder.AddDefinition(new ConstantInstr(Object::sentinel()));
623 builder.AddBranch(new StrictCompareInstr(
624 {}, Token::kEQ_STRICT, new Value(v3), new Value(v5),
625 /*needs_number_check=*/false, S.GetNextDeoptId()),
626 b2, b3);
627 }
628
629 {
630 BlockBuilder builder(H.flow_graph(), b2);
631 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
632 }
633
634 {
635 BlockBuilder builder(H.flow_graph(), b3);
636 v7 = builder.AddDefinition(new RedefinitionInstr(new Value(v3)));
637 CompileType int_type =
638 CompileType::FromAbstractType(Type::Handle(Type::IntType()),
639 /*can_be_null=*/true,
640 /*can_be_sentinel=*/false);
641 v7->AsRedefinition()->set_constrained_type(new CompileType(int_type));
642 builder.AddInstruction(new GotoInstr(b4, S.GetNextDeoptId()));
643 }
644
645 {
646 BlockBuilder builder(H.flow_graph(), b4);
647 v9 = H.Phi(b4, {{b2, v2}, {b3, v7}});
648 builder.AddPhi(v9);
649 builder.AddReturn(new Value(v9));
650 }
651
652 H.FinishGraph();
653
654 FlowGraphPrinter::PrintGraph("Before TypePropagator", H.flow_graph());
655 FlowGraphTypePropagator::Propagate(H.flow_graph());
656 FlowGraphPrinter::PrintGraph("After TypePropagator", H.flow_graph());
657
658 auto& blocks = H.flow_graph()->reverse_postorder();
659 EXPECT_EQ(5, blocks.length());
660 EXPECT_PROPERTY(blocks[0], it.IsGraphEntry());
661
662 // We expect the following types:
663 //
664 // B1[function entry]:2
665 // v3 <- LoadStaticField:10(x) T{int?~} // T{int~} in null safe mode
666 // v5 <- Constant(#sentinel) T{Sentinel~}
667 // Branch if StrictCompare:12(===, v3, v5) goto (2, 3)
668
669 EXPECT_PROPERTY(blocks[1], it.IsFunctionEntry());
670 EXPECT_PROPERTY(blocks[1]->next(), it.IsLoadStaticField());
671 EXPECT_PROPERTY(blocks[1]->next()->AsLoadStaticField(), it.HasType());
672 EXPECT_PROPERTY(blocks[1]->next()->AsLoadStaticField()->Type(),
673 it.can_be_sentinel());
674
675 // B3[target]:6
676 // v7 <- Redefinition(v3 ^ T{int?}) T{int?} // T{int} in null safe mode
677 // goto:18 B4
678 EXPECT_PROPERTY(blocks[3], it.IsTargetEntry());
679 EXPECT_PROPERTY(blocks[3]->next(), it.IsRedefinition());
680 EXPECT_PROPERTY(blocks[3]->next()->AsRedefinition(), it.HasType());
681 EXPECT_PROPERTY(blocks[3]->next()->AsRedefinition()->Type(),
682 !it.can_be_sentinel());
683
684 // B4[join]:8 pred(B2, B3) {
685 // v9 <- phi(v2, v7) alive T{int?} // T{int} in null safe mode
686 // }
687 // Return:20(v9)
688 EXPECT_PROPERTY(blocks[4], it.IsJoinEntry());
689 EXPECT_PROPERTY(blocks[4], it.AsJoinEntry()->phis() != nullptr);
690 EXPECT_PROPERTY(blocks[4]->AsJoinEntry()->phis()->At(0), it.HasType());
691 EXPECT_PROPERTY(blocks[4]->AsJoinEntry()->phis()->At(0)->Type(),
692 !it.can_be_sentinel());
693}
694
695#endif // defined(DART_PRECOMPILER)
696
697// This test verifies that static type is propagated through a LoadField
698// for a record field.
699ISOLATE_UNIT_TEST_CASE(TypePropagator_RecordFieldAccess) {
700 const char* kScript = R"(
701 @pragma('vm:never-inline')
702 (int, {String foo}) bar() => (42, foo: 'hi');
703 @pragma('vm:never-inline')
704 baz(x) {}
705
706 void main() {
707 final x = bar();
708 baz(x.$1 + 1);
709 baz(x.foo);
710 }
711 )";
712
713 const auto& root_library = Library::Handle(LoadTestScript(kScript));
714 Invoke(root_library, "main");
715
716 const auto& function = Function::Handle(GetFunction(root_library, "main"));
717 TestPipeline pipeline(function, CompilerPass::kJIT);
718 FlowGraph* flow_graph = pipeline.RunPasses({});
719
720 auto entry = flow_graph->graph_entry()->normal_entry();
721 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
722 ParallelMovesHandling::kSkip);
723
724 LoadFieldInstr* load1 = nullptr;
725 LoadFieldInstr* load2 = nullptr;
726
727 RELEASE_ASSERT(cursor.TryMatch({
728 kMoveGlob,
729 kMatchAndMoveStaticCall,
730 {kMatchAndMoveLoadField, &load1},
731 kMatchAndMoveCheckSmi,
732 kMatchAndMoveBinarySmiOp,
733 kMatchAndMoveMoveArgument,
734 kMatchAndMoveStaticCall,
735 {kMatchAndMoveLoadField, &load2},
736 kMatchAndMoveMoveArgument,
737 kMatchAndMoveStaticCall,
738 kMatchDartReturn,
739 }));
740
741 EXPECT_PROPERTY(load1->Type()->ToAbstractType(), it.IsIntType());
742 EXPECT_PROPERTY(load2->Type()->ToAbstractType(), it.IsStringType());
743}
744
745} // namespace dart
const char * options
static float next(float f)
static SkV4 v4(SkV3 v, SkScalar w)
Definition SkM44.cpp:329
SI T load(const P *ptr)
#define EXPECT(type, expectedAlignment, expectedSize)
Vec2Value v2
#define RELEASE_ASSERT(cond)
Definition assert.h:327
#define Z
bool IsObjectType() const
Definition object.h:9181
bool IsDynamicType() const
Definition object.h:9166
Instruction * last_instruction() const
Definition il.h:1680
static constexpr bool kCannotBeSentinel
static constexpr bool kCanBeNull
static CompileType FromAbstractType(const AbstractType &type, bool can_be_null, bool can_be_sentinel)
const AbstractType * ToAbstractType()
CompileType * Type()
Definition il.h:2503
void ReplaceUsesWith(Definition *other)
Definition il.cc:1484
void Fail(const char *format,...) const PRINTF_ATTRIBUTE(2
Definition assert.cc:58
GraphEntryInstr * graph_entry() const
Definition flow_graph.h:268
void set_result_type(const AbstractType &value) const
Definition object.cc:8633
static FunctionTypePtr New(intptr_t num_parent_type_arguments=0, Nullability nullability=Nullability::kLegacy, Heap::Space space=Heap::kOld)
Definition object.cc:11682
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:10301
FunctionEntryInstr * normal_entry() const
Definition il.h:1986
bool TryMatch(std::initializer_list< MatchCode > match_codes, MatchOpCode insert_before=kInvalidMatchOpCode)
@ kNotSpeculative
Definition il.h:969
virtual BlockEntryInstr * PredecessorAt(intptr_t index) const
Definition il.h:2051
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
static Object & Handle()
Definition object.h:407
static Object & ZoneHandle()
Definition object.h:419
JoinEntryInstr * block() const
Definition il.h:2799
virtual void set_representation(Representation r)
Definition il.h:2826
static StringPtr New(Thread *thread, const char *cstr)
Definition symbols.h:722
static const char * LateTag()
Definition unit_test.h:423
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
static TypePtr DynamicType()
Definition object.cc:21866
static TypePtr IntType()
Definition object.cc:21886
std::unique_ptr< char, decltype(std::free) * > CStringUniquePtr
Definition utils.h:644
intptr_t InputCount() const
Definition il.h:2776
Value * InputAt(intptr_t i) const
Definition il.h:2777
#define H
Dart_NativeFunction function
Definition fuchsia.cc:51
#define EXPECT_PROPERTY(entity, property)
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
GrowableArray< Value * > InputsArray
Definition il.h:895
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
ClassPtr GetClass(const Library &lib, const char *name)
Definition SkMD5.cpp:130
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64