| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef jit_MIR_h |
| #define jit_MIR_h |
| |
| // This file declares everything needed to build actual MIR instructions: the |
| // actual opcodes and instructions themselves, the instruction interface, and |
| // use chains. |
| #include "jscntxt.h" |
| #include "jslibmath.h" |
| #include "jsinfer.h" |
| #include "jsinferinlines.h" |
| #include "jstypedarrayinlines.h" |
| #include "TypePolicy.h" |
| #include "IonAllocPolicy.h" |
| #include "InlineList.h" |
| #include "MOpcodes.h" |
| #include "FixedArityList.h" |
| #include "IonMacroAssembler.h" |
| #include "Bailouts.h" |
| #include "FixedList.h" |
| #include "CompilerRoot.h" |
| |
| namespace js { |
| namespace jit { |
| |
| class BaselineInspector; |
| class ValueNumberData; |
| class Range; |
| |
| static const inline |
| MIRType MIRTypeFromValue(const js::Value &vp) |
| { |
| if (vp.isDouble()) |
| return MIRType_Double; |
| return MIRTypeFromValueType(vp.extractNonDoubleType()); |
| } |
| |
| #define MIR_FLAG_LIST(_) \ |
| _(InWorklist) \ |
| _(EmittedAtUses) \ |
| _(LoopInvariant) \ |
| _(Commutative) \ |
| _(Movable) /* Allow LICM and GVN to move this instruction */ \ |
| _(Lowered) /* (Debug only) has a virtual register */ \ |
| _(Guard) /* Not removable if uses == 0 */ \ |
| _(Folded) /* Has constant folded uses not reflected in SSA */ \ |
| \ |
| /* The instruction has been marked dead for lazy removal from resume |
| * points. |
| */ \ |
| _(Unused) \ |
| _(DOMFunction) /* Contains or uses a common DOM method function */ \ |
| \ |
| /* Marks if an instruction has fewer uses than the original code. |
| * E.g. UCE can remove code. |
| * Every instruction where an use is/was removed from an instruction and |
| * as a result the number of operands doesn't equal the original code |
| * need to get marked as UseRemoved. This is important for truncation |
| * analysis to know, since if all original uses are still present, |
| * it can ignore resumepoints. |
| * Currently this is done for every pass after IonBuilder and before |
| * Truncate Doubles. So every time removeUse is called, UseRemoved needs |
| * to get set. |
| */ \ |
| _(UseRemoved) |
| |
| class MDefinition; |
| class MInstruction; |
| class MBasicBlock; |
| class MNode; |
| class MUse; |
| class MIRGraph; |
| class MResumePoint; |
| |
| // Represents a use of a node. |
| class MUse : public TempObject, public InlineListNode<MUse> |
| { |
| friend class MDefinition; |
| |
| MDefinition *producer_; // MDefinition that is being used. |
| MNode *consumer_; // The node that is using this operand. |
| uint32_t index_; // The index of this operand in its consumer. |
| |
| MUse(MDefinition *producer, MNode *consumer, uint32_t index) |
| : producer_(producer), |
| consumer_(consumer), |
| index_(index) |
| { } |
| |
| public: |
| // Default constructor for use in vectors. |
| MUse() |
| : producer_(NULL), consumer_(NULL), index_(0) |
| { } |
| |
| static inline MUse *New(MDefinition *producer, MNode *consumer, uint32_t index) { |
| return new MUse(producer, consumer, index); |
| } |
| |
| // Set data inside the MUse. |
| void set(MDefinition *producer, MNode *consumer, uint32_t index) { |
| producer_ = producer; |
| consumer_ = consumer; |
| index_ = index; |
| } |
| |
| MDefinition *producer() const { |
| JS_ASSERT(producer_ != NULL); |
| return producer_; |
| } |
| bool hasProducer() const { |
| return producer_ != NULL; |
| } |
| MNode *consumer() const { |
| JS_ASSERT(consumer_ != NULL); |
| return consumer_; |
| } |
| uint32_t index() const { |
| return index_; |
| } |
| }; |
| |
| typedef InlineList<MUse>::iterator MUseIterator; |
| |
| // A node is an entry in the MIR graph. It has two kinds: |
| // MInstruction: an instruction which appears in the IR stream. |
| // MResumePoint: a list of instructions that correspond to the state of the |
| // interpreter stack. |
| // |
| // Nodes can hold references to MDefinitions. Each MDefinition has a list of |
| // nodes holding such a reference (its use chain). |
| class MNode : public TempObject |
| { |
| friend class MDefinition; |
| |
| protected: |
| MBasicBlock *block_; // Containing basic block. |
| |
| public: |
| enum Kind { |
| Definition, |
| ResumePoint |
| }; |
| |
| MNode() |
| : block_(NULL) |
| { } |
| |
| MNode(MBasicBlock *block) |
| : block_(block) |
| { } |
| |
| virtual Kind kind() const = 0; |
| |
| // Returns the definition at a given operand. |
| virtual MDefinition *getOperand(size_t index) const = 0; |
| virtual size_t numOperands() const = 0; |
| |
| bool isDefinition() const { |
| return kind() == Definition; |
| } |
| bool isResumePoint() const { |
| return kind() == ResumePoint; |
| } |
| MBasicBlock *block() const { |
| return block_; |
| } |
| |
| // Instructions needing to hook into type analysis should return a |
| // TypePolicy. |
| virtual TypePolicy *typePolicy() { |
| return NULL; |
| } |
| |
| // Replaces an already-set operand during iteration over a use chain. |
| MUseIterator replaceOperand(MUseIterator use, MDefinition *ins); |
| |
| // Replaces an already-set operand, updating use information. |
| void replaceOperand(size_t index, MDefinition *ins); |
| |
| // Resets the operand to an uninitialized state, breaking the link |
| // with the previous operand's producer. |
| void discardOperand(size_t index); |
| |
| inline MDefinition *toDefinition(); |
| inline MResumePoint *toResumePoint(); |
| |
| protected: |
| // Sets an unset operand, updating use information. |
| virtual void setOperand(size_t index, MDefinition *operand) = 0; |
| |
| // Gets the MUse corresponding to given operand. |
| virtual MUse *getUseFor(size_t index) = 0; |
| }; |
| |
| class AliasSet { |
| private: |
| uint32_t flags_; |
| |
| public: |
| enum Flag { |
| None_ = 0, |
| ObjectFields = 1 << 0, // shape, class, slots, length etc. |
| Element = 1 << 1, // A member of obj->elements. |
| DynamicSlot = 1 << 2, // A member of obj->slots. |
| FixedSlot = 1 << 3, // A member of obj->fixedSlots(). |
| TypedArrayElement = 1 << 4, // A typed array element. |
| DOMProperty = 1 << 5, // A DOM property |
| TypedArrayLength = 1 << 6,// A typed array's length |
| Last = TypedArrayLength, |
| Any = Last | (Last - 1), |
| |
| NumCategories = 7, |
| |
| // Indicates load or store. |
| Store_ = 1 << 31 |
| }; |
| |
| AliasSet(uint32_t flags) |
| : flags_(flags) |
| { |
| JS_STATIC_ASSERT((1 << NumCategories) - 1 == Any); |
| } |
| |
| public: |
| inline bool isNone() const { |
| return flags_ == None_; |
| } |
| uint32_t flags() const { |
| return flags_ & Any; |
| } |
| inline bool isStore() const { |
| return !!(flags_ & Store_); |
| } |
| inline bool isLoad() const { |
| return !isStore() && !isNone(); |
| } |
| inline AliasSet operator |(const AliasSet &other) const { |
| return AliasSet(flags_ | other.flags_); |
| } |
| inline AliasSet operator &(const AliasSet &other) const { |
| return AliasSet(flags_ & other.flags_); |
| } |
| static AliasSet None() { |
| return AliasSet(None_); |
| } |
| static AliasSet Load(uint32_t flags) { |
| JS_ASSERT(flags && !(flags & Store_)); |
| return AliasSet(flags); |
| } |
| static AliasSet Store(uint32_t flags) { |
| JS_ASSERT(flags && !(flags & Store_)); |
| return AliasSet(flags | Store_); |
| } |
| }; |
| |
| // An MDefinition is an SSA name. |
| class MDefinition : public MNode |
| { |
| friend class MBasicBlock; |
| friend class Loop; |
| |
| public: |
| enum Opcode { |
| # define DEFINE_OPCODES(op) Op_##op, |
| MIR_OPCODE_LIST(DEFINE_OPCODES) |
| # undef DEFINE_OPCODES |
| Op_Invalid |
| }; |
| |
| private: |
| InlineList<MUse> uses_; // Use chain. |
| uint32_t id_; // Instruction ID, which after block re-ordering |
| // is sorted within a basic block. |
| ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use) |
| Range *range_; // Any computed range for this def. |
| MIRType resultType_; // Representation of result type. |
| types::StackTypeSet *resultTypeSet_; // Optional refinement of the result type. |
| uint32_t flags_; // Bit flags. |
| union { |
| MDefinition *dependency_; // Implicit dependency (store, call, etc.) of this instruction. |
| // Used by alias analysis, GVN and LICM. |
| uint32_t virtualRegister_; // Used by lowering to map definitions to virtual registers. |
| }; |
| |
| // Track bailouts by storing the current pc in MIR instruction. Also used |
| // for profiling and keeping track of what the last known pc was. |
| jsbytecode *trackedPc_; |
| |
| private: |
| enum Flag { |
| None = 0, |
| # define DEFINE_FLAG(flag) flag, |
| MIR_FLAG_LIST(DEFINE_FLAG) |
| # undef DEFINE_FLAG |
| Total |
| }; |
| |
| void setBlock(MBasicBlock *block) { |
| block_ = block; |
| } |
| |
| bool hasFlags(uint32_t flags) const { |
| return (flags_ & flags) == flags; |
| } |
| void removeFlags(uint32_t flags) { |
| flags_ &= ~flags; |
| } |
| void setFlags(uint32_t flags) { |
| flags_ |= flags; |
| } |
| virtual bool neverHoist() const { return false; } |
| public: |
| MDefinition() |
| : id_(0), |
| valueNumber_(NULL), |
| range_(NULL), |
| resultType_(MIRType_None), |
| resultTypeSet_(NULL), |
| flags_(0), |
| dependency_(NULL), |
| trackedPc_(NULL) |
| { } |
| |
| virtual Opcode op() const = 0; |
| virtual const char *opName() const = 0; |
| void printName(FILE *fp); |
| static void PrintOpcodeName(FILE *fp, Opcode op); |
| virtual void printOpcode(FILE *fp); |
| |
| void setTrackedPc(jsbytecode *pc) { |
| trackedPc_ = pc; |
| } |
| |
| jsbytecode *trackedPc() { |
| return trackedPc_; |
| } |
| |
| Range *range() const { |
| return range_; |
| } |
| void setRange(Range *range) { |
| range_ = range; |
| } |
| |
| virtual HashNumber valueHash() const; |
| virtual bool congruentTo(MDefinition* const &ins) const { |
| return false; |
| } |
| bool congruentIfOperandsEqual(MDefinition * const &ins) const; |
| virtual MDefinition *foldsTo(bool useValueNumbers); |
| virtual void analyzeEdgeCasesForward(); |
| virtual void analyzeEdgeCasesBackward(); |
| |
| virtual bool truncate(); |
| virtual bool isOperandTruncated(size_t index) const; |
| |
| bool earlyAbortCheck(); |
| |
| // Compute an absolute or symbolic range for the value of this node. |
| // Ranges are only computed for definitions whose type is int32. |
| virtual void computeRange() { |
| } |
| |
| MNode::Kind kind() const { |
| return MNode::Definition; |
| } |
| |
| uint32_t id() const { |
| JS_ASSERT(block_); |
| return id_; |
| } |
| void setId(uint32_t id) { |
| id_ = id; |
| } |
| |
| uint32_t valueNumber() const; |
| void setValueNumber(uint32_t vn); |
| ValueNumberData *valueNumberData() { |
| return valueNumber_; |
| } |
| void clearValueNumberData() { |
| valueNumber_ = NULL; |
| } |
| void setValueNumberData(ValueNumberData *vn) { |
| JS_ASSERT(valueNumber_ == NULL); |
| valueNumber_ = vn; |
| } |
| #define FLAG_ACCESSOR(flag) \ |
| bool is##flag() const {\ |
| return hasFlags(1 << flag);\ |
| }\ |
| void set##flag() {\ |
| JS_ASSERT(!hasFlags(1 << flag));\ |
| setFlags(1 << flag);\ |
| }\ |
| void setNot##flag() {\ |
| JS_ASSERT(hasFlags(1 << flag));\ |
| removeFlags(1 << flag);\ |
| }\ |
| void set##flag##Unchecked() {\ |
| setFlags(1 << flag);\ |
| } |
| |
| MIR_FLAG_LIST(FLAG_ACCESSOR) |
| #undef FLAG_ACCESSOR |
| |
| MIRType type() const { |
| return resultType_; |
| } |
| |
| types::StackTypeSet *resultTypeSet() const { |
| return resultTypeSet_; |
| } |
| bool emptyResultTypeSet() const; |
| |
| bool mightBeType(MIRType type) const { |
| JS_ASSERT(type != MIRType_Value); |
| |
| if (type == this->type()) |
| return true; |
| |
| if (MIRType_Value != this->type()) |
| return false; |
| |
| return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type)); |
| } |
| |
| // Returns the beginning of this definition's use chain. |
| MUseIterator usesBegin() const { |
| return uses_.begin(); |
| } |
| |
| // Returns the end of this definition's use chain. |
| MUseIterator usesEnd() const { |
| return uses_.end(); |
| } |
| |
| bool canEmitAtUses() const { |
| return !isEmittedAtUses(); |
| } |
| |
| // Removes a use at the given position |
| MUseIterator removeUse(MUseIterator use); |
| void removeUse(MUse *use) { |
| uses_.remove(use); |
| } |
| |
| // Number of uses of this instruction. |
| size_t useCount() const; |
| |
| // Number of uses of this instruction. |
| // (only counting MDefinitions, ignoring MResumePoints) |
| size_t defUseCount() const; |
| |
| bool hasUses() const { |
| return !uses_.empty(); |
| } |
| |
| virtual bool isControlInstruction() const { |
| return false; |
| } |
| |
| void addUse(MUse *use) { |
| uses_.pushFront(use); |
| } |
| void replaceAllUsesWith(MDefinition *dom); |
| |
| // Mark this instruction as having replaced all uses of ins, as during GVN, |
| // returning false if the replacement should not be performed. For use when |
| // GVN eliminates instructions which are not equivalent to one another. |
| virtual bool updateForReplacement(MDefinition *ins) { |
| return true; |
| } |
| |
| // Same thing, but for folding |
| virtual bool updateForFolding(MDefinition *ins) { |
| return true; |
| } |
| |
| void setVirtualRegister(uint32_t vreg) { |
| virtualRegister_ = vreg; |
| #ifdef DEBUG |
| setLoweredUnchecked(); |
| #endif |
| } |
| uint32_t virtualRegister() const { |
| JS_ASSERT(isLowered()); |
| return virtualRegister_; |
| } |
| |
| public: |
| // Opcode testing and casts. |
| # define OPCODE_CASTS(opcode) \ |
| bool is##opcode() const { \ |
| return op() == Op_##opcode; \ |
| } \ |
| inline M##opcode *to##opcode(); |
| MIR_OPCODE_LIST(OPCODE_CASTS) |
| # undef OPCODE_CASTS |
| |
| inline MInstruction *toInstruction(); |
| bool isInstruction() const { |
| return !isPhi(); |
| } |
| |
| void setResultType(MIRType type) { |
| resultType_ = type; |
| } |
| void setResultTypeSet(types::StackTypeSet *types) { |
| resultTypeSet_ = types; |
| } |
| |
| MDefinition *dependency() const { |
| return dependency_; |
| } |
| void setDependency(MDefinition *dependency) { |
| dependency_ = dependency; |
| } |
| virtual AliasSet getAliasSet() const { |
| // Instructions are effectful by default. |
| return AliasSet::Store(AliasSet::Any); |
| } |
| bool isEffectful() const { |
| return getAliasSet().isStore(); |
| } |
| virtual bool mightAlias(MDefinition *store) { |
| // Return whether this load may depend on the specified store, given |
| // that the alias sets intersect. This may be refined to exclude |
| // possible aliasing in cases where alias set flags are too imprecise. |
| JS_ASSERT(!isEffectful() && store->isEffectful()); |
| JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags()); |
| return true; |
| } |
| }; |
| |
| // An MUseDefIterator walks over uses in a definition, skipping any use that is |
| // not a definition. Items from the use list must not be deleted during |
| // iteration. |
| class MUseDefIterator |
| { |
| MDefinition *def_; |
| MUseIterator current_; |
| |
| MUseIterator search(MUseIterator start) { |
| MUseIterator i(start); |
| for (; i != def_->usesEnd(); i++) { |
| if (i->consumer()->isDefinition()) |
| return i; |
| } |
| return def_->usesEnd(); |
| } |
| |
| public: |
| MUseDefIterator(MDefinition *def) |
| : def_(def), |
| current_(search(def->usesBegin())) |
| { } |
| |
| operator bool() const { |
| return current_ != def_->usesEnd(); |
| } |
| MUseDefIterator operator ++(int) { |
| MUseDefIterator old(*this); |
| if (current_ != def_->usesEnd()) |
| current_++; |
| current_ = search(current_); |
| return old; |
| } |
| MUse *use() const { |
| return *current_; |
| } |
| MDefinition *def() const { |
| return current_->consumer()->toDefinition(); |
| } |
| size_t index() const { |
| return current_->index(); |
| } |
| }; |
| |
| // An instruction is an SSA name that is inserted into a basic block's IR |
| // stream. |
| class MInstruction |
| : public MDefinition, |
| public InlineListNode<MInstruction> |
| { |
| MResumePoint *resumePoint_; |
| |
| public: |
| MInstruction() |
| : resumePoint_(NULL) |
| { } |
| |
| virtual bool accept(MInstructionVisitor *visitor) = 0; |
| |
| void setResumePoint(MResumePoint *resumePoint) { |
| JS_ASSERT(!resumePoint_); |
| resumePoint_ = resumePoint; |
| } |
| MResumePoint *resumePoint() const { |
| return resumePoint_; |
| } |
| }; |
| |
| #define INSTRUCTION_HEADER(opcode) \ |
| Opcode op() const { \ |
| return MDefinition::Op_##opcode; \ |
| } \ |
| const char *opName() const { \ |
| return #opcode; \ |
| } \ |
| bool accept(MInstructionVisitor *visitor) { \ |
| return visitor->visit##opcode(this); \ |
| } |
| |
| template <size_t Arity> |
| class MAryInstruction : public MInstruction |
| { |
| protected: |
| FixedArityList<MUse, Arity> operands_; |
| |
| void setOperand(size_t index, MDefinition *operand) { |
| operands_[index].set(operand, this, index); |
| operand->addUse(&operands_[index]); |
| } |
| |
| MUse *getUseFor(size_t index) { |
| return &operands_[index]; |
| } |
| |
| public: |
| MDefinition *getOperand(size_t index) const { |
| return operands_[index].producer(); |
| } |
| size_t numOperands() const { |
| return Arity; |
| } |
| }; |
| |
| class MNullaryInstruction : public MAryInstruction<0> |
| { }; |
| |
| class MUnaryInstruction : public MAryInstruction<1> |
| { |
| protected: |
| MUnaryInstruction(MDefinition *ins) |
| { |
| setOperand(0, ins); |
| } |
| }; |
| |
| // Generates an LSnapshot without further effect. |
| class MStart : public MNullaryInstruction |
| { |
| public: |
| enum StartType { |
| StartType_Default, |
| StartType_Osr |
| }; |
| |
| private: |
| StartType startType_; |
| |
| private: |
| MStart(StartType startType) |
| : startType_(startType) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(Start) |
| static MStart *New(StartType startType) { |
| return new MStart(startType); |
| } |
| |
| StartType startType() { |
| return startType_; |
| } |
| }; |
| |
| // Instruction marking on entrypoint for on-stack replacement. |
| // OSR may occur at loop headers (at JSOP_TRACE). |
| // There is at most one MOsrEntry per MIRGraph. |
| class MOsrEntry : public MNullaryInstruction |
| { |
| protected: |
| MOsrEntry() { |
| setResultType(MIRType_Pointer); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(OsrEntry) |
| static MOsrEntry *New() { |
| return new MOsrEntry; |
| } |
| }; |
| |
| // No-op instruction. This cannot be moved or eliminated, and is intended for |
| // anchoring resume points at arbitrary points in a block. |
| class MNop : public MNullaryInstruction |
| { |
| protected: |
| MNop() { |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Nop) |
| static MNop *New() { |
| return new MNop(); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // A constant js::Value. |
| class MConstant : public MNullaryInstruction |
| { |
| Value value_; |
| |
| protected: |
| MConstant(const Value &v); |
| |
| public: |
| INSTRUCTION_HEADER(Constant) |
| static MConstant *New(const Value &v); |
| |
| const js::Value &value() const { |
| return value_; |
| } |
| const js::Value *vp() const { |
| return &value_; |
| } |
| |
| void printOpcode(FILE *fp); |
| |
| HashNumber valueHash() const; |
| bool congruentTo(MDefinition * const &ins) const; |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| |
| void computeRange(); |
| bool truncate(); |
| }; |
| |
| class MParameter : public MNullaryInstruction |
| { |
| int32_t index_; |
| |
| public: |
| static const int32_t THIS_SLOT = -1; |
| |
| MParameter(int32_t index, types::StackTypeSet *types) |
| : index_(index) |
| { |
| setResultType(MIRType_Value); |
| setResultTypeSet(types); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Parameter) |
| static MParameter *New(int32_t index, types::StackTypeSet *types); |
| |
| int32_t index() const { |
| return index_; |
| } |
| void printOpcode(FILE *fp); |
| |
| HashNumber valueHash() const; |
| bool congruentTo(MDefinition * const &ins) const; |
| }; |
| |
| class MCallee : public MNullaryInstruction |
| { |
| public: |
| MCallee() |
| { |
| setResultType(MIRType_Object); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Callee) |
| |
| bool congruentTo(MDefinition * const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| |
| static MCallee *New() { |
| return new MCallee(); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MControlInstruction : public MInstruction |
| { |
| public: |
| MControlInstruction() |
| { } |
| |
| virtual size_t numSuccessors() const = 0; |
| virtual MBasicBlock *getSuccessor(size_t i) const = 0; |
| virtual void replaceSuccessor(size_t i, MBasicBlock *successor) = 0; |
| |
| bool isControlInstruction() const { |
| return true; |
| } |
| }; |
| |
| class MTableSwitch |
| : public MControlInstruction |
| { |
| // The successors of the tableswitch |
| // - First successor = the default case |
| // - Successor 2 and higher = the cases sorted on case index. |
| Vector<MBasicBlock*, 0, IonAllocPolicy> successors_; |
| |
| // Contains the blocks/cases that still need to get build |
| Vector<MBasicBlock*, 0, IonAllocPolicy> blocks_; |
| |
| MUse operand_; |
| int32_t low_; |
| int32_t high_; |
| |
| MTableSwitch(MDefinition *ins, |
| int32_t low, int32_t high) |
| : successors_(), |
| blocks_(), |
| low_(low), |
| high_(high) |
| { |
| setOperand(0, ins); |
| } |
| |
| protected: |
| void setOperand(size_t index, MDefinition *operand) { |
| JS_ASSERT(index == 0); |
| operand_.set(operand, this, index); |
| operand->addUse(&operand_); |
| } |
| |
| MUse *getUseFor(size_t index) { |
| JS_ASSERT(index == 0); |
| return &operand_; |
| } |
| |
| public: |
| INSTRUCTION_HEADER(TableSwitch) |
| static MTableSwitch *New(MDefinition *ins, int32_t low, int32_t high); |
| |
| size_t numSuccessors() const { |
| return successors_.length(); |
| } |
| |
| MBasicBlock *getSuccessor(size_t i) const { |
| JS_ASSERT(i < numSuccessors()); |
| return successors_[i]; |
| } |
| |
| void replaceSuccessor(size_t i, MBasicBlock *successor) { |
| JS_ASSERT(i < numSuccessors()); |
| successors_[i] = successor; |
| } |
| |
| MBasicBlock** blocks() { |
| return &blocks_[0]; |
| } |
| |
| size_t numBlocks() const { |
| return blocks_.length(); |
| } |
| |
| int32_t low() const { |
| return low_; |
| } |
| |
| int32_t high() const { |
| return high_; |
| } |
| |
| MBasicBlock *getDefault() const { |
| return getSuccessor(0); |
| } |
| |
| MBasicBlock *getCase(size_t i) const { |
| return getSuccessor(i+1); |
| } |
| |
| size_t numCases() const { |
| return high() - low() + 1; |
| } |
| |
| void addDefault(MBasicBlock *block) { |
| JS_ASSERT(successors_.length() == 0); |
| successors_.append(block); |
| } |
| |
| void addCase(MBasicBlock *block) { |
| JS_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2)); |
| JS_ASSERT(successors_.length() != 0); |
| successors_.append(block); |
| } |
| |
| MBasicBlock *getBlock(size_t i) const { |
| JS_ASSERT(i < numBlocks()); |
| return blocks_[i]; |
| } |
| |
| void addBlock(MBasicBlock *block) { |
| blocks_.append(block); |
| } |
| |
| MDefinition *getOperand(size_t index) const { |
| JS_ASSERT(index == 0); |
| return operand_.producer(); |
| } |
| |
| size_t numOperands() const { |
| return 1; |
| } |
| }; |
| |
| template <size_t Arity, size_t Successors> |
| class MAryControlInstruction : public MControlInstruction |
| { |
| FixedArityList<MUse, Arity> operands_; |
| FixedArityList<MBasicBlock *, Successors> successors_; |
| |
| protected: |
| void setOperand(size_t index, MDefinition *operand) { |
| operands_[index].set(operand, this, index); |
| operand->addUse(&operands_[index]); |
| } |
| void setSuccessor(size_t index, MBasicBlock *successor) { |
| successors_[index] = successor; |
| } |
| |
| MUse *getUseFor(size_t index) { |
| return &operands_[index]; |
| } |
| |
| public: |
| MDefinition *getOperand(size_t index) const { |
| return operands_[index].producer(); |
| } |
| size_t numOperands() const { |
| return Arity; |
| } |
| size_t numSuccessors() const { |
| return Successors; |
| } |
| MBasicBlock *getSuccessor(size_t i) const { |
| return successors_[i]; |
| } |
| void replaceSuccessor(size_t i, MBasicBlock *succ) { |
| successors_[i] = succ; |
| } |
| }; |
| |
| // Jump to the start of another basic block. |
| class MGoto : public MAryControlInstruction<0, 1> |
| { |
| MGoto(MBasicBlock *target) { |
| setSuccessor(0, target); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Goto) |
| static MGoto *New(MBasicBlock *target); |
| |
| MBasicBlock *target() { |
| return getSuccessor(0); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| enum BranchDirection { |
| FALSE_BRANCH, |
| TRUE_BRANCH |
| }; |
| |
| static inline BranchDirection |
| NegateBranchDirection(BranchDirection dir) |
| { |
| return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH; |
| } |
| |
| // Tests if the input instruction evaluates to true or false, and jumps to the |
| // start of a corresponding basic block. |
| class MTest |
| : public MAryControlInstruction<1, 2>, |
| public TestPolicy |
| { |
| bool operandMightEmulateUndefined_; |
| |
| MTest(MDefinition *ins, MBasicBlock *if_true, MBasicBlock *if_false) |
| : operandMightEmulateUndefined_(true) |
| { |
| setOperand(0, ins); |
| setSuccessor(0, if_true); |
| setSuccessor(1, if_false); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Test) |
| static MTest *New(MDefinition *ins, |
| MBasicBlock *ifTrue, MBasicBlock *ifFalse); |
| |
| MBasicBlock *ifTrue() const { |
| return getSuccessor(0); |
| } |
| MBasicBlock *ifFalse() const { |
| return getSuccessor(1); |
| } |
| MBasicBlock *branchSuccessor(BranchDirection dir) const { |
| return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse(); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| void infer(JSContext *cx); |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| void markOperandCantEmulateUndefined() { |
| operandMightEmulateUndefined_ = false; |
| } |
| bool operandMightEmulateUndefined() const { |
| return operandMightEmulateUndefined_; |
| } |
| }; |
| |
| // Returns from this function to the previous caller. |
| class MReturn |
| : public MAryControlInstruction<1, 0>, |
| public BoxInputsPolicy |
| { |
| MReturn(MDefinition *ins) { |
| setOperand(0, ins); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Return) |
| static MReturn *New(MDefinition *ins) { |
| return new MReturn(ins); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MThrow |
| : public MAryControlInstruction<1, 0>, |
| public BoxInputsPolicy |
| { |
| MThrow(MDefinition *ins) { |
| setOperand(0, ins); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Throw) |
| static MThrow *New(MDefinition *ins) { |
| return new MThrow(ins); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| virtual AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MNewParallelArray : public MNullaryInstruction |
| { |
| CompilerRootObject templateObject_; |
| |
| MNewParallelArray(JSObject *templateObject) |
| : templateObject_(templateObject) |
| { |
| setResultType(MIRType_Object); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(NewParallelArray); |
| |
| static MNewParallelArray *New(JSObject *templateObject) { |
| return new MNewParallelArray(templateObject); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| |
| JSObject *templateObject() const { |
| return templateObject_; |
| } |
| }; |
| |
| // Fabricate a type set containing only the type of the specified object. |
| types::StackTypeSet * |
| MakeSingletonTypeSet(JSObject *obj); |
| |
| void |
| MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet, |
| MIRType newType, types::StackTypeSet *newTypeSet); |
| |
| class MNewArray : public MNullaryInstruction |
| { |
| public: |
| enum AllocatingBehaviour { |
| NewArray_Allocating, |
| NewArray_Unallocating |
| }; |
| |
| private: |
| // Number of space to allocate for the array. |
| uint32_t count_; |
| // Template for the created object. |
| CompilerRootObject templateObject_; |
| // Allocate space at initialization or not |
| AllocatingBehaviour allocating_; |
| |
| public: |
| INSTRUCTION_HEADER(NewArray) |
| |
| MNewArray(uint32_t count, JSObject *templateObject, AllocatingBehaviour allocating) |
| : count_(count), |
| templateObject_(templateObject), |
| allocating_(allocating) |
| { |
| setResultType(MIRType_Object); |
| setResultTypeSet(MakeSingletonTypeSet(templateObject)); |
| } |
| |
| uint32_t count() const { |
| return count_; |
| } |
| |
| JSObject *templateObject() const { |
| return templateObject_; |
| } |
| |
| bool isAllocating() const { |
| return allocating_ == NewArray_Allocating; |
| } |
| |
| // Returns true if the code generator should call through to the |
| // VM rather than the fast path. |
| bool shouldUseVM() const; |
| |
| // NewArray is marked as non-effectful because all our allocations are |
| // either lazy when we are using "new Array(length)" or bounded by the |
| // script or the stack size when we are using "new Array(...)" or "[...]" |
| // notations. So we might have to allocate the array twice if we bail |
| // during the computation of the first element of the square braket |
| // notation. |
| virtual AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MNewObject : public MNullaryInstruction |
| { |
| CompilerRootObject templateObject_; |
| bool templateObjectIsClassPrototype_; |
| |
| MNewObject(JSObject *templateObject, bool templateObjectIsClassPrototype) |
| : templateObject_(templateObject), |
| templateObjectIsClassPrototype_(templateObjectIsClassPrototype) |
| { |
| JS_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM()); |
| setResultType(MIRType_Object); |
| setResultTypeSet(MakeSingletonTypeSet(templateObject)); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(NewObject) |
| |
| static MNewObject *New(JSObject *templateObject, bool templateObjectIsClassPrototype) { |
| return new MNewObject(templateObject, templateObjectIsClassPrototype); |
| } |
| |
| // Returns true if the code generator should call through to the |
| // VM rather than the fast path. |
| bool shouldUseVM() const; |
| |
| bool templateObjectIsClassPrototype() const { |
| return templateObjectIsClassPrototype_; |
| } |
| |
| JSObject *templateObject() const { |
| return templateObject_; |
| } |
| }; |
| |
| // Could be allocating either a new array or a new object. |
| class MParNew : public MUnaryInstruction |
| { |
| CompilerRootObject templateObject_; |
| |
| public: |
| INSTRUCTION_HEADER(ParNew); |
| |
| MParNew(MDefinition *parSlice, |
| JSObject *templateObject) |
| : MUnaryInstruction(parSlice), |
| templateObject_(templateObject) |
| { |
| setResultType(MIRType_Object); |
| } |
| |
| MDefinition *parSlice() const { |
| return getOperand(0); |
| } |
| |
| JSObject *templateObject() const { |
| return templateObject_; |
| } |
| }; |
| |
| // Could be allocating either a new array or a new object. |
| class MParBailout : public MAryControlInstruction<0, 0> |
| { |
| public: |
| INSTRUCTION_HEADER(ParBailout); |
| |
| MParBailout() |
| : MAryControlInstruction<0, 0>() |
| { |
| setResultType(MIRType_Undefined); |
| setGuard(); |
| } |
| }; |
| |
| // Slow path for adding a property to an object without a known base. |
| class MInitProp |
| : public MAryInstruction<2>, |
| public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
| { |
| public: |
| CompilerRootPropertyName name_; |
| |
| protected: |
| MInitProp(MDefinition *obj, HandlePropertyName name, MDefinition *value) |
| : name_(name) |
| { |
| setOperand(0, obj); |
| setOperand(1, value); |
| setResultType(MIRType_None); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(InitProp) |
| |
| static MInitProp *New(MDefinition *obj, HandlePropertyName name, MDefinition *value) { |
| return new MInitProp(obj, name, value); |
| } |
| |
| MDefinition *getObject() const { |
| return getOperand(0); |
| } |
| MDefinition *getValue() const { |
| return getOperand(1); |
| } |
| |
| PropertyName *propertyName() const { |
| return name_; |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MInitElem |
| : public MAryInstruction<3>, |
| public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> > |
| { |
| MInitElem(MDefinition *obj, MDefinition *id, MDefinition *value) |
| { |
| setOperand(0, obj); |
| setOperand(1, id); |
| setOperand(2, value); |
| setResultType(MIRType_None); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(InitElem) |
| |
| static MInitElem *New(MDefinition *obj, MDefinition *id, MDefinition *value) { |
| return new MInitElem(obj, id, value); |
| } |
| |
| MDefinition *getObject() const { |
| return getOperand(0); |
| } |
| MDefinition *getId() const { |
| return getOperand(1); |
| } |
| MDefinition *getValue() const { |
| return getOperand(2); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| // Designates the start of call frame construction. |
| // Generates code to adjust the stack pointer for the argument vector. |
| // Argc is inferred by checking the use chain during lowering. |
| class MPrepareCall : public MNullaryInstruction |
| { |
| public: |
| INSTRUCTION_HEADER(PrepareCall) |
| |
| MPrepareCall() |
| { } |
| |
| // Get the vector size for the upcoming call by looking at the call. |
| uint32_t argc() const; |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MVariadicInstruction : public MInstruction |
| { |
| FixedList<MUse> operands_; |
| |
| protected: |
| bool init(size_t length) { |
| return operands_.init(length); |
| } |
| |
| public: |
| // Will assert if called before initialization. |
| MDefinition *getOperand(size_t index) const { |
| return operands_[index].producer(); |
| } |
| size_t numOperands() const { |
| return operands_.length(); |
| } |
| void setOperand(size_t index, MDefinition *operand) { |
| operands_[index].set(operand, this, index); |
| operand->addUse(&operands_[index]); |
| } |
| |
| MUse *getUseFor(size_t index) { |
| return &operands_[index]; |
| } |
| }; |
| |
| class MCall |
| : public MVariadicInstruction, |
| public CallPolicy |
| { |
| private: |
| // An MCall uses the MPrepareCall, MDefinition for the function, and |
| // MPassArg instructions. They are stored in the same list. |
| static const size_t PrepareCallOperandIndex = 0; |
| static const size_t FunctionOperandIndex = 1; |
| static const size_t NumNonArgumentOperands = 2; |
| |
| protected: |
| // True if the call is for JSOP_NEW. |
| bool construct_; |
| // Monomorphic cache of single target from TI, or NULL. |
| CompilerRootFunction target_; |
| // Holds a target's Script alive. |
| CompilerRootScript targetScript_; |
| // Original value of argc from the bytecode. |
| uint32_t numActualArgs_; |
| |
| bool needsArgCheck_; |
| |
| MCall(JSFunction *target, uint32_t numActualArgs, bool construct) |
| : construct_(construct), |
| target_(target), |
| targetScript_(NULL), |
| numActualArgs_(numActualArgs), |
| needsArgCheck_(true) |
| { |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Call) |
| static MCall *New(JSFunction *target, size_t maxArgc, size_t numActualArgs, bool construct); |
| |
| void initPrepareCall(MDefinition *start) { |
| JS_ASSERT(start->isPrepareCall()); |
| return setOperand(PrepareCallOperandIndex, start); |
| } |
| void initFunction(MDefinition *func) { |
| JS_ASSERT(!func->isPassArg()); |
| return setOperand(FunctionOperandIndex, func); |
| } |
| |
| bool needsArgCheck() const { |
| return needsArgCheck_; |
| } |
| |
| void disableArgCheck() { |
| needsArgCheck_ = false; |
| } |
| |
| MPrepareCall *getPrepareCall() { |
| return getOperand(PrepareCallOperandIndex)->toPrepareCall(); |
| } |
| MDefinition *getFunction() const { |
| return getOperand(FunctionOperandIndex); |
| } |
| void replaceFunction(MInstruction *newfunc) { |
| replaceOperand(FunctionOperandIndex, newfunc); |
| } |
| |
| void addArg(size_t argnum, MPassArg *arg); |
| |
| MDefinition *getArg(uint32_t index) const { |
| return getOperand(NumNonArgumentOperands + index); |
| } |
| |
| void rootTargetScript(JSFunction *target) { |
| targetScript_.setRoot(target->nonLazyScript()); |
| } |
| bool hasRootedScript() { |
| return targetScript_ != NULL; |
| } |
| |
| // For TI-informed monomorphic callsites. |
| JSFunction *getSingleTarget() const { |
| return target_; |
| } |
| |
| bool isConstructing() const { |
| return construct_; |
| } |
| |
| // The number of stack arguments is the max between the number of formal |
| // arguments and the number of actual arguments. The number of stack |
| // argument includes the |undefined| padding added in case of underflow. |
| // Includes |this|. |
| uint32_t numStackArgs() const { |
| return numOperands() - NumNonArgumentOperands; |
| } |
| |
| // Does not include |this|. |
| uint32_t numActualArgs() const { |
| return numActualArgs_; |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::Store(AliasSet::Any); |
| } |
| }; |
| |
| // fun.apply(self, arguments) |
| class MApplyArgs |
| : public MAryInstruction<3>, |
| public MixPolicy<ObjectPolicy<0>, MixPolicy<IntPolicy<1>, BoxPolicy<2> > > |
| { |
| protected: |
| // Monomorphic cache of single target from TI, or NULL. |
| CompilerRootFunction target_; |
| |
| MApplyArgs(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self) |
| : target_(target) |
| { |
| setOperand(0, fun); |
| setOperand(1, argc); |
| setOperand(2, self); |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ApplyArgs) |
| static MApplyArgs *New(JSFunction *target, MDefinition *fun, MDefinition *argc, |
| MDefinition *self); |
| |
| MDefinition *getFunction() const { |
| return getOperand(0); |
| } |
| |
| // For TI-informed monomorphic callsites. |
| JSFunction *getSingleTarget() const { |
| return target_; |
| } |
| |
| MDefinition *getArgc() const { |
| return getOperand(1); |
| } |
| MDefinition *getThis() const { |
| return getOperand(2); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MGetDynamicName |
| : public MAryInstruction<2>, |
| public MixPolicy<ObjectPolicy<0>, StringPolicy<1> > |
| { |
| protected: |
| MGetDynamicName(MDefinition *scopeChain, MDefinition *name) |
| { |
| setOperand(0, scopeChain); |
| setOperand(1, name); |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(GetDynamicName) |
| |
| static MGetDynamicName * |
| New(MDefinition *scopeChain, MDefinition *name) { |
| return new MGetDynamicName(scopeChain, name); |
| } |
| |
| MDefinition *getScopeChain() const { |
| return getOperand(0); |
| } |
| MDefinition *getName() const { |
| return getOperand(1); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| // Bailout if the input string contains 'arguments' |
| class MFilterArguments |
| : public MAryInstruction<1>, |
| public StringPolicy<0> |
| { |
| protected: |
| MFilterArguments(MDefinition *string) |
| { |
| setOperand(0, string); |
| setGuard(); |
| setResultType(MIRType_None); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(FilterArguments) |
| |
| static MFilterArguments * |
| New(MDefinition *string) { |
| return new MFilterArguments(string); |
| } |
| |
| MDefinition *getString() const { |
| return getOperand(0); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MCallDirectEval |
| : public MAryInstruction<3>, |
| public MixPolicy<ObjectPolicy<0>, MixPolicy<StringPolicy<1>, BoxPolicy<2> > > |
| { |
| protected: |
| MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, |
| jsbytecode *pc) |
| : pc_(pc) |
| { |
| setOperand(0, scopeChain); |
| setOperand(1, string); |
| setOperand(2, thisValue); |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(CallDirectEval) |
| |
| static MCallDirectEval * |
| New(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, |
| jsbytecode *pc) { |
| return new MCallDirectEval(scopeChain, string, thisValue, pc); |
| } |
| |
| MDefinition *getScopeChain() const { |
| return getOperand(0); |
| } |
| MDefinition *getString() const { |
| return getOperand(1); |
| } |
| MDefinition *getThisValue() const { |
| return getOperand(2); |
| } |
| |
| jsbytecode *pc() const { |
| return pc_; |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| private: |
| jsbytecode *pc_; |
| }; |
| |
| class MBinaryInstruction : public MAryInstruction<2> |
| { |
| protected: |
| MBinaryInstruction(MDefinition *left, MDefinition *right) |
| { |
| setOperand(0, left); |
| setOperand(1, right); |
| } |
| |
| public: |
| MDefinition *lhs() const { |
| return getOperand(0); |
| } |
| MDefinition *rhs() const { |
| return getOperand(1); |
| } |
| |
| protected: |
| HashNumber valueHash() const |
| { |
| MDefinition *lhs = getOperand(0); |
| MDefinition *rhs = getOperand(1); |
| |
| return op() ^ lhs->valueNumber() ^ rhs->valueNumber(); |
| } |
| void swapOperands() { |
| MDefinition *temp = getOperand(0); |
| replaceOperand(0, getOperand(1)); |
| replaceOperand(1, temp); |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const |
| { |
| if (op() != ins->op()) |
| return false; |
| |
| if (type() != ins->type()) |
| return false; |
| |
| if (isEffectful() || ins->isEffectful()) |
| return false; |
| |
| MDefinition *left = getOperand(0); |
| MDefinition *right = getOperand(1); |
| MDefinition *tmp; |
| |
| if (isCommutative() && left->valueNumber() > right->valueNumber()) { |
| tmp = right; |
| right = left; |
| left = tmp; |
| } |
| |
| MDefinition *insLeft = ins->getOperand(0); |
| MDefinition *insRight = ins->getOperand(1); |
| if (isCommutative() && insLeft->valueNumber() > insRight->valueNumber()) { |
| tmp = insRight; |
| insRight = insLeft; |
| insLeft = tmp; |
| } |
| |
| return (left->valueNumber() == insLeft->valueNumber()) && |
| (right->valueNumber() == insRight->valueNumber()); |
| } |
| }; |
| |
| class MTernaryInstruction : public MAryInstruction<3> |
| { |
| protected: |
| MTernaryInstruction(MDefinition *first, MDefinition *second, MDefinition *third) |
| { |
| setOperand(0, first); |
| setOperand(1, second); |
| setOperand(2, third); |
| } |
| |
| protected: |
| HashNumber valueHash() const |
| { |
| MDefinition *first = getOperand(0); |
| MDefinition *second = getOperand(1); |
| MDefinition *third = getOperand(2); |
| |
| return op() ^ first->valueNumber() ^ second->valueNumber() ^ third->valueNumber(); |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const |
| { |
| if (op() != ins->op()) |
| return false; |
| |
| if (type() != ins->type()) |
| return false; |
| |
| if (isEffectful() || ins->isEffectful()) |
| return false; |
| |
| MDefinition *first = getOperand(0); |
| MDefinition *second = getOperand(1); |
| MDefinition *third = getOperand(2); |
| MDefinition *insFirst = ins->getOperand(0); |
| MDefinition *insSecond = ins->getOperand(1); |
| MDefinition *insThird = ins->getOperand(2); |
| |
| return first->valueNumber() == insFirst->valueNumber() && |
| second->valueNumber() == insSecond->valueNumber() && |
| third->valueNumber() == insThird->valueNumber(); |
| } |
| }; |
| |
| class MQuaternaryInstruction : public MAryInstruction<4> |
| { |
| protected: |
| MQuaternaryInstruction(MDefinition *first, MDefinition *second, |
| MDefinition *third, MDefinition *fourth) |
| { |
| setOperand(0, first); |
| setOperand(1, second); |
| setOperand(2, third); |
| setOperand(3, fourth); |
| } |
| |
| protected: |
| HashNumber valueHash() const |
| { |
| MDefinition *first = getOperand(0); |
| MDefinition *second = getOperand(1); |
| MDefinition *third = getOperand(2); |
| MDefinition *fourth = getOperand(3); |
| |
| return op() ^ first->valueNumber() ^ second->valueNumber() ^ |
| third->valueNumber() ^ fourth->valueNumber(); |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const |
| { |
| if (op() != ins->op()) |
| return false; |
| |
| if (type() != ins->type()) |
| return false; |
| |
| if (isEffectful() || ins->isEffectful()) |
| return false; |
| |
| MDefinition *first = getOperand(0); |
| MDefinition *second = getOperand(1); |
| MDefinition *third = getOperand(2); |
| MDefinition *fourth = getOperand(3); |
| MDefinition *insFirst = ins->getOperand(0); |
| MDefinition *insSecond = ins->getOperand(1); |
| MDefinition *insThird = ins->getOperand(2); |
| MDefinition *insFourth = ins->getOperand(3); |
| |
| return first->valueNumber() == insFirst->valueNumber() && |
| second->valueNumber() == insSecond->valueNumber() && |
| third->valueNumber() == insThird->valueNumber() && |
| fourth->valueNumber() == insFourth->valueNumber(); |
| } |
| }; |
| |
| class MCompare |
| : public MBinaryInstruction, |
| public ComparePolicy |
| { |
| public: |
| enum CompareType { |
| |
| // Anything compared to Undefined |
| Compare_Undefined, |
| |
| // Anything compared to Null |
| Compare_Null, |
| |
| // Undefined compared to Boolean |
| // Null compared to Boolean |
| // Double compared to Boolean |
| // String compared to Boolean |
| // Object compared to Boolean |
| // Value compared to Boolean |
| Compare_Boolean, |
| |
| // Int32 compared to Int32 |
| // Boolean compared to Boolean |
| Compare_Int32, |
| |
| // Int32 compared as unsigneds |
| Compare_UInt32, |
| |
| // Double compared to Double |
| Compare_Double, |
| |
| Compare_DoubleMaybeCoerceLHS, |
| Compare_DoubleMaybeCoerceRHS, |
| |
| // String compared to String |
| Compare_String, |
| |
| // Undefined compared to String |
| // Null compared to String |
| // Boolean compared to String |
| // Int32 compared to String |
| // Double compared to String |
| // Object compared to String |
| // Value compared to String |
| Compare_StrictString, |
| |
| // Object compared to Object |
| Compare_Object, |
| |
| // Compare 2 values bitwise |
| Compare_Value, |
| |
| // All other possible compares |
| Compare_Unknown |
| }; |
| |
| private: |
| CompareType compareType_; |
| JSOp jsop_; |
| bool operandMightEmulateUndefined_; |
| |
| MCompare(MDefinition *left, MDefinition *right, JSOp jsop) |
| : MBinaryInstruction(left, right), |
| compareType_(Compare_Unknown), |
| jsop_(jsop), |
| operandMightEmulateUndefined_(true) |
| { |
| setResultType(MIRType_Boolean); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Compare) |
| static MCompare *New(MDefinition *left, MDefinition *right, JSOp op); |
| static MCompare *NewAsmJS(MDefinition *left, MDefinition *right, JSOp op, CompareType compareType); |
| |
| bool tryFold(bool *result); |
| bool evaluateConstantOperands(bool *result); |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| void infer(JSContext *cx, BaselineInspector *inspector, jsbytecode *pc); |
| CompareType compareType() const { |
| return compareType_; |
| } |
| bool isDoubleComparison() const { |
| return compareType() == Compare_Double || |
| compareType() == Compare_DoubleMaybeCoerceLHS || |
| compareType() == Compare_DoubleMaybeCoerceRHS; |
| } |
| void setCompareType(CompareType type) { |
| compareType_ = type; |
| } |
| MIRType inputType(); |
| |
| JSOp jsop() const { |
| return jsop_; |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| void markNoOperandEmulatesUndefined() { |
| operandMightEmulateUndefined_ = false; |
| } |
| bool operandMightEmulateUndefined() const { |
| return operandMightEmulateUndefined_; |
| } |
| AliasSet getAliasSet() const { |
| // Strict equality is never effectful. |
| if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE) |
| return AliasSet::None(); |
| if (compareType_ == Compare_Unknown) |
| return AliasSet::Store(AliasSet::Any); |
| JS_ASSERT(compareType_ <= Compare_Value); |
| return AliasSet::None(); |
| } |
| |
| protected: |
| bool congruentTo(MDefinition *const &ins) const { |
| if (!MBinaryInstruction::congruentTo(ins)) |
| return false; |
| return compareType() == ins->toCompare()->compareType() && |
| jsop() == ins->toCompare()->jsop(); |
| } |
| }; |
| |
| // Takes a typed value and returns an untyped value. |
| class MBox : public MUnaryInstruction |
| { |
| MBox(MDefinition *ins) |
| : MUnaryInstruction(ins) |
| { |
| setResultType(MIRType_Value); |
| if (ins->resultTypeSet()) { |
| setResultTypeSet(ins->resultTypeSet()); |
| } else if (ins->type() != MIRType_Value) { |
| types::Type ntype = ins->type() == MIRType_Object |
| ? types::Type::AnyObjectType() |
| : types::Type::PrimitiveType(ValueTypeFromMIRType(ins->type())); |
| setResultTypeSet(GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype)); |
| } |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Box) |
| static MBox *New(MDefinition *ins) |
| { |
| // Cannot box a box. |
| JS_ASSERT(ins->type() != MIRType_Value); |
| |
| return new MBox(ins); |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // Note: the op may have been inverted during lowering (to put constants in a |
| // position where they can be immediates), so it is important to use the |
| // lir->jsop() instead of the mir->jsop() when it is present. |
| static inline Assembler::Condition |
| JSOpToCondition(MCompare::CompareType compareType, JSOp op) |
| { |
| bool isSigned = (compareType != MCompare::Compare_UInt32); |
| return JSOpToCondition(op, isSigned); |
| } |
| |
| // Takes a typed value and checks if it is a certain type. If so, the payload |
| // is unpacked and returned as that type. Otherwise, it is considered a |
| // deoptimization. |
| class MUnbox : public MUnaryInstruction, public BoxInputsPolicy |
| { |
| public: |
| enum Mode { |
| Fallible, // Check the type, and deoptimize if unexpected. |
| Infallible, // Type guard is not necessary. |
| TypeBarrier, // Guard on the type, and act like a TypeBarrier on failure. |
| TypeGuard // Guard on the type, and deoptimize otherwise. |
| }; |
| |
| private: |
| Mode mode_; |
| |
| MUnbox(MDefinition *ins, MIRType type, Mode mode) |
| : MUnaryInstruction(ins), |
| mode_(mode) |
| { |
| JS_ASSERT(ins->type() == MIRType_Value); |
| JS_ASSERT(type == MIRType_Boolean || |
| type == MIRType_Int32 || |
| type == MIRType_Double || |
| type == MIRType_String || |
| type == MIRType_Object); |
| |
| setResultType(type); |
| setResultTypeSet(ins->resultTypeSet()); |
| setMovable(); |
| |
| if (mode_ == TypeBarrier || mode_ == TypeGuard) |
| setGuard(); |
| if (mode_ == TypeGuard) |
| mode_ = Fallible; |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Unbox) |
| static MUnbox *New(MDefinition *ins, MIRType type, Mode mode) |
| { |
| return new MUnbox(ins, type, mode); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| Mode mode() const { |
| return mode_; |
| } |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| BailoutKind bailoutKind() const { |
| // If infallible, no bailout should be generated. |
| JS_ASSERT(fallible()); |
| return mode() == Fallible |
| ? Bailout_Normal |
| : Bailout_TypeBarrier; |
| } |
| bool fallible() const { |
| return mode() != Infallible; |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) |
| return false; |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| void printOpcode(FILE *fp); |
| }; |
| |
| class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy |
| { |
| MGuardObject(MDefinition *ins) |
| : MUnaryInstruction(ins) |
| { |
| setGuard(); |
| setMovable(); |
| setResultType(MIRType_Object); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(GuardObject) |
| |
| static MGuardObject *New(MDefinition *ins) { |
| return new MGuardObject(ins); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MGuardString |
| : public MUnaryInstruction, |
| public StringPolicy<0> |
| { |
| MGuardString(MDefinition *ins) |
| : MUnaryInstruction(ins) |
| { |
| setGuard(); |
| setMovable(); |
| setResultType(MIRType_String); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(GuardString) |
| |
| static MGuardString *New(MDefinition *ins) { |
| return new MGuardString(ins); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // Caller-side allocation of |this| for |new|: |
| // Given a templateobject, construct |this| for JSOP_NEW |
| class MCreateThisWithTemplate |
| : public MNullaryInstruction |
| { |
| // Template for |this|, provided by TI |
| CompilerRootObject templateObject_; |
| |
| MCreateThisWithTemplate(JSObject *templateObject) |
| : templateObject_(templateObject) |
| { |
| setResultType(MIRType_Object); |
| setResultTypeSet(MakeSingletonTypeSet(templateObject)); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(CreateThisWithTemplate); |
| static MCreateThisWithTemplate *New(JSObject *templateObject) |
| { |
| return new MCreateThisWithTemplate(templateObject); |
| } |
| JSObject *getTemplateObject() const { |
| return templateObject_; |
| } |
| |
| // Although creation of |this| modifies global state, it is safely repeatable. |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // Caller-side allocation of |this| for |new|: |
| // Given a prototype operand, construct |this| for JSOP_NEW. |
| class MCreateThisWithProto |
| : public MBinaryInstruction, |
| public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> > |
| { |
| MCreateThisWithProto(MDefinition *callee, MDefinition *prototype) |
| : MBinaryInstruction(callee, prototype) |
| { |
| setResultType(MIRType_Object); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(CreateThisWithProto) |
| static MCreateThisWithProto *New(MDefinition *callee, MDefinition *prototype) |
| { |
| return new MCreateThisWithProto(callee, prototype); |
| } |
| |
| MDefinition *getCallee() const { |
| return getOperand(0); |
| } |
| MDefinition *getPrototype() const { |
| return getOperand(1); |
| } |
| |
| // Although creation of |this| modifies global state, it is safely repeatable. |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| // Caller-side allocation of |this| for |new|: |
| // Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING). |
| class MCreateThis |
| : public MUnaryInstruction, |
| public ObjectPolicy<0> |
| { |
| MCreateThis(MDefinition *callee) |
| : MUnaryInstruction(callee) |
| { |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(CreateThis) |
| static MCreateThis *New(MDefinition *callee) |
| { |
| return new MCreateThis(callee); |
| } |
| |
| MDefinition *getCallee() const { |
| return getOperand(0); |
| } |
| |
| // Although creation of |this| modifies global state, it is safely repeatable. |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| // Eager initialization of arguments object. |
| class MCreateArgumentsObject |
| : public MUnaryInstruction, |
| public ObjectPolicy<0> |
| { |
| MCreateArgumentsObject(MDefinition *callObj) |
| : MUnaryInstruction(callObj) |
| { |
| setResultType(MIRType_Object); |
| setGuard(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(CreateArgumentsObject) |
| static MCreateArgumentsObject *New(MDefinition *callObj) { |
| return new MCreateArgumentsObject(callObj); |
| } |
| |
| MDefinition *getCallObject() const { |
| return getOperand(0); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MGetArgumentsObjectArg |
| : public MUnaryInstruction, |
| public ObjectPolicy<0> |
| { |
| size_t argno_; |
| |
| MGetArgumentsObjectArg(MDefinition *argsObject, size_t argno) |
| : MUnaryInstruction(argsObject), |
| argno_(argno) |
| { |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(GetArgumentsObjectArg) |
| static MGetArgumentsObjectArg *New(MDefinition *argsObj, size_t argno) |
| { |
| return new MGetArgumentsObjectArg(argsObj, argno); |
| } |
| |
| MDefinition *getArgsObject() const { |
| return getOperand(0); |
| } |
| |
| size_t argno() const { |
| return argno_; |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::Load(AliasSet::Any); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MSetArgumentsObjectArg |
| : public MBinaryInstruction, |
| public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
| { |
| size_t argno_; |
| |
| MSetArgumentsObjectArg(MDefinition *argsObj, size_t argno, MDefinition *value) |
| : MBinaryInstruction(argsObj, value), |
| argno_(argno) |
| { |
| } |
| |
| public: |
| INSTRUCTION_HEADER(SetArgumentsObjectArg) |
| static MSetArgumentsObjectArg *New(MDefinition *argsObj, size_t argno, MDefinition *value) |
| { |
| return new MSetArgumentsObjectArg(argsObj, argno, value); |
| } |
| |
| MDefinition *getArgsObject() const { |
| return getOperand(0); |
| } |
| |
| size_t argno() const { |
| return argno_; |
| } |
| |
| MDefinition *getValue() const { |
| return getOperand(1); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::Store(AliasSet::Any); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MRunOncePrologue |
| : public MNullaryInstruction |
| { |
| protected: |
| MRunOncePrologue() |
| { |
| setGuard(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(RunOncePrologue) |
| |
| static MRunOncePrologue *New() { |
| return new MRunOncePrologue(); |
| } |
| }; |
| |
| // Given a MIRType_Value A and a MIRType_Object B: |
| // If the Value may be safely unboxed to an Object, return Object(A). |
| // Otherwise, return B. |
| // Used to implement return behavior for inlined constructors. |
| class MReturnFromCtor |
| : public MAryInstruction<2>, |
| public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> > |
| { |
| MReturnFromCtor(MDefinition *value, MDefinition *object) { |
| setOperand(0, value); |
| setOperand(1, object); |
| setResultType(MIRType_Object); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ReturnFromCtor) |
| static MReturnFromCtor *New(MDefinition *value, MDefinition *object) |
| { |
| return new MReturnFromCtor(value, object); |
| } |
| |
| MDefinition *getValue() const { |
| return getOperand(0); |
| } |
| MDefinition *getObject() const { |
| return getOperand(1); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| // Passes an MDefinition to an MCall. Must occur between an MPrepareCall and |
| // MCall. Boxes the input and stores it to the correct location on stack. |
| // |
| // Arguments are *not* simply pushed onto a call stack: they are evaluated |
| // left-to-right, but stored in the arg vector in C-style, right-to-left. |
| class MPassArg : public MUnaryInstruction |
| { |
| int32_t argnum_; |
| |
| private: |
| MPassArg(MDefinition *def) |
| : MUnaryInstruction(def), argnum_(-1) |
| { |
| setResultType(def->type()); |
| setResultTypeSet(def->resultTypeSet()); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(PassArg) |
| static MPassArg *New(MDefinition *def) |
| { |
| return new MPassArg(def); |
| } |
| |
| MDefinition *getArgument() const { |
| return getOperand(0); |
| } |
| |
| // Set by the MCall. |
| void setArgnum(uint32_t argnum) { |
| argnum_ = argnum; |
| } |
| uint32_t getArgnum() const { |
| JS_ASSERT(argnum_ >= 0); |
| return (uint32_t)argnum_; |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| void printOpcode(FILE *fp); |
| }; |
| |
| // Converts a primitive (either typed or untyped) to a double. If the input is |
| // not primitive at runtime, a bailout occurs. |
| class MToDouble |
| : public MUnaryInstruction, |
| public ToDoublePolicy |
| { |
| public: |
| // Types of values which can be converted. |
| enum ConversionKind { |
| NonStringPrimitives, |
| NonNullNonStringPrimitives, |
| NumbersOnly |
| }; |
| |
| private: |
| ConversionKind conversion_; |
| |
| MToDouble(MDefinition *def, ConversionKind conversion = NonStringPrimitives) |
| : MUnaryInstruction(def), conversion_(conversion) |
| { |
| setResultType(MIRType_Double); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ToDouble) |
| static MToDouble *New(MDefinition *def, ConversionKind conversion = NonStringPrimitives) { |
| return new MToDouble(def, conversion); |
| } |
| static MToDouble *NewAsmJS(MDefinition *def) { |
| return new MToDouble(def); |
| } |
| |
| ConversionKind conversion() const { |
| return conversion_; |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion()) |
| return false; |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| |
| void computeRange(); |
| bool truncate(); |
| bool isOperandTruncated(size_t index) const; |
| }; |
| |
| // Converts a uint32 to a double (coming from asm.js). |
| class MAsmJSUnsignedToDouble |
| : public MUnaryInstruction |
| { |
| MAsmJSUnsignedToDouble(MDefinition *def) |
| : MUnaryInstruction(def) |
| { |
| setResultType(MIRType_Double); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(AsmJSUnsignedToDouble); |
| static MAsmJSUnsignedToDouble *NewAsmJS(MDefinition *def) { |
| return new MAsmJSUnsignedToDouble(def); |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // Converts a primitive (either typed or untyped) to an int32. If the input is |
| // not primitive at runtime, a bailout occurs. If the input cannot be converted |
| // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. |
| class MToInt32 : public MUnaryInstruction |
| { |
| bool canBeNegativeZero_; |
| |
| MToInt32(MDefinition *def) |
| : MUnaryInstruction(def), |
| canBeNegativeZero_(true) |
| { |
| setResultType(MIRType_Int32); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ToInt32) |
| static MToInt32 *New(MDefinition *def) |
| { |
| return new MToInt32(def); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| // this only has backwards information flow. |
| void analyzeEdgeCasesBackward(); |
| |
| bool canBeNegativeZero() { |
| return canBeNegativeZero_; |
| } |
| void setCanBeNegativeZero(bool negativeZero) { |
| canBeNegativeZero_ = negativeZero; |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| void computeRange(); |
| }; |
| |
| // Converts a value or typed input to a truncated int32, for use with bitwise |
| // operations. This is an infallible ValueToECMAInt32. |
| class MTruncateToInt32 : public MUnaryInstruction |
| { |
| MTruncateToInt32(MDefinition *def) |
| : MUnaryInstruction(def) |
| { |
| setResultType(MIRType_Int32); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(TruncateToInt32) |
| static MTruncateToInt32 *New(MDefinition *def) { |
| return new MTruncateToInt32(def); |
| } |
| static MTruncateToInt32 *NewAsmJS(MDefinition *def) { |
| return new MTruncateToInt32(def); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| |
| void computeRange(); |
| bool isOperandTruncated(size_t index) const; |
| }; |
| |
| // Converts any type to a string |
| class MToString : public MUnaryInstruction |
| { |
| MToString(MDefinition *def) |
| : MUnaryInstruction(def) |
| { |
| setResultType(MIRType_String); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ToString) |
| static MToString *New(MDefinition *def) |
| { |
| return new MToString(def); |
| } |
| |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| JS_ASSERT(input()->type() < MIRType_Object); |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MBitNot |
| : public MUnaryInstruction, |
| public BitwisePolicy |
| { |
| protected: |
| MBitNot(MDefinition *input) |
| : MUnaryInstruction(input) |
| { |
| setResultType(MIRType_Int32); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(BitNot) |
| static MBitNot *New(MDefinition *input); |
| static MBitNot *NewAsmJS(MDefinition *input); |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| void infer(); |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| if (specialization_ == MIRType_None) |
| return AliasSet::Store(AliasSet::Any); |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MTypeOf |
| : public MUnaryInstruction, |
| public BoxInputsPolicy |
| { |
| MIRType inputType_; |
| |
| MTypeOf(MDefinition *def, MIRType inputType) |
| : MUnaryInstruction(def), inputType_(inputType) |
| { |
| setResultType(MIRType_String); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(TypeOf) |
| |
| static MTypeOf *New(MDefinition *def, MIRType inputType) { |
| return new MTypeOf(def, inputType); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| MIRType inputType() const { |
| return inputType_; |
| } |
| MDefinition *input() const { |
| return getOperand(0); |
| } |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| AliasSet getAliasSet() const { |
| if (inputType_ <= MIRType_String) |
| return AliasSet::None(); |
| |
| // For objects, typeof may invoke an effectful typeof hook. |
| return AliasSet::Store(AliasSet::Any); |
| } |
| }; |
| |
| class MToId |
| : public MBinaryInstruction, |
| public BoxInputsPolicy |
| { |
| MToId(MDefinition *object, MDefinition *index) |
| : MBinaryInstruction(object, index) |
| { |
| setResultType(MIRType_Value); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(ToId) |
| |
| static MToId *New(MDefinition *object, MDefinition *index) { |
| return new MToId(object, index); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| }; |
| |
| class MBinaryBitwiseInstruction |
| : public MBinaryInstruction, |
| public BitwisePolicy |
| { |
| protected: |
| MBinaryBitwiseInstruction(MDefinition *left, MDefinition *right) |
| : MBinaryInstruction(left, right) |
| { |
| setResultType(MIRType_Int32); |
| setMovable(); |
| } |
| |
| void specializeForAsmJS(); |
| |
| public: |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| MDefinition *foldUnnecessaryBitop(); |
| virtual MDefinition *foldIfZero(size_t operand) = 0; |
| virtual MDefinition *foldIfNegOne(size_t operand) = 0; |
| virtual MDefinition *foldIfEqual() = 0; |
| virtual void infer(BaselineInspector *inspector, jsbytecode *pc); |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| AliasSet getAliasSet() const { |
| if (specialization_ >= MIRType_Object) |
| return AliasSet::Store(AliasSet::Any); |
| return AliasSet::None(); |
| } |
| |
| bool isOperandTruncated(size_t index) const; |
| }; |
| |
| class MBitAnd : public MBinaryBitwiseInstruction |
| { |
| MBitAnd(MDefinition *left, MDefinition *right) |
| : MBinaryBitwiseInstruction(left, right) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(BitAnd) |
| static MBitAnd *New(MDefinition *left, MDefinition *right); |
| static MBitAnd *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| return getOperand(operand); // 0 & x => 0; |
| } |
| MDefinition *foldIfNegOne(size_t operand) { |
| return getOperand(1 - operand); // x & -1 => x |
| } |
| MDefinition *foldIfEqual() { |
| return getOperand(0); // x & x => x; |
| } |
| void computeRange(); |
| }; |
| |
| class MBitOr : public MBinaryBitwiseInstruction |
| { |
| MBitOr(MDefinition *left, MDefinition *right) |
| : MBinaryBitwiseInstruction(left, right) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(BitOr) |
| static MBitOr *New(MDefinition *left, MDefinition *right); |
| static MBitOr *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th |
| } |
| MDefinition *foldIfNegOne(size_t operand) { |
| return getOperand(operand); // x | -1 => -1 |
| } |
| MDefinition *foldIfEqual() { |
| return getOperand(0); // x | x => x |
| } |
| }; |
| |
| class MBitXor : public MBinaryBitwiseInstruction |
| { |
| MBitXor(MDefinition *left, MDefinition *right) |
| : MBinaryBitwiseInstruction(left, right) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(BitXor) |
| static MBitXor *New(MDefinition *left, MDefinition *right); |
| static MBitXor *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| return getOperand(1 - operand); // 0 ^ x => x |
| } |
| MDefinition *foldIfNegOne(size_t operand) { |
| return this; |
| } |
| MDefinition *foldIfEqual() { |
| return this; |
| } |
| }; |
| |
| class MShiftInstruction |
| : public MBinaryBitwiseInstruction |
| { |
| protected: |
| MShiftInstruction(MDefinition *left, MDefinition *right) |
| : MBinaryBitwiseInstruction(left, right) |
| { } |
| |
| public: |
| MDefinition *foldIfNegOne(size_t operand) { |
| return this; |
| } |
| MDefinition *foldIfEqual() { |
| return this; |
| } |
| virtual void infer(BaselineInspector *inspector, jsbytecode *pc); |
| }; |
| |
| class MLsh : public MShiftInstruction |
| { |
| MLsh(MDefinition *left, MDefinition *right) |
| : MShiftInstruction(left, right) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(Lsh) |
| static MLsh *New(MDefinition *left, MDefinition *right); |
| static MLsh *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| // 0 << x => 0 |
| // x << 0 => x |
| return getOperand(0); |
| } |
| |
| void computeRange(); |
| }; |
| |
| class MRsh : public MShiftInstruction |
| { |
| MRsh(MDefinition *left, MDefinition *right) |
| : MShiftInstruction(left, right) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(Rsh) |
| static MRsh *New(MDefinition *left, MDefinition *right); |
| static MRsh *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| // 0 >> x => 0 |
| // x >> 0 => x |
| return getOperand(0); |
| } |
| void computeRange(); |
| }; |
| |
| class MUrsh : public MShiftInstruction |
| { |
| bool canOverflow_; |
| |
| MUrsh(MDefinition *left, MDefinition *right) |
| : MShiftInstruction(left, right), |
| canOverflow_(true) |
| { } |
| |
| public: |
| INSTRUCTION_HEADER(Ursh) |
| static MUrsh *New(MDefinition *left, MDefinition *right); |
| static MUrsh *NewAsmJS(MDefinition *left, MDefinition *right); |
| |
| MDefinition *foldIfZero(size_t operand) { |
| // 0 >>> x => 0 |
| if (operand == 0) |
| return getOperand(0); |
| |
| return this; |
| } |
| |
| void infer(BaselineInspector *inspector, jsbytecode *pc); |
| |
| bool canOverflow() { |
| // solution is only negative when lhs < 0 and rhs & 0x1f == 0 |
| MDefinition *lhs = getOperand(0); |
| MDefinition *rhs = getOperand(1); |
| |
| if (lhs->isConstant()) { |
| Value lhsv = lhs->toConstant()->value(); |
| if (lhsv.isInt32() && lhsv.toInt32() >= 0) |
| return false; |
| } |
| |
| if (rhs->isConstant()) { |
| Value rhsv = rhs->toConstant()->value(); |
| if (rhsv.isInt32() && rhsv.toInt32() % 32 != 0) |
| return false; |
| } |
| |
| return canOverflow_; |
| } |
| |
| bool fallible() { |
| return canOverflow(); |
| } |
| }; |
| |
| class MBinaryArithInstruction |
| : public MBinaryInstruction, |
| public ArithPolicy |
| { |
| // Implicit truncate flag is set by the truncate backward range analysis |
| // optimization phase, and by asm.js pre-processing. It is used in |
| // NeedNegativeZeroCheck to check if the result of a multiplication needs to |
| // produce -0 double value, and for avoiding overflow checks. |
| |
| // This optimization happens when the multiplication cannot be truncated |
| // even if all uses are truncating its result, such as when the range |
| // analysis detect a precision loss in the multiplication. |
| bool implicitTruncate_; |
| |
| void inferFallback(BaselineInspector *inspector, jsbytecode *pc); |
| |
| public: |
| MBinaryArithInstruction(MDefinition *left, MDefinition *right) |
| : MBinaryInstruction(left, right), |
| implicitTruncate_(false) |
| { |
| setMovable(); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| MIRType specialization() const { |
| return specialization_; |
| } |
| |
| MDefinition *foldsTo(bool useValueNumbers); |
| |
| virtual double getIdentity() = 0; |
| |
| void infer(BaselineInspector *inspector, |
| jsbytecode *pc, bool overflowed); |
| |
| void setInt32() { |
| specialization_ = MIRType_Int32; |
| setResultType(MIRType_Int32); |
| } |
| |
| bool congruentTo(MDefinition *const &ins) const { |
| return MBinaryInstruction::congruentTo(ins); |
| } |
| AliasSet getAliasSet() const { |
| if (specialization_ >= MIRType_Object) |
| return AliasSet::Store(AliasSet::Any); |
| return AliasSet::None(); |
| } |
| |
| bool isTruncated() const { |
| return implicitTruncate_; |
| } |
| void setTruncated(bool truncate) { |
| implicitTruncate_ = truncate; |
| } |
| }; |
| |
| class MMinMax |
| : public MBinaryInstruction, |
| public ArithPolicy |
| { |
| bool isMax_; |
| |
| MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax) |
| : MBinaryInstruction(left, right), |
| isMax_(isMax) |
| { |
| JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); |
| setResultType(type); |
| setMovable(); |
| specialization_ = type; |
| } |
| |
| public: |
| INSTRUCTION_HEADER(MinMax) |
| static MMinMax *New(MDefinition *left, MDefinition *right, MIRType type, bool isMax) { |
| return new MMinMax(left, right, type, isMax); |
| } |
| |
| bool isMax() const { |
| return isMax_; |
| } |
| MIRType specialization() const { |
| return specialization_; |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| if (!ins->isMinMax()) |
| return false; |
| if (isMax() != ins->toMinMax()->isMax()) |
| return false; |
| return congruentIfOperandsEqual(ins); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| class MAbs |
| : public MUnaryInstruction, |
| public ArithPolicy |
| { |
| bool implicitTruncate_; |
| |
| MAbs(MDefinition *num, MIRType type) |
| : MUnaryInstruction(num), |
| implicitTruncate_(false) |
| { |
| JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); |
| setResultType(type); |
| setMovable(); |
| specialization_ = type; |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Abs) |
| static MAbs *New(MDefinition *num, MIRType type) { |
| return new MAbs(num, type); |
| } |
| static MAbs *NewAsmJS(MDefinition *num, MIRType type) { |
| MAbs *ins = new MAbs(num, type); |
| ins->implicitTruncate_ = true; |
| return ins; |
| } |
| MDefinition *num() const { |
| return getOperand(0); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| bool fallible() const; |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| void computeRange(); |
| }; |
| |
| // Inline implementation of Math.sqrt(). |
| class MSqrt |
| : public MUnaryInstruction, |
| public DoublePolicy<0> |
| { |
| MSqrt(MDefinition *num) |
| : MUnaryInstruction(num) |
| { |
| setResultType(MIRType_Double); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Sqrt) |
| static MSqrt *New(MDefinition *num) { |
| return new MSqrt(num); |
| } |
| static MSqrt *NewAsmJS(MDefinition *num, MIRType type) { |
| JS_ASSERT(type == MIRType_Double); |
| return new MSqrt(num); |
| } |
| MDefinition *num() const { |
| return getOperand(0); |
| } |
| TypePolicy *typePolicy() { |
| return this; |
| } |
| bool congruentTo(MDefinition *const &ins) const { |
| return congruentIfOperandsEqual(ins); |
| } |
| |
| AliasSet getAliasSet() const { |
| return AliasSet::None(); |
| } |
| }; |
| |
| // Inline implementation of atan2 (arctangent of y/x). |
| class MAtan2 |
| : public MBinaryInstruction, |
| public MixPolicy<DoublePolicy<0>, DoublePolicy<1> > |
| { |
| MAtan2(MDefinition *y, MDefinition *x) |
| : MBinaryInstruction(y, x) |
| { |
| setResultType(MIRType_Double); |
| setMovable(); |
| } |
| |
| public: |
| INSTRUCTION_HEADER(Atan2) |
| static MAtan2 *New(MDefinition *y, MDefinition *x) { |
| return new MAtan2(y, x); |
| } |
| |
| MDefinition *y() const { |
| return getOperand(0); |
| } |
| |
| MDefinition *x() const { |
| return getOperand(1); |
| } |
| |
| TypePolicy *typePolicy() { |
| return this; |
|