// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_CRANKSHAFT_HYDROGEN_INSTRUCTIONS_H_ #define V8_CRANKSHAFT_HYDROGEN_INSTRUCTIONS_H_ #include <cstring> #include <iosfwd> #include "src/allocation.h" #include "src/ast/ast.h" #include "src/base/bits.h" #include "src/bit-vector.h" #include "src/conversions.h" #include "src/crankshaft/hydrogen-types.h" #include "src/crankshaft/unique.h" #include "src/deoptimizer.h" #include "src/globals.h" #include "src/interface-descriptors.h" #include "src/small-pointer-list.h" #include "src/utils.h" #include "src/zone/zone.h" namespace v8 { namespace internal { // Forward declarations. struct ChangesOf; class HBasicBlock; class HDiv; class HEnvironment; class HInferRepresentationPhase; class HInstruction; class HLoopInformation; class HStoreNamedField; class HValue; class LInstruction; class LChunkBuilder; class SmallMapList; #define HYDROGEN_ABSTRACT_INSTRUCTION_LIST(V) \ V(ArithmeticBinaryOperation) \ V(BinaryOperation) \ V(BitwiseBinaryOperation) \ V(ControlInstruction) \ V(Instruction) #define HYDROGEN_CONCRETE_INSTRUCTION_LIST(V) \ V(AbnormalExit) \ V(AccessArgumentsAt) \ V(Add) \ V(Allocate) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ V(ArgumentsObject) \ V(Bitwise) \ V(BlockEntry) \ V(BoundsCheck) \ V(Branch) \ V(CallWithDescriptor) \ V(CallNewArray) \ V(CallRuntime) \ V(CapturedObject) \ V(Change) \ V(CheckArrayBufferNotNeutered) \ V(CheckHeapObject) \ V(CheckInstanceType) \ V(CheckMaps) \ V(CheckMapValue) \ V(CheckSmi) \ V(CheckValue) \ V(ClampToUint8) \ V(ClassOfTestAndBranch) \ V(CompareNumericAndBranch) \ V(CompareHoleAndBranch) \ V(CompareGeneric) \ V(CompareObjectEqAndBranch) \ V(CompareMap) \ V(Constant) \ V(Context) \ V(DebugBreak) \ V(DeclareGlobals) \ V(Deoptimize) \ V(Div) \ V(DummyUse) \ V(EnterInlined) \ V(EnvironmentMarker) \ V(ForceRepresentation) \ V(ForInCacheArray) \ V(ForInPrepareMap) \ V(Goto) \ V(HasInstanceTypeAndBranch) \ V(InnerAllocatedObject) \ V(InvokeFunction) \ V(HasInPrototypeChainAndBranch) \ V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ V(LeaveInlined) \ V(LoadContextSlot) \ V(LoadFieldByIndex) \ V(LoadFunctionPrototype) \ V(LoadKeyed) \ V(LoadNamedField) \ V(LoadRoot) \ V(MathFloorOfDiv) \ V(MathMinMax) \ V(MaybeGrowElements) \ V(Mod) \ V(Mul) \ V(OsrEntry) \ V(Parameter) \ V(Power) \ V(Prologue) \ V(PushArguments) \ V(Return) \ V(Ror) \ V(Sar) \ V(SeqStringGetChar) \ V(SeqStringSetChar) \ V(Shl) \ V(Shr) \ V(Simulate) \ V(StackCheck) \ V(StoreCodeEntry) \ V(StoreContextSlot) \ V(StoreKeyed) \ V(StoreNamedField) \ V(StringAdd) \ V(StringCharCodeAt) \ V(StringCharFromCode) \ V(StringCompareAndBranch) \ V(Sub) \ V(ThisFunction) \ V(TransitionElementsKind) \ V(TrapAllocationMemento) \ V(Typeof) \ V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ V(UnknownOSRValue) \ V(UseConst) \ V(WrapReceiver) #define GVN_TRACKED_FLAG_LIST(V) \ V(NewSpacePromotion) #define GVN_UNTRACKED_FLAG_LIST(V) \ V(ArrayElements) \ V(ArrayLengths) \ V(StringLengths) \ V(BackingStoreFields) \ V(Calls) \ V(ContextSlots) \ V(DoubleArrayElements) \ V(DoubleFields) \ V(ElementsKind) \ V(ElementsPointer) \ V(GlobalVars) \ V(InobjectFields) \ V(Maps) \ V(OsrEntries) \ V(ExternalMemory) \ V(StringChars) \ V(TypedArrayElements) #define DECLARE_ABSTRACT_INSTRUCTION(type) \ bool Is##type() const final { return true; } \ static H##type* cast(HValue* value) { \ DCHECK(value->Is##type()); \ return reinterpret_cast<H##type*>(value); \ } #define DECLARE_CONCRETE_INSTRUCTION(type) \ LInstruction* CompileToLithium(LChunkBuilder* builder) final; \ static H##type* cast(HValue* value) { \ DCHECK(value->Is##type()); \ return reinterpret_cast<H##type*>(value); \ } \ Opcode opcode() const final { return HValue::k##type; } enum PropertyAccessType { LOAD, STORE }; Representation RepresentationFromMachineType(MachineType type); class Range final : public ZoneObject { public: Range() : lower_(kMinInt), upper_(kMaxInt), next_(NULL), can_be_minus_zero_(false) { } Range(int32_t lower, int32_t upper) : lower_(lower), upper_(upper), next_(NULL), can_be_minus_zero_(false) { } int32_t upper() const { return upper_; } int32_t lower() const { return lower_; } Range* next() const { return next_; } Range* CopyClearLower(Zone* zone) const { return new(zone) Range(kMinInt, upper_); } Range* CopyClearUpper(Zone* zone) const { return new(zone) Range(lower_, kMaxInt); } Range* Copy(Zone* zone) const { Range* result = new(zone) Range(lower_, upper_); result->set_can_be_minus_zero(CanBeMinusZero()); return result; } int32_t Mask() const; void set_can_be_minus_zero(bool b) { can_be_minus_zero_ = b; } bool CanBeMinusZero() const { return CanBeZero() && can_be_minus_zero_; } bool CanBeZero() const { return upper_ >= 0 && lower_ <= 0; } bool CanBeNegative() const { return lower_ < 0; } bool CanBePositive() const { return upper_ > 0; } bool Includes(int value) const { return lower_ <= value && upper_ >= value; } bool IsMostGeneric() const { return lower_ == kMinInt && upper_ == kMaxInt && CanBeMinusZero(); } bool IsInSmiRange() const { return lower_ >= Smi::kMinValue && upper_ <= Smi::kMaxValue; } void ClampToSmi() { lower_ = Max(lower_, Smi::kMinValue); upper_ = Min(upper_, Smi::kMaxValue); } void Clear(); void KeepOrder(); #ifdef DEBUG void Verify() const; #endif void StackUpon(Range* other) { Intersect(other); next_ = other; } void Intersect(Range* other); void Union(Range* other); void CombinedMax(Range* other); void CombinedMin(Range* other); void AddConstant(int32_t value); void Sar(int32_t value); void Shl(int32_t value); bool AddAndCheckOverflow(const Representation& r, Range* other); bool SubAndCheckOverflow(const Representation& r, Range* other); bool MulAndCheckOverflow(const Representation& r, Range* other); private: int32_t lower_; int32_t upper_; Range* next_; bool can_be_minus_zero_; }; class HUseListNode: public ZoneObject { public: HUseListNode(HValue* value, int index, HUseListNode* tail) : tail_(tail), value_(value), index_(index) { } HUseListNode* tail(); HValue* value() const { return value_; } int index() const { return index_; } void set_tail(HUseListNode* list) { tail_ = list; } #ifdef DEBUG void Zap() { tail_ = reinterpret_cast<HUseListNode*>(1); value_ = NULL; index_ = -1; } #endif private: HUseListNode* tail_; HValue* value_; int index_; }; // We reuse use list nodes behind the scenes as uses are added and deleted. // This class is the safe way to iterate uses while deleting them. class HUseIterator final BASE_EMBEDDED { public: bool Done() { return current_ == NULL; } void Advance(); HValue* value() { DCHECK(!Done()); return value_; } int index() { DCHECK(!Done()); return index_; } private: explicit HUseIterator(HUseListNode* head); HUseListNode* current_; HUseListNode* next_; HValue* value_; int index_; friend class HValue; }; // All tracked flags should appear before untracked ones. enum GVNFlag { // Declare global value numbering flags. #define DECLARE_FLAG(Type) k##Type, GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) #undef DECLARE_FLAG #define COUNT_FLAG(Type) + 1 kNumberOfTrackedSideEffects = 0 GVN_TRACKED_FLAG_LIST(COUNT_FLAG), kNumberOfUntrackedSideEffects = 0 GVN_UNTRACKED_FLAG_LIST(COUNT_FLAG), #undef COUNT_FLAG kNumberOfFlags = kNumberOfTrackedSideEffects + kNumberOfUntrackedSideEffects }; static inline GVNFlag GVNFlagFromInt(int i) { DCHECK(i >= 0); DCHECK(i < kNumberOfFlags); return static_cast<GVNFlag>(i); } class DecompositionResult final BASE_EMBEDDED { public: DecompositionResult() : base_(NULL), offset_(0), scale_(0) {} HValue* base() { return base_; } int offset() { return offset_; } int scale() { return scale_; } bool Apply(HValue* other_base, int other_offset, int other_scale = 0) { if (base_ == NULL) { base_ = other_base; offset_ = other_offset; scale_ = other_scale; return true; } else { if (scale_ == 0) { base_ = other_base; offset_ += other_offset; scale_ = other_scale; return true; } else { return false; } } } void SwapValues(HValue** other_base, int* other_offset, int* other_scale) { swap(&base_, other_base); swap(&offset_, other_offset); swap(&scale_, other_scale); } private: template <class T> void swap(T* a, T* b) { T c(*a); *a = *b; *b = c; } HValue* base_; int offset_; int scale_; }; typedef EnumSet<GVNFlag, int32_t> GVNFlagSet; class HValue : public ZoneObject { public: static const int kNoNumber = -1; enum Flag { kFlexibleRepresentation, kCannotBeTagged, // Participate in Global Value Numbering, i.e. elimination of // unnecessary recomputations. If an instruction sets this flag, it must // implement DataEquals(), which will be used to determine if other // occurrences of the instruction are indeed the same. kUseGVN, // Track instructions that are dominating side effects. If an instruction // sets this flag, it must implement HandleSideEffectDominator() and should // indicate which side effects to track by setting GVN flags. kTrackSideEffectDominators, kCanOverflow, kBailoutOnMinusZero, kCanBeDivByZero, kLeftCanBeMinInt, kLeftCanBeNegative, kLeftCanBePositive, kTruncatingToNumber, kIsArguments, kTruncatingToInt32, kAllUsesTruncatingToInt32, kTruncatingToSmi, kAllUsesTruncatingToSmi, // Set after an instruction is killed. kIsDead, // Instructions that are allowed to produce full range unsigned integer // values are marked with kUint32 flag. If arithmetic shift or a load from // EXTERNAL_UINT32_ELEMENTS array is not marked with this flag // it will deoptimize if result does not fit into signed integer range. // HGraph::ComputeSafeUint32Operations is responsible for setting this // flag. kUint32, kHasNoObservableSideEffects, // Indicates an instruction shouldn't be replaced by optimization, this flag // is useful to set in cases where recomputing a value is cheaper than // extending the value's live range and spilling it. kCantBeReplaced, // Indicates the instruction is live during dead code elimination. kIsLive, // HEnvironmentMarkers are deleted before dead code // elimination takes place, so they can repurpose the kIsLive flag: kEndsLiveRange = kIsLive, // TODO(everyone): Don't forget to update this! kLastFlag = kIsLive }; STATIC_ASSERT(kLastFlag < kBitsPerInt); static HValue* cast(HValue* value) { return value; } enum Opcode { // Declare a unique enum value for each hydrogen instruction. #define DECLARE_OPCODE(type) k##type, HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE) kPhi #undef DECLARE_OPCODE }; virtual Opcode opcode() const = 0; // Declare a non-virtual predicates for each concrete HInstruction or HValue. #define DECLARE_PREDICATE(type) \ bool Is##type() const { return opcode() == k##type; } HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE) #undef DECLARE_PREDICATE bool IsPhi() const { return opcode() == kPhi; } // Declare virtual predicates for abstract HInstruction or HValue #define DECLARE_PREDICATE(type) \ virtual bool Is##type() const { return false; } HYDROGEN_ABSTRACT_INSTRUCTION_LIST(DECLARE_PREDICATE) #undef DECLARE_PREDICATE bool IsBitwiseBinaryShift() { return IsShl() || IsShr() || IsSar(); } explicit HValue(HType type = HType::Tagged()) : block_(NULL), id_(kNoNumber), type_(type), use_list_(NULL), range_(NULL), #ifdef DEBUG range_poisoned_(false), #endif flags_(0) {} virtual ~HValue() {} virtual SourcePosition position() const { return SourcePosition::Unknown(); } HBasicBlock* block() const { return block_; } void SetBlock(HBasicBlock* block); // Note: Never call this method for an unlinked value. Isolate* isolate() const; int id() const { return id_; } void set_id(int id) { id_ = id; } HUseIterator uses() const { return HUseIterator(use_list_); } virtual bool EmitAtUses() { return false; } Representation representation() const { return representation_; } void ChangeRepresentation(Representation r) { DCHECK(CheckFlag(kFlexibleRepresentation)); DCHECK(!CheckFlag(kCannotBeTagged) || !r.IsTagged()); RepresentationChanged(r); representation_ = r; if (r.IsTagged()) { // Tagged is the bottom of the lattice, don't go any further. ClearFlag(kFlexibleRepresentation); } } virtual void AssumeRepresentation(Representation r); virtual Representation KnownOptimalRepresentation() { Representation r = representation(); if (r.IsTagged()) { HType t = type(); if (t.IsSmi()) return Representation::Smi(); if (t.IsHeapNumber()) return Representation::Double(); if (t.IsHeapObject()) return r; return Representation::None(); } return r; } HType type() const { return type_; } void set_type(HType new_type) { DCHECK(new_type.IsSubtypeOf(type_)); type_ = new_type; } // There are HInstructions that do not really change a value, they // only add pieces of information to it (like bounds checks, map checks, // smi checks...). // We call these instructions "informative definitions", or "iDef". // One of the iDef operands is special because it is the value that is // "transferred" to the output, we call it the "redefined operand". // If an HValue is an iDef it must override RedefinedOperandIndex() so that // it does not return kNoRedefinedOperand; static const int kNoRedefinedOperand = -1; virtual int RedefinedOperandIndex() { return kNoRedefinedOperand; } bool IsInformativeDefinition() { return RedefinedOperandIndex() != kNoRedefinedOperand; } HValue* RedefinedOperand() { int index = RedefinedOperandIndex(); return index == kNoRedefinedOperand ? NULL : OperandAt(index); } bool CanReplaceWithDummyUses(); virtual int argument_delta() const { return 0; } // A purely informative definition is an idef that will not emit code and // should therefore be removed from the graph in the RestoreActualValues // phase (so that live ranges will be shorter). virtual bool IsPurelyInformativeDefinition() { return false; } // This method must always return the original HValue SSA definition, // regardless of any chain of iDefs of this value. HValue* ActualValue() { HValue* value = this; int index; while ((index = value->RedefinedOperandIndex()) != kNoRedefinedOperand) { value = value->OperandAt(index); } return value; } bool IsInteger32Constant(); int32_t GetInteger32Constant(); bool EqualsInteger32Constant(int32_t value); bool IsDefinedAfter(HBasicBlock* other) const; // Operands. virtual int OperandCount() const = 0; virtual HValue* OperandAt(int index) const = 0; void SetOperandAt(int index, HValue* value); void DeleteAndReplaceWith(HValue* other); void ReplaceAllUsesWith(HValue* other); bool HasNoUses() const { return use_list_ == NULL; } bool HasOneUse() const { return use_list_ != NULL && use_list_->tail() == NULL; } bool HasMultipleUses() const { return use_list_ != NULL && use_list_->tail() != NULL; } int UseCount() const; // Mark this HValue as dead and to be removed from other HValues' use lists. void Kill(); int flags() const { return flags_; } void SetFlag(Flag f) { flags_ |= (1 << f); } void ClearFlag(Flag f) { flags_ &= ~(1 << f); } bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; } void CopyFlag(Flag f, HValue* other) { if (other->CheckFlag(f)) SetFlag(f); } // Returns true if the flag specified is set for all uses, false otherwise. bool CheckUsesForFlag(Flag f) const; // Same as before and the first one without the flag is returned in value. bool CheckUsesForFlag(Flag f, HValue** value) const; // Returns true if the flag specified is set for all uses, and this set // of uses is non-empty. bool HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) const; GVNFlagSet ChangesFlags() const { return changes_flags_; } GVNFlagSet DependsOnFlags() const { return depends_on_flags_; } void SetChangesFlag(GVNFlag f) { changes_flags_.Add(f); } void SetDependsOnFlag(GVNFlag f) { depends_on_flags_.Add(f); } void ClearChangesFlag(GVNFlag f) { changes_flags_.Remove(f); } void ClearDependsOnFlag(GVNFlag f) { depends_on_flags_.Remove(f); } bool CheckChangesFlag(GVNFlag f) const { return changes_flags_.Contains(f); } bool CheckDependsOnFlag(GVNFlag f) const { return depends_on_flags_.Contains(f); } void SetAllSideEffects() { changes_flags_.Add(AllSideEffectsFlagSet()); } void ClearAllSideEffects() { changes_flags_.Remove(AllSideEffectsFlagSet()); } bool HasSideEffects() const { return changes_flags_.ContainsAnyOf(AllSideEffectsFlagSet()); } bool HasObservableSideEffects() const { return !CheckFlag(kHasNoObservableSideEffects) && changes_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet()); } GVNFlagSet SideEffectFlags() const { GVNFlagSet result = ChangesFlags(); result.Intersect(AllSideEffectsFlagSet()); return result; } GVNFlagSet ObservableChangesFlags() const { GVNFlagSet result = ChangesFlags(); result.Intersect(AllObservableSideEffectsFlagSet()); return result; } Range* range() const { DCHECK(!range_poisoned_); return range_; } bool HasRange() const { DCHECK(!range_poisoned_); return range_ != NULL; } #ifdef DEBUG void PoisonRange() { range_poisoned_ = true; } #endif void AddNewRange(Range* r, Zone* zone); void RemoveLastAddedRange(); void ComputeInitialRange(Zone* zone); // Escape analysis helpers. virtual bool HasEscapingOperandAt(int index) { return true; } virtual bool HasOutOfBoundsAccess(int size) { return false; } // Representation helpers. virtual Representation observed_input_representation(int index) { return Representation::None(); } virtual Representation RequiredInputRepresentation(int index) = 0; virtual void InferRepresentation(HInferRepresentationPhase* h_infer); // This gives the instruction an opportunity to replace itself with an // instruction that does the same in some better way. To replace an // instruction with a new one, first add the new instruction to the graph, // then return it. Return NULL to have the instruction deleted. virtual HValue* Canonicalize() { return this; } bool Equals(HValue* other); virtual intptr_t Hashcode(); // Compute unique ids upfront that is safe wrt GC and concurrent compilation. virtual void FinalizeUniqueness() { } // Printing support. virtual std::ostream& PrintTo(std::ostream& os) const = 0; // NOLINT const char* Mnemonic() const; // Type information helpers. bool HasMonomorphicJSObjectType(); // TODO(mstarzinger): For now instructions can override this function to // specify statically known types, once HType can convey more information // it should be based on the HType. virtual Handle<Map> GetMonomorphicJSObjectMap() { return Handle<Map>(); } // Updated the inferred type of this instruction and returns true if // it has changed. bool UpdateInferredType(); virtual HType CalculateInferredType(); // This function must be overridden for instructions which have the // kTrackSideEffectDominators flag set, to track instructions that are // dominating side effects. // It returns true if it removed an instruction which had side effects. virtual bool HandleSideEffectDominator(GVNFlag side_effect, HValue* dominator) { UNREACHABLE(); return false; } // Check if this instruction has some reason that prevents elimination. bool CannotBeEliminated() const { return HasObservableSideEffects() || !IsDeletable(); } #ifdef DEBUG virtual void Verify() = 0; #endif // Returns true conservatively if the program might be able to observe a // ToString() operation on this value. bool ToStringCanBeObserved() const { return ToStringOrToNumberCanBeObserved(); } // Returns true conservatively if the program might be able to observe a // ToNumber() operation on this value. bool ToNumberCanBeObserved() const { return ToStringOrToNumberCanBeObserved(); } MinusZeroMode GetMinusZeroMode() { return CheckFlag(kBailoutOnMinusZero) ? FAIL_ON_MINUS_ZERO : TREAT_MINUS_ZERO_AS_ZERO; } protected: // This function must be overridden for instructions with flag kUseGVN, to // compare the non-Operand parts of the instruction. virtual bool DataEquals(HValue* other) { UNREACHABLE(); return false; } bool ToStringOrToNumberCanBeObserved() const { if (type().IsTaggedPrimitive()) return false; if (type().IsJSReceiver()) return true; return !representation().IsSmiOrInteger32() && !representation().IsDouble(); } virtual Representation RepresentationFromInputs() { return representation(); } virtual Representation RepresentationFromUses(); Representation RepresentationFromUseRequirements(); bool HasNonSmiUse(); virtual void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason); void AddDependantsToWorklist(HInferRepresentationPhase* h_infer); virtual void RepresentationChanged(Representation to) { } virtual Range* InferRange(Zone* zone); virtual void DeleteFromGraph() = 0; virtual void InternalSetOperandAt(int index, HValue* value) = 0; void clear_block() { DCHECK(block_ != NULL); block_ = NULL; } void set_representation(Representation r) { DCHECK(representation_.IsNone() && !r.IsNone()); representation_ = r; } static GVNFlagSet AllFlagSet() { GVNFlagSet result; #define ADD_FLAG(Type) result.Add(k##Type); GVN_TRACKED_FLAG_LIST(ADD_FLAG) GVN_UNTRACKED_FLAG_LIST(ADD_FLAG) #undef ADD_FLAG return result; } // A flag mask to mark an instruction as having arbitrary side effects. static GVNFlagSet AllSideEffectsFlagSet() { GVNFlagSet result = AllFlagSet(); result.Remove(kOsrEntries); return result; } friend std::ostream& operator<<(std::ostream& os, const ChangesOf& v); // A flag mask of all side effects that can make observable changes in // an executing program (i.e. are not safe to repeat, move or remove); static GVNFlagSet AllObservableSideEffectsFlagSet() { GVNFlagSet result = AllFlagSet(); result.Remove(kNewSpacePromotion); result.Remove(kElementsKind); result.Remove(kElementsPointer); result.Remove(kMaps); return result; } // Remove the matching use from the use list if present. Returns the // removed list node or NULL. HUseListNode* RemoveUse(HValue* value, int index); void RegisterUse(int index, HValue* new_value); HBasicBlock* block_; // The id of this instruction in the hydrogen graph, assigned when first // added to the graph. Reflects creation order. int id_; Representation representation_; HType type_; HUseListNode* use_list_; Range* range_; #ifdef DEBUG bool range_poisoned_; #endif int flags_; GVNFlagSet changes_flags_; GVNFlagSet depends_on_flags_; private: virtual bool IsDeletable() const { return false; } DISALLOW_COPY_AND_ASSIGN(HValue); }; // Support for printing various aspects of an HValue. struct NameOf { explicit NameOf(const HValue* const v) : value(v) {} const HValue* value; }; struct TypeOf { explicit TypeOf(const HValue* const v) : value(v) {} const HValue* value; }; struct ChangesOf { explicit ChangesOf(const HValue* const v) : value(v) {} const HValue* value; }; std::ostream& operator<<(std::ostream& os, const HValue& v); std::ostream& operator<<(std::ostream& os, const NameOf& v); std::ostream& operator<<(std::ostream& os, const TypeOf& v); std::ostream& operator<<(std::ostream& os, const ChangesOf& v); #define DECLARE_INSTRUCTION_FACTORY_P0(I) \ static I* New(Isolate* isolate, Zone* zone, HValue* context) { \ return new (zone) I(); \ } #define DECLARE_INSTRUCTION_FACTORY_P1(I, P1) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1) { \ return new (zone) I(p1); \ } #define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2) { \ return new (zone) I(p1, p2); \ } #define DECLARE_INSTRUCTION_FACTORY_P3(I, P1, P2, P3) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3) { \ return new (zone) I(p1, p2, p3); \ } #define DECLARE_INSTRUCTION_FACTORY_P4(I, P1, P2, P3, P4) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4) { \ return new (zone) I(p1, p2, p3, p4); \ } #define DECLARE_INSTRUCTION_FACTORY_P5(I, P1, P2, P3, P4, P5) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4, P5 p5) { \ return new (zone) I(p1, p2, p3, p4, p5); \ } #define DECLARE_INSTRUCTION_FACTORY_P6(I, P1, P2, P3, P4, P5, P6) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4, P5 p5, P6 p6) { \ return new (zone) I(p1, p2, p3, p4, p5, p6); \ } #define DECLARE_INSTRUCTION_FACTORY_P7(I, P1, P2, P3, P4, P5, P6, P7) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) { \ return new (zone) I(p1, p2, p3, p4, p5, p6, p7); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P0(I) \ static I* New(Isolate* isolate, Zone* zone, HValue* context) { \ return new (zone) I(context); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(I, P1) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1) { \ return new (zone) I(context, p1); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(I, P1, P2) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2) { \ return new (zone) I(context, p1, p2); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(I, P1, P2, P3) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3) { \ return new (zone) I(context, p1, p2, p3); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(I, P1, P2, P3, P4) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4) { \ return new (zone) I(context, p1, p2, p3, p4); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(I, P1, P2, P3, P4, P5) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4, P5 p5) { \ return new (zone) I(context, p1, p2, p3, p4, p5); \ } #define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P6(I, P1, P2, P3, P4, P5, P6) \ static I* New(Isolate* isolate, Zone* zone, HValue* context, P1 p1, P2 p2, \ P3 p3, P4 p4, P5 p5, P6 p6) { \ return new (zone) I(context, p1, p2, p3, p4, p5, p6); \ } class HInstruction : public HValue { public: HInstruction* next() const { return next_; } HInstruction* previous() const { return previous_; } std::ostream& PrintTo(std::ostream& os) const override; // NOLINT virtual std::ostream& PrintDataTo(std::ostream& os) const; // NOLINT bool IsLinked() const { return block() != NULL; } void Unlink(); void InsertBefore(HInstruction* next); template<class T> T* Prepend(T* instr) { instr->InsertBefore(this); return instr; } void InsertAfter(HInstruction* previous); template<class T> T* Append(T* instr) { instr->InsertAfter(this); return instr; } // The position is a write-once variable. SourcePosition position() const override { return position_; } bool has_position() const { return position_.IsKnown(); } void set_position(SourcePosition position) { DCHECK(position.IsKnown()); position_ = position; } bool Dominates(HInstruction* other); bool CanTruncateToSmi() const { return CheckFlag(kTruncatingToSmi); } bool CanTruncateToInt32() const { return CheckFlag(kTruncatingToInt32); } bool CanTruncateToNumber() const { return CheckFlag(kTruncatingToNumber); } virtual LInstruction* CompileToLithium(LChunkBuilder* builder) = 0; #ifdef DEBUG void Verify() override; #endif bool CanDeoptimize(); virtual bool HasStackCheck() { return false; } DECLARE_ABSTRACT_INSTRUCTION(Instruction) protected: explicit HInstruction(HType type = HType::Tagged()) : HValue(type), next_(NULL), previous_(NULL), position_(SourcePosition::Unknown()) { SetDependsOnFlag(kOsrEntries); } void DeleteFromGraph() override { Unlink(); } private: void InitializeAsFirst(HBasicBlock* block) { DCHECK(!IsLinked()); SetBlock(block); } HInstruction* next_; HInstruction* previous_; SourcePosition position_; friend class HBasicBlock; }; template<int V> class HTemplateInstruction : public HInstruction { public: int OperandCount() const final { return V; } HValue* OperandAt(int i) const final { return inputs_[i]; } protected: explicit HTemplateInstruction(HType type = HType::Tagged()) : HInstruction(type) {} void InternalSetOperandAt(int i, HValue* value) final { inputs_[i] = value; } private: EmbeddedContainer<HValue*, V> inputs_; }; class HControlInstruction : public HInstruction { public: virtual HBasicBlock* SuccessorAt(int i) const = 0; virtual int SuccessorCount() const = 0; virtual void SetSuccessorAt(int i, HBasicBlock* block) = 0; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT virtual bool KnownSuccessorBlock(HBasicBlock** block) { *block = NULL; return false; } HBasicBlock* FirstSuccessor() { return SuccessorCount() > 0 ? SuccessorAt(0) : NULL; } HBasicBlock* SecondSuccessor() { return SuccessorCount() > 1 ? SuccessorAt(1) : NULL; } void Not() { HBasicBlock* swap = SuccessorAt(0); SetSuccessorAt(0, SuccessorAt(1)); SetSuccessorAt(1, swap); } DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction) }; class HSuccessorIterator final BASE_EMBEDDED { public: explicit HSuccessorIterator(const HControlInstruction* instr) : instr_(instr), current_(0) {} bool Done() { return current_ >= instr_->SuccessorCount(); } HBasicBlock* Current() { return instr_->SuccessorAt(current_); } void Advance() { current_++; } private: const HControlInstruction* instr_; int current_; }; template<int S, int V> class HTemplateControlInstruction : public HControlInstruction { public: int SuccessorCount() const override { return S; } HBasicBlock* SuccessorAt(int i) const override { return successors_[i]; } void SetSuccessorAt(int i, HBasicBlock* block) override { successors_[i] = block; } int OperandCount() const override { return V; } HValue* OperandAt(int i) const override { return inputs_[i]; } protected: void InternalSetOperandAt(int i, HValue* value) override { inputs_[i] = value; } private: EmbeddedContainer<HBasicBlock*, S> successors_; EmbeddedContainer<HValue*, V> inputs_; }; class HBlockEntry final : public HTemplateInstruction<0> { public: Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(BlockEntry) }; class HDummyUse final : public HTemplateInstruction<1> { public: explicit HDummyUse(HValue* value) : HTemplateInstruction<1>(HType::Smi()) { SetOperandAt(0, value); // Pretend to be a Smi so that the HChange instructions inserted // before any use generate as little code as possible. set_representation(Representation::Tagged()); } HValue* value() const { return OperandAt(0); } bool HasEscapingOperandAt(int index) override { return false; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(DummyUse); }; // Inserts an int3/stop break instruction for debugging purposes. class HDebugBreak final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P0(HDebugBreak); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(DebugBreak) }; class HPrologue final : public HTemplateInstruction<0> { public: static HPrologue* New(Zone* zone) { return new (zone) HPrologue(); } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(Prologue) }; class HGoto final : public HTemplateControlInstruction<1, 0> { public: explicit HGoto(HBasicBlock* target) { SetSuccessorAt(0, target); } bool KnownSuccessorBlock(HBasicBlock** block) override { *block = FirstSuccessor(); return true; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(Goto) }; class HDeoptimize final : public HTemplateControlInstruction<1, 0> { public: static HDeoptimize* New(Isolate* isolate, Zone* zone, HValue* context, DeoptimizeReason reason, Deoptimizer::BailoutType type, HBasicBlock* unreachable_continuation) { return new(zone) HDeoptimize(reason, type, unreachable_continuation); } bool KnownSuccessorBlock(HBasicBlock** block) override { *block = NULL; return true; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DeoptimizeReason reason() const { return reason_; } Deoptimizer::BailoutType type() { return type_; } DECLARE_CONCRETE_INSTRUCTION(Deoptimize) private: explicit HDeoptimize(DeoptimizeReason reason, Deoptimizer::BailoutType type, HBasicBlock* unreachable_continuation) : reason_(reason), type_(type) { SetSuccessorAt(0, unreachable_continuation); } DeoptimizeReason reason_; Deoptimizer::BailoutType type_; }; class HUnaryControlInstruction : public HTemplateControlInstruction<2, 1> { public: HUnaryControlInstruction(HValue* value, HBasicBlock* true_target, HBasicBlock* false_target) { SetOperandAt(0, value); SetSuccessorAt(0, true_target); SetSuccessorAt(1, false_target); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* value() const { return OperandAt(0); } }; class HBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P1(HBranch, HValue*); DECLARE_INSTRUCTION_FACTORY_P2(HBranch, HValue*, ToBooleanHints); DECLARE_INSTRUCTION_FACTORY_P4(HBranch, HValue*, ToBooleanHints, HBasicBlock*, HBasicBlock*); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } Representation observed_input_representation(int index) override; bool KnownSuccessorBlock(HBasicBlock** block) override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT ToBooleanHints expected_input_types() const { return expected_input_types_; } DECLARE_CONCRETE_INSTRUCTION(Branch) private: HBranch(HValue* value, ToBooleanHints expected_input_types = ToBooleanHint::kNone, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target), expected_input_types_(expected_input_types) {} ToBooleanHints expected_input_types_; }; class HCompareMap final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P2(HCompareMap, HValue*, Handle<Map>); DECLARE_INSTRUCTION_FACTORY_P4(HCompareMap, HValue*, Handle<Map>, HBasicBlock*, HBasicBlock*); bool KnownSuccessorBlock(HBasicBlock** block) override { if (known_successor_index() != kNoKnownSuccessorIndex) { *block = SuccessorAt(known_successor_index()); return true; } *block = NULL; return false; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT static const int kNoKnownSuccessorIndex = -1; int known_successor_index() const { return KnownSuccessorIndexField::decode(bit_field_) - kInternalKnownSuccessorOffset; } void set_known_successor_index(int index) { DCHECK(index >= 0 - kInternalKnownSuccessorOffset); bit_field_ = KnownSuccessorIndexField::update( bit_field_, index + kInternalKnownSuccessorOffset); } Unique<Map> map() const { return map_; } bool map_is_stable() const { return MapIsStableField::decode(bit_field_); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(CompareMap) protected: int RedefinedOperandIndex() override { return 0; } private: HCompareMap(HValue* value, Handle<Map> map, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target), bit_field_(KnownSuccessorIndexField::encode( kNoKnownSuccessorIndex + kInternalKnownSuccessorOffset) | MapIsStableField::encode(map->is_stable())), map_(Unique<Map>::CreateImmovable(map)) { set_representation(Representation::Tagged()); } // BitFields can only store unsigned values, so use an offset. // Adding kInternalKnownSuccessorOffset must yield an unsigned value. static const int kInternalKnownSuccessorOffset = 1; STATIC_ASSERT(kNoKnownSuccessorIndex + kInternalKnownSuccessorOffset >= 0); class KnownSuccessorIndexField : public BitField<int, 0, 31> {}; class MapIsStableField : public BitField<bool, 31, 1> {}; uint32_t bit_field_; Unique<Map> map_; }; class HContext final : public HTemplateInstruction<0> { public: static HContext* New(Zone* zone) { return new(zone) HContext(); } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(Context) protected: bool DataEquals(HValue* other) override { return true; } private: HContext() { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } bool IsDeletable() const override { return true; } }; class HReturn final : public HTemplateControlInstruction<0, 3> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HReturn, HValue*, HValue*); DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HReturn, HValue*); Representation RequiredInputRepresentation(int index) override { // TODO(titzer): require an Int32 input for faster returns. if (index == 2) return Representation::Smi(); return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* value() const { return OperandAt(0); } HValue* context() const { return OperandAt(1); } HValue* parameter_count() const { return OperandAt(2); } DECLARE_CONCRETE_INSTRUCTION(Return) private: HReturn(HValue* context, HValue* value, HValue* parameter_count = 0) { SetOperandAt(0, value); SetOperandAt(1, context); SetOperandAt(2, parameter_count); } }; class HAbnormalExit final : public HTemplateControlInstruction<0, 0> { public: DECLARE_INSTRUCTION_FACTORY_P0(HAbnormalExit); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(AbnormalExit) private: HAbnormalExit() {} }; class HUnaryOperation : public HTemplateInstruction<1> { public: explicit HUnaryOperation(HValue* value, HType type = HType::Tagged()) : HTemplateInstruction<1>(type) { SetOperandAt(0, value); } static HUnaryOperation* cast(HValue* value) { return reinterpret_cast<HUnaryOperation*>(value); } HValue* value() const { return OperandAt(0); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT }; class HUseConst final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HUseConst, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(UseConst) private: explicit HUseConst(HValue* old_value) : HUnaryOperation(old_value) { } }; class HForceRepresentation final : public HTemplateInstruction<1> { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, Representation required_representation); HValue* value() const { return OperandAt(0); } Representation observed_input_representation(int index) override { // We haven't actually *observed* this, but it's closer to the truth // than 'None'. return representation(); // Same as the output representation. } Representation RequiredInputRepresentation(int index) override { return representation(); // Same as the output representation. } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(ForceRepresentation) private: HForceRepresentation(HValue* value, Representation required_representation) { SetOperandAt(0, value); set_representation(required_representation); } }; class HChange final : public HUnaryOperation { public: HChange(HValue* value, Representation to, bool is_truncating_to_smi, bool is_truncating_to_int32, bool is_truncating_to_number) : HUnaryOperation(value) { DCHECK(!value->representation().IsNone()); DCHECK(!to.IsNone()); DCHECK(!value->representation().Equals(to)); set_representation(to); SetFlag(kUseGVN); SetFlag(kCanOverflow); if (is_truncating_to_smi && to.IsSmi()) { SetFlag(kTruncatingToSmi); SetFlag(kTruncatingToInt32); SetFlag(kTruncatingToNumber); } else if (is_truncating_to_int32) { SetFlag(kTruncatingToInt32); SetFlag(kTruncatingToNumber); } else if (is_truncating_to_number) { SetFlag(kTruncatingToNumber); } if (value->representation().IsSmi() || value->type().IsSmi()) { set_type(HType::Smi()); } else { set_type(HType::TaggedNumber()); if (to.IsTagged()) SetChangesFlag(kNewSpacePromotion); } } HType CalculateInferredType() override; HValue* Canonicalize() override; Representation from() const { return value()->representation(); } Representation to() const { return representation(); } bool deoptimize_on_minus_zero() const { return CheckFlag(kBailoutOnMinusZero); } Representation RequiredInputRepresentation(int index) override { return from(); } Range* InferRange(Zone* zone) override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(Change) protected: bool DataEquals(HValue* other) override { return true; } private: bool IsDeletable() const override { return !from().IsTagged() || value()->type().IsSmi(); } }; class HClampToUint8 final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HClampToUint8, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(ClampToUint8) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HClampToUint8(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Integer32()); SetFlag(kTruncatingToNumber); SetFlag(kUseGVN); } bool IsDeletable() const override { return true; } }; enum RemovableSimulate { REMOVABLE_SIMULATE, FIXED_SIMULATE }; class HSimulate final : public HInstruction { public: HSimulate(BailoutId ast_id, int pop_count, Zone* zone, RemovableSimulate removable) : ast_id_(ast_id), pop_count_(pop_count), values_(2, zone), assigned_indexes_(2, zone), zone_(zone), bit_field_(RemovableField::encode(removable) | DoneWithReplayField::encode(false)) {} ~HSimulate() {} std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT bool HasAstId() const { return !ast_id_.IsNone(); } BailoutId ast_id() const { return ast_id_; } void set_ast_id(BailoutId id) { DCHECK(!HasAstId()); ast_id_ = id; } int pop_count() const { return pop_count_; } const ZoneList<HValue*>* values() const { return &values_; } int GetAssignedIndexAt(int index) const { DCHECK(HasAssignedIndexAt(index)); return assigned_indexes_[index]; } bool HasAssignedIndexAt(int index) const { return assigned_indexes_[index] != kNoIndex; } void AddAssignedValue(int index, HValue* value) { AddValue(index, value); } void AddPushedValue(HValue* value) { AddValue(kNoIndex, value); } int ToOperandIndex(int environment_index) { for (int i = 0; i < assigned_indexes_.length(); ++i) { if (assigned_indexes_[i] == environment_index) return i; } return -1; } int OperandCount() const override { return values_.length(); } HValue* OperandAt(int index) const override { return values_[index]; } bool HasEscapingOperandAt(int index) override { return false; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } void MergeWith(ZoneList<HSimulate*>* list); bool is_candidate_for_removal() { return RemovableField::decode(bit_field_) == REMOVABLE_SIMULATE; } // Replay effects of this instruction on the given environment. void ReplayEnvironment(HEnvironment* env); DECLARE_CONCRETE_INSTRUCTION(Simulate) #ifdef DEBUG void Verify() override; void set_closure(Handle<JSFunction> closure) { closure_ = closure; } Handle<JSFunction> closure() const { return closure_; } #endif protected: void InternalSetOperandAt(int index, HValue* value) override { values_[index] = value; } private: static const int kNoIndex = -1; void AddValue(int index, HValue* value) { assigned_indexes_.Add(index, zone_); // Resize the list of pushed values. values_.Add(NULL, zone_); // Set the operand through the base method in HValue to make sure that the // use lists are correctly updated. SetOperandAt(values_.length() - 1, value); } bool HasValueForIndex(int index) { for (int i = 0; i < assigned_indexes_.length(); ++i) { if (assigned_indexes_[i] == index) return true; } return false; } bool is_done_with_replay() const { return DoneWithReplayField::decode(bit_field_); } void set_done_with_replay() { bit_field_ = DoneWithReplayField::update(bit_field_, true); } class RemovableField : public BitField<RemovableSimulate, 0, 1> {}; class DoneWithReplayField : public BitField<bool, 1, 1> {}; BailoutId ast_id_; int pop_count_; ZoneList<HValue*> values_; ZoneList<int> assigned_indexes_; Zone* zone_; uint32_t bit_field_; #ifdef DEBUG Handle<JSFunction> closure_; #endif }; class HEnvironmentMarker final : public HTemplateInstruction<1> { public: enum Kind { BIND, LOOKUP }; DECLARE_INSTRUCTION_FACTORY_P2(HEnvironmentMarker, Kind, int); Kind kind() const { return kind_; } int index() const { return index_; } HSimulate* next_simulate() { return next_simulate_; } void set_next_simulate(HSimulate* simulate) { next_simulate_ = simulate; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT #ifdef DEBUG void set_closure(Handle<JSFunction> closure) { DCHECK(closure_.is_null()); DCHECK(!closure.is_null()); closure_ = closure; } Handle<JSFunction> closure() const { return closure_; } #endif DECLARE_CONCRETE_INSTRUCTION(EnvironmentMarker); private: HEnvironmentMarker(Kind kind, int index) : kind_(kind), index_(index), next_simulate_(NULL) { } Kind kind_; int index_; HSimulate* next_simulate_; #ifdef DEBUG Handle<JSFunction> closure_; #endif }; class HStackCheck final : public HTemplateInstruction<1> { public: enum Type { kFunctionEntry, kBackwardsBranch }; DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HStackCheck, Type); HValue* context() { return OperandAt(0); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } void Eliminate() { // The stack check eliminator might try to eliminate the same stack // check instruction multiple times. if (IsLinked()) { DeleteAndReplaceWith(NULL); } } bool is_function_entry() { return type_ == kFunctionEntry; } bool is_backwards_branch() { return type_ == kBackwardsBranch; } DECLARE_CONCRETE_INSTRUCTION(StackCheck) private: HStackCheck(HValue* context, Type type) : type_(type) { SetOperandAt(0, context); SetChangesFlag(kNewSpacePromotion); } Type type_; }; enum InliningKind { NORMAL_RETURN, // Drop the function from the environment on return. CONSTRUCT_CALL_RETURN, // Either use allocated receiver or return value. GETTER_CALL_RETURN, // Returning from a getter, need to restore context. SETTER_CALL_RETURN // Use the RHS of the assignment as the return value. }; class HArgumentsObject; class HConstant; class HEnterInlined final : public HTemplateInstruction<0> { public: static HEnterInlined* New(Isolate* isolate, Zone* zone, HValue* context, BailoutId return_id, Handle<JSFunction> closure, HConstant* closure_context, int arguments_count, FunctionLiteral* function, InliningKind inlining_kind, Variable* arguments_var, HArgumentsObject* arguments_object, TailCallMode syntactic_tail_call_mode) { return new (zone) HEnterInlined(return_id, closure, closure_context, arguments_count, function, inlining_kind, arguments_var, arguments_object, syntactic_tail_call_mode, zone); } void RegisterReturnTarget(HBasicBlock* return_target, Zone* zone); ZoneList<HBasicBlock*>* return_targets() { return &return_targets_; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Handle<SharedFunctionInfo> shared() const { return shared_; } Handle<JSFunction> closure() const { return closure_; } HConstant* closure_context() const { return closure_context_; } int arguments_count() const { return arguments_count_; } bool arguments_pushed() const { return arguments_pushed_; } void set_arguments_pushed() { arguments_pushed_ = true; } FunctionLiteral* function() const { return function_; } InliningKind inlining_kind() const { return inlining_kind_; } TailCallMode syntactic_tail_call_mode() const { return syntactic_tail_call_mode_; } BailoutId ReturnId() const { return return_id_; } int inlining_id() const { return inlining_id_; } void set_inlining_id(int inlining_id) { inlining_id_ = inlining_id; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } Variable* arguments_var() { return arguments_var_; } HArgumentsObject* arguments_object() { return arguments_object_; } DECLARE_CONCRETE_INSTRUCTION(EnterInlined) private: HEnterInlined(BailoutId return_id, Handle<JSFunction> closure, HConstant* closure_context, int arguments_count, FunctionLiteral* function, InliningKind inlining_kind, Variable* arguments_var, HArgumentsObject* arguments_object, TailCallMode syntactic_tail_call_mode, Zone* zone) : return_id_(return_id), shared_(handle(closure->shared())), closure_(closure), closure_context_(closure_context), arguments_count_(arguments_count), arguments_pushed_(false), function_(function), inlining_kind_(inlining_kind), syntactic_tail_call_mode_(syntactic_tail_call_mode), inlining_id_(-1), arguments_var_(arguments_var), arguments_object_(arguments_object), return_targets_(2, zone) {} BailoutId return_id_; Handle<SharedFunctionInfo> shared_; Handle<JSFunction> closure_; HConstant* closure_context_; int arguments_count_; bool arguments_pushed_; FunctionLiteral* function_; InliningKind inlining_kind_; TailCallMode syntactic_tail_call_mode_; int inlining_id_; Variable* arguments_var_; HArgumentsObject* arguments_object_; ZoneList<HBasicBlock*> return_targets_; }; class HLeaveInlined final : public HTemplateInstruction<0> { public: HLeaveInlined(HEnterInlined* entry, int drop_count) : entry_(entry), drop_count_(drop_count) { } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } int argument_delta() const override { return entry_->arguments_pushed() ? -drop_count_ : 0; } DECLARE_CONCRETE_INSTRUCTION(LeaveInlined) private: HEnterInlined* entry_; int drop_count_; }; class HPushArguments final : public HInstruction { public: static HPushArguments* New(Isolate* isolate, Zone* zone, HValue* context) { return new(zone) HPushArguments(zone); } static HPushArguments* New(Isolate* isolate, Zone* zone, HValue* context, HValue* arg1) { HPushArguments* instr = new(zone) HPushArguments(zone); instr->AddInput(arg1); return instr; } static HPushArguments* New(Isolate* isolate, Zone* zone, HValue* context, HValue* arg1, HValue* arg2) { HPushArguments* instr = new(zone) HPushArguments(zone); instr->AddInput(arg1); instr->AddInput(arg2); return instr; } static HPushArguments* New(Isolate* isolate, Zone* zone, HValue* context, HValue* arg1, HValue* arg2, HValue* arg3) { HPushArguments* instr = new(zone) HPushArguments(zone); instr->AddInput(arg1); instr->AddInput(arg2); instr->AddInput(arg3); return instr; } static HPushArguments* New(Isolate* isolate, Zone* zone, HValue* context, HValue* arg1, HValue* arg2, HValue* arg3, HValue* arg4) { HPushArguments* instr = new(zone) HPushArguments(zone); instr->AddInput(arg1); instr->AddInput(arg2); instr->AddInput(arg3); instr->AddInput(arg4); return instr; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } int argument_delta() const override { return inputs_.length(); } HValue* argument(int i) { return OperandAt(i); } int OperandCount() const final { return inputs_.length(); } HValue* OperandAt(int i) const final { return inputs_[i]; } void AddInput(HValue* value); DECLARE_CONCRETE_INSTRUCTION(PushArguments) protected: void InternalSetOperandAt(int i, HValue* value) final { inputs_[i] = value; } private: explicit HPushArguments(Zone* zone) : HInstruction(HType::Tagged()), inputs_(4, zone) { set_representation(Representation::Tagged()); } ZoneList<HValue*> inputs_; }; class HThisFunction final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P0(HThisFunction); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(ThisFunction) protected: bool DataEquals(HValue* other) override { return true; } private: HThisFunction() { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } bool IsDeletable() const override { return true; } }; class HDeclareGlobals final : public HUnaryOperation { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HDeclareGlobals, Handle<FixedArray>, int, Handle<TypeFeedbackVector>); HValue* context() { return OperandAt(0); } Handle<FixedArray> pairs() const { return pairs_; } int flags() const { return flags_; } Handle<TypeFeedbackVector> feedback_vector() const { return feedback_vector_; } DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals) Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } private: HDeclareGlobals(HValue* context, Handle<FixedArray> pairs, int flags, Handle<TypeFeedbackVector> feedback_vector) : HUnaryOperation(context), pairs_(pairs), feedback_vector_(feedback_vector), flags_(flags) { set_representation(Representation::Tagged()); SetAllSideEffects(); } Handle<FixedArray> pairs_; Handle<TypeFeedbackVector> feedback_vector_; int flags_; }; template <int V> class HCall : public HTemplateInstruction<V> { public: // The argument count includes the receiver. explicit HCall<V>(int argument_count) : argument_count_(argument_count) { this->set_representation(Representation::Tagged()); this->SetAllSideEffects(); } virtual int argument_count() const { return argument_count_; } int argument_delta() const override { return -argument_count(); } private: int argument_count_; }; class HUnaryCall : public HCall<1> { public: HUnaryCall(HValue* value, int argument_count) : HCall<1>(argument_count) { SetOperandAt(0, value); } Representation RequiredInputRepresentation(int index) final { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* value() const { return OperandAt(0); } }; class HBinaryCall : public HCall<2> { public: HBinaryCall(HValue* first, HValue* second, int argument_count) : HCall<2>(argument_count) { SetOperandAt(0, first); SetOperandAt(1, second); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) final { return Representation::Tagged(); } HValue* first() const { return OperandAt(0); } HValue* second() const { return OperandAt(1); } }; class HCallWithDescriptor final : public HInstruction { public: static HCallWithDescriptor* New( Isolate* isolate, Zone* zone, HValue* context, HValue* target, int argument_count, CallInterfaceDescriptor descriptor, const Vector<HValue*>& operands, TailCallMode syntactic_tail_call_mode = TailCallMode::kDisallow, TailCallMode tail_call_mode = TailCallMode::kDisallow) { HCallWithDescriptor* res = new (zone) HCallWithDescriptor( Code::STUB, context, target, argument_count, descriptor, operands, syntactic_tail_call_mode, tail_call_mode, zone); return res; } static HCallWithDescriptor* New( Isolate* isolate, Zone* zone, HValue* context, Code::Kind kind, HValue* target, int argument_count, CallInterfaceDescriptor descriptor, const Vector<HValue*>& operands, TailCallMode syntactic_tail_call_mode = TailCallMode::kDisallow, TailCallMode tail_call_mode = TailCallMode::kDisallow) { HCallWithDescriptor* res = new (zone) HCallWithDescriptor( kind, context, target, argument_count, descriptor, operands, syntactic_tail_call_mode, tail_call_mode, zone); return res; } int OperandCount() const final { return values_.length(); } HValue* OperandAt(int index) const final { return values_[index]; } Representation RequiredInputRepresentation(int index) final { if (index == 0 || index == 1) { // Target + context return Representation::Tagged(); } else { int par_index = index - 2; DCHECK(par_index < GetParameterCount()); return RepresentationFromMachineType( descriptor_.GetParameterType(par_index)); } } DECLARE_CONCRETE_INSTRUCTION(CallWithDescriptor) // Defines whether this instruction corresponds to a JS call at tail position. TailCallMode syntactic_tail_call_mode() const { return SyntacticTailCallModeField::decode(bit_field_); } // Defines whether this call should be generated as a tail call. TailCallMode tail_call_mode() const { return TailCallModeField::decode(bit_field_); } bool IsTailCall() const { return tail_call_mode() == TailCallMode::kAllow; } Code::Kind kind() const { return KindField::decode(bit_field_); } virtual int argument_count() const { return argument_count_; } int argument_delta() const override { return -argument_count_; } CallInterfaceDescriptor descriptor() const { return descriptor_; } HValue* target() { return OperandAt(0); } HValue* context() { return OperandAt(1); } HValue* parameter(int index) { DCHECK_LT(index, GetParameterCount()); return OperandAt(index + 2); } HValue* Canonicalize() override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT private: // The argument count includes the receiver. HCallWithDescriptor(Code::Kind kind, HValue* context, HValue* target, int argument_count, CallInterfaceDescriptor descriptor, const Vector<HValue*>& operands, TailCallMode syntactic_tail_call_mode, TailCallMode tail_call_mode, Zone* zone) : descriptor_(descriptor), values_(GetParameterCount() + 2, zone), // +2 for context and target. argument_count_(argument_count), bit_field_( TailCallModeField::encode(tail_call_mode) | SyntacticTailCallModeField::encode(syntactic_tail_call_mode) | KindField::encode(kind)) { DCHECK_EQ(operands.length(), GetParameterCount()); // We can only tail call without any stack arguments. DCHECK(tail_call_mode != TailCallMode::kAllow || argument_count == 0); AddOperand(target, zone); AddOperand(context, zone); for (int i = 0; i < operands.length(); i++) { AddOperand(operands[i], zone); } this->set_representation(Representation::Tagged()); this->SetAllSideEffects(); } void AddOperand(HValue* v, Zone* zone) { values_.Add(NULL, zone); SetOperandAt(values_.length() - 1, v); } int GetParameterCount() const { return descriptor_.GetParameterCount(); } void InternalSetOperandAt(int index, HValue* value) final { values_[index] = value; } CallInterfaceDescriptor descriptor_; ZoneList<HValue*> values_; int argument_count_; class TailCallModeField : public BitField<TailCallMode, 0, 1> {}; class SyntacticTailCallModeField : public BitField<TailCallMode, TailCallModeField::kNext, 1> {}; class KindField : public BitField<Code::Kind, SyntacticTailCallModeField::kNext, 5> {}; uint32_t bit_field_; }; class HInvokeFunction final : public HBinaryCall { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(HInvokeFunction, HValue*, Handle<JSFunction>, int, TailCallMode, TailCallMode); HValue* context() { return first(); } HValue* function() { return second(); } Handle<JSFunction> known_function() { return known_function_; } int formal_parameter_count() const { return formal_parameter_count_; } bool HasStackCheck() final { return HasStackCheckField::decode(bit_field_); } // Defines whether this instruction corresponds to a JS call at tail position. TailCallMode syntactic_tail_call_mode() const { return SyntacticTailCallModeField::decode(bit_field_); } // Defines whether this call should be generated as a tail call. TailCallMode tail_call_mode() const { return TailCallModeField::decode(bit_field_); } DECLARE_CONCRETE_INSTRUCTION(InvokeFunction) std::ostream& PrintTo(std::ostream& os) const override; // NOLINT std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT private: void set_has_stack_check(bool has_stack_check) { bit_field_ = HasStackCheckField::update(bit_field_, has_stack_check); } HInvokeFunction(HValue* context, HValue* function, Handle<JSFunction> known_function, int argument_count, TailCallMode syntactic_tail_call_mode, TailCallMode tail_call_mode) : HBinaryCall(context, function, argument_count), known_function_(known_function), bit_field_( TailCallModeField::encode(tail_call_mode) | SyntacticTailCallModeField::encode(syntactic_tail_call_mode)) { DCHECK(tail_call_mode != TailCallMode::kAllow || syntactic_tail_call_mode == TailCallMode::kAllow); formal_parameter_count_ = known_function.is_null() ? 0 : known_function->shared()->internal_formal_parameter_count(); set_has_stack_check( !known_function.is_null() && (known_function->code()->kind() == Code::FUNCTION || known_function->code()->kind() == Code::OPTIMIZED_FUNCTION)); } Handle<JSFunction> known_function_; int formal_parameter_count_; class HasStackCheckField : public BitField<bool, 0, 1> {}; class TailCallModeField : public BitField<TailCallMode, HasStackCheckField::kNext, 1> {}; class SyntacticTailCallModeField : public BitField<TailCallMode, TailCallModeField::kNext, 1> {}; uint32_t bit_field_; }; class HCallNewArray final : public HBinaryCall { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(HCallNewArray, HValue*, int, ElementsKind, Handle<AllocationSite>); HValue* context() { return first(); } HValue* constructor() { return second(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT ElementsKind elements_kind() const { return elements_kind_; } Handle<AllocationSite> site() const { return site_; } DECLARE_CONCRETE_INSTRUCTION(CallNewArray) private: HCallNewArray(HValue* context, HValue* constructor, int argument_count, ElementsKind elements_kind, Handle<AllocationSite> site) : HBinaryCall(context, constructor, argument_count), elements_kind_(elements_kind), site_(site) {} ElementsKind elements_kind_; Handle<AllocationSite> site_; }; class HCallRuntime final : public HCall<1> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HCallRuntime, const Runtime::Function*, int); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* context() { return OperandAt(0); } const Runtime::Function* function() const { return c_function_; } SaveFPRegsMode save_doubles() const { return save_doubles_; } void set_save_doubles(SaveFPRegsMode save_doubles) { save_doubles_ = save_doubles; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(CallRuntime) private: HCallRuntime(HValue* context, const Runtime::Function* c_function, int argument_count) : HCall<1>(argument_count), c_function_(c_function), save_doubles_(kDontSaveFPRegs) { SetOperandAt(0, context); } const Runtime::Function* c_function_; SaveFPRegsMode save_doubles_; }; class HUnaryMathOperation final : public HTemplateInstruction<2> { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, BuiltinFunctionId op); HValue* context() const { return OperandAt(0); } HValue* value() const { return OperandAt(1); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { if (index == 0) { return Representation::Tagged(); } else { switch (op_) { case kMathCos: case kMathFloor: case kMathRound: case kMathFround: case kMathSin: case kMathSqrt: case kMathPowHalf: case kMathLog: case kMathExp: return Representation::Double(); case kMathAbs: return representation(); case kMathClz32: return Representation::Integer32(); default: UNREACHABLE(); return Representation::None(); } } } Range* InferRange(Zone* zone) override; HValue* Canonicalize() override; Representation RepresentationFromUses() override; Representation RepresentationFromInputs() override; BuiltinFunctionId op() const { return op_; } const char* OpName() const; DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation) protected: bool DataEquals(HValue* other) override { HUnaryMathOperation* b = HUnaryMathOperation::cast(other); return op_ == b->op(); } private: // Indicates if we support a double (and int32) output for Math.floor and // Math.round. bool SupportsFlexibleFloorAndRound() const { #if V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_PPC return true; #elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 return CpuFeatures::IsSupported(SSE4_1); #else return false; #endif } HUnaryMathOperation(HValue* context, HValue* value, BuiltinFunctionId op) : HTemplateInstruction<2>(HType::TaggedNumber()), op_(op) { SetOperandAt(0, context); SetOperandAt(1, value); switch (op) { case kMathFloor: case kMathRound: if (SupportsFlexibleFloorAndRound()) { SetFlag(kFlexibleRepresentation); } else { set_representation(Representation::Integer32()); } break; case kMathClz32: set_representation(Representation::Integer32()); break; case kMathAbs: // Not setting representation here: it is None intentionally. SetFlag(kFlexibleRepresentation); // TODO(svenpanne) This flag is actually only needed if representation() // is tagged, and not when it is an unboxed double or unboxed integer. SetChangesFlag(kNewSpacePromotion); break; case kMathCos: case kMathFround: case kMathLog: case kMathExp: case kMathSin: case kMathSqrt: case kMathPowHalf: set_representation(Representation::Double()); break; default: UNREACHABLE(); } SetFlag(kUseGVN); SetFlag(kTruncatingToNumber); } bool IsDeletable() const override { // TODO(crankshaft): This should be true, however the semantics of this // instruction also include the ToNumber conversion that is mentioned in the // spec, which is of course observable. return false; } HValue* SimplifiedDividendForMathFloorOfDiv(HDiv* hdiv); HValue* SimplifiedDivisorForMathFloorOfDiv(HDiv* hdiv); BuiltinFunctionId op_; }; class HLoadRoot final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P1(HLoadRoot, Heap::RootListIndex); DECLARE_INSTRUCTION_FACTORY_P2(HLoadRoot, Heap::RootListIndex, HType); Representation RequiredInputRepresentation(int index) override { return Representation::None(); } Heap::RootListIndex index() const { return index_; } DECLARE_CONCRETE_INSTRUCTION(LoadRoot) protected: bool DataEquals(HValue* other) override { HLoadRoot* b = HLoadRoot::cast(other); return index_ == b->index_; } private: explicit HLoadRoot(Heap::RootListIndex index, HType type = HType::Tagged()) : HTemplateInstruction<0>(type), index_(index) { SetFlag(kUseGVN); // TODO(bmeurer): We'll need kDependsOnRoots once we add the // corresponding HStoreRoot instruction. SetDependsOnFlag(kCalls); set_representation(Representation::Tagged()); } bool IsDeletable() const override { return true; } const Heap::RootListIndex index_; }; class HCheckMaps final : public HTemplateInstruction<2> { public: static HCheckMaps* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, Handle<Map> map, HValue* typecheck = NULL) { return new(zone) HCheckMaps(value, new(zone) UniqueSet<Map>( Unique<Map>::CreateImmovable(map), zone), typecheck); } static HCheckMaps* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, SmallMapList* map_list, HValue* typecheck = NULL) { UniqueSet<Map>* maps = new(zone) UniqueSet<Map>(map_list->length(), zone); for (int i = 0; i < map_list->length(); ++i) { maps->Add(Unique<Map>::CreateImmovable(map_list->at(i)), zone); } return new(zone) HCheckMaps(value, maps, typecheck); } bool IsStabilityCheck() const { return IsStabilityCheckField::decode(bit_field_); } void MarkAsStabilityCheck() { bit_field_ = MapsAreStableField::encode(true) | HasMigrationTargetField::encode(false) | IsStabilityCheckField::encode(true); ClearChangesFlag(kNewSpacePromotion); ClearDependsOnFlag(kElementsKind); ClearDependsOnFlag(kMaps); } bool HasEscapingOperandAt(int index) override { return false; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HType CalculateInferredType() override { if (value()->type().IsHeapObject()) return value()->type(); return HType::HeapObject(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* value() const { return OperandAt(0); } HValue* typecheck() const { return OperandAt(1); } const UniqueSet<Map>* maps() const { return maps_; } void set_maps(const UniqueSet<Map>* maps) { maps_ = maps; } bool maps_are_stable() const { return MapsAreStableField::decode(bit_field_); } bool HasMigrationTarget() const { return HasMigrationTargetField::decode(bit_field_); } HValue* Canonicalize() override; static HCheckMaps* CreateAndInsertAfter(Zone* zone, HValue* value, Unique<Map> map, bool map_is_stable, HInstruction* instr) { return instr->Append(new(zone) HCheckMaps( value, new(zone) UniqueSet<Map>(map, zone), map_is_stable)); } static HCheckMaps* CreateAndInsertBefore(Zone* zone, HValue* value, const UniqueSet<Map>* maps, bool maps_are_stable, HInstruction* instr) { return instr->Prepend(new(zone) HCheckMaps(value, maps, maps_are_stable)); } DECLARE_CONCRETE_INSTRUCTION(CheckMaps) protected: bool DataEquals(HValue* other) override { return this->maps()->Equals(HCheckMaps::cast(other)->maps()); } int RedefinedOperandIndex() override { return 0; } private: HCheckMaps(HValue* value, const UniqueSet<Map>* maps, bool maps_are_stable) : HTemplateInstruction<2>(HType::HeapObject()), maps_(maps), bit_field_(HasMigrationTargetField::encode(false) | IsStabilityCheckField::encode(false) | MapsAreStableField::encode(maps_are_stable)) { DCHECK_NE(0, maps->size()); SetOperandAt(0, value); // Use the object value for the dependency. SetOperandAt(1, value); set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kMaps); SetDependsOnFlag(kElementsKind); } HCheckMaps(HValue* value, const UniqueSet<Map>* maps, HValue* typecheck) : HTemplateInstruction<2>(HType::HeapObject()), maps_(maps), bit_field_(HasMigrationTargetField::encode(false) | IsStabilityCheckField::encode(false) | MapsAreStableField::encode(true)) { DCHECK_NE(0, maps->size()); SetOperandAt(0, value); // Use the object value for the dependency if NULL is passed. SetOperandAt(1, typecheck ? typecheck : value); set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kMaps); SetDependsOnFlag(kElementsKind); for (int i = 0; i < maps->size(); ++i) { Handle<Map> map = maps->at(i).handle(); if (map->is_migration_target()) { bit_field_ = HasMigrationTargetField::update(bit_field_, true); } if (!map->is_stable()) { bit_field_ = MapsAreStableField::update(bit_field_, false); } } if (HasMigrationTarget()) SetChangesFlag(kNewSpacePromotion); } class HasMigrationTargetField : public BitField<bool, 0, 1> {}; class IsStabilityCheckField : public BitField<bool, 1, 1> {}; class MapsAreStableField : public BitField<bool, 2, 1> {}; const UniqueSet<Map>* maps_; uint32_t bit_field_; }; class HCheckValue final : public HUnaryOperation { public: static HCheckValue* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, Handle<JSFunction> func) { bool in_new_space = isolate->heap()->InNewSpace(*func); // NOTE: We create an uninitialized Unique and initialize it later. // This is because a JSFunction can move due to GC during graph creation. Unique<JSFunction> target = Unique<JSFunction>::CreateUninitialized(func); HCheckValue* check = new(zone) HCheckValue(value, target, in_new_space); return check; } static HCheckValue* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, Unique<HeapObject> target, bool object_in_new_space) { return new(zone) HCheckValue(value, target, object_in_new_space); } void FinalizeUniqueness() override { object_ = Unique<HeapObject>(object_.handle()); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* Canonicalize() override; #ifdef DEBUG void Verify() override; #endif Unique<HeapObject> object() const { return object_; } bool object_in_new_space() const { return object_in_new_space_; } DECLARE_CONCRETE_INSTRUCTION(CheckValue) protected: bool DataEquals(HValue* other) override { HCheckValue* b = HCheckValue::cast(other); return object_ == b->object_; } private: HCheckValue(HValue* value, Unique<HeapObject> object, bool object_in_new_space) : HUnaryOperation(value, value->type()), object_(object), object_in_new_space_(object_in_new_space) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } Unique<HeapObject> object_; bool object_in_new_space_; }; class HCheckInstanceType final : public HUnaryOperation { public: enum Check { IS_JS_RECEIVER, IS_JS_ARRAY, IS_JS_FUNCTION, IS_JS_DATE, IS_STRING, IS_INTERNALIZED_STRING, LAST_INTERVAL_CHECK = IS_JS_DATE }; DECLARE_INSTRUCTION_FACTORY_P2(HCheckInstanceType, HValue*, Check); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HType CalculateInferredType() override { switch (check_) { case IS_JS_RECEIVER: return HType::JSReceiver(); case IS_JS_ARRAY: return HType::JSArray(); case IS_JS_FUNCTION: return HType::JSObject(); case IS_JS_DATE: return HType::JSObject(); case IS_STRING: return HType::String(); case IS_INTERNALIZED_STRING: return HType::String(); } UNREACHABLE(); return HType::Tagged(); } HValue* Canonicalize() override; bool is_interval_check() const { return check_ <= LAST_INTERVAL_CHECK; } void GetCheckInterval(InstanceType* first, InstanceType* last); void GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag); Check check() const { return check_; } DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType) protected: // TODO(ager): It could be nice to allow the ommision of instance // type checks if we have already performed an instance type check // with a larger range. bool DataEquals(HValue* other) override { HCheckInstanceType* b = HCheckInstanceType::cast(other); return check_ == b->check_; } int RedefinedOperandIndex() override { return 0; } private: const char* GetCheckName() const; HCheckInstanceType(HValue* value, Check check) : HUnaryOperation(value, HType::HeapObject()), check_(check) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } const Check check_; }; class HCheckSmi final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HCheckSmi, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* Canonicalize() override { HType value_type = value()->type(); if (value_type.IsSmi()) { return NULL; } return this; } DECLARE_CONCRETE_INSTRUCTION(CheckSmi) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HCheckSmi(HValue* value) : HUnaryOperation(value, HType::Smi()) { set_representation(Representation::Smi()); SetFlag(kUseGVN); } }; class HCheckArrayBufferNotNeutered final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HCheckArrayBufferNotNeutered, HValue*); bool HasEscapingOperandAt(int index) override { return false; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HType CalculateInferredType() override { if (value()->type().IsHeapObject()) return value()->type(); return HType::HeapObject(); } DECLARE_CONCRETE_INSTRUCTION(CheckArrayBufferNotNeutered) protected: bool DataEquals(HValue* other) override { return true; } int RedefinedOperandIndex() override { return 0; } private: explicit HCheckArrayBufferNotNeutered(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kCalls); } }; class HCheckHeapObject final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HCheckHeapObject, HValue*); bool HasEscapingOperandAt(int index) override { return false; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HType CalculateInferredType() override { if (value()->type().IsHeapObject()) return value()->type(); return HType::HeapObject(); } #ifdef DEBUG void Verify() override; #endif HValue* Canonicalize() override { return value()->type().IsHeapObject() ? NULL : this; } DECLARE_CONCRETE_INSTRUCTION(CheckHeapObject) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HCheckHeapObject(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); } }; class HPhi final : public HValue { public: HPhi(int merged_index, Zone* zone) : inputs_(2, zone), merged_index_(merged_index) { DCHECK(merged_index >= 0 || merged_index == kInvalidMergedIndex); SetFlag(kFlexibleRepresentation); } Representation RepresentationFromInputs() override; Range* InferRange(Zone* zone) override; void InferRepresentation(HInferRepresentationPhase* h_infer) override; Representation RequiredInputRepresentation(int index) override { return representation(); } Representation KnownOptimalRepresentation() override { return representation(); } HType CalculateInferredType() override; int OperandCount() const override { return inputs_.length(); } HValue* OperandAt(int index) const override { return inputs_[index]; } HValue* GetRedundantReplacement(); void AddInput(HValue* value); bool HasRealUses(); bool IsReceiver() const { return merged_index_ == 0; } bool HasMergedIndex() const { return merged_index_ != kInvalidMergedIndex; } SourcePosition position() const override; int merged_index() const { return merged_index_; } std::ostream& PrintTo(std::ostream& os) const override; // NOLINT #ifdef DEBUG void Verify() override; #endif void InitRealUses(int id); void AddNonPhiUsesFrom(HPhi* other); Representation representation_from_indirect_uses() const { return representation_from_indirect_uses_; } bool has_type_feedback_from_uses() const { return has_type_feedback_from_uses_; } int phi_id() { return phi_id_; } static HPhi* cast(HValue* value) { DCHECK(value->IsPhi()); return reinterpret_cast<HPhi*>(value); } Opcode opcode() const override { return HValue::kPhi; } void SimplifyConstantInputs(); // Marker value representing an invalid merge index. static const int kInvalidMergedIndex = -1; protected: void DeleteFromGraph() override; void InternalSetOperandAt(int index, HValue* value) override { inputs_[index] = value; } private: Representation representation_from_non_phi_uses() const { return representation_from_non_phi_uses_; } ZoneList<HValue*> inputs_; int merged_index_ = 0; int phi_id_ = -1; Representation representation_from_indirect_uses_ = Representation::None(); Representation representation_from_non_phi_uses_ = Representation::None(); bool has_type_feedback_from_uses_ = false; bool IsDeletable() const override { return !IsReceiver(); } }; // Common base class for HArgumentsObject and HCapturedObject. class HDematerializedObject : public HInstruction { public: HDematerializedObject(int count, Zone* zone) : values_(count, zone) {} int OperandCount() const final { return values_.length(); } HValue* OperandAt(int index) const final { return values_[index]; } bool HasEscapingOperandAt(int index) final { return false; } Representation RequiredInputRepresentation(int index) final { return Representation::None(); } protected: void InternalSetOperandAt(int index, HValue* value) final { values_[index] = value; } // List of values tracked by this marker. ZoneList<HValue*> values_; }; class HArgumentsObject final : public HDematerializedObject { public: static HArgumentsObject* New(Isolate* isolate, Zone* zone, HValue* context, int count) { return new(zone) HArgumentsObject(count, zone); } // The values contain a list of all elements in the arguments object // including the receiver object, which is skipped when materializing. const ZoneList<HValue*>* arguments_values() const { return &values_; } int arguments_count() const { return values_.length(); } void AddArgument(HValue* argument, Zone* zone) { values_.Add(NULL, zone); // Resize list. SetOperandAt(values_.length() - 1, argument); } DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject) private: HArgumentsObject(int count, Zone* zone) : HDematerializedObject(count, zone) { set_representation(Representation::Tagged()); SetFlag(kIsArguments); } }; class HCapturedObject final : public HDematerializedObject { public: HCapturedObject(int length, int id, Zone* zone) : HDematerializedObject(length, zone), capture_id_(id) { set_representation(Representation::Tagged()); values_.AddBlock(NULL, length, zone); // Resize list. } // The values contain a list of all in-object properties inside the // captured object and is index by field index. Properties in the // properties or elements backing store are not tracked here. const ZoneList<HValue*>* values() const { return &values_; } int length() const { return values_.length(); } int capture_id() const { return capture_id_; } // Shortcut for the map value of this captured object. HValue* map_value() const { return values()->first(); } void ReuseSideEffectsFromStore(HInstruction* store) { DCHECK(store->HasObservableSideEffects()); DCHECK(store->IsStoreNamedField()); changes_flags_.Add(store->ChangesFlags()); } // Replay effects of this instruction on the given environment. void ReplayEnvironment(HEnvironment* env); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(CapturedObject) private: int capture_id_; // Note that we cannot DCE captured objects as they are used to replay // the environment. This method is here as an explicit reminder. // TODO(mstarzinger): Turn HSimulates into full snapshots maybe? bool IsDeletable() const final { return false; } }; class HConstant final : public HTemplateInstruction<0> { public: enum Special { kHoleNaN }; DECLARE_INSTRUCTION_FACTORY_P1(HConstant, Special); DECLARE_INSTRUCTION_FACTORY_P1(HConstant, int32_t); DECLARE_INSTRUCTION_FACTORY_P2(HConstant, int32_t, Representation); DECLARE_INSTRUCTION_FACTORY_P1(HConstant, double); DECLARE_INSTRUCTION_FACTORY_P1(HConstant, Handle<Object>); DECLARE_INSTRUCTION_FACTORY_P1(HConstant, ExternalReference); static HConstant* CreateAndInsertAfter(Isolate* isolate, Zone* zone, HValue* context, int32_t value, Representation representation, HInstruction* instruction) { return instruction->Append( HConstant::New(isolate, zone, context, value, representation)); } Handle<Map> GetMonomorphicJSObjectMap() override { Handle<Object> object = object_.handle(); if (!object.is_null() && object->IsHeapObject()) { return v8::internal::handle(HeapObject::cast(*object)->map()); } return Handle<Map>(); } static HConstant* CreateAndInsertBefore(Isolate* isolate, Zone* zone, HValue* context, int32_t value, Representation representation, HInstruction* instruction) { return instruction->Prepend( HConstant::New(isolate, zone, context, value, representation)); } static HConstant* CreateAndInsertBefore(Zone* zone, Unique<Map> map, bool map_is_stable, HInstruction* instruction) { return instruction->Prepend(new(zone) HConstant( map, Unique<Map>(Handle<Map>::null()), map_is_stable, Representation::Tagged(), HType::HeapObject(), true, false, false, MAP_TYPE)); } static HConstant* CreateAndInsertAfter(Zone* zone, Unique<Map> map, bool map_is_stable, HInstruction* instruction) { return instruction->Append(new(zone) HConstant( map, Unique<Map>(Handle<Map>::null()), map_is_stable, Representation::Tagged(), HType::HeapObject(), true, false, false, MAP_TYPE)); } Handle<Object> handle(Isolate* isolate) { if (object_.handle().is_null()) { // Default arguments to is_not_in_new_space depend on this heap number // to be tenured so that it's guaranteed not to be located in new space. object_ = Unique<Object>::CreateUninitialized( isolate->factory()->NewNumber(double_value_, TENURED)); } AllowDeferredHandleDereference smi_check; DCHECK(HasInteger32Value() || !object_.handle()->IsSmi()); return object_.handle(); } bool IsSpecialDouble() const { return HasDoubleValue() && (bit_cast<int64_t>(double_value_) == bit_cast<int64_t>(-0.0) || std::isnan(double_value_)); } bool NotInNewSpace() const { return IsNotInNewSpaceField::decode(bit_field_); } bool ImmortalImmovable() const; bool IsCell() const { InstanceType instance_type = GetInstanceType(); return instance_type == CELL_TYPE; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } Representation KnownOptimalRepresentation() override { if (HasSmiValue() && SmiValuesAre31Bits()) return Representation::Smi(); if (HasInteger32Value()) return Representation::Integer32(); if (HasNumberValue()) return Representation::Double(); if (HasExternalReferenceValue()) return Representation::External(); return Representation::Tagged(); } bool EmitAtUses() override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HConstant* CopyToRepresentation(Representation r, Zone* zone) const; Maybe<HConstant*> CopyToTruncatedInt32(Zone* zone); Maybe<HConstant*> CopyToTruncatedNumber(Isolate* isolate, Zone* zone); bool HasInteger32Value() const { return HasInt32ValueField::decode(bit_field_); } int32_t Integer32Value() const { DCHECK(HasInteger32Value()); return int32_value_; } bool HasSmiValue() const { return HasSmiValueField::decode(bit_field_); } bool HasDoubleValue() const { return HasDoubleValueField::decode(bit_field_); } double DoubleValue() const { DCHECK(HasDoubleValue()); return double_value_; } uint64_t DoubleValueAsBits() const { uint64_t bits; DCHECK(HasDoubleValue()); STATIC_ASSERT(sizeof(bits) == sizeof(double_value_)); std::memcpy(&bits, &double_value_, sizeof(bits)); return bits; } bool IsTheHole() const { if (HasDoubleValue() && DoubleValueAsBits() == kHoleNanInt64) { return true; } return object_.IsInitialized() && object_.IsKnownGlobal(isolate()->heap()->the_hole_value()); } bool HasNumberValue() const { return HasDoubleValue(); } int32_t NumberValueAsInteger32() const { DCHECK(HasNumberValue()); // Irrespective of whether a numeric HConstant can be safely // represented as an int32, we store the (in some cases lossy) // representation of the number in int32_value_. return int32_value_; } bool HasStringValue() const { if (HasNumberValue()) return false; DCHECK(!object_.handle().is_null()); return GetInstanceType() < FIRST_NONSTRING_TYPE; } Handle<String> StringValue() const { DCHECK(HasStringValue()); return Handle<String>::cast(object_.handle()); } bool HasInternalizedStringValue() const { return HasStringValue() && StringShape(GetInstanceType()).IsInternalized(); } bool HasExternalReferenceValue() const { return HasExternalReferenceValueField::decode(bit_field_); } ExternalReference ExternalReferenceValue() const { return external_reference_value_; } bool HasBooleanValue() const { return type_.IsBoolean(); } bool BooleanValue() const { return BooleanValueField::decode(bit_field_); } bool IsCallable() const { return IsCallableField::decode(bit_field_); } bool IsUndetectable() const { return IsUndetectableField::decode(bit_field_); } InstanceType GetInstanceType() const { return InstanceTypeField::decode(bit_field_); } bool HasMapValue() const { return GetInstanceType() == MAP_TYPE; } Unique<Map> MapValue() const { DCHECK(HasMapValue()); return Unique<Map>::cast(GetUnique()); } bool HasStableMapValue() const { DCHECK(HasMapValue() || !HasStableMapValueField::decode(bit_field_)); return HasStableMapValueField::decode(bit_field_); } bool HasObjectMap() const { return !object_map_.IsNull(); } Unique<Map> ObjectMap() const { DCHECK(HasObjectMap()); return object_map_; } intptr_t Hashcode() override { if (HasInteger32Value()) { return static_cast<intptr_t>(int32_value_); } else if (HasDoubleValue()) { uint64_t bits = DoubleValueAsBits(); if (sizeof(bits) > sizeof(intptr_t)) { bits ^= (bits >> 32); } return static_cast<intptr_t>(bits); } else if (HasExternalReferenceValue()) { return reinterpret_cast<intptr_t>(external_reference_value_.address()); } else { DCHECK(!object_.handle().is_null()); return object_.Hashcode(); } } void FinalizeUniqueness() override { if (!HasDoubleValue() && !HasExternalReferenceValue()) { DCHECK(!object_.handle().is_null()); object_ = Unique<Object>(object_.handle()); } } Unique<Object> GetUnique() const { return object_; } bool EqualsUnique(Unique<Object> other) const { return object_.IsInitialized() && object_ == other; } bool DataEquals(HValue* other) override { HConstant* other_constant = HConstant::cast(other); if (HasInteger32Value()) { return other_constant->HasInteger32Value() && int32_value_ == other_constant->int32_value_; } else if (HasDoubleValue()) { return other_constant->HasDoubleValue() && std::memcmp(&double_value_, &other_constant->double_value_, sizeof(double_value_)) == 0; } else if (HasExternalReferenceValue()) { return other_constant->HasExternalReferenceValue() && external_reference_value_ == other_constant->external_reference_value_; } else { if (other_constant->HasInteger32Value() || other_constant->HasDoubleValue() || other_constant->HasExternalReferenceValue()) { return false; } DCHECK(!object_.handle().is_null()); return other_constant->object_ == object_; } } #ifdef DEBUG void Verify() override {} #endif DECLARE_CONCRETE_INSTRUCTION(Constant) protected: Range* InferRange(Zone* zone) override; private: friend class HGraph; explicit HConstant(Special special); explicit HConstant(Handle<Object> handle, Representation r = Representation::None()); HConstant(int32_t value, Representation r = Representation::None(), bool is_not_in_new_space = true, Unique<Object> optional = Unique<Object>(Handle<Object>::null())); HConstant(double value, Representation r = Representation::None(), bool is_not_in_new_space = true, Unique<Object> optional = Unique<Object>(Handle<Object>::null())); HConstant(Unique<Object> object, Unique<Map> object_map, bool has_stable_map_value, Representation r, HType type, bool is_not_in_new_space, bool boolean_value, bool is_undetectable, InstanceType instance_type); explicit HConstant(ExternalReference reference); void Initialize(Representation r); bool IsDeletable() const override { return true; } // If object_ is a map, this indicates whether the map is stable. class HasStableMapValueField : public BitField<bool, 0, 1> {}; // We store the HConstant in the most specific form safely possible. // These flags tell us if the respective member fields hold valid, safe // representations of the constant. More specific flags imply more general // flags, but not the converse (i.e. smi => int32 => double). class HasSmiValueField : public BitField<bool, 1, 1> {}; class HasInt32ValueField : public BitField<bool, 2, 1> {}; class HasDoubleValueField : public BitField<bool, 3, 1> {}; class HasExternalReferenceValueField : public BitField<bool, 4, 1> {}; class IsNotInNewSpaceField : public BitField<bool, 5, 1> {}; class BooleanValueField : public BitField<bool, 6, 1> {}; class IsUndetectableField : public BitField<bool, 7, 1> {}; class IsCallableField : public BitField<bool, 8, 1> {}; static const InstanceType kUnknownInstanceType = FILLER_TYPE; class InstanceTypeField : public BitField<InstanceType, 16, 8> {}; // If this is a numerical constant, object_ either points to the // HeapObject the constant originated from or is null. If the // constant is non-numeric, object_ always points to a valid // constant HeapObject. Unique<Object> object_; // If object_ is a heap object, this points to the stable map of the object. Unique<Map> object_map_; uint32_t bit_field_; int32_t int32_value_; double double_value_; ExternalReference external_reference_value_; }; class HBinaryOperation : public HTemplateInstruction<3> { public: HBinaryOperation(HValue* context, HValue* left, HValue* right, HType type = HType::Tagged()) : HTemplateInstruction<3>(type), observed_output_representation_(Representation::None()) { DCHECK(left != NULL && right != NULL); SetOperandAt(0, context); SetOperandAt(1, left); SetOperandAt(2, right); observed_input_representation_[0] = Representation::None(); observed_input_representation_[1] = Representation::None(); } HValue* context() const { return OperandAt(0); } HValue* left() const { return OperandAt(1); } HValue* right() const { return OperandAt(2); } // True if switching left and right operands likely generates better code. bool AreOperandsBetterSwitched() { if (!IsCommutative()) return false; // Constant operands are better off on the right, they can be inlined in // many situations on most platforms. if (left()->IsConstant()) return true; if (right()->IsConstant()) return false; // Otherwise, if there is only one use of the right operand, it would be // better off on the left for platforms that only have 2-arg arithmetic // ops (e.g ia32, x64) that clobber the left operand. return right()->HasOneUse(); } HValue* BetterLeftOperand() { return AreOperandsBetterSwitched() ? right() : left(); } HValue* BetterRightOperand() { return AreOperandsBetterSwitched() ? left() : right(); } void set_observed_input_representation(int index, Representation rep) { DCHECK(index >= 1 && index <= 2); observed_input_representation_[index - 1] = rep; } virtual void initialize_output_representation(Representation observed) { observed_output_representation_ = observed; } Representation observed_input_representation(int index) override { if (index == 0) return Representation::Tagged(); return observed_input_representation_[index - 1]; } void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { Representation rep = !FLAG_smi_binop && new_rep.IsSmi() ? Representation::Integer32() : new_rep; HValue::UpdateRepresentation(rep, h_infer, reason); } void InferRepresentation(HInferRepresentationPhase* h_infer) override; Representation RepresentationFromInputs() override; Representation RepresentationFromOutput(); void AssumeRepresentation(Representation r) override; virtual bool IsCommutative() const { return false; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { if (index == 0) return Representation::Tagged(); return representation(); } bool RightIsPowerOf2() { if (!right()->IsInteger32Constant()) return false; int32_t value = right()->GetInteger32Constant(); if (value < 0) { return base::bits::IsPowerOfTwo32(static_cast<uint32_t>(-value)); } return base::bits::IsPowerOfTwo32(static_cast<uint32_t>(value)); } DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation) private: bool IgnoreObservedOutputRepresentation(Representation current_rep); Representation observed_input_representation_[2]; Representation observed_output_representation_; }; class HWrapReceiver final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HWrapReceiver, HValue*, HValue*); bool DataEquals(HValue* other) override { return true; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* receiver() const { return OperandAt(0); } HValue* function() const { return OperandAt(1); } HValue* Canonicalize() override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT bool known_function() const { return known_function_; } DECLARE_CONCRETE_INSTRUCTION(WrapReceiver) private: HWrapReceiver(HValue* receiver, HValue* function) { known_function_ = function->IsConstant() && HConstant::cast(function)->handle(function->isolate())->IsJSFunction(); set_representation(Representation::Tagged()); SetOperandAt(0, receiver); SetOperandAt(1, function); SetFlag(kUseGVN); } bool known_function_; }; class HApplyArguments final : public HTemplateInstruction<4> { public: DECLARE_INSTRUCTION_FACTORY_P5(HApplyArguments, HValue*, HValue*, HValue*, HValue*, TailCallMode); Representation RequiredInputRepresentation(int index) override { // The length is untagged, all other inputs are tagged. return (index == 2) ? Representation::Integer32() : Representation::Tagged(); } HValue* function() { return OperandAt(0); } HValue* receiver() { return OperandAt(1); } HValue* length() { return OperandAt(2); } HValue* elements() { return OperandAt(3); } TailCallMode tail_call_mode() const { return TailCallModeField::decode(bit_field_); } DECLARE_CONCRETE_INSTRUCTION(ApplyArguments) private: HApplyArguments(HValue* function, HValue* receiver, HValue* length, HValue* elements, TailCallMode tail_call_mode) : bit_field_(TailCallModeField::encode(tail_call_mode)) { set_representation(Representation::Tagged()); SetOperandAt(0, function); SetOperandAt(1, receiver); SetOperandAt(2, length); SetOperandAt(3, elements); SetAllSideEffects(); } class TailCallModeField : public BitField<TailCallMode, 0, 1> {}; uint32_t bit_field_; }; class HArgumentsElements final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P1(HArgumentsElements, bool); DECLARE_INSTRUCTION_FACTORY_P2(HArgumentsElements, bool, bool); DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements) Representation RequiredInputRepresentation(int index) override { return Representation::None(); } bool from_inlined() const { return from_inlined_; } bool arguments_adaptor() const { return arguments_adaptor_; } protected: bool DataEquals(HValue* other) override { return true; } private: explicit HArgumentsElements(bool from_inlined, bool arguments_adaptor = true) : from_inlined_(from_inlined), arguments_adaptor_(arguments_adaptor) { // The value produced by this instruction is a pointer into the stack // that looks as if it was a smi because of alignment. set_representation(Representation::Tagged()); SetFlag(kUseGVN); } bool IsDeletable() const override { return true; } bool from_inlined_; bool arguments_adaptor_; }; class HArgumentsLength final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HArgumentsLength, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HArgumentsLength(HValue* value) : HUnaryOperation(value) { set_representation(Representation::Integer32()); SetFlag(kUseGVN); } bool IsDeletable() const override { return true; } }; class HAccessArgumentsAt final : public HTemplateInstruction<3> { public: DECLARE_INSTRUCTION_FACTORY_P3(HAccessArgumentsAt, HValue*, HValue*, HValue*); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { // The arguments elements is considered tagged. return index == 0 ? Representation::Tagged() : Representation::Integer32(); } HValue* arguments() const { return OperandAt(0); } HValue* length() const { return OperandAt(1); } HValue* index() const { return OperandAt(2); } DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt) private: HAccessArgumentsAt(HValue* arguments, HValue* length, HValue* index) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetOperandAt(0, arguments); SetOperandAt(1, length); SetOperandAt(2, index); } bool DataEquals(HValue* other) override { return true; } }; class HBoundsCheck final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HBoundsCheck, HValue*, HValue*); bool skip_check() const { return skip_check_; } void set_skip_check() { skip_check_ = true; } HValue* base() const { return base_; } int offset() const { return offset_; } int scale() const { return scale_; } Representation RequiredInputRepresentation(int index) override { return representation(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT void InferRepresentation(HInferRepresentationPhase* h_infer) override; HValue* index() const { return OperandAt(0); } HValue* length() const { return OperandAt(1); } bool allow_equality() const { return allow_equality_; } void set_allow_equality(bool v) { allow_equality_ = v; } int RedefinedOperandIndex() override { return 0; } bool IsPurelyInformativeDefinition() override { return skip_check(); } DECLARE_CONCRETE_INSTRUCTION(BoundsCheck) protected: Range* InferRange(Zone* zone) override; bool DataEquals(HValue* other) override { return true; } bool skip_check_; HValue* base_; int offset_; int scale_; bool allow_equality_; private: // Normally HBoundsCheck should be created using the // HGraphBuilder::AddBoundsCheck() helper. // However when building stubs, where we know that the arguments are Int32, // it makes sense to invoke this constructor directly. HBoundsCheck(HValue* index, HValue* length) : skip_check_(false), base_(NULL), offset_(0), scale_(0), allow_equality_(false) { SetOperandAt(0, index); SetOperandAt(1, length); SetFlag(kFlexibleRepresentation); SetFlag(kUseGVN); } bool IsDeletable() const override { return skip_check() && !FLAG_debug_code; } }; class HBitwiseBinaryOperation : public HBinaryOperation { public: HBitwiseBinaryOperation(HValue* context, HValue* left, HValue* right, HType type = HType::TaggedNumber()) : HBinaryOperation(context, left, right, type) { SetFlag(kFlexibleRepresentation); SetFlag(kTruncatingToInt32); SetFlag(kTruncatingToNumber); SetAllSideEffects(); } void RepresentationChanged(Representation to) override { if (to.IsTagged() && (left()->ToNumberCanBeObserved() || right()->ToNumberCanBeObserved())) { SetAllSideEffects(); ClearFlag(kUseGVN); } else { ClearAllSideEffects(); SetFlag(kUseGVN); } if (to.IsTagged()) SetChangesFlag(kNewSpacePromotion); } void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { // We only generate either int32 or generic tagged bitwise operations. if (new_rep.IsDouble()) new_rep = Representation::Integer32(); HBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } Representation observed_input_representation(int index) override { Representation r = HBinaryOperation::observed_input_representation(index); if (r.IsDouble()) return Representation::Integer32(); return r; } void initialize_output_representation(Representation observed) override { if (observed.IsDouble()) observed = Representation::Integer32(); HBinaryOperation::initialize_output_representation(observed); } DECLARE_ABSTRACT_INSTRUCTION(BitwiseBinaryOperation) private: bool IsDeletable() const override { return true; } }; class HMathFloorOfDiv final : public HBinaryOperation { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HMathFloorOfDiv, HValue*, HValue*); DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv) protected: bool DataEquals(HValue* other) override { return true; } private: HMathFloorOfDiv(HValue* context, HValue* left, HValue* right) : HBinaryOperation(context, left, right) { set_representation(Representation::Integer32()); SetFlag(kUseGVN); SetFlag(kCanOverflow); SetFlag(kCanBeDivByZero); SetFlag(kLeftCanBeMinInt); SetFlag(kLeftCanBeNegative); SetFlag(kLeftCanBePositive); SetFlag(kTruncatingToNumber); } Range* InferRange(Zone* zone) override; bool IsDeletable() const override { return true; } }; class HArithmeticBinaryOperation : public HBinaryOperation { public: HArithmeticBinaryOperation(HValue* context, HValue* left, HValue* right, HType type = HType::TaggedNumber()) : HBinaryOperation(context, left, right, type) { SetAllSideEffects(); SetFlag(kFlexibleRepresentation); SetFlag(kTruncatingToNumber); } void RepresentationChanged(Representation to) override { if (to.IsTagged() && (left()->ToNumberCanBeObserved() || right()->ToNumberCanBeObserved())) { SetAllSideEffects(); ClearFlag(kUseGVN); } else { ClearAllSideEffects(); SetFlag(kUseGVN); } if (to.IsTagged()) SetChangesFlag(kNewSpacePromotion); } DECLARE_ABSTRACT_INSTRUCTION(ArithmeticBinaryOperation) private: bool IsDeletable() const override { return true; } }; class HCompareGeneric final : public HBinaryOperation { public: static HCompareGeneric* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right, Token::Value token) { return new (zone) HCompareGeneric(context, left, right, token); } Representation RequiredInputRepresentation(int index) override { return index == 0 ? Representation::Tagged() : representation(); } Token::Value token() const { return token_; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(CompareGeneric) private: HCompareGeneric(HValue* context, HValue* left, HValue* right, Token::Value token) : HBinaryOperation(context, left, right, HType::Boolean()), token_(token) { DCHECK(Token::IsCompareOp(token)); set_representation(Representation::Tagged()); SetAllSideEffects(); } Token::Value token_; }; class HCompareNumericAndBranch : public HTemplateControlInstruction<2, 2> { public: static HCompareNumericAndBranch* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right, Token::Value token, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) { return new (zone) HCompareNumericAndBranch(left, right, token, true_target, false_target); } HValue* left() const { return OperandAt(0); } HValue* right() const { return OperandAt(1); } Token::Value token() const { return token_; } void set_observed_input_representation(Representation left, Representation right) { observed_input_representation_[0] = left; observed_input_representation_[1] = right; } void InferRepresentation(HInferRepresentationPhase* h_infer) override; Representation RequiredInputRepresentation(int index) override { return representation(); } Representation observed_input_representation(int index) override { return observed_input_representation_[index]; } bool KnownSuccessorBlock(HBasicBlock** block) override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch) private: HCompareNumericAndBranch(HValue* left, HValue* right, Token::Value token, HBasicBlock* true_target, HBasicBlock* false_target) : token_(token) { SetFlag(kFlexibleRepresentation); DCHECK(Token::IsCompareOp(token)); SetOperandAt(0, left); SetOperandAt(1, right); SetSuccessorAt(0, true_target); SetSuccessorAt(1, false_target); } Representation observed_input_representation_[2]; Token::Value token_; }; class HCompareHoleAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P1(HCompareHoleAndBranch, HValue*); DECLARE_INSTRUCTION_FACTORY_P3(HCompareHoleAndBranch, HValue*, HBasicBlock*, HBasicBlock*); void InferRepresentation(HInferRepresentationPhase* h_infer) override; Representation RequiredInputRepresentation(int index) override { return representation(); } DECLARE_CONCRETE_INSTRUCTION(CompareHoleAndBranch) private: HCompareHoleAndBranch(HValue* value, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target) { SetFlag(kFlexibleRepresentation); } }; class HCompareObjectEqAndBranch : public HTemplateControlInstruction<2, 2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HCompareObjectEqAndBranch, HValue*, HValue*); DECLARE_INSTRUCTION_FACTORY_P4(HCompareObjectEqAndBranch, HValue*, HValue*, HBasicBlock*, HBasicBlock*); bool KnownSuccessorBlock(HBasicBlock** block) override; static const int kNoKnownSuccessorIndex = -1; int known_successor_index() const { return known_successor_index_; } void set_known_successor_index(int known_successor_index) { known_successor_index_ = known_successor_index; } HValue* left() const { return OperandAt(0); } HValue* right() const { return OperandAt(1); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } Representation observed_input_representation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(CompareObjectEqAndBranch) private: HCompareObjectEqAndBranch(HValue* left, HValue* right, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : known_successor_index_(kNoKnownSuccessorIndex) { SetOperandAt(0, left); SetOperandAt(1, right); SetSuccessorAt(0, true_target); SetSuccessorAt(1, false_target); } int known_successor_index_; }; class HIsStringAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P1(HIsStringAndBranch, HValue*); DECLARE_INSTRUCTION_FACTORY_P3(HIsStringAndBranch, HValue*, HBasicBlock*, HBasicBlock*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } bool KnownSuccessorBlock(HBasicBlock** block) override; static const int kNoKnownSuccessorIndex = -1; int known_successor_index() const { return known_successor_index_; } void set_known_successor_index(int known_successor_index) { known_successor_index_ = known_successor_index; } DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch) protected: int RedefinedOperandIndex() override { return 0; } private: HIsStringAndBranch(HValue* value, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target), known_successor_index_(kNoKnownSuccessorIndex) { set_representation(Representation::Tagged()); } int known_successor_index_; }; class HIsSmiAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P1(HIsSmiAndBranch, HValue*); DECLARE_INSTRUCTION_FACTORY_P3(HIsSmiAndBranch, HValue*, HBasicBlock*, HBasicBlock*); DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch) Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } protected: bool DataEquals(HValue* other) override { return true; } int RedefinedOperandIndex() override { return 0; } private: HIsSmiAndBranch(HValue* value, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target) { set_representation(Representation::Tagged()); } }; class HIsUndetectableAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P1(HIsUndetectableAndBranch, HValue*); DECLARE_INSTRUCTION_FACTORY_P3(HIsUndetectableAndBranch, HValue*, HBasicBlock*, HBasicBlock*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } bool KnownSuccessorBlock(HBasicBlock** block) override; DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch) private: HIsUndetectableAndBranch(HValue* value, HBasicBlock* true_target = NULL, HBasicBlock* false_target = NULL) : HUnaryControlInstruction(value, true_target, false_target) {} }; class HStringCompareAndBranch final : public HTemplateControlInstruction<2, 3> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HStringCompareAndBranch, HValue*, HValue*, Token::Value); HValue* context() const { return OperandAt(0); } HValue* left() const { return OperandAt(1); } HValue* right() const { return OperandAt(2); } Token::Value token() const { return token_; } std::ostream& PrintDataTo(std::ostream& os) const final; // NOLINT Representation RequiredInputRepresentation(int index) final { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch) private: HStringCompareAndBranch(HValue* context, HValue* left, HValue* right, Token::Value token) : token_(token) { DCHECK(Token::IsCompareOp(token)); SetOperandAt(0, context); SetOperandAt(1, left); SetOperandAt(2, right); set_representation(Representation::Tagged()); SetChangesFlag(kNewSpacePromotion); SetDependsOnFlag(kStringChars); SetDependsOnFlag(kStringLengths); } Token::Value const token_; }; class HHasInstanceTypeAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P2( HHasInstanceTypeAndBranch, HValue*, InstanceType); DECLARE_INSTRUCTION_FACTORY_P3( HHasInstanceTypeAndBranch, HValue*, InstanceType, InstanceType); InstanceType from() { return from_; } InstanceType to() { return to_; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } bool KnownSuccessorBlock(HBasicBlock** block) override; DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch) private: HHasInstanceTypeAndBranch(HValue* value, InstanceType type) : HUnaryControlInstruction(value, NULL, NULL), from_(type), to_(type) { } HHasInstanceTypeAndBranch(HValue* value, InstanceType from, InstanceType to) : HUnaryControlInstruction(value, NULL, NULL), from_(from), to_(to) { DCHECK(to == LAST_TYPE); // Others not implemented yet in backend. } InstanceType from_; InstanceType to_; // Inclusive range, not all combinations work. }; class HClassOfTestAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P2(HClassOfTestAndBranch, HValue*, Handle<String>); DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch) Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Handle<String> class_name() const { return class_name_; } private: HClassOfTestAndBranch(HValue* value, Handle<String> class_name) : HUnaryControlInstruction(value, NULL, NULL), class_name_(class_name) { } Handle<String> class_name_; }; class HTypeofIsAndBranch final : public HUnaryControlInstruction { public: DECLARE_INSTRUCTION_FACTORY_P2(HTypeofIsAndBranch, HValue*, Handle<String>); Handle<String> type_literal() const { return type_literal_.handle(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch) Representation RequiredInputRepresentation(int index) override { return Representation::None(); } bool KnownSuccessorBlock(HBasicBlock** block) override; void FinalizeUniqueness() override { type_literal_ = Unique<String>(type_literal_.handle()); } private: HTypeofIsAndBranch(HValue* value, Handle<String> type_literal) : HUnaryControlInstruction(value, NULL, NULL), type_literal_(Unique<String>::CreateUninitialized(type_literal)) { } Unique<String> type_literal_; }; class HHasInPrototypeChainAndBranch final : public HTemplateControlInstruction<2, 2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HHasInPrototypeChainAndBranch, HValue*, HValue*); HValue* object() const { return OperandAt(0); } HValue* prototype() const { return OperandAt(1); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } bool ObjectNeedsSmiCheck() const { return !object()->type().IsHeapObject() && !object()->representation().IsHeapObject(); } DECLARE_CONCRETE_INSTRUCTION(HasInPrototypeChainAndBranch) private: HHasInPrototypeChainAndBranch(HValue* object, HValue* prototype) { SetOperandAt(0, object); SetOperandAt(1, prototype); SetDependsOnFlag(kCalls); } }; class HPower final : public HTemplateInstruction<2> { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); HValue* left() { return OperandAt(0); } HValue* right() const { return OperandAt(1); } Representation RequiredInputRepresentation(int index) override { return index == 0 ? Representation::Double() : Representation::None(); } Representation observed_input_representation(int index) override { return RequiredInputRepresentation(index); } DECLARE_CONCRETE_INSTRUCTION(Power) protected: bool DataEquals(HValue* other) override { return true; } private: HPower(HValue* left, HValue* right) { SetOperandAt(0, left); SetOperandAt(1, right); set_representation(Representation::Double()); SetFlag(kUseGVN); SetChangesFlag(kNewSpacePromotion); } bool IsDeletable() const override { return !right()->representation().IsTagged(); } }; enum ExternalAddType { AddOfExternalAndTagged, AddOfExternalAndInt32, NoExternalAdd }; class HAdd final : public HArithmeticBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right, ExternalAddType external_add_type); // Add is only commutative if two integer values are added and not if two // tagged values are added (because it might be a String concatenation). // We also do not commute (pointer + offset). bool IsCommutative() const override { return !representation().IsTagged() && !representation().IsExternal(); } HValue* Canonicalize() override; void RepresentationChanged(Representation to) override { if (to.IsTagged() && (left()->ToNumberCanBeObserved() || right()->ToNumberCanBeObserved() || left()->ToStringCanBeObserved() || right()->ToStringCanBeObserved())) { SetAllSideEffects(); ClearFlag(kUseGVN); } else { ClearAllSideEffects(); SetFlag(kUseGVN); } if (to.IsTagged()) { SetChangesFlag(kNewSpacePromotion); ClearFlag(kTruncatingToNumber); } if (!right()->type().IsTaggedNumber() && !right()->representation().IsDouble() && !right()->representation().IsSmiOrInteger32()) { ClearFlag(kTruncatingToNumber); } } Representation RepresentationFromInputs() override; Representation RequiredInputRepresentation(int index) override; bool IsConsistentExternalRepresentation() { return left()->representation().IsExternal() && ((external_add_type_ == AddOfExternalAndInt32 && right()->representation().IsInteger32()) || (external_add_type_ == AddOfExternalAndTagged && right()->representation().IsTagged())); } ExternalAddType external_add_type() const { return external_add_type_; } DECLARE_CONCRETE_INSTRUCTION(Add) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override; private: HAdd(HValue* context, HValue* left, HValue* right, ExternalAddType external_add_type = NoExternalAdd) : HArithmeticBinaryOperation(context, left, right, HType::Tagged()), external_add_type_(external_add_type) { SetFlag(kCanOverflow); switch (external_add_type_) { case AddOfExternalAndTagged: DCHECK(left->representation().IsExternal()); DCHECK(right->representation().IsTagged()); SetDependsOnFlag(kNewSpacePromotion); ClearFlag(HValue::kCanOverflow); SetFlag(kHasNoObservableSideEffects); break; case NoExternalAdd: // This is a bit of a hack: The call to this constructor is generated // by a macro that also supports sub and mul, so it doesn't pass in // a value for external_add_type but uses the default. if (left->representation().IsExternal()) { external_add_type_ = AddOfExternalAndInt32; } break; case AddOfExternalAndInt32: // See comment above. UNREACHABLE(); break; } } ExternalAddType external_add_type_; }; class HSub final : public HArithmeticBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); HValue* Canonicalize() override; DECLARE_CONCRETE_INSTRUCTION(Sub) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override; private: HSub(HValue* context, HValue* left, HValue* right) : HArithmeticBinaryOperation(context, left, right) { SetFlag(kCanOverflow); } }; class HMul final : public HArithmeticBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); static HInstruction* NewImul(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right) { HInstruction* instr = HMul::New(isolate, zone, context, left, right); if (!instr->IsMul()) return instr; HMul* mul = HMul::cast(instr); // TODO(mstarzinger): Prevent bailout on minus zero for imul. mul->AssumeRepresentation(Representation::Integer32()); mul->ClearFlag(HValue::kCanOverflow); return mul; } HValue* Canonicalize() override; // Only commutative if it is certain that not two objects are multiplicated. bool IsCommutative() const override { return !representation().IsTagged(); } void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } bool MulMinusOne(); DECLARE_CONCRETE_INSTRUCTION(Mul) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override; private: HMul(HValue* context, HValue* left, HValue* right) : HArithmeticBinaryOperation(context, left, right) { SetFlag(kCanOverflow); } }; class HMod final : public HArithmeticBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); HValue* Canonicalize() override; void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi()) new_rep = Representation::Integer32(); HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Mod) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override; private: HMod(HValue* context, HValue* left, HValue* right) : HArithmeticBinaryOperation(context, left, right) { SetFlag(kCanBeDivByZero); SetFlag(kCanOverflow); SetFlag(kLeftCanBeNegative); } }; class HDiv final : public HArithmeticBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); HValue* Canonicalize() override; void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi()) new_rep = Representation::Integer32(); HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Div) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override; private: HDiv(HValue* context, HValue* left, HValue* right) : HArithmeticBinaryOperation(context, left, right) { SetFlag(kCanBeDivByZero); SetFlag(kCanOverflow); } }; class HMathMinMax final : public HArithmeticBinaryOperation { public: enum Operation { kMathMin, kMathMax }; static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right, Operation op); Representation observed_input_representation(int index) override { return RequiredInputRepresentation(index); } void InferRepresentation(HInferRepresentationPhase* h_infer) override; Representation RepresentationFromInputs() override { Representation left_rep = left()->representation(); Representation right_rep = right()->representation(); Representation result = Representation::Smi(); result = result.generalize(left_rep); result = result.generalize(right_rep); if (result.IsTagged()) return Representation::Double(); return result; } bool IsCommutative() const override { return true; } Operation operation() { return operation_; } DECLARE_CONCRETE_INSTRUCTION(MathMinMax) protected: bool DataEquals(HValue* other) override { return other->IsMathMinMax() && HMathMinMax::cast(other)->operation_ == operation_; } Range* InferRange(Zone* zone) override; private: HMathMinMax(HValue* context, HValue* left, HValue* right, Operation op) : HArithmeticBinaryOperation(context, left, right), operation_(op) {} Operation operation_; }; class HBitwise final : public HBitwiseBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, Token::Value op, HValue* left, HValue* right); Token::Value op() const { return op_; } bool IsCommutative() const override { return true; } HValue* Canonicalize() override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(Bitwise) protected: bool DataEquals(HValue* other) override { return op() == HBitwise::cast(other)->op(); } Range* InferRange(Zone* zone) override; private: HBitwise(HValue* context, Token::Value op, HValue* left, HValue* right) : HBitwiseBinaryOperation(context, left, right), op_(op) { DCHECK(op == Token::BIT_AND || op == Token::BIT_OR || op == Token::BIT_XOR); // BIT_AND with a smi-range positive value will always unset the // entire sign-extension of the smi-sign. if (op == Token::BIT_AND && ((left->IsConstant() && left->representation().IsSmi() && HConstant::cast(left)->Integer32Value() >= 0) || (right->IsConstant() && right->representation().IsSmi() && HConstant::cast(right)->Integer32Value() >= 0))) { SetFlag(kTruncatingToSmi); SetFlag(kTruncatingToInt32); // BIT_OR with a smi-range negative value will always set the entire // sign-extension of the smi-sign. } else if (op == Token::BIT_OR && ((left->IsConstant() && left->representation().IsSmi() && HConstant::cast(left)->Integer32Value() < 0) || (right->IsConstant() && right->representation().IsSmi() && HConstant::cast(right)->Integer32Value() < 0))) { SetFlag(kTruncatingToSmi); SetFlag(kTruncatingToInt32); } } Token::Value op_; }; class HShl final : public HBitwiseBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); Range* InferRange(Zone* zone) override; void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi() && !(right()->IsInteger32Constant() && right()->GetInteger32Constant() >= 0)) { new_rep = Representation::Integer32(); } HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Shl) protected: bool DataEquals(HValue* other) override { return true; } private: HShl(HValue* context, HValue* left, HValue* right) : HBitwiseBinaryOperation(context, left, right) {} }; class HShr final : public HBitwiseBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); Range* InferRange(Zone* zone) override; void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi()) new_rep = Representation::Integer32(); HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Shr) protected: bool DataEquals(HValue* other) override { return true; } private: HShr(HValue* context, HValue* left, HValue* right) : HBitwiseBinaryOperation(context, left, right) {} }; class HSar final : public HBitwiseBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right); Range* InferRange(Zone* zone) override; void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi()) new_rep = Representation::Integer32(); HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Sar) protected: bool DataEquals(HValue* other) override { return true; } private: HSar(HValue* context, HValue* left, HValue* right) : HBitwiseBinaryOperation(context, left, right) {} }; class HRor final : public HBitwiseBinaryOperation { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right) { return new (zone) HRor(context, left, right); } void UpdateRepresentation(Representation new_rep, HInferRepresentationPhase* h_infer, const char* reason) override { if (new_rep.IsSmi()) new_rep = Representation::Integer32(); HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason); } DECLARE_CONCRETE_INSTRUCTION(Ror) protected: bool DataEquals(HValue* other) override { return true; } private: HRor(HValue* context, HValue* left, HValue* right) : HBitwiseBinaryOperation(context, left, right) { ChangeRepresentation(Representation::Integer32()); } }; class HOsrEntry final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P1(HOsrEntry, BailoutId); BailoutId ast_id() const { return ast_id_; } Representation RequiredInputRepresentation(int index) override { return Representation::None(); } DECLARE_CONCRETE_INSTRUCTION(OsrEntry) private: explicit HOsrEntry(BailoutId ast_id) : ast_id_(ast_id) { SetChangesFlag(kOsrEntries); SetChangesFlag(kNewSpacePromotion); } BailoutId ast_id_; }; class HParameter final : public HTemplateInstruction<0> { public: enum ParameterKind { STACK_PARAMETER, REGISTER_PARAMETER }; DECLARE_INSTRUCTION_FACTORY_P1(HParameter, unsigned); DECLARE_INSTRUCTION_FACTORY_P2(HParameter, unsigned, ParameterKind); DECLARE_INSTRUCTION_FACTORY_P3(HParameter, unsigned, ParameterKind, Representation); unsigned index() const { return index_; } ParameterKind kind() const { return kind_; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::None(); } Representation KnownOptimalRepresentation() override { // If a parameter is an input to a phi, that phi should not // choose any more optimistic representation than Tagged. return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(Parameter) private: explicit HParameter(unsigned index, ParameterKind kind = STACK_PARAMETER) : index_(index), kind_(kind) { set_representation(Representation::Tagged()); } explicit HParameter(unsigned index, ParameterKind kind, Representation r) : index_(index), kind_(kind) { set_representation(r); } unsigned index_; ParameterKind kind_; }; class HUnknownOSRValue final : public HTemplateInstruction<0> { public: DECLARE_INSTRUCTION_FACTORY_P2(HUnknownOSRValue, HEnvironment*, int); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::None(); } void set_incoming_value(HPhi* value) { incoming_value_ = value; } HPhi* incoming_value() { return incoming_value_; } HEnvironment *environment() { return environment_; } int index() { return index_; } Representation KnownOptimalRepresentation() override { if (incoming_value_ == NULL) return Representation::None(); return incoming_value_->KnownOptimalRepresentation(); } DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue) private: HUnknownOSRValue(HEnvironment* environment, int index) : environment_(environment), index_(index), incoming_value_(NULL) { set_representation(Representation::Tagged()); } HEnvironment* environment_; int index_; HPhi* incoming_value_; }; class HAllocate final : public HTemplateInstruction<3> { public: static bool CompatibleInstanceTypes(InstanceType type1, InstanceType type2) { return ComputeFlags(TENURED, type1) == ComputeFlags(TENURED, type2) && ComputeFlags(NOT_TENURED, type1) == ComputeFlags(NOT_TENURED, type2); } static HAllocate* New( Isolate* isolate, Zone* zone, HValue* context, HValue* size, HType type, PretenureFlag pretenure_flag, InstanceType instance_type, HValue* dominator, Handle<AllocationSite> allocation_site = Handle<AllocationSite>::null()) { return new (zone) HAllocate(context, size, type, pretenure_flag, instance_type, dominator, allocation_site); } // Maximum instance size for which allocations will be inlined. static const int kMaxInlineSize = 64 * kPointerSize; HValue* context() const { return OperandAt(0); } HValue* size() const { return OperandAt(1); } HValue* allocation_folding_dominator() const { return OperandAt(2); } Representation RequiredInputRepresentation(int index) override { if (index == 0) { return Representation::Tagged(); } else { return Representation::Integer32(); } } Handle<Map> GetMonomorphicJSObjectMap() override { return known_initial_map_; } void set_known_initial_map(Handle<Map> known_initial_map) { known_initial_map_ = known_initial_map; } bool IsNewSpaceAllocation() const { return (flags_ & ALLOCATE_IN_NEW_SPACE) != 0; } bool IsOldSpaceAllocation() const { return (flags_ & ALLOCATE_IN_OLD_SPACE) != 0; } bool MustAllocateDoubleAligned() const { return (flags_ & ALLOCATE_DOUBLE_ALIGNED) != 0; } bool MustPrefillWithFiller() const { return (flags_ & PREFILL_WITH_FILLER) != 0; } void MakePrefillWithFiller() { flags_ = static_cast<HAllocate::Flags>(flags_ | PREFILL_WITH_FILLER); } void MakeDoubleAligned() { flags_ = static_cast<HAllocate::Flags>(flags_ | ALLOCATE_DOUBLE_ALIGNED); } void MakeAllocationFoldingDominator() { flags_ = static_cast<HAllocate::Flags>(flags_ | ALLOCATION_FOLDING_DOMINATOR); } bool IsAllocationFoldingDominator() const { return (flags_ & ALLOCATION_FOLDING_DOMINATOR) != 0; } void MakeFoldedAllocation(HAllocate* dominator) { flags_ = static_cast<HAllocate::Flags>(flags_ | ALLOCATION_FOLDED); ClearFlag(kTrackSideEffectDominators); ClearChangesFlag(kNewSpacePromotion); SetOperandAt(2, dominator); } bool IsAllocationFolded() const { return (flags_ & ALLOCATION_FOLDED) != 0; } bool HandleSideEffectDominator(GVNFlag side_effect, HValue* dominator) override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(Allocate) private: enum Flags { ALLOCATE_IN_NEW_SPACE = 1 << 0, ALLOCATE_IN_OLD_SPACE = 1 << 2, ALLOCATE_DOUBLE_ALIGNED = 1 << 3, PREFILL_WITH_FILLER = 1 << 4, ALLOCATION_FOLDING_DOMINATOR = 1 << 5, ALLOCATION_FOLDED = 1 << 6 }; HAllocate( HValue* context, HValue* size, HType type, PretenureFlag pretenure_flag, InstanceType instance_type, HValue* dominator, Handle<AllocationSite> allocation_site = Handle<AllocationSite>::null()) : HTemplateInstruction<3>(type), flags_(ComputeFlags(pretenure_flag, instance_type)) { SetOperandAt(0, context); UpdateSize(size); SetOperandAt(2, dominator); set_representation(Representation::Tagged()); SetFlag(kTrackSideEffectDominators); SetChangesFlag(kNewSpacePromotion); SetDependsOnFlag(kNewSpacePromotion); if (FLAG_trace_pretenuring) { PrintF("HAllocate with AllocationSite %p %s\n", allocation_site.is_null() ? static_cast<void*>(NULL) : static_cast<void*>(*allocation_site), pretenure_flag == TENURED ? "tenured" : "not tenured"); } } static Flags ComputeFlags(PretenureFlag pretenure_flag, InstanceType instance_type) { Flags flags = pretenure_flag == TENURED ? ALLOCATE_IN_OLD_SPACE : ALLOCATE_IN_NEW_SPACE; if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) { flags = static_cast<Flags>(flags | ALLOCATE_DOUBLE_ALIGNED); } // We have to fill the allocated object with one word fillers if we do // not use allocation folding since some allocations may depend on each // other, i.e., have a pointer to each other. A GC in between these // allocations may leave such objects behind in a not completely initialized // state. if (!FLAG_use_gvn || !FLAG_use_allocation_folding) { flags = static_cast<Flags>(flags | PREFILL_WITH_FILLER); } return flags; } void UpdateSize(HValue* size) { SetOperandAt(1, size); } bool IsFoldable(HAllocate* allocate) { return (IsNewSpaceAllocation() && allocate->IsNewSpaceAllocation()) || (IsOldSpaceAllocation() && allocate->IsOldSpaceAllocation()); } Flags flags_; Handle<Map> known_initial_map_; }; class HStoreCodeEntry final : public HTemplateInstruction<2> { public: static HStoreCodeEntry* New(Isolate* isolate, Zone* zone, HValue* context, HValue* function, HValue* code) { return new(zone) HStoreCodeEntry(function, code); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* function() { return OperandAt(0); } HValue* code_object() { return OperandAt(1); } DECLARE_CONCRETE_INSTRUCTION(StoreCodeEntry) private: HStoreCodeEntry(HValue* function, HValue* code) { SetOperandAt(0, function); SetOperandAt(1, code); } }; class HInnerAllocatedObject final : public HTemplateInstruction<2> { public: static HInnerAllocatedObject* New(Isolate* isolate, Zone* zone, HValue* context, HValue* value, HValue* offset, HType type) { return new(zone) HInnerAllocatedObject(value, offset, type); } HValue* base_object() const { return OperandAt(0); } HValue* offset() const { return OperandAt(1); } Representation RequiredInputRepresentation(int index) override { return index == 0 ? Representation::Tagged() : Representation::Integer32(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject) private: HInnerAllocatedObject(HValue* value, HValue* offset, HType type) : HTemplateInstruction<2>(type) { DCHECK(value->IsAllocate()); DCHECK(type.IsHeapObject()); SetOperandAt(0, value); SetOperandAt(1, offset); set_representation(Representation::Tagged()); } }; inline bool StoringValueNeedsWriteBarrier(HValue* value) { return !value->type().IsSmi() && !value->type().IsNull() && !value->type().IsBoolean() && !value->type().IsUndefined() && !(value->IsConstant() && HConstant::cast(value)->ImmortalImmovable()); } inline bool ReceiverObjectNeedsWriteBarrier(HValue* object, HValue* value, HValue* dominator) { // There may be multiple inner allocates dominated by one allocate. while (object->IsInnerAllocatedObject()) { object = HInnerAllocatedObject::cast(object)->base_object(); } if (object->IsAllocate()) { HAllocate* allocate = HAllocate::cast(object); if (allocate->IsAllocationFolded()) { HValue* dominator = allocate->allocation_folding_dominator(); // There is no guarantee that all allocations are folded together because // GVN performs a fixpoint. if (HAllocate::cast(dominator)->IsAllocationFoldingDominator()) { object = dominator; } } } if (object->IsConstant() && HConstant::cast(object)->HasExternalReferenceValue()) { // Stores to external references require no write barriers return false; } // We definitely need a write barrier unless the object is the allocation // dominator. if (object == dominator && object->IsAllocate()) { // Stores to new space allocations require no write barriers. if (HAllocate::cast(object)->IsNewSpaceAllocation()) { return false; } } return true; } inline PointersToHereCheck PointersToHereCheckForObject(HValue* object, HValue* dominator) { while (object->IsInnerAllocatedObject()) { object = HInnerAllocatedObject::cast(object)->base_object(); } if (object == dominator && object->IsAllocate() && HAllocate::cast(object)->IsNewSpaceAllocation()) { return kPointersToHereAreAlwaysInteresting; } return kPointersToHereMaybeInteresting; } class HLoadContextSlot final : public HUnaryOperation { public: enum Mode { // Perform a normal load of the context slot without checking its value. kNoCheck, // Load and check the value of the context slot. Deoptimize if it's the // hole value. This is used for checking for loading of uninitialized // harmony bindings where we deoptimize into full-codegen generated code // which will subsequently throw a reference error. kCheckDeoptimize }; HLoadContextSlot(HValue* context, int slot_index, Mode mode) : HUnaryOperation(context), slot_index_(slot_index), mode_(mode) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kContextSlots); } int slot_index() const { return slot_index_; } Mode mode() const { return mode_; } bool DeoptimizesOnHole() { return mode_ == kCheckDeoptimize; } bool RequiresHoleCheck() const { return mode_ != kNoCheck; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot) protected: bool DataEquals(HValue* other) override { HLoadContextSlot* b = HLoadContextSlot::cast(other); return (slot_index() == b->slot_index()); } private: bool IsDeletable() const override { return !RequiresHoleCheck(); } int slot_index_; Mode mode_; }; class HStoreContextSlot final : public HTemplateInstruction<2> { public: enum Mode { // Perform a normal store to the context slot without checking its previous // value. kNoCheck, // Check the previous value of the context slot and deoptimize if it's the // hole value. This is used for checking for assignments to uninitialized // harmony bindings where we deoptimize into full-codegen generated code // which will subsequently throw a reference error. kCheckDeoptimize }; DECLARE_INSTRUCTION_FACTORY_P4(HStoreContextSlot, HValue*, int, Mode, HValue*); HValue* context() const { return OperandAt(0); } HValue* value() const { return OperandAt(1); } int slot_index() const { return slot_index_; } Mode mode() const { return mode_; } bool NeedsWriteBarrier() { return StoringValueNeedsWriteBarrier(value()); } bool DeoptimizesOnHole() { return mode_ == kCheckDeoptimize; } bool RequiresHoleCheck() { return mode_ != kNoCheck; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot) private: HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value) : slot_index_(slot_index), mode_(mode) { SetOperandAt(0, context); SetOperandAt(1, value); SetChangesFlag(kContextSlots); } int slot_index_; Mode mode_; }; // Represents an access to a portion of an object, such as the map pointer, // array elements pointer, etc, but not accesses to array elements themselves. class HObjectAccess final { public: inline bool IsInobject() const { return portion() != kBackingStore && portion() != kExternalMemory; } inline bool IsExternalMemory() const { return portion() == kExternalMemory; } inline bool IsStringLength() const { return portion() == kStringLengths; } inline bool IsMap() const { return portion() == kMaps; } inline int offset() const { return OffsetField::decode(value_); } inline Representation representation() const { return Representation::FromKind(RepresentationField::decode(value_)); } inline Handle<Name> name() const { return name_; } inline bool immutable() const { return ImmutableField::decode(value_); } // Returns true if access is being made to an in-object property that // was already added to the object. inline bool existing_inobject_property() const { return ExistingInobjectPropertyField::decode(value_); } inline HObjectAccess WithRepresentation(Representation representation) { return HObjectAccess(portion(), offset(), representation, name(), immutable(), existing_inobject_property()); } static HObjectAccess ForHeapNumberValue() { return HObjectAccess( kDouble, HeapNumber::kValueOffset, Representation::Double()); } static HObjectAccess ForHeapNumberValueLowestBits() { return HObjectAccess(kDouble, HeapNumber::kValueOffset, Representation::Integer32()); } static HObjectAccess ForHeapNumberValueHighestBits() { return HObjectAccess(kDouble, HeapNumber::kValueOffset + kIntSize, Representation::Integer32()); } static HObjectAccess ForOddballToNumber( Representation representation = Representation::Tagged()) { return HObjectAccess(kInobject, Oddball::kToNumberOffset, representation); } static HObjectAccess ForOddballTypeOf() { return HObjectAccess(kInobject, Oddball::kTypeOfOffset, Representation::HeapObject()); } static HObjectAccess ForElementsPointer() { return HObjectAccess(kElementsPointer, JSObject::kElementsOffset); } static HObjectAccess ForLiteralsPointer() { return HObjectAccess(kInobject, JSFunction::kLiteralsOffset); } static HObjectAccess ForNextFunctionLinkPointer() { return HObjectAccess(kInobject, JSFunction::kNextFunctionLinkOffset); } static HObjectAccess ForArrayLength(ElementsKind elements_kind) { return HObjectAccess( kArrayLengths, JSArray::kLengthOffset, IsFastElementsKind(elements_kind) ? Representation::Smi() : Representation::Tagged()); } static HObjectAccess ForAllocationSiteOffset(int offset); static HObjectAccess ForAllocationSiteList() { return HObjectAccess(kExternalMemory, 0, Representation::Tagged(), Handle<Name>::null(), false, false); } static HObjectAccess ForFixedArrayLength() { return HObjectAccess( kArrayLengths, FixedArray::kLengthOffset, Representation::Smi()); } static HObjectAccess ForFixedTypedArrayBaseBasePointer() { return HObjectAccess(kInobject, FixedTypedArrayBase::kBasePointerOffset, Representation::Tagged()); } static HObjectAccess ForFixedTypedArrayBaseExternalPointer() { return HObjectAccess::ForObservableJSObjectOffset( FixedTypedArrayBase::kExternalPointerOffset, Representation::External()); } static HObjectAccess ForStringHashField() { return HObjectAccess(kInobject, String::kHashFieldOffset, Representation::Integer32()); } static HObjectAccess ForStringLength() { STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue); return HObjectAccess( kStringLengths, String::kLengthOffset, Representation::Smi()); } static HObjectAccess ForConsStringFirst() { return HObjectAccess(kInobject, ConsString::kFirstOffset); } static HObjectAccess ForConsStringSecond() { return HObjectAccess(kInobject, ConsString::kSecondOffset); } static HObjectAccess ForPropertiesPointer() { return HObjectAccess(kInobject, JSObject::kPropertiesOffset); } static HObjectAccess ForPrototypeOrInitialMap() { return HObjectAccess(kInobject, JSFunction::kPrototypeOrInitialMapOffset); } static HObjectAccess ForSharedFunctionInfoPointer() { return HObjectAccess(kInobject, JSFunction::kSharedFunctionInfoOffset); } static HObjectAccess ForCodeEntryPointer() { return HObjectAccess(kInobject, JSFunction::kCodeEntryOffset); } static HObjectAccess ForCodeOffset() { return HObjectAccess(kInobject, SharedFunctionInfo::kCodeOffset); } static HObjectAccess ForOptimizedCodeMap() { return HObjectAccess(kInobject, SharedFunctionInfo::kOptimizedCodeMapOffset); } static HObjectAccess ForFunctionContextPointer() { return HObjectAccess(kInobject, JSFunction::kContextOffset); } static HObjectAccess ForMap() { return HObjectAccess(kMaps, JSObject::kMapOffset); } static HObjectAccess ForPrototype() { return HObjectAccess(kMaps, Map::kPrototypeOffset); } static HObjectAccess ForMapAsInteger32() { return HObjectAccess(kMaps, JSObject::kMapOffset, Representation::Integer32()); } static HObjectAccess ForMapInObjectPropertiesOrConstructorFunctionIndex() { return HObjectAccess( kInobject, Map::kInObjectPropertiesOrConstructorFunctionIndexOffset, Representation::UInteger8()); } static HObjectAccess ForMapInstanceType() { return HObjectAccess(kInobject, Map::kInstanceTypeOffset, Representation::UInteger8()); } static HObjectAccess ForMapInstanceSize() { return HObjectAccess(kInobject, Map::kInstanceSizeOffset, Representation::UInteger8()); } static HObjectAccess ForMapBitField() { return HObjectAccess(kInobject, Map::kBitFieldOffset, Representation::UInteger8()); } static HObjectAccess ForMapBitField2() { return HObjectAccess(kInobject, Map::kBitField2Offset, Representation::UInteger8()); } static HObjectAccess ForMapBitField3() { return HObjectAccess(kInobject, Map::kBitField3Offset, Representation::Integer32()); } static HObjectAccess ForMapDescriptors() { return HObjectAccess(kInobject, Map::kDescriptorsOffset); } static HObjectAccess ForNameHashField() { return HObjectAccess(kInobject, Name::kHashFieldOffset, Representation::Integer32()); } static HObjectAccess ForMapInstanceTypeAndBitField() { STATIC_ASSERT((Map::kInstanceTypeAndBitFieldOffset & 1) == 0); // Ensure the two fields share one 16-bit word, endian-independent. STATIC_ASSERT((Map::kBitFieldOffset & ~1) == (Map::kInstanceTypeOffset & ~1)); return HObjectAccess(kInobject, Map::kInstanceTypeAndBitFieldOffset, Representation::UInteger16()); } static HObjectAccess ForPropertyCellValue() { return HObjectAccess(kInobject, PropertyCell::kValueOffset); } static HObjectAccess ForPropertyCellDetails() { return HObjectAccess(kInobject, PropertyCell::kDetailsOffset, Representation::Smi()); } static HObjectAccess ForCellValue() { return HObjectAccess(kInobject, Cell::kValueOffset); } static HObjectAccess ForWeakCellValue() { return HObjectAccess(kInobject, WeakCell::kValueOffset); } static HObjectAccess ForWeakCellNext() { return HObjectAccess(kInobject, WeakCell::kNextOffset); } static HObjectAccess ForAllocationMementoSite() { return HObjectAccess(kInobject, AllocationMemento::kAllocationSiteOffset); } static HObjectAccess ForCounter() { return HObjectAccess(kExternalMemory, 0, Representation::Integer32(), Handle<Name>::null(), false, false); } static HObjectAccess ForExternalUInteger8() { return HObjectAccess(kExternalMemory, 0, Representation::UInteger8(), Handle<Name>::null(), false, false); } static HObjectAccess ForBoundTargetFunction() { return HObjectAccess(kInobject, JSBoundFunction::kBoundTargetFunctionOffset); } static HObjectAccess ForBoundThis() { return HObjectAccess(kInobject, JSBoundFunction::kBoundThisOffset); } static HObjectAccess ForBoundArguments() { return HObjectAccess(kInobject, JSBoundFunction::kBoundArgumentsOffset); } // Create an access to an offset in a fixed array header. static HObjectAccess ForFixedArrayHeader(int offset); // Create an access to an in-object property in a JSObject. // This kind of access must be used when the object |map| is known and // in-object properties are being accessed. Accesses of the in-object // properties can have different semantics depending on whether corresponding // property was added to the map or not. static HObjectAccess ForMapAndOffset(Handle<Map> map, int offset, Representation representation = Representation::Tagged()); // Create an access to an in-object property in a JSObject. // This kind of access can be used for accessing object header fields or // in-object properties if the map of the object is not known. static HObjectAccess ForObservableJSObjectOffset(int offset, Representation representation = Representation::Tagged()) { return ForMapAndOffset(Handle<Map>::null(), offset, representation); } // Create an access to an in-object property in a JSArray. static HObjectAccess ForJSArrayOffset(int offset); static HObjectAccess ForContextSlot(int index); static HObjectAccess ForScriptContext(int index); // Create an access to the backing store of an object. static HObjectAccess ForBackingStoreOffset(int offset, Representation representation = Representation::Tagged()); // Create an access to a resolved field (in-object or backing store). static HObjectAccess ForField(Handle<Map> map, int index, Representation representation, Handle<Name> name); static HObjectAccess ForJSTypedArrayLength() { return HObjectAccess::ForObservableJSObjectOffset( JSTypedArray::kLengthOffset); } static HObjectAccess ForJSArrayBufferBackingStore() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBuffer::kBackingStoreOffset, Representation::External()); } static HObjectAccess ForJSArrayBufferByteLength() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBuffer::kByteLengthOffset, Representation::Tagged()); } static HObjectAccess ForJSArrayBufferBitField() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBuffer::kBitFieldOffset, Representation::Integer32()); } static HObjectAccess ForJSArrayBufferBitFieldSlot() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBuffer::kBitFieldSlot, Representation::Smi()); } static HObjectAccess ForJSArrayBufferViewBuffer() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBufferView::kBufferOffset); } static HObjectAccess ForJSArrayBufferViewByteOffset() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBufferView::kByteOffsetOffset); } static HObjectAccess ForJSArrayBufferViewByteLength() { return HObjectAccess::ForObservableJSObjectOffset( JSArrayBufferView::kByteLengthOffset); } static HObjectAccess ForJSGlobalObjectNativeContext() { return HObjectAccess(kInobject, JSGlobalObject::kNativeContextOffset); } static HObjectAccess ForJSRegExpFlags() { return HObjectAccess(kInobject, JSRegExp::kFlagsOffset); } static HObjectAccess ForJSRegExpSource() { return HObjectAccess(kInobject, JSRegExp::kSourceOffset); } static HObjectAccess ForJSCollectionTable() { return HObjectAccess::ForObservableJSObjectOffset( JSCollection::kTableOffset); } template <typename CollectionType> static HObjectAccess ForOrderedHashTableNumberOfBuckets() { return HObjectAccess(kInobject, CollectionType::kNumberOfBucketsOffset, Representation::Smi()); } template <typename CollectionType> static HObjectAccess ForOrderedHashTableNumberOfElements() { return HObjectAccess(kInobject, CollectionType::kNumberOfElementsOffset, Representation::Smi()); } template <typename CollectionType> static HObjectAccess ForOrderedHashTableNumberOfDeletedElements() { return HObjectAccess(kInobject, CollectionType::kNumberOfDeletedElementsOffset, Representation::Smi()); } template <typename CollectionType> static HObjectAccess ForOrderedHashTableNextTable() { return HObjectAccess(kInobject, CollectionType::kNextTableOffset); } template <typename CollectionType> static HObjectAccess ForOrderedHashTableBucket(int bucket) { return HObjectAccess(kInobject, CollectionType::kHashTableStartOffset + (bucket * kPointerSize), Representation::Smi()); } // Access into the data table of an OrderedHashTable with a // known-at-compile-time bucket count. template <typename CollectionType, int kBucketCount> static HObjectAccess ForOrderedHashTableDataTableIndex(int index) { return HObjectAccess(kInobject, CollectionType::kHashTableStartOffset + (kBucketCount * kPointerSize) + (index * kPointerSize)); } inline bool Equals(HObjectAccess that) const { return value_ == that.value_; // portion and offset must match } protected: void SetGVNFlags(HValue *instr, PropertyAccessType access_type); private: // internal use only; different parts of an object or array enum Portion { kMaps, // map of an object kArrayLengths, // the length of an array kStringLengths, // the length of a string kElementsPointer, // elements pointer kBackingStore, // some field in the backing store kDouble, // some double field kInobject, // some other in-object field kExternalMemory // some field in external memory }; HObjectAccess() : value_(0) {} HObjectAccess(Portion portion, int offset, Representation representation = Representation::Tagged(), Handle<Name> name = Handle<Name>::null(), bool immutable = false, bool existing_inobject_property = true) : value_(PortionField::encode(portion) | RepresentationField::encode(representation.kind()) | ImmutableField::encode(immutable ? 1 : 0) | ExistingInobjectPropertyField::encode( existing_inobject_property ? 1 : 0) | OffsetField::encode(offset)), name_(name) { // assert that the fields decode correctly DCHECK(this->offset() == offset); DCHECK(this->portion() == portion); DCHECK(this->immutable() == immutable); DCHECK(this->existing_inobject_property() == existing_inobject_property); DCHECK(RepresentationField::decode(value_) == representation.kind()); DCHECK(!this->existing_inobject_property() || IsInobject()); } class PortionField : public BitField<Portion, 0, 3> {}; class RepresentationField : public BitField<Representation::Kind, 3, 4> {}; class ImmutableField : public BitField<bool, 7, 1> {}; class ExistingInobjectPropertyField : public BitField<bool, 8, 1> {}; class OffsetField : public BitField<int, 9, 23> {}; uint32_t value_; // encodes portion, representation, immutable, and offset Handle<Name> name_; friend class HLoadNamedField; friend class HStoreNamedField; friend class SideEffectsTracker; friend std::ostream& operator<<(std::ostream& os, const HObjectAccess& access); inline Portion portion() const { return PortionField::decode(value_); } }; std::ostream& operator<<(std::ostream& os, const HObjectAccess& access); class HLoadNamedField final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P3(HLoadNamedField, HValue*, HValue*, HObjectAccess); DECLARE_INSTRUCTION_FACTORY_P5(HLoadNamedField, HValue*, HValue*, HObjectAccess, const UniqueSet<Map>*, HType); HValue* object() const { return OperandAt(0); } HValue* dependency() const { DCHECK(HasDependency()); return OperandAt(1); } bool HasDependency() const { return OperandAt(0) != OperandAt(1); } HObjectAccess access() const { return access_; } Representation field_representation() const { return access_.representation(); } const UniqueSet<Map>* maps() const { return maps_; } bool HasEscapingOperandAt(int index) override { return false; } bool HasOutOfBoundsAccess(int size) override { return !access().IsInobject() || access().offset() >= size; } Representation RequiredInputRepresentation(int index) override { if (index == 0) { // object must be external in case of external memory access return access().IsExternalMemory() ? Representation::External() : Representation::Tagged(); } DCHECK(index == 1); return Representation::None(); } Range* InferRange(Zone* zone) override; std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT bool CanBeReplacedWith(HValue* other) const { if (!CheckFlag(HValue::kCantBeReplaced)) return false; if (!type().Equals(other->type())) return false; if (!representation().Equals(other->representation())) return false; if (!other->IsLoadNamedField()) return true; HLoadNamedField* that = HLoadNamedField::cast(other); if (this->maps_ == that->maps_) return true; if (this->maps_ == NULL || that->maps_ == NULL) return false; return this->maps_->IsSubset(that->maps_); } DECLARE_CONCRETE_INSTRUCTION(LoadNamedField) protected: bool DataEquals(HValue* other) override { HLoadNamedField* that = HLoadNamedField::cast(other); if (!this->access_.Equals(that->access_)) return false; if (this->maps_ == that->maps_) return true; return (this->maps_ != NULL && that->maps_ != NULL && this->maps_->Equals(that->maps_)); } private: HLoadNamedField(HValue* object, HValue* dependency, HObjectAccess access) : access_(access), maps_(NULL) { DCHECK_NOT_NULL(object); SetOperandAt(0, object); SetOperandAt(1, dependency ? dependency : object); Representation representation = access.representation(); if (representation.IsInteger8() || representation.IsUInteger8() || representation.IsInteger16() || representation.IsUInteger16()) { set_representation(Representation::Integer32()); } else if (representation.IsSmi()) { set_type(HType::Smi()); if (SmiValuesAre32Bits()) { set_representation(Representation::Integer32()); } else { set_representation(representation); } } else if (representation.IsDouble() || representation.IsExternal() || representation.IsInteger32()) { set_representation(representation); } else if (representation.IsHeapObject()) { set_type(HType::HeapObject()); set_representation(Representation::Tagged()); } else { set_representation(Representation::Tagged()); } access.SetGVNFlags(this, LOAD); } HLoadNamedField(HValue* object, HValue* dependency, HObjectAccess access, const UniqueSet<Map>* maps, HType type) : HTemplateInstruction<2>(type), access_(access), maps_(maps) { DCHECK_NOT_NULL(maps); DCHECK_NE(0, maps->size()); DCHECK_NOT_NULL(object); SetOperandAt(0, object); SetOperandAt(1, dependency ? dependency : object); DCHECK(access.representation().IsHeapObject()); DCHECK(type.IsHeapObject()); set_representation(Representation::Tagged()); access.SetGVNFlags(this, LOAD); } bool IsDeletable() const override { return true; } HObjectAccess access_; const UniqueSet<Map>* maps_; }; class HLoadFunctionPrototype final : public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HLoadFunctionPrototype, HValue*); HValue* function() { return OperandAt(0); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HLoadFunctionPrototype(HValue* function) : HUnaryOperation(function) { set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kCalls); } }; class ArrayInstructionInterface { public: virtual HValue* GetKey() = 0; virtual void SetKey(HValue* key) = 0; virtual ElementsKind elements_kind() const = 0; // TryIncreaseBaseOffset returns false if overflow would result. virtual bool TryIncreaseBaseOffset(uint32_t increase_by_value) = 0; virtual bool IsDehoisted() const = 0; virtual void SetDehoisted(bool is_dehoisted) = 0; virtual ~ArrayInstructionInterface() { } static Representation KeyedAccessIndexRequirement(Representation r) { return r.IsInteger32() || SmiValuesAre32Bits() ? Representation::Integer32() : Representation::Smi(); } }; static const int kDefaultKeyedHeaderOffsetSentinel = -1; enum LoadKeyedHoleMode { NEVER_RETURN_HOLE, ALLOW_RETURN_HOLE, CONVERT_HOLE_TO_UNDEFINED }; class HLoadKeyed final : public HTemplateInstruction<4>, public ArrayInstructionInterface { public: DECLARE_INSTRUCTION_FACTORY_P5(HLoadKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind); DECLARE_INSTRUCTION_FACTORY_P6(HLoadKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, LoadKeyedHoleMode); DECLARE_INSTRUCTION_FACTORY_P7(HLoadKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, LoadKeyedHoleMode, int); bool is_fixed_typed_array() const { return IsFixedTypedArrayElementsKind(elements_kind()); } HValue* elements() const { return OperandAt(0); } HValue* key() const { return OperandAt(1); } HValue* dependency() const { DCHECK(HasDependency()); return OperandAt(2); } bool HasDependency() const { return OperandAt(0) != OperandAt(2); } HValue* backing_store_owner() const { DCHECK(HasBackingStoreOwner()); return OperandAt(3); } bool HasBackingStoreOwner() const { return OperandAt(0) != OperandAt(3); } uint32_t base_offset() const { return BaseOffsetField::decode(bit_field_); } bool TryIncreaseBaseOffset(uint32_t increase_by_value) override; HValue* GetKey() override { return key(); } void SetKey(HValue* key) override { SetOperandAt(1, key); } bool IsDehoisted() const override { return IsDehoistedField::decode(bit_field_); } void SetDehoisted(bool is_dehoisted) override { bit_field_ = IsDehoistedField::update(bit_field_, is_dehoisted); } ElementsKind elements_kind() const override { return ElementsKindField::decode(bit_field_); } LoadKeyedHoleMode hole_mode() const { return HoleModeField::decode(bit_field_); } Representation RequiredInputRepresentation(int index) override { // kind_fast: tagged[int32] (none) // kind_double: tagged[int32] (none) // kind_fixed_typed_array: external[int32] (none) // kind_external: external[int32] (none) if (index == 0) { return is_fixed_typed_array() ? Representation::External() : Representation::Tagged(); } if (index == 1) { return ArrayInstructionInterface::KeyedAccessIndexRequirement( OperandAt(1)->representation()); } if (index == 2) { return Representation::None(); } DCHECK_EQ(3, index); return HasBackingStoreOwner() ? Representation::Tagged() : Representation::None(); } Representation observed_input_representation(int index) override { return RequiredInputRepresentation(index); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT bool UsesMustHandleHole() const; bool AllUsesCanTreatHoleAsNaN() const; bool RequiresHoleCheck() const; Range* InferRange(Zone* zone) override; DECLARE_CONCRETE_INSTRUCTION(LoadKeyed) protected: bool DataEquals(HValue* other) override { if (!other->IsLoadKeyed()) return false; HLoadKeyed* other_load = HLoadKeyed::cast(other); if (base_offset() != other_load->base_offset()) return false; return elements_kind() == other_load->elements_kind(); } private: HLoadKeyed(HValue* obj, HValue* key, HValue* dependency, HValue* backing_store_owner, ElementsKind elements_kind, LoadKeyedHoleMode mode = NEVER_RETURN_HOLE, int offset = kDefaultKeyedHeaderOffsetSentinel) : bit_field_(0) { offset = offset == kDefaultKeyedHeaderOffsetSentinel ? GetDefaultHeaderSizeForElementsKind(elements_kind) : offset; bit_field_ = ElementsKindField::encode(elements_kind) | HoleModeField::encode(mode) | BaseOffsetField::encode(offset); SetOperandAt(0, obj); SetOperandAt(1, key); SetOperandAt(2, dependency != nullptr ? dependency : obj); SetOperandAt(3, backing_store_owner != nullptr ? backing_store_owner : obj); DCHECK_EQ(HasBackingStoreOwner(), is_fixed_typed_array()); if (!is_fixed_typed_array()) { // I can detect the case between storing double (holey and fast) and // smi/object by looking at elements_kind_. DCHECK(IsFastSmiOrObjectElementsKind(elements_kind) || IsFastDoubleElementsKind(elements_kind)); if (IsFastSmiOrObjectElementsKind(elements_kind)) { if (IsFastSmiElementsKind(elements_kind) && (!IsHoleyElementsKind(elements_kind) || mode == NEVER_RETURN_HOLE)) { set_type(HType::Smi()); if (SmiValuesAre32Bits() && !RequiresHoleCheck()) { set_representation(Representation::Integer32()); } else { set_representation(Representation::Smi()); } } else { set_representation(Representation::Tagged()); } SetDependsOnFlag(kArrayElements); } else { set_representation(Representation::Double()); SetDependsOnFlag(kDoubleArrayElements); } } else { if (elements_kind == FLOAT32_ELEMENTS || elements_kind == FLOAT64_ELEMENTS) { set_representation(Representation::Double()); } else { set_representation(Representation::Integer32()); } if (is_fixed_typed_array()) { SetDependsOnFlag(kExternalMemory); SetDependsOnFlag(kTypedArrayElements); } else { UNREACHABLE(); } // Native code could change the specialized array. SetDependsOnFlag(kCalls); } SetFlag(kUseGVN); } bool IsDeletable() const override { return !RequiresHoleCheck(); } // Establish some checks around our packed fields enum LoadKeyedBits { kBitsForElementsKind = 5, kBitsForHoleMode = 2, kBitsForBaseOffset = 24, kBitsForIsDehoisted = 1, kStartElementsKind = 0, kStartHoleMode = kStartElementsKind + kBitsForElementsKind, kStartBaseOffset = kStartHoleMode + kBitsForHoleMode, kStartIsDehoisted = kStartBaseOffset + kBitsForBaseOffset }; STATIC_ASSERT((kBitsForElementsKind + kBitsForHoleMode + kBitsForBaseOffset + kBitsForIsDehoisted) <= sizeof(uint32_t) * 8); STATIC_ASSERT(kElementsKindCount <= (1 << kBitsForElementsKind)); class ElementsKindField: public BitField<ElementsKind, kStartElementsKind, kBitsForElementsKind> {}; // NOLINT class HoleModeField: public BitField<LoadKeyedHoleMode, kStartHoleMode, kBitsForHoleMode> {}; // NOLINT class BaseOffsetField: public BitField<uint32_t, kStartBaseOffset, kBitsForBaseOffset> {}; // NOLINT class IsDehoistedField: public BitField<bool, kStartIsDehoisted, kBitsForIsDehoisted> {}; // NOLINT uint32_t bit_field_; }; // Indicates whether the store is a store to an entry that was previously // initialized or not. enum StoreFieldOrKeyedMode { // The entry could be either previously initialized or not. INITIALIZING_STORE, // At the time of this store it is guaranteed that the entry is already // initialized. STORE_TO_INITIALIZED_ENTRY }; class HStoreNamedField final : public HTemplateInstruction<3> { public: DECLARE_INSTRUCTION_FACTORY_P3(HStoreNamedField, HValue*, HObjectAccess, HValue*); DECLARE_INSTRUCTION_FACTORY_P4(HStoreNamedField, HValue*, HObjectAccess, HValue*, StoreFieldOrKeyedMode); DECLARE_CONCRETE_INSTRUCTION(StoreNamedField) bool HasEscapingOperandAt(int index) override { return index == 1; } bool HasOutOfBoundsAccess(int size) override { return !access().IsInobject() || access().offset() >= size; } Representation RequiredInputRepresentation(int index) override { if (index == 0 && access().IsExternalMemory()) { // object must be external in case of external memory access return Representation::External(); } else if (index == 1) { if (field_representation().IsInteger8() || field_representation().IsUInteger8() || field_representation().IsInteger16() || field_representation().IsUInteger16() || field_representation().IsInteger32()) { return Representation::Integer32(); } else if (field_representation().IsDouble()) { return field_representation(); } else if (field_representation().IsSmi()) { if (SmiValuesAre32Bits() && store_mode() == STORE_TO_INITIALIZED_ENTRY) { return Representation::Integer32(); } return field_representation(); } else if (field_representation().IsExternal()) { return Representation::External(); } } return Representation::Tagged(); } bool HandleSideEffectDominator(GVNFlag side_effect, HValue* dominator) override { DCHECK(side_effect == kNewSpacePromotion); if (!FLAG_use_write_barrier_elimination) return false; dominator_ = dominator; return false; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HValue* object() const { return OperandAt(0); } HValue* value() const { return OperandAt(1); } HValue* transition() const { return OperandAt(2); } HObjectAccess access() const { return access_; } HValue* dominator() const { return dominator_; } bool has_transition() const { return HasTransitionField::decode(bit_field_); } StoreFieldOrKeyedMode store_mode() const { return StoreModeField::decode(bit_field_); } Handle<Map> transition_map() const { if (has_transition()) { return Handle<Map>::cast( HConstant::cast(transition())->handle(isolate())); } else { return Handle<Map>(); } } void SetTransition(HConstant* transition) { DCHECK(!has_transition()); // Only set once. SetOperandAt(2, transition); bit_field_ = HasTransitionField::update(bit_field_, true); SetChangesFlag(kMaps); } bool NeedsWriteBarrier() const { DCHECK(!field_representation().IsDouble() || (FLAG_unbox_double_fields && access_.IsInobject()) || !has_transition()); if (field_representation().IsDouble()) return false; if (field_representation().IsSmi()) return false; if (field_representation().IsInteger32()) return false; if (field_representation().IsExternal()) return false; return StoringValueNeedsWriteBarrier(value()) && ReceiverObjectNeedsWriteBarrier(object(), value(), dominator()); } bool NeedsWriteBarrierForMap() { return ReceiverObjectNeedsWriteBarrier(object(), transition(), dominator()); } SmiCheck SmiCheckForWriteBarrier() const { if (field_representation().IsHeapObject()) return OMIT_SMI_CHECK; if (value()->type().IsHeapObject()) return OMIT_SMI_CHECK; return INLINE_SMI_CHECK; } PointersToHereCheck PointersToHereCheckForValue() const { return PointersToHereCheckForObject(value(), dominator()); } Representation field_representation() const { return access_.representation(); } void UpdateValue(HValue* value) { SetOperandAt(1, value); } bool CanBeReplacedWith(HStoreNamedField* that) const { if (!this->access().Equals(that->access())) return false; if (SmiValuesAre32Bits() && this->field_representation().IsSmi() && this->store_mode() == INITIALIZING_STORE && that->store_mode() == STORE_TO_INITIALIZED_ENTRY) { // We cannot replace an initializing store to a smi field with a store to // an initialized entry on 64-bit architectures (with 32-bit smis). return false; } return true; } private: HStoreNamedField(HValue* obj, HObjectAccess access, HValue* val, StoreFieldOrKeyedMode store_mode = INITIALIZING_STORE) : access_(access), dominator_(NULL), bit_field_(HasTransitionField::encode(false) | StoreModeField::encode(store_mode)) { // Stores to a non existing in-object property are allowed only to the // newly allocated objects (via HAllocate or HInnerAllocatedObject). DCHECK(!access.IsInobject() || access.existing_inobject_property() || obj->IsAllocate() || obj->IsInnerAllocatedObject()); SetOperandAt(0, obj); SetOperandAt(1, val); SetOperandAt(2, obj); access.SetGVNFlags(this, STORE); } class HasTransitionField : public BitField<bool, 0, 1> {}; class StoreModeField : public BitField<StoreFieldOrKeyedMode, 1, 1> {}; HObjectAccess access_; HValue* dominator_; uint32_t bit_field_; }; class HStoreKeyed final : public HTemplateInstruction<4>, public ArrayInstructionInterface { public: DECLARE_INSTRUCTION_FACTORY_P5(HStoreKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind); DECLARE_INSTRUCTION_FACTORY_P6(HStoreKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, StoreFieldOrKeyedMode); DECLARE_INSTRUCTION_FACTORY_P7(HStoreKeyed, HValue*, HValue*, HValue*, HValue*, ElementsKind, StoreFieldOrKeyedMode, int); Representation RequiredInputRepresentation(int index) override { // kind_fast: tagged[int32] = tagged // kind_double: tagged[int32] = double // kind_smi : tagged[int32] = smi // kind_fixed_typed_array: tagged[int32] = (double | int32) // kind_external: external[int32] = (double | int32) if (index == 0) { return is_fixed_typed_array() ? Representation::External() : Representation::Tagged(); } else if (index == 1) { return ArrayInstructionInterface::KeyedAccessIndexRequirement( OperandAt(1)->representation()); } else if (index == 2) { return RequiredValueRepresentation(elements_kind(), store_mode()); } DCHECK_EQ(3, index); return HasBackingStoreOwner() ? Representation::Tagged() : Representation::None(); } static Representation RequiredValueRepresentation( ElementsKind kind, StoreFieldOrKeyedMode mode) { if (IsDoubleOrFloatElementsKind(kind)) { return Representation::Double(); } if (kind == FAST_SMI_ELEMENTS && SmiValuesAre32Bits() && mode == STORE_TO_INITIALIZED_ENTRY) { return Representation::Integer32(); } if (IsFastSmiElementsKind(kind)) { return Representation::Smi(); } if (IsFixedTypedArrayElementsKind(kind)) { return Representation::Integer32(); } return Representation::Tagged(); } bool is_fixed_typed_array() const { return IsFixedTypedArrayElementsKind(elements_kind()); } Representation observed_input_representation(int index) override { if (index != 2) return RequiredInputRepresentation(index); if (IsUninitialized()) { return Representation::None(); } Representation r = RequiredValueRepresentation(elements_kind(), store_mode()); // For fast object elements kinds, don't assume anything. if (r.IsTagged()) return Representation::None(); return r; } HValue* elements() const { return OperandAt(0); } HValue* key() const { return OperandAt(1); } HValue* value() const { return OperandAt(2); } HValue* backing_store_owner() const { DCHECK(HasBackingStoreOwner()); return OperandAt(3); } bool HasBackingStoreOwner() const { return OperandAt(0) != OperandAt(3); } bool value_is_smi() const { return IsFastSmiElementsKind(elements_kind()); } StoreFieldOrKeyedMode store_mode() const { return StoreModeField::decode(bit_field_); } ElementsKind elements_kind() const override { return ElementsKindField::decode(bit_field_); } uint32_t base_offset() const { return base_offset_; } bool TryIncreaseBaseOffset(uint32_t increase_by_value) override; HValue* GetKey() override { return key(); } void SetKey(HValue* key) override { SetOperandAt(1, key); } bool IsDehoisted() const override { return IsDehoistedField::decode(bit_field_); } void SetDehoisted(bool is_dehoisted) override { bit_field_ = IsDehoistedField::update(bit_field_, is_dehoisted); } bool IsUninitialized() { return IsUninitializedField::decode(bit_field_); } void SetUninitialized(bool is_uninitialized) { bit_field_ = IsUninitializedField::update(bit_field_, is_uninitialized); } bool IsConstantHoleStore() { return value()->IsConstant() && HConstant::cast(value())->IsTheHole(); } bool HandleSideEffectDominator(GVNFlag side_effect, HValue* dominator) override { DCHECK(side_effect == kNewSpacePromotion); dominator_ = dominator; return false; } HValue* dominator() const { return dominator_; } bool NeedsWriteBarrier() { if (value_is_smi()) { return false; } else { return StoringValueNeedsWriteBarrier(value()) && ReceiverObjectNeedsWriteBarrier(elements(), value(), dominator()); } } PointersToHereCheck PointersToHereCheckForValue() const { return PointersToHereCheckForObject(value(), dominator()); } bool NeedsCanonicalization(); std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(StoreKeyed) private: HStoreKeyed(HValue* obj, HValue* key, HValue* val, HValue* backing_store_owner, ElementsKind elements_kind, StoreFieldOrKeyedMode store_mode = INITIALIZING_STORE, int offset = kDefaultKeyedHeaderOffsetSentinel) : base_offset_(offset == kDefaultKeyedHeaderOffsetSentinel ? GetDefaultHeaderSizeForElementsKind(elements_kind) : offset), bit_field_(IsDehoistedField::encode(false) | IsUninitializedField::encode(false) | StoreModeField::encode(store_mode) | ElementsKindField::encode(elements_kind)), dominator_(NULL) { SetOperandAt(0, obj); SetOperandAt(1, key); SetOperandAt(2, val); SetOperandAt(3, backing_store_owner != nullptr ? backing_store_owner : obj); DCHECK_EQ(HasBackingStoreOwner(), is_fixed_typed_array()); if (IsFastObjectElementsKind(elements_kind)) { SetFlag(kTrackSideEffectDominators); SetDependsOnFlag(kNewSpacePromotion); } if (IsFastDoubleElementsKind(elements_kind)) { SetChangesFlag(kDoubleArrayElements); } else if (IsFastSmiElementsKind(elements_kind)) { SetChangesFlag(kArrayElements); } else if (is_fixed_typed_array()) { SetChangesFlag(kTypedArrayElements); SetChangesFlag(kExternalMemory); SetFlag(kTruncatingToNumber); } else { SetChangesFlag(kArrayElements); } // {UNSIGNED_,}{BYTE,SHORT,INT}_ELEMENTS are truncating. if (elements_kind >= UINT8_ELEMENTS && elements_kind <= INT32_ELEMENTS) { SetFlag(kTruncatingToInt32); } } class IsDehoistedField : public BitField<bool, 0, 1> {}; class IsUninitializedField : public BitField<bool, 1, 1> {}; class StoreModeField : public BitField<StoreFieldOrKeyedMode, 2, 1> {}; class ElementsKindField : public BitField<ElementsKind, 3, 5> {}; uint32_t base_offset_; uint32_t bit_field_; HValue* dominator_; }; class HTransitionElementsKind final : public HTemplateInstruction<2> { public: inline static HTransitionElementsKind* New(Isolate* isolate, Zone* zone, HValue* context, HValue* object, Handle<Map> original_map, Handle<Map> transitioned_map) { return new(zone) HTransitionElementsKind(context, object, original_map, transitioned_map); } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* object() const { return OperandAt(0); } HValue* context() const { return OperandAt(1); } Unique<Map> original_map() const { return original_map_; } Unique<Map> transitioned_map() const { return transitioned_map_; } ElementsKind from_kind() const { return FromElementsKindField::decode(bit_field_); } ElementsKind to_kind() const { return ToElementsKindField::decode(bit_field_); } bool map_is_stable() const { return MapIsStableField::decode(bit_field_); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind) protected: bool DataEquals(HValue* other) override { HTransitionElementsKind* instr = HTransitionElementsKind::cast(other); return original_map_ == instr->original_map_ && transitioned_map_ == instr->transitioned_map_; } int RedefinedOperandIndex() override { return 0; } private: HTransitionElementsKind(HValue* context, HValue* object, Handle<Map> original_map, Handle<Map> transitioned_map) : original_map_(Unique<Map>(original_map)), transitioned_map_(Unique<Map>(transitioned_map)), bit_field_( FromElementsKindField::encode(original_map->elements_kind()) | ToElementsKindField::encode(transitioned_map->elements_kind()) | MapIsStableField::encode(transitioned_map->is_stable())) { SetOperandAt(0, object); SetOperandAt(1, context); SetFlag(kUseGVN); SetChangesFlag(kElementsKind); if (!IsSimpleMapChangeTransition(from_kind(), to_kind())) { SetChangesFlag(kElementsPointer); SetChangesFlag(kNewSpacePromotion); } set_representation(Representation::Tagged()); } class FromElementsKindField : public BitField<ElementsKind, 0, 5> {}; class ToElementsKindField : public BitField<ElementsKind, 5, 5> {}; class MapIsStableField : public BitField<bool, 10, 1> {}; Unique<Map> original_map_; Unique<Map> transitioned_map_; uint32_t bit_field_; }; class HStringAdd final : public HBinaryOperation { public: static HInstruction* New( Isolate* isolate, Zone* zone, HValue* context, HValue* left, HValue* right, PretenureFlag pretenure_flag = NOT_TENURED, StringAddFlags flags = STRING_ADD_CHECK_BOTH, Handle<AllocationSite> allocation_site = Handle<AllocationSite>::null()); StringAddFlags flags() const { return flags_; } PretenureFlag pretenure_flag() const { return pretenure_flag_; } Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT DECLARE_CONCRETE_INSTRUCTION(StringAdd) protected: bool DataEquals(HValue* other) override { return flags_ == HStringAdd::cast(other)->flags_ && pretenure_flag_ == HStringAdd::cast(other)->pretenure_flag_; } private: HStringAdd(HValue* context, HValue* left, HValue* right, PretenureFlag pretenure_flag, StringAddFlags flags, Handle<AllocationSite> allocation_site) : HBinaryOperation(context, left, right, HType::String()), flags_(flags), pretenure_flag_(pretenure_flag) { set_representation(Representation::Tagged()); if ((flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT) { SetAllSideEffects(); ClearFlag(kUseGVN); } else { SetChangesFlag(kNewSpacePromotion); SetFlag(kUseGVN); } SetDependsOnFlag(kMaps); if (FLAG_trace_pretenuring) { PrintF("HStringAdd with AllocationSite %p %s\n", allocation_site.is_null() ? static_cast<void*>(NULL) : static_cast<void*>(*allocation_site), pretenure_flag == TENURED ? "tenured" : "not tenured"); } } bool IsDeletable() const final { return (flags_ & STRING_ADD_CONVERT) != STRING_ADD_CONVERT; } const StringAddFlags flags_; const PretenureFlag pretenure_flag_; }; class HStringCharCodeAt final : public HTemplateInstruction<3> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HStringCharCodeAt, HValue*, HValue*); Representation RequiredInputRepresentation(int index) override { // The index is supposed to be Integer32. return index == 2 ? Representation::Integer32() : Representation::Tagged(); } HValue* context() const { return OperandAt(0); } HValue* string() const { return OperandAt(1); } HValue* index() const { return OperandAt(2); } DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt) protected: bool DataEquals(HValue* other) override { return true; } Range* InferRange(Zone* zone) override { return new(zone) Range(0, String::kMaxUtf16CodeUnit); } private: HStringCharCodeAt(HValue* context, HValue* string, HValue* index) { SetOperandAt(0, context); SetOperandAt(1, string); SetOperandAt(2, index); set_representation(Representation::Integer32()); SetFlag(kUseGVN); SetDependsOnFlag(kMaps); SetDependsOnFlag(kStringChars); SetChangesFlag(kNewSpacePromotion); } // No side effects: runtime function assumes string + number inputs. bool IsDeletable() const override { return true; } }; class HStringCharFromCode final : public HTemplateInstruction<2> { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, HValue* char_code); Representation RequiredInputRepresentation(int index) override { return index == 0 ? Representation::Tagged() : Representation::Integer32(); } HValue* context() const { return OperandAt(0); } HValue* value() const { return OperandAt(1); } bool DataEquals(HValue* other) override { return true; } DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode) private: HStringCharFromCode(HValue* context, HValue* char_code) : HTemplateInstruction<2>(HType::String()) { SetOperandAt(0, context); SetOperandAt(1, char_code); set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetChangesFlag(kNewSpacePromotion); } bool IsDeletable() const override { return !value()->ToNumberCanBeObserved(); } }; class HTypeof final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HTypeof, HValue*); HValue* context() const { return OperandAt(0); } HValue* value() const { return OperandAt(1); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(Typeof) private: explicit HTypeof(HValue* context, HValue* value) { SetOperandAt(0, context); SetOperandAt(1, value); set_representation(Representation::Tagged()); } bool IsDeletable() const override { return true; } }; class HTrapAllocationMemento final : public HTemplateInstruction<1> { public: DECLARE_INSTRUCTION_FACTORY_P1(HTrapAllocationMemento, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* object() { return OperandAt(0); } DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento) private: explicit HTrapAllocationMemento(HValue* obj) { SetOperandAt(0, obj); } }; class HMaybeGrowElements final : public HTemplateInstruction<5> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P6(HMaybeGrowElements, HValue*, HValue*, HValue*, HValue*, bool, ElementsKind); Representation RequiredInputRepresentation(int index) override { if (index < 3) { return Representation::Tagged(); } DCHECK(index == 3 || index == 4); return Representation::Integer32(); } HValue* context() const { return OperandAt(0); } HValue* object() const { return OperandAt(1); } HValue* elements() const { return OperandAt(2); } HValue* key() const { return OperandAt(3); } HValue* current_capacity() const { return OperandAt(4); } bool is_js_array() const { return is_js_array_; } ElementsKind kind() const { return kind_; } DECLARE_CONCRETE_INSTRUCTION(MaybeGrowElements) protected: bool DataEquals(HValue* other) override { return true; } private: explicit HMaybeGrowElements(HValue* context, HValue* object, HValue* elements, HValue* key, HValue* current_capacity, bool is_js_array, ElementsKind kind) { is_js_array_ = is_js_array; kind_ = kind; SetOperandAt(0, context); SetOperandAt(1, object); SetOperandAt(2, elements); SetOperandAt(3, key); SetOperandAt(4, current_capacity); SetFlag(kUseGVN); SetChangesFlag(kElementsPointer); SetChangesFlag(kNewSpacePromotion); set_representation(Representation::Tagged()); } bool is_js_array_; ElementsKind kind_; }; class HSeqStringGetChar final : public HTemplateInstruction<2> { public: static HInstruction* New(Isolate* isolate, Zone* zone, HValue* context, String::Encoding encoding, HValue* string, HValue* index); Representation RequiredInputRepresentation(int index) override { return (index == 0) ? Representation::Tagged() : Representation::Integer32(); } String::Encoding encoding() const { return encoding_; } HValue* string() const { return OperandAt(0); } HValue* index() const { return OperandAt(1); } DECLARE_CONCRETE_INSTRUCTION(SeqStringGetChar) protected: bool DataEquals(HValue* other) override { return encoding() == HSeqStringGetChar::cast(other)->encoding(); } Range* InferRange(Zone* zone) override { if (encoding() == String::ONE_BYTE_ENCODING) { return new(zone) Range(0, String::kMaxOneByteCharCode); } else { DCHECK_EQ(String::TWO_BYTE_ENCODING, encoding()); return new(zone) Range(0, String::kMaxUtf16CodeUnit); } } private: HSeqStringGetChar(String::Encoding encoding, HValue* string, HValue* index) : encoding_(encoding) { SetOperandAt(0, string); SetOperandAt(1, index); set_representation(Representation::Integer32()); SetFlag(kUseGVN); SetDependsOnFlag(kStringChars); } bool IsDeletable() const override { return true; } String::Encoding encoding_; }; class HSeqStringSetChar final : public HTemplateInstruction<4> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4( HSeqStringSetChar, String::Encoding, HValue*, HValue*, HValue*); String::Encoding encoding() { return encoding_; } HValue* context() { return OperandAt(0); } HValue* string() { return OperandAt(1); } HValue* index() { return OperandAt(2); } HValue* value() { return OperandAt(3); } Representation RequiredInputRepresentation(int index) override { return (index <= 1) ? Representation::Tagged() : Representation::Integer32(); } DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar) private: HSeqStringSetChar(HValue* context, String::Encoding encoding, HValue* string, HValue* index, HValue* value) : encoding_(encoding) { SetOperandAt(0, context); SetOperandAt(1, string); SetOperandAt(2, index); SetOperandAt(3, value); set_representation(Representation::Tagged()); SetChangesFlag(kStringChars); } String::Encoding encoding_; }; class HCheckMapValue final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HCheckMapValue, HValue*, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HType CalculateInferredType() override { if (value()->type().IsHeapObject()) return value()->type(); return HType::HeapObject(); } HValue* value() const { return OperandAt(0); } HValue* map() const { return OperandAt(1); } HValue* Canonicalize() override; DECLARE_CONCRETE_INSTRUCTION(CheckMapValue) protected: int RedefinedOperandIndex() override { return 0; } bool DataEquals(HValue* other) override { return true; } private: HCheckMapValue(HValue* value, HValue* map) : HTemplateInstruction<2>(HType::HeapObject()) { SetOperandAt(0, value); SetOperandAt(1, map); set_representation(Representation::Tagged()); SetFlag(kUseGVN); SetDependsOnFlag(kMaps); SetDependsOnFlag(kElementsKind); } }; class HForInPrepareMap final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HForInPrepareMap, HValue*); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* context() const { return OperandAt(0); } HValue* enumerable() const { return OperandAt(1); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HType CalculateInferredType() override { return HType::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap); private: HForInPrepareMap(HValue* context, HValue* object) { SetOperandAt(0, context); SetOperandAt(1, object); set_representation(Representation::Tagged()); SetAllSideEffects(); } }; class HForInCacheArray final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P3(HForInCacheArray, HValue*, HValue*, int); Representation RequiredInputRepresentation(int index) override { return Representation::Tagged(); } HValue* enumerable() const { return OperandAt(0); } HValue* map() const { return OperandAt(1); } int idx() const { return idx_; } HForInCacheArray* index_cache() { return index_cache_; } void set_index_cache(HForInCacheArray* index_cache) { index_cache_ = index_cache; } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HType CalculateInferredType() override { return HType::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray); private: HForInCacheArray(HValue* enumerable, HValue* keys, int idx) : idx_(idx) { SetOperandAt(0, enumerable); SetOperandAt(1, keys); set_representation(Representation::Tagged()); } int idx_; HForInCacheArray* index_cache_; }; class HLoadFieldByIndex final : public HTemplateInstruction<2> { public: DECLARE_INSTRUCTION_FACTORY_P2(HLoadFieldByIndex, HValue*, HValue*); HLoadFieldByIndex(HValue* object, HValue* index) { SetOperandAt(0, object); SetOperandAt(1, index); SetChangesFlag(kNewSpacePromotion); set_representation(Representation::Tagged()); } Representation RequiredInputRepresentation(int index) override { if (index == 1) { return Representation::Smi(); } else { return Representation::Tagged(); } } HValue* object() const { return OperandAt(0); } HValue* index() const { return OperandAt(1); } std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT HType CalculateInferredType() override { return HType::Tagged(); } DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex); private: bool IsDeletable() const override { return true; } }; #undef DECLARE_INSTRUCTION #undef DECLARE_CONCRETE_INSTRUCTION } // namespace internal } // namespace v8 #endif // V8_CRANKSHAFT_HYDROGEN_INSTRUCTIONS_H_