michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Everything needed to build actual MIR instructions: the actual opcodes and michael@0: * instructions, the instruction interface, and use chains. michael@0: */ michael@0: michael@0: #ifndef jit_MIR_h michael@0: #define jit_MIR_h michael@0: michael@0: #include "mozilla/Array.h" michael@0: michael@0: #include "jsinfer.h" michael@0: michael@0: #include "jit/CompilerRoot.h" michael@0: #include "jit/FixedList.h" michael@0: #include "jit/InlineList.h" michael@0: #include "jit/IonAllocPolicy.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: #include "jit/MOpcodes.h" michael@0: #include "jit/TypeDescrSet.h" michael@0: #include "jit/TypePolicy.h" michael@0: #include "vm/ScopeObject.h" michael@0: #include "vm/TypedArrayObject.h" michael@0: michael@0: namespace js { michael@0: michael@0: class StringObject; michael@0: michael@0: namespace jit { michael@0: michael@0: class BaselineInspector; michael@0: class ValueNumberData; michael@0: class Range; michael@0: michael@0: static const inline michael@0: MIRType MIRTypeFromValue(const js::Value &vp) michael@0: { michael@0: if (vp.isDouble()) michael@0: return MIRType_Double; michael@0: if (vp.isMagic()) { michael@0: switch (vp.whyMagic()) { michael@0: case JS_OPTIMIZED_ARGUMENTS: michael@0: return MIRType_MagicOptimizedArguments; michael@0: case JS_OPTIMIZED_OUT: michael@0: return MIRType_MagicOptimizedOut; michael@0: case JS_ELEMENTS_HOLE: michael@0: return MIRType_MagicHole; michael@0: case JS_IS_CONSTRUCTING: michael@0: return MIRType_MagicIsConstructing; michael@0: default: michael@0: MOZ_ASSERT(!"Unexpected magic constant"); michael@0: } michael@0: } michael@0: return MIRTypeFromValueType(vp.extractNonDoubleType()); michael@0: } michael@0: michael@0: #define MIR_FLAG_LIST(_) \ michael@0: _(InWorklist) \ michael@0: _(EmittedAtUses) \ michael@0: _(LoopInvariant) \ michael@0: _(Commutative) \ michael@0: _(Movable) /* Allow LICM and GVN to move this instruction */ \ michael@0: _(Lowered) /* (Debug only) has a virtual register */ \ michael@0: _(Guard) /* Not removable if uses == 0 */ \ michael@0: \ michael@0: /* Keep the flagged instruction in resume points and do not substitute this michael@0: * instruction by an UndefinedValue. This might be used by call inlining michael@0: * when a function argument is not used by the inlined instructions. michael@0: */ \ michael@0: _(ImplicitlyUsed) \ michael@0: \ michael@0: /* The instruction has been marked dead for lazy removal from resume michael@0: * points. michael@0: */ \ michael@0: _(Unused) \ michael@0: /* Marks if an instruction has fewer uses than the original code. michael@0: * E.g. UCE can remove code. michael@0: * Every instruction where an use is/was removed from an instruction and michael@0: * as a result the number of operands doesn't equal the original code michael@0: * need to get marked as UseRemoved. This is important for truncation michael@0: * analysis to know, since if all original uses are still present, michael@0: * it can ignore resumepoints. michael@0: * Currently this is done for every pass after IonBuilder and before michael@0: * Truncate Doubles. So every time removeUse is called, UseRemoved needs michael@0: * to get set. michael@0: */ \ michael@0: _(UseRemoved) michael@0: michael@0: class MDefinition; michael@0: class MInstruction; michael@0: class MBasicBlock; michael@0: class MNode; michael@0: class MUse; michael@0: class MIRGraph; michael@0: class MResumePoint; michael@0: michael@0: // Represents a use of a node. michael@0: class MUse : public TempObject, public InlineListNode michael@0: { michael@0: friend class MDefinition; michael@0: michael@0: MDefinition *producer_; // MDefinition that is being used. michael@0: MNode *consumer_; // The node that is using this operand. michael@0: uint32_t index_; // The index of this operand in its consumer. michael@0: michael@0: MUse(MDefinition *producer, MNode *consumer, uint32_t index) michael@0: : producer_(producer), michael@0: consumer_(consumer), michael@0: index_(index) michael@0: { } michael@0: michael@0: public: michael@0: // Default constructor for use in vectors. michael@0: MUse() michael@0: : producer_(nullptr), consumer_(nullptr), index_(0) michael@0: { } michael@0: michael@0: // Set data inside the MUse. michael@0: void set(MDefinition *producer, MNode *consumer, uint32_t index) { michael@0: producer_ = producer; michael@0: consumer_ = consumer; michael@0: index_ = index; michael@0: } michael@0: michael@0: MDefinition *producer() const { michael@0: JS_ASSERT(producer_ != nullptr); michael@0: return producer_; michael@0: } michael@0: bool hasProducer() const { michael@0: return producer_ != nullptr; michael@0: } michael@0: MNode *consumer() const { michael@0: JS_ASSERT(consumer_ != nullptr); michael@0: return consumer_; michael@0: } michael@0: uint32_t index() const { michael@0: return index_; michael@0: } michael@0: }; michael@0: michael@0: typedef InlineList::iterator MUseIterator; michael@0: michael@0: // A node is an entry in the MIR graph. It has two kinds: michael@0: // MInstruction: an instruction which appears in the IR stream. michael@0: // MResumePoint: a list of instructions that correspond to the state of the michael@0: // interpreter/Baseline stack. michael@0: // michael@0: // Nodes can hold references to MDefinitions. Each MDefinition has a list of michael@0: // nodes holding such a reference (its use chain). michael@0: class MNode : public TempObject michael@0: { michael@0: friend class MDefinition; michael@0: michael@0: protected: michael@0: MBasicBlock *block_; // Containing basic block. michael@0: michael@0: public: michael@0: enum Kind { michael@0: Definition, michael@0: ResumePoint michael@0: }; michael@0: michael@0: MNode() michael@0: : block_(nullptr) michael@0: { } michael@0: michael@0: MNode(MBasicBlock *block) michael@0: : block_(block) michael@0: { } michael@0: michael@0: virtual Kind kind() const = 0; michael@0: michael@0: // Returns the definition at a given operand. michael@0: virtual MDefinition *getOperand(size_t index) const = 0; michael@0: virtual size_t numOperands() const = 0; michael@0: michael@0: bool isDefinition() const { michael@0: return kind() == Definition; michael@0: } michael@0: bool isResumePoint() const { michael@0: return kind() == ResumePoint; michael@0: } michael@0: MBasicBlock *block() const { michael@0: return block_; michael@0: } michael@0: michael@0: // Instructions needing to hook into type analysis should return a michael@0: // TypePolicy. michael@0: virtual TypePolicy *typePolicy() { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Replaces an already-set operand during iteration over a use chain. michael@0: MUseIterator replaceOperand(MUseIterator use, MDefinition *ins); michael@0: michael@0: // Replaces an already-set operand, updating use information. michael@0: void replaceOperand(size_t index, MDefinition *ins); michael@0: michael@0: // Resets the operand to an uninitialized state, breaking the link michael@0: // with the previous operand's producer. michael@0: void discardOperand(size_t index); michael@0: michael@0: inline MDefinition *toDefinition(); michael@0: inline MResumePoint *toResumePoint(); michael@0: michael@0: protected: michael@0: // Sets an unset operand, updating use information. michael@0: virtual void setOperand(size_t index, MDefinition *operand) = 0; michael@0: michael@0: // Gets the MUse corresponding to given operand. michael@0: virtual MUse *getUseFor(size_t index) = 0; michael@0: }; michael@0: michael@0: class AliasSet { michael@0: private: michael@0: uint32_t flags_; michael@0: michael@0: public: michael@0: enum Flag { michael@0: None_ = 0, michael@0: ObjectFields = 1 << 0, // shape, class, slots, length etc. michael@0: Element = 1 << 1, // A member of obj->elements. michael@0: DynamicSlot = 1 << 2, // A member of obj->slots. michael@0: FixedSlot = 1 << 3, // A member of obj->fixedSlots(). michael@0: TypedArrayElement = 1 << 4, // A typed array element. michael@0: DOMProperty = 1 << 5, // A DOM property michael@0: FrameArgument = 1 << 6, // An argument kept on the stack frame michael@0: AsmJSGlobalVar = 1 << 7, // An asm.js global var michael@0: AsmJSHeap = 1 << 8, // An asm.js heap load michael@0: TypedArrayLength = 1 << 9,// A typed array's length michael@0: Last = TypedArrayLength, michael@0: Any = Last | (Last - 1), michael@0: michael@0: NumCategories = 10, michael@0: michael@0: // Indicates load or store. michael@0: Store_ = 1 << 31 michael@0: }; michael@0: michael@0: static_assert((1 << NumCategories) - 1 == Any, michael@0: "NumCategories must include all flags present in Any"); michael@0: michael@0: AliasSet(uint32_t flags) michael@0: : flags_(flags) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: inline bool isNone() const { michael@0: return flags_ == None_; michael@0: } michael@0: uint32_t flags() const { michael@0: return flags_ & Any; michael@0: } michael@0: inline bool isStore() const { michael@0: return !!(flags_ & Store_); michael@0: } michael@0: inline bool isLoad() const { michael@0: return !isStore() && !isNone(); michael@0: } michael@0: inline AliasSet operator |(const AliasSet &other) const { michael@0: return AliasSet(flags_ | other.flags_); michael@0: } michael@0: inline AliasSet operator &(const AliasSet &other) const { michael@0: return AliasSet(flags_ & other.flags_); michael@0: } michael@0: static AliasSet None() { michael@0: return AliasSet(None_); michael@0: } michael@0: static AliasSet Load(uint32_t flags) { michael@0: JS_ASSERT(flags && !(flags & Store_)); michael@0: return AliasSet(flags); michael@0: } michael@0: static AliasSet Store(uint32_t flags) { michael@0: JS_ASSERT(flags && !(flags & Store_)); michael@0: return AliasSet(flags | Store_); michael@0: } michael@0: }; michael@0: michael@0: // An MDefinition is an SSA name. michael@0: class MDefinition : public MNode michael@0: { michael@0: friend class MBasicBlock; michael@0: michael@0: public: michael@0: enum Opcode { michael@0: # define DEFINE_OPCODES(op) Op_##op, michael@0: MIR_OPCODE_LIST(DEFINE_OPCODES) michael@0: # undef DEFINE_OPCODES michael@0: Op_Invalid michael@0: }; michael@0: michael@0: private: michael@0: InlineList uses_; // Use chain. michael@0: uint32_t id_; // Instruction ID, which after block re-ordering michael@0: // is sorted within a basic block. michael@0: ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use) michael@0: Range *range_; // Any computed range for this def. michael@0: MIRType resultType_; // Representation of result type. michael@0: types::TemporaryTypeSet *resultTypeSet_; // Optional refinement of the result type. michael@0: uint32_t flags_; // Bit flags. michael@0: union { michael@0: MDefinition *dependency_; // Implicit dependency (store, call, etc.) of this instruction. michael@0: // Used by alias analysis, GVN and LICM. michael@0: uint32_t virtualRegister_; // Used by lowering to map definitions to virtual registers. michael@0: }; michael@0: michael@0: // Track bailouts by storing the current pc in MIR instruction. Also used michael@0: // for profiling and keeping track of what the last known pc was. michael@0: jsbytecode *trackedPc_; michael@0: michael@0: private: michael@0: enum Flag { michael@0: None = 0, michael@0: # define DEFINE_FLAG(flag) flag, michael@0: MIR_FLAG_LIST(DEFINE_FLAG) michael@0: # undef DEFINE_FLAG michael@0: Total michael@0: }; michael@0: michael@0: bool hasFlags(uint32_t flags) const { michael@0: return (flags_ & flags) == flags; michael@0: } michael@0: void removeFlags(uint32_t flags) { michael@0: flags_ &= ~flags; michael@0: } michael@0: void setFlags(uint32_t flags) { michael@0: flags_ |= flags; michael@0: } michael@0: michael@0: protected: michael@0: virtual void setBlock(MBasicBlock *block) { michael@0: block_ = block; michael@0: } michael@0: michael@0: public: michael@0: MDefinition() michael@0: : id_(0), michael@0: valueNumber_(nullptr), michael@0: range_(nullptr), michael@0: resultType_(MIRType_None), michael@0: resultTypeSet_(nullptr), michael@0: flags_(0), michael@0: dependency_(nullptr), michael@0: trackedPc_(nullptr) michael@0: { } michael@0: michael@0: virtual Opcode op() const = 0; michael@0: virtual const char *opName() const = 0; michael@0: void printName(FILE *fp) const; michael@0: static void PrintOpcodeName(FILE *fp, Opcode op); michael@0: virtual void printOpcode(FILE *fp) const; michael@0: void dump(FILE *fp) const; michael@0: void dump() const; michael@0: michael@0: // For LICM. michael@0: virtual bool neverHoist() const { return false; } michael@0: michael@0: // Also for LICM. Test whether this definition is likely to be a call, which michael@0: // would clobber all or many of the floating-point registers, such that michael@0: // hoisting floating-point constants out of containing loops isn't likely to michael@0: // be worthwhile. michael@0: virtual bool possiblyCalls() const { return false; } michael@0: michael@0: void setTrackedPc(jsbytecode *pc) { michael@0: trackedPc_ = pc; michael@0: } michael@0: michael@0: jsbytecode *trackedPc() { michael@0: return trackedPc_; michael@0: } michael@0: michael@0: // Return the range of this value, *before* any bailout checks. Contrast michael@0: // this with the type() method, and the Range constructor which takes an michael@0: // MDefinition*, which describe the value *after* any bailout checks. michael@0: // michael@0: // Warning: Range analysis is removing the bit-operations such as '| 0' at michael@0: // the end of the transformations. Using this function to analyse any michael@0: // operands after the truncate phase of the range analysis will lead to michael@0: // errors. Instead, one should define the collectRangeInfoPreTrunc() to set michael@0: // the right set of flags which are dependent on the range of the inputs. michael@0: Range *range() const { michael@0: JS_ASSERT(type() != MIRType_None); michael@0: return range_; michael@0: } michael@0: void setRange(Range *range) { michael@0: JS_ASSERT(type() != MIRType_None); michael@0: range_ = range; michael@0: } michael@0: michael@0: virtual HashNumber valueHash() const; michael@0: virtual bool congruentTo(const MDefinition *ins) const { michael@0: return false; michael@0: } michael@0: bool congruentIfOperandsEqual(const MDefinition *ins) const; michael@0: virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: virtual void analyzeEdgeCasesForward(); michael@0: virtual void analyzeEdgeCasesBackward(); michael@0: michael@0: virtual bool truncate(); michael@0: virtual bool isOperandTruncated(size_t index) const; michael@0: michael@0: // Compute an absolute or symbolic range for the value of this node. michael@0: virtual void computeRange(TempAllocator &alloc) { michael@0: } michael@0: michael@0: // Collect information from the pre-truncated ranges. michael@0: virtual void collectRangeInfoPreTrunc() { michael@0: } michael@0: michael@0: MNode::Kind kind() const { michael@0: return MNode::Definition; michael@0: } michael@0: michael@0: uint32_t id() const { michael@0: JS_ASSERT(block_); michael@0: return id_; michael@0: } michael@0: void setId(uint32_t id) { michael@0: id_ = id; michael@0: } michael@0: michael@0: uint32_t valueNumber() const; michael@0: void setValueNumber(uint32_t vn); michael@0: ValueNumberData *valueNumberData() { michael@0: return valueNumber_; michael@0: } michael@0: void clearValueNumberData() { michael@0: valueNumber_ = nullptr; michael@0: } michael@0: void setValueNumberData(ValueNumberData *vn) { michael@0: JS_ASSERT(valueNumber_ == nullptr); michael@0: valueNumber_ = vn; michael@0: } michael@0: #define FLAG_ACCESSOR(flag) \ michael@0: bool is##flag() const {\ michael@0: return hasFlags(1 << flag);\ michael@0: }\ michael@0: void set##flag() {\ michael@0: JS_ASSERT(!hasFlags(1 << flag));\ michael@0: setFlags(1 << flag);\ michael@0: }\ michael@0: void setNot##flag() {\ michael@0: JS_ASSERT(hasFlags(1 << flag));\ michael@0: removeFlags(1 << flag);\ michael@0: }\ michael@0: void set##flag##Unchecked() {\ michael@0: setFlags(1 << flag);\ michael@0: } michael@0: michael@0: MIR_FLAG_LIST(FLAG_ACCESSOR) michael@0: #undef FLAG_ACCESSOR michael@0: michael@0: // Return the type of this value. This may be speculative, and enforced michael@0: // dynamically with the use of bailout checks. If all the bailout checks michael@0: // pass, the value will have this type. michael@0: // michael@0: // Unless this is an MUrsh that has bailouts disabled, which, as a special michael@0: // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type() michael@0: // is MIRType_Int32. michael@0: MIRType type() const { michael@0: return resultType_; michael@0: } michael@0: michael@0: types::TemporaryTypeSet *resultTypeSet() const { michael@0: return resultTypeSet_; michael@0: } michael@0: bool emptyResultTypeSet() const; michael@0: michael@0: bool mightBeType(MIRType type) const { michael@0: MOZ_ASSERT(type != MIRType_Value); michael@0: michael@0: if (type == this->type()) michael@0: return true; michael@0: michael@0: if (MIRType_Value != this->type()) michael@0: return false; michael@0: michael@0: return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type); michael@0: } michael@0: michael@0: // Float32 specialization operations (see big comment in IonAnalysis before the Float32 michael@0: // specialization algorithm). michael@0: virtual bool isFloat32Commutative() const { return false; } michael@0: virtual bool canProduceFloat32() const { return false; } michael@0: virtual bool canConsumeFloat32(MUse *use) const { return false; } michael@0: virtual void trySpecializeFloat32(TempAllocator &alloc) {} michael@0: #ifdef DEBUG michael@0: // Used during the pass that checks that Float32 flow into valid MDefinitions michael@0: virtual bool isConsistentFloat32Use(MUse *use) const { michael@0: return type() == MIRType_Float32 || canConsumeFloat32(use); michael@0: } michael@0: #endif michael@0: michael@0: // Returns the beginning of this definition's use chain. michael@0: MUseIterator usesBegin() const { michael@0: return uses_.begin(); michael@0: } michael@0: michael@0: // Returns the end of this definition's use chain. michael@0: MUseIterator usesEnd() const { michael@0: return uses_.end(); michael@0: } michael@0: michael@0: bool canEmitAtUses() const { michael@0: return !isEmittedAtUses(); michael@0: } michael@0: michael@0: // Removes a use at the given position michael@0: MUseIterator removeUse(MUseIterator use); michael@0: void removeUse(MUse *use) { michael@0: uses_.remove(use); michael@0: } michael@0: michael@0: // Number of uses of this instruction. michael@0: size_t useCount() const; michael@0: michael@0: // Number of uses of this instruction. michael@0: // (only counting MDefinitions, ignoring MResumePoints) michael@0: size_t defUseCount() const; michael@0: michael@0: // Test whether this MDefinition has exactly one use. michael@0: bool hasOneUse() const; michael@0: michael@0: // Test whether this MDefinition has exactly one use. michael@0: // (only counting MDefinitions, ignoring MResumePoints) michael@0: bool hasOneDefUse() const; michael@0: michael@0: // Test whether this MDefinition has at least one use. michael@0: // (only counting MDefinitions, ignoring MResumePoints) michael@0: bool hasDefUses() const; michael@0: michael@0: bool hasUses() const { michael@0: return !uses_.empty(); michael@0: } michael@0: michael@0: virtual bool isControlInstruction() const { michael@0: return false; michael@0: } michael@0: michael@0: void addUse(MUse *use) { michael@0: uses_.pushFront(use); michael@0: } michael@0: void replaceAllUsesWith(MDefinition *dom); michael@0: michael@0: // Mark this instruction as having replaced all uses of ins, as during GVN, michael@0: // returning false if the replacement should not be performed. For use when michael@0: // GVN eliminates instructions which are not equivalent to one another. michael@0: virtual bool updateForReplacement(MDefinition *ins) { michael@0: return true; michael@0: } michael@0: michael@0: void setVirtualRegister(uint32_t vreg) { michael@0: virtualRegister_ = vreg; michael@0: #ifdef DEBUG michael@0: setLoweredUnchecked(); michael@0: #endif michael@0: } michael@0: uint32_t virtualRegister() const { michael@0: JS_ASSERT(isLowered()); michael@0: return virtualRegister_; michael@0: } michael@0: michael@0: public: michael@0: // Opcode testing and casts. michael@0: # define OPCODE_CASTS(opcode) \ michael@0: bool is##opcode() const { \ michael@0: return op() == Op_##opcode; \ michael@0: } \ michael@0: inline M##opcode *to##opcode(); \ michael@0: inline const M##opcode *to##opcode() const; michael@0: MIR_OPCODE_LIST(OPCODE_CASTS) michael@0: # undef OPCODE_CASTS michael@0: michael@0: inline MInstruction *toInstruction(); michael@0: bool isInstruction() const { michael@0: return !isPhi(); michael@0: } michael@0: michael@0: void setResultType(MIRType type) { michael@0: resultType_ = type; michael@0: } michael@0: void setResultTypeSet(types::TemporaryTypeSet *types) { michael@0: resultTypeSet_ = types; michael@0: } michael@0: michael@0: MDefinition *dependency() const { michael@0: return dependency_; michael@0: } michael@0: void setDependency(MDefinition *dependency) { michael@0: dependency_ = dependency; michael@0: } michael@0: virtual AliasSet getAliasSet() const { michael@0: // Instructions are effectful by default. michael@0: return AliasSet::Store(AliasSet::Any); michael@0: } michael@0: bool isEffectful() const { michael@0: return getAliasSet().isStore(); michael@0: } michael@0: virtual bool mightAlias(const MDefinition *store) const { michael@0: // Return whether this load may depend on the specified store, given michael@0: // that the alias sets intersect. This may be refined to exclude michael@0: // possible aliasing in cases where alias set flags are too imprecise. michael@0: JS_ASSERT(!isEffectful() && store->isEffectful()); michael@0: JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags()); michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // An MUseDefIterator walks over uses in a definition, skipping any use that is michael@0: // not a definition. Items from the use list must not be deleted during michael@0: // iteration. michael@0: class MUseDefIterator michael@0: { michael@0: MDefinition *def_; michael@0: MUseIterator current_; michael@0: michael@0: MUseIterator search(MUseIterator start) { michael@0: MUseIterator i(start); michael@0: for (; i != def_->usesEnd(); i++) { michael@0: if (i->consumer()->isDefinition()) michael@0: return i; michael@0: } michael@0: return def_->usesEnd(); michael@0: } michael@0: michael@0: public: michael@0: MUseDefIterator(MDefinition *def) michael@0: : def_(def), michael@0: current_(search(def->usesBegin())) michael@0: { } michael@0: michael@0: operator bool() const { michael@0: return current_ != def_->usesEnd(); michael@0: } michael@0: MUseDefIterator operator ++(int) { michael@0: MUseDefIterator old(*this); michael@0: if (current_ != def_->usesEnd()) michael@0: current_++; michael@0: current_ = search(current_); michael@0: return old; michael@0: } michael@0: MUse *use() const { michael@0: return *current_; michael@0: } michael@0: MDefinition *def() const { michael@0: return current_->consumer()->toDefinition(); michael@0: } michael@0: size_t index() const { michael@0: return current_->index(); michael@0: } michael@0: }; michael@0: michael@0: // An instruction is an SSA name that is inserted into a basic block's IR michael@0: // stream. michael@0: class MInstruction michael@0: : public MDefinition, michael@0: public InlineListNode michael@0: { michael@0: MResumePoint *resumePoint_; michael@0: michael@0: public: michael@0: MInstruction() michael@0: : resumePoint_(nullptr) michael@0: { } michael@0: michael@0: virtual bool accept(MInstructionVisitor *visitor) = 0; michael@0: michael@0: void setResumePoint(MResumePoint *resumePoint) { michael@0: JS_ASSERT(!resumePoint_); michael@0: resumePoint_ = resumePoint; michael@0: } michael@0: MResumePoint *resumePoint() const { michael@0: return resumePoint_; michael@0: } michael@0: }; michael@0: michael@0: #define INSTRUCTION_HEADER(opcode) \ michael@0: Opcode op() const { \ michael@0: return MDefinition::Op_##opcode; \ michael@0: } \ michael@0: const char *opName() const { \ michael@0: return #opcode; \ michael@0: } \ michael@0: bool accept(MInstructionVisitor *visitor) { \ michael@0: return visitor->visit##opcode(this); \ michael@0: } michael@0: michael@0: template michael@0: class MAryInstruction : public MInstruction michael@0: { michael@0: protected: michael@0: mozilla::Array operands_; michael@0: michael@0: void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { michael@0: operands_[index].set(operand, this, index); michael@0: operand->addUse(&operands_[index]); michael@0: } michael@0: michael@0: MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &operands_[index]; michael@0: } michael@0: michael@0: public: michael@0: MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { michael@0: return operands_[index].producer(); michael@0: } michael@0: size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Arity; michael@0: } michael@0: }; michael@0: michael@0: class MNullaryInstruction : public MAryInstruction<0> michael@0: { }; michael@0: michael@0: class MUnaryInstruction : public MAryInstruction<1> michael@0: { michael@0: protected: michael@0: MUnaryInstruction(MDefinition *ins) michael@0: { michael@0: setOperand(0, ins); michael@0: } michael@0: michael@0: public: michael@0: MDefinition *input() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: class MBinaryInstruction : public MAryInstruction<2> michael@0: { michael@0: protected: michael@0: MBinaryInstruction(MDefinition *left, MDefinition *right) michael@0: { michael@0: setOperand(0, left); michael@0: setOperand(1, right); michael@0: } michael@0: michael@0: public: michael@0: MDefinition *lhs() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *rhs() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: protected: michael@0: HashNumber valueHash() const michael@0: { michael@0: MDefinition *lhs = getOperand(0); michael@0: MDefinition *rhs = getOperand(1); michael@0: michael@0: return op() ^ lhs->valueNumber() ^ rhs->valueNumber(); michael@0: } michael@0: void swapOperands() { michael@0: MDefinition *temp = getOperand(0); michael@0: replaceOperand(0, getOperand(1)); michael@0: replaceOperand(1, temp); michael@0: } michael@0: michael@0: bool binaryCongruentTo(const MDefinition *ins) const michael@0: { michael@0: if (op() != ins->op()) michael@0: return false; michael@0: michael@0: if (type() != ins->type()) michael@0: return false; michael@0: michael@0: if (isEffectful() || ins->isEffectful()) michael@0: return false; michael@0: michael@0: const MDefinition *left = getOperand(0); michael@0: const MDefinition *right = getOperand(1); michael@0: const MDefinition *tmp; michael@0: michael@0: if (isCommutative() && left->valueNumber() > right->valueNumber()) { michael@0: tmp = right; michael@0: right = left; michael@0: left = tmp; michael@0: } michael@0: michael@0: const MBinaryInstruction *bi = static_cast(ins); michael@0: const MDefinition *insLeft = bi->getOperand(0); michael@0: const MDefinition *insRight = bi->getOperand(1); michael@0: if (isCommutative() && insLeft->valueNumber() > insRight->valueNumber()) { michael@0: tmp = insRight; michael@0: insRight = insLeft; michael@0: insLeft = tmp; michael@0: } michael@0: michael@0: return (left->valueNumber() == insLeft->valueNumber()) && michael@0: (right->valueNumber() == insRight->valueNumber()); michael@0: } michael@0: michael@0: // Return true if the operands to this instruction are both unsigned, michael@0: // in which case any wrapping operands were replaced with the underlying michael@0: // int32 operands. michael@0: bool tryUseUnsignedOperands(); michael@0: }; michael@0: michael@0: class MTernaryInstruction : public MAryInstruction<3> michael@0: { michael@0: protected: michael@0: MTernaryInstruction(MDefinition *first, MDefinition *second, MDefinition *third) michael@0: { michael@0: setOperand(0, first); michael@0: setOperand(1, second); michael@0: setOperand(2, third); michael@0: } michael@0: michael@0: protected: michael@0: HashNumber valueHash() const michael@0: { michael@0: MDefinition *first = getOperand(0); michael@0: MDefinition *second = getOperand(1); michael@0: MDefinition *third = getOperand(2); michael@0: michael@0: return op() ^ first->valueNumber() ^ second->valueNumber() ^ third->valueNumber(); michael@0: } michael@0: }; michael@0: michael@0: class MQuaternaryInstruction : public MAryInstruction<4> michael@0: { michael@0: protected: michael@0: MQuaternaryInstruction(MDefinition *first, MDefinition *second, michael@0: MDefinition *third, MDefinition *fourth) michael@0: { michael@0: setOperand(0, first); michael@0: setOperand(1, second); michael@0: setOperand(2, third); michael@0: setOperand(3, fourth); michael@0: } michael@0: michael@0: protected: michael@0: HashNumber valueHash() const michael@0: { michael@0: MDefinition *first = getOperand(0); michael@0: MDefinition *second = getOperand(1); michael@0: MDefinition *third = getOperand(2); michael@0: MDefinition *fourth = getOperand(3); michael@0: michael@0: return op() ^ first->valueNumber() ^ second->valueNumber() ^ michael@0: third->valueNumber() ^ fourth->valueNumber(); michael@0: } michael@0: }; michael@0: michael@0: // Generates an LSnapshot without further effect. michael@0: class MStart : public MNullaryInstruction michael@0: { michael@0: public: michael@0: enum StartType { michael@0: StartType_Default, michael@0: StartType_Osr michael@0: }; michael@0: michael@0: private: michael@0: StartType startType_; michael@0: michael@0: private: michael@0: MStart(StartType startType) michael@0: : startType_(startType) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Start) michael@0: static MStart *New(TempAllocator &alloc, StartType startType) { michael@0: return new(alloc) MStart(startType); michael@0: } michael@0: michael@0: StartType startType() { michael@0: return startType_; michael@0: } michael@0: }; michael@0: michael@0: // Instruction marking on entrypoint for on-stack replacement. michael@0: // OSR may occur at loop headers (at JSOP_TRACE). michael@0: // There is at most one MOsrEntry per MIRGraph. michael@0: class MOsrEntry : public MNullaryInstruction michael@0: { michael@0: protected: michael@0: MOsrEntry() { michael@0: setResultType(MIRType_Pointer); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(OsrEntry) michael@0: static MOsrEntry *New(TempAllocator &alloc) { michael@0: return new(alloc) MOsrEntry; michael@0: } michael@0: }; michael@0: michael@0: // No-op instruction. This cannot be moved or eliminated, and is intended for michael@0: // anchoring resume points at arbitrary points in a block. michael@0: class MNop : public MNullaryInstruction michael@0: { michael@0: protected: michael@0: MNop() { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Nop) michael@0: static MNop *New(TempAllocator &alloc) { michael@0: return new(alloc) MNop(); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // A constant js::Value. michael@0: class MConstant : public MNullaryInstruction michael@0: { michael@0: Value value_; michael@0: michael@0: protected: michael@0: MConstant(const Value &v, types::CompilerConstraintList *constraints); michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Constant) michael@0: static MConstant *New(TempAllocator &alloc, const Value &v, michael@0: types::CompilerConstraintList *constraints = nullptr); michael@0: static MConstant *NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type); michael@0: michael@0: const js::Value &value() const { michael@0: return value_; michael@0: } michael@0: const js::Value *vp() const { michael@0: return &value_; michael@0: } michael@0: const bool valueToBoolean() const { michael@0: // A hack to avoid this wordy pattern everywhere in the JIT. michael@0: return ToBoolean(HandleValue::fromMarkedLocation(&value_)); michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: HashNumber valueHash() const; michael@0: bool congruentTo(const MDefinition *ins) const; michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool updateForReplacement(MDefinition *def) { michael@0: MConstant *c = def->toConstant(); michael@0: // During constant folding, we don't want to replace a float32 michael@0: // value by a double value. michael@0: if (type() == MIRType_Float32) michael@0: return c->type() == MIRType_Float32; michael@0: if (type() == MIRType_Double) michael@0: return c->type() != MIRType_Float32; michael@0: return true; michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: michael@0: bool canProduceFloat32() const; michael@0: }; michael@0: michael@0: // Deep clone a constant JSObject. michael@0: class MCloneLiteral michael@0: : public MUnaryInstruction, michael@0: public ObjectPolicy<0> michael@0: { michael@0: protected: michael@0: MCloneLiteral(MDefinition *obj) michael@0: : MUnaryInstruction(obj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CloneLiteral) michael@0: static MCloneLiteral *New(TempAllocator &alloc, MDefinition *obj); michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MParameter : public MNullaryInstruction michael@0: { michael@0: int32_t index_; michael@0: michael@0: public: michael@0: static const int32_t THIS_SLOT = -1; michael@0: michael@0: MParameter(int32_t index, types::TemporaryTypeSet *types) michael@0: : index_(index) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setResultTypeSet(types); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Parameter) michael@0: static MParameter *New(TempAllocator &alloc, int32_t index, types::TemporaryTypeSet *types); michael@0: michael@0: int32_t index() const { michael@0: return index_; michael@0: } michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: HashNumber valueHash() const; michael@0: bool congruentTo(const MDefinition *ins) const; michael@0: }; michael@0: michael@0: class MCallee : public MNullaryInstruction michael@0: { michael@0: public: michael@0: MCallee() michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Callee) michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: static MCallee *New(TempAllocator &alloc) { michael@0: return new(alloc) MCallee(); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MControlInstruction : public MInstruction michael@0: { michael@0: public: michael@0: MControlInstruction() michael@0: { } michael@0: michael@0: virtual size_t numSuccessors() const = 0; michael@0: virtual MBasicBlock *getSuccessor(size_t i) const = 0; michael@0: virtual void replaceSuccessor(size_t i, MBasicBlock *successor) = 0; michael@0: michael@0: bool isControlInstruction() const { michael@0: return true; michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: }; michael@0: michael@0: class MTableSwitch MOZ_FINAL michael@0: : public MControlInstruction, michael@0: public NoFloatPolicy<0> michael@0: { michael@0: // The successors of the tableswitch michael@0: // - First successor = the default case michael@0: // - Successor 2 and higher = the cases sorted on case index. michael@0: Vector successors_; michael@0: Vector cases_; michael@0: michael@0: // Contains the blocks/cases that still need to get build michael@0: Vector blocks_; michael@0: michael@0: MUse operand_; michael@0: int32_t low_; michael@0: int32_t high_; michael@0: michael@0: MTableSwitch(TempAllocator &alloc, MDefinition *ins, michael@0: int32_t low, int32_t high) michael@0: : successors_(alloc), michael@0: cases_(alloc), michael@0: blocks_(alloc), michael@0: low_(low), michael@0: high_(high) michael@0: { michael@0: setOperand(0, ins); michael@0: } michael@0: michael@0: protected: michael@0: void setOperand(size_t index, MDefinition *operand) { michael@0: JS_ASSERT(index == 0); michael@0: operand_.set(operand, this, index); michael@0: operand->addUse(&operand_); michael@0: } michael@0: michael@0: MUse *getUseFor(size_t index) { michael@0: JS_ASSERT(index == 0); michael@0: return &operand_; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TableSwitch) michael@0: static MTableSwitch *New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high); michael@0: michael@0: size_t numSuccessors() const { michael@0: return successors_.length(); michael@0: } michael@0: michael@0: size_t addSuccessor(MBasicBlock *successor) { michael@0: JS_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2)); michael@0: JS_ASSERT(!successors_.empty()); michael@0: successors_.append(successor); michael@0: return successors_.length() - 1; michael@0: } michael@0: michael@0: MBasicBlock *getSuccessor(size_t i) const { michael@0: JS_ASSERT(i < numSuccessors()); michael@0: return successors_[i]; michael@0: } michael@0: michael@0: void replaceSuccessor(size_t i, MBasicBlock *successor) { michael@0: JS_ASSERT(i < numSuccessors()); michael@0: successors_[i] = successor; michael@0: } michael@0: michael@0: MBasicBlock** blocks() { michael@0: return &blocks_[0]; michael@0: } michael@0: michael@0: size_t numBlocks() const { michael@0: return blocks_.length(); michael@0: } michael@0: michael@0: int32_t low() const { michael@0: return low_; michael@0: } michael@0: michael@0: int32_t high() const { michael@0: return high_; michael@0: } michael@0: michael@0: MBasicBlock *getDefault() const { michael@0: return getSuccessor(0); michael@0: } michael@0: michael@0: MBasicBlock *getCase(size_t i) const { michael@0: return getSuccessor(cases_[i]); michael@0: } michael@0: michael@0: size_t numCases() const { michael@0: return high() - low() + 1; michael@0: } michael@0: michael@0: size_t addDefault(MBasicBlock *block) { michael@0: JS_ASSERT(successors_.empty()); michael@0: successors_.append(block); michael@0: return 0; michael@0: } michael@0: michael@0: void addCase(size_t successorIndex) { michael@0: cases_.append(successorIndex); michael@0: } michael@0: michael@0: MBasicBlock *getBlock(size_t i) const { michael@0: JS_ASSERT(i < numBlocks()); michael@0: return blocks_[i]; michael@0: } michael@0: michael@0: void addBlock(MBasicBlock *block) { michael@0: blocks_.append(block); michael@0: } michael@0: michael@0: MDefinition *getOperand(size_t index) const { michael@0: JS_ASSERT(index == 0); michael@0: return operand_.producer(); michael@0: } michael@0: michael@0: size_t numOperands() const { michael@0: return 1; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: class MAryControlInstruction : public MControlInstruction michael@0: { michael@0: mozilla::Array operands_; michael@0: mozilla::Array successors_; michael@0: michael@0: protected: michael@0: void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { michael@0: operands_[index].set(operand, this, index); michael@0: operand->addUse(&operands_[index]); michael@0: } michael@0: void setSuccessor(size_t index, MBasicBlock *successor) { michael@0: successors_[index] = successor; michael@0: } michael@0: michael@0: MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &operands_[index]; michael@0: } michael@0: michael@0: public: michael@0: MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { michael@0: return operands_[index].producer(); michael@0: } michael@0: size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Arity; michael@0: } michael@0: size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Successors; michael@0: } michael@0: MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE { michael@0: return successors_[i]; michael@0: } michael@0: void replaceSuccessor(size_t i, MBasicBlock *succ) MOZ_FINAL MOZ_OVERRIDE { michael@0: successors_[i] = succ; michael@0: } michael@0: }; michael@0: michael@0: // Jump to the start of another basic block. michael@0: class MGoto : public MAryControlInstruction<0, 1> michael@0: { michael@0: MGoto(MBasicBlock *target) { michael@0: setSuccessor(0, target); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Goto) michael@0: static MGoto *New(TempAllocator &alloc, MBasicBlock *target); michael@0: michael@0: MBasicBlock *target() { michael@0: return getSuccessor(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: enum BranchDirection { michael@0: FALSE_BRANCH, michael@0: TRUE_BRANCH michael@0: }; michael@0: michael@0: static inline BranchDirection michael@0: NegateBranchDirection(BranchDirection dir) michael@0: { michael@0: return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH; michael@0: } michael@0: michael@0: // Tests if the input instruction evaluates to true or false, and jumps to the michael@0: // start of a corresponding basic block. michael@0: class MTest michael@0: : public MAryControlInstruction<1, 2>, michael@0: public TestPolicy michael@0: { michael@0: bool operandMightEmulateUndefined_; michael@0: michael@0: MTest(MDefinition *ins, MBasicBlock *if_true, MBasicBlock *if_false) michael@0: : operandMightEmulateUndefined_(true) michael@0: { michael@0: setOperand(0, ins); michael@0: setSuccessor(0, if_true); michael@0: setSuccessor(1, if_false); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Test) michael@0: static MTest *New(TempAllocator &alloc, MDefinition *ins, michael@0: MBasicBlock *ifTrue, MBasicBlock *ifFalse); michael@0: michael@0: MDefinition *input() const { michael@0: return getOperand(0); michael@0: } michael@0: MBasicBlock *ifTrue() const { michael@0: return getSuccessor(0); michael@0: } michael@0: MBasicBlock *ifFalse() const { michael@0: return getSuccessor(1); michael@0: } michael@0: MBasicBlock *branchSuccessor(BranchDirection dir) const { michael@0: return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void infer(); michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, michael@0: bool *filtersNull); michael@0: michael@0: void markOperandCantEmulateUndefined() { michael@0: operandMightEmulateUndefined_ = false; michael@0: } michael@0: bool operandMightEmulateUndefined() const { michael@0: return operandMightEmulateUndefined_; michael@0: } michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: // Returns from this function to the previous caller. michael@0: class MReturn michael@0: : public MAryControlInstruction<1, 0>, michael@0: public BoxInputsPolicy michael@0: { michael@0: MReturn(MDefinition *ins) { michael@0: setOperand(0, ins); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Return) michael@0: static MReturn *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MReturn(ins); michael@0: } michael@0: michael@0: MDefinition *input() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MThrow michael@0: : public MAryControlInstruction<1, 0>, michael@0: public BoxInputsPolicy michael@0: { michael@0: MThrow(MDefinition *ins) { michael@0: setOperand(0, ins); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Throw) michael@0: static MThrow *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MThrow(ins); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Fabricate a type set containing only the type of the specified object. michael@0: types::TemporaryTypeSet * michael@0: MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj); michael@0: michael@0: bool michael@0: MergeTypes(MIRType *ptype, types::TemporaryTypeSet **ptypeSet, michael@0: MIRType newType, types::TemporaryTypeSet *newTypeSet); michael@0: michael@0: class MNewArray : public MNullaryInstruction michael@0: { michael@0: public: michael@0: enum AllocatingBehaviour { michael@0: NewArray_Allocating, michael@0: NewArray_Unallocating michael@0: }; michael@0: michael@0: private: michael@0: // Number of space to allocate for the array. michael@0: uint32_t count_; michael@0: // Template for the created object. michael@0: CompilerRootObject templateObject_; michael@0: gc::InitialHeap initialHeap_; michael@0: // Allocate space at initialization or not michael@0: AllocatingBehaviour allocating_; michael@0: michael@0: MNewArray(types::CompilerConstraintList *constraints, uint32_t count, JSObject *templateObject, michael@0: gc::InitialHeap initialHeap, AllocatingBehaviour allocating) michael@0: : count_(count), michael@0: templateObject_(templateObject), michael@0: initialHeap_(initialHeap), michael@0: allocating_(allocating) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: if (!templateObject->hasSingletonType()) michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewArray) michael@0: michael@0: static MNewArray *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: uint32_t count, JSObject *templateObject, michael@0: gc::InitialHeap initialHeap, AllocatingBehaviour allocating) michael@0: { michael@0: return new(alloc) MNewArray(constraints, count, templateObject, initialHeap, allocating); michael@0: } michael@0: michael@0: uint32_t count() const { michael@0: return count_; michael@0: } michael@0: michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: michael@0: gc::InitialHeap initialHeap() const { michael@0: return initialHeap_; michael@0: } michael@0: michael@0: bool isAllocating() const { michael@0: return allocating_ == NewArray_Allocating; michael@0: } michael@0: michael@0: // Returns true if the code generator should call through to the michael@0: // VM rather than the fast path. michael@0: bool shouldUseVM() const; michael@0: michael@0: // NewArray is marked as non-effectful because all our allocations are michael@0: // either lazy when we are using "new Array(length)" or bounded by the michael@0: // script or the stack size when we are using "new Array(...)" or "[...]" michael@0: // notations. So we might have to allocate the array twice if we bail michael@0: // during the computation of the first element of the square braket michael@0: // notation. michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MNewObject : public MNullaryInstruction michael@0: { michael@0: CompilerRootObject templateObject_; michael@0: gc::InitialHeap initialHeap_; michael@0: bool templateObjectIsClassPrototype_; michael@0: michael@0: MNewObject(types::CompilerConstraintList *constraints, JSObject *templateObject, michael@0: gc::InitialHeap initialHeap, bool templateObjectIsClassPrototype) michael@0: : templateObject_(templateObject), michael@0: initialHeap_(initialHeap), michael@0: templateObjectIsClassPrototype_(templateObjectIsClassPrototype) michael@0: { michael@0: JS_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM()); michael@0: setResultType(MIRType_Object); michael@0: if (!templateObject->hasSingletonType()) michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewObject) michael@0: michael@0: static MNewObject *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: JSObject *templateObject, gc::InitialHeap initialHeap, michael@0: bool templateObjectIsClassPrototype) michael@0: { michael@0: return new(alloc) MNewObject(constraints, templateObject, initialHeap, michael@0: templateObjectIsClassPrototype); michael@0: } michael@0: michael@0: // Returns true if the code generator should call through to the michael@0: // VM rather than the fast path. michael@0: bool shouldUseVM() const; michael@0: michael@0: bool templateObjectIsClassPrototype() const { michael@0: return templateObjectIsClassPrototype_; michael@0: } michael@0: michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: michael@0: gc::InitialHeap initialHeap() const { michael@0: return initialHeap_; michael@0: } michael@0: }; michael@0: michael@0: // Could be allocating either a new array or a new object. michael@0: class MNewPar : public MUnaryInstruction michael@0: { michael@0: CompilerRootObject templateObject_; michael@0: michael@0: MNewPar(MDefinition *cx, JSObject *templateObject) michael@0: : MUnaryInstruction(cx), michael@0: templateObject_(templateObject) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewPar); michael@0: michael@0: static MNewPar *New(TempAllocator &alloc, MDefinition *cx, JSObject *templateObject) { michael@0: return new(alloc) MNewPar(cx, templateObject); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: }; michael@0: michael@0: // Creates a new derived type object. At runtime, this is just a call michael@0: // to `BinaryBlock::createDerived()`. That is, the MIR itself does not michael@0: // compile to particularly optimized code. However, using a distinct michael@0: // MIR for creating derived type objects allows the compiler to michael@0: // optimize ephemeral typed objects as would be created for a michael@0: // reference like `a.b.c` -- here, the `a.b` will create an ephemeral michael@0: // derived type object that aliases the memory of `a` itself. The michael@0: // specific nature of `a.b` is revealed by using michael@0: // `MNewDerivedTypedObject` rather than `MGetProperty` or what have michael@0: // you. Moreover, the compiler knows that there are no side-effects, michael@0: // so `MNewDerivedTypedObject` instructions can be reordered or pruned michael@0: // as dead code. michael@0: class MNewDerivedTypedObject michael@0: : public MTernaryInstruction, michael@0: public Mix3Policy, michael@0: ObjectPolicy<1>, michael@0: IntPolicy<2> > michael@0: { michael@0: private: michael@0: TypeDescrSet set_; michael@0: michael@0: MNewDerivedTypedObject(TypeDescrSet set, michael@0: MDefinition *type, michael@0: MDefinition *owner, michael@0: MDefinition *offset) michael@0: : MTernaryInstruction(type, owner, offset), michael@0: set_(set) michael@0: { michael@0: setMovable(); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewDerivedTypedObject); michael@0: michael@0: static MNewDerivedTypedObject *New(TempAllocator &alloc, TypeDescrSet set, michael@0: MDefinition *type, MDefinition *owner, MDefinition *offset) michael@0: { michael@0: return new(alloc) MNewDerivedTypedObject(set, type, owner, offset); michael@0: } michael@0: michael@0: TypeDescrSet set() const { michael@0: return set_; michael@0: } michael@0: michael@0: MDefinition *type() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *owner() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: MDefinition *offset() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Abort parallel execution. michael@0: class MAbortPar : public MAryControlInstruction<0, 0> michael@0: { michael@0: MAbortPar() michael@0: : MAryControlInstruction<0, 0>() michael@0: { michael@0: setResultType(MIRType_None); michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AbortPar); michael@0: michael@0: static MAbortPar *New(TempAllocator &alloc) { michael@0: return new(alloc) MAbortPar(); michael@0: } michael@0: }; michael@0: michael@0: // Setting __proto__ in an object literal. michael@0: class MMutateProto michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: protected: michael@0: MMutateProto(MDefinition *obj, MDefinition *value) michael@0: { michael@0: setOperand(0, obj); michael@0: setOperand(1, value); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(MutateProto) michael@0: michael@0: static MMutateProto *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) michael@0: { michael@0: return new(alloc) MMutateProto(obj, value); michael@0: } michael@0: michael@0: MDefinition *getObject() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getValue() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Slow path for adding a property to an object without a known base. michael@0: class MInitProp michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: public: michael@0: CompilerRootPropertyName name_; michael@0: michael@0: protected: michael@0: MInitProp(MDefinition *obj, PropertyName *name, MDefinition *value) michael@0: : name_(name) michael@0: { michael@0: setOperand(0, obj); michael@0: setOperand(1, value); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InitProp) michael@0: michael@0: static MInitProp *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MInitProp(obj, name, value); michael@0: } michael@0: michael@0: MDefinition *getObject() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getValue() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: PropertyName *propertyName() const { michael@0: return name_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MInitPropGetterSetter michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: michael@0: MInitPropGetterSetter(MDefinition *obj, PropertyName *name, MDefinition *value) michael@0: : MBinaryInstruction(obj, value), michael@0: name_(name) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InitPropGetterSetter) michael@0: michael@0: static MInitPropGetterSetter *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MInitPropGetterSetter(obj, name, value); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MInitElem michael@0: : public MAryInstruction<3>, michael@0: public Mix3Policy, BoxPolicy<1>, BoxPolicy<2> > michael@0: { michael@0: MInitElem(MDefinition *obj, MDefinition *id, MDefinition *value) michael@0: { michael@0: setOperand(0, obj); michael@0: setOperand(1, id); michael@0: setOperand(2, value); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InitElem) michael@0: michael@0: static MInitElem *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MInitElem(obj, id, value); michael@0: } michael@0: michael@0: MDefinition *getObject() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getId() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *getValue() const { michael@0: return getOperand(2); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MInitElemGetterSetter michael@0: : public MTernaryInstruction, michael@0: public Mix3Policy, BoxPolicy<1>, ObjectPolicy<2> > michael@0: { michael@0: MInitElemGetterSetter(MDefinition *obj, MDefinition *id, MDefinition *value) michael@0: : MTernaryInstruction(obj, id, value) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InitElemGetterSetter) michael@0: michael@0: static MInitElemGetterSetter *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MInitElemGetterSetter(obj, id, value); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *idValue() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(2); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MVariadicInstruction : public MInstruction michael@0: { michael@0: FixedList operands_; michael@0: michael@0: protected: michael@0: bool init(TempAllocator &alloc, size_t length) { michael@0: return operands_.init(alloc, length); michael@0: } michael@0: michael@0: public: michael@0: // Will assert if called before initialization. michael@0: MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { michael@0: return operands_[index].producer(); michael@0: } michael@0: size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return operands_.length(); michael@0: } michael@0: void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { michael@0: operands_[index].set(operand, this, index); michael@0: operand->addUse(&operands_[index]); michael@0: } michael@0: michael@0: MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &operands_[index]; michael@0: } michael@0: }; michael@0: michael@0: class MCall michael@0: : public MVariadicInstruction, michael@0: public CallPolicy michael@0: { michael@0: private: michael@0: // An MCall uses the MPrepareCall, MDefinition for the function, and michael@0: // MPassArg instructions. They are stored in the same list. michael@0: static const size_t FunctionOperandIndex = 0; michael@0: static const size_t NumNonArgumentOperands = 1; michael@0: michael@0: protected: michael@0: // True if the call is for JSOP_NEW. michael@0: bool construct_; michael@0: // Monomorphic cache of single target from TI, or nullptr. michael@0: CompilerRootFunction target_; michael@0: // Original value of argc from the bytecode. michael@0: uint32_t numActualArgs_; michael@0: michael@0: bool needsArgCheck_; michael@0: michael@0: MCall(JSFunction *target, uint32_t numActualArgs, bool construct) michael@0: : construct_(construct), michael@0: target_(target), michael@0: numActualArgs_(numActualArgs), michael@0: needsArgCheck_(true) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Call) michael@0: static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs, michael@0: bool construct, bool isDOMCall); michael@0: michael@0: void initFunction(MDefinition *func) { michael@0: return setOperand(FunctionOperandIndex, func); michael@0: } michael@0: michael@0: bool needsArgCheck() const { michael@0: return needsArgCheck_; michael@0: } michael@0: michael@0: void disableArgCheck() { michael@0: needsArgCheck_ = false; michael@0: } michael@0: MDefinition *getFunction() const { michael@0: return getOperand(FunctionOperandIndex); michael@0: } michael@0: void replaceFunction(MInstruction *newfunc) { michael@0: replaceOperand(FunctionOperandIndex, newfunc); michael@0: } michael@0: michael@0: void addArg(size_t argnum, MDefinition *arg); michael@0: michael@0: MDefinition *getArg(uint32_t index) const { michael@0: return getOperand(NumNonArgumentOperands + index); michael@0: } michael@0: michael@0: static size_t IndexOfThis() { michael@0: return NumNonArgumentOperands; michael@0: } michael@0: static size_t IndexOfArgument(size_t index) { michael@0: return NumNonArgumentOperands + index + 1; // +1 to skip |this|. michael@0: } michael@0: static size_t IndexOfStackArg(size_t index) { michael@0: return NumNonArgumentOperands + index; michael@0: } michael@0: michael@0: // For TI-informed monomorphic callsites. michael@0: JSFunction *getSingleTarget() const { michael@0: return target_; michael@0: } michael@0: michael@0: bool isConstructing() const { michael@0: return construct_; michael@0: } michael@0: michael@0: // The number of stack arguments is the max between the number of formal michael@0: // arguments and the number of actual arguments. The number of stack michael@0: // argument includes the |undefined| padding added in case of underflow. michael@0: // Includes |this|. michael@0: uint32_t numStackArgs() const { michael@0: return numOperands() - NumNonArgumentOperands; michael@0: } michael@0: michael@0: // Does not include |this|. michael@0: uint32_t numActualArgs() const { michael@0: return numActualArgs_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: virtual bool isCallDOMNative() const { michael@0: return false; michael@0: } michael@0: michael@0: // A method that can be called to tell the MCall to figure out whether it's michael@0: // movable or not. This can't be done in the constructor, because it michael@0: // depends on the arguments to the call, and those aren't passed to the michael@0: // constructor but are set up later via addArg. michael@0: virtual void computeMovable() { michael@0: } michael@0: }; michael@0: michael@0: class MCallDOMNative : public MCall michael@0: { michael@0: // A helper class for MCalls for DOM natives. Note that this is NOT michael@0: // actually a separate MIR op from MCall, because all sorts of places use michael@0: // isCall() to check for calls and all we really want is to overload a few michael@0: // virtual things from MCall. michael@0: protected: michael@0: MCallDOMNative(JSFunction *target, uint32_t numActualArgs) michael@0: : MCall(target, numActualArgs, false) michael@0: { michael@0: // If our jitinfo is not marked movable, that means that our C++ michael@0: // implementation is fallible or that we have no hope of ever doing the michael@0: // sort of argument analysis that would allow us to detemine that we're michael@0: // side-effect-free. In the latter case we wouldn't get DCEd no matter michael@0: // what, but for the former case we have to explicitly say that we can't michael@0: // be DCEd. michael@0: if (!getJitInfo()->isMovable) michael@0: setGuard(); michael@0: } michael@0: michael@0: friend MCall *MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, michael@0: size_t numActualArgs, bool construct, bool isDOMCall); michael@0: michael@0: const JSJitInfo *getJitInfo() const; michael@0: public: michael@0: virtual AliasSet getAliasSet() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE; michael@0: michael@0: virtual bool isCallDOMNative() const MOZ_OVERRIDE { michael@0: return true; michael@0: } michael@0: michael@0: virtual void computeMovable() MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // arr.splice(start, deleteCount) with unused return value. michael@0: class MArraySplice michael@0: : public MTernaryInstruction, michael@0: public Mix3Policy, IntPolicy<1>, IntPolicy<2> > michael@0: { michael@0: private: michael@0: michael@0: MArraySplice(MDefinition *object, MDefinition *start, MDefinition *deleteCount) michael@0: : MTernaryInstruction(object, start, deleteCount) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArraySplice) michael@0: static MArraySplice *New(TempAllocator &alloc, MDefinition *object, michael@0: MDefinition *start, MDefinition *deleteCount) michael@0: { michael@0: return new(alloc) MArraySplice(object, start, deleteCount); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *start() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: MDefinition *deleteCount() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: // fun.apply(self, arguments) michael@0: class MApplyArgs michael@0: : public MAryInstruction<3>, michael@0: public MixPolicy, MixPolicy, BoxPolicy<2> > > michael@0: { michael@0: protected: michael@0: // Monomorphic cache of single target from TI, or nullptr. michael@0: CompilerRootFunction target_; michael@0: michael@0: MApplyArgs(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self) michael@0: : target_(target) michael@0: { michael@0: setOperand(0, fun); michael@0: setOperand(1, argc); michael@0: setOperand(2, self); michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ApplyArgs) michael@0: static MApplyArgs *New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, michael@0: MDefinition *argc, MDefinition *self); michael@0: michael@0: MDefinition *getFunction() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: // For TI-informed monomorphic callsites. michael@0: JSFunction *getSingleTarget() const { michael@0: return target_; michael@0: } michael@0: michael@0: MDefinition *getArgc() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *getThis() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MBail : public MNullaryInstruction michael@0: { michael@0: protected: michael@0: MBail() michael@0: { michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Bail) michael@0: michael@0: static MBail * michael@0: New(TempAllocator &alloc) { michael@0: return new(alloc) MBail(); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MAssertFloat32 : public MUnaryInstruction michael@0: { michael@0: protected: michael@0: bool mustBeFloat32_; michael@0: michael@0: MAssertFloat32(MDefinition *value, bool mustBeFloat32) michael@0: : MUnaryInstruction(value), mustBeFloat32_(mustBeFloat32) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AssertFloat32) michael@0: michael@0: static MAssertFloat32 *New(TempAllocator &alloc, MDefinition *value, bool mustBeFloat32) { michael@0: return new(alloc) MAssertFloat32(value, mustBeFloat32); michael@0: } michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { return true; } michael@0: michael@0: bool mustBeFloat32() const { return mustBeFloat32_; } michael@0: }; michael@0: michael@0: class MGetDynamicName michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, ConvertToStringPolicy<1> > michael@0: { michael@0: protected: michael@0: MGetDynamicName(MDefinition *scopeChain, MDefinition *name) michael@0: { michael@0: setOperand(0, scopeChain); michael@0: setOperand(1, name); michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetDynamicName) michael@0: michael@0: static MGetDynamicName * michael@0: New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *name) { michael@0: return new(alloc) MGetDynamicName(scopeChain, name); michael@0: } michael@0: michael@0: MDefinition *getScopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getName() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Bailout if the input string contains 'arguments' or 'eval'. michael@0: class MFilterArgumentsOrEval michael@0: : public MAryInstruction<1>, michael@0: public BoxExceptPolicy<0, MIRType_String> michael@0: { michael@0: protected: michael@0: MFilterArgumentsOrEval(MDefinition *string) michael@0: { michael@0: setOperand(0, string); michael@0: setGuard(); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(FilterArgumentsOrEval) michael@0: michael@0: static MFilterArgumentsOrEval *New(TempAllocator &alloc, MDefinition *string) { michael@0: return new(alloc) MFilterArgumentsOrEval(string); michael@0: } michael@0: michael@0: MDefinition *getString() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MCallDirectEval michael@0: : public MAryInstruction<3>, michael@0: public MixPolicy, michael@0: MixPolicy, BoxPolicy<2> > > michael@0: { michael@0: protected: michael@0: MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, michael@0: jsbytecode *pc) michael@0: : pc_(pc) michael@0: { michael@0: setOperand(0, scopeChain); michael@0: setOperand(1, string); michael@0: setOperand(2, thisValue); michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallDirectEval) michael@0: michael@0: static MCallDirectEval * michael@0: New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, michael@0: jsbytecode *pc) michael@0: { michael@0: return new(alloc) MCallDirectEval(scopeChain, string, thisValue, pc); michael@0: } michael@0: michael@0: MDefinition *getScopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getString() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *getThisValue() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: jsbytecode *pc() const { michael@0: return pc_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: jsbytecode *pc_; michael@0: }; michael@0: michael@0: class MCompare michael@0: : public MBinaryInstruction, michael@0: public ComparePolicy michael@0: { michael@0: public: michael@0: enum CompareType { michael@0: michael@0: // Anything compared to Undefined michael@0: Compare_Undefined, michael@0: michael@0: // Anything compared to Null michael@0: Compare_Null, michael@0: michael@0: // Undefined compared to Boolean michael@0: // Null compared to Boolean michael@0: // Double compared to Boolean michael@0: // String compared to Boolean michael@0: // Object compared to Boolean michael@0: // Value compared to Boolean michael@0: Compare_Boolean, michael@0: michael@0: // Int32 compared to Int32 michael@0: // Boolean compared to Boolean michael@0: Compare_Int32, michael@0: Compare_Int32MaybeCoerceBoth, michael@0: Compare_Int32MaybeCoerceLHS, michael@0: Compare_Int32MaybeCoerceRHS, michael@0: michael@0: // Int32 compared as unsigneds michael@0: Compare_UInt32, michael@0: michael@0: // Double compared to Double michael@0: Compare_Double, michael@0: michael@0: Compare_DoubleMaybeCoerceLHS, michael@0: Compare_DoubleMaybeCoerceRHS, michael@0: michael@0: // Float compared to Float michael@0: Compare_Float32, michael@0: michael@0: // String compared to String michael@0: Compare_String, michael@0: michael@0: // Undefined compared to String michael@0: // Null compared to String michael@0: // Boolean compared to String michael@0: // Int32 compared to String michael@0: // Double compared to String michael@0: // Object compared to String michael@0: // Value compared to String michael@0: Compare_StrictString, michael@0: michael@0: // Object compared to Object michael@0: Compare_Object, michael@0: michael@0: // Compare 2 values bitwise michael@0: Compare_Value, michael@0: michael@0: // All other possible compares michael@0: Compare_Unknown michael@0: }; michael@0: michael@0: private: michael@0: CompareType compareType_; michael@0: JSOp jsop_; michael@0: bool operandMightEmulateUndefined_; michael@0: bool operandsAreNeverNaN_; michael@0: michael@0: // When a floating-point comparison is converted to an integer comparison michael@0: // (when range analysis proves it safe), we need to convert the operands michael@0: // to integer as well. michael@0: bool truncateOperands_; michael@0: michael@0: MCompare(MDefinition *left, MDefinition *right, JSOp jsop) michael@0: : MBinaryInstruction(left, right), michael@0: compareType_(Compare_Unknown), michael@0: jsop_(jsop), michael@0: operandMightEmulateUndefined_(true), michael@0: operandsAreNeverNaN_(false), michael@0: truncateOperands_(false) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Compare) michael@0: static MCompare *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op); michael@0: static MCompare *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op, michael@0: CompareType compareType); michael@0: michael@0: bool tryFold(bool *result); michael@0: bool evaluateConstantOperands(bool *result); michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, michael@0: bool *filtersNull); michael@0: michael@0: void infer(BaselineInspector *inspector, jsbytecode *pc); michael@0: CompareType compareType() const { michael@0: return compareType_; michael@0: } michael@0: bool isInt32Comparison() const { michael@0: return compareType() == Compare_Int32 || michael@0: compareType() == Compare_Int32MaybeCoerceBoth || michael@0: compareType() == Compare_Int32MaybeCoerceLHS || michael@0: compareType() == Compare_Int32MaybeCoerceRHS; michael@0: } michael@0: bool isDoubleComparison() const { michael@0: return compareType() == Compare_Double || michael@0: compareType() == Compare_DoubleMaybeCoerceLHS || michael@0: compareType() == Compare_DoubleMaybeCoerceRHS; michael@0: } michael@0: bool isFloat32Comparison() const { michael@0: return compareType() == Compare_Float32; michael@0: } michael@0: void setCompareType(CompareType type) { michael@0: compareType_ = type; michael@0: } michael@0: MIRType inputType(); michael@0: michael@0: JSOp jsop() const { michael@0: return jsop_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: void markNoOperandEmulatesUndefined() { michael@0: operandMightEmulateUndefined_ = false; michael@0: } michael@0: bool operandMightEmulateUndefined() const { michael@0: return operandMightEmulateUndefined_; michael@0: } michael@0: bool operandsAreNeverNaN() const { michael@0: return operandsAreNeverNaN_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // Strict equality is never effectful. michael@0: if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE) michael@0: return AliasSet::None(); michael@0: if (compareType_ == Compare_Unknown) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: JS_ASSERT(compareType_ <= Compare_Value); michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: void collectRangeInfoPreTrunc(); michael@0: michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: bool isFloat32Commutative() const { return true; } michael@0: bool truncate(); michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: # ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: // Both sides of the compare can be Float32 michael@0: return compareType_ == Compare_Float32; michael@0: } michael@0: # endif michael@0: michael@0: protected: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!binaryCongruentTo(ins)) michael@0: return false; michael@0: return compareType() == ins->toCompare()->compareType() && michael@0: jsop() == ins->toCompare()->jsop(); michael@0: } michael@0: }; michael@0: michael@0: // Takes a typed value and returns an untyped value. michael@0: class MBox : public MUnaryInstruction michael@0: { michael@0: MBox(TempAllocator &alloc, MDefinition *ins) michael@0: : MUnaryInstruction(ins) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: if (ins->resultTypeSet()) { michael@0: setResultTypeSet(ins->resultTypeSet()); michael@0: } else if (ins->type() != MIRType_Value) { michael@0: types::Type ntype = ins->type() == MIRType_Object michael@0: ? types::Type::AnyObjectType() michael@0: : types::Type::PrimitiveType(ValueTypeFromMIRType(ins->type())); michael@0: setResultTypeSet(alloc.lifoAlloc()->new_(ntype)); michael@0: } michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Box) michael@0: static MBox *New(TempAllocator &alloc, MDefinition *ins) michael@0: { michael@0: // Cannot box a box. michael@0: JS_ASSERT(ins->type() != MIRType_Value); michael@0: michael@0: return new(alloc) MBox(alloc, ins); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Note: the op may have been inverted during lowering (to put constants in a michael@0: // position where they can be immediates), so it is important to use the michael@0: // lir->jsop() instead of the mir->jsop() when it is present. michael@0: static inline Assembler::Condition michael@0: JSOpToCondition(MCompare::CompareType compareType, JSOp op) michael@0: { michael@0: bool isSigned = (compareType != MCompare::Compare_UInt32); michael@0: return JSOpToCondition(op, isSigned); michael@0: } michael@0: michael@0: // Takes a typed value and checks if it is a certain type. If so, the payload michael@0: // is unpacked and returned as that type. Otherwise, it is considered a michael@0: // deoptimization. michael@0: class MUnbox : public MUnaryInstruction, public BoxInputsPolicy michael@0: { michael@0: public: michael@0: enum Mode { michael@0: Fallible, // Check the type, and deoptimize if unexpected. michael@0: Infallible, // Type guard is not necessary. michael@0: TypeBarrier // Guard on the type, and act like a TypeBarrier on failure. michael@0: }; michael@0: michael@0: private: michael@0: Mode mode_; michael@0: BailoutKind bailoutKind_; michael@0: michael@0: MUnbox(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind) michael@0: : MUnaryInstruction(ins), michael@0: mode_(mode) michael@0: { michael@0: JS_ASSERT(ins->type() == MIRType_Value); michael@0: JS_ASSERT(type == MIRType_Boolean || michael@0: type == MIRType_Int32 || michael@0: type == MIRType_Double || michael@0: type == MIRType_String || michael@0: type == MIRType_Object); michael@0: michael@0: setResultType(type); michael@0: setResultTypeSet(ins->resultTypeSet()); michael@0: setMovable(); michael@0: michael@0: if (mode_ == TypeBarrier || mode_ == Fallible) michael@0: setGuard(); michael@0: michael@0: bailoutKind_ = kind; michael@0: } michael@0: public: michael@0: INSTRUCTION_HEADER(Unbox) michael@0: static MUnbox *New(TempAllocator &alloc, MDefinition *ins, MIRType type, Mode mode) michael@0: { michael@0: return new(alloc) MUnbox(ins, type, mode, Bailout_Normal); michael@0: } michael@0: michael@0: static MUnbox *New(TempAllocator &alloc, MDefinition *ins, MIRType type, Mode mode, michael@0: BailoutKind kind) michael@0: { michael@0: return new(alloc) MUnbox(ins, type, mode, kind); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: Mode mode() const { michael@0: return mode_; michael@0: } michael@0: BailoutKind bailoutKind() const { michael@0: // If infallible, no bailout should be generated. michael@0: JS_ASSERT(fallible()); michael@0: return bailoutKind_; michael@0: } michael@0: bool fallible() const { michael@0: return mode() != Infallible; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void printOpcode(FILE *fp) const; michael@0: void makeInfallible() { michael@0: // Should only be called if we're already Infallible or TypeBarrier michael@0: JS_ASSERT(mode() != Fallible); michael@0: mode_ = Infallible; michael@0: } michael@0: }; michael@0: michael@0: class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy michael@0: { michael@0: MGuardObject(MDefinition *ins) michael@0: : MUnaryInstruction(ins) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardObject) michael@0: michael@0: static MGuardObject *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MGuardObject(ins); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MGuardString michael@0: : public MUnaryInstruction, michael@0: public StringPolicy<0> michael@0: { michael@0: MGuardString(MDefinition *ins) michael@0: : MUnaryInstruction(ins) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_String); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardString) michael@0: michael@0: static MGuardString *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MGuardString(ins); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MAssertRange michael@0: : public MUnaryInstruction michael@0: { michael@0: // This is the range checked by the assertion. Don't confuse this with the michael@0: // range_ member or the range() accessor. Since MAssertRange doesn't return michael@0: // a value, it doesn't use those. michael@0: const Range *assertedRange_; michael@0: michael@0: MAssertRange(MDefinition *ins, const Range *assertedRange) michael@0: : MUnaryInstruction(ins), assertedRange_(assertedRange) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AssertRange) michael@0: michael@0: static MAssertRange *New(TempAllocator &alloc, MDefinition *ins, const Range *assertedRange) { michael@0: return new(alloc) MAssertRange(ins, assertedRange); michael@0: } michael@0: michael@0: const Range *assertedRange() const { michael@0: return assertedRange_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: }; michael@0: michael@0: // Caller-side allocation of |this| for |new|: michael@0: // Given a templateobject, construct |this| for JSOP_NEW michael@0: class MCreateThisWithTemplate michael@0: : public MNullaryInstruction michael@0: { michael@0: // Template for |this|, provided by TI michael@0: CompilerRootObject templateObject_; michael@0: gc::InitialHeap initialHeap_; michael@0: michael@0: MCreateThisWithTemplate(types::CompilerConstraintList *constraints, JSObject *templateObject, michael@0: gc::InitialHeap initialHeap) michael@0: : templateObject_(templateObject), michael@0: initialHeap_(initialHeap) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CreateThisWithTemplate); michael@0: static MCreateThisWithTemplate *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: JSObject *templateObject, gc::InitialHeap initialHeap) michael@0: { michael@0: return new(alloc) MCreateThisWithTemplate(constraints, templateObject, initialHeap); michael@0: } michael@0: michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: michael@0: gc::InitialHeap initialHeap() const { michael@0: return initialHeap_; michael@0: } michael@0: michael@0: // Although creation of |this| modifies global state, it is safely repeatable. michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Caller-side allocation of |this| for |new|: michael@0: // Given a prototype operand, construct |this| for JSOP_NEW. michael@0: class MCreateThisWithProto michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: MCreateThisWithProto(MDefinition *callee, MDefinition *prototype) michael@0: : MBinaryInstruction(callee, prototype) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CreateThisWithProto) michael@0: static MCreateThisWithProto *New(TempAllocator &alloc, MDefinition *callee, michael@0: MDefinition *prototype) michael@0: { michael@0: return new(alloc) MCreateThisWithProto(callee, prototype); michael@0: } michael@0: michael@0: MDefinition *getCallee() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getPrototype() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: // Although creation of |this| modifies global state, it is safely repeatable. michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Caller-side allocation of |this| for |new|: michael@0: // Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING). michael@0: class MCreateThis michael@0: : public MUnaryInstruction, michael@0: public ObjectPolicy<0> michael@0: { michael@0: MCreateThis(MDefinition *callee) michael@0: : MUnaryInstruction(callee) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CreateThis) michael@0: static MCreateThis *New(TempAllocator &alloc, MDefinition *callee) michael@0: { michael@0: return new(alloc) MCreateThis(callee); michael@0: } michael@0: michael@0: MDefinition *getCallee() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: // Although creation of |this| modifies global state, it is safely repeatable. michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Eager initialization of arguments object. michael@0: class MCreateArgumentsObject michael@0: : public MUnaryInstruction, michael@0: public ObjectPolicy<0> michael@0: { michael@0: MCreateArgumentsObject(MDefinition *callObj) michael@0: : MUnaryInstruction(callObj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CreateArgumentsObject) michael@0: static MCreateArgumentsObject *New(TempAllocator &alloc, MDefinition *callObj) { michael@0: return new(alloc) MCreateArgumentsObject(callObj); michael@0: } michael@0: michael@0: MDefinition *getCallObject() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MGetArgumentsObjectArg michael@0: : public MUnaryInstruction, michael@0: public ObjectPolicy<0> michael@0: { michael@0: size_t argno_; michael@0: michael@0: MGetArgumentsObjectArg(MDefinition *argsObject, size_t argno) michael@0: : MUnaryInstruction(argsObject), michael@0: argno_(argno) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetArgumentsObjectArg) michael@0: static MGetArgumentsObjectArg *New(TempAllocator &alloc, MDefinition *argsObj, size_t argno) michael@0: { michael@0: return new(alloc) MGetArgumentsObjectArg(argsObj, argno); michael@0: } michael@0: michael@0: MDefinition *getArgsObject() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: size_t argno() const { michael@0: return argno_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::Any); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MSetArgumentsObjectArg michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: size_t argno_; michael@0: michael@0: MSetArgumentsObjectArg(MDefinition *argsObj, size_t argno, MDefinition *value) michael@0: : MBinaryInstruction(argsObj, value), michael@0: argno_(argno) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetArgumentsObjectArg) michael@0: static MSetArgumentsObjectArg *New(TempAllocator &alloc, MDefinition *argsObj, size_t argno, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MSetArgumentsObjectArg(argsObj, argno, value); michael@0: } michael@0: michael@0: MDefinition *getArgsObject() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: size_t argno() const { michael@0: return argno_; michael@0: } michael@0: michael@0: MDefinition *getValue() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::Any); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MRunOncePrologue michael@0: : public MNullaryInstruction michael@0: { michael@0: protected: michael@0: MRunOncePrologue() michael@0: { michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RunOncePrologue) michael@0: michael@0: static MRunOncePrologue *New(TempAllocator &alloc) { michael@0: return new(alloc) MRunOncePrologue(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Given a MIRType_Value A and a MIRType_Object B: michael@0: // If the Value may be safely unboxed to an Object, return Object(A). michael@0: // Otherwise, return B. michael@0: // Used to implement return behavior for inlined constructors. michael@0: class MReturnFromCtor michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: MReturnFromCtor(MDefinition *value, MDefinition *object) { michael@0: setOperand(0, value); michael@0: setOperand(1, object); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ReturnFromCtor) michael@0: static MReturnFromCtor *New(TempAllocator &alloc, MDefinition *value, MDefinition *object) michael@0: { michael@0: return new(alloc) MReturnFromCtor(value, object); michael@0: } michael@0: michael@0: MDefinition *getValue() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *getObject() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: // Converts a primitive (either typed or untyped) to a double. If the input is michael@0: // not primitive at runtime, a bailout occurs. michael@0: class MToDouble michael@0: : public MUnaryInstruction, michael@0: public ToDoublePolicy michael@0: { michael@0: public: michael@0: // Types of values which can be converted. michael@0: enum ConversionKind { michael@0: NonStringPrimitives, michael@0: NonNullNonStringPrimitives, michael@0: NumbersOnly michael@0: }; michael@0: michael@0: private: michael@0: ConversionKind conversion_; michael@0: michael@0: MToDouble(MDefinition *def, ConversionKind conversion = NonStringPrimitives) michael@0: : MUnaryInstruction(def), conversion_(conversion) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: michael@0: // An object might have "valueOf", which means it is effectful. michael@0: if (def->mightBeType(MIRType_Object)) michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ToDouble) michael@0: static MToDouble *New(TempAllocator &alloc, MDefinition *def, michael@0: ConversionKind conversion = NonStringPrimitives) michael@0: { michael@0: return new(alloc) MToDouble(def, conversion); michael@0: } michael@0: static MToDouble *NewAsmJS(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MToDouble(def); michael@0: } michael@0: michael@0: ConversionKind conversion() const { michael@0: return conversion_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { return true; } michael@0: #endif michael@0: }; michael@0: michael@0: // Converts a primitive (either typed or untyped) to a float32. If the input is michael@0: // not primitive at runtime, a bailout occurs. michael@0: class MToFloat32 michael@0: : public MUnaryInstruction, michael@0: public ToDoublePolicy michael@0: { michael@0: public: michael@0: // Types of values which can be converted. michael@0: enum ConversionKind { michael@0: NonStringPrimitives, michael@0: NonNullNonStringPrimitives, michael@0: NumbersOnly michael@0: }; michael@0: michael@0: protected: michael@0: ConversionKind conversion_; michael@0: michael@0: MToFloat32(MDefinition *def, ConversionKind conversion) michael@0: : MUnaryInstruction(def), conversion_(conversion) michael@0: { michael@0: setResultType(MIRType_Float32); michael@0: setMovable(); michael@0: michael@0: // An object might have "valueOf", which means it is effectful. michael@0: if (def->mightBeType(MIRType_Object)) michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ToFloat32) michael@0: static MToFloat32 *New(TempAllocator &alloc, MDefinition *def, michael@0: ConversionKind conversion = NonStringPrimitives) michael@0: { michael@0: return new(alloc) MToFloat32(def, conversion); michael@0: } michael@0: static MToFloat32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MToFloat32(def, NonStringPrimitives); michael@0: } michael@0: michael@0: ConversionKind conversion() const { michael@0: return conversion_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { return true; } michael@0: bool canProduceFloat32() const { return true; } michael@0: }; michael@0: michael@0: // Converts a uint32 to a double (coming from asm.js). michael@0: class MAsmJSUnsignedToDouble michael@0: : public MUnaryInstruction michael@0: { michael@0: MAsmJSUnsignedToDouble(MDefinition *def) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSUnsignedToDouble); michael@0: static MAsmJSUnsignedToDouble *NewAsmJS(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MAsmJSUnsignedToDouble(def); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Converts a uint32 to a float32 (coming from asm.js). michael@0: class MAsmJSUnsignedToFloat32 michael@0: : public MUnaryInstruction michael@0: { michael@0: MAsmJSUnsignedToFloat32(MDefinition *def) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: setResultType(MIRType_Float32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSUnsignedToFloat32); michael@0: static MAsmJSUnsignedToFloat32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MAsmJSUnsignedToFloat32(def); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool canProduceFloat32() const { return true; } michael@0: }; michael@0: michael@0: // Converts a primitive (either typed or untyped) to an int32. If the input is michael@0: // not primitive at runtime, a bailout occurs. If the input cannot be converted michael@0: // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. michael@0: class MToInt32 michael@0: : public MUnaryInstruction, michael@0: public ToInt32Policy michael@0: { michael@0: bool canBeNegativeZero_; michael@0: MacroAssembler::IntConversionInputKind conversion_; michael@0: michael@0: MToInt32(MDefinition *def, MacroAssembler::IntConversionInputKind conversion) michael@0: : MUnaryInstruction(def), michael@0: canBeNegativeZero_(true), michael@0: conversion_(conversion) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: michael@0: // An object might have "valueOf", which means it is effectful. michael@0: if (def->mightBeType(MIRType_Object)) michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ToInt32) michael@0: static MToInt32 *New(TempAllocator &alloc, MDefinition *def, michael@0: MacroAssembler::IntConversionInputKind conversion = michael@0: MacroAssembler::IntConversion_Any) michael@0: { michael@0: return new(alloc) MToInt32(def, conversion); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: // this only has backwards information flow. michael@0: void analyzeEdgeCasesBackward(); michael@0: michael@0: bool canBeNegativeZero() const { michael@0: return canBeNegativeZero_; michael@0: } michael@0: void setCanBeNegativeZero(bool negativeZero) { michael@0: canBeNegativeZero_ = negativeZero; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MacroAssembler::IntConversionInputKind conversion() const { michael@0: return conversion_; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { return true; } michael@0: #endif michael@0: }; michael@0: michael@0: // Converts a value or typed input to a truncated int32, for use with bitwise michael@0: // operations. This is an infallible ValueToECMAInt32. michael@0: class MTruncateToInt32 : public MUnaryInstruction michael@0: { michael@0: MTruncateToInt32(MDefinition *def) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: michael@0: // An object might have "valueOf", which means it is effectful. michael@0: if (def->mightBeType(MIRType_Object)) michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TruncateToInt32) michael@0: static MTruncateToInt32 *New(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MTruncateToInt32(def); michael@0: } michael@0: static MTruncateToInt32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MTruncateToInt32(def); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool isOperandTruncated(size_t index) const; michael@0: # ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: // Converts any type to a string michael@0: class MToString : public MUnaryInstruction michael@0: { michael@0: MToString(MDefinition *def) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: // Converting an object to a string might be effectful. michael@0: JS_ASSERT(!def->mightBeType(MIRType_Object)); michael@0: michael@0: // NOP michael@0: JS_ASSERT(def->type() != MIRType_String); michael@0: michael@0: setResultType(MIRType_String); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ToString) michael@0: static MToString *New(TempAllocator &alloc, MDefinition *def) michael@0: { michael@0: return new(alloc) MToString(def); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: JS_ASSERT(!input()->mightBeType(MIRType_Object)); michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MBitNot michael@0: : public MUnaryInstruction, michael@0: public BitwisePolicy michael@0: { michael@0: protected: michael@0: MBitNot(MDefinition *input) michael@0: : MUnaryInstruction(input) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BitNot) michael@0: static MBitNot *New(TempAllocator &alloc, MDefinition *input); michael@0: static MBitNot *NewAsmJS(TempAllocator &alloc, MDefinition *input); michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void infer(); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: if (specialization_ == MIRType_None) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MTypeOf michael@0: : public MUnaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: MIRType inputType_; michael@0: bool inputMaybeCallableOrEmulatesUndefined_; michael@0: michael@0: MTypeOf(MDefinition *def, MIRType inputType) michael@0: : MUnaryInstruction(def), inputType_(inputType), michael@0: inputMaybeCallableOrEmulatesUndefined_(true) michael@0: { michael@0: setResultType(MIRType_String); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypeOf) michael@0: michael@0: static MTypeOf *New(TempAllocator &alloc, MDefinition *def, MIRType inputType) { michael@0: return new(alloc) MTypeOf(def, inputType); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MIRType inputType() const { michael@0: return inputType_; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void infer(); michael@0: michael@0: bool inputMaybeCallableOrEmulatesUndefined() const { michael@0: return inputMaybeCallableOrEmulatesUndefined_; michael@0: } michael@0: void markInputNotCallableOrEmulatesUndefined() { michael@0: inputMaybeCallableOrEmulatesUndefined_ = false; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MToId michael@0: : public MBinaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: MToId(MDefinition *object, MDefinition *index) michael@0: : MBinaryInstruction(object, index) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ToId) michael@0: michael@0: static MToId *New(TempAllocator &alloc, MDefinition *object, MDefinition *index) { michael@0: return new(alloc) MToId(object, index); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MBinaryBitwiseInstruction michael@0: : public MBinaryInstruction, michael@0: public BitwisePolicy michael@0: { michael@0: protected: michael@0: MBinaryBitwiseInstruction(MDefinition *left, MDefinition *right) michael@0: : MBinaryInstruction(left, right) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: void specializeAsInt32(); michael@0: michael@0: public: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: MDefinition *foldUnnecessaryBitop(); michael@0: virtual MDefinition *foldIfZero(size_t operand) = 0; michael@0: virtual MDefinition *foldIfNegOne(size_t operand) = 0; michael@0: virtual MDefinition *foldIfEqual() = 0; michael@0: virtual void infer(BaselineInspector *inspector, jsbytecode *pc); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return binaryCongruentTo(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: if (specialization_ >= MIRType_Object) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool isOperandTruncated(size_t index) const; michael@0: }; michael@0: michael@0: class MBitAnd : public MBinaryBitwiseInstruction michael@0: { michael@0: MBitAnd(MDefinition *left, MDefinition *right) michael@0: : MBinaryBitwiseInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BitAnd) michael@0: static MBitAnd *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MBitAnd *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: return getOperand(operand); // 0 & x => 0; michael@0: } michael@0: MDefinition *foldIfNegOne(size_t operand) { michael@0: return getOperand(1 - operand); // x & -1 => x michael@0: } michael@0: MDefinition *foldIfEqual() { michael@0: return getOperand(0); // x & x => x; michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MBitOr : public MBinaryBitwiseInstruction michael@0: { michael@0: MBitOr(MDefinition *left, MDefinition *right) michael@0: : MBinaryBitwiseInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BitOr) michael@0: static MBitOr *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MBitOr *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th michael@0: } michael@0: MDefinition *foldIfNegOne(size_t operand) { michael@0: return getOperand(operand); // x | -1 => -1 michael@0: } michael@0: MDefinition *foldIfEqual() { michael@0: return getOperand(0); // x | x => x michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MBitXor : public MBinaryBitwiseInstruction michael@0: { michael@0: MBitXor(MDefinition *left, MDefinition *right) michael@0: : MBinaryBitwiseInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BitXor) michael@0: static MBitXor *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MBitXor *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: return getOperand(1 - operand); // 0 ^ x => x michael@0: } michael@0: MDefinition *foldIfNegOne(size_t operand) { michael@0: return this; michael@0: } michael@0: MDefinition *foldIfEqual() { michael@0: return this; michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MShiftInstruction michael@0: : public MBinaryBitwiseInstruction michael@0: { michael@0: protected: michael@0: MShiftInstruction(MDefinition *left, MDefinition *right) michael@0: : MBinaryBitwiseInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: MDefinition *foldIfNegOne(size_t operand) { michael@0: return this; michael@0: } michael@0: MDefinition *foldIfEqual() { michael@0: return this; michael@0: } michael@0: virtual void infer(BaselineInspector *inspector, jsbytecode *pc); michael@0: }; michael@0: michael@0: class MLsh : public MShiftInstruction michael@0: { michael@0: MLsh(MDefinition *left, MDefinition *right) michael@0: : MShiftInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Lsh) michael@0: static MLsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MLsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: // 0 << x => 0 michael@0: // x << 0 => x michael@0: return getOperand(0); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MRsh : public MShiftInstruction michael@0: { michael@0: MRsh(MDefinition *left, MDefinition *right) michael@0: : MShiftInstruction(left, right) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Rsh) michael@0: static MRsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MRsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: // 0 >> x => 0 michael@0: // x >> 0 => x michael@0: return getOperand(0); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MUrsh : public MShiftInstruction michael@0: { michael@0: bool bailoutsDisabled_; michael@0: michael@0: MUrsh(MDefinition *left, MDefinition *right) michael@0: : MShiftInstruction(left, right), michael@0: bailoutsDisabled_(false) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Ursh) michael@0: static MUrsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: static MUrsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); michael@0: michael@0: MDefinition *foldIfZero(size_t operand) { michael@0: // 0 >>> x => 0 michael@0: if (operand == 0) michael@0: return getOperand(0); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void infer(BaselineInspector *inspector, jsbytecode *pc); michael@0: michael@0: bool bailoutsDisabled() const { michael@0: return bailoutsDisabled_; michael@0: } michael@0: michael@0: bool fallible() const; michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: class MBinaryArithInstruction michael@0: : public MBinaryInstruction, michael@0: public ArithPolicy michael@0: { michael@0: // Implicit truncate flag is set by the truncate backward range analysis michael@0: // optimization phase, and by asm.js pre-processing. It is used in michael@0: // NeedNegativeZeroCheck to check if the result of a multiplication needs to michael@0: // produce -0 double value, and for avoiding overflow checks. michael@0: michael@0: // This optimization happens when the multiplication cannot be truncated michael@0: // even if all uses are truncating its result, such as when the range michael@0: // analysis detect a precision loss in the multiplication. michael@0: bool implicitTruncate_; michael@0: michael@0: void inferFallback(BaselineInspector *inspector, jsbytecode *pc); michael@0: michael@0: public: michael@0: MBinaryArithInstruction(MDefinition *left, MDefinition *right) michael@0: : MBinaryInstruction(left, right), michael@0: implicitTruncate_(false) michael@0: { michael@0: setMovable(); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MIRType specialization() const { michael@0: return specialization_; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: virtual double getIdentity() = 0; michael@0: michael@0: void infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc); michael@0: michael@0: void setInt32() { michael@0: specialization_ = MIRType_Int32; michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: virtual void trySpecializeFloat32(TempAllocator &alloc); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return binaryCongruentTo(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: if (specialization_ >= MIRType_Object) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool isTruncated() const { michael@0: return implicitTruncate_; michael@0: } michael@0: void setTruncated(bool truncate) { michael@0: implicitTruncate_ = truncate; michael@0: } michael@0: }; michael@0: michael@0: class MMinMax michael@0: : public MBinaryInstruction, michael@0: public ArithPolicy michael@0: { michael@0: bool isMax_; michael@0: michael@0: MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax) michael@0: : MBinaryInstruction(left, right), michael@0: isMax_(isMax) michael@0: { michael@0: JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); michael@0: setResultType(type); michael@0: setMovable(); michael@0: specialization_ = type; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(MinMax) michael@0: static MMinMax *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type, michael@0: bool isMax) michael@0: { michael@0: return new(alloc) MMinMax(left, right, type, isMax); michael@0: } michael@0: michael@0: bool isMax() const { michael@0: return isMax_; michael@0: } michael@0: MIRType specialization() const { michael@0: return specialization_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isMinMax()) michael@0: return false; michael@0: if (isMax() != ins->toMinMax()->isMax()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MAbs michael@0: : public MUnaryInstruction, michael@0: public ArithPolicy michael@0: { michael@0: bool implicitTruncate_; michael@0: michael@0: MAbs(MDefinition *num, MIRType type) michael@0: : MUnaryInstruction(num), michael@0: implicitTruncate_(false) michael@0: { michael@0: JS_ASSERT(IsNumberType(type)); michael@0: setResultType(type); michael@0: setMovable(); michael@0: specialization_ = type; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Abs) michael@0: static MAbs *New(TempAllocator &alloc, MDefinition *num, MIRType type) { michael@0: return new(alloc) MAbs(num, type); michael@0: } michael@0: static MAbs *NewAsmJS(TempAllocator &alloc, MDefinition *num, MIRType type) { michael@0: MAbs *ins = new(alloc) MAbs(num, type); michael@0: if (type == MIRType_Int32) michael@0: ins->implicitTruncate_ = true; michael@0: return ins; michael@0: } michael@0: MDefinition *num() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: bool fallible() const; michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: bool isFloat32Commutative() const { return true; } michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Inline implementation of Math.sqrt(). michael@0: class MSqrt michael@0: : public MUnaryInstruction, michael@0: public FloatingPointPolicy<0> michael@0: { michael@0: MSqrt(MDefinition *num, MIRType type) michael@0: : MUnaryInstruction(num) michael@0: { michael@0: setResultType(type); michael@0: setPolicyType(type); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Sqrt) michael@0: static MSqrt *New(TempAllocator &alloc, MDefinition *num) { michael@0: return new(alloc) MSqrt(num, MIRType_Double); michael@0: } michael@0: static MSqrt *NewAsmJS(TempAllocator &alloc, MDefinition *num, MIRType type) { michael@0: JS_ASSERT(IsFloatingPointType(type)); michael@0: return new(alloc) MSqrt(num, type); michael@0: } michael@0: MDefinition *num() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: michael@0: bool isFloat32Commutative() const { return true; } michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Inline implementation of atan2 (arctangent of y/x). michael@0: class MAtan2 michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, DoublePolicy<1> > michael@0: { michael@0: MAtan2(MDefinition *y, MDefinition *x) michael@0: : MBinaryInstruction(y, x) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Atan2) michael@0: static MAtan2 *New(TempAllocator &alloc, MDefinition *y, MDefinition *x) { michael@0: return new(alloc) MAtan2(y, x); michael@0: } michael@0: michael@0: MDefinition *y() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *x() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Inline implementation of Math.hypot(). michael@0: class MHypot michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, DoublePolicy<1> > michael@0: { michael@0: MHypot(MDefinition *y, MDefinition *x) michael@0: : MBinaryInstruction(x, y) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Hypot) michael@0: static MHypot *New(TempAllocator &alloc, MDefinition *x, MDefinition *y) { michael@0: return new(alloc) MHypot(y, x); michael@0: } michael@0: michael@0: MDefinition *x() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *y() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Inline implementation of Math.pow(). michael@0: class MPow michael@0: : public MBinaryInstruction, michael@0: public PowPolicy michael@0: { michael@0: MPow(MDefinition *input, MDefinition *power, MIRType powerType) michael@0: : MBinaryInstruction(input, power), michael@0: PowPolicy(powerType) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Pow) michael@0: static MPow *New(TempAllocator &alloc, MDefinition *input, MDefinition *power, michael@0: MIRType powerType) michael@0: { michael@0: JS_ASSERT(powerType == MIRType_Double || powerType == MIRType_Int32); michael@0: return new(alloc) MPow(input, power, powerType); michael@0: } michael@0: michael@0: MDefinition *input() const { michael@0: return lhs(); michael@0: } michael@0: MDefinition *power() const { michael@0: return rhs(); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x). michael@0: class MPowHalf michael@0: : public MUnaryInstruction, michael@0: public DoublePolicy<0> michael@0: { michael@0: bool operandIsNeverNegativeInfinity_; michael@0: bool operandIsNeverNegativeZero_; michael@0: bool operandIsNeverNaN_; michael@0: michael@0: MPowHalf(MDefinition *input) michael@0: : MUnaryInstruction(input), michael@0: operandIsNeverNegativeInfinity_(false), michael@0: operandIsNeverNegativeZero_(false), michael@0: operandIsNeverNaN_(false) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(PowHalf) michael@0: static MPowHalf *New(TempAllocator &alloc, MDefinition *input) { michael@0: return new(alloc) MPowHalf(input); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: bool operandIsNeverNegativeInfinity() const { michael@0: return operandIsNeverNegativeInfinity_; michael@0: } michael@0: bool operandIsNeverNegativeZero() const { michael@0: return operandIsNeverNegativeZero_; michael@0: } michael@0: bool operandIsNeverNaN() const { michael@0: return operandIsNeverNaN_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: // Inline implementation of Math.random(). michael@0: class MRandom : public MNullaryInstruction michael@0: { michael@0: MRandom() michael@0: { michael@0: setResultType(MIRType_Double); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Random) michael@0: static MRandom *New(TempAllocator &alloc) { michael@0: return new(alloc) MRandom; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MMathFunction michael@0: : public MUnaryInstruction, michael@0: public FloatingPointPolicy<0> michael@0: { michael@0: public: michael@0: enum Function { michael@0: Log, michael@0: Sin, michael@0: Cos, michael@0: Exp, michael@0: Tan, michael@0: ACos, michael@0: ASin, michael@0: ATan, michael@0: Log10, michael@0: Log2, michael@0: Log1P, michael@0: ExpM1, michael@0: CosH, michael@0: SinH, michael@0: TanH, michael@0: ACosH, michael@0: ASinH, michael@0: ATanH, michael@0: Sign, michael@0: Trunc, michael@0: Cbrt, michael@0: Floor, michael@0: Ceil, michael@0: Round michael@0: }; michael@0: michael@0: private: michael@0: Function function_; michael@0: const MathCache *cache_; michael@0: michael@0: MMathFunction(MDefinition *input, Function function, const MathCache *cache) michael@0: : MUnaryInstruction(input), function_(function), cache_(cache) michael@0: { michael@0: setResultType(MIRType_Double); michael@0: setPolicyType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(MathFunction) michael@0: michael@0: // A nullptr cache means this function will neither access nor update the cache. michael@0: static MMathFunction *New(TempAllocator &alloc, MDefinition *input, Function function, michael@0: const MathCache *cache) michael@0: { michael@0: return new(alloc) MMathFunction(input, function, cache); michael@0: } michael@0: Function function() const { michael@0: return function_; michael@0: } michael@0: const MathCache *cache() const { michael@0: return cache_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isMathFunction()) michael@0: return false; michael@0: if (ins->toMathFunction()->function() != function()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: static const char *FunctionName(Function function); michael@0: michael@0: bool isFloat32Commutative() const { michael@0: return function_ == Floor || function_ == Ceil || function_ == Round; michael@0: } michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MAdd : public MBinaryArithInstruction michael@0: { michael@0: // Is this instruction really an int at heart? michael@0: MAdd(MDefinition *left, MDefinition *right) michael@0: : MBinaryArithInstruction(left, right) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Add) michael@0: static MAdd *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MAdd(left, right); michael@0: } michael@0: michael@0: static MAdd *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, michael@0: MIRType type) michael@0: { michael@0: MAdd *add = new(alloc) MAdd(left, right); michael@0: add->specialization_ = type; michael@0: add->setResultType(type); michael@0: if (type == MIRType_Int32) { michael@0: add->setTruncated(true); michael@0: add->setCommutative(); michael@0: } michael@0: return add; michael@0: } michael@0: michael@0: bool isFloat32Commutative() const { return true; } michael@0: michael@0: double getIdentity() { michael@0: return 0; michael@0: } michael@0: michael@0: bool fallible() const; michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: bool isOperandTruncated(size_t index) const; michael@0: }; michael@0: michael@0: class MSub : public MBinaryArithInstruction michael@0: { michael@0: MSub(MDefinition *left, MDefinition *right) michael@0: : MBinaryArithInstruction(left, right) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Sub) michael@0: static MSub *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MSub(left, right); michael@0: } michael@0: static MSub *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, michael@0: MIRType type) michael@0: { michael@0: MSub *sub = new(alloc) MSub(left, right); michael@0: sub->specialization_ = type; michael@0: sub->setResultType(type); michael@0: if (type == MIRType_Int32) michael@0: sub->setTruncated(true); michael@0: return sub; michael@0: } michael@0: michael@0: double getIdentity() { michael@0: return 0; michael@0: } michael@0: michael@0: bool isFloat32Commutative() const { return true; } michael@0: michael@0: bool fallible() const; michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: bool isOperandTruncated(size_t index) const; michael@0: }; michael@0: michael@0: class MMul : public MBinaryArithInstruction michael@0: { michael@0: public: michael@0: enum Mode { michael@0: Normal, michael@0: Integer michael@0: }; michael@0: michael@0: private: michael@0: // Annotation the result could be a negative zero michael@0: // and we need to guard this during execution. michael@0: bool canBeNegativeZero_; michael@0: michael@0: Mode mode_; michael@0: michael@0: MMul(MDefinition *left, MDefinition *right, MIRType type, Mode mode) michael@0: : MBinaryArithInstruction(left, right), michael@0: canBeNegativeZero_(true), michael@0: mode_(mode) michael@0: { michael@0: if (mode == Integer) { michael@0: // This implements the required behavior for Math.imul, which michael@0: // can never fail and always truncates its output to int32. michael@0: canBeNegativeZero_ = false; michael@0: setTruncated(true); michael@0: setCommutative(); michael@0: } michael@0: JS_ASSERT_IF(mode != Integer, mode == Normal); michael@0: michael@0: if (type != MIRType_Value) michael@0: specialization_ = type; michael@0: setResultType(type); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Mul) michael@0: static MMul *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MMul(left, right, MIRType_Value, MMul::Normal); michael@0: } michael@0: static MMul *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type, michael@0: Mode mode = Normal) michael@0: { michael@0: return new(alloc) MMul(left, right, type, mode); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void analyzeEdgeCasesForward(); michael@0: void analyzeEdgeCasesBackward(); michael@0: michael@0: double getIdentity() { michael@0: return 1; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isMul()) michael@0: return false; michael@0: michael@0: const MMul *mul = ins->toMul(); michael@0: if (canBeNegativeZero_ != mul->canBeNegativeZero()) michael@0: return false; michael@0: michael@0: if (mode_ != mul->mode()) michael@0: return false; michael@0: michael@0: return binaryCongruentTo(ins); michael@0: } michael@0: michael@0: bool canOverflow() const; michael@0: michael@0: bool canBeNegativeZero() const { michael@0: return canBeNegativeZero_; michael@0: } michael@0: void setCanBeNegativeZero(bool negativeZero) { michael@0: canBeNegativeZero_ = negativeZero; michael@0: } michael@0: michael@0: bool updateForReplacement(MDefinition *ins); michael@0: michael@0: bool fallible() const { michael@0: return canBeNegativeZero_ || canOverflow(); michael@0: } michael@0: michael@0: void setSpecialization(MIRType type) { michael@0: specialization_ = type; michael@0: } michael@0: michael@0: bool isFloat32Commutative() const { return true; } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: Mode mode() const { return mode_; } michael@0: }; michael@0: michael@0: class MDiv : public MBinaryArithInstruction michael@0: { michael@0: bool canBeNegativeZero_; michael@0: bool canBeNegativeOverflow_; michael@0: bool canBeDivideByZero_; michael@0: bool canBeNegativeDividend_; michael@0: bool unsigned_; michael@0: michael@0: // A Division can be truncated in 4 differents ways: michael@0: // 1. Ignore Infinities (x / 0 --> 0). michael@0: // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN) michael@0: // 3. Ignore negative zeros. (-0 --> 0) michael@0: // 4. Ignore remainder. (3 / 4 --> 0) michael@0: // michael@0: // isTruncatedIndirectly is used to represent that we are interested in the michael@0: // truncated result, but only if they it can safely flow in operations which michael@0: // are computed modulo 2^32, such as (2) and (3). michael@0: // michael@0: // A division can return either Infinities (1) or a remainder (4) when both michael@0: // operands are integers. Infinities are not safe, as they would have michael@0: // absorbed other math operations. Remainders are not safe, as multiple can michael@0: // add up to integers. This implies that we need to distinguish between a michael@0: // division which is truncated directly (isTruncated) or which flow into michael@0: // truncated operations (isTruncatedIndirectly). michael@0: bool isTruncatedIndirectly_; michael@0: michael@0: MDiv(MDefinition *left, MDefinition *right, MIRType type) michael@0: : MBinaryArithInstruction(left, right), michael@0: canBeNegativeZero_(true), michael@0: canBeNegativeOverflow_(true), michael@0: canBeDivideByZero_(true), michael@0: canBeNegativeDividend_(true), michael@0: unsigned_(false), michael@0: isTruncatedIndirectly_(false) michael@0: { michael@0: if (type != MIRType_Value) michael@0: specialization_ = type; michael@0: setResultType(type); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Div) michael@0: static MDiv *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MDiv(left, right, MIRType_Value); michael@0: } michael@0: static MDiv *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type) { michael@0: return new(alloc) MDiv(left, right, type); michael@0: } michael@0: static MDiv *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, michael@0: MIRType type, bool unsignd) michael@0: { michael@0: MDiv *div = new(alloc) MDiv(left, right, type); michael@0: div->unsigned_ = unsignd; michael@0: if (type == MIRType_Int32) michael@0: div->setTruncated(true); michael@0: return div; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: void analyzeEdgeCasesForward(); michael@0: void analyzeEdgeCasesBackward(); michael@0: michael@0: double getIdentity() { michael@0: MOZ_ASSUME_UNREACHABLE("not used"); michael@0: } michael@0: michael@0: bool canBeNegativeZero() const { michael@0: return canBeNegativeZero_; michael@0: } michael@0: void setCanBeNegativeZero(bool negativeZero) { michael@0: canBeNegativeZero_ = negativeZero; michael@0: } michael@0: michael@0: bool canBeNegativeOverflow() const { michael@0: return canBeNegativeOverflow_; michael@0: } michael@0: michael@0: bool canBeDivideByZero() const { michael@0: return canBeDivideByZero_; michael@0: } michael@0: michael@0: bool canBeNegativeDividend() const { michael@0: return canBeNegativeDividend_; michael@0: } michael@0: michael@0: bool isUnsigned() const { michael@0: return unsigned_; michael@0: } michael@0: michael@0: bool isTruncatedIndirectly() const { michael@0: return isTruncatedIndirectly_; michael@0: } michael@0: void setTruncatedIndirectly(bool truncate) { michael@0: isTruncatedIndirectly_ = truncate; michael@0: } michael@0: michael@0: bool canTruncateInfinities() const { michael@0: return isTruncated(); michael@0: } michael@0: bool canTruncateRemainder() const { michael@0: return isTruncated(); michael@0: } michael@0: bool canTruncateOverflow() const { michael@0: return isTruncated() || isTruncatedIndirectly(); michael@0: } michael@0: bool canTruncateNegativeZero() const { michael@0: return isTruncated() || isTruncatedIndirectly(); michael@0: } michael@0: michael@0: bool isFloat32Commutative() const { return true; } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool fallible() const; michael@0: bool truncate(); michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: class MMod : public MBinaryArithInstruction michael@0: { michael@0: bool unsigned_; michael@0: bool canBeNegativeDividend_; michael@0: michael@0: MMod(MDefinition *left, MDefinition *right, MIRType type) michael@0: : MBinaryArithInstruction(left, right), michael@0: unsigned_(false), michael@0: canBeNegativeDividend_(true) michael@0: { michael@0: if (type != MIRType_Value) michael@0: specialization_ = type; michael@0: setResultType(type); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Mod) michael@0: static MMod *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MMod(left, right, MIRType_Value); michael@0: } michael@0: static MMod *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, michael@0: MIRType type, bool unsignd) michael@0: { michael@0: MMod *mod = new(alloc) MMod(left, right, type); michael@0: mod->unsigned_ = unsignd; michael@0: if (type == MIRType_Int32) michael@0: mod->setTruncated(true); michael@0: return mod; michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: double getIdentity() { michael@0: MOZ_ASSUME_UNREACHABLE("not used"); michael@0: } michael@0: michael@0: bool canBeNegativeDividend() const { michael@0: JS_ASSERT(specialization_ == MIRType_Int32); michael@0: return canBeNegativeDividend_; michael@0: } michael@0: bool canBeDivideByZero() const; michael@0: bool canBePowerOfTwoDivisor() const; michael@0: michael@0: bool isUnsigned() const { michael@0: return unsigned_; michael@0: } michael@0: michael@0: bool fallible() const; michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: class MConcat michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ConvertToStringPolicy<1>> michael@0: { michael@0: MConcat(MDefinition *left, MDefinition *right) michael@0: : MBinaryInstruction(left, right) michael@0: { michael@0: // At least one input should be definitely string michael@0: JS_ASSERT(left->type() == MIRType_String || right->type() == MIRType_String); michael@0: michael@0: setMovable(); michael@0: setResultType(MIRType_String); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Concat) michael@0: static MConcat *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MConcat(left, right); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MConcatPar michael@0: : public MTernaryInstruction michael@0: { michael@0: MConcatPar(MDefinition *cx, MDefinition *left, MDefinition *right) michael@0: : MTernaryInstruction(cx, left, right) michael@0: { michael@0: // Type analysis has already run, before replacing with the parallel michael@0: // variant. michael@0: JS_ASSERT(left->type() == MIRType_String && right->type() == MIRType_String); michael@0: michael@0: setMovable(); michael@0: setResultType(MIRType_String); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ConcatPar) michael@0: michael@0: static MConcatPar *New(TempAllocator &alloc, MDefinition *cx, MConcat *concat) { michael@0: return new(alloc) MConcatPar(cx, concat->lhs(), concat->rhs()); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *lhs() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *rhs() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MCharCodeAt michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, IntPolicy<1> > michael@0: { michael@0: MCharCodeAt(MDefinition *str, MDefinition *index) michael@0: : MBinaryInstruction(str, index) michael@0: { michael@0: setMovable(); michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CharCodeAt) michael@0: michael@0: static MCharCodeAt *New(TempAllocator &alloc, MDefinition *str, MDefinition *index) { michael@0: return new(alloc) MCharCodeAt(str, index); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: virtual AliasSet getAliasSet() const { michael@0: // Strings are immutable, so there is no implicit dependency. michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MFromCharCode michael@0: : public MUnaryInstruction, michael@0: public IntPolicy<0> michael@0: { michael@0: MFromCharCode(MDefinition *code) michael@0: : MUnaryInstruction(code) michael@0: { michael@0: setMovable(); michael@0: setResultType(MIRType_String); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(FromCharCode) michael@0: michael@0: static MFromCharCode *New(TempAllocator &alloc, MDefinition *code) { michael@0: return new(alloc) MFromCharCode(code); michael@0: } michael@0: michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MStringSplit michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, StringPolicy<1> > michael@0: { michael@0: types::TypeObject *typeObject_; michael@0: michael@0: MStringSplit(types::CompilerConstraintList *constraints, MDefinition *string, MDefinition *sep, michael@0: JSObject *templateObject) michael@0: : MBinaryInstruction(string, sep), michael@0: typeObject_(templateObject->type()) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StringSplit) michael@0: michael@0: static MStringSplit *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MDefinition *string, MDefinition *sep, michael@0: JSObject *templateObject) michael@0: { michael@0: return new(alloc) MStringSplit(constraints, string, sep, templateObject); michael@0: } michael@0: types::TypeObject *typeObject() const { michael@0: return typeObject_; michael@0: } michael@0: MDefinition *string() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *separator() const { michael@0: return getOperand(1); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: virtual AliasSet getAliasSet() const { michael@0: // Although this instruction returns a new array, we don't have to mark michael@0: // it as store instruction, see also MNewArray. michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Returns an object to use as |this| value. See also ComputeThis and michael@0: // BoxNonStrictThis in Interpreter.h. michael@0: class MComputeThis michael@0: : public MUnaryInstruction, michael@0: public BoxPolicy<0> michael@0: { michael@0: MComputeThis(MDefinition *def) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ComputeThis) michael@0: michael@0: static MComputeThis *New(TempAllocator &alloc, MDefinition *def) { michael@0: return new(alloc) MComputeThis(def); michael@0: } michael@0: michael@0: MDefinition *input() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: michael@0: // Note: don't override getAliasSet: the thisObject hook can be michael@0: // effectful. michael@0: }; michael@0: michael@0: // Load an arrow function's |this| value. michael@0: class MLoadArrowThis michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MLoadArrowThis(MDefinition *callee) michael@0: : MUnaryInstruction(callee) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadArrowThis) michael@0: michael@0: static MLoadArrowThis *New(TempAllocator &alloc, MDefinition *callee) { michael@0: return new(alloc) MLoadArrowThis(callee); michael@0: } michael@0: MDefinition *callee() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // An arrow function's lexical |this| value is immutable. michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode michael@0: { michael@0: js::Vector inputs_; michael@0: michael@0: uint32_t slot_; michael@0: bool hasBackedgeType_; michael@0: bool triedToSpecialize_; michael@0: bool isIterator_; michael@0: bool canProduceFloat32_; michael@0: bool canConsumeFloat32_; michael@0: michael@0: #if DEBUG michael@0: bool specialized_; michael@0: uint32_t capacity_; michael@0: #endif michael@0: michael@0: protected: michael@0: MUse *getUseFor(size_t index) { michael@0: return &inputs_[index]; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Phi) michael@0: michael@0: MPhi(TempAllocator &alloc, uint32_t slot, MIRType resultType) michael@0: : inputs_(alloc), michael@0: slot_(slot), michael@0: hasBackedgeType_(false), michael@0: triedToSpecialize_(false), michael@0: isIterator_(false), michael@0: canProduceFloat32_(false), michael@0: canConsumeFloat32_(false) michael@0: #if DEBUG michael@0: , specialized_(false) michael@0: , capacity_(0) michael@0: #endif michael@0: { michael@0: setResultType(resultType); michael@0: } michael@0: michael@0: static MPhi *New(TempAllocator &alloc, uint32_t slot, MIRType resultType = MIRType_Value) { michael@0: return new(alloc) MPhi(alloc, slot, resultType); michael@0: } michael@0: michael@0: void setOperand(size_t index, MDefinition *operand) { michael@0: // Note: after the initial IonBuilder pass, it is OK to change phi michael@0: // operands such that they do not include the type sets of their michael@0: // operands. This can arise during e.g. value numbering, where michael@0: // definitions producing the same value may have different type sets. michael@0: JS_ASSERT(index < numOperands()); michael@0: inputs_[index].set(operand, this, index); michael@0: operand->addUse(&inputs_[index]); michael@0: } michael@0: michael@0: void removeOperand(size_t index); michael@0: michael@0: MDefinition *getOperand(size_t index) const { michael@0: return inputs_[index].producer(); michael@0: } michael@0: size_t numOperands() const { michael@0: return inputs_.length(); michael@0: } michael@0: uint32_t slot() const { michael@0: return slot_; michael@0: } michael@0: bool hasBackedgeType() const { michael@0: return hasBackedgeType_; michael@0: } michael@0: bool triedToSpecialize() const { michael@0: return triedToSpecialize_; michael@0: } michael@0: void specialize(MIRType type) { michael@0: triedToSpecialize_ = true; michael@0: setResultType(type); michael@0: } michael@0: bool specializeType(); michael@0: michael@0: // Whether this phi's type already includes information for def. michael@0: bool typeIncludes(MDefinition *def); michael@0: michael@0: // Add types for this phi which speculate about new inputs that may come in michael@0: // via a loop backedge. michael@0: bool addBackedgeType(MIRType type, types::TemporaryTypeSet *typeSet); michael@0: michael@0: // Initializes the operands vector to the given capacity, michael@0: // permitting use of addInput() instead of addInputSlow(). michael@0: bool reserveLength(size_t length); michael@0: michael@0: // Use only if capacity has been reserved by reserveLength michael@0: void addInput(MDefinition *ins); michael@0: michael@0: // Appends a new input to the input vector. May call realloc_(). michael@0: // Prefer reserveLength() and addInput() instead, where possible. michael@0: bool addInputSlow(MDefinition *ins, bool *ptypeChange = nullptr); michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: bool congruentTo(const MDefinition *ins) const; michael@0: michael@0: bool isIterator() const { michael@0: return isIterator_; michael@0: } michael@0: void setIterator() { michael@0: isIterator_ = true; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: michael@0: MDefinition *operandIfRedundant() { michael@0: // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)), michael@0: // returns the operand that it will always be equal to (a, in michael@0: // those two cases). michael@0: MDefinition *first = getOperand(0); michael@0: for (size_t i = 1, e = numOperands(); i < e; i++) { michael@0: if (getOperand(i) != first && getOperand(i) != this) michael@0: return nullptr; michael@0: } michael@0: return first; michael@0: } michael@0: michael@0: bool canProduceFloat32() const { michael@0: return canProduceFloat32_; michael@0: } michael@0: michael@0: void setCanProduceFloat32(bool can) { michael@0: canProduceFloat32_ = can; michael@0: } michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { michael@0: return canConsumeFloat32_; michael@0: } michael@0: michael@0: void setCanConsumeFloat32(bool can) { michael@0: canConsumeFloat32_ = can; michael@0: } michael@0: }; michael@0: michael@0: // The goal of a Beta node is to split a def at a conditionally taken michael@0: // branch, so that uses dominated by it have a different name. michael@0: class MBeta : public MUnaryInstruction michael@0: { michael@0: private: michael@0: // This is the range induced by a comparison and branch in a preceding michael@0: // block. Note that this does not reflect any range constraints from michael@0: // the input value itself, so this value may differ from the range() michael@0: // range after it is computed. michael@0: const Range *comparison_; michael@0: michael@0: MBeta(MDefinition *val, const Range *comp) michael@0: : MUnaryInstruction(val), michael@0: comparison_(comp) michael@0: { michael@0: setResultType(val->type()); michael@0: setResultTypeSet(val->resultTypeSet()); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Beta) michael@0: void printOpcode(FILE *fp) const; michael@0: static MBeta *New(TempAllocator &alloc, MDefinition *val, const Range *comp) michael@0: { michael@0: return new(alloc) MBeta(val, comp); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // MIR representation of a Value on the OSR BaselineFrame. michael@0: // The Value is indexed off of OsrFrameReg. michael@0: class MOsrValue : public MUnaryInstruction michael@0: { michael@0: private: michael@0: ptrdiff_t frameOffset_; michael@0: michael@0: MOsrValue(MOsrEntry *entry, ptrdiff_t frameOffset) michael@0: : MUnaryInstruction(entry), michael@0: frameOffset_(frameOffset) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(OsrValue) michael@0: static MOsrValue *New(TempAllocator &alloc, MOsrEntry *entry, ptrdiff_t frameOffset) { michael@0: return new(alloc) MOsrValue(entry, frameOffset); michael@0: } michael@0: michael@0: ptrdiff_t frameOffset() const { michael@0: return frameOffset_; michael@0: } michael@0: michael@0: MOsrEntry *entry() { michael@0: return getOperand(0)->toOsrEntry(); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame. michael@0: // The pointer is indexed off of OsrFrameReg. michael@0: class MOsrScopeChain : public MUnaryInstruction michael@0: { michael@0: private: michael@0: MOsrScopeChain(MOsrEntry *entry) michael@0: : MUnaryInstruction(entry) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(OsrScopeChain) michael@0: static MOsrScopeChain *New(TempAllocator &alloc, MOsrEntry *entry) { michael@0: return new(alloc) MOsrScopeChain(entry); michael@0: } michael@0: michael@0: MOsrEntry *entry() { michael@0: return getOperand(0)->toOsrEntry(); michael@0: } michael@0: }; michael@0: michael@0: // MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame. michael@0: // The pointer is indexed off of OsrFrameReg. michael@0: class MOsrArgumentsObject : public MUnaryInstruction michael@0: { michael@0: private: michael@0: MOsrArgumentsObject(MOsrEntry *entry) michael@0: : MUnaryInstruction(entry) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(OsrArgumentsObject) michael@0: static MOsrArgumentsObject *New(TempAllocator &alloc, MOsrEntry *entry) { michael@0: return new(alloc) MOsrArgumentsObject(entry); michael@0: } michael@0: michael@0: MOsrEntry *entry() { michael@0: return getOperand(0)->toOsrEntry(); michael@0: } michael@0: }; michael@0: michael@0: // MIR representation of the return value on the OSR BaselineFrame. michael@0: // The Value is indexed off of OsrFrameReg. michael@0: class MOsrReturnValue : public MUnaryInstruction michael@0: { michael@0: private: michael@0: MOsrReturnValue(MOsrEntry *entry) michael@0: : MUnaryInstruction(entry) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(OsrReturnValue) michael@0: static MOsrReturnValue *New(TempAllocator &alloc, MOsrEntry *entry) { michael@0: return new(alloc) MOsrReturnValue(entry); michael@0: } michael@0: michael@0: MOsrEntry *entry() { michael@0: return getOperand(0)->toOsrEntry(); michael@0: } michael@0: }; michael@0: michael@0: // Check the current frame for over-recursion past the global stack limit. michael@0: class MCheckOverRecursed : public MNullaryInstruction michael@0: { michael@0: public: michael@0: INSTRUCTION_HEADER(CheckOverRecursed) michael@0: michael@0: static MCheckOverRecursed *New(TempAllocator &alloc) { michael@0: return new(alloc) MCheckOverRecursed(); michael@0: } michael@0: }; michael@0: michael@0: // Check the current frame for over-recursion past the global stack limit. michael@0: // Uses the per-thread recursion limit. michael@0: class MCheckOverRecursedPar : public MUnaryInstruction michael@0: { michael@0: MCheckOverRecursedPar(MDefinition *cx) michael@0: : MUnaryInstruction(cx) michael@0: { michael@0: setResultType(MIRType_None); michael@0: setGuard(); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CheckOverRecursedPar); michael@0: michael@0: static MCheckOverRecursedPar *New(TempAllocator &alloc, MDefinition *cx) { michael@0: return new(alloc) MCheckOverRecursedPar(cx); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: // Check for an interrupt (or rendezvous) in parallel mode. michael@0: class MInterruptCheckPar : public MUnaryInstruction michael@0: { michael@0: MInterruptCheckPar(MDefinition *cx) michael@0: : MUnaryInstruction(cx) michael@0: { michael@0: setResultType(MIRType_None); michael@0: setGuard(); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InterruptCheckPar); michael@0: michael@0: static MInterruptCheckPar *New(TempAllocator &alloc, MDefinition *cx) { michael@0: return new(alloc) MInterruptCheckPar(cx); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: // Check whether we need to fire the interrupt handler. michael@0: class MInterruptCheck : public MNullaryInstruction michael@0: { michael@0: MInterruptCheck() { michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InterruptCheck) michael@0: michael@0: static MInterruptCheck *New(TempAllocator &alloc) { michael@0: return new(alloc) MInterruptCheck(); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // If not defined, set a global variable to |undefined|. michael@0: class MDefVar : public MUnaryInstruction michael@0: { michael@0: CompilerRootPropertyName name_; // Target name to be defined. michael@0: unsigned attrs_; // Attributes to be set. michael@0: michael@0: private: michael@0: MDefVar(PropertyName *name, unsigned attrs, MDefinition *scopeChain) michael@0: : MUnaryInstruction(scopeChain), michael@0: name_(name), michael@0: attrs_(attrs) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(DefVar) michael@0: michael@0: static MDefVar *New(TempAllocator &alloc, PropertyName *name, unsigned attrs, michael@0: MDefinition *scopeChain) michael@0: { michael@0: return new(alloc) MDefVar(name, attrs, scopeChain); michael@0: } michael@0: michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: unsigned attrs() const { michael@0: return attrs_; michael@0: } michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MDefFun : public MUnaryInstruction michael@0: { michael@0: CompilerRootFunction fun_; michael@0: michael@0: private: michael@0: MDefFun(JSFunction *fun, MDefinition *scopeChain) michael@0: : MUnaryInstruction(scopeChain), michael@0: fun_(fun) michael@0: {} michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(DefFun) michael@0: michael@0: static MDefFun *New(TempAllocator &alloc, JSFunction *fun, MDefinition *scopeChain) { michael@0: return new(alloc) MDefFun(fun, scopeChain); michael@0: } michael@0: michael@0: JSFunction *fun() const { michael@0: return fun_; michael@0: } michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MRegExp : public MNullaryInstruction michael@0: { michael@0: CompilerRoot source_; michael@0: bool mustClone_; michael@0: michael@0: MRegExp(types::CompilerConstraintList *constraints, RegExpObject *source, bool mustClone) michael@0: : source_(source), michael@0: mustClone_(mustClone) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, source)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RegExp) michael@0: michael@0: static MRegExp *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: RegExpObject *source, bool mustClone) michael@0: { michael@0: return new(alloc) MRegExp(constraints, source, mustClone); michael@0: } michael@0: michael@0: bool mustClone() const { michael@0: return mustClone_; michael@0: } michael@0: RegExpObject *source() const { michael@0: return source_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MRegExpExec michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1>> michael@0: { michael@0: private: michael@0: michael@0: MRegExpExec(MDefinition *regexp, MDefinition *string) michael@0: : MBinaryInstruction(string, regexp) michael@0: { michael@0: // May be object or null. michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RegExpExec) michael@0: michael@0: static MRegExpExec *New(TempAllocator &alloc, MDefinition *regexp, MDefinition *string) { michael@0: return new(alloc) MRegExpExec(regexp, string); michael@0: } michael@0: michael@0: MDefinition *string() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *regexp() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MRegExpTest michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ConvertToStringPolicy<0> > michael@0: { michael@0: private: michael@0: michael@0: MRegExpTest(MDefinition *regexp, MDefinition *string) michael@0: : MBinaryInstruction(string, regexp) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RegExpTest) michael@0: michael@0: static MRegExpTest *New(TempAllocator &alloc, MDefinition *regexp, MDefinition *string) { michael@0: return new(alloc) MRegExpTest(regexp, string); michael@0: } michael@0: michael@0: MDefinition *string() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *regexp() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: class MStrReplace michael@0: : public MTernaryInstruction, michael@0: public Mix3Policy, Policy1, StringPolicy<2> > michael@0: { michael@0: protected: michael@0: michael@0: MStrReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) michael@0: : MTernaryInstruction(string, pattern, replacement) michael@0: { michael@0: setMovable(); michael@0: setResultType(MIRType_String); michael@0: } michael@0: michael@0: public: michael@0: michael@0: MDefinition *string() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *pattern() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *replacement() const { michael@0: return getOperand(2); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MRegExpReplace michael@0: : public MStrReplace< ObjectPolicy<1> > michael@0: { michael@0: private: michael@0: michael@0: MRegExpReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) michael@0: : MStrReplace< ObjectPolicy<1> >(string, pattern, replacement) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RegExpReplace); michael@0: michael@0: static MRegExpReplace *New(TempAllocator &alloc, MDefinition *string, MDefinition *pattern, MDefinition *replacement) { michael@0: return new(alloc) MRegExpReplace(string, pattern, replacement); michael@0: } michael@0: }; michael@0: michael@0: class MStringReplace michael@0: : public MStrReplace< StringPolicy<1> > michael@0: { michael@0: private: michael@0: michael@0: MStringReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) michael@0: : MStrReplace< StringPolicy<1> >(string, pattern, replacement) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StringReplace); michael@0: michael@0: static MStringReplace *New(TempAllocator &alloc, MDefinition *string, MDefinition *pattern, MDefinition *replacement) { michael@0: return new(alloc) MStringReplace(string, pattern, replacement); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: struct LambdaFunctionInfo michael@0: { michael@0: // The functions used in lambdas are the canonical original function in michael@0: // the script, and are immutable except for delazification. Record this michael@0: // information while still on the main thread to avoid races. michael@0: CompilerRootFunction fun; michael@0: uint16_t flags; michael@0: gc::Cell *scriptOrLazyScript; michael@0: bool singletonType; michael@0: bool useNewTypeForClone; michael@0: michael@0: LambdaFunctionInfo(JSFunction *fun) michael@0: : fun(fun), flags(fun->flags()), michael@0: scriptOrLazyScript(fun->hasScript() michael@0: ? (gc::Cell *) fun->nonLazyScript() michael@0: : (gc::Cell *) fun->lazyScript()), michael@0: singletonType(fun->hasSingletonType()), michael@0: useNewTypeForClone(types::UseNewTypeForClone(fun)) michael@0: {} michael@0: michael@0: LambdaFunctionInfo(const LambdaFunctionInfo &info) michael@0: : fun((JSFunction *) info.fun), flags(info.flags), michael@0: scriptOrLazyScript(info.scriptOrLazyScript), michael@0: singletonType(info.singletonType), michael@0: useNewTypeForClone(info.useNewTypeForClone) michael@0: {} michael@0: }; michael@0: michael@0: class MLambda michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: LambdaFunctionInfo info_; michael@0: michael@0: MLambda(types::CompilerConstraintList *constraints, MDefinition *scopeChain, JSFunction *fun) michael@0: : MUnaryInstruction(scopeChain), info_(fun) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: if (!fun->hasSingletonType() && !types::UseNewTypeForClone(fun)) michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Lambda) michael@0: michael@0: static MLambda *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MDefinition *scopeChain, JSFunction *fun) michael@0: { michael@0: return new(alloc) MLambda(constraints, scopeChain, fun); michael@0: } michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: const LambdaFunctionInfo &info() const { michael@0: return info_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MLambdaArrow michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: LambdaFunctionInfo info_; michael@0: michael@0: MLambdaArrow(types::CompilerConstraintList *constraints, MDefinition *scopeChain, michael@0: MDefinition *this_, JSFunction *fun) michael@0: : MBinaryInstruction(scopeChain, this_), info_(fun) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: MOZ_ASSERT(!types::UseNewTypeForClone(fun)); michael@0: if (!fun->hasSingletonType()) michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LambdaArrow) michael@0: michael@0: static MLambdaArrow *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MDefinition *scopeChain, MDefinition *this_, JSFunction *fun) michael@0: { michael@0: return new(alloc) MLambdaArrow(constraints, scopeChain, this_, fun); michael@0: } michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *thisDef() const { michael@0: return getOperand(1); michael@0: } michael@0: const LambdaFunctionInfo &info() const { michael@0: return info_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MLambdaPar michael@0: : public MBinaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: LambdaFunctionInfo info_; michael@0: michael@0: MLambdaPar(MDefinition *cx, MDefinition *scopeChain, JSFunction *fun, michael@0: types::TemporaryTypeSet *resultTypes, const LambdaFunctionInfo &info) michael@0: : MBinaryInstruction(cx, scopeChain), info_(info) michael@0: { michael@0: JS_ASSERT(!info_.singletonType); michael@0: JS_ASSERT(!info_.useNewTypeForClone); michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(resultTypes); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LambdaPar); michael@0: michael@0: static MLambdaPar *New(TempAllocator &alloc, MDefinition *cx, MLambda *lambda) { michael@0: return new(alloc) MLambdaPar(cx, lambda->scopeChain(), lambda->info().fun, michael@0: lambda->resultTypeSet(), lambda->info()); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: const LambdaFunctionInfo &info() const { michael@0: return info_; michael@0: } michael@0: }; michael@0: michael@0: // Determines the implicit |this| value for function calls. michael@0: class MImplicitThis michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MImplicitThis(MDefinition *callee) michael@0: : MUnaryInstruction(callee) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ImplicitThis) michael@0: michael@0: static MImplicitThis *New(TempAllocator &alloc, MDefinition *callee) { michael@0: return new(alloc) MImplicitThis(callee); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *callee() const { michael@0: return getOperand(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Returns obj->slots. michael@0: class MSlots michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MSlots(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: setResultType(MIRType_Slots); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Slots) michael@0: michael@0: static MSlots *New(TempAllocator &alloc, MDefinition *object) { michael@0: return new(alloc) MSlots(object); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Returns obj->elements. michael@0: class MElements michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MElements(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: setResultType(MIRType_Elements); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Elements) michael@0: michael@0: static MElements *New(TempAllocator &alloc, MDefinition *object) { michael@0: return new(alloc) MElements(object); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // A constant value for some object's array elements or typed array elements. michael@0: class MConstantElements : public MNullaryInstruction michael@0: { michael@0: void *value_; michael@0: michael@0: protected: michael@0: MConstantElements(void *v) michael@0: : value_(v) michael@0: { michael@0: setResultType(MIRType_Elements); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ConstantElements) michael@0: static MConstantElements *New(TempAllocator &alloc, void *v) { michael@0: return new(alloc) MConstantElements(v); michael@0: } michael@0: michael@0: void *value() const { michael@0: return value_; michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: HashNumber valueHash() const { michael@0: return (HashNumber)(size_t) value_; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return ins->isConstantElements() && ins->toConstantElements()->value() == value(); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Passes through an object's elements, after ensuring it is entirely doubles. michael@0: class MConvertElementsToDoubles michael@0: : public MUnaryInstruction michael@0: { michael@0: MConvertElementsToDoubles(MDefinition *elements) michael@0: : MUnaryInstruction(elements) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Elements); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ConvertElementsToDoubles) michael@0: michael@0: static MConvertElementsToDoubles *New(TempAllocator &alloc, MDefinition *elements) { michael@0: return new(alloc) MConvertElementsToDoubles(elements); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // This instruction can read and write to the elements' contents. michael@0: // However, it is alright to hoist this from loops which explicitly michael@0: // read or write to the elements: such reads and writes will use double michael@0: // values and can be reordered freely wrt this conversion, except that michael@0: // definite double loads must follow the conversion. The latter michael@0: // property is ensured by chaining this instruction with the elements michael@0: // themselves, in the same manner as MBoundsCheck. michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // If |elements| has the CONVERT_DOUBLE_ELEMENTS flag, convert value to michael@0: // double. Else return the original value. michael@0: class MMaybeToDoubleElement michael@0: : public MBinaryInstruction, michael@0: public IntPolicy<1> michael@0: { michael@0: MMaybeToDoubleElement(MDefinition *elements, MDefinition *value) michael@0: : MBinaryInstruction(elements, value) michael@0: { michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: setMovable(); michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(MaybeToDoubleElement) michael@0: michael@0: static MMaybeToDoubleElement *New(TempAllocator &alloc, MDefinition *elements, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MMaybeToDoubleElement(elements, value); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Load the initialized length from an elements header. michael@0: class MInitializedLength michael@0: : public MUnaryInstruction michael@0: { michael@0: MInitializedLength(MDefinition *elements) michael@0: : MUnaryInstruction(elements) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InitializedLength) michael@0: michael@0: static MInitializedLength *New(TempAllocator &alloc, MDefinition *elements) { michael@0: return new(alloc) MInitializedLength(elements); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Store to the initialized length in an elements header. Note the input is an michael@0: // *index*, one less than the desired length. michael@0: class MSetInitializedLength michael@0: : public MAryInstruction<2> michael@0: { michael@0: MSetInitializedLength(MDefinition *elements, MDefinition *index) { michael@0: setOperand(0, elements); michael@0: setOperand(1, index); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetInitializedLength) michael@0: michael@0: static MSetInitializedLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) { michael@0: return new(alloc) MSetInitializedLength(elements, index); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Load the array length from an elements header. michael@0: class MArrayLength michael@0: : public MUnaryInstruction michael@0: { michael@0: MArrayLength(MDefinition *elements) michael@0: : MUnaryInstruction(elements) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArrayLength) michael@0: michael@0: static MArrayLength *New(TempAllocator &alloc, MDefinition *elements) { michael@0: return new(alloc) MArrayLength(elements); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Store to the length in an elements header. Note the input is an *index*, one michael@0: // less than the desired length. michael@0: class MSetArrayLength michael@0: : public MAryInstruction<2> michael@0: { michael@0: MSetArrayLength(MDefinition *elements, MDefinition *index) { michael@0: setOperand(0, elements); michael@0: setOperand(1, index); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetArrayLength) michael@0: michael@0: static MSetArrayLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) { michael@0: return new(alloc) MSetArrayLength(elements, index); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Read the length of a typed array. michael@0: class MTypedArrayLength michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MTypedArrayLength(MDefinition *obj) michael@0: : MUnaryInstruction(obj) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypedArrayLength) michael@0: michael@0: static MTypedArrayLength *New(TempAllocator &alloc, MDefinition *obj) { michael@0: return new(alloc) MTypedArrayLength(obj); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::TypedArrayLength); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Load a typed array's elements vector. michael@0: class MTypedArrayElements michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MTypedArrayElements(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: setResultType(MIRType_Elements); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypedArrayElements) michael@0: michael@0: static MTypedArrayElements *New(TempAllocator &alloc, MDefinition *object) { michael@0: return new(alloc) MTypedArrayElements(object); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Checks whether a typed object is neutered. michael@0: class MNeuterCheck michael@0: : public MUnaryInstruction michael@0: { michael@0: private: michael@0: MNeuterCheck(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: JS_ASSERT(object->type() == MIRType_Object); michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(object->resultTypeSet()); michael@0: setGuard(); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NeuterCheck) michael@0: michael@0: static MNeuterCheck *New(TempAllocator &alloc, MDefinition *object) { michael@0: return new(alloc) MNeuterCheck(object); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Load a binary data object's "elements", which is just its opaque michael@0: // binary data space. Eventually this should probably be michael@0: // unified with `MTypedArrayElements`. michael@0: class MTypedObjectElements michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: private: michael@0: MTypedObjectElements(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: setResultType(MIRType_Elements); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypedObjectElements) michael@0: michael@0: static MTypedObjectElements *New(TempAllocator &alloc, MDefinition *object) { michael@0: return new(alloc) MTypedObjectElements(object); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Inlined version of the js::SetTypedObjectOffset() intrinsic. michael@0: class MSetTypedObjectOffset michael@0: : public MBinaryInstruction michael@0: { michael@0: private: michael@0: MSetTypedObjectOffset(MDefinition *object, MDefinition *offset) michael@0: : MBinaryInstruction(object, offset) michael@0: { michael@0: JS_ASSERT(object->type() == MIRType_Object); michael@0: JS_ASSERT(offset->type() == MIRType_Int32); michael@0: setResultType(MIRType_None); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetTypedObjectOffset) michael@0: michael@0: static MSetTypedObjectOffset *New(TempAllocator &alloc, michael@0: MDefinition *object, michael@0: MDefinition *offset) michael@0: { michael@0: return new(alloc) MSetTypedObjectOffset(object, offset); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *offset() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: // This affects the result of MTypedObjectElements, michael@0: // which is described as a load of ObjectFields. michael@0: return AliasSet::Store(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Perform !-operation michael@0: class MNot michael@0: : public MUnaryInstruction, michael@0: public TestPolicy michael@0: { michael@0: bool operandMightEmulateUndefined_; michael@0: bool operandIsNeverNaN_; michael@0: michael@0: public: michael@0: MNot(MDefinition *input) michael@0: : MUnaryInstruction(input), michael@0: operandMightEmulateUndefined_(true), michael@0: operandIsNeverNaN_(false) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: } michael@0: michael@0: static MNot *New(TempAllocator &alloc, MDefinition *elements) { michael@0: return new(alloc) MNot(elements); michael@0: } michael@0: static MNot *NewAsmJS(TempAllocator &alloc, MDefinition *elements) { michael@0: MNot *ins = new(alloc) MNot(elements); michael@0: ins->setResultType(MIRType_Int32); michael@0: return ins; michael@0: } michael@0: michael@0: INSTRUCTION_HEADER(Not); michael@0: michael@0: void infer(); michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: void markOperandCantEmulateUndefined() { michael@0: operandMightEmulateUndefined_ = false; michael@0: } michael@0: bool operandMightEmulateUndefined() const { michael@0: return operandMightEmulateUndefined_; michael@0: } michael@0: bool operandIsNeverNaN() const { michael@0: return operandIsNeverNaN_; michael@0: } michael@0: michael@0: MDefinition *operand() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: void collectRangeInfoPreTrunc(); michael@0: michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: bool isFloat32Commutative() const { return true; } michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: // Bailout if index + minimum < 0 or index + maximum >= length. The length used michael@0: // in a bounds check must not be negative, or the wrong result may be computed michael@0: // (unsigned comparisons may be used). michael@0: class MBoundsCheck michael@0: : public MBinaryInstruction michael@0: { michael@0: // Range over which to perform the bounds check, may be modified by GVN. michael@0: int32_t minimum_; michael@0: int32_t maximum_; michael@0: michael@0: MBoundsCheck(MDefinition *index, MDefinition *length) michael@0: : MBinaryInstruction(index, length), minimum_(0), maximum_(0) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(length->type() == MIRType_Int32); michael@0: michael@0: // Returns the checked index. michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BoundsCheck) michael@0: michael@0: static MBoundsCheck *New(TempAllocator &alloc, MDefinition *index, MDefinition *length) { michael@0: return new(alloc) MBoundsCheck(index, length); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *length() const { michael@0: return getOperand(1); michael@0: } michael@0: int32_t minimum() const { michael@0: return minimum_; michael@0: } michael@0: void setMinimum(int32_t n) { michael@0: minimum_ = n; michael@0: } michael@0: int32_t maximum() const { michael@0: return maximum_; michael@0: } michael@0: void setMaximum(int32_t n) { michael@0: maximum_ = n; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isBoundsCheck()) michael@0: return false; michael@0: const MBoundsCheck *other = ins->toBoundsCheck(); michael@0: if (minimum() != other->minimum() || maximum() != other->maximum()) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: virtual AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Bailout if index < minimum. michael@0: class MBoundsCheckLower michael@0: : public MUnaryInstruction michael@0: { michael@0: int32_t minimum_; michael@0: bool fallible_; michael@0: michael@0: MBoundsCheckLower(MDefinition *index) michael@0: : MUnaryInstruction(index), minimum_(0), fallible_(true) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BoundsCheckLower) michael@0: michael@0: static MBoundsCheckLower *New(TempAllocator &alloc, MDefinition *index) { michael@0: return new(alloc) MBoundsCheckLower(index); michael@0: } michael@0: michael@0: MDefinition *index() const { michael@0: return getOperand(0); michael@0: } michael@0: int32_t minimum() const { michael@0: return minimum_; michael@0: } michael@0: void setMinimum(int32_t n) { michael@0: minimum_ = n; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool fallible() const { michael@0: return fallible_; michael@0: } michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: // Load a value from a dense array's element vector and does a hole check if the michael@0: // array is not known to be packed. michael@0: class MLoadElement michael@0: : public MBinaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: bool needsHoleCheck_; michael@0: bool loadDoubles_; michael@0: michael@0: MLoadElement(MDefinition *elements, MDefinition *index, bool needsHoleCheck, bool loadDoubles) michael@0: : MBinaryInstruction(elements, index), michael@0: needsHoleCheck_(needsHoleCheck), michael@0: loadDoubles_(loadDoubles) michael@0: { michael@0: if (needsHoleCheck) { michael@0: // Uses may be optimized away based on this instruction's result michael@0: // type. This means it's invalid to DCE this instruction, as we michael@0: // have to invalidate when we read a hole. michael@0: setGuard(); michael@0: } michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadElement) michael@0: michael@0: static MLoadElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: bool needsHoleCheck, bool loadDoubles) { michael@0: return new(alloc) MLoadElement(elements, index, needsHoleCheck, loadDoubles); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: bool needsHoleCheck() const { michael@0: return needsHoleCheck_; michael@0: } michael@0: bool loadDoubles() const { michael@0: return loadDoubles_; michael@0: } michael@0: bool fallible() const { michael@0: return needsHoleCheck(); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadElement()) michael@0: return false; michael@0: const MLoadElement *other = ins->toLoadElement(); michael@0: if (needsHoleCheck() != other->needsHoleCheck()) michael@0: return false; michael@0: if (loadDoubles() != other->loadDoubles()) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::Element); michael@0: } michael@0: }; michael@0: michael@0: // Load a value from a dense array's element vector. If the index is michael@0: // out-of-bounds, or the indexed slot has a hole, undefined is returned michael@0: // instead. michael@0: class MLoadElementHole michael@0: : public MTernaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: bool needsNegativeIntCheck_; michael@0: bool needsHoleCheck_; michael@0: michael@0: MLoadElementHole(MDefinition *elements, MDefinition *index, MDefinition *initLength, bool needsHoleCheck) michael@0: : MTernaryInstruction(elements, index, initLength), michael@0: needsNegativeIntCheck_(true), michael@0: needsHoleCheck_(needsHoleCheck) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(initLength->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadElementHole) michael@0: michael@0: static MLoadElementHole *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: MDefinition *initLength, bool needsHoleCheck) { michael@0: return new(alloc) MLoadElementHole(elements, index, initLength, needsHoleCheck); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *initLength() const { michael@0: return getOperand(2); michael@0: } michael@0: bool needsNegativeIntCheck() const { michael@0: return needsNegativeIntCheck_; michael@0: } michael@0: bool needsHoleCheck() const { michael@0: return needsHoleCheck_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadElementHole()) michael@0: return false; michael@0: const MLoadElementHole *other = ins->toLoadElementHole(); michael@0: if (needsHoleCheck() != other->needsHoleCheck()) michael@0: return false; michael@0: if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::Element); michael@0: } michael@0: void collectRangeInfoPreTrunc(); michael@0: }; michael@0: michael@0: class MStoreElementCommon michael@0: { michael@0: bool needsBarrier_; michael@0: MIRType elementType_; michael@0: bool racy_; // if true, exempted from normal data race req. during par. exec. michael@0: michael@0: protected: michael@0: MStoreElementCommon() michael@0: : needsBarrier_(false), michael@0: elementType_(MIRType_Value), michael@0: racy_(false) michael@0: { } michael@0: michael@0: public: michael@0: MIRType elementType() const { michael@0: return elementType_; michael@0: } michael@0: void setElementType(MIRType elementType) { michael@0: JS_ASSERT(elementType != MIRType_None); michael@0: elementType_ = elementType; michael@0: } michael@0: bool needsBarrier() const { michael@0: return needsBarrier_; michael@0: } michael@0: void setNeedsBarrier() { michael@0: needsBarrier_ = true; michael@0: } michael@0: bool racy() const { michael@0: return racy_; michael@0: } michael@0: void setRacy() { michael@0: racy_ = true; michael@0: } michael@0: }; michael@0: michael@0: // Store a value to a dense array slots vector. michael@0: class MStoreElement michael@0: : public MAryInstruction<3>, michael@0: public MStoreElementCommon, michael@0: public MixPolicy > michael@0: { michael@0: bool needsHoleCheck_; michael@0: michael@0: MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) { michael@0: setOperand(0, elements); michael@0: setOperand(1, index); michael@0: setOperand(2, value); michael@0: needsHoleCheck_ = needsHoleCheck; michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreElement) michael@0: michael@0: static MStoreElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: MDefinition *value, bool needsHoleCheck) { michael@0: return new(alloc) MStoreElement(elements, index, value, needsHoleCheck); michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(2); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::Element); michael@0: } michael@0: bool needsHoleCheck() const { michael@0: return needsHoleCheck_; michael@0: } michael@0: bool fallible() const { michael@0: return needsHoleCheck(); michael@0: } michael@0: }; michael@0: michael@0: // Like MStoreElement, but supports indexes >= initialized length. The downside michael@0: // is that we cannot hoist the elements vector and bounds check, since this michael@0: // instruction may update the (initialized) length and reallocate the elements michael@0: // vector. michael@0: class MStoreElementHole michael@0: : public MAryInstruction<4>, michael@0: public MStoreElementCommon, michael@0: public MixPolicy > michael@0: { michael@0: MStoreElementHole(MDefinition *object, MDefinition *elements, michael@0: MDefinition *index, MDefinition *value) { michael@0: setOperand(0, object); michael@0: setOperand(1, elements); michael@0: setOperand(2, index); michael@0: setOperand(3, value); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreElementHole) michael@0: michael@0: static MStoreElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *elements, michael@0: MDefinition *index, MDefinition *value) { michael@0: return new(alloc) MStoreElementHole(object, elements, index, value); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(2); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(3); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // StoreElementHole can update the initialized length, the array length michael@0: // or reallocate obj->elements. michael@0: return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Array.prototype.pop or Array.prototype.shift on a dense array. michael@0: class MArrayPopShift michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: public: michael@0: enum Mode { michael@0: Pop, michael@0: Shift michael@0: }; michael@0: michael@0: private: michael@0: Mode mode_; michael@0: bool needsHoleCheck_; michael@0: bool maybeUndefined_; michael@0: michael@0: MArrayPopShift(MDefinition *object, Mode mode, bool needsHoleCheck, bool maybeUndefined) michael@0: : MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck), michael@0: maybeUndefined_(maybeUndefined) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArrayPopShift) michael@0: michael@0: static MArrayPopShift *New(TempAllocator &alloc, MDefinition *object, Mode mode, michael@0: bool needsHoleCheck, bool maybeUndefined) michael@0: { michael@0: return new(alloc) MArrayPopShift(object, mode, needsHoleCheck, maybeUndefined); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: bool needsHoleCheck() const { michael@0: return needsHoleCheck_; michael@0: } michael@0: bool maybeUndefined() const { michael@0: return maybeUndefined_; michael@0: } michael@0: bool mode() const { michael@0: return mode_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Array.prototype.push on a dense array. Returns the new array length. michael@0: class MArrayPush michael@0: : public MBinaryInstruction, michael@0: public MixPolicy > michael@0: { michael@0: MArrayPush(MDefinition *object, MDefinition *value) michael@0: : MBinaryInstruction(object, value) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArrayPush) michael@0: michael@0: static MArrayPush *New(TempAllocator &alloc, MDefinition *object, MDefinition *value) { michael@0: return new(alloc) MArrayPush(object, value); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Array.prototype.concat on two dense arrays. michael@0: class MArrayConcat michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: CompilerRootObject templateObj_; michael@0: gc::InitialHeap initialHeap_; michael@0: michael@0: MArrayConcat(types::CompilerConstraintList *constraints, MDefinition *lhs, MDefinition *rhs, michael@0: JSObject *templateObj, gc::InitialHeap initialHeap) michael@0: : MBinaryInstruction(lhs, rhs), michael@0: templateObj_(templateObj), michael@0: initialHeap_(initialHeap) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArrayConcat) michael@0: michael@0: static MArrayConcat *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MDefinition *lhs, MDefinition *rhs, michael@0: JSObject *templateObj, gc::InitialHeap initialHeap) michael@0: { michael@0: return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap); michael@0: } michael@0: michael@0: JSObject *templateObj() const { michael@0: return templateObj_; michael@0: } michael@0: michael@0: gc::InitialHeap initialHeap() const { michael@0: return initialHeap_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MLoadTypedArrayElement michael@0: : public MBinaryInstruction michael@0: { michael@0: ScalarTypeDescr::Type arrayType_; michael@0: michael@0: MLoadTypedArrayElement(MDefinition *elements, MDefinition *index, michael@0: ScalarTypeDescr::Type arrayType) michael@0: : MBinaryInstruction(elements, index), arrayType_(arrayType) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadTypedArrayElement) michael@0: michael@0: static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: ScalarTypeDescr::Type arrayType) michael@0: { michael@0: return new(alloc) MLoadTypedArrayElement(elements, index, arrayType); michael@0: } michael@0: michael@0: ScalarTypeDescr::Type arrayType() const { michael@0: return arrayType_; michael@0: } michael@0: bool fallible() const { michael@0: // Bailout if the result does not fit in an int32. michael@0: return arrayType_ == ScalarTypeDescr::TYPE_UINT32 && type() == MIRType_Int32; michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::TypedArrayElement); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadTypedArrayElement()) michael@0: return false; michael@0: const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement(); michael@0: if (arrayType_ != other->arrayType_) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: michael@0: bool canProduceFloat32() const { return arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; } michael@0: }; michael@0: michael@0: // Load a value from a typed array. Out-of-bounds accesses are handled using michael@0: // a VM call. michael@0: class MLoadTypedArrayElementHole michael@0: : public MBinaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: int arrayType_; michael@0: bool allowDouble_; michael@0: michael@0: MLoadTypedArrayElementHole(MDefinition *object, MDefinition *index, int arrayType, bool allowDouble) michael@0: : MBinaryInstruction(object, index), arrayType_(arrayType), allowDouble_(allowDouble) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadTypedArrayElementHole) michael@0: michael@0: static MLoadTypedArrayElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *index, michael@0: int arrayType, bool allowDouble) michael@0: { michael@0: return new(alloc) MLoadTypedArrayElementHole(object, index, arrayType, allowDouble); michael@0: } michael@0: michael@0: int arrayType() const { michael@0: return arrayType_; michael@0: } michael@0: bool allowDouble() const { michael@0: return allowDouble_; michael@0: } michael@0: bool fallible() const { michael@0: return arrayType_ == ScalarTypeDescr::TYPE_UINT32 && !allowDouble_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadTypedArrayElementHole()) michael@0: return false; michael@0: const MLoadTypedArrayElementHole *other = ins->toLoadTypedArrayElementHole(); michael@0: if (arrayType() != other->arrayType()) michael@0: return false; michael@0: if (allowDouble() != other->allowDouble()) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::TypedArrayElement); michael@0: } michael@0: bool canProduceFloat32() const { return arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; } michael@0: }; michael@0: michael@0: // Load a value fallibly or infallibly from a statically known typed array. michael@0: class MLoadTypedArrayElementStatic michael@0: : public MUnaryInstruction, michael@0: public ConvertToInt32Policy<0> michael@0: { michael@0: MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr) michael@0: : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true) michael@0: { michael@0: int type = typedArray_->type(); michael@0: if (type == ScalarTypeDescr::TYPE_FLOAT32) michael@0: setResultType(MIRType_Float32); michael@0: else if (type == ScalarTypeDescr::TYPE_FLOAT64) michael@0: setResultType(MIRType_Double); michael@0: else michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: CompilerRoot typedArray_; michael@0: bool fallible_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadTypedArrayElementStatic); michael@0: michael@0: static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray, michael@0: MDefinition *ptr) michael@0: { michael@0: return new(alloc) MLoadTypedArrayElementStatic(typedArray, ptr); michael@0: } michael@0: michael@0: ArrayBufferView::ViewType viewType() const { michael@0: return (ArrayBufferView::ViewType) typedArray_->type(); michael@0: } michael@0: void *base() const; michael@0: size_t length() const; michael@0: michael@0: MDefinition *ptr() const { return getOperand(0); } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::TypedArrayElement); michael@0: } michael@0: michael@0: bool fallible() const { michael@0: return fallible_; michael@0: } michael@0: michael@0: void setInfallible() { michael@0: fallible_ = false; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: bool truncate(); michael@0: bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; } michael@0: }; michael@0: michael@0: class MStoreTypedArrayElement michael@0: : public MTernaryInstruction, michael@0: public StoreTypedArrayPolicy michael@0: { michael@0: int arrayType_; michael@0: michael@0: // See note in MStoreElementCommon. michael@0: bool racy_; michael@0: michael@0: MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value, michael@0: int arrayType) michael@0: : MTernaryInstruction(elements, index, value), arrayType_(arrayType), racy_(false) michael@0: { michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreTypedArrayElement) michael@0: michael@0: static MStoreTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: MDefinition *value, int arrayType) michael@0: { michael@0: return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType); michael@0: } michael@0: michael@0: int arrayType() const { michael@0: return arrayType_; michael@0: } michael@0: bool isByteArray() const { michael@0: return (arrayType_ == ScalarTypeDescr::TYPE_INT8 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_UINT8 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_UINT8_CLAMPED); michael@0: } michael@0: bool isFloatArray() const { michael@0: return (arrayType_ == ScalarTypeDescr::TYPE_FLOAT32 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_FLOAT64); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(2); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::TypedArrayElement); michael@0: } michael@0: bool racy() const { michael@0: return racy_; michael@0: } michael@0: void setRacy() { michael@0: racy_ = true; michael@0: } michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { michael@0: return use->index() == 2 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; michael@0: } michael@0: }; michael@0: michael@0: class MStoreTypedArrayElementHole michael@0: : public MAryInstruction<4>, michael@0: public StoreTypedArrayHolePolicy michael@0: { michael@0: int arrayType_; michael@0: michael@0: MStoreTypedArrayElementHole(MDefinition *elements, MDefinition *length, MDefinition *index, michael@0: MDefinition *value, int arrayType) michael@0: : MAryInstruction<4>(), arrayType_(arrayType) michael@0: { michael@0: setOperand(0, elements); michael@0: setOperand(1, length); michael@0: setOperand(2, index); michael@0: setOperand(3, value); michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(length->type() == MIRType_Int32); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreTypedArrayElementHole) michael@0: michael@0: static MStoreTypedArrayElementHole *New(TempAllocator &alloc, MDefinition *elements, michael@0: MDefinition *length, MDefinition *index, michael@0: MDefinition *value, int arrayType) michael@0: { michael@0: return new(alloc) MStoreTypedArrayElementHole(elements, length, index, value, arrayType); michael@0: } michael@0: michael@0: int arrayType() const { michael@0: return arrayType_; michael@0: } michael@0: bool isByteArray() const { michael@0: return (arrayType_ == ScalarTypeDescr::TYPE_INT8 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_UINT8 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_UINT8_CLAMPED); michael@0: } michael@0: bool isFloatArray() const { michael@0: return (arrayType_ == ScalarTypeDescr::TYPE_FLOAT32 || michael@0: arrayType_ == ScalarTypeDescr::TYPE_FLOAT64); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *length() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(2); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(3); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::TypedArrayElement); michael@0: } michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { michael@0: return use->index() == 3 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; michael@0: } michael@0: }; michael@0: michael@0: // Store a value infallibly to a statically known typed array. michael@0: class MStoreTypedArrayElementStatic : michael@0: public MBinaryInstruction michael@0: , public StoreTypedArrayElementStaticPolicy michael@0: { michael@0: MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v) michael@0: : MBinaryInstruction(ptr, v), typedArray_(typedArray) michael@0: {} michael@0: michael@0: CompilerRoot typedArray_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreTypedArrayElementStatic); michael@0: michael@0: static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray, michael@0: MDefinition *ptr, MDefinition *v) michael@0: { michael@0: return new(alloc) MStoreTypedArrayElementStatic(typedArray, ptr, v); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: ArrayBufferView::ViewType viewType() const { michael@0: return (ArrayBufferView::ViewType) typedArray_->type(); michael@0: } michael@0: bool isFloatArray() const { michael@0: return (viewType() == ArrayBufferView::TYPE_FLOAT32 || michael@0: viewType() == ArrayBufferView::TYPE_FLOAT64); michael@0: } michael@0: michael@0: void *base() const; michael@0: size_t length() const; michael@0: michael@0: MDefinition *ptr() const { return getOperand(0); } michael@0: MDefinition *value() const { return getOperand(1); } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::TypedArrayElement); michael@0: } michael@0: bool isOperandTruncated(size_t index) const; michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { michael@0: return use->index() == 1 && typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; michael@0: } michael@0: }; michael@0: michael@0: // Compute an "effective address", i.e., a compound computation of the form: michael@0: // base + index * scale + displacement michael@0: class MEffectiveAddress : public MBinaryInstruction michael@0: { michael@0: MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement) michael@0: : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement) michael@0: { michael@0: JS_ASSERT(base->type() == MIRType_Int32); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: setMovable(); michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: Scale scale_; michael@0: int32_t displacement_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(EffectiveAddress); michael@0: michael@0: static MEffectiveAddress *New(TempAllocator &alloc, MDefinition *base, MDefinition *index, michael@0: Scale s, int32_t d) michael@0: { michael@0: return new(alloc) MEffectiveAddress(base, index, s, d); michael@0: } michael@0: MDefinition *base() const { michael@0: return lhs(); michael@0: } michael@0: MDefinition *index() const { michael@0: return rhs(); michael@0: } michael@0: Scale scale() const { michael@0: return scale_; michael@0: } michael@0: int32_t displacement() const { michael@0: return displacement_; michael@0: } michael@0: }; michael@0: michael@0: // Clamp input to range [0, 255] for Uint8ClampedArray. michael@0: class MClampToUint8 michael@0: : public MUnaryInstruction, michael@0: public ClampPolicy michael@0: { michael@0: MClampToUint8(MDefinition *input) michael@0: : MUnaryInstruction(input) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ClampToUint8) michael@0: michael@0: static MClampToUint8 *New(TempAllocator &alloc, MDefinition *input) { michael@0: return new(alloc) MClampToUint8(input); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: class MLoadFixedSlot michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: size_t slot_; michael@0: michael@0: protected: michael@0: MLoadFixedSlot(MDefinition *obj, size_t slot) michael@0: : MUnaryInstruction(obj), slot_(slot) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadFixedSlot) michael@0: michael@0: static MLoadFixedSlot *New(TempAllocator &alloc, MDefinition *obj, size_t slot) { michael@0: return new(alloc) MLoadFixedSlot(obj, slot); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: size_t slot() const { michael@0: return slot_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadFixedSlot()) michael@0: return false; michael@0: if (slot() != ins->toLoadFixedSlot()->slot()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::FixedSlot); michael@0: } michael@0: michael@0: bool mightAlias(const MDefinition *store) const; michael@0: }; michael@0: michael@0: class MStoreFixedSlot michael@0: : public MBinaryInstruction, michael@0: public MixPolicy > michael@0: { michael@0: bool needsBarrier_; michael@0: size_t slot_; michael@0: michael@0: MStoreFixedSlot(MDefinition *obj, MDefinition *rval, size_t slot, bool barrier) michael@0: : MBinaryInstruction(obj, rval), michael@0: needsBarrier_(barrier), michael@0: slot_(slot) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreFixedSlot) michael@0: michael@0: static MStoreFixedSlot *New(TempAllocator &alloc, MDefinition *obj, size_t slot, michael@0: MDefinition *rval) michael@0: { michael@0: return new(alloc) MStoreFixedSlot(obj, rval, slot, false); michael@0: } michael@0: static MStoreFixedSlot *NewBarriered(TempAllocator &alloc, MDefinition *obj, size_t slot, michael@0: MDefinition *rval) michael@0: { michael@0: return new(alloc) MStoreFixedSlot(obj, rval, slot, true); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: size_t slot() const { michael@0: return slot_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::FixedSlot); michael@0: } michael@0: bool needsBarrier() const { michael@0: return needsBarrier_; michael@0: } michael@0: void setNeedsBarrier() { michael@0: needsBarrier_ = true; michael@0: } michael@0: }; michael@0: michael@0: typedef Vector ObjectVector; michael@0: typedef Vector BoolVector; michael@0: michael@0: class InlinePropertyTable : public TempObject michael@0: { michael@0: struct Entry : public TempObject { michael@0: CompilerRoot typeObj; michael@0: CompilerRootFunction func; michael@0: michael@0: Entry(types::TypeObject *typeObj, JSFunction *func) michael@0: : typeObj(typeObj), func(func) michael@0: { } michael@0: }; michael@0: michael@0: jsbytecode *pc_; michael@0: MResumePoint *priorResumePoint_; michael@0: Vector entries_; michael@0: michael@0: public: michael@0: InlinePropertyTable(TempAllocator &alloc, jsbytecode *pc) michael@0: : pc_(pc), priorResumePoint_(nullptr), entries_(alloc) michael@0: { } michael@0: michael@0: void setPriorResumePoint(MResumePoint *resumePoint) { michael@0: JS_ASSERT(priorResumePoint_ == nullptr); michael@0: priorResumePoint_ = resumePoint; michael@0: } michael@0: michael@0: MResumePoint *priorResumePoint() const { michael@0: return priorResumePoint_; michael@0: } michael@0: michael@0: jsbytecode *pc() const { michael@0: return pc_; michael@0: } michael@0: michael@0: bool addEntry(TempAllocator &alloc, types::TypeObject *typeObj, JSFunction *func) { michael@0: return entries_.append(new(alloc) Entry(typeObj, func)); michael@0: } michael@0: michael@0: size_t numEntries() const { michael@0: return entries_.length(); michael@0: } michael@0: michael@0: types::TypeObject *getTypeObject(size_t i) const { michael@0: JS_ASSERT(i < numEntries()); michael@0: return entries_[i]->typeObj; michael@0: } michael@0: michael@0: JSFunction *getFunction(size_t i) const { michael@0: JS_ASSERT(i < numEntries()); michael@0: return entries_[i]->func; michael@0: } michael@0: michael@0: bool hasFunction(JSFunction *func) const; michael@0: types::TemporaryTypeSet *buildTypeSetForFunction(JSFunction *func) const; michael@0: michael@0: // Remove targets that vetoed inlining from the InlinePropertyTable. michael@0: void trimTo(ObjectVector &targets, BoolVector &choiceSet); michael@0: michael@0: // Ensure that the InlinePropertyTable's domain is a subset of |targets|. michael@0: void trimToTargets(ObjectVector &targets); michael@0: }; michael@0: michael@0: class CacheLocationList : public InlineConcatList michael@0: { michael@0: public: michael@0: CacheLocationList() michael@0: : pc(nullptr), michael@0: script(nullptr) michael@0: { } michael@0: michael@0: jsbytecode *pc; michael@0: JSScript *script; michael@0: }; michael@0: michael@0: class MGetPropertyCache michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: bool idempotent_; michael@0: bool monitoredResult_; michael@0: michael@0: CacheLocationList location_; michael@0: michael@0: InlinePropertyTable *inlinePropertyTable_; michael@0: michael@0: MGetPropertyCache(MDefinition *obj, PropertyName *name, bool monitoredResult) michael@0: : MUnaryInstruction(obj), michael@0: name_(name), michael@0: idempotent_(false), michael@0: monitoredResult_(monitoredResult), michael@0: location_(), michael@0: inlinePropertyTable_(nullptr) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: michael@0: // The cache will invalidate if there are objects with e.g. lookup or michael@0: // resolve hooks on the proto chain. setGuard ensures this check is not michael@0: // eliminated. michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetPropertyCache) michael@0: michael@0: static MGetPropertyCache *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, michael@0: bool monitoredResult) { michael@0: return new(alloc) MGetPropertyCache(obj, name, monitoredResult); michael@0: } michael@0: michael@0: InlinePropertyTable *initInlinePropertyTable(TempAllocator &alloc, jsbytecode *pc) { michael@0: JS_ASSERT(inlinePropertyTable_ == nullptr); michael@0: inlinePropertyTable_ = new(alloc) InlinePropertyTable(alloc, pc); michael@0: return inlinePropertyTable_; michael@0: } michael@0: michael@0: void clearInlinePropertyTable() { michael@0: inlinePropertyTable_ = nullptr; michael@0: } michael@0: michael@0: InlinePropertyTable *propTable() const { michael@0: return inlinePropertyTable_; michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: bool idempotent() const { michael@0: return idempotent_; michael@0: } michael@0: void setIdempotent() { michael@0: idempotent_ = true; michael@0: setMovable(); michael@0: } michael@0: bool monitoredResult() const { michael@0: return monitoredResult_; michael@0: } michael@0: CacheLocationList &location() { michael@0: return location_; michael@0: } michael@0: TypePolicy *typePolicy() { return this; } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!idempotent_) michael@0: return false; michael@0: if (!ins->isGetPropertyCache()) michael@0: return false; michael@0: if (name() != ins->toGetPropertyCache()->name()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: if (idempotent_) { michael@0: return AliasSet::Load(AliasSet::ObjectFields | michael@0: AliasSet::FixedSlot | michael@0: AliasSet::DynamicSlot); michael@0: } michael@0: return AliasSet::Store(AliasSet::Any); michael@0: } michael@0: michael@0: void setBlock(MBasicBlock *block); michael@0: bool updateForReplacement(MDefinition *ins); michael@0: }; michael@0: michael@0: // Emit code to load a value from an object's slots if its shape matches michael@0: // one of the shapes observed by the baseline IC, else bails out. michael@0: class MGetPropertyPolymorphic michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: struct Entry { michael@0: // The shape to guard against. michael@0: Shape *objShape; michael@0: michael@0: // The property to laod. michael@0: Shape *shape; michael@0: }; michael@0: michael@0: Vector shapes_; michael@0: CompilerRootPropertyName name_; michael@0: michael@0: MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name) michael@0: : MUnaryInstruction(obj), michael@0: shapes_(alloc), michael@0: name_(name) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetPropertyPolymorphic) michael@0: michael@0: static MGetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name) { michael@0: return new(alloc) MGetPropertyPolymorphic(alloc, obj, name); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isGetPropertyPolymorphic()) michael@0: return false; michael@0: if (name() != ins->toGetPropertyPolymorphic()->name()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool addShape(Shape *objShape, Shape *shape) { michael@0: Entry entry; michael@0: entry.objShape = objShape; michael@0: entry.shape = shape; michael@0: return shapes_.append(entry); michael@0: } michael@0: size_t numShapes() const { michael@0: return shapes_.length(); michael@0: } michael@0: Shape *objShape(size_t i) const { michael@0: return shapes_[i].objShape; michael@0: } michael@0: Shape *shape(size_t i) const { michael@0: return shapes_[i].shape; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); michael@0: } michael@0: michael@0: bool mightAlias(const MDefinition *store) const; michael@0: }; michael@0: michael@0: // Emit code to store a value to an object's slots if its shape matches michael@0: // one of the shapes observed by the baseline IC, else bails out. michael@0: class MSetPropertyPolymorphic michael@0: : public MBinaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: struct Entry { michael@0: // The shape to guard against. michael@0: Shape *objShape; michael@0: michael@0: // The property to laod. michael@0: Shape *shape; michael@0: }; michael@0: michael@0: Vector shapes_; michael@0: bool needsBarrier_; michael@0: michael@0: MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value) michael@0: : MBinaryInstruction(obj, value), michael@0: shapes_(alloc), michael@0: needsBarrier_(false) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetPropertyPolymorphic) michael@0: michael@0: static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) { michael@0: return new(alloc) MSetPropertyPolymorphic(alloc, obj, value); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool addShape(Shape *objShape, Shape *shape) { michael@0: Entry entry; michael@0: entry.objShape = objShape; michael@0: entry.shape = shape; michael@0: return shapes_.append(entry); michael@0: } michael@0: size_t numShapes() const { michael@0: return shapes_.length(); michael@0: } michael@0: Shape *objShape(size_t i) const { michael@0: return shapes_[i].objShape; michael@0: } michael@0: Shape *shape(size_t i) const { michael@0: return shapes_[i].shape; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: bool needsBarrier() const { michael@0: return needsBarrier_; michael@0: } michael@0: void setNeedsBarrier() { michael@0: needsBarrier_ = true; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); michael@0: } michael@0: }; michael@0: michael@0: class MDispatchInstruction michael@0: : public MControlInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: // Map from JSFunction* -> MBasicBlock. michael@0: struct Entry { michael@0: JSFunction *func; michael@0: MBasicBlock *block; michael@0: michael@0: Entry(JSFunction *func, MBasicBlock *block) michael@0: : func(func), block(block) michael@0: { } michael@0: }; michael@0: Vector map_; michael@0: michael@0: // An optional fallback path that uses MCall. michael@0: MBasicBlock *fallback_; michael@0: MUse operand_; michael@0: michael@0: public: michael@0: MDispatchInstruction(TempAllocator &alloc, MDefinition *input) michael@0: : map_(alloc), fallback_(nullptr) michael@0: { michael@0: setOperand(0, input); michael@0: } michael@0: michael@0: protected: michael@0: void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { michael@0: JS_ASSERT(index == 0); michael@0: operand_.set(operand, this, 0); michael@0: operand->addUse(&operand_); michael@0: } michael@0: MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: JS_ASSERT(index == 0); michael@0: return &operand_; michael@0: } michael@0: MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { michael@0: JS_ASSERT(index == 0); michael@0: return operand_.producer(); michael@0: } michael@0: size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return 1; michael@0: } michael@0: michael@0: public: michael@0: void setSuccessor(size_t i, MBasicBlock *successor) { michael@0: JS_ASSERT(i < numSuccessors()); michael@0: if (i == map_.length()) michael@0: fallback_ = successor; michael@0: else michael@0: map_[i].block = successor; michael@0: } michael@0: size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return map_.length() + (fallback_ ? 1 : 0); michael@0: } michael@0: void replaceSuccessor(size_t i, MBasicBlock *successor) MOZ_FINAL MOZ_OVERRIDE { michael@0: setSuccessor(i, successor); michael@0: } michael@0: MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE { michael@0: JS_ASSERT(i < numSuccessors()); michael@0: if (i == map_.length()) michael@0: return fallback_; michael@0: return map_[i].block; michael@0: } michael@0: michael@0: public: michael@0: void addCase(JSFunction *func, MBasicBlock *block) { michael@0: map_.append(Entry(func, block)); michael@0: } michael@0: uint32_t numCases() const { michael@0: return map_.length(); michael@0: } michael@0: JSFunction *getCase(uint32_t i) const { michael@0: return map_[i].func; michael@0: } michael@0: MBasicBlock *getCaseBlock(uint32_t i) const { michael@0: return map_[i].block; michael@0: } michael@0: michael@0: bool hasFallback() const { michael@0: return bool(fallback_); michael@0: } michael@0: void addFallback(MBasicBlock *block) { michael@0: JS_ASSERT(!hasFallback()); michael@0: fallback_ = block; michael@0: } michael@0: MBasicBlock *getFallback() const { michael@0: JS_ASSERT(hasFallback()); michael@0: return fallback_; michael@0: } michael@0: michael@0: public: michael@0: MDefinition *input() const { michael@0: return getOperand(0); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: // Polymorphic dispatch for inlining, keyed off incoming TypeObject. michael@0: class MTypeObjectDispatch : public MDispatchInstruction michael@0: { michael@0: // Map TypeObject (of CallProp's Target Object) -> JSFunction (yielded by the CallProp). michael@0: InlinePropertyTable *inlinePropertyTable_; michael@0: michael@0: MTypeObjectDispatch(TempAllocator &alloc, MDefinition *input, InlinePropertyTable *table) michael@0: : MDispatchInstruction(alloc, input), michael@0: inlinePropertyTable_(table) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypeObjectDispatch) michael@0: michael@0: static MTypeObjectDispatch *New(TempAllocator &alloc, MDefinition *ins, michael@0: InlinePropertyTable *table) michael@0: { michael@0: return new(alloc) MTypeObjectDispatch(alloc, ins, table); michael@0: } michael@0: michael@0: InlinePropertyTable *propTable() const { michael@0: return inlinePropertyTable_; michael@0: } michael@0: }; michael@0: michael@0: // Polymorphic dispatch for inlining, keyed off incoming JSFunction*. michael@0: class MFunctionDispatch : public MDispatchInstruction michael@0: { michael@0: MFunctionDispatch(TempAllocator &alloc, MDefinition *input) michael@0: : MDispatchInstruction(alloc, input) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(FunctionDispatch) michael@0: michael@0: static MFunctionDispatch *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MFunctionDispatch(alloc, ins); michael@0: } michael@0: }; michael@0: michael@0: class MGetElementCache michael@0: : public MBinaryInstruction michael@0: { michael@0: MixPolicy, BoxPolicy<1> > PolicyV; michael@0: MixPolicy, IntPolicy<1> > PolicyT; michael@0: michael@0: // See the comment in IonBuilder::jsop_getelem. michael@0: bool monitoredResult_; michael@0: michael@0: MGetElementCache(MDefinition *obj, MDefinition *value, bool monitoredResult) michael@0: : MBinaryInstruction(obj, value), monitoredResult_(monitoredResult) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetElementCache) michael@0: michael@0: static MGetElementCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, michael@0: bool monitoredResult) michael@0: { michael@0: return new(alloc) MGetElementCache(obj, value, monitoredResult); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: bool monitoredResult() const { michael@0: return monitoredResult_; michael@0: } michael@0: michael@0: bool allowDoubleResult() const; michael@0: michael@0: TypePolicy *typePolicy() { michael@0: if (type() == MIRType_Value) michael@0: return &PolicyV; michael@0: return &PolicyT; michael@0: } michael@0: }; michael@0: michael@0: class MBindNameCache michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: CompilerRootScript script_; michael@0: jsbytecode *pc_; michael@0: michael@0: MBindNameCache(MDefinition *scopeChain, PropertyName *name, JSScript *script, jsbytecode *pc) michael@0: : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(BindNameCache) michael@0: michael@0: static MBindNameCache *New(TempAllocator &alloc, MDefinition *scopeChain, PropertyName *name, michael@0: JSScript *script, jsbytecode *pc) michael@0: { michael@0: return new(alloc) MBindNameCache(scopeChain, name, script, pc); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *scopeChain() const { michael@0: return getOperand(0); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: JSScript *script() const { michael@0: return script_; michael@0: } michael@0: jsbytecode *pc() const { michael@0: return pc_; michael@0: } michael@0: }; michael@0: michael@0: // Guard on an object's shape. michael@0: class MGuardShape michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: CompilerRootShape shape_; michael@0: BailoutKind bailoutKind_; michael@0: michael@0: MGuardShape(MDefinition *obj, Shape *shape, BailoutKind bailoutKind) michael@0: : MUnaryInstruction(obj), michael@0: shape_(shape), michael@0: bailoutKind_(bailoutKind) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardShape) michael@0: michael@0: static MGuardShape *New(TempAllocator &alloc, MDefinition *obj, Shape *shape, michael@0: BailoutKind bailoutKind) michael@0: { michael@0: return new(alloc) MGuardShape(obj, shape, bailoutKind); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: const Shape *shape() const { michael@0: return shape_; michael@0: } michael@0: BailoutKind bailoutKind() const { michael@0: return bailoutKind_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isGuardShape()) michael@0: return false; michael@0: if (shape() != ins->toGuardShape()->shape()) michael@0: return false; michael@0: if (bailoutKind() != ins->toGuardShape()->bailoutKind()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Guard on an object's type, inclusively or exclusively. michael@0: class MGuardObjectType michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: CompilerRoot typeObject_; michael@0: bool bailOnEquality_; michael@0: michael@0: MGuardObjectType(MDefinition *obj, types::TypeObject *typeObject, bool bailOnEquality) michael@0: : MUnaryInstruction(obj), michael@0: typeObject_(typeObject), michael@0: bailOnEquality_(bailOnEquality) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardObjectType) michael@0: michael@0: static MGuardObjectType *New(TempAllocator &alloc, MDefinition *obj, types::TypeObject *typeObject, michael@0: bool bailOnEquality) { michael@0: return new(alloc) MGuardObjectType(obj, typeObject, bailOnEquality); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: const types::TypeObject *typeObject() const { michael@0: return typeObject_; michael@0: } michael@0: bool bailOnEquality() const { michael@0: return bailOnEquality_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isGuardObjectType()) michael@0: return false; michael@0: if (typeObject() != ins->toGuardObjectType()->typeObject()) michael@0: return false; michael@0: if (bailOnEquality() != ins->toGuardObjectType()->bailOnEquality()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Guard on an object's identity, inclusively or exclusively. michael@0: class MGuardObjectIdentity michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: CompilerRoot singleObject_; michael@0: bool bailOnEquality_; michael@0: michael@0: MGuardObjectIdentity(MDefinition *obj, JSObject *singleObject, bool bailOnEquality) michael@0: : MUnaryInstruction(obj), michael@0: singleObject_(singleObject), michael@0: bailOnEquality_(bailOnEquality) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardObjectIdentity) michael@0: michael@0: static MGuardObjectIdentity *New(TempAllocator &alloc, MDefinition *obj, JSObject *singleObject, michael@0: bool bailOnEquality) { michael@0: return new(alloc) MGuardObjectIdentity(obj, singleObject, bailOnEquality); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: JSObject *singleObject() const { michael@0: return singleObject_; michael@0: } michael@0: bool bailOnEquality() const { michael@0: return bailOnEquality_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isGuardObjectIdentity()) michael@0: return false; michael@0: if (singleObject() != ins->toGuardObjectIdentity()->singleObject()) michael@0: return false; michael@0: if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Guard on an object's class. michael@0: class MGuardClass michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: const Class *class_; michael@0: michael@0: MGuardClass(MDefinition *obj, const Class *clasp) michael@0: : MUnaryInstruction(obj), michael@0: class_(clasp) michael@0: { michael@0: setGuard(); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardClass) michael@0: michael@0: static MGuardClass *New(TempAllocator &alloc, MDefinition *obj, const Class *clasp) { michael@0: return new(alloc) MGuardClass(obj, clasp); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *obj() const { michael@0: return getOperand(0); michael@0: } michael@0: const Class *getClass() const { michael@0: return class_; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isGuardClass()) michael@0: return false; michael@0: if (getClass() != ins->toGuardClass()->getClass()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::ObjectFields); michael@0: } michael@0: }; michael@0: michael@0: // Load from vp[slot] (slots that are not inline in an object). michael@0: class MLoadSlot michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: uint32_t slot_; michael@0: michael@0: MLoadSlot(MDefinition *slots, uint32_t slot) michael@0: : MUnaryInstruction(slots), michael@0: slot_(slot) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: JS_ASSERT(slots->type() == MIRType_Slots); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(LoadSlot) michael@0: michael@0: static MLoadSlot *New(TempAllocator &alloc, MDefinition *slots, uint32_t slot) { michael@0: return new(alloc) MLoadSlot(slots, slot); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *slots() const { michael@0: return getOperand(0); michael@0: } michael@0: uint32_t slot() const { michael@0: return slot_; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isLoadSlot()) michael@0: return false; michael@0: if (slot() != ins->toLoadSlot()->slot()) michael@0: return false; michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: JS_ASSERT(slots()->type() == MIRType_Slots); michael@0: return AliasSet::Load(AliasSet::DynamicSlot); michael@0: } michael@0: bool mightAlias(const MDefinition *store) const; michael@0: }; michael@0: michael@0: // Inline call to access a function's environment (scope chain). michael@0: class MFunctionEnvironment michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: public: michael@0: MFunctionEnvironment(MDefinition *function) michael@0: : MUnaryInstruction(function) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setMovable(); michael@0: } michael@0: michael@0: INSTRUCTION_HEADER(FunctionEnvironment) michael@0: michael@0: static MFunctionEnvironment *New(TempAllocator &alloc, MDefinition *function) { michael@0: return new(alloc) MFunctionEnvironment(function); michael@0: } michael@0: michael@0: MDefinition *function() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: // A function's environment is fixed. michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Loads the current js::ForkJoinContext*. michael@0: // Only applicable in ParallelExecution. michael@0: class MForkJoinContext michael@0: : public MNullaryInstruction michael@0: { michael@0: MForkJoinContext() michael@0: : MNullaryInstruction() michael@0: { michael@0: setResultType(MIRType_ForkJoinContext); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ForkJoinContext); michael@0: michael@0: static MForkJoinContext *New(TempAllocator &alloc) { michael@0: return new(alloc) MForkJoinContext(); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: // Indicate that this instruction reads nothing, stores nothing. michael@0: // (For all intents and purposes) michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Calls the ForkJoinGetSlice stub, used for inlining the eponymous intrinsic. michael@0: // Only applicable in ParallelExecution. michael@0: class MForkJoinGetSlice michael@0: : public MUnaryInstruction michael@0: { michael@0: MForkJoinGetSlice(MDefinition *cx) michael@0: : MUnaryInstruction(cx) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ForkJoinGetSlice); michael@0: michael@0: static MForkJoinGetSlice *New(TempAllocator &alloc, MDefinition *cx) { michael@0: return new(alloc) MForkJoinGetSlice(cx); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Store to vp[slot] (slots that are not inline in an object). michael@0: class MStoreSlot michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, NoFloatPolicy<1> > michael@0: { michael@0: uint32_t slot_; michael@0: MIRType slotType_; michael@0: bool needsBarrier_; michael@0: michael@0: MStoreSlot(MDefinition *slots, uint32_t slot, MDefinition *value, bool barrier) michael@0: : MBinaryInstruction(slots, value), michael@0: slot_(slot), michael@0: slotType_(MIRType_Value), michael@0: needsBarrier_(barrier) michael@0: { michael@0: JS_ASSERT(slots->type() == MIRType_Slots); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(StoreSlot) michael@0: michael@0: static MStoreSlot *New(TempAllocator &alloc, MDefinition *slots, uint32_t slot, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MStoreSlot(slots, slot, value, false); michael@0: } michael@0: static MStoreSlot *NewBarriered(TempAllocator &alloc, MDefinition *slots, uint32_t slot, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MStoreSlot(slots, slot, value, true); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *slots() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: uint32_t slot() const { michael@0: return slot_; michael@0: } michael@0: MIRType slotType() const { michael@0: return slotType_; michael@0: } michael@0: void setSlotType(MIRType slotType) { michael@0: JS_ASSERT(slotType != MIRType_None); michael@0: slotType_ = slotType; michael@0: } michael@0: bool needsBarrier() const { michael@0: return needsBarrier_; michael@0: } michael@0: void setNeedsBarrier() { michael@0: needsBarrier_ = true; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::DynamicSlot); michael@0: } michael@0: }; michael@0: michael@0: class MGetNameCache michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: public: michael@0: enum AccessKind { michael@0: NAMETYPEOF, michael@0: NAME michael@0: }; michael@0: michael@0: private: michael@0: CompilerRootPropertyName name_; michael@0: AccessKind kind_; michael@0: michael@0: MGetNameCache(MDefinition *obj, PropertyName *name, AccessKind kind) michael@0: : MUnaryInstruction(obj), michael@0: name_(name), michael@0: kind_(kind) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetNameCache) michael@0: michael@0: static MGetNameCache *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, michael@0: AccessKind kind) michael@0: { michael@0: return new(alloc) MGetNameCache(obj, name, kind); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *scopeObj() const { michael@0: return getOperand(0); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: AccessKind accessKind() const { michael@0: return kind_; michael@0: } michael@0: }; michael@0: michael@0: class MCallGetIntrinsicValue : public MNullaryInstruction michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: michael@0: MCallGetIntrinsicValue(PropertyName *name) michael@0: : name_(name) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallGetIntrinsicValue) michael@0: michael@0: static MCallGetIntrinsicValue *New(TempAllocator &alloc, PropertyName *name) { michael@0: return new(alloc) MCallGetIntrinsicValue(name); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MCallsiteCloneCache michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: jsbytecode *callPc_; michael@0: michael@0: MCallsiteCloneCache(MDefinition *callee, jsbytecode *callPc) michael@0: : MUnaryInstruction(callee), michael@0: callPc_(callPc) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallsiteCloneCache); michael@0: michael@0: static MCallsiteCloneCache *New(TempAllocator &alloc, MDefinition *callee, jsbytecode *callPc) { michael@0: return new(alloc) MCallsiteCloneCache(callee, callPc); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *callee() const { michael@0: return getOperand(0); michael@0: } michael@0: jsbytecode *callPc() const { michael@0: return callPc_; michael@0: } michael@0: michael@0: // Callsite cloning is idempotent. michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MSetPropertyInstruction : public MBinaryInstruction michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: bool strict_; michael@0: bool needsBarrier_; michael@0: michael@0: protected: michael@0: MSetPropertyInstruction(MDefinition *obj, MDefinition *value, PropertyName *name, michael@0: bool strict) michael@0: : MBinaryInstruction(obj, value), michael@0: name_(name), strict_(strict), needsBarrier_(true) michael@0: {} michael@0: michael@0: public: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool needsBarrier() const { michael@0: return needsBarrier_; michael@0: } michael@0: void setNeedsBarrier() { michael@0: needsBarrier_ = true; michael@0: } michael@0: }; michael@0: michael@0: class MSetElementInstruction michael@0: : public MTernaryInstruction michael@0: { michael@0: protected: michael@0: MSetElementInstruction(MDefinition *object, MDefinition *index, MDefinition *value) michael@0: : MTernaryInstruction(object, index, value) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(2); michael@0: } michael@0: }; michael@0: michael@0: class MDeleteProperty michael@0: : public MUnaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: michael@0: protected: michael@0: MDeleteProperty(MDefinition *val, PropertyName *name) michael@0: : MUnaryInstruction(val), michael@0: name_(name) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(DeleteProperty) michael@0: michael@0: static MDeleteProperty *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name) { michael@0: return new(alloc) MDeleteProperty(obj, name); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(0); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: virtual TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MDeleteElement michael@0: : public MBinaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: MDeleteElement(MDefinition *value, MDefinition *index) michael@0: : MBinaryInstruction(value, index) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(DeleteElement) michael@0: michael@0: static MDeleteElement *New(TempAllocator &alloc, MDefinition *value, MDefinition *index) { michael@0: return new(alloc) MDeleteElement(value, index); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: virtual TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: // Note: This uses CallSetElementPolicy to always box its second input, michael@0: // ensuring we don't need two LIR instructions to lower this. michael@0: class MCallSetProperty michael@0: : public MSetPropertyInstruction, michael@0: public CallSetElementPolicy michael@0: { michael@0: MCallSetProperty(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) michael@0: : MSetPropertyInstruction(obj, value, name, strict) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallSetProperty) michael@0: michael@0: static MCallSetProperty *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, michael@0: PropertyName *name, bool strict) michael@0: { michael@0: return new(alloc) MCallSetProperty(obj, value, name, strict); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MSetPropertyCache michael@0: : public MSetPropertyInstruction, michael@0: public MixPolicy > michael@0: { michael@0: bool needsTypeBarrier_; michael@0: michael@0: MSetPropertyCache(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict, michael@0: bool typeBarrier) michael@0: : MSetPropertyInstruction(obj, value, name, strict), michael@0: needsTypeBarrier_(typeBarrier) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetPropertyCache) michael@0: michael@0: static MSetPropertyCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, michael@0: PropertyName *name, bool strict, bool typeBarrier) michael@0: { michael@0: return new(alloc) MSetPropertyCache(obj, value, name, strict, typeBarrier); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool needsTypeBarrier() const { michael@0: return needsTypeBarrier_; michael@0: } michael@0: }; michael@0: michael@0: class MSetElementCache michael@0: : public MSetElementInstruction, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: bool strict_; michael@0: bool guardHoles_; michael@0: michael@0: MSetElementCache(MDefinition *obj, MDefinition *index, MDefinition *value, bool strict, michael@0: bool guardHoles) michael@0: : MSetElementInstruction(obj, index, value), michael@0: strict_(strict), michael@0: guardHoles_(guardHoles) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetElementCache); michael@0: michael@0: static MSetElementCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *index, michael@0: MDefinition *value, bool strict, bool guardHoles) michael@0: { michael@0: return new(alloc) MSetElementCache(obj, index, value, strict, guardHoles); michael@0: } michael@0: michael@0: bool strict() const { michael@0: return strict_; michael@0: } michael@0: bool guardHoles() const { michael@0: return guardHoles_; michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool canConsumeFloat32(MUse *use) const { return use->index() == 2; } michael@0: }; michael@0: michael@0: class MCallGetProperty michael@0: : public MUnaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: CompilerRootPropertyName name_; michael@0: bool idempotent_; michael@0: bool callprop_; michael@0: michael@0: MCallGetProperty(MDefinition *value, PropertyName *name, bool callprop) michael@0: : MUnaryInstruction(value), name_(name), michael@0: idempotent_(false), michael@0: callprop_(callprop) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallGetProperty) michael@0: michael@0: static MCallGetProperty *New(TempAllocator &alloc, MDefinition *value, PropertyName *name, michael@0: bool callprop) michael@0: { michael@0: return new(alloc) MCallGetProperty(value, name, callprop); michael@0: } michael@0: MDefinition *value() const { michael@0: return getOperand(0); michael@0: } michael@0: PropertyName *name() const { michael@0: return name_; michael@0: } michael@0: bool callprop() const { michael@0: return callprop_; michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: // Constructors need to perform a GetProp on the function prototype. michael@0: // Since getters cannot be set on the prototype, fetching is non-effectful. michael@0: // The operation may be safely repeated in case of bailout. michael@0: void setIdempotent() { michael@0: idempotent_ = true; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: if (!idempotent_) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Inline call to handle lhs[rhs]. The first input is a Value so that this michael@0: // instruction can handle both objects and strings. michael@0: class MCallGetElement michael@0: : public MBinaryInstruction, michael@0: public BoxInputsPolicy michael@0: { michael@0: MCallGetElement(MDefinition *lhs, MDefinition *rhs) michael@0: : MBinaryInstruction(lhs, rhs) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallGetElement) michael@0: michael@0: static MCallGetElement *New(TempAllocator &alloc, MDefinition *lhs, MDefinition *rhs) { michael@0: return new(alloc) MCallGetElement(lhs, rhs); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MCallSetElement michael@0: : public MSetElementInstruction, michael@0: public CallSetElementPolicy michael@0: { michael@0: MCallSetElement(MDefinition *object, MDefinition *index, MDefinition *value) michael@0: : MSetElementInstruction(object, index, value) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallSetElement) michael@0: michael@0: static MCallSetElement *New(TempAllocator &alloc, MDefinition *object, MDefinition *index, michael@0: MDefinition *value) michael@0: { michael@0: return new(alloc) MCallSetElement(object, index, value); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MCallInitElementArray michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: uint32_t index_; michael@0: michael@0: MCallInitElementArray(MDefinition *obj, uint32_t index, MDefinition *val) michael@0: : index_(index) michael@0: { michael@0: setOperand(0, obj); michael@0: setOperand(1, val); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallInitElementArray) michael@0: michael@0: static MCallInitElementArray *New(TempAllocator &alloc, MDefinition *obj, uint32_t index, michael@0: MDefinition *val) michael@0: { michael@0: return new(alloc) MCallInitElementArray(obj, index, val); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: uint32_t index() const { michael@0: return index_; michael@0: } michael@0: michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MSetDOMProperty michael@0: : public MAryInstruction<2>, michael@0: public MixPolicy, BoxPolicy<1> > michael@0: { michael@0: const JSJitSetterOp func_; michael@0: michael@0: MSetDOMProperty(const JSJitSetterOp func, MDefinition *obj, MDefinition *val) michael@0: : func_(func) michael@0: { michael@0: setOperand(0, obj); michael@0: setOperand(1, val); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetDOMProperty) michael@0: michael@0: static MSetDOMProperty *New(TempAllocator &alloc, const JSJitSetterOp func, MDefinition *obj, michael@0: MDefinition *val) michael@0: { michael@0: return new(alloc) MSetDOMProperty(func, obj, val); michael@0: } michael@0: michael@0: const JSJitSetterOp fun() { michael@0: return func_; michael@0: } michael@0: michael@0: MDefinition *object() { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *value() michael@0: { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MGetDOMProperty michael@0: : public MAryInstruction<2>, michael@0: public ObjectPolicy<0> michael@0: { michael@0: const JSJitInfo *info_; michael@0: michael@0: protected: michael@0: MGetDOMProperty(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard) michael@0: : info_(jitinfo) michael@0: { michael@0: JS_ASSERT(jitinfo); michael@0: JS_ASSERT(jitinfo->type() == JSJitInfo::Getter); michael@0: michael@0: setOperand(0, obj); michael@0: michael@0: // Pin the guard as an operand if we want to hoist later michael@0: setOperand(1, guard); michael@0: michael@0: // We are movable iff the jitinfo says we can be. michael@0: if (isDomMovable()) { michael@0: JS_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything); michael@0: setMovable(); michael@0: } else { michael@0: // If we're not movable, that means we shouldn't be DCEd either, michael@0: // because we might throw an exception when called, and getting rid michael@0: // of that is observable. michael@0: setGuard(); michael@0: } michael@0: michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: const JSJitInfo *info() const { michael@0: return info_; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetDOMProperty) michael@0: michael@0: static MGetDOMProperty *New(TempAllocator &alloc, const JSJitInfo *info, MDefinition *obj, michael@0: MDefinition *guard) michael@0: { michael@0: return new(alloc) MGetDOMProperty(info, obj, guard); michael@0: } michael@0: michael@0: const JSJitGetterOp fun() { michael@0: return info_->getter; michael@0: } michael@0: bool isInfallible() const { michael@0: return info_->isInfallible; michael@0: } michael@0: bool isDomMovable() const { michael@0: return info_->isMovable; michael@0: } michael@0: JSJitInfo::AliasSet domAliasSet() const { michael@0: return info_->aliasSet(); michael@0: } michael@0: size_t domMemberSlotIndex() const { michael@0: MOZ_ASSERT(info_->isInSlot); michael@0: return info_->slotIndex; michael@0: } michael@0: MDefinition *object() { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!isDomMovable()) michael@0: return false; michael@0: michael@0: if (!ins->isGetDOMProperty()) michael@0: return false; michael@0: michael@0: // Checking the jitinfo is the same as checking the constant function michael@0: if (!(info() == ins->toGetDOMProperty()->info())) michael@0: return false; michael@0: michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: JSJitInfo::AliasSet aliasSet = domAliasSet(); michael@0: if (aliasSet == JSJitInfo::AliasNone) michael@0: return AliasSet::None(); michael@0: if (aliasSet == JSJitInfo::AliasDOMSets) michael@0: return AliasSet::Load(AliasSet::DOMProperty); michael@0: JS_ASSERT(aliasSet == JSJitInfo::AliasEverything); michael@0: return AliasSet::Store(AliasSet::Any); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MGetDOMMember : public MGetDOMProperty michael@0: { michael@0: // We inherit everything from MGetDOMProperty except our possiblyCalls value michael@0: MGetDOMMember(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard) michael@0: : MGetDOMProperty(jitinfo, obj, guard) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetDOMMember) michael@0: michael@0: static MGetDOMMember *New(TempAllocator &alloc, const JSJitInfo *info, MDefinition *obj, michael@0: MDefinition *guard) michael@0: { michael@0: return new(alloc) MGetDOMMember(info, obj, guard); michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return false; michael@0: } michael@0: }; michael@0: michael@0: class MStringLength michael@0: : public MUnaryInstruction, michael@0: public StringPolicy<0> michael@0: { michael@0: MStringLength(MDefinition *string) michael@0: : MUnaryInstruction(string) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: public: michael@0: INSTRUCTION_HEADER(StringLength) michael@0: michael@0: static MStringLength *New(TempAllocator &alloc, MDefinition *string) { michael@0: return new(alloc) MStringLength(string); michael@0: } michael@0: michael@0: MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *string() const { michael@0: return getOperand(0); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // The string |length| property is immutable, so there is no michael@0: // implicit dependency. michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // Inlined version of Math.floor(). michael@0: class MFloor michael@0: : public MUnaryInstruction, michael@0: public FloatingPointPolicy<0> michael@0: { michael@0: MFloor(MDefinition *num) michael@0: : MUnaryInstruction(num) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setPolicyType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Floor) michael@0: michael@0: static MFloor *New(TempAllocator &alloc, MDefinition *num) { michael@0: return new(alloc) MFloor(num); michael@0: } michael@0: michael@0: MDefinition *num() const { michael@0: return getOperand(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool isFloat32Commutative() const { michael@0: return true; michael@0: } michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: // Inlined version of Math.round(). michael@0: class MRound michael@0: : public MUnaryInstruction, michael@0: public FloatingPointPolicy<0> michael@0: { michael@0: MRound(MDefinition *num) michael@0: : MUnaryInstruction(num) michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setPolicyType(MIRType_Double); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Round) michael@0: michael@0: static MRound *New(TempAllocator &alloc, MDefinition *num) { michael@0: return new(alloc) MRound(num); michael@0: } michael@0: michael@0: MDefinition *num() const { michael@0: return getOperand(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool isFloat32Commutative() const { michael@0: return true; michael@0: } michael@0: void trySpecializeFloat32(TempAllocator &alloc); michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: return true; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: class MIteratorStart michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: uint8_t flags_; michael@0: michael@0: MIteratorStart(MDefinition *obj, uint8_t flags) michael@0: : MUnaryInstruction(obj), flags_(flags) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(IteratorStart) michael@0: michael@0: static MIteratorStart *New(TempAllocator &alloc, MDefinition *obj, uint8_t flags) { michael@0: return new(alloc) MIteratorStart(obj, flags); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: uint8_t flags() const { michael@0: return flags_; michael@0: } michael@0: }; michael@0: michael@0: class MIteratorNext michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MIteratorNext(MDefinition *iter) michael@0: : MUnaryInstruction(iter) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(IteratorNext) michael@0: michael@0: static MIteratorNext *New(TempAllocator &alloc, MDefinition *iter) { michael@0: return new(alloc) MIteratorNext(iter); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *iterator() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: class MIteratorMore michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MIteratorMore(MDefinition *iter) michael@0: : MUnaryInstruction(iter) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(IteratorMore) michael@0: michael@0: static MIteratorMore *New(TempAllocator &alloc, MDefinition *iter) { michael@0: return new(alloc) MIteratorMore(iter); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *iterator() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: class MIteratorEnd michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MIteratorEnd(MDefinition *iter) michael@0: : MUnaryInstruction(iter) michael@0: { } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(IteratorEnd) michael@0: michael@0: static MIteratorEnd *New(TempAllocator &alloc, MDefinition *iter) { michael@0: return new(alloc) MIteratorEnd(iter); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: MDefinition *iterator() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: // Implementation for 'in' operator. michael@0: class MIn michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: MIn(MDefinition *key, MDefinition *obj) michael@0: : MBinaryInstruction(key, obj) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(In) michael@0: michael@0: static MIn *New(TempAllocator &alloc, MDefinition *key, MDefinition *obj) { michael@0: return new(alloc) MIn(key, obj); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: michael@0: // Test whether the index is in the array bounds or a hole. michael@0: class MInArray michael@0: : public MQuaternaryInstruction, michael@0: public ObjectPolicy<3> michael@0: { michael@0: bool needsHoleCheck_; michael@0: bool needsNegativeIntCheck_; michael@0: michael@0: MInArray(MDefinition *elements, MDefinition *index, michael@0: MDefinition *initLength, MDefinition *object, michael@0: bool needsHoleCheck) michael@0: : MQuaternaryInstruction(elements, index, initLength, object), michael@0: needsHoleCheck_(needsHoleCheck), michael@0: needsNegativeIntCheck_(true) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: JS_ASSERT(elements->type() == MIRType_Elements); michael@0: JS_ASSERT(index->type() == MIRType_Int32); michael@0: JS_ASSERT(initLength->type() == MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InArray) michael@0: michael@0: static MInArray *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, michael@0: MDefinition *initLength, MDefinition *object, michael@0: bool needsHoleCheck) michael@0: { michael@0: return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck); michael@0: } michael@0: michael@0: MDefinition *elements() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *index() const { michael@0: return getOperand(1); michael@0: } michael@0: MDefinition *initLength() const { michael@0: return getOperand(2); michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(3); michael@0: } michael@0: bool needsHoleCheck() const { michael@0: return needsHoleCheck_; michael@0: } michael@0: bool needsNegativeIntCheck() const { michael@0: return needsNegativeIntCheck_; michael@0: } michael@0: void collectRangeInfoPreTrunc(); michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::Element); michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: if (!ins->isInArray()) michael@0: return false; michael@0: const MInArray *other = ins->toInArray(); michael@0: if (needsHoleCheck() != other->needsHoleCheck()) michael@0: return false; michael@0: if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) michael@0: return false; michael@0: return congruentIfOperandsEqual(other); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: }; michael@0: michael@0: // Implementation for instanceof operator with specific rhs. michael@0: class MInstanceOf michael@0: : public MUnaryInstruction, michael@0: public InstanceOfPolicy michael@0: { michael@0: CompilerRootObject protoObj_; michael@0: michael@0: MInstanceOf(MDefinition *obj, JSObject *proto) michael@0: : MUnaryInstruction(obj), michael@0: protoObj_(proto) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(InstanceOf) michael@0: michael@0: static MInstanceOf *New(TempAllocator &alloc, MDefinition *obj, JSObject *proto) { michael@0: return new(alloc) MInstanceOf(obj, proto); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: JSObject *prototypeObject() { michael@0: return protoObj_; michael@0: } michael@0: }; michael@0: michael@0: // Implementation for instanceof operator with unknown rhs. michael@0: class MCallInstanceOf michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: MCallInstanceOf(MDefinition *obj, MDefinition *proto) michael@0: : MBinaryInstruction(obj, proto) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(CallInstanceOf) michael@0: michael@0: static MCallInstanceOf *New(TempAllocator &alloc, MDefinition *obj, MDefinition *proto) { michael@0: return new(alloc) MCallInstanceOf(obj, proto); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MArgumentsLength : public MNullaryInstruction michael@0: { michael@0: MArgumentsLength() michael@0: { michael@0: setResultType(MIRType_Int32); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ArgumentsLength) michael@0: michael@0: static MArgumentsLength *New(TempAllocator &alloc) { michael@0: return new(alloc) MArgumentsLength(); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // Arguments |length| cannot be mutated by Ion Code. michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: void computeRange(TempAllocator &alloc); michael@0: }; michael@0: michael@0: // This MIR instruction is used to get an argument from the actual arguments. michael@0: class MGetFrameArgument michael@0: : public MUnaryInstruction, michael@0: public IntPolicy<0> michael@0: { michael@0: bool scriptHasSetArg_; michael@0: michael@0: MGetFrameArgument(MDefinition *idx, bool scriptHasSetArg) michael@0: : MUnaryInstruction(idx), michael@0: scriptHasSetArg_(scriptHasSetArg) michael@0: { michael@0: setResultType(MIRType_Value); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GetFrameArgument) michael@0: michael@0: static MGetFrameArgument *New(TempAllocator &alloc, MDefinition *idx, bool scriptHasSetArg) { michael@0: return new(alloc) MGetFrameArgument(idx, scriptHasSetArg); michael@0: } michael@0: michael@0: MDefinition *index() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: // If the script doesn't have any JSOP_SETARG ops, then this instruction is never michael@0: // aliased. michael@0: if (scriptHasSetArg_) michael@0: return AliasSet::Load(AliasSet::FrameArgument); michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // This MIR instruction is used to set an argument value in the frame. michael@0: class MSetFrameArgument michael@0: : public MUnaryInstruction, michael@0: public NoFloatPolicy<0> michael@0: { michael@0: uint32_t argno_; michael@0: michael@0: MSetFrameArgument(uint32_t argno, MDefinition *value) michael@0: : MUnaryInstruction(value), michael@0: argno_(argno) michael@0: { michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(SetFrameArgument) michael@0: michael@0: static MSetFrameArgument *New(TempAllocator &alloc, uint32_t argno, MDefinition *value) { michael@0: return new(alloc) MSetFrameArgument(argno, value); michael@0: } michael@0: michael@0: uint32_t argno() const { michael@0: return argno_; michael@0: } michael@0: michael@0: MDefinition *value() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return false; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::FrameArgument); michael@0: } michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: class MRestCommon michael@0: { michael@0: unsigned numFormals_; michael@0: CompilerRootObject templateObject_; michael@0: michael@0: protected: michael@0: MRestCommon(unsigned numFormals, JSObject *templateObject) michael@0: : numFormals_(numFormals), michael@0: templateObject_(templateObject) michael@0: { } michael@0: michael@0: public: michael@0: unsigned numFormals() const { michael@0: return numFormals_; michael@0: } michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: }; michael@0: michael@0: class MRest michael@0: : public MUnaryInstruction, michael@0: public MRestCommon, michael@0: public IntPolicy<0> michael@0: { michael@0: MRest(types::CompilerConstraintList *constraints, MDefinition *numActuals, unsigned numFormals, michael@0: JSObject *templateObject) michael@0: : MUnaryInstruction(numActuals), michael@0: MRestCommon(numFormals, templateObject) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(Rest); michael@0: michael@0: static MRest *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MDefinition *numActuals, unsigned numFormals, michael@0: JSObject *templateObject) michael@0: { michael@0: return new(alloc) MRest(constraints, numActuals, numFormals, templateObject); michael@0: } michael@0: michael@0: MDefinition *numActuals() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MRestPar michael@0: : public MBinaryInstruction, michael@0: public MRestCommon, michael@0: public IntPolicy<1> michael@0: { michael@0: MRestPar(MDefinition *cx, MDefinition *numActuals, unsigned numFormals, michael@0: JSObject *templateObject, types::TemporaryTypeSet *resultTypes) michael@0: : MBinaryInstruction(cx, numActuals), michael@0: MRestCommon(numFormals, templateObject) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: setResultTypeSet(resultTypes); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RestPar); michael@0: michael@0: static MRestPar *New(TempAllocator &alloc, MDefinition *cx, MRest *rest) { michael@0: return new(alloc) MRestPar(cx, rest->numActuals(), rest->numFormals(), michael@0: rest->templateObject(), rest->resultTypeSet()); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *numActuals() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // Guard on an object being safe for writes by current parallel cx. michael@0: // Must be either thread-local or else a handle into the destination array. michael@0: class MGuardThreadExclusive michael@0: : public MBinaryInstruction, michael@0: public ObjectPolicy<1> michael@0: { michael@0: MGuardThreadExclusive(MDefinition *cx, MDefinition *obj) michael@0: : MBinaryInstruction(cx, obj) michael@0: { michael@0: setResultType(MIRType_None); michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(GuardThreadExclusive); michael@0: michael@0: static MGuardThreadExclusive *New(TempAllocator &alloc, MDefinition *cx, MDefinition *obj) { michael@0: return new(alloc) MGuardThreadExclusive(cx, obj); michael@0: } michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: MDefinition *object() const { michael@0: return getOperand(1); michael@0: } michael@0: BailoutKind bailoutKind() const { michael@0: return Bailout_Normal; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MFilterTypeSet michael@0: : public MUnaryInstruction, michael@0: public FilterTypeSetPolicy michael@0: { michael@0: MFilterTypeSet(MDefinition *def, types::TemporaryTypeSet *types) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: JS_ASSERT(!types->unknown()); michael@0: setResultType(types->getKnownMIRType()); michael@0: setResultTypeSet(types); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(FilterTypeSet) michael@0: michael@0: static MFilterTypeSet *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) { michael@0: return new(alloc) MFilterTypeSet(def, types); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *def) const { michael@0: return false; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: virtual bool neverHoist() const { michael@0: return resultTypeSet()->empty(); michael@0: } michael@0: }; michael@0: michael@0: // Given a value, guard that the value is in a particular TypeSet, then returns michael@0: // that value. michael@0: class MTypeBarrier michael@0: : public MUnaryInstruction, michael@0: public TypeBarrierPolicy michael@0: { michael@0: MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types) michael@0: : MUnaryInstruction(def) michael@0: { michael@0: JS_ASSERT(!types->unknown()); michael@0: setResultType(types->getKnownMIRType()); michael@0: setResultTypeSet(types); michael@0: michael@0: setGuard(); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(TypeBarrier) michael@0: michael@0: static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) { michael@0: return new(alloc) MTypeBarrier(def, types); michael@0: } michael@0: michael@0: void printOpcode(FILE *fp) const; michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: bool congruentTo(const MDefinition *def) const { michael@0: return false; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: virtual bool neverHoist() const { michael@0: return resultTypeSet()->empty(); michael@0: } michael@0: michael@0: bool alwaysBails() const { michael@0: // If mirtype of input doesn't agree with mirtype of barrier, michael@0: // we will definitely bail. michael@0: MIRType type = resultTypeSet()->getKnownMIRType(); michael@0: if (type == MIRType_Value) michael@0: return false; michael@0: if (input()->type() == MIRType_Value) michael@0: return false; michael@0: return input()->type() != type; michael@0: } michael@0: }; michael@0: michael@0: // Like MTypeBarrier, guard that the value is in the given type set. This is michael@0: // used before property writes to ensure the value being written is represented michael@0: // in the property types for the object. michael@0: class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy michael@0: { michael@0: const types::TemporaryTypeSet *typeSet_; michael@0: michael@0: MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types) michael@0: : MUnaryInstruction(def), michael@0: typeSet_(types) michael@0: { michael@0: setGuard(); michael@0: JS_ASSERT(!types->unknown()); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(MonitorTypes) michael@0: michael@0: static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types) { michael@0: return new(alloc) MMonitorTypes(def, types); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: const types::TemporaryTypeSet *typeSet() const { michael@0: return typeSet_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Given a value being written to another object, update the generational store michael@0: // buffer if the value is in the nursery and object is in the tenured heap. michael@0: class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0> michael@0: { michael@0: MPostWriteBarrier(MDefinition *obj, MDefinition *value) michael@0: : MBinaryInstruction(obj, value) michael@0: { michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(PostWriteBarrier) michael@0: michael@0: static MPostWriteBarrier *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) { michael@0: return new(alloc) MPostWriteBarrier(obj, value); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *value() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: bool isConsistentFloat32Use(MUse *use) const { michael@0: // During lowering, values that neither have object nor value MIR type michael@0: // are ignored, thus Float32 can show up at this point without any issue. michael@0: return use->index() == 1; michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: class MNewSlots : public MNullaryInstruction michael@0: { michael@0: unsigned nslots_; michael@0: michael@0: MNewSlots(unsigned nslots) michael@0: : nslots_(nslots) michael@0: { michael@0: setResultType(MIRType_Slots); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewSlots) michael@0: michael@0: static MNewSlots *New(TempAllocator &alloc, unsigned nslots) { michael@0: return new(alloc) MNewSlots(nslots); michael@0: } michael@0: unsigned nslots() const { michael@0: return nslots_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class MNewDeclEnvObject : public MNullaryInstruction michael@0: { michael@0: CompilerRootObject templateObj_; michael@0: michael@0: MNewDeclEnvObject(JSObject *templateObj) michael@0: : MNullaryInstruction(), michael@0: templateObj_(templateObj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewDeclEnvObject); michael@0: michael@0: static MNewDeclEnvObject *New(TempAllocator &alloc, JSObject *templateObj) { michael@0: return new(alloc) MNewDeclEnvObject(templateObj); michael@0: } michael@0: michael@0: JSObject *templateObj() { michael@0: return templateObj_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MNewCallObjectBase : public MUnaryInstruction michael@0: { michael@0: CompilerRootObject templateObj_; michael@0: michael@0: protected: michael@0: MNewCallObjectBase(JSObject *templateObj, MDefinition *slots) michael@0: : MUnaryInstruction(slots), michael@0: templateObj_(templateObj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: MDefinition *slots() { michael@0: return getOperand(0); michael@0: } michael@0: JSObject *templateObject() { michael@0: return templateObj_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MNewCallObject : public MNewCallObjectBase michael@0: { michael@0: public: michael@0: INSTRUCTION_HEADER(NewCallObject) michael@0: michael@0: MNewCallObject(JSObject *templateObj, MDefinition *slots) michael@0: : MNewCallObjectBase(templateObj, slots) michael@0: {} michael@0: michael@0: static MNewCallObject * michael@0: New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) michael@0: { michael@0: return new(alloc) MNewCallObject(templateObj, slots); michael@0: } michael@0: }; michael@0: michael@0: class MNewRunOnceCallObject : public MNewCallObjectBase michael@0: { michael@0: public: michael@0: INSTRUCTION_HEADER(NewRunOnceCallObject) michael@0: michael@0: MNewRunOnceCallObject(JSObject *templateObj, MDefinition *slots) michael@0: : MNewCallObjectBase(templateObj, slots) michael@0: {} michael@0: michael@0: static MNewRunOnceCallObject * michael@0: New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) michael@0: { michael@0: return new(alloc) MNewRunOnceCallObject(templateObj, slots); michael@0: } michael@0: }; michael@0: michael@0: class MNewCallObjectPar : public MBinaryInstruction michael@0: { michael@0: CompilerRootObject templateObj_; michael@0: michael@0: MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots) michael@0: : MBinaryInstruction(cx, slots), michael@0: templateObj_(templateObj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewCallObjectPar); michael@0: michael@0: static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObjectBase *callObj) { michael@0: return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots()); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *slots() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: JSObject *templateObj() const { michael@0: return templateObj_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MNewStringObject : michael@0: public MUnaryInstruction, michael@0: public ConvertToStringPolicy<0> michael@0: { michael@0: CompilerRootObject templateObj_; michael@0: michael@0: MNewStringObject(MDefinition *input, JSObject *templateObj) michael@0: : MUnaryInstruction(input), michael@0: templateObj_(templateObj) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewStringObject) michael@0: michael@0: static MNewStringObject *New(TempAllocator &alloc, MDefinition *input, JSObject *templateObj) { michael@0: return new(alloc) MNewStringObject(input, templateObj); michael@0: } michael@0: michael@0: StringObject *templateObj() const; michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: // Node that represents that a script has begun executing. This comes at the michael@0: // start of the function and is called once per function (including inline michael@0: // ones) michael@0: class MProfilerStackOp : public MNullaryInstruction michael@0: { michael@0: public: michael@0: enum Type { michael@0: Enter, // a function has begun executing and it is not inline michael@0: Exit, // any function has exited (inlined or normal) michael@0: InlineEnter, // an inline function has begun executing michael@0: michael@0: InlineExit // all instructions of an inline function are done, a michael@0: // return from the inline function could have occurred michael@0: // before this boundary michael@0: }; michael@0: michael@0: private: michael@0: JSScript *script_; michael@0: Type type_; michael@0: unsigned inlineLevel_; michael@0: michael@0: MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel) michael@0: : script_(script), type_(type), inlineLevel_(inlineLevel) michael@0: { michael@0: JS_ASSERT_IF(type != InlineExit, script != nullptr); michael@0: JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0); michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(ProfilerStackOp) michael@0: michael@0: static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type, michael@0: unsigned inlineLevel = 0) { michael@0: return new(alloc) MProfilerStackOp(script, type, inlineLevel); michael@0: } michael@0: michael@0: JSScript *script() { michael@0: return script_; michael@0: } michael@0: michael@0: Type type() { michael@0: return type_; michael@0: } michael@0: michael@0: unsigned inlineLevel() { michael@0: return inlineLevel_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // This is an alias for MLoadFixedSlot. michael@0: class MEnclosingScope : public MLoadFixedSlot michael@0: { michael@0: MEnclosingScope(MDefinition *obj) michael@0: : MLoadFixedSlot(obj, ScopeObject::enclosingScopeSlot()) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: static MEnclosingScope *New(TempAllocator &alloc, MDefinition *obj) { michael@0: return new(alloc) MEnclosingScope(obj); michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: // ScopeObject reserved slots are immutable. michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Creates a dense array of the given length. michael@0: // michael@0: // Note: the template object should be an *empty* dense array! michael@0: class MNewDenseArrayPar : public MBinaryInstruction michael@0: { michael@0: CompilerRootObject templateObject_; michael@0: michael@0: MNewDenseArrayPar(MDefinition *cx, MDefinition *length, JSObject *templateObject) michael@0: : MBinaryInstruction(cx, length), michael@0: templateObject_(templateObject) michael@0: { michael@0: setResultType(MIRType_Object); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(NewDenseArrayPar); michael@0: michael@0: static MNewDenseArrayPar *New(TempAllocator &alloc, MDefinition *cx, MDefinition *length, michael@0: JSObject *templateObject) michael@0: { michael@0: return new(alloc) MNewDenseArrayPar(cx, length, templateObject); michael@0: } michael@0: michael@0: MDefinition *forkJoinContext() const { michael@0: return getOperand(0); michael@0: } michael@0: michael@0: MDefinition *length() const { michael@0: return getOperand(1); michael@0: } michael@0: michael@0: JSObject *templateObject() const { michael@0: return templateObject_; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: // A resume point contains the information needed to reconstruct the Baseline michael@0: // state from a position in the JIT. See the big comment near resumeAfter() in michael@0: // IonBuilder.cpp. michael@0: class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode michael@0: { michael@0: public: michael@0: enum Mode { michael@0: ResumeAt, // Resume until before the current instruction michael@0: ResumeAfter, // Resume after the current instruction michael@0: Outer // State before inlining. michael@0: }; michael@0: michael@0: private: michael@0: friend class MBasicBlock; michael@0: friend void AssertBasicGraphCoherency(MIRGraph &graph); michael@0: michael@0: FixedList operands_; michael@0: uint32_t stackDepth_; michael@0: jsbytecode *pc_; michael@0: MResumePoint *caller_; michael@0: MInstruction *instruction_; michael@0: Mode mode_; michael@0: michael@0: MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode); michael@0: void inherit(MBasicBlock *state); michael@0: michael@0: protected: michael@0: // Initializes operands_ to an empty array of a fixed length. michael@0: // The array may then be filled in by inherit(). michael@0: bool init(TempAllocator &alloc) { michael@0: return operands_.init(alloc, stackDepth_); michael@0: } michael@0: michael@0: // Overwrites an operand without updating its Uses. michael@0: void setOperand(size_t index, MDefinition *operand) { michael@0: JS_ASSERT(index < stackDepth_); michael@0: operands_[index].set(operand, this, index); michael@0: operand->addUse(&operands_[index]); michael@0: } michael@0: michael@0: void clearOperand(size_t index) { michael@0: JS_ASSERT(index < stackDepth_); michael@0: operands_[index].set(nullptr, this, index); michael@0: } michael@0: michael@0: MUse *getUseFor(size_t index) { michael@0: return &operands_[index]; michael@0: } michael@0: michael@0: public: michael@0: static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, michael@0: MResumePoint *parent, Mode mode); michael@0: michael@0: MNode::Kind kind() const { michael@0: return MNode::ResumePoint; michael@0: } michael@0: size_t numOperands() const { michael@0: return stackDepth_; michael@0: } michael@0: MDefinition *getOperand(size_t index) const { michael@0: JS_ASSERT(index < stackDepth_); michael@0: return operands_[index].producer(); michael@0: } michael@0: jsbytecode *pc() const { michael@0: return pc_; michael@0: } michael@0: uint32_t stackDepth() const { michael@0: return stackDepth_; michael@0: } michael@0: MResumePoint *caller() { michael@0: return caller_; michael@0: } michael@0: void setCaller(MResumePoint *caller) { michael@0: caller_ = caller; michael@0: } michael@0: uint32_t frameCount() const { michael@0: uint32_t count = 1; michael@0: for (MResumePoint *it = caller_; it; it = it->caller_) michael@0: count++; michael@0: return count; michael@0: } michael@0: MInstruction *instruction() { michael@0: return instruction_; michael@0: } michael@0: void setInstruction(MInstruction *ins) { michael@0: instruction_ = ins; michael@0: } michael@0: Mode mode() const { michael@0: return mode_; michael@0: } michael@0: michael@0: void discardUses() { michael@0: for (size_t i = 0; i < stackDepth_; i++) { michael@0: if (operands_[i].hasProducer()) michael@0: operands_[i].producer()->removeUse(&operands_[i]); michael@0: } michael@0: } michael@0: michael@0: bool writeRecoverData(CompactBufferWriter &writer) const; michael@0: }; michael@0: michael@0: class MIsCallable michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: MIsCallable(MDefinition *object) michael@0: : MUnaryInstruction(object) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(IsCallable); michael@0: michael@0: static MIsCallable *New(TempAllocator &alloc, MDefinition *obj) { michael@0: return new(alloc) MIsCallable(obj); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MHaveSameClass michael@0: : public MBinaryInstruction, michael@0: public MixPolicy, ObjectPolicy<1> > michael@0: { michael@0: MHaveSameClass(MDefinition *left, MDefinition *right) michael@0: : MBinaryInstruction(left, right) michael@0: { michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(HaveSameClass); michael@0: michael@0: static MHaveSameClass *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { michael@0: return new(alloc) MHaveSameClass(left, right); michael@0: } michael@0: michael@0: TypePolicy *typePolicy() { michael@0: return this; michael@0: } michael@0: bool congruentTo(const MDefinition *ins) const { michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MHasClass michael@0: : public MUnaryInstruction, michael@0: public SingleObjectPolicy michael@0: { michael@0: const Class *class_; michael@0: michael@0: MHasClass(MDefinition *object, const Class *clasp) michael@0: : MUnaryInstruction(object) michael@0: , class_(clasp) michael@0: { michael@0: JS_ASSERT(object->type() == MIRType_Object); michael@0: setResultType(MIRType_Boolean); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(HasClass); michael@0: michael@0: static MHasClass *New(TempAllocator &alloc, MDefinition *obj, const Class *clasp) { michael@0: return new(alloc) MHasClass(obj, clasp); michael@0: } michael@0: michael@0: MDefinition *object() const { michael@0: return getOperand(0); michael@0: } michael@0: const Class *getClass() const { michael@0: return class_; michael@0: } michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: // Increase the usecount of the provided script upon execution and test if michael@0: // the usecount surpasses the threshold. Upon hit it will recompile the michael@0: // outermost script (i.e. not the inlined script). michael@0: class MRecompileCheck : public MNullaryInstruction michael@0: { michael@0: JSScript *script_; michael@0: uint32_t recompileThreshold_; michael@0: michael@0: MRecompileCheck(JSScript *script, uint32_t recompileThreshold) michael@0: : script_(script), michael@0: recompileThreshold_(recompileThreshold) michael@0: { michael@0: setGuard(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(RecompileCheck); michael@0: michael@0: static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t useCount) { michael@0: return new(alloc) MRecompileCheck(script_, useCount); michael@0: } michael@0: michael@0: JSScript *script() const { michael@0: return script_; michael@0: } michael@0: michael@0: uint32_t recompileThreshold() const { michael@0: return recompileThreshold_; michael@0: } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::None(); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSNeg : public MUnaryInstruction michael@0: { michael@0: MAsmJSNeg(MDefinition *op, MIRType type) michael@0: : MUnaryInstruction(op) michael@0: { michael@0: setResultType(type); michael@0: setMovable(); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSNeg); michael@0: static MAsmJSNeg *NewAsmJS(TempAllocator &alloc, MDefinition *op, MIRType type) { michael@0: return new(alloc) MAsmJSNeg(op, type); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSHeapAccess michael@0: { michael@0: ArrayBufferView::ViewType viewType_; michael@0: bool skipBoundsCheck_; michael@0: michael@0: public: michael@0: MAsmJSHeapAccess(ArrayBufferView::ViewType vt, bool s) michael@0: : viewType_(vt), skipBoundsCheck_(s) michael@0: {} michael@0: michael@0: ArrayBufferView::ViewType viewType() const { return viewType_; } michael@0: bool skipBoundsCheck() const { return skipBoundsCheck_; } michael@0: void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; } michael@0: }; michael@0: michael@0: class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess michael@0: { michael@0: MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr) michael@0: : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false) michael@0: { michael@0: setMovable(); michael@0: if (vt == ArrayBufferView::TYPE_FLOAT32) michael@0: setResultType(MIRType_Float32); michael@0: else if (vt == ArrayBufferView::TYPE_FLOAT64) michael@0: setResultType(MIRType_Double); michael@0: else michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSLoadHeap); michael@0: michael@0: static MAsmJSLoadHeap *New(TempAllocator &alloc, ArrayBufferView::ViewType vt, MDefinition *ptr) { michael@0: return new(alloc) MAsmJSLoadHeap(vt, ptr); michael@0: } michael@0: michael@0: MDefinition *ptr() const { return getOperand(0); } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const; michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Load(AliasSet::AsmJSHeap); michael@0: } michael@0: bool mightAlias(const MDefinition *def) const; michael@0: }; michael@0: michael@0: class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess michael@0: { michael@0: MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) michael@0: : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false) michael@0: {} michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSStoreHeap); michael@0: michael@0: static MAsmJSStoreHeap *New(TempAllocator &alloc, ArrayBufferView::ViewType vt, michael@0: MDefinition *ptr, MDefinition *v) michael@0: { michael@0: return new(alloc) MAsmJSStoreHeap(vt, ptr, v); michael@0: } michael@0: michael@0: MDefinition *ptr() const { return getOperand(0); } michael@0: MDefinition *value() const { return getOperand(1); } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::AsmJSHeap); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSLoadGlobalVar : public MNullaryInstruction michael@0: { michael@0: MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant) michael@0: : globalDataOffset_(globalDataOffset), isConstant_(isConstant) michael@0: { michael@0: JS_ASSERT(IsNumberType(type)); michael@0: setResultType(type); michael@0: setMovable(); michael@0: } michael@0: michael@0: unsigned globalDataOffset_; michael@0: bool isConstant_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSLoadGlobalVar); michael@0: michael@0: static MAsmJSLoadGlobalVar *New(TempAllocator &alloc, MIRType type, unsigned globalDataOffset, michael@0: bool isConstant) michael@0: { michael@0: return new(alloc) MAsmJSLoadGlobalVar(type, globalDataOffset, isConstant); michael@0: } michael@0: michael@0: unsigned globalDataOffset() const { return globalDataOffset_; } michael@0: michael@0: bool congruentTo(const MDefinition *ins) const; michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return isConstant_ ? AliasSet::None() : AliasSet::Load(AliasSet::AsmJSGlobalVar); michael@0: } michael@0: michael@0: bool mightAlias(const MDefinition *def) const; michael@0: }; michael@0: michael@0: class MAsmJSStoreGlobalVar : public MUnaryInstruction michael@0: { michael@0: MAsmJSStoreGlobalVar(unsigned globalDataOffset, MDefinition *v) michael@0: : MUnaryInstruction(v), globalDataOffset_(globalDataOffset) michael@0: {} michael@0: michael@0: unsigned globalDataOffset_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSStoreGlobalVar); michael@0: michael@0: static MAsmJSStoreGlobalVar *New(TempAllocator &alloc, unsigned globalDataOffset, MDefinition *v) { michael@0: return new(alloc) MAsmJSStoreGlobalVar(globalDataOffset, v); michael@0: } michael@0: michael@0: unsigned globalDataOffset() const { return globalDataOffset_; } michael@0: MDefinition *value() const { return getOperand(0); } michael@0: michael@0: AliasSet getAliasSet() const { michael@0: return AliasSet::Store(AliasSet::AsmJSGlobalVar); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSLoadFuncPtr : public MUnaryInstruction michael@0: { michael@0: MAsmJSLoadFuncPtr(unsigned globalDataOffset, MDefinition *index) michael@0: : MUnaryInstruction(index), globalDataOffset_(globalDataOffset) michael@0: { michael@0: setResultType(MIRType_Pointer); michael@0: } michael@0: michael@0: unsigned globalDataOffset_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSLoadFuncPtr); michael@0: michael@0: static MAsmJSLoadFuncPtr *New(TempAllocator &alloc, unsigned globalDataOffset, michael@0: MDefinition *index) michael@0: { michael@0: return new(alloc) MAsmJSLoadFuncPtr(globalDataOffset, index); michael@0: } michael@0: michael@0: unsigned globalDataOffset() const { return globalDataOffset_; } michael@0: MDefinition *index() const { return getOperand(0); } michael@0: }; michael@0: michael@0: class MAsmJSLoadFFIFunc : public MNullaryInstruction michael@0: { michael@0: MAsmJSLoadFFIFunc(unsigned globalDataOffset) michael@0: : globalDataOffset_(globalDataOffset) michael@0: { michael@0: setResultType(MIRType_Pointer); michael@0: } michael@0: michael@0: unsigned globalDataOffset_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSLoadFFIFunc); michael@0: michael@0: static MAsmJSLoadFFIFunc *New(TempAllocator &alloc, unsigned globalDataOffset) michael@0: { michael@0: return new(alloc) MAsmJSLoadFFIFunc(globalDataOffset); michael@0: } michael@0: michael@0: unsigned globalDataOffset() const { return globalDataOffset_; } michael@0: }; michael@0: michael@0: class MAsmJSParameter : public MNullaryInstruction michael@0: { michael@0: ABIArg abi_; michael@0: michael@0: MAsmJSParameter(ABIArg abi, MIRType mirType) michael@0: : abi_(abi) michael@0: { michael@0: setResultType(mirType); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSParameter); michael@0: michael@0: static MAsmJSParameter *New(TempAllocator &alloc, ABIArg abi, MIRType mirType) { michael@0: return new(alloc) MAsmJSParameter(abi, mirType); michael@0: } michael@0: michael@0: ABIArg abi() const { return abi_; } michael@0: }; michael@0: michael@0: class MAsmJSReturn : public MAryControlInstruction<1, 0> michael@0: { michael@0: MAsmJSReturn(MDefinition *ins) { michael@0: setOperand(0, ins); michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSReturn); michael@0: static MAsmJSReturn *New(TempAllocator &alloc, MDefinition *ins) { michael@0: return new(alloc) MAsmJSReturn(ins); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSVoidReturn : public MAryControlInstruction<0, 0> michael@0: { michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSVoidReturn); michael@0: static MAsmJSVoidReturn *New(TempAllocator &alloc) { michael@0: return new(alloc) MAsmJSVoidReturn(); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSPassStackArg : public MUnaryInstruction michael@0: { michael@0: MAsmJSPassStackArg(uint32_t spOffset, MDefinition *ins) michael@0: : MUnaryInstruction(ins), michael@0: spOffset_(spOffset) michael@0: {} michael@0: michael@0: uint32_t spOffset_; michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSPassStackArg); michael@0: static MAsmJSPassStackArg *New(TempAllocator &alloc, uint32_t spOffset, MDefinition *ins) { michael@0: return new(alloc) MAsmJSPassStackArg(spOffset, ins); michael@0: } michael@0: uint32_t spOffset() const { michael@0: return spOffset_; michael@0: } michael@0: void incrementOffset(uint32_t inc) { michael@0: spOffset_ += inc; michael@0: } michael@0: MDefinition *arg() const { michael@0: return getOperand(0); michael@0: } michael@0: }; michael@0: michael@0: class MAsmJSCall MOZ_FINAL : public MInstruction michael@0: { michael@0: public: michael@0: class Callee { michael@0: public: michael@0: enum Which { Internal, Dynamic, Builtin }; michael@0: private: michael@0: Which which_; michael@0: union { michael@0: Label *internal_; michael@0: MDefinition *dynamic_; michael@0: AsmJSImmKind builtin_; michael@0: } u; michael@0: public: michael@0: Callee() {} michael@0: Callee(Label *callee) : which_(Internal) { u.internal_ = callee; } michael@0: Callee(MDefinition *callee) : which_(Dynamic) { u.dynamic_ = callee; } michael@0: Callee(AsmJSImmKind callee) : which_(Builtin) { u.builtin_ = callee; } michael@0: Which which() const { return which_; } michael@0: Label *internal() const { JS_ASSERT(which_ == Internal); return u.internal_; } michael@0: MDefinition *dynamic() const { JS_ASSERT(which_ == Dynamic); return u.dynamic_; } michael@0: AsmJSImmKind builtin() const { JS_ASSERT(which_ == Builtin); return u.builtin_; } michael@0: }; michael@0: michael@0: private: michael@0: struct Operand { michael@0: AnyRegister reg; michael@0: MUse use; michael@0: }; michael@0: michael@0: CallSiteDesc desc_; michael@0: Callee callee_; michael@0: FixedList operands_; michael@0: FixedList argRegs_; michael@0: size_t spIncrement_; michael@0: michael@0: MAsmJSCall(const CallSiteDesc &desc, Callee callee, size_t spIncrement) michael@0: : desc_(desc), callee_(callee), spIncrement_(spIncrement) michael@0: { } michael@0: michael@0: protected: michael@0: void setOperand(size_t index, MDefinition *operand) { michael@0: operands_[index].set(operand, this, index); michael@0: operand->addUse(&operands_[index]); michael@0: } michael@0: MUse *getUseFor(size_t index) { michael@0: return &operands_[index]; michael@0: } michael@0: michael@0: public: michael@0: INSTRUCTION_HEADER(AsmJSCall); michael@0: michael@0: struct Arg { michael@0: AnyRegister reg; michael@0: MDefinition *def; michael@0: Arg(AnyRegister reg, MDefinition *def) : reg(reg), def(def) {} michael@0: }; michael@0: typedef Vector Args; michael@0: michael@0: static MAsmJSCall *New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee, michael@0: const Args &args, MIRType resultType, size_t spIncrement); michael@0: michael@0: size_t numOperands() const { michael@0: return operands_.length(); michael@0: } michael@0: MDefinition *getOperand(size_t index) const { michael@0: JS_ASSERT(index < numOperands()); michael@0: return operands_[index].producer(); michael@0: } michael@0: size_t numArgs() const { michael@0: return argRegs_.length(); michael@0: } michael@0: AnyRegister registerForArg(size_t index) const { michael@0: JS_ASSERT(index < numArgs()); michael@0: return argRegs_[index]; michael@0: } michael@0: const CallSiteDesc &desc() const { michael@0: return desc_; michael@0: } michael@0: Callee callee() const { michael@0: return callee_; michael@0: } michael@0: size_t dynamicCalleeOperandIndex() const { michael@0: JS_ASSERT(callee_.which() == Callee::Dynamic); michael@0: JS_ASSERT(numArgs() == numOperands() - 1); michael@0: return numArgs(); michael@0: } michael@0: size_t spIncrement() const { michael@0: return spIncrement_; michael@0: } michael@0: michael@0: bool possiblyCalls() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: #undef INSTRUCTION_HEADER michael@0: michael@0: // Implement opcode casts now that the compiler can see the inheritance. michael@0: #define OPCODE_CASTS(opcode) \ michael@0: M##opcode *MDefinition::to##opcode() \ michael@0: { \ michael@0: JS_ASSERT(is##opcode()); \ michael@0: return static_cast(this); \ michael@0: } \ michael@0: const M##opcode *MDefinition::to##opcode() const \ michael@0: { \ michael@0: JS_ASSERT(is##opcode()); \ michael@0: return static_cast(this); \ michael@0: } michael@0: MIR_OPCODE_LIST(OPCODE_CASTS) michael@0: #undef OPCODE_CASTS michael@0: michael@0: MDefinition *MNode::toDefinition() michael@0: { michael@0: JS_ASSERT(isDefinition()); michael@0: return (MDefinition *)this; michael@0: } michael@0: michael@0: MResumePoint *MNode::toResumePoint() michael@0: { michael@0: JS_ASSERT(isResumePoint()); michael@0: return (MResumePoint *)this; michael@0: } michael@0: michael@0: MInstruction *MDefinition::toInstruction() michael@0: { michael@0: JS_ASSERT(!isPhi()); michael@0: return (MInstruction *)this; michael@0: } michael@0: michael@0: typedef Vector MDefinitionVector; michael@0: michael@0: // Helper functions used to decide how to build MIR. michael@0: michael@0: bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id); michael@0: bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, michael@0: ScalarTypeDescr::Type *arrayType); michael@0: bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj); michael@0: bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj); michael@0: MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj); michael@0: bool PropertyReadNeedsTypeBarrier(JSContext *propertycx, michael@0: types::CompilerConstraintList *constraints, michael@0: types::TypeObjectKey *object, PropertyName *name, michael@0: types::TemporaryTypeSet *observed, bool updateObserved); michael@0: bool PropertyReadNeedsTypeBarrier(JSContext *propertycx, michael@0: types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed); michael@0: bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed); michael@0: bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name); michael@0: void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed); michael@0: bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MBasicBlock *current, MDefinition **pobj, michael@0: PropertyName *name, MDefinition **pvalue, michael@0: bool canModify); michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_MIR_h */