| // 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> declarations() const { return declarations_; } |
| 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> declarations, int flags, |
| Handle<TypeFeedbackVector> feedback_vector) |
| : HUnaryOperation(context), |
| declarations_(declarations), |
| feedback_vector_(feedback_vector), |
| flags_(flags) { |
| set_representation(Representation::Tagged()); |
| SetAllSideEffects(); |
| } |
| |
| Handle<FixedArray> declarations_; |
| 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: |
|