Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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
#ifndef jit_shared_LIR_shared_h
#define jit_shared_LIR_shared_h
#include "mozilla/Maybe.h"
#include "jit/AtomicOp.h"
#include "jit/shared/Assembler-shared.h"
#include "util/Memory.h"
// This file declares LIR instructions that are common to every platform.
namespace js {
namespace jit {
LIR_OPCODE_CLASS_GENERATED
#ifdef FUZZING_JS_FUZZILLI
class LFuzzilliHashT : public LInstructionHelper<1, 1, 2> {
public:
LIR_HEADER(FuzzilliHashT);
LFuzzilliHashT(const LAllocation& value, const LDefinition& temp,
const LDefinition& tempFloat)
: LInstructionHelper(classOpcode) {
setOperand(0, value);
setTemp(0, temp);
setTemp(1, tempFloat);
}
const LAllocation* value() { return getOperand(0); }
MFuzzilliHash* mir() const { return mir_->toFuzzilliHash(); }
};
class LFuzzilliHashV : public LInstructionHelper<1, BOX_PIECES, 2> {
public:
LIR_HEADER(FuzzilliHashV);
LFuzzilliHashV(const LBoxAllocation& value, const LDefinition& temp,
const LDefinition& tempFloat)
: LInstructionHelper(classOpcode) {
setBoxOperand(0, value);
setTemp(0, temp);
setTemp(1, tempFloat);
}
MFuzzilliHash* mir() const { return mir_->toFuzzilliHash(); }
};
class LFuzzilliHashStore : public LInstructionHelper<0, 1, 2> {
public:
LIR_HEADER(FuzzilliHashStore);
LFuzzilliHashStore(const LAllocation& value, const LDefinition& temp1,
const LDefinition& temp2)
: LInstructionHelper(classOpcode) {
setOperand(0, value);
setTemp(0, temp1);
setTemp(1, temp2);
}
const LAllocation* value() { return getOperand(0); }
MFuzzilliHashStore* mir() const { return mir_->toFuzzilliHashStore(); }
};
#endif
class LBox : public LInstructionHelper<BOX_PIECES, 1, 0> {
MIRType type_;
public:
LIR_HEADER(Box);
LBox(const LAllocation& payload, MIRType type)
: LInstructionHelper(classOpcode), type_(type) {
setOperand(0, payload);
}
MIRType type() const { return type_; }
const char* extraName() const { return StringFromMIRType(type_); }
};
template <size_t Temps, size_t ExtraUses = 0>
class LBinaryMath : public LInstructionHelper<1, 2 + ExtraUses, Temps> {
protected:
explicit LBinaryMath(LNode::Opcode opcode)
: LInstructionHelper<1, 2 + ExtraUses, Temps>(opcode) {}
public:
const LAllocation* lhs() { return this->getOperand(0); }
const LAllocation* rhs() { return this->getOperand(1); }
};
template <size_t Temps, size_t ExtraUses = 0>
class LUnaryMath : public LInstructionHelper<1, 1 + ExtraUses, Temps> {
protected:
explicit LUnaryMath(LNode::Opcode opcode)
: LInstructionHelper<1, 1 + ExtraUses, Temps>(opcode) {}
public:
const LAllocation* input() { return this->getOperand(0); }
};
// An LOsiPoint captures a snapshot after a call and ensures enough space to
// patch in a call to the invalidation mechanism.
//
// Note: LSafepoints are 1:1 with LOsiPoints, so it holds a reference to the
// corresponding LSafepoint to inform it of the LOsiPoint's masm offset when it
// gets GC'd.
class LOsiPoint : public LInstructionHelper<0, 0, 0> {
LSafepoint* safepoint_;
public:
LOsiPoint(LSafepoint* safepoint, LSnapshot* snapshot)
: LInstructionHelper(classOpcode), safepoint_(safepoint) {
MOZ_ASSERT(safepoint && snapshot);
assignSnapshot(snapshot);
}
LSafepoint* associatedSafepoint() { return safepoint_; }
LIR_HEADER(OsiPoint)
};
class LMove {
LAllocation from_;
LAllocation to_;
LDefinition::Type type_;
public:
LMove(LAllocation from, LAllocation to, LDefinition::Type type)
: from_(from), to_(to), type_(type) {}
LAllocation from() const { return from_; }
LAllocation to() const { return to_; }
LDefinition::Type type() const { return type_; }
};
class LMoveGroup : public LInstructionHelper<0, 0, 0> {
js::Vector<LMove, 2, JitAllocPolicy> moves_;
#ifdef JS_CODEGEN_X86
// Optional general register available for use when executing moves.
LAllocation scratchRegister_;
#endif
explicit LMoveGroup(TempAllocator& alloc)
: LInstructionHelper(classOpcode), moves_(alloc) {}
public:
LIR_HEADER(MoveGroup)
static LMoveGroup* New(TempAllocator& alloc) {
return new (alloc) LMoveGroup(alloc);
}
void printOperands(GenericPrinter& out);
// Add a move which takes place simultaneously with all others in the group.
bool add(LAllocation from, LAllocation to, LDefinition::Type type);
// Add a move which takes place after existing moves in the group.
bool addAfter(LAllocation from, LAllocation to, LDefinition::Type type);
size_t numMoves() const { return moves_.length(); }
const LMove& getMove(size_t i) const { return moves_[i]; }
#ifdef JS_CODEGEN_X86
void setScratchRegister(Register reg) { scratchRegister_ = LGeneralReg(reg); }
LAllocation maybeScratchRegister() { return scratchRegister_; }
#endif
bool uses(Register reg) {
for (size_t i = 0; i < numMoves(); i++) {
LMove move = getMove(i);
if (move.from() == LGeneralReg(reg) || move.to() == LGeneralReg(reg)) {
return true;
}
}
return false;
}
};
// A constant Value.
class LValue : public LInstructionHelper<BOX_PIECES, 0, 0> {
Value v_;
public:
LIR_HEADER(Value)
explicit LValue(const Value& v) : LInstructionHelper(classOpcode), v_(v) {}
Value value() const { return v_; }
};
// Base class for control instructions (goto, branch, etc.)
template <size_t Succs, size_t Operands, size_t Temps>
class LControlInstructionHelper
: public LInstructionHelper<0, Operands, Temps> {
mozilla::Array<MBasicBlock*, Succs> successors_;
protected:
explicit LControlInstructionHelper(LNode::Opcode opcode)
: LInstructionHelper<0, Operands, Temps>(opcode) {}
public:
size_t numSuccessors() const { return Succs; }
MBasicBlock* getSuccessor(size_t i) const { return successors_[i]; }
void setSuccessor(size_t i, MBasicBlock* successor) {
successors_[i] = successor;
}
};
// Jumps to the start of a basic block.
class LGoto : public LControlInstructionHelper<1, 0, 0> {
public:
LIR_HEADER(Goto)
explicit LGoto(MBasicBlock* block) : LControlInstructionHelper(classOpcode) {
setSuccessor(0, block);
}
MBasicBlock* target() const { return getSuccessor(0); }
};
class LNewArray : public LInstructionHelper<1, 0, 1> {
public:
LIR_HEADER(NewArray)
explicit LNewArray(const LDefinition& temp)
: LInstructionHelper(classOpcode) {
setTemp(0, temp);
}
const char* extraName() const {
return mir()->isVMCall() ? "VMCall" : nullptr;
}
const LDefinition* temp() { return getTemp(0); }
MNewArray* mir() const { return mir_->toNewArray(); }
};
class LNewObject : public LInstructionHelper<1, 0, 1> {
public:
LIR_HEADER(NewObject)
explicit LNewObject(const LDefinition& temp)
: LInstructionHelper(classOpcode) {
setTemp(0, temp);
}
const char* extraName() const {
return mir()->isVMCall() ? "VMCall" : nullptr;
}
const LDefinition* temp() { return getTemp(0); }
MNewObject* mir() const { return mir_->toNewObject(); }
};
template <size_t Defs, size_t Ops>
class LWasmReinterpretBase : public LInstructionHelper<Defs, Ops, 0> {
typedef LInstructionHelper<Defs, Ops, 0> Base;
protected:
explicit LWasmReinterpretBase(LNode::Opcode opcode) : Base(opcode) {}
public:
const LAllocation* input() { return Base::getOperand(0); }
MWasmReinterpret* mir() const { return Base::mir_->toWasmReinterpret(); }
};
class LWasmReinterpret : public LWasmReinterpretBase<1, 1> {
public:
LIR_HEADER(WasmReinterpret);
explicit LWasmReinterpret(const LAllocation& input)
: LWasmReinterpretBase(classOpcode) {
setOperand(0, input);
}
};
class LWasmReinterpretFromI64 : public LWasmReinterpretBase<1, INT64_PIECES> {
public:
static const size_t Input = 0;
LIR_HEADER(WasmReinterpretFromI64);
explicit LWasmReinterpretFromI64(const LInt64Allocation& input)
: LWasmReinterpretBase(classOpcode) {
setInt64Operand(Input, input);
}
};
class LWasmReinterpretToI64 : public LWasmReinterpretBase<INT64_PIECES, 1> {
public:
LIR_HEADER(WasmReinterpretToI64);
explicit LWasmReinterpretToI64(const LAllocation& input)
: LWasmReinterpretBase(classOpcode) {
setOperand(0, input);
}
};
namespace details {
template <size_t Defs, size_t Ops, size_t Temps>
class RotateBase : public LInstructionHelper<Defs, Ops, Temps> {
typedef LInstructionHelper<Defs, Ops, Temps> Base;
protected:
explicit RotateBase(LNode::Opcode opcode) : Base(opcode) {}
public:
MRotate* mir() { return Base::mir_->toRotate(); }
};
} // namespace details
class LRotate : public details::RotateBase<1, 2, 0> {
public:
LIR_HEADER(Rotate);
LRotate() : RotateBase(classOpcode) {}
const LAllocation* input() { return getOperand(0); }
LAllocation* count() { return getOperand(1); }
};
class LRotateI64
: public details::RotateBase<INT64_PIECES, INT64_PIECES + 1, 1> {
public:
LIR_HEADER(RotateI64);
LRotateI64() : RotateBase(classOpcode) {
setTemp(0, LDefinition::BogusTemp());
}
static const size_t Input = 0;
static const size_t Count = INT64_PIECES;
const LInt64Allocation input() { return getInt64Operand(Input); }
const LDefinition* temp() { return getTemp(0); }
LAllocation* count() { return getOperand(Count); }
};
// Allocate a new arguments object for an inlined frame.
class LCreateInlinedArgumentsObject : public LVariadicInstruction<1, 2> {
public:
LIR_HEADER(CreateInlinedArgumentsObject)
static const size_t CallObj = 0;
static const size_t Callee = 1;
static const size_t NumNonArgumentOperands = 2;
static size_t ArgIndex(size_t i) {
return NumNonArgumentOperands + BOX_PIECES * i;
}
LCreateInlinedArgumentsObject(uint32_t numOperands, const LDefinition& temp1,
const LDefinition& temp2)
: LVariadicInstruction(classOpcode, numOperands) {
setIsCall();
setTemp(0, temp1);
setTemp(1, temp2);
}
const LAllocation* getCallObject() { return getOperand(CallObj); }
const LAllocation* getCallee() { return getOperand(Callee); }
const LDefinition* temp1() { return getTemp(0); }
const LDefinition* temp2() { return getTemp(1); }
MCreateInlinedArgumentsObject* mir() const {
return mir_->toCreateInlinedArgumentsObject();
}
};
class LGetInlinedArgument : public LVariadicInstruction<BOX_PIECES, 0> {
public:
LIR_HEADER(GetInlinedArgument)
static const size_t Index = 0;
static const size_t NumNonArgumentOperands = 1;
static size_t ArgIndex(size_t i) {
return NumNonArgumentOperands + BOX_PIECES * i;
}
explicit LGetInlinedArgument(uint32_t numOperands)
: LVariadicInstruction(classOpcode, numOperands) {}
const LAllocation* getIndex() { return getOperand(Index); }
MGetInlinedArgument* mir() const { return mir_->toGetInlinedArgument(); }
};
class LGetInlinedArgumentHole : public LVariadicInstruction<BOX_PIECES, 0> {
public:
LIR_HEADER(GetInlinedArgumentHole)
static const size_t Index = 0;
static const size_t NumNonArgumentOperands = 1;
static size_t ArgIndex(size_t i) {
return NumNonArgumentOperands + BOX_PIECES * i;
}
explicit LGetInlinedArgumentHole(uint32_t numOperands)
: LVariadicInstruction(classOpcode, numOperands) {}
const LAllocation* getIndex() { return getOperand(Index); }
MGetInlinedArgumentHole* mir() const {
return mir_->toGetInlinedArgumentHole();
}
};
class LInlineArgumentsSlice : public LVariadicInstruction<1, 1> {
public:
LIR_HEADER(InlineArgumentsSlice)
static const size_t Begin = 0;
static const size_t Count = 1;
static const size_t NumNonArgumentOperands = 2;
static size_t ArgIndex(size_t i) {
return NumNonArgumentOperands + BOX_PIECES * i;
}
explicit LInlineArgumentsSlice(uint32_t numOperands, const LDefinition& temp)
: LVariadicInstruction(classOpcode, numOperands) {
setTemp(0, temp);
}
const LAllocation* begin() { return getOperand(Begin); }
const LAllocation* count() { return getOperand(Count); }
const LDefinition* temp() { return getTemp(0); }
MInlineArgumentsSlice* mir() const { return mir_->toInlineArgumentsSlice(); }
};
// Common code for LIR descended from MCall.
template <size_t Defs, size_t Operands, size_t Temps>
class LJSCallInstructionHelper
: public LCallInstructionHelper<Defs, Operands, Temps> {
protected:
explicit LJSCallInstructionHelper(LNode::Opcode opcode)
: LCallInstructionHelper<Defs, Operands, Temps>(opcode) {}
public:
MCall* mir() const { return this->mir_->toCall(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
// Does not include |this|.
uint32_t numActualArgs() const { return mir()->numActualArgs(); }
bool isConstructing() const { return mir()->isConstructing(); }
bool ignoresReturnValue() const { return mir()->ignoresReturnValue(); }
};
// Generates a polymorphic callsite, wherein the function being called is
// unknown and anticipated to vary.
class LCallGeneric : public LJSCallInstructionHelper<BOX_PIECES, 1, 2> {
public:
LIR_HEADER(CallGeneric)
LCallGeneric(const LAllocation& func, const LDefinition& nargsreg,
const LDefinition& tmpobjreg)
: LJSCallInstructionHelper(classOpcode) {
setOperand(0, func);
setTemp(0, nargsreg);
setTemp(1, tmpobjreg);
}
const LAllocation* getFunction() { return getOperand(0); }
const LDefinition* getNargsReg() { return getTemp(0); }
const LDefinition* getTempObject() { return getTemp(1); }
};
// Generates a hardcoded callsite for a known, non-native target.
class LCallKnown : public LJSCallInstructionHelper<BOX_PIECES, 1, 1> {
public:
LIR_HEADER(CallKnown)
LCallKnown(const LAllocation& func, const LDefinition& tmpobjreg)
: LJSCallInstructionHelper(classOpcode) {
setOperand(0, func);
setTemp(0, tmpobjreg);
}
const LAllocation* getFunction() { return getOperand(0); }
const LDefinition* getTempObject() { return getTemp(0); }
};
// Generates a hardcoded callsite for a known, native target.
class LCallNative : public LJSCallInstructionHelper<BOX_PIECES, 0, 4> {
public:
LIR_HEADER(CallNative)
LCallNative(const LDefinition& argContext, const LDefinition& argUintN,
const LDefinition& argVp, const LDefinition& tmpreg)
: LJSCallInstructionHelper(classOpcode) {
// Registers used for callWithABI().
setTemp(0, argContext);
setTemp(1, argUintN);
setTemp(2, argVp);
// Temporary registers.
setTemp(3, tmpreg);
}
const LDefinition* getArgContextReg() { return getTemp(0); }
const LDefinition* getArgUintNReg() { return getTemp(1); }
const LDefinition* getArgVpReg() { return getTemp(2); }
const LDefinition* getTempReg() { return getTemp(3); }
};
class LCallClassHook : public LCallInstructionHelper<BOX_PIECES, 1, 4> {
public:
LIR_HEADER(CallClassHook)
LCallClassHook(const LAllocation& callee, const LDefinition& argContext,
const LDefinition& argUintN, const LDefinition& argVp,
const LDefinition& tmpreg)
: LCallInstructionHelper(classOpcode) {
setOperand(0, callee);
// Registers used for callWithABI().
setTemp(0, argContext);
setTemp(1, argUintN);
setTemp(2, argVp);
// Temporary registers.
setTemp(3, tmpreg);
}
MCallClassHook* mir() const { return mir_->toCallClassHook(); }
const LAllocation* getCallee() { return this->getOperand(0); }
const LDefinition* getArgContextReg() { return getTemp(0); }
const LDefinition* getArgUintNReg() { return getTemp(1); }
const LDefinition* getArgVpReg() { return getTemp(2); }
const LDefinition* getTempReg() { return getTemp(3); }
};
// Generates a hardcoded callsite for a known, DOM-native target.
class LCallDOMNative : public LJSCallInstructionHelper<BOX_PIECES, 0, 4> {
public:
LIR_HEADER(CallDOMNative)
LCallDOMNative(const LDefinition& argJSContext, const LDefinition& argObj,
const LDefinition& argPrivate, const LDefinition& argArgs)
: LJSCallInstructionHelper(classOpcode) {
setTemp(0, argJSContext);
setTemp(1, argObj);
setTemp(2, argPrivate);
setTemp(3, argArgs);
}
const LDefinition* getArgJSContext() { return getTemp(0); }
const LDefinition* getArgObj() { return getTemp(1); }
const LDefinition* getArgPrivate() { return getTemp(2); }
const LDefinition* getArgArgs() { return getTemp(3); }
};
class LUnreachable : public LControlInstructionHelper<0, 0, 0> {
public:
LIR_HEADER(Unreachable)
LUnreachable() : LControlInstructionHelper(classOpcode) {}
};
class LUnreachableResultV : public LInstructionHelper<BOX_PIECES, 0, 0> {
public:
LIR_HEADER(UnreachableResultV)
LUnreachableResultV() : LInstructionHelper(classOpcode) {}
};
template <size_t defs, size_t ops>
class LDOMPropertyInstructionHelper
: public LCallInstructionHelper<defs, 1 + ops, 3> {
protected:
LDOMPropertyInstructionHelper(LNode::Opcode opcode,
const LDefinition& JSContextReg,
const LAllocation& ObjectReg,
const LDefinition& PrivReg,
const LDefinition& ValueReg)
: LCallInstructionHelper<defs, 1 + ops, 3>(opcode) {
this->setOperand(0, ObjectReg);
this->setTemp(0, JSContextReg);
this->setTemp(1, PrivReg);
this->setTemp(2, ValueReg);
}
public:
const LDefinition* getJSContextReg() { return this->getTemp(0); }
const LAllocation* getObjectReg() { return this->getOperand(0); }
const LDefinition* getPrivReg() { return this->getTemp(1); }
const LDefinition* getValueReg() { return this->getTemp(2); }
};
class LGetDOMProperty : public LDOMPropertyInstructionHelper<BOX_PIECES, 0> {
public:
LIR_HEADER(GetDOMProperty)
LGetDOMProperty(const LDefinition& JSContextReg, const LAllocation& ObjectReg,
const LDefinition& PrivReg, const LDefinition& ValueReg)
: LDOMPropertyInstructionHelper<BOX_PIECES, 0>(
classOpcode, JSContextReg, ObjectReg, PrivReg, ValueReg) {}
MGetDOMProperty* mir() const { return mir_->toGetDOMProperty(); }
};
class LGetDOMMemberV : public LInstructionHelper<BOX_PIECES, 1, 0> {
public:
LIR_HEADER(GetDOMMemberV);
explicit LGetDOMMemberV(const LAllocation& object)
: LInstructionHelper(classOpcode) {
setOperand(0, object);
}
const LAllocation* object() { return getOperand(0); }
MGetDOMMember* mir() const { return mir_->toGetDOMMember(); }
};
class LSetDOMProperty : public LDOMPropertyInstructionHelper<0, BOX_PIECES> {
public:
LIR_HEADER(SetDOMProperty)
LSetDOMProperty(const LDefinition& JSContextReg, const LAllocation& ObjectReg,
const LBoxAllocation& value, const LDefinition& PrivReg,
const LDefinition& ValueReg)
: LDOMPropertyInstructionHelper<0, BOX_PIECES>(
classOpcode, JSContextReg, ObjectReg, PrivReg, ValueReg) {
setBoxOperand(Value, value);
}
static const size_t Value = 1;
MSetDOMProperty* mir() const { return mir_->toSetDOMProperty(); }
};
// Generates a polymorphic callsite, wherein the function being called is
// unknown and anticipated to vary.
class LApplyArgsGeneric
: public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 2, 2> {
public:
LIR_HEADER(ApplyArgsGeneric)
LApplyArgsGeneric(const LAllocation& func, const LAllocation& argc,
const LBoxAllocation& thisv, const LDefinition& tmpobjreg,
const LDefinition& tmpcopy)
: LCallInstructionHelper(classOpcode) {
setOperand(0, func);
setOperand(1, argc);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
setTemp(1, tmpcopy);
}
MApplyArgs* mir() const { return mir_->toApplyArgs(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
uint32_t numExtraFormals() const { return mir()->numExtraFormals(); }
const LAllocation* getFunction() { return getOperand(0); }
const LAllocation* getArgc() { return getOperand(1); }
static const size_t ThisIndex = 2;
const LDefinition* getTempObject() { return getTemp(0); }
const LDefinition* getTempForArgCopy() { return getTemp(1); }
};
class LApplyArgsObj
: public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 2, 2> {
public:
LIR_HEADER(ApplyArgsObj)
LApplyArgsObj(const LAllocation& func, const LAllocation& argsObj,
const LBoxAllocation& thisv, const LDefinition& tmpObjReg,
const LDefinition& tmpCopy)
: LCallInstructionHelper(classOpcode) {
setOperand(0, func);
setOperand(1, argsObj);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpObjReg);
setTemp(1, tmpCopy);
}
MApplyArgsObj* mir() const { return mir_->toApplyArgsObj(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
const LAllocation* getFunction() { return getOperand(0); }
const LAllocation* getArgsObj() { return getOperand(1); }
// All registers are calltemps. argc is mapped to the same register as
// ArgsObj. argc becomes live as ArgsObj is dying.
const LAllocation* getArgc() { return getOperand(1); }
static const size_t ThisIndex = 2;
const LDefinition* getTempObject() { return getTemp(0); }
const LDefinition* getTempForArgCopy() { return getTemp(1); }
};
class LApplyArrayGeneric
: public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 2, 2> {
public:
LIR_HEADER(ApplyArrayGeneric)
LApplyArrayGeneric(const LAllocation& func, const LAllocation& elements,
const LBoxAllocation& thisv, const LDefinition& tmpobjreg,
const LDefinition& tmpcopy)
: LCallInstructionHelper(classOpcode) {
setOperand(0, func);
setOperand(1, elements);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
setTemp(1, tmpcopy);
}
MApplyArray* mir() const { return mir_->toApplyArray(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
const LAllocation* getFunction() { return getOperand(0); }
const LAllocation* getElements() { return getOperand(1); }
// argc is mapped to the same register as elements: argc becomes
// live as elements is dying, all registers are calltemps.
const LAllocation* getArgc() { return getOperand(1); }
static const size_t ThisIndex = 2;
const LDefinition* getTempObject() { return getTemp(0); }
const LDefinition* getTempForArgCopy() { return getTemp(1); }
};
class LConstructArgsGeneric
: public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 3, 1> {
public:
LIR_HEADER(ConstructArgsGeneric)
LConstructArgsGeneric(const LAllocation& func, const LAllocation& argc,
const LAllocation& newTarget,
const LBoxAllocation& thisv,
const LDefinition& tmpobjreg)
: LCallInstructionHelper(classOpcode) {
setOperand(0, func);
setOperand(1, argc);
setOperand(2, newTarget);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
}
MConstructArgs* mir() const { return mir_->toConstructArgs(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
uint32_t numExtraFormals() const { return mir()->numExtraFormals(); }
const LAllocation* getFunction() { return getOperand(0); }
const LAllocation* getArgc() { return getOperand(1); }
const LAllocation* getNewTarget() { return getOperand(2); }
static const size_t ThisIndex = 3;
const LDefinition* getTempObject() { return getTemp(0); }
// tempForArgCopy is mapped to the same register as newTarget:
// tempForArgCopy becomes live as newTarget is dying, all registers are
// calltemps.
const LAllocation* getTempForArgCopy() { return getOperand(2); }
};
class LConstructArrayGeneric
: public LCallInstructionHelper<BOX_PIECES, BOX_PIECES + 3, 1> {
public:
LIR_HEADER(ConstructArrayGeneric)
LConstructArrayGeneric(const LAllocation& func, const LAllocation& elements,
const LAllocation& newTarget,
const LBoxAllocation& thisv,
const LDefinition& tmpobjreg)
: LCallInstructionHelper(classOpcode) {
setOperand(0, func);
setOperand(1, elements);
setOperand(2, newTarget);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
}
MConstructArray* mir() const { return mir_->toConstructArray(); }
bool hasSingleTarget() const { return getSingleTarget() != nullptr; }
WrappedFunction* getSingleTarget() const { return mir()->getSingleTarget(); }
const LAllocation* getFunction() { return getOperand(0); }
const LAllocation* getElements() { return getOperand(1); }
const LAllocation* getNewTarget() { return getOperand(2); }
static const size_t ThisIndex = 3;
const LDefinition* getTempObject() { return getTemp(0); }
// argc is mapped to the same register as elements: argc becomes
// live as elements is dying, all registers are calltemps.
const LAllocation* getArgc() { return getOperand(1); }
// tempForArgCopy is mapped to the same register as newTarget:
// tempForArgCopy becomes live as newTarget is dying, all registers are
// calltemps.
const LAllocation* getTempForArgCopy() { return getOperand(2); }
};
// Takes in either an integer or boolean input and tests it for truthiness.
class LTestIAndBranch : public LControlInstructionHelper<2, 1, 0> {
public:
LIR_HEADER(TestIAndBranch)
LTestIAndBranch(const LAllocation& in, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode) {
setOperand(0, in);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
};
// Takes in an int64 input and tests it for truthiness.
class LTestI64AndBranch : public LControlInstructionHelper<2, INT64_PIECES, 0> {
public:
LIR_HEADER(TestI64AndBranch)
LTestI64AndBranch(const LInt64Allocation& in, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode) {
setInt64Operand(0, in);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
};
// Takes in a double input and tests it for truthiness.
class LTestDAndBranch : public LControlInstructionHelper<2, 1, 0> {
public:
LIR_HEADER(TestDAndBranch)
LTestDAndBranch(const LAllocation& in, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode) {
setOperand(0, in);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
};
// Takes in a float32 input and tests it for truthiness.
class LTestFAndBranch : public LControlInstructionHelper<2, 1, 0> {
public:
LIR_HEADER(TestFAndBranch)
LTestFAndBranch(const LAllocation& in, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode) {
setOperand(0, in);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
};
// Takes in a bigint input and tests it for truthiness.
class LTestBIAndBranch : public LControlInstructionHelper<2, 1, 0> {
public:
LIR_HEADER(TestBIAndBranch)
LTestBIAndBranch(const LAllocation& in, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode) {
setOperand(0, in);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() { return getSuccessor(0); }
MBasicBlock* ifFalse() { return getSuccessor(1); }
};
// Takes an object and tests it for truthiness. An object is falsy iff it
// emulates |undefined|; see js::EmulatesUndefined.
class LTestOAndBranch : public LControlInstructionHelper<2, 1, 1> {
public:
LIR_HEADER(TestOAndBranch)
LTestOAndBranch(const LAllocation& input, MBasicBlock* ifTruthy,
MBasicBlock* ifFalsy, const LDefinition& temp)
: LControlInstructionHelper(classOpcode) {
setOperand(0, input);
setSuccessor(0, ifTruthy);
setSuccessor(1, ifFalsy);
setTemp(0, temp);
}
const LDefinition* temp() { return getTemp(0); }
MBasicBlock* ifTruthy() { return getSuccessor(0); }
MBasicBlock* ifFalsy() { return getSuccessor(1); }
MTest* mir() { return mir_->toTest(); }
};
// Takes in a boxed value and tests it for truthiness.
class LTestVAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 3> {
public:
LIR_HEADER(TestVAndBranch)
LTestVAndBranch(MBasicBlock* ifTruthy, MBasicBlock* ifFalsy,
const LBoxAllocation& input, const LDefinition& temp0,
const LDefinition& temp1, const LDefinition& temp2)
: LControlInstructionHelper(classOpcode) {
setSuccessor(0, ifTruthy);
setSuccessor(1, ifFalsy);
setBoxOperand(Input, input);
setTemp(0, temp0);
setTemp(1, temp1);
setTemp(2, temp2);
}
static const size_t Input = 0;
const LDefinition* tempFloat() { return getTemp(0); }
const LDefinition* temp1() { return getTemp(1); }
const LDefinition* temp2() { return getTemp(2); }
MBasicBlock* ifTruthy() { return getSuccessor(0); }
MBasicBlock* ifFalsy() { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
};
// Compares two integral values of the same JS type, either integer or object.
// For objects, both operands are in registers.
class LCompare : public LInstructionHelper<1, 2, 0> {
JSOp jsop_;
public:
LIR_HEADER(Compare)
LCompare(JSOp jsop, const LAllocation& left, const LAllocation& right)
: LInstructionHelper(classOpcode), jsop_(jsop) {
setOperand(0, left);
setOperand(1, right);
}
JSOp jsop() const { return jsop_; }
const LAllocation* left() { return getOperand(0); }
const LAllocation* right() { return getOperand(1); }
MCompare* mir() { return mir_->toCompare(); }
const char* extraName() const { return CodeName(jsop_); }
};
class LCompareI64 : public LInstructionHelper<1, 2 * INT64_PIECES, 0> {
JSOp jsop_;
public:
LIR_HEADER(CompareI64)
static const size_t Lhs = 0;
static const size_t Rhs = INT64_PIECES;
LCompareI64(JSOp jsop, const LInt64Allocation& left,
const LInt64Allocation& right)
: LInstructionHelper(classOpcode), jsop_(jsop) {
setInt64Operand(Lhs, left);
setInt64Operand(Rhs, right);
}
JSOp jsop() const { return jsop_; }
MCompare* mir() { return mir_->toCompare(); }
const char* extraName() const { return CodeName(jsop_); }
};
class LCompareI64AndBranch
: public LControlInstructionHelper<2, 2 * INT64_PIECES, 0> {
MCompare* cmpMir_;
JSOp jsop_;
public:
LIR_HEADER(CompareI64AndBranch)
static const size_t Lhs = 0;
static const size_t Rhs = INT64_PIECES;
LCompareI64AndBranch(MCompare* cmpMir, JSOp jsop,
const LInt64Allocation& left,
const LInt64Allocation& right, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir), jsop_(jsop) {
setInt64Operand(Lhs, left);
setInt64Operand(Rhs, right);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
JSOp jsop() const { return jsop_; }
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
const char* extraName() const { return CodeName(jsop_); }
};
// Compares two integral values of the same JS type, either integer or object.
// For objects, both operands are in registers.
class LCompareAndBranch : public LControlInstructionHelper<2, 2, 0> {
MCompare* cmpMir_;
JSOp jsop_;
public:
LIR_HEADER(CompareAndBranch)
LCompareAndBranch(MCompare* cmpMir, JSOp jsop, const LAllocation& left,
const LAllocation& right, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir), jsop_(jsop) {
setOperand(0, left);
setOperand(1, right);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
JSOp jsop() const { return jsop_; }
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
const LAllocation* left() { return getOperand(0); }
const LAllocation* right() { return getOperand(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
const char* extraName() const { return CodeName(jsop_); }
};
class LCompareDAndBranch : public LControlInstructionHelper<2, 2, 0> {
MCompare* cmpMir_;
public:
LIR_HEADER(CompareDAndBranch)
LCompareDAndBranch(MCompare* cmpMir, const LAllocation& left,
const LAllocation& right, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setOperand(0, left);
setOperand(1, right);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
const LAllocation* left() { return getOperand(0); }
const LAllocation* right() { return getOperand(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
};
class LCompareFAndBranch : public LControlInstructionHelper<2, 2, 0> {
MCompare* cmpMir_;
public:
LIR_HEADER(CompareFAndBranch)
LCompareFAndBranch(MCompare* cmpMir, const LAllocation& left,
const LAllocation& right, MBasicBlock* ifTrue,
MBasicBlock* ifFalse)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setOperand(0, left);
setOperand(1, right);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
const LAllocation* left() { return getOperand(0); }
const LAllocation* right() { return getOperand(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
};
class LBitAndAndBranch : public LControlInstructionHelper<2, 2, 0> {
// This denotes only a single-word AND on the target. Hence `is64_` is
// required to be `false` on a 32-bit target.
bool is64_;
Assembler::Condition cond_;
public:
LIR_HEADER(BitAndAndBranch)
LBitAndAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse, bool is64,
Assembler::Condition cond = Assembler::NonZero)
: LControlInstructionHelper(classOpcode), is64_(is64), cond_(cond) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
const LAllocation* left() { return getOperand(0); }
const LAllocation* right() { return getOperand(1); }
bool is64() const { return is64_; }
Assembler::Condition cond() const {
MOZ_ASSERT(cond_ == Assembler::Zero || cond_ == Assembler::NonZero);
return cond_;
}
};
class LIsNullOrLikeUndefinedAndBranchV
: public LControlInstructionHelper<2, BOX_PIECES, 2> {
MCompare* cmpMir_;
public:
LIR_HEADER(IsNullOrLikeUndefinedAndBranchV)
LIsNullOrLikeUndefinedAndBranchV(MCompare* cmpMir, MBasicBlock* ifTrue,
MBasicBlock* ifFalse,
const LBoxAllocation& value,
const LDefinition& temp,
const LDefinition& tempToUnbox)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Value, value);
setTemp(0, temp);
setTemp(1, tempToUnbox);
}
static const size_t Value = 0;
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
const LDefinition* temp() { return getTemp(0); }
const LDefinition* tempToUnbox() { return getTemp(1); }
};
class LIsNullOrLikeUndefinedAndBranchT
: public LControlInstructionHelper<2, 1, 1> {
MCompare* cmpMir_;
public:
LIR_HEADER(IsNullOrLikeUndefinedAndBranchT)
LIsNullOrLikeUndefinedAndBranchT(MCompare* cmpMir, const LAllocation& input,
MBasicBlock* ifTrue, MBasicBlock* ifFalse,
const LDefinition& temp)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setOperand(0, input);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setTemp(0, temp);
}
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
const LDefinition* temp() { return getTemp(0); }
};
class LIsNullAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0> {
MCompare* cmpMir_;
public:
LIR_HEADER(IsNullAndBranch)
LIsNullAndBranch(MCompare* cmpMir, MBasicBlock* ifTrue, MBasicBlock* ifFalse,
const LBoxAllocation& value)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Value, value);
}
static const size_t Value = 0;
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
};
class LIsUndefinedAndBranch
: public LControlInstructionHelper<2, BOX_PIECES, 0> {
MCompare* cmpMir_;
public:
LIR_HEADER(IsUndefinedAndBranch)
LIsUndefinedAndBranch(MCompare* cmpMir, MBasicBlock* ifTrue,
MBasicBlock* ifFalse, const LBoxAllocation& value)
: LControlInstructionHelper(classOpcode), cmpMir_(cmpMir) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Value, value);
}
static const size_t Value = 0;
MBasicBlock* ifTrue() const { return getSuccessor(0); }
MBasicBlock* ifFalse() const { return getSuccessor(1); }
MTest* mir() const { return mir_->toTest(); }
MCompare* cmpMir() const { return cmpMir_; }
};
// Bitwise not operation, takes a 32-bit integer as input and returning
// a 32-bit integer result as an output.
class LBitNotI : public LInstructionHelper<1, 1, 0> {
public:
LIR_HEADER(BitNotI)
LBitNotI() : LInstructionHelper(classOpcode) {}
};
class LBitNotI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 0> {
public:
LIR_HEADER(BitNotI64)
LBitNotI64() : LInstructionHelper(classOpcode) {}
};
// Binary bitwise operation, taking two 32-bit integers as inputs and returning
// a 32-bit integer result as an output.
class LBitOpI : public LInstructionHelper<1, 2, 0> {
JSOp op_;
public:
LIR_HEADER(BitOpI)
explicit LBitOpI(JSOp op) : LInstructionHelper(classOpcode), op_(op) {}
const char* extraName() const {
if (bitop() == JSOp::Ursh && mir_->toUrsh()->bailoutsDisabled()) {
return "ursh:BailoutsDisabled";
}
return CodeName(op_);
}
JSOp bitop() const { return op_; }
};
class LBitOpI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0> {
JSOp op_;
public:
LIR_HEADER(BitOpI64)