Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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)) {
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::VisitTestSmi(TestSmiInstr* 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) &&
921 (checked_type.IsIntType() && (rep == kUnboxedInt64))) {
922 // Ensure that compile time type matches representation.
923 ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) ||
924 ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) ||
925 ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) ||
926 ((rep == kUnboxedInt64) && (value_cid == kMintCid)));
927 // The representation guarantees the type check to be true.
928 SetValue(instr, Bool::True());
929 } else {
930 SetValue(instr, non_constant_);
931 }
932 } else if (IsConstant(value)) {
933 if (value.IsInstance() && (value.ptr() != Object::sentinel().ptr())) {
934 const Instance& instance = Instance::Cast(value);
935 if (instr->instantiator_type_arguments()->BindsToConstantNull() &&
936 instr->function_type_arguments()->BindsToConstantNull()) {
937 bool is_instance =
938 instance.IsInstanceOf(checked_type, Object::null_type_arguments(),
939 Object::null_type_arguments());
940 SetValue(instr, Bool::Get(is_instance));
941 return;
942 }
943 }
944 SetValue(instr, non_constant_);
945 }
946}
947
948void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) {
949 SetValue(instr, non_constant_);
950}
951
952void ConstantPropagator::VisitAllocateTypedData(AllocateTypedDataInstr* instr) {
953 SetValue(instr, non_constant_);
954}
955
956void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) {
957 SetValue(instr, non_constant_);
958}
959
960void ConstantPropagator::VisitAllocateClosure(AllocateClosureInstr* instr) {
961 SetValue(instr, non_constant_);
962}
963
964void ConstantPropagator::VisitAllocateRecord(AllocateRecordInstr* instr) {
965 SetValue(instr, non_constant_);
966}
967
968void ConstantPropagator::VisitAllocateSmallRecord(
969 AllocateSmallRecordInstr* instr) {
970 SetValue(instr, non_constant_);
971}
972
973void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) {
974 SetValue(instr, non_constant_);
975}
976
977void ConstantPropagator::VisitCalculateElementAddress(
978 CalculateElementAddressInstr* instr) {
979 SetValue(instr, non_constant_);
980}
981
982void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) {
983 // This first part duplicates the work done in LoadClassIdInstr::Canonicalize,
984 // which replaces uses of LoadClassIdInstr where the object has a concrete
985 // type with a Constant. Canonicalize runs before the ConstantPropagation
986 // pass, so if that was all, this wouldn't be needed.
987 //
988 // However, the ConstantPropagator also runs as part of OptimizeBranches, and
989 // TypePropagation runs between it and the previous Canonicalize. Thus, the
990 // type may have become concrete and we should take that into account. Not
991 // doing so led to some benchmark regressions.
992 intptr_t cid = instr->object()->Type()->ToCid();
993 if (cid != kDynamicCid) {
994 SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
995 return;
996 }
997 const Object& object = instr->object()->definition()->constant_value();
998 if (IsConstant(object)) {
999 cid = object.GetClassId();
1000 SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid)));
1001 return;
1002 }
1003 SetValue(instr, non_constant_);
1004}
1005
1006void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) {
1007 Value* instance = instr->instance();
1008 if ((instr->slot().kind() == Slot::Kind::kArray_length) &&
1009 instance->definition()->OriginalDefinition()->IsCreateArray()) {
1010 Value* num_elements = instance->definition()
1011 ->OriginalDefinition()
1012 ->AsCreateArray()
1013 ->num_elements();
1014 if (num_elements->BindsToConstant() &&
1015 num_elements->BoundConstant().IsSmi()) {
1016 intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
1017 const Object& result = Smi::ZoneHandle(Z, Smi::New(length));
1018 SetValue(instr, result);
1019 return;
1020 }
1021 }
1022
1023 const Object& constant = instance->definition()->constant_value();
1024 if (IsConstant(constant)) {
1025 if (instr->IsImmutableLengthLoad()) {
1026 if (constant.IsString()) {
1027 SetValue(instr,
1028 Smi::ZoneHandle(Z, Smi::New(String::Cast(constant).Length())));
1029 return;
1030 }
1031 if (constant.IsArray()) {
1032 SetValue(instr,
1033 Smi::ZoneHandle(Z, Smi::New(Array::Cast(constant).Length())));
1034 return;
1035 }
1036 if (constant.IsTypedData()) {
1037 SetValue(instr, Smi::ZoneHandle(
1038 Z, Smi::New(TypedData::Cast(constant).Length())));
1039 return;
1040 }
1041 } else {
1042 Object& value = Object::Handle();
1043 if (instr->Evaluate(constant, &value)) {
1044 SetValue(instr, Object::ZoneHandle(Z, value.ptr()));
1045 return;
1046 }
1047 }
1048 }
1049
1050 SetValue(instr, non_constant_);
1051}
1052
1053void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) {
1054 TypeArguments& instantiator_type_args = TypeArguments::Handle(Z);
1055 TypeArguments& function_type_args = TypeArguments::Handle(Z);
1056 if (!instr->type().IsInstantiated(kCurrentClass)) {
1057 // Type refers to class type parameters.
1058 const Object& instantiator_type_args_obj =
1059 instr->instantiator_type_arguments()->definition()->constant_value();
1060 if (IsUnknown(instantiator_type_args_obj)) {
1061 return;
1062 }
1063 if (instantiator_type_args_obj.IsTypeArguments()) {
1064 instantiator_type_args ^= instantiator_type_args_obj.ptr();
1065 } else {
1066 SetValue(instr, non_constant_);
1067 return;
1068 }
1069 }
1070 if (!instr->type().IsInstantiated(kFunctions)) {
1071 // Type refers to function type parameters.
1072 const Object& function_type_args_obj =
1073 instr->function_type_arguments()->definition()->constant_value();
1074 if (IsUnknown(function_type_args_obj)) {
1075 return;
1076 }
1077 if (function_type_args_obj.IsTypeArguments()) {
1078 function_type_args ^= function_type_args_obj.ptr();
1079 } else {
1080 SetValue(instr, non_constant_);
1081 return;
1082 }
1083 }
1084 AbstractType& result = AbstractType::Handle(
1085 Z, instr->type().InstantiateFrom(
1086 instantiator_type_args, function_type_args, kAllFree, Heap::kOld));
1087 ASSERT(result.IsInstantiated());
1088 result = result.Canonicalize(T);
1089 SetValue(instr, result);
1090}
1091
1092void ConstantPropagator::VisitInstantiateTypeArguments(
1093 InstantiateTypeArgumentsInstr* instr) {
1094 const auto& type_arguments_obj =
1095 instr->type_arguments()->definition()->constant_value();
1096 if (IsUnknown(type_arguments_obj)) {
1097 return;
1098 }
1099 if (type_arguments_obj.IsNull()) {
1100 SetValue(instr, type_arguments_obj);
1101 return;
1102 }
1103 if (!type_arguments_obj.IsTypeArguments()) {
1104 SetValue(instr, non_constant_);
1105 return;
1106 }
1107 const auto& type_arguments = TypeArguments::Cast(type_arguments_obj);
1108 if (type_arguments.IsInstantiated()) {
1109 ASSERT(type_arguments.IsCanonical());
1110 SetValue(instr, type_arguments);
1111 return;
1112 }
1113 auto& instantiator_type_args = TypeArguments::Handle(Z);
1114 if (!type_arguments.IsInstantiated(kCurrentClass)) {
1115 // Type arguments refer to class type parameters.
1116 const Object& instantiator_type_args_obj =
1117 instr->instantiator_type_arguments()->definition()->constant_value();
1118 if (IsUnknown(instantiator_type_args_obj)) {
1119 return;
1120 }
1121 if (!instantiator_type_args_obj.IsNull() &&
1122 !instantiator_type_args_obj.IsTypeArguments()) {
1123 SetValue(instr, non_constant_);
1124 return;
1125 }
1126 instantiator_type_args ^= instantiator_type_args_obj.ptr();
1127 if (instr->CanShareInstantiatorTypeArguments()) {
1128 SetValue(instr, instantiator_type_args);
1129 return;
1130 }
1131 }
1132 auto& function_type_args = TypeArguments::Handle(Z);
1133 if (!type_arguments.IsInstantiated(kFunctions)) {
1134 // Type arguments refer to function type parameters.
1135 const Object& function_type_args_obj =
1136 instr->function_type_arguments()->definition()->constant_value();
1137 if (IsUnknown(function_type_args_obj)) {
1138 return;
1139 }
1140 if (!function_type_args_obj.IsNull() &&
1141 !function_type_args_obj.IsTypeArguments()) {
1142 SetValue(instr, non_constant_);
1143 return;
1144 }
1145 function_type_args ^= function_type_args_obj.ptr();
1146 if (instr->CanShareFunctionTypeArguments()) {
1147 SetValue(instr, function_type_args);
1148 return;
1149 }
1150 }
1152 Z, type_arguments.InstantiateFrom(
1153 instantiator_type_args, function_type_args, kAllFree, Heap::kOld));
1154 ASSERT(result.IsInstantiated());
1155 result = result.Canonicalize(T);
1156 SetValue(instr, result);
1157}
1158
1159void ConstantPropagator::VisitAllocateContext(AllocateContextInstr* instr) {
1160 SetValue(instr, non_constant_);
1161}
1162
1163void ConstantPropagator::VisitAllocateUninitializedContext(
1164 AllocateUninitializedContextInstr* instr) {
1165 SetValue(instr, non_constant_);
1166}
1167
1168void ConstantPropagator::VisitCloneContext(CloneContextInstr* instr) {
1169 SetValue(instr, non_constant_);
1170}
1171
1172void ConstantPropagator::VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op) {
1173 const Object& left = binary_op->left()->definition()->constant_value();
1174 const Object& right = binary_op->right()->definition()->constant_value();
1175 if (IsNonConstant(left) || IsNonConstant(right)) {
1176 SetValue(binary_op, non_constant_);
1177 return;
1178 } else if (IsUnknown(left) || IsUnknown(right)) {
1179 return;
1180 }
1181 ASSERT(IsConstant(left) && IsConstant(right));
1182 if (left.IsInteger() && right.IsInteger()) {
1183 const Integer& result = Integer::Handle(
1184 Z, Evaluator::BinaryIntegerEvaluate(left, right, binary_op->op_kind(),
1185 binary_op->is_truncating(),
1186 binary_op->representation(), T));
1187 if (!result.IsNull()) {
1188 SetValue(binary_op, Integer::ZoneHandle(Z, result.ptr()));
1189 return;
1190 }
1191 }
1192 SetValue(binary_op, non_constant_);
1193}
1194
1195void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) {
1196 VisitBinaryIntegerOp(instr);
1197}
1198
1199void ConstantPropagator::VisitBinaryInt32Op(BinaryInt32OpInstr* instr) {
1200 VisitBinaryIntegerOp(instr);
1201}
1202
1203void ConstantPropagator::VisitBinaryUint32Op(BinaryUint32OpInstr* instr) {
1204 VisitBinaryIntegerOp(instr);
1205}
1206
1207void ConstantPropagator::VisitBinaryInt64Op(BinaryInt64OpInstr* instr) {
1208 VisitBinaryIntegerOp(instr);
1209}
1210
1211void ConstantPropagator::VisitShiftInt64Op(ShiftInt64OpInstr* instr) {
1212 VisitBinaryIntegerOp(instr);
1213}
1214
1215void ConstantPropagator::VisitSpeculativeShiftInt64Op(
1216 SpeculativeShiftInt64OpInstr* instr) {
1217 VisitBinaryIntegerOp(instr);
1218}
1219
1220void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) {
1221 VisitBinaryIntegerOp(instr);
1222}
1223
1224void ConstantPropagator::VisitSpeculativeShiftUint32Op(
1225 SpeculativeShiftUint32OpInstr* instr) {
1226 VisitBinaryIntegerOp(instr);
1227}
1228
1229void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) {
1230 VisitBox(instr);
1231}
1232
1233void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) {
1234 VisitUnbox(instr);
1235}
1236
1237void ConstantPropagator::VisitHashDoubleOp(HashDoubleOpInstr* instr) {
1238 const Object& value = instr->value()->definition()->constant_value();
1239 if (IsUnknown(value)) {
1240 return;
1241 }
1242 if (value.IsDouble()) {
1243 // TODO(aam): Add constant hash evaluation
1244 }
1245 SetValue(instr, non_constant_);
1246}
1247
1248void ConstantPropagator::VisitHashIntegerOp(HashIntegerOpInstr* instr) {
1249 const Object& value = instr->value()->definition()->constant_value();
1250 if (IsUnknown(value)) {
1251 return;
1252 }
1253 if (value.IsInteger()) {
1254 // TODO(aam): Add constant hash evaluation
1255 }
1256 SetValue(instr, non_constant_);
1257}
1258
1259void ConstantPropagator::VisitUnaryIntegerOp(UnaryIntegerOpInstr* unary_op) {
1260 const Object& value = unary_op->value()->definition()->constant_value();
1261 if (IsUnknown(value)) {
1262 return;
1263 }
1264 if (value.IsInteger()) {
1265 const Integer& result = Integer::Handle(
1266 Z, Evaluator::UnaryIntegerEvaluate(value, unary_op->op_kind(),
1267 unary_op->representation(), T));
1268 if (!result.IsNull()) {
1269 SetValue(unary_op, Integer::ZoneHandle(Z, result.ptr()));
1270 return;
1271 }
1272 }
1273 SetValue(unary_op, non_constant_);
1274}
1275
1276void ConstantPropagator::VisitUnaryInt64Op(UnaryInt64OpInstr* instr) {
1277 VisitUnaryIntegerOp(instr);
1278}
1279
1280void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) {
1281 VisitUnaryIntegerOp(instr);
1282}
1283
1284static bool IsIntegerOrDouble(const Object& value) {
1285 return value.IsInteger() || value.IsDouble();
1286}
1287
1288static double ToDouble(const Object& value) {
1289 return value.IsInteger() ? Integer::Cast(value).AsDoubleValue()
1290 : Double::Cast(value).value();
1291}
1292
1293void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) {
1294 const Object& value = instr->value()->definition()->constant_value();
1295 if (IsUnknown(value)) {
1296 return;
1297 }
1298 if (value.IsDouble()) {
1299 const double result_val = Evaluator::EvaluateUnaryDoubleOp(
1300 ToDouble(value), instr->op_kind(), instr->representation());
1301 const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val));
1302 SetValue(instr, result);
1303 return;
1304 }
1305 SetValue(instr, non_constant_);
1306}
1307
1308void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) {
1309 const Object& value = instr->value()->definition()->constant_value();
1310 if (IsUnknown(value)) {
1311 return;
1312 }
1313 if (value.IsInteger()) {
1314 SetValue(instr,
1315 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1316 Heap::kOld)));
1317 } else {
1318 SetValue(instr, non_constant_);
1319 }
1320}
1321
1322void ConstantPropagator::VisitInt64ToDouble(Int64ToDoubleInstr* instr) {
1323 const Object& value = instr->value()->definition()->constant_value();
1324 if (IsUnknown(value)) {
1325 return;
1326 }
1327 if (value.IsInteger()) {
1328 SetValue(instr,
1329 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1330 Heap::kOld)));
1331 } else {
1332 SetValue(instr, non_constant_);
1333 }
1334}
1335
1336void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) {
1337 const Object& value = instr->value()->definition()->constant_value();
1338 if (IsUnknown(value)) {
1339 return;
1340 }
1341 if (value.IsInteger()) {
1342 SetValue(instr,
1343 Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(),
1344 Heap::kOld)));
1345 } else {
1346 SetValue(instr, non_constant_);
1347 }
1348}
1349
1350void ConstantPropagator::VisitDoubleToInteger(DoubleToIntegerInstr* instr) {
1351 // TODO(kmillikin): Handle conversion.
1352 SetValue(instr, non_constant_);
1353}
1354
1355void ConstantPropagator::VisitDoubleToSmi(DoubleToSmiInstr* instr) {
1356 // TODO(kmillikin): Handle conversion.
1357 SetValue(instr, non_constant_);
1358}
1359
1360void ConstantPropagator::VisitDoubleToFloat(DoubleToFloatInstr* instr) {
1361 // TODO(kmillikin): Handle conversion.
1362 SetValue(instr, non_constant_);
1363}
1364
1365void ConstantPropagator::VisitFloatToDouble(FloatToDoubleInstr* instr) {
1366 // TODO(kmillikin): Handle conversion.
1367 SetValue(instr, non_constant_);
1368}
1369
1370void ConstantPropagator::VisitFloatCompare(FloatCompareInstr* instr) {
1371 // TODO(riscv)
1372 SetValue(instr, non_constant_);
1373}
1374
1375void ConstantPropagator::VisitInvokeMathCFunction(
1376 InvokeMathCFunctionInstr* instr) {
1377 // TODO(kmillikin): Handle conversion.
1378 SetValue(instr, non_constant_);
1379}
1380
1381void ConstantPropagator::VisitTruncDivMod(TruncDivModInstr* instr) {
1382 // TODO(srdjan): Handle merged instruction.
1383 SetValue(instr, non_constant_);
1384}
1385
1386void ConstantPropagator::VisitExtractNthOutput(ExtractNthOutputInstr* instr) {
1387 SetValue(instr, non_constant_);
1388}
1389
1390void ConstantPropagator::VisitMakePair(MakePairInstr* instr) {
1391 SetValue(instr, non_constant_);
1392}
1393
1394void ConstantPropagator::VisitUnboxLane(UnboxLaneInstr* instr) {
1395 if (BoxLanesInstr* box = instr->value()->definition()->AsBoxLanes()) {
1396 const Object& value =
1397 box->InputAt(instr->lane())->definition()->constant_value();
1398 if (IsUnknown(value)) {
1399 return;
1400 }
1401 SetValue(instr, value);
1402 return;
1403 }
1404
1405 SetValue(instr, non_constant_);
1406}
1407
1408void ConstantPropagator::VisitBoxLanes(BoxLanesInstr* instr) {
1409 // TODO(riscv)
1410 SetValue(instr, non_constant_);
1411}
1412
1413void ConstantPropagator::VisitConstant(ConstantInstr* instr) {
1414 SetValue(instr, instr->value());
1415}
1416
1417void ConstantPropagator::VisitUnboxedConstant(UnboxedConstantInstr* instr) {
1418 SetValue(instr, instr->value());
1419}
1420
1421void ConstantPropagator::VisitConstraint(ConstraintInstr* instr) {
1422 // Should not be used outside of range analysis.
1423 UNREACHABLE();
1424}
1425
1426void ConstantPropagator::VisitMaterializeObject(MaterializeObjectInstr* instr) {
1427 // Should not be used outside of allocation elimination pass.
1428 UNREACHABLE();
1429}
1430
1431void ConstantPropagator::VisitBinaryDoubleOp(BinaryDoubleOpInstr* instr) {
1432 const Object& left = instr->left()->definition()->constant_value();
1433 const Object& right = instr->right()->definition()->constant_value();
1434 if (IsNonConstant(left) || IsNonConstant(right)) {
1435 SetValue(instr, non_constant_);
1436 return;
1437 } else if (IsUnknown(left) || IsUnknown(right)) {
1438 return;
1439 }
1440 ASSERT(IsConstant(left) && IsConstant(right));
1441 const bool both_are_integers = left.IsInteger() && right.IsInteger();
1443 !both_are_integers) {
1444 const double result_val = Evaluator::EvaluateBinaryDoubleOp(
1445 ToDouble(left), ToDouble(right), instr->op_kind(),
1446 instr->representation());
1447 const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val));
1448 SetValue(instr, result);
1449 return;
1450 }
1451 SetValue(instr, non_constant_);
1452}
1453
1454void ConstantPropagator::VisitDoubleTestOp(DoubleTestOpInstr* instr) {
1455 const Object& value = instr->value()->definition()->constant_value();
1456 if (IsUnknown(value)) {
1457 return;
1458 }
1459 bool result;
1460 if (value.IsInteger()) {
1461 switch (instr->op_kind()) {
1462 case MethodRecognizer::kDouble_getIsNaN:
1464 case MethodRecognizer::kDouble_getIsInfinite:
1465 result = false;
1466 break;
1467 case MethodRecognizer::kDouble_getIsNegative: {
1468 result = Integer::Cast(value).IsNegative();
1469 break;
1470 }
1471 default:
1472 UNREACHABLE();
1473 }
1474 } else if (value.IsDouble()) {
1475 const double double_value = ToDouble(value);
1476 switch (instr->op_kind()) {
1477 case MethodRecognizer::kDouble_getIsNaN: {
1478 result = isnan(double_value);
1479 break;
1480 }
1481 case MethodRecognizer::kDouble_getIsInfinite: {
1482 result = isinf(double_value);
1483 break;
1484 }
1485 case MethodRecognizer::kDouble_getIsNegative: {
1486 result = signbit(double_value) && !isnan(double_value);
1487 break;
1488 }
1489 default:
1490 UNREACHABLE();
1491 }
1492 } else {
1493 SetValue(instr, non_constant_);
1494 return;
1495 }
1496 const bool is_negated = instr->kind() != Token::kEQ;
1497 SetValue(instr, Bool::Get(is_negated ? !result : result));
1498}
1499
1500void ConstantPropagator::VisitSimdOp(SimdOpInstr* instr) {
1501 SetValue(instr, non_constant_);
1502}
1503
1504void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* instr) {
1505 // TODO(srdjan): Handle min and max.
1506 SetValue(instr, non_constant_);
1507}
1508
1509void ConstantPropagator::VisitCaseInsensitiveCompare(
1510 CaseInsensitiveCompareInstr* instr) {
1511 SetValue(instr, non_constant_);
1512}
1513
1514void ConstantPropagator::VisitUnbox(UnboxInstr* instr) {
1515 const Object& value = instr->value()->definition()->constant_value();
1516 if (IsUnknown(value)) {
1517 return;
1518 }
1519
1520 SetValue(instr, value);
1521}
1522
1523void ConstantPropagator::VisitBox(BoxInstr* instr) {
1524 const Object& value = instr->value()->definition()->constant_value();
1525 if (IsUnknown(value)) {
1526 return;
1527 }
1528
1529 if (instr->value()->definition()->representation() ==
1530 instr->from_representation()) {
1531 SetValue(instr, value);
1532 } else {
1533 SetValue(instr, non_constant_);
1534 }
1535}
1536
1537void ConstantPropagator::VisitBoxSmallInt(BoxSmallIntInstr* instr) {
1538 VisitBox(instr);
1539}
1540
1541void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) {
1542 VisitBox(instr);
1543}
1544
1545void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) {
1546 VisitUnbox(instr);
1547}
1548
1549void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) {
1550 VisitBox(instr);
1551}
1552
1553void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) {
1554 VisitUnbox(instr);
1555}
1556
1557void ConstantPropagator::VisitIntConverter(IntConverterInstr* instr) {
1558 SetValue(instr, non_constant_);
1559}
1560
1561void ConstantPropagator::VisitBitCast(BitCastInstr* instr) {
1562 SetValue(instr, non_constant_);
1563}
1564
1565void ConstantPropagator::VisitCall1ArgStub(Call1ArgStubInstr* instr) {
1566 SetValue(instr, non_constant_);
1567}
1568
1569void ConstantPropagator::VisitSuspend(SuspendInstr* instr) {
1570 SetValue(instr, non_constant_);
1571}
1572
1573void ConstantPropagator::VisitLoadThread(LoadThreadInstr* instr) {
1574 SetValue(instr, non_constant_);
1575}
1576
1577void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) {
1578 // TODO(kmillikin): Handle unary operations.
1579 SetValue(instr, non_constant_);
1580}
1581
1582// Insert redefinition for |original| definition which conveys information
1583// that |original| is equal to |constant_value| in the dominated code.
1586 Definition* original,
1587 const Object& constant_value) {
1588 auto redef = new RedefinitionInstr(new Value(original),
1589 /*inserted_by_constant_propagation=*/true);
1590
1591 graph->InsertAfter(dom, redef, nullptr, FlowGraph::kValue);
1592 graph->RenameDominatedUses(original, redef, redef);
1593
1594 if (redef->input_use_list() == nullptr) {
1595 // There are no dominated uses, so the newly added Redefinition is useless.
1596 redef->RemoveFromGraph();
1597 return nullptr;
1598 }
1599
1600 redef->constant_value() = constant_value.ptr();
1601 return redef;
1602}
1603
1604// Find all Branch(v eq constant) (eq being one of ==, !=, === or !==) in the
1605// graph and redefine |v| in the true successor to record information about
1606// it being equal to the constant. For comparisons between boolean values
1607// we also redefine |v| in the false successor - because booleans have
1608// only two possible values (e.g. if |v| is |true| in true successor, then
1609// it is |false| in false successor).
1610//
1611// We don't actually _replace_ |v| with |constant| in the dominated code
1612// because it might complicate subsequent optimizations (e.g. lead to
1613// redundant phis).
1614void ConstantPropagator::InsertRedefinitionsAfterEqualityComparisons() {
1615 for (auto block : graph_->reverse_postorder()) {
1616 if (auto branch = block->last_instruction()->AsBranch()) {
1617 auto comparison = branch->comparison();
1618 if (comparison->IsStrictCompare() ||
1619 (comparison->IsEqualityCompare() &&
1620 comparison->operation_cid() != kDoubleCid)) {
1621 Value* value;
1622 ConstantInstr* constant_defn;
1623 if (comparison->IsComparisonWithConstant(&value, &constant_defn) &&
1624 !value->BindsToConstant()) {
1625 const Object& constant_value = constant_defn->value();
1626
1627 // Found comparison with constant. Introduce Redefinition().
1628 ASSERT(comparison->kind() == Token::kNE_STRICT ||
1629 comparison->kind() == Token::kNE ||
1630 comparison->kind() == Token::kEQ_STRICT ||
1631 comparison->kind() == Token::kEQ);
1632 const bool negated = (comparison->kind() == Token::kNE_STRICT ||
1633 comparison->kind() == Token::kNE);
1634 const auto true_successor =
1635 negated ? branch->false_successor() : branch->true_successor();
1636 InsertRedefinition(graph_, true_successor, value->definition(),
1637 constant_value);
1638
1639 // When comparing two boolean values we can also apply renaming
1640 // to the false successor because we know that only true and false
1641 // are possible values.
1642 if (constant_value.IsBool() && value->Type()->IsBool()) {
1643 const auto false_successor =
1644 negated ? branch->true_successor() : branch->false_successor();
1645 InsertRedefinition(graph_, false_successor, value->definition(),
1646 Bool::Get(!Bool::Cast(constant_value).value()));
1647 }
1648 }
1649 }
1650 }
1651 }
1652}
1653
1654void ConstantPropagator::Analyze() {
1655 InsertRedefinitionsAfterEqualityComparisons();
1656
1657 GraphEntryInstr* entry = graph_->graph_entry();
1658 reachable_->Add(entry->preorder_number());
1659 block_worklist_.Add(entry);
1660
1661 while (true) {
1662 if (block_worklist_.is_empty()) {
1663 if (definition_worklist_.IsEmpty()) break;
1664 Definition* definition = definition_worklist_.RemoveLast();
1665 for (Value* use = definition->input_use_list(); use != nullptr;
1666 use = use->next_use()) {
1667 use->instruction()->Accept(this);
1668 }
1669 } else {
1670 BlockEntryInstr* block = block_worklist_.RemoveLast();
1671 block->Accept(this);
1672 }
1673 }
1674}
1675
1676static bool HasPhis(BlockEntryInstr* block) {
1677 if (auto* join = block->AsJoinEntry()) {
1678 return (join->phis() != nullptr) && !join->phis()->is_empty();
1679 }
1680 return false;
1681}
1682
1683static bool IsEmptyBlock(BlockEntryInstr* block) {
1684 // A block containing a goto to itself forms an infinite loop.
1685 // We don't consider this an empty block to handle the edge-case where code
1686 // reduces to an infinite loop.
1687 return block->next()->IsGoto() &&
1688 block->next()->AsGoto()->successor() != block && !HasPhis(block) &&
1689 !block->IsIndirectEntry();
1690}
1691
1692// Traverses a chain of empty blocks and returns the first reachable non-empty
1693// block that is not dominated by the start block. The empty blocks are added
1694// to the supplied bit vector.
1696 BitVector* empty_blocks) {
1697 BlockEntryInstr* current = block;
1698 while (IsEmptyBlock(current) && block->Dominates(current)) {
1699 ASSERT(!HasPhis(block));
1700 empty_blocks->Add(current->preorder_number());
1701 current = current->next()->AsGoto()->successor();
1702 }
1703 return current;
1704}
1705
1706void ConstantPropagator::EliminateRedundantBranches() {
1707 // Canonicalize branches that have no side-effects and where true- and
1708 // false-targets are the same.
1709 bool changed = false;
1710 BitVector* empty_blocks = new (Z) BitVector(Z, graph_->preorder().length());
1711 for (BlockIterator b = graph_->postorder_iterator(); !b.Done(); b.Advance()) {
1712 BlockEntryInstr* block = b.Current();
1713 BranchInstr* branch = block->last_instruction()->AsBranch();
1714 empty_blocks->Clear();
1715 if ((branch != nullptr) && !branch->HasUnknownSideEffects()) {
1716 ASSERT(branch->previous() != nullptr); // Not already eliminated.
1717 BlockEntryInstr* if_true =
1718 FindFirstNonEmptySuccessor(branch->true_successor(), empty_blocks);
1719 BlockEntryInstr* if_false =
1720 FindFirstNonEmptySuccessor(branch->false_successor(), empty_blocks);
1721 if (if_true == if_false) {
1722 // Replace the branch with a jump to the common successor.
1723 // Drop the comparison, which does not have side effects
1724 JoinEntryInstr* join = if_true->AsJoinEntry();
1725 if (!HasPhis(join)) {
1726 GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone);
1727 graph_->CopyDeoptTarget(jump, branch);
1728
1729 Instruction* previous = branch->previous();
1730 branch->set_previous(nullptr);
1731 previous->LinkTo(jump);
1732
1733 // Remove uses from branch and all the empty blocks that
1734 // are now unreachable.
1735 branch->UnuseAllInputs();
1736 for (BitVector::Iterator it(empty_blocks); !it.Done(); it.Advance()) {
1737 BlockEntryInstr* empty_block = graph_->preorder()[it.Current()];
1738 empty_block->ClearAllInstructions();
1739 }
1740
1741 changed = true;
1742
1743 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1744 THR_Print("Eliminated branch in B%" Pd " common target B%" Pd "\n",
1745 block->block_id(), join->block_id());
1746 }
1747 }
1748 }
1749 }
1750 }
1751
1752 if (changed) {
1753 graph_->DiscoverBlocks();
1754 graph_->MergeBlocks();
1755 // TODO(fschneider): Update dominator tree in place instead of recomputing.
1756 GrowableArray<BitVector*> dominance_frontier;
1757 graph_->ComputeDominators(&dominance_frontier);
1758 }
1759}
1760
1761void ConstantPropagator::Transform() {
1762 // We will recompute dominators, block ordering, block ids, block last
1763 // instructions, previous pointers, predecessors, etc. after eliminating
1764 // unreachable code. We do not maintain those properties during the
1765 // transformation.
1766 for (BlockIterator b = graph_->reverse_postorder_iterator(); !b.Done();
1767 b.Advance()) {
1768 BlockEntryInstr* block = b.Current();
1769 if (!reachable_->Contains(block->preorder_number())) {
1770 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1771 THR_Print("Unreachable B%" Pd "\n", block->block_id());
1772 }
1773 // Remove all uses in unreachable blocks.
1774 block->ClearAllInstructions();
1775 continue;
1776 }
1777
1778 JoinEntryInstr* join = block->AsJoinEntry();
1779 if (join != nullptr) {
1780 // Remove phi inputs corresponding to unreachable predecessor blocks.
1781 // Predecessors will be recomputed (in block id order) after removing
1782 // unreachable code so we merely have to keep the phi inputs in order.
1783 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
1784 if ((phis != nullptr) && !phis->is_empty()) {
1785 intptr_t pred_count = join->PredecessorCount();
1786 intptr_t live_count = 0;
1787 for (intptr_t pred_idx = 0; pred_idx < pred_count; ++pred_idx) {
1788 if (reachable_->Contains(
1789 join->PredecessorAt(pred_idx)->preorder_number())) {
1790 if (live_count < pred_idx) {
1791 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1792 PhiInstr* phi = it.Current();
1793 ASSERT(phi != nullptr);
1794 phi->SetInputAt(live_count, phi->InputAt(pred_idx));
1795 }
1796 }
1797 ++live_count;
1798 } else {
1799 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1800 PhiInstr* phi = it.Current();
1801 ASSERT(phi != nullptr);
1802 phi->InputAt(pred_idx)->RemoveFromUseList();
1803 }
1804 }
1805 }
1806 if (live_count < pred_count) {
1807 intptr_t to_idx = 0;
1808 for (intptr_t from_idx = 0; from_idx < phis->length(); ++from_idx) {
1809 PhiInstr* phi = (*phis)[from_idx];
1810 ASSERT(phi != nullptr);
1811 if (FLAG_remove_redundant_phis && (live_count == 1)) {
1812 Value* input = phi->InputAt(0);
1813 phi->ReplaceUsesWith(input->definition());
1814 input->RemoveFromUseList();
1815 } else {
1816 phi->inputs_.TruncateTo(live_count);
1817 (*phis)[to_idx++] = phi;
1818 }
1819 }
1820 if (to_idx == 0) {
1821 join->phis_ = nullptr;
1822 } else {
1823 phis->TruncateTo(to_idx);
1824 }
1825 }
1826 }
1827 }
1828
1829 if (join != nullptr) {
1830 for (PhiIterator it(join); !it.Done(); it.Advance()) {
1831 auto phi = it.Current();
1832 if (TransformDefinition(phi)) {
1833 it.RemoveCurrentFromGraph();
1834 }
1835 }
1836 }
1837 for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) {
1838 Definition* defn = i.Current()->AsDefinition();
1839 if (TransformDefinition(defn)) {
1840 i.RemoveCurrentFromGraph();
1841 }
1842 }
1843
1844 // Replace branches where one target is unreachable with jumps.
1845 BranchInstr* branch = block->last_instruction()->AsBranch();
1846 if (branch != nullptr) {
1847 TargetEntryInstr* if_true = branch->true_successor();
1848 TargetEntryInstr* if_false = branch->false_successor();
1849 JoinEntryInstr* join = nullptr;
1850 Instruction* next = nullptr;
1851
1852 if (!reachable_->Contains(if_true->preorder_number())) {
1853 ASSERT(reachable_->Contains(if_false->preorder_number()));
1854 ASSERT(if_false->parallel_move() == nullptr);
1855 join = new (Z) JoinEntryInstr(if_false->block_id(),
1856 if_false->try_index(), DeoptId::kNone);
1857 graph_->CopyDeoptTarget(join, if_false);
1858 if_false->UnuseAllInputs();
1859 next = if_false->next();
1860 } else if (!reachable_->Contains(if_false->preorder_number())) {
1861 ASSERT(if_true->parallel_move() == nullptr);
1862 join = new (Z) JoinEntryInstr(if_true->block_id(), if_true->try_index(),
1864 graph_->CopyDeoptTarget(join, if_true);
1865 if_true->UnuseAllInputs();
1866 next = if_true->next();
1867 }
1868
1869 if (join != nullptr) {
1870 // Replace the branch with a jump to the reachable successor.
1871 // Drop the comparison, which does not have side effects as long
1872 // as it is a strict compare (the only one we can determine is
1873 // constant with the current analysis).
1874 GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone);
1875 graph_->CopyDeoptTarget(jump, branch);
1876
1877 Instruction* previous = branch->previous();
1878 branch->set_previous(nullptr);
1879 previous->LinkTo(jump);
1880
1881 // Replace the false target entry with the new join entry. We will
1882 // recompute the dominators after this pass.
1883 join->LinkTo(next);
1884 branch->UnuseAllInputs();
1885 }
1886 }
1887 }
1888
1889 graph_->DiscoverBlocks();
1890 graph_->MergeBlocks();
1891 GrowableArray<BitVector*> dominance_frontier;
1892 graph_->ComputeDominators(&dominance_frontier);
1893}
1894
1895bool ConstantPropagator::TransformDefinition(Definition* defn) {
1896 if (defn == nullptr) {
1897 return false;
1898 }
1899
1900 if (auto redef = defn->AsRedefinition()) {
1901 if (redef->inserted_by_constant_propagation()) {
1902 redef->ReplaceUsesWith(redef->value()->definition());
1903 return true;
1904 }
1905
1906 if (IsConstant(defn->constant_value()) &&
1907 !IsConstant(defn->OriginalDefinition()->constant_value())) {
1908 // Redefinition might have become constant because some other
1909 // redefinition narrowed it, we should ignore this and not
1910 // replace it.
1911 return false;
1912 }
1913 }
1914
1915 // Replace constant-valued instructions without observable side
1916 // effects. Do this for smis and old objects only to avoid having to
1917 // copy other objects into the heap's old generation.
1918 if (IsConstant(defn->constant_value()) &&
1919 (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) &&
1920 !defn->IsConstant() && !defn->IsStoreIndexed() && !defn->IsStoreField() &&
1921 !defn->IsStoreStaticField()) {
1922 if (FLAG_trace_constant_propagation && graph_->should_print()) {
1923 THR_Print("Constant v%" Pd " = %s\n", defn->ssa_temp_index(),
1924 defn->constant_value().ToCString());
1925 }
1926 constant_value_ = defn->constant_value().ptr();
1927 if ((constant_value_.IsString() || constant_value_.IsMint() ||
1928 constant_value_.IsDouble()) &&
1929 !constant_value_.IsCanonical()) {
1930 constant_value_ = Instance::Cast(constant_value_).Canonicalize(T);
1931 ASSERT(!constant_value_.IsNull());
1932 }
1933 if (auto call = defn->AsStaticCall()) {
1934 ASSERT(!call->HasMoveArguments());
1935 }
1936 Definition* replacement =
1937 graph_->TryCreateConstantReplacementFor(defn, constant_value_);
1938 if (replacement != defn) {
1939 defn->ReplaceUsesWith(replacement);
1940 return true;
1941 }
1942 }
1943 return false;
1944}
1945
1946} // namespace dart
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static float next(float f)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
SI F table(const skcms_Curve *curve, F v)
#define UNREACHABLE()
Definition assert.h:248
#define Z
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:1649
bool Dominates(BlockEntryInstr *other) const
Definition il.cc:1806
Instruction * last_instruction() const
Definition il.h:1680
bool Done() const
Definition flow_graph.h:46
static const Bool & Get(bool value)
Definition object.h:10780
static const Bool & True()
Definition object.h:10776
static void Optimize(FlowGraph *graph)
ConstantPropagator(FlowGraph *graph, const GrowableArray< BlockEntryInstr * > &ignored)
static void OptimizeBranches(FlowGraph *graph)
void Add(Definition *defn)
Definition flow_graph.h:831
Definition * RemoveLast()
Definition flow_graph.h:845
void ReplaceUsesWith(Definition *other)
Definition il.cc:1484
static constexpr intptr_t kNone
Definition deopt_id.h:27
static DoublePtr New(double d, Heap::Space space=Heap::kNew)
Definition object.cc:23481
static DoublePtr NewCanonical(double d)
Definition object.cc:23497
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 bool SupportsUnboxedDoubles()
static void PrintGraph(const char *phase, FlowGraph *flow_graph)
GraphEntryInstr * graph_entry() const
Definition flow_graph.h:268
bool should_print() const
Definition flow_graph.h:505
const GrowableArray< BlockEntryInstr * > & preorder() const
Definition flow_graph.h:203
BlockIterator postorder_iterator() const
Definition flow_graph.h:222
void DiscoverBlocks()
void ComputeDominators(GrowableArray< BitVector * > *dominance_frontier)
const ParsedFunction & parsed_function() const
Definition flow_graph.h:129
Definition * TryCreateConstantReplacementFor(Definition *op, const Object &value)
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)
void InsertAfter(Instruction *prev, Instruction *instr, Environment *env, UseKind use_kind)
@ kOld
Definition heap.h:39
virtual void Accept(InstructionVisitor *visitor)=0
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:9985
@ kMaxOneCharCodeSymbol
Definition symbols.h:576
static StringPtr * PredefinedAddress()
Definition symbols.h:771
#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
#define DEFINE_FLAG(type, name, default_value, comment)
Definition flags.h:16
size_t length
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)
@ kAllFree
Definition object.h:2920
const intptr_t cid
static bool IsEmptyBlock(BlockEntryInstr *block)
@ kFunctions
Definition object.h:2231
@ kCurrentClass
Definition object.h:2230
bool IsIntegerClassId(intptr_t index)
Definition class_id.h:340
static bool IsIntegerOrDouble(const Object &value)
Definition dom.py:1
call(args)
Definition dom.py:159
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:242
#define FALL_THROUGH
Definition globals.h:15
#define Pd
Definition globals.h:408
#define T
Value(int value, int *counter)
const uintptr_t id
#define NOT_IN_PRODUCT(code)
Definition globals.h:84