|  | // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/compiler/backend/constant_propagator.h" | 
|  |  | 
|  | #include "vm/bit_vector.h" | 
|  | #include "vm/compiler/backend/evaluator.h" | 
|  | #include "vm/compiler/backend/flow_graph_compiler.h" | 
|  | #include "vm/compiler/backend/il.h" | 
|  | #include "vm/compiler/backend/il_printer.h" | 
|  | #include "vm/compiler/backend/range_analysis.h" | 
|  | #include "vm/compiler/frontend/flow_graph_builder.h" | 
|  | #include "vm/parser.h" | 
|  | #include "vm/symbols.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis."); | 
|  | DEFINE_FLAG(bool, | 
|  | trace_constant_propagation, | 
|  | false, | 
|  | "Print constant propagation and useless code elimination."); | 
|  |  | 
|  | // Quick access to the current thread & zone. | 
|  | #define Z (graph_->zone()) | 
|  | #define T (graph_->thread()) | 
|  |  | 
|  | ConstantPropagator::ConstantPropagator( | 
|  | FlowGraph* graph, | 
|  | const GrowableArray<BlockEntryInstr*>& ignored) | 
|  | : FlowGraphVisitor(ignored), | 
|  | graph_(graph), | 
|  | unknown_(Object::unknown_constant()), | 
|  | non_constant_(Object::non_constant()), | 
|  | constant_value_(Object::Handle(Z)), | 
|  | reachable_(new(Z) BitVector(Z, graph->preorder().length())), | 
|  | unwrapped_phis_(new(Z) BitVector(Z, graph->current_ssa_temp_index())), | 
|  | block_worklist_(), | 
|  | definition_worklist_(graph, 10) {} | 
|  |  | 
|  | void ConstantPropagator::Optimize(FlowGraph* graph) { | 
|  | GrowableArray<BlockEntryInstr*> ignored; | 
|  | ConstantPropagator cp(graph, ignored); | 
|  | cp.Analyze(); | 
|  | cp.Transform(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::OptimizeBranches(FlowGraph* graph) { | 
|  | GrowableArray<BlockEntryInstr*> ignored; | 
|  | ConstantPropagator cp(graph, ignored); | 
|  | cp.Analyze(); | 
|  | cp.Transform(); | 
|  | cp.EliminateRedundantBranches(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::SetReachable(BlockEntryInstr* block) { | 
|  | if (!reachable_->Contains(block->preorder_number())) { | 
|  | reachable_->Add(block->preorder_number()); | 
|  | block_worklist_.Add(block); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ConstantPropagator::SetValue(Definition* definition, const Object& value) { | 
|  | // We would like to assert we only go up (toward non-constant) in the lattice. | 
|  | // | 
|  | // ASSERT(IsUnknown(definition->constant_value()) || | 
|  | //        IsNonConstant(value) || | 
|  | //        (definition->constant_value().ptr() == value.ptr())); | 
|  | // | 
|  | // But the final disjunct is not true (e.g., mint or double constants are | 
|  | // heap-allocated and so not necessarily pointer-equal on each iteration). | 
|  | if (definition->constant_value().ptr() != value.ptr()) { | 
|  | definition->constant_value() = value.ptr(); | 
|  | if (definition->input_use_list() != nullptr) { | 
|  | definition_worklist_.Add(definition); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool IsIdenticalConstants(const Object& left, const Object& right) { | 
|  | // This should be kept in line with Identical_comparison (identical.cc) | 
|  | // (=> Instance::IsIdenticalTo in object.cc). | 
|  |  | 
|  | if (left.ptr() == right.ptr()) return true; | 
|  | if (left.GetClassId() != right.GetClassId()) return false; | 
|  | if (left.IsInteger()) { | 
|  | return Integer::Cast(left).Equals(Integer::Cast(right)); | 
|  | } | 
|  | if (left.IsDouble()) { | 
|  | return Double::Cast(left).BitwiseEqualsToDouble( | 
|  | Double::Cast(right).value()); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Compute the join of two values in the lattice, assign it to the first. | 
|  | void ConstantPropagator::Join(Object* left, const Object& right) { | 
|  | // Join(non-constant, X) = non-constant | 
|  | // Join(X, unknown)      = X | 
|  | if (IsNonConstant(*left) || IsUnknown(right)) return; | 
|  |  | 
|  | // Join(unknown, X)      = X | 
|  | // Join(X, non-constant) = non-constant | 
|  | if (IsUnknown(*left) || IsNonConstant(right)) { | 
|  | *left = right.ptr(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Join(X, X) = X | 
|  | if (IsIdenticalConstants(*left, right)) return; | 
|  |  | 
|  | // Join(X, Y) = non-constant | 
|  | *left = non_constant_.ptr(); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Analysis of blocks.  Called at most once per block.  The block is already | 
|  | // marked as reachable.  All instructions in the block are analyzed. | 
|  | void ConstantPropagator::VisitGraphEntry(GraphEntryInstr* block) { | 
|  | for (auto def : *block->initial_definitions()) { | 
|  | def->Accept(this); | 
|  | } | 
|  | ASSERT(ForwardInstructionIterator(block).Done()); | 
|  |  | 
|  | // TODO(fschneider): Improve this approximation. The catch entry is only | 
|  | // reachable if a call in the try-block is reachable. | 
|  | for (intptr_t i = 0; i < block->SuccessorCount(); ++i) { | 
|  | SetReachable(block->SuccessorAt(i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitFunctionEntry(FunctionEntryInstr* block) { | 
|  | for (auto def : *block->initial_definitions()) { | 
|  | def->Accept(this); | 
|  | } | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitNativeEntry(NativeEntryInstr* block) { | 
|  | VisitFunctionEntry(block); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitOsrEntry(OsrEntryInstr* block) { | 
|  | for (auto def : *block->initial_definitions()) { | 
|  | def->Accept(this); | 
|  | } | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCatchBlockEntry(CatchBlockEntryInstr* block) { | 
|  | for (auto def : *block->initial_definitions()) { | 
|  | def->Accept(this); | 
|  | } | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitJoinEntry(JoinEntryInstr* block) { | 
|  | // Phis are visited when visiting Goto at a predecessor. See VisitGoto. | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitTargetEntry(TargetEntryInstr* block) { | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitIndirectEntry(IndirectEntryInstr* block) { | 
|  | for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | it.Current()->Accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitParallelMove(ParallelMoveInstr* instr) { | 
|  | // Parallel moves have not yet been inserted in the graph. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Analysis of control instructions.  Unconditional successors are | 
|  | // reachable.  Conditional successors are reachable depending on the | 
|  | // constant value of the condition. | 
|  | void ConstantPropagator::VisitDartReturn(DartReturnInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitNativeReturn(NativeReturnInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitThrow(ThrowInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitReThrow(ReThrowInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStop(StopInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitGoto(GotoInstr* instr) { | 
|  | SetReachable(instr->successor()); | 
|  |  | 
|  | // Phi value depends on the reachability of a predecessor. We have | 
|  | // to revisit phis every time a predecessor becomes reachable. | 
|  | for (PhiIterator it(instr->successor()); !it.Done(); it.Advance()) { | 
|  | PhiInstr* phi = it.Current(); | 
|  | phi->Accept(this); | 
|  |  | 
|  | // If this phi was previously unwrapped as redundant and it is no longer | 
|  | // redundant (does not unwrap) then we need to revisit the uses. | 
|  | if (unwrapped_phis_->Contains(phi->ssa_temp_index()) && | 
|  | (UnwrapPhi(phi) == phi)) { | 
|  | unwrapped_phis_->Remove(phi->ssa_temp_index()); | 
|  | definition_worklist_.Add(phi); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) { | 
|  | if (reachable_->Contains(instr->GetBlock()->preorder_number())) { | 
|  | for (intptr_t i = 0; i < instr->SuccessorCount(); i++) { | 
|  | SetReachable(instr->SuccessorAt(i)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBranch(BranchInstr* instr) { | 
|  | instr->comparison()->Accept(this); | 
|  |  | 
|  | // The successors may be reachable, but only if this instruction is.  (We | 
|  | // might be analyzing it because the constant value of one of its inputs | 
|  | // has changed.) | 
|  | if (reachable_->Contains(instr->GetBlock()->preorder_number())) { | 
|  | if (instr->constant_target() != nullptr) { | 
|  | ASSERT((instr->constant_target() == instr->true_successor()) || | 
|  | (instr->constant_target() == instr->false_successor())); | 
|  | SetReachable(instr->constant_target()); | 
|  | } else { | 
|  | const Object& value = instr->comparison()->constant_value(); | 
|  | if (IsNonConstant(value)) { | 
|  | SetReachable(instr->true_successor()); | 
|  | SetReachable(instr->false_successor()); | 
|  | } else if (value.ptr() == Bool::True().ptr()) { | 
|  | SetReachable(instr->true_successor()); | 
|  | } else if (!IsUnknown(value)) {  // Any other constant. | 
|  | SetReachable(instr->false_successor()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Analysis of non-definition instructions.  They do not have values so they | 
|  | // cannot have constant values. | 
|  | void ConstantPropagator::VisitCheckStackOverflow( | 
|  | CheckStackOverflowInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitCheckClass(CheckClassInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitCheckCondition(CheckConditionInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitCheckClassId(CheckClassIdInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitGuardFieldClass(GuardFieldClassInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitGuardFieldLength(GuardFieldLengthInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitGuardFieldType(GuardFieldTypeInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitCheckSmi(CheckSmiInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitTailCall(TailCallInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitCheckEitherNonSmi(CheckEitherNonSmiInstr* instr) { | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStoreIndexedUnsafe( | 
|  | StoreIndexedUnsafeInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitStoreIndexed(StoreIndexedInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitStoreField(StoreFieldInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitMemoryCopy(MemoryCopyInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitDeoptimize(DeoptimizeInstr* instr) { | 
|  | // TODO(vegorov) remove all code after DeoptimizeInstr as dead. | 
|  | } | 
|  |  | 
|  | Definition* ConstantPropagator::UnwrapPhi(Definition* defn) { | 
|  | if (defn->IsPhi()) { | 
|  | JoinEntryInstr* block = defn->AsPhi()->block(); | 
|  |  | 
|  | Definition* input = nullptr; | 
|  | for (intptr_t i = 0; i < defn->InputCount(); ++i) { | 
|  | if (reachable_->Contains(block->PredecessorAt(i)->preorder_number())) { | 
|  | if (input == nullptr) { | 
|  | input = defn->InputAt(i)->definition(); | 
|  | } else { | 
|  | return defn; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return input; | 
|  | } | 
|  |  | 
|  | return defn; | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::MarkUnwrappedPhi(Definition* phi) { | 
|  | ASSERT(phi->IsPhi()); | 
|  | unwrapped_phis_->Add(phi->ssa_temp_index()); | 
|  | } | 
|  |  | 
|  | ConstantPropagator::PhiInfo* ConstantPropagator::GetPhiInfo(PhiInstr* phi) { | 
|  | if (phi->HasPassSpecificId(CompilerPass::kConstantPropagation)) { | 
|  | const intptr_t id = | 
|  | phi->GetPassSpecificId(CompilerPass::kConstantPropagation); | 
|  | // Note: id might have been assigned by the previous round of constant | 
|  | // propagation, so we need to verify it before using it. | 
|  | if (id < phis_.length() && phis_[id].phi == phi) { | 
|  | return &phis_[id]; | 
|  | } | 
|  | } | 
|  |  | 
|  | phi->SetPassSpecificId(CompilerPass::kConstantPropagation, phis_.length()); | 
|  | phis_.Add({phi, 0}); | 
|  | return &phis_.Last(); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Analysis of definitions.  Compute the constant value.  If it has changed | 
|  | // and the definition has input uses, add the definition to the definition | 
|  | // worklist so that the used can be processed. | 
|  | void ConstantPropagator::VisitPhi(PhiInstr* instr) { | 
|  | // Detect convergence issues by checking if visit count for this phi | 
|  | // is too high. We should only visit this phi once for every predecessor | 
|  | // becoming reachable, once for every input changing its constant value and | 
|  | // once for an unwrapped redundant phi becoming non-redundant. | 
|  | // Inputs can only change their constant value at most three times: from | 
|  | // non-constant to unknown to specific constant to non-constant. The first | 
|  | // link (non-constant to ...) can happen when we run the second round of | 
|  | // constant propagation - some instructions can have non-constant assigned to | 
|  | // them at the end of the previous constant propagation. | 
|  | auto info = GetPhiInfo(instr); | 
|  | info->visit_count++; | 
|  | const intptr_t kMaxVisitsExpected = 5 * instr->InputCount(); | 
|  | if (info->visit_count > kMaxVisitsExpected) { | 
|  | OS::PrintErr( | 
|  | "ConstantPropagation pass is failing to converge on graph for %s\n", | 
|  | graph_->parsed_function().function().ToCString()); | 
|  | OS::PrintErr("Phi %s was visited %" Pd " times\n", instr->ToCString(), | 
|  | info->visit_count); | 
|  | NOT_IN_PRODUCT( | 
|  | FlowGraphPrinter::PrintGraph("Constant Propagation", graph_)); | 
|  | FATAL("Aborting due to non-convergence."); | 
|  | } | 
|  |  | 
|  | // Compute the join over all the reachable predecessor values. | 
|  | JoinEntryInstr* block = instr->block(); | 
|  | Object& value = Object::ZoneHandle(Z, Unknown()); | 
|  | for (intptr_t pred_idx = 0; pred_idx < instr->InputCount(); ++pred_idx) { | 
|  | if (reachable_->Contains( | 
|  | block->PredecessorAt(pred_idx)->preorder_number())) { | 
|  | Join(&value, instr->InputAt(pred_idx)->definition()->constant_value()); | 
|  | } | 
|  | } | 
|  | SetValue(instr, value); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) { | 
|  | if (instr->inserted_by_constant_propagation()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsConstant(value)) { | 
|  | SetValue(instr, value); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitReachabilityFence(ReachabilityFenceInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCheckArrayBound(CheckArrayBoundInstr* instr) { | 
|  | // Don't propagate constants through check, since it would eliminate | 
|  | // the data dependence between the bound check and the load/store. | 
|  | // Graph finalization will expose the constant eventually. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitGenericCheckBound(GenericCheckBoundInstr* instr) { | 
|  | // Don't propagate constants through check, since it would eliminate | 
|  | // the data dependence between the bound check and the load/store. | 
|  | // Graph finalization will expose the constant eventually. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCheckWritable(CheckWritableInstr* instr) { | 
|  | // Don't propagate constants through check, since it would eliminate | 
|  | // the data dependence between the writable check and its use. | 
|  | // Graph finalization will expose the constant eventually. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCheckNull(CheckNullInstr* instr) { | 
|  | // Don't propagate constants through check, since it would eliminate | 
|  | // the data dependence between the null check and its use. | 
|  | // Graph finalization will expose the constant eventually. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitParameter(ParameterInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitNativeParameter(NativeParameterInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitMoveArgument(MoveArgumentInstr* instr) { | 
|  | UNREACHABLE();  // Inserted right before register allocation. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAssertAssignable(AssertAssignableInstr* instr) { | 
|  | const auto& value = instr->value()->definition()->constant_value(); | 
|  | const auto& dst_type = instr->dst_type()->definition()->constant_value(); | 
|  |  | 
|  | if (IsNonConstant(value) || IsNonConstant(dst_type)) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } else if (IsUnknown(value) || IsUnknown(dst_type)) { | 
|  | return; | 
|  | } | 
|  | ASSERT(IsConstant(value) && IsConstant(dst_type)); | 
|  | if (dst_type.IsAbstractType()) { | 
|  | // We are ignoring the instantiator and instantiator_type_arguments, but | 
|  | // still monotonic and safe. | 
|  | if (instr->value()->Type()->IsAssignableTo(AbstractType::Cast(dst_type))) { | 
|  | SetValue(instr, value); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAssertSubtype(AssertSubtypeInstr* instr) {} | 
|  |  | 
|  | void ConstantPropagator::VisitAssertBoolean(AssertBooleanInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsBool()) { | 
|  | SetValue(instr, value); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitClosureCall(ClosureCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInstanceCall(InstanceCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitPolymorphicInstanceCall( | 
|  | PolymorphicInstanceCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDispatchTableCall(DispatchTableCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStaticCall(StaticCallInstr* instr) { | 
|  | const auto kind = instr->function().recognized_kind(); | 
|  | if (kind != MethodRecognizer::kUnknown) { | 
|  | if (instr->ArgumentCount() == 1) { | 
|  | const Object& argument = instr->ArgumentAt(0)->constant_value(); | 
|  | if (IsUnknown(argument)) { | 
|  | return; | 
|  | } | 
|  | if (IsConstant(argument)) { | 
|  | Object& value = Object::ZoneHandle(Z); | 
|  | if (instr->Evaluate(graph_, argument, &value)) { | 
|  | SetValue(instr, value); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } else if (instr->ArgumentCount() == 2) { | 
|  | const Object& argument1 = instr->ArgumentAt(0)->constant_value(); | 
|  | const Object& argument2 = instr->ArgumentAt(1)->constant_value(); | 
|  | if (IsUnknown(argument1) || IsUnknown(argument2)) { | 
|  | return; | 
|  | } | 
|  | if (IsConstant(argument1) && IsConstant(argument2)) { | 
|  | Object& value = Object::ZoneHandle(Z); | 
|  | if (instr->Evaluate(graph_, argument1, argument2, &value)) { | 
|  | SetValue(instr, value); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (kind) { | 
|  | case MethodRecognizer::kOneByteString_equality: | 
|  | case MethodRecognizer::kTwoByteString_equality: { | 
|  | ASSERT(instr->FirstArgIndex() == 0); | 
|  | // Use pure identity as a fast equality test. | 
|  | if (instr->ArgumentAt(0)->OriginalDefinition() == | 
|  | instr->ArgumentAt(1)->OriginalDefinition()) { | 
|  | SetValue(instr, Bool::True()); | 
|  | return; | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCachableIdempotentCall( | 
|  | CachableIdempotentCallInstr* instr) { | 
|  | // This instruction should not be inserted if its value is constant. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadLocal(LoadLocalInstr* instr) { | 
|  | // Instruction is eliminated when translating to SSA. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDropTemps(DropTempsInstr* instr) { | 
|  | // Instruction is eliminated when translating to SSA. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitMakeTemp(MakeTempInstr* instr) { | 
|  | // Instruction is eliminated when translating to SSA. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStoreLocal(StoreLocalInstr* instr) { | 
|  | // Instruction is eliminated when translating to SSA. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitIfThenElse(IfThenElseInstr* instr) { | 
|  | instr->comparison()->Accept(this); | 
|  | const Object& value = instr->comparison()->constant_value(); | 
|  | ASSERT(!value.IsNull()); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsBool()) { | 
|  | bool result = Bool::Cast(value).value(); | 
|  | SetValue(instr, Smi::Handle(Z, Smi::New(result ? instr->if_true() | 
|  | : instr->if_false()))); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStrictCompare(StrictCompareInstr* instr) { | 
|  | Definition* left_defn = instr->left()->definition(); | 
|  | Definition* right_defn = instr->right()->definition(); | 
|  |  | 
|  | Definition* unwrapped_left_defn = UnwrapPhi(left_defn); | 
|  | Definition* unwrapped_right_defn = UnwrapPhi(right_defn); | 
|  | if (unwrapped_left_defn == unwrapped_right_defn) { | 
|  | // Fold x === x, and x !== x to true/false. | 
|  | if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT))) { | 
|  | if (unwrapped_left_defn != left_defn) { | 
|  | MarkUnwrappedPhi(left_defn); | 
|  | } | 
|  | if (unwrapped_right_defn != right_defn) { | 
|  | MarkUnwrappedPhi(right_defn); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | const Object& left = left_defn->constant_value(); | 
|  | const Object& right = right_defn->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | if ((left.ptr() == Object::sentinel().ptr() && | 
|  | !instr->right()->Type()->can_be_sentinel()) || | 
|  | (right.ptr() == Object::sentinel().ptr() && | 
|  | !instr->left()->Type()->can_be_sentinel())) { | 
|  | // Handle provably false (EQ_STRICT) or true (NE_STRICT) sentinel checks. | 
|  | SetValue(instr, Bool::Get(instr->kind() != Token::kEQ_STRICT)); | 
|  | } else if ((left.IsNull() && | 
|  | instr->right()->Type()->HasDecidableNullability()) || | 
|  | (right.IsNull() && | 
|  | instr->left()->Type()->HasDecidableNullability())) { | 
|  | // TODO(vegorov): incorporate nullability information into the lattice. | 
|  | bool result = left.IsNull() ? instr->right()->Type()->IsNull() | 
|  | : instr->left()->Type()->IsNull(); | 
|  | if (instr->kind() == Token::kNE_STRICT) { | 
|  | result = !result; | 
|  | } | 
|  | SetValue(instr, Bool::Get(result)); | 
|  | } else { | 
|  | const intptr_t left_cid = instr->left()->Type()->ToCid(); | 
|  | const intptr_t right_cid = instr->right()->Type()->ToCid(); | 
|  | // If exact classes (cids) are known and they differ, the result | 
|  | // of strict compare can be computed. | 
|  | if ((left_cid != kDynamicCid) && (right_cid != kDynamicCid) && | 
|  | (left_cid != right_cid)) { | 
|  | const bool result = (instr->kind() != Token::kEQ_STRICT); | 
|  | SetValue(instr, Bool::Get(result)); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  | } else if (IsConstant(left) && IsConstant(right)) { | 
|  | bool result = IsIdenticalConstants(left, right); | 
|  | if (instr->kind() == Token::kNE_STRICT) { | 
|  | result = !result; | 
|  | } | 
|  | SetValue(instr, Bool::Get(result)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool CompareIntegers(Token::Kind kind, | 
|  | const Integer& left, | 
|  | const Integer& right) { | 
|  | const int result = left.CompareWith(right); | 
|  | switch (kind) { | 
|  | case Token::kEQ: | 
|  | return (result == 0); | 
|  | case Token::kNE: | 
|  | return (result != 0); | 
|  | case Token::kLT: | 
|  | return (result < 0); | 
|  | case Token::kGT: | 
|  | return (result > 0); | 
|  | case Token::kLTE: | 
|  | return (result <= 0); | 
|  | case Token::kGTE: | 
|  | return (result >= 0); | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Comparison instruction that is equivalent to the (left & right) == 0 | 
|  | // comparison pattern. | 
|  | void ConstantPropagator::VisitTestInt(TestIntInstr* instr) { | 
|  | const Object& left = instr->left()->definition()->constant_value(); | 
|  | const Object& right = instr->right()->definition()->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } else if (IsUnknown(left) || IsUnknown(right)) { | 
|  | return; | 
|  | } | 
|  | ASSERT(IsConstant(left) && IsConstant(right)); | 
|  | if (left.IsInteger() && right.IsInteger()) { | 
|  | const bool result = CompareIntegers( | 
|  | instr->kind(), | 
|  | Integer::Handle(Z, Integer::Cast(left).BitOp(Token::kBIT_AND, | 
|  | Integer::Cast(right))), | 
|  | Object::smi_zero()); | 
|  | SetValue(instr, result ? Bool::True() : Bool::False()); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitTestCids(TestCidsInstr* instr) { | 
|  | // TODO(sra): Constant fold test. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitTestRange(TestRangeInstr* instr) { | 
|  | const Object& input = instr->value()->definition()->constant_value(); | 
|  | if (IsNonConstant(input)) { | 
|  | SetValue(instr, non_constant_); | 
|  | } else if (IsConstant(input) && input.IsSmi()) { | 
|  | uword value = Smi::Cast(input).Value(); | 
|  | bool in_range = (instr->lower() <= value) && (value <= instr->upper()); | 
|  | ASSERT((instr->kind() == Token::kIS) || (instr->kind() == Token::kISNOT)); | 
|  | SetValue(instr, Bool::Get(in_range == (instr->kind() == Token::kIS))); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { | 
|  | Definition* left_defn = instr->left()->definition(); | 
|  | Definition* right_defn = instr->right()->definition(); | 
|  |  | 
|  | if (IsIntegerClassId(instr->operation_cid())) { | 
|  | // Fold x == x, and x != x to true/false for numbers comparisons. | 
|  | Definition* unwrapped_left_defn = UnwrapPhi(left_defn); | 
|  | Definition* unwrapped_right_defn = UnwrapPhi(right_defn); | 
|  | if (unwrapped_left_defn == unwrapped_right_defn) { | 
|  | // Fold x === x, and x !== x to true/false. | 
|  | if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ))) { | 
|  | if (unwrapped_left_defn != left_defn) { | 
|  | MarkUnwrappedPhi(left_defn); | 
|  | } | 
|  | if (unwrapped_right_defn != right_defn) { | 
|  | MarkUnwrappedPhi(right_defn); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | const Object& left = left_defn->constant_value(); | 
|  | const Object& right = right_defn->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | SetValue(instr, non_constant_); | 
|  | } else if (IsConstant(left) && IsConstant(right)) { | 
|  | if (left.IsInteger() && right.IsInteger()) { | 
|  | const bool result = CompareIntegers(instr->kind(), Integer::Cast(left), | 
|  | Integer::Cast(right)); | 
|  | SetValue(instr, Bool::Get(result)); | 
|  | } else if (left.IsString() && right.IsString()) { | 
|  | const bool result = String::Cast(left).Equals(String::Cast(right)); | 
|  | SetValue(instr, Bool::Get((instr->kind() == Token::kEQ) == result)); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitRelationalOp(RelationalOpInstr* instr) { | 
|  | const Object& left = instr->left()->definition()->constant_value(); | 
|  | const Object& right = instr->right()->definition()->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | SetValue(instr, non_constant_); | 
|  | } else if (IsConstant(left) && IsConstant(right)) { | 
|  | if (left.IsInteger() && right.IsInteger()) { | 
|  | const bool result = CompareIntegers(instr->kind(), Integer::Cast(left), | 
|  | Integer::Cast(right)); | 
|  | SetValue(instr, Bool::Get(result)); | 
|  | } else if (left.IsDouble() && right.IsDouble()) { | 
|  | // TODO(srdjan): Implement. | 
|  | SetValue(instr, non_constant_); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitNativeCall(NativeCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitFfiCall(FfiCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLeafRuntimeCall(LeafRuntimeCallInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDebugStepCheck(DebugStepCheckInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitRecordCoverage(RecordCoverageInstr* instr) { | 
|  | // Nothing to do. | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitOneByteStringFromCharCode( | 
|  | OneByteStringFromCharCodeInstr* instr) { | 
|  | const Object& o = instr->char_code()->definition()->constant_value(); | 
|  | if (IsUnknown(o)) { | 
|  | return; | 
|  | } | 
|  | if (o.IsSmi()) { | 
|  | const intptr_t ch_code = Smi::Cast(o).Value(); | 
|  | ASSERT(ch_code >= 0); | 
|  | if (ch_code < Symbols::kMaxOneCharCodeSymbol) { | 
|  | StringPtr* table = Symbols::PredefinedAddress(); | 
|  | SetValue(instr, String::ZoneHandle(Z, table[ch_code])); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStringToCharCode(StringToCharCodeInstr* instr) { | 
|  | const Object& o = instr->str()->definition()->constant_value(); | 
|  | if (IsUnknown(o)) { | 
|  | return; | 
|  | } | 
|  | if (o.IsString()) { | 
|  | const String& str = String::Cast(o); | 
|  | const intptr_t result = | 
|  | (str.Length() == 1) ? static_cast<intptr_t>(str.CharAt(0)) : -1; | 
|  | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(result))); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUtf8Scan(Utf8ScanInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadIndexed(LoadIndexedInstr* instr) { | 
|  | const Object& array_obj = instr->array()->definition()->constant_value(); | 
|  | const Object& index_obj = instr->index()->definition()->constant_value(); | 
|  | if (IsNonConstant(array_obj) || IsNonConstant(index_obj)) { | 
|  | SetValue(instr, non_constant_); | 
|  | } else if (IsConstant(array_obj) && IsConstant(index_obj)) { | 
|  | // Need index to be Smi and array to be either String or an immutable array. | 
|  | if (!index_obj.IsSmi()) { | 
|  | // Should not occur. | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | const intptr_t index = Smi::Cast(index_obj).Value(); | 
|  | if (index >= 0) { | 
|  | if (array_obj.IsString()) { | 
|  | const String& str = String::Cast(array_obj); | 
|  | if (str.Length() > index) { | 
|  | SetValue(instr, | 
|  | Smi::Handle( | 
|  | Z, Smi::New(static_cast<intptr_t>(str.CharAt(index))))); | 
|  | return; | 
|  | } | 
|  | } else if (array_obj.IsArray()) { | 
|  | const Array& a = Array::Cast(array_obj); | 
|  | if ((a.Length() > index) && a.IsImmutable()) { | 
|  | Instance& result = Instance::Handle(Z); | 
|  | result ^= a.At(index); | 
|  | SetValue(instr, result); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { | 
|  | // TODO(zerny): Implement constant propagation. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadIndexedUnsafe(LoadIndexedUnsafeInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) { | 
|  | // Cannot treat an initialized field as constant because the same code will be | 
|  | // used when the AppAOT or AppJIT starts over with everything uninitialized or | 
|  | // another isolate in the isolate group starts with everything uninitialized. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitStoreStaticField(StoreStaticFieldInstr* instr) { | 
|  | SetValue(instr, instr->value()->definition()->constant_value()); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBooleanNegate(BooleanNegateInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsBool()) { | 
|  | bool val = value.ptr() != Bool::True().ptr(); | 
|  | SetValue(instr, Bool::Get(val)); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoolToInt(BoolToIntInstr* instr) { | 
|  | // TODO(riscv) | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitIntToBool(IntToBoolInstr* instr) { | 
|  | // TODO(riscv) | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) { | 
|  | Definition* def = instr->value()->definition(); | 
|  | const Object& value = def->constant_value(); | 
|  | const AbstractType& checked_type = instr->type(); | 
|  | // If the checked type is a top type, the result is always true. | 
|  | if (checked_type.IsTopTypeForInstanceOf()) { | 
|  | SetValue(instr, Bool::True()); | 
|  | } else if (IsNonConstant(value)) { | 
|  | intptr_t value_cid = instr->value()->definition()->Type()->ToCid(); | 
|  | Representation rep = def->representation(); | 
|  | if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) || | 
|  | (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) || | 
|  | (checked_type.IsDoubleType() && (rep == kUnboxedDouble)) || | 
|  | (checked_type.IsIntType() && (rep == kUnboxedInt64))) { | 
|  | // Ensure that compile time type matches representation. | 
|  | ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) || | 
|  | ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) || | 
|  | ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) || | 
|  | ((rep == kUnboxedInt64) && (value_cid == kMintCid))); | 
|  | // The representation guarantees the type check to be true. | 
|  | SetValue(instr, Bool::True()); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } else if (IsConstant(value)) { | 
|  | if (value.IsInstance() && (value.ptr() != Object::sentinel().ptr())) { | 
|  | const Instance& instance = Instance::Cast(value); | 
|  | if (instr->instantiator_type_arguments()->BindsToConstantNull() && | 
|  | instr->function_type_arguments()->BindsToConstantNull()) { | 
|  | bool is_instance = | 
|  | instance.IsInstanceOf(checked_type, Object::null_type_arguments(), | 
|  | Object::null_type_arguments()); | 
|  | SetValue(instr, Bool::Get(is_instance)); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateTypedData(AllocateTypedDataInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateClosure(AllocateClosureInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateRecord(AllocateRecordInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateSmallRecord( | 
|  | AllocateSmallRecordInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCalculateElementAddress( | 
|  | CalculateElementAddressInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) { | 
|  | // This first part duplicates the work done in LoadClassIdInstr::Canonicalize, | 
|  | // which replaces uses of LoadClassIdInstr where the object has a concrete | 
|  | // type with a Constant. Canonicalize runs before the ConstantPropagation | 
|  | // pass, so if that was all, this wouldn't be needed. | 
|  | // | 
|  | // However, the ConstantPropagator also runs as part of OptimizeBranches, and | 
|  | // TypePropagation runs between it and the previous Canonicalize. Thus, the | 
|  | // type may have become concrete and we should take that into account. Not | 
|  | // doing so led to some benchmark regressions. | 
|  | intptr_t cid = instr->object()->Type()->ToCid(); | 
|  | if (cid != kDynamicCid) { | 
|  | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid))); | 
|  | return; | 
|  | } | 
|  | const Object& object = instr->object()->definition()->constant_value(); | 
|  | if (IsConstant(object)) { | 
|  | cid = object.GetClassId(); | 
|  | SetValue(instr, Smi::ZoneHandle(Z, Smi::New(cid))); | 
|  | return; | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) { | 
|  | Value* instance = instr->instance(); | 
|  | if ((instr->slot().kind() == Slot::Kind::kArray_length) && | 
|  | instance->definition()->OriginalDefinition()->IsCreateArray()) { | 
|  | Value* num_elements = instance->definition() | 
|  | ->OriginalDefinition() | 
|  | ->AsCreateArray() | 
|  | ->num_elements(); | 
|  | if (num_elements->BindsToConstant() && | 
|  | num_elements->BoundConstant().IsSmi()) { | 
|  | intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value(); | 
|  | const Object& result = Smi::ZoneHandle(Z, Smi::New(length)); | 
|  | SetValue(instr, result); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | const Object& constant = instance->definition()->constant_value(); | 
|  | if (IsConstant(constant)) { | 
|  | if (instr->IsImmutableLengthLoad()) { | 
|  | if (constant.IsString()) { | 
|  | SetValue(instr, | 
|  | Smi::ZoneHandle(Z, Smi::New(String::Cast(constant).Length()))); | 
|  | return; | 
|  | } | 
|  | if (constant.IsArray()) { | 
|  | SetValue(instr, | 
|  | Smi::ZoneHandle(Z, Smi::New(Array::Cast(constant).Length()))); | 
|  | return; | 
|  | } | 
|  | if (constant.IsTypedData()) { | 
|  | SetValue(instr, Smi::ZoneHandle( | 
|  | Z, Smi::New(TypedData::Cast(constant).Length()))); | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | Object& value = Object::Handle(); | 
|  | if (instr->Evaluate(constant, &value)) { | 
|  | SetValue(instr, Object::ZoneHandle(Z, value.ptr())); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) { | 
|  | TypeArguments& instantiator_type_args = TypeArguments::Handle(Z); | 
|  | TypeArguments& function_type_args = TypeArguments::Handle(Z); | 
|  | if (!instr->type().IsInstantiated(kCurrentClass)) { | 
|  | // Type refers to class type parameters. | 
|  | const Object& instantiator_type_args_obj = | 
|  | instr->instantiator_type_arguments()->definition()->constant_value(); | 
|  | if (IsUnknown(instantiator_type_args_obj)) { | 
|  | return; | 
|  | } | 
|  | if (instantiator_type_args_obj.IsTypeArguments()) { | 
|  | instantiator_type_args ^= instantiator_type_args_obj.ptr(); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | } | 
|  | if (!instr->type().IsInstantiated(kFunctions)) { | 
|  | // Type refers to function type parameters. | 
|  | const Object& function_type_args_obj = | 
|  | instr->function_type_arguments()->definition()->constant_value(); | 
|  | if (IsUnknown(function_type_args_obj)) { | 
|  | return; | 
|  | } | 
|  | if (function_type_args_obj.IsTypeArguments()) { | 
|  | function_type_args ^= function_type_args_obj.ptr(); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | } | 
|  | AbstractType& result = AbstractType::Handle( | 
|  | Z, instr->type().InstantiateFrom( | 
|  | instantiator_type_args, function_type_args, kAllFree, Heap::kOld)); | 
|  | ASSERT(result.IsInstantiated()); | 
|  | result = result.Canonicalize(T); | 
|  | SetValue(instr, result); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInstantiateTypeArguments( | 
|  | InstantiateTypeArgumentsInstr* instr) { | 
|  | const auto& type_arguments_obj = | 
|  | instr->type_arguments()->definition()->constant_value(); | 
|  | if (IsUnknown(type_arguments_obj)) { | 
|  | return; | 
|  | } | 
|  | if (type_arguments_obj.IsNull()) { | 
|  | SetValue(instr, type_arguments_obj); | 
|  | return; | 
|  | } | 
|  | if (!type_arguments_obj.IsTypeArguments()) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | const auto& type_arguments = TypeArguments::Cast(type_arguments_obj); | 
|  | if (type_arguments.IsInstantiated()) { | 
|  | ASSERT(type_arguments.IsCanonical()); | 
|  | SetValue(instr, type_arguments); | 
|  | return; | 
|  | } | 
|  | auto& instantiator_type_args = TypeArguments::Handle(Z); | 
|  | if (!type_arguments.IsInstantiated(kCurrentClass)) { | 
|  | // Type arguments refer to class type parameters. | 
|  | const Object& instantiator_type_args_obj = | 
|  | instr->instantiator_type_arguments()->definition()->constant_value(); | 
|  | if (IsUnknown(instantiator_type_args_obj)) { | 
|  | return; | 
|  | } | 
|  | if (!instantiator_type_args_obj.IsNull() && | 
|  | !instantiator_type_args_obj.IsTypeArguments()) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | instantiator_type_args ^= instantiator_type_args_obj.ptr(); | 
|  | if (instr->CanShareInstantiatorTypeArguments()) { | 
|  | SetValue(instr, instantiator_type_args); | 
|  | return; | 
|  | } | 
|  | } | 
|  | auto& function_type_args = TypeArguments::Handle(Z); | 
|  | if (!type_arguments.IsInstantiated(kFunctions)) { | 
|  | // Type arguments refer to function type parameters. | 
|  | const Object& function_type_args_obj = | 
|  | instr->function_type_arguments()->definition()->constant_value(); | 
|  | if (IsUnknown(function_type_args_obj)) { | 
|  | return; | 
|  | } | 
|  | if (!function_type_args_obj.IsNull() && | 
|  | !function_type_args_obj.IsTypeArguments()) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | function_type_args ^= function_type_args_obj.ptr(); | 
|  | if (instr->CanShareFunctionTypeArguments()) { | 
|  | SetValue(instr, function_type_args); | 
|  | return; | 
|  | } | 
|  | } | 
|  | auto& result = TypeArguments::Handle( | 
|  | Z, type_arguments.InstantiateFrom( | 
|  | instantiator_type_args, function_type_args, kAllFree, Heap::kOld)); | 
|  | ASSERT(result.IsInstantiated()); | 
|  | result = result.Canonicalize(T); | 
|  | SetValue(instr, result); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateContext(AllocateContextInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitAllocateUninitializedContext( | 
|  | AllocateUninitializedContextInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCloneContext(CloneContextInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op) { | 
|  | const Object& left = binary_op->left()->definition()->constant_value(); | 
|  | const Object& right = binary_op->right()->definition()->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | SetValue(binary_op, non_constant_); | 
|  | return; | 
|  | } else if (IsUnknown(left) || IsUnknown(right)) { | 
|  | return; | 
|  | } | 
|  | ASSERT(IsConstant(left) && IsConstant(right)); | 
|  | if (left.IsInteger() && right.IsInteger()) { | 
|  | const Integer& result = Integer::Handle( | 
|  | Z, Evaluator::BinaryIntegerEvaluate(left, right, binary_op->op_kind(), | 
|  | binary_op->is_truncating(), | 
|  | binary_op->representation(), T)); | 
|  | if (!result.IsNull()) { | 
|  | SetValue(binary_op, Integer::ZoneHandle(Z, result.ptr())); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SetValue(binary_op, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinaryInt32Op(BinaryInt32OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinaryUint32Op(BinaryUint32OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinaryInt64Op(BinaryInt64OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitShiftInt64Op(ShiftInt64OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitSpeculativeShiftInt64Op( | 
|  | SpeculativeShiftInt64OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitShiftUint32Op(ShiftUint32OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitSpeculativeShiftUint32Op( | 
|  | SpeculativeShiftUint32OpInstr* instr) { | 
|  | VisitBinaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) { | 
|  | VisitBox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) { | 
|  | VisitUnbox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitHashDoubleOp(HashDoubleOpInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsDouble()) { | 
|  | // TODO(aam): Add constant hash evaluation | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitHashIntegerOp(HashIntegerOpInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsInteger()) { | 
|  | // TODO(aam): Add constant hash evaluation | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnaryIntegerOp(UnaryIntegerOpInstr* unary_op) { | 
|  | const Object& value = unary_op->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsInteger()) { | 
|  | const Integer& result = Integer::Handle( | 
|  | Z, Evaluator::UnaryIntegerEvaluate(value, unary_op->op_kind(), | 
|  | unary_op->representation(), T)); | 
|  | if (!result.IsNull()) { | 
|  | SetValue(unary_op, Integer::ZoneHandle(Z, result.ptr())); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SetValue(unary_op, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnaryInt64Op(UnaryInt64OpInstr* instr) { | 
|  | VisitUnaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) { | 
|  | VisitUnaryIntegerOp(instr); | 
|  | } | 
|  |  | 
|  | static bool IsIntegerOrDouble(const Object& value) { | 
|  | return value.IsInteger() || value.IsDouble(); | 
|  | } | 
|  |  | 
|  | static double ToDouble(const Object& value) { | 
|  | return value.IsInteger() ? Integer::Cast(value).AsDoubleValue() | 
|  | : Double::Cast(value).value(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsDouble()) { | 
|  | const double result_val = Evaluator::EvaluateUnaryDoubleOp( | 
|  | ToDouble(value), instr->op_kind(), instr->representation()); | 
|  | const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val)); | 
|  | SetValue(instr, result); | 
|  | return; | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsInteger()) { | 
|  | SetValue(instr, | 
|  | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), | 
|  | Heap::kOld))); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInt64ToDouble(Int64ToDoubleInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsInteger()) { | 
|  | SetValue(instr, | 
|  | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), | 
|  | Heap::kOld))); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | if (value.IsInteger()) { | 
|  | SetValue(instr, | 
|  | Double::Handle(Z, Double::New(Integer::Cast(value).AsDoubleValue(), | 
|  | Heap::kOld))); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDoubleToInteger(DoubleToIntegerInstr* instr) { | 
|  | // TODO(kmillikin): Handle conversion. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDoubleToSmi(DoubleToSmiInstr* instr) { | 
|  | // TODO(kmillikin): Handle conversion. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDoubleToFloat(DoubleToFloatInstr* instr) { | 
|  | // TODO(kmillikin): Handle conversion. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitFloatToDouble(FloatToDoubleInstr* instr) { | 
|  | // TODO(kmillikin): Handle conversion. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitFloatCompare(FloatCompareInstr* instr) { | 
|  | // TODO(riscv) | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitInvokeMathCFunction( | 
|  | InvokeMathCFunctionInstr* instr) { | 
|  | // TODO(kmillikin): Handle conversion. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitTruncDivMod(TruncDivModInstr* instr) { | 
|  | // TODO(srdjan): Handle merged instruction. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitExtractNthOutput(ExtractNthOutputInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitMakePair(MakePairInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnboxLane(UnboxLaneInstr* instr) { | 
|  | if (BoxLanesInstr* box = instr->value()->definition()->AsBoxLanes()) { | 
|  | const Object& value = | 
|  | box->InputAt(instr->lane())->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | SetValue(instr, value); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoxLanes(BoxLanesInstr* instr) { | 
|  | // TODO(riscv) | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitConstant(ConstantInstr* instr) { | 
|  | SetValue(instr, instr->value()); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnboxedConstant(UnboxedConstantInstr* instr) { | 
|  | SetValue(instr, instr->value()); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitConstraint(ConstraintInstr* instr) { | 
|  | // Should not be used outside of range analysis. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitMaterializeObject(MaterializeObjectInstr* instr) { | 
|  | // Should not be used outside of allocation elimination pass. | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBinaryDoubleOp(BinaryDoubleOpInstr* instr) { | 
|  | const Object& left = instr->left()->definition()->constant_value(); | 
|  | const Object& right = instr->right()->definition()->constant_value(); | 
|  | if (IsNonConstant(left) || IsNonConstant(right)) { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } else if (IsUnknown(left) || IsUnknown(right)) { | 
|  | return; | 
|  | } | 
|  | ASSERT(IsConstant(left) && IsConstant(right)); | 
|  | const bool both_are_integers = left.IsInteger() && right.IsInteger(); | 
|  | if (IsIntegerOrDouble(left) && IsIntegerOrDouble(right) && | 
|  | !both_are_integers) { | 
|  | const double result_val = Evaluator::EvaluateBinaryDoubleOp( | 
|  | ToDouble(left), ToDouble(right), instr->op_kind(), | 
|  | instr->representation()); | 
|  | const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val)); | 
|  | SetValue(instr, result); | 
|  | return; | 
|  | } | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitDoubleTestOp(DoubleTestOpInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  | bool result; | 
|  | if (value.IsInteger()) { | 
|  | switch (instr->op_kind()) { | 
|  | case MethodRecognizer::kDouble_getIsNaN: | 
|  | FALL_THROUGH; | 
|  | case MethodRecognizer::kDouble_getIsInfinite: | 
|  | result = false; | 
|  | break; | 
|  | case MethodRecognizer::kDouble_getIsNegative: { | 
|  | result = Integer::Cast(value).IsNegative(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } else if (value.IsDouble()) { | 
|  | const double double_value = ToDouble(value); | 
|  | switch (instr->op_kind()) { | 
|  | case MethodRecognizer::kDouble_getIsNaN: { | 
|  | result = isnan(double_value); | 
|  | break; | 
|  | } | 
|  | case MethodRecognizer::kDouble_getIsInfinite: { | 
|  | result = isinf(double_value); | 
|  | break; | 
|  | } | 
|  | case MethodRecognizer::kDouble_getIsNegative: { | 
|  | result = signbit(double_value) && !isnan(double_value); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | return; | 
|  | } | 
|  | const bool is_negated = instr->kind() != Token::kEQ; | 
|  | SetValue(instr, Bool::Get(is_negated ? !result : result)); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitSimdOp(SimdOpInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* instr) { | 
|  | // TODO(srdjan): Handle min and max. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCaseInsensitiveCompare( | 
|  | CaseInsensitiveCompareInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnbox(UnboxInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SetValue(instr, value); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBox(BoxInstr* instr) { | 
|  | const Object& value = instr->value()->definition()->constant_value(); | 
|  | if (IsUnknown(value)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (instr->value()->definition()->representation() == | 
|  | instr->from_representation()) { | 
|  | SetValue(instr, value); | 
|  | } else { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoxSmallInt(BoxSmallIntInstr* instr) { | 
|  | VisitBox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) { | 
|  | VisitBox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) { | 
|  | VisitUnbox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) { | 
|  | VisitBox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) { | 
|  | VisitUnbox(instr); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitIntConverter(IntConverterInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitBitCast(BitCastInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitCall1ArgStub(Call1ArgStubInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitSuspend(SuspendInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitLoadThread(LoadThreadInstr* instr) { | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) { | 
|  | // TODO(kmillikin): Handle unary operations. | 
|  | SetValue(instr, non_constant_); | 
|  | } | 
|  |  | 
|  | // Insert redefinition for |original| definition which conveys information | 
|  | // that |original| is equal to |constant_value| in the dominated code. | 
|  | static RedefinitionInstr* InsertRedefinition(FlowGraph* graph, | 
|  | BlockEntryInstr* dom, | 
|  | Definition* original, | 
|  | const Object& constant_value) { | 
|  | auto redef = new RedefinitionInstr(new Value(original), | 
|  | /*inserted_by_constant_propagation=*/true); | 
|  |  | 
|  | graph->InsertAfter(dom, redef, nullptr, FlowGraph::kValue); | 
|  | graph->RenameDominatedUses(original, redef, redef); | 
|  |  | 
|  | if (redef->input_use_list() == nullptr) { | 
|  | // There are no dominated uses, so the newly added Redefinition is useless. | 
|  | redef->RemoveFromGraph(); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | redef->constant_value() = constant_value.ptr(); | 
|  | return redef; | 
|  | } | 
|  |  | 
|  | // Find all Branch(v eq constant) (eq being one of ==, !=, === or !==) in the | 
|  | // graph and redefine |v| in the true successor to record information about | 
|  | // it being equal to the constant. For comparisons between boolean values | 
|  | // we also redefine |v| in the false successor - because booleans have | 
|  | // only two possible values (e.g. if |v| is |true| in true successor, then | 
|  | // it is |false| in false successor). | 
|  | // | 
|  | // We don't actually _replace_ |v| with |constant| in the dominated code | 
|  | // because it might complicate subsequent optimizations (e.g. lead to | 
|  | // redundant phis). | 
|  | void ConstantPropagator::InsertRedefinitionsAfterEqualityComparisons() { | 
|  | for (auto block : graph_->reverse_postorder()) { | 
|  | if (auto branch = block->last_instruction()->AsBranch()) { | 
|  | auto comparison = branch->comparison(); | 
|  | if (comparison->IsStrictCompare() || | 
|  | (comparison->IsEqualityCompare() && | 
|  | comparison->operation_cid() != kDoubleCid)) { | 
|  | Value* value; | 
|  | ConstantInstr* constant_defn; | 
|  | if (comparison->IsComparisonWithConstant(&value, &constant_defn) && | 
|  | !value->BindsToConstant()) { | 
|  | const Object& constant_value = constant_defn->value(); | 
|  |  | 
|  | // Found comparison with constant. Introduce Redefinition(). | 
|  | ASSERT(comparison->kind() == Token::kNE_STRICT || | 
|  | comparison->kind() == Token::kNE || | 
|  | comparison->kind() == Token::kEQ_STRICT || | 
|  | comparison->kind() == Token::kEQ); | 
|  | const bool negated = (comparison->kind() == Token::kNE_STRICT || | 
|  | comparison->kind() == Token::kNE); | 
|  | const auto true_successor = | 
|  | negated ? branch->false_successor() : branch->true_successor(); | 
|  | InsertRedefinition(graph_, true_successor, value->definition(), | 
|  | constant_value); | 
|  |  | 
|  | // When comparing two boolean values we can also apply renaming | 
|  | // to the false successor because we know that only true and false | 
|  | // are possible values. | 
|  | if (constant_value.IsBool() && value->Type()->IsBool()) { | 
|  | const auto false_successor = | 
|  | negated ? branch->true_successor() : branch->false_successor(); | 
|  | InsertRedefinition(graph_, false_successor, value->definition(), | 
|  | Bool::Get(!Bool::Cast(constant_value).value())); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::Analyze() { | 
|  | InsertRedefinitionsAfterEqualityComparisons(); | 
|  |  | 
|  | GraphEntryInstr* entry = graph_->graph_entry(); | 
|  | reachable_->Add(entry->preorder_number()); | 
|  | block_worklist_.Add(entry); | 
|  |  | 
|  | while (true) { | 
|  | if (block_worklist_.is_empty()) { | 
|  | if (definition_worklist_.IsEmpty()) break; | 
|  | Definition* definition = definition_worklist_.RemoveLast(); | 
|  | for (Value* use = definition->input_use_list(); use != nullptr; | 
|  | use = use->next_use()) { | 
|  | use->instruction()->Accept(this); | 
|  | } | 
|  | } else { | 
|  | BlockEntryInstr* block = block_worklist_.RemoveLast(); | 
|  | block->Accept(this); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool HasPhis(BlockEntryInstr* block) { | 
|  | if (auto* join = block->AsJoinEntry()) { | 
|  | return (join->phis() != nullptr) && !join->phis()->is_empty(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool IsEmptyBlock(BlockEntryInstr* block) { | 
|  | // A block containing a goto to itself forms an infinite loop. | 
|  | // We don't consider this an empty block to handle the edge-case where code | 
|  | // reduces to an infinite loop. | 
|  | return block->next()->IsGoto() && | 
|  | block->next()->AsGoto()->successor() != block && !HasPhis(block) && | 
|  | !block->IsIndirectEntry(); | 
|  | } | 
|  |  | 
|  | // Traverses a chain of empty blocks and returns the first reachable non-empty | 
|  | // block that is not dominated by the start block. The empty blocks are added | 
|  | // to the supplied bit vector. | 
|  | static BlockEntryInstr* FindFirstNonEmptySuccessor(TargetEntryInstr* block, | 
|  | BitVector* empty_blocks) { | 
|  | BlockEntryInstr* current = block; | 
|  | while (IsEmptyBlock(current) && block->Dominates(current)) { | 
|  | ASSERT(!HasPhis(block)); | 
|  | empty_blocks->Add(current->preorder_number()); | 
|  | current = current->next()->AsGoto()->successor(); | 
|  | } | 
|  | return current; | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::EliminateRedundantBranches() { | 
|  | // Canonicalize branches that have no side-effects and where true- and | 
|  | // false-targets are the same. | 
|  | bool changed = false; | 
|  | BitVector* empty_blocks = new (Z) BitVector(Z, graph_->preorder().length()); | 
|  | for (BlockIterator b = graph_->postorder_iterator(); !b.Done(); b.Advance()) { | 
|  | BlockEntryInstr* block = b.Current(); | 
|  | BranchInstr* branch = block->last_instruction()->AsBranch(); | 
|  | empty_blocks->Clear(); | 
|  | if ((branch != nullptr) && !branch->HasUnknownSideEffects()) { | 
|  | ASSERT(branch->previous() != nullptr);  // Not already eliminated. | 
|  | BlockEntryInstr* if_true = | 
|  | FindFirstNonEmptySuccessor(branch->true_successor(), empty_blocks); | 
|  | BlockEntryInstr* if_false = | 
|  | FindFirstNonEmptySuccessor(branch->false_successor(), empty_blocks); | 
|  | if (if_true == if_false) { | 
|  | // Replace the branch with a jump to the common successor. | 
|  | // Drop the comparison, which does not have side effects | 
|  | JoinEntryInstr* join = if_true->AsJoinEntry(); | 
|  | if (!HasPhis(join)) { | 
|  | GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone); | 
|  | graph_->CopyDeoptTarget(jump, branch); | 
|  |  | 
|  | Instruction* previous = branch->previous(); | 
|  | branch->set_previous(nullptr); | 
|  | previous->LinkTo(jump); | 
|  |  | 
|  | // Remove uses from branch and all the empty blocks that | 
|  | // are now unreachable. | 
|  | branch->UnuseAllInputs(); | 
|  | for (BitVector::Iterator it(empty_blocks); !it.Done(); it.Advance()) { | 
|  | BlockEntryInstr* empty_block = graph_->preorder()[it.Current()]; | 
|  | empty_block->ClearAllInstructions(); | 
|  | } | 
|  |  | 
|  | changed = true; | 
|  |  | 
|  | if (FLAG_trace_constant_propagation && graph_->should_print()) { | 
|  | THR_Print("Eliminated branch in B%" Pd " common target B%" Pd "\n", | 
|  | block->block_id(), join->block_id()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (changed) { | 
|  | graph_->DiscoverBlocks(); | 
|  | graph_->MergeBlocks(); | 
|  | // TODO(fschneider): Update dominator tree in place instead of recomputing. | 
|  | GrowableArray<BitVector*> dominance_frontier; | 
|  | graph_->ComputeDominators(&dominance_frontier); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConstantPropagator::Transform() { | 
|  | // We will recompute dominators, block ordering, block ids, block last | 
|  | // instructions, previous pointers, predecessors, etc. after eliminating | 
|  | // unreachable code.  We do not maintain those properties during the | 
|  | // transformation. | 
|  | for (BlockIterator b = graph_->reverse_postorder_iterator(); !b.Done(); | 
|  | b.Advance()) { | 
|  | BlockEntryInstr* block = b.Current(); | 
|  | if (!reachable_->Contains(block->preorder_number())) { | 
|  | if (FLAG_trace_constant_propagation && graph_->should_print()) { | 
|  | THR_Print("Unreachable B%" Pd "\n", block->block_id()); | 
|  | } | 
|  | // Remove all uses in unreachable blocks. | 
|  | block->ClearAllInstructions(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | JoinEntryInstr* join = block->AsJoinEntry(); | 
|  | if (join != nullptr) { | 
|  | // Remove phi inputs corresponding to unreachable predecessor blocks. | 
|  | // Predecessors will be recomputed (in block id order) after removing | 
|  | // unreachable code so we merely have to keep the phi inputs in order. | 
|  | ZoneGrowableArray<PhiInstr*>* phis = join->phis(); | 
|  | if ((phis != nullptr) && !phis->is_empty()) { | 
|  | intptr_t pred_count = join->PredecessorCount(); | 
|  | intptr_t live_count = 0; | 
|  | for (intptr_t pred_idx = 0; pred_idx < pred_count; ++pred_idx) { | 
|  | if (reachable_->Contains( | 
|  | join->PredecessorAt(pred_idx)->preorder_number())) { | 
|  | if (live_count < pred_idx) { | 
|  | for (PhiIterator it(join); !it.Done(); it.Advance()) { | 
|  | PhiInstr* phi = it.Current(); | 
|  | ASSERT(phi != nullptr); | 
|  | phi->SetInputAt(live_count, phi->InputAt(pred_idx)); | 
|  | } | 
|  | } | 
|  | ++live_count; | 
|  | } else { | 
|  | for (PhiIterator it(join); !it.Done(); it.Advance()) { | 
|  | PhiInstr* phi = it.Current(); | 
|  | ASSERT(phi != nullptr); | 
|  | phi->InputAt(pred_idx)->RemoveFromUseList(); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (live_count < pred_count) { | 
|  | intptr_t to_idx = 0; | 
|  | for (intptr_t from_idx = 0; from_idx < phis->length(); ++from_idx) { | 
|  | PhiInstr* phi = (*phis)[from_idx]; | 
|  | ASSERT(phi != nullptr); | 
|  | if (FLAG_remove_redundant_phis && (live_count == 1)) { | 
|  | Value* input = phi->InputAt(0); | 
|  | phi->ReplaceUsesWith(input->definition()); | 
|  | input->RemoveFromUseList(); | 
|  | } else { | 
|  | phi->inputs_.TruncateTo(live_count); | 
|  | (*phis)[to_idx++] = phi; | 
|  | } | 
|  | } | 
|  | if (to_idx == 0) { | 
|  | join->phis_ = nullptr; | 
|  | } else { | 
|  | phis->TruncateTo(to_idx); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (join != nullptr) { | 
|  | for (PhiIterator it(join); !it.Done(); it.Advance()) { | 
|  | auto phi = it.Current(); | 
|  | if (TransformDefinition(phi)) { | 
|  | it.RemoveCurrentFromGraph(); | 
|  | } | 
|  | } | 
|  | } | 
|  | for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) { | 
|  | Definition* defn = i.Current()->AsDefinition(); | 
|  | if (TransformDefinition(defn)) { | 
|  | i.RemoveCurrentFromGraph(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Replace branches where one target is unreachable with jumps. | 
|  | BranchInstr* branch = block->last_instruction()->AsBranch(); | 
|  | if (branch != nullptr) { | 
|  | TargetEntryInstr* if_true = branch->true_successor(); | 
|  | TargetEntryInstr* if_false = branch->false_successor(); | 
|  | JoinEntryInstr* join = nullptr; | 
|  | Instruction* next = nullptr; | 
|  |  | 
|  | if (!reachable_->Contains(if_true->preorder_number())) { | 
|  | ASSERT(reachable_->Contains(if_false->preorder_number())); | 
|  | ASSERT(if_false->parallel_move() == nullptr); | 
|  | join = new (Z) JoinEntryInstr(if_false->block_id(), | 
|  | if_false->try_index(), DeoptId::kNone); | 
|  | graph_->CopyDeoptTarget(join, if_false); | 
|  | if_false->UnuseAllInputs(); | 
|  | next = if_false->next(); | 
|  | } else if (!reachable_->Contains(if_false->preorder_number())) { | 
|  | ASSERT(if_true->parallel_move() == nullptr); | 
|  | join = new (Z) JoinEntryInstr(if_true->block_id(), if_true->try_index(), | 
|  | DeoptId::kNone); | 
|  | graph_->CopyDeoptTarget(join, if_true); | 
|  | if_true->UnuseAllInputs(); | 
|  | next = if_true->next(); | 
|  | } | 
|  |  | 
|  | if (join != nullptr) { | 
|  | // Replace the branch with a jump to the reachable successor. | 
|  | // Drop the comparison, which does not have side effects as long | 
|  | // as it is a strict compare (the only one we can determine is | 
|  | // constant with the current analysis). | 
|  | GotoInstr* jump = new (Z) GotoInstr(join, DeoptId::kNone); | 
|  | graph_->CopyDeoptTarget(jump, branch); | 
|  |  | 
|  | Instruction* previous = branch->previous(); | 
|  | branch->set_previous(nullptr); | 
|  | previous->LinkTo(jump); | 
|  |  | 
|  | // Replace the false target entry with the new join entry. We will | 
|  | // recompute the dominators after this pass. | 
|  | join->LinkTo(next); | 
|  | branch->UnuseAllInputs(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | graph_->DiscoverBlocks(); | 
|  | graph_->MergeBlocks(); | 
|  | GrowableArray<BitVector*> dominance_frontier; | 
|  | graph_->ComputeDominators(&dominance_frontier); | 
|  | } | 
|  |  | 
|  | bool ConstantPropagator::TransformDefinition(Definition* defn) { | 
|  | if (defn == nullptr) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (auto redef = defn->AsRedefinition()) { | 
|  | if (redef->inserted_by_constant_propagation()) { | 
|  | redef->ReplaceUsesWith(redef->value()->definition()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (IsConstant(defn->constant_value()) && | 
|  | !IsConstant(defn->OriginalDefinition()->constant_value())) { | 
|  | // Redefinition might have become constant because some other | 
|  | // redefinition narrowed it, we should ignore this and not | 
|  | // replace it. | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Replace constant-valued instructions without observable side | 
|  | // effects.  Do this for smis and old objects only to avoid having to | 
|  | // copy other objects into the heap's old generation. | 
|  | if (IsConstant(defn->constant_value()) && | 
|  | (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) && | 
|  | !defn->IsConstant() && !defn->IsStoreIndexed() && !defn->IsStoreField() && | 
|  | !defn->IsStoreStaticField()) { | 
|  | if (FLAG_trace_constant_propagation && graph_->should_print()) { | 
|  | THR_Print("Constant v%" Pd " = %s\n", defn->ssa_temp_index(), | 
|  | defn->constant_value().ToCString()); | 
|  | } | 
|  | constant_value_ = defn->constant_value().ptr(); | 
|  | if ((constant_value_.IsString() || constant_value_.IsMint() || | 
|  | constant_value_.IsDouble()) && | 
|  | !constant_value_.IsCanonical()) { | 
|  | constant_value_ = Instance::Cast(constant_value_).Canonicalize(T); | 
|  | ASSERT(!constant_value_.IsNull()); | 
|  | } | 
|  | if (auto call = defn->AsStaticCall()) { | 
|  | ASSERT(!call->HasMoveArguments()); | 
|  | } | 
|  | Definition* replacement = | 
|  | graph_->TryCreateConstantReplacementFor(defn, constant_value_); | 
|  | if (replacement != defn) { | 
|  | defn->ReplaceUsesWith(replacement); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace dart |