Flutter Engine
The Flutter Engine
constant_propagator.cc
Go to the documentation of this file.
1// Copyright (c) 2013, 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 "vm/bit_vector.h"
14#include "vm/parser.h"
15#include "vm/symbols.h"
16
17namespace dart {
18
19DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis.");
21 trace_constant_propagation,
22 false,
23 "Print constant propagation and useless code elimination.");
24
25// Quick access to the current thread & zone.
26#define Z (graph_->zone())
27#define T (graph_->thread())
28
30 FlowGraph* graph,
32 : FlowGraphVisitor(ignored),
33 graph_(graph),
34 unknown_(Object::unknown_constant()),
35 non_constant_(Object::non_constant()),
36 constant_value_(Object::Handle(Z)),
37 reachable_(new(Z) BitVector(Z, graph->preorder().length())),
38 unwrapped_phis_(new(Z) BitVector(Z, graph->current_ssa_temp_index())),
39 block_worklist_(),
40 definition_worklist_(graph, 10) {}
41
44 ConstantPropagator cp(graph, ignored);
45 cp.Analyze();
46 cp.Transform();
47}
48
51 ConstantPropagator cp(graph, ignored);
52 cp.Analyze();
53 cp.Transform();
54 cp.EliminateRedundantBranches();
55}
56
57void ConstantPropagator::SetReachable(BlockEntryInstr* block) {
58 if (!reachable_->Contains(block->preorder_number())) {
59 reachable_->Add(block->preorder_number());
60 block_worklist_.Add(block);
61 }
62}
63
64bool ConstantPropagator::SetValue(Definition* definition, const Object& value) {
65 // We would like to assert we only go up (toward non-constant) in the lattice.
66 //
67 // ASSERT(IsUnknown(definition->constant_value()) ||
68 // IsNonConstant(value) ||
69 // (definition->constant_value().ptr() == value.ptr()));
70 //
71 // But the final disjunct is not true (e.g., mint or double constants are
72 // heap-allocated and so not necessarily pointer-equal on each iteration).
73 if (definition->constant_value().ptr() != value.ptr()) {
74 definition->constant_value() = value.ptr();
75 if (definition->input_use_list() != nullptr) {
76 definition_worklist_.Add(definition);
77 }
78 return true;
79 }
80 return false;
81}
82
83static bool IsIdenticalConstants(const Object& left, const Object& right) {
84 // This should be kept in line with Identical_comparison (identical.cc)
85 // (=> Instance::IsIdenticalTo in object.cc).
86
87 if (left.ptr() == right.ptr()) return true;
88 if (left.GetClassId() != right.GetClassId()) return false;
89 if (left.IsInteger()) {
90 return Integer::Cast(left).Equals(Integer::Cast(right));
91 }
92 if (left.IsDouble()) {
93 return Double::Cast(left).BitwiseEqualsToDouble(
94 Double::Cast(right).value());
95 }
96 return false;
97}
98
99// Compute the join of two values in the lattice, assign it to the first.
100void ConstantPropagator::Join(Object* left, const Object& right) {
101 // Join(non-constant, X) = non-constant
102 // Join(X, unknown) = X
103 if (IsNonConstant(*left) || IsUnknown(right)) return;
104
105 // Join(unknown, X) = X
106 // Join(X, non-constant) = non-constant
107 if (IsUnknown(*left) || IsNonConstant(right)) {
108 *left = right.ptr();
109 return;
110 }
111
112 // Join(X, X) = X
113 if (IsIdenticalConstants(*left, right)) return;
114
115 // Join(X, Y) = non-constant
116 *left = non_constant_.ptr();
117}
118
119// --------------------------------------------------------------------------
120// Analysis of blocks. Called at most once per block. The block is already
121// marked as reachable. All instructions in the block are analyzed.
122void ConstantPropagator::VisitGraphEntry(GraphEntryInstr* block) {
123 for (auto def : *block->initial_definitions()) {
124 def->Accept(this);
125 }
126 ASSERT(ForwardInstructionIterator(block).Done());
127
128 // TODO(fschneider): Improve this approximation. The catch entry is only
129 // reachable if a call in the try-block is reachable.
130 for (intptr_t i = 0; i < block->SuccessorCount(); ++i) {
131 SetReachable(block->SuccessorAt(i));
132 }
133}
134
135void ConstantPropagator::VisitFunctionEntry(FunctionEntryInstr* block) {
136 for (auto def : *block->initial_definitions()) {
137 def->Accept(this);
138 }
139 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
140 it.Current()->Accept(this);
141 }
142}
143
144void ConstantPropagator::VisitNativeEntry(NativeEntryInstr* block) {
145 VisitFunctionEntry(block);
146}
147
148void ConstantPropagator::VisitOsrEntry(OsrEntryInstr* block) {
149 for (auto def : *block->initial_definitions()) {
150 def->Accept(this);
151 }
152 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
153 it.Current()->Accept(this);
154 }
155}
156
157void ConstantPropagator::VisitCatchBlockEntry(CatchBlockEntryInstr* block) {
158 for (auto def : *block->initial_definitions()) {
159 def->Accept(this);
160 }
161 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
162 it.Current()->Accept(this);
163 }
164}
165
166void ConstantPropagator::VisitJoinEntry(JoinEntryInstr* block) {
167 // Phis are visited when visiting Goto at a predecessor. See VisitGoto.
168 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
169 it.Current()->Accept(this);
170 }
171}
172
173void ConstantPropagator::VisitTargetEntry(TargetEntryInstr* block) {
174 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
175 it.Current()->Accept(this);
176 }
177}
178
179void ConstantPropagator::VisitIndirectEntry(IndirectEntryInstr* block) {
180 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
181 it.Current()->Accept(this);
182 }
183}
184
185void ConstantPropagator::VisitParallelMove(ParallelMoveInstr* instr) {
186 // Parallel moves have not yet been inserted in the graph.
187 UNREACHABLE();
188}
189
190// --------------------------------------------------------------------------
191// Analysis of control instructions. Unconditional successors are
192// reachable. Conditional successors are reachable depending on the
193// constant value of the condition.
194void ConstantPropagator::VisitDartReturn(DartReturnInstr* instr) {
195 // Nothing to do.
196}
197
198void ConstantPropagator::VisitNativeReturn(NativeReturnInstr* instr) {
199 // Nothing to do.
200}
201
202void ConstantPropagator::VisitThrow(ThrowInstr* instr) {
203 // Nothing to do.
204}
205
206void ConstantPropagator::VisitReThrow(ReThrowInstr* instr) {
207 // Nothing to do.
208}
209
210void ConstantPropagator::VisitStop(StopInstr* instr) {
211 // Nothing to do.
212}
213
214void ConstantPropagator::VisitGoto(GotoInstr* instr) {
215 SetReachable(instr->successor());
216
217 // Phi value depends on the reachability of a predecessor. We have
218 // to revisit phis every time a predecessor becomes reachable.
219 for (PhiIterator it(instr->successor()); !it.Done(); it.Advance()) {
220 PhiInstr* phi = it.Current();
221 phi->Accept(this);
222
223 // If this phi was previously unwrapped as redundant and it is no longer
224 // redundant (does not unwrap) then we need to revisit the uses.
225 if (unwrapped_phis_->Contains(phi->ssa_temp_index()) &&
226 (UnwrapPhi(phi) == phi)) {
227 unwrapped_phis_->Remove(phi->ssa_temp_index());
228 definition_worklist_.Add(phi);
229 }
230 }
231}
232
233void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) {
234 if (reachable_->Contains(instr->GetBlock()->preorder_number())) {
235 for (intptr_t i = 0; i < instr->SuccessorCount(); i++) {
236 SetReachable(instr->SuccessorAt(i));
237 }
238 }
239}
240
241void ConstantPropagator::VisitBranch(BranchInstr* instr) {
242 instr->comparison()->Accept(this);
243
244 // The successors may be reachable, but only if this instruction is. (We
245 // might be analyzing it because the constant value of one of its inputs
246 // has changed.)
247 if (reachable_->Contains(instr->GetBlock()->preorder_number())) {
248 if (instr->constant_target() != nullptr) {
249 ASSERT((instr->constant_target() == instr->true_successor()) ||
250 (instr->constant_target() == instr->false_successor()));
251 SetReachable(instr->constant_target());
252 } else {
253 const Object& value = instr->comparison()->constant_value();
254 if (IsNonConstant(value)) {
255 SetReachable(instr->true_successor());
256 SetReachable(instr->false_successor());
257 } else if (value.ptr() == Bool::True().ptr()) {
258 SetReachable(instr->true_successor());
259 } else if (!IsUnknown(value)) { // Any other constant.
260 SetReachable(instr->false_successor());
261 }
262 }
263 }
264}
265
266// --------------------------------------------------------------------------
267// Analysis of non-definition instructions. They do not have values so they
268// cannot have constant values.
269void ConstantPropagator::VisitCheckStackOverflow(
270 CheckStackOverflowInstr* instr) {}
271
272void ConstantPropagator::VisitCheckClass(CheckClassInstr* instr) {}
273
274void ConstantPropagator::VisitCheckCondition(CheckConditionInstr* instr) {}
275
276void ConstantPropagator::VisitCheckClassId(CheckClassIdInstr* instr) {}
277
278void ConstantPropagator::VisitGuardFieldClass(GuardFieldClassInstr* instr) {}
279
280void ConstantPropagator::VisitGuardFieldLength(GuardFieldLengthInstr* instr) {}
281
282void ConstantPropagator::VisitGuardFieldType(GuardFieldTypeInstr* instr) {}
283
284void ConstantPropagator::VisitCheckSmi(CheckSmiInstr* instr) {}
285
286void ConstantPropagator::VisitTailCall(TailCallInstr* instr) {}
287
288void ConstantPropagator::VisitCheckEitherNonSmi(CheckEitherNonSmiInstr* instr) {
289}
290
291void ConstantPropagator::VisitStoreIndexedUnsafe(
292 StoreIndexedUnsafeInstr* instr) {}
293
294void ConstantPropagator::VisitStoreIndexed(StoreIndexedInstr* instr) {}
295
296void ConstantPropagator::VisitStoreField(StoreFieldInstr* instr) {}
297
298void ConstantPropagator::VisitMemoryCopy(MemoryCopyInstr* instr) {}
299
300void ConstantPropagator::VisitDeoptimize(DeoptimizeInstr* instr) {
301 // TODO(vegorov) remove all code after DeoptimizeInstr as dead.
302}
303
304Definition* ConstantPropagator::UnwrapPhi(Definition* defn) {
305 if (defn->IsPhi()) {
306 JoinEntryInstr* block = defn->AsPhi()->block();
307
308 Definition* input = nullptr;
309 for (intptr_t i = 0; i < defn->InputCount(); ++i) {
310 if (reachable_->Contains(block->PredecessorAt(i)->preorder_number())) {
311 if (input == nullptr) {
312 input = defn->InputAt(i)->definition();
313 } else {
314 return defn;
315 }
316 }
317 }
318
319 return input;
320 }
321
322 return defn;
323}
324
325void ConstantPropagator::MarkUnwrappedPhi(Definition* phi) {
326 ASSERT(phi->IsPhi());
327 unwrapped_phis_->Add(phi->ssa_temp_index());
328}
329
330ConstantPropagator::PhiInfo* ConstantPropagator::GetPhiInfo(PhiInstr* phi) {
331 if (phi->HasPassSpecificId(CompilerPass::kConstantPropagation)) {
332 const intptr_t id =
333 phi->GetPassSpecificId(CompilerPass::kConstantPropagation);
334 // Note: id might have been assigned by the previous round of constant
335 // propagation, so we need to verify it before using it.
336 if (id < phis_.length() && phis_[id].phi == phi) {
337 return &phis_[id];
338 }
339 }
340
341 phi->SetPassSpecificId(CompilerPass::kConstantPropagation, phis_.length());
342 phis_.Add({phi, 0});
343 return &phis_.Last();
344}
345
346// --------------------------------------------------------------------------
347// Analysis of definitions. Compute the constant value. If it has changed
348// and the definition has input uses, add the definition to the definition
349// worklist so that the used can be processed.
350void ConstantPropagator::VisitPhi(PhiInstr* instr) {
351 // Detect convergence issues by checking if visit count for this phi
352 // is too high. We should only visit this phi once for every predecessor
353 // becoming reachable, once for every input changing its constant value and
354 // once for an unwrapped redundant phi becoming non-redundant.
355 // Inputs can only change their constant value at most three times: from
356 // non-constant to unknown to specific constant to non-constant. The first
357 // link (non-constant to ...) can happen when we run the second round of
358 // constant propagation - some instructions can have non-constant assigned to
359 // them at the end of the previous constant propagation.
360 auto info = GetPhiInfo(instr);
361 info->visit_count++;
362 const intptr_t kMaxVisitsExpected = 5 * instr->InputCount();
363 if (info->visit_count > kMaxVisitsExpected) {
365 "ConstantPropagation pass is failing to converge on graph for %s\n",
366 graph_->parsed_function().function().ToCString());
367 OS::PrintErr("Phi %s was visited %" Pd " times\n", instr->ToCString(),
368 info->visit_count);
370 FlowGraphPrinter::PrintGraph("Constant Propagation", graph_));
371 FATAL("Aborting due to non-convergence.");
372 }
373
374 // Compute the join over all the reachable predecessor values.
375 JoinEntryInstr* block = instr->block();
376 Object& value = Object::ZoneHandle(Z, Unknown());
377 for (intptr_t pred_idx = 0; pred_idx < instr->InputCount(); ++pred_idx) {
378 if (reachable_->Contains(
379 block->PredecessorAt(pred_idx)->preorder_number())) {
380 Join(&value, instr->InputAt(pred_idx)->definition()->constant_value());
381 }
382 }
383 SetValue(instr, value);
384}
385
386void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) {
387 if (instr->inserted_by_constant_propagation()) {
388 return;
389 }
390
391 const Object& value = instr->value()->definition()->constant_value();
392 if (IsConstant(value)) {
393 SetValue(instr, value);
394 } else {
395 SetValue(instr, non_constant_);
396 }
397}
398
399void ConstantPropagator::VisitReachabilityFence(ReachabilityFenceInstr* instr) {
400 // Nothing to do.
401}
402
403void ConstantPropagator::VisitCheckArrayBound(CheckArrayBoundInstr* instr) {
404 // Don't propagate constants through check, since it would eliminate
405 // the data dependence between the bound check and the load/store.
406 // Graph finalization will expose the constant eventually.
407 SetValue(instr, non_constant_);
408}
409
410void ConstantPropagator::VisitGenericCheckBound(GenericCheckBoundInstr* instr) {
411 // Don't propagate constants through check, since it would eliminate
412 // the data dependence between the bound check and the load/store.
413 // Graph finalization will expose the constant eventually.
414 SetValue(instr, non_constant_);
415}
416
417void ConstantPropagator::VisitCheckWritable(CheckWritableInstr* instr) {
418 // Don't propagate constants through check, since it would eliminate
419 // the data dependence between the writable check and its use.
420 // Graph finalization will expose the constant eventually.
421 SetValue(instr, non_constant_);
422}
423
424void ConstantPropagator::VisitCheckNull(CheckNullInstr* instr) {
425 // Don't propagate constants through check, since it would eliminate
426 // the data dependence between the null check and its use.
427 // Graph finalization will expose the constant eventually.
428 SetValue(instr, non_constant_);
429}
430
431void ConstantPropagator::VisitParameter(ParameterInstr* instr) {
432 SetValue(instr, non_constant_);
433}
434
435void ConstantPropagator::VisitNativeParameter(NativeParameterInstr* instr) {
436 SetValue(instr, non_constant_);
437}
438
439void ConstantPropagator::VisitMoveArgument(MoveArgumentInstr* instr) {
440 UNREACHABLE(); // Inserted right before register allocation.
441}
442
443void ConstantPropagator::VisitAssertAssignable(AssertAssignableInstr* instr) {
444 const auto& value = instr->value()->definition()->constant_value();
445 const auto& dst_type = instr->dst_type()->definition()->constant_value();
446
447 if (IsNonConstant(value) || IsNonConstant(dst_type)) {
448 SetValue(instr, non_constant_);
449 return;
450 } else if (IsUnknown(value) || IsUnknown(dst_type)) {
451 return;
452 }
453 ASSERT(IsConstant(value) && IsConstant(dst_type));
454 if (dst_type.IsAbstractType()) {
455 // We are ignoring the instantiator and instantiator_type_arguments, but
456 // still monotonic and safe.
457 if (instr->value()->Type()->IsAssignableTo(AbstractType::Cast(dst_type))) {
458 SetValue(instr, value);
459 return;
460 }
461 }
462 SetValue(instr, non_constant_);
463}
464
465void ConstantPropagator::VisitAssertSubtype(AssertSubtypeInstr* instr) {}
466
467void ConstantPropagator::VisitAssertBoolean(AssertBooleanInstr* instr) {
468 const Object& value = instr->value()->definition()->constant_value();
469 if (IsUnknown(value)) {
470 return;
471 }
472 if (value.IsBool()) {
473 SetValue(instr, value);
474 } else {
475 SetValue(instr, non_constant_);
476 }
477}
478
479void ConstantPropagator::VisitClosureCall(ClosureCallInstr* instr) {
480 SetValue(instr, non_constant_);
481}
482
483void ConstantPropagator::VisitInstanceCall(InstanceCallInstr* instr) {
484 SetValue(instr, non_constant_);
485}
486
487void ConstantPropagator::VisitPolymorphicInstanceCall(
488 PolymorphicInstanceCallInstr* instr) {
489 SetValue(instr, non_constant_);
490}
491
492void ConstantPropagator::VisitDispatchTableCall(DispatchTableCallInstr* instr) {
493 SetValue(instr, non_constant_);
494}
495
496void ConstantPropagator::VisitStaticCall(StaticCallInstr* instr) {
497 const auto kind = instr->function().recognized_kind();
498 if (kind != MethodRecognizer::kUnknown) {
499 if (instr->ArgumentCount() == 1) {
500 const Object& argument = instr->ArgumentAt(0)->constant_value();
501 if (IsUnknown(argument)) {
502 return;
503 }
504 if (IsConstant(argument)) {
505 Object& value = Object::ZoneHandle(Z);
506 if (instr->Evaluate(graph_, argument, &value)) {
507 SetValue(instr, value);
508 return;
509 }
510 }
511 } else if (instr->ArgumentCount() == 2) {
512 const Object& argument1 = instr->ArgumentAt(0)->constant_value();
513 const Object& argument2 = instr->ArgumentAt(1)->constant_value();
514 if (IsUnknown(argument1) || IsUnknown(argument2)) {
515 return;
516 }
517 if (IsConstant(argument1) && IsConstant(argument2)) {
518 Object& value = Object::ZoneHandle(Z);
519 if (instr->Evaluate(graph_, argument1, argument2, &value)) {
520 SetValue(instr, value);
521 return;
522 }
523 }
524 }
525 }
526
527 switch (kind) {
528 case MethodRecognizer::kOneByteString_equality:
529 case MethodRecognizer::kTwoByteString_equality: {
530 ASSERT(instr->FirstArgIndex() == 0);
531 // Use pure identity as a fast equality test.
532 if (instr->ArgumentAt(0)->OriginalDefinition() ==
533 instr->ArgumentAt(1)->OriginalDefinition()) {
534 SetValue(instr, Bool::True());
535 return;
536 }
537 break;
538 }
539 default:
540 break;
541 }
542 SetValue(instr, non_constant_);
543}
544
545void ConstantPropagator::VisitCachableIdempotentCall(
546 CachableIdempotentCallInstr* instr) {
547 // This instruction should not be inserted if its value is constant.
548 SetValue(instr, non_constant_);
549}
550
551void ConstantPropagator::VisitLoadLocal(LoadLocalInstr* instr) {
552 // Instruction is eliminated when translating to SSA.
553 UNREACHABLE();
554}
555
556void ConstantPropagator::VisitDropTemps(DropTempsInstr* instr) {
557 // Instruction is eliminated when translating to SSA.
558 UNREACHABLE();
559}
560
561void ConstantPropagator::VisitMakeTemp(MakeTempInstr* instr) {
562 // Instruction is eliminated when translating to SSA.
563 UNREACHABLE();
564}
565
566void ConstantPropagator::VisitStoreLocal(StoreLocalInstr* instr) {
567 // Instruction is eliminated when translating to SSA.
568 UNREACHABLE();
569}
570
571void ConstantPropagator::VisitIfThenElse(IfThenElseInstr* instr) {
572 instr->comparison()->Accept(this);
573 const Object& value = instr->comparison()->constant_value();
574 ASSERT(!value.IsNull());
575 if (IsUnknown(value)) {
576 return;
577 }
578 if (value.IsBool()) {
579 bool result = Bool::Cast(value).value();
580 SetValue(instr, Smi::Handle(Z, Smi::New(result ? instr->if_true()
581 : instr->if_false())));
582 } else {
583 SetValue(instr, non_constant_);
584 }
585}
586
587void ConstantPropagator::VisitStrictCompare(StrictCompareInstr* instr) {
588 Definition* left_defn = instr->left()->definition();
589 Definition* right_defn = instr->right()->definition();
590
591 Definition* unwrapped_left_defn = UnwrapPhi(left_defn);
592 Definition* unwrapped_right_defn = UnwrapPhi(right_defn);
593 if (unwrapped_left_defn == unwrapped_right_defn) {
594 // Fold x === x, and x !== x to true/false.
595 if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT))) {
596 if (unwrapped_left_defn != left_defn) {
597 MarkUnwrappedPhi(left_defn);
598 }
599 if (unwrapped_right_defn != right_defn) {
600 MarkUnwrappedPhi(right_defn);
601 }
602 }
603 return;
604 }
605
606 const Object& left = left_defn->constant_value();
607 const Object& right = right_defn->constant_value();
608 if (IsNonConstant(left) || IsNonConstant(right)) {
609 if ((left.ptr() == Object::sentinel().ptr() &&
610 !instr->right()->Type()->can_be_sentinel()) ||
611 (right.ptr() == Object::sentinel().ptr() &&
612 !instr->left()->Type()->can_be_sentinel())) {
613 // Handle provably false (EQ_STRICT) or true (NE_STRICT) sentinel checks.
614 SetValue(instr, Bool::Get(instr->kind() != Token::kEQ_STRICT));
615 } else if ((left.IsNull() &&
616 instr->right()->Type()->HasDecidableNullability()) ||
617 (right.IsNull() &&
618 instr->left()->Type()->HasDecidableNullability())) {
619 // TODO(vegorov): incorporate nullability information into the lattice.
620 bool result = left.IsNull() ? instr->right()->Type()->IsNull()
621 : instr->left()->Type()->IsNull();
622 if (instr->kind() == Token::kNE_STRICT) {
623 result = !result;
624 }
625 SetValue(instr, Bool::Get(result));
626 } else {
627 const intptr_t left_cid = instr->left()->Type()->ToCid();
628 const intptr_t right_cid = instr->right()->Type()->ToCid();
629 // If exact classes (cids) are known and they differ, the result
630 // of strict compare can be computed.
631 if ((left_cid != kDynamicCid) && (right_cid != kDynamicCid) &&
632 (left_cid != right_cid)) {
633 const bool result = (instr->kind() != Token::kEQ_STRICT);
634 SetValue(instr, Bool::Get(result));
635 } else {
636 SetValue(instr, non_constant_);
637 }
638 }
639 } else if (IsConstant(left) && IsConstant(right)) {
640 bool result = IsIdenticalConstants(left, right);
641 if (instr->kind() == Token::kNE_STRICT) {
642 result = !result;
643 }
644 SetValue(instr, Bool::Get(result));
645 }
646}
647
649 const Integer& left,
650 const Integer& right) {
651 const int result = left.CompareWith(right);
652 switch (kind) {
653 case Token::kEQ:
654 return (result == 0);
655 case Token::kNE:
656 return (result != 0);
657 case Token::kLT:
658 return (result < 0);
659 case Token::kGT:
660 return (result > 0);
661 case Token::kLTE:
662 return (result <= 0);
663 case Token::kGTE:
664 return (result >= 0);
665 default:
666 UNREACHABLE();
667 return false;
668 }
669}
670
671// Comparison instruction that is equivalent to the (left & right) == 0
672// comparison pattern.
673void ConstantPropagator::VisitTestInt(TestIntInstr* instr) {
674 const Object& left = instr->left()->definition()->constant_value();
675 const Object& right = instr->right()->definition()->constant_value();
676 if (IsNonConstant(left) || IsNonConstant(right)) {
677 SetValue(instr, non_constant_);
678 return;
679 } else if (IsUnknown(left) || IsUnknown(right)) {
680 return;
681 }
682 ASSERT(IsConstant(left) && IsConstant(right));
683 if (left.IsInteger() && right.IsInteger()) {
684 const bool result = CompareIntegers(
685 instr->kind(),
686 Integer::Handle(Z, Integer::Cast(left).BitOp(Token::kBIT_AND,
687 Integer::Cast(right))),
688 Object::smi_zero());
689 SetValue(instr, result ? Bool::True() : Bool::False());
690 } else {
691 SetValue(instr, non_constant_);
692 }
693}
694
695void ConstantPropagator::VisitTestCids(TestCidsInstr* instr) {
696 // TODO(sra): Constant fold test.
697 SetValue(instr, non_constant_);
698}
699
700void ConstantPropagator::VisitTestRange(TestRangeInstr* instr) {
701 const Object& input = instr->value()->definition()->constant_value();
702 if (IsNonConstant(input)) {
703 SetValue(instr, non_constant_);
704 } else if (IsConstant(input) && input.IsSmi()) {
705 uword value = Smi::Cast(input).Value();
706 bool in_range = (instr->lower() <= value) && (value <= instr->upper());
707 ASSERT((instr->kind() == Token::kIS) || (instr->kind() == Token::kISNOT));
708 SetValue(instr, Bool::Get(in_range == (instr->kind() == Token::kIS)));
709 }
710}
711
712void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) {
713 Definition* left_defn = instr->left()->definition();
714 Definition* right_defn = instr->right()->definition();
715
716 if (IsIntegerClassId(instr->operation_cid())) {
717 // Fold x == x, and x != x to true/false for numbers comparisons.
718 Definition* unwrapped_left_defn = UnwrapPhi(left_defn);
719 Definition* unwrapped_right_defn = UnwrapPhi(right_defn);
720 if (unwrapped_left_defn == unwrapped_right_defn) {
721 // Fold x === x, and x !== x to true/false.
722 if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ))) {
723 if (unwrapped_left_defn != left_defn) {
724 MarkUnwrappedPhi(left_defn);
725 }
726 if (unwrapped_right_defn != right_defn) {
727 MarkUnwrappedPhi(right_defn);
728 }
729 }
730 return;
731 }
732 }
733
734 const Object& left = left_defn->constant_value();
735 const Object& right = right_defn->constant_value();
736 if (IsNonConstant(left) || IsNonConstant(right)) {
737 SetValue(instr, non_constant_);
738 } else if (IsConstant(left) && IsConstant(right)) {
739 if (left.IsInteger() && right.IsInteger()) {
740 const bool result = CompareIntegers(instr->kind(), Integer::Cast(left),
741 Integer::Cast(right));
742 SetValue(instr, Bool::Get(result));
743 } else if (left.IsString() && right.IsString()) {
744 const bool result = String::Cast(left).Equals(String::Cast(right));
745 SetValue(instr, Bool::Get((instr->kind() == Token::kEQ) == result));
746 } else {
747 SetValue(instr, non_constant_);
748 }
749 }
750}
751
752void ConstantPropagator::VisitRelationalOp(RelationalOpInstr* instr) {
753 const Object& left = instr->left()->definition()->constant_value();
754 const Object& right = instr->right()->definition()->constant_value();
755 if (IsNonConstant(left) || IsNonConstant(right)) {
756 SetValue(instr, non_constant_);
757 } else if (IsConstant(left) && IsConstant(right)) {
758 if (left.IsInteger() && right.IsInteger()) {
759 const bool result = CompareIntegers(instr->kind(), Integer::Cast(left),
760 Integer::Cast(right));
761 SetValue(instr, Bool::Get(result));
762 } else if (left.IsDouble() && right.IsDouble()) {
763 // TODO(srdjan): Implement.
764 SetValue(instr, non_constant_);
765 } else {
766 SetValue(instr, non_constant_);
767 }
768 }
769}
770
771void ConstantPropagator::VisitNativeCall(NativeCallInstr* instr) {
772 SetValue(instr, non_constant_);
773}
774
775void ConstantPropagator::VisitFfiCall(FfiCallInstr* instr) {
776 SetValue(instr, non_constant_);
777}
778
779void ConstantPropagator::VisitLeafRuntimeCall(LeafRuntimeCallInstr* instr) {
780 SetValue(instr, non_constant_);
781}
782
783void ConstantPropagator::VisitDebugStepCheck(DebugStepCheckInstr* instr) {
784 // Nothing to do.
785}
786
787void ConstantPropagator::VisitRecordCoverage(RecordCoverageInstr* instr) {
788 // Nothing to do.
789}
790
791void ConstantPropagator::VisitOneByteStringFromCharCode(
792 OneByteStringFromCharCodeInstr* instr) {
793 const Object& o = instr->char_code()->definition()->constant_value();
794 if (IsUnknown(o)) {
795 return;
796 }
797 if (o.IsSmi()) {
798 const intptr_t ch_code = Smi::Cast(o).Value();
799 ASSERT(ch_code >= 0);
800 if (ch_code < Symbols::kMaxOneCharCodeSymbol) {
801 StringPtr* table = Symbols::PredefinedAddress();
802 SetValue(instr, String::ZoneHandle(Z, table[ch_code]));
803 return;
804 }
805 }
806 SetValue(instr, non_constant_);
807}
808
809void ConstantPropagator::VisitStringToCharCode(StringToCharCodeInstr* instr) {
810 const Object& o = instr->str()->definition()->constant_value();
811 if (IsUnknown(o)) {
812 return;
813 }
814 if (o.IsString()) {
815 const String& str = String::Cast(o);
816 const intptr_t result =
817 (str.Length() == 1) ? static_cast<intptr_t>(str.CharAt(0)) : -1;
818 SetValue(instr, Smi::ZoneHandle(Z, Smi::New(result)));
819 } else {
820 SetValue(instr, non_constant_);
821 }
822}
823
824void ConstantPropagator::VisitUtf8Scan(Utf8ScanInstr* instr) {
825 SetValue(instr, non_constant_);
826}
827
828void ConstantPropagator::VisitLoadIndexed(LoadIndexedInstr* instr) {
829 const Object& array_obj = instr->array()->definition()->constant_value();
830 const Object& index_obj = instr->index()->definition()->constant_value();
831 if (IsNonConstant(array_obj) || IsNonConstant(index_obj)) {
832 SetValue(instr, non_constant_);
833 } else if (IsConstant(array_obj) && IsConstant(index_obj)) {
834 // Need index to be Smi and array to be either String or an immutable array.
835 if (!index_obj.IsSmi()) {
836 // Should not occur.
837 SetValue(instr, non_constant_);
838 return;
839 }
840 const intptr_t index = Smi::Cast(index_obj).Value();
841 if (index >= 0) {
842 if (array_obj.IsString()) {
843 const String& str = String::Cast(array_obj);
844 if (str.Length() > index) {
845 SetValue(instr,
847 Z, Smi::New(static_cast<intptr_t>(str.CharAt(index)))));
848 return;
849 }
850 } else if (array_obj.IsArray()) {
851 const Array& a = Array::Cast(array_obj);
852 if ((a.Length() > index) && a.IsImmutable()) {
853 Instance& result = Instance::Handle(Z);
854 result ^= a.At(index);
855 SetValue(instr, result);
856 return;
857 }
858 }
859 }
860 SetValue(instr, non_constant_);
861 }
862}
863
864void ConstantPropagator::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) {
865 // TODO(zerny): Implement constant propagation.
866 SetValue(instr, non_constant_);
867}
868
869void ConstantPropagator::VisitLoadIndexedUnsafe(LoadIndexedUnsafeInstr* instr) {
870 SetValue(instr, non_constant_);
871}
872
873void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) {
874 // Cannot treat an initialized field as constant because the same code will be
875 // used when the AppAOT or AppJIT starts over with everything uninitialized or
876 // another isolate in the isolate group starts with everything uninitialized.
877 SetValue(instr, non_constant_);
878}
879
880void ConstantPropagator::VisitStoreStaticField(StoreStaticFieldInstr* instr) {
881 SetValue(instr, instr->value()->definition()->constant_value());
882}
883
884void ConstantPropagator::VisitBooleanNegate(BooleanNegateInstr* instr) {
885 const Object& value = instr->value()->definition()->constant_value();
886 if (IsUnknown(value)) {
887 return;
888 }
889 if (value.IsBool()) {
890 bool val = value.ptr() != Bool::True().ptr();
891 SetValue(instr, Bool::Get(val));
892 } else {
893 SetValue(instr, non_constant_);
894 }
895}
896
897void ConstantPropagator::VisitBoolToInt(BoolToIntInstr* instr) {
898 // TODO(riscv)
899 SetValue(instr, non_constant_);
900}
901
902void ConstantPropagator::VisitIntToBool(IntToBoolInstr* instr) {
903 // TODO(riscv)
904 SetValue(instr, non_constant_);
905}
906
907void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) {
908 Definition* def = instr->value()->definition();
909 const Object& value = def->constant_value();
910 const AbstractType& checked_type = instr->type();
911 // If the checked type is a top type, the result is always true.
912 if (checked_type.IsTopTypeForInstanceOf()) {
913 SetValue(instr, Bool::True());
914 } else if (IsNonConstant(value)) {
915 intptr_t value_cid = instr->value()->definition()->Type()->ToCid();
916 Representation rep = def->representation();
917 if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) ||
918 (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) ||
919 (checked_type.IsDoubleType() && (rep == kUnboxedDouble)) ||
920 (checked_type.IsIntType() && (rep == kUnboxedInt64))) {
921 // Ensure that compile time type matches representation.
922 ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) ||
923 ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) ||
924 ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) ||
925 ((rep == kUnboxedInt64) && (value_cid == kMintCid)));
926 // The representation guarantees the type check to be true.
927 SetValue(instr, Bool::True());
928 } else {
929 SetValue(instr, non_constant_);
930 }
931 } else if (IsConstant(value)) {
932 if (value.IsInstance() && (value.ptr() != Object::sentinel().ptr())) {
933 const Instance& instance = Instance::Cast(value);
934 if (instr->instantiator_type_arguments()->BindsToConstantNull() &&
935 instr->function_type_arguments()->BindsToConstantNull()) {
936 bool is_instance =
937 instance.IsInstanceOf(checked_type, Object::null_type_arguments(),
938 Object::null_type_arguments());
939 SetValue(instr, Bool::Get(is_instance));
940 return;
941 }
942 }
943 SetValue(instr, non_constant_);
944 }
945}
946
947void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) {
948 SetValue(instr, non_constant_);
949}
950
951void ConstantPropagator::VisitAllocateTypedData(AllocateTypedDataInstr* instr) {
952 SetValue(instr, non_constant_);
953}
954
955void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) {
956 SetValue(instr, non_constant_);
957}
958
959void ConstantPropagator::VisitAllocateClosure(AllocateClosureInstr* instr) {
960 SetValue(instr, non_constant_);
961}
962
963void ConstantPropagator::VisitAllocateRecord(AllocateRecordInstr* instr) {
964 SetValue(instr, non_constant_);
965}
966
967void ConstantPropagator::VisitAllocateSmallRecord(
968 AllocateSmallRecordInstr* instr) {
969 SetValue(instr, non_constant_);
970}
971
972void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) {
973 SetValue(instr, non_constant_);
974}
975
976void ConstantPropagator::VisitCalculateElementAddress(
977 CalculateElementAddressInstr* instr) {
978 SetValue(instr, non_constant_);
979}
980
981void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) {
982 // This first part duplicates the work done in LoadClassIdInstr::Canonicalize,
983 // which replaces uses of LoadClassIdInstr where the object has a concrete
984 // type with a Constant. Canonicalize runs before the ConstantPropagation
985 // pass, so if that was all, this wouldn't be needed.
986 //
987 // However, the ConstantPropagator also runs as part of OptimizeBranches, and
988 // TypePropagation runs between it and the previous Canonicalize. Thus, the
989 // type may have become concrete and we should take that into account. Not
990 // doing so led to some benchmark regressions.
991 intptr_t cid = instr->object()->Type()->ToCid();
992 if (cid != kDynamicCid) {
993 SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
994 return;
995 }
996 const Object& object = instr->object()->definition()->constant_value();
997 if (IsConstant(object)) {
998 cid = object.GetClassId();
999 SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
1000 return;
1001 }
1002 SetValue(instr, non_constant_);
1003}
1004
1005void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) {
1006 Value* instance = instr->instance();
1007 if ((instr->slot().kind() == Slot::Kind::kArray_length) &&
1008 instance->definition()->OriginalDefinition()->IsCreateArray()) {
1009 Value* num_elements = instance->definition()
1010 ->OriginalDefinition()
1011 ->AsCreateArray()
1012 ->num_elements();
1013 if (num_elements->BindsToConstant() &&
1014 num_elements->BoundConstant().IsSmi()) {
1015 intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
1016 const Object& result = Smi::ZoneHandle(Z, Smi::New(length));
1017 SetValue(instr, result);
1018 return;
1019 }
1020 }
1021
1022 const Object& constant = instance->definition()->constant_value();
1023 if (IsConstant(constant)) {
1024 if (instr->IsImmutableLengthLoad()) {
1025 if (constant.IsString()) {
1026 SetValue(instr,
1027 Smi::ZoneHandle(Z, Smi::New(String::Cast(constant).Length())));
1028 return;
1029 }
1030 if (constant.IsArray()) {
1031 SetValue(instr,
1032 Smi::ZoneHandle(Z, Smi::New(Array::Cast(constant).Length())));
1033 return;
1034 }
1035 if (constant.IsTypedData()) {
1036 SetValue(instr, Smi::ZoneHandle(
1037 Z, Smi::New(TypedData::Cast(constant).Length())));
1038 return;
1039 }
1040 } else {
1041 Object& value = Object::Handle();
1042 if (instr->Evaluate(constant, &value)) {
1043 SetValue(instr, Object::ZoneHandle(Z, value.ptr()));
1044 return;
1045 }
1046 }
1047 }
1048
1049 SetValue(instr, non_constant_);
1050}
1051
1052void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) {
1053 TypeArguments& instantiator_type_args = TypeArguments::Handle(Z);
1054 TypeArguments& function_type_args = TypeArguments::Handle(Z);
1055 if (!instr->type().IsInstantiated(kCurrentClass)) {
1056 // Type refers to class type parameters.
1057 const Object& instantiator_type_args_obj =
1058 instr->instantiator_type_arguments()->definition()->constant_value();
1059 if (IsUnknown(instantiator_type_args_obj)) {
1060 return;
1061 }
1062 if (instantiator_type_args_obj.IsTypeArguments()) {
1063 instantiator_type_args ^= instantiator_type_args_obj.ptr();
1064 } else {
1065 SetValue(instr, non_constant_);
1066 return;
1067 }
1068 }
1069 if (!instr->type().IsInstantiated(kFunctions)) {
1070 // Type refers to function type parameters.
1071 const Object& function_type_args_obj =
1072 instr->function_type_arguments()->definition()->constant_value();
1073 if (IsUnknown(function_type_args_obj)) {
1074 return;
1075 }
1076 if (function_type_args_obj.IsTypeArguments()) {
1077 function_type_args ^= function_type_args_obj.ptr();
1078 } else {
1079 SetValue(instr, non_constant_);
1080 return;
1081 }
1082 }
1083 AbstractType& result = AbstractType::Handle(
1084 Z, instr->type().InstantiateFrom(
1085 instantiator_type_args, function_type_args, kAllFree, Heap::kOld));
1086 ASSERT(result.IsInstantiated());
1087 result = result.Canonicalize(T);
1088 SetValue(instr, result);
1089}
1090
1091void ConstantPropagator::VisitInstantiateTypeArguments(
1092 InstantiateTypeArgumentsInstr* instr) {
1093 const auto& type_arguments_obj =
1094 instr->type_arguments()->definition()->constant_value();
1095 if (IsUnknown(type_arguments_obj)) {
1096 return;
1097 }
1098 if (type_arguments_obj.IsNull()) {
1099 SetValue(instr, type_arguments_obj);
1100 return;
1101 }
1102 if (!type_arguments_obj.IsTypeArguments()) {
1103 SetValue(instr, non_constant_);
1104 return;
1105 }
1106 const auto& type_arguments = TypeArguments::Cast(type_arguments_obj);
1107 if (type_arguments.IsInstantiated()) {
1108 ASSERT(type_arguments.IsCanonical());
1109 SetValue(instr, type_arguments);
1110 return;
1111 }
1112 auto& instantiator_type_args = TypeArguments::Handle(Z);
1113 if (!type_arguments.IsInstantiated(kCurrentClass)) {
1114 // Type arguments refer to class type parameters.
1115 const Object& instantiator_type_args_obj =
1116 instr->instantiator_type_arguments()->definition()->constant_value();
1117 if (IsUnknown(instantiator_type_args_obj)) {
1118 return;
1119 }
1120 if (!instantiator_type_args_obj.IsNull() &&
1121 !instantiator_type_args_obj.IsTypeArguments()) {
1122 SetValue(instr, non_constant_);
1123 return;
1124 }
1125 instantiator_type_args ^= instantiator_type_args_obj.ptr();
1126 if (instr->CanShareInstantiatorTypeArguments()) {
1127 SetValue(instr, instantiator_type_args);
1128 return;
1129 }
1130 }
1131 auto& function_type_args = TypeArguments::Handle(Z);
1132 if (!type_arguments.IsInstantiated(kFunctions)) {
1133 // Type arguments refer to function type parameters.
1134 const Object& function_type_args_obj =
1135 instr->function_type_arguments()->definition()->constant_value();
1136 if (IsUnknown(function_type_args_obj)) {
1137 return;
1138 }
1139 if (!function_type_args_obj.IsNull() &&
1140 !function_type_args_obj.IsTypeArguments()) {
1141 SetValue(instr, non_constant_);
1142 return;
1143 }
1144 function_type_args ^= function_type_args_obj.ptr();
1145 if (instr->CanShareFunctionTypeArguments()) {
1146 SetValue(instr, function_type_args);
1147 return;
1148 }
1149 }
1151 Z, type_arguments.InstantiateFrom(
1152 instantiator_type_args, function_type_args, kAllFree, Heap::kOld));
1153 ASSERT(result.IsInstantiated());
1154 result = result.Canonicalize(T);
1155 SetValue(instr, result);
1156}
1157
1158void ConstantPropagator::VisitAllocateContext(AllocateContextInstr* instr) {
1159 SetValue(instr, non_constant_);
1160}
1161
1162void ConstantPropagator::VisitAllocateUninitializedContext(
1163 AllocateUninitializedContextInstr* instr) {
1164 SetValue(instr, non_constant_);
1165}
1166
1167void ConstantPropagator::VisitCloneContext(CloneContextInstr* instr) {
1168 SetValue(instr, non_constant_);
1169}
1170
1171void ConstantPropagator::VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op) {
1172 const Object& left = binary_op->left()->definition()->constant_value();
1173 const Object& right = binary_op->right()->definition()->constant_value();
1174 if (IsNonConstant(left) || IsNonConstant(right)) {
1175 SetValue(binary_op, non_constant_);
1176 return;
1177 } else if (IsUnknown(left) || IsUnknown(right)) {
1178 return;
1179 }
1180 ASSERT(IsConstant(left) && IsConstant(right));
1181 if (left.IsInteger() && right.IsInteger()) {
1182 const Integer& result = Integer::Handle(
1183 Z, Evaluator::BinaryIntegerEvaluate(left, right, binary_op->op_kind(),
1184 binary_op->is_truncating(),
1185 binary_op->representation(), T));
1186 if (!result.IsNull()) {
1187 SetValue(binary_op, Integer::ZoneHandle(Z, result.ptr()));
1188 return;
1189 }
1190 }
1191 SetValue(binary_op, non_constant_);
1192}
1193
1194void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) {
1195 VisitBinaryIntegerOp(instr);
1196}
1197
1198void ConstantPropagator::VisitBinaryInt32Op(BinaryInt32OpInstr* instr) {
1199 VisitBinaryIntegerOp(instr);
1200}
1201
1202void ConstantPropagator::VisitBinaryUint32Op(BinaryUint32OpInstr* instr) {
1203 VisitBinaryIntegerOp(instr);
1204}
1205
1206void ConstantPropagator::VisitBinaryInt64Op(BinaryInt64OpInstr* instr) {
1207 VisitBinaryIntegerOp(instr);
1208}
1209
1210void ConstantPropagator::VisitShiftInt64Op(ShiftInt64OpInstr* instr) {
1211 VisitBinaryIntegerOp(instr);
1212}
1213
1214void ConstantPropagator::VisitSpeculativeShiftInt64Op(
1215 SpeculativeShiftInt64OpInstr* instr) {
1216 VisitBinaryIntegerOp(instr);
1217}
1218
1219void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) {
1220 VisitBinaryIntegerOp(instr);
1221}
1222
1223void ConstantPropagator::VisitSpeculativeShiftUint32Op(
1224 SpeculativeShiftUint32OpInstr* instr) {
1225 VisitBinaryIntegerOp(instr);
1226}
1227
1228void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) {
1229 VisitBox(instr);
1230}
1231
1232void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) {
1233 VisitUnbox(instr);
1234}
1235
1236void ConstantPropagator::VisitHashDoubleOp(HashDoubleOpInstr* instr) {
1237 const Object& value = instr->value()->definition()->constant_value();
1238 if (IsUnknown(value)) {
1239 return;
1240 }
1241 if (value.IsDouble()) {
1242 // TODO(aam): Add constant hash evaluation
1243 }
1244 SetValue(instr, non_constant_);
1245}
1246
1247void ConstantPropagator::VisitHashIntegerOp(HashIntegerOpInstr* instr) {
1248 const Object& value = instr->value()->definition()->constant_value();
1249 if (IsUnknown(value)) {
1250 return;
1251 }
1252 if (value.IsInteger()) {
1253 // TODO(aam): Add constant hash evaluation
1254 }
1255 SetValue(instr, non_constant_);
1256}
1257
1258void ConstantPropagator::VisitUnaryIntegerOp(UnaryIntegerOpInstr* unary_op) {
1259 const Object& value = unary_op->value()->definition()->constant_value();
1260 if (IsUnknown(value)) {
1261 return;
1262 }
1263 if (value.IsInteger()) {
1264 const Integer& result = Integer::Handle(
1265 Z, Evaluator::UnaryIntegerEvaluate(value, unary_op->op_kind(),
1266 unary_op->representation(), T));
1267 if (!result.IsNull()) {
1268 SetValue(unary_op, Integer::ZoneHandle(Z, result.ptr()));
1269 return;
1270 }
1271 }
1272 SetValue(unary_op, non_constant_);
1273}
1274
1275void ConstantPropagator::VisitUnaryInt64Op(UnaryInt64OpInstr* instr) {
1276 VisitUnaryIntegerOp(instr);
1277}
1278
1279void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) {
1280 VisitUnaryIntegerOp(instr);
1281}
1282
1283static bool IsIntegerOrDouble(const Object& value) {
1284 return value.IsInteger() || value.IsDouble();
1285}
1286
1287static double ToDouble(const Object& value) {
1288 return value.IsInteger() ? Integer::Cast(value).AsDoubleValue()
1289 : Double::Cast(value).value();
1290}
1291
1292void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) {
1293 const Object& value = instr->value()->definition()->constant_value();
1294 if (IsUnknown(value)) {
1295 return;
1296 }
1297 if (value.IsDouble()) {
1298 const double result_val = Evaluator::EvaluateUnaryDoubleOp(
1299 ToDouble(value), instr->op_kind(), instr->representation());
1300 const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val));
1301 SetValue(instr, result);
1302 return;
1303 }
1304 SetValue(instr, non_constant_);
1305}
1306
1307void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) {
1308 const Object& value = instr->value()->definition()->constant_value();
1309 if (IsUnknown(value)) {
1310 return;
1311 }
1312 if (value.IsInteger()) {
1313 SetValue(instr,
1314 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1315 Heap::kOld)));
1316 } else {
1317 SetValue(instr, non_constant_);
1318 }
1319}
1320
1321void ConstantPropagator::VisitInt64ToDouble(Int64ToDoubleInstr* instr) {
1322 const Object& value = instr->value()->definition()->constant_value();
1323 if (IsUnknown(value)) {
1324 return;
1325 }
1326 if (value.IsInteger()) {
1327 SetValue(instr,
1328 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1329 Heap::kOld)));
1330 } else {
1331 SetValue(instr, non_constant_);
1332 }
1333}
1334
1335void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) {
1336 const Object& value = instr->value()->definition()->constant_value();
1337 if (IsUnknown(value)) {
1338 return;
1339 }
1340 if (value.IsInteger()) {
1341 SetValue(instr,
1342 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1343 Heap::kOld)));
1344 } else {
1345 SetValue(instr, non_constant_);
1346 }
1347}
1348
1349void ConstantPropagator::VisitDoubleToInteger(DoubleToIntegerInstr* instr) {
1350 // TODO(kmillikin): Handle conversion.
1351 SetValue(instr, non_constant_);
1352}
1353
1354void ConstantPropagator::VisitDoubleToSmi(DoubleToSmiInstr* instr) {
1355 // TODO(kmillikin): Handle conversion.
1356 SetValue(instr, non_constant_);
1357}
1358
1359void ConstantPropagator::VisitDoubleToFloat(DoubleToFloatInstr* instr) {
1360 // TODO(kmillikin): Handle conversion.
1361 SetValue(instr, non_constant_);
1362}
1363
1364void ConstantPropagator::VisitFloatToDouble(FloatToDoubleInstr* instr) {
1365 // TODO(kmillikin): Handle conversion.
1366 SetValue(instr, non_constant_);
1367}
1368
1369void ConstantPropagator::VisitFloatCompare(FloatCompareInstr* instr) {
1370 // TODO(riscv)
1371 SetValue(instr, non_constant_);
1372}
1373
1374void ConstantPropagator::VisitInvokeMathCFunction(
1375 InvokeMathCFunctionInstr* instr) {
1376 // TODO(kmillikin): Handle conversion.
1377 SetValue(instr, non_constant_);
1378}
1379
1380void ConstantPropagator::VisitTruncDivMod(TruncDivModInstr* instr) {
1381 // TODO(srdjan): Handle merged instruction.
1382 SetValue(instr, non_constant_);
1383}
1384
1385void ConstantPropagator::VisitExtractNthOutput(ExtractNthOutputInstr* instr) {
1386 SetValue(instr, non_constant_);
1387}
1388
1389void ConstantPropagator::VisitMakePair(MakePairInstr* instr) {
1390 SetValue(instr, non_constant_);
1391}
1392
1393void ConstantPropagator::VisitUnboxLane(UnboxLaneInstr* instr) {
1394 if (BoxLanesInstr* box = instr->value()->definition()->AsBoxLanes()) {
1395 const Object& value =
1396 box->InputAt(instr->lane())->definition()->constant_value();
1397 if (IsUnknown(value)) {
1398 return;
1399 }
1400 SetValue(instr, value);
1401 return;
1402 }
1403
1404 SetValue(instr, non_constant_);
1405}
1406
1407void ConstantPropagator::VisitBoxLanes(BoxLanesInstr* instr) {
1408 // TODO(riscv)
1409 SetValue(instr, non_constant_);
1410}
1411
1412void ConstantPropagator::VisitConstant(ConstantInstr* instr) {
1413 SetValue(instr, instr->value());
1414}
1415
1416void ConstantPropagator::VisitUnboxedConstant(UnboxedConstantInstr* instr) {
1417 SetValue(instr, instr->value());
1418}
1419
1420void ConstantPropagator::VisitConstraint(ConstraintInstr* instr) {
1421 // Should not be used outside of range analysis.
1422 UNREACHABLE();
1423}
1424
1425void ConstantPropagator::VisitMaterializeObject(MaterializeObjectInstr* instr) {
1426 // Should not be used outside of allocation elimination pass.
1427 UNREACHABLE();
1428}
1429
1430void ConstantPropagator::VisitBinaryDoubleOp(BinaryDoubleOpInstr* instr) {
1431 const Object& left = instr->left()->definition()->constant_value();
1432 const Object& right = instr->right()->definition()->constant_value();
1433 if (IsNonConstant(left) || IsNonConstant(right)) {
1434 SetValue(instr, non_constant_);
1435 return;
1436 } else if (IsUnknown(left) || IsUnknown(right)) {
1437 return;
1438 }
1439 ASSERT(IsConstant(left) && IsConstant(right));
1440 const bool both_are_integers = left.IsInteger() && right.IsInteger();
1441 if (IsIntegerOrDouble(left) && IsIntegerOrDouble(right) &&
1442 !both_are_integers) {
1443 const double result_val = Evaluator::EvaluateBinaryDoubleOp(
1444 ToDouble(left), ToDouble(right), instr->op_kind(),
1445 instr->representation());
1446 const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val));
1447 SetValue(instr, result);
1448 return;
1449 }
1450 SetValue(instr, non_constant_);
1451}
1452
1453void ConstantPropagator::VisitDoubleTestOp(DoubleTestOpInstr* instr) {
1454 const Object& value = instr->value()->definition()->constant_value();
1455 if (IsUnknown(value)) {
1456 return;
1457 }
1458 bool result;
1459 if (value.IsInteger()) {
1460 switch (instr->op_kind()) {
1461 case MethodRecognizer::kDouble_getIsNaN:
1463 case MethodRecognizer::kDouble_getIsInfinite:
1464 result = false;
1465 break;
1466 case MethodRecognizer::kDouble_getIsNegative: {
1467 result = Integer::Cast(value).IsNegative();
1468 break;
1469 }
1470 default:
1471 UNREACHABLE();
1472 }
1473 } else if (value.IsDouble()) {
1474 const double double_value = ToDouble(value);
1475 switch (instr->op_kind()) {
1476 case MethodRecognizer::kDouble_getIsNaN: {
1477 result = isnan(double_value);
1478 break;
1479 }
1480 case MethodRecognizer::kDouble_getIsInfinite: {
1481 result = isinf(double_value);
1482 break;
1483 }
1484 case MethodRecognizer::kDouble_getIsNegative: {
1485 result = signbit(double_value) && !isnan(double_value);
1486 break;
1487 }
1488 default:
1489 UNREACHABLE();
1490 }
1491 } else {
1492 SetValue(instr, non_constant_);
1493 return;
1494 }
1495 const bool is_negated = instr->kind() != Token::kEQ;
1496 SetValue(instr, Bool::Get(is_negated ? !result : result));
1497}
1498
1499void ConstantPropagator::VisitSimdOp(SimdOpInstr* instr) {
1500 SetValue(instr, non_constant_);
1501}
1502
1503void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* instr) {
1504 // TODO(srdjan): Handle min and max.
1505 SetValue(instr, non_constant_);
1506}
1507
1508void ConstantPropagator::VisitCaseInsensitiveCompare(
1509 CaseInsensitiveCompareInstr* instr) {
1510 SetValue(instr, non_constant_);
1511}
1512
1513void ConstantPropagator::VisitUnbox(UnboxInstr* instr) {
1514 const Object& value = instr->value()->definition()->constant_value();
1515 if (IsUnknown(value)) {
1516 return;
1517 }
1518
1519 SetValue(instr, value);
1520}
1521
1522void ConstantPropagator::VisitBox(BoxInstr* instr) {
1523 const Object& value = instr->value()->definition()->constant_value();
1524 if (IsUnknown(value)) {
1525 return;
1526 }
1527
1528 if (instr->value()->definition()->representation() ==
1529 instr->from_representation()) {
1530 SetValue(instr, value);
1531 } else {
1532 SetValue(instr, non_constant_);
1533 }
1534}
1535
1536void ConstantPropagator::VisitBoxSmallInt(BoxSmallIntInstr* instr) {
1537 VisitBox(instr);
1538}
1539
1540void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) {
1541 VisitBox(instr);
1542}
1543
1544void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) {
1545 VisitUnbox(instr);
1546}
1547
1548void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) {
1549 VisitBox(instr);
1550}
1551
1552void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) {
1553 VisitUnbox(instr);
1554}
1555
1556void ConstantPropagator::VisitIntConverter(IntConverterInstr* instr) {
1557 SetValue(instr, non_constant_);
1558}
1559
1560void ConstantPropagator::VisitBitCast(BitCastInstr* instr) {
1561 SetValue(instr, non_constant_);
1562}
1563
1564void ConstantPropagator::VisitCall1ArgStub(Call1ArgStubInstr* instr) {
1565 SetValue(instr, non_constant_);
1566}
1567
1568void ConstantPropagator::VisitSuspend(SuspendInstr* instr) {
1569 SetValue(instr, non_constant_);
1570}
1571
1572void ConstantPropagator::VisitLoadThread(LoadThreadInstr* instr) {
1573 SetValue(instr, non_constant_);
1574}
1575
1576void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) {
1577 // TODO(kmillikin): Handle unary operations.
1578 SetValue(instr, non_constant_);
1579}
1580
1581// Insert redefinition for |original| definition which conveys information
1582// that |original| is equal to |constant_value| in the dominated code.
1585 Definition* original,
1586 const Object& constant_value) {
1587 auto redef = new RedefinitionInstr(new Value(original),
1588 /*inserted_by_constant_propagation=*/true);
1589
1590 graph->InsertAfter(dom, redef, nullptr, FlowGraph::kValue);
1591 graph->RenameDominatedUses(original, redef, redef);
1592
1593 if (redef->input_use_list() == nullptr) {
1594 // There are no dominated uses, so the newly added Redefinition is useless.
1595 redef->RemoveFromGraph();
1596 return nullptr;
1597 }
1598
1599 redef->constant_value() = constant_value.ptr();
1600 return redef;
1601}
1602
1603// Find all Branch(v eq constant) (eq being one of ==, !=, === or !==) in the
1604// graph and redefine |v| in the true successor to record information about
1605// it being equal to the constant. For comparisons between boolean values
1606// we also redefine |v| in the false successor - because booleans have
1607// only two possible values (e.g. if |v| is |true| in true successor, then
1608// it is |false| in false successor).
1609//
1610// We don't actually _replace_ |v| with |constant| in the dominated code
1611// because it might complicate subsequent optimizations (e.g. lead to
1612// redundant phis).
1613void ConstantPropagator::InsertRedefinitionsAfterEqualityComparisons() {
1614 for (auto block : graph_->reverse_postorder()) {
1615 if (auto branch = block->last_instruction()->AsBranch()) {
1616 auto comparison = branch->comparison();
1617 if (comparison->IsStrictCompare() ||
1618 (comparison->IsEqualityCompare() &&
1619 comparison->operation_cid() != kDoubleCid)) {
1620 Value* value;
1621 ConstantInstr* constant_defn;
1622 if (comparison->IsComparisonWithConstant(&value, &constant_defn) &&
1623 !value->BindsToConstant()) {
1624 const Object& constant_value = constant_defn->value();
1625
1626 // Found comparison with constant. Introduce Redefinition().
1627 ASSERT(comparison->kind() == Token::kNE_STRICT ||
1628 comparison->kind() == Token::kNE ||
1629 comparison->kind() == Token::kEQ_STRICT ||
1630 comparison->kind() == Token::kEQ);
1631 const bool negated = (comparison->kind() == Token::kNE_STRICT ||
1632 comparison->kind() == Token::kNE);
1633 const auto true_successor =
1634 negated ? branch->false_successor() : branch->true_successor();
1635 InsertRedefinition(graph_, true_successor, value->definition(),
1636 constant_value);
1637
1638 // When comparing two boolean values we can also apply renaming
1639 // to the false successor because we know that only true and false
1640 // are possible values.
1641 if (constant_value.IsBool() && value->Type()->IsBool()) {
1642 const auto false_successor =
1643 negated ? branch->true_successor() : branch->false_successor();
1644 InsertRedefinition(graph_, false_successor, value->definition(),
1645 Bool::Get(!Bool::Cast(constant_value).value()));
1646 }
1647 }
1648 }
1649 }
1650 }
1651}
1652
1653void ConstantPropagator::Analyze() {
1654 InsertRedefinitionsAfterEqualityComparisons();
1655
1656 GraphEntryInstr* entry = graph_->graph_entry();
1657 reachable_->Add(entry->preorder_number());
1658 block_worklist_.Add(entry);
1659
1660 while (true) {
1661 if (block_worklist_.is_empty()) {
1662 if (definition_worklist_.IsEmpty()) break;
1663 Definition* definition = definition_worklist_.RemoveLast();
1664 for (Value* use = definition->input_use_list(); use != nullptr;
1665 use = use->next_use()) {
1666 use->instruction()->Accept(this);
1667 }
1668 } else {
1669 BlockEntryInstr* block = block_worklist_.RemoveLast();
1670 block->Accept(this);
1671 }
1672 }
1673}
1674
1675static bool HasPhis(BlockEntryInstr* block) {
1676 if (auto* join = block->AsJoinEntry()) {
1677 return (join->phis() != nullptr) && !join->phis()->is_empty();
1678 }
1679 return false;
1680}
1681
1682static bool IsEmptyBlock(BlockEntryInstr* block) {
1683 // A block containing a goto to itself forms an infinite loop.
1684 // We don't consider this an empty block to handle the edge-case where code
1685 // reduces to an infinite loop.
1686 return block->next()->IsGoto() &&
1687 block->next()->AsGoto()->successor() != block && !HasPhis(block) &&
1688 !block->IsIndirectEntry();
1689}
1690
1691// Traverses a chain of empty blocks and returns the first reachable non-empty
1692// block that is not dominated by the start block. The empty blocks are added
1693// to the supplied bit vector.
1695 BitVector* empty_blocks) {
1696 BlockEntryInstr* current = block;
1697 while (IsEmptyBlock(current) && block->Dominates(current)) {
1698 ASSERT(!HasPhis(block));
1699 empty_blocks->Add(current->preorder_number());
1700 current = current->next()->AsGoto()->successor();
1701 }
1702 return current;
1703}
1704
1705void ConstantPropagator::EliminateRedundantBranches() {
1706 // Canonicalize branches that have no side-effects and where true- and
1707 // false-targets are the same.
1708 bool changed = false;
1709 BitVector* empty_blocks = new (Z) BitVector(Z, graph_->preorder().length());
1710 for (BlockIterator b = graph_->postorder_iterator(); !b.Done(); b.Advance()) {
1711 BlockEntryInstr* block = b.Current();
1712 BranchInstr* branch = block->last_instruction()->AsBranch();
1713 empty_blocks->Clear();
1714 if ((branch != nullptr) && !branch->HasUnknownSideEffects()) {
1715 ASSERT(branch->previous() != nullptr); // Not already eliminated.
1716 BlockEntryInstr* if_true =
1717 FindFirstNonEmptySuccessor(branch->true_successor(), empty_blocks);
1718 BlockEntryInstr* if_false =
1719 FindFirstNonEmptySuccessor(branch->false_successor(), empty_blocks);
1720 if (if_true == if_false) {
1721 // Replace the branch with a jump to the common successor.
1722 // Drop the comparison, which does not have side effects
1723 JoinEntryInstr* join = if_true->AsJoinEntry();
1724 if (!HasPhis(join)) {
1725 GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone);
1726 graph_->CopyDeoptTarget(jump, branch);
1727
1728 Instruction* previous = branch->previous();
1729 branch->set_previous(nullptr);
1730 previous->LinkTo(jump);
1731
1732 // Remove uses from branch and all the empty blocks that
1733 // are now unreachable.
1734 branch->UnuseAllInputs();
1735 for (BitVector::Iterator it(empty_blocks); !it.Done(); it.Advance()) {
1736 BlockEntryInstr* empty_block = graph_->preorder()[it.Current()];
1737 empty_block->ClearAllInstructions();
1738 }
1739
1740 changed = true;
1741
1742 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1743 THR_Print("Eliminated branch in B%" Pd " common target B%" Pd "\n",
1744 block->block_id(), join->block_id());
1745 }
1746 }
1747 }
1748 }
1749 }
1750
1751 if (changed) {
1752 graph_->DiscoverBlocks();
1753 graph_->MergeBlocks();
1754 // TODO(fschneider): Update dominator tree in place instead of recomputing.
1755 GrowableArray<BitVector*> dominance_frontier;
1756 graph_->ComputeDominators(&dominance_frontier);
1757 }
1758}
1759
1760void ConstantPropagator::Transform() {
1761 // We will recompute dominators, block ordering, block ids, block last
1762 // instructions, previous pointers, predecessors, etc. after eliminating
1763 // unreachable code. We do not maintain those properties during the
1764 // transformation.
1765 for (BlockIterator b = graph_->reverse_postorder_iterator(); !b.Done();
1766 b.Advance()) {
1767 BlockEntryInstr* block = b.Current();
1768 if (!reachable_->Contains(block->preorder_number())) {
1769 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1770 THR_Print("Unreachable B%" Pd "\n", block->block_id());
1771 }
1772 // Remove all uses in unreachable blocks.
1773 block->ClearAllInstructions();
1774 continue;
1775 }
1776
1777 JoinEntryInstr* join = block->AsJoinEntry();
1778 if (join != nullptr) {
1779 // Remove phi inputs corresponding to unreachable predecessor blocks.
1780 // Predecessors will be recomputed (in block id order) after removing
1781 // unreachable code so we merely have to keep the phi inputs in order.
1782 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
1783 if ((phis != nullptr) && !phis->is_empty()) {
1784 intptr_t pred_count = join->PredecessorCount();
1785 intptr_t live_count = 0;
1786 for (intptr_t pred_idx = 0; pred_idx < pred_count; ++pred_idx) {
1787 if (reachable_->Contains(
1788 join->PredecessorAt(pred_idx)->preorder_number())) {
1789 if (live_count < pred_idx) {
1790 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1791 PhiInstr* phi = it.Current();
1792 ASSERT(phi != nullptr);
1793 phi->SetInputAt(live_count, phi->InputAt(pred_idx));
1794 }
1795 }
1796 ++live_count;
1797 } else {
1798 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1799 PhiInstr* phi = it.Current();
1800 ASSERT(phi != nullptr);
1801 phi->InputAt(pred_idx)->RemoveFromUseList();
1802 }
1803 }
1804 }
1805 if (live_count < pred_count) {
1806 intptr_t to_idx = 0;
1807 for (intptr_t from_idx = 0; from_idx < phis->length(); ++from_idx) {
1808 PhiInstr* phi = (*phis)[from_idx];
1809 ASSERT(phi != nullptr);
1810 if (FLAG_remove_redundant_phis && (live_count == 1)) {
1811 Value* input = phi->InputAt(0);
1812 phi->ReplaceUsesWith(input->definition());
1813 input->RemoveFromUseList();
1814 } else {
1815 phi->inputs_.TruncateTo(live_count);
1816 (*phis)[to_idx++] = phi;
1817 }
1818 }
1819 if (to_idx == 0) {
1820 join->phis_ = nullptr;
1821 } else {
1822 phis->TruncateTo(to_idx);
1823 }
1824 }
1825 }
1826 }
1827
1828 if (join != nullptr) {
1829 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1830 auto phi = it.Current();
1831 if (TransformDefinition(phi)) {
1832 it.RemoveCurrentFromGraph();
1833 }
1834 }
1835 }
1836 for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) {
1837 Definition* defn = i.Current()->AsDefinition();
1838 if (TransformDefinition(defn)) {
1839 i.RemoveCurrentFromGraph();
1840 }
1841 }
1842
1843 // Replace branches where one target is unreachable with jumps.
1844 BranchInstr* branch = block->last_instruction()->AsBranch();
1845 if (branch != nullptr) {
1846 TargetEntryInstr* if_true = branch->true_successor();
1847 TargetEntryInstr* if_false = branch->false_successor();
1848 JoinEntryInstr* join = nullptr;
1849 Instruction* next = nullptr;
1850
1851 if (!reachable_->Contains(if_true->preorder_number())) {
1852 ASSERT(reachable_->Contains(if_false->preorder_number()));
1853 ASSERT(if_false->parallel_move() == nullptr);
1854 join = new (Z) JoinEntryInstr(if_false->block_id(),
1855 if_false->try_index(), DeoptId::kNone);
1856 graph_->CopyDeoptTarget(join, if_false);
1857 if_false->UnuseAllInputs();
1858 next = if_false->next();
1859 } else if (!reachable_->Contains(if_false->preorder_number())) {
1860 ASSERT(if_true->parallel_move() == nullptr);
1861 join = new (Z) JoinEntryInstr(if_true->block_id(), if_true->try_index(),
1863 graph_->CopyDeoptTarget(join, if_true);
1864 if_true->UnuseAllInputs();
1865 next = if_true->next();
1866 }
1867
1868 if (join != nullptr) {
1869 // Replace the branch with a jump to the reachable successor.
1870 // Drop the comparison, which does not have side effects as long
1871 // as it is a strict compare (the only one we can determine is
1872 // constant with the current analysis).
1873 GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone);
1874 graph_->CopyDeoptTarget(jump, branch);
1875
1876 Instruction* previous = branch->previous();
1877 branch->set_previous(nullptr);
1878 previous->LinkTo(jump);
1879
1880 // Replace the false target entry with the new join entry. We will
1881 // recompute the dominators after this pass.
1882 join->LinkTo(next);
1883 branch->UnuseAllInputs();
1884 }
1885 }
1886 }
1887
1888 graph_->DiscoverBlocks();
1889 graph_->MergeBlocks();
1890 GrowableArray<BitVector*> dominance_frontier;
1891 graph_->ComputeDominators(&dominance_frontier);
1892}
1893
1894bool ConstantPropagator::TransformDefinition(Definition* defn) {
1895 if (defn == nullptr) {
1896 return false;
1897 }
1898
1899 if (auto redef = defn->AsRedefinition()) {
1900 if (redef->inserted_by_constant_propagation()) {
1901 redef->ReplaceUsesWith(redef->value()->definition());
1902 return true;
1903 }
1904
1905 if (IsConstant(defn->constant_value()) &&
1906 !IsConstant(defn->OriginalDefinition()->constant_value())) {
1907 // Redefinition might have become constant because some other
1908 // redefinition narrowed it, we should ignore this and not
1909 // replace it.
1910 return false;
1911 }
1912 }
1913
1914 // Replace constant-valued instructions without observable side
1915 // effects. Do this for smis and old objects only to avoid having to
1916 // copy other objects into the heap's old generation.
1917 if (IsConstant(defn->constant_value()) &&
1918 (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) &&
1919 !defn->IsConstant() && !defn->IsStoreIndexed() && !defn->IsStoreField() &&
1920 !defn->IsStoreStaticField()) {
1921 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1922 THR_Print("Constant v%" Pd " = %s\n", defn->ssa_temp_index(),
1923 defn->constant_value().ToCString());
1924 }
1925 constant_value_ = defn->constant_value().ptr();
1926 if ((constant_value_.IsString() || constant_value_.IsMint() ||
1927 constant_value_.IsDouble()) &&
1928 !constant_value_.IsCanonical()) {
1929 constant_value_ = Instance::Cast(constant_value_).Canonicalize(T);
1930 ASSERT(!constant_value_.IsNull());
1931 }
1932 if (auto call = defn->AsStaticCall()) {
1933 ASSERT(!call->HasMoveArguments());
1934 }
1935 Definition* replacement =
1936 graph_->TryCreateConstantReplacementFor(defn, constant_value_);
1937 if (replacement != defn) {
1938 defn->ReplaceUsesWith(replacement);
1939 return true;
1940 }
1941 }
1942 return false;
1943}
1944
1945} // namespace dart
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static float next(float f)
SI F table(const skcms_Curve *curve, F v)
#define UNREACHABLE()
Definition: assert.h:248
void Add(const T &value)
intptr_t length() const
void Add(intptr_t i)
Definition: bit_vector.h:63
bool Contains(intptr_t i) const
Definition: bit_vector.h:91
void Remove(intptr_t i)
Definition: bit_vector.h:68
intptr_t preorder_number() const
Definition: il.h:1655
bool Dominates(BlockEntryInstr *other) const
Definition: il.cc:1815
static const Bool & False()
Definition: object.h:10799
static const Bool & Get(bool value)
Definition: object.h:10801
static const Bool & True()
Definition: object.h:10797
static void Optimize(FlowGraph *graph)
static ObjectPtr Unknown()
ConstantPropagator(FlowGraph *graph, const GrowableArray< BlockEntryInstr * > &ignored)
static void OptimizeBranches(FlowGraph *graph)
void Add(Definition *defn)
Definition: flow_graph.h:829
Definition * RemoveLast()
Definition: flow_graph.h:843
void ReplaceUsesWith(Definition *other)
Definition: il.cc:1493
static constexpr intptr_t kNone
Definition: deopt_id.h:27
static DoublePtr New(double d, Heap::Space space=Heap::kNew)
Definition: object.cc:23402
static DoublePtr NewCanonical(double d)
Definition: object.cc:23418
static double EvaluateUnaryDoubleOp(const double value, Token::Kind token_kind, Representation representation)
Definition: evaluator.cc:192
static IntegerPtr UnaryIntegerEvaluate(const Object &value, Token::Kind token_kind, Representation representation, Thread *thread)
Definition: evaluator.cc:135
static IntegerPtr BinaryIntegerEvaluate(const Object &left, const Object &right, Token::Kind token_kind, bool is_truncating, Representation representation, Thread *thread)
Definition: evaluator.cc:99
static double EvaluateBinaryDoubleOp(const double left, const double right, Token::Kind token_kind, Representation representation)
Definition: evaluator.cc:238
static void PrintGraph(const char *phase, FlowGraph *flow_graph)
Definition: il_printer.cc:1706
const GrowableArray< BlockEntryInstr * > & reverse_postorder() const
Definition: flow_graph.h:207
GraphEntryInstr * graph_entry() const
Definition: flow_graph.h:268
bool should_print() const
Definition: flow_graph.h:503
const GrowableArray< BlockEntryInstr * > & preorder() const
Definition: flow_graph.h:203
BlockIterator postorder_iterator() const
Definition: flow_graph.h:222
void DiscoverBlocks()
Definition: flow_graph.cc:346
void ComputeDominators(GrowableArray< BitVector * > *dominance_frontier)
Definition: flow_graph.cc:975
const ParsedFunction & parsed_function() const
Definition: flow_graph.h:129
Definition * TryCreateConstantReplacementFor(Definition *op, const Object &value)
Definition: flow_graph.cc:236
void CopyDeoptTarget(Instruction *to, Instruction *from)
Definition: flow_graph.h:395
BlockIterator reverse_postorder_iterator() const
Definition: flow_graph.h:219
static void RenameDominatedUses(Definition *def, Instruction *dom, Definition *other)
Definition: flow_graph.cc:2642
void MergeBlocks()
Definition: flow_graph.cc:389
void InsertAfter(Instruction *prev, Instruction *instr, Environment *env, UseKind use_kind)
Definition: flow_graph.cc:273
@ kOld
Definition: heap.h:39
virtual void Accept(InstructionVisitor *visitor)=0
Instruction * next() const
Definition: il.h:1093
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
ObjectPtr ptr() const
Definition: object.h:332
bool IsCanonical() const
Definition: object.h:335
virtual const char * ToCString() const
Definition: object.h:366
bool IsNull() const
Definition: object.h:363
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
const Function & function() const
Definition: parser.h:73
static SmiPtr New(intptr_t value)
Definition: object.h:10006
@ kMaxOneCharCodeSymbol
Definition: symbols.h:577
static StringPtr * PredefinedAddress()
Definition: symbols.h:772
Definition: il.h:75
#define T
#define Z
#define THR_Print(format,...)
Definition: log.h:20
#define ASSERT(E)
VkInstance instance
Definition: main.cc:48
static bool b
struct MyStruct a[10]
#define FATAL(error)
uint8_t value
GAsyncResult * result
size_t length
Definition: dart_vm.cc:33
static bool IsIdenticalConstants(const Object &left, const Object &right)
static double ToDouble(const Object &value)
static bool CompareIntegers(Token::Kind kind, const Integer &left, const Integer &right)
static RedefinitionInstr * InsertRedefinition(FlowGraph *graph, BlockEntryInstr *dom, Definition *original, const Object &constant_value)
@ kDynamicCid
Definition: class_id.h:253
Representation
Definition: locations.h:66
static bool HasPhis(BlockEntryInstr *block)
uintptr_t uword
Definition: globals.h:501
static BlockEntryInstr * FindFirstNonEmptySuccessor(TargetEntryInstr *block, BitVector *empty_blocks)
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
const intptr_t cid
static bool IsEmptyBlock(BlockEntryInstr *block)
@ kFunctions
Definition: object.h:2251
@ kCurrentClass
Definition: object.h:2250
bool IsIntegerClassId(intptr_t index)
Definition: class_id.h:340
static bool IsIntegerOrDouble(const Object &value)
NOT_IN_PRODUCT(LibraryPtr ReloadTestScript(const char *script))
@ kAllFree
Definition: object.h:2940
Definition: dom.py:1
def call(args)
Definition: dom.py:159
#define FALL_THROUGH
Definition: globals.h:15
#define Pd
Definition: globals.h:408
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
const uintptr_t id