// 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. #ifndef RUNTIME_VM_INTERMEDIATE_LANGUAGE_H_ #define RUNTIME_VM_INTERMEDIATE_LANGUAGE_H_ #include "vm/allocation.h" #include "vm/ast.h" #include "vm/growable_array.h" #include "vm/locations.h" #include "vm/method_recognizer.h" #include "vm/object.h" #include "vm/parser.h" #include "vm/token_position.h" namespace dart { class BitVector; class BlockEntryInstr; class BoxIntegerInstr; class BufferFormatter; class CatchBlockEntryInstr; class ComparisonInstr; class Definition; class Environment; class FlowGraph; class FlowGraphBuilder; class FlowGraphCompiler; class FlowGraphVisitor; class Instruction; class LocalVariable; class ParsedFunction; class Range; class RangeAnalysis; class RangeBoundary; class UnboxIntegerInstr; // CompileType describes type of the value produced by the definition. // // It captures the following properties: // - whether value can potentially be null or it is definitely not null; // - concrete class id of the value or kDynamicCid if unknown statically; // - abstract super type of the value, concrete type of the value in runtime // is guaranteed to be sub type of this type. // // Values of CompileType form a lattice with a None type as a bottom and a // nullable Dynamic type as a top element. Method Union provides a join // operation for the lattice. class CompileType : public ValueObject { public: static const bool kNullable = true; static const bool kNonNullable = false; CompileType(bool is_nullable, intptr_t cid, const AbstractType* type) : is_nullable_(is_nullable), cid_(cid), type_(type) { } CompileType(const CompileType& other) : ValueObject(), is_nullable_(other.is_nullable_), cid_(other.cid_), type_(other.type_) { } CompileType& operator=(const CompileType& other) { is_nullable_ = other.is_nullable_; cid_ = other.cid_; type_ = other.type_; return *this; } bool is_nullable() const { return is_nullable_; } // Return type such that concrete value's type in runtime is guaranteed to // be subtype of it. const AbstractType* ToAbstractType(); // Return class id such that it is either kDynamicCid or in runtime // value is guaranteed to have an equal class id. intptr_t ToCid(); // Return class id such that it is either kDynamicCid or in runtime // value is guaranteed to be either null or have an equal class id. intptr_t ToNullableCid(); // Returns true if the value is guaranteed to be not-null or is known to be // always null. bool HasDecidableNullability(); // Returns true if the value is known to be always null. bool IsNull(); // Returns true if this type is more specific than given type. bool IsMoreSpecificThan(const AbstractType& other); // Returns true if value of this type is assignable to a location of the // given type. bool IsAssignableTo(const AbstractType& type) { bool is_instance; return CanComputeIsInstanceOf(type, kNullable, &is_instance) && is_instance; } // Create a new CompileType representing given combination of class id and // abstract type. The pair is assumed to be coherent. static CompileType Create(intptr_t cid, const AbstractType& type); CompileType CopyNonNullable() const { return CompileType(kNonNullable, cid_, type_); } static CompileType CreateNullable(bool is_nullable, intptr_t cid) { return CompileType(is_nullable, cid, NULL); } // Create a new CompileType representing given abstract type. By default // values as assumed to be nullable. static CompileType FromAbstractType(const AbstractType& type, bool is_nullable = kNullable); // Create a new CompileType representing an value with the given class id. // Resulting CompileType is nullable only if cid is kDynamicCid or kNullCid. static CompileType FromCid(intptr_t cid); // Create None CompileType. It is the bottom of the lattice and is used to // represent type of the phi that was not yet inferred. static CompileType None() { return CompileType(kNullable, kIllegalCid, NULL); } // Create Dynamic CompileType. It is the top of the lattice and is used to // represent unknown type. static CompileType Dynamic(); static CompileType Null(); // Create non-nullable Bool type. static CompileType Bool(); // Create non-nullable Int type. static CompileType Int(); // Create non-nullable Smi type. static CompileType Smi(); // Create non-nullable String type. static CompileType String(); // Perform a join operation over the type lattice. void Union(CompileType* other); // Returns true if this and other types are the same. bool IsEqualTo(CompileType* other) { return (is_nullable_ == other->is_nullable_) && (ToNullableCid() == other->ToNullableCid()) && (ToAbstractType()->Equals(*other->ToAbstractType())); } bool IsNone() const { return (cid_ == kIllegalCid) && (type_ == NULL); } bool IsInt() { return !is_nullable() && ((ToCid() == kSmiCid) || (ToCid() == kMintCid) || ((type_ != NULL) && (type_->Equals(Type::Handle(Type::IntType()))))); } void PrintTo(BufferFormatter* f) const; const char* ToCString() const; private: bool CanComputeIsInstanceOf(const AbstractType& type, bool is_nullable, bool* is_instance); bool is_nullable_; intptr_t cid_; const AbstractType* type_; }; // Zone allocated wrapper for the CompileType value. class ZoneCompileType : public ZoneAllocated { public: static CompileType* Wrap(const CompileType& type) { ZoneCompileType* zone_type = new ZoneCompileType(type); return zone_type->ToCompileType(); } CompileType* ToCompileType() { return &type_; } protected: explicit ZoneCompileType(const CompileType& type) : type_(type) { } CompileType type_; }; // ConstrainedCompileType represents a compile type that is computed from // another compile type. class ConstrainedCompileType : public ZoneCompileType { public: virtual ~ConstrainedCompileType() { } // Recompute compile type. virtual void Update() = 0; protected: explicit ConstrainedCompileType(const CompileType& type) : ZoneCompileType(type) { } }; // NotNullConstrainedCompileType represents not-null constraint applied to // the source compile type. Result is non-nullable version of the incoming // compile type. It is used to represent compile type propagated downwards // from strict comparison with the null constant. class NotNullConstrainedCompileType : public ConstrainedCompileType { public: explicit NotNullConstrainedCompileType(CompileType* source) : ConstrainedCompileType(source->CopyNonNullable()), source_(source) { } virtual void Update() { type_ = source_->CopyNonNullable(); } private: CompileType* source_; }; class EffectSet : public ValueObject { public: enum Effects { kNoEffects = 0, kExternalization = 1, kLastEffect = kExternalization }; EffectSet(const EffectSet& other) : ValueObject(), effects_(other.effects_) { } bool IsNone() const { return effects_ == kNoEffects; } static EffectSet None() { return EffectSet(kNoEffects); } static EffectSet All() { ASSERT(EffectSet::kLastEffect == 1); return EffectSet(kExternalization); } static EffectSet Externalization() { return EffectSet(kExternalization); } bool ToInt() { return effects_; } private: explicit EffectSet(intptr_t effects) : effects_(effects) { } intptr_t effects_; }; class Value : public ZoneAllocated { public: // A forward iterator that allows removing the current value from the // underlying use list during iteration. class Iterator { public: explicit Iterator(Value* head) : next_(head) { Advance(); } Value* Current() const { return current_; } bool Done() const { return current_ == NULL; } void Advance() { // Pre-fetch next on advance and cache it. current_ = next_; if (next_ != NULL) next_ = next_->next_use(); } private: Value* current_; Value* next_; }; explicit Value(Definition* definition) : definition_(definition), previous_use_(NULL), next_use_(NULL), instruction_(NULL), use_index_(-1), reaching_type_(NULL) { } Definition* definition() const { return definition_; } void set_definition(Definition* definition) { definition_ = definition; } Value* previous_use() const { return previous_use_; } void set_previous_use(Value* previous) { previous_use_ = previous; } Value* next_use() const { return next_use_; } void set_next_use(Value* next) { next_use_ = next; } bool IsSingleUse() const { return (next_use_ == NULL) && (previous_use_ == NULL); } Instruction* instruction() const { return instruction_; } void set_instruction(Instruction* instruction) { instruction_ = instruction; } intptr_t use_index() const { return use_index_; } void set_use_index(intptr_t index) { use_index_ = index; } static void AddToList(Value* value, Value** list); void RemoveFromUseList(); // Change the definition after use lists have been computed. inline void BindTo(Definition* definition); inline void BindToEnvironment(Definition* definition); Value* Copy(Zone* zone) { return new(zone) Value(definition_); } // This function must only be used when the new Value is dominated by // the original Value. Value* CopyWithType() { Value* copy = new Value(definition_); copy->reaching_type_ = reaching_type_; return copy; } CompileType* Type(); void SetReachingType(CompileType* type) { reaching_type_ = type; } void PrintTo(BufferFormatter* f) const; const char* ToCString() const; bool IsSmiValue() { return Type()->ToCid() == kSmiCid; } // Return true if the value represents a constant. bool BindsToConstant() const; // Return true if the value represents the constant null. bool BindsToConstantNull() const; // Assert if BindsToConstant() is false, otherwise returns the constant value. const Object& BoundConstant() const; // Compile time constants, Bool, Smi and Nulls do not need to update // the store buffer. bool NeedsStoreBuffer(); bool Equals(Value* other) const; private: friend class FlowGraphPrinter; Definition* definition_; Value* previous_use_; Value* next_use_; Instruction* instruction_; intptr_t use_index_; CompileType* reaching_type_; DISALLOW_COPY_AND_ASSIGN(Value); }; // An embedded container with N elements of type T. Used (with partial // specialization for N=0) because embedded arrays cannot have size 0. template class EmbeddedArray { public: EmbeddedArray() : elements_() { } intptr_t length() const { return N; } const T& operator[](intptr_t i) const { ASSERT(i < length()); return elements_[i]; } T& operator[](intptr_t i) { ASSERT(i < length()); return elements_[i]; } const T& At(intptr_t i) const { return (*this)[i]; } void SetAt(intptr_t i, const T& val) { (*this)[i] = val; } private: T elements_[N]; }; template class EmbeddedArray { public: intptr_t length() const { return 0; } const T& operator[](intptr_t i) const { UNREACHABLE(); static T sentinel = 0; return sentinel; } T& operator[](intptr_t i) { UNREACHABLE(); static T sentinel = 0; return sentinel; } }; // Instructions. // M is a single argument macro. It is applied to each concrete instruction // type name. The concrete instruction classes are the name with Instr // concatenated. #define FOR_EACH_INSTRUCTION(M) \ M(GraphEntry) \ M(JoinEntry) \ M(TargetEntry) \ M(IndirectEntry) \ M(CatchBlockEntry) \ M(Phi) \ M(Redefinition) \ M(Parameter) \ M(ParallelMove) \ M(PushArgument) \ M(Return) \ M(Throw) \ M(ReThrow) \ M(Stop) \ M(Goto) \ M(IndirectGoto) \ M(Branch) \ M(AssertAssignable) \ M(AssertBoolean) \ M(CurrentContext) \ M(ClosureCall) \ M(InstanceCall) \ M(PolymorphicInstanceCall) \ M(StaticCall) \ M(LoadLocal) \ M(DropTemps) \ M(StoreLocal) \ M(StrictCompare) \ M(EqualityCompare) \ M(RelationalOp) \ M(NativeCall) \ M(DebugStepCheck) \ M(LoadIndexed) \ M(LoadCodeUnits) \ M(StoreIndexed) \ M(StoreInstanceField) \ M(InitStaticField) \ M(LoadStaticField) \ M(StoreStaticField) \ M(BooleanNegate) \ M(InstanceOf) \ M(CreateArray) \ M(AllocateObject) \ M(LoadField) \ M(LoadUntagged) \ M(LoadClassId) \ M(InstantiateType) \ M(InstantiateTypeArguments) \ M(AllocateContext) \ M(AllocateUninitializedContext) \ M(CloneContext) \ M(BinarySmiOp) \ M(CheckedSmiOp) \ M(BinaryInt32Op) \ M(UnarySmiOp) \ M(UnaryDoubleOp) \ M(CheckStackOverflow) \ M(SmiToDouble) \ M(Int32ToDouble) \ M(MintToDouble) \ M(DoubleToInteger) \ M(DoubleToSmi) \ M(DoubleToDouble) \ M(DoubleToFloat) \ M(FloatToDouble) \ M(CheckClass) \ M(CheckClassId) \ M(CheckSmi) \ M(Constant) \ M(UnboxedConstant) \ M(CheckEitherNonSmi) \ M(BinaryDoubleOp) \ M(DoubleTestOp) \ M(MathUnary) \ M(MathMinMax) \ M(Box) \ M(Unbox) \ M(BoxInt64) \ M(UnboxInt64) \ M(CaseInsensitiveCompareUC16) \ M(BinaryMintOp) \ M(ShiftMintOp) \ M(UnaryMintOp) \ M(CheckArrayBound) \ M(GenericCheckBound) \ M(Constraint) \ M(StringToCharCode) \ M(OneByteStringFromCharCode) \ M(StringInterpolate) \ M(InvokeMathCFunction) \ M(MergedMath) \ M(GuardFieldClass) \ M(GuardFieldLength) \ M(IfThenElse) \ M(BinaryFloat32x4Op) \ M(Simd32x4Shuffle) \ M(Simd32x4ShuffleMix) \ M(Simd32x4GetSignMask) \ M(Float32x4Constructor) \ M(Float32x4Zero) \ M(Float32x4Splat) \ M(Float32x4Comparison) \ M(Float32x4MinMax) \ M(Float32x4Scale) \ M(Float32x4Sqrt) \ M(Float32x4ZeroArg) \ M(Float32x4Clamp) \ M(Float32x4With) \ M(Float32x4ToInt32x4) \ M(MaterializeObject) \ M(Int32x4Constructor) \ M(Int32x4BoolConstructor) \ M(Int32x4GetFlag) \ M(Int32x4Select) \ M(Int32x4SetFlag) \ M(Int32x4ToFloat32x4) \ M(BinaryInt32x4Op) \ M(TestSmi) \ M(TestCids) \ M(BinaryFloat64x2Op) \ M(Float64x2Zero) \ M(Float64x2Constructor) \ M(Float64x2Splat) \ M(Float32x4ToFloat64x2) \ M(Float64x2ToFloat32x4) \ M(Simd64x2Shuffle) \ M(Float64x2ZeroArg) \ M(Float64x2OneArg) \ M(ExtractNthOutput) \ M(BinaryUint32Op) \ M(ShiftUint32Op) \ M(UnaryUint32Op) \ M(BoxUint32) \ M(UnboxUint32) \ M(BoxInt32) \ M(UnboxInt32) \ M(UnboxedIntConverter) \ M(GrowRegExpStack) \ M(Deoptimize) #define FOR_EACH_ABSTRACT_INSTRUCTION(M) \ M(BlockEntry) \ M(BoxInteger) \ M(UnboxInteger) \ M(Comparison) \ M(UnaryIntegerOp) \ M(BinaryIntegerOp) \ #define FORWARD_DECLARATION(type) class type##Instr; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) FOR_EACH_ABSTRACT_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION #define DEFINE_INSTRUCTION_TYPE_CHECK(type) \ virtual type##Instr* As##type() { return this; } \ virtual const char* DebugName() const { return #type; } \ // Functions required in all concrete instruction classes. #define DECLARE_INSTRUCTION_NO_BACKEND(type) \ virtual Tag tag() const { return k##type; } \ virtual void Accept(FlowGraphVisitor* visitor); \ DEFINE_INSTRUCTION_TYPE_CHECK(type) #define DECLARE_INSTRUCTION_BACKEND() \ virtual LocationSummary* MakeLocationSummary(Zone* zone, \ bool optimizing) const; \ virtual void EmitNativeCode(FlowGraphCompiler* compiler); \ // Functions required in all concrete instruction classes. #define DECLARE_INSTRUCTION(type) \ DECLARE_INSTRUCTION_NO_BACKEND(type) \ DECLARE_INSTRUCTION_BACKEND() \ #ifndef PRODUCT #define PRINT_TO_SUPPORT \ virtual void PrintTo(BufferFormatter* f) const; #else #define PRINT_TO_SUPPORT #endif // !PRODUCT #ifndef PRODUCT #define PRINT_OPERANDS_TO_SUPPORT \ virtual void PrintOperandsTo(BufferFormatter* f) const; #else #define PRINT_OPERANDS_TO_SUPPORT #endif // !PRODUCT class Instruction : public ZoneAllocated { public: #define DECLARE_TAG(type) k##type, enum Tag { FOR_EACH_INSTRUCTION(DECLARE_TAG) }; #undef DECLARE_TAG explicit Instruction(intptr_t deopt_id = Thread::kNoDeoptId) : deopt_id_(deopt_id), lifetime_position_(kNoPlaceId), previous_(NULL), next_(NULL), env_(NULL), locs_(NULL), inlining_id_(-1) { } virtual ~Instruction() { } virtual Tag tag() const = 0; intptr_t deopt_id() const { ASSERT(CanDeoptimize() || CanBecomeDeoptimizationTarget()); return GetDeoptId(); } const ICData* GetICData( const ZoneGrowableArray& ic_data_array) const; virtual TokenPosition token_pos() const { return TokenPosition::kNoSource; } virtual intptr_t InputCount() const = 0; virtual Value* InputAt(intptr_t i) const = 0; void SetInputAt(intptr_t i, Value* value) { ASSERT(value != NULL); value->set_instruction(this); value->set_use_index(i); RawSetInputAt(i, value); } // Remove all inputs (including in the environment) from their // definition's use lists. void UnuseAllInputs(); // Call instructions override this function and return the number of // pushed arguments. virtual intptr_t ArgumentCount() const { return 0; } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { UNREACHABLE(); return NULL; } inline Definition* ArgumentAt(intptr_t index) const; // Returns true, if this instruction can deoptimize. virtual bool CanDeoptimize() const = 0; // Visiting support. virtual void Accept(FlowGraphVisitor* visitor) = 0; Instruction* previous() const { return previous_; } void set_previous(Instruction* instr) { ASSERT(!IsBlockEntry()); previous_ = instr; } Instruction* next() const { return next_; } void set_next(Instruction* instr) { ASSERT(!IsGraphEntry()); ASSERT(!IsReturn()); ASSERT(!IsBranch() || (instr == NULL)); ASSERT(!IsPhi()); ASSERT(instr == NULL || !instr->IsBlockEntry()); // TODO(fschneider): Also add Throw and ReThrow to the list of instructions // that do not have a successor. Currently, the graph builder will continue // to append instruction in case of a Throw inside an expression. This // condition should be handled in the graph builder next_ = instr; } // Link together two instruction. void LinkTo(Instruction* next) { ASSERT(this != next); this->set_next(next); next->set_previous(this); } // Removed this instruction from the graph, after use lists have been // computed. If the instruction is a definition with uses, those uses are // unaffected (so the instruction can be reinserted, e.g., hoisting). Instruction* RemoveFromGraph(bool return_previous = true); // Normal instructions can have 0 (inside a block) or 1 (last instruction in // a block) successors. Branch instruction with >1 successors override this // function. virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; void Goto(JoinEntryInstr* entry); virtual const char* DebugName() const = 0; #if defined(DEBUG) // Checks that the field stored in an instruction has proper form: // - must be a zone-handle // - In background compilation, must be cloned. // Aborts if field is not OK. void CheckField(const Field& field) const; #else void CheckField(const Field& field) const {} #endif // DEBUG // Printing support. const char* ToCString() const; #ifndef PRODUCT virtual void PrintTo(BufferFormatter* f) const; virtual void PrintOperandsTo(BufferFormatter* f) const; #endif #define DECLARE_INSTRUCTION_TYPE_CHECK(Name, Type) \ bool Is##Name() { return (As##Name() != NULL); } \ virtual Type* As##Name() { return NULL; } #define INSTRUCTION_TYPE_CHECK(Name) \ DECLARE_INSTRUCTION_TYPE_CHECK(Name, Name##Instr) DECLARE_INSTRUCTION_TYPE_CHECK(Definition, Definition) FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK #undef DECLARE_INSTRUCTION_TYPE_CHECK // Returns structure describing location constraints required // to emit native code for this instruction. LocationSummary* locs() { ASSERT(locs_ != NULL); return locs_; } bool HasLocs() const { return locs_ != NULL; } virtual LocationSummary* MakeLocationSummary(Zone* zone, bool is_optimizing) const = 0; void InitializeLocationSummary(Zone* zone, bool optimizing) { ASSERT(locs_ == NULL); locs_ = MakeLocationSummary(zone, optimizing); } static LocationSummary* MakeCallSummary(Zone* zone); virtual void EmitNativeCode(FlowGraphCompiler* compiler) { UNIMPLEMENTED(); } Environment* env() const { return env_; } void SetEnvironment(Environment* deopt_env); void RemoveEnvironment(); intptr_t lifetime_position() const { return lifetime_position_; } void set_lifetime_position(intptr_t pos) { lifetime_position_ = pos; } bool HasUnmatchedInputRepresentations() const; // Returns representation expected for the input operand at the given index. virtual Representation RequiredInputRepresentation(intptr_t idx) const { return kTagged; } // Representation of the value produced by this computation. virtual Representation representation() const { return kTagged; } bool WasEliminated() const { return next() == NULL; } // Returns deoptimization id that corresponds to the deoptimization target // that input operands conversions inserted for this instruction can jump // to. virtual intptr_t DeoptimizationTarget() const { UNREACHABLE(); return Thread::kNoDeoptId; } // Returns a replacement for the instruction or NULL if the instruction can // be eliminated. By default returns the this instruction which means no // change. virtual Instruction* Canonicalize(FlowGraph* flow_graph); // Insert this instruction before 'next' after use lists are computed. // Instructions cannot be inserted before a block entry or any other // instruction without a previous instruction. void InsertBefore(Instruction* next) { InsertAfter(next->previous()); } // Insert this instruction after 'prev' after use lists are computed. void InsertAfter(Instruction* prev); // Append an instruction to the current one and return the tail. // This function updated def-use chains of the newly appended // instruction. Instruction* AppendInstruction(Instruction* tail); virtual bool AllowsDCE() const { return false; } // Returns true if CSE and LICM are allowed for this instruction. virtual bool AllowsCSE() const { return false; } // Returns set of effects created by this instruction. virtual EffectSet Effects() const = 0; // Returns set of effects that affect this instruction. virtual EffectSet Dependencies() const { UNREACHABLE(); return EffectSet::All(); } // Get the block entry for this instruction. virtual BlockEntryInstr* GetBlock(); // Place identifiers used by the load optimization pass. intptr_t place_id() const { return place_id_; } void set_place_id(intptr_t place_id) { place_id_ = place_id; } bool HasPlaceId() const { return place_id_ != kNoPlaceId; } intptr_t inlining_id() const { return inlining_id_; } void set_inlining_id(intptr_t value) { ASSERT(value >= 0); inlining_id_ = value; } bool has_inlining_id() const { return inlining_id_ >= 0; } // Returns a hash code for use with hash maps. virtual intptr_t Hashcode() const; // Compares two instructions. Returns true, iff: // 1. They have the same tag. // 2. All input operands are Equals. // 3. They satisfy AttributesEqual. bool Equals(Instruction* other) const; // Compare attributes of a instructions (except input operands and tag). // All instructions that participate in CSE have to override this function. // This function can assume that the argument has the same type as this. virtual bool AttributesEqual(Instruction* other) const { UNREACHABLE(); return false; } virtual void InheritDeoptTarget(Zone* zone, Instruction* other); bool NeedsEnvironment() const { return CanDeoptimize() || CanBecomeDeoptimizationTarget(); } virtual bool CanBecomeDeoptimizationTarget() const { return false; } void InheritDeoptTargetAfter(FlowGraph* flow_graph, Definition* call, Definition* result); virtual bool MayThrow() const = 0; bool IsDominatedBy(Instruction* dom); void ClearEnv() { env_ = NULL; } void Unsupported(FlowGraphCompiler* compiler); protected: // GetDeoptId and/or CopyDeoptIdFrom. friend class CallSiteInliner; friend class LICM; friend class ComparisonInstr; friend class Scheduler; friend class BlockEntryInstr; friend class CatchBlockEntryInstr; // deopt_id_ // Fetch deopt id without checking if this computation can deoptimize. intptr_t GetDeoptId() const { return deopt_id_; } void CopyDeoptIdFrom(const Instruction& instr) { deopt_id_ = instr.deopt_id_; } private: virtual void RawSetInputAt(intptr_t i, Value* value) = 0; enum { kNoPlaceId = -1 }; intptr_t deopt_id_; union { intptr_t lifetime_position_; // Position used by register allocator. intptr_t place_id_; }; Instruction* previous_; Instruction* next_; Environment* env_; LocationSummary* locs_; intptr_t inlining_id_; DISALLOW_COPY_AND_ASSIGN(Instruction); }; class PureInstruction : public Instruction { public: explicit PureInstruction(intptr_t deopt_id) : Instruction(deopt_id) { } virtual bool AllowsCSE() const { return true; } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual EffectSet Effects() const { return EffectSet::None(); } }; // Types to be used as ThrowsTrait for TemplateInstruction/TemplateDefinition. struct Throws { static const bool kCanThrow = true; }; struct NoThrow { static const bool kCanThrow = false; }; // Types to be used as CSETrait for TemplateInstruction/TemplateDefinition. // Pure instructions are those that allow CSE and have no effects and // no dependencies. template struct Pure { typedef PureBase Base; }; template struct NoCSE { typedef DefaultBase Base; }; template class CSETrait = NoCSE> class TemplateInstruction: public CSETrait::Base { public: explicit TemplateInstruction(intptr_t deopt_id = Thread::kNoDeoptId) : CSETrait::Base(deopt_id), inputs_() { } virtual intptr_t InputCount() const { return N; } virtual Value* InputAt(intptr_t i) const { return inputs_[i]; } virtual bool MayThrow() const { return ThrowsTrait::kCanThrow; } protected: EmbeddedArray inputs_; private: virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } }; class MoveOperands : public ZoneAllocated { public: MoveOperands(Location dest, Location src) : dest_(dest), src_(src) { } Location src() const { return src_; } Location dest() const { return dest_; } Location* src_slot() { return &src_; } Location* dest_slot() { return &dest_; } void set_src(const Location& value) { src_ = value; } void set_dest(const Location& value) { dest_ = value; } // The parallel move resolver marks moves as "in-progress" by clearing the // destination (but not the source). Location MarkPending() { ASSERT(!IsPending()); Location dest = dest_; dest_ = Location::NoLocation(); return dest; } void ClearPending(Location dest) { ASSERT(IsPending()); dest_ = dest; } bool IsPending() const { ASSERT(!src_.IsInvalid() || dest_.IsInvalid()); return dest_.IsInvalid() && !src_.IsInvalid(); } // True if this move a move from the given location. bool Blocks(Location loc) const { return !IsEliminated() && src_.Equals(loc); } // A move is redundant if it's been eliminated, if its source and // destination are the same, or if its destination is unneeded. bool IsRedundant() const { return IsEliminated() || dest_.IsInvalid() || src_.Equals(dest_); } // We clear both operands to indicate move that's been eliminated. void Eliminate() { src_ = dest_ = Location::NoLocation(); } bool IsEliminated() const { ASSERT(!src_.IsInvalid() || dest_.IsInvalid()); return src_.IsInvalid(); } private: Location dest_; Location src_; DISALLOW_COPY_AND_ASSIGN(MoveOperands); }; class ParallelMoveInstr : public TemplateInstruction<0, NoThrow> { public: ParallelMoveInstr() : moves_(4) { } DECLARE_INSTRUCTION(ParallelMove) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { UNREACHABLE(); // This instruction never visited by optimization passes. return EffectSet::None(); } virtual EffectSet Dependencies() const { UNREACHABLE(); // This instruction never visited by optimization passes. return EffectSet::None(); } MoveOperands* AddMove(Location dest, Location src) { MoveOperands* move = new MoveOperands(dest, src); moves_.Add(move); return move; } MoveOperands* MoveOperandsAt(intptr_t index) const { return moves_[index]; } intptr_t NumMoves() const { return moves_.length(); } bool IsRedundant() const; virtual TokenPosition token_pos() const { return TokenPosition::kParallelMove; } PRINT_TO_SUPPORT private: GrowableArray moves_; // Elements cannot be null. DISALLOW_COPY_AND_ASSIGN(ParallelMoveInstr); }; // Basic block entries are administrative nodes. There is a distinguished // graph entry with no predecessor. Joins are the only nodes with multiple // predecessors. Targets are all other basic block entries. The types // enforce edge-split form---joins are forbidden as the successors of // branches. class BlockEntryInstr : public Instruction { public: virtual intptr_t PredecessorCount() const = 0; virtual BlockEntryInstr* PredecessorAt(intptr_t index) const = 0; intptr_t preorder_number() const { return preorder_number_; } void set_preorder_number(intptr_t number) { preorder_number_ = number; } intptr_t postorder_number() const { return postorder_number_; } void set_postorder_number(intptr_t number) { postorder_number_ = number; } intptr_t block_id() const { return block_id_; } // NOTE: These are SSA positions and not token positions. These are used by // the register allocator. void set_start_pos(intptr_t pos) { start_pos_ = pos; } intptr_t start_pos() const { return start_pos_; } void set_end_pos(intptr_t pos) { end_pos_ = pos; } intptr_t end_pos() const { return end_pos_; } BlockEntryInstr* dominator() const { return dominator_; } BlockEntryInstr* ImmediateDominator() const; const GrowableArray& dominated_blocks() { return dominated_blocks_; } void AddDominatedBlock(BlockEntryInstr* block) { block->set_dominator(this); dominated_blocks_.Add(block); } void ClearDominatedBlocks() { dominated_blocks_.Clear(); } bool Dominates(BlockEntryInstr* other) const; Instruction* last_instruction() const { return last_instruction_; } void set_last_instruction(Instruction* instr) { last_instruction_ = instr; } ParallelMoveInstr* parallel_move() const { return parallel_move_; } bool HasParallelMove() const { return parallel_move_ != NULL; } bool HasNonRedundantParallelMove() const { return HasParallelMove() && !parallel_move()->IsRedundant(); } ParallelMoveInstr* GetParallelMove() { if (parallel_move_ == NULL) { parallel_move_ = new ParallelMoveInstr(); } return parallel_move_; } // Discover basic-block structure of the current block. Must be called // on all graph blocks in preorder to yield valid results. As a side effect, // the block entry instructions in the graph are assigned preorder numbers. // The array 'preorder' maps preorder block numbers to the block entry // instruction with that number. The depth first spanning tree is recorded // in the array 'parent', which maps preorder block numbers to the preorder // number of the block's spanning-tree parent. As a side effect of this // function, the set of basic block predecessors (e.g., block entry // instructions of predecessor blocks) and also the last instruction in the // block is recorded in each entry instruction. Returns true when called the // first time on this particular block within one graph traversal, and false // on all successive calls. bool DiscoverBlock( BlockEntryInstr* predecessor, GrowableArray* preorder, GrowableArray* parent); // Perform a depth first search to prune code not reachable from an OSR // entry point. bool PruneUnreachable(GraphEntryInstr* graph_entry, Instruction* parent, intptr_t osr_id, BitVector* block_marks); virtual intptr_t InputCount() const { return 0; } virtual Value* InputAt(intptr_t i) const { UNREACHABLE(); return NULL; } virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanBecomeDeoptimizationTarget() const { // BlockEntry environment is copied to Goto and Branch instructions // when we insert new blocks targeting this block. return true; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual bool MayThrow() const { return false; } intptr_t try_index() const { return try_index_; } void set_try_index(intptr_t index) { try_index_ = index; } // True for blocks inside a try { } region. bool InsideTryBlock() const { return try_index_ != CatchClauseNode::kInvalidTryIndex; } BitVector* loop_info() const { return loop_info_; } void set_loop_info(BitVector* loop_info) { loop_info_ = loop_info; } virtual BlockEntryInstr* GetBlock() { return this; } virtual TokenPosition token_pos() const { return TokenPosition::kControlFlow; } // Helper to mutate the graph during inlining. This block should be // replaced with new_block as a predecessor of all of this block's // successors. void ReplaceAsPredecessorWith(BlockEntryInstr* new_block); void set_block_id(intptr_t block_id) { block_id_ = block_id; } intptr_t offset() const { return offset_; } void set_offset(intptr_t offset) { offset_ = offset; } // For all instruction in this block: Remove all inputs (including in the // environment) from their definition's use lists for all instructions. void ClearAllInstructions(); DEFINE_INSTRUCTION_TYPE_CHECK(BlockEntry) protected: BlockEntryInstr(intptr_t block_id, intptr_t try_index) : Instruction(Thread::Current()->GetNextDeoptId()), block_id_(block_id), try_index_(try_index), preorder_number_(-1), postorder_number_(-1), dominator_(NULL), dominated_blocks_(1), last_instruction_(NULL), offset_(-1), parallel_move_(NULL), loop_info_(NULL) { } private: virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); } virtual void ClearPredecessors() = 0; virtual void AddPredecessor(BlockEntryInstr* predecessor) = 0; void set_dominator(BlockEntryInstr* instr) { dominator_ = instr; } intptr_t block_id_; intptr_t try_index_; intptr_t preorder_number_; intptr_t postorder_number_; // Starting and ending lifetime positions for this block. Used by // the linear scan register allocator. intptr_t start_pos_; intptr_t end_pos_; BlockEntryInstr* dominator_; // Immediate dominator, NULL for graph entry. // TODO(fschneider): Optimize the case of one child to save space. GrowableArray dominated_blocks_; Instruction* last_instruction_; // Offset of this block from the start of the emitted code. intptr_t offset_; // Parallel move that will be used by linear scan register allocator to // connect live ranges at the start of the block. ParallelMoveInstr* parallel_move_; // Bit vector containg loop blocks for a loop header indexed by block // preorder number. BitVector* loop_info_; DISALLOW_COPY_AND_ASSIGN(BlockEntryInstr); }; class ForwardInstructionIterator : public ValueObject { public: explicit ForwardInstructionIterator(BlockEntryInstr* block_entry) : current_(block_entry) { Advance(); } void Advance() { ASSERT(!Done()); current_ = current_->next(); } bool Done() const { return current_ == NULL; } // Removes 'current_' from graph and sets 'current_' to previous instruction. void RemoveCurrentFromGraph(); Instruction* Current() const { return current_; } private: Instruction* current_; }; class BackwardInstructionIterator : public ValueObject { public: explicit BackwardInstructionIterator(BlockEntryInstr* block_entry) : block_entry_(block_entry), current_(block_entry->last_instruction()) { ASSERT(block_entry_->previous() == NULL); } void Advance() { ASSERT(!Done()); current_ = current_->previous(); } bool Done() const { return current_ == block_entry_; } void RemoveCurrentFromGraph(); Instruction* Current() const { return current_; } private: BlockEntryInstr* block_entry_; Instruction* current_; }; class GraphEntryInstr : public BlockEntryInstr { public: GraphEntryInstr(const ParsedFunction& parsed_function, TargetEntryInstr* normal_entry, intptr_t osr_id); DECLARE_INSTRUCTION(GraphEntry) virtual intptr_t PredecessorCount() const { return 0; } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { UNREACHABLE(); return NULL; } virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; void AddCatchEntry(CatchBlockEntryInstr* entry) { catch_entries_.Add(entry); } CatchBlockEntryInstr* GetCatchEntry(intptr_t index); void AddIndirectEntry(IndirectEntryInstr* entry) { indirect_entries_.Add(entry); } GrowableArray* initial_definitions() { return &initial_definitions_; } ConstantInstr* constant_null(); bool IsCompiledForOsr() const; intptr_t entry_count() const { return entry_count_; } void set_entry_count(intptr_t count) { entry_count_ = count; } intptr_t spill_slot_count() const { return spill_slot_count_; } void set_spill_slot_count(intptr_t count) { ASSERT(count >= 0); spill_slot_count_ = count; } // Number of stack slots reserved for compiling try-catch. For functions // without try-catch, this is 0. Otherwise, it is the number of local // variables. intptr_t fixed_slot_count() const { return fixed_slot_count_; } void set_fixed_slot_count(intptr_t count) { ASSERT(count >= 0); fixed_slot_count_ = count; } TargetEntryInstr* normal_entry() const { return normal_entry_; } const ParsedFunction& parsed_function() const { return parsed_function_; } const GrowableArray& catch_entries() const { return catch_entries_; } const GrowableArray& indirect_entries() const { return indirect_entries_; } PRINT_TO_SUPPORT private: virtual void ClearPredecessors() {} virtual void AddPredecessor(BlockEntryInstr* predecessor) { UNREACHABLE(); } const ParsedFunction& parsed_function_; TargetEntryInstr* normal_entry_; GrowableArray catch_entries_; // Indirect targets are blocks reachable only through indirect gotos. GrowableArray indirect_entries_; GrowableArray initial_definitions_; const intptr_t osr_id_; intptr_t entry_count_; intptr_t spill_slot_count_; intptr_t fixed_slot_count_; // For try-catch in optimized code. DISALLOW_COPY_AND_ASSIGN(GraphEntryInstr); }; class JoinEntryInstr : public BlockEntryInstr { public: JoinEntryInstr(intptr_t block_id, intptr_t try_index) : BlockEntryInstr(block_id, try_index), predecessors_(2), // Two is the assumed to be the common case. phis_(NULL) { } DECLARE_INSTRUCTION(JoinEntry) virtual intptr_t PredecessorCount() const { return predecessors_.length(); } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { return predecessors_[index]; } // Returns -1 if pred is not in the list. intptr_t IndexOfPredecessor(BlockEntryInstr* pred) const; ZoneGrowableArray* phis() const { return phis_; } PhiInstr* InsertPhi(intptr_t var_index, intptr_t var_count); void RemoveDeadPhis(Definition* replacement); void InsertPhi(PhiInstr* phi); void RemovePhi(PhiInstr* phi); virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } PRINT_TO_SUPPORT private: // Classes that have access to predecessors_ when inlining. friend class BlockEntryInstr; friend class InlineExitCollector; friend class PolymorphicInliner; friend class IndirectEntryInstr; // Access in il_printer.cc. // Direct access to phis_ in order to resize it due to phi elimination. friend class ConstantPropagator; friend class DeadCodeElimination; virtual void ClearPredecessors() { predecessors_.Clear(); } virtual void AddPredecessor(BlockEntryInstr* predecessor); GrowableArray predecessors_; ZoneGrowableArray* phis_; DISALLOW_COPY_AND_ASSIGN(JoinEntryInstr); }; class PhiIterator : public ValueObject { public: explicit PhiIterator(JoinEntryInstr* join) : phis_(join->phis()), index_(0) { } void Advance() { ASSERT(!Done()); index_++; } bool Done() const { return (phis_ == NULL) || (index_ >= phis_->length()); } PhiInstr* Current() const { return (*phis_)[index_]; } private: ZoneGrowableArray* phis_; intptr_t index_; }; class TargetEntryInstr : public BlockEntryInstr { public: TargetEntryInstr(intptr_t block_id, intptr_t try_index) : BlockEntryInstr(block_id, try_index), predecessor_(NULL), edge_weight_(0.0) { } DECLARE_INSTRUCTION(TargetEntry) double edge_weight() const { return edge_weight_; } void set_edge_weight(double weight) { edge_weight_ = weight; } void adjust_edge_weight(double scale_factor) { edge_weight_ *= scale_factor; } virtual intptr_t PredecessorCount() const { return (predecessor_ == NULL) ? 0 : 1; } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { ASSERT((index == 0) && (predecessor_ != NULL)); return predecessor_; } PRINT_TO_SUPPORT private: friend class BlockEntryInstr; // Access to predecessor_ when inlining. virtual void ClearPredecessors() { predecessor_ = NULL; } virtual void AddPredecessor(BlockEntryInstr* predecessor) { ASSERT(predecessor_ == NULL); predecessor_ = predecessor; } BlockEntryInstr* predecessor_; double edge_weight_; DISALLOW_COPY_AND_ASSIGN(TargetEntryInstr); }; class IndirectEntryInstr : public JoinEntryInstr { public: IndirectEntryInstr(intptr_t block_id, intptr_t indirect_id, intptr_t try_index) : JoinEntryInstr(block_id, try_index), indirect_id_(indirect_id) { } DECLARE_INSTRUCTION(IndirectEntry) intptr_t indirect_id() const { return indirect_id_; } PRINT_TO_SUPPORT private: const intptr_t indirect_id_; }; class CatchBlockEntryInstr : public BlockEntryInstr { public: CatchBlockEntryInstr(intptr_t block_id, intptr_t try_index, GraphEntryInstr* graph_entry, const Array& handler_types, intptr_t catch_try_index, const LocalVariable& exception_var, const LocalVariable& stacktrace_var, bool needs_stacktrace, intptr_t deopt_id, bool should_restore_closure_context = false) : BlockEntryInstr(block_id, try_index), graph_entry_(graph_entry), predecessor_(NULL), catch_handler_types_(Array::ZoneHandle(handler_types.raw())), catch_try_index_(catch_try_index), exception_var_(exception_var), stacktrace_var_(stacktrace_var), needs_stacktrace_(needs_stacktrace), should_restore_closure_context_(should_restore_closure_context) { deopt_id_ = deopt_id; } DECLARE_INSTRUCTION(CatchBlockEntry) virtual intptr_t PredecessorCount() const { return (predecessor_ == NULL) ? 0 : 1; } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { ASSERT((index == 0) && (predecessor_ != NULL)); return predecessor_; } GraphEntryInstr* graph_entry() const { return graph_entry_; } const LocalVariable& exception_var() const { return exception_var_; } const LocalVariable& stacktrace_var() const { return stacktrace_var_; } bool needs_stacktrace() const { return needs_stacktrace_; } // Returns try index for the try block to which this catch handler // corresponds. intptr_t catch_try_index() const { return catch_try_index_; } GrowableArray* initial_definitions() { return &initial_definitions_; } PRINT_TO_SUPPORT private: friend class BlockEntryInstr; // Access to predecessor_ when inlining. virtual void ClearPredecessors() { predecessor_ = NULL; } virtual void AddPredecessor(BlockEntryInstr* predecessor) { ASSERT(predecessor_ == NULL); predecessor_ = predecessor; } bool should_restore_closure_context() const { ASSERT(exception_var_.is_captured() == stacktrace_var_.is_captured()); ASSERT(!exception_var_.is_captured() || should_restore_closure_context_); return should_restore_closure_context_; } GraphEntryInstr* graph_entry_; BlockEntryInstr* predecessor_; const Array& catch_handler_types_; const intptr_t catch_try_index_; GrowableArray initial_definitions_; const LocalVariable& exception_var_; const LocalVariable& stacktrace_var_; const bool needs_stacktrace_; const bool should_restore_closure_context_; DISALLOW_COPY_AND_ASSIGN(CatchBlockEntryInstr); }; // If the result of the allocation is not stored into any field, passed // as an argument or used in a phi then it can't alias with any other // SSA value. class AliasIdentity : public ValueObject { public: // It is unknown if value has aliases. static AliasIdentity Unknown() { return AliasIdentity(kUnknown); } // It is known that value can have aliases. static AliasIdentity Aliased() { return AliasIdentity(kAliased); } // It is known that value has no aliases. static AliasIdentity NotAliased() { return AliasIdentity(kNotAliased); } // It is known that value has no aliases and it was selected by // allocation sinking pass as a candidate. static AliasIdentity AllocationSinkingCandidate() { return AliasIdentity(kAllocationSinkingCandidate); } bool IsUnknown() const { return value_ == kUnknown; } bool IsAliased() const { return value_ == kAliased; } bool IsNotAliased() const { return (value_ & kNotAliased) != 0; } bool IsAllocationSinkingCandidate() const { return value_ == kAllocationSinkingCandidate; } AliasIdentity(const AliasIdentity& other) : ValueObject(), value_(other.value_) { } AliasIdentity& operator=(const AliasIdentity& other) { value_ = other.value_; return *this; } private: explicit AliasIdentity(intptr_t value) : value_(value) { } enum { kUnknown = 0, kNotAliased = 1, kAliased = 2, kAllocationSinkingCandidate = 3, }; COMPILE_ASSERT((kUnknown & kNotAliased) == 0); COMPILE_ASSERT((kAliased & kNotAliased) == 0); COMPILE_ASSERT((kAllocationSinkingCandidate & kNotAliased) != 0); intptr_t value_; }; // Abstract super-class of all instructions that define a value (Bind, Phi). class Definition : public Instruction { public: explicit Definition(intptr_t deopt_id = Thread::kNoDeoptId); // Overridden by definitions that have call counts. virtual intptr_t CallCount() const { UNREACHABLE(); return -1; } intptr_t temp_index() const { return temp_index_; } void set_temp_index(intptr_t index) { temp_index_ = index; } void ClearTempIndex() { temp_index_ = -1; } bool HasTemp() const { return temp_index_ >= 0; } intptr_t ssa_temp_index() const { return ssa_temp_index_; } void set_ssa_temp_index(intptr_t index) { ASSERT(index >= 0); ssa_temp_index_ = index; } bool HasSSATemp() const { return ssa_temp_index_ >= 0; } void ClearSSATempIndex() { ssa_temp_index_ = -1; } bool HasPairRepresentation() const { #if defined(TARGET_ARCH_X64) return (representation() == kPairOfTagged) || (representation() == kPairOfUnboxedDouble); #else return (representation() == kPairOfTagged) || (representation() == kPairOfUnboxedDouble) || (representation() == kUnboxedMint); #endif } // Compile time type of the definition, which may be requested before type // propagation during graph building. CompileType* Type() { if (type_ == NULL) { type_ = ZoneCompileType::Wrap(ComputeType()); } return type_; } // Does this define a mint? inline bool IsMintDefinition(); bool IsInt32Definition() { return IsBinaryInt32Op() || IsBoxInt32() || IsUnboxInt32() || IsUnboxedIntConverter(); } // Compute compile type for this definition. It is safe to use this // approximation even before type propagator was run (e.g. during graph // building). virtual CompileType ComputeType() const { return CompileType::Dynamic(); } // Update CompileType of the definition. Returns true if the type has changed. virtual bool RecomputeType() { return false; } PRINT_OPERANDS_TO_SUPPORT PRINT_TO_SUPPORT bool UpdateType(CompileType new_type) { if (type_ == NULL) { type_ = ZoneCompileType::Wrap(new_type); return true; } if (type_->IsNone() || !type_->IsEqualTo(&new_type)) { *type_ = new_type; return true; } return false; } bool HasUses() const { return (input_use_list_ != NULL) || (env_use_list_ != NULL); } bool HasOnlyUse(Value* use) const; bool HasOnlyInputUse(Value* use) const; Value* input_use_list() const { return input_use_list_; } void set_input_use_list(Value* head) { input_use_list_ = head; } Value* env_use_list() const { return env_use_list_; } void set_env_use_list(Value* head) { env_use_list_ = head; } void AddInputUse(Value* value) { Value::AddToList(value, &input_use_list_); } void AddEnvUse(Value* value) { Value::AddToList(value, &env_use_list_); } // Replace uses of this definition with uses of other definition or value. // Precondition: use lists must be properly calculated. // Postcondition: use lists and use values are still valid. void ReplaceUsesWith(Definition* other); // Replace this definition and all uses with another definition. If // replacing during iteration, pass the iterator so that the instruction // can be replaced without affecting iteration order, otherwise pass a // NULL iterator. void ReplaceWith(Definition* other, ForwardInstructionIterator* iterator); // A value in the constant propagation lattice. // - non-constant sentinel // - a constant (any non-sentinel value) // - unknown sentinel Object& constant_value(); virtual void InferRange(RangeAnalysis* analysis, Range* range); Range* range() const { return range_; } void set_range(const Range&); // Definitions can be canonicalized only into definitions to ensure // this check statically we override base Canonicalize with a Canonicalize // returning Definition (return type is covariant). virtual Definition* Canonicalize(FlowGraph* flow_graph); static const intptr_t kReplacementMarker = -2; Definition* Replacement() { if (ssa_temp_index_ == kReplacementMarker) { return reinterpret_cast(temp_index_); } return this; } void SetReplacement(Definition* other) { ASSERT(ssa_temp_index_ >= 0); ASSERT(WasEliminated()); ssa_temp_index_ = kReplacementMarker; temp_index_ = reinterpret_cast(other); } virtual AliasIdentity Identity() const { return AliasIdentity::Unknown(); } virtual void SetIdentity(AliasIdentity identity) { UNREACHABLE(); } Definition* OriginalDefinition(); virtual Definition* AsDefinition() { return this; } protected: friend class RangeAnalysis; friend class Value; Range* range_; CompileType* type_; private: intptr_t temp_index_; intptr_t ssa_temp_index_; Value* input_use_list_; Value* env_use_list_; Object* constant_value_; DISALLOW_COPY_AND_ASSIGN(Definition); }; // Change a value's definition after use lists have been computed. inline void Value::BindTo(Definition* def) { RemoveFromUseList(); set_definition(def); def->AddInputUse(this); } inline void Value::BindToEnvironment(Definition* def) { RemoveFromUseList(); set_definition(def); def->AddEnvUse(this); } class PureDefinition : public Definition { public: explicit PureDefinition(intptr_t deopt_id) : Definition(deopt_id) { } virtual bool AllowsCSE() const { return true; } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual EffectSet Effects() const { return EffectSet::None(); } }; template class CSETrait = NoCSE> class TemplateDefinition : public CSETrait::Base { public: explicit TemplateDefinition(intptr_t deopt_id = Thread::kNoDeoptId) : CSETrait::Base(deopt_id), inputs_() { } virtual intptr_t InputCount() const { return N; } virtual Value* InputAt(intptr_t i) const { return inputs_[i]; } virtual bool MayThrow() const { return ThrowsTrait::kCanThrow; } protected: EmbeddedArray inputs_; private: friend class BranchInstr; friend class IfThenElseInstr; virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } }; struct BranchLabels { Label* true_label; Label* false_label; Label* fall_through; }; class InductionVariableInfo; class PhiInstr : public Definition { public: PhiInstr(JoinEntryInstr* block, intptr_t num_inputs) : block_(block), inputs_(num_inputs), representation_(kTagged), reaching_defs_(NULL), loop_variable_info_(NULL), is_alive_(false), is_receiver_(kUnknownReceiver) { for (intptr_t i = 0; i < num_inputs; ++i) { inputs_.Add(NULL); } } // Get the block entry for that instruction. virtual BlockEntryInstr* GetBlock() { return block(); } JoinEntryInstr* block() const { return block_; } virtual CompileType ComputeType() const; virtual bool RecomputeType(); intptr_t InputCount() const { return inputs_.length(); } Value* InputAt(intptr_t i) const { return inputs_[i]; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } // Phi is alive if it reaches a non-environment use. bool is_alive() const { return is_alive_; } void mark_alive() { is_alive_ = true; } void mark_dead() { is_alive_ = false; } virtual Representation RequiredInputRepresentation(intptr_t i) const { return representation_; } virtual Representation representation() const { return representation_; } virtual void set_representation(Representation r) { representation_ = r; } virtual intptr_t Hashcode() const { UNREACHABLE(); return 0; } DECLARE_INSTRUCTION(Phi) virtual void InferRange(RangeAnalysis* analysis, Range* range); BitVector* reaching_defs() const { return reaching_defs_; } void set_reaching_defs(BitVector* reaching_defs) { reaching_defs_ = reaching_defs; } virtual bool MayThrow() const { return false; } // A phi is redundant if all input operands are the same. bool IsRedundant() const; void set_induction_variable_info(InductionVariableInfo* info) { loop_variable_info_ = info; } InductionVariableInfo* induction_variable_info() { return loop_variable_info_; } PRINT_TO_SUPPORT enum ReceiverType { kUnknownReceiver = -1, kNotReceiver = 0, kReceiver = 1 }; ReceiverType is_receiver() const { return static_cast(is_receiver_); } void set_is_receiver(ReceiverType is_receiver) { is_receiver_ = is_receiver; } private: // Direct access to inputs_ in order to resize it due to unreachable // predecessors. friend class ConstantPropagator; void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } JoinEntryInstr* block_; GrowableArray inputs_; Representation representation_; BitVector* reaching_defs_; InductionVariableInfo* loop_variable_info_; bool is_alive_; int8_t is_receiver_; DISALLOW_COPY_AND_ASSIGN(PhiInstr); }; class ParameterInstr : public Definition { public: ParameterInstr(intptr_t index, BlockEntryInstr* block, Register base_reg = FPREG) : index_(index), base_reg_(base_reg), block_(block) { } DECLARE_INSTRUCTION(Parameter) intptr_t index() const { return index_; } Register base_reg() const { return base_reg_; } // Get the block entry for that instruction. virtual BlockEntryInstr* GetBlock() { return block_; } intptr_t InputCount() const { return 0; } Value* InputAt(intptr_t i) const { UNREACHABLE(); return NULL; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual intptr_t Hashcode() const { UNREACHABLE(); return 0; } virtual CompileType ComputeType() const; virtual bool MayThrow() const { return false; } PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); } const intptr_t index_; const Register base_reg_; BlockEntryInstr* block_; DISALLOW_COPY_AND_ASSIGN(ParameterInstr); }; class PushArgumentInstr : public TemplateDefinition<1, NoThrow> { public: explicit PushArgumentInstr(Value* value) { SetInputAt(0, value); } DECLARE_INSTRUCTION(PushArgument) virtual CompileType ComputeType() const; Value* value() const { return InputAt(0); } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual TokenPosition token_pos() const { return TokenPosition::kPushArgument; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(PushArgumentInstr); }; inline Definition* Instruction::ArgumentAt(intptr_t index) const { return PushArgumentAt(index)->value()->definition(); } class ReturnInstr : public TemplateInstruction<1, NoThrow> { public: ReturnInstr(TokenPosition token_pos, Value* value) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos) { SetInputAt(0, value); } DECLARE_INSTRUCTION(Return) virtual TokenPosition token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } virtual bool CanBecomeDeoptimizationTarget() const { // Return instruction might turn into a Goto instruction after inlining. // Every Goto must have an environment. return true; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(ReturnInstr); }; class ThrowInstr : public TemplateInstruction<0, Throws> { public: explicit ThrowInstr(TokenPosition token_pos) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos) { } DECLARE_INSTRUCTION(Throw) virtual intptr_t ArgumentCount() const { return 1; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(ThrowInstr); }; class ReThrowInstr : public TemplateInstruction<0, Throws> { public: // 'catch_try_index' can be CatchClauseNode::kInvalidTryIndex if the // rethrow has been artifically generated by the parser. ReThrowInstr(TokenPosition token_pos, intptr_t catch_try_index) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), catch_try_index_(catch_try_index) { } DECLARE_INSTRUCTION(ReThrow) virtual intptr_t ArgumentCount() const { return 2; } virtual TokenPosition token_pos() const { return token_pos_; } intptr_t catch_try_index() const { return catch_try_index_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } private: const TokenPosition token_pos_; const intptr_t catch_try_index_; DISALLOW_COPY_AND_ASSIGN(ReThrowInstr); }; class StopInstr : public TemplateInstruction<0, NoThrow> { public: explicit StopInstr(const char* message) : message_(message) { ASSERT(message != NULL); } const char* message() const { return message_; } DECLARE_INSTRUCTION(Stop); virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } private: const char* message_; DISALLOW_COPY_AND_ASSIGN(StopInstr); }; class GotoInstr : public TemplateInstruction<0, NoThrow> { public: explicit GotoInstr(JoinEntryInstr* entry) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), block_(NULL), successor_(entry), edge_weight_(0.0), parallel_move_(NULL) { } DECLARE_INSTRUCTION(Goto) BlockEntryInstr* block() const { return block_; } void set_block(BlockEntryInstr* block) { block_ = block; } JoinEntryInstr* successor() const { return successor_; } void set_successor(JoinEntryInstr* successor) { successor_ = successor; } virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; double edge_weight() const { return edge_weight_; } void set_edge_weight(double weight) { edge_weight_ = weight; } void adjust_edge_weight(double scale_factor) { edge_weight_ *= scale_factor; } virtual bool CanBecomeDeoptimizationTarget() const { // Goto instruction can be used as a deoptimization target when LICM // hoists instructions out of the loop. return true; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } ParallelMoveInstr* parallel_move() const { return parallel_move_; } bool HasParallelMove() const { return parallel_move_ != NULL; } bool HasNonRedundantParallelMove() const { return HasParallelMove() && !parallel_move()->IsRedundant(); } ParallelMoveInstr* GetParallelMove() { if (parallel_move_ == NULL) { parallel_move_ = new ParallelMoveInstr(); } return parallel_move_; } virtual TokenPosition token_pos() const { return TokenPosition::kControlFlow; } PRINT_TO_SUPPORT private: BlockEntryInstr* block_; JoinEntryInstr* successor_; double edge_weight_; // Parallel move that will be used by linear scan register allocator to // connect live ranges at the end of the block and resolve phis. ParallelMoveInstr* parallel_move_; }; // IndirectGotoInstr represents a dynamically computed jump. Only // IndirectEntryInstr targets are valid targets of an indirect goto. The // concrete target to jump to is given as a parameter to the indirect goto. // // In order to preserve split-edge form, an indirect goto does not itself point // to its targets. Instead, for each possible target, the successors_ field // will contain an ordinary goto instruction that jumps to the target. // TODO(zerny): Implement direct support instead of embedding gotos. // // Byte offsets of all possible targets are stored in the offsets_ array. The // desired offset is looked up while the generated code is executing, and passed // to IndirectGoto as an input. class IndirectGotoInstr : public TemplateInstruction<1, NoThrow> { public: IndirectGotoInstr(TypedData* offsets, Value* offset_from_start) : offsets_(*offsets) { SetInputAt(0, offset_from_start); } DECLARE_INSTRUCTION(IndirectGoto) virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kNoRepresentation; } virtual intptr_t ArgumentCount() const { return 0; } void AddSuccessor(TargetEntryInstr* successor) { ASSERT(successor->next()->IsGoto()); ASSERT(successor->next()->AsGoto()->successor()->IsIndirectEntry()); successors_.Add(successor); } virtual intptr_t SuccessorCount() const { return successors_.length(); } virtual TargetEntryInstr* SuccessorAt(intptr_t index) const { ASSERT(index < SuccessorCount()); return successors_[index]; } virtual bool CanDeoptimize() const { return false; } virtual bool CanBecomeDeoptimizationTarget() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } Value* offset() const { return inputs_[0]; } void ComputeOffsetTable(); PRINT_TO_SUPPORT private: GrowableArray successors_; TypedData& offsets_; }; class ComparisonInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } virtual TokenPosition token_pos() const { return token_pos_; } Token::Kind kind() const { return kind_; } virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right) = 0; virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch) = 0; virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels) = 0; void SetDeoptId(const Instruction& instr) { CopyDeoptIdFrom(instr); } // Operation class id is computed from collected ICData. void set_operation_cid(intptr_t value) { operation_cid_ = value; } intptr_t operation_cid() const { return operation_cid_; } void NegateComparison() { kind_ = Token::NegateComparison(kind_); } virtual bool CanBecomeDeoptimizationTarget() const { return true; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual bool AttributesEqual(Instruction* other) const { ComparisonInstr* other_comparison = other->AsComparison(); return kind() == other_comparison->kind() && (operation_cid() == other_comparison->operation_cid()); } DEFINE_INSTRUCTION_TYPE_CHECK(Comparison) protected: ComparisonInstr(TokenPosition token_pos, Token::Kind kind, Value* left, Value* right, intptr_t deopt_id = Thread::kNoDeoptId) : TemplateDefinition(deopt_id), token_pos_(token_pos), kind_(kind), operation_cid_(kIllegalCid) { SetInputAt(0, left); if (right != NULL) { SetInputAt(1, right); } } private: const TokenPosition token_pos_; Token::Kind kind_; intptr_t operation_cid_; // Set by optimizer. DISALLOW_COPY_AND_ASSIGN(ComparisonInstr); }; class BranchInstr : public Instruction { public: explicit BranchInstr(ComparisonInstr* comparison) : Instruction(Thread::Current()->GetNextDeoptId()), comparison_(comparison), is_checked_(false), constrained_type_(NULL), constant_target_(NULL) { ASSERT(comparison->env() == NULL); for (intptr_t i = comparison->InputCount() - 1; i >= 0; --i) { comparison->InputAt(i)->set_instruction(this); } } DECLARE_INSTRUCTION(Branch) virtual intptr_t ArgumentCount() const { return comparison()->ArgumentCount(); } intptr_t InputCount() const { return comparison()->InputCount(); } Value* InputAt(intptr_t i) const { return comparison()->InputAt(i); } virtual TokenPosition token_pos() const { return comparison_->token_pos(); } virtual bool CanDeoptimize() const { // Branches need a deoptimization info in checked mode if they // can throw a type check error. return comparison()->CanDeoptimize() || is_checked(); } virtual bool CanBecomeDeoptimizationTarget() const { return comparison()->CanBecomeDeoptimizationTarget(); } virtual EffectSet Effects() const { return comparison()->Effects(); } ComparisonInstr* comparison() const { return comparison_; } void SetComparison(ComparisonInstr* comp); void set_is_checked(bool value) { is_checked_ = value; } bool is_checked() const { return is_checked_; } virtual intptr_t DeoptimizationTarget() const { return comparison()->DeoptimizationTarget(); } virtual Representation RequiredInputRepresentation(intptr_t i) const { return comparison()->RequiredInputRepresentation(i); } virtual Instruction* Canonicalize(FlowGraph* flow_graph); // Set compile type constrained by the comparison of this branch. // FlowGraphPropagator propagates it downwards into either true or false // successor. void set_constrained_type(ConstrainedCompileType* type) { constrained_type_ = type; } // Return compile type constrained by the comparison of this branch. ConstrainedCompileType* constrained_type() const { return constrained_type_; } void set_constant_target(TargetEntryInstr* target) { ASSERT(target == true_successor() || target == false_successor()); constant_target_ = target; } TargetEntryInstr* constant_target() const { return constant_target_; } virtual void InheritDeoptTarget(Zone* zone, Instruction* other); virtual bool MayThrow() const { return comparison()->MayThrow(); } TargetEntryInstr* true_successor() const { return true_successor_; } TargetEntryInstr* false_successor() const { return false_successor_; } TargetEntryInstr** true_successor_address() { return &true_successor_; } TargetEntryInstr** false_successor_address() { return &false_successor_; } virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; PRINT_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { comparison()->RawSetInputAt(i, value); } TargetEntryInstr* true_successor_; TargetEntryInstr* false_successor_; ComparisonInstr* comparison_; bool is_checked_; ConstrainedCompileType* constrained_type_; TargetEntryInstr* constant_target_; DISALLOW_COPY_AND_ASSIGN(BranchInstr); }; class DeoptimizeInstr : public TemplateInstruction<0, NoThrow, Pure> { public: DeoptimizeInstr(ICData::DeoptReasonId deopt_reason, intptr_t deopt_id) : TemplateInstruction(deopt_id), deopt_reason_(deopt_reason) { } virtual bool CanDeoptimize() const { return true; } virtual bool AttributesEqual(Instruction* other) const { return true; } DECLARE_INSTRUCTION(Deoptimize) private: const ICData::DeoptReasonId deopt_reason_; DISALLOW_COPY_AND_ASSIGN(DeoptimizeInstr); }; class RedefinitionInstr : public TemplateDefinition<1, NoThrow> { public: explicit RedefinitionInstr(Value* value) { SetInputAt(0, value); } DECLARE_INSTRUCTION(Redefinition) Value* value() const { return inputs_[0]; } virtual CompileType ComputeType() const; virtual bool RecomputeType(); virtual bool CanDeoptimize() const { return false; } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual EffectSet Effects() const { return EffectSet::None(); } private: DISALLOW_COPY_AND_ASSIGN(RedefinitionInstr); }; class ConstraintInstr : public TemplateDefinition<1, NoThrow> { public: ConstraintInstr(Value* value, Range* constraint) : constraint_(constraint), target_(NULL) { SetInputAt(0, value); } DECLARE_INSTRUCTION(Constraint) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { UNREACHABLE(); return false; } Value* value() const { return inputs_[0]; } Range* constraint() const { return constraint_; } virtual void InferRange(RangeAnalysis* analysis, Range* range); // Constraints for branches have their target block stored in order // to find the comparison that generated the constraint: // target->predecessor->last_instruction->comparison. void set_target(TargetEntryInstr* target) { target_ = target; } TargetEntryInstr* target() const { return target_; } PRINT_OPERANDS_TO_SUPPORT private: Range* constraint_; TargetEntryInstr* target_; DISALLOW_COPY_AND_ASSIGN(ConstraintInstr); }; class ConstantInstr : public TemplateDefinition<0, NoThrow, Pure> { public: ConstantInstr(const Object& value, TokenPosition token_pos = TokenPosition::kConstant); DECLARE_INSTRUCTION(Constant) virtual CompileType ComputeType() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); const Object& value() const { return value_; } virtual bool CanDeoptimize() const { return false; } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual bool AttributesEqual(Instruction* other) const; virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT private: const Object& value_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(ConstantInstr); }; // Merged ConstantInstr -> UnboxedXXX into UnboxedConstantInstr. // TODO(srdjan): Implemented currently for doubles only, should implement // for other unboxing instructions. class UnboxedConstantInstr : public ConstantInstr { public: explicit UnboxedConstantInstr(const Object& value, Representation representation); virtual Representation representation() const { return representation_; } // Either NULL or the address of the unboxed constant. uword constant_address() const { return constant_address_; } DECLARE_INSTRUCTION(UnboxedConstant) private: const Representation representation_; uword constant_address_; // Either NULL or points to the untagged constant. DISALLOW_COPY_AND_ASSIGN(UnboxedConstantInstr); }; class AssertAssignableInstr : public TemplateDefinition<2, Throws, Pure> { public: AssertAssignableInstr(TokenPosition token_pos, Value* value, Value* instantiator_type_arguments, const AbstractType& dst_type, const String& dst_name, intptr_t deopt_id) : TemplateDefinition(deopt_id), token_pos_(token_pos), dst_type_(AbstractType::ZoneHandle(dst_type.raw())), dst_name_(dst_name) { ASSERT(!dst_type.IsNull()); ASSERT(!dst_type.IsTypeRef()); ASSERT(!dst_name.IsNull()); SetInputAt(0, value); SetInputAt(1, instantiator_type_arguments); } DECLARE_INSTRUCTION(AssertAssignable) virtual CompileType ComputeType() const; virtual bool RecomputeType(); Value* value() const { return inputs_[0]; } Value* instantiator_type_arguments() const { return inputs_[1]; } virtual TokenPosition token_pos() const { return token_pos_; } const AbstractType& dst_type() const { return dst_type_; } void set_dst_type(const AbstractType& dst_type) { ASSERT(!dst_type.IsTypeRef()); dst_type_ = dst_type.raw(); } const String& dst_name() const { return dst_name_; } virtual bool CanDeoptimize() const { return true; } virtual bool CanBecomeDeoptimizationTarget() const { // AssertAssignable instructions that are specialized by the optimizer // (e.g. replaced with CheckClass) need a deoptimization descriptor before. return true; } virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const; PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; AbstractType& dst_type_; const String& dst_name_; DISALLOW_COPY_AND_ASSIGN(AssertAssignableInstr); }; class AssertBooleanInstr : public TemplateDefinition<1, Throws, Pure> { public: AssertBooleanInstr(TokenPosition token_pos, Value* value) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos) { SetInputAt(0, value); } DECLARE_INSTRUCTION(AssertBoolean) virtual CompileType ComputeType() const; virtual TokenPosition token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return true; } virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(AssertBooleanInstr); }; // Denotes the current context, normally held in a register. This is // a computation, not a value, because it's mutable. class CurrentContextInstr : public TemplateDefinition<0, NoThrow> { public: CurrentContextInstr() : TemplateDefinition(Thread::Current()->GetNextDeoptId()) { } DECLARE_INSTRUCTION(CurrentContext) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(CurrentContextInstr); }; class ClosureCallInstr : public TemplateDefinition<1, Throws> { public: ClosureCallInstr(Value* function, ClosureCallNode* node, ZoneGrowableArray* arguments) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), argument_names_(node->arguments()->names()), token_pos_(node->token_pos()), arguments_(arguments) { SetInputAt(0, function); } ClosureCallInstr(Value* function, ZoneGrowableArray* arguments, const Array& argument_names, TokenPosition token_pos) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), argument_names_(argument_names), token_pos_(token_pos), arguments_(arguments) { SetInputAt(0, function); } DECLARE_INSTRUCTION(ClosureCall) const Array& argument_names() const { return argument_names_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { return (*arguments_)[index]; } // TODO(kmillikin): implement exact call counts for closure calls. virtual intptr_t CallCount() const { return 1; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::All(); } PRINT_OPERANDS_TO_SUPPORT private: const Array& argument_names_; TokenPosition token_pos_; ZoneGrowableArray* arguments_; DISALLOW_COPY_AND_ASSIGN(ClosureCallInstr); }; class InstanceCallInstr : public TemplateDefinition<0, Throws> { public: InstanceCallInstr(TokenPosition token_pos, const String& function_name, Token::Kind token_kind, ZoneGrowableArray* arguments, const Array& argument_names, intptr_t checked_argument_count, const ZoneGrowableArray& ic_data_array) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), ic_data_(NULL), token_pos_(token_pos), function_name_(function_name), token_kind_(token_kind), arguments_(arguments), argument_names_(argument_names), checked_argument_count_(checked_argument_count), has_unique_selector_(false) { ic_data_ = GetICData(ic_data_array); ASSERT(function_name.IsNotTemporaryScopedHandle()); ASSERT(!arguments->is_empty()); ASSERT(argument_names.IsZoneHandle() || argument_names.InVMHeap()); ASSERT(Token::IsBinaryOperator(token_kind) || Token::IsEqualityOperator(token_kind) || Token::IsRelationalOperator(token_kind) || Token::IsUnaryOperator(token_kind) || Token::IsIndexOperator(token_kind) || Token::IsTypeTestOperator(token_kind) || Token::IsTypeCastOperator(token_kind) || token_kind == Token::kGET || token_kind == Token::kSET || token_kind == Token::kILLEGAL); } DECLARE_INSTRUCTION(InstanceCall) const ICData* ic_data() const { return ic_data_; } bool HasICData() const { return (ic_data() != NULL) && !ic_data()->IsNull(); } // ICData can be replaced by optimizer. void set_ic_data(const ICData* value) { ic_data_ = value; } virtual TokenPosition token_pos() const { return token_pos_; } const String& function_name() const { return function_name_; } Token::Kind token_kind() const { return token_kind_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { return (*arguments_)[index]; } const Array& argument_names() const { return argument_names_; } intptr_t checked_argument_count() const { return checked_argument_count_; } bool has_unique_selector() const { return has_unique_selector_; } void set_has_unique_selector(bool b) { has_unique_selector_ = b; } virtual bool CanDeoptimize() const { return true; } virtual bool CanBecomeDeoptimizationTarget() const { // Instance calls that are specialized by the optimizer need a // deoptimization descriptor before the call. return true; } virtual EffectSet Effects() const { return EffectSet::All(); } PRINT_OPERANDS_TO_SUPPORT protected: friend class JitOptimizer; void set_ic_data(ICData* value) { ic_data_ = value; } private: const ICData* ic_data_; const TokenPosition token_pos_; const String& function_name_; const Token::Kind token_kind_; // Binary op, unary op, kGET or kILLEGAL. ZoneGrowableArray* const arguments_; const Array& argument_names_; const intptr_t checked_argument_count_; bool has_unique_selector_; DISALLOW_COPY_AND_ASSIGN(InstanceCallInstr); }; class PolymorphicInstanceCallInstr : public TemplateDefinition<0, Throws> { public: PolymorphicInstanceCallInstr(InstanceCallInstr* instance_call, const ICData& ic_data, bool with_checks, bool complete) : TemplateDefinition(instance_call->deopt_id()), instance_call_(instance_call), ic_data_(ic_data), with_checks_(with_checks), complete_(complete) { ASSERT(instance_call_ != NULL); ASSERT(ic_data.NumberOfChecks() > 0); } InstanceCallInstr* instance_call() const { return instance_call_; } bool with_checks() const { return with_checks_; } void set_with_checks(bool b) { with_checks_ = b; } bool complete() const { return complete_; } virtual TokenPosition token_pos() const { return instance_call_->token_pos(); } virtual CompileType ComputeType() const; virtual intptr_t ArgumentCount() const { return instance_call()->ArgumentCount(); } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { return instance_call()->PushArgumentAt(index); } bool HasSingleRecognizedTarget() const; virtual intptr_t CallCount() const { return ic_data().AggregateCount(); } DECLARE_INSTRUCTION(PolymorphicInstanceCall) const ICData& ic_data() const { return ic_data_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::All(); } PRINT_OPERANDS_TO_SUPPORT private: InstanceCallInstr* instance_call_; const ICData& ic_data_; bool with_checks_; const bool complete_; DISALLOW_COPY_AND_ASSIGN(PolymorphicInstanceCallInstr); }; class StrictCompareInstr : public ComparisonInstr { public: StrictCompareInstr(TokenPosition token_pos, Token::Kind kind, Value* left, Value* right, bool needs_number_check); DECLARE_INSTRUCTION(StrictCompare) virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right); virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels); bool needs_number_check() const { return needs_number_check_; } void set_needs_number_check(bool value) { needs_number_check_ = value; } bool AttributesEqual(Instruction* other) const; PRINT_OPERANDS_TO_SUPPORT private: // True if the comparison must check for double, Mint or Bigint and // use value comparison instead. bool needs_number_check_; DISALLOW_COPY_AND_ASSIGN(StrictCompareInstr); }; // Comparison instruction that is equivalent to the (left & right) == 0 // comparison pattern. class TestSmiInstr : public ComparisonInstr { public: TestSmiInstr(TokenPosition token_pos, Token::Kind kind, Value* left, Value* right) : ComparisonInstr(token_pos, kind, left, right) { ASSERT(kind == Token::kEQ || kind == Token::kNE); } DECLARE_INSTRUCTION(TestSmi); virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right); virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { return kTagged; } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels); private: DISALLOW_COPY_AND_ASSIGN(TestSmiInstr); }; // Checks the input value cid against cids stored in a table and returns either // a result or deoptimizes. // TODO(srdjan): Modify ComparisonInstr to allow 1 or 2 arguments, since // TestCidInstr needs only one argument class TestCidsInstr : public ComparisonInstr { public: TestCidsInstr(TokenPosition token_pos, Token::Kind kind, Value* value, const ZoneGrowableArray& cid_results, intptr_t deopt_id) : ComparisonInstr(token_pos, kind, value, NULL, deopt_id), cid_results_(cid_results), licm_hoisted_(false) { ASSERT((kind == Token::kIS) || (kind == Token::kISNOT)); set_operation_cid(kObjectCid); } virtual intptr_t InputCount() const { return 1; } const ZoneGrowableArray& cid_results() const { return cid_results_; } DECLARE_INSTRUCTION(TestCids); virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right); virtual CompileType ComputeType() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool CanDeoptimize() const { return GetDeoptId() != Thread::kNoDeoptId; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { return kTagged; } virtual bool AttributesEqual(Instruction* other) const; virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels); void set_licm_hoisted(bool value) { licm_hoisted_ = value; } PRINT_OPERANDS_TO_SUPPORT private: const ZoneGrowableArray& cid_results_; bool licm_hoisted_; DISALLOW_COPY_AND_ASSIGN(TestCidsInstr); }; class EqualityCompareInstr : public ComparisonInstr { public: EqualityCompareInstr(TokenPosition token_pos, Token::Kind kind, Value* left, Value* right, intptr_t cid, intptr_t deopt_id) : ComparisonInstr(token_pos, kind, left, right, deopt_id) { ASSERT(Token::IsEqualityOperator(kind)); set_operation_cid(cid); } DECLARE_INSTRUCTION(EqualityCompare) virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right); virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels); virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (operation_cid() == kDoubleCid) return kUnboxedDouble; if (operation_cid() == kMintCid) return kUnboxedMint; return kTagged; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(EqualityCompareInstr); }; class RelationalOpInstr : public ComparisonInstr { public: RelationalOpInstr(TokenPosition token_pos, Token::Kind kind, Value* left, Value* right, intptr_t cid, intptr_t deopt_id) : ComparisonInstr(token_pos, kind, left, right, deopt_id) { ASSERT(Token::IsRelationalOperator(kind)); set_operation_cid(cid); } DECLARE_INSTRUCTION(RelationalOp) virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right); virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler, BranchLabels labels); virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (operation_cid() == kDoubleCid) return kUnboxedDouble; if (operation_cid() == kMintCid) return kUnboxedMint; return kTagged; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(RelationalOpInstr); }; // TODO(vegorov): ComparisonInstr should be switched to use IfTheElseInstr for // materialization of true and false constants. class IfThenElseInstr : public Definition { public: IfThenElseInstr(ComparisonInstr* comparison, Value* if_true, Value* if_false) : Definition(Thread::Current()->GetNextDeoptId()), comparison_(comparison), if_true_(Smi::Cast(if_true->BoundConstant()).Value()), if_false_(Smi::Cast(if_false->BoundConstant()).Value()) { // Adjust uses at the comparison. ASSERT(comparison->env() == NULL); for (intptr_t i = comparison->InputCount() - 1; i >= 0; --i) { comparison->InputAt(i)->set_instruction(this); } } // Returns true if this combination of comparison and values flowing on // the true and false paths is supported on the current platform. static bool Supports(ComparisonInstr* comparison, Value* v1, Value* v2); DECLARE_INSTRUCTION(IfThenElse) intptr_t InputCount() const { return comparison()->InputCount(); } Value* InputAt(intptr_t i) const { return comparison()->InputAt(i); } virtual bool CanDeoptimize() const { return comparison()->CanDeoptimize(); } virtual bool CanBecomeDeoptimizationTarget() const { return comparison()->CanBecomeDeoptimizationTarget(); } virtual intptr_t DeoptimizationTarget() const { return comparison()->DeoptimizationTarget(); } virtual Representation RequiredInputRepresentation(intptr_t i) const { return comparison()->RequiredInputRepresentation(i); } virtual CompileType ComputeType() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); ComparisonInstr* comparison() const { return comparison_; } intptr_t if_true() const { return if_true_; } intptr_t if_false() const { return if_false_; } virtual bool AllowsCSE() const { return comparison()->AllowsCSE(); } virtual EffectSet Effects() const { return comparison()->Effects(); } virtual EffectSet Dependencies() const { return comparison()->Dependencies(); } virtual bool AttributesEqual(Instruction* other) const { IfThenElseInstr* other_if_then_else = other->AsIfThenElse(); return (comparison()->tag() == other_if_then_else->comparison()->tag()) && comparison()->AttributesEqual(other_if_then_else->comparison()) && (if_true_ == other_if_then_else->if_true_) && (if_false_ == other_if_then_else->if_false_); } virtual bool MayThrow() const { return comparison()->MayThrow(); } PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { comparison()->RawSetInputAt(i, value); } ComparisonInstr* comparison_; const intptr_t if_true_; const intptr_t if_false_; DISALLOW_COPY_AND_ASSIGN(IfThenElseInstr); }; class StaticCallInstr : public TemplateDefinition<0, Throws> { public: StaticCallInstr(TokenPosition token_pos, const Function& function, const Array& argument_names, ZoneGrowableArray* arguments, const ZoneGrowableArray& ic_data_array) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), ic_data_(NULL), token_pos_(token_pos), function_(function), argument_names_(argument_names), arguments_(arguments), result_cid_(kDynamicCid), is_known_list_constructor_(false), identity_(AliasIdentity::Unknown()) { ic_data_ = GetICData(ic_data_array); ASSERT(function.IsZoneHandle()); ASSERT(!function.IsNull()); ASSERT(argument_names.IsZoneHandle() || argument_names.InVMHeap()); } StaticCallInstr(TokenPosition token_pos, const Function& function, const Array& argument_names, ZoneGrowableArray* arguments, intptr_t deopt_id) : TemplateDefinition(deopt_id), ic_data_(NULL), token_pos_(token_pos), function_(function), argument_names_(argument_names), arguments_(arguments), result_cid_(kDynamicCid), is_known_list_constructor_(false), identity_(AliasIdentity::Unknown()) { ASSERT(function.IsZoneHandle()); ASSERT(!function.IsNull()); ASSERT(argument_names.IsZoneHandle() || argument_names.InVMHeap()); } // ICData for static calls carries call count. const ICData* ic_data() const { return ic_data_; } bool HasICData() const { return (ic_data() != NULL) && !ic_data()->IsNull(); } DECLARE_INSTRUCTION(StaticCall) virtual CompileType ComputeType() const; // Accessors forwarded to the AST node. const Function& function() const { return function_; } const Array& argument_names() const { return argument_names_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { return (*arguments_)[index]; } virtual intptr_t CallCount() const { return ic_data() == NULL ? 0 : ic_data()->AggregateCount(); } virtual bool CanDeoptimize() const { return true; } virtual bool CanBecomeDeoptimizationTarget() const { // Static calls that are specialized by the optimizer (e.g. sqrt) need a // deoptimization descriptor before the call. return true; } virtual EffectSet Effects() const { return EffectSet::All(); } void set_result_cid(intptr_t value) { result_cid_ = value; } bool is_known_list_constructor() const { return is_known_list_constructor_; } void set_is_known_list_constructor(bool value) { is_known_list_constructor_ = value; } bool IsRecognizedFactory() const { return is_known_list_constructor(); } virtual AliasIdentity Identity() const { return identity_; } virtual void SetIdentity(AliasIdentity identity) { identity_ = identity; } PRINT_OPERANDS_TO_SUPPORT private: const ICData* ic_data_; const TokenPosition token_pos_; const Function& function_; const Array& argument_names_; ZoneGrowableArray* arguments_; intptr_t result_cid_; // For some library functions we know the result. // 'True' for recognized list constructors. bool is_known_list_constructor_; AliasIdentity identity_; DISALLOW_COPY_AND_ASSIGN(StaticCallInstr); }; class LoadLocalInstr : public TemplateDefinition<0, NoThrow> { public: LoadLocalInstr(const LocalVariable& local, TokenPosition token_pos) : local_(local), is_last_(false), token_pos_(token_pos) { } DECLARE_INSTRUCTION(LoadLocal) virtual CompileType ComputeType() const; const LocalVariable& local() const { return local_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { UNREACHABLE(); // Eliminated by SSA construction. return EffectSet::None(); } void mark_last() { is_last_ = true; } bool is_last() const { return is_last_; } virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT private: const LocalVariable& local_; bool is_last_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(LoadLocalInstr); }; class DropTempsInstr : public Definition { public: DropTempsInstr(intptr_t num_temps, Value* value) : num_temps_(num_temps), value_(NULL) { if (value != NULL) { SetInputAt(0, value); } } DECLARE_INSTRUCTION(DropTemps) virtual intptr_t InputCount() const { return value_ != NULL ? 1 : 0; } virtual Value* InputAt(intptr_t i) const { ASSERT((value_ != NULL) && (i == 0)); return value_; } Value* value() const { return value_; } intptr_t num_temps() const { return num_temps_; } virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { UNREACHABLE(); // Eliminated by SSA construction. return EffectSet::None(); } virtual bool MayThrow() const { UNREACHABLE(); return false; } virtual TokenPosition token_pos() const { return TokenPosition::kTempMove; } PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { value_ = value; } const intptr_t num_temps_; Value* value_; DISALLOW_COPY_AND_ASSIGN(DropTempsInstr); }; class StoreLocalInstr : public TemplateDefinition<1, NoThrow> { public: StoreLocalInstr(const LocalVariable& local, Value* value, TokenPosition token_pos) : local_(local), is_dead_(false), is_last_(false), token_pos_(token_pos) { SetInputAt(0, value); } DECLARE_INSTRUCTION(StoreLocal) virtual CompileType ComputeType() const; const LocalVariable& local() const { return local_; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } void mark_dead() { is_dead_ = true; } bool is_dead() const { return is_dead_; } void mark_last() { is_last_ = true; } bool is_last() const { return is_last_; } virtual EffectSet Effects() const { UNREACHABLE(); // Eliminated by SSA construction. return EffectSet::None(); } virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT private: const LocalVariable& local_; bool is_dead_; bool is_last_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(StoreLocalInstr); }; class NativeCallInstr : public TemplateDefinition<0, Throws> { public: explicit NativeCallInstr(NativeBodyNode* node) : native_name_(&node->native_c_function_name()), function_(&node->function()), native_c_function_(NULL), is_bootstrap_native_(false), link_lazily_(node->link_lazily()), token_pos_(node->token_pos()) { } NativeCallInstr(const String* name, const Function* function, bool link_lazily, TokenPosition position) : native_name_(name), function_(function), native_c_function_(NULL), is_bootstrap_native_(false), link_lazily_(link_lazily), token_pos_(position) { } DECLARE_INSTRUCTION(NativeCall) const String& native_name() const { return *native_name_; } const Function& function() const { return *function_; } NativeFunction native_c_function() const { return native_c_function_; } bool is_bootstrap_native() const { return is_bootstrap_native_; } bool link_lazily() const { return link_lazily_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::All(); } void SetupNative(); PRINT_OPERANDS_TO_SUPPORT private: void set_native_c_function(NativeFunction value) { native_c_function_ = value; } void set_is_bootstrap_native(bool value) { is_bootstrap_native_ = value; } const String* native_name_; const Function* function_; NativeFunction native_c_function_; bool is_bootstrap_native_; bool link_lazily_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(NativeCallInstr); }; class DebugStepCheckInstr : public TemplateInstruction<0, NoThrow> { public: DebugStepCheckInstr(TokenPosition token_pos, RawPcDescriptors::Kind stub_kind) : token_pos_(token_pos), stub_kind_(stub_kind) { } DECLARE_INSTRUCTION(DebugStepCheck) virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::All(); } virtual Instruction* Canonicalize(FlowGraph* flow_graph); private: const TokenPosition token_pos_; const RawPcDescriptors::Kind stub_kind_; DISALLOW_COPY_AND_ASSIGN(DebugStepCheckInstr); }; enum StoreBarrierType { kNoStoreBarrier, kEmitStoreBarrier }; class StoreInstanceFieldInstr : public TemplateDefinition<2, NoThrow> { public: StoreInstanceFieldInstr(const Field& field, Value* instance, Value* value, StoreBarrierType emit_store_barrier, TokenPosition token_pos) : field_(field), offset_in_bytes_(field.Offset()), emit_store_barrier_(emit_store_barrier), token_pos_(token_pos), is_initialization_(false) { SetInputAt(kInstancePos, instance); SetInputAt(kValuePos, value); CheckField(field); } StoreInstanceFieldInstr(intptr_t offset_in_bytes, Value* instance, Value* value, StoreBarrierType emit_store_barrier, TokenPosition token_pos) : field_(Field::ZoneHandle()), offset_in_bytes_(offset_in_bytes), emit_store_barrier_(emit_store_barrier), token_pos_(token_pos), is_initialization_(false) { SetInputAt(kInstancePos, instance); SetInputAt(kValuePos, value); } DECLARE_INSTRUCTION(StoreInstanceField) void set_is_initialization(bool value) { is_initialization_ = value; } enum { kInstancePos = 0, kValuePos = 1 }; Value* instance() const { return inputs_[kInstancePos]; } Value* value() const { return inputs_[kValuePos]; } bool is_initialization() const { return is_initialization_; } virtual TokenPosition token_pos() const { return token_pos_; } const Field& field() const { return field_; } intptr_t offset_in_bytes() const { return offset_in_bytes_; } bool ShouldEmitStoreBarrier() const { return value()->NeedsStoreBuffer() && (emit_store_barrier_ == kEmitStoreBarrier); } virtual bool CanDeoptimize() const { return false; } // May require a deoptimization target for input conversions. virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } // Currently CSE/LICM don't operate on any instructions that can be affected // by stores/loads. LoadOptimizer handles loads separately. Hence stores // are marked as having no side-effects. virtual EffectSet Effects() const { return EffectSet::None(); } bool IsUnboxedStore() const; bool IsPotentialUnboxedStore() const; virtual Representation RequiredInputRepresentation(intptr_t index) const; PRINT_OPERANDS_TO_SUPPORT private: friend class JitOptimizer; // For ASSERT(initialization_). bool CanValueBeSmi() const { const intptr_t cid = value()->Type()->ToNullableCid(); // Write barrier is skipped for nullable and non-nullable smis. ASSERT(cid != kSmiCid); return (cid == kDynamicCid); } const Field& field_; intptr_t offset_in_bytes_; const StoreBarrierType emit_store_barrier_; const TokenPosition token_pos_; // Marks initialiing stores. E.g. in the constructor. bool is_initialization_; DISALLOW_COPY_AND_ASSIGN(StoreInstanceFieldInstr); }; class GuardFieldInstr : public TemplateInstruction<1, NoThrow, Pure> { public: GuardFieldInstr(Value* value, const Field& field, intptr_t deopt_id) : TemplateInstruction(deopt_id), field_(field) { SetInputAt(0, value); CheckField(field); } Value* value() const { return inputs_[0]; } const Field& field() const { return field_; } virtual bool CanDeoptimize() const { return true; } virtual bool CanBecomeDeoptimizationTarget() const { // Ensure that we record kDeopt PC descriptor in unoptimized code. return true; } PRINT_OPERANDS_TO_SUPPORT private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(GuardFieldInstr); }; class GuardFieldClassInstr : public GuardFieldInstr { public: GuardFieldClassInstr(Value* value, const Field& field, intptr_t deopt_id) : GuardFieldInstr(value, field, deopt_id) { CheckField(field); } DECLARE_INSTRUCTION(GuardFieldClass) virtual Instruction* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const; private: DISALLOW_COPY_AND_ASSIGN(GuardFieldClassInstr); }; class GuardFieldLengthInstr : public GuardFieldInstr { public: GuardFieldLengthInstr(Value* value, const Field& field, intptr_t deopt_id) : GuardFieldInstr(value, field, deopt_id) { CheckField(field); } DECLARE_INSTRUCTION(GuardFieldLength) virtual Instruction* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const; private: DISALLOW_COPY_AND_ASSIGN(GuardFieldLengthInstr); }; class LoadStaticFieldInstr : public TemplateDefinition<1, NoThrow> { public: LoadStaticFieldInstr(Value* field_value, TokenPosition token_pos) : token_pos_(token_pos) { ASSERT(field_value->BindsToConstant()); SetInputAt(0, field_value); } DECLARE_INSTRUCTION(LoadStaticField) virtual CompileType ComputeType() const; const Field& StaticField() const; Value* field_value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool AllowsCSE() const { return StaticField().is_final(); } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const; virtual bool AttributesEqual(Instruction* other) const; virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(LoadStaticFieldInstr); }; class StoreStaticFieldInstr : public TemplateDefinition<1, NoThrow> { public: StoreStaticFieldInstr(const Field& field, Value* value, TokenPosition token_pos) : field_(field), token_pos_(token_pos) { ASSERT(field.IsZoneHandle()); SetInputAt(kValuePos, value); CheckField(field); } enum { kValuePos = 0 }; DECLARE_INSTRUCTION(StoreStaticField) const Field& field() const { return field_; } Value* value() const { return inputs_[kValuePos]; } virtual bool CanDeoptimize() const { return false; } // Currently CSE/LICM don't operate on any instructions that can be affected // by stores/loads. LoadOptimizer handles loads separately. Hence stores // are marked as having no side-effects. virtual EffectSet Effects() const { return EffectSet::None(); } virtual TokenPosition token_pos() const { return token_pos_; } PRINT_OPERANDS_TO_SUPPORT private: bool CanValueBeSmi() const { const intptr_t cid = value()->Type()->ToNullableCid(); // Write barrier is skipped for nullable and non-nullable smis. ASSERT(cid != kSmiCid); return (cid == kDynamicCid); } const Field& field_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(StoreStaticFieldInstr); }; class LoadIndexedInstr : public TemplateDefinition<2, NoThrow> { public: LoadIndexedInstr(Value* array, Value* index, intptr_t index_scale, intptr_t class_id, intptr_t deopt_id, TokenPosition token_pos) : TemplateDefinition(deopt_id), index_scale_(index_scale), class_id_(class_id), token_pos_(token_pos) { SetInputAt(0, array); SetInputAt(1, index); } TokenPosition token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(LoadIndexed) virtual CompileType ComputeType() const; virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0 || idx == 1); // The array may be tagged or untagged (for external arrays). if (idx == 0) return kNoRepresentation; return kTagged; } bool IsExternal() const { return array()->definition()->representation() == kUntagged; } Value* array() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } intptr_t index_scale() const { return index_scale_; } intptr_t class_id() const { return class_id_; } virtual bool CanDeoptimize() const { return GetDeoptId() != Thread::kNoDeoptId; } virtual Representation representation() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual EffectSet Effects() const { return EffectSet::None(); } private: const intptr_t index_scale_; const intptr_t class_id_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(LoadIndexedInstr); }; // Loads the specified number of code units from the given string, packing // multiple code units into a single datatype. In essence, this is a specialized // version of LoadIndexedInstr which accepts only string targets and can load // multiple elements at once. The result datatype differs depending on the // string type, element count, and architecture; if possible, the result is // packed into a Smi, falling back to a Mint otherwise. // TODO(zerny): Add support for loading into UnboxedInt32x4. class LoadCodeUnitsInstr : public TemplateDefinition<2, NoThrow> { public: LoadCodeUnitsInstr(Value* str, Value* index, intptr_t element_count, intptr_t class_id, TokenPosition token_pos) : class_id_(class_id), token_pos_(token_pos), element_count_(element_count), representation_(kTagged) { ASSERT(element_count == 1 || element_count == 2 || element_count == 4); ASSERT(RawObject::IsStringClassId(class_id)); SetInputAt(0, str); SetInputAt(1, index); } TokenPosition token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(LoadCodeUnits) virtual CompileType ComputeType() const; virtual Representation RequiredInputRepresentation(intptr_t idx) const { if (idx == 0) { // The string may be tagged or untagged (for external strings). return kNoRepresentation; } ASSERT(idx == 1); return kTagged; } bool IsExternal() const { return array()->definition()->representation() == kUntagged; } Value* array() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } intptr_t index_scale() const { return Instance::ElementSizeFor(class_id_); } intptr_t class_id() const { return class_id_; } intptr_t element_count() const { return element_count_; } bool can_pack_into_smi() const { return element_count() <= kSmiBits / (index_scale() * kBitsPerByte); } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return representation_; } void set_representation(Representation repr) { representation_ = repr; } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual EffectSet Effects() const { return EffectSet::None(); } private: const intptr_t class_id_; const TokenPosition token_pos_; const intptr_t element_count_; Representation representation_; DISALLOW_COPY_AND_ASSIGN(LoadCodeUnitsInstr); }; class OneByteStringFromCharCodeInstr : public TemplateDefinition<1, NoThrow, Pure> { public: explicit OneByteStringFromCharCodeInstr(Value* char_code) { SetInputAt(0, char_code); } DECLARE_INSTRUCTION(OneByteStringFromCharCode) virtual CompileType ComputeType() const; Value* char_code() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(OneByteStringFromCharCodeInstr); }; class StringToCharCodeInstr : public TemplateDefinition<1, NoThrow, Pure> { public: StringToCharCodeInstr(Value* str, intptr_t cid) : cid_(cid) { ASSERT(str != NULL); SetInputAt(0, str); } DECLARE_INSTRUCTION(StringToCharCode) virtual CompileType ComputeType() const; Value* str() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return other->AsStringToCharCode()->cid_ == cid_; } private: const intptr_t cid_; DISALLOW_COPY_AND_ASSIGN(StringToCharCodeInstr); }; class StringInterpolateInstr : public TemplateDefinition<1, Throws> { public: StringInterpolateInstr(Value* value, TokenPosition token_pos) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), function_(Function::ZoneHandle()) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } virtual TokenPosition token_pos() const { return token_pos_; } virtual CompileType ComputeType() const; // Issues a static call to Dart code which calls toString on objects. virtual EffectSet Effects() const { return EffectSet::All(); } virtual bool CanDeoptimize() const { return true; } const Function& CallFunction() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); DECLARE_INSTRUCTION(StringInterpolate) private: const TokenPosition token_pos_; Function& function_; DISALLOW_COPY_AND_ASSIGN(StringInterpolateInstr); }; class StoreIndexedInstr : public TemplateDefinition<3, NoThrow> { public: StoreIndexedInstr(Value* array, Value* index, Value* value, StoreBarrierType emit_store_barrier, intptr_t index_scale, intptr_t class_id, intptr_t deopt_id, TokenPosition token_pos) : TemplateDefinition(deopt_id), emit_store_barrier_(emit_store_barrier), index_scale_(index_scale), class_id_(class_id), token_pos_(token_pos) { SetInputAt(kArrayPos, array); SetInputAt(kIndexPos, index); SetInputAt(kValuePos, value); } DECLARE_INSTRUCTION(StoreIndexed) enum { kArrayPos = 0, kIndexPos = 1, kValuePos = 2 }; Value* array() const { return inputs_[kArrayPos]; } Value* index() const { return inputs_[kIndexPos]; } Value* value() const { return inputs_[kValuePos]; } intptr_t index_scale() const { return index_scale_; } intptr_t class_id() const { return class_id_; } bool ShouldEmitStoreBarrier() const { return value()->NeedsStoreBuffer() && (emit_store_barrier_ == kEmitStoreBarrier); } virtual bool CanDeoptimize() const { return false; } virtual Representation RequiredInputRepresentation(intptr_t idx) const; bool IsExternal() const { return array()->definition()->representation() == kUntagged; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } virtual EffectSet Effects() const { return EffectSet::None(); } private: const StoreBarrierType emit_store_barrier_; const intptr_t index_scale_; const intptr_t class_id_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(StoreIndexedInstr); }; // Note overrideable, built-in: value ? false : true. class BooleanNegateInstr : public TemplateDefinition<1, NoThrow> { public: explicit BooleanNegateInstr(Value* value) { SetInputAt(0, value); } DECLARE_INSTRUCTION(BooleanNegate) virtual CompileType ComputeType() const; Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual Definition* Canonicalize(FlowGraph* flow_graph); private: DISALLOW_COPY_AND_ASSIGN(BooleanNegateInstr); }; class InstanceOfInstr : public TemplateDefinition<2, Throws> { public: InstanceOfInstr(TokenPosition token_pos, Value* value, Value* instantiator_type_arguments, const AbstractType& type, bool negate_result, intptr_t deopt_id) : TemplateDefinition(deopt_id), token_pos_(token_pos), type_(type), negate_result_(negate_result) { ASSERT(!type.IsNull()); SetInputAt(0, value); SetInputAt(1, instantiator_type_arguments); } DECLARE_INSTRUCTION(InstanceOf) virtual CompileType ComputeType() const; Value* value() const { return inputs_[0]; } Value* instantiator_type_arguments() const { return inputs_[1]; } bool negate_result() const { return negate_result_; } const AbstractType& type() const { return type_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; Value* value_; Value* type_arguments_; const AbstractType& type_; const bool negate_result_; DISALLOW_COPY_AND_ASSIGN(InstanceOfInstr); }; class AllocateObjectInstr : public TemplateDefinition<0, NoThrow> { public: AllocateObjectInstr(TokenPosition token_pos, const Class& cls, ZoneGrowableArray* arguments) : token_pos_(token_pos), cls_(cls), arguments_(arguments), identity_(AliasIdentity::Unknown()), closure_function_(Function::ZoneHandle()) { // Either no arguments or one type-argument and one instantiator. ASSERT(arguments->is_empty() || (arguments->length() == 1)); } DECLARE_INSTRUCTION(AllocateObject) virtual CompileType ComputeType() const; virtual intptr_t ArgumentCount() const { return arguments_->length(); } virtual PushArgumentInstr* PushArgumentAt(intptr_t index) const { return (*arguments_)[index]; } const Class& cls() const { return cls_; } virtual TokenPosition token_pos() const { return token_pos_; } const Function& closure_function() const { return closure_function_; } void set_closure_function(const Function& function) { closure_function_ ^= function.raw(); } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual AliasIdentity Identity() const { return identity_; } virtual void SetIdentity(AliasIdentity identity) { identity_ = identity; } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const Class& cls_; ZoneGrowableArray* const arguments_; AliasIdentity identity_; Function& closure_function_; DISALLOW_COPY_AND_ASSIGN(AllocateObjectInstr); }; class AllocateUninitializedContextInstr : public TemplateDefinition<0, NoThrow> { public: AllocateUninitializedContextInstr(TokenPosition token_pos, intptr_t num_context_variables) : token_pos_(token_pos), num_context_variables_(num_context_variables), identity_(AliasIdentity::Unknown()) {} DECLARE_INSTRUCTION(AllocateUninitializedContext) virtual CompileType ComputeType() const; virtual TokenPosition token_pos() const { return token_pos_; } intptr_t num_context_variables() const { return num_context_variables_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual AliasIdentity Identity() const { return identity_; } virtual void SetIdentity(AliasIdentity identity) { identity_ = identity; } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const intptr_t num_context_variables_; AliasIdentity identity_; DISALLOW_COPY_AND_ASSIGN(AllocateUninitializedContextInstr); }; // This instruction captures the state of the object which had its allocation // removed during the AllocationSinking pass. // It does not produce any real code only deoptimization information. class MaterializeObjectInstr : public Definition { public: MaterializeObjectInstr(AllocateObjectInstr* allocation, const ZoneGrowableArray& slots, ZoneGrowableArray* values) : allocation_(allocation), cls_(allocation->cls()), num_variables_(-1), slots_(slots), values_(values), locations_(NULL), visited_for_liveness_(false), registers_remapped_(false) { ASSERT(slots_.length() == values_->length()); for (intptr_t i = 0; i < InputCount(); i++) { InputAt(i)->set_instruction(this); InputAt(i)->set_use_index(i); } } MaterializeObjectInstr(AllocateUninitializedContextInstr* allocation, const ZoneGrowableArray& slots, ZoneGrowableArray* values) : allocation_(allocation), cls_(Class::ZoneHandle(Object::context_class())), num_variables_(allocation->num_context_variables()), slots_(slots), values_(values), locations_(NULL), visited_for_liveness_(false), registers_remapped_(false) { ASSERT(slots_.length() == values_->length()); for (intptr_t i = 0; i < InputCount(); i++) { InputAt(i)->set_instruction(this); InputAt(i)->set_use_index(i); } } Definition* allocation() const { return allocation_; } const Class& cls() const { return cls_; } intptr_t num_variables() const { return num_variables_; } intptr_t FieldOffsetAt(intptr_t i) const { return slots_[i]->IsField() ? Field::Cast(*slots_[i]).Offset() : Smi::Cast(*slots_[i]).Value(); } const Location& LocationAt(intptr_t i) { return locations_[i]; } DECLARE_INSTRUCTION(MaterializeObject) virtual intptr_t InputCount() const { return values_->length(); } virtual Value* InputAt(intptr_t i) const { return (*values_)[i]; } // SelectRepresentations pass is run once more while MaterializeObject // instructions are still in the graph. To avoid any redundant boxing // operations inserted by that pass we should indicate that this // instruction can cope with any representation as it is essentially // an environment use. virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(0 <= idx && idx < InputCount()); return kNoRepresentation; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } Location* locations() { return locations_; } void set_locations(Location* locations) { locations_ = locations; } virtual bool MayThrow() const { return false; } void RemapRegisters(intptr_t* cpu_reg_slots, intptr_t* fpu_reg_slots); bool was_visited_for_liveness() const { return visited_for_liveness_; } void mark_visited_for_liveness() { visited_for_liveness_ = true; } PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { (*values_)[i] = value; } Definition* allocation_; const Class& cls_; intptr_t num_variables_; const ZoneGrowableArray& slots_; ZoneGrowableArray* values_; Location* locations_; bool visited_for_liveness_; bool registers_remapped_; DISALLOW_COPY_AND_ASSIGN(MaterializeObjectInstr); }; class CreateArrayInstr : public TemplateDefinition<2, Throws> { public: CreateArrayInstr(TokenPosition token_pos, Value* element_type, Value* num_elements) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), identity_(AliasIdentity::Unknown()) { SetInputAt(kElementTypePos, element_type); SetInputAt(kLengthPos, num_elements); } enum { kElementTypePos = 0, kLengthPos = 1 }; DECLARE_INSTRUCTION(CreateArray) virtual CompileType ComputeType() const; virtual TokenPosition token_pos() const { return token_pos_; } Value* element_type() const { return inputs_[kElementTypePos]; } Value* num_elements() const { return inputs_[kLengthPos]; } // Throw needs environment, which is created only if instruction can // deoptimize. virtual bool CanDeoptimize() const { return MayThrow(); } virtual EffectSet Effects() const { return EffectSet::None(); } virtual AliasIdentity Identity() const { return identity_; } virtual void SetIdentity(AliasIdentity identity) { identity_ = identity; } private: const TokenPosition token_pos_; AliasIdentity identity_; DISALLOW_COPY_AND_ASSIGN(CreateArrayInstr); }; // Note: this instruction must not be moved without the indexed access that // depends on it (e.g. out of loops). GC may cause collect // the array while the external data-array is still accessed. // TODO(vegorov) enable LICMing this instruction by ensuring that array itself // is kept alive. class LoadUntaggedInstr : public TemplateDefinition<1, NoThrow> { public: LoadUntaggedInstr(Value* object, intptr_t offset) : offset_(offset) { SetInputAt(0, object); } virtual Representation representation() const { return kUntagged; } DECLARE_INSTRUCTION(LoadUntagged) virtual CompileType ComputeType() const; virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); // The object may be tagged or untagged (for external objects). return kNoRepresentation; } Value* object() const { return inputs_[0]; } intptr_t offset() const { return offset_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { return true; } private: intptr_t offset_; DISALLOW_COPY_AND_ASSIGN(LoadUntaggedInstr); }; class LoadClassIdInstr : public TemplateDefinition<1, NoThrow> { public: explicit LoadClassIdInstr(Value* object) { SetInputAt(0, object); } virtual Representation representation() const { return kTagged; } DECLARE_INSTRUCTION(LoadClassId) virtual CompileType ComputeType() const; Value* object() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool AllowsCSE() const { return true; } virtual EffectSet Dependencies() const { return EffectSet::Externalization(); } virtual EffectSet Effects() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(LoadClassIdInstr); }; class LoadFieldInstr : public TemplateDefinition<1, NoThrow> { public: LoadFieldInstr(Value* instance, intptr_t offset_in_bytes, const AbstractType& type, TokenPosition token_pos) : offset_in_bytes_(offset_in_bytes), type_(type), result_cid_(kDynamicCid), immutable_(false), recognized_kind_(MethodRecognizer::kUnknown), field_(NULL), token_pos_(token_pos) { ASSERT(offset_in_bytes >= 0); // May be null if field is not an instance. ASSERT(type.IsZoneHandle() || type.IsReadOnlyHandle()); SetInputAt(0, instance); } LoadFieldInstr(Value* instance, const Field* field, const AbstractType& type, TokenPosition token_pos) : offset_in_bytes_(field->Offset()), type_(type), result_cid_(kDynamicCid), immutable_(false), recognized_kind_(MethodRecognizer::kUnknown), field_(field), token_pos_(token_pos) { ASSERT(field->IsZoneHandle()); // May be null if field is not an instance. ASSERT(type.IsZoneHandle() || type.IsReadOnlyHandle()); SetInputAt(0, instance); } void set_is_immutable(bool value) { immutable_ = value; } Value* instance() const { return inputs_[0]; } intptr_t offset_in_bytes() const { return offset_in_bytes_; } const AbstractType& type() const { return type_; } void set_result_cid(intptr_t value) { result_cid_ = value; } intptr_t result_cid() const { return result_cid_; } virtual TokenPosition token_pos() const { return token_pos_; } const Field* field() const { return field_; } virtual Representation representation() const; bool IsUnboxedLoad() const; bool IsPotentialUnboxedLoad() const; void set_recognized_kind(MethodRecognizer::Kind kind) { recognized_kind_ = kind; } MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } DECLARE_INSTRUCTION(LoadField) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual void InferRange(RangeAnalysis* analysis, Range* range); bool IsImmutableLengthLoad() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); static MethodRecognizer::Kind RecognizedKindFromArrayCid(intptr_t cid); static bool IsFixedLengthArrayCid(intptr_t cid); virtual bool AllowsCSE() const { return immutable_; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const; virtual bool AttributesEqual(Instruction* other) const; PRINT_OPERANDS_TO_SUPPORT private: const intptr_t offset_in_bytes_; const AbstractType& type_; intptr_t result_cid_; bool immutable_; MethodRecognizer::Kind recognized_kind_; const Field* field_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(LoadFieldInstr); }; class InstantiateTypeInstr : public TemplateDefinition<1, Throws> { public: InstantiateTypeInstr(TokenPosition token_pos, const AbstractType& type, const Class& instantiator_class, Value* instantiator) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), type_(type), instantiator_class_(instantiator_class) { ASSERT(type.IsZoneHandle() || type.IsReadOnlyHandle()); SetInputAt(0, instantiator); } DECLARE_INSTRUCTION(InstantiateType) Value* instantiator() const { return inputs_[0]; } const AbstractType& type() const { return type_; } const Class& instantiator_class() const { return instantiator_class_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const AbstractType& type_; const Class& instantiator_class_; DISALLOW_COPY_AND_ASSIGN(InstantiateTypeInstr); }; class InstantiateTypeArgumentsInstr : public TemplateDefinition<1, Throws> { public: InstantiateTypeArgumentsInstr(TokenPosition token_pos, const TypeArguments& type_arguments, const Class& instantiator_class, Value* instantiator) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), type_arguments_(type_arguments), instantiator_class_(instantiator_class) { ASSERT(type_arguments.IsZoneHandle()); SetInputAt(0, instantiator); } DECLARE_INSTRUCTION(InstantiateTypeArguments) Value* instantiator() const { return inputs_[0]; } const TypeArguments& type_arguments() const { return type_arguments_; } const Class& instantiator_class() const { return instantiator_class_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual Definition* Canonicalize(FlowGraph* flow_graph); PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const TypeArguments& type_arguments_; const Class& instantiator_class_; DISALLOW_COPY_AND_ASSIGN(InstantiateTypeArgumentsInstr); }; class AllocateContextInstr : public TemplateDefinition<0, NoThrow> { public: AllocateContextInstr(TokenPosition token_pos, intptr_t num_context_variables) : token_pos_(token_pos), num_context_variables_(num_context_variables) { } DECLARE_INSTRUCTION(AllocateContext) virtual CompileType ComputeType() const; virtual TokenPosition token_pos() const { return token_pos_; } intptr_t num_context_variables() const { return num_context_variables_; } virtual bool CanDeoptimize() const { return false; } virtual EffectSet Effects() const { return EffectSet::None(); } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const intptr_t num_context_variables_; DISALLOW_COPY_AND_ASSIGN(AllocateContextInstr); }; class InitStaticFieldInstr : public TemplateInstruction<1, Throws> { public: InitStaticFieldInstr(Value* input, const Field& field) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), field_(field) { SetInputAt(0, input); CheckField(field); } virtual TokenPosition token_pos() const { return field_.token_pos(); } const Field& field() const { return field_; } DECLARE_INSTRUCTION(InitStaticField) virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::All(); } virtual Instruction* Canonicalize(FlowGraph* flow_graph); private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(InitStaticFieldInstr); }; class CloneContextInstr : public TemplateDefinition<1, NoThrow> { public: CloneContextInstr(TokenPosition token_pos, Value* context_value) : TemplateDefinition(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos) { SetInputAt(0, context_value); } virtual TokenPosition token_pos() const { return token_pos_; } Value* context_value() const { return inputs_[0]; } DECLARE_INSTRUCTION(CloneContext) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(CloneContextInstr); }; class CheckEitherNonSmiInstr : public TemplateInstruction<2, NoThrow, Pure> { public: CheckEitherNonSmiInstr(Value* left, Value* right, intptr_t deopt_id) : TemplateInstruction(deopt_id), licm_hoisted_(false) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } DECLARE_INSTRUCTION(CheckEitherNonSmi) virtual bool CanDeoptimize() const { return true; } virtual Instruction* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const { return true; } void set_licm_hoisted(bool value) { licm_hoisted_ = value; } private: bool licm_hoisted_; DISALLOW_COPY_AND_ASSIGN(CheckEitherNonSmiInstr); }; class Boxing : public AllStatic { public: static bool Supports(Representation rep) { switch (rep) { case kUnboxedDouble: case kUnboxedFloat32x4: case kUnboxedFloat64x2: case kUnboxedInt32x4: case kUnboxedMint: case kUnboxedInt32: case kUnboxedUint32: return true; default: return false; } } static intptr_t ValueOffset(Representation rep) { switch (rep) { case kUnboxedDouble: return Double::value_offset(); case kUnboxedFloat32x4: return Float32x4::value_offset(); case kUnboxedFloat64x2: return Float64x2::value_offset(); case kUnboxedInt32x4: return Int32x4::value_offset(); case kUnboxedMint: return Mint::value_offset(); default: UNREACHABLE(); return 0; } } static intptr_t BoxCid(Representation rep) { switch (rep) { case kUnboxedMint: return kMintCid; case kUnboxedDouble: return kDoubleCid; case kUnboxedFloat32x4: return kFloat32x4Cid; case kUnboxedFloat64x2: return kFloat64x2Cid; case kUnboxedInt32x4: return kInt32x4Cid; default: UNREACHABLE(); return kIllegalCid; } } }; class BoxInstr : public TemplateDefinition<1, NoThrow, Pure> { public: static BoxInstr* Create(Representation from, Value* value); Value* value() const { return inputs_[0]; } Representation from_representation() const { return from_representation_; } DECLARE_INSTRUCTION(Box) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual intptr_t DeoptimizationTarget() const { return Thread::kNoDeoptId; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return from_representation(); } virtual bool AttributesEqual(Instruction* other) const { return other->AsBox()->from_representation() == from_representation(); } Definition* Canonicalize(FlowGraph* flow_graph); virtual TokenPosition token_pos() const { return TokenPosition::kBox; } protected: BoxInstr(Representation from_representation, Value* value) : from_representation_(from_representation) { SetInputAt(0, value); } private: intptr_t ValueOffset() const { return Boxing::ValueOffset(from_representation()); } const Representation from_representation_; DISALLOW_COPY_AND_ASSIGN(BoxInstr); }; class BoxIntegerInstr : public BoxInstr { public: BoxIntegerInstr(Representation representation, Value* value) : BoxInstr(representation, value) { } virtual bool ValueFitsSmi() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const; virtual bool RecomputeType(); virtual Definition* Canonicalize(FlowGraph* flow_graph); DEFINE_INSTRUCTION_TYPE_CHECK(BoxInteger) private: DISALLOW_COPY_AND_ASSIGN(BoxIntegerInstr); }; class BoxInteger32Instr : public BoxIntegerInstr { public: BoxInteger32Instr(Representation representation, Value* value) : BoxIntegerInstr(representation, value) { } DECLARE_INSTRUCTION_BACKEND() private: DISALLOW_COPY_AND_ASSIGN(BoxInteger32Instr); }; class BoxInt32Instr : public BoxInteger32Instr { public: explicit BoxInt32Instr(Value* value) : BoxInteger32Instr(kUnboxedInt32, value) { } DECLARE_INSTRUCTION_NO_BACKEND(BoxInt32) private: DISALLOW_COPY_AND_ASSIGN(BoxInt32Instr); }; class BoxUint32Instr : public BoxInteger32Instr { public: explicit BoxUint32Instr(Value* value) : BoxInteger32Instr(kUnboxedUint32, value) { } DECLARE_INSTRUCTION_NO_BACKEND(BoxUint32) private: DISALLOW_COPY_AND_ASSIGN(BoxUint32Instr); }; class BoxInt64Instr : public BoxIntegerInstr { public: explicit BoxInt64Instr(Value* value) : BoxIntegerInstr(kUnboxedMint, value) { } virtual Definition* Canonicalize(FlowGraph* flow_graph); DECLARE_INSTRUCTION(BoxInt64) private: DISALLOW_COPY_AND_ASSIGN(BoxInt64Instr); }; class UnboxInstr : public TemplateDefinition<1, NoThrow, Pure> { public: static UnboxInstr* Create(Representation to, Value* value, intptr_t deopt_id); Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { const intptr_t value_cid = value()->Type()->ToCid(); if (CanConvertSmi() && (value()->Type()->ToCid() == kSmiCid)) { return false; } return (value_cid != BoxCid()); } virtual Representation representation() const { return representation_; } DECLARE_INSTRUCTION(Unbox) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return representation() == other->AsUnbox()->representation(); } Definition* Canonicalize(FlowGraph* flow_graph); virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual TokenPosition token_pos() const { return TokenPosition::kBox; } protected: UnboxInstr(Representation representation, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), representation_(representation) { SetInputAt(0, value); } private: bool CanConvertSmi() const; void EmitLoadFromBox(FlowGraphCompiler* compiler); void EmitSmiConversion(FlowGraphCompiler* compiler); intptr_t BoxCid() const { return Boxing::BoxCid(representation_); } intptr_t ValueOffset() const { return Boxing::ValueOffset(representation_); } const Representation representation_; DISALLOW_COPY_AND_ASSIGN(UnboxInstr); }; class UnboxIntegerInstr : public UnboxInstr { public: enum TruncationMode { kTruncate, kNoTruncation }; UnboxIntegerInstr(Representation representation, TruncationMode truncation_mode, Value* value, intptr_t deopt_id) : UnboxInstr(representation, value, deopt_id), is_truncating_(truncation_mode == kTruncate) { } bool is_truncating() const { return is_truncating_; } virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { UnboxIntegerInstr* other_unbox = other->AsUnboxInteger(); return UnboxInstr::AttributesEqual(other) && (other_unbox->is_truncating_ == is_truncating_); } virtual Definition* Canonicalize(FlowGraph* flow_graph); DEFINE_INSTRUCTION_TYPE_CHECK(UnboxInteger) PRINT_OPERANDS_TO_SUPPORT private: bool is_truncating_; DISALLOW_COPY_AND_ASSIGN(UnboxIntegerInstr); }; class UnboxInteger32Instr : public UnboxIntegerInstr { public: UnboxInteger32Instr(Representation representation, TruncationMode truncation_mode, Value* value, intptr_t deopt_id) : UnboxIntegerInstr(representation, truncation_mode, value, deopt_id) { } DECLARE_INSTRUCTION_BACKEND() private: DISALLOW_COPY_AND_ASSIGN(UnboxInteger32Instr); }; class UnboxUint32Instr : public UnboxInteger32Instr { public: UnboxUint32Instr(Value* value, intptr_t deopt_id) : UnboxInteger32Instr(kUnboxedUint32, kTruncate, value, deopt_id) { ASSERT(is_truncating()); } virtual bool CanDeoptimize() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); DECLARE_INSTRUCTION_NO_BACKEND(UnboxUint32) private: DISALLOW_COPY_AND_ASSIGN(UnboxUint32Instr); }; class UnboxInt32Instr : public UnboxInteger32Instr { public: UnboxInt32Instr(TruncationMode truncation_mode, Value* value, intptr_t deopt_id) : UnboxInteger32Instr(kUnboxedInt32, truncation_mode, value, deopt_id) { } virtual bool CanDeoptimize() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual Definition* Canonicalize(FlowGraph* flow_graph); DECLARE_INSTRUCTION_NO_BACKEND(UnboxInt32) private: DISALLOW_COPY_AND_ASSIGN(UnboxInt32Instr); }; class UnboxInt64Instr : public UnboxIntegerInstr { public: UnboxInt64Instr(Value* value, intptr_t deopt_id) : UnboxIntegerInstr(kUnboxedMint, kNoTruncation, value, deopt_id) { } virtual void InferRange(RangeAnalysis* analysis, Range* range); DECLARE_INSTRUCTION_NO_BACKEND(UnboxInt64) private: DISALLOW_COPY_AND_ASSIGN(UnboxInt64Instr); }; bool Definition::IsMintDefinition() { return (Type()->ToCid() == kMintCid) || IsBinaryMintOp() || IsUnaryMintOp() || IsShiftMintOp() || IsBoxInt64() || IsUnboxInt64(); } class MathUnaryInstr : public TemplateDefinition<1, NoThrow, Pure> { public: enum MathUnaryKind { kIllegal, kSqrt, kDoubleSquare, }; MathUnaryInstr(MathUnaryKind kind, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), kind_(kind) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MathUnaryKind kind() const { return kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(MathUnary) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return kind() == other->AsMathUnary()->kind(); } Definition* Canonicalize(FlowGraph* flow_graph); static const char* KindToCString(MathUnaryKind kind); PRINT_OPERANDS_TO_SUPPORT private: const MathUnaryKind kind_; DISALLOW_COPY_AND_ASSIGN(MathUnaryInstr); }; // Calls into the runtime and performs a case-insensitive comparison of the // UTF16 strings (i.e. TwoByteString or ExternalTwoByteString) located at // str[lhs_index:lhs_index + length] and str[rhs_index:rhs_index + length]. // // TODO(zerny): Remove this once (if) functions inherited from unibrow // are moved to dart code. class CaseInsensitiveCompareUC16Instr : public TemplateDefinition<4, NoThrow, Pure> { public: CaseInsensitiveCompareUC16Instr( Value* str, Value* lhs_index, Value* rhs_index, Value* length, intptr_t cid) : cid_(cid) { ASSERT(cid == kTwoByteStringCid || cid == kExternalTwoByteStringCid); ASSERT(index_scale() == 2); SetInputAt(0, str); SetInputAt(1, lhs_index); SetInputAt(2, rhs_index); SetInputAt(3, length); } Value* str() const { return inputs_[0]; } Value* lhs_index() const { return inputs_[1]; } Value* rhs_index() const { return inputs_[2]; } Value* length() const { return inputs_[3]; } const RuntimeEntry& TargetFunction() const; bool IsExternal() const { return cid_ == kExternalTwoByteStringCid; } intptr_t class_id() const { return cid_; } intptr_t index_scale() const { return Instance::ElementSizeFor(cid_); } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kTagged; } DECLARE_INSTRUCTION(CaseInsensitiveCompareUC16) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return other->AsCaseInsensitiveCompareUC16()->cid_ == cid_; } private: const intptr_t cid_; DISALLOW_COPY_AND_ASSIGN(CaseInsensitiveCompareUC16Instr); }; // Represents Math's static min and max functions. class MathMinMaxInstr : public TemplateDefinition<2, NoThrow, Pure> { public: MathMinMaxInstr(MethodRecognizer::Kind op_kind, Value* left_value, Value* right_value, intptr_t deopt_id, intptr_t result_cid) : TemplateDefinition(deopt_id), op_kind_(op_kind), result_cid_(result_cid) { ASSERT((result_cid == kSmiCid) || (result_cid == kDoubleCid)); SetInputAt(0, left_value); SetInputAt(1, right_value); } MethodRecognizer::Kind op_kind() const { return op_kind_; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } intptr_t result_cid() const { return result_cid_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { if (result_cid() == kSmiCid) { return kTagged; } ASSERT(result_cid() == kDoubleCid); return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { if (result_cid() == kSmiCid) { return kTagged; } ASSERT(result_cid() == kDoubleCid); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(MathMinMax) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const; private: const MethodRecognizer::Kind op_kind_; const intptr_t result_cid_; DISALLOW_COPY_AND_ASSIGN(MathMinMaxInstr); }; class BinaryDoubleOpInstr : public TemplateDefinition<2, NoThrow, Pure> { public: BinaryDoubleOpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id, TokenPosition token_pos) : TemplateDefinition(deopt_id), op_kind_(op_kind), token_pos_(token_pos) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(BinaryDoubleOp) virtual CompileType ComputeType() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryDoubleOp()->op_kind(); } private: const Token::Kind op_kind_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(BinaryDoubleOpInstr); }; class DoubleTestOpInstr : public TemplateDefinition<1, NoThrow, Pure> { public: DoubleTestOpInstr(MethodRecognizer::Kind op_kind, Value* d, intptr_t deopt_id, TokenPosition token_pos) : TemplateDefinition(deopt_id), op_kind_(op_kind), token_pos_(token_pos) { SetInputAt(0, d); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual TokenPosition token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return false; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(DoubleTestOp) virtual CompileType ComputeType() const; virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const { return op_kind_ == other->AsDoubleTestOp()->op_kind(); } private: const MethodRecognizer::Kind op_kind_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(DoubleTestOpInstr); }; class BinaryFloat32x4OpInstr : public TemplateDefinition<2, NoThrow, Pure> { public: BinaryFloat32x4OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(BinaryFloat32x4Op) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryFloat32x4Op()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(BinaryFloat32x4OpInstr); }; class Simd32x4ShuffleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Simd32x4ShuffleInstr(MethodRecognizer::Kind op_kind, Value* value, intptr_t mask, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind), mask_(mask) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } intptr_t mask() const { return mask_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { if ((op_kind_ == MethodRecognizer::kFloat32x4ShuffleX) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleY) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleZ) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleW)) { return kUnboxedDouble; } if ((op_kind_ == MethodRecognizer::kInt32x4Shuffle)) { return kUnboxedInt32x4; } ASSERT((op_kind_ == MethodRecognizer::kFloat32x4Shuffle)); return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); if ((op_kind_ == MethodRecognizer::kFloat32x4ShuffleX) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleY) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleZ) || (op_kind_ == MethodRecognizer::kFloat32x4ShuffleW) || (op_kind_ == MethodRecognizer::kFloat32x4Shuffle)) { return kUnboxedFloat32x4; } ASSERT((op_kind_ == MethodRecognizer::kInt32x4Shuffle)); return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(Simd32x4Shuffle) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return (op_kind() == other->AsSimd32x4Shuffle()->op_kind()) && (mask() == other->AsSimd32x4Shuffle()->mask()); } private: const MethodRecognizer::Kind op_kind_; const intptr_t mask_; DISALLOW_COPY_AND_ASSIGN(Simd32x4ShuffleInstr); }; class Simd32x4ShuffleMixInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Simd32x4ShuffleMixInstr(MethodRecognizer::Kind op_kind, Value* xy, Value* zw, intptr_t mask, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind), mask_(mask) { SetInputAt(0, xy); SetInputAt(1, zw); } Value* xy() const { return inputs_[0]; } Value* zw() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } intptr_t mask() const { return mask_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { if (op_kind() == MethodRecognizer::kInt32x4ShuffleMix) { return kUnboxedInt32x4; } ASSERT(op_kind() == MethodRecognizer::kFloat32x4ShuffleMix); return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (op_kind() == MethodRecognizer::kInt32x4ShuffleMix) { return kUnboxedInt32x4; } ASSERT(op_kind() == MethodRecognizer::kFloat32x4ShuffleMix); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(Simd32x4ShuffleMix) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return (op_kind() == other->AsSimd32x4ShuffleMix()->op_kind()) && (mask() == other->AsSimd32x4ShuffleMix()->mask()); } private: const MethodRecognizer::Kind op_kind_; const intptr_t mask_; DISALLOW_COPY_AND_ASSIGN(Simd32x4ShuffleMixInstr); }; class Float32x4ConstructorInstr : public TemplateDefinition<4, NoThrow, Pure> { public: Float32x4ConstructorInstr(Value* value0, Value* value1, Value* value2, Value* value3, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value0); SetInputAt(1, value1); SetInputAt(2, value2); SetInputAt(3, value3); } Value* value0() const { return inputs_[0]; } Value* value1() const { return inputs_[1]; } Value* value2() const { return inputs_[2]; } Value* value3() const { return inputs_[3]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx >= 0 && idx < 4); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Constructor) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float32x4ConstructorInstr); }; class Float32x4SplatInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Float32x4SplatInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Splat) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float32x4SplatInstr); }; // TODO(vegorov) replace with UnboxedConstantInstr. class Float32x4ZeroInstr : public TemplateDefinition<0, NoThrow, Pure> { public: Float32x4ZeroInstr() { } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } DECLARE_INSTRUCTION(Float32x4Zero) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(Float32x4ZeroInstr); }; class Float32x4ComparisonInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float32x4ComparisonInstr(MethodRecognizer::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Comparison) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4Comparison()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4ComparisonInstr); }; class Float32x4MinMaxInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float32x4MinMaxInstr(MethodRecognizer::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4MinMax) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4MinMax()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4MinMaxInstr); }; class Float32x4ScaleInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float32x4ScaleInstr(MethodRecognizer::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (idx == 0) { return kUnboxedDouble; } return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Scale) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4Scale()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4ScaleInstr); }; class Float32x4SqrtInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Float32x4SqrtInstr(MethodRecognizer::Kind op_kind, Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Sqrt) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4Sqrt()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4SqrtInstr); }; // TODO(vegorov) rename to Unary to match naming convention for arithmetic. class Float32x4ZeroArgInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Float32x4ZeroArgInstr(MethodRecognizer::Kind op_kind, Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4ZeroArg) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4ZeroArg()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4ZeroArgInstr); }; class Float32x4ClampInstr : public TemplateDefinition<3, NoThrow, Pure> { public: Float32x4ClampInstr(Value* left, Value* lower, Value* upper, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, left); SetInputAt(1, lower); SetInputAt(2, upper); } Value* left() const { return inputs_[0]; } Value* lower() const { return inputs_[1]; } Value* upper() const { return inputs_[2]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1) || (idx == 2)); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4Clamp) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float32x4ClampInstr); }; class Float32x4WithInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float32x4WithInstr(MethodRecognizer::Kind op_kind, Value* left, Value* replacement, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, replacement); SetInputAt(1, left); } Value* left() const { return inputs_[1]; } Value* replacement() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (idx == 0) { return kUnboxedDouble; } return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4With) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat32x4With()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float32x4WithInstr); }; class Simd64x2ShuffleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Simd64x2ShuffleInstr(MethodRecognizer::Kind op_kind, Value* value, intptr_t mask, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind), mask_(mask) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } intptr_t mask() const { return mask_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { if ((op_kind_ == MethodRecognizer::kFloat64x2GetX) || (op_kind_ == MethodRecognizer::kFloat64x2GetY)) { return kUnboxedDouble; } UNIMPLEMENTED(); return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); if ((op_kind_ == MethodRecognizer::kFloat64x2GetX) || (op_kind_ == MethodRecognizer::kFloat64x2GetY)) { return kUnboxedFloat64x2; } UNIMPLEMENTED(); return kUnboxedFloat64x2; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Simd64x2Shuffle) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return (op_kind() == other->AsSimd64x2Shuffle()->op_kind()) && (mask() == other->AsSimd64x2Shuffle()->mask()); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; const intptr_t mask_; DISALLOW_COPY_AND_ASSIGN(Simd64x2ShuffleInstr); }; class Float32x4ToInt32x4Instr : public TemplateDefinition<1, NoThrow, Pure> { public: Float32x4ToInt32x4Instr(Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4ToInt32x4) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float32x4ToInt32x4Instr); }; class Float32x4ToFloat64x2Instr : public TemplateDefinition<1, NoThrow, Pure> { public: Float32x4ToFloat64x2Instr(Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float32x4ToFloat64x2) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float32x4ToFloat64x2Instr); }; class Float64x2ToFloat32x4Instr : public TemplateDefinition<1, NoThrow, Pure> { public: Float64x2ToFloat32x4Instr(Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat64x2; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float64x2ToFloat32x4) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float64x2ToFloat32x4Instr); }; class Float64x2ConstructorInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float64x2ConstructorInstr(Value* value0, Value* value1, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value0); SetInputAt(1, value1); } Value* value0() const { return inputs_[0]; } Value* value1() const { return inputs_[1]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx >= 0 && idx < 2); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(Float64x2Constructor) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(Float64x2ConstructorInstr); }; class Float64x2SplatInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Float64x2SplatInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Float64x2Splat) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Float64x2SplatInstr); }; class Float64x2ZeroInstr : public TemplateDefinition<0, NoThrow, Pure> { public: Float64x2ZeroInstr() { } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } DECLARE_INSTRUCTION(Float64x2Zero) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(Float64x2ZeroInstr); }; // TODO(vegorov) rename to Unary to match arithmetic instructions. class Float64x2ZeroArgInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Float64x2ZeroArgInstr(MethodRecognizer::Kind op_kind, Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { if (op_kind() == MethodRecognizer::kFloat64x2GetSignMask) { // Smi. return kTagged; } return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedFloat64x2; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(Float64x2ZeroArg) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat64x2ZeroArg()->op_kind(); } private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float64x2ZeroArgInstr); }; class Float64x2OneArgInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Float64x2OneArgInstr(MethodRecognizer::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { if (idx == 0) { return kUnboxedFloat64x2; } ASSERT(idx == 1); if ((op_kind() == MethodRecognizer::kFloat64x2WithX) || (op_kind() == MethodRecognizer::kFloat64x2WithY) || (op_kind() == MethodRecognizer::kFloat64x2Scale)) { return kUnboxedDouble; } return kUnboxedFloat64x2; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(Float64x2OneArg) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsFloat64x2OneArg()->op_kind(); } private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Float64x2OneArgInstr); }; class Int32x4ConstructorInstr : public TemplateDefinition<4, NoThrow, Pure> { public: Int32x4ConstructorInstr(Value* value0, Value* value1, Value* value2, Value* value3, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value0); SetInputAt(1, value1); SetInputAt(2, value2); SetInputAt(3, value3); } Value* value0() const { return inputs_[0]; } Value* value1() const { return inputs_[1]; } Value* value2() const { return inputs_[2]; } Value* value3() const { return inputs_[3]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx >= 0) && (idx < 4)); return kUnboxedInt32; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4Constructor) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Int32x4ConstructorInstr); }; class Int32x4BoolConstructorInstr : public TemplateDefinition<4, NoThrow, Pure> { public: Int32x4BoolConstructorInstr(Value* value0, Value* value1, Value* value2, Value* value3, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value0); SetInputAt(1, value1); SetInputAt(2, value2); SetInputAt(3, value3); } Value* value0() const { return inputs_[0]; } Value* value1() const { return inputs_[1]; } Value* value2() const { return inputs_[2]; } Value* value3() const { return inputs_[3]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx >= 0) && (idx < 4)); return kTagged; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4BoolConstructor) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Int32x4BoolConstructorInstr); }; class Int32x4GetFlagInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Int32x4GetFlagInstr(MethodRecognizer::Kind op_kind, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kTagged; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4GetFlag) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsInt32x4GetFlag()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Int32x4GetFlagInstr); }; class Simd32x4GetSignMaskInstr : public TemplateDefinition<1, NoThrow, Pure> { public: Simd32x4GetSignMaskInstr(MethodRecognizer::Kind op_kind, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kTagged; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); if (op_kind_ == MethodRecognizer::kFloat32x4GetSignMask) { return kUnboxedFloat32x4; } ASSERT(op_kind_ == MethodRecognizer::kInt32x4GetSignMask); return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Simd32x4GetSignMask) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return other->AsSimd32x4GetSignMask()->op_kind() == op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Simd32x4GetSignMaskInstr); }; class Int32x4SelectInstr : public TemplateDefinition<3, NoThrow, Pure> { public: Int32x4SelectInstr(Value* mask, Value* trueValue, Value* falseValue, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, mask); SetInputAt(1, trueValue); SetInputAt(2, falseValue); } Value* mask() const { return inputs_[0]; } Value* trueValue() const { return inputs_[1]; } Value* falseValue() const { return inputs_[2]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1) || (idx == 2)); if (idx == 0) { return kUnboxedInt32x4; } return kUnboxedFloat32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4Select) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Int32x4SelectInstr); }; class Int32x4SetFlagInstr : public TemplateDefinition<2, NoThrow, Pure> { public: Int32x4SetFlagInstr(MethodRecognizer::Kind op_kind, Value* value, Value* flagValue, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, value); SetInputAt(1, flagValue); } Value* value() const { return inputs_[0]; } Value* flagValue() const { return inputs_[1]; } MethodRecognizer::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (idx == 1) { return kTagged; } return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4SetFlag) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsInt32x4SetFlag()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const MethodRecognizer::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(Int32x4SetFlagInstr); }; class Int32x4ToFloat32x4Instr : public TemplateDefinition<1, NoThrow, Pure> { public: Int32x4ToFloat32x4Instr(Value* left, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, left); } Value* left() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(Int32x4ToFloat32x4) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: DISALLOW_COPY_AND_ASSIGN(Int32x4ToFloat32x4Instr); }; class BinaryInt32x4OpInstr : public TemplateDefinition<2, NoThrow, Pure> { public: BinaryInt32x4OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedInt32x4; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedInt32x4; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(BinaryInt32x4Op) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryInt32x4Op()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(BinaryInt32x4OpInstr); }; class BinaryFloat64x2OpInstr : public TemplateDefinition<2, NoThrow, Pure> { public: BinaryFloat64x2OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedFloat64x2; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedFloat64x2; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } DECLARE_INSTRUCTION(BinaryFloat64x2Op) virtual CompileType ComputeType() const; virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryFloat64x2Op()->op_kind(); } PRINT_OPERANDS_TO_SUPPORT private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(BinaryFloat64x2OpInstr); }; class UnaryIntegerOpInstr : public TemplateDefinition<1, NoThrow, Pure> { public: UnaryIntegerOpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { ASSERT((op_kind == Token::kNEGATE) || (op_kind == Token::kBIT_NOT)); SetInputAt(0, value); } static UnaryIntegerOpInstr* Make(Representation representation, Token::Kind op_kind, Value* value, intptr_t deopt_id, Range* range); Value* value() const { return inputs_[0]; } Token::Kind op_kind() const { return op_kind_; } virtual bool AttributesEqual(Instruction* other) const { return other->AsUnaryIntegerOp()->op_kind() == op_kind(); } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT RawInteger* Evaluate(const Integer& value) const; DEFINE_INSTRUCTION_TYPE_CHECK(UnaryIntegerOp) private: const Token::Kind op_kind_; }; // Handles both Smi operations: BIT_OR and NEGATE. class UnarySmiOpInstr : public UnaryIntegerOpInstr { public: UnarySmiOpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id) : UnaryIntegerOpInstr(op_kind, value, deopt_id) {} virtual bool CanDeoptimize() const { return op_kind() == Token::kNEGATE; } virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(UnarySmiOp) private: DISALLOW_COPY_AND_ASSIGN(UnarySmiOpInstr); }; class UnaryUint32OpInstr : public UnaryIntegerOpInstr { public: UnaryUint32OpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id) : UnaryIntegerOpInstr(op_kind, value, deopt_id) { ASSERT(op_kind == Token::kBIT_NOT); } virtual bool CanDeoptimize() const { return false; } virtual CompileType ComputeType() const; virtual Representation representation() const { return kUnboxedUint32; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedUint32; } DECLARE_INSTRUCTION(UnaryUint32Op) private: DISALLOW_COPY_AND_ASSIGN(UnaryUint32OpInstr); }; class UnaryMintOpInstr : public UnaryIntegerOpInstr { public: UnaryMintOpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id) : UnaryIntegerOpInstr(op_kind, value, deopt_id) { ASSERT(op_kind == Token::kBIT_NOT); } virtual bool CanDeoptimize() const { return false; } virtual CompileType ComputeType() const; virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedMint; } DECLARE_INSTRUCTION(UnaryMintOp) private: DISALLOW_COPY_AND_ASSIGN(UnaryMintOpInstr); }; class CheckedSmiOpInstr : public TemplateDefinition<2, Throws> { public: CheckedSmiOpInstr(Token::Kind op_kind, Value* left, Value* right, InstanceCallInstr* call) : TemplateDefinition(call->deopt_id()), call_(call), op_kind_(op_kind) { SetInputAt(0, left); SetInputAt(1, right); } InstanceCallInstr* call() const { return call_; } Token::Kind op_kind() const { return op_kind_; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::All(); } virtual Definition* Canonicalize(FlowGraph* flow_graph); PRINT_OPERANDS_TO_SUPPORT DECLARE_INSTRUCTION(CheckedSmiOp) private: InstanceCallInstr* call_; const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(CheckedSmiOpInstr); }; class BinaryIntegerOpInstr : public TemplateDefinition<2, NoThrow, Pure> { public: BinaryIntegerOpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind), can_overflow_(true), is_truncating_(false) { SetInputAt(0, left); SetInputAt(1, right); } static BinaryIntegerOpInstr* Make(Representation representation, Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id, bool can_overflow, bool is_truncating, Range* range); Token::Kind op_kind() const { return op_kind_; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } bool can_overflow() const { return can_overflow_; } void set_can_overflow(bool overflow) { ASSERT(!is_truncating_ || !overflow); can_overflow_ = overflow; } bool is_truncating() const { return is_truncating_; } void mark_truncating() { is_truncating_ = true; set_can_overflow(false); } // Returns true if right is a non-zero Smi constant which absolute value is // a power of two. bool RightIsPowerOfTwoConstant() const; RawInteger* Evaluate(const Integer& left, const Integer& right) const; virtual Definition* Canonicalize(FlowGraph* flow_graph); virtual bool AllowsDCE() const { switch (op_kind()) { case Token::kADD: case Token::kSUB: case Token::kMUL: case Token::kBIT_AND: case Token::kBIT_OR: case Token::kBIT_XOR: return true; case Token::kSHR: case Token::kSHL: // These instructions throw on negative shifts. return !CanDeoptimize(); default: return false; } } virtual bool AttributesEqual(Instruction* other) const; virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } PRINT_OPERANDS_TO_SUPPORT DEFINE_INSTRUCTION_TYPE_CHECK(BinaryIntegerOp) protected: void InferRangeHelper(const Range* left_range, const Range* right_range, Range* range); private: Definition* CreateConstantResult(FlowGraph* graph, const Integer& result); const Token::Kind op_kind_; bool can_overflow_; bool is_truncating_; }; class BinarySmiOpInstr : public BinaryIntegerOpInstr { public: BinarySmiOpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { } virtual bool CanDeoptimize() const; virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(BinarySmiOp) private: DISALLOW_COPY_AND_ASSIGN(BinarySmiOpInstr); }; class BinaryInt32OpInstr : public BinaryIntegerOpInstr { public: BinaryInt32OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { SetInputAt(0, left); SetInputAt(1, right); } static bool IsSupported(Token::Kind op, Value* left, Value* right) { #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) switch (op) { case Token::kADD: case Token::kSUB: case Token::kMUL: case Token::kBIT_AND: case Token::kBIT_OR: case Token::kBIT_XOR: return true; case Token::kSHL: case Token::kSHR: return right->BindsToConstant(); default: return false; } #else return false; #endif } virtual bool CanDeoptimize() const; virtual Representation representation() const { return kUnboxedInt32; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedInt32; } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(BinaryInt32Op) private: DISALLOW_COPY_AND_ASSIGN(BinaryInt32OpInstr); }; class BinaryUint32OpInstr : public BinaryIntegerOpInstr { public: BinaryUint32OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { mark_truncating(); } virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedUint32; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedUint32; } virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(BinaryUint32Op) private: DISALLOW_COPY_AND_ASSIGN(BinaryUint32OpInstr); }; class ShiftUint32OpInstr : public BinaryIntegerOpInstr { public: ShiftUint32OpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { ASSERT((op_kind == Token::kSHR) || (op_kind == Token::kSHL)); } virtual bool CanDeoptimize() const { return true; } virtual Representation representation() const { return kUnboxedUint32; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return (idx == 0) ? kUnboxedUint32 : kTagged; } virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(ShiftUint32Op) private: DISALLOW_COPY_AND_ASSIGN(ShiftUint32OpInstr); }; class BinaryMintOpInstr : public BinaryIntegerOpInstr { public: BinaryMintOpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { } virtual bool CanDeoptimize() const { return (can_overflow() && ((op_kind() == Token::kADD) || (op_kind() == Token::kSUB))) || (op_kind() == Token::kMUL); // Deopt if inputs are not int32. } virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedMint; } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(BinaryMintOp) private: DISALLOW_COPY_AND_ASSIGN(BinaryMintOpInstr); }; class ShiftMintOpInstr : public BinaryIntegerOpInstr { public: ShiftMintOpInstr(Token::Kind op_kind, Value* left, Value* right, intptr_t deopt_id) : BinaryIntegerOpInstr(op_kind, left, right, deopt_id) { ASSERT((op_kind == Token::kSHR) || (op_kind == Token::kSHL)); } virtual bool CanDeoptimize() const { return has_shift_count_check() || (can_overflow() && (op_kind() == Token::kSHL)); } virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return (idx == 0) ? kUnboxedMint : kTagged; } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const; DECLARE_INSTRUCTION(ShiftMintOp) private: bool has_shift_count_check() const; DISALLOW_COPY_AND_ASSIGN(ShiftMintOpInstr); }; // Handles only NEGATE. class UnaryDoubleOpInstr : public TemplateDefinition<1, NoThrow, Pure> { public: UnaryDoubleOpInstr(Token::Kind op_kind, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), op_kind_(op_kind) { ASSERT(op_kind == Token::kNEGATE); SetInputAt(0, value); } Value* value() const { return inputs_[0]; } Token::Kind op_kind() const { return op_kind_; } DECLARE_INSTRUCTION(UnaryDoubleOp) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(UnaryDoubleOpInstr); }; class CheckStackOverflowInstr : public TemplateInstruction<0, NoThrow> { public: CheckStackOverflowInstr(TokenPosition token_pos, intptr_t loop_depth) : TemplateInstruction(Thread::Current()->GetNextDeoptId()), token_pos_(token_pos), loop_depth_(loop_depth) { } virtual TokenPosition token_pos() const { return token_pos_; } bool in_loop() const { return loop_depth_ > 0; } intptr_t loop_depth() const { return loop_depth_; } DECLARE_INSTRUCTION(CheckStackOverflow) virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } PRINT_OPERANDS_TO_SUPPORT private: const TokenPosition token_pos_; const intptr_t loop_depth_; DISALLOW_COPY_AND_ASSIGN(CheckStackOverflowInstr); }; // TODO(vegorov): remove this instruction in favor of Int32ToDouble. class SmiToDoubleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: SmiToDoubleInstr(Value* value, TokenPosition token_pos) : token_pos_(token_pos) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } virtual TokenPosition token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(SmiToDouble) virtual CompileType ComputeType() const; virtual Representation representation() const { return kUnboxedDouble; } virtual bool CanDeoptimize() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } private: const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(SmiToDoubleInstr); }; class Int32ToDoubleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: explicit Int32ToDoubleInstr(Value* value) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(Int32ToDouble) virtual CompileType ComputeType() const; virtual Representation RequiredInputRepresentation(intptr_t index) const { ASSERT(index == 0); return kUnboxedInt32; } virtual Representation representation() const { return kUnboxedDouble; } virtual bool CanDeoptimize() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(Int32ToDoubleInstr); }; class MintToDoubleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: MintToDoubleInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(MintToDouble) virtual CompileType ComputeType() const; virtual Representation RequiredInputRepresentation(intptr_t index) const { ASSERT(index == 0); return kUnboxedMint; } virtual Representation representation() const { return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { // Direct access since this instruction cannot deoptimize, and the deopt-id // was inherited from another instruction that could deoptimize. return GetDeoptId(); } virtual bool CanDeoptimize() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(MintToDoubleInstr); }; class DoubleToIntegerInstr : public TemplateDefinition<1, Throws> { public: DoubleToIntegerInstr(Value* value, InstanceCallInstr* instance_call) : TemplateDefinition(instance_call->deopt_id()), instance_call_(instance_call) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } InstanceCallInstr* instance_call() const { return instance_call_; } DECLARE_INSTRUCTION(DoubleToInteger) virtual CompileType ComputeType() const; virtual intptr_t ArgumentCount() const { return 1; } virtual bool CanDeoptimize() const { return true; } virtual EffectSet Effects() const { return EffectSet::None(); } private: InstanceCallInstr* instance_call_; DISALLOW_COPY_AND_ASSIGN(DoubleToIntegerInstr); }; // Similar to 'DoubleToIntegerInstr' but expects unboxed double as input // and creates a Smi. class DoubleToSmiInstr : public TemplateDefinition<1, NoThrow, Pure> { public: DoubleToSmiInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(DoubleToSmi) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return true; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual bool AttributesEqual(Instruction* other) const { return true; } private: DISALLOW_COPY_AND_ASSIGN(DoubleToSmiInstr); }; class DoubleToDoubleInstr : public TemplateDefinition<1, NoThrow, Pure> { public: DoubleToDoubleInstr(Value* value, MethodRecognizer::Kind recognized_kind, intptr_t deopt_id) : TemplateDefinition(deopt_id), recognized_kind_(recognized_kind) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } DECLARE_INSTRUCTION(DoubleToDouble) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual bool AttributesEqual(Instruction* other) const { return other->AsDoubleToDouble()->recognized_kind() == recognized_kind(); } private: const MethodRecognizer::Kind recognized_kind_; DISALLOW_COPY_AND_ASSIGN(DoubleToDoubleInstr); }; class DoubleToFloatInstr: public TemplateDefinition<1, NoThrow, Pure> { public: DoubleToFloatInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(DoubleToFloat) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { // This works since double is the representation that the typed array // store expects. // TODO(fschneider): Change this to a genuine float representation once it // is supported. return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual Definition* Canonicalize(FlowGraph* flow_graph); private: DISALLOW_COPY_AND_ASSIGN(DoubleToFloatInstr); }; class FloatToDoubleInstr: public TemplateDefinition<1, NoThrow, Pure> { public: FloatToDoubleInstr(Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(FloatToDouble) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual Definition* Canonicalize(FlowGraph* flow_graph); private: DISALLOW_COPY_AND_ASSIGN(FloatToDoubleInstr); }; class InvokeMathCFunctionInstr : public PureDefinition { public: InvokeMathCFunctionInstr(ZoneGrowableArray* inputs, intptr_t deopt_id, MethodRecognizer::Kind recognized_kind, TokenPosition token_pos); static intptr_t ArgumentCountFor(MethodRecognizer::Kind recognized_kind_); const RuntimeEntry& TargetFunction() const; MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } virtual TokenPosition token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(InvokeMathCFunction) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((0 <= idx) && (idx < InputCount())); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual intptr_t InputCount() const { return inputs_->length(); } virtual Value* InputAt(intptr_t i) const { return (*inputs_)[i]; } virtual bool AttributesEqual(Instruction* other) const { InvokeMathCFunctionInstr* other_invoke = other->AsInvokeMathCFunction(); return other_invoke->recognized_kind() == recognized_kind(); } virtual bool MayThrow() const { return false; } static const intptr_t kSavedSpTempIndex = 0; static const intptr_t kObjectTempIndex = 1; static const intptr_t kDoubleTempIndex = 2; PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { (*inputs_)[i] = value; } ZoneGrowableArray* inputs_; const MethodRecognizer::Kind recognized_kind_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(InvokeMathCFunctionInstr); }; class ExtractNthOutputInstr : public TemplateDefinition<1, NoThrow, Pure> { public: // Extract the Nth output register from value. ExtractNthOutputInstr(Value* value, intptr_t n, Representation definition_rep, intptr_t definition_cid) : index_(n), definition_rep_(definition_rep), definition_cid_(definition_cid) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(ExtractNthOutput) virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { return false; } intptr_t index() const { return index_; } virtual Representation representation() const { return definition_rep_; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); if (representation() == kTagged) { return kPairOfTagged; } else if (representation() == kUnboxedDouble) { return kPairOfUnboxedDouble; } UNREACHABLE(); return definition_rep_; } virtual bool AttributesEqual(Instruction* other) const { ExtractNthOutputInstr* other_extract = other->AsExtractNthOutput(); return (other_extract->representation() == representation()) && (other_extract->index() == index()); } PRINT_OPERANDS_TO_SUPPORT private: const intptr_t index_; const Representation definition_rep_; const intptr_t definition_cid_; DISALLOW_COPY_AND_ASSIGN(ExtractNthOutputInstr); }; class MergedMathInstr : public PureDefinition { public: enum Kind { kTruncDivMod, kSinCos, }; MergedMathInstr(ZoneGrowableArray* inputs, intptr_t original_deopt_id, MergedMathInstr::Kind kind); static intptr_t InputCountFor(MergedMathInstr::Kind kind) { if (kind == kTruncDivMod) { return 2; } else if (kind == kSinCos) { return 1; } else { UNIMPLEMENTED(); return -1; } } MergedMathInstr::Kind kind() const { return kind_; } virtual intptr_t InputCount() const { return inputs_->length(); } virtual Value* InputAt(intptr_t i) const { return (*inputs_)[i]; } static intptr_t OutputIndexOf(MethodRecognizer::Kind kind); static intptr_t OutputIndexOf(Token::Kind token); virtual CompileType ComputeType() const; virtual bool CanDeoptimize() const { if (kind_ == kTruncDivMod) { return true; } else if (kind_ == kSinCos) { return false; } else { UNIMPLEMENTED(); return false; } } virtual Representation representation() const { if (kind_ == kTruncDivMod) { return kPairOfTagged; } else if (kind_ == kSinCos) { return kPairOfUnboxedDouble; } else { UNIMPLEMENTED(); return kTagged; } } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((0 <= idx) && (idx < InputCount())); if (kind_ == kTruncDivMod) { return kTagged; } else if (kind_ == kSinCos) { return kUnboxedDouble; } else { UNIMPLEMENTED(); return kTagged; } } virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } DECLARE_INSTRUCTION(MergedMath) virtual bool AttributesEqual(Instruction* other) const { MergedMathInstr* other_invoke = other->AsMergedMath(); return other_invoke->kind() == kind(); } virtual bool MayThrow() const { return false; } static const char* KindToCString(MergedMathInstr::Kind kind) { if (kind == kTruncDivMod) return "TruncDivMod"; if (kind == kSinCos) return "SinCos"; UNIMPLEMENTED(); return ""; } PRINT_OPERANDS_TO_SUPPORT private: virtual void RawSetInputAt(intptr_t i, Value* value) { (*inputs_)[i] = value; } ZoneGrowableArray* inputs_; MergedMathInstr::Kind kind_; DISALLOW_COPY_AND_ASSIGN(MergedMathInstr); }; class CheckClassInstr : public TemplateInstruction<1, NoThrow> { public: CheckClassInstr(Value* value, intptr_t deopt_id, const ICData& unary_checks, TokenPosition token_pos); DECLARE_INSTRUCTION(CheckClass) virtual bool CanDeoptimize() const { return true; } virtual TokenPosition token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } const ICData& unary_checks() const { return unary_checks_; } const GrowableArray& cids() const { return cids_; } virtual Instruction* Canonicalize(FlowGraph* flow_graph); bool IsNullCheck() const { return DeoptIfNull() || DeoptIfNotNull(); } bool DeoptIfNull() const; bool DeoptIfNotNull() const; bool IsDenseSwitch() const; static bool IsDenseCidRange(const ICData& unary_checks); intptr_t ComputeCidMask() const; static bool IsDenseMask(intptr_t mask); virtual bool AllowsCSE() const { return true; } virtual EffectSet Dependencies() const; virtual EffectSet Effects() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const; bool licm_hoisted() const { return licm_hoisted_; } void set_licm_hoisted(bool value) { licm_hoisted_ = value; } PRINT_OPERANDS_TO_SUPPORT private: const ICData& unary_checks_; GrowableArray cids_; // Sorted, lowest first. bool licm_hoisted_; bool is_dense_switch_; const TokenPosition token_pos_; DISALLOW_COPY_AND_ASSIGN(CheckClassInstr); }; class CheckSmiInstr : public TemplateInstruction<1, NoThrow, Pure> { public: CheckSmiInstr(Value* value, intptr_t deopt_id, TokenPosition token_pos) : TemplateInstruction(deopt_id), token_pos_(token_pos), licm_hoisted_(false) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } virtual TokenPosition token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(CheckSmi) virtual bool CanDeoptimize() const { return true; } virtual Instruction* Canonicalize(FlowGraph* flow_graph); virtual bool AttributesEqual(Instruction* other) const { return true; } bool licm_hoisted() const { return licm_hoisted_; } void set_licm_hoisted(bool value) { licm_hoisted_ = value; } private: const TokenPosition token_pos_; bool licm_hoisted_; DISALLOW_COPY_AND_ASSIGN(CheckSmiInstr); }; class CheckClassIdInstr : public TemplateInstruction<1, NoThrow> { public: CheckClassIdInstr(Value* value, intptr_t cid, intptr_t deopt_id) : TemplateInstruction(deopt_id), cid_(cid) { SetInputAt(0, value); } Value* value() const { return inputs_[0]; } intptr_t cid() const { return cid_; } DECLARE_INSTRUCTION(CheckClassId) virtual bool CanDeoptimize() const { return true; } virtual Instruction* Canonicalize(FlowGraph* flow_graph); virtual bool AllowsCSE() const { return true; } virtual EffectSet Dependencies() const; virtual EffectSet Effects() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { return true; } PRINT_OPERANDS_TO_SUPPORT private: intptr_t cid_; DISALLOW_COPY_AND_ASSIGN(CheckClassIdInstr); }; class CheckArrayBoundInstr : public TemplateInstruction<2, NoThrow, Pure> { public: CheckArrayBoundInstr(Value* length, Value* index, intptr_t deopt_id) : TemplateInstruction(deopt_id), generalized_(false), licm_hoisted_(false) { SetInputAt(kLengthPos, length); SetInputAt(kIndexPos, index); } Value* length() const { return inputs_[kLengthPos]; } Value* index() const { return inputs_[kIndexPos]; } DECLARE_INSTRUCTION(CheckArrayBound) virtual bool CanDeoptimize() const { return true; } bool IsRedundant(const RangeBoundary& length); void mark_generalized() { generalized_ = true; } virtual Instruction* Canonicalize(FlowGraph* flow_graph); // Returns the length offset for array and string types. static intptr_t LengthOffsetFor(intptr_t class_id); static bool IsFixedLengthArrayType(intptr_t class_id); virtual bool AttributesEqual(Instruction* other) const { return true; } void set_licm_hoisted(bool value) { licm_hoisted_ = value; } // Give a name to the location/input indices. enum { kLengthPos = 0, kIndexPos = 1 }; private: bool generalized_; bool licm_hoisted_; DISALLOW_COPY_AND_ASSIGN(CheckArrayBoundInstr); }; class GenericCheckBoundInstr : public TemplateInstruction<2, Throws, NoCSE> { public: GenericCheckBoundInstr(Value* length, Value* index, intptr_t deopt_id) : TemplateInstruction(deopt_id) { SetInputAt(kLengthPos, length); SetInputAt(kIndexPos, index); } Value* length() const { return inputs_[kLengthPos]; } Value* index() const { return inputs_[kIndexPos]; } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } DECLARE_INSTRUCTION(GenericCheckBound) virtual bool CanDeoptimize() const { return true; } // Give a name to the location/input indices. enum { kLengthPos = 0, kIndexPos = 1 }; private: DISALLOW_COPY_AND_ASSIGN(GenericCheckBoundInstr); }; class UnboxedIntConverterInstr : public TemplateDefinition<1, NoThrow> { public: UnboxedIntConverterInstr(Representation from, Representation to, Value* value, intptr_t deopt_id) : TemplateDefinition(deopt_id), from_representation_(from), to_representation_(to), is_truncating_(to == kUnboxedUint32) { ASSERT(from != to); ASSERT((from == kUnboxedMint) || (from == kUnboxedUint32) || (from == kUnboxedInt32)); ASSERT((to == kUnboxedMint) || (to == kUnboxedUint32) || (to == kUnboxedInt32)); SetInputAt(0, value); } Value* value() const { return inputs_[0]; } Representation from() const { return from_representation_; } Representation to() const { return to_representation_; } bool is_truncating() const { return is_truncating_; } void mark_truncating() { is_truncating_ = true; } Definition* Canonicalize(FlowGraph* flow_graph); virtual bool CanDeoptimize() const; virtual Representation representation() const { return to(); } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return from(); } virtual EffectSet Effects() const { return EffectSet::None(); } virtual EffectSet Dependencies() const { return EffectSet::None(); } virtual bool AttributesEqual(Instruction* other) const { ASSERT(other->IsUnboxedIntConverter()); UnboxedIntConverterInstr* converter = other->AsUnboxedIntConverter(); return (converter->from() == from()) && (converter->to() == to()) && (converter->is_truncating() == is_truncating()); } virtual void InferRange(RangeAnalysis* analysis, Range* range); virtual CompileType ComputeType() const { // TODO(vegorov) use range information to improve type. return CompileType::Int(); } DECLARE_INSTRUCTION(UnboxedIntConverter); PRINT_OPERANDS_TO_SUPPORT private: const Representation from_representation_; const Representation to_representation_; bool is_truncating_; DISALLOW_COPY_AND_ASSIGN(UnboxedIntConverterInstr); }; class GrowRegExpStackInstr : public TemplateDefinition<1, Throws> { public: explicit GrowRegExpStackInstr(Value* typed_data_cell) { SetInputAt(0, typed_data_cell); } Value* typed_data_cell() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return MayThrow(); } virtual EffectSet Effects() const { return EffectSet::None(); } DECLARE_INSTRUCTION(GrowRegExpStack); private: DISALLOW_COPY_AND_ASSIGN(GrowRegExpStackInstr); }; #undef DECLARE_INSTRUCTION class Environment : public ZoneAllocated { public: // Iterate the non-NULL values in the innermost level of an environment. class ShallowIterator : public ValueObject { public: explicit ShallowIterator(Environment* environment) : environment_(environment), index_(0) { } ShallowIterator(const ShallowIterator& other) : ValueObject(), environment_(other.environment_), index_(other.index_) { } ShallowIterator& operator=(const ShallowIterator& other) { environment_ = other.environment_; index_ = other.index_; return *this; } Environment* environment() const { return environment_; } void Advance() { ASSERT(!Done()); ++index_; } bool Done() const { return (environment_ == NULL) || (index_ >= environment_->Length()); } Value* CurrentValue() const { ASSERT(!Done()); ASSERT(environment_->values_[index_] != NULL); return environment_->values_[index_]; } void SetCurrentValue(Value* value) { ASSERT(!Done()); ASSERT(value != NULL); environment_->values_[index_] = value; } Location CurrentLocation() const { ASSERT(!Done()); return environment_->locations_[index_]; } void SetCurrentLocation(Location loc) { ASSERT(!Done()); environment_->locations_[index_] = loc; } private: Environment* environment_; intptr_t index_; }; // Iterate all non-NULL values in an environment, including outer // environments. Note that the iterator skips empty environments. class DeepIterator : public ValueObject { public: explicit DeepIterator(Environment* environment) : iterator_(environment) { SkipDone(); } void Advance() { ASSERT(!Done()); iterator_.Advance(); SkipDone(); } bool Done() const { return iterator_.environment() == NULL; } Value* CurrentValue() const { ASSERT(!Done()); return iterator_.CurrentValue(); } void SetCurrentValue(Value* value) { ASSERT(!Done()); iterator_.SetCurrentValue(value); } Location CurrentLocation() const { ASSERT(!Done()); return iterator_.CurrentLocation(); } void SetCurrentLocation(Location loc) { ASSERT(!Done()); iterator_.SetCurrentLocation(loc); } private: void SkipDone() { while (!Done() && iterator_.Done()) { iterator_ = ShallowIterator(iterator_.environment()->outer()); } } ShallowIterator iterator_; }; // Construct an environment by constructing uses from an array of definitions. static Environment* From(Zone* zone, const GrowableArray& definitions, intptr_t fixed_parameter_count, const ParsedFunction& parsed_function); void set_locations(Location* locations) { ASSERT(locations_ == NULL); locations_ = locations; } void set_deopt_id(intptr_t deopt_id) { deopt_id_ = deopt_id; } intptr_t deopt_id() const { return deopt_id_; } Environment* outer() const { return outer_; } Environment* Outermost() { Environment* result = this; while (result->outer() != NULL) result = result->outer(); return result; } Value* ValueAt(intptr_t ix) const { return values_[ix]; } intptr_t Length() const { return values_.length(); } Location LocationAt(intptr_t index) const { ASSERT((index >= 0) && (index < values_.length())); return locations_[index]; } // The use index is the index in the flattened environment. Value* ValueAtUseIndex(intptr_t index) const { const Environment* env = this; while (index >= env->Length()) { ASSERT(env->outer_ != NULL); index -= env->Length(); env = env->outer_; } return env->ValueAt(index); } intptr_t fixed_parameter_count() const { return fixed_parameter_count_; } intptr_t CountArgsPushed() { intptr_t count = 0; for (Environment::DeepIterator it(this); !it.Done(); it.Advance()) { if (it.CurrentValue()->definition()->IsPushArgument()) { count++; } } return count; } const Function& function() const { return parsed_function_.function(); } Environment* DeepCopy(Zone* zone) const { return DeepCopy(zone, Length()); } void DeepCopyTo(Zone* zone, Instruction* instr) const; void DeepCopyToOuter(Zone* zone, Instruction* instr) const; void DeepCopyAfterTo(Zone* zone, Instruction* instr, intptr_t argc, Definition* dead, Definition* result) const; void PrintTo(BufferFormatter* f) const; const char* ToCString() const; // Deep copy an environment. The 'length' parameter may be less than the // environment's length in order to drop values (e.g., passed arguments) // from the copy. Environment* DeepCopy(Zone* zone, intptr_t length) const; #if defined(TARGET_ARCH_DBC) // Return/ReturnTOS instruction drops incoming arguments so // we have to drop outgoing arguments from the innermost environment. // On all other architectures caller drops outgoing arguments itself // hence the difference. // Note: this method can only be used at the code generation stage because // it mutates environment in unsafe way (e.g. does not update def-use // chains). void DropArguments(intptr_t argc); #endif private: friend class ShallowIterator; Environment(intptr_t length, intptr_t fixed_parameter_count, intptr_t deopt_id, const ParsedFunction& parsed_function, Environment* outer) : values_(length), locations_(NULL), fixed_parameter_count_(fixed_parameter_count), deopt_id_(deopt_id), parsed_function_(parsed_function), outer_(outer) { } GrowableArray values_; Location* locations_; const intptr_t fixed_parameter_count_; intptr_t deopt_id_; const ParsedFunction& parsed_function_; Environment* outer_; DISALLOW_COPY_AND_ASSIGN(Environment); }; // Visitor base class to visit each instruction and computation in a flow // graph as defined by a reversed list of basic blocks. class FlowGraphVisitor : public ValueObject { public: explicit FlowGraphVisitor(const GrowableArray& block_order) : current_iterator_(NULL), block_order_(block_order) { } virtual ~FlowGraphVisitor() { } ForwardInstructionIterator* current_iterator() const { return current_iterator_; } // Visit each block in the block order, and for each block its // instructions in order from the block entry to exit. virtual void VisitBlocks(); // Visit functions for instruction classes, with an empty default // implementation. #define DECLARE_VISIT_INSTRUCTION(ShortName) \ virtual void Visit##ShortName(ShortName##Instr* instr) { } FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION protected: ForwardInstructionIterator* current_iterator_; private: const GrowableArray& block_order_; DISALLOW_COPY_AND_ASSIGN(FlowGraphVisitor); }; // Helper macros for platform ports. #define DEFINE_UNIMPLEMENTED_INSTRUCTION(Name) \ LocationSummary* Name::MakeLocationSummary(Zone* zone, bool opt) const { \ UNIMPLEMENTED(); \ return NULL; \ } \ void Name::EmitNativeCode(FlowGraphCompiler* compiler) { UNIMPLEMENTED(); } } // namespace dart #endif // RUNTIME_VM_INTERMEDIATE_LANGUAGE_H_