// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef VM_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 { // TODO(srdjan): Add _ByteArrayBase, get:length. #define RECOGNIZED_LIST(V) \ V(ObjectArray, get:length, ObjectArrayLength) \ V(ImmutableArray, get:length, ImmutableArrayLength) \ V(GrowableObjectArray, get:length, GrowableArrayLength) \ V(StringBase, get:length, StringBaseLength) \ V(IntegerImplementation, toDouble, IntegerToDouble) \ V(Double, toDouble, DoubleToDouble) \ V(Math, sqrt, MathSqrt) \ // 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) k##enum_name, RECOGNIZED_LIST(DEFINE_ENUM_LIST) #undef DEFINE_ENUM_LIST }; static Kind RecognizeKind(const Function& function); static const char* KindToCString(Kind kind); }; class BitVector; class FlowGraphAllocator; class FlowGraphCompiler; class FlowGraphVisitor; class Function; class LocalVariable; // M is a two argument macro. It is applied to each concrete value's // typename and classname. #define FOR_EACH_VALUE(M) \ M(Use, UseVal) \ M(Constant, ConstantVal) \ // M is a two argument macro. It is applied to each concrete instruction's // (including the values) typename and classname. #define FOR_EACH_COMPUTATION(M) \ M(AssertAssignable, AssertAssignableComp) \ M(AssertBoolean, AssertBooleanComp) \ M(CurrentContext, CurrentContextComp) \ M(StoreContext, StoreContextComp) \ M(ClosureCall, ClosureCallComp) \ M(InstanceCall, InstanceCallComp) \ M(PolymorphicInstanceCall, PolymorphicInstanceCallComp) \ M(StaticCall, StaticCallComp) \ M(LoadLocal, LoadLocalComp) \ M(StoreLocal, StoreLocalComp) \ M(StrictCompare, StrictCompareComp) \ M(EqualityCompare, EqualityCompareComp) \ M(RelationalOp, RelationalOpComp) \ M(NativeCall, NativeCallComp) \ M(LoadIndexed, LoadIndexedComp) \ M(StoreIndexed, StoreIndexedComp) \ M(LoadInstanceField, LoadInstanceFieldComp) \ M(StoreInstanceField, StoreInstanceFieldComp) \ M(LoadStaticField, LoadStaticFieldComp) \ M(StoreStaticField, StoreStaticFieldComp) \ M(BooleanNegate, BooleanNegateComp) \ M(InstanceOf, InstanceOfComp) \ M(CreateArray, CreateArrayComp) \ M(CreateClosure, CreateClosureComp) \ M(AllocateObject, AllocateObjectComp) \ M(AllocateObjectWithBoundsCheck, AllocateObjectWithBoundsCheckComp) \ M(LoadVMField, LoadVMFieldComp) \ M(StoreVMField, StoreVMFieldComp) \ M(InstantiateTypeArguments, InstantiateTypeArgumentsComp) \ M(ExtractConstructorTypeArguments, ExtractConstructorTypeArgumentsComp) \ M(ExtractConstructorInstantiator, ExtractConstructorInstantiatorComp) \ M(AllocateContext, AllocateContextComp) \ M(ChainContext, ChainContextComp) \ M(CloneContext, CloneContextComp) \ M(CatchEntry, CatchEntryComp) \ M(BinaryOp, BinaryOpComp) \ M(DoubleBinaryOp, DoubleBinaryOpComp) \ M(UnarySmiOp, UnarySmiOpComp) \ M(NumberNegate, NumberNegateComp) \ M(CheckStackOverflow, CheckStackOverflowComp) \ M(DoubleToDouble, DoubleToDoubleComp) \ M(SmiToDouble, SmiToDoubleComp) \ M(CheckClass, CheckClassComp) \ M(Materialize, MaterializeComp) #define FORWARD_DECLARATION(ShortName, ClassName) class ClassName; FOR_EACH_COMPUTATION(FORWARD_DECLARATION) FOR_EACH_VALUE(FORWARD_DECLARATION) #undef FORWARD_DECLARATION // Forward declarations. class BindInstr; class BranchInstr; class BufferFormatter; class ComparisonComp; class Definition; class Instruction; class PushArgumentInstr; class Value; class Computation : public ZoneAllocated { public: Computation() : deopt_id_(Isolate::kNoDeoptId), ic_data_(NULL), locs_(NULL) { Isolate* isolate = Isolate::Current(); deopt_id_ = isolate->GetNextDeoptId(); ic_data_ = isolate->GetICDataForDeoptId(deopt_id_); } // Unique id used for deoptimization. intptr_t deopt_id() const { return deopt_id_; } const ICData* ic_data() const { return ic_data_; } void set_ic_data(const ICData* value) { ic_data_ = value; } bool HasICData() const { return (ic_data() != NULL) && !ic_data()->IsNull(); } // Visiting support. virtual void Accept(FlowGraphVisitor* visitor, BindInstr* instr) = 0; virtual intptr_t InputCount() const = 0; virtual Value* InputAt(intptr_t i) const = 0; virtual void SetInputAt(intptr_t i, Value* value) = 0; // Call computations override this function and return the // number of pushed arguments. virtual intptr_t ArgumentCount() const = 0; // Returns true, if this computation can deoptimize. virtual bool CanDeoptimize() const = 0; // Optimize this computation. Returns a replacement for the instruction // that wraps this computation or NULL if nothing to replace. virtual Definition* TryReplace(BindInstr* instr) { return NULL; } // Compares two computations. Returns true, if: // 1. They are of the same kind. // 2. All input operands match. // 3. All other attributes match. bool Equals(Computation* other) const; // Returns a hash code for use with hash maps. virtual intptr_t Hashcode() const; // Compare attributes of an computation (except input operands and kind). // TODO(fschneider): Make this abstract and implement for all computations. virtual bool AttributesEqual(Computation* other) const { return true; } // Returns true if the instruction may have side effects. // TODO(fschneider): Make this abstract and implement for all computations // instead of returning the safe default (true). virtual bool HasSideEffect() const { return true; } // Compile time type of the computation, which typically depends on the // compile time types (and possibly propagated types) of its inputs. virtual RawAbstractType* CompileType() const = 0; virtual intptr_t ResultCid() const { return kDynamicCid; } // Mutate assigned_vars to add the local variable index for all // frame-allocated locals assigned to by the computation. virtual void RecordAssignedVars(BitVector* assigned_vars, intptr_t fixed_parameter_count); virtual const char* DebugName() const = 0; // 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; // Returns structure describing location constraints required // to emit native code for this computation. LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } virtual ComparisonComp* AsComparison() { return NULL; } // Create a location summary for this computation. // TODO(fschneider): Temporarily returns NULL for instructions // that are not yet converted to the location based code generation. virtual LocationSummary* MakeLocationSummary() const = 0; // TODO(fschneider): Make EmitNativeCode and locs const. virtual void EmitNativeCode(FlowGraphCompiler* compiler) = 0; virtual void RemoveInputUses() = 0; static LocationSummary* MakeCallSummary(); // Declare an enum value used to define kind-test predicates. enum ComputationKind { #define DECLARE_COMPUTATION_KIND(ShortName, ClassName) k##ShortName, FOR_EACH_COMPUTATION(DECLARE_COMPUTATION_KIND) #undef DECLARE_COMPUTATION_KIND }; virtual ComputationKind computation_kind() const = 0; // Declare predicate for each computation. #define DECLARE_PREDICATE(ShortName, ClassName) \ inline bool Is##ShortName() const; \ inline const ClassName* As##ShortName() const; \ inline ClassName* As##ShortName(); FOR_EACH_COMPUTATION(DECLARE_PREDICATE) #undef DECLARE_PREDICATE private: intptr_t deopt_id_; const ICData* ic_data_; LocationSummary* locs_; DISALLOW_COPY_AND_ASSIGN(Computation); }; // 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; } }; template class TemplateComputation : public Computation { public: 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 void RemoveInputUses() { for (intptr_t i = 0; i < N; ++i) { ASSERT(inputs_[i] != NULL); inputs_[i]->RemoveFromUseList(); } } protected: EmbeddedArray inputs_; }; class Value : public ZoneAllocated { public: Value() { } // Declare an enum value used to define kind-test predicates. enum ValueKind { #define DECLARE_VALUE_KIND(ShortName, ClassName) k##ShortName, FOR_EACH_VALUE(DECLARE_VALUE_KIND) #undef DECLARE_VALUE_KIND }; // Declare predicate for each value. #define DECLARE_PREDICATE(ShortName, ClassName) \ inline bool Is##ShortName() const; \ inline const ClassName* As##ShortName() const; \ inline ClassName* As##ShortName(); FOR_EACH_VALUE(DECLARE_PREDICATE) #undef DECLARE_PREDICATE virtual ValueKind value_kind() const = 0; virtual RawAbstractType* CompileType() const = 0; virtual intptr_t ResultCid() const = 0; virtual void PrintTo(BufferFormatter* f) const = 0; // Returns true if the value represents a constant. virtual bool BindsToConstant() const = 0; // Returns true if the value represents constant null. virtual bool BindsToConstantNull() const = 0; // Assert if BindsToConstant() is false, otherwise returns constant. virtual const Object& BoundConstant() const = 0; // Reminder: The type of the constant null is the bottom type, which is more // specific than any type. bool CompileTypeIsMoreSpecificThan(const AbstractType& dst_type) const; virtual void RemoveFromUseList() = 0; virtual bool Equals(Value* other) const = 0; private: DISALLOW_COPY_AND_ASSIGN(Value); }; // Functions defined in all concrete computation classes. #define DECLARE_COMPUTATION(ShortName) \ virtual void Accept(FlowGraphVisitor* visitor, BindInstr* instr); \ virtual ComputationKind computation_kind() const { \ return Computation::k##ShortName; \ } \ virtual intptr_t ArgumentCount() const { return 0; } \ virtual const char* DebugName() const { return #ShortName; } \ virtual RawAbstractType* CompileType() const; \ virtual LocationSummary* MakeLocationSummary() const; \ virtual void EmitNativeCode(FlowGraphCompiler* compiler); // Functions defined in all concrete value classes. #define DECLARE_VALUE(ShortName) \ virtual ValueKind value_kind() const { \ return Value::k##ShortName; \ } \ virtual const char* DebugName() const { return #ShortName; } \ virtual RawAbstractType* CompileType() const; \ virtual bool Equals(Value* other) const; \ virtual void PrintTo(BufferFormatter* f) const; // Function defined in all call computation classes. #define DECLARE_CALL_COMPUTATION(ShortName) \ virtual void Accept(FlowGraphVisitor* visitor, BindInstr* instr); \ virtual ComputationKind computation_kind() const { \ return Computation::k##ShortName; \ } \ virtual const char* DebugName() const { return #ShortName; } \ virtual RawAbstractType* CompileType() const; \ virtual LocationSummary* MakeLocationSummary() const; \ virtual void EmitNativeCode(FlowGraphCompiler* compiler); class Definition; class PhiInstr; class UseVal : public Value { public: explicit UseVal(Definition* definition); DECLARE_VALUE(Use) inline Definition* definition() const; void SetDefinition(Definition* definition); // Returns true if the value represents a constant. virtual bool BindsToConstant() const; virtual const Object& BoundConstant() const; // Returns true if the value represents constant null. virtual bool BindsToConstantNull() const; virtual bool CanDeoptimize() const { return false; } UseVal* next_use() const { return next_use_; } UseVal* previous_use() const { return previous_use_; } virtual void RemoveFromUseList(); virtual void RemoveInputUses() { RemoveFromUseList(); } virtual intptr_t ResultCid() const; private: void AddToUseList(); Definition* definition_; UseVal* next_use_; UseVal* previous_use_; friend class Definition; DISALLOW_COPY_AND_ASSIGN(UseVal); }; class ConstantVal : public Value { public: explicit ConstantVal(const Object& value) : value_(value) { ASSERT(value.IsZoneHandle()); ASSERT(value.IsSmi() || value.IsOld()); } DECLARE_VALUE(Constant) const Object& value() const { return value_; } // Returns true if the value represents a constant. virtual bool BindsToConstant() const { return true; } virtual const Object& BoundConstant() const { return value(); } // Returns true if the value represents constant null. virtual bool BindsToConstantNull() const { return value().IsNull(); } virtual bool CanDeoptimize() const { return false; } virtual void RemoveFromUseList() { } virtual intptr_t ResultCid() const; private: const Object& value_; DISALLOW_COPY_AND_ASSIGN(ConstantVal); }; #undef DECLARE_VALUE class MaterializeComp : public TemplateComputation<0> { public: explicit MaterializeComp(ConstantVal* constant_val) : constant_val_(constant_val) { } DECLARE_COMPUTATION(Materialize) virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } ConstantVal* constant_val() const { return constant_val_; } virtual intptr_t ResultCid() const; private: ConstantVal* constant_val_; }; class AssertAssignableComp : public TemplateComputation<3> { public: AssertAssignableComp(intptr_t token_pos, intptr_t try_index, Value* value, Value* instantiator, Value* instantiator_type_arguments, const AbstractType& dst_type, const String& dst_name) : token_pos_(token_pos), try_index_(try_index), dst_type_(dst_type), dst_name_(dst_name), is_eliminated_(false) { 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_COMPUTATION(AssertAssignable) 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_; } intptr_t try_index() const { return try_index_; } const AbstractType& dst_type() const { return dst_type_; } const String& dst_name() const { return dst_name_; } bool is_eliminated() const { return is_eliminated_; } void eliminate() { ASSERT(!is_eliminated_); is_eliminated_ = true; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; const AbstractType& dst_type_; const String& dst_name_; bool is_eliminated_; DISALLOW_COPY_AND_ASSIGN(AssertAssignableComp); }; class AssertBooleanComp : public TemplateComputation<1> { public: AssertBooleanComp(intptr_t token_pos, intptr_t try_index, Value* value) : token_pos_(token_pos), try_index_(try_index), is_eliminated_(false) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(AssertBoolean) intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } Value* value() const { return inputs_[0]; } bool is_eliminated() const { return is_eliminated_; } void eliminate() { ASSERT(!is_eliminated_); is_eliminated_ = true; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual intptr_t ResultCid() const { return kBoolCid; } private: const intptr_t token_pos_; const intptr_t try_index_; bool is_eliminated_; DISALLOW_COPY_AND_ASSIGN(AssertBooleanComp); }; // Denotes the current context, normally held in a register. This is // a computation, not a value, because it's mutable. class CurrentContextComp : public TemplateComputation<0> { public: CurrentContextComp() { } DECLARE_COMPUTATION(CurrentContext) virtual bool CanDeoptimize() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(CurrentContextComp); }; class StoreContextComp : public TemplateComputation<1> { public: explicit StoreContextComp(Value* value) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(StoreContext); Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(StoreContextComp); }; class ClosureCallComp : public TemplateComputation<0> { public: ClosureCallComp(ClosureCallNode* node, intptr_t try_index, ZoneGrowableArray* arguments) : ast_node_(*node), try_index_(try_index), arguments_(arguments) { } DECLARE_CALL_COMPUTATION(ClosureCall) const Array& argument_names() const { return ast_node_.arguments()->names(); } intptr_t token_pos() const { return ast_node_.token_pos(); } intptr_t try_index() const { return try_index_; } 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; } private: const ClosureCallNode& ast_node_; const intptr_t try_index_; ZoneGrowableArray* arguments_; DISALLOW_COPY_AND_ASSIGN(ClosureCallComp); }; class InstanceCallComp : public TemplateComputation<0> { public: InstanceCallComp(intptr_t token_pos, intptr_t try_index, const String& function_name, Token::Kind token_kind, ZoneGrowableArray* arguments, const Array& argument_names, intptr_t checked_argument_count) : token_pos_(token_pos), try_index_(try_index), function_name_(function_name), token_kind_(token_kind), arguments_(arguments), argument_names_(argument_names), checked_argument_count_(checked_argument_count) { ASSERT(function_name.IsZoneHandle()); ASSERT(!arguments->is_empty()); ASSERT(argument_names.IsZoneHandle()); ASSERT(Token::IsBinaryToken(token_kind) || Token::IsUnaryToken(token_kind) || Token::IsIndexOperator(token_kind) || token_kind == Token::kGET || token_kind == Token::kSET || token_kind == Token::kILLEGAL); } DECLARE_CALL_COMPUTATION(InstanceCall) intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } 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 false; } private: const intptr_t token_pos_; const intptr_t try_index_; 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(InstanceCallComp); }; class PolymorphicInstanceCallComp : public TemplateComputation<0> { public: explicit PolymorphicInstanceCallComp(InstanceCallComp* comp) : instance_call_(comp) { ASSERT(instance_call_ != NULL); } InstanceCallComp* instance_call() const { return instance_call_; } void PrintTo(BufferFormatter* f) const; virtual intptr_t ArgumentCount() const { return instance_call()->ArgumentCount(); } DECLARE_CALL_COMPUTATION(PolymorphicInstanceCall) virtual bool CanDeoptimize() const { return true; } private: InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(PolymorphicInstanceCallComp); }; class ComparisonComp : public TemplateComputation<2> { public: ComparisonComp(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 ComparisonComp* AsComparison() { return this; } Token::Kind kind() const { return kind_; } private: Token::Kind kind_; }; class StrictCompareComp : public ComparisonComp { public: StrictCompareComp(Token::Kind kind, Value* left, Value* right) : ComparisonComp(kind, left, right) { ASSERT((kind == Token::kEQ_STRICT) || (kind == Token::kNE_STRICT)); } DECLARE_COMPUTATION(StrictCompare) virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual Definition* TryReplace(BindInstr* instr); virtual intptr_t ResultCid() const { return kBoolCid; } private: DISALLOW_COPY_AND_ASSIGN(StrictCompareComp); }; class EqualityCompareComp : public ComparisonComp { public: EqualityCompareComp(intptr_t token_pos, intptr_t try_index, Token::Kind kind, Value* left, Value* right) : ComparisonComp(kind, left, right), token_pos_(token_pos), try_index_(try_index), receiver_class_id_(kIllegalCid) { ASSERT((kind == Token::kEQ) || (kind == Token::kNE)); } DECLARE_COMPUTATION(EqualityCompare) intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } // 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_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const; private: const intptr_t token_pos_; const intptr_t try_index_; intptr_t receiver_class_id_; // Set by optimizer. DISALLOW_COPY_AND_ASSIGN(EqualityCompareComp); }; class RelationalOpComp : public ComparisonComp { public: RelationalOpComp(intptr_t token_pos, intptr_t try_index, Token::Kind kind, Value* left, Value* right) : ComparisonComp(kind, left, right), token_pos_(token_pos), try_index_(try_index), operands_class_id_(kIllegalCid) { ASSERT(Token::IsRelationalOperator(kind)); } DECLARE_COMPUTATION(RelationalOp) intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } // 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_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const; private: const intptr_t token_pos_; const intptr_t try_index_; intptr_t operands_class_id_; // class id of both operands. DISALLOW_COPY_AND_ASSIGN(RelationalOpComp); }; class StaticCallComp : public TemplateComputation<0> { public: StaticCallComp(intptr_t token_pos, intptr_t try_index, const Function& function, const Array& argument_names, ZoneGrowableArray* arguments) : token_pos_(token_pos), try_index_(try_index), function_(function), argument_names_(argument_names), arguments_(arguments), recognized_(MethodRecognizer::kUnknown) { ASSERT(function.IsZoneHandle()); ASSERT(argument_names.IsZoneHandle()); } DECLARE_CALL_COMPUTATION(StaticCall) // 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_; } intptr_t try_index() const { return try_index_; } virtual intptr_t ArgumentCount() const { return arguments_->length(); } PushArgumentInstr* ArgumentAt(intptr_t index) const { return (*arguments_)[index]; } MethodRecognizer::Kind recognized() const { return recognized_; } void set_recognized(MethodRecognizer::Kind kind) { recognized_ = kind; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; const Function& function_; const Array& argument_names_; ZoneGrowableArray* arguments_; MethodRecognizer::Kind recognized_; DISALLOW_COPY_AND_ASSIGN(StaticCallComp); }; class LoadLocalComp : public TemplateComputation<0> { public: LoadLocalComp(const LocalVariable& local, intptr_t context_level) : local_(local), context_level_(context_level) { } DECLARE_COMPUTATION(LoadLocal) 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; } private: const LocalVariable& local_; const intptr_t context_level_; DISALLOW_COPY_AND_ASSIGN(LoadLocalComp); }; class StoreLocalComp : public TemplateComputation<1> { public: StoreLocalComp(const LocalVariable& local, Value* value, intptr_t context_level) : local_(local), context_level_(context_level) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(StoreLocal) 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; } private: const LocalVariable& local_; const intptr_t context_level_; DISALLOW_COPY_AND_ASSIGN(StoreLocalComp); }; class NativeCallComp : public TemplateComputation<0> { public: NativeCallComp(NativeBodyNode* node, intptr_t try_index) : ast_node_(*node), try_index_(try_index) {} DECLARE_COMPUTATION(NativeCall) intptr_t token_pos() const { return ast_node_.token_pos(); } intptr_t try_index() const { return try_index_; } const String& native_name() const { return ast_node_.native_c_function_name(); } NativeFunction native_c_function() const { return ast_node_.native_c_function(); } intptr_t argument_count() const { return ast_node_.argument_count(); } bool has_optional_parameters() const { return ast_node_.has_optional_parameters(); } bool is_native_instance_closure() const { return ast_node_.is_native_instance_closure(); } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const NativeBodyNode& ast_node_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(NativeCallComp); }; class LoadInstanceFieldComp : public TemplateComputation<1> { public: LoadInstanceFieldComp(const Field& field, Value* instance, InstanceCallComp* original, // Maybe NULL. bool can_deoptimize) : field_(field), original_(original), can_deoptimize_(can_deoptimize) { ASSERT(instance != NULL); inputs_[0] = instance; } DECLARE_COMPUTATION(LoadInstanceField) const Field& field() const { return field_; } Value* instance() const { return inputs_[0]; } const InstanceCallComp* original() const { return original_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return can_deoptimize_; } private: const Field& field_; const InstanceCallComp* original_; // For optimizations. const bool can_deoptimize_; DISALLOW_COPY_AND_ASSIGN(LoadInstanceFieldComp); }; class StoreInstanceFieldComp : public TemplateComputation<2> { public: StoreInstanceFieldComp(const Field& field, Value* instance, Value* value, InstanceCallComp* original) // Maybe NULL. : field_(field), original_(original) { ASSERT(instance != NULL); ASSERT(value != NULL); inputs_[0] = instance; inputs_[1] = value; } DECLARE_COMPUTATION(StoreInstanceField) const Field& field() const { return field_; } Value* instance() const { return inputs_[0]; } Value* value() const { return inputs_[1]; } const InstanceCallComp* original() const { return original_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } private: const Field& field_; const InstanceCallComp* original_; // For optimizations. DISALLOW_COPY_AND_ASSIGN(StoreInstanceFieldComp); }; class LoadStaticFieldComp : public TemplateComputation<0> { public: explicit LoadStaticFieldComp(const Field& field) : field_(field) {} DECLARE_COMPUTATION(LoadStaticField); const Field& field() const { return field_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(LoadStaticFieldComp); }; class StoreStaticFieldComp : public TemplateComputation<1> { public: StoreStaticFieldComp(const Field& field, Value* value) : field_(field) { ASSERT(field.IsZoneHandle()); ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(StoreStaticField); const Field& field() const { return field_; } Value* value() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const Field& field_; DISALLOW_COPY_AND_ASSIGN(StoreStaticFieldComp); }; class LoadIndexedComp : public TemplateComputation<2> { public: LoadIndexedComp(Value* array, Value* index, intptr_t receiver_type, InstanceCallComp* original) : receiver_type_(receiver_type), original_(original) { ASSERT(array != NULL); ASSERT(index != NULL); inputs_[0] = array; inputs_[1] = index; } DECLARE_COMPUTATION(LoadIndexed) Value* array() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } intptr_t receiver_type() const { return receiver_type_; } InstanceCallComp* original() const { return original_; } virtual bool CanDeoptimize() const { return true; } private: intptr_t receiver_type_; InstanceCallComp* original_; DISALLOW_COPY_AND_ASSIGN(LoadIndexedComp); }; class StoreIndexedComp : public TemplateComputation<3> { public: StoreIndexedComp(Value* array, Value* index, Value* value, intptr_t receiver_type, InstanceCallComp* original) : receiver_type_(receiver_type), original_(original) { ASSERT(array != NULL); ASSERT(index != NULL); ASSERT(value != NULL); inputs_[0] = array; inputs_[1] = index; inputs_[2] = value; } DECLARE_COMPUTATION(StoreIndexed) Value* array() const { return inputs_[0]; } Value* index() const { return inputs_[1]; } Value* value() const { return inputs_[2]; } InstanceCallComp* original() const { return original_; } intptr_t receiver_type() const { return receiver_type_; } virtual bool CanDeoptimize() const { return true; } private: intptr_t receiver_type_; InstanceCallComp* original_; DISALLOW_COPY_AND_ASSIGN(StoreIndexedComp); }; // Note overrideable, built-in: value? false : true. class BooleanNegateComp : public TemplateComputation<1> { public: explicit BooleanNegateComp(Value* value) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(BooleanNegate) Value* value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(BooleanNegateComp); }; class InstanceOfComp : public TemplateComputation<3> { public: InstanceOfComp(intptr_t token_pos, intptr_t try_index, Value* value, Value* instantiator, Value* instantiator_type_arguments, const AbstractType& type, bool negate_result) : token_pos_(token_pos), try_index_(try_index), 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_COMPUTATION(InstanceOf) 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_; } intptr_t try_index() const { return try_index_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } virtual intptr_t ResultCid() const { return kBoolCid; } private: const intptr_t token_pos_; const intptr_t try_index_; Value* value_; Value* instantiator_; Value* type_arguments_; const AbstractType& type_; const bool negate_result_; DISALLOW_COPY_AND_ASSIGN(InstanceOfComp); }; class AllocateObjectComp : public TemplateComputation<0> { public: AllocateObjectComp(ConstructorCallNode* node, intptr_t try_index, ZoneGrowableArray* arguments) : ast_node_(*node), try_index_(try_index), arguments_(arguments) { // Either no arguments or one type-argument and one instantiator. ASSERT(arguments->is_empty() || (arguments->length() == 2)); } DECLARE_CALL_COMPUTATION(AllocateObject) 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(); } intptr_t try_index() const { return try_index_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const ConstructorCallNode& ast_node_; const intptr_t try_index_; ZoneGrowableArray* const arguments_; DISALLOW_COPY_AND_ASSIGN(AllocateObjectComp); }; class AllocateObjectWithBoundsCheckComp : public TemplateComputation<2> { public: AllocateObjectWithBoundsCheckComp(ConstructorCallNode* node, intptr_t try_index, Value* type_arguments, Value* instantiator) : ast_node_(*node), try_index_(try_index) { ASSERT(type_arguments != NULL); ASSERT(instantiator != NULL); inputs_[0] = type_arguments; inputs_[1] = instantiator; } DECLARE_COMPUTATION(AllocateObjectWithBoundsCheck) const Function& constructor() const { return ast_node_.constructor(); } intptr_t token_pos() const { return ast_node_.token_pos(); } intptr_t try_index() const { return try_index_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const ConstructorCallNode& ast_node_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(AllocateObjectWithBoundsCheckComp); }; class CreateArrayComp : public TemplateComputation<1> { public: CreateArrayComp(intptr_t token_pos, intptr_t try_index, ZoneGrowableArray* arguments, Value* element_type) : token_pos_(token_pos), try_index_(try_index), arguments_(arguments) { #if defined(DEBUG) for (int i = 0; i < ArgumentCount(); ++i) { ASSERT(ArgumentAt(i) != NULL); } ASSERT(element_type != NULL); #endif inputs_[0] = element_type; } DECLARE_CALL_COMPUTATION(CreateArray) virtual intptr_t ArgumentCount() const { return arguments_->length(); } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } PushArgumentInstr* ArgumentAt(intptr_t i) const { return (*arguments_)[i]; } Value* element_type() const { return inputs_[0]; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; ZoneGrowableArray* const arguments_; DISALLOW_COPY_AND_ASSIGN(CreateArrayComp); }; class CreateClosureComp : public TemplateComputation<0> { public: CreateClosureComp(ClosureNode* node, intptr_t try_index, ZoneGrowableArray* arguments) : ast_node_(*node), try_index_(try_index), arguments_(arguments) { } DECLARE_CALL_COMPUTATION(CreateClosure) intptr_t token_pos() const { return ast_node_.token_pos(); } intptr_t try_index() const { return try_index_; } const Function& function() const { return ast_node_.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; } private: const ClosureNode& ast_node_; const intptr_t try_index_; ZoneGrowableArray* arguments_; DISALLOW_COPY_AND_ASSIGN(CreateClosureComp); }; class LoadVMFieldComp : public TemplateComputation<1> { public: LoadVMFieldComp(Value* value, intptr_t offset_in_bytes, const AbstractType& type) : offset_in_bytes_(offset_in_bytes), type_(type), original_(NULL) { ASSERT(value != NULL); ASSERT(type.IsZoneHandle()); // May be null if field is not an instance. inputs_[0] = value; } DECLARE_COMPUTATION(LoadVMField) Value* value() const { return inputs_[0]; } intptr_t offset_in_bytes() const { return offset_in_bytes_; } const AbstractType& type() const { return type_; } const InstanceCallComp* original() const { return original_; } void set_original(InstanceCallComp* value) { original_ = value; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return true; } private: const intptr_t offset_in_bytes_; const AbstractType& type_; const InstanceCallComp* original_; // For optimizations. // If non-NULL, the instruction is valid only for the class ids listed. DISALLOW_COPY_AND_ASSIGN(LoadVMFieldComp); }; class StoreVMFieldComp : public TemplateComputation<2> { public: StoreVMFieldComp(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_COMPUTATION(StoreVMField) 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; } private: const intptr_t offset_in_bytes_; const AbstractType& type_; DISALLOW_COPY_AND_ASSIGN(StoreVMFieldComp); }; class InstantiateTypeArgumentsComp : public TemplateComputation<1> { public: InstantiateTypeArgumentsComp(intptr_t token_pos, intptr_t try_index, const AbstractTypeArguments& type_arguments, Value* instantiator) : token_pos_(token_pos), try_index_(try_index), type_arguments_(type_arguments) { ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_COMPUTATION(InstantiateTypeArguments) Value* instantiator() const { return inputs_[0]; } const AbstractTypeArguments& type_arguments() const { return type_arguments_; } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; const AbstractTypeArguments& type_arguments_; DISALLOW_COPY_AND_ASSIGN(InstantiateTypeArgumentsComp); }; class ExtractConstructorTypeArgumentsComp : public TemplateComputation<1> { public: ExtractConstructorTypeArgumentsComp( intptr_t token_pos, intptr_t try_index, const AbstractTypeArguments& type_arguments, Value* instantiator) : token_pos_(token_pos), try_index_(try_index), type_arguments_(type_arguments) { ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_COMPUTATION(ExtractConstructorTypeArguments) Value* instantiator() const { return inputs_[0]; } const AbstractTypeArguments& type_arguments() const { return type_arguments_; } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; const AbstractTypeArguments& type_arguments_; DISALLOW_COPY_AND_ASSIGN(ExtractConstructorTypeArgumentsComp); }; class ExtractConstructorInstantiatorComp : public TemplateComputation<1> { public: ExtractConstructorInstantiatorComp(ConstructorCallNode* ast_node, Value* instantiator) : ast_node_(*ast_node) { ASSERT(instantiator != NULL); inputs_[0] = instantiator; } DECLARE_COMPUTATION(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; } private: const ConstructorCallNode& ast_node_; DISALLOW_COPY_AND_ASSIGN(ExtractConstructorInstantiatorComp); }; class AllocateContextComp : public TemplateComputation<0> { public: AllocateContextComp(intptr_t token_pos, intptr_t try_index, intptr_t num_context_variables) : token_pos_(token_pos), try_index_(try_index), num_context_variables_(num_context_variables) {} DECLARE_COMPUTATION(AllocateContext); intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } intptr_t num_context_variables() const { return num_context_variables_; } virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; const intptr_t num_context_variables_; DISALLOW_COPY_AND_ASSIGN(AllocateContextComp); }; class ChainContextComp : public TemplateComputation<1> { public: explicit ChainContextComp(Value* context_value) { ASSERT(context_value != NULL); inputs_[0] = context_value; } DECLARE_COMPUTATION(ChainContext) Value* context_value() const { return inputs_[0]; } virtual bool CanDeoptimize() const { return false; } private: DISALLOW_COPY_AND_ASSIGN(ChainContextComp); }; class CloneContextComp : public TemplateComputation<1> { public: CloneContextComp(intptr_t token_pos, intptr_t try_index, Value* context_value) : token_pos_(token_pos), try_index_(try_index) { ASSERT(context_value != NULL); inputs_[0] = context_value; } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } Value* context_value() const { return inputs_[0]; } DECLARE_COMPUTATION(CloneContext) virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(CloneContextComp); }; class CatchEntryComp : public TemplateComputation<0> { public: CatchEntryComp(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_COMPUTATION(CatchEntry) virtual void PrintOperandsTo(BufferFormatter* f) const; virtual bool CanDeoptimize() const { return false; } private: const LocalVariable& exception_var_; const LocalVariable& stacktrace_var_; DISALLOW_COPY_AND_ASSIGN(CatchEntryComp); }; class BinaryOpComp : public TemplateComputation<2> { public: enum OperandsType { kDynamicOperands, kSmiOperands, kMintOperands, kDoubleOperands }; BinaryOpComp(Token::Kind op_kind, OperandsType operands_type, InstanceCallComp* instance_call, Value* left, Value* right) : op_kind_(op_kind), operands_type_(operands_type), instance_call_(instance_call) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind op_kind() const { return op_kind_; } OperandsType operands_type() const { return operands_type_; } InstanceCallComp* instance_call() const { return instance_call_; } virtual void PrintOperandsTo(BufferFormatter* f) const; DECLARE_COMPUTATION(BinaryOp) virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const; private: const Token::Kind op_kind_; const OperandsType operands_type_; InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(BinaryOpComp); }; class DoubleBinaryOpComp : public TemplateComputation<0> { public: DoubleBinaryOpComp(Token::Kind op_kind, InstanceCallComp* instance_call) : op_kind_(op_kind), instance_call_(instance_call) { } Token::Kind op_kind() const { return op_kind_; } InstanceCallComp* instance_call() const { return instance_call_; } virtual void PrintOperandsTo(BufferFormatter* f) const; DECLARE_CALL_COMPUTATION(DoubleBinaryOp) virtual intptr_t ArgumentCount() const { return 2; } virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const; private: const Token::Kind op_kind_; InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(DoubleBinaryOpComp); }; // Handles both Smi operations: BIT_OR and NEGATE. class UnarySmiOpComp : public TemplateComputation<1> { public: UnarySmiOpComp(Token::Kind op_kind, InstanceCallComp* instance_call, Value* value) : op_kind_(op_kind), instance_call_(instance_call) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } Token::Kind op_kind() const { return op_kind_; } InstanceCallComp* instance_call() const { return instance_call_; } virtual void PrintOperandsTo(BufferFormatter* f) const; DECLARE_COMPUTATION(UnarySmiOp) virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const { return kSmiCid; } private: const Token::Kind op_kind_; InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(UnarySmiOpComp); }; // Handles non-Smi NEGATE operations class NumberNegateComp : public TemplateComputation<1> { public: NumberNegateComp(InstanceCallComp* instance_call, Value* value) : instance_call_(instance_call) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } InstanceCallComp* instance_call() const { return instance_call_; } DECLARE_COMPUTATION(NumberNegate) virtual bool CanDeoptimize() const { return true; } private: InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(NumberNegateComp); }; class CheckStackOverflowComp : public TemplateComputation<0> { public: CheckStackOverflowComp(intptr_t token_pos, intptr_t try_index) : token_pos_(token_pos), try_index_(try_index) {} intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } DECLARE_COMPUTATION(CheckStackOverflow) virtual bool CanDeoptimize() const { return false; } private: const intptr_t token_pos_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(CheckStackOverflowComp); }; class DoubleToDoubleComp : public TemplateComputation<1> { public: DoubleToDoubleComp(Value* value, InstanceCallComp* instance_call) : instance_call_(instance_call) { ASSERT(value != NULL); inputs_[0] = value; } Value* value() const { return inputs_[0]; } InstanceCallComp* instance_call() const { return instance_call_; } DECLARE_COMPUTATION(DoubleToDouble) virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const { return kDoubleCid; } private: InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(DoubleToDoubleComp); }; class SmiToDoubleComp : public TemplateComputation<0> { public: explicit SmiToDoubleComp(InstanceCallComp* instance_call) : instance_call_(instance_call) { } InstanceCallComp* instance_call() const { return instance_call_; } DECLARE_CALL_COMPUTATION(SmiToDouble) virtual intptr_t ArgumentCount() const { return 1; } virtual bool CanDeoptimize() const { return true; } virtual intptr_t ResultCid() const { return kDoubleCid; } private: InstanceCallComp* instance_call_; DISALLOW_COPY_AND_ASSIGN(SmiToDoubleComp); }; class CheckClassComp : public TemplateComputation<1> { public: CheckClassComp(Value* value, InstanceCallComp* original) : original_(original) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_COMPUTATION(CheckClass) virtual bool CanDeoptimize() const { return true; } virtual bool AttributesEqual(Computation* other) const; virtual bool HasSideEffect() const { return false; } Value* value() const { return inputs_[0]; } intptr_t deopt_id() const { return original_->deopt_id(); } intptr_t try_index() const { return original_->try_index(); } private: InstanceCallComp* original_; DISALLOW_COPY_AND_ASSIGN(CheckClassComp); }; #undef DECLARE_COMPUTATION // Implementation of type testers and cast functins. #define DEFINE_COMPUTATION_PREDICATE(ShortName, ClassName) \ bool Computation::Is##ShortName() const { \ return computation_kind() == k##ShortName; \ } \ const ClassName* Computation::As##ShortName() const { \ if (!Is##ShortName()) return NULL; \ return static_cast(this); \ } \ ClassName* Computation::As##ShortName() { \ if (!Is##ShortName()) return NULL; \ return static_cast(this); \ } FOR_EACH_COMPUTATION(DEFINE_COMPUTATION_PREDICATE) #undef DEFINE_COMPUTATION_PREDICATE #define DEFINE_VALUE_PREDICATE(ShortName, ClassName) \ bool Value::Is##ShortName() const { \ return value_kind() == k##ShortName; \ } \ const ClassName* Value::As##ShortName() const { \ if (!Is##ShortName()) return NULL; \ return static_cast(this); \ } \ ClassName* Value::As##ShortName() { \ if (!Is##ShortName()) return NULL; \ return static_cast(this); \ } FOR_EACH_VALUE(DEFINE_VALUE_PREDICATE) #undef DEFINE_VALUE_PREDICATE // 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(Bind) \ M(Parameter) \ M(ParallelMove) \ M(PushArgument) \ M(Return) \ M(Throw) \ M(ReThrow) \ M(Goto) \ M(Branch) \ // Forward declarations for Instruction classes. class BlockEntryInstr; class FlowGraphBuilder; class Environment; #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 void Accept(FlowGraphVisitor* visitor); \ virtual bool Is##type() const { return true; } \ virtual type##Instr* As##type() { return this; } \ virtual const char* DebugName() const { return #type; } \ virtual void PrintTo(BufferFormatter* f) const; \ virtual void PrintToVisualizer(BufferFormatter* f) const; class Instruction : public ZoneAllocated { public: Instruction() : lifetime_position_(-1), previous_(NULL), next_(NULL), env_(NULL) { } virtual bool IsBlockEntry() const { return false; } BlockEntryInstr* AsBlockEntry() { return IsBlockEntry() ? reinterpret_cast(this) : NULL; } virtual bool IsDefinition() const { return false; } virtual Definition* AsDefinition() { 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; // Call instructions override this function and return the // number of pushed arguments. virtual intptr_t ArgumentCount() const = 0; // Returns true, if this instruction can deoptimize. virtual bool CanDeoptimize() const = 0; // Visiting support. virtual void Accept(FlowGraphVisitor* visitor) = 0; Instruction* previous() const { return previous_; } void set_previous(Instruction* instr) { ASSERT(!IsBlockEntry()); previous_ = instr; } Instruction* next() const { return next_; } void set_next(Instruction* instr) { ASSERT(!IsGraphEntry()); ASSERT(!IsReturn()); ASSERT(!IsBranch()); 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; } // Removed this instruction from the graph. Instruction* RemoveFromGraph(bool return_previous = true); // Remove value uses within this instruction and its inputs. virtual void RemoveInputUses() = 0; // 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); // 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. virtual void DiscoverBlocks( BlockEntryInstr* current_block, GrowableArray* preorder, GrowableArray* postorder, GrowableArray* parent, GrowableArray* assigned_vars, intptr_t variable_count, intptr_t fixed_parameter_count) { // Never called for instructions except block entries and branches. UNREACHABLE(); } // 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); // Printing support. virtual void PrintTo(BufferFormatter* f) const = 0; virtual void PrintToVisualizer(BufferFormatter* f) const = 0; #define INSTRUCTION_TYPE_CHECK(type) \ virtual bool Is##type() const { return false; } \ 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 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; } private: friend class BindInstr; // Needed for BindInstr::InsertBefore. intptr_t lifetime_position_; // Position used by register allocator. Instruction* previous_; Instruction* next_; Environment* env_; 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_; } virtual LocationSummary* MakeLocationSummary() const = 0; virtual void RemoveInputUses() { for (intptr_t i = 0; i < N; ++i) { ASSERT(inputs_[i] != NULL); inputs_[i]->RemoveFromUseList(); } } 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; } 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(); } LocationSummary* MakeLocationSummary() const { return NULL; } void EmitNativeCode(FlowGraphCompiler* compiler) { UNREACHABLE(); } 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 bool IsBlockEntry() const { return true; } virtual intptr_t PredecessorCount() const = 0; virtual BlockEntryInstr* PredecessorAt(intptr_t index) const = 0; virtual void AddPredecessor(BlockEntryInstr* predecessor) = 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_block_id(intptr_t value) { block_id_ = value; } 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); } 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_; } virtual void DiscoverBlocks( BlockEntryInstr* current_block, 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 void RemoveInputUses() { } protected: BlockEntryInstr() : preorder_number_(-1), postorder_number_(-1), block_id_(-1), dominator_(NULL), dominated_blocks_(1), last_instruction_(NULL), parallel_move_(NULL) { } private: 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 block_id_; 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_; DISALLOW_COPY_AND_ASSIGN(BlockEntryInstr); }; class ForwardInstructionIterator : public ValueObject { public: explicit ForwardInstructionIterator(BlockEntryInstr* block_entry) : block_entry_(block_entry), current_(block_entry) { ASSERT(block_entry_->last_instruction()->next() == NULL); Advance(); } void Advance() { ASSERT(!Done()); current_ = current_->next(); } bool Done() const { return current_ == NULL; } // Removes 'current_' from graph and sets 'current_' to previous instruction. void RemoveCurrentFromGraph(); Instruction* Current() const { return current_; } private: 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) : BlockEntryInstr(), normal_entry_(normal_entry), catch_entries_(), start_env_(NULL), spill_slot_count_(0) { } DECLARE_INSTRUCTION(GraphEntry) virtual intptr_t PredecessorCount() const { return 0; } virtual BlockEntryInstr* PredecessorAt(intptr_t index) const { UNREACHABLE(); return NULL; } virtual void AddPredecessor(BlockEntryInstr* predecessor) { UNREACHABLE(); } virtual intptr_t SuccessorCount() const; virtual BlockEntryInstr* SuccessorAt(intptr_t index) const; virtual void DiscoverBlocks( BlockEntryInstr* current_block, GrowableArray* preorder, GrowableArray* postorder, GrowableArray* parent, GrowableArray* assigned_vars, intptr_t variable_count, intptr_t fixed_parameter_count); void AddCatchEntry(TargetEntryInstr* entry) { catch_entries_.Add(entry); } virtual void PrepareEntry(FlowGraphCompiler* compiler); Environment* start_env() const { return start_env_; } void set_start_env(Environment* env) { start_env_ = env; } 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; } private: TargetEntryInstr* normal_entry_; GrowableArray catch_entries_; Environment* start_env_; intptr_t spill_slot_count_; DISALLOW_COPY_AND_ASSIGN(GraphEntryInstr); }; class JoinEntryInstr : public BlockEntryInstr { public: JoinEntryInstr() : BlockEntryInstr(), 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]; } virtual void AddPredecessor(BlockEntryInstr* predecessor) { predecessors_.Add(predecessor); } // 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(); intptr_t phi_count() const { return phi_count_; } private: GrowableArray predecessors_; ZoneGrowableArray* phis_; intptr_t phi_count_; DISALLOW_COPY_AND_ASSIGN(JoinEntryInstr); }; class TargetEntryInstr : public BlockEntryInstr { public: TargetEntryInstr() : BlockEntryInstr(), predecessor_(NULL), try_index_(CatchClauseNode::kInvalidTryIndex) { } // Used for exception catch entries. explicit TargetEntryInstr(intptr_t try_index) : BlockEntryInstr(), predecessor_(NULL), try_index_(try_index) { } 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_; } virtual void AddPredecessor(BlockEntryInstr* predecessor) { ASSERT(predecessor_ == NULL); predecessor_ = predecessor; } bool HasTryIndex() const { return try_index_ != CatchClauseNode::kInvalidTryIndex; } intptr_t try_index() const { ASSERT(HasTryIndex()); return try_index_; } virtual void PrepareEntry(FlowGraphCompiler* compiler); private: BlockEntryInstr* predecessor_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(TargetEntryInstr); }; // Abstract super-class of all instructions that define a value (Bind, Phi). class Definition : public Instruction { public: Definition() : temp_index_(-1), ssa_temp_index_(-1), propagated_type_(AbstractType::Handle()), propagated_cid_(kIllegalCid), use_list_(NULL) { } virtual bool IsDefinition() const { return true; } virtual Definition* AsDefinition() { return this; } intptr_t temp_index() const { return temp_index_; } void set_temp_index(intptr_t index) { temp_index_ = index; } intptr_t ssa_temp_index() const { return ssa_temp_index_; } void set_ssa_temp_index(intptr_t index) { ASSERT(index >= 0); ssa_temp_index_ = index; } bool HasSSATemp() const { return ssa_temp_index_ >= 0; } // Compile time type of the definition, which may be requested before type // propagation during graph building. virtual RawAbstractType* CompileType() const = 0; bool HasPropagatedType() const { return !propagated_type_.IsNull(); } RawAbstractType* PropagatedType() const { ASSERT(HasPropagatedType()); return propagated_type_.raw(); } // Returns true if the propagated type has changed. bool SetPropagatedType(const AbstractType& propagated_type) { if (propagated_type.IsNull()) { // Not a typed definition, e.g. access to a VM field. return false; } const bool changed = propagated_type_.IsNull() || !propagated_type.Equals(propagated_type_); propagated_type_ = propagated_type.raw(); return changed; } bool has_propagated_cid() const { return propagated_cid_ != kIllegalCid; } intptr_t propagated_cid() const { return propagated_cid_; } // May compute and set propagated cid. virtual intptr_t GetPropagatedCid() = 0; // Returns true if the propagated cid has changed. bool SetPropagatedCid(intptr_t cid); UseVal* use_list() { return use_list_; } void set_use_list(UseVal* head) { ASSERT(head == NULL || head->previous_use() == NULL); use_list_ = head; } void ReplaceUsesWith(Definition* other); private: intptr_t temp_index_; intptr_t ssa_temp_index_; // TODO(regis): GrowableArray propagated_types_; // For now: AbstractType& propagated_type_; intptr_t propagated_cid_; UseVal* use_list_; DISALLOW_COPY_AND_ASSIGN(Definition); }; Definition* UseVal::definition() const { // Check that the definition is either a Phi or a linked in the the IR. ASSERT(definition_ != NULL); return definition_; } class BindInstr : public Definition { public: enum UseKind { kUnused, kUsed }; BindInstr(UseKind used, Computation* computation) : computation_(computation), is_used_(used != kUnused) { ASSERT(computation != NULL); } DECLARE_INSTRUCTION(Bind) virtual intptr_t ArgumentCount() const { return computation()->ArgumentCount(); } intptr_t InputCount() const { return computation()->InputCount(); } Value* InputAt(intptr_t i) const { return computation()->InputAt(i); } void SetInputAt(intptr_t i, Value* value) { computation()->SetInputAt(i, value); } virtual bool CanDeoptimize() const { return computation()->CanDeoptimize(); } Computation* computation() const { return computation_; } void set_computation(Computation* value) { computation_ = value; } bool is_used() const { return is_used_; } virtual RawAbstractType* CompileType() const; virtual intptr_t GetPropagatedCid(); virtual void RecordAssignedVars(BitVector* assigned_vars, intptr_t fixed_parameter_count); intptr_t Hashcode() const { return computation()->Hashcode(); } bool Equals(BindInstr* other) const { return computation()->Equals(other->computation()); } virtual LocationSummary* locs() { return computation()->locs(); } virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual void RemoveInputUses() { computation()->RemoveInputUses(); } // Insert this instruction before 'next'. void InsertBefore(BindInstr* next); private: Computation* computation_; const bool is_used_; DISALLOW_COPY_AND_ASSIGN(BindInstr); }; class PhiInstr : public Definition { public: explicit PhiInstr(intptr_t num_inputs) : inputs_(num_inputs), is_alive_(false) { for (intptr_t i = 0; i < num_inputs; ++i) { inputs_.Add(NULL); } } virtual RawAbstractType* CompileType() const; virtual intptr_t GetPropagatedCid() { return propagated_cid(); } 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 void RemoveInputUses() { for (intptr_t i = 0; i < inputs_.length(); ++i) { ASSERT(inputs_[i] != NULL); inputs_[i]->RemoveFromUseList(); } } // TODO(regis): This helper will be removed once we support type sets. RawAbstractType* LeastSpecificInputType() const; // Phi is alive if it reaches a non-environment use. bool is_alive() const { return is_alive_; } void mark_alive() { is_alive_ = true; } DECLARE_INSTRUCTION(Phi) private: GrowableArray inputs_; bool is_alive_; DISALLOW_COPY_AND_ASSIGN(PhiInstr); }; class ParameterInstr : public Definition { public: explicit ParameterInstr(intptr_t index) : index_(index) { } DECLARE_INSTRUCTION(Parameter) intptr_t index() const { return index_; } // Compile type of the passed-in parameter. virtual RawAbstractType* CompileType() const; // No known propagated cid for parameters. virtual intptr_t GetPropagatedCid() { return propagated_cid(); } 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 void RemoveInputUses() { } private: const intptr_t index_; DISALLOW_COPY_AND_ASSIGN(ParameterInstr); }; class PushArgumentInstr : public Definition { public: explicit PushArgumentInstr(Value* value) : value_(value), locs_(NULL) { ASSERT(value != NULL); } 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 RawAbstractType* CompileType() const; virtual intptr_t GetPropagatedCid() { return propagated_cid(); } Value* value() const { return value_; } virtual LocationSummary* locs() { if (locs_ == NULL) { locs_ = MakeLocationSummary(); } return locs_; } LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual bool CanDeoptimize() const { return false; } bool WasEliminated() const { return next() == NULL; } virtual void RemoveInputUses() { value_->RemoveFromUseList(); } private: Value* value_; LocationSummary* locs_; DISALLOW_COPY_AND_ASSIGN(PushArgumentInstr); }; class ReturnInstr : public TemplateInstruction<1> { public: ReturnInstr(intptr_t token_pos, Value* value) : deopt_id_(Isolate::Current()->GetNextDeoptId()), token_pos_(token_pos) { ASSERT(value != NULL); inputs_[0] = value; } DECLARE_INSTRUCTION(Return) virtual intptr_t ArgumentCount() const { return 0; } intptr_t deopt_id() const { return deopt_id_; } intptr_t token_pos() const { return token_pos_; } Value* value() const { return inputs_[0]; } virtual LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual bool CanDeoptimize() const { return false; } private: const intptr_t deopt_id_; const intptr_t token_pos_; DISALLOW_COPY_AND_ASSIGN(ReturnInstr); }; class ThrowInstr : public TemplateInstruction<0> { public: ThrowInstr(intptr_t token_pos, intptr_t try_index) : deopt_id_(Isolate::Current()->GetNextDeoptId()), token_pos_(token_pos), try_index_(try_index) { } DECLARE_INSTRUCTION(Throw) virtual intptr_t ArgumentCount() const { return 1; } intptr_t deopt_id() const { return deopt_id_; } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } virtual LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual bool CanDeoptimize() const { return false; } private: const intptr_t deopt_id_; const intptr_t token_pos_; const intptr_t try_index_; DISALLOW_COPY_AND_ASSIGN(ThrowInstr); }; class ReThrowInstr : public TemplateInstruction<0> { public: ReThrowInstr(intptr_t token_pos, intptr_t try_index) : deopt_id_(Isolate::Current()->GetNextDeoptId()), token_pos_(token_pos), try_index_(try_index) { } DECLARE_INSTRUCTION(ReThrow) virtual intptr_t ArgumentCount() const { return 2; } intptr_t deopt_id() const { return deopt_id_; } intptr_t token_pos() const { return token_pos_; } intptr_t try_index() const { return try_index_; } virtual LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual bool CanDeoptimize() const { return false; } private: const intptr_t deopt_id_; const intptr_t token_pos_; const intptr_t try_index_; 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 LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); virtual bool CanDeoptimize() 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_; } 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 BranchInstr : public TemplateInstruction<2> { public: BranchInstr(intptr_t token_pos, intptr_t try_index, Value* left, Value* right, Token::Kind kind) : deopt_id_(Isolate::kNoDeoptId), ic_data_(NULL), token_pos_(token_pos), try_index_(try_index), kind_(kind), true_successor_(NULL), false_successor_(NULL) { ASSERT(left != NULL); ASSERT(right != NULL); inputs_[0] = left; inputs_[1] = right; ASSERT(Token::IsEqualityOperator(kind) || Token::IsRelationalOperator(kind) || Token::IsTypeTestOperator(kind)); Isolate* isolate = Isolate::Current(); deopt_id_ = isolate->GetNextDeoptId(); ic_data_ = isolate->GetICDataForDeoptId(deopt_id_); } DECLARE_INSTRUCTION(Branch) virtual intptr_t ArgumentCount() const { return 0; } Value* left() const { return inputs_[0]; } Value* right() const { return inputs_[1]; } Token::Kind kind() const { return kind_; } void set_kind(Token::Kind kind) { ASSERT(Token::IsEqualityOperator(kind) || Token::IsRelationalOperator(kind) || Token::IsTypeTestOperator(kind)); kind_ = kind; } intptr_t deopt_id() const { return deopt_id_; } 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_;} intptr_t try_index() const { return try_index_; } 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; virtual void DiscoverBlocks( BlockEntryInstr* current_block, GrowableArray* preorder, GrowableArray* postorder, GrowableArray* parent, GrowableArray* assigned_vars, intptr_t variable_count, intptr_t fixed_parameter_count); virtual LocationSummary* MakeLocationSummary() const; virtual void EmitNativeCode(FlowGraphCompiler* compiler); void EmitBranchOnCondition(FlowGraphCompiler* compiler, Condition true_condition); virtual bool CanDeoptimize() const { return true; } private: intptr_t deopt_id_; ICData* ic_data_; const intptr_t token_pos_; const intptr_t try_index_; Token::Kind kind_; TargetEntryInstr* true_successor_; TargetEntryInstr* false_successor_; DISALLOW_COPY_AND_ASSIGN(BranchInstr); }; #undef DECLARE_INSTRUCTION class Environment : public ZoneAllocated { public: // Construct an environment by constructing uses from an array of definitions. Environment(const GrowableArray& definitions, intptr_t fixed_parameter_count); void set_locations(Location* locations) { ASSERT(locations_ == NULL); locations_ = locations; } const GrowableArray& values() const { return values_; } GrowableArray* values_ptr() { return &values_; } Location LocationAt(intptr_t ix) const { ASSERT((ix >= 0) && (ix < values_.length())); return locations_[ix]; } Location* LocationSlotAt(intptr_t ix) const { ASSERT((ix >= 0) && (ix < values_.length())); return &locations_[ix]; } intptr_t fixed_parameter_count() const { return fixed_parameter_count_; } void PrintTo(BufferFormatter* f) const; private: GrowableArray values_; Location* locations_; const intptr_t fixed_parameter_count_; 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 and computation classes, with empty // default implementations. #define DECLARE_VISIT_COMPUTATION(ShortName, ClassName) \ virtual void Visit##ShortName(ClassName* comp, BindInstr* instr) { } #define DECLARE_VISIT_INSTRUCTION(ShortName) \ virtual void Visit##ShortName(ShortName##Instr* instr) { } FOR_EACH_COMPUTATION(DECLARE_VISIT_COMPUTATION) FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_COMPUTATION #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_