// 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 VM_INTERMEDIATE_LANGUAGE_H_ #define VM_INTERMEDIATE_LANGUAGE_H_ #include "vm/allocation.h" #include "vm/ast.h" #include "vm/growable_array.h" #include "vm/handles_impl.h" #include "vm/locations.h" #include "vm/object.h" namespace dart { class BitVector; class BlockEntryInstr; class BufferFormatter; class ComparisonInstr; class ControlInstruction; class Definition; class Environment; class FlowGraphCompiler; class FlowGraphVisitor; class Instruction; class LocalVariable; class Range; class FlowGraphOptimizer; // TODO(srdjan): Unify with INTRINSIC_LIST. // (class-name, function-name, recognized enum, fingerprint). // See intrinsifier for fingerprint computation. #define RECOGNIZED_LIST(V) \ V(_ObjectArray, get:length, ObjectArrayLength, 405297088) \ V(_ImmutableArray, get:length, ImmutableArrayLength, 433698233) \ V(_ByteArrayBase, get:length, ByteArrayBaseLength, 1098081765) \ V(_ByteArrayBase, _getInt8, ByteArrayBaseGetInt8, 261365835) \ V(_ByteArrayBase, _getUint8, ByteArrayBaseGetUint8, 261365835) \ V(_ByteArrayBase, _getInt16, ByteArrayBaseGetInt16, 261365835) \ V(_ByteArrayBase, _getUint16, ByteArrayBaseGetUint16, 261365835) \ V(_ByteArrayBase, _getInt32, ByteArrayBaseGetInt32, 261365835) \ V(_ByteArrayBase, _getUint32, ByteArrayBaseGetUint32, 261365835) \ V(_ByteArrayBase, _getFloat32, ByteArrayBaseGetFloat32, 434247298) \ V(_ByteArrayBase, _getFloat64, ByteArrayBaseGetFloat64, 434247298) \ V(_GrowableObjectArray, get:length, GrowableArrayLength, 725548050) \ V(_GrowableObjectArray, get:_capacity, GrowableArrayCapacity, 725548050) \ V(_StringBase, get:length, StringBaseLength, 320803993) \ V(_StringBase, get:isEmpty, StringBaseIsEmpty, 711547329) \ V(_StringBase, charCodeAt, StringBaseCharCodeAt, 984449525) \ V(_StringBase, [], StringBaseCharAt, 1062366987) \ V(_IntegerImplementation, toDouble, IntegerToDouble, 733149324) \ V(_Double, toInt, DoubleToInteger, 362666636) \ V(_Double, truncate, DoubleTruncate, 620870996) \ V(_Double, round, DoubleRound, 620870996) \ V(_Double, floor, DoubleFloor, 620870996) \ V(_Double, ceil, DoubleCeil, 620870996) \ V(_Double, pow, DoublePow, 1131958048) \ V(_Double, _modulo, DoubleMod, 437099337) \ V(::, sqrt, MathSqrt, 1662640002) \ // Class that recognizes the name and owner of a function and returns the // corresponding enum. See RECOGNIZED_LIST above for list of recognizable // functions. class MethodRecognizer : public AllStatic { public: enum Kind { kUnknown, #define DEFINE_ENUM_LIST(class_name, function_name, enum_name, fp) k##enum_name, RECOGNIZED_LIST(DEFINE_ENUM_LIST) #undef DEFINE_ENUM_LIST }; static Kind RecognizeKind(const Function& function); static const char* KindToCString(Kind kind); }; // 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 ZoneAllocated { public: static const bool kNullable = true; static const bool kNonNullable = false; // 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* New(intptr_t cid, const AbstractType& type); // 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 new CompileType(true, 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(); // 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())); } // Replaces this type with other. void ReplaceWith(CompileType* other) { is_nullable_ = other->is_nullable_; cid_ = other->cid_; type_ = other->type_; } bool IsNone() const { return (cid_ == kIllegalCid) && (type_ == NULL); } void PrintTo(BufferFormatter* f) const; const char* ToCString() const; private: CompileType(bool is_nullable, intptr_t cid, const AbstractType* type) : is_nullable_(is_nullable), cid_(cid), type_(type) { } bool CanComputeIsInstanceOf(const AbstractType& type, bool is_nullable, bool* is_instance); bool is_nullable_; intptr_t cid_; const AbstractType* type_; }; 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; } 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(); Value* Copy() { return new Value(definition_); } CompileType* Type(); void SetReachingType(CompileType* type) { reaching_type_ = type; } void PrintTo(BufferFormatter* f) const; const char* DebugName() const { return "Value"; } // 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: Definition* definition_; Value* previous_use_; Value* next_use_; Instruction* instruction_; intptr_t use_index_; CompileType* reaching_type_; DISALLOW_COPY_AND_ASSIGN(Value); }; enum Representation { kTagged, kUnboxedDouble, kUnboxedMint }; // 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() { for (intptr_t i = 0; i < N; i++) elements_[i] = NULL; } 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(Phi) \ M(Parameter) \ M(ParallelMove) \ M(PushArgument) \ M(Return) \ M(Throw) \ M(ReThrow) \ M(Goto) \ M(Branch) \ M(AssertAssignable) \ M(AssertBoolean) \ M(ArgumentDefinitionTest) \ M(CurrentContext) \ M(StoreContext) \ M(ClosureCall) \ M(InstanceCall) \ M(PolymorphicInstanceCall) \ M(StaticCall) \ M(LoadLocal) \ M(StoreLocal) \ M(StrictCompare) \ M(EqualityCompare) \ M(RelationalOp) \ M(NativeCall) \ M(LoadIndexed) \ M(StoreIndexed) \ M(StoreInstanceField) \ M(LoadStaticField) \ M(StoreStaticField) \ M(BooleanNegate) \ M(InstanceOf) \ M(CreateArray) \ M(CreateClosure) \ M(AllocateObject) \ M(AllocateObjectWithBoundsCheck) \ M(LoadField) \ M(StoreVMField) \ M(InstantiateTypeArguments) \ M(ExtractConstructorTypeArguments) \ M(ExtractConstructorInstantiator) \ M(AllocateContext) \ M(ChainContext) \ M(CloneContext) \ M(CatchEntry) \ M(BinarySmiOp) \ M(UnarySmiOp) \ M(CheckStackOverflow) \ M(SmiToDouble) \ M(DoubleToInteger) \ M(DoubleToSmi) \ M(DoubleToDouble) \ M(CheckClass) \ M(CheckSmi) \ M(Constant) \ M(CheckEitherNonSmi) \ M(BinaryDoubleOp) \ M(MathSqrt) \ M(UnboxDouble) \ M(BoxDouble) \ M(UnboxInteger) \ M(BoxInteger) \ M(BinaryMintOp) \ M(ShiftMintOp) \ M(UnaryMintOp) \ M(CheckArrayBound) \ M(Constraint) \ M(StringFromCharCode) \ M(InvokeMathCFunction) \ #define FORWARD_DECLARATION(type) class type##Instr; FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION // Functions required in all concrete instruction classes. #define DECLARE_INSTRUCTION(type) \ virtual Tag tag() const { return k##type; } \ virtual void Accept(FlowGraphVisitor* visitor); \ virtual type##Instr* As##type() { return this; } \ virtual const char* DebugName() const { return #type; } \ virtual LocationSummary* MakeLocationSummary() const; \ virtual void EmitNativeCode(FlowGraphCompiler* compiler); \ class Instruction : public ZoneAllocated { public: #define DECLARE_TAG(type) k##type, enum Tag { FOR_EACH_INSTRUCTION(DECLARE_TAG) }; #undef DECLARE_TAG Instruction() : deopt_id_(Isolate::Current()->GetNextDeoptId()), lifetime_position_(-1), previous_(NULL), next_(NULL), env_(NULL), expr_id_(-1) { } virtual Tag tag() const = 0; intptr_t deopt_id() const { ASSERT(CanDeoptimize()); return deopt_id_; } bool IsBlockEntry() { return (AsBlockEntry() != NULL); } virtual BlockEntryInstr* AsBlockEntry() { return NULL; } bool IsDefinition() { return (AsDefinition() != NULL); } virtual Definition* AsDefinition() { return NULL; } bool IsControl() { return (AsControl() != NULL); } virtual ControlInstruction* AsControl() { return NULL; } virtual intptr_t InputCount() const = 0; virtual Value* InputAt(intptr_t i) const = 0; virtual void SetInputAt(intptr_t i, Value* value) = 0; // 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 = 0; virtual PushArgumentInstr* ArgumentAt(intptr_t index) const { UNREACHABLE(); return NULL; }; // Returns true, if this instruction can deoptimize. virtual bool CanDeoptimize() const = 0; // Returns true if the instruction may have side effects. virtual bool HasSideEffect() 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(!IsControl()); 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. 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); // Mutate assigned_vars to add the local variable index for all // frame-allocated locals assigned to by the instruction. virtual void RecordAssignedVars(BitVector* assigned_vars, intptr_t fixed_parameter_count); virtual const char* DebugName() const = 0; // Printing support. virtual void PrintTo(BufferFormatter* f) const; virtual void PrintOperandsTo(BufferFormatter* f) const; #define INSTRUCTION_TYPE_CHECK(type) \ bool Is##type() { return (As##type() != NULL); } \ virtual type##Instr* As##type() { return NULL; } FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK // Returns structure describing location constraints required // to emit native code for this instruction. virtual LocationSummary* locs() { // TODO(vegorov): This should be pure virtual method. // However we are temporary using NULL for instructions that // were not converted to the location based code generation yet. return NULL; } virtual LocationSummary* MakeLocationSummary() const = 0; static LocationSummary* MakeCallSummary(); virtual void EmitNativeCode(FlowGraphCompiler* compiler) { UNIMPLEMENTED(); } Environment* env() const { return env_; } void set_env(Environment* env) { env_ = env; } intptr_t lifetime_position() const { return lifetime_position_; } void set_lifetime_position(intptr_t pos) { lifetime_position_ = pos; } // 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 Isolate::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(FlowGraphOptimizer* optimizer); // Insert this instruction before 'next'. void InsertBefore(Instruction* next); // Insert this instruction after 'prev'. void InsertAfter(Instruction* prev); // Returns true if the instruction is affected by side effects. // Only instructions that are not affected by side effects can participate // in redundancy elimination or loop invariant code motion. // TODO(fschneider): Make this abstract and implement for all instructions // instead of returning the safe default (true). virtual bool AffectedBySideEffect() const { return true; } // Get the block entry for this instruction. virtual BlockEntryInstr* GetBlock() const; // Id for instructions used in CSE. intptr_t expr_id() const { return expr_id_; } void set_expr_id(intptr_t expr_id) { expr_id_ = expr_id; } // 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; } protected: // Fetch deopt id without checking if this computation can deoptimize. intptr_t GetDeoptId() const { return deopt_id_; } private: friend class Definition; // Needed for InsertBefore, InsertAfter. // Classes that set deopt_id_. friend class UnboxIntegerInstr; friend class UnboxDoubleInstr; friend class BinaryDoubleOpInstr; friend class BinaryMintOpInstr; friend class BinarySmiOpInstr; friend class UnarySmiOpInstr; friend class ShiftMintOpInstr; friend class UnaryMintOpInstr; friend class MathSqrtInstr; friend class CheckClassInstr; friend class CheckSmiInstr; friend class CheckArrayBoundInstr; friend class CheckEitherNonSmiInstr; friend class LICM; friend class DoubleToSmiInstr; friend class DoubleToDoubleInstr; friend class InvokeMathCFunctionInstr; friend class FlowGraphOptimizer; friend class LoadIndexedInstr; intptr_t deopt_id_; intptr_t lifetime_position_; // Position used by register allocator. Instruction* previous_; Instruction* next_; Environment* env_; intptr_t expr_id_; DISALLOW_COPY_AND_ASSIGN(Instruction); }; template class TemplateInstruction: public Instruction { public: TemplateInstruction() : locs_(NULL) { } virtual intptr_t InputCount() const { return N; } virtual Value* InputAt(intptr_t i) const { return inputs_[i]; } virtual void SetInputAt(intptr_t i, Value* value) { ASSERT(value != NULL); inputs_[i] = value; } virtual LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } protected: EmbeddedArray inputs_; private: LocationSummary* locs_; }; 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> { public: ParallelMoveInstr() : moves_(4) { } DECLARE_INSTRUCTION(ParallelMove) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } 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]; } void SetSrcSlotAt(intptr_t index, const Location& loc); void SetDestSlotAt(intptr_t index, const Location& loc); intptr_t NumMoves() const { return moves_.length(); } virtual void PrintTo(BufferFormatter* f) const; 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 BlockEntryInstr* AsBlockEntry() { return this; } virtual intptr_t PredecessorCount() const = 0; virtual BlockEntryInstr* PredecessorAt(intptr_t index) const = 0; virtual void PrepareEntry(FlowGraphCompiler* compiler) = 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_; } 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_; } void set_dominator(BlockEntryInstr* instr) { dominator_ = instr; } const GrowableArray& dominated_blocks() { return dominated_blocks_; } void AddDominatedBlock(BlockEntryInstr* block) { 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; } ParallelMoveInstr* GetParallelMove() { if (parallel_move_ == NULL) { parallel_move_ = new ParallelMoveInstr(); } return parallel_move_; } // Discover basic-block structure by performing a recursive depth first // traversal of the instruction graph reachable from this instruction. As // a side effect, the block entry instructions in the graph are assigned // numbers in both preorder and postorder. The array 'preorder' maps // preorder block numbers to the block entry instruction with that number // and analogously for the array 'postorder'. 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. // The array 'assigned_vars' maps preorder block numbers to the set of // assigned frame-allocated local variables in the block. 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. void DiscoverBlocks( BlockEntryInstr* predecessor, GrowableArray* preorder, GrowableArray* postorder, GrowableArray* parent, GrowableArray* assigned_vars, intptr_t variable_count, intptr_t fixed_parameter_count); virtual intptr_t InputCount() const { return 0; } virtual Value* InputAt(intptr_t i) const { UNREACHABLE(); return NULL; } virtual void SetInputAt(intptr_t i, Value* value) { UNREACHABLE(); } virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } intptr_t try_index() const { return try_index_; } BitVector* loop_info() const { return loop_info_; } void set_loop_info(BitVector* loop_info) { loop_info_ = loop_info; } virtual BlockEntryInstr* GetBlock() const { return const_cast(this); } // 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); protected: BlockEntryInstr(intptr_t block_id, intptr_t try_index) : block_id_(block_id), try_index_(try_index), preorder_number_(-1), postorder_number_(-1), dominator_(NULL), dominated_blocks_(1), last_instruction_(NULL), parallel_move_(NULL), loop_info_(NULL) { } private: virtual void ClearPredecessors() = 0; virtual void AddPredecessor(BlockEntryInstr* predecessor) = 0; const intptr_t block_id_; const 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_; // 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) : block_entry_(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(); // Inserts replaces 'current_', which must be a definition, with another // definition. The new definition becomes 'current_'. void ReplaceCurrentWith(Definition* other); Instruction* Current() const { return current_; } private: BlockEntryInstr* block_entry_; 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_; } Instruction* Current() const { return current_; } private: BlockEntryInstr* block_entry_; Instruction* current_; }; class GraphEntryInstr : public BlockEntryInstr { public: explicit GraphEntryInstr(TargetEntryInstr* normal_entry); 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(TargetEntryInstr* entry) { catch_entries_.Add(entry); } virtual void PrepareEntry(FlowGraphCompiler* compiler); GrowableArray* initial_definitions() { return &initial_definitions_; } ConstantInstr* constant_null(); 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; } TargetEntryInstr* normal_entry() const { return normal_entry_; } virtual void PrintTo(BufferFormatter* f) const; private: virtual void ClearPredecessors() {} virtual void AddPredecessor(BlockEntryInstr* predecessor) { UNREACHABLE(); } TargetEntryInstr* normal_entry_; GrowableArray catch_entries_; GrowableArray initial_definitions_; intptr_t spill_slot_count_; 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), phi_count_(0) { } 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_; } virtual void PrepareEntry(FlowGraphCompiler* compiler); void InsertPhi(intptr_t var_index, intptr_t var_count); void RemoveDeadPhis(); void InsertPhi(PhiInstr* phi); intptr_t phi_count() const { return phi_count_; } virtual void PrintTo(BufferFormatter* f) const; private: // Classes that have access to predecessors_ when inlining. friend class BlockEntryInstr; friend class ValueInliningContext; virtual void ClearPredecessors() { predecessors_.Clear(); } virtual void AddPredecessor(BlockEntryInstr* predecessor); GrowableArray predecessors_; ZoneGrowableArray* phis_; intptr_t phi_count_; DISALLOW_COPY_AND_ASSIGN(JoinEntryInstr); }; class PhiIterator : public ValueObject { public: explicit PhiIterator(JoinEntryInstr* join) : phis_(join->phis()), index_(-1) { if (!Done()) Advance(); // Advance to the first smi. } void Advance() { ASSERT(!Done()); do { index_++; } while (!Done() && (Current() == NULL)); } 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), catch_try_index_(CatchClauseNode::kInvalidTryIndex), catch_handler_types_(Array::ZoneHandle()) { } DECLARE_INSTRUCTION(TargetEntry) virtual intptr_t PredecessorCount() const { return (predecessor_ == NULL) ? 0 : 1; } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { ASSERT((index == 0) && (predecessor_ != NULL)); return predecessor_; } // Returns true if this Block is an entry of a catch handler. bool IsCatchEntry() const { return catch_try_index_ != CatchClauseNode::kInvalidTryIndex; } // Returns try index for the try block to which this catch handler // corresponds. intptr_t catch_try_index() const { ASSERT(IsCatchEntry()); return catch_try_index_; } void set_catch_try_index(intptr_t index) { catch_try_index_ = index; } void set_catch_handler_types(const Array& handler_types) { catch_handler_types_ = handler_types.raw(); } virtual void PrepareEntry(FlowGraphCompiler* compiler); virtual void PrintTo(BufferFormatter* f) const; 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_; intptr_t catch_try_index_; Array& catch_handler_types_; DISALLOW_COPY_AND_ASSIGN(TargetEntryInstr); }; // Abstract super-class of all instructions that define a value (Bind, Phi). class Definition : public Instruction { public: enum UseKind { kEffect, kValue }; Definition(); virtual Definition* AsDefinition() { return this; } bool IsComparison() { return (AsComparison() != NULL); } virtual ComparisonInstr* AsComparison() { return NULL; } // Overridden by definitions that push arguments. virtual intptr_t ArgumentCount() const { return 0; } intptr_t temp_index() const { return temp_index_; } void set_temp_index(intptr_t index) { temp_index_ = index; } void ClearTempIndex() { temp_index_ = -1; } intptr_t ssa_temp_index() const { return ssa_temp_index_; } void set_ssa_temp_index(intptr_t index) { ASSERT(index >= 0); ASSERT(is_used()); ssa_temp_index_ = index; } bool HasSSATemp() const { return ssa_temp_index_ >= 0; } void ClearSSATempIndex() { ssa_temp_index_ = -1; } bool is_used() const { return (use_kind_ != kEffect); } void set_use_kind(UseKind kind) { use_kind_ = kind; } // Compile time type of the definition, which may be requested before type // propagation during graph building. CompileType* Type() { if (type_ == NULL) { type_ = ComputeInitialType(); } return type_; } // Compute initial 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* ComputeInitialType() const { return CompileType::Dynamic(); } // Update CompileType of the definition. Returns true if the type has changed. virtual bool RecomputeType() { return false; } bool HasUses() const { return (input_use_list_ != NULL) || (env_use_list_ != NULL); } 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); virtual void RecordAssignedVars(BitVector* assigned_vars, intptr_t fixed_parameter_count); // Printing support. These functions are sometimes overridden for custom // formatting. Otherwise, it prints in the format "opcode(op1, op2, op3)". virtual void PrintTo(BufferFormatter* f) const; virtual void PrintOperandsTo(BufferFormatter* f) const; // A value in the constant propagation lattice. // - non-constant sentinel // - a constant (any non-sentinel value) // - unknown sentinel Object& constant_value() const { return constant_value_; } virtual void InferRange(); Range* range() const { return 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(FlowGraphOptimizer* optimizer); 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); } protected: friend class RangeAnalysis; Range* range_; CompileType* type_; private: intptr_t temp_index_; intptr_t ssa_temp_index_; Value* input_use_list_; Value* env_use_list_; UseKind use_kind_; Object& constant_value_; DISALLOW_COPY_AND_ASSIGN(Definition); }; class PhiInstr : public Definition { public: explicit PhiInstr(JoinEntryInstr* block, intptr_t num_inputs) : block_(block), inputs_(num_inputs), is_alive_(false), representation_(kTagged), reaching_defs_(NULL) { for (intptr_t i = 0; i < num_inputs; ++i) { inputs_.Add(NULL); } } // Get the block entry for that instruction. virtual BlockEntryInstr* GetBlock() const { return block(); } JoinEntryInstr* block() const { return block_; } virtual CompileType* ComputeInitialType() const; virtual bool RecomputeType(); virtual intptr_t ArgumentCount() const { return 0; } intptr_t InputCount() const { return inputs_.length(); } Value* InputAt(intptr_t i) const { return inputs_[i]; } void SetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } // 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 PrintTo(BufferFormatter* f) const; virtual void InferRange(); BitVector* reaching_defs() const { return reaching_defs_; } void set_reaching_defs(BitVector* reaching_defs) { reaching_defs_ = reaching_defs; } private: friend class ConstantPropagator; // Direct access to inputs_. JoinEntryInstr* block_; GrowableArray inputs_; bool is_alive_; Representation representation_; BitVector* reaching_defs_; DISALLOW_COPY_AND_ASSIGN(PhiInstr); }; class ParameterInstr : public Definition { public: explicit ParameterInstr(intptr_t index, GraphEntryInstr* block) : index_(index), block_(block) { } DECLARE_INSTRUCTION(Parameter) intptr_t index() const { return index_; } // Get the block entry for that instruction. virtual BlockEntryInstr* GetBlock() const { return block_; } virtual intptr_t ArgumentCount() const { return 0; } intptr_t InputCount() const { return 0; } Value* InputAt(intptr_t i) const { UNREACHABLE(); return NULL; } void SetInputAt(intptr_t i, Value* value) { UNREACHABLE(); } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual intptr_t Hashcode() const { UNREACHABLE(); return 0; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual CompileType* ComputeInitialType() const; private: const intptr_t index_; GraphEntryInstr* block_; DISALLOW_COPY_AND_ASSIGN(ParameterInstr); }; class PushArgumentInstr : public Definition { public: explicit PushArgumentInstr(Value* value) : value_(value), locs_(NULL) { ASSERT(value != NULL); set_use_kind(kEffect); // Override the default. } DECLARE_INSTRUCTION(PushArgument) intptr_t InputCount() const { return 1; } Value* InputAt(intptr_t i) const { ASSERT(i == 0); return value_; } void SetInputAt(intptr_t i, Value* value) { ASSERT(i == 0); value_ = value; } virtual intptr_t ArgumentCount() const { return 0; } virtual CompileType* ComputeInitialType() const; Value* value() const { return value_; } virtual LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } virtual intptr_t Hashcode() const { UNREACHABLE(); return 0; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual void PrintOperandsTo(BufferFormatter* f) const; private: Value* value_; LocationSummary* locs_; DISALLOW_COPY_AND_ASSIGN(PushArgumentInstr); }; class ReturnInstr : public TemplateInstruction<1> { public: ReturnInstr(intptr_t token_pos, Value* value) : token_pos_(token_pos) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(Return) virtual intptr_t ArgumentCount() const { return 0; } intptr_t token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(ReturnInstr); }; class ThrowInstr : public TemplateInstruction<0> { public: explicit ThrowInstr(intptr_t token_pos) : token_pos_(token_pos) { } DECLARE_INSTRUCTION(Throw) virtual intptr_t ArgumentCount() const { return 1; } intptr_t token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(ThrowInstr); }; class ReThrowInstr : public TemplateInstruction<0> { public: explicit ReThrowInstr(intptr_t token_pos) : token_pos_(token_pos) { } DECLARE_INSTRUCTION(ReThrow) virtual intptr_t ArgumentCount() const { return 2; } intptr_t token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(ReThrowInstr); }; class GotoInstr : public TemplateInstruction<0> { public: explicit GotoInstr(JoinEntryInstr* entry) : successor_(entry), parallel_move_(NULL) { } DECLARE_INSTRUCTION(Goto) virtual intptr_t ArgumentCount() const { return 0; } JoinEntryInstr* successor() const { return successor_; } void set_successor(JoinEntryInstr* successor) { successor_ = successor; } virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } ParallelMoveInstr* parallel_move() const { return parallel_move_; } bool HasParallelMove() const { return parallel_move_ != NULL; } ParallelMoveInstr* GetParallelMove() { if (parallel_move_ == NULL) { parallel_move_ = new ParallelMoveInstr(); } return parallel_move_; } virtual void PrintTo(BufferFormatter* f) const; private: JoinEntryInstr* successor_; // 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_; }; class ControlInstruction : public Instruction { public: ControlInstruction() : true_successor_(NULL), false_successor_(NULL) { } virtual ControlInstruction* AsControl() { return this; } 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; void EmitBranchOnCondition(FlowGraphCompiler* compiler, Condition true_condition); void EmitBranchOnValue(FlowGraphCompiler* compiler, bool result); private: TargetEntryInstr* true_successor_; TargetEntryInstr* false_successor_; DISALLOW_COPY_AND_ASSIGN(ControlInstruction); }; class BranchInstr : public ControlInstruction { public: explicit BranchInstr(ComparisonInstr* comparison, bool is_checked = false) : comparison_(comparison), is_checked_(is_checked) { } DECLARE_INSTRUCTION(Branch) virtual intptr_t ArgumentCount() const; intptr_t InputCount() const; Value* InputAt(intptr_t i) const; void SetInputAt(intptr_t i, Value* value); virtual bool CanDeoptimize() const; virtual bool HasSideEffect() const; ComparisonInstr* comparison() const { return comparison_; } void set_comparison(ComparisonInstr* value) { comparison_ = value; } bool is_checked() const { return is_checked_; } virtual LocationSummary* locs(); virtual intptr_t DeoptimizationTarget() const; virtual Representation RequiredInputRepresentation(intptr_t i) const; // Replace the comparison with another, leaving the branch intact. void ReplaceWith(ComparisonInstr* other, ForwardInstructionIterator* ignored) { comparison_ = other; } virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer); virtual void PrintTo(BufferFormatter* f) const; private: ComparisonInstr* comparison_; const bool is_checked_; DISALLOW_COPY_AND_ASSIGN(BranchInstr); }; class StoreContextInstr : public TemplateInstruction<1> { public: explicit StoreContextInstr(Value* value) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(StoreContext); virtual intptr_t ArgumentCount() const { return 0; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(StoreContextInstr); }; template class TemplateDefinition : public Definition { public: TemplateDefinition() : locs_(NULL) { } virtual intptr_t InputCount() const { return N; } virtual Value* InputAt(intptr_t i) const { return inputs_[i]; } virtual void SetInputAt(intptr_t i, Value* value) { ASSERT(value != NULL); inputs_[i] = value; } // Returns a structure describing the location constraints required // to emit native code for this definition. LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } protected: EmbeddedArray inputs_; private: friend class BranchInstr; LocationSummary* locs_; }; class RangeBoundary : public ValueObject { public: enum Kind { kUnknown, kSymbol, kConstant }; RangeBoundary() : kind_(kUnknown), value_(0), offset_(0) { } RangeBoundary(const RangeBoundary& other) : ValueObject(), kind_(other.kind_), value_(other.value_), offset_(other.offset_) { } RangeBoundary& operator=(const RangeBoundary& other) { kind_ = other.kind_; value_ = other.value_; offset_ = other.offset_; return *this; } static RangeBoundary FromConstant(intptr_t val) { return RangeBoundary(kConstant, val, 0); } static RangeBoundary FromDefinition(Definition* defn, intptr_t offs = 0); static RangeBoundary MinSmi() { return FromConstant(Smi::kMinValue); } static RangeBoundary MaxSmi() { return FromConstant(Smi::kMaxValue); } static const intptr_t kMinusInfinity = Smi::kMinValue - 1; static const intptr_t kPlusInfinity = Smi::kMaxValue + 1; static RangeBoundary OverflowedMinSmi() { return FromConstant(Smi::kMinValue - 1); } static RangeBoundary OverflowedMaxSmi() { return FromConstant(Smi::kMaxValue + 1); } static RangeBoundary Min(RangeBoundary a, RangeBoundary b); static RangeBoundary Max(RangeBoundary a, RangeBoundary b); bool Overflowed() const { return IsConstant() && !Smi::IsValid(value()); } RangeBoundary Clamp() const { if (IsConstant()) { if (value() < Smi::kMinValue) return MinSmi(); if (value() > Smi::kMaxValue) return MaxSmi(); } return *this; } bool Equals(const RangeBoundary& other) const { return (kind_ == other.kind_) && (value_ == other.value_); } bool IsUnknown() const { return kind_ == kUnknown; } bool IsConstant() const { return kind_ == kConstant; } bool IsSymbol() const { return kind_ == kSymbol; } intptr_t value() const { ASSERT(IsConstant()); return value_; } Definition* symbol() const { ASSERT(IsSymbol()); return reinterpret_cast(value_); } intptr_t offset() const { return offset_; } RangeBoundary LowerBound() const; RangeBoundary UpperBound() const; void PrintTo(BufferFormatter* f) const; const char* ToCString() const; static RangeBoundary Add(const RangeBoundary& a, const RangeBoundary& b, const RangeBoundary& overflow) { ASSERT(a.IsConstant() && b.IsConstant()); intptr_t result = a.value() + b.value(); if (!Smi::IsValid(result)) { return overflow; } return RangeBoundary::FromConstant(result); } static RangeBoundary Sub(const RangeBoundary& a, const RangeBoundary& b, const RangeBoundary& overflow) { ASSERT(a.IsConstant() && b.IsConstant()); intptr_t result = a.value() - b.value(); if (!Smi::IsValid(result)) { return overflow; } return RangeBoundary::FromConstant(result); } private: RangeBoundary(Kind kind, intptr_t value, intptr_t offset) : kind_(kind), value_(value), offset_(offset) { } Kind kind_; intptr_t value_; intptr_t offset_; }; class Range : public ZoneAllocated { public: Range(RangeBoundary min, RangeBoundary max) : min_(min), max_(max) { } static Range* Unknown() { return new Range(RangeBoundary::MinSmi(), RangeBoundary::MaxSmi()); } void PrintTo(BufferFormatter* f) const; static const char* ToCString(Range* range); const RangeBoundary& min() const { return min_; } const RangeBoundary& max() const { return max_; } bool Equals(Range* other) { return min_.Equals(other->min_) && max_.Equals(other->max_); } static RangeBoundary ConstantMin(Range* range) { if (range == NULL) return RangeBoundary::MinSmi(); return range->min().LowerBound().Clamp(); } static RangeBoundary ConstantMax(Range* range) { if (range == NULL) return RangeBoundary::MaxSmi(); return range->max().UpperBound().Clamp(); } // Inclusive. bool IsWithin(intptr_t min_int, intptr_t max_int) const; private: RangeBoundary min_; RangeBoundary max_; }; class ConstraintInstr : public TemplateDefinition<2> { public: ConstraintInstr(Value* value, Range* constraint) : constraint_(constraint) { inputs_[0] = value; inputs_[1] = NULL; // Dependency. } DECLARE_INSTRUCTION(Constraint) virtual intptr_t InputCount() const { return (inputs_[1] == NULL) ? 1 : 2; } virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { UNREACHABLE(); return false; } virtual void PrintOperandsTo(BufferFormatter* f) const; Value* value() const { return inputs_[0]; } Range* constraint() const { return constraint_; } virtual void InferRange(); void AddDependency(Definition* defn) { Value* val = new Value(defn); val->set_use_index(1); val->set_instruction(this); defn->AddInputUse(val); set_dependency(val); } private: Value* dependency() { return inputs_[1]; } void set_dependency(Value* value) { inputs_[1] = value; } Range* constraint_; DISALLOW_COPY_AND_ASSIGN(ConstraintInstr); }; class ConstantInstr : public TemplateDefinition<0> { public: explicit ConstantInstr(const Object& value) : value_(value) { } DECLARE_INSTRUCTION(Constant) virtual CompileType* ComputeInitialType() const; const Object& value() const { return value_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const { return false; } virtual void InferRange(); private: const Object& value_; DISALLOW_COPY_AND_ASSIGN(ConstantInstr); }; class AssertAssignableInstr : public TemplateDefinition<3> { public: AssertAssignableInstr(intptr_t token_pos, Value* value, Value* instantiator, Value* instantiator_type_arguments, const AbstractType& dst_type, const String& dst_name) : token_pos_(token_pos), dst_type_(AbstractType::ZoneHandle(dst_type.raw())), dst_name_(dst_name) { ASSERT(value != NULL); ASSERT(instantiator != NULL); ASSERT(instantiator_type_arguments != NULL); ASSERT(!dst_type.IsNull()); ASSERT(!dst_name.IsNull()); inputs_[0] = value; inputs_[1] = instantiator; inputs_[2] = instantiator_type_arguments; } DECLARE_INSTRUCTION(AssertAssignable) virtual CompileType* ComputeInitialType() const; virtual bool RecomputeType(); Value* value() const { return inputs_[0]; } Value* instantiator() const { return inputs_[1]; } Value* instantiator_type_arguments() const { return inputs_[2]; } intptr_t token_pos() const { return token_pos_; } const AbstractType& dst_type() const { return dst_type_; } void set_dst_type(const AbstractType& dst_type) { dst_type_ = dst_type.raw(); } const String& dst_name() const { return dst_name_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); private: const intptr_t token_pos_; AbstractType& dst_type_; const String& dst_name_; DISALLOW_COPY_AND_ASSIGN(AssertAssignableInstr); }; class AssertBooleanInstr : public TemplateDefinition<1> { public: AssertBooleanInstr(intptr_t token_pos, Value* value) : token_pos_(token_pos) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(AssertBoolean) virtual CompileType* ComputeInitialType() const; intptr_t token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(AssertBooleanInstr); }; class ArgumentDefinitionTestInstr : public TemplateDefinition<1> { public: ArgumentDefinitionTestInstr(ArgumentDefinitionTestNode* node, Value* saved_arguments_descriptor) : ast_node_(*node) { ASSERT(saved_arguments_descriptor != NULL); inputs_[0] = saved_arguments_descriptor; } DECLARE_INSTRUCTION(ArgumentDefinitionTest) virtual CompileType* ComputeInitialType() const; intptr_t token_pos() const { return ast_node_.token_pos(); } intptr_t formal_parameter_index() const { return ast_node_.formal_parameter_index(); } const String& formal_parameter_name() const { return ast_node_.formal_parameter_name(); } Value* saved_arguments_descriptor() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const ArgumentDefinitionTestNode& ast_node_; DISALLOW_COPY_AND_ASSIGN(ArgumentDefinitionTestInstr); }; // 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> { public: CurrentContextInstr() { } DECLARE_INSTRUCTION(CurrentContext) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(CurrentContextInstr); }; class ClosureCallInstr : public TemplateDefinition<0> { public: ClosureCallInstr(ClosureCallNode* node, ZoneGrowableArray* arguments) : ast_node_(*node), arguments_(arguments) { } DECLARE_INSTRUCTION(ClosureCall) const Array& argument_names() const { return ast_node_.arguments()->names(); } intptr_t token_pos() const { return ast_node_.token_pos(); } virtual intptr_t ArgumentCount() const { return arguments_->length(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return (*arguments_)[index]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const ClosureCallNode& ast_node_; ZoneGrowableArray* arguments_; DISALLOW_COPY_AND_ASSIGN(ClosureCallInstr); }; class InstanceCallInstr : public TemplateDefinition<0> { public: InstanceCallInstr(intptr_t token_pos, const String& function_name, Token::Kind token_kind, ZoneGrowableArray* arguments, const Array& argument_names, intptr_t checked_argument_count) : ic_data_(Isolate::Current()->GetICDataForDeoptId(deopt_id())), token_pos_(token_pos), function_name_(function_name), token_kind_(token_kind), arguments_(arguments), argument_names_(argument_names), checked_argument_count_(checked_argument_count) { ASSERT(function_name.IsNotTemporaryScopedHandle()); ASSERT(!arguments->is_empty()); ASSERT(argument_names.IsZoneHandle()); ASSERT(Token::IsBinaryOperator(token_kind) || Token::IsPrefixOperator(token_kind) || Token::IsIndexOperator(token_kind) || Token::IsTypeTestOperator(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; } intptr_t 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(); } PushArgumentInstr* ArgumentAt(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_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } protected: friend class FlowGraphOptimizer; void set_ic_data(ICData* value) { ic_data_ = value; } private: const ICData* ic_data_; const intptr_t 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_; DISALLOW_COPY_AND_ASSIGN(InstanceCallInstr); }; class PolymorphicInstanceCallInstr : public TemplateDefinition<0> { public: PolymorphicInstanceCallInstr(InstanceCallInstr* instance_call, const ICData& ic_data, bool with_checks) : instance_call_(instance_call), ic_data_(ic_data), with_checks_(with_checks) { ASSERT(instance_call_ != NULL); } InstanceCallInstr* instance_call() const { return instance_call_; } bool with_checks() const { return with_checks_; } virtual intptr_t ArgumentCount() const { return instance_call()->ArgumentCount(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return instance_call()->ArgumentAt(index); } DECLARE_INSTRUCTION(PolymorphicInstanceCall) const ICData& ic_data() const { return ic_data_; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } virtual void PrintOperandsTo(BufferFormatter* f) const; private: InstanceCallInstr* instance_call_; const ICData& ic_data_; const bool with_checks_; DISALLOW_COPY_AND_ASSIGN(PolymorphicInstanceCallInstr); }; class ComparisonInstr : public TemplateDefinition<2> { public: ComparisonInstr(Token::Kind kind, Value* left, Value* right) : kind_(kind) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } virtual ComparisonInstr* AsComparison() { return this; } Token::Kind kind() const { return kind_; } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch) = 0; private: Token::Kind kind_; }; // Inlined functions from class BranchInstr that forward to their comparison. inline intptr_t BranchInstr::ArgumentCount() const { return comparison()->ArgumentCount(); } inline intptr_t BranchInstr::InputCount() const { return comparison()->InputCount(); } inline Value* BranchInstr::InputAt(intptr_t i) const { return comparison()->InputAt(i); } inline void BranchInstr::SetInputAt(intptr_t i, Value* value) { comparison()->SetInputAt(i, value); } inline bool BranchInstr::CanDeoptimize() const { // Branches need a deoptimization info in checked mode if they // can throw a type check error. return comparison()->CanDeoptimize() || is_checked(); } inline bool BranchInstr::HasSideEffect() const { return comparison()->HasSideEffect(); } inline LocationSummary* BranchInstr::locs() { if (comparison()->locs_ == NULL) { LocationSummary* summary = comparison()->MakeLocationSummary(); // Branches don't produce a result. summary->set_out(Location::NoLocation()); comparison()->locs_ = summary; } return comparison()->locs_; } inline intptr_t BranchInstr::DeoptimizationTarget() const { return comparison()->DeoptimizationTarget(); } inline Representation BranchInstr::RequiredInputRepresentation( intptr_t i) const { return comparison()->RequiredInputRepresentation(i); } class StrictCompareInstr : public ComparisonInstr { public: StrictCompareInstr(Token::Kind kind, Value* left, Value* right); DECLARE_INSTRUCTION(StrictCompare) virtual CompileType* ComputeInitialType() const; virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const { return false; } virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); bool needs_number_check() const { return needs_number_check_; } void set_needs_number_check(bool value) { needs_number_check_ = value; } 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); }; class EqualityCompareInstr : public ComparisonInstr { public: EqualityCompareInstr(intptr_t token_pos, Token::Kind kind, Value* left, Value* right) : ComparisonInstr(kind, left, right), token_pos_(token_pos), receiver_class_id_(kIllegalCid) { // deopt_id() checks receiver_class_id_ value. ic_data_ = Isolate::Current()->GetICDataForDeoptId(deopt_id()); ASSERT((kind == Token::kEQ) || (kind == Token::kNE)); } DECLARE_INSTRUCTION(EqualityCompare) virtual CompileType* ComputeInitialType() const; const ICData* ic_data() const { return ic_data_; } bool HasICData() const { return (ic_data() != NULL) && !ic_data()->IsNull(); } intptr_t token_pos() const { return token_pos_; } // Receiver class id is computed from collected ICData. void set_receiver_class_id(intptr_t value) { receiver_class_id_ = value; } intptr_t receiver_class_id() const { return receiver_class_id_; } bool IsInlinedNumericComparison() const { return (receiver_class_id() == kDoubleCid) || (receiver_class_id() == kMintCid) || (receiver_class_id() == kSmiCid); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return !IsInlinedNumericComparison(); } virtual bool HasSideEffect() const { return !IsInlinedNumericComparison(); } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (receiver_class_id() == kDoubleCid) return kUnboxedDouble; if (receiver_class_id() == kMintCid) return kUnboxedMint; return kTagged; } bool IsPolymorphic() const; private: const ICData* ic_data_; const intptr_t token_pos_; intptr_t receiver_class_id_; // Set by optimizer. DISALLOW_COPY_AND_ASSIGN(EqualityCompareInstr); }; class RelationalOpInstr : public ComparisonInstr { public: RelationalOpInstr(intptr_t token_pos, Token::Kind kind, Value* left, Value* right) : ComparisonInstr(kind, left, right), token_pos_(token_pos), operands_class_id_(kIllegalCid) { // deopt_id() checks operands_class_id_ value. ic_data_ = Isolate::Current()->GetICDataForDeoptId(deopt_id()); ASSERT(Token::IsRelationalOperator(kind)); } DECLARE_INSTRUCTION(RelationalOp) virtual CompileType* ComputeInitialType() const; const ICData* ic_data() const { return ic_data_; } bool HasICData() const { return (ic_data() != NULL) && !ic_data()->IsNull(); } intptr_t token_pos() const { return token_pos_; } // TODO(srdjan): instead of class-id pass an enum that can differentiate // between boxed and unboxed doubles and integers. void set_operands_class_id(intptr_t value) { operands_class_id_ = value; } intptr_t operands_class_id() const { return operands_class_id_; } bool IsInlinedNumericComparison() const { return (operands_class_id() == kDoubleCid) || (operands_class_id() == kMintCid) || (operands_class_id() == kSmiCid); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return !IsInlinedNumericComparison(); } virtual bool HasSideEffect() const { return !IsInlinedNumericComparison(); } virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch); virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); if (operands_class_id() == kDoubleCid) return kUnboxedDouble; if (operands_class_id() == kMintCid) return kUnboxedMint; return kTagged; } private: const ICData* ic_data_; const intptr_t token_pos_; intptr_t operands_class_id_; // class id of both operands. DISALLOW_COPY_AND_ASSIGN(RelationalOpInstr); }; class StaticCallInstr : public TemplateDefinition<0> { public: StaticCallInstr(intptr_t token_pos, const Function& function, const Array& argument_names, ZoneGrowableArray* arguments) : token_pos_(token_pos), function_(function), argument_names_(argument_names), arguments_(arguments), result_cid_(kDynamicCid), is_known_constructor_(false) { ASSERT(function.IsZoneHandle()); ASSERT(argument_names.IsZoneHandle()); } DECLARE_INSTRUCTION(StaticCall) virtual CompileType* ComputeInitialType() const; // Accessors forwarded to the AST node. const Function& function() const { return function_; } const Array& argument_names() const { return argument_names_; } intptr_t token_pos() const { return token_pos_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return (*arguments_)[index]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } void set_result_cid(intptr_t value) { result_cid_ = value; } bool is_known_constructor() const { return is_known_constructor_; } void set_is_known_constructor(bool is_known_constructor) { is_known_constructor_ = is_known_constructor; } private: const intptr_t token_pos_; const Function& function_; const Array& argument_names_; ZoneGrowableArray* arguments_; intptr_t result_cid_; // For some library functions we know the result. // Some library constructors have known semantics. bool is_known_constructor_; DISALLOW_COPY_AND_ASSIGN(StaticCallInstr); }; class LoadLocalInstr : public TemplateDefinition<0> { public: LoadLocalInstr(const LocalVariable& local, intptr_t context_level) : local_(local), context_level_(context_level) { } DECLARE_INSTRUCTION(LoadLocal) virtual CompileType* ComputeInitialType() const; const LocalVariable& local() const { return local_; } intptr_t context_level() const { return context_level_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { UNREACHABLE(); return false; } private: const LocalVariable& local_; const intptr_t context_level_; DISALLOW_COPY_AND_ASSIGN(LoadLocalInstr); }; class StoreLocalInstr : public TemplateDefinition<1> { public: StoreLocalInstr(const LocalVariable& local, Value* value, intptr_t context_level) : local_(local), context_level_(context_level) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(StoreLocal) virtual CompileType* ComputeInitialType() const; const LocalVariable& local() const { return local_; } Value* value() const { return inputs_[0]; } intptr_t context_level() const { return context_level_; } virtual void RecordAssignedVars(BitVector* assigned_vars, intptr_t fixed_parameter_count); virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { UNREACHABLE(); return false; } private: const LocalVariable& local_; const intptr_t context_level_; DISALLOW_COPY_AND_ASSIGN(StoreLocalInstr); }; class NativeCallInstr : public TemplateDefinition<0> { public: explicit NativeCallInstr(NativeBodyNode* node) : ast_node_(*node) {} DECLARE_INSTRUCTION(NativeCall) intptr_t token_pos() const { return ast_node_.token_pos(); } const Function& function() const { return ast_node_.function(); } const String& native_name() const { return ast_node_.native_c_function_name(); } NativeFunction native_c_function() const { return ast_node_.native_c_function(); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const NativeBodyNode& ast_node_; DISALLOW_COPY_AND_ASSIGN(NativeCallInstr); }; class StoreInstanceFieldInstr : public TemplateDefinition<2> { public: StoreInstanceFieldInstr(const Field& field, Value* instance, Value* value, bool emit_store_barrier) : field_(field), emit_store_barrier_(emit_store_barrier) { ASSERT(instance != NULL); ASSERT(value != NULL); inputs_[0] = instance; inputs_[1] = value; } DECLARE_INSTRUCTION(StoreInstanceField) virtual CompileType* ComputeInitialType() const; const Field& field() const { return field_; } Value* instance() const { return inputs_[0]; } Value* value() const { return inputs_[1]; } bool ShouldEmitStoreBarrier() const { return value()->NeedsStoreBuffer() && emit_store_barrier_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const Field& field_; const bool emit_store_barrier_; DISALLOW_COPY_AND_ASSIGN(StoreInstanceFieldInstr); }; class LoadStaticFieldInstr : public TemplateDefinition<0> { public: explicit LoadStaticFieldInstr(const Field& field) : field_(field) {} DECLARE_INSTRUCTION(LoadStaticField); virtual CompileType* ComputeInitialType() const; const Field& field() const { return field_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return !field().is_final(); } virtual bool AttributesEqual(Instruction* other) const; private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(LoadStaticFieldInstr); }; class StoreStaticFieldInstr : public TemplateDefinition<1> { public: StoreStaticFieldInstr(const Field& field, Value* value) : field_(field) { ASSERT(field.IsZoneHandle()); ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(StoreStaticField); virtual CompileType* ComputeInitialType() const; const Field& field() const { return field_; } Value* value() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(StoreStaticFieldInstr); }; class LoadIndexedInstr : public TemplateDefinition<2> { public: LoadIndexedInstr(Value* array, Value* index, intptr_t index_scale, intptr_t class_id, intptr_t deopt_id) : index_scale_(index_scale), class_id_(class_id) { ASSERT(array != NULL); ASSERT(index != NULL); inputs_[0] = array; inputs_[1] = index; deopt_id_ = deopt_id; } DECLARE_INSTRUCTION(LoadIndexed) virtual CompileType* ComputeInitialType() const; 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 deopt_id_ != Isolate::kNoDeoptId; } virtual bool HasSideEffect() const { return false; } virtual Representation representation() const; virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const { return true; } virtual void InferRange(); private: const intptr_t index_scale_; const intptr_t class_id_; DISALLOW_COPY_AND_ASSIGN(LoadIndexedInstr); }; class StringFromCharCodeInstr : public TemplateDefinition<1> { public: explicit StringFromCharCodeInstr(Value* char_code, intptr_t cid) : cid_(cid) { ASSERT(char_code != NULL); ASSERT(char_code->definition()->IsLoadIndexed() && (char_code->definition()->AsLoadIndexed()->class_id() == kOneByteStringCid)); inputs_[0] = char_code; } DECLARE_INSTRUCTION(StringFromCharCode) virtual CompileType* ComputeInitialType() const; Value* char_code() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual bool AffectedBySideEffect() const { return false; } private: const intptr_t cid_; DISALLOW_COPY_AND_ASSIGN(StringFromCharCodeInstr); }; class StoreIndexedInstr : public TemplateDefinition<3> { public: StoreIndexedInstr(Value* array, Value* index, Value* value, bool emit_store_barrier, intptr_t class_id, intptr_t deopt_id) : emit_store_barrier_(emit_store_barrier), class_id_(class_id), deopt_id_(deopt_id) { ASSERT(array != NULL); ASSERT(index != NULL); ASSERT(value != NULL); inputs_[0] = array; inputs_[1] = index; inputs_[2] = value; } DECLARE_INSTRUCTION(StoreIndexed) Value* array() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } Value* value() const { return inputs_[2]; } intptr_t class_id() const { return class_id_; } bool ShouldEmitStoreBarrier() const { return value()->NeedsStoreBuffer() && emit_store_barrier_; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } virtual Representation RequiredInputRepresentation(intptr_t idx) const; 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 deopt_id_; } private: const bool emit_store_barrier_; const intptr_t class_id_; const intptr_t deopt_id_; DISALLOW_COPY_AND_ASSIGN(StoreIndexedInstr); }; // Note overrideable, built-in: value? false : true. class BooleanNegateInstr : public TemplateDefinition<1> { public: explicit BooleanNegateInstr(Value* value) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(BooleanNegate) virtual CompileType* ComputeInitialType() const; Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(BooleanNegateInstr); }; class InstanceOfInstr : public TemplateDefinition<3> { public: InstanceOfInstr(intptr_t token_pos, Value* value, Value* instantiator, Value* instantiator_type_arguments, const AbstractType& type, bool negate_result) : token_pos_(token_pos), type_(type), negate_result_(negate_result) { ASSERT(value != NULL); ASSERT(instantiator != NULL); ASSERT(instantiator_type_arguments != NULL); ASSERT(!type.IsNull()); inputs_[0] = value; inputs_[1] = instantiator; inputs_[2] = instantiator_type_arguments; } DECLARE_INSTRUCTION(InstanceOf) virtual CompileType* ComputeInitialType() const; Value* value() const { return inputs_[0]; } Value* instantiator() const { return inputs_[1]; } Value* instantiator_type_arguments() const { return inputs_[2]; } bool negate_result() const { return negate_result_; } const AbstractType& type() const { return type_; } intptr_t token_pos() const { return token_pos_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const intptr_t token_pos_; Value* value_; Value* instantiator_; Value* type_arguments_; const AbstractType& type_; const bool negate_result_; DISALLOW_COPY_AND_ASSIGN(InstanceOfInstr); }; class AllocateObjectInstr : public TemplateDefinition<0> { public: AllocateObjectInstr(ConstructorCallNode* node, ZoneGrowableArray* arguments) : ast_node_(*node), arguments_(arguments), cid_(Class::Handle(node->constructor().Owner()).id()) { // Either no arguments or one type-argument and one instantiator. ASSERT(arguments->is_empty() || (arguments->length() == 2)); } DECLARE_INSTRUCTION(AllocateObject) virtual CompileType* ComputeInitialType() const; virtual intptr_t ArgumentCount() const { return arguments_->length(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return (*arguments_)[index]; } const Function& constructor() const { return ast_node_.constructor(); } intptr_t token_pos() const { return ast_node_.token_pos(); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const ConstructorCallNode& ast_node_; ZoneGrowableArray* const arguments_; const intptr_t cid_; DISALLOW_COPY_AND_ASSIGN(AllocateObjectInstr); }; class AllocateObjectWithBoundsCheckInstr : public TemplateDefinition<2> { public: AllocateObjectWithBoundsCheckInstr(ConstructorCallNode* node, Value* type_arguments, Value* instantiator) : ast_node_(*node) { ASSERT(type_arguments != NULL); ASSERT(instantiator != NULL); inputs_[0] = type_arguments; inputs_[1] = instantiator; } DECLARE_INSTRUCTION(AllocateObjectWithBoundsCheck) const Function& constructor() const { return ast_node_.constructor(); } intptr_t token_pos() const { return ast_node_.token_pos(); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const ConstructorCallNode& ast_node_; DISALLOW_COPY_AND_ASSIGN(AllocateObjectWithBoundsCheckInstr); }; class CreateArrayInstr : public TemplateDefinition<1> { public: CreateArrayInstr(intptr_t token_pos, intptr_t num_elements, const AbstractType& type, Value* element_type) : token_pos_(token_pos), num_elements_(num_elements), type_(type) { #if defined(DEBUG) ASSERT(element_type != NULL); ASSERT(type_.IsZoneHandle()); ASSERT(!type_.IsNull()); ASSERT(type_.IsFinalized()); #endif inputs_[0] = element_type; } DECLARE_INSTRUCTION(CreateArray) virtual CompileType* ComputeInitialType() const; intptr_t num_elements() const { return num_elements_; } intptr_t token_pos() const { return token_pos_; } const AbstractType& type() const { return type_; } Value* element_type() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const intptr_t token_pos_; const intptr_t num_elements_; const AbstractType& type_; DISALLOW_COPY_AND_ASSIGN(CreateArrayInstr); }; class CreateClosureInstr : public TemplateDefinition<0> { public: CreateClosureInstr(const Function& function, ZoneGrowableArray* arguments, intptr_t token_pos) : function_(function), arguments_(arguments), token_pos_(token_pos) { } DECLARE_INSTRUCTION(CreateClosure) virtual CompileType* ComputeInitialType() const; intptr_t token_pos() const { return token_pos_; } const Function& function() const { return function_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return (*arguments_)[index]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const Function& function_; ZoneGrowableArray* arguments_; intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(CreateClosureInstr); }; class LoadFieldInstr : public TemplateDefinition<1> { public: LoadFieldInstr(Value* value, intptr_t offset_in_bytes, const AbstractType& type, bool immutable = false) : offset_in_bytes_(offset_in_bytes), type_(type), result_cid_(kDynamicCid), immutable_(immutable), recognized_kind_(MethodRecognizer::kUnknown) { ASSERT(value != NULL); ASSERT(type.IsZoneHandle()); // May be null if field is not an instance. inputs_[0] = value; } DECLARE_INSTRUCTION(LoadField) virtual CompileType* ComputeInitialType() const; Value* value() 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; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const { return !immutable_; } virtual void InferRange(); void set_recognized_kind(MethodRecognizer::Kind kind) { recognized_kind_ = kind; } MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } bool IsImmutableLengthLoad() const; virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); static MethodRecognizer::Kind RecognizedKindFromArrayCid(intptr_t cid); private: const intptr_t offset_in_bytes_; const AbstractType& type_; intptr_t result_cid_; const bool immutable_; MethodRecognizer::Kind recognized_kind_; DISALLOW_COPY_AND_ASSIGN(LoadFieldInstr); }; class StoreVMFieldInstr : public TemplateDefinition<2> { public: StoreVMFieldInstr(Value* dest, intptr_t offset_in_bytes, Value* value, const AbstractType& type) : offset_in_bytes_(offset_in_bytes), type_(type) { ASSERT(value != NULL); ASSERT(dest != NULL); ASSERT(type.IsZoneHandle()); // May be null if field is not an instance. inputs_[0] = value; inputs_[1] = dest; } DECLARE_INSTRUCTION(StoreVMField) virtual CompileType* ComputeInitialType() const; Value* value() const { return inputs_[0]; } Value* dest() const { return inputs_[1]; } intptr_t offset_in_bytes() const { return offset_in_bytes_; } const AbstractType& type() const { return type_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const intptr_t offset_in_bytes_; const AbstractType& type_; DISALLOW_COPY_AND_ASSIGN(StoreVMFieldInstr); }; class InstantiateTypeArgumentsInstr : public TemplateDefinition<1> { public: InstantiateTypeArgumentsInstr(intptr_t token_pos, const AbstractTypeArguments& type_arguments, Value* instantiator) : token_pos_(token_pos), type_arguments_(type_arguments) { ASSERT(type_arguments.IsZoneHandle()); ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_INSTRUCTION(InstantiateTypeArguments) Value* instantiator() const { return inputs_[0]; } const AbstractTypeArguments& type_arguments() const { return type_arguments_; } intptr_t token_pos() const { return token_pos_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return true; } private: const intptr_t token_pos_; const AbstractTypeArguments& type_arguments_; DISALLOW_COPY_AND_ASSIGN(InstantiateTypeArgumentsInstr); }; class ExtractConstructorTypeArgumentsInstr : public TemplateDefinition<1> { public: ExtractConstructorTypeArgumentsInstr( intptr_t token_pos, const AbstractTypeArguments& type_arguments, Value* instantiator) : token_pos_(token_pos), type_arguments_(type_arguments) { ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_INSTRUCTION(ExtractConstructorTypeArguments) Value* instantiator() const { return inputs_[0]; } const AbstractTypeArguments& type_arguments() const { return type_arguments_; } intptr_t token_pos() const { return token_pos_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: const intptr_t token_pos_; const AbstractTypeArguments& type_arguments_; DISALLOW_COPY_AND_ASSIGN(ExtractConstructorTypeArgumentsInstr); }; class ExtractConstructorInstantiatorInstr : public TemplateDefinition<1> { public: ExtractConstructorInstantiatorInstr(ConstructorCallNode* ast_node, Value* instantiator) : ast_node_(*ast_node) { ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_INSTRUCTION(ExtractConstructorInstantiator) Value* instantiator() const { return inputs_[0]; } const AbstractTypeArguments& type_arguments() const { return ast_node_.type_arguments(); } const Function& constructor() const { return ast_node_.constructor(); } intptr_t token_pos() const { return ast_node_.token_pos(); } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: const ConstructorCallNode& ast_node_; DISALLOW_COPY_AND_ASSIGN(ExtractConstructorInstantiatorInstr); }; class AllocateContextInstr : public TemplateDefinition<0> { public: AllocateContextInstr(intptr_t token_pos, intptr_t num_context_variables) : token_pos_(token_pos), num_context_variables_(num_context_variables) {} DECLARE_INSTRUCTION(AllocateContext); virtual CompileType* ComputeInitialType() const; intptr_t token_pos() const { return token_pos_; } intptr_t num_context_variables() const { return num_context_variables_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } private: const intptr_t token_pos_; const intptr_t num_context_variables_; DISALLOW_COPY_AND_ASSIGN(AllocateContextInstr); }; class ChainContextInstr : public TemplateInstruction<1> { public: explicit ChainContextInstr(Value* context_value) { ASSERT(context_value != NULL); inputs_[0] = context_value; } DECLARE_INSTRUCTION(ChainContext) virtual intptr_t ArgumentCount() const { return 0; } Value* context_value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: DISALLOW_COPY_AND_ASSIGN(ChainContextInstr); }; class CloneContextInstr : public TemplateDefinition<1> { public: CloneContextInstr(intptr_t token_pos, Value* context_value) : token_pos_(token_pos) { ASSERT(context_value != NULL); inputs_[0] = context_value; } intptr_t token_pos() const { return token_pos_; } Value* context_value() const { return inputs_[0]; } DECLARE_INSTRUCTION(CloneContext) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(CloneContextInstr); }; class CatchEntryInstr : public TemplateInstruction<0> { public: CatchEntryInstr(const LocalVariable& exception_var, const LocalVariable& stacktrace_var) : exception_var_(exception_var), stacktrace_var_(stacktrace_var) {} const LocalVariable& exception_var() const { return exception_var_; } const LocalVariable& stacktrace_var() const { return stacktrace_var_; } DECLARE_INSTRUCTION(CatchEntry) virtual intptr_t ArgumentCount() const { return 0; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return true; } private: const LocalVariable& exception_var_; const LocalVariable& stacktrace_var_; DISALLOW_COPY_AND_ASSIGN(CatchEntryInstr); }; class CheckEitherNonSmiInstr : public TemplateInstruction<2> { public: CheckEitherNonSmiInstr(Value* left, Value* right, InstanceCallInstr* instance_call) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; deopt_id_ = instance_call->deopt_id(); } DECLARE_INSTRUCTION(CheckEitherNonSmi) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual bool AffectedBySideEffect() const { return false; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer); private: DISALLOW_COPY_AND_ASSIGN(CheckEitherNonSmiInstr); }; class BoxDoubleInstr : public TemplateDefinition<1> { public: BoxDoubleInstr(Value* value, InstanceCallInstr* instance_call) : token_pos_((instance_call != NULL) ? instance_call->token_pos() : 0) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } intptr_t token_pos() const { return token_pos_; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } DECLARE_INSTRUCTION(BoxDouble) virtual CompileType* ComputeInitialType() const; private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(BoxDoubleInstr); }; class BoxIntegerInstr : public TemplateDefinition<1> { public: explicit BoxIntegerInstr(Value* value) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedMint; } DECLARE_INSTRUCTION(BoxInteger) virtual CompileType* ComputeInitialType() const; private: DISALLOW_COPY_AND_ASSIGN(BoxIntegerInstr); }; class UnboxDoubleInstr : public TemplateDefinition<1> { public: UnboxDoubleInstr(Value* value, intptr_t deopt_id) { ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = deopt_id; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return (value()->Type()->ToCid() != kDoubleCid) && (value()->Type()->ToCid() != kSmiCid); } virtual bool HasSideEffect() const { return false; } virtual Representation representation() const { return kUnboxedDouble; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } DECLARE_INSTRUCTION(UnboxDouble) virtual CompileType* ComputeInitialType() const; private: DISALLOW_COPY_AND_ASSIGN(UnboxDoubleInstr); }; class UnboxIntegerInstr : public TemplateDefinition<1> { public: UnboxIntegerInstr(Value* value, intptr_t deopt_id) { ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = deopt_id; } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return (value()->Type()->ToCid() != kSmiCid) && (value()->Type()->ToCid() != kMintCid); } virtual bool HasSideEffect() const { return false; } virtual CompileType* ComputeInitialType() const; virtual Representation representation() const { return kUnboxedMint; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } DECLARE_INSTRUCTION(UnboxInteger) private: DISALLOW_COPY_AND_ASSIGN(UnboxIntegerInstr); }; class MathSqrtInstr : public TemplateDefinition<1> { public: MathSqrtInstr(Value* value, StaticCallInstr* instance_call) { ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = instance_call->deopt_id(); } Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } 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 deopt_id_; } DECLARE_INSTRUCTION(MathSqrt) virtual CompileType* ComputeInitialType() const; private: DISALLOW_COPY_AND_ASSIGN(MathSqrtInstr); }; class BinaryDoubleOpInstr : public TemplateDefinition<2> { public: BinaryDoubleOpInstr(Token::Kind op_kind, Value* left, Value* right, InstanceCallInstr* instance_call) : op_kind_(op_kind) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; deopt_id_ = instance_call->deopt_id(); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryDoubleOp()->op_kind(); } 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 deopt_id_; } DECLARE_INSTRUCTION(BinaryDoubleOp) virtual CompileType* ComputeInitialType() const; virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(BinaryDoubleOpInstr); }; class BinaryMintOpInstr : public TemplateDefinition<2> { public: BinaryMintOpInstr(Token::Kind op_kind, Value* left, Value* right, InstanceCallInstr* instance_call) : op_kind_(op_kind) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; deopt_id_ = instance_call->deopt_id(); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return (op_kind() == Token::kADD) || (op_kind() == Token::kSUB); } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsBinaryMintOp()->op_kind(); } virtual CompileType* ComputeInitialType() const; virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return kUnboxedMint; } 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 deopt_id_; } virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); DECLARE_INSTRUCTION(BinaryMintOp) private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(BinaryMintOpInstr); }; class ShiftMintOpInstr : public TemplateDefinition<2> { public: ShiftMintOpInstr(Token::Kind op_kind, Value* left, Value* right, InstanceCallInstr* instance_call) : op_kind_(op_kind) { ASSERT(left != NULL); ASSERT(right != NULL); ASSERT(op_kind == Token::kSHR || op_kind == Token::kSHL); inputs_[0] = left; inputs_[1] = right; deopt_id_ = instance_call->deopt_id(); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsShiftMintOp()->op_kind(); } virtual CompileType* ComputeInitialType() const; virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT((idx == 0) || (idx == 1)); return (idx == 0) ? kUnboxedMint : 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 deopt_id_; } DECLARE_INSTRUCTION(ShiftMintOp) private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(ShiftMintOpInstr); }; class UnaryMintOpInstr : public TemplateDefinition<1> { public: UnaryMintOpInstr(Token::Kind op_kind, Value* value, InstanceCallInstr* instance_call) : op_kind_(op_kind) { ASSERT(value != NULL); ASSERT(op_kind == Token::kBIT_NOT); inputs_[0] = value; deopt_id_ = instance_call->deopt_id(); } Value* value() const { return inputs_[0]; } Token::Kind op_kind() const { return op_kind_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return op_kind() == other->AsUnaryMintOp()->op_kind(); } virtual CompileType* ComputeInitialType() const; virtual Representation representation() const { return kUnboxedMint; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedMint; } 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 deopt_id_; } DECLARE_INSTRUCTION(UnaryMintOp) private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(UnaryMintOpInstr); }; class BinarySmiOpInstr : public TemplateDefinition<2> { public: BinarySmiOpInstr(Token::Kind op_kind, InstanceCallInstr* instance_call, Value* left, Value* right) : op_kind_(op_kind), instance_call_(instance_call), overflow_(true) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; deopt_id_ = instance_call->deopt_id(); } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } InstanceCallInstr* instance_call() const { return instance_call_; } const ICData* ic_data() const { return instance_call()->ic_data(); } virtual void PrintOperandsTo(BufferFormatter* f) const; DECLARE_INSTRUCTION(BinarySmiOp) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const; virtual bool HasSideEffect() const { return false; } virtual bool AffectedBySideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; void set_overflow(bool overflow) { overflow_ = overflow; } void PrintTo(BufferFormatter* f) const; virtual void InferRange(); virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer); // Returns true if right is a non-zero Smi constant which absolute value is // a power of two. bool RightIsPowerOfTwoConstant() const; private: const Token::Kind op_kind_; InstanceCallInstr* instance_call_; bool overflow_; DISALLOW_COPY_AND_ASSIGN(BinarySmiOpInstr); }; // Handles both Smi operations: BIT_OR and NEGATE. class UnarySmiOpInstr : public TemplateDefinition<1> { public: UnarySmiOpInstr(Token::Kind op_kind, InstanceCallInstr* instance_call, Value* value) : op_kind_(op_kind) { ASSERT((op_kind == Token::kNEGATE) || (op_kind == Token::kBIT_NOT)); ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = instance_call->deopt_id(); } Value* value() const { return inputs_[0]; } Token::Kind op_kind() const { return op_kind_; } virtual void PrintOperandsTo(BufferFormatter* f) const; DECLARE_INSTRUCTION(UnarySmiOp) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return op_kind() == Token::kNEGATE; } virtual bool HasSideEffect() const { return false; } private: const Token::Kind op_kind_; DISALLOW_COPY_AND_ASSIGN(UnarySmiOpInstr); }; class CheckStackOverflowInstr : public TemplateInstruction<0> { public: explicit CheckStackOverflowInstr(intptr_t token_pos) : token_pos_(token_pos) {} intptr_t token_pos() const { return token_pos_; } DECLARE_INSTRUCTION(CheckStackOverflow) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } private: const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(CheckStackOverflowInstr); }; class SmiToDoubleInstr : public TemplateDefinition<0> { public: explicit SmiToDoubleInstr(InstanceCallInstr* instance_call) : instance_call_(instance_call) { } InstanceCallInstr* instance_call() const { return instance_call_; } DECLARE_INSTRUCTION(SmiToDouble) virtual CompileType* ComputeInitialType() const; virtual intptr_t ArgumentCount() const { return 1; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } private: InstanceCallInstr* instance_call_; DISALLOW_COPY_AND_ASSIGN(SmiToDoubleInstr); }; class DoubleToIntegerInstr : public TemplateDefinition<1> { public: DoubleToIntegerInstr(Value* value, InstanceCallInstr* instance_call) : instance_call_(instance_call) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } InstanceCallInstr* instance_call() const { return instance_call_; } DECLARE_INSTRUCTION(DoubleToInteger) virtual CompileType* ComputeInitialType() const; virtual intptr_t ArgumentCount() const { return 1; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } 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> { public: DoubleToSmiInstr(Value* value, InstanceCallInstr* instance_call) { ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = instance_call->deopt_id(); } Value* value() const { return inputs_[0]; } DECLARE_INSTRUCTION(DoubleToSmi) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual Representation RequiredInputRepresentation(intptr_t idx) const { ASSERT(idx == 0); return kUnboxedDouble; } virtual intptr_t DeoptimizationTarget() const { return deopt_id_; } private: DISALLOW_COPY_AND_ASSIGN(DoubleToSmiInstr); }; class DoubleToDoubleInstr : public TemplateDefinition<1> { public: DoubleToDoubleInstr(Value* value, InstanceCallInstr* instance_call, MethodRecognizer::Kind recognized_kind) : recognized_kind_(recognized_kind) { ASSERT(value != NULL); inputs_[0] = value; deopt_id_ = instance_call->deopt_id(); } Value* value() const { return inputs_[0]; } MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } DECLARE_INSTRUCTION(DoubleToDouble) virtual CompileType* ComputeInitialType() const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() 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 deopt_id_; } private: const MethodRecognizer::Kind recognized_kind_; DISALLOW_COPY_AND_ASSIGN(DoubleToDoubleInstr); }; class InvokeMathCFunctionInstr : public Definition { public: InvokeMathCFunctionInstr(ZoneGrowableArray* inputs, InstanceCallInstr* instance_call, MethodRecognizer::Kind recognized_kind) : inputs_(inputs), locs_(NULL), recognized_kind_(recognized_kind) { ASSERT(inputs_->length() == ArgumentCountFor(recognized_kind_)); deopt_id_ = instance_call->deopt_id(); } static intptr_t ArgumentCountFor(MethodRecognizer::Kind recognized_kind_); const RuntimeEntry& TargetFunction() const; MethodRecognizer::Kind recognized_kind() const { return recognized_kind_; } DECLARE_INSTRUCTION(InvokeMathCFunction) virtual CompileType* ComputeInitialType() const; virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual bool HasSideEffect() 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 deopt_id_; } virtual intptr_t InputCount() const { return inputs_->length(); } virtual Value* InputAt(intptr_t i) const { return (*inputs_)[i]; } virtual void SetInputAt(intptr_t i, Value* value) { ASSERT(value != NULL); (*inputs_)[i] = value; } // Returns a structure describing the location constraints required // to emit native code for this definition. LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } private: ZoneGrowableArray* inputs_; LocationSummary* locs_; const MethodRecognizer::Kind recognized_kind_; DISALLOW_COPY_AND_ASSIGN(InvokeMathCFunctionInstr); }; class CheckClassInstr : public TemplateInstruction<1> { public: CheckClassInstr(Value* value, intptr_t deopt_id, const ICData& unary_checks); DECLARE_INSTRUCTION(CheckClass) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const; Value* value() const { return inputs_[0]; } const ICData& unary_checks() const { return unary_checks_; } virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer); virtual void PrintOperandsTo(BufferFormatter* f) const; private: const ICData& unary_checks_; DISALLOW_COPY_AND_ASSIGN(CheckClassInstr); }; class CheckSmiInstr : public TemplateInstruction<1> { public: CheckSmiInstr(Value* value, intptr_t original_deopt_id) { ASSERT(value != NULL); ASSERT(original_deopt_id != Isolate::kNoDeoptId); inputs_[0] = value; deopt_id_ = original_deopt_id; } DECLARE_INSTRUCTION(CheckSmi) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const { return true; } virtual bool AffectedBySideEffect() const { return false; } virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer); Value* value() const { return inputs_[0]; } private: DISALLOW_COPY_AND_ASSIGN(CheckSmiInstr); }; class CheckArrayBoundInstr : public TemplateInstruction<2> { public: CheckArrayBoundInstr(Value* length, Value* index, intptr_t array_type, InstanceCallInstr* instance_call) : array_type_(array_type) { ASSERT(length != NULL); ASSERT(index != NULL); inputs_[0] = length; inputs_[1] = index; deopt_id_ = instance_call->deopt_id(); } DECLARE_INSTRUCTION(CheckArrayBound) virtual intptr_t ArgumentCount() const { return 0; } virtual bool CanDeoptimize() const { return true; } virtual bool HasSideEffect() const { return false; } virtual bool AttributesEqual(Instruction* other) const; virtual bool AffectedBySideEffect() const { return false; } Value* length() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } intptr_t array_type() const { return array_type_; } bool IsRedundant(RangeBoundary length); // Returns the length offset for array and string types. static intptr_t LengthOffsetFor(intptr_t class_id); static bool IsFixedLengthArrayType(intptr_t class_id); private: intptr_t array_type_; DISALLOW_COPY_AND_ASSIGN(CheckArrayBoundInstr); }; #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(const GrowableArray& definitions, intptr_t fixed_parameter_count, const Function& 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_; } 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]; } Location* LocationSlotAt(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_; } const Function& function() const { return function_; } void DeepCopyTo(Instruction* instr) const; void DeepCopyToOuter(Instruction* instr) const; void PrintTo(BufferFormatter* f) const; private: friend class ShallowIterator; Environment(intptr_t length, intptr_t fixed_parameter_count, intptr_t deopt_id, const Function& function, Environment* outer) : values_(length), locations_(NULL), fixed_parameter_count_(fixed_parameter_count), deopt_id_(deopt_id), function_(function), outer_(outer) { } // Deep copy the environment. A 'length' parameter can be given, which // may be less than the environment's length in order to drop values // (e.g., passed arguments) from the copy. Environment* DeepCopy() const; Environment* DeepCopy(intptr_t length) const; GrowableArray values_; Location* locations_; const intptr_t fixed_parameter_count_; intptr_t deopt_id_; const Function& 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) : block_order_(block_order), current_iterator_(NULL) { } 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: const GrowableArray& block_order_; ForwardInstructionIterator* current_iterator_; private: DISALLOW_COPY_AND_ASSIGN(FlowGraphVisitor); }; } // namespace dart #endif // VM_INTERMEDIATE_LANGUAGE_H_