From 009eb5d33ce8230611b059bc361097ef6ca97e48 Mon Sep 17 00:00:00 2001 From: "fschneider@google.com" Date: Mon, 29 Dec 2014 14:41:27 +0000 Subject: [PATCH] Move ConstantPropagator from flow_graph_optimizer.cc/.h into separate files. flow_graph_optimizer.cc is becoming unnecessarily large, so I'm moving self-contained optimization passes into separate files. In a separate CL I'll also move load/store optimization into separate files. R=srdjan@google.com Review URL: https://codereview.chromium.org//820883004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42567 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/vm/compiler.cc | 1 + runtime/vm/constant_propagator.cc | 1638 +++++++++++++++++++++++++++ runtime/vm/constant_propagator.h | 92 ++ runtime/vm/flow_graph_optimizer.cc | 1616 -------------------------- runtime/vm/flow_graph_optimizer.h | 77 -- runtime/vm/intermediate_language.cc | 2 +- runtime/vm/vm_sources.gypi | 2 + 7 files changed, 1734 insertions(+), 1694 deletions(-) create mode 100644 runtime/vm/constant_propagator.cc create mode 100644 runtime/vm/constant_propagator.h diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc index 922fb669ebb..aa0c5a13deb 100644 --- a/runtime/vm/compiler.cc +++ b/runtime/vm/compiler.cc @@ -11,6 +11,7 @@ #include "vm/cha.h" #include "vm/code_generator.h" #include "vm/code_patcher.h" +#include "vm/constant_propagator.h" #include "vm/dart_entry.h" #include "vm/debugger.h" #include "vm/deopt_instructions.h" diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc new file mode 100644 index 00000000000..efff680f2b5 --- /dev/null +++ b/runtime/vm/constant_propagator.cc @@ -0,0 +1,1638 @@ +// 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/constant_propagator.h" + +#include "vm/bit_vector.h" +#include "vm/flow_graph_builder.h" +#include "vm/flow_graph_compiler.h" +#include "vm/flow_graph_range_analysis.h" +#include "vm/il_printer.h" +#include "vm/intermediate_language.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 locally defined isolate() method. +#define I (isolate()) + + +ConstantPropagator::ConstantPropagator( + FlowGraph* graph, + const GrowableArray& ignored) + : FlowGraphVisitor(ignored), + graph_(graph), + unknown_(Object::unknown_constant()), + non_constant_(Object::non_constant()), + reachable_(new(graph->isolate()) BitVector( + graph->isolate(), graph->preorder().length())), + marked_phis_(new(graph->isolate()) BitVector( + graph->isolate(), graph->max_virtual_register_number())), + block_worklist_(), + definition_worklist_(graph, 10) {} + + +void ConstantPropagator::Optimize(FlowGraph* graph) { + GrowableArray ignored; + ConstantPropagator cp(graph, ignored); + cp.Analyze(); + cp.Transform(); +} + + +void ConstantPropagator::OptimizeBranches(FlowGraph* graph) { + GrowableArray 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().raw() == value.raw())); + // + // 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().raw() != value.raw()) { + definition->constant_value() = value.raw(); + if (definition->input_use_list() != NULL) { + definition_worklist_.Add(definition); + } + return true; + } + 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.raw(); + return; + } + + // Join(X, X) = X + // TODO(kmillikin): support equality for doubles, mints, etc. + if (left->raw() == right.raw()) return; + + // Join(X, Y) = non-constant + *left = non_constant_.raw(); +} + + +// -------------------------------------------------------------------------- +// 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) { + const GrowableArray& defs = *block->initial_definitions(); + for (intptr_t i = 0; i < defs.length(); ++i) { + defs[i]->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::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::VisitCatchBlockEntry(CatchBlockEntryInstr* block) { + const GrowableArray& defs = *block->initial_definitions(); + for (intptr_t i = 0; i < defs.length(); ++i) { + defs[i]->Accept(this); + } + 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::VisitReturn(ReturnInstr* instr) { + // Nothing to do. +} + + +void ConstantPropagator::VisitThrow(ThrowInstr* instr) { + // Nothing to do. +} + + +void ConstantPropagator::VisitReThrow(ReThrowInstr* 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()) { + it.Current()->Accept(this); + } +} + + +void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) { + 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() != NULL) { + 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.raw() == Bool::True().raw()) { + 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::VisitCheckClassId(CheckClassIdInstr* instr) { } + + +void ConstantPropagator::VisitGuardFieldClass(GuardFieldClassInstr* instr) { } + + +void ConstantPropagator::VisitGuardFieldLength(GuardFieldLengthInstr* instr) { } + + +void ConstantPropagator::VisitCheckSmi(CheckSmiInstr* instr) { } + + +void ConstantPropagator::VisitCheckEitherNonSmi( + CheckEitherNonSmiInstr* instr) { } + + +void ConstantPropagator::VisitCheckArrayBound(CheckArrayBoundInstr* 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 = NULL; + for (intptr_t i = 0; i < defn->InputCount(); ++i) { + if (reachable_->Contains(block->PredecessorAt(i)->preorder_number())) { + if (input == NULL) { + input = defn->InputAt(i)->definition(); + } else { + return defn; + } + } + } + + return input; + } + + return defn; +} + + +void ConstantPropagator::MarkPhi(Definition* phi) { + ASSERT(phi->IsPhi()); + marked_phis_->Add(phi->ssa_temp_index()); +} + + +// -------------------------------------------------------------------------- +// 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) { + // Compute the join over all the reachable predecessor values. + JoinEntryInstr* block = instr->block(); + Object& value = Object::ZoneHandle(I, 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()); + } + } + if (!SetValue(instr, value) && + marked_phis_->Contains(instr->ssa_temp_index())) { + marked_phis_->Remove(instr->ssa_temp_index()); + definition_worklist_.Add(instr); + } +} + + +void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) { + SetValue(instr, instr->value()->definition()->constant_value()); +} + + +void ConstantPropagator::VisitParameter(ParameterInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitPushArgument(PushArgumentInstr* instr) { + SetValue(instr, instr->value()->definition()->constant_value()); +} + + +void ConstantPropagator::VisitAssertAssignable(AssertAssignableInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // We are ignoring the instantiator and instantiator_type_arguments, but + // still monotonic and safe. + if (instr->value()->Type()->IsAssignableTo(instr->dst_type())) { + SetValue(instr, value); + } else { + SetValue(instr, non_constant_); + } + } +} + + +void ConstantPropagator::VisitAssertBoolean(AssertBooleanInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + if (value.IsBool()) { + SetValue(instr, value); + } else { + SetValue(instr, non_constant_); + } + } +} + + +void ConstantPropagator::VisitCurrentContext(CurrentContextInstr* instr) { + 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::VisitStaticCall(StaticCallInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitLoadLocal(LoadLocalInstr* instr) { + // Instruction is eliminated when translating to SSA. + UNREACHABLE(); +} + + +void ConstantPropagator::VisitPushTemp(PushTempInstr* instr) { + // Instruction is eliminated when translating to SSA. + UNREACHABLE(); +} + + +void ConstantPropagator::VisitDropTemps(DropTempsInstr* 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(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + ASSERT(!value.IsNull()); + ASSERT(value.IsBool()); + bool result = Bool::Cast(value).value(); + SetValue(instr, + Smi::Handle(I, Smi::New( + result ? instr->if_true() : instr->if_false()))); + } +} + + +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. + SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT)); + if (unwrapped_left_defn != left_defn) { + MarkPhi(left_defn); + } + if (unwrapped_right_defn != right_defn) { + MarkPhi(right_defn); + } + return; + } + + const Object& left = left_defn->constant_value(); + const Object& right = right_defn->constant_value(); + if (IsNonConstant(left) || IsNonConstant(right)) { + // TODO(vegorov): incorporate nullability information into the lattice. + if ((left.IsNull() && instr->right()->Type()->HasDecidableNullability()) || + (right.IsNull() && instr->left()->Type()->HasDecidableNullability())) { + 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 = (left.raw() == right.raw()); + 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; + } +} + + +void ConstantPropagator::VisitTestSmi(TestSmiInstr* 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::Handle(I, Integer::Cast(left).BitOp(Token::kBIT_AND, + Integer::Cast(right))), + Smi::Handle(I, Smi::New(0))); + SetValue(instr, result ? Bool::True() : Bool::False()); + } else { + SetValue(instr, non_constant_); + } + } +} + + +void ConstantPropagator::VisitTestCids(TestCidsInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { + Definition* left_defn = instr->left()->definition(); + Definition* right_defn = instr->right()->definition(); + + if (RawObject::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. + SetValue(instr, Bool::Get(instr->kind() == Token::kEQ)); + if (unwrapped_left_defn != left_defn) { + MarkPhi(left_defn); + } + if (unwrapped_right_defn != right_defn) { + MarkPhi(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::VisitDebugStepCheck(DebugStepCheckInstr* instr) { + // Nothing to do. +} + + +void ConstantPropagator::VisitStringFromCharCode( + StringFromCharCodeInstr* instr) { + const Object& o = instr->char_code()->definition()->constant_value(); + if (o.IsNull() || IsNonConstant(o)) { + SetValue(instr, non_constant_); + } else if (IsConstant(o)) { + const intptr_t ch_code = Smi::Cast(o).Value(); + ASSERT(ch_code >= 0); + if (ch_code < Symbols::kMaxOneCharCodeSymbol) { + RawString** table = Symbols::PredefinedAddress(); + SetValue(instr, String::ZoneHandle(I, table[ch_code])); + } else { + SetValue(instr, non_constant_); + } + } +} + + +void ConstantPropagator::VisitStringToCharCode(StringToCharCodeInstr* instr) { + const Object& o = instr->str()->definition()->constant_value(); + if (o.IsNull() || IsNonConstant(o)) { + SetValue(instr, non_constant_); + } else if (IsConstant(o)) { + const String& str = String::Cast(o); + const intptr_t result = + (str.Length() == 1) ? static_cast(str.CharAt(0)) : -1; + SetValue(instr, Smi::ZoneHandle(I, Smi::New(result))); + } +} + + +void ConstantPropagator::VisitStringInterpolate(StringInterpolateInstr* 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(I, + Smi::New(static_cast(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(I); + 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::VisitStoreIndexed(StoreIndexedInstr* instr) { + SetValue(instr, instr->value()->definition()->constant_value()); +} + + +void ConstantPropagator::VisitStoreInstanceField( + StoreInstanceFieldInstr* instr) { + SetValue(instr, instr->value()->definition()->constant_value()); +} + + +void ConstantPropagator::VisitInitStaticField(InitStaticFieldInstr* instr) { + // Nothing to do. +} + + +void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) { + const Field& field = instr->StaticField(); + ASSERT(field.is_static()); + if (field.is_final()) { + Instance& obj = Instance::Handle(I, field.value()); + ASSERT(obj.raw() != Object::sentinel().raw()); + ASSERT(obj.raw() != Object::transition_sentinel().raw()); + if (obj.IsSmi() || obj.IsOld()) { + SetValue(instr, obj); + return; + } + } + 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 (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + bool val = value.raw() != Bool::True().raw(); + SetValue(instr, Bool::Get(val)); + } +} + + +void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) { + const Definition* def = instr->value()->definition(); + const Object& value = def->constant_value(); + if (IsNonConstant(value)) { + const AbstractType& checked_type = instr->type(); + intptr_t value_cid = instr->value()->Type()->ToCid(); + Representation rep = def->representation(); + if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) || + (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) || + (checked_type.IsDoubleType() && (rep == kUnboxedDouble) && + FlowGraphCompiler::SupportsUnboxedDoubles()) || + (checked_type.IsIntType() && (rep == kUnboxedMint))) { + // Ensure that compile time type matches representation. + ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) || + ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) || + ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) || + ((rep == kUnboxedMint) && (value_cid == kMintCid))); + // The representation guarantees the type check to be true. + SetValue(instr, instr->negate_result() ? Bool::False() : Bool::True()); + } else { + SetValue(instr, non_constant_); + } + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle instanceof on constants. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) { + intptr_t cid = instr->object()->Type()->ToCid(); + if (cid != kDynamicCid) { + SetValue(instr, Smi::ZoneHandle(I, Smi::New(cid))); + return; + } + const Object& object = instr->object()->definition()->constant_value(); + if (IsConstant(object)) { + SetValue(instr, Smi::ZoneHandle(I, Smi::New(object.GetClassId()))); + return; + } + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) { + Value* instance = instr->instance(); + if ((instr->recognized_kind() == MethodRecognizer::kObjectArrayLength) && + 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(I, Smi::New(length)); + SetValue(instr, result); + return; + } + } + + if (instr->IsImmutableLengthLoad()) { + ConstantInstr* constant = + instance->definition()->OriginalDefinition()->AsConstant(); + if (constant != NULL) { + if (constant->value().IsString()) { + SetValue(instr, Smi::ZoneHandle(I, + Smi::New(String::Cast(constant->value()).Length()))); + return; + } + if (constant->value().IsArray()) { + SetValue(instr, Smi::ZoneHandle(I, + Smi::New(Array::Cast(constant->value()).Length()))); + return; + } + } + } + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) { + const Object& object = + instr->instantiator()->definition()->constant_value(); + if (IsNonConstant(object)) { + SetValue(instr, non_constant_); + return; + } + if (IsConstant(object)) { + if (instr->type().IsTypeParameter()) { + if (object.IsNull()) { + SetValue(instr, Type::ZoneHandle(I, Type::DynamicType())); + return; + } + // We could try to instantiate the type parameter and return it if no + // malformed error is reported. + } + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitInstantiateTypeArguments( + InstantiateTypeArgumentsInstr* instr) { + const Object& object = + instr->instantiator()->definition()->constant_value(); + if (IsNonConstant(object)) { + SetValue(instr, non_constant_); + return; + } + if (IsConstant(object)) { + const intptr_t len = instr->type_arguments().Length(); + if (instr->type_arguments().IsRawInstantiatedRaw(len) && + object.IsNull()) { + SetValue(instr, object); + return; + } + if (instr->type_arguments().IsUninstantiatedIdentity() || + instr->type_arguments().CanShareInstantiatorTypeArguments( + instr->instantiator_class())) { + SetValue(instr, object); + return; + } + SetValue(instr, non_constant_); + } +} + + +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 (IsConstant(left) && IsConstant(right)) { + if (left.IsInteger() && right.IsInteger()) { + const Integer& left_int = Integer::Cast(left); + const Integer& right_int = Integer::Cast(right); + const Integer& result = + Integer::Handle(I, binary_op->Evaluate(left_int, right_int)); + if (!result.IsNull()) { + SetValue(binary_op, Integer::ZoneHandle(I, result.raw())); + 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::VisitShiftUint32Op(ShiftUint32OpInstr* instr) { + VisitBinaryIntegerOp(instr); +} + + +void ConstantPropagator::VisitBinaryMintOp(BinaryMintOpInstr* instr) { + VisitBinaryIntegerOp(instr); +} + + +void ConstantPropagator::VisitShiftMintOp(ShiftMintOpInstr* instr) { + VisitBinaryIntegerOp(instr); +} + + +void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) { + // TODO(kmillikin): Handle box operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) { + // TODO(kmillikin): Handle unbox operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnaryMintOp(UnaryMintOpInstr* instr) { + // TODO(kmillikin): Handle unary operations. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle unary operations. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle unary operations. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsConstant(value) && value.IsInteger()) { + SetValue(instr, Double::Handle(I, + Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); + } else if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitMintToDouble(MintToDoubleInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsConstant(value) && value.IsInteger()) { + SetValue(instr, Double::Handle(I, + Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); + } else if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsConstant(value) && value.IsInteger()) { + SetValue(instr, Double::Handle(I, + Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); + } else if (IsNonConstant(value)) { + 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::VisitDoubleToDouble(DoubleToDoubleInstr* 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::VisitInvokeMathCFunction( + InvokeMathCFunctionInstr* instr) { + // TODO(kmillikin): Handle conversion. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitMergedMath(MergedMathInstr* instr) { + // TODO(srdjan): Handle merged instruction. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitExtractNthOutput(ExtractNthOutputInstr* instr) { + 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(); +} + + +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::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_); + } else if (left.IsInteger() && right.IsInteger()) { + SetValue(instr, non_constant_); + } else if (IsIntegerOrDouble(left) && IsIntegerOrDouble(right)) { + const double left_val = ToDouble(left); + const double right_val = ToDouble(right); + double result_val = 0.0; + switch (instr->op_kind()) { + case Token::kADD: + result_val = left_val + right_val; + break; + case Token::kSUB: + result_val = left_val - right_val; + break; + case Token::kMUL: + result_val = left_val * right_val; + break; + case Token::kDIV: + result_val = left_val / right_val; + break; + default: + UNREACHABLE(); + } + const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val)); + SetValue(instr, result); + } +} + + +void ConstantPropagator::VisitBinaryFloat32x4Op( + BinaryFloat32x4OpInstr* 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)) { + // TODO(kmillikin): Handle binary operation. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitFloat32x4Constructor( + Float32x4ConstructorInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitSimd32x4Shuffle(Simd32x4ShuffleInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitSimd32x4ShuffleMix( + Simd32x4ShuffleMixInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitSimd32x4GetSignMask( + Simd32x4GetSignMaskInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Zero(Float32x4ZeroInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Splat(Float32x4SplatInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Comparison( + Float32x4ComparisonInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4MinMax(Float32x4MinMaxInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Scale(Float32x4ScaleInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Sqrt(Float32x4SqrtInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4ZeroArg(Float32x4ZeroArgInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4Clamp(Float32x4ClampInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4With(Float32x4WithInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4ToInt32x4( + Float32x4ToInt32x4Instr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4Constructor( + Int32x4ConstructorInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4BoolConstructor( + Int32x4BoolConstructorInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4GetFlag(Int32x4GetFlagInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4SetFlag(Int32x4SetFlagInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4Select(Int32x4SelectInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitInt32x4ToFloat32x4( + Int32x4ToFloat32x4Instr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitBinaryInt32x4Op(BinaryInt32x4OpInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitSimd64x2Shuffle(Simd64x2ShuffleInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitBinaryFloat64x2Op(BinaryFloat64x2OpInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat32x4ToFloat64x2( + Float32x4ToFloat64x2Instr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2ToFloat32x4( + Float64x2ToFloat32x4Instr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2Zero( + Float64x2ZeroInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2Splat( + Float64x2SplatInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2Constructor( + Float64x2ConstructorInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2ZeroArg(Float64x2ZeroArgInstr* instr) { + // TODO(johnmccutchan): Implement constant propagation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitFloat64x2OneArg(Float64x2OneArgInstr* instr) { + // TODO(johnmccutchan): Implement constant propagation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitMathUnary(MathUnaryInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle Math's unary operations (sqrt, cos, sin). + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* 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)) { + // TODO(srdjan): Handle min and max. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitCaseInsensitiveCompareUC16( + CaseInsensitiveCompareUC16Instr *instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnbox(UnboxInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle conversion. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitBox(BoxInstr* instr) { + const Object& value = instr->value()->definition()->constant_value(); + if (IsNonConstant(value)) { + SetValue(instr, non_constant_); + } else if (IsConstant(value)) { + // TODO(kmillikin): Handle conversion. + SetValue(instr, non_constant_); + } +} + + +void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) { + // TODO(kmillikin): Handle box operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) { + // TODO(kmillikin): Handle unbox operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) { + // TODO(kmillikin): Handle box operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) { + // TODO(kmillikin): Handle unbox operation. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnboxedIntConverter( + UnboxedIntConverterInstr* instr) { + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) { + // TODO(kmillikin): Handle unary operations. + SetValue(instr, non_constant_); +} + + +void ConstantPropagator::Analyze() { + 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(); + Value* use = definition->input_use_list(); + while (use != NULL) { + use->instruction()->Accept(this); + use = use->next_use(); + } + } else { + BlockEntryInstr* block = block_worklist_.RemoveLast(); + block->Accept(this); + } + } +} + + +static bool IsEmptyBlock(BlockEntryInstr* block) { + return block->next()->IsGoto() && + (!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL)) && + !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(!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL)); + 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(I) BitVector(I, 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 != NULL) && branch->Effects().IsNone()) { + ASSERT(branch->previous() != NULL); // 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 (join->phis() == NULL) { + GotoInstr* jump = new(I) GotoInstr(if_true->AsJoinEntry()); + jump->InheritDeoptTarget(I, branch); + + Instruction* previous = branch->previous(); + branch->set_previous(NULL); + 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) { + OS::Print("Eliminated branch in B%" Pd " common target B%" Pd "\n", + block->block_id(), join->block_id()); + } + } + } + } + } + + if (changed) { + graph_->DiscoverBlocks(); + // TODO(fschneider): Update dominator tree in place instead of recomputing. + GrowableArray dominance_frontier; + graph_->ComputeDominators(&dominance_frontier); + } +} + + +void ConstantPropagator::Transform() { + if (FLAG_trace_constant_propagation) { + OS::Print("\n==== Before constant propagation ====\n"); + FlowGraphPrinter printer(*graph_); + printer.PrintBlocks(); + } + + // 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) { + OS::Print("Unreachable B%" Pd "\n", block->block_id()); + } + // Remove all uses in unreachable blocks. + block->ClearAllInstructions(); + continue; + } + + JoinEntryInstr* join = block->AsJoinEntry(); + if (join != NULL) { + // 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* phis = join->phis(); + if ((phis != NULL) && !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 != NULL); + 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 != NULL); + 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 != NULL); + 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_ = NULL; + } else { + phis->TruncateTo(to_idx); + } + } + } + } + + for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) { + Definition* defn = i.Current()->AsDefinition(); + // Replace constant-valued instructions without observable side + // effects. Do this for smis only to avoid having to copy other + // objects into the heap's old generation. + if ((defn != NULL) && + IsConstant(defn->constant_value()) && + (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) && + !defn->IsConstant() && + !defn->IsPushArgument() && + !defn->IsStoreIndexed() && + !defn->IsStoreInstanceField() && + !defn->IsStoreStaticField()) { + if (FLAG_trace_constant_propagation) { + OS::Print("Constant v%" Pd " = %s\n", + defn->ssa_temp_index(), + defn->constant_value().ToCString()); + } + ConstantInstr* constant = graph_->GetConstant(defn->constant_value()); + defn->ReplaceUsesWith(constant); + i.RemoveCurrentFromGraph(); + } + } + + // Replace branches where one target is unreachable with jumps. + BranchInstr* branch = block->last_instruction()->AsBranch(); + if (branch != NULL) { + TargetEntryInstr* if_true = branch->true_successor(); + TargetEntryInstr* if_false = branch->false_successor(); + JoinEntryInstr* join = NULL; + Instruction* next = NULL; + + if (!reachable_->Contains(if_true->preorder_number())) { + ASSERT(reachable_->Contains(if_false->preorder_number())); + ASSERT(if_false->parallel_move() == NULL); + ASSERT(if_false->loop_info() == NULL); + join = new(I) JoinEntryInstr(if_false->block_id(), + if_false->try_index()); + join->InheritDeoptTarget(I, if_false); + if_false->UnuseAllInputs(); + next = if_false->next(); + } else if (!reachable_->Contains(if_false->preorder_number())) { + ASSERT(if_true->parallel_move() == NULL); + ASSERT(if_true->loop_info() == NULL); + join = new(I) JoinEntryInstr(if_true->block_id(), + if_true->try_index()); + join->InheritDeoptTarget(I, if_true); + if_true->UnuseAllInputs(); + next = if_true->next(); + } + + if (join != NULL) { + // 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(I) GotoInstr(join); + jump->InheritDeoptTarget(I, branch); + + Instruction* previous = branch->previous(); + branch->set_previous(NULL); + 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 dominance_frontier; + graph_->ComputeDominators(&dominance_frontier); + + if (FLAG_trace_constant_propagation) { + OS::Print("\n==== After constant propagation ====\n"); + FlowGraphPrinter printer(*graph_); + printer.PrintBlocks(); + } +} + +} // namespace dart diff --git a/runtime/vm/constant_propagator.h b/runtime/vm/constant_propagator.h new file mode 100644 index 00000000000..c8a2b5925ad --- /dev/null +++ b/runtime/vm/constant_propagator.h @@ -0,0 +1,92 @@ +// Copyright (c) 2012, 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. + +#ifndef VM_CONSTANT_PROPAGATOR_H_ +#define VM_CONSTANT_PROPAGATOR_H_ + +#include "vm/intermediate_language.h" +#include "vm/flow_graph.h" + +namespace dart { + +// Sparse conditional constant propagation and unreachable code elimination. +// Assumes that use lists are computed and preserves them. +class ConstantPropagator : public FlowGraphVisitor { + public: + ConstantPropagator(FlowGraph* graph, + const GrowableArray& ignored); + + static void Optimize(FlowGraph* graph); + + // (1) Visit branches to optimize away unreachable blocks discovered by range + // analysis. + // (2) Eliminate branches that have the same true- and false-target: For + // example, this occurs after expressions like + // + // if (a == null || b == null) { + // ... + // } + // + // where b is known to be null. + static void OptimizeBranches(FlowGraph* graph); + + // Used to initialize the abstract value of definitions. + static RawObject* Unknown() { return Object::unknown_constant().raw(); } + + private: + void Analyze(); + void Transform(); + void EliminateRedundantBranches(); + + void SetReachable(BlockEntryInstr* block); + bool SetValue(Definition* definition, const Object& value); + + Definition* UnwrapPhi(Definition* defn); + void MarkPhi(Definition* defn); + + // Assign the join (least upper bound) of a pair of abstract values to the + // first one. + void Join(Object* left, const Object& right); + + bool IsUnknown(const Object& value) { + return value.raw() == unknown_.raw(); + } + bool IsNonConstant(const Object& value) { + return value.raw() == non_constant_.raw(); + } + bool IsConstant(const Object& value) { + return !IsNonConstant(value) && !IsUnknown(value); + } + + void VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op); + + virtual void VisitBlocks() { UNREACHABLE(); } + +#define DECLARE_VISIT(type) virtual void Visit##type(type##Instr* instr); + FOR_EACH_INSTRUCTION(DECLARE_VISIT) +#undef DECLARE_VISIT + + Isolate* isolate() const { return graph_->isolate(); } + + FlowGraph* graph_; + + // Sentinels for unknown constant and non-constant values. + const Object& unknown_; + const Object& non_constant_; + + // Analysis results. For each block, a reachability bit. Indexed by + // preorder number. + BitVector* reachable_; + + BitVector* marked_phis_; + + // Worklists of blocks and definitions. + GrowableArray block_worklist_; + DefinitionWorklist definition_worklist_; +}; + + +} // namespace dart + +#endif // VM_CONSTANT_PROPAGATOR_H_ diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc index cf75b91425a..75519d29955 100644 --- a/runtime/vm/flow_graph_optimizer.cc +++ b/runtime/vm/flow_graph_optimizer.cc @@ -33,9 +33,6 @@ DEFINE_FLAG(int, max_polymorphic_checks, 4, DEFINE_FLAG(int, max_equality_polymorphic_checks, 32, "Maximum number of polymorphic checks in equality operator," " otherwise use megamorphic dispatch."); -DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis."); -DEFINE_FLAG(bool, trace_constant_propagation, false, - "Print constant propagation and useless code elimination."); DEFINE_FLAG(bool, trace_load_optimization, false, "Print live sets for load optimization pass."); DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); @@ -7500,1619 +7497,6 @@ bool DominatorBasedCSE::OptimizeRecursive( } -ConstantPropagator::ConstantPropagator( - FlowGraph* graph, - const GrowableArray& ignored) - : FlowGraphVisitor(ignored), - graph_(graph), - unknown_(Object::unknown_constant()), - non_constant_(Object::non_constant()), - reachable_(new(graph->isolate()) BitVector( - graph->isolate(), graph->preorder().length())), - marked_phis_(new(graph->isolate()) BitVector( - graph->isolate(), graph->max_virtual_register_number())), - block_worklist_(), - definition_worklist_(graph, 10) {} - - -void ConstantPropagator::Optimize(FlowGraph* graph) { - GrowableArray ignored; - ConstantPropagator cp(graph, ignored); - cp.Analyze(); - cp.Transform(); -} - - -void ConstantPropagator::OptimizeBranches(FlowGraph* graph) { - GrowableArray 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().raw() == value.raw())); - // - // 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().raw() != value.raw()) { - definition->constant_value() = value.raw(); - if (definition->input_use_list() != NULL) { - definition_worklist_.Add(definition); - } - return true; - } - 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.raw(); - return; - } - - // Join(X, X) = X - // TODO(kmillikin): support equality for doubles, mints, etc. - if (left->raw() == right.raw()) return; - - // Join(X, Y) = non-constant - *left = non_constant_.raw(); -} - - -// -------------------------------------------------------------------------- -// 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) { - const GrowableArray& defs = *block->initial_definitions(); - for (intptr_t i = 0; i < defs.length(); ++i) { - defs[i]->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::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::VisitCatchBlockEntry(CatchBlockEntryInstr* block) { - const GrowableArray& defs = *block->initial_definitions(); - for (intptr_t i = 0; i < defs.length(); ++i) { - defs[i]->Accept(this); - } - 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::VisitReturn(ReturnInstr* instr) { - // Nothing to do. -} - - -void ConstantPropagator::VisitThrow(ThrowInstr* instr) { - // Nothing to do. -} - - -void ConstantPropagator::VisitReThrow(ReThrowInstr* 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()) { - it.Current()->Accept(this); - } -} - - -void ConstantPropagator::VisitIndirectGoto(IndirectGotoInstr* instr) { - 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() != NULL) { - 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.raw() == Bool::True().raw()) { - 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::VisitCheckClassId(CheckClassIdInstr* instr) { } - - -void ConstantPropagator::VisitGuardFieldClass(GuardFieldClassInstr* instr) { } - - -void ConstantPropagator::VisitGuardFieldLength(GuardFieldLengthInstr* instr) { } - - -void ConstantPropagator::VisitCheckSmi(CheckSmiInstr* instr) { } - - -void ConstantPropagator::VisitCheckEitherNonSmi( - CheckEitherNonSmiInstr* instr) { } - - -void ConstantPropagator::VisitCheckArrayBound(CheckArrayBoundInstr* 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 = NULL; - for (intptr_t i = 0; i < defn->InputCount(); ++i) { - if (reachable_->Contains(block->PredecessorAt(i)->preorder_number())) { - if (input == NULL) { - input = defn->InputAt(i)->definition(); - } else { - return defn; - } - } - } - - return input; - } - - return defn; -} - - -void ConstantPropagator::MarkPhi(Definition* phi) { - ASSERT(phi->IsPhi()); - marked_phis_->Add(phi->ssa_temp_index()); -} - - -// -------------------------------------------------------------------------- -// 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) { - // Compute the join over all the reachable predecessor values. - JoinEntryInstr* block = instr->block(); - Object& value = Object::ZoneHandle(I, 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()); - } - } - if (!SetValue(instr, value) && - marked_phis_->Contains(instr->ssa_temp_index())) { - marked_phis_->Remove(instr->ssa_temp_index()); - definition_worklist_.Add(instr); - } -} - - -void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) { - SetValue(instr, instr->value()->definition()->constant_value()); -} - - -void ConstantPropagator::VisitParameter(ParameterInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitPushArgument(PushArgumentInstr* instr) { - SetValue(instr, instr->value()->definition()->constant_value()); -} - - -void ConstantPropagator::VisitAssertAssignable(AssertAssignableInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // We are ignoring the instantiator and instantiator_type_arguments, but - // still monotonic and safe. - if (instr->value()->Type()->IsAssignableTo(instr->dst_type())) { - SetValue(instr, value); - } else { - SetValue(instr, non_constant_); - } - } -} - - -void ConstantPropagator::VisitAssertBoolean(AssertBooleanInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - if (value.IsBool()) { - SetValue(instr, value); - } else { - SetValue(instr, non_constant_); - } - } -} - - -void ConstantPropagator::VisitCurrentContext(CurrentContextInstr* instr) { - 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::VisitStaticCall(StaticCallInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitLoadLocal(LoadLocalInstr* instr) { - // Instruction is eliminated when translating to SSA. - UNREACHABLE(); -} - - -void ConstantPropagator::VisitPushTemp(PushTempInstr* instr) { - // Instruction is eliminated when translating to SSA. - UNREACHABLE(); -} - - -void ConstantPropagator::VisitDropTemps(DropTempsInstr* 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(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - ASSERT(!value.IsNull()); - ASSERT(value.IsBool()); - bool result = Bool::Cast(value).value(); - SetValue(instr, - Smi::Handle(I, Smi::New( - result ? instr->if_true() : instr->if_false()))); - } -} - - -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. - SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT)); - if (unwrapped_left_defn != left_defn) { - MarkPhi(left_defn); - } - if (unwrapped_right_defn != right_defn) { - MarkPhi(right_defn); - } - return; - } - - const Object& left = left_defn->constant_value(); - const Object& right = right_defn->constant_value(); - if (IsNonConstant(left) || IsNonConstant(right)) { - // TODO(vegorov): incorporate nullability information into the lattice. - if ((left.IsNull() && instr->right()->Type()->HasDecidableNullability()) || - (right.IsNull() && instr->left()->Type()->HasDecidableNullability())) { - 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 = (left.raw() == right.raw()); - 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; - } -} - - -void ConstantPropagator::VisitTestSmi(TestSmiInstr* 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::Handle(I, Integer::Cast(left).BitOp(Token::kBIT_AND, - Integer::Cast(right))), - Smi::Handle(I, Smi::New(0))); - SetValue(instr, result ? Bool::True() : Bool::False()); - } else { - SetValue(instr, non_constant_); - } - } -} - - -void ConstantPropagator::VisitTestCids(TestCidsInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { - Definition* left_defn = instr->left()->definition(); - Definition* right_defn = instr->right()->definition(); - - if (RawObject::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. - SetValue(instr, Bool::Get(instr->kind() == Token::kEQ)); - if (unwrapped_left_defn != left_defn) { - MarkPhi(left_defn); - } - if (unwrapped_right_defn != right_defn) { - MarkPhi(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::VisitDebugStepCheck(DebugStepCheckInstr* instr) { - // Nothing to do. -} - - -void ConstantPropagator::VisitStringFromCharCode( - StringFromCharCodeInstr* instr) { - const Object& o = instr->char_code()->definition()->constant_value(); - if (o.IsNull() || IsNonConstant(o)) { - SetValue(instr, non_constant_); - } else if (IsConstant(o)) { - const intptr_t ch_code = Smi::Cast(o).Value(); - ASSERT(ch_code >= 0); - if (ch_code < Symbols::kMaxOneCharCodeSymbol) { - RawString** table = Symbols::PredefinedAddress(); - SetValue(instr, String::ZoneHandle(I, table[ch_code])); - } else { - SetValue(instr, non_constant_); - } - } -} - - -void ConstantPropagator::VisitStringToCharCode(StringToCharCodeInstr* instr) { - const Object& o = instr->str()->definition()->constant_value(); - if (o.IsNull() || IsNonConstant(o)) { - SetValue(instr, non_constant_); - } else if (IsConstant(o)) { - const String& str = String::Cast(o); - const intptr_t result = - (str.Length() == 1) ? static_cast(str.CharAt(0)) : -1; - SetValue(instr, Smi::ZoneHandle(I, Smi::New(result))); - } -} - - -void ConstantPropagator::VisitStringInterpolate(StringInterpolateInstr* 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(I, - Smi::New(static_cast(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(I); - 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::VisitStoreIndexed(StoreIndexedInstr* instr) { - SetValue(instr, instr->value()->definition()->constant_value()); -} - - -void ConstantPropagator::VisitStoreInstanceField( - StoreInstanceFieldInstr* instr) { - SetValue(instr, instr->value()->definition()->constant_value()); -} - - -void ConstantPropagator::VisitInitStaticField(InitStaticFieldInstr* instr) { - // Nothing to do. -} - - -void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) { - const Field& field = instr->StaticField(); - ASSERT(field.is_static()); - if (field.is_final()) { - Instance& obj = Instance::Handle(I, field.value()); - ASSERT(obj.raw() != Object::sentinel().raw()); - ASSERT(obj.raw() != Object::transition_sentinel().raw()); - if (obj.IsSmi() || obj.IsOld()) { - SetValue(instr, obj); - return; - } - } - 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 (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - bool val = value.raw() != Bool::True().raw(); - SetValue(instr, Bool::Get(val)); - } -} - - -void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) { - const Definition* def = instr->value()->definition(); - const Object& value = def->constant_value(); - if (IsNonConstant(value)) { - const AbstractType& checked_type = instr->type(); - intptr_t value_cid = instr->value()->Type()->ToCid(); - Representation rep = def->representation(); - if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) || - (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) || - (checked_type.IsDoubleType() && (rep == kUnboxedDouble) && - CanUnboxDouble()) || - (checked_type.IsIntType() && (rep == kUnboxedMint))) { - // Ensure that compile time type matches representation. - ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) || - ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) || - ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) || - ((rep == kUnboxedMint) && (value_cid == kMintCid))); - // The representation guarantees the type check to be true. - SetValue(instr, instr->negate_result() ? Bool::False() : Bool::True()); - } else { - SetValue(instr, non_constant_); - } - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle instanceof on constants. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitCreateArray(CreateArrayInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitAllocateObject(AllocateObjectInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) { - intptr_t cid = instr->object()->Type()->ToCid(); - if (cid != kDynamicCid) { - SetValue(instr, Smi::ZoneHandle(I, Smi::New(cid))); - return; - } - const Object& object = instr->object()->definition()->constant_value(); - if (IsConstant(object)) { - SetValue(instr, Smi::ZoneHandle(I, Smi::New(object.GetClassId()))); - return; - } - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) { - Value* instance = instr->instance(); - if ((instr->recognized_kind() == MethodRecognizer::kObjectArrayLength) && - 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(I, Smi::New(length)); - SetValue(instr, result); - return; - } - } - - if (instr->IsImmutableLengthLoad()) { - ConstantInstr* constant = - instance->definition()->OriginalDefinition()->AsConstant(); - if (constant != NULL) { - if (constant->value().IsString()) { - SetValue(instr, Smi::ZoneHandle(I, - Smi::New(String::Cast(constant->value()).Length()))); - return; - } - if (constant->value().IsArray()) { - SetValue(instr, Smi::ZoneHandle(I, - Smi::New(Array::Cast(constant->value()).Length()))); - return; - } - } - } - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInstantiateType(InstantiateTypeInstr* instr) { - const Object& object = - instr->instantiator()->definition()->constant_value(); - if (IsNonConstant(object)) { - SetValue(instr, non_constant_); - return; - } - if (IsConstant(object)) { - if (instr->type().IsTypeParameter()) { - if (object.IsNull()) { - SetValue(instr, Type::ZoneHandle(I, Type::DynamicType())); - return; - } - // We could try to instantiate the type parameter and return it if no - // malformed error is reported. - } - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitInstantiateTypeArguments( - InstantiateTypeArgumentsInstr* instr) { - const Object& object = - instr->instantiator()->definition()->constant_value(); - if (IsNonConstant(object)) { - SetValue(instr, non_constant_); - return; - } - if (IsConstant(object)) { - const intptr_t len = instr->type_arguments().Length(); - if (instr->type_arguments().IsRawInstantiatedRaw(len) && - object.IsNull()) { - SetValue(instr, object); - return; - } - if (instr->type_arguments().IsUninstantiatedIdentity() || - instr->type_arguments().CanShareInstantiatorTypeArguments( - instr->instantiator_class())) { - SetValue(instr, object); - return; - } - SetValue(instr, non_constant_); - } -} - - -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 (IsConstant(left) && IsConstant(right)) { - if (left.IsInteger() && right.IsInteger()) { - const Integer& left_int = Integer::Cast(left); - const Integer& right_int = Integer::Cast(right); - const Integer& result = - Integer::Handle(I, binary_op->Evaluate(left_int, right_int)); - if (!result.IsNull()) { - SetValue(binary_op, Integer::ZoneHandle(I, result.raw())); - 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::VisitShiftUint32Op(ShiftUint32OpInstr* instr) { - VisitBinaryIntegerOp(instr); -} - - -void ConstantPropagator::VisitBinaryMintOp(BinaryMintOpInstr* instr) { - VisitBinaryIntegerOp(instr); -} - - -void ConstantPropagator::VisitShiftMintOp(ShiftMintOpInstr* instr) { - VisitBinaryIntegerOp(instr); -} - - -void ConstantPropagator::VisitBoxInt64(BoxInt64Instr* instr) { - // TODO(kmillikin): Handle box operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnboxInt64(UnboxInt64Instr* instr) { - // TODO(kmillikin): Handle unbox operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnaryMintOp(UnaryMintOpInstr* instr) { - // TODO(kmillikin): Handle unary operations. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnarySmiOp(UnarySmiOpInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle unary operations. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitUnaryDoubleOp(UnaryDoubleOpInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle unary operations. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitSmiToDouble(SmiToDoubleInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsConstant(value) && value.IsInteger()) { - SetValue(instr, Double::Handle(I, - Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); - } else if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitMintToDouble(MintToDoubleInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsConstant(value) && value.IsInteger()) { - SetValue(instr, Double::Handle(I, - Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); - } else if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitInt32ToDouble(Int32ToDoubleInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsConstant(value) && value.IsInteger()) { - SetValue(instr, Double::Handle(I, - Double::New(Integer::Cast(value).AsDoubleValue(), Heap::kOld))); - } else if (IsNonConstant(value)) { - 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::VisitDoubleToDouble(DoubleToDoubleInstr* 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::VisitInvokeMathCFunction( - InvokeMathCFunctionInstr* instr) { - // TODO(kmillikin): Handle conversion. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitMergedMath(MergedMathInstr* instr) { - // TODO(srdjan): Handle merged instruction. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitExtractNthOutput(ExtractNthOutputInstr* instr) { - 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(); -} - - -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::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_); - } else if (left.IsInteger() && right.IsInteger()) { - SetValue(instr, non_constant_); - } else if (IsIntegerOrDouble(left) && IsIntegerOrDouble(right)) { - const double left_val = ToDouble(left); - const double right_val = ToDouble(right); - double result_val = 0.0; - switch (instr->op_kind()) { - case Token::kADD: - result_val = left_val + right_val; - break; - case Token::kSUB: - result_val = left_val - right_val; - break; - case Token::kMUL: - result_val = left_val * right_val; - break; - case Token::kDIV: - result_val = left_val / right_val; - break; - default: - UNREACHABLE(); - } - const Double& result = Double::ZoneHandle(Double::NewCanonical(result_val)); - SetValue(instr, result); - } -} - - -void ConstantPropagator::VisitBinaryFloat32x4Op( - BinaryFloat32x4OpInstr* 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)) { - // TODO(kmillikin): Handle binary operation. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitFloat32x4Constructor( - Float32x4ConstructorInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitSimd32x4Shuffle(Simd32x4ShuffleInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitSimd32x4ShuffleMix( - Simd32x4ShuffleMixInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitSimd32x4GetSignMask( - Simd32x4GetSignMaskInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Zero(Float32x4ZeroInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Splat(Float32x4SplatInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Comparison( - Float32x4ComparisonInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4MinMax(Float32x4MinMaxInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Scale(Float32x4ScaleInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Sqrt(Float32x4SqrtInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4ZeroArg(Float32x4ZeroArgInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4Clamp(Float32x4ClampInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4With(Float32x4WithInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4ToInt32x4( - Float32x4ToInt32x4Instr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4Constructor( - Int32x4ConstructorInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4BoolConstructor( - Int32x4BoolConstructorInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4GetFlag(Int32x4GetFlagInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4SetFlag(Int32x4SetFlagInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4Select(Int32x4SelectInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitInt32x4ToFloat32x4( - Int32x4ToFloat32x4Instr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitBinaryInt32x4Op(BinaryInt32x4OpInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitSimd64x2Shuffle(Simd64x2ShuffleInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitBinaryFloat64x2Op(BinaryFloat64x2OpInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat32x4ToFloat64x2( - Float32x4ToFloat64x2Instr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2ToFloat32x4( - Float64x2ToFloat32x4Instr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2Zero( - Float64x2ZeroInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2Splat( - Float64x2SplatInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2Constructor( - Float64x2ConstructorInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2ZeroArg(Float64x2ZeroArgInstr* instr) { - // TODO(johnmccutchan): Implement constant propagation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitFloat64x2OneArg(Float64x2OneArgInstr* instr) { - // TODO(johnmccutchan): Implement constant propagation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitMathUnary(MathUnaryInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle Math's unary operations (sqrt, cos, sin). - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitMathMinMax(MathMinMaxInstr* 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)) { - // TODO(srdjan): Handle min and max. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitCaseInsensitiveCompareUC16( - CaseInsensitiveCompareUC16Instr *instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnbox(UnboxInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle conversion. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitBox(BoxInstr* instr) { - const Object& value = instr->value()->definition()->constant_value(); - if (IsNonConstant(value)) { - SetValue(instr, non_constant_); - } else if (IsConstant(value)) { - // TODO(kmillikin): Handle conversion. - SetValue(instr, non_constant_); - } -} - - -void ConstantPropagator::VisitBoxUint32(BoxUint32Instr* instr) { - // TODO(kmillikin): Handle box operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnboxUint32(UnboxUint32Instr* instr) { - // TODO(kmillikin): Handle unbox operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitBoxInt32(BoxInt32Instr* instr) { - // TODO(kmillikin): Handle box operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnboxInt32(UnboxInt32Instr* instr) { - // TODO(kmillikin): Handle unbox operation. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnboxedIntConverter( - UnboxedIntConverterInstr* instr) { - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::VisitUnaryUint32Op(UnaryUint32OpInstr* instr) { - // TODO(kmillikin): Handle unary operations. - SetValue(instr, non_constant_); -} - - -void ConstantPropagator::Analyze() { - 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(); - Value* use = definition->input_use_list(); - while (use != NULL) { - use->instruction()->Accept(this); - use = use->next_use(); - } - } else { - BlockEntryInstr* block = block_worklist_.RemoveLast(); - block->Accept(this); - } - } -} - - -static bool IsEmptyBlock(BlockEntryInstr* block) { - return block->next()->IsGoto() && - (!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL)) && - !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(!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL)); - 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(I) BitVector(I, 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 != NULL) && branch->Effects().IsNone()) { - ASSERT(branch->previous() != NULL); // 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 (join->phis() == NULL) { - GotoInstr* jump = new(I) GotoInstr(if_true->AsJoinEntry()); - jump->InheritDeoptTarget(I, branch); - - Instruction* previous = branch->previous(); - branch->set_previous(NULL); - 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) { - OS::Print("Eliminated branch in B%" Pd " common target B%" Pd "\n", - block->block_id(), join->block_id()); - } - } - } - } - } - - if (changed) { - graph_->DiscoverBlocks(); - // TODO(fschneider): Update dominator tree in place instead of recomputing. - GrowableArray dominance_frontier; - graph_->ComputeDominators(&dominance_frontier); - } -} - - -void ConstantPropagator::Transform() { - if (FLAG_trace_constant_propagation) { - OS::Print("\n==== Before constant propagation ====\n"); - FlowGraphPrinter printer(*graph_); - printer.PrintBlocks(); - } - - // 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) { - OS::Print("Unreachable B%" Pd "\n", block->block_id()); - } - // Remove all uses in unreachable blocks. - block->ClearAllInstructions(); - continue; - } - - JoinEntryInstr* join = block->AsJoinEntry(); - if (join != NULL) { - // 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* phis = join->phis(); - if ((phis != NULL) && !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 != NULL); - 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 != NULL); - 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 != NULL); - 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_ = NULL; - } else { - phis->TruncateTo(to_idx); - } - } - } - } - - for (ForwardInstructionIterator i(block); !i.Done(); i.Advance()) { - Definition* defn = i.Current()->AsDefinition(); - // Replace constant-valued instructions without observable side - // effects. Do this for smis only to avoid having to copy other - // objects into the heap's old generation. - if ((defn != NULL) && - IsConstant(defn->constant_value()) && - (defn->constant_value().IsSmi() || defn->constant_value().IsOld()) && - !defn->IsConstant() && - !defn->IsPushArgument() && - !defn->IsStoreIndexed() && - !defn->IsStoreInstanceField() && - !defn->IsStoreStaticField()) { - if (FLAG_trace_constant_propagation) { - OS::Print("Constant v%" Pd " = %s\n", - defn->ssa_temp_index(), - defn->constant_value().ToCString()); - } - ConstantInstr* constant = graph_->GetConstant(defn->constant_value()); - defn->ReplaceUsesWith(constant); - i.RemoveCurrentFromGraph(); - } - } - - // Replace branches where one target is unreachable with jumps. - BranchInstr* branch = block->last_instruction()->AsBranch(); - if (branch != NULL) { - TargetEntryInstr* if_true = branch->true_successor(); - TargetEntryInstr* if_false = branch->false_successor(); - JoinEntryInstr* join = NULL; - Instruction* next = NULL; - - if (!reachable_->Contains(if_true->preorder_number())) { - ASSERT(reachable_->Contains(if_false->preorder_number())); - ASSERT(if_false->parallel_move() == NULL); - ASSERT(if_false->loop_info() == NULL); - join = new(I) JoinEntryInstr(if_false->block_id(), - if_false->try_index()); - join->InheritDeoptTarget(I, if_false); - if_false->UnuseAllInputs(); - next = if_false->next(); - } else if (!reachable_->Contains(if_false->preorder_number())) { - ASSERT(if_true->parallel_move() == NULL); - ASSERT(if_true->loop_info() == NULL); - join = new(I) JoinEntryInstr(if_true->block_id(), - if_true->try_index()); - join->InheritDeoptTarget(I, if_true); - if_true->UnuseAllInputs(); - next = if_true->next(); - } - - if (join != NULL) { - // 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(I) GotoInstr(join); - jump->InheritDeoptTarget(I, branch); - - Instruction* previous = branch->previous(); - branch->set_previous(NULL); - 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 dominance_frontier; - graph_->ComputeDominators(&dominance_frontier); - - if (FLAG_trace_constant_propagation) { - OS::Print("\n==== After constant propagation ====\n"); - FlowGraphPrinter printer(*graph_); - printer.PrintBlocks(); - } -} - - // Returns true if the given phi has a single input use and // is used in the environments either at the corresponding block entry or // at the same instruction where input use is. diff --git a/runtime/vm/flow_graph_optimizer.h b/runtime/vm/flow_graph_optimizer.h index b5ba79ca521..ce2290df202 100644 --- a/runtime/vm/flow_graph_optimizer.h +++ b/runtime/vm/flow_graph_optimizer.h @@ -316,83 +316,6 @@ class DeadCodeElimination : public AllStatic { }; -// Sparse conditional constant propagation and unreachable code elimination. -// Assumes that use lists are computed and preserves them. -class ConstantPropagator : public FlowGraphVisitor { - public: - ConstantPropagator(FlowGraph* graph, - const GrowableArray& ignored); - - static void Optimize(FlowGraph* graph); - - // (1) Visit branches to optimize away unreachable blocks discovered by range - // analysis. - // (2) Eliminate branches that have the same true- and false-target: For - // example, this occurs after expressions like - // - // if (a == null || b == null) { - // ... - // } - // - // where b is known to be null. - static void OptimizeBranches(FlowGraph* graph); - - // Used to initialize the abstract value of definitions. - static RawObject* Unknown() { return Object::unknown_constant().raw(); } - - private: - void Analyze(); - void Transform(); - void EliminateRedundantBranches(); - - void SetReachable(BlockEntryInstr* block); - bool SetValue(Definition* definition, const Object& value); - - Definition* UnwrapPhi(Definition* defn); - void MarkPhi(Definition* defn); - - // Assign the join (least upper bound) of a pair of abstract values to the - // first one. - void Join(Object* left, const Object& right); - - bool IsUnknown(const Object& value) { - return value.raw() == unknown_.raw(); - } - bool IsNonConstant(const Object& value) { - return value.raw() == non_constant_.raw(); - } - bool IsConstant(const Object& value) { - return !IsNonConstant(value) && !IsUnknown(value); - } - - void VisitBinaryIntegerOp(BinaryIntegerOpInstr* binary_op); - - virtual void VisitBlocks() { UNREACHABLE(); } - -#define DECLARE_VISIT(type) virtual void Visit##type(type##Instr* instr); - FOR_EACH_INSTRUCTION(DECLARE_VISIT) -#undef DECLARE_VISIT - - Isolate* isolate() const { return graph_->isolate(); } - - FlowGraph* graph_; - - // Sentinels for unknown constant and non-constant values. - const Object& unknown_; - const Object& non_constant_; - - // Analysis results. For each block, a reachability bit. Indexed by - // preorder number. - BitVector* reachable_; - - BitVector* marked_phis_; - - // Worklists of blocks and definitions. - GrowableArray block_worklist_; - DefinitionWorklist definition_worklist_; -}; - - // Rewrite branches to eliminate materialization of boolean values after // inlining, and to expose other optimizations (e.g., constant folding of // branches, unreachable code elimination). diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc index dacba9930c2..df597c811db 100644 --- a/runtime/vm/intermediate_language.cc +++ b/runtime/vm/intermediate_language.cc @@ -5,6 +5,7 @@ #include "vm/intermediate_language.h" #include "vm/bit_vector.h" +#include "vm/constant_propagator.h" #include "vm/cpu.h" #include "vm/dart_entry.h" #include "vm/flow_graph_allocator.h" @@ -39,7 +40,6 @@ DEFINE_FLAG(bool, unbox_numeric_fields, true, DECLARE_FLAG(bool, enable_type_checks); DECLARE_FLAG(bool, eliminate_type_checks); DECLARE_FLAG(bool, trace_optimization); -DECLARE_FLAG(bool, trace_constant_propagation); DECLARE_FLAG(bool, throw_on_javascript_int_overflow); Definition::Definition(intptr_t deopt_id) diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi index 4bdb9c55d54..e869eb38261 100644 --- a/runtime/vm/vm_sources.gypi +++ b/runtime/vm/vm_sources.gypi @@ -95,6 +95,8 @@ 'compiler_stats.cc', 'compiler_stats.h', 'compiler_test.cc', + 'constant_propagator.h', + 'constant_propagator.cc', 'constants_arm.h', 'constants_arm64.h', 'constants_ia32.h',