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