blob: f1720f444247d83635c84d00a99cf2457ee0022e [file] [log] [blame]
// 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_HYDROGEN_INSTRUCTIONS_H_
#define V8_HYDROGEN_INSTRUCTIONS_H_
#include "src/v8.h"
#include "src/allocation.h"
#include "src/code-stubs.h"
#include "src/conversions.h"
#include "src/data-flow.h"
#include "src/deoptimizer.h"
#include "src/hydrogen-types.h"
#include "src/small-pointer-list.h"
#include "src/string-stream.h"
#include "src/unique.h"
#include "src/utils.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
// Forward declarations.
class HBasicBlock;
class HDiv;
class HEnvironment;
class HInferRepresentationPhase;
class HInstruction;
class HLoopInformation;
class HStoreNamedField;
class HValue;
class LInstruction;
class LChunkBuilder;
#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(AllocateBlockContext) \
V(Allocate) \
V(ApplyArguments) \
V(ArgumentsElements) \
V(ArgumentsLength) \
V(ArgumentsObject) \
V(Bitwise) \
V(BlockEntry) \
V(BoundsCheck) \
V(BoundsCheckBaseIndexInformation) \
V(Branch) \
V(CallWithDescriptor) \
V(CallJSFunction) \
V(CallFunction) \
V(CallNew) \
V(CallNewArray) \
V(CallRuntime) \
V(CallStub) \
V(CapturedObject) \
V(Change) \
V(CheckHeapObject) \
V(CheckInstanceType) \
V(CheckMaps) \
V(CheckMapValue) \
V(CheckSmi) \
V(CheckValue) \
V(ClampToUint8) \
V(ClassOfTestAndBranch) \
V(CompareNumericAndBranch) \
V(CompareHoleAndBranch) \
V(CompareGeneric) \
V(CompareMinusZeroAndBranch) \
V(CompareObjectEqAndBranch) \
V(CompareMap) \
V(Constant) \
V(ConstructDouble) \
V(Context) \
V(DateField) \
V(DebugBreak) \
V(DeclareGlobals) \
V(Deoptimize) \
V(Div) \
V(DoubleBits) \
V(DummyUse) \
V(EnterInlined) \
V(EnvironmentMarker) \
V(ForceRepresentation) \
V(ForInCacheArray) \
V(ForInPrepareMap) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
V(Goto) \
V(HasCachedArrayIndexAndBranch) \
V(HasInstanceTypeAndBranch) \
V(InnerAllocatedObject) \
V(InstanceOf) \
V(InstanceOfKnownGlobal) \
V(InvokeFunction) \
V(IsConstructCallAndBranch) \
V(IsObjectAndBranch) \
V(IsStringAndBranch) \
V(IsSmiAndBranch) \
V(IsUndetectableAndBranch) \
V(LeaveInlined) \
V(LoadContextSlot) \
V(LoadFieldByIndex) \
V(LoadFunctionPrototype) \
V(LoadGlobalCell) \
V(LoadGlobalGeneric) \
V(LoadKeyed) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedGeneric) \
V(LoadRoot) \
V(MapEnumLength) \
V(MathFloorOfDiv) \
V(MathMinMax) \
V(Mod) \
V(Mul) \
V(OsrEntry) \
V(Parameter) \
V(Power) \
V(PushArguments) \
V(RegExpLiteral) \
V(Return) \
V(Ror) \
V(Sar) \
V(SeqStringGetChar) \
V(SeqStringSetChar) \
V(Shl) \
V(Shr) \
V(Simulate) \
V(StackCheck) \
V(StoreCodeEntry) \
V(StoreContextSlot) \
V(StoreFrameContext) \
V(StoreGlobalCell) \
V(StoreKeyed) \
V(StoreKeyedGeneric) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringCompareAndBranch) \
V(Sub) \
V(ThisFunction) \
V(ToFastProperties) \
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) \
virtual bool Is##type() const V8_FINAL V8_OVERRIDE { return true; } \
static H##type* cast(HValue* value) { \
ASSERT(value->Is##type()); \
return reinterpret_cast<H##type*>(value); \
}
#define DECLARE_CONCRETE_INSTRUCTION(type) \
virtual LInstruction* CompileToLithium( \
LChunkBuilder* builder) V8_FINAL V8_OVERRIDE; \
static H##type* cast(HValue* value) { \
ASSERT(value->Is##type()); \
return reinterpret_cast<H##type*>(value); \
} \
virtual Opcode opcode() const V8_FINAL V8_OVERRIDE { \
return HValue::k##type; \
}
enum PropertyAccessType { LOAD, STORE };
class Range V8_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 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 V8_FINAL BASE_EMBEDDED {
public:
bool Done() { return current_ == NULL; }
void Advance();
HValue* value() {
ASSERT(!Done());
return value_;
}
int index() {
ASSERT(!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) {
ASSERT(i >= 0);
ASSERT(i < kNumberOfFlags);
return static_cast<GVNFlag>(i);
}
class DecompositionResult V8_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;
// This class encapsulates encoding and decoding of sources positions from
// which hydrogen values originated.
// When FLAG_track_hydrogen_positions is set this object encodes the
// identifier of the inlining and absolute offset from the start of the
// inlined function.
// When the flag is not set we simply track absolute offset from the
// script start.
class HSourcePosition {
public:
HSourcePosition(const HSourcePosition& other) : value_(other.value_) { }
static HSourcePosition Unknown() {
return HSourcePosition(RelocInfo::kNoPosition);
}
bool IsUnknown() const { return value_ == RelocInfo::kNoPosition; }
int position() const { return PositionField::decode(value_); }
void set_position(int position) {
if (FLAG_hydrogen_track_positions) {
value_ = static_cast<int>(PositionField::update(value_, position));
} else {
value_ = position;
}
}
int inlining_id() const { return InliningIdField::decode(value_); }
void set_inlining_id(int inlining_id) {
if (FLAG_hydrogen_track_positions) {
value_ = static_cast<int>(InliningIdField::update(value_, inlining_id));
}
}
int raw() const { return value_; }
void PrintTo(FILE* f);
private:
typedef BitField<int, 0, 9> InliningIdField;
// Offset from the start of the inlined function.
typedef BitField<int, 9, 22> PositionField;
// On HPositionInfo can use this constructor.
explicit HSourcePosition(int value) : value_(value) { }
friend class HPositionInfo;
// If FLAG_hydrogen_track_positions is set contains bitfields InliningIdField
// and PositionField.
// Otherwise contains absolute offset from the script start.
int value_;
};
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,
kAllowUndefinedAsNaN,
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();
}
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 HSourcePosition position() const {
return HSourcePosition::Unknown();
}
virtual HSourcePosition operand_position(int index) const {
return position();
}
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) {
ASSERT(CheckFlag(kFlexibleRepresentation));
ASSERT(!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) {
ASSERT(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() = 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 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 {
ASSERT(!range_poisoned_);
return range_;
}
bool HasRange() const {
ASSERT(!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 void PrintTo(StringStream* stream) = 0;
void PrintNameTo(StringStream* stream);
void PrintTypeTo(StringStream* stream);
void PrintChangesTo(StringStream* stream);
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
virtual bool TryDecompose(DecompositionResult* decomposition) {
if (RedefinedOperand() != NULL) {
return RedefinedOperand()->TryDecompose(decomposition);
} else {
return false;
}
}
// 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().IsJSObject()) 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() {
ASSERT(block_ != NULL);
block_ = NULL;
}
void set_representation(Representation r) {
ASSERT(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;
}
// 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);
};
#define DECLARE_INSTRUCTION_FACTORY_P0(I) \
static I* New(Zone* zone, HValue* context) { \
return new(zone) I(); \
}
#define DECLARE_INSTRUCTION_FACTORY_P1(I, P1) \
static I* New(Zone* zone, HValue* context, P1 p1) { \
return new(zone) I(p1); \
}
#define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2) \
static I* New(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(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(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(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(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_WITH_CONTEXT_FACTORY_P0(I) \
static I* New(Zone* zone, HValue* context) { \
return new(zone) I(context); \
}
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(I, P1) \
static I* New(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(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(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(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(Zone* zone, \
HValue* context, \
P1 p1, \
P2 p2, \
P3 p3, \
P4 p4, \
P5 p5) { \
return new(zone) I(context, p1, p2, p3, p4, p5); \
}
// A helper class to represent per-operand position information attached to
// the HInstruction in the compact form. Uses tagging to distinguish between
// case when only instruction's position is available and case when operands'
// positions are also available.
// In the first case it contains intruction's position as a tagged value.
// In the second case it points to an array which contains instruction's
// position and operands' positions.
class HPositionInfo {
public:
explicit HPositionInfo(int pos) : data_(TagPosition(pos)) { }
HSourcePosition position() const {
if (has_operand_positions()) {
return operand_positions()[kInstructionPosIndex];
}
return HSourcePosition(static_cast<int>(UntagPosition(data_)));
}
void set_position(HSourcePosition pos) {
if (has_operand_positions()) {
operand_positions()[kInstructionPosIndex] = pos;
} else {
data_ = TagPosition(pos.raw());
}
}
void ensure_storage_for_operand_positions(Zone* zone, int operand_count) {
if (has_operand_positions()) {
return;
}
const int length = kFirstOperandPosIndex + operand_count;
HSourcePosition* positions =
zone->NewArray<HSourcePosition>(length);
for (int i = 0; i < length; i++) {
positions[i] = HSourcePosition::Unknown();
}
const HSourcePosition pos = position();
data_ = reinterpret_cast<intptr_t>(positions);
set_position(pos);
ASSERT(has_operand_positions());
}
HSourcePosition operand_position(int idx) const {
if (!has_operand_positions()) {
return position();
}
return *operand_position_slot(idx);
}
void set_operand_position(int idx, HSourcePosition pos) {
*operand_position_slot(idx) = pos;
}
private:
static const intptr_t kInstructionPosIndex = 0;
static const intptr_t kFirstOperandPosIndex = 1;
HSourcePosition* operand_position_slot(int idx) const {
ASSERT(has_operand_positions());
return &(operand_positions()[kFirstOperandPosIndex + idx]);
}
bool has_operand_positions() const {
return !IsTaggedPosition(data_);
}
HSourcePosition* operand_positions() const {
ASSERT(has_operand_positions());
return reinterpret_cast<HSourcePosition*>(data_);
}
static const intptr_t kPositionTag = 1;
static const intptr_t kPositionShift = 1;
static bool IsTaggedPosition(intptr_t val) {
return (val & kPositionTag) != 0;
}
static intptr_t UntagPosition(intptr_t val) {
ASSERT(IsTaggedPosition(val));
return val >> kPositionShift;
}
static intptr_t TagPosition(intptr_t val) {
const intptr_t result = (val << kPositionShift) | kPositionTag;
ASSERT(UntagPosition(result) == val);
return result;
}
intptr_t data_;
};
class HInstruction : public HValue {
public:
HInstruction* next() const { return next_; }
HInstruction* previous() const { return previous_; }
virtual void PrintTo(StringStream* stream) V8_OVERRIDE;
virtual void PrintDataTo(StringStream* stream);
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.
virtual HSourcePosition position() const V8_OVERRIDE {
return HSourcePosition(position_.position());
}
bool has_position() const {
return !position().IsUnknown();
}
void set_position(HSourcePosition position) {
ASSERT(!has_position());
ASSERT(!position.IsUnknown());
position_.set_position(position);
}
virtual HSourcePosition operand_position(int index) const V8_OVERRIDE {
const HSourcePosition pos = position_.operand_position(index);
return pos.IsUnknown() ? position() : pos;
}
void set_operand_position(Zone* zone, int index, HSourcePosition pos) {
ASSERT(0 <= index && index < OperandCount());
position_.ensure_storage_for_operand_positions(zone, OperandCount());
position_.set_operand_position(index, pos);
}
bool Dominates(HInstruction* other);
bool CanTruncateToSmi() const { return CheckFlag(kTruncatingToSmi); }
bool CanTruncateToInt32() const { return CheckFlag(kTruncatingToInt32); }
virtual LInstruction* CompileToLithium(LChunkBuilder* builder) = 0;
#ifdef DEBUG
virtual void Verify() V8_OVERRIDE;
#endif
bool CanDeoptimize();
virtual bool HasStackCheck() { return false; }
DECLARE_ABSTRACT_INSTRUCTION(Instruction)
protected:
HInstruction(HType type = HType::Tagged())
: HValue(type),
next_(NULL),
previous_(NULL),
position_(RelocInfo::kNoPosition) {
SetDependsOnFlag(kOsrEntries);
}
virtual void DeleteFromGraph() V8_OVERRIDE { Unlink(); }
private:
void InitializeAsFirst(HBasicBlock* block) {
ASSERT(!IsLinked());
SetBlock(block);
}
void PrintMnemonicTo(StringStream* stream);
HInstruction* next_;
HInstruction* previous_;
HPositionInfo position_;
friend class HBasicBlock;
};
template<int V>
class HTemplateInstruction : public HInstruction {
public:
virtual int OperandCount() V8_FINAL V8_OVERRIDE { return V; }
virtual HValue* OperandAt(int i) const V8_FINAL V8_OVERRIDE {
return inputs_[i];
}
protected:
HTemplateInstruction(HType type = HType::Tagged()) : HInstruction(type) {}
virtual void InternalSetOperandAt(int i, HValue* value) V8_FINAL V8_OVERRIDE {
inputs_[i] = value;
}
private:
EmbeddedContainer<HValue*, V> inputs_;
};
class HControlInstruction : public HInstruction {
public:
virtual HBasicBlock* SuccessorAt(int i) = 0;
virtual int SuccessorCount() = 0;
virtual void SetSuccessorAt(int i, HBasicBlock* block) = 0;
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
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 V8_FINAL BASE_EMBEDDED {
public:
explicit HSuccessorIterator(HControlInstruction* instr)
: instr_(instr), current_(0) { }
bool Done() { return current_ >= instr_->SuccessorCount(); }
HBasicBlock* Current() { return instr_->SuccessorAt(current_); }
void Advance() { current_++; }
private:
HControlInstruction* instr_;
int current_;
};
template<int S, int V>
class HTemplateControlInstruction : public HControlInstruction {
public:
int SuccessorCount() V8_OVERRIDE { return S; }
HBasicBlock* SuccessorAt(int i) V8_OVERRIDE { return successors_[i]; }
void SetSuccessorAt(int i, HBasicBlock* block) V8_OVERRIDE {
successors_[i] = block;
}
int OperandCount() V8_OVERRIDE { return V; }
HValue* OperandAt(int i) const V8_OVERRIDE { return inputs_[i]; }
protected:
void InternalSetOperandAt(int i, HValue* value) V8_OVERRIDE {
inputs_[i] = value;
}
private:
EmbeddedContainer<HBasicBlock*, S> successors_;
EmbeddedContainer<HValue*, V> inputs_;
};
class HBlockEntry V8_FINAL : public HTemplateInstruction<0> {
public:
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(BlockEntry)
};
class HDummyUse V8_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() { return OperandAt(0); }
virtual bool HasEscapingOperandAt(int index) V8_OVERRIDE { return false; }
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
DECLARE_CONCRETE_INSTRUCTION(DummyUse);
};
// Inserts an int3/stop break instruction for debugging purposes.
class HDebugBreak V8_FINAL : public HTemplateInstruction<0> {
public:
DECLARE_INSTRUCTION_FACTORY_P0(HDebugBreak);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(DebugBreak)
};
class HGoto V8_FINAL : public HTemplateControlInstruction<1, 0> {
public:
explicit HGoto(HBasicBlock* target) {
SetSuccessorAt(0, target);
}
virtual bool KnownSuccessorBlock(HBasicBlock** block) V8_OVERRIDE {
*block = FirstSuccessor();
return true;
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
DECLARE_CONCRETE_INSTRUCTION(Goto)
};
class HDeoptimize V8_FINAL : public HTemplateControlInstruction<1, 0> {
public:
static HDeoptimize* New(Zone* zone,
HValue* context,
const char* reason,
Deoptimizer::BailoutType type,
HBasicBlock* unreachable_continuation) {
return new(zone) HDeoptimize(reason, type, unreachable_continuation);
}
virtual bool KnownSuccessorBlock(HBasicBlock** block) V8_OVERRIDE {
*block = NULL;
return true;
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
const char* reason() const { return reason_; }
Deoptimizer::BailoutType type() { return type_; }
DECLARE_CONCRETE_INSTRUCTION(Deoptimize)
private:
explicit HDeoptimize(const char* reason,
Deoptimizer::BailoutType type,
HBasicBlock* unreachable_continuation)
: reason_(reason), type_(type) {
SetSuccessorAt(0, unreachable_continuation);
}
const char* 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);
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
HValue* value() { return OperandAt(0); }
};
class HBranch V8_FINAL : public HUnaryControlInstruction {
public:
DECLARE_INSTRUCTION_FACTORY_P1(HBranch, HValue*);
DECLARE_INSTRUCTION_FACTORY_P2(HBranch, HValue*,
ToBooleanStub::Types);
DECLARE_INSTRUCTION_FACTORY_P4(HBranch, HValue*,
ToBooleanStub::Types,
HBasicBlock*, HBasicBlock*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
virtual Representation observed_input_representation(int index) V8_OVERRIDE;
virtual bool KnownSuccessorBlock(HBasicBlock** block) V8_OVERRIDE;
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
ToBooleanStub::Types expected_input_types() const {
return expected_input_types_;
}
DECLARE_CONCRETE_INSTRUCTION(Branch)
private:
HBranch(HValue* value,
ToBooleanStub::Types expected_input_types = ToBooleanStub::Types(),
HBasicBlock* true_target = NULL,
HBasicBlock* false_target = NULL)
: HUnaryControlInstruction(value, true_target, false_target),
expected_input_types_(expected_input_types) {
SetFlag(kAllowUndefinedAsNaN);
}
ToBooleanStub::Types expected_input_types_;
};
class HCompareMap V8_FINAL : public HUnaryControlInstruction {
public:
DECLARE_INSTRUCTION_FACTORY_P2(HCompareMap, HValue*, Handle<Map>);
DECLARE_INSTRUCTION_FACTORY_P4(HCompareMap, HValue*, Handle<Map>,
HBasicBlock*, HBasicBlock*);
virtual bool KnownSuccessorBlock(HBasicBlock** block) V8_OVERRIDE {
if (known_successor_index() != kNoKnownSuccessorIndex) {
*block = SuccessorAt(known_successor_index());
return true;
}
*block = NULL;
return false;
}
virtual void PrintDataTo(StringStream* stream) V8_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;
}
Unique<Map> map() const { return map_; }
bool map_is_stable() const { return map_is_stable_; }
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(CompareMap)
protected:
virtual int RedefinedOperandIndex() { return 0; }
private:
HCompareMap(HValue* value,
Handle<Map> map,
HBasicBlock* true_target = NULL,
HBasicBlock* false_target = NULL)
: HUnaryControlInstruction(value, true_target, false_target),
known_successor_index_(kNoKnownSuccessorIndex),
map_is_stable_(map->is_stable()),
map_(Unique<Map>::CreateImmovable(map)) {
set_representation(Representation::Tagged());
}
int known_successor_index_ : 31;
bool map_is_stable_ : 1;
Unique<Map> map_;
};
class HContext V8_FINAL : public HTemplateInstruction<0> {
public:
static HContext* New(Zone* zone) {
return new(zone) HContext();
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(Context)
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
HContext() {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
};
class HReturn V8_FINAL : public HTemplateControlInstruction<0, 3> {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HReturn, HValue*, HValue*);
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HReturn, HValue*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
// TODO(titzer): require an Int32 input for faster returns.
if (index == 2) return Representation::Smi();
return Representation::Tagged();
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
HValue* value() { return OperandAt(0); }
HValue* context() { return OperandAt(1); }
HValue* parameter_count() { 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 V8_FINAL : public HTemplateControlInstruction<0, 0> {
public:
DECLARE_INSTRUCTION_FACTORY_P0(HAbnormalExit);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(AbnormalExit)
private:
HAbnormalExit() {}
};
class HUnaryOperation : public HTemplateInstruction<1> {
public:
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); }
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
};
class HUseConst V8_FINAL : public HUnaryOperation {
public:
DECLARE_INSTRUCTION_FACTORY_P1(HUseConst, HValue*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(UseConst)
private:
explicit HUseConst(HValue* old_value) : HUnaryOperation(old_value) { }
};
class HForceRepresentation V8_FINAL : public HTemplateInstruction<1> {
public:
static HInstruction* New(Zone* zone, HValue* context, HValue* value,
Representation required_representation);
HValue* value() { return OperandAt(0); }
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return representation(); // Same as the output representation.
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
DECLARE_CONCRETE_INSTRUCTION(ForceRepresentation)
private:
HForceRepresentation(HValue* value, Representation required_representation) {
SetOperandAt(0, value);
set_representation(required_representation);
}
};
class HChange V8_FINAL : public HUnaryOperation {
public:
HChange(HValue* value,
Representation to,
bool is_truncating_to_smi,
bool is_truncating_to_int32)
: HUnaryOperation(value) {
ASSERT(!value->representation().IsNone());
ASSERT(!to.IsNone());
ASSERT(!value->representation().Equals(to));
set_representation(to);
SetFlag(kUseGVN);
SetFlag(kCanOverflow);
if (is_truncating_to_smi && to.IsSmi()) {
SetFlag(kTruncatingToSmi);
SetFlag(kTruncatingToInt32);
}
if (is_truncating_to_int32) SetFlag(kTruncatingToInt32);
if (value->representation().IsSmi() || value->type().IsSmi()) {
set_type(HType::Smi());
} else {
set_type(HType::TaggedNumber());
if (to.IsTagged()) SetChangesFlag(kNewSpacePromotion);
}
}
bool can_convert_undefined_to_nan() {
return CheckUsesForFlag(kAllowUndefinedAsNaN);
}
virtual HType CalculateInferredType() V8_OVERRIDE;
virtual HValue* Canonicalize() V8_OVERRIDE;
Representation from() const { return value()->representation(); }
Representation to() const { return representation(); }
bool deoptimize_on_minus_zero() const {
return CheckFlag(kBailoutOnMinusZero);
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return from();
}
virtual Range* InferRange(Zone* zone) V8_OVERRIDE;
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
DECLARE_CONCRETE_INSTRUCTION(Change)
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
virtual bool IsDeletable() const V8_OVERRIDE {
return !from().IsTagged() || value()->type().IsSmi();
}
};
class HClampToUint8 V8_FINAL : public HUnaryOperation {
public:
DECLARE_INSTRUCTION_FACTORY_P1(HClampToUint8, HValue*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(ClampToUint8)
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
explicit HClampToUint8(HValue* value)
: HUnaryOperation(value) {
set_representation(Representation::Integer32());
SetFlag(kAllowUndefinedAsNaN);
SetFlag(kUseGVN);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
};
class HDoubleBits V8_FINAL : public HUnaryOperation {
public:
enum Bits { HIGH, LOW };
DECLARE_INSTRUCTION_FACTORY_P2(HDoubleBits, HValue*, Bits);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Double();
}
DECLARE_CONCRETE_INSTRUCTION(DoubleBits)
Bits bits() { return bits_; }
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE {
return other->IsDoubleBits() && HDoubleBits::cast(other)->bits() == bits();
}
private:
HDoubleBits(HValue* value, Bits bits)
: HUnaryOperation(value), bits_(bits) {
set_representation(Representation::Integer32());
SetFlag(kUseGVN);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
Bits bits_;
};
class HConstructDouble V8_FINAL : public HTemplateInstruction<2> {
public:
DECLARE_INSTRUCTION_FACTORY_P2(HConstructDouble, HValue*, HValue*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Integer32();
}
DECLARE_CONCRETE_INSTRUCTION(ConstructDouble)
HValue* hi() { return OperandAt(0); }
HValue* lo() { return OperandAt(1); }
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
explicit HConstructDouble(HValue* hi, HValue* lo) {
set_representation(Representation::Double());
SetFlag(kUseGVN);
SetOperandAt(0, hi);
SetOperandAt(1, lo);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
};
enum RemovableSimulate {
REMOVABLE_SIMULATE,
FIXED_SIMULATE
};
class HSimulate V8_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),
removable_(removable),
done_with_replay_(false) {}
~HSimulate() {}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
bool HasAstId() const { return !ast_id_.IsNone(); }
BailoutId ast_id() const { return ast_id_; }
void set_ast_id(BailoutId id) {
ASSERT(!HasAstId());
ast_id_ = id;
}
int pop_count() const { return pop_count_; }
const ZoneList<HValue*>* values() const { return &values_; }
int GetAssignedIndexAt(int index) const {
ASSERT(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;
}
virtual int OperandCount() V8_OVERRIDE { return values_.length(); }
virtual HValue* OperandAt(int index) const V8_OVERRIDE {
return values_[index];
}
virtual bool HasEscapingOperandAt(int index) V8_OVERRIDE { return false; }
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
void MergeWith(ZoneList<HSimulate*>* list);
bool is_candidate_for_removal() { return removable_ == REMOVABLE_SIMULATE; }
// Replay effects of this instruction on the given environment.
void ReplayEnvironment(HEnvironment* env);
DECLARE_CONCRETE_INSTRUCTION(Simulate)
#ifdef DEBUG
virtual void Verify() V8_OVERRIDE;
void set_closure(Handle<JSFunction> closure) { closure_ = closure; }
Handle<JSFunction> closure() const { return closure_; }
#endif
protected:
virtual void InternalSetOperandAt(int index, HValue* value) V8_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;
}
BailoutId ast_id_;
int pop_count_;
ZoneList<HValue*> values_;
ZoneList<int> assigned_indexes_;
Zone* zone_;
RemovableSimulate removable_ : 2;
bool done_with_replay_ : 1;
#ifdef DEBUG
Handle<JSFunction> closure_;
#endif
};
class HEnvironmentMarker V8_FINAL : public HTemplateInstruction<1> {
public:
enum Kind { BIND, LOOKUP };
DECLARE_INSTRUCTION_FACTORY_P2(HEnvironmentMarker, Kind, int);
Kind kind() { return kind_; }
int index() { return index_; }
HSimulate* next_simulate() { return next_simulate_; }
void set_next_simulate(HSimulate* simulate) {
next_simulate_ = simulate;
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
#ifdef DEBUG
void set_closure(Handle<JSFunction> closure) {
ASSERT(closure_.is_null());
ASSERT(!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 V8_FINAL : public HTemplateInstruction<1> {
public:
enum Type {
kFunctionEntry,
kBackwardsBranch
};
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(HStackCheck, Type);
HValue* context() { return OperandAt(0); }
virtual Representation RequiredInputRepresentation(int index) V8_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 HEnterInlined V8_FINAL : public HTemplateInstruction<0> {
public:
static HEnterInlined* New(Zone* zone,
HValue* context,
BailoutId return_id,
Handle<JSFunction> closure,
int arguments_count,
FunctionLiteral* function,
InliningKind inlining_kind,
Variable* arguments_var,
HArgumentsObject* arguments_object) {
return new(zone) HEnterInlined(return_id, closure, arguments_count,
function, inlining_kind, arguments_var,
arguments_object, zone);
}
void RegisterReturnTarget(HBasicBlock* return_target, Zone* zone);
ZoneList<HBasicBlock*>* return_targets() { return &return_targets_; }
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
Handle<JSFunction> closure() const { return closure_; }
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_; }
BailoutId ReturnId() const { return return_id_; }
virtual Representation RequiredInputRepresentation(int index) V8_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,
int arguments_count,
FunctionLiteral* function,
InliningKind inlining_kind,
Variable* arguments_var,
HArgumentsObject* arguments_object,
Zone* zone)
: return_id_(return_id),
closure_(closure),
arguments_count_(arguments_count),
arguments_pushed_(false),
function_(function),
inlining_kind_(inlining_kind),
arguments_var_(arguments_var),
arguments_object_(arguments_object),
return_targets_(2, zone) {
}
BailoutId return_id_;
Handle<JSFunction> closure_;
int arguments_count_;
bool arguments_pushed_;
FunctionLiteral* function_;
InliningKind inlining_kind_;
Variable* arguments_var_;
HArgumentsObject* arguments_object_;
ZoneList<HBasicBlock*> return_targets_;
};
class HLeaveInlined V8_FINAL : public HTemplateInstruction<0> {
public:
HLeaveInlined(HEnterInlined* entry,
int drop_count)
: entry_(entry),
drop_count_(drop_count) { }
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
virtual int argument_delta() const V8_OVERRIDE {
return entry_->arguments_pushed() ? -drop_count_ : 0;
}
DECLARE_CONCRETE_INSTRUCTION(LeaveInlined)
private:
HEnterInlined* entry_;
int drop_count_;
};
class HPushArguments V8_FINAL : public HInstruction {
public:
static HPushArguments* New(Zone* zone, HValue* context) {
return new(zone) HPushArguments(zone);
}
static HPushArguments* New(Zone* zone, HValue* context, HValue* arg1) {
HPushArguments* instr = new(zone) HPushArguments(zone);
instr->AddInput(arg1);
return instr;
}
static HPushArguments* New(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(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(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;
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Tagged();
}
virtual int argument_delta() const V8_OVERRIDE { return inputs_.length(); }
HValue* argument(int i) { return OperandAt(i); }
virtual int OperandCount() V8_FINAL V8_OVERRIDE { return inputs_.length(); }
virtual HValue* OperandAt(int i) const V8_FINAL V8_OVERRIDE {
return inputs_[i];
}
void AddInput(HValue* value);
DECLARE_CONCRETE_INSTRUCTION(PushArguments)
protected:
virtual void InternalSetOperandAt(int i, HValue* value) V8_FINAL V8_OVERRIDE {
inputs_[i] = value;
}
private:
explicit HPushArguments(Zone* zone)
: HInstruction(HType::Tagged()), inputs_(4, zone) {
set_representation(Representation::Tagged());
}
ZoneList<HValue*> inputs_;
};
class HThisFunction V8_FINAL : public HTemplateInstruction<0> {
public:
DECLARE_INSTRUCTION_FACTORY_P0(HThisFunction);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::None();
}
DECLARE_CONCRETE_INSTRUCTION(ThisFunction)
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
HThisFunction() {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
};
class HDeclareGlobals V8_FINAL : public HUnaryOperation {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HDeclareGlobals,
Handle<FixedArray>,
int);
HValue* context() { return OperandAt(0); }
Handle<FixedArray> pairs() const { return pairs_; }
int flags() const { return flags_; }
DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals)
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Tagged();
}
private:
HDeclareGlobals(HValue* context,
Handle<FixedArray> pairs,
int flags)
: HUnaryOperation(context),
pairs_(pairs),
flags_(flags) {
set_representation(Representation::Tagged());
SetAllSideEffects();
}
Handle<FixedArray> pairs_;
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 HType CalculateInferredType() V8_FINAL V8_OVERRIDE {
return HType::Tagged();
}
virtual int argument_count() const {
return argument_count_;
}
virtual int argument_delta() const V8_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);
}
virtual Representation RequiredInputRepresentation(
int index) V8_FINAL V8_OVERRIDE {
return Representation::Tagged();
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
HValue* value() { 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);
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
virtual Representation RequiredInputRepresentation(
int index) V8_FINAL V8_OVERRIDE {
return Representation::Tagged();
}
HValue* first() { return OperandAt(0); }
HValue* second() { return OperandAt(1); }
};
class HCallJSFunction V8_FINAL : public HCall<1> {
public:
static HCallJSFunction* New(Zone* zone,
HValue* context,
HValue* function,
int argument_count,
bool pass_argument_count);
HValue* function() { return OperandAt(0); }
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
virtual Representation RequiredInputRepresentation(
int index) V8_FINAL V8_OVERRIDE {
ASSERT(index == 0);
return Representation::Tagged();
}
bool pass_argument_count() const { return pass_argument_count_; }
virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
return has_stack_check_;
}
DECLARE_CONCRETE_INSTRUCTION(CallJSFunction)
private:
// The argument count includes the receiver.
HCallJSFunction(HValue* function,
int argument_count,
bool pass_argument_count,
bool has_stack_check)
: HCall<1>(argument_count),
pass_argument_count_(pass_argument_count),
has_stack_check_(has_stack_check) {
SetOperandAt(0, function);
}
bool pass_argument_count_;
bool has_stack_check_;
};
class HCallWithDescriptor V8_FINAL : public HInstruction {
public:
static HCallWithDescriptor* New(Zone* zone, HValue* context,
HValue* target,
int argument_count,
const CallInterfaceDescriptor* descriptor,
const Vector<HValue*>& operands) {
ASSERT(operands.length() == descriptor->environment_length());
HCallWithDescriptor* res =
new(zone) HCallWithDescriptor(target, argument_count,
descriptor, operands, zone);
return res;
}
virtual int OperandCount() V8_FINAL V8_OVERRIDE { return values_.length(); }
virtual HValue* OperandAt(int index) const V8_FINAL V8_OVERRIDE {
return values_[index];
}
virtual Representation RequiredInputRepresentation(
int index) V8_FINAL V8_OVERRIDE {
if (index == 0) {
return Representation::Tagged();
} else {
int par_index = index - 1;
ASSERT(par_index < descriptor_->environment_length());
return descriptor_->GetParameterRepresentation(par_index);
}
}
DECLARE_CONCRETE_INSTRUCTION(CallWithDescriptor)
virtual HType CalculateInferredType() V8_FINAL V8_OVERRIDE {
return HType::Tagged();
}
virtual int argument_count() const {
return argument_count_;
}
virtual int argument_delta() const V8_OVERRIDE {
return -argument_count_;
}
const CallInterfaceDescriptor* descriptor() const {
return descriptor_;
}
HValue* target() {
return OperandAt(0);
}
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
private:
// The argument count includes the receiver.
HCallWithDescriptor(HValue* target,
int argument_count,
const CallInterfaceDescriptor* descriptor,
const Vector<HValue*>& operands,
Zone* zone)
: descriptor_(descriptor),
values_(descriptor->environment_length() + 1, zone) {
argument_count_ = argument_count;
AddOperand(target, 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);
}
void InternalSetOperandAt(int index,
HValue* value) V8_FINAL V8_OVERRIDE {
values_[index] = value;
}
const CallInterfaceDescriptor* descriptor_;
ZoneList<HValue*> values_;
int argument_count_;
};
class HInvokeFunction V8_FINAL : public HBinaryCall {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HInvokeFunction, HValue*, int);
HInvokeFunction(HValue* context,
HValue* function,
Handle<JSFunction> known_function,
int argument_count)
: HBinaryCall(context, function, argument_count),
known_function_(known_function) {
formal_parameter_count_ = known_function.is_null()
? 0 : known_function->shared()->formal_parameter_count();
has_stack_check_ = !known_function.is_null() &&
(known_function->code()->kind() == Code::FUNCTION ||
known_function->code()->kind() == Code::OPTIMIZED_FUNCTION);
}
static HInvokeFunction* New(Zone* zone,
HValue* context,
HValue* function,
Handle<JSFunction> known_function,
int argument_count) {
return new(zone) HInvokeFunction(context, function,
known_function, argument_count);
}
HValue* context() { return first(); }
HValue* function() { return second(); }
Handle<JSFunction> known_function() { return known_function_; }
int formal_parameter_count() const { return formal_parameter_count_; }
virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
return has_stack_check_;
}
DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
private:
HInvokeFunction(HValue* context, HValue* function, int argument_count)
: HBinaryCall(context, function, argument_count),
has_stack_check_(false) {
}
Handle<JSFunction> known_function_;
int formal_parameter_count_;
bool has_stack_check_;
};
class HCallFunction V8_FINAL : public HBinaryCall {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HCallFunction, HValue*, int);
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(
HCallFunction, HValue*, int, CallFunctionFlags);
HValue* context() { return first(); }
HValue* function() { return second(); }
CallFunctionFlags function_flags() const { return function_flags_; }
DECLARE_CONCRETE_INSTRUCTION(CallFunction)
virtual int argument_delta() const V8_OVERRIDE { return -argument_count(); }
private:
HCallFunction(HValue* context,
HValue* function,
int argument_count,
CallFunctionFlags flags = NO_CALL_FUNCTION_FLAGS)
: HBinaryCall(context, function, argument_count), function_flags_(flags) {
}
CallFunctionFlags function_flags_;
};
class HCallNew V8_FINAL : public HBinaryCall {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(HCallNew, HValue*, int);
HValue* context() { return first(); }
HValue* constructor() { return second(); }
DECLARE_CONCRETE_INSTRUCTION(CallNew)
private:
HCallNew(HValue* context, HValue* constructor, int argument_count)
: HBinaryCall(context, constructor, argument_count) {}
};
class HCallNewArray V8_FINAL : public HBinaryCall {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HCallNewArray,
HValue*,
int,
ElementsKind);
HValue* context() { return first(); }
HValue* constructor() { return second(); }
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
ElementsKind elements_kind() const { return elements_kind_; }
DECLARE_CONCRETE_INSTRUCTION(CallNewArray)
private:
HCallNewArray(HValue* context, HValue* constructor, int argument_count,
ElementsKind elements_kind)
: HBinaryCall(context, constructor, argument_count),
elements_kind_(elements_kind) {}
ElementsKind elements_kind_;
};
class HCallRuntime V8_FINAL : public HCall<1> {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HCallRuntime,
Handle<String>,
const Runtime::Function*,
int);
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
HValue* context() { return OperandAt(0); }
const Runtime::Function* function() const { return c_function_; }
Handle<String> name() const { return name_; }
SaveFPRegsMode save_doubles() const { return save_doubles_; }
void set_save_doubles(SaveFPRegsMode save_doubles) {
save_doubles_ = save_doubles;
}
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(CallRuntime)
private:
HCallRuntime(HValue* context,
Handle<String> name,
const Runtime::Function* c_function,
int argument_count)
: HCall<1>(argument_count), c_function_(c_function), name_(name),
save_doubles_(kDontSaveFPRegs) {
SetOperandAt(0, context);
}
const Runtime::Function* c_function_;
Handle<String> name_;
SaveFPRegsMode save_doubles_;
};
class HMapEnumLength V8_FINAL : public HUnaryOperation {
public:
DECLARE_INSTRUCTION_FACTORY_P1(HMapEnumLength, HValue*);
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(MapEnumLength)
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE { return true; }
private:
explicit HMapEnumLength(HValue* value)
: HUnaryOperation(value, HType::Smi()) {
set_representation(Representation::Smi());
SetFlag(kUseGVN);
SetDependsOnFlag(kMaps);
}
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
};
class HUnaryMathOperation V8_FINAL : public HTemplateInstruction<2> {
public:
static HInstruction* New(Zone* zone,
HValue* context,
HValue* value,
BuiltinFunctionId op);
HValue* context() { return OperandAt(0); }
HValue* value() { return OperandAt(1); }
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
if (index == 0) {
return Representation::Tagged();
} else {
switch (op_) {
case kMathFloor:
case kMathRound:
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();
}
}
}
virtual Range* InferRange(Zone* zone) V8_OVERRIDE;
virtual HValue* Canonicalize() V8_OVERRIDE;
virtual Representation RepresentationFromUses() V8_OVERRIDE;
virtual Representation RepresentationFromInputs() V8_OVERRIDE;
BuiltinFunctionId op() const { return op_; }
const char* OpName() const;
DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation)
protected:
virtual bool DataEquals(HValue* other) V8_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 {
#ifdef V8_TARGET_ARCH_ARM64
return true;
#else
return false;
#endif
}
HUnaryMathOperation(HValue* context, HValue* value, BuiltinFunctionId op)
: HTemplateInstruction<2>(HType::TaggedNumber()), op_(op) {