--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1425,33 +1425,16 @@ class MTernaryInstruction : public MAryI
initOperand(0, first);
initOperand(1, second);
initOperand(2, third);
}
HashNumber valueHash() const override;
};
-class MQuaternaryInstruction : public MAryInstruction<4>
-{
- protected:
- MQuaternaryInstruction(Opcode op,
- MDefinition* first, MDefinition* second,
- MDefinition* third, MDefinition* fourth)
- : MAryInstruction(op)
- {
- initOperand(0, first);
- initOperand(1, second);
- initOperand(2, third);
- initOperand(3, fourth);
- }
-
- HashNumber valueHash() const override;
-};
-
template <class T>
class MVariadicT : public T
{
FixedList<MUse> operands_;
protected:
explicit MVariadicT(typename T::Opcode op)
: T(op)
@@ -1485,687 +1468,16 @@ class MVariadicT : public T
}
void replaceOperand(size_t index, MDefinition* operand) final override {
operands_[index].replaceProducer(operand);
}
};
typedef MVariadicT<MInstruction> MVariadicInstruction;
-// Generates an LSnapshot without further effect.
-class MStart : public MNullaryInstruction
-{
- MStart()
- : MNullaryInstruction(classOpcode)
- { }
-
- public:
- INSTRUCTION_HEADER(Start)
- TRIVIAL_NEW_WRAPPERS
-};
-
-// 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()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Pointer);
- }
-
- public:
- INSTRUCTION_HEADER(OsrEntry)
- TRIVIAL_NEW_WRAPPERS
-};
-
-// 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()
- : MNullaryInstruction(classOpcode)
- { }
-
- public:
- INSTRUCTION_HEADER(Nop)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MNop)
-};
-
-// Truncation barrier. This is intended for protecting its input against
-// follow-up truncation optimizations.
-class MLimitedTruncate
- : public MUnaryInstruction,
- public ConvertToInt32Policy<0>::Data
-{
- public:
- TruncateKind truncate_;
- TruncateKind truncateLimit_;
-
- protected:
- MLimitedTruncate(MDefinition* input, TruncateKind limit)
- : MUnaryInstruction(classOpcode, input),
- truncate_(NoTruncate),
- truncateLimit_(limit)
- {
- setResultType(MIRType::Int32);
- setResultTypeSet(input->resultTypeSet());
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(LimitedTruncate)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
- bool needTruncation(TruncateKind kind) override;
- TruncateKind operandTruncateKind(size_t index) const override;
- TruncateKind truncateKind() const {
- return truncate_;
- }
- void setTruncateKind(TruncateKind kind) {
- truncate_ = kind;
- }
-};
-
-// A constant js::Value.
-class MConstant : public MNullaryInstruction
-{
- struct Payload {
- union {
- bool b;
- int32_t i32;
- int64_t i64;
- float f;
- double d;
- JSString* str;
- JS::Symbol* sym;
- JSObject* obj;
- uint64_t asBits;
- };
- Payload() : asBits(0) {}
- };
-
- Payload payload_;
-
- static_assert(sizeof(Payload) == sizeof(uint64_t),
- "asBits must be big enough for all payload bits");
-
-#ifdef DEBUG
- void assertInitializedPayload() const;
-#else
- void assertInitializedPayload() const {}
-#endif
-
- protected:
- MConstant(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints);
- explicit MConstant(JSObject* obj);
- explicit MConstant(float f);
- explicit MConstant(int64_t i);
-
- public:
- INSTRUCTION_HEADER(Constant)
- static MConstant* New(TempAllocator& alloc, const Value& v,
- CompilerConstraintList* constraints = nullptr);
- static MConstant* New(TempAllocator::Fallible alloc, const Value& v,
- CompilerConstraintList* constraints = nullptr);
- static MConstant* New(TempAllocator& alloc, const Value& v, MIRType type);
- static MConstant* NewFloat32(TempAllocator& alloc, double d);
- static MConstant* NewInt64(TempAllocator& alloc, int64_t i);
- static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
- static MConstant* Copy(TempAllocator& alloc, MConstant* src) {
- return new(alloc) MConstant(*src);
- }
-
- // Try to convert this constant to boolean, similar to js::ToBoolean.
- // Returns false if the type is MIRType::Magic*.
- bool MOZ_MUST_USE valueToBoolean(bool* res) const;
-
- // Like valueToBoolean, but returns the result directly instead of using
- // an outparam. Should not be used if this constant might be a magic value.
- bool valueToBooleanInfallible() const {
- bool res;
- MOZ_ALWAYS_TRUE(valueToBoolean(&res));
- return res;
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- HashNumber valueHash() const override;
- bool congruentTo(const MDefinition* ins) const override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool updateForReplacement(MDefinition* def) override {
- MConstant* c = def->toConstant();
- // During constant folding, we don't want to replace a float32
- // value by a double value.
- if (type() == MIRType::Float32)
- return c->type() == MIRType::Float32;
- if (type() == MIRType::Double)
- return c->type() != MIRType::Float32;
- return true;
- }
-
- void computeRange(TempAllocator& alloc) override;
- bool needTruncation(TruncateKind kind) override;
- void truncate() override;
-
- bool canProduceFloat32() const override;
-
- ALLOW_CLONE(MConstant)
-
- bool equals(const MConstant* other) const {
- assertInitializedPayload();
- return type() == other->type() && payload_.asBits == other->payload_.asBits;
- }
-
- bool toBoolean() const {
- MOZ_ASSERT(type() == MIRType::Boolean);
- return payload_.b;
- }
- int32_t toInt32() const {
- MOZ_ASSERT(type() == MIRType::Int32);
- return payload_.i32;
- }
- int64_t toInt64() const {
- MOZ_ASSERT(type() == MIRType::Int64);
- return payload_.i64;
- }
- bool isInt32(int32_t i) const {
- return type() == MIRType::Int32 && payload_.i32 == i;
- }
- const double& toDouble() const {
- MOZ_ASSERT(type() == MIRType::Double);
- return payload_.d;
- }
- const float& toFloat32() const {
- MOZ_ASSERT(type() == MIRType::Float32);
- return payload_.f;
- }
- JSString* toString() const {
- MOZ_ASSERT(type() == MIRType::String);
- return payload_.str;
- }
- JS::Symbol* toSymbol() const {
- MOZ_ASSERT(type() == MIRType::Symbol);
- return payload_.sym;
- }
- JSObject& toObject() const {
- MOZ_ASSERT(type() == MIRType::Object);
- return *payload_.obj;
- }
- JSObject* toObjectOrNull() const {
- if (type() == MIRType::Object)
- return payload_.obj;
- MOZ_ASSERT(type() == MIRType::Null);
- return nullptr;
- }
-
- bool isTypeRepresentableAsDouble() const {
- return IsTypeRepresentableAsDouble(type());
- }
- double numberToDouble() const {
- MOZ_ASSERT(isTypeRepresentableAsDouble());
- if (type() == MIRType::Int32)
- return toInt32();
- if (type() == MIRType::Double)
- return toDouble();
- return toFloat32();
- }
-
- // Convert this constant to a js::Value. Float32 constants will be stored
- // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
- // all constants can be represented by js::Value (wasm supports int64).
- Value toJSValue() const;
-
- bool appendRoots(MRootList& roots) const override;
-};
-
-// Floating-point value as created by wasm. Just a constant value, used to
-// effectively inhibite all the MIR optimizations. This uses the same LIR nodes
-// as a MConstant of the same type would.
-class MWasmFloatConstant : public MNullaryInstruction
-{
- union {
- float f32_;
- double f64_;
- uint64_t bits_;
- } u;
-
- explicit MWasmFloatConstant(MIRType type)
- : MNullaryInstruction(classOpcode)
- {
- u.bits_ = 0;
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(WasmFloatConstant)
-
- static MWasmFloatConstant* NewDouble(TempAllocator& alloc, double d) {
- auto* ret = new(alloc) MWasmFloatConstant(MIRType::Double);
- ret->u.f64_ = d;
- return ret;
- }
-
- static MWasmFloatConstant* NewFloat32(TempAllocator& alloc, float f) {
- auto* ret = new(alloc) MWasmFloatConstant(MIRType::Float32);
- ret->u.f32_ = f;
- return ret;
- }
-
- HashNumber valueHash() const override;
- bool congruentTo(const MDefinition* ins) const override;
- AliasSet getAliasSet() const override { return AliasSet::None(); }
-
- const double& toDouble() const {
- MOZ_ASSERT(type() == MIRType::Double);
- return u.f64_;
- }
- const float& toFloat32() const {
- MOZ_ASSERT(type() == MIRType::Float32);
- return u.f32_;
- }
-};
-
-// Generic constructor of SIMD valuesX4.
-class MSimdValueX4
- : public MQuaternaryInstruction,
- public MixPolicy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
- SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
-{
- protected:
- MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w)
- : MQuaternaryInstruction(classOpcode, x, y, z, w)
- {
- MOZ_ASSERT(IsSimdType(type));
- MOZ_ASSERT(SimdTypeToLength(type) == 4);
-
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(SimdValueX4)
- TRIVIAL_NEW_WRAPPERS
-
- bool canConsumeFloat32(MUse* use) const override {
- return SimdTypeToLaneType(type()) == MIRType::Float32;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MSimdValueX4)
-};
-
-// Generic constructor of SIMD values with identical lanes.
-class MSimdSplat
- : public MUnaryInstruction,
- public SimdScalarPolicy<0>::Data
-{
- protected:
- MSimdSplat(MDefinition* v, MIRType type)
- : MUnaryInstruction(classOpcode, v)
- {
- MOZ_ASSERT(IsSimdType(type));
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(SimdSplat)
- TRIVIAL_NEW_WRAPPERS
-
- bool canConsumeFloat32(MUse* use) const override {
- return SimdTypeToLaneType(type()) == MIRType::Float32;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MSimdSplat)
-};
-
-// A constant SIMD value.
-class MSimdConstant
- : public MNullaryInstruction
-{
- SimdConstant value_;
-
- protected:
- MSimdConstant(const SimdConstant& v, MIRType type)
- : MNullaryInstruction(classOpcode),
- value_(v)
- {
- MOZ_ASSERT(IsSimdType(type));
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(SimdConstant)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isSimdConstant())
- return false;
- // Bool32x4 and Int32x4 share the same underlying SimdConstant representation.
- if (type() != ins->type())
- return false;
- return value() == ins->toSimdConstant()->value();
- }
-
- const SimdConstant& value() const {
- return value_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MSimdConstant)
-};
-
-// Converts all lanes of a given vector into the type of another vector
-class MSimdConvert
- : public MUnaryInstruction,
- public SimdPolicy<0>::Data
-{
- // When either fromType or toType is an integer vector, should it be treated
- // as signed or unsigned. Note that we don't support int-int conversions -
- // use MSimdReinterpretCast for that.
- SimdSign sign_;
- wasm::BytecodeOffset bytecodeOffset_;
-
- MSimdConvert(MDefinition* obj, MIRType toType, SimdSign sign,
- wasm::BytecodeOffset bytecodeOffset)
- : MUnaryInstruction(classOpcode, obj), sign_(sign), bytecodeOffset_(bytecodeOffset)
- {
- MIRType fromType = obj->type();
- MOZ_ASSERT(IsSimdType(fromType));
- MOZ_ASSERT(IsSimdType(toType));
- // All conversions are int <-> float, so signedness is required.
- MOZ_ASSERT(sign != SimdSign::NotApplicable);
-
- setResultType(toType);
- specialization_ = fromType; // expects fromType as input
-
- setMovable();
- if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) {
- // Does the extra range check => do not remove
- setGuard();
- }
- }
-
- static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign,
- wasm::BytecodeOffset bytecodeOffset)
- {
- return new (alloc) MSimdConvert(obj, toType, sign, bytecodeOffset);
- }
-
- public:
- INSTRUCTION_HEADER(SimdConvert)
-
- // Create a MSimdConvert instruction and add it to the basic block.
- // Possibly create and add an equivalent sequence of instructions instead if
- // the current target doesn't support the requested conversion directly.
- // Return the inserted MInstruction that computes the converted value.
- static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
- MIRType toType, SimdSign sign,
- wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset());
-
- SimdSign signedness() const {
- return sign_;
- }
- wasm::BytecodeOffset bytecodeOffset() const {
- return bytecodeOffset_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- const MSimdConvert* other = ins->toSimdConvert();
- return sign_ == other->sign_;
- }
- ALLOW_CLONE(MSimdConvert)
-};
-
-// Casts bits of a vector input to another SIMD type (doesn't generate code).
-class MSimdReinterpretCast
- : public MUnaryInstruction,
- public SimdPolicy<0>::Data
-{
- MSimdReinterpretCast(MDefinition* obj, MIRType toType)
- : MUnaryInstruction(classOpcode, obj)
- {
- MIRType fromType = obj->type();
- MOZ_ASSERT(IsSimdType(fromType));
- MOZ_ASSERT(IsSimdType(toType));
- setMovable();
- setResultType(toType);
- specialization_ = fromType; // expects fromType as input
- }
-
- public:
- INSTRUCTION_HEADER(SimdReinterpretCast)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- ALLOW_CLONE(MSimdReinterpretCast)
-};
-
-// Extracts a lane element from a given vector type, given by its lane symbol.
-//
-// For integer SIMD types, a SimdSign must be provided so the lane value can be
-// converted to a scalar correctly.
-class MSimdExtractElement
- : public MUnaryInstruction,
- public SimdPolicy<0>::Data
-{
- protected:
- unsigned lane_;
- SimdSign sign_;
-
- MSimdExtractElement(MDefinition* obj, MIRType laneType, unsigned lane, SimdSign sign)
- : MUnaryInstruction(classOpcode, obj), lane_(lane), sign_(sign)
- {
- MIRType vecType = obj->type();
- MOZ_ASSERT(IsSimdType(vecType));
- MOZ_ASSERT(lane < SimdTypeToLength(vecType));
- MOZ_ASSERT(!IsSimdType(laneType));
- MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(vecType),
- "Signedness must be specified for integer SIMD extractLanes");
- // The resulting type should match the lane type.
- // Allow extracting boolean lanes directly into an Int32 (for wasm).
- // Allow extracting Uint32 lanes into a double.
- //
- // We also allow extracting Uint32 lanes into a MIRType::Int32. This is
- // equivalent to extracting the Uint32 lane to a double and then
- // applying MTruncateToInt32, but it bypasses the conversion to/from
- // double.
- MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType ||
- (IsBooleanSimdType(vecType) && laneType == MIRType::Int32) ||
- (vecType == MIRType::Int32x4 && laneType == MIRType::Double &&
- sign == SimdSign::Unsigned));
-
- setMovable();
- specialization_ = vecType;
- setResultType(laneType);
- }
-
- public:
- INSTRUCTION_HEADER(SimdExtractElement)
- TRIVIAL_NEW_WRAPPERS
-
- unsigned lane() const {
- return lane_;
- }
-
- SimdSign signedness() const {
- return sign_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isSimdExtractElement())
- return false;
- const MSimdExtractElement* other = ins->toSimdExtractElement();
- if (other->lane_ != lane_ || other->sign_ != sign_)
- return false;
- return congruentIfOperandsEqual(other);
- }
- ALLOW_CLONE(MSimdExtractElement)
-};
-
-// Replaces the datum in the given lane by a scalar value of the same type.
-class MSimdInsertElement
- : public MBinaryInstruction,
- public MixPolicy< SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
-{
- private:
- unsigned lane_;
-
- MSimdInsertElement(MDefinition* vec, MDefinition* val, unsigned lane)
- : MBinaryInstruction(classOpcode, vec, val), lane_(lane)
- {
- MIRType type = vec->type();
- MOZ_ASSERT(IsSimdType(type));
- MOZ_ASSERT(lane < SimdTypeToLength(type));
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(SimdInsertElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, vector), (1, value))
-
- unsigned lane() const {
- return lane_;
- }
-
- bool canConsumeFloat32(MUse* use) const override {
- return use == getUseFor(1) && SimdTypeToLaneType(type()) == MIRType::Float32;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdInsertElement)
-};
-
-// Returns true if all lanes are true.
-class MSimdAllTrue
- : public MUnaryInstruction,
- public SimdPolicy<0>::Data
-{
- protected:
- explicit MSimdAllTrue(MDefinition* obj, MIRType result)
- : MUnaryInstruction(classOpcode, obj)
- {
- MIRType simdType = obj->type();
- MOZ_ASSERT(IsBooleanSimdType(simdType));
- MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
- setResultType(result);
- specialization_ = simdType;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdAllTrue)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- ALLOW_CLONE(MSimdAllTrue)
-};
-
-// Returns true if any lane is true.
-class MSimdAnyTrue
- : public MUnaryInstruction,
- public SimdPolicy<0>::Data
-{
- protected:
- explicit MSimdAnyTrue(MDefinition* obj, MIRType result)
- : MUnaryInstruction(classOpcode, obj)
- {
- MIRType simdType = obj->type();
- MOZ_ASSERT(IsBooleanSimdType(simdType));
- MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
- setResultType(result);
- specialization_ = simdType;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdAnyTrue)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- ALLOW_CLONE(MSimdAnyTrue)
-};
-
// Base for the MSimdSwizzle and MSimdShuffle classes.
class MSimdShuffleBase
{
protected:
// As of now, there are at most 16 lanes. For each lane, we need to know
// which input we choose and which of the lanes we choose.
mozilla::Array<uint8_t, 16> lane_;
uint32_t arity_;
@@ -2193,54 +1505,16 @@ class MSimdShuffleBase
}
bool lanesMatch(uint32_t x, uint32_t y, uint32_t z, uint32_t w) const {
return arity_ == 4 && lane(0) == x && lane(1) == y && lane(2) == z &&
lane(3) == w;
}
};
-// Applies a swizzle operation to the input, putting the input lanes as
-// indicated in the output register's lanes. This implements the SIMD.js
-// "swizzle" function, that takes one vector and an array of lane indexes.
-class MSimdSwizzle
- : public MUnaryInstruction,
- public MSimdShuffleBase,
- public NoTypePolicy::Data
-{
- protected:
- MSimdSwizzle(MDefinition* obj, const uint8_t lanes[])
- : MUnaryInstruction(classOpcode, obj), MSimdShuffleBase(lanes, obj->type())
- {
- for (unsigned i = 0; i < arity_; i++)
- MOZ_ASSERT(lane(i) < arity_);
- setResultType(obj->type());
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdSwizzle)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isSimdSwizzle())
- return false;
- const MSimdSwizzle* other = ins->toSimdSwizzle();
- return sameLanes(other) && congruentIfOperandsEqual(other);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MSimdSwizzle)
-};
-
// A "general shuffle" is a swizzle or a shuffle with non-constant lane
// indices. This is the one that Ion inlines and it can be folded into a
// MSimdSwizzle/MSimdShuffle if lane indices are constant. Performance of
// general swizzle/shuffle does not really matter, as we expect to get
// constant indices most of the time.
class MSimdGeneralShuffle :
public MVariadicInstruction,
public SimdShufflePolicy::Data
@@ -2303,612 +1577,16 @@ class MSimdGeneralShuffle :
MDefinition* foldsTo(TempAllocator& alloc) override;
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
-// Applies a shuffle operation to the inputs. The lane indexes select a source
-// lane from the concatenation of the two input vectors.
-class MSimdShuffle
- : public MBinaryInstruction,
- public MSimdShuffleBase,
- public NoTypePolicy::Data
-{
- MSimdShuffle(MDefinition* lhs, MDefinition* rhs, const uint8_t lanes[])
- : MBinaryInstruction(classOpcode, lhs, rhs), MSimdShuffleBase(lanes, lhs->type())
- {
- MOZ_ASSERT(IsSimdType(lhs->type()));
- MOZ_ASSERT(IsSimdType(rhs->type()));
- MOZ_ASSERT(lhs->type() == rhs->type());
- for (unsigned i = 0; i < arity_; i++)
- MOZ_ASSERT(lane(i) < 2 * arity_);
- setResultType(lhs->type());
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdShuffle)
-
- static MInstruction* New(TempAllocator& alloc, MDefinition* lhs, MDefinition* rhs,
- const uint8_t lanes[])
- {
- unsigned arity = SimdTypeToLength(lhs->type());
-
- // Swap operands so that new lanes come from LHS in majority.
- // In the balanced case, swap operands if needs be, in order to be able
- // to do only one vshufps on x86.
- unsigned lanesFromLHS = 0;
- for (unsigned i = 0; i < arity; i++) {
- if (lanes[i] < arity)
- lanesFromLHS++;
- }
-
- if (lanesFromLHS < arity / 2 ||
- (arity == 4 && lanesFromLHS == 2 && lanes[0] >= 4 && lanes[1] >= 4)) {
- mozilla::Array<uint8_t, 16> newLanes;
- for (unsigned i = 0; i < arity; i++)
- newLanes[i] = (lanes[i] + arity) % (2 * arity);
- return New(alloc, rhs, lhs, &newLanes[0]);
- }
-
- // If all lanes come from the same vector, just use swizzle instead.
- if (lanesFromLHS == arity)
- return MSimdSwizzle::New(alloc, lhs, lanes);
-
- return new(alloc) MSimdShuffle(lhs, rhs, lanes);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isSimdShuffle())
- return false;
- const MSimdShuffle* other = ins->toSimdShuffle();
- return sameLanes(other) && binaryCongruentTo(other);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MSimdShuffle)
-};
-
-class MSimdUnaryArith
- : public MUnaryInstruction,
- public SimdSameAsReturnedTypePolicy<0>::Data
-{
- public:
- enum Operation {
-#define OP_LIST_(OP) OP,
- FOREACH_FLOAT_SIMD_UNOP(OP_LIST_)
- neg,
- not_
-#undef OP_LIST_
- };
-
- static const char* OperationName(Operation op) {
- switch (op) {
- case abs: return "abs";
- case neg: return "neg";
- case not_: return "not";
- case reciprocalApproximation: return "reciprocalApproximation";
- case reciprocalSqrtApproximation: return "reciprocalSqrtApproximation";
- case sqrt: return "sqrt";
- }
- MOZ_CRASH("unexpected operation");
- }
-
- private:
- Operation operation_;
-
- MSimdUnaryArith(MDefinition* def, Operation op)
- : MUnaryInstruction(classOpcode, def), operation_(op)
- {
- MIRType type = def->type();
- MOZ_ASSERT(IsSimdType(type));
- MOZ_ASSERT_IF(IsIntegerSimdType(type), op == neg || op == not_);
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdUnaryArith)
- TRIVIAL_NEW_WRAPPERS
-
- Operation operation() const { return operation_; }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) && ins->toSimdUnaryArith()->operation() == operation();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdUnaryArith);
-};
-
-// Compares each value of a SIMD vector to each corresponding lane's value of
-// another SIMD vector, and returns a boolean vector containing the results of
-// the comparison: all bits are set to 1 if the comparison is true, 0 otherwise.
-// When comparing integer vectors, a SimdSign must be provided to request signed
-// or unsigned comparison.
-class MSimdBinaryComp
- : public MBinaryInstruction,
- public SimdAllPolicy::Data
-{
- public:
- enum Operation {
-#define NAME_(x) x,
- FOREACH_COMP_SIMD_OP(NAME_)
-#undef NAME_
- };
-
- static const char* OperationName(Operation op) {
- switch (op) {
-#define NAME_(x) case x: return #x;
- FOREACH_COMP_SIMD_OP(NAME_)
-#undef NAME_
- }
- MOZ_CRASH("unexpected operation");
- }
-
- private:
- Operation operation_;
- SimdSign sign_;
-
- MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
- : MBinaryInstruction(classOpcode, left, right), operation_(op), sign_(sign)
- {
- MOZ_ASSERT(left->type() == right->type());
- MIRType opType = left->type();
- MOZ_ASSERT(IsSimdType(opType));
- MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(opType),
- "Signedness must be specified for integer SIMD compares");
- setResultType(MIRTypeToBooleanSimdType(opType));
- specialization_ = opType;
- setMovable();
- if (op == equal || op == notEqual)
- setCommutative();
- }
-
- static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
- Operation op, SimdSign sign)
- {
- return new (alloc) MSimdBinaryComp(left, right, op, sign);
- }
-
- public:
- INSTRUCTION_HEADER(SimdBinaryComp)
-
- // Create a MSimdBinaryComp or an equivalent sequence of instructions
- // supported by the current target.
- // Add all instructions to the basic block |addTo|.
- static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
- MDefinition* right, Operation op, SimdSign sign);
-
- AliasSet getAliasSet() const override
- {
- return AliasSet::None();
- }
-
- Operation operation() const { return operation_; }
- SimdSign signedness() const { return sign_; }
- MIRType specialization() const { return specialization_; }
-
- // Swap the operands and reverse the comparison predicate.
- void reverse() {
- switch (operation()) {
- case greaterThan: operation_ = lessThan; break;
- case greaterThanOrEqual: operation_ = lessThanOrEqual; break;
- case lessThan: operation_ = greaterThan; break;
- case lessThanOrEqual: operation_ = greaterThanOrEqual; break;
- case equal:
- case notEqual:
- break;
- default: MOZ_CRASH("Unexpected compare operation");
- }
- swapOperands();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(ins))
- return false;
- const MSimdBinaryComp* other = ins->toSimdBinaryComp();
- return specialization_ == other->specialization() &&
- operation_ == other->operation() &&
- sign_ == other->signedness();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdBinaryComp)
-};
-
-class MSimdBinaryArith
- : public MBinaryInstruction,
- public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
-{
- public:
- enum Operation {
-#define OP_LIST_(OP) Op_##OP,
- FOREACH_NUMERIC_SIMD_BINOP(OP_LIST_)
- FOREACH_FLOAT_SIMD_BINOP(OP_LIST_)
-#undef OP_LIST_
- };
-
- static const char* OperationName(Operation op) {
- switch (op) {
-#define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
- FOREACH_NUMERIC_SIMD_BINOP(OP_CASE_LIST_)
- FOREACH_FLOAT_SIMD_BINOP(OP_CASE_LIST_)
-#undef OP_CASE_LIST_
- }
- MOZ_CRASH("unexpected operation");
- }
-
- private:
- Operation operation_;
-
- MSimdBinaryArith(MDefinition* left, MDefinition* right, Operation op)
- : MBinaryInstruction(classOpcode, left, right), operation_(op)
- {
- MOZ_ASSERT(left->type() == right->type());
- MIRType type = left->type();
- MOZ_ASSERT(IsSimdType(type));
- MOZ_ASSERT_IF(IsIntegerSimdType(type), op == Op_add || op == Op_sub || op == Op_mul);
- setResultType(type);
- setMovable();
- if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
- setCommutative();
- }
-
- static MSimdBinaryArith* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
- Operation op)
- {
- return new (alloc) MSimdBinaryArith(left, right, op);
- }
-
- public:
- INSTRUCTION_HEADER(SimdBinaryArith)
-
- // Create an MSimdBinaryArith instruction and add it to the basic block. Possibly
- // create and add an equivalent sequence of instructions instead if the
- // current target doesn't support the requested shift operation directly.
- static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
- MDefinition* right, Operation op);
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- Operation operation() const { return operation_; }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(ins))
- return false;
- return operation_ == ins->toSimdBinaryArith()->operation();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdBinaryArith)
-};
-
-class MSimdBinarySaturating
- : public MBinaryInstruction,
- public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1>>::Data
-{
- public:
- enum Operation
- {
- add,
- sub,
- };
-
- static const char* OperationName(Operation op)
- {
- switch (op) {
- case add:
- return "add";
- case sub:
- return "sub";
- }
- MOZ_CRASH("unexpected operation");
- }
-
- private:
- Operation operation_;
- SimdSign sign_;
-
- MSimdBinarySaturating(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
- : MBinaryInstruction(classOpcode, left, right)
- , operation_(op)
- , sign_(sign)
- {
- MOZ_ASSERT(left->type() == right->type());
- MIRType type = left->type();
- MOZ_ASSERT(type == MIRType::Int8x16 || type == MIRType::Int16x8);
- setResultType(type);
- setMovable();
- if (op == add)
- setCommutative();
- }
-
- public:
- INSTRUCTION_HEADER(SimdBinarySaturating)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override { return AliasSet::None(); }
-
- Operation operation() const { return operation_; }
- SimdSign signedness() const { return sign_; }
-
- bool congruentTo(const MDefinition* ins) const override
- {
- if (!binaryCongruentTo(ins))
- return false;
- return operation_ == ins->toSimdBinarySaturating()->operation() &&
- sign_ == ins->toSimdBinarySaturating()->signedness();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdBinarySaturating)
-};
-
-class MSimdBinaryBitwise
- : public MBinaryInstruction,
- public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
-{
- public:
- enum Operation {
- and_,
- or_,
- xor_
- };
-
- static const char* OperationName(Operation op) {
- switch (op) {
- case and_: return "and";
- case or_: return "or";
- case xor_: return "xor";
- }
- MOZ_CRASH("unexpected operation");
- }
-
- private:
- Operation operation_;
-
- MSimdBinaryBitwise(MDefinition* left, MDefinition* right, Operation op)
- : MBinaryInstruction(classOpcode, left, right), operation_(op)
- {
- MOZ_ASSERT(left->type() == right->type());
- MIRType type = left->type();
- MOZ_ASSERT(IsSimdType(type));
- setResultType(type);
- setMovable();
- setCommutative();
- }
-
- public:
- INSTRUCTION_HEADER(SimdBinaryBitwise)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- Operation operation() const { return operation_; }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(ins))
- return false;
- return operation_ == ins->toSimdBinaryBitwise()->operation();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MSimdBinaryBitwise)
-};
-
-class MSimdShift
- : public MBinaryInstruction,
- public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
-{
- public:
- enum Operation {
- lsh,
- rsh,
- ursh
- };
-
- private:
- Operation operation_;
-
- MSimdShift(MDefinition* left, MDefinition* right, Operation op)
- : MBinaryInstruction(classOpcode, left, right), operation_(op)
- {
- MIRType type = left->type();
- MOZ_ASSERT(IsIntegerSimdType(type));
- setResultType(type);
- setMovable();
- }
-
- static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
- Operation op)
- {
- return new (alloc) MSimdShift(left, right, op);
- }
-
- public:
- INSTRUCTION_HEADER(SimdShift)
-
- // Create an MSimdShift instruction and add it to the basic block. Possibly
- // create and add an equivalent sequence of instructions instead if the
- // current target doesn't support the requested shift operation directly.
- // Return the inserted MInstruction that computes the shifted value.
- static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
- MDefinition* right, Operation op);
-
- // Get the relevant right shift operation given the signedness of a type.
- static Operation rshForSign(SimdSign sign) {
- return sign == SimdSign::Unsigned ? ursh : rsh;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- Operation operation() const { return operation_; }
-
- static const char* OperationName(Operation op) {
- switch (op) {
- case lsh: return "lsh";
- case rsh: return "rsh-arithmetic";
- case ursh: return "rsh-logical";
- }
- MOZ_CRASH("unexpected operation");
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(ins))
- return false;
- return operation_ == ins->toSimdShift()->operation();
- }
-
- ALLOW_CLONE(MSimdShift)
-};
-
-class MSimdSelect
- : public MTernaryInstruction,
- public SimdSelectPolicy::Data
-{
- MSimdSelect(MDefinition* mask, MDefinition* lhs, MDefinition* rhs)
- : MTernaryInstruction(classOpcode, mask, lhs, rhs)
- {
- MOZ_ASSERT(IsBooleanSimdType(mask->type()));
- MOZ_ASSERT(lhs->type() == lhs->type());
- MIRType type = lhs->type();
- MOZ_ASSERT(IsSimdType(type));
- setResultType(type);
- specialization_ = type;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SimdSelect)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, mask))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- ALLOW_CLONE(MSimdSelect)
-};
-
-// Deep clone a constant JSObject.
-class MCloneLiteral
- : public MUnaryInstruction,
- public ObjectPolicy<0>::Data
-{
- protected:
- explicit MCloneLiteral(MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(CloneLiteral)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MParameter : public MNullaryInstruction
-{
- int32_t index_;
-
- MParameter(int32_t index, TemporaryTypeSet* types)
- : MNullaryInstruction(classOpcode),
- index_(index)
- {
- setResultType(MIRType::Value);
- setResultTypeSet(types);
- }
-
- public:
- INSTRUCTION_HEADER(Parameter)
- TRIVIAL_NEW_WRAPPERS
-
- static const int32_t THIS_SLOT = -1;
- int32_t index() const {
- return index_;
- }
- void printOpcode(GenericPrinter& out) const override;
-
- HashNumber valueHash() const override;
- bool congruentTo(const MDefinition* ins) const override;
-};
-
-class MCallee : public MNullaryInstruction
-{
- public:
- MCallee()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Callee)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MIsConstructing : public MNullaryInstruction
-{
- public:
- MIsConstructing()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsConstructing)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
class MControlInstruction : public MInstruction
{
protected:
explicit MControlInstruction(Opcode op)
: MInstruction(op)
{ }
public:
@@ -3290,592 +1968,16 @@ TypeSetIncludes(TypeSet* types, MIRType
bool
EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
MIRType type2, TemporaryTypeSet* typeset2);
bool
CanStoreUnboxedType(TempAllocator& alloc,
JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
-class MNewArray
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- // Number of elements to allocate for the array.
- uint32_t length_;
-
- // Heap where the array should be allocated.
- gc::InitialHeap initialHeap_;
-
- // Whether values written to this array should be converted to double first.
- bool convertDoubleElements_;
-
- jsbytecode* pc_;
-
- bool vmCall_;
-
- MNewArray(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t length,
- MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc,
- bool vmCall = false);
-
- public:
- INSTRUCTION_HEADER(NewArray)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- static MNewArray* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
- uint32_t length, MConstant* templateConst,
- gc::InitialHeap initialHeap, jsbytecode* pc)
- {
- return new(alloc) MNewArray(alloc, constraints, length, templateConst, initialHeap, pc,
- true);
- }
-
- uint32_t length() const {
- return length_;
- }
-
- JSObject* templateObject() const {
- return getOperand(0)->toConstant()->toObjectOrNull();
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- jsbytecode* pc() const {
- return pc_;
- }
-
- bool isVMCall() const {
- return vmCall_;
- }
-
- bool convertDoubleElements() const {
- return convertDoubleElements_;
- }
-
- // 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 override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- // The template object can safely be used in the recover instruction
- // because it can never be mutated by any other function execution.
- return templateObject() != nullptr;
- }
-};
-
-class MNewArrayCopyOnWrite : public MNullaryInstruction
-{
- CompilerGCPointer<ArrayObject*> templateObject_;
- gc::InitialHeap initialHeap_;
-
- MNewArrayCopyOnWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
- ArrayObject* templateObject, gc::InitialHeap initialHeap)
- : MNullaryInstruction(classOpcode),
- templateObject_(templateObject),
- initialHeap_(initialHeap)
- {
- MOZ_ASSERT(!templateObject->isSingleton());
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(NewArrayCopyOnWrite)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- ArrayObject* templateObject() const {
- return templateObject_;
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject_);
- }
-};
-
-class MNewArrayDynamicLength
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- CompilerObject templateObject_;
- gc::InitialHeap initialHeap_;
-
- MNewArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
- JSObject* templateObject, gc::InitialHeap initialHeap,
- MDefinition* length)
- : MUnaryInstruction(classOpcode, length),
- templateObject_(templateObject),
- initialHeap_(initialHeap)
- {
- setGuard(); // Need to throw if length is negative.
- setResultType(MIRType::Object);
- if (!templateObject->isSingleton())
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(NewArrayDynamicLength)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
- NAMED_OPERANDS((0, length))
-
- JSObject* templateObject() const {
- return templateObject_;
- }
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject_);
- }
-};
-
-class MNewTypedArray
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- gc::InitialHeap initialHeap_;
-
- MNewTypedArray(TempAllocator& alloc, CompilerConstraintList* constraints,
- MConstant* templateConst, gc::InitialHeap initialHeap)
- : MUnaryInstruction(classOpcode, templateConst),
- initialHeap_(initialHeap)
- {
- MOZ_ASSERT(!templateObject()->isSingleton());
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
- }
-
- public:
- INSTRUCTION_HEADER(NewTypedArray)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- TypedArrayObject* templateObject() const {
- return &getOperand(0)->toConstant()->toObject().as<TypedArrayObject>();
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-class MNewTypedArrayDynamicLength
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- CompilerObject templateObject_;
- gc::InitialHeap initialHeap_;
-
- MNewTypedArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
- JSObject* templateObject, gc::InitialHeap initialHeap,
- MDefinition* length)
- : MUnaryInstruction(classOpcode, length),
- templateObject_(templateObject),
- initialHeap_(initialHeap)
- {
- setGuard(); // Need to throw if length is negative.
- setResultType(MIRType::Object);
- if (!templateObject->isSingleton())
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(NewTypedArrayDynamicLength)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- MDefinition* length() const {
- return getOperand(0);
- }
- JSObject* templateObject() const {
- return templateObject_;
- }
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject_);
- }
-};
-
-class MNewObject
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- public:
- enum Mode { ObjectLiteral, ObjectCreate };
-
- private:
- gc::InitialHeap initialHeap_;
- Mode mode_;
- bool vmCall_;
-
- MNewObject(TempAllocator& alloc, CompilerConstraintList* constraints, MConstant* templateConst,
- gc::InitialHeap initialHeap, Mode mode, bool vmCall = false)
- : MUnaryInstruction(classOpcode, templateConst),
- initialHeap_(initialHeap),
- mode_(mode),
- vmCall_(vmCall)
- {
- MOZ_ASSERT_IF(mode != ObjectLiteral, templateObject());
- setResultType(MIRType::Object);
-
- if (JSObject* obj = templateObject())
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, obj));
-
- // The constant is kept separated in a MConstant, this way we can safely
- // mark it during GC if we recover the object allocation. Otherwise, by
- // making it emittedAtUses, we do not produce register allocations for
- // it and inline its content inside the code produced by the
- // CodeGenerator.
- if (templateConst->toConstant()->type() == MIRType::Object)
- templateConst->setEmittedAtUses();
- }
-
- public:
- INSTRUCTION_HEADER(NewObject)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- static MNewObject* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
- MConstant* templateConst, gc::InitialHeap initialHeap,
- Mode mode)
- {
- return new(alloc) MNewObject(alloc, constraints, templateConst, initialHeap, mode, true);
- }
-
- Mode mode() const {
- return mode_;
- }
-
- JSObject* templateObject() const {
- return getOperand(0)->toConstant()->toObjectOrNull();
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- bool isVMCall() const {
- return vmCall_;
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- // The template object can safely be used in the recover instruction
- // because it can never be mutated by any other function execution.
- return templateObject() != nullptr;
- }
-};
-
-
-class MNewIterator
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- public:
- enum Type {
- ArrayIterator,
- StringIterator,
- };
-
-private:
- Type type_;
-
- MNewIterator(TempAllocator& alloc, CompilerConstraintList* constraints,
- MConstant* templateConst, Type type)
- : MUnaryInstruction(classOpcode, templateConst),
- type_(type)
- {
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
- templateConst->setEmittedAtUses();
- }
-
- public:
- INSTRUCTION_HEADER(NewIterator)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- Type type() const {
- return type_;
- }
-
- JSObject* templateObject() {
- return getOperand(0)->toConstant()->toObjectOrNull();
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-
-class MNewTypedObject : public MNullaryInstruction
-{
- CompilerGCPointer<InlineTypedObject*> templateObject_;
- gc::InitialHeap initialHeap_;
-
- MNewTypedObject(TempAllocator& alloc, CompilerConstraintList* constraints,
- InlineTypedObject* templateObject,
- gc::InitialHeap initialHeap)
- : MNullaryInstruction(classOpcode),
- templateObject_(templateObject),
- initialHeap_(initialHeap)
- {
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(NewTypedObject)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- InlineTypedObject* templateObject() const {
- return templateObject_;
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject_);
- }
-};
-
-class MTypedObjectDescr
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- private:
- explicit MTypedObjectDescr(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypedObjectDescr)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Generic way for constructing a SIMD object in IonMonkey, this instruction
-// takes as argument a SIMD instruction and returns a new SIMD object which
-// corresponds to the MIRType of its operand.
-class MSimdBox
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- protected:
- CompilerGCPointer<InlineTypedObject*> templateObject_;
- SimdType simdType_;
- gc::InitialHeap initialHeap_;
-
- MSimdBox(TempAllocator& alloc,
- CompilerConstraintList* constraints,
- MDefinition* op,
- InlineTypedObject* templateObject,
- SimdType simdType,
- gc::InitialHeap initialHeap)
- : MUnaryInstruction(classOpcode, op),
- templateObject_(templateObject),
- simdType_(simdType),
- initialHeap_(initialHeap)
- {
- MOZ_ASSERT(IsSimdType(op->type()));
- setMovable();
- setResultType(MIRType::Object);
- if (constraints)
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(SimdBox)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- InlineTypedObject* templateObject() const {
- return templateObject_;
- }
-
- SimdType simdType() const {
- return simdType_;
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- const MSimdBox* box = ins->toSimdBox();
- if (box->simdType() != simdType())
- return false;
- MOZ_ASSERT(box->templateObject() == templateObject());
- if (box->initialHeap() != initialHeap())
- return false;
- return true;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void printOpcode(GenericPrinter& out) const override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject_);
- }
-};
-
-class MSimdUnbox
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- protected:
- SimdType simdType_;
-
- MSimdUnbox(MDefinition* op, SimdType simdType)
- : MUnaryInstruction(classOpcode, op),
- simdType_(simdType)
- {
- MIRType type = SimdTypeToMIRType(simdType);
- MOZ_ASSERT(IsSimdType(type));
- setGuard();
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(SimdUnbox)
- TRIVIAL_NEW_WRAPPERS
- ALLOW_CLONE(MSimdUnbox)
-
- SimdType simdType() const { return simdType_; }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- return ins->toSimdUnbox()->simdType() == simdType();
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-};
-
-// Creates a new derived type object. At runtime, this is just a call
-// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
-// compile to particularly optimized code. However, using a distinct
-// MIR for creating derived type objects allows the compiler to
-// optimize ephemeral typed objects as would be created for a
-// reference like `a.b.c` -- here, the `a.b` will create an ephemeral
-// derived type object that aliases the memory of `a` itself. The
-// specific nature of `a.b` is revealed by using
-// `MNewDerivedTypedObject` rather than `MGetProperty` or what have
-// you. Moreover, the compiler knows that there are no side-effects,
-// so `MNewDerivedTypedObject` instructions can be reordered or pruned
-// as dead code.
-class MNewDerivedTypedObject
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>,
- ObjectPolicy<1>,
- IntPolicy<2> >::Data
-{
- private:
- TypedObjectPrediction prediction_;
-
- MNewDerivedTypedObject(TypedObjectPrediction prediction,
- MDefinition* type,
- MDefinition* owner,
- MDefinition* offset)
- : MTernaryInstruction(classOpcode, type, owner, offset),
- prediction_(prediction)
- {
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(NewDerivedTypedObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, type), (1, owner), (2, offset))
-
- TypedObjectPrediction prediction() const {
- return prediction_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
// This vector is used when the recovered object is kept unboxed. We map the
// offset of each property to the index of the corresponding operands in the
// object state.
struct OperandIndexMap : public TempObject
{
// The number of properties is limited by scalar replacement. Thus we cannot
// have any large number of properties.
FixedList<uint8_t> map;
@@ -4056,98 +2158,16 @@ class MArgumentState
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
-// Setting __proto__ in an object literal.
-class MMutateProto
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
-{
- protected:
- MMutateProto(MDefinition* obj, MDefinition* value)
- : MBinaryInstruction(classOpcode, obj, value)
- {
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(MutateProto)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getObject), (1, getValue))
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MInitPropGetterSetter
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
- CompilerPropertyName name_;
-
- MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value)
- : MBinaryInstruction(classOpcode, obj, value),
- name_(name)
- { }
-
- public:
- INSTRUCTION_HEADER(InitPropGetterSetter)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, value))
-
- PropertyName* name() const {
- return name_;
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MInitElem
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
-{
- MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
- : MTernaryInstruction(classOpcode, obj, id, value)
- {
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(InitElem)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getObject), (1, getId), (2, getValue))
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MInitElemGetterSetter
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >::Data
-{
- MInitElemGetterSetter(MDefinition* obj, MDefinition* id, MDefinition* value)
- : MTernaryInstruction(classOpcode, obj, id, value)
- { }
-
- public:
- INSTRUCTION_HEADER(InitElemGetterSetter)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, idValue), (2, value))
-
-};
-
// WrappedFunction wraps a JSFunction so it can safely be used off-thread.
// In particular, a function's flags can be modified on the active thread as
// functions are relazified and delazified, so we must be careful not to access
// these flags off-thread.
class WrappedFunction : public TempObject
{
CompilerFunction fun_;
uint16_t nargs_;
@@ -4333,124 +2353,16 @@ class MCallDOMNative : public MCall
virtual bool isCallDOMNative() const override {
return true;
}
virtual void computeMovable() override;
};
-// fun.apply(self, arguments)
-class MApplyArgs
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
-{
- protected:
- // Monomorphic cache of single target from TI, or nullptr.
- WrappedFunction* target_;
-
- MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
- : MTernaryInstruction(classOpcode, fun, argc, self),
- target_(target)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(ApplyArgs)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis))
-
- // For TI-informed monomorphic callsites.
- WrappedFunction* getSingleTarget() const {
- return target_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- bool appendRoots(MRootList& roots) const override {
- if (target_)
- return target_->appendRoots(roots);
- return true;
- }
-};
-
-// fun.apply(fn, array)
-class MApplyArray
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
-{
- protected:
- // Monomorphic cache of single target from TI, or nullptr.
- WrappedFunction* target_;
-
- MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
- : MTernaryInstruction(classOpcode, fun, elements, self),
- target_(target)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(ApplyArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis))
-
- // For TI-informed monomorphic callsites.
- WrappedFunction* getSingleTarget() const {
- return target_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- bool appendRoots(MRootList& roots) const override {
- if (target_)
- return target_->appendRoots(roots);
- return true;
- }
-};
-
-class MBail : public MNullaryInstruction
-{
- protected:
- explicit MBail(BailoutKind kind)
- : MNullaryInstruction(classOpcode)
- {
- bailoutKind_ = kind;
- setGuard();
- }
-
- private:
- BailoutKind bailoutKind_;
-
- public:
- INSTRUCTION_HEADER(Bail)
-
- static MBail*
- New(TempAllocator& alloc, BailoutKind kind) {
- return new(alloc) MBail(kind);
- }
- static MBail*
- New(TempAllocator& alloc) {
- return new(alloc) MBail(Bailout_Inevitable);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
-};
-
class MUnreachable
: public MAryControlInstruction<0, 0>,
public NoTypePolicy::Data
{
MUnreachable()
: MAryControlInstruction(classOpcode)
{ }
@@ -4458,858 +2370,28 @@ class MUnreachable
INSTRUCTION_HEADER(Unreachable)
TRIVIAL_NEW_WRAPPERS
AliasSet getAliasSet() const override {
return AliasSet::None();
}
};
-// This class serve as a way to force the encoding of a snapshot, even if there
-// is no resume point using it. This is useful to run MAssertRecoveredOnBailout
-// assertions.
-class MEncodeSnapshot : public MNullaryInstruction
-{
- protected:
- MEncodeSnapshot()
- : MNullaryInstruction(classOpcode)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(EncodeSnapshot)
-
- static MEncodeSnapshot*
- New(TempAllocator& alloc) {
- return new(alloc) MEncodeSnapshot();
- }
-};
-
-class MAssertRecoveredOnBailout
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- protected:
- bool mustBeRecovered_;
-
- MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered)
- : MUnaryInstruction(classOpcode, ins), mustBeRecovered_(mustBeRecovered)
- {
- setResultType(MIRType::Value);
- setRecoveredOnBailout();
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(AssertRecoveredOnBailout)
- TRIVIAL_NEW_WRAPPERS
-
- // Needed to assert that float32 instructions are correctly recovered.
- bool canConsumeFloat32(MUse* use) const override { return true; }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-class MAssertFloat32
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- protected:
- bool mustBeFloat32_;
-
- MAssertFloat32(MDefinition* value, bool mustBeFloat32)
- : MUnaryInstruction(classOpcode, value), mustBeFloat32_(mustBeFloat32)
- {
- }
-
- public:
- INSTRUCTION_HEADER(AssertFloat32)
- TRIVIAL_NEW_WRAPPERS
-
- bool canConsumeFloat32(MUse* use) const override { return true; }
-
- bool mustBeFloat32() const { return mustBeFloat32_; }
-};
-
-class MGetDynamicName
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >::Data
-{
- protected:
- MGetDynamicName(MDefinition* envChain, MDefinition* name)
- : MBinaryInstruction(classOpcode, envChain, name)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(GetDynamicName)
- NAMED_OPERANDS((0, getEnvironmentChain), (1, getName))
-
- static MGetDynamicName*
- New(TempAllocator& alloc, MDefinition* envChain, MDefinition* name) {
- return new(alloc) MGetDynamicName(envChain, name);
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MCallDirectEval
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>,
- StringPolicy<1>,
- BoxPolicy<2> >::Data
-{
- protected:
- MCallDirectEval(MDefinition* envChain, MDefinition* string,
- MDefinition* newTargetValue, jsbytecode* pc)
- : MTernaryInstruction(classOpcode, envChain, string, newTargetValue),
- pc_(pc)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(CallDirectEval)
- NAMED_OPERANDS((0, getEnvironmentChain), (1, getString), (2, getNewTargetValue))
-
- static MCallDirectEval*
- New(TempAllocator& alloc, MDefinition* envChain, MDefinition* string,
- MDefinition* newTargetValue, jsbytecode* pc)
- {
- return new(alloc) MCallDirectEval(envChain, string, newTargetValue, pc);
- }
-
- jsbytecode* pc() const {
- return pc_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- private:
- jsbytecode* pc_;
-};
-
-class MCompare
- : public MBinaryInstruction,
- public ComparePolicy::Data
-{
- 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
- // Symbol compared to Boolean
- // Object compared to Boolean
- // Value compared to Boolean
- Compare_Boolean,
-
- // Int32 compared to Int32
- // Boolean compared to Boolean
- Compare_Int32,
- Compare_Int32MaybeCoerceBoth,
- Compare_Int32MaybeCoerceLHS,
- Compare_Int32MaybeCoerceRHS,
-
- // Int32 compared as unsigneds
- Compare_UInt32,
-
- // Int64 compared to Int64.
- Compare_Int64,
-
- // Int64 compared as unsigneds.
- Compare_UInt64,
-
- // Double compared to Double
- Compare_Double,
-
- Compare_DoubleMaybeCoerceLHS,
- Compare_DoubleMaybeCoerceRHS,
-
- // Float compared to Float
- Compare_Float32,
-
- // String compared to String
- Compare_String,
-
- // Symbol compared to Symbol
- Compare_Symbol,
-
- // 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_Bitwise,
-
- // All other possible compares
- Compare_Unknown
- };
-
- private:
- CompareType compareType_;
- JSOp jsop_;
- bool operandMightEmulateUndefined_;
- bool operandsAreNeverNaN_;
-
- // When a floating-point comparison is converted to an integer comparison
- // (when range analysis proves it safe), we need to convert the operands
- // to integer as well.
- bool truncateOperands_;
-
- MCompare(MDefinition* left, MDefinition* right, JSOp jsop)
- : MBinaryInstruction(classOpcode, left, right),
- compareType_(Compare_Unknown),
- jsop_(jsop),
- operandMightEmulateUndefined_(true),
- operandsAreNeverNaN_(false),
- truncateOperands_(false)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- MCompare(MDefinition* left, MDefinition* right, JSOp jsop, CompareType compareType)
- : MCompare(left, right, jsop)
- {
- MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
- compareType == Compare_Int64 || compareType == Compare_UInt64 ||
- compareType == Compare_Double || compareType == Compare_Float32);
- compareType_ = compareType;
- operandMightEmulateUndefined_ = false;
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(Compare)
- TRIVIAL_NEW_WRAPPERS
-
- MOZ_MUST_USE bool tryFold(bool* result);
- MOZ_MUST_USE bool evaluateConstantOperands(TempAllocator& alloc, bool* result);
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
- bool* filtersNull);
-
- CompareType compareType() const {
- return compareType_;
- }
- bool isInt32Comparison() const {
- return compareType() == Compare_Int32 ||
- compareType() == Compare_Int32MaybeCoerceBoth ||
- compareType() == Compare_Int32MaybeCoerceLHS ||
- compareType() == Compare_Int32MaybeCoerceRHS;
- }
- bool isDoubleComparison() const {
- return compareType() == Compare_Double ||
- compareType() == Compare_DoubleMaybeCoerceLHS ||
- compareType() == Compare_DoubleMaybeCoerceRHS;
- }
- bool isFloat32Comparison() const {
- return compareType() == Compare_Float32;
- }
- bool isNumericComparison() const {
- return isInt32Comparison() ||
- isDoubleComparison() ||
- isFloat32Comparison();
- }
- void setCompareType(CompareType type) {
- compareType_ = type;
- }
- MIRType inputType();
-
- JSOp jsop() const {
- return jsop_;
- }
- void markNoOperandEmulatesUndefined() {
- operandMightEmulateUndefined_ = false;
- }
- bool operandMightEmulateUndefined() const {
- return operandMightEmulateUndefined_;
- }
- bool operandsAreNeverNaN() const {
- return operandsAreNeverNaN_;
- }
- AliasSet getAliasSet() const override {
- // Strict equality is never effectful.
- if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
- return AliasSet::None();
- if (compareType_ == Compare_Unknown)
- return AliasSet::Store(AliasSet::Any);
- MOZ_ASSERT(compareType_ <= Compare_Bitwise);
- return AliasSet::None();
- }
-
- void printOpcode(GenericPrinter& out) const override;
- void collectRangeInfoPreTrunc() override;
-
- void trySpecializeFloat32(TempAllocator& alloc) override;
- bool isFloat32Commutative() const override { return true; }
- bool needTruncation(TruncateKind kind) override;
- void truncate() override;
- TruncateKind operandTruncateKind(size_t index) const override;
-
- static CompareType determineCompareType(JSOp op, MDefinition* left, MDefinition* right);
- void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
-
-# ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- // Both sides of the compare can be Float32
- return compareType_ == Compare_Float32;
- }
-# endif
-
- ALLOW_CLONE(MCompare)
-
- protected:
- MOZ_MUST_USE bool tryFoldEqualOperands(bool* result);
- MOZ_MUST_USE bool tryFoldTypeOf(bool* result);
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(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,
- public NoTypePolicy::Data
-{
- MBox(TempAllocator& alloc, MDefinition* ins)
- : MUnaryInstruction(classOpcode, ins)
- {
- setResultType(MIRType::Value);
- if (ins->resultTypeSet()) {
- setResultTypeSet(ins->resultTypeSet());
- } else if (ins->type() != MIRType::Value) {
- TypeSet::Type ntype = ins->type() == MIRType::Object
- ? TypeSet::AnyObjectType()
- : TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type()));
- setResultTypeSet(alloc.lifoAlloc()->new_<TemporaryTypeSet>(alloc.lifoAlloc(), ntype));
- }
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Box)
- static MBox* New(TempAllocator& alloc, MDefinition* ins)
- {
- // Cannot box a box.
- MOZ_ASSERT(ins->type() != MIRType::Value);
-
- return new(alloc) MBox(alloc, ins);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MBox)
-};
+
// 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 final : public MUnaryInstruction, public BoxInputsPolicy::Data
-{
- 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.
- };
-
- private:
- Mode mode_;
- BailoutKind bailoutKind_;
-
- MUnbox(MDefinition* ins, MIRType type, Mode mode, BailoutKind kind, TempAllocator& alloc)
- : MUnaryInstruction(classOpcode, ins),
- mode_(mode)
- {
- // Only allow unboxing a non MIRType::Value when input and output types
- // don't match. This is often used to force a bailout. Boxing happens
- // during type analysis.
- MOZ_ASSERT_IF(ins->type() != MIRType::Value, type != ins->type());
-
- MOZ_ASSERT(type == MIRType::Boolean ||
- type == MIRType::Int32 ||
- type == MIRType::Double ||
- type == MIRType::String ||
- type == MIRType::Symbol ||
- type == MIRType::Object);
-
- TemporaryTypeSet* resultSet = ins->resultTypeSet();
- if (resultSet && type == MIRType::Object)
- resultSet = resultSet->cloneObjectsOnly(alloc.lifoAlloc());
-
- setResultType(type);
- setResultTypeSet(resultSet);
- setMovable();
-
- if (mode_ == TypeBarrier || mode_ == Fallible)
- setGuard();
-
- bailoutKind_ = kind;
- }
- public:
- INSTRUCTION_HEADER(Unbox)
- static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode)
- {
- // Unless we were given a specific BailoutKind, pick a default based on
- // the type we expect.
- BailoutKind kind;
- switch (type) {
- case MIRType::Boolean:
- kind = Bailout_NonBooleanInput;
- break;
- case MIRType::Int32:
- kind = Bailout_NonInt32Input;
- break;
- case MIRType::Double:
- kind = Bailout_NonNumericInput; // Int32s are fine too
- break;
- case MIRType::String:
- kind = Bailout_NonStringInput;
- break;
- case MIRType::Symbol:
- kind = Bailout_NonSymbolInput;
- break;
- case MIRType::Object:
- kind = Bailout_NonObjectInput;
- break;
- default:
- MOZ_CRASH("Given MIRType cannot be unboxed.");
- }
-
- return new(alloc) MUnbox(ins, type, mode, kind, alloc);
- }
-
- static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode,
- BailoutKind kind)
- {
- return new(alloc) MUnbox(ins, type, mode, kind, alloc);
- }
-
- Mode mode() const {
- return mode_;
- }
- BailoutKind bailoutKind() const {
- // If infallible, no bailout should be generated.
- MOZ_ASSERT(fallible());
- return bailoutKind_;
- }
- bool fallible() const {
- return mode() != Infallible;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isUnbox() || ins->toUnbox()->mode() != mode())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void printOpcode(GenericPrinter& out) const override;
- void makeInfallible() {
- // Should only be called if we're already Infallible or TypeBarrier
- MOZ_ASSERT(mode() != Fallible);
- mode_ = Infallible;
- }
-
- ALLOW_CLONE(MUnbox)
-};
-
-class MGuardObject
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MGuardObject(MDefinition* ins)
- : MUnaryInstruction(classOpcode, ins)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- setResultTypeSet(ins->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(GuardObject)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MGuardString
- : public MUnaryInstruction,
- public StringPolicy<0>::Data
-{
- explicit MGuardString(MDefinition* ins)
- : MUnaryInstruction(classOpcode, ins)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(GuardString)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MPolyInlineGuard
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MPolyInlineGuard(MDefinition* ins)
- : MUnaryInstruction(classOpcode, ins)
- {
- setGuard();
- setResultType(MIRType::Object);
- setResultTypeSet(ins->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(PolyInlineGuard)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MAssertRange
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- // This is the range checked by the assertion. Don't confuse this with the
- // range_ member or the range() accessor. Since MAssertRange doesn't return
- // a value, it doesn't use those.
- const Range* assertedRange_;
-
- MAssertRange(MDefinition* ins, const Range* assertedRange)
- : MUnaryInstruction(classOpcode, ins), assertedRange_(assertedRange)
- {
- setGuard();
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(AssertRange)
- TRIVIAL_NEW_WRAPPERS
-
- const Range* assertedRange() const {
- return assertedRange_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void printOpcode(GenericPrinter& out) const override;
-};
-
-// Caller-side allocation of |this| for |new|:
-// Given a templateobject, construct |this| for JSOP_NEW
-class MCreateThisWithTemplate
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- gc::InitialHeap initialHeap_;
-
- MCreateThisWithTemplate(TempAllocator& alloc, CompilerConstraintList* constraints,
- MConstant* templateConst, gc::InitialHeap initialHeap)
- : MUnaryInstruction(classOpcode, templateConst),
- initialHeap_(initialHeap)
- {
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
- }
-
- public:
- INSTRUCTION_HEADER(CreateThisWithTemplate)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- // Template for |this|, provided by TI.
- JSObject* templateObject() const {
- return &getOperand(0)->toConstant()->toObject();
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- // Although creation of |this| modifies global state, it is safely repeatable.
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override;
-};
-
-// Caller-side allocation of |this| for |new|:
-// Given a prototype operand, construct |this| for JSOP_NEW.
-class MCreateThisWithProto
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >::Data
-{
- MCreateThisWithProto(MDefinition* callee, MDefinition* newTarget, MDefinition* prototype)
- : MTernaryInstruction(classOpcode, callee, newTarget, prototype)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(CreateThisWithProto)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getCallee), (1, getNewTarget), (2, getPrototype))
-
- // Although creation of |this| modifies global state, it is safely repeatable.
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-// Caller-side allocation of |this| for |new|:
-// Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING).
-class MCreateThis
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
- explicit MCreateThis(MDefinition* callee, MDefinition* newTarget)
- : MBinaryInstruction(classOpcode, callee, newTarget)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(CreateThis)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getCallee), (1, getNewTarget))
-
- // Although creation of |this| modifies global state, it is safely repeatable.
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-// Eager initialization of arguments object.
-class MCreateArgumentsObject
- : public MUnaryInstruction,
- public ObjectPolicy<0>::Data
-{
- CompilerGCPointer<ArgumentsObject*> templateObj_;
-
- MCreateArgumentsObject(MDefinition* callObj, ArgumentsObject* templateObj)
- : MUnaryInstruction(classOpcode, callObj),
- templateObj_(templateObj)
- {
- setResultType(MIRType::Object);
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(CreateArgumentsObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getCallObject))
-
- ArgumentsObject* templateObject() const {
- return templateObj_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObj_);
- }
-};
-
-class MGetArgumentsObjectArg
- : public MUnaryInstruction,
- public ObjectPolicy<0>::Data
-{
- size_t argno_;
-
- MGetArgumentsObjectArg(MDefinition* argsObject, size_t argno)
- : MUnaryInstruction(classOpcode, argsObject),
- argno_(argno)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(GetArgumentsObjectArg)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getArgsObject))
-
- size_t argno() const {
- return argno_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::Any);
- }
-};
-
-class MSetArgumentsObjectArg
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
-{
- size_t argno_;
-
- MSetArgumentsObjectArg(MDefinition* argsObj, size_t argno, MDefinition* value)
- : MBinaryInstruction(classOpcode, argsObj, value),
- argno_(argno)
- {
- }
-
- public:
- INSTRUCTION_HEADER(SetArgumentsObjectArg)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getArgsObject), (1, getValue))
-
- size_t argno() const {
- return argno_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::Any);
- }
-};
-
-class MRunOncePrologue
- : public MNullaryInstruction
-{
- protected:
- MRunOncePrologue()
- : MNullaryInstruction(classOpcode)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(RunOncePrologue)
- TRIVIAL_NEW_WRAPPERS
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-// 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 MBinaryInstruction,
- public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
-{
- MReturnFromCtor(MDefinition* value, MDefinition* object)
- : MBinaryInstruction(classOpcode, value, object)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ReturnFromCtor)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, getValue), (1, getObject))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MToFPInstruction
- : public MUnaryInstruction,
- public ToDoublePolicy::Data
-{
- public:
- // Types of values which can be converted.
- enum ConversionKind {
- NonStringPrimitives,
- NonNullNonStringPrimitives,
- NumbersOnly
- };
-
- private:
- ConversionKind conversion_;
-
- protected:
- MToFPInstruction(Opcode op, MDefinition* def, ConversionKind conversion = NonStringPrimitives)
- : MUnaryInstruction(op, def), conversion_(conversion)
- { }
-
- public:
- ConversionKind conversion() const {
- return conversion_;
- }
-};
-
// Converts a primitive (either typed or untyped) to a double. If the input is
// not primitive at runtime, a bailout occurs.
class MToDouble
: public MToFPInstruction
{
private:
TruncateKind implicitTruncate_;
@@ -5419,656 +2501,16 @@ class MToFloat32
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return true;
}
ALLOW_CLONE(MToFloat32)
};
-// Converts a uint32 to a double (coming from wasm).
-class MWasmUnsignedToDouble
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MWasmUnsignedToDouble(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::Double);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(WasmUnsignedToDouble)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Converts a uint32 to a float32 (coming from wasm).
-class MWasmUnsignedToFloat32
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MWasmUnsignedToFloat32(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::Float32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(WasmUnsignedToFloat32)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool canProduceFloat32() const override { return true; }
-};
-
-class MWrapInt64ToInt32
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- bool bottomHalf_;
-
- explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf = true)
- : MUnaryInstruction(classOpcode, def),
- bottomHalf_(bottomHalf)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(WrapInt64ToInt32)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isWrapInt64ToInt32())
- return false;
- if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool bottomHalf() const {
- return bottomHalf_;
- }
-};
-
-class MExtendInt32ToInt64
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- bool isUnsigned_;
-
- MExtendInt32ToInt64(MDefinition* def, bool isUnsigned)
- : MUnaryInstruction(classOpcode, def),
- isUnsigned_(isUnsigned)
- {
- setResultType(MIRType::Int64);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ExtendInt32ToInt64)
- TRIVIAL_NEW_WRAPPERS
-
- bool isUnsigned() const { return isUnsigned_; }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isExtendInt32ToInt64())
- return false;
- if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_)
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MWasmTruncateToInt64
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- bool isUnsigned_;
- wasm::BytecodeOffset bytecodeOffset_;
-
- MWasmTruncateToInt64(MDefinition* def, bool isUnsigned, wasm::BytecodeOffset bytecodeOffset)
- : MUnaryInstruction(classOpcode, def),
- isUnsigned_(isUnsigned),
- bytecodeOffset_(bytecodeOffset)
- {
- setResultType(MIRType::Int64);
- setGuard(); // neither removable nor movable because of possible side-effects.
- }
-
- public:
- INSTRUCTION_HEADER(WasmTruncateToInt64)
- TRIVIAL_NEW_WRAPPERS
-
- bool isUnsigned() const { return isUnsigned_; }
- wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) &&
- ins->toWasmTruncateToInt64()->isUnsigned() == isUnsigned_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Truncate a value to an int32, with wasm semantics: this will trap when the
-// value is out of range.
-class MWasmTruncateToInt32
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- bool isUnsigned_;
- wasm::BytecodeOffset bytecodeOffset_;
-
- explicit MWasmTruncateToInt32(MDefinition* def, bool isUnsigned,
- wasm::BytecodeOffset bytecodeOffset)
- : MUnaryInstruction(classOpcode, def),
- isUnsigned_(isUnsigned), bytecodeOffset_(bytecodeOffset)
- {
- setResultType(MIRType::Int32);
- setGuard(); // neither removable nor movable because of possible side-effects.
- }
-
- public:
- INSTRUCTION_HEADER(WasmTruncateToInt32)
- TRIVIAL_NEW_WRAPPERS
-
- bool isUnsigned() const {
- return isUnsigned_;
- }
- wasm::BytecodeOffset bytecodeOffset() const {
- return bytecodeOffset_;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) &&
- ins->toWasmTruncateToInt32()->isUnsigned() == isUnsigned_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MInt64ToFloatingPoint
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- bool isUnsigned_;
- wasm::BytecodeOffset bytecodeOffset_;
-
- MInt64ToFloatingPoint(MDefinition* def, MIRType type, wasm::BytecodeOffset bytecodeOffset,
- bool isUnsigned)
- : MUnaryInstruction(classOpcode, def),
- isUnsigned_(isUnsigned),
- bytecodeOffset_(bytecodeOffset)
- {
- MOZ_ASSERT(IsFloatingPointType(type));
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Int64ToFloatingPoint)
- TRIVIAL_NEW_WRAPPERS
-
- bool isUnsigned() const { return isUnsigned_; }
- wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isInt64ToFloatingPoint())
- return false;
- if (ins->toInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_)
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- 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,
- public ToInt32Policy::Data
-{
- bool canBeNegativeZero_;
- MacroAssembler::IntConversionInputKind conversion_;
-
- explicit MToInt32(MDefinition* def, MacroAssembler::IntConversionInputKind conversion =
- MacroAssembler::IntConversion_Any)
- : MUnaryInstruction(classOpcode, def),
- canBeNegativeZero_(true),
- conversion_(conversion)
- {
- setResultType(MIRType::Int32);
- setMovable();
-
- // An object might have "valueOf", which means it is effectful.
- // ToNumber(symbol) throws.
- if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(ToInt32)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- // this only has backwards information flow.
- void analyzeEdgeCasesBackward() override;
-
- bool canBeNegativeZero() const {
- return canBeNegativeZero_;
- }
- void setCanBeNegativeZero(bool negativeZero) {
- canBeNegativeZero_ = negativeZero;
- }
-
- MacroAssembler::IntConversionInputKind conversion() const {
- return conversion_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isToInt32() || ins->toToInt32()->conversion() != conversion())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
- void collectRangeInfoPreTrunc() override;
-
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override { return true; }
-#endif
-
- ALLOW_CLONE(MToInt32)
-};
-
-// Converts a value or typed input to a truncated int32, for use with bitwise
-// operations. This is an infallible ValueToECMAInt32.
-class MTruncateToInt32
- : public MUnaryInstruction,
- public ToInt32Policy::Data
-{
- wasm::BytecodeOffset bytecodeOffset_;
-
- explicit MTruncateToInt32(MDefinition* def,
- wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
- : MUnaryInstruction(classOpcode, def),
- bytecodeOffset_(bytecodeOffset)
- {
- setResultType(MIRType::Int32);
- setMovable();
-
- // An object might have "valueOf", which means it is effectful.
- // ToInt32(symbol) throws.
- if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(TruncateToInt32)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
- TruncateKind operandTruncateKind(size_t index) const override;
-# ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return input()->type() < MIRType::Symbol;
- }
-
- wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
-
- ALLOW_CLONE(MTruncateToInt32)
-};
-
-// Converts any type to a string
-class MToString :
- public MUnaryInstruction,
- public ToStringPolicy::Data
-{
- explicit MToString(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::String);
- setMovable();
-
- // Objects might override toString and Symbols throw. We bailout in
- // those cases and run side-effects in baseline instead.
- if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(ToString)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool fallible() const {
- return input()->mightBeType(MIRType::Object) ||
- input()->mightBeType(MIRType::Symbol);
- }
-
- ALLOW_CLONE(MToString)
-};
-
-// Converts any type to an object, throwing on null or undefined.
-class MToObject :
- public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MToObject(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::Object);
- setGuard(); // Throws on null or undefined.
- }
-
- public:
- INSTRUCTION_HEADER(ToObject)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MToObject)
-};
-
-// Converts any type to an object or null value, throwing on undefined.
-class MToObjectOrNull :
- public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MToObjectOrNull(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::ObjectOrNull);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ToObjectOrNull)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MToObjectOrNull)
-};
-
-class MBitNot
- : public MUnaryInstruction,
- public BitwisePolicy::Data
-{
- protected:
- explicit MBitNot(MDefinition* input)
- : MUnaryInstruction(classOpcode, input)
- {
- specialization_ = MIRType::None;
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(BitNot)
- TRIVIAL_NEW_WRAPPERS
-
- static MBitNot* NewInt32(TempAllocator& alloc, MDefinition* input);
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void setSpecialization(MIRType type) {
- specialization_ = type;
- setResultType(type);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- if (specialization_ == MIRType::None)
- return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return specialization_ != MIRType::None;
- }
-
- ALLOW_CLONE(MBitNot)
-};
-
-class MTypeOf
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- MIRType inputType_;
- bool inputMaybeCallableOrEmulatesUndefined_;
-
- MTypeOf(MDefinition* def, MIRType inputType)
- : MUnaryInstruction(classOpcode, def), inputType_(inputType),
- inputMaybeCallableOrEmulatesUndefined_(true)
- {
- setResultType(MIRType::String);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypeOf)
- TRIVIAL_NEW_WRAPPERS
-
- MIRType inputType() const {
- return inputType_;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints);
-
- bool inputMaybeCallableOrEmulatesUndefined() const {
- return inputMaybeCallableOrEmulatesUndefined_;
- }
- void markInputNotCallableOrEmulatesUndefined() {
- inputMaybeCallableOrEmulatesUndefined_ = false;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isTypeOf())
- return false;
- if (inputType() != ins->toTypeOf()->inputType())
- return false;
- if (inputMaybeCallableOrEmulatesUndefined() !=
- ins->toTypeOf()->inputMaybeCallableOrEmulatesUndefined())
- {
- return false;
- }
- return congruentIfOperandsEqual(ins);
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-class MToAsync
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MToAsync(MDefinition* unwrapped)
- : MUnaryInstruction(classOpcode, unwrapped)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ToAsync)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MToAsyncGen
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MToAsyncGen(MDefinition* unwrapped)
- : MUnaryInstruction(classOpcode, unwrapped)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ToAsyncGen)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MToAsyncIter
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MToAsyncIter(MDefinition* unwrapped)
- : MUnaryInstruction(classOpcode, unwrapped)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ToAsyncIter)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MToId
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MToId(MDefinition* index)
- : MUnaryInstruction(classOpcode, index)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(ToId)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MBinaryBitwiseInstruction
- : public MBinaryInstruction,
- public BitwisePolicy::Data
-{
- protected:
- MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right, MIRType type)
- : MBinaryInstruction(op, left, right), maskMatchesLeftRange(false),
- maskMatchesRightRange(false)
- {
- MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
- setResultType(type);
- setMovable();
- }
-
- void specializeAs(MIRType type);
- bool maskMatchesLeftRange;
- bool maskMatchesRightRange;
-
- public:
- MDefinition* foldsTo(TempAllocator& alloc) override;
- MDefinition* foldUnnecessaryBitop();
- virtual MDefinition* foldIfZero(size_t operand) = 0;
- virtual MDefinition* foldIfNegOne(size_t operand) = 0;
- virtual MDefinition* foldIfEqual() = 0;
- virtual MDefinition* foldIfAllBitsSet(size_t operand) = 0;
- virtual void infer(BaselineInspector* inspector, jsbytecode* pc);
- void collectRangeInfoPreTrunc() override;
-
- void setInt32Specialization() {
- specialization_ = MIRType::Int32;
- setResultType(MIRType::Int32);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return binaryCongruentTo(ins);
- }
- AliasSet getAliasSet() const override {
- if (specialization_ >= MIRType::Object)
- return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
- }
-
- TruncateKind operandTruncateKind(size_t index) const override;
-};
-
class MBitAnd : public MBinaryBitwiseInstruction
{
MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
: MBinaryBitwiseInstruction(classOpcode, left, right, type)
{ }
public:
INSTRUCTION_HEADER(BitAnd)
@@ -6274,485 +2716,16 @@ class MUrsh : public MShiftInstruction
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return specialization_ < MIRType::Object;
}
ALLOW_CLONE(MUrsh)
};
-class MSignExtendInt32
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- public:
- enum Mode {
- Byte,
- Half
- };
-
- private:
- Mode mode_;
-
- MSignExtendInt32(MDefinition* op, Mode mode)
- : MUnaryInstruction(classOpcode, op), mode_(mode)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SignExtendInt32)
- TRIVIAL_NEW_WRAPPERS
-
- Mode mode() const { return mode_; }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- return ins->isSignExtendInt32() && ins->toSignExtendInt32()->mode_ == mode_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MSignExtendInt32)
-};
-
-class MSignExtendInt64
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- public:
- enum Mode {
- Byte,
- Half,
- Word
- };
-
- private:
- Mode mode_;
-
- MSignExtendInt64(MDefinition* op, Mode mode)
- : MUnaryInstruction(classOpcode, op), mode_(mode)
- {
- setResultType(MIRType::Int64);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SignExtendInt64)
- TRIVIAL_NEW_WRAPPERS
-
- Mode mode() const { return mode_; }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- return ins->isSignExtendInt64() && ins->toSignExtendInt64()->mode_ == mode_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MSignExtendInt64)
-};
-
-class MBinaryArithInstruction
- : public MBinaryInstruction,
- public ArithPolicy::Data
-{
- // Implicit truncate flag is set by the truncate backward range analysis
- // optimization phase, and by wasm 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.
- TruncateKind implicitTruncate_;
-
- // Whether we must preserve NaN semantics, and in particular not fold
- // (x op id) or (id op x) to x, or replace a division by a multiply of the
- // exact reciprocal.
- bool mustPreserveNaN_;
-
- public:
- MBinaryArithInstruction(Opcode op, MDefinition* left, MDefinition* right)
- : MBinaryInstruction(op, left, right),
- implicitTruncate_(NoTruncate),
- mustPreserveNaN_(false)
- {
- specialization_ = MIRType::None;
- setMovable();
- }
-
- static MBinaryArithInstruction* New(TempAllocator& alloc, Opcode op,
- MDefinition* left, MDefinition* right);
-
- bool constantDoubleResult(TempAllocator& alloc);
-
- void setMustPreserveNaN(bool b) { mustPreserveNaN_ = b; }
- bool mustPreserveNaN() const { return mustPreserveNaN_; }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void printOpcode(GenericPrinter& out) const override;
-
- virtual double getIdentity() = 0;
-
- void setSpecialization(MIRType type) {
- specialization_ = type;
- setResultType(type);
- }
- void setInt32Specialization() {
- specialization_ = MIRType::Int32;
- setResultType(MIRType::Int32);
- }
- void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
-
- virtual void trySpecializeFloat32(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!binaryCongruentTo(ins))
- return false;
- const auto* other = static_cast<const MBinaryArithInstruction*>(ins);
- return other->mustPreserveNaN_ == mustPreserveNaN_;
- }
- AliasSet getAliasSet() const override {
- if (specialization_ >= MIRType::Object)
- return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
- }
-
- bool isTruncated() const {
- return implicitTruncate_ == Truncate;
- }
- TruncateKind truncateKind() const {
- return implicitTruncate_;
- }
- void setTruncateKind(TruncateKind kind) {
- implicitTruncate_ = Max(implicitTruncate_, kind);
- }
-};
-
-class MMinMax
- : public MBinaryInstruction,
- public ArithPolicy::Data
-{
- bool isMax_;
-
- MMinMax(MDefinition* left, MDefinition* right, MIRType type, bool isMax)
- : MBinaryInstruction(classOpcode, left, right),
- isMax_(isMax)
- {
- MOZ_ASSERT(IsNumberType(type));
- setResultType(type);
- setMovable();
- specialization_ = type;
- }
-
- public:
- INSTRUCTION_HEADER(MinMax)
- TRIVIAL_NEW_WRAPPERS
-
- static MMinMax* NewWasm(TempAllocator& alloc, MDefinition* left, MDefinition* right,
- MIRType type, bool isMax)
- {
- return New(alloc, left, right, type, isMax);
- }
-
- bool isMax() const {
- return isMax_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- const MMinMax* other = ins->toMinMax();
- return other->isMax() == isMax();
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void computeRange(TempAllocator& alloc) override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- bool isFloat32Commutative() const override { return true; }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MMinMax)
-};
-
-class MAbs
- : public MUnaryInstruction,
- public ArithPolicy::Data
-{
- bool implicitTruncate_;
-
- MAbs(MDefinition* num, MIRType type)
- : MUnaryInstruction(classOpcode, num),
- implicitTruncate_(false)
- {
- MOZ_ASSERT(IsNumberType(type));
- setResultType(type);
- setMovable();
- specialization_ = type;
- }
-
- public:
- INSTRUCTION_HEADER(Abs)
- TRIVIAL_NEW_WRAPPERS
-
- static MAbs* NewWasm(TempAllocator& alloc, MDefinition* num, MIRType type) {
- auto* ins = new(alloc) MAbs(num, type);
- if (type == MIRType::Int32)
- ins->implicitTruncate_ = true;
- return ins;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- bool fallible() const;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
- bool isFloat32Commutative() const override { return true; }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MAbs)
-};
-
-class MClz
- : public MUnaryInstruction
- , public BitwisePolicy::Data
-{
- bool operandIsNeverZero_;
-
- explicit MClz(MDefinition* num, MIRType type)
- : MUnaryInstruction(classOpcode, num),
- operandIsNeverZero_(false)
- {
- MOZ_ASSERT(IsIntType(type));
- MOZ_ASSERT(IsNumberType(num->type()));
- specialization_ = type;
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Clz)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, num))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool operandIsNeverZero() const {
- return operandIsNeverZero_;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void computeRange(TempAllocator& alloc) override;
- void collectRangeInfoPreTrunc() override;
-};
-
-class MCtz
- : public MUnaryInstruction
- , public BitwisePolicy::Data
-{
- bool operandIsNeverZero_;
-
- explicit MCtz(MDefinition* num, MIRType type)
- : MUnaryInstruction(classOpcode, num),
- operandIsNeverZero_(false)
- {
- MOZ_ASSERT(IsIntType(type));
- MOZ_ASSERT(IsNumberType(num->type()));
- specialization_ = type;
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Ctz)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, num))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool operandIsNeverZero() const {
- return operandIsNeverZero_;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void computeRange(TempAllocator& alloc) override;
- void collectRangeInfoPreTrunc() override;
-};
-
-class MPopcnt
- : public MUnaryInstruction
- , public BitwisePolicy::Data
-{
- explicit MPopcnt(MDefinition* num, MIRType type)
- : MUnaryInstruction(classOpcode, num)
- {
- MOZ_ASSERT(IsNumberType(num->type()));
- MOZ_ASSERT(IsIntType(type));
- specialization_ = type;
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Popcnt)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, num))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- void computeRange(TempAllocator& alloc) override;
-};
-
-// Inline implementation of Math.sqrt().
-class MSqrt
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- MSqrt(MDefinition* num, MIRType type)
- : MUnaryInstruction(classOpcode, num)
- {
- setResultType(type);
- specialization_ = type;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Sqrt)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
-
- bool isFloat32Commutative() const override { return true; }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MSqrt)
-};
-
-class MCopySign
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- MCopySign(MDefinition* lhs, MDefinition* rhs, MIRType type)
- : MBinaryInstruction(classOpcode, lhs, rhs)
- {
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(CopySign)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MCopySign)
-};
-
-// Inline implementation of atan2 (arctangent of y/x).
-class MAtan2
- : public MBinaryInstruction,
- public MixPolicy<DoublePolicy<0>, DoublePolicy<1> >::Data
-{
- MAtan2(MDefinition* y, MDefinition* x)
- : MBinaryInstruction(classOpcode, y, x)
- {
- setResultType(MIRType::Double);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Atan2)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, y), (1, x))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MAtan2)
-};
-
// Inline implementation of Math.hypot().
class MHypot
: public MVariadicInstruction,
public AllDoublePolicy::Data
{
MHypot()
: MVariadicInstruction(classOpcode)
{
@@ -6786,254 +2759,16 @@ class MHypot
}
MInstruction* clone(TempAllocator& alloc,
const MDefinitionVector& inputs) const override {
return MHypot::New(alloc, inputs);
}
};
-// Inline implementation of Math.pow().
-class MPow
- : public MBinaryInstruction,
- public PowPolicy::Data
-{
- MPow(MDefinition* input, MDefinition* power, MIRType powerType)
- : MBinaryInstruction(classOpcode, input, power)
- {
- MOZ_ASSERT(powerType == MIRType::Double ||
- powerType == MIRType::Int32 ||
- powerType == MIRType::None);
- specialization_ = powerType;
- if (powerType == MIRType::None)
- setResultType(MIRType::Value);
- else
- setResultType(MIRType::Double);
- setMovable();
- }
-
- // Helpers for `foldsTo`
- MDefinition* foldsConstant(TempAllocator &alloc);
- MDefinition* foldsConstantPower(TempAllocator &alloc);
-
- public:
- INSTRUCTION_HEADER(Pow)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* input() const {
- return lhs();
- }
- MDefinition* power() const {
- return rhs();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- if (specialization_ == MIRType::None)
- return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return specialization_ != MIRType::None;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MPow)
-};
-
-// Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
-class MPowHalf
- : public MUnaryInstruction,
- public DoublePolicy<0>::Data
-{
- bool operandIsNeverNegativeInfinity_;
- bool operandIsNeverNegativeZero_;
- bool operandIsNeverNaN_;
-
- explicit MPowHalf(MDefinition* input)
- : MUnaryInstruction(classOpcode, input),
- operandIsNeverNegativeInfinity_(false),
- operandIsNeverNegativeZero_(false),
- operandIsNeverNaN_(false)
- {
- setResultType(MIRType::Double);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(PowHalf)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- bool operandIsNeverNegativeInfinity() const {
- return operandIsNeverNegativeInfinity_;
- }
- bool operandIsNeverNegativeZero() const {
- return operandIsNeverNegativeZero_;
- }
- bool operandIsNeverNaN() const {
- return operandIsNeverNaN_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void collectRangeInfoPreTrunc() override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MPowHalf)
-};
-
-// Inline implementation of Math.random().
-class MRandom : public MNullaryInstruction
-{
- MRandom()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Double);
- }
-
- public:
- INSTRUCTION_HEADER(Random)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
-
- bool canRecoverOnBailout() const override {
-#ifdef JS_MORE_DETERMINISTIC
- return false;
-#else
- return true;
-#endif
- }
-
- ALLOW_CLONE(MRandom)
-};
-
-class MMathFunction
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- public:
- enum Function {
- Log,
- Sin,
- Cos,
- Exp,
- Tan,
- ACos,
- ASin,
- ATan,
- Log10,
- Log2,
- Log1P,
- ExpM1,
- CosH,
- SinH,
- TanH,
- ACosH,
- ASinH,
- ATanH,
- Sign,
- Trunc,
- Cbrt,
- Floor,
- Ceil,
- Round
- };
-
- private:
- Function function_;
- const MathCache* cache_;
-
- // A nullptr cache means this function will neither access nor update the cache.
- MMathFunction(MDefinition* input, Function function, const MathCache* cache)
- : MUnaryInstruction(classOpcode, input), function_(function), cache_(cache)
- {
- setResultType(MIRType::Double);
- specialization_ = MIRType::Double;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(MathFunction)
- TRIVIAL_NEW_WRAPPERS
-
- Function function() const {
- return function_;
- }
- const MathCache* cache() const {
- return cache_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isMathFunction())
- return false;
- if (ins->toMathFunction()->function() != function())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- void printOpcode(GenericPrinter& out) const override;
-
- static const char* FunctionName(Function function);
-
- bool isFloat32Commutative() const override {
- return function_ == Floor || function_ == Ceil || function_ == Round;
- }
- void trySpecializeFloat32(TempAllocator& alloc) override;
- void computeRange(TempAllocator& alloc) override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- if (input()->type() == MIRType::SinCosDouble)
- return false;
- switch(function_) {
- case Sin:
- case Log:
- case Ceil:
- case Floor:
- case Round:
- return true;
- default:
- return false;
- }
- }
-
- ALLOW_CLONE(MMathFunction)
-};
-
class MAdd : public MBinaryArithInstruction
{
MAdd(MDefinition* left, MDefinition* right)
: MBinaryArithInstruction(classOpcode, left, right)
{
setResultType(MIRType::Value);
}
@@ -7470,302 +3205,16 @@ class MMod : public MBinaryArithInstruct
bool possiblyCalls() const override {
return type() == MIRType::Double;
}
ALLOW_CLONE(MMod)
};
-class MConcat
- : public MBinaryInstruction,
- public MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >::Data
-{
- MConcat(MDefinition* left, MDefinition* right)
- : MBinaryInstruction(classOpcode, left, right)
- {
- // At least one input should be definitely string
- MOZ_ASSERT(left->type() == MIRType::String || right->type() == MIRType::String);
-
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(Concat)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MConcat)
-};
-
-class MCharCodeAt
- : public MBinaryInstruction,
- public MixPolicy<StringPolicy<0>, IntPolicy<1> >::Data
-{
- MCharCodeAt(MDefinition* str, MDefinition* index)
- : MBinaryInstruction(classOpcode, str, index)
- {
- setMovable();
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(CharCodeAt)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- virtual AliasSet getAliasSet() const override {
- // Strings are immutable, so there is no implicit dependency.
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MCharCodeAt)
-};
-
-class MFromCharCode
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- explicit MFromCharCode(MDefinition* code)
- : MUnaryInstruction(classOpcode, code)
- {
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(FromCharCode)
- TRIVIAL_NEW_WRAPPERS
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MFromCharCode)
-};
-
-class MFromCodePoint
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- explicit MFromCodePoint(MDefinition* codePoint)
- : MUnaryInstruction(classOpcode, codePoint)
- {
- setGuard(); // throws on invalid code point
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(FromCodePoint)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- ALLOW_CLONE(MFromCodePoint)
-};
-
-class MStringConvertCase
- : public MUnaryInstruction,
- public StringPolicy<0>::Data
-{
- public:
- enum Mode { LowerCase, UpperCase };
-
- private:
- Mode mode_;
-
- MStringConvertCase(MDefinition* string, Mode mode)
- : MUnaryInstruction(classOpcode, string), mode_(mode)
- {
- setResultType(MIRType::String);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(StringConvertCase)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, string))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) && ins->toStringConvertCase()->mode() == mode();
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- Mode mode() const {
- return mode_;
- }
-};
-
-class MSinCos
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- const MathCache* cache_;
-
- MSinCos(MDefinition *input, const MathCache *cache)
- : MUnaryInstruction(classOpcode, input),
- cache_(cache)
- {
- setResultType(MIRType::SinCosDouble);
- specialization_ = MIRType::Double;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SinCos)
-
- static MSinCos *New(TempAllocator &alloc, MDefinition *input, const MathCache *cache)
- {
- return new (alloc) MSinCos(input, cache);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition *ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- bool possiblyCalls() const override {
- return true;
- }
- const MathCache* cache() const {
- return cache_;
- }
-};
-
-class MStringSplit
- : public MBinaryInstruction,
- public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
-{
- CompilerObjectGroup group_;
-
- MStringSplit(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* string,
- MDefinition* sep, ObjectGroup* group)
- : MBinaryInstruction(classOpcode, string, sep),
- group_(group)
- {
- setResultType(MIRType::Object);
- TemporaryTypeSet* types = MakeSingletonTypeSet(alloc, constraints, group);
- setResultTypeSet(types);
- }
-
- public:
- INSTRUCTION_HEADER(StringSplit)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
- NAMED_OPERANDS((0, string), (1, separator))
-
- ObjectGroup* group() const {
- return group_;
- }
- bool possiblyCalls() const override {
- return true;
- }
- virtual AliasSet getAliasSet() const override {
- // Although this instruction returns a new array, we don't have to mark
- // it as store instruction, see also MNewArray.
- return AliasSet::None();
- }
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(group_);
- }
-};
-
-// Returns the value to use as |this| value. See also ComputeThis and
-// BoxNonStrictThis in Interpreter.h.
-class MComputeThis
- : public MUnaryInstruction,
- public BoxPolicy<0>::Data
-{
- explicit MComputeThis(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(ComputeThis)
- TRIVIAL_NEW_WRAPPERS
-
- bool possiblyCalls() const override {
- return true;
- }
-
- // Note: don't override getAliasSet: the thisValue hook can be effectful.
-};
-
-// Load an arrow function's |new.target| value.
-class MArrowNewTarget
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MArrowNewTarget(MDefinition* callee)
- : MUnaryInstruction(classOpcode, callee)
- {
- setResultType(MIRType::Value);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ArrowNewTarget)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, callee))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- // An arrow function's lexical |this| value is immutable.
- return AliasSet::None();
- }
-};
-
class MPhi final
: public MDefinition,
public InlineListNode<MPhi>,
public NoTypePolicy::Data
{
using InputVector = js::Vector<MUse, 2, JitAllocPolicy>;
InputVector inputs_;
@@ -7944,273 +3393,16 @@ class MPhi final
canConsumeFloat32_ = can;
}
TruncateKind operandTruncateKind(size_t index) const override;
bool needTruncation(TruncateKind kind) override;
void truncate() override;
};
-// The goal of a Beta node is to split a def at a conditionally taken
-// branch, so that uses dominated by it have a different name.
-class MBeta
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- // This is the range induced by a comparison and branch in a preceding
- // block. Note that this does not reflect any range constraints from
- // the input value itself, so this value may differ from the range()
- // range after it is computed.
- const Range* comparison_;
-
- MBeta(MDefinition* val, const Range* comp)
- : MUnaryInstruction(classOpcode, val),
- comparison_(comp)
- {
- setResultType(val->type());
- setResultTypeSet(val->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(Beta)
- TRIVIAL_NEW_WRAPPERS
-
- void printOpcode(GenericPrinter& out) const override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
-};
-
-// If input evaluates to false (i.e. it's NaN, 0 or -0), 0 is returned, else the input is returned
-class MNaNToZero
- : public MUnaryInstruction,
- public DoublePolicy<0>::Data
-{
- bool operandIsNeverNaN_;
- bool operandIsNeverNegativeZero_;
-
- explicit MNaNToZero(MDefinition* input)
- : MUnaryInstruction(classOpcode, input),
- operandIsNeverNaN_(false),
- operandIsNeverNegativeZero_(false)
- {
- setResultType(MIRType::Double);
- setMovable();
- }
- public:
- INSTRUCTION_HEADER(NaNToZero)
- TRIVIAL_NEW_WRAPPERS
-
- bool operandIsNeverNaN() const {
- return operandIsNeverNaN_;
- }
-
- bool operandIsNeverNegativeZero() const {
- return operandIsNeverNegativeZero_;
- }
-
- void collectRangeInfoPreTrunc() override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MNaNToZero)
-};
-
-// MIR representation of a Value on the OSR BaselineFrame.
-// The Value is indexed off of OsrFrameReg.
-class MOsrValue
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- ptrdiff_t frameOffset_;
-
- MOsrValue(MOsrEntry* entry, ptrdiff_t frameOffset)
- : MUnaryInstruction(classOpcode, entry),
- frameOffset_(frameOffset)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(OsrValue)
- TRIVIAL_NEW_WRAPPERS
-
- ptrdiff_t frameOffset() const {
- return frameOffset_;
- }
-
- MOsrEntry* entry() {
- return getOperand(0)->toOsrEntry();
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame.
-// The pointer is indexed off of OsrFrameReg.
-class MOsrEnvironmentChain
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- explicit MOsrEnvironmentChain(MOsrEntry* entry)
- : MUnaryInstruction(classOpcode, entry)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(OsrEnvironmentChain)
- TRIVIAL_NEW_WRAPPERS
-
- MOsrEntry* entry() {
- return getOperand(0)->toOsrEntry();
- }
-};
-
-// MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame.
-// The pointer is indexed off of OsrFrameReg.
-class MOsrArgumentsObject
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- explicit MOsrArgumentsObject(MOsrEntry* entry)
- : MUnaryInstruction(classOpcode, entry)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(OsrArgumentsObject)
- TRIVIAL_NEW_WRAPPERS
-
- MOsrEntry* entry() {
- return getOperand(0)->toOsrEntry();
- }
-};
-
-// MIR representation of the return value on the OSR BaselineFrame.
-// The Value is indexed off of OsrFrameReg.
-class MOsrReturnValue
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- explicit MOsrReturnValue(MOsrEntry* entry)
- : MUnaryInstruction(classOpcode, entry)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(OsrReturnValue)
- TRIVIAL_NEW_WRAPPERS
-
- MOsrEntry* entry() {
- return getOperand(0)->toOsrEntry();
- }
-};
-
-class MBinarySharedStub
- : public MBinaryInstruction,
- public MixPolicy<BoxPolicy<0>, BoxPolicy<1> >::Data
-{
- protected:
- explicit MBinarySharedStub(MDefinition* left, MDefinition* right)
- : MBinaryInstruction(classOpcode, left, right)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(BinarySharedStub)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MUnarySharedStub
- : public MUnaryInstruction,
- public BoxPolicy<0>::Data
-{
- explicit MUnarySharedStub(MDefinition* input)
- : MUnaryInstruction(classOpcode, input)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(UnarySharedStub)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MNullarySharedStub
- : public MNullaryInstruction
-{
- explicit MNullarySharedStub()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(NullarySharedStub)
- TRIVIAL_NEW_WRAPPERS
-};
-
-// Check the current frame for over-recursion past the global stack limit.
-class MCheckOverRecursed
- : public MNullaryInstruction
-{
- MCheckOverRecursed()
- : MNullaryInstruction(classOpcode)
- { }
-
- public:
- INSTRUCTION_HEADER(CheckOverRecursed)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Check whether we need to fire the interrupt handler.
-class MInterruptCheck : public MNullaryInstruction
-{
- MInterruptCheck()
- : MNullaryInstruction(classOpcode)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(InterruptCheck)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
// Directly jumps to the indicated trap, leaving Wasm code and reporting a
// runtime error.
class MWasmTrap
: public MAryControlInstruction<0, 0>,
public NoTypePolicy::Data
{
wasm::Trap trap_;
@@ -8229,478 +3421,16 @@ class MWasmTrap
AliasSet getAliasSet() const override {
return AliasSet::None();
}
wasm::Trap trap() const { return trap_; }
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
};
-// Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
-// it to baseline to throw at the correct pc.
-class MLexicalCheck
- : public MUnaryInstruction,
- public BoxPolicy<0>::Data
-{
- BailoutKind kind_;
- explicit MLexicalCheck(MDefinition* input, BailoutKind kind = Bailout_UninitializedLexical)
- : MUnaryInstruction(classOpcode, input),
- kind_(kind)
- {
- setResultType(MIRType::Value);
- setResultTypeSet(input->resultTypeSet());
- setMovable();
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(LexicalCheck)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- BailoutKind bailoutKind() const {
- return kind_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-};
-
-// Unconditionally throw an uninitialized let error.
-class MThrowRuntimeLexicalError : public MNullaryInstruction
-{
- unsigned errorNumber_;
-
- explicit MThrowRuntimeLexicalError(unsigned errorNumber)
- : MNullaryInstruction(classOpcode),
- errorNumber_(errorNumber)
- {
- setGuard();
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(ThrowRuntimeLexicalError)
- TRIVIAL_NEW_WRAPPERS
-
- unsigned errorNumber() const {
- return errorNumber_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// In the prologues of global and eval scripts, check for redeclarations.
-class MGlobalNameConflictsCheck : public MNullaryInstruction
-{
- MGlobalNameConflictsCheck()
- : MNullaryInstruction(classOpcode)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(GlobalNameConflictsCheck)
- TRIVIAL_NEW_WRAPPERS
-};
-
-// If not defined, set a global variable to |undefined|.
-class MDefVar
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- CompilerPropertyName name_; // Target name to be defined.
- unsigned attrs_; // Attributes to be set.
-
- private:
- MDefVar(PropertyName* name, unsigned attrs, MDefinition* envChain)
- : MUnaryInstruction(classOpcode, envChain),
- name_(name),
- attrs_(attrs)
- {
- }
-
- public:
- INSTRUCTION_HEADER(DefVar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, environmentChain))
-
- PropertyName* name() const {
- return name_;
- }
- unsigned attrs() const {
- return attrs_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MDefLexical
- : public MNullaryInstruction
-{
- CompilerPropertyName name_; // Target name to be defined.
- unsigned attrs_; // Attributes to be set.
-
- private:
- MDefLexical(PropertyName* name, unsigned attrs)
- : MNullaryInstruction(classOpcode),
- name_(name),
- attrs_(attrs)
- { }
-
- public:
- INSTRUCTION_HEADER(DefLexical)
- TRIVIAL_NEW_WRAPPERS
-
- PropertyName* name() const {
- return name_;
- }
- unsigned attrs() const {
- return attrs_;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MDefFun
- : public MBinaryInstruction,
- public ObjectPolicy<0>::Data
-{
- private:
- MDefFun(MDefinition* fun, MDefinition* envChain)
- : MBinaryInstruction(classOpcode, fun, envChain)
- {}
-
- public:
- INSTRUCTION_HEADER(DefFun)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, fun), (1, environmentChain))
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MRegExp : public MNullaryInstruction
-{
- CompilerGCPointer<RegExpObject*> source_;
- bool mustClone_;
- bool hasShared_;
-
- MRegExp(TempAllocator& alloc, CompilerConstraintList* constraints, RegExpObject* source,
- bool hasShared)
- : MNullaryInstruction(classOpcode),
- source_(source),
- mustClone_(true),
- hasShared_(hasShared)
- {
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, source));
- }
-
- public:
- INSTRUCTION_HEADER(RegExp)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
-
- void setDoNotClone() {
- mustClone_ = false;
- }
- bool mustClone() const {
- return mustClone_;
- }
- bool hasShared() const {
- return hasShared_;
- }
- RegExpObject* source() const {
- return source_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(source_);
- }
-};
-
-class MRegExpMatcher
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>,
- StringPolicy<1>,
- IntPolicy<2> >::Data
-{
- private:
-
- MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
- : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
- {
- setMovable();
- // May be object or null.
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(RegExpMatcher)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
-
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MRegExpSearcher
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>,
- StringPolicy<1>,
- IntPolicy<2> >::Data
-{
- private:
-
- MRegExpSearcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
- : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
- {
- setMovable();
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(RegExpSearcher)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
-
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MRegExpTester
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>,
- StringPolicy<1>,
- IntPolicy<2> >::Data
-{
- private:
-
- MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
- : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
- {
- setMovable();
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(RegExpTester)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
-
- bool possiblyCalls() const override {
- return true;
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-class MRegExpPrototypeOptimizable
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MRegExpPrototypeOptimizable(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(RegExpPrototypeOptimizable)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MRegExpInstanceOptimizable
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
- explicit MRegExpInstanceOptimizable(MDefinition* object, MDefinition* proto)
- : MBinaryInstruction(classOpcode, object, proto)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(RegExpInstanceOptimizable)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, proto))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MGetFirstDollarIndex
- : public MUnaryInstruction,
- public StringPolicy<0>::Data
-{
- explicit MGetFirstDollarIndex(MDefinition* str)
- : MUnaryInstruction(classOpcode, str)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(GetFirstDollarIndex)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, str))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-};
-
-class MStringReplace
- : public MTernaryInstruction,
- public MixPolicy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >::Data
-{
- private:
-
- bool isFlatReplacement_;
-
- MStringReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
- : MTernaryInstruction(classOpcode, string, pattern, replacement),
- isFlatReplacement_(false)
- {
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(StringReplace)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, string), (1, pattern), (2, replacement))
-
- void setFlatReplacement() {
- MOZ_ASSERT(!isFlatReplacement_);
- isFlatReplacement_ = true;
- }
-
- bool isFlatReplacement() const {
- return isFlatReplacement_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isStringReplace())
- return false;
- if (isFlatReplacement_ != ins->toStringReplace()->isFlatReplacement())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- if (isFlatReplacement_) {
- MOZ_ASSERT(!pattern()->isRegExp());
- return true;
- }
- return false;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MSubstr
- : public MTernaryInstruction,
- public MixPolicy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
-{
- private:
-
- MSubstr(MDefinition* string, MDefinition* begin, MDefinition* length)
- : MTernaryInstruction(classOpcode, string, begin, length)
- {
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(Substr)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, string), (1, begin), (2, length))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MClassConstructor : public MNullaryInstruction
-{
- jsbytecode* pc_;
-
- explicit MClassConstructor(jsbytecode* pc)
- : MNullaryInstruction(classOpcode),
- pc_(pc)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ClassConstructor)
- TRIVIAL_NEW_WRAPPERS
-
- jsbytecode* pc() const {
- return pc_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
struct LambdaFunctionInfo
{
// The functions used in lambdas are the canonical original function in
// the script, and are immutable except for delazification. Record this
// information while still on the active thread to avoid races.
CompilerFunction fun;
uint16_t flags;
uint16_t nargs;
@@ -8725,1027 +3455,27 @@ struct LambdaFunctionInfo
return roots.append(fun->lazyScript());
}
private:
LambdaFunctionInfo(const LambdaFunctionInfo&) = delete;
void operator=(const LambdaFunctionInfo&) = delete;
};
-class MLambda
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- const LambdaFunctionInfo info_;
-
- MLambda(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
- MConstant* cst)
- : MBinaryInstruction(classOpcode, envChain, cst),
- info_(&cst->toObject().as<JSFunction>())
- {
- setResultType(MIRType::Object);
- if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
- }
-
- public:
- INSTRUCTION_HEADER(Lambda)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
- NAMED_OPERANDS((0, environmentChain))
-
- MConstant* functionOperand() const {
- return getOperand(1)->toConstant();
- }
- const LambdaFunctionInfo& info() const {
- return info_;
- }
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return info_.appendRoots(roots);
- }
-};
-
-class MLambdaArrow
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>::Data
-{
- const LambdaFunctionInfo info_;
-
- MLambdaArrow(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
- MDefinition* newTarget, MConstant* cst)
- : MTernaryInstruction(classOpcode, envChain, newTarget, cst),
- info_(&cst->toObject().as<JSFunction>())
- {
- setResultType(MIRType::Object);
- MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun));
- if (!info().fun->isSingleton())
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
- }
-
- public:
- INSTRUCTION_HEADER(LambdaArrow)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
- NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
-
- MConstant* functionOperand() const {
- return getOperand(2)->toConstant();
- }
- const LambdaFunctionInfo& info() const {
- return info_;
- }
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return info_.appendRoots(roots);
- }
-};
-
-class MSetFunName
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
-{
- uint8_t prefixKind_;
-
- explicit MSetFunName(MDefinition* fun, MDefinition* name, uint8_t prefixKind)
- : MBinaryInstruction(classOpcode, fun, name),
- prefixKind_(prefixKind)
- {
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(SetFunName)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, fun), (1, name))
-
- uint8_t prefixKind() const {
- return prefixKind_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-// Returns obj->slots.
-class MSlots
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MSlots(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Slots);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Slots)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MSlots)
-};
-
-// Returns obj->elements.
-class MElements
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool unboxed_;
-
- explicit MElements(MDefinition* object, bool unboxed = false)
- : MUnaryInstruction(classOpcode, object), unboxed_(unboxed)
- {
- setResultType(MIRType::Elements);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Elements)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool unboxed() const {
- return unboxed_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) &&
- ins->toElements()->unboxed() == unboxed();
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MElements)
-};
-
-// A constant value for some object's typed array elements.
-class MConstantElements : public MNullaryInstruction
-{
- SharedMem<void*> value_;
-
- protected:
- explicit MConstantElements(SharedMem<void*> v)
- : MNullaryInstruction(classOpcode),
- value_(v)
- {
- setResultType(MIRType::Elements);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ConstantElements)
- TRIVIAL_NEW_WRAPPERS
-
- SharedMem<void*> value() const {
- return value_;
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- HashNumber valueHash() const override {
- return (HashNumber)(size_t) value_.asValue();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return ins->isConstantElements() && ins->toConstantElements()->value() == value();
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- ALLOW_CLONE(MConstantElements)
-};
-
-// Passes through an object's elements, after ensuring it is entirely doubles.
-class MConvertElementsToDoubles
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MConvertElementsToDoubles(MDefinition* elements)
- : MUnaryInstruction(classOpcode, elements)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Elements);
- }
-
- public:
- INSTRUCTION_HEADER(ConvertElementsToDoubles)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- // This instruction can read and write to the elements' contents.
- // However, it is alright to hoist this from loops which explicitly
- // read or write to the elements: such reads and writes will use double
- // values and can be reordered freely wrt this conversion, except that
- // definite double loads must follow the conversion. The latter
- // property is ensured by chaining this instruction with the elements
- // themselves, in the same manner as MBoundsCheck.
- return AliasSet::None();
- }
-};
-
-// If |elements| has the CONVERT_DOUBLE_ELEMENTS flag, convert value to
-// double. Else return the original value.
-class MMaybeToDoubleElement
- : public MBinaryInstruction,
- public IntPolicy<1>::Data
-{
- MMaybeToDoubleElement(MDefinition* elements, MDefinition* value)
- : MBinaryInstruction(classOpcode, elements, value)
- {
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- setMovable();
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(MaybeToDoubleElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, value))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Passes through an object, after ensuring its elements are not copy on write.
-class MMaybeCopyElementsForWrite
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool checkNative_;
-
- explicit MMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
- : MUnaryInstruction(classOpcode, object), checkNative_(checkNative)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- setResultTypeSet(object->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(MaybeCopyElementsForWrite)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool checkNative() const {
- return checkNative_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) &&
- checkNative() == ins->toMaybeCopyElementsForWrite()->checkNative();
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-#ifdef DEBUG
- bool needsResumePoint() const override {
- // This instruction is idempotent and does not change observable
- // behavior, so does not need its own resume point.
- return false;
- }
-#endif
-
-};
-
-// Load the initialized length from an elements header.
-class MInitializedLength
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MInitializedLength(MDefinition* elements)
- : MUnaryInstruction(classOpcode, elements)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(InitializedLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MInitializedLength)
-};
-
-// Store to the initialized length in an elements header. Note the input is an
-// *index*, one less than the desired length.
-class MSetInitializedLength
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- MSetInitializedLength(MDefinition* elements, MDefinition* index)
- : MBinaryInstruction(classOpcode, elements, index)
- { }
-
- public:
- INSTRUCTION_HEADER(SetInitializedLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MSetInitializedLength)
-};
-
-// Load the array length from an elements header.
-class MArrayLength
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MArrayLength(MDefinition* elements)
- : MUnaryInstruction(classOpcode, elements)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ArrayLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MArrayLength)
-};
-
-// Store to the length in an elements header. Note the input is an *index*, one
-// less than the desired length.
-class MSetArrayLength
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- MSetArrayLength(MDefinition* elements, MDefinition* index)
- : MBinaryInstruction(classOpcode, elements, index)
- { }
-
- public:
- INSTRUCTION_HEADER(SetArrayLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-
- // By default no, unless built as a recovered instruction.
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return isRecoveredOnBailout();
- }
-};
-
-class MGetNextEntryForIterator
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
- public:
- enum Mode {
- Map,
- Set
- };
-
- private:
- Mode mode_;
-
- explicit MGetNextEntryForIterator(MDefinition* iter, MDefinition* result, Mode mode)
- : MBinaryInstruction(classOpcode, iter, result), mode_(mode)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(GetNextEntryForIterator)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, iter), (1, result))
-
- Mode mode() const {
- return mode_;
- }
-};
-
-// Read the length of a typed array.
-class MTypedArrayLength
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MTypedArrayLength(MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypedArrayLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::TypedArrayLength);
- }
-
- void computeRange(TempAllocator& alloc) override;
-};
-
-// Load a typed array's elements vector.
-class MTypedArrayElements
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MTypedArrayElements(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Elements);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypedArrayElements)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MTypedArrayElements)
-};
-
-class MSetDisjointTypedElements
- : public MTernaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MSetDisjointTypedElements(MDefinition* target, MDefinition* targetOffset,
- MDefinition* source)
- : MTernaryInstruction(classOpcode, target, targetOffset, source)
- {
- MOZ_ASSERT(target->type() == MIRType::Object);
- MOZ_ASSERT(targetOffset->type() == MIRType::Int32);
- MOZ_ASSERT(source->type() == MIRType::Object);
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(SetDisjointTypedElements)
- NAMED_OPERANDS((0, target), (1, targetOffset), (2, source))
-
- static MSetDisjointTypedElements*
- New(TempAllocator& alloc, MDefinition* target, MDefinition* targetOffset,
- MDefinition* source)
- {
- return new(alloc) MSetDisjointTypedElements(target, targetOffset, source);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-
- ALLOW_CLONE(MSetDisjointTypedElements)
-};
-
-// Load a binary data object's "elements", which is just its opaque
-// binary data space. Eventually this should probably be
-// unified with `MTypedArrayElements`.
-class MTypedObjectElements
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool definitelyOutline_;
-
- private:
- explicit MTypedObjectElements(MDefinition* object, bool definitelyOutline)
- : MUnaryInstruction(classOpcode, object),
- definitelyOutline_(definitelyOutline)
- {
- setResultType(MIRType::Elements);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypedObjectElements)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool definitelyOutline() const {
- return definitelyOutline_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isTypedObjectElements())
- return false;
- const MTypedObjectElements* other = ins->toTypedObjectElements();
- if (other->definitelyOutline() != definitelyOutline())
- return false;
- return congruentIfOperandsEqual(other);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Inlined version of the js::SetTypedObjectOffset() intrinsic.
-class MSetTypedObjectOffset
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- private:
- MSetTypedObjectOffset(MDefinition* object, MDefinition* offset)
- : MBinaryInstruction(classOpcode, object, offset)
- {
- MOZ_ASSERT(object->type() == MIRType::Object);
- MOZ_ASSERT(offset->type() == MIRType::Int32);
- setResultType(MIRType::None);
- }
-
- public:
- INSTRUCTION_HEADER(SetTypedObjectOffset)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, offset))
-
- AliasSet getAliasSet() const override {
- // This affects the result of MTypedObjectElements,
- // which is described as a load of ObjectFields.
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-};
-
-class MKeepAliveObject
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MKeepAliveObject(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::None);
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(KeepAliveObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
-};
-
-// Perform !-operation
-class MNot
- : public MUnaryInstruction,
- public TestPolicy::Data
-{
- bool operandMightEmulateUndefined_;
- bool operandIsNeverNaN_;
-
- explicit MNot(MDefinition* input, CompilerConstraintList* constraints = nullptr)
- : MUnaryInstruction(classOpcode, input),
- operandMightEmulateUndefined_(true),
- operandIsNeverNaN_(false)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- if (constraints)
- cacheOperandMightEmulateUndefined(constraints);
- }
-
- void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
-
- public:
- static MNot* NewInt32(TempAllocator& alloc, MDefinition* input) {
- MOZ_ASSERT(input->type() == MIRType::Int32 || input->type() == MIRType::Int64);
- auto* ins = new(alloc) MNot(input);
- ins->setResultType(MIRType::Int32);
- return ins;
- }
-
- INSTRUCTION_HEADER(Not)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- void markNoOperandEmulatesUndefined() {
- operandMightEmulateUndefined_ = false;
- }
- bool operandMightEmulateUndefined() const {
- return operandMightEmulateUndefined_;
- }
- bool operandIsNeverNaN() const {
- return operandIsNeverNaN_;
- }
-
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void collectRangeInfoPreTrunc() override;
-
- void trySpecializeFloat32(TempAllocator& alloc) override;
- bool isFloat32Commutative() const override { return true; }
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-// Bailout if index + minimum < 0 or index + maximum >= length. The length used
-// in a bounds check must not be negative, or the wrong result may be computed
-// (unsigned comparisons may be used).
-class MBoundsCheck
- : public MBinaryInstruction,
- public MixPolicy<IntPolicy<0>, IntPolicy<1>>::Data
-{
- // Range over which to perform the bounds check, may be modified by GVN.
- int32_t minimum_;
- int32_t maximum_;
- bool fallible_;
-
- MBoundsCheck(MDefinition* index, MDefinition* length)
- : MBinaryInstruction(classOpcode, index, length),
- minimum_(0), maximum_(0), fallible_(true)
- {
- setGuard();
- setMovable();
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(length->type() == MIRType::Int32);
-
- // Returns the checked index.
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(BoundsCheck)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, index), (1, length))
-
- int32_t minimum() const {
- return minimum_;
- }
- void setMinimum(int32_t n) {
- MOZ_ASSERT(fallible_);
- minimum_ = n;
- }
- int32_t maximum() const {
- return maximum_;
- }
- void setMaximum(int32_t n) {
- MOZ_ASSERT(fallible_);
- maximum_ = n;
- }
- MDefinition* foldsTo(TempAllocator& alloc) override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isBoundsCheck())
- return false;
- const MBoundsCheck* other = ins->toBoundsCheck();
- if (minimum() != other->minimum() || maximum() != other->maximum())
- return false;
- if (fallible() != other->fallible())
- return false;
- return congruentIfOperandsEqual(other);
- }
- virtual AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
- bool fallible() const {
- return fallible_;
- }
- void collectRangeInfoPreTrunc() override;
-
- ALLOW_CLONE(MBoundsCheck)
-};
-
-// Bailout if index < minimum.
-class MBoundsCheckLower
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- int32_t minimum_;
- bool fallible_;
-
- explicit MBoundsCheckLower(MDefinition* index)
- : MUnaryInstruction(classOpcode, index), minimum_(0), fallible_(true)
- {
- setGuard();
- setMovable();
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(BoundsCheckLower)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, index))
-
- int32_t minimum() const {
- return minimum_;
- }
- void setMinimum(int32_t n) {
- minimum_ = n;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool fallible() const {
- return fallible_;
- }
- void collectRangeInfoPreTrunc() override;
-};
-
// Instructions which access an object's elements can either do so on a
// definition accessing that elements pointer, or on the object itself, if its
// elements are inline. In the latter case there must be an offset associated
// with the access.
static inline bool
IsValidElementsType(MDefinition* elements, int32_t offsetAdjustment)
{
return elements->type() == MIRType::Elements ||
(elements->type() == MIRType::Object && offsetAdjustment != 0);
}
-// Load a value from a dense array's element vector and does a hole check if the
-// array is not known to be packed.
-class MLoadElement
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool needsHoleCheck_;
- bool loadDoubles_;
- int32_t offsetAdjustment_;
-
- MLoadElement(MDefinition* elements, MDefinition* index,
- bool needsHoleCheck, bool loadDoubles, int32_t offsetAdjustment = 0)
- : MBinaryInstruction(classOpcode, elements, index),
- needsHoleCheck_(needsHoleCheck),
- loadDoubles_(loadDoubles),
- offsetAdjustment_(offsetAdjustment)
- {
- if (needsHoleCheck) {
- // Uses may be optimized away based on this instruction's result
- // type. This means it's invalid to DCE this instruction, as we
- // have to invalidate when we read a hole.
- setGuard();
- }
- setResultType(MIRType::Value);
- setMovable();
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(LoadElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- bool needsHoleCheck() const {
- return needsHoleCheck_;
- }
- bool loadDoubles() const {
- return loadDoubles_;
- }
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool fallible() const {
- return needsHoleCheck();
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadElement())
- return false;
- const MLoadElement* other = ins->toLoadElement();
- if (needsHoleCheck() != other->needsHoleCheck())
- return false;
- if (loadDoubles() != other->loadDoubles())
- return false;
- if (offsetAdjustment() != other->offsetAdjustment())
- return false;
- return congruentIfOperandsEqual(other);
- }
- AliasType mightAlias(const MDefinition* store) const override;
- MDefinition* foldsTo(TempAllocator& alloc) override;
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::Element);
- }
-
- ALLOW_CLONE(MLoadElement)
-};
-
-// Load a value from the elements vector of a native object. If the index is
-// out-of-bounds, or the indexed slot has a hole, undefined is returned instead.
-class MLoadElementHole
- : public MTernaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool needsNegativeIntCheck_;
- bool needsHoleCheck_;
-
- MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
- bool needsHoleCheck)
- : MTernaryInstruction(classOpcode, elements, index, initLength),
- needsNegativeIntCheck_(true),
- needsHoleCheck_(needsHoleCheck)
- {
- setResultType(MIRType::Value);
- setMovable();
-
- // Set the guard flag to make sure we bail when we see a negative
- // index. We can clear this flag (and needsNegativeIntCheck_) in
- // collectRangeInfoPreTrunc.
- setGuard();
-
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(initLength->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(LoadElementHole)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
-
- bool needsNegativeIntCheck() const {
- return needsNegativeIntCheck_;
- }
- bool needsHoleCheck() const {
- return needsHoleCheck_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadElementHole())
- return false;
- const MLoadElementHole* other = ins->toLoadElementHole();
- if (needsHoleCheck() != other->needsHoleCheck())
- return false;
- if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
- return false;
- return congruentIfOperandsEqual(other);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::Element);
- }
- void collectRangeInfoPreTrunc() override;
-
- ALLOW_CLONE(MLoadElementHole)
-};
-
-class MLoadUnboxedObjectOrNull
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- public:
- enum NullBehavior {
- HandleNull,
- BailOnNull,
- NullNotPossible
- };
-
- private:
- NullBehavior nullBehavior_;
- int32_t offsetAdjustment_;
-
- MLoadUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
- NullBehavior nullBehavior, int32_t offsetAdjustment)
- : MBinaryInstruction(classOpcode, elements, index),
- nullBehavior_(nullBehavior),
- offsetAdjustment_(offsetAdjustment)
- {
- if (nullBehavior == BailOnNull) {
- // Don't eliminate loads which bail out on a null pointer, for the
- // same reason as MLoadElement.
- setGuard();
- }
- setResultType(nullBehavior == HandleNull ? MIRType::Value : MIRType::Object);
- setMovable();
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(LoadUnboxedObjectOrNull)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- NullBehavior nullBehavior() const {
- return nullBehavior_;
- }
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool fallible() const {
- return nullBehavior() == BailOnNull;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadUnboxedObjectOrNull())
- return false;
- const MLoadUnboxedObjectOrNull* other = ins->toLoadUnboxedObjectOrNull();
- if (nullBehavior() != other->nullBehavior())
- return false;
- if (offsetAdjustment() != other->offsetAdjustment())
- return false;
- return congruentIfOperandsEqual(other);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::UnboxedElement);
- }
- AliasType mightAlias(const MDefinition* store) const override;
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MLoadUnboxedObjectOrNull)
-};
-
-class MLoadUnboxedString
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- int32_t offsetAdjustment_;
-
- MLoadUnboxedString(MDefinition* elements, MDefinition* index, int32_t offsetAdjustment = 0)
- : MBinaryInstruction(classOpcode, elements, index),
- offsetAdjustment_(offsetAdjustment)
- {
- setResultType(MIRType::String);
- setMovable();
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(LoadUnboxedString)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadUnboxedString())
- return false;
- const MLoadUnboxedString* other = ins->toLoadUnboxedString();
- if (offsetAdjustment() != other->offsetAdjustment())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::UnboxedElement);
- }
-
- ALLOW_CLONE(MLoadUnboxedString)
-};
-
class MStoreElementCommon
{
MIRType elementType_;
bool needsBarrier_;
protected:
MStoreElementCommon()
: elementType_(MIRType::Value),
@@ -9763,418 +3493,16 @@ class MStoreElementCommon
bool needsBarrier() const {
return needsBarrier_;
}
void setNeedsBarrier() {
needsBarrier_ = true;
}
};
-// This instruction is used to load an element of a non-escaped inlined array.
-class MLoadElementFromState
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- MLoadElementFromState(MDefinition* array, MDefinition* index)
- : MBinaryInstruction(classOpcode, array, index)
- {
- MOZ_ASSERT(array->isArgumentState());
- MOZ_ASSERT(index->type() == MIRType::Int32);
- setResultType(MIRType::Value);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(LoadElementFromState)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, array), (1, index));
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Store a value to a dense array slots vector.
-class MStoreElement
- : public MTernaryInstruction,
- public MStoreElementCommon,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >::Data
-{
- bool needsHoleCheck_;
- int32_t offsetAdjustment_;
-
- MStoreElement(MDefinition* elements, MDefinition* index, MDefinition* value,
- bool needsHoleCheck, int32_t offsetAdjustment = 0)
- : MTernaryInstruction(classOpcode, elements, index, value)
- {
- needsHoleCheck_ = needsHoleCheck;
- offsetAdjustment_ = offsetAdjustment;
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(StoreElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::Element);
- }
- bool needsHoleCheck() const {
- return needsHoleCheck_;
- }
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool fallible() const {
- return needsHoleCheck();
- }
-
- ALLOW_CLONE(MStoreElement)
-};
-
-// Like MStoreElement, but supports indexes >= initialized length. The downside
-// is that we cannot hoist the elements vector and bounds check, since this
-// instruction may update the (initialized) length and reallocate the elements
-// vector.
-class MStoreElementHole
- : public MQuaternaryInstruction,
- public MStoreElementCommon,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
-{
- MStoreElementHole(MDefinition* object, MDefinition* elements,
- MDefinition* index, MDefinition* value)
- : MQuaternaryInstruction(classOpcode, object, elements, index, value)
- {
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(StoreElementHole)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
-
- AliasSet getAliasSet() const override {
- // StoreElementHole can update the initialized length, the array length
- // or reallocate obj->elements.
- return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
- }
-
- ALLOW_CLONE(MStoreElementHole)
-};
-
-// Try to store a value to a dense array slots vector. May fail due to the object being frozen.
-// Cannot be used on an object that has extra indexed properties.
-class MFallibleStoreElement
- : public MQuaternaryInstruction,
- public MStoreElementCommon,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
-{
- bool strict_;
-
- MFallibleStoreElement(MDefinition* object, MDefinition* elements,
- MDefinition* index, MDefinition* value,
- bool strict)
- : MQuaternaryInstruction(classOpcode, object, elements, index, value),
- strict_(strict)
- {
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(FallibleStoreElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
- }
- bool strict() const {
- return strict_;
- }
-
- ALLOW_CLONE(MFallibleStoreElement)
-};
-
-
-// Store an unboxed object or null pointer to a v\ector.
-class MStoreUnboxedObjectOrNull
- : public MQuaternaryInstruction,
- public StoreUnboxedObjectOrNullPolicy::Data
-{
- int32_t offsetAdjustment_;
- bool preBarrier_;
-
- MStoreUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
- MDefinition* value, MDefinition* typedObj,
- int32_t offsetAdjustment = 0, bool preBarrier = true)
- : MQuaternaryInstruction(classOpcode, elements, index, value, typedObj),
- offsetAdjustment_(offsetAdjustment),
- preBarrier_(preBarrier)
- {
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(typedObj->type() == MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(StoreUnboxedObjectOrNull)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value), (3, typedObj))
-
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool preBarrier() const {
- return preBarrier_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-
- // For StoreUnboxedObjectOrNullPolicy.
- void setValue(MDefinition* def) {
- replaceOperand(2, def);
- }
-
- ALLOW_CLONE(MStoreUnboxedObjectOrNull)
-};
-
-// Store an unboxed object or null pointer to a vector.
-class MStoreUnboxedString
- : public MTernaryInstruction,
- public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
-{
- int32_t offsetAdjustment_;
- bool preBarrier_;
-
- MStoreUnboxedString(MDefinition* elements, MDefinition* index, MDefinition* value,
- int32_t offsetAdjustment = 0, bool preBarrier = true)
- : MTernaryInstruction(classOpcode, elements, index, value),
- offsetAdjustment_(offsetAdjustment),
- preBarrier_(preBarrier)
- {
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(StoreUnboxedString)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value))
-
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- bool preBarrier() const {
- return preBarrier_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-
- ALLOW_CLONE(MStoreUnboxedString)
-};
-
-// Passes through an object, after ensuring it is converted from an unboxed
-// object to a native representation.
-class MConvertUnboxedObjectToNative
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerObjectGroup group_;
-
- explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group)
- : MUnaryInstruction(classOpcode, obj),
- group_(group)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ConvertUnboxedObjectToNative)
- NAMED_OPERANDS((0, object))
-
- static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj,
- ObjectGroup* group);
-
- ObjectGroup* group() const {
- return group_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- return ins->toConvertUnboxedObjectToNative()->group() == group();
- }
- AliasSet getAliasSet() const override {
- // This instruction can read and write to all parts of the object, but
- // is marked as non-effectful so it can be consolidated by LICM and GVN
- // and avoid inhibiting other optimizations.
- //
- // This is valid to do because when unboxed objects might have a native
- // group they can be converted to, we do not optimize accesses to the
- // unboxed objects and do not guard on their group or shape (other than
- // in this opcode).
- //
- // Later accesses can assume the object has a native representation
- // and optimize accordingly. Those accesses cannot be reordered before
- // this instruction, however. This is prevented by chaining this
- // instruction with the object itself, in the same way as MBoundsCheck.
- return AliasSet::None();
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(group_);
- }
-};
-
-// Array.prototype.pop or Array.prototype.shift on a dense array.
-class MArrayPopShift
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- public:
- enum Mode {
- Pop,
- Shift
- };
-
- private:
- Mode mode_;
- bool needsHoleCheck_;
- bool maybeUndefined_;
-
- MArrayPopShift(MDefinition* object, Mode mode,
- bool needsHoleCheck, bool maybeUndefined)
- : MUnaryInstruction(classOpcode, object), mode_(mode),
- needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
- { }
-
- public:
- INSTRUCTION_HEADER(ArrayPopShift)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool needsHoleCheck() const {
- return needsHoleCheck_;
- }
- bool maybeUndefined() const {
- return maybeUndefined_;
- }
- bool mode() const {
- return mode_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
- }
-
- ALLOW_CLONE(MArrayPopShift)
-};
-
-// Array.prototype.push on a dense array. Returns the new array length.
-class MArrayPush
- : public MBinaryInstruction,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
-{
- MArrayPush(MDefinition* object, MDefinition* value)
- : MBinaryInstruction(classOpcode, object, value)
- {
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(ArrayPush)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, value))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
- }
- void computeRange(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MArrayPush)
-};
-
-// Array.prototype.slice on a dense array.
-class MArraySlice
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
-{
- CompilerObject templateObj_;
- gc::InitialHeap initialHeap_;
-
- MArraySlice(CompilerConstraintList* constraints, MDefinition* obj,
- MDefinition* begin, MDefinition* end,
- JSObject* templateObj, gc::InitialHeap initialHeap)
- : MTernaryInstruction(classOpcode, obj, begin, end),
- templateObj_(templateObj),
- initialHeap_(initialHeap)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ArraySlice)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, begin), (2, end))
-
- JSObject* templateObj() const {
- return templateObj_;
- }
-
- gc::InitialHeap initialHeap() const {
- return initialHeap_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObj_);
- }
-};
-
-class MArrayJoin
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, StringPolicy<1> >::Data
-{
- bool optimizeForArray_;
-
- MArrayJoin(MDefinition* array, MDefinition* sep, bool optimizeForArray)
- : MBinaryInstruction(classOpcode, array, sep),
- optimizeForArray_(optimizeForArray)
- {
- setResultType(MIRType::String);
- }
- public:
- INSTRUCTION_HEADER(ArrayJoin)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, array), (1, sep))
-
- bool optimizeForArray() const {
- return optimizeForArray_;
- }
- bool possiblyCalls() const override {
- return true;
- }
- virtual AliasSet getAliasSet() const override {
- // Array.join might coerce the elements of the Array to strings. This
- // coercion might cause the evaluation of the some JavaScript code.
- return AliasSet::Store(AliasSet::Any);
- }
- MDefinition* foldsTo(TempAllocator& alloc) override;
-};
-
// All barriered operations - MCompareExchangeTypedArrayElement,
// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as
// well as MLoadUnboxedScalar and MStoreUnboxedScalar when they are
// marked as requiring a memory barrer - have the following
// attributes:
//
// - Not movable
// - Not removable
@@ -10189,237 +3517,16 @@ class MArrayJoin
enum MemoryBarrierRequirement
{
DoesNotRequireMemoryBarrier,
DoesRequireMemoryBarrier
};
// Also see comments at MMemoryBarrierRequirement, above.
-// Load an unboxed scalar value from a typed array or other object.
-class MLoadUnboxedScalar
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- Scalar::Type storageType_;
- Scalar::Type readType_;
- unsigned numElems_; // used only for SIMD
- bool requiresBarrier_;
- int32_t offsetAdjustment_;
- bool canonicalizeDoubles_;
-
- MLoadUnboxedScalar(MDefinition* elements, MDefinition* index, Scalar::Type storageType,
- MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier,
- int32_t offsetAdjustment = 0, bool canonicalizeDoubles = true)
- : MBinaryInstruction(classOpcode, elements, index),
- storageType_(storageType),
- readType_(storageType),
- numElems_(1),
- requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
- offsetAdjustment_(offsetAdjustment),
- canonicalizeDoubles_(canonicalizeDoubles)
- {
- setResultType(MIRType::Value);
- if (requiresBarrier_)
- setGuard(); // Not removable or movable
- else
- setMovable();
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
- }
-
- public:
- INSTRUCTION_HEADER(LoadUnboxedScalar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index))
-
- void setSimdRead(Scalar::Type type, unsigned numElems) {
- readType_ = type;
- numElems_ = numElems;
- }
- unsigned numElems() const {
- return numElems_;
- }
- Scalar::Type readType() const {
- return readType_;
- }
-
- Scalar::Type storageType() const {
- return storageType_;
- }
- bool fallible() const {
- // Bailout if the result does not fit in an int32.
- return readType_ == Scalar::Uint32 && type() == MIRType::Int32;
- }
- bool requiresMemoryBarrier() const {
- return requiresBarrier_;
- }
- bool canonicalizeDoubles() const {
- return canonicalizeDoubles_;
- }
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- void setOffsetAdjustment(int32_t offsetAdjustment) {
- offsetAdjustment_ = offsetAdjustment;
- }
- AliasSet getAliasSet() const override {
- // When a barrier is needed make the instruction effectful by
- // giving it a "store" effect.
- if (requiresBarrier_)
- return AliasSet::Store(AliasSet::UnboxedElement);
- return AliasSet::Load(AliasSet::UnboxedElement);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (requiresBarrier_)
- return false;
- if (!ins->isLoadUnboxedScalar())
- return false;
- const MLoadUnboxedScalar* other = ins->toLoadUnboxedScalar();
- if (storageType_ != other->storageType_)
- return false;
- if (readType_ != other->readType_)
- return false;
- if (numElems_ != other->numElems_)
- return false;
- if (offsetAdjustment() != other->offsetAdjustment())
- return false;
- if (canonicalizeDoubles() != other->canonicalizeDoubles())
- return false;
- return congruentIfOperandsEqual(other);
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- void computeRange(TempAllocator& alloc) override;
-
- bool canProduceFloat32() const override { return storageType_ == Scalar::Float32; }
-
- ALLOW_CLONE(MLoadUnboxedScalar)
-};
-
-// Load a value from a typed array. Out-of-bounds accesses are handled in-line.
-class MLoadTypedArrayElementHole
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- Scalar::Type arrayType_;
- bool allowDouble_;
-
- MLoadTypedArrayElementHole(MDefinition* object, MDefinition* index, Scalar::Type arrayType, bool allowDouble)
- : MBinaryInstruction(classOpcode, object, index),
- arrayType_(arrayType), allowDouble_(allowDouble)
- {
- setResultType(MIRType::Value);
- setMovable();
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
- }
-
- public:
- INSTRUCTION_HEADER(LoadTypedArrayElementHole)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, index))
-
- Scalar::Type arrayType() const {
- return arrayType_;
- }
- bool allowDouble() const {
- return allowDouble_;
- }
- bool fallible() const {
- return arrayType_ == Scalar::Uint32 && !allowDouble_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadTypedArrayElementHole())
- return false;
- const MLoadTypedArrayElementHole* other = ins->toLoadTypedArrayElementHole();
- if (arrayType() != other->arrayType())
- return false;
- if (allowDouble() != other->allowDouble())
- return false;
- return congruentIfOperandsEqual(other);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::UnboxedElement);
- }
- bool canProduceFloat32() const override { return arrayType_ == Scalar::Float32; }
-
- ALLOW_CLONE(MLoadTypedArrayElementHole)
-};
-
-// Load a value fallibly or infallibly from a statically known typed array.
-class MLoadTypedArrayElementStatic
- : public MUnaryInstruction,
- public ConvertToInt32Policy<0>::Data
-{
- MLoadTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr,
- int32_t offset = 0, bool needsBoundsCheck = true)
- : MUnaryInstruction(classOpcode, ptr), someTypedArray_(someTypedArray), offset_(offset),
- needsBoundsCheck_(needsBoundsCheck), fallible_(true)
- {
- int type = accessType();
- if (type == Scalar::Float32)
- setResultType(MIRType::Float32);
- else if (type == Scalar::Float64)
- setResultType(MIRType::Double);
- else
- setResultType(MIRType::Int32);
- }
-
- CompilerObject someTypedArray_;
-
- // An offset to be encoded in the load instruction - taking advantage of the
- // addressing modes. This is only non-zero when the access is proven to be
- // within bounds.
- int32_t offset_;
- bool needsBoundsCheck_;
- bool fallible_;
-
- public:
- INSTRUCTION_HEADER(LoadTypedArrayElementStatic)
- TRIVIAL_NEW_WRAPPERS
-
- Scalar::Type accessType() const {
- return someTypedArray_->as<TypedArrayObject>().type();
- }
- SharedMem<void*> base() const;
- size_t length() const;
-
- MDefinition* ptr() const { return getOperand(0); }
- int32_t offset() const { return offset_; }
- void setOffset(int32_t offset) { offset_ = offset; }
- bool congruentTo(const MDefinition* ins) const override;
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::UnboxedElement);
- }
-
- bool needsBoundsCheck() const { return needsBoundsCheck_; }
- void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; }
-
- bool fallible() const {
- return fallible_;
- }
-
- void setInfallible() {
- fallible_ = false;
- }
-
- void computeRange(TempAllocator& alloc) override;
- bool needTruncation(TruncateKind kind) override;
- bool canProduceFloat32() const override { return accessType() == Scalar::Float32; }
- void collectRangeInfoPreTrunc() override;
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(someTypedArray_);
- }
-};
-
// Base class for MIR ops that write unboxed scalar values.
class StoreUnboxedScalarBase
{
Scalar::Type writeType_;
protected:
explicit StoreUnboxedScalarBase(Scalar::Type writeType)
: writeType_(writeType)
@@ -10450,399 +3557,16 @@ class StoreUnboxedScalarBase
return writeType_ == Scalar::Float32 ||
writeType_ == Scalar::Float64;
}
bool isSimdWrite() const {
return Scalar::isSimdType(writeType());
}
};
-// Store an unboxed scalar value to a typed array or other object.
-class MStoreUnboxedScalar
- : public MTernaryInstruction,
- public StoreUnboxedScalarBase,
- public StoreUnboxedScalarPolicy::Data
-{
- public:
- enum TruncateInputKind {
- DontTruncateInput,
- TruncateInput
- };
-
- private:
- Scalar::Type storageType_;
-
- // Whether this store truncates out of range inputs, for use by range analysis.
- TruncateInputKind truncateInput_;
-
- bool requiresBarrier_;
- int32_t offsetAdjustment_;
- unsigned numElems_; // used only for SIMD
-
- MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, MDefinition* value,
- Scalar::Type storageType, TruncateInputKind truncateInput,
- MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier,
- int32_t offsetAdjustment = 0)
- : MTernaryInstruction(classOpcode, elements, index, value),
- StoreUnboxedScalarBase(storageType),
- storageType_(storageType),
- truncateInput_(truncateInput),
- requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
- offsetAdjustment_(offsetAdjustment),
- numElems_(1)
- {
- if (requiresBarrier_)
- setGuard(); // Not removable or movable
- else
- setMovable();
- MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
- }
-
- public:
- INSTRUCTION_HEADER(StoreUnboxedScalar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value))
-
- void setSimdWrite(Scalar::Type writeType, unsigned numElems) {
- MOZ_ASSERT(Scalar::isSimdType(writeType));
- setWriteType(writeType);
- numElems_ = numElems;
- }
- unsigned numElems() const {
- return numElems_;
- }
- Scalar::Type storageType() const {
- return storageType_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
- TruncateInputKind truncateInput() const {
- return truncateInput_;
- }
- bool requiresMemoryBarrier() const {
- return requiresBarrier_;
- }
- int32_t offsetAdjustment() const {
- return offsetAdjustment_;
- }
- TruncateKind operandTruncateKind(size_t index) const override;
-
- bool canConsumeFloat32(MUse* use) const override {
- return use == getUseFor(2) && writeType() == Scalar::Float32;
- }
-
- ALLOW_CLONE(MStoreUnboxedScalar)
-};
-
-class MStoreTypedArrayElementHole
- : public MQuaternaryInstruction,
- public StoreUnboxedScalarBase,
- public StoreTypedArrayHolePolicy::Data
-{
- MStoreTypedArrayElementHole(MDefinition* elements, MDefinition* length, MDefinition* index,
- MDefinition* value, Scalar::Type arrayType)
- : MQuaternaryInstruction(classOpcode, elements, length, index, value),
- StoreUnboxedScalarBase(arrayType)
- {
- setMovable();
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- MOZ_ASSERT(length->type() == MIRType::Int32);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
- }
-
- public:
- INSTRUCTION_HEADER(StoreTypedArrayElementHole)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, length), (2, index), (3, value))
-
- Scalar::Type arrayType() const {
- MOZ_ASSERT(!Scalar::isSimdType(writeType()),
- "arrayType == writeType iff the write type isn't SIMD");
- return writeType();
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
- TruncateKind operandTruncateKind(size_t index) const override;
-
- bool canConsumeFloat32(MUse* use) const override {
- return use == getUseFor(3) && arrayType() == Scalar::Float32;
- }
-
- ALLOW_CLONE(MStoreTypedArrayElementHole)
-};
-
-// Store a value infallibly to a statically known typed array.
-class MStoreTypedArrayElementStatic :
- public MBinaryInstruction,
- public StoreUnboxedScalarBase,
- public StoreTypedArrayElementStaticPolicy::Data
-{
- MStoreTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr, MDefinition* v,
- int32_t offset = 0, bool needsBoundsCheck = true)
- : MBinaryInstruction(classOpcode, ptr, v),
- StoreUnboxedScalarBase(someTypedArray->as<TypedArrayObject>().type()),
- someTypedArray_(someTypedArray),
- offset_(offset), needsBoundsCheck_(needsBoundsCheck)
- {}
-
- CompilerObject someTypedArray_;
-
- // An offset to be encoded in the store instruction - taking advantage of the
- // addressing modes. This is only non-zero when the access is proven to be
- // within bounds.
- int32_t offset_;
- bool needsBoundsCheck_;
-
- public:
- INSTRUCTION_HEADER(StoreTypedArrayElementStatic)
- TRIVIAL_NEW_WRAPPERS
-
- Scalar::Type accessType() const {
- return writeType();
- }
-
- SharedMem<void*> base() const;
- size_t length() const;
-
- MDefinition* ptr() const { return getOperand(0); }
- MDefinition* value() const { return getOperand(1); }
- bool needsBoundsCheck() const { return needsBoundsCheck_; }
- void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; }
- int32_t offset() const { return offset_; }
- void setOffset(int32_t offset) { offset_ = offset; }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
- TruncateKind operandTruncateKind(size_t index) const override;
-
- bool canConsumeFloat32(MUse* use) const override {
- return use == getUseFor(1) && accessType() == Scalar::Float32;
- }
- void collectRangeInfoPreTrunc() override;
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(someTypedArray_);
- }
-};
-
-// Compute an "effective address", i.e., a compound computation of the form:
-// base + index * scale + displacement
-class MEffectiveAddress
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- MEffectiveAddress(MDefinition* base, MDefinition* index, Scale scale, int32_t displacement)
- : MBinaryInstruction(classOpcode, base, index),
- scale_(scale), displacement_(displacement)
- {
- MOZ_ASSERT(base->type() == MIRType::Int32);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- setMovable();
- setResultType(MIRType::Int32);
- }
-
- Scale scale_;
- int32_t displacement_;
-
- public:
- INSTRUCTION_HEADER(EffectiveAddress)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* base() const {
- return lhs();
- }
- MDefinition* index() const {
- return rhs();
- }
- Scale scale() const {
- return scale_;
- }
- int32_t displacement() const {
- return displacement_;
- }
-
- ALLOW_CLONE(MEffectiveAddress)
-};
-
-// Clamp input to range [0, 255] for Uint8ClampedArray.
-class MClampToUint8
- : public MUnaryInstruction,
- public ClampPolicy::Data
-{
- explicit MClampToUint8(MDefinition* input)
- : MUnaryInstruction(classOpcode, input)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ClampToUint8)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- void computeRange(TempAllocator& alloc) override;
-
- ALLOW_CLONE(MClampToUint8)
-};
-
-class MLoadFixedSlot
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- size_t slot_;
-
- protected:
- MLoadFixedSlot(MDefinition* obj, size_t slot)
- : MUnaryInstruction(classOpcode, obj), slot_(slot)
- {
- setResultType(MIRType::Value);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(LoadFixedSlot)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- size_t slot() const {
- return slot_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadFixedSlot())
- return false;
- if (slot() != ins->toLoadFixedSlot()->slot())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::FixedSlot);
- }
-
- AliasType mightAlias(const MDefinition* store) const override;
-
- ALLOW_CLONE(MLoadFixedSlot)
-};
-
-class MLoadFixedSlotAndUnbox
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- size_t slot_;
- MUnbox::Mode mode_;
- BailoutKind bailoutKind_;
- protected:
- MLoadFixedSlotAndUnbox(MDefinition* obj, size_t slot, MUnbox::Mode mode, MIRType type,
- BailoutKind kind)
- : MUnaryInstruction(classOpcode, obj), slot_(slot), mode_(mode), bailoutKind_(kind)
- {
- setResultType(type);
- setMovable();
- if (mode_ == MUnbox::TypeBarrier || mode_ == MUnbox::Fallible)
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(LoadFixedSlotAndUnbox)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- size_t slot() const {
- return slot_;
- }
- MUnbox::Mode mode() const {
- return mode_;
- }
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
- bool fallible() const {
- return mode_ != MUnbox::Infallible;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadFixedSlotAndUnbox() ||
- slot() != ins->toLoadFixedSlotAndUnbox()->slot() ||
- mode() != ins->toLoadFixedSlotAndUnbox()->mode())
- {
- return false;
- }
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::FixedSlot);
- }
-
- AliasType mightAlias(const MDefinition* store) const override;
-
- ALLOW_CLONE(MLoadFixedSlotAndUnbox);
-};
-
-class MStoreFixedSlot
- : public MBinaryInstruction,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
-{
- bool needsBarrier_;
- size_t slot_;
-
- MStoreFixedSlot(MDefinition* obj, MDefinition* rval, size_t slot, bool barrier)
- : MBinaryInstruction(classOpcode, obj, rval),
- needsBarrier_(barrier),
- slot_(slot)
- { }
-
- public:
- INSTRUCTION_HEADER(StoreFixedSlot)
- NAMED_OPERANDS((0, object), (1, value))
-
- static MStoreFixedSlot* New(TempAllocator& alloc, MDefinition* obj, size_t slot,
- MDefinition* rval)
- {
- return new(alloc) MStoreFixedSlot(obj, rval, slot, false);
- }
- static MStoreFixedSlot* NewBarriered(TempAllocator& alloc, MDefinition* obj, size_t slot,
- MDefinition* rval)
- {
- return new(alloc) MStoreFixedSlot(obj, rval, slot, true);
- }
-
- size_t slot() const {
- return slot_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::FixedSlot);
- }
- bool needsBarrier() const {
- return needsBarrier_;
- }
- void setNeedsBarrier(bool needsBarrier = true) {
- needsBarrier_ = needsBarrier;
- }
-
- ALLOW_CLONE(MStoreFixedSlot)
-};
-
struct InliningTarget
{
JSObject* target;
// If target is a singleton, group is nullptr. If target is not a singleton,
// this is the group we need to guard on when doing a polymorphic inlining
// dispatch. Note that this can be different from target->group() due to
// proto mutation.
@@ -10923,281 +3647,28 @@ class InlinePropertyTable : public TempO
void trimTo(const InliningTargets& targets, const BoolVector& choiceSet);
// Ensure that the InlinePropertyTable's domain is a subset of |targets|.
void trimToTargets(const InliningTargets& targets);
bool appendRoots(MRootList& roots) const;
};
-class MGetPropertyCache
- : public MBinaryInstruction,
- public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
-{
- bool idempotent_ : 1;
- bool monitoredResult_ : 1;
-
- InlinePropertyTable* inlinePropertyTable_;
-
- MGetPropertyCache(MDefinition* obj, MDefinition* id, bool monitoredResult)
- : MBinaryInstruction(classOpcode, obj, id),
- idempotent_(false),
- monitoredResult_(monitoredResult),
- inlinePropertyTable_(nullptr)
- {
- setResultType(MIRType::Value);
-
- // The cache will invalidate if there are objects with e.g. lookup or
- // resolve hooks on the proto chain. setGuard ensures this check is not
- // eliminated.
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(GetPropertyCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value), (1, idval))
-
- InlinePropertyTable* initInlinePropertyTable(TempAllocator& alloc, jsbytecode* pc) {
- MOZ_ASSERT(inlinePropertyTable_ == nullptr);
- inlinePropertyTable_ = new(alloc) InlinePropertyTable(alloc, pc);
- return inlinePropertyTable_;
- }
-
- void clearInlinePropertyTable() {
- inlinePropertyTable_ = nullptr;
- }
-
- InlinePropertyTable* propTable() const {
- return inlinePropertyTable_;
- }
-
- bool idempotent() const {
- return idempotent_;
- }
- void setIdempotent() {
- idempotent_ = true;
- setMovable();
- }
- bool monitoredResult() const {
- return monitoredResult_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!idempotent_)
- return false;
- if (!ins->isGetPropertyCache())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- if (idempotent_) {
- return AliasSet::Load(AliasSet::ObjectFields |
- AliasSet::FixedSlot |
- AliasSet::DynamicSlot);
- }
- return AliasSet::Store(AliasSet::Any);
- }
-
- bool allowDoubleResult() const;
-
- bool appendRoots(MRootList& roots) const override {
- if (inlinePropertyTable_)
- return inlinePropertyTable_->appendRoots(roots);
- return true;
- }
-};
-
-class MHomeObjectSuperBase
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MHomeObjectSuperBase(MDefinition* homeObject)
- : MUnaryInstruction(classOpcode, homeObject)
- {
- setResultType(MIRType::Object);
- setGuard(); // May throw if [[Prototype]] is null
- }
-
- public:
- INSTRUCTION_HEADER(HomeObjectSuperBase)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, homeObject))
-};
-
-class MGetPropSuperCache
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, CacheIdPolicy<2>>::Data
-{
- MGetPropSuperCache(MDefinition* obj, MDefinition* receiver, MDefinition* id)
- : MTernaryInstruction(classOpcode, obj, receiver, id)
- {
- setResultType(MIRType::Value);
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(GetPropSuperCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, receiver), (2, idval))
-};
-
struct PolymorphicEntry {
// The group and/or shape to guard against.
ReceiverGuard receiver;
// The property to load, null for loads from unboxed properties.
Shape* shape;
bool appendRoots(MRootList& roots) const {
return roots.append(receiver) && roots.append(shape);
}
};
-// Emit code to load a value from an object if it matches one of the receivers
-// observed by the baseline IC, else bails out.
-class MGetPropertyPolymorphic
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
- CompilerPropertyName name_;
-
- MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name)
- : MUnaryInstruction(classOpcode, obj),
- receivers_(alloc),
- name_(name)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(GetPropertyPolymorphic)
- NAMED_OPERANDS((0, object))
-
- static MGetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, PropertyName* name) {
- return new(alloc) MGetPropertyPolymorphic(alloc, obj, name);
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isGetPropertyPolymorphic())
- return false;
- if (name() != ins->toGetPropertyPolymorphic()->name())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
- PolymorphicEntry entry;
- entry.receiver = receiver;
- entry.shape = shape;
- return receivers_.append(entry);
- }
- size_t numReceivers() const {
- return receivers_.length();
- }
- const ReceiverGuard receiver(size_t i) const {
- return receivers_[i].receiver;
- }
- Shape* shape(size_t i) const {
- return receivers_[i].shape;
- }
- PropertyName* name() const {
- return name_;
- }
- AliasSet getAliasSet() const override {
- bool hasUnboxedLoad = false;
- for (size_t i = 0; i < numReceivers(); i++) {
- if (!shape(i)) {
- hasUnboxedLoad = true;
- break;
- }
- }
- return AliasSet::Load(AliasSet::ObjectFields |
- AliasSet::FixedSlot |
- AliasSet::DynamicSlot |
- (hasUnboxedLoad ? AliasSet::UnboxedElement : 0));
- }
-
- AliasType mightAlias(const MDefinition* store) const override;
-
- bool appendRoots(MRootList& roots) const override;
-};
-
-// Emit code to store a value to an object's slots if its shape/group matches
-// one of the shapes/groups observed by the baseline IC, else bails out.
-class MSetPropertyPolymorphic
- : public MBinaryInstruction,
- public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
-{
- Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
- CompilerPropertyName name_;
- bool needsBarrier_;
-
- MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
- PropertyName* name)
- : MBinaryInstruction(classOpcode, obj, value),
- receivers_(alloc),
- name_(name),
- needsBarrier_(false)
- {
- }
-
- public:
- INSTRUCTION_HEADER(SetPropertyPolymorphic)
- NAMED_OPERANDS((0, object), (1, value))
-
- static MSetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
- PropertyName* name) {
- return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name);
- }
-
- MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
- PolymorphicEntry entry;
- entry.receiver = receiver;
- entry.shape = shape;
- return receivers_.append(entry);
- }
- size_t numReceivers() const {
- return receivers_.length();
- }
- const ReceiverGuard& receiver(size_t i) const {
- return receivers_[i].receiver;
- }
- Shape* shape(size_t i) const {
- return receivers_[i].shape;
- }
- PropertyName* name() const {
- return name_;
- }
- bool needsBarrier() const {
- return needsBarrier_;
- }
- void setNeedsBarrier() {
- needsBarrier_ = true;
- }
- AliasSet getAliasSet() const override {
- bool hasUnboxedStore = false;
- for (size_t i = 0; i < numReceivers(); i++) {
- if (!shape(i)) {
- hasUnboxedStore = true;
- break;
- }
- }
- return AliasSet::Store(AliasSet::ObjectFields |
- AliasSet::FixedSlot |
- AliasSet::DynamicSlot |
- (hasUnboxedStore ? AliasSet::UnboxedElement : 0));
- }
- bool appendRoots(MRootList& roots) const override;
-};
-
class MDispatchInstruction
: public MControlInstruction,
public SingleObjectPolicy::Data
{
// Map from JSFunction* -> MBasicBlock.
struct Entry {
JSFunction* func;
// If |func| has a singleton group, |funcGroup| is null. Otherwise,
@@ -11346,734 +3817,16 @@ class MFunctionDispatch : public MDispat
INSTRUCTION_HEADER(FunctionDispatch)
static MFunctionDispatch* New(TempAllocator& alloc, MDefinition* ins) {
return new(alloc) MFunctionDispatch(alloc, ins);
}
bool appendRoots(MRootList& roots) const override;
};
-class MBindNameCache
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerPropertyName name_;
- CompilerScript script_;
- jsbytecode* pc_;
-
- MBindNameCache(MDefinition* envChain, PropertyName* name, JSScript* script, jsbytecode* pc)
- : MUnaryInstruction(classOpcode, envChain), name_(name), script_(script), pc_(pc)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(BindNameCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, environmentChain))
-
- PropertyName* name() const {
- return name_;
- }
- JSScript* script() const {
- return script_;
- }
- jsbytecode* pc() const {
- return pc_;
- }
- bool appendRoots(MRootList& roots) const override {
- // Don't append the script, all scripts are added anyway.
- return roots.append(name_);
- }
-};
-
-class MCallBindVar
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MCallBindVar(MDefinition* envChain)
- : MUnaryInstruction(classOpcode, envChain)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(CallBindVar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, environmentChain))
-
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isCallBindVar())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Guard on an object's shape.
-class MGuardShape
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerShape shape_;
- BailoutKind bailoutKind_;
-
- MGuardShape(MDefinition* obj, Shape* shape, BailoutKind bailoutKind)
- : MUnaryInstruction(classOpcode, obj),
- shape_(shape),
- bailoutKind_(bailoutKind)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- setResultTypeSet(obj->resultTypeSet());
-
- // Disallow guarding on unboxed object shapes. The group is better to
- // guard on, and guarding on the shape can interact badly with
- // MConvertUnboxedObjectToNative.
- MOZ_ASSERT(shape->getObjectClass() != &UnboxedPlainObject::class_);
- }
-
- public:
- INSTRUCTION_HEADER(GuardShape)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- const Shape* shape() const {
- return shape_;
- }
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isGuardShape())
- return false;
- if (shape() != ins->toGuardShape()->shape())
- return false;
- if (bailoutKind() != ins->toGuardShape()->bailoutKind())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(shape_);
- }
-};
-
-// Bail if the object's shape or unboxed group is not in the input list.
-class MGuardReceiverPolymorphic
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- Vector<ReceiverGuard, 4, JitAllocPolicy> receivers_;
-
- MGuardReceiverPolymorphic(TempAllocator& alloc, MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj),
- receivers_(alloc)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- setResultTypeSet(obj->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(GuardReceiverPolymorphic)
- NAMED_OPERANDS((0, object))
-
- static MGuardReceiverPolymorphic* New(TempAllocator& alloc, MDefinition* obj) {
- return new(alloc) MGuardReceiverPolymorphic(alloc, obj);
- }
-
- MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver) {
- return receivers_.append(receiver);
- }
- size_t numReceivers() const {
- return receivers_.length();
- }
- const ReceiverGuard& receiver(size_t i) const {
- return receivers_[i];
- }
-
- bool congruentTo(const MDefinition* ins) const override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- bool appendRoots(MRootList& roots) const override;
-
-};
-
-// Guard on an object's group, inclusively or exclusively.
-class MGuardObjectGroup
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerObjectGroup group_;
- bool bailOnEquality_;
- BailoutKind bailoutKind_;
-
- MGuardObjectGroup(MDefinition* obj, ObjectGroup* group, bool bailOnEquality,
- BailoutKind bailoutKind)
- : MUnaryInstruction(classOpcode, obj),
- group_(group),
- bailOnEquality_(bailOnEquality),
- bailoutKind_(bailoutKind)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
-
- // Unboxed groups which might be converted to natives can't be guarded
- // on, due to MConvertUnboxedObjectToNative.
- MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(),
- !group->unboxedLayoutDontCheckGeneration().nativeGroup());
- }
-
- public:
- INSTRUCTION_HEADER(GuardObjectGroup)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- const ObjectGroup* group() const {
- return group_;
- }
- bool bailOnEquality() const {
- return bailOnEquality_;
- }
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isGuardObjectGroup())
- return false;
- if (group() != ins->toGuardObjectGroup()->group())
- return false;
- if (bailOnEquality() != ins->toGuardObjectGroup()->bailOnEquality())
- return false;
- if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(group_);
- }
-};
-
-// Guard on an object's identity, inclusively or exclusively.
-class MGuardObjectIdentity
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool bailOnEquality_;
-
- MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality)
- : MBinaryInstruction(classOpcode, obj, expected),
- bailOnEquality_(bailOnEquality)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(GuardObjectIdentity)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, expected))
-
- bool bailOnEquality() const {
- return bailOnEquality_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isGuardObjectIdentity())
- return false;
- if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Guard on an object's class.
-class MGuardClass
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- const Class* class_;
-
- MGuardClass(MDefinition* obj, const Class* clasp)
- : MUnaryInstruction(classOpcode, obj),
- class_(clasp)
- {
- setGuard();
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(GuardClass)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- const Class* getClass() const {
- return class_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isGuardClass())
- return false;
- if (getClass() != ins->toGuardClass()->getClass())
- return false;
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MGuardClass)
-};
-
-// Guard on the presence or absence of an unboxed object's expando.
-class MGuardUnboxedExpando
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool requireExpando_;
- BailoutKind bailoutKind_;
-
- MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind)
- : MUnaryInstruction(classOpcode, obj),
- requireExpando_(requireExpando),
- bailoutKind_(bailoutKind)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(GuardUnboxedExpando)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool requireExpando() const {
- return requireExpando_;
- }
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando())
- return false;
- return true;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Load an unboxed plain object's expando.
-class MLoadUnboxedExpando
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- private:
- explicit MLoadUnboxedExpando(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(LoadUnboxedExpando)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Load from vp[slot] (slots that are not inline in an object).
-class MLoadSlot
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- uint32_t slot_;
-
- MLoadSlot(MDefinition* slots, uint32_t slot)
- : MUnaryInstruction(classOpcode, slots),
- slot_(slot)
- {
- setResultType(MIRType::Value);
- setMovable();
- MOZ_ASSERT(slots->type() == MIRType::Slots);
- }
-
- public:
- INSTRUCTION_HEADER(LoadSlot)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, slots))
-
- uint32_t slot() const {
- return slot_;
- }
-
- HashNumber valueHash() const override;
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isLoadSlot())
- return false;
- if (slot() != ins->toLoadSlot()->slot())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- MOZ_ASSERT(slots()->type() == MIRType::Slots);
- return AliasSet::Load(AliasSet::DynamicSlot);
- }
- AliasType mightAlias(const MDefinition* store) const override;
-
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MLoadSlot)
-};
-
-// Inline call to access a function's environment (scope chain).
-class MFunctionEnvironment
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MFunctionEnvironment(MDefinition* function)
- : MUnaryInstruction(classOpcode, function)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(FunctionEnvironment)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, function))
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- // A function's environment is fixed.
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Allocate a new LexicalEnvironmentObject.
-class MNewLexicalEnvironmentObject
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerGCPointer<LexicalScope*> scope_;
-
- MNewLexicalEnvironmentObject(MDefinition* enclosing, LexicalScope* scope)
- : MUnaryInstruction(classOpcode, enclosing),
- scope_(scope)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(NewLexicalEnvironmentObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, enclosing))
-
- LexicalScope* scope() const {
- return scope_;
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(scope_);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Allocate a new LexicalEnvironmentObject from existing one
-class MCopyLexicalEnvironmentObject
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool copySlots_;
-
- MCopyLexicalEnvironmentObject(MDefinition* env, bool copySlots)
- : MUnaryInstruction(classOpcode, env),
- copySlots_(copySlots)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(CopyLexicalEnvironmentObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, env))
-
- bool copySlots() const {
- return copySlots_;
- }
- bool possiblyCalls() const override {
- return true;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields |
- AliasSet::FixedSlot |
- AliasSet::DynamicSlot);
- }
-};
-
-class MHomeObject
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MHomeObject(MDefinition* function)
- : MUnaryInstruction(classOpcode, function)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(HomeObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, function))
-
- // A function's [[HomeObject]] is fixed.
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Store to vp[slot] (slots that are not inline in an object).
-class MStoreSlot
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >::Data
-{
- uint32_t slot_;
- MIRType slotType_;
- bool needsBarrier_;
-
- MStoreSlot(MDefinition* slots, uint32_t slot, MDefinition* value, bool barrier)
- : MBinaryInstruction(classOpcode, slots, value),
- slot_(slot),
- slotType_(MIRType::Value),
- needsBarrier_(barrier)
- {
- MOZ_ASSERT(slots->type() == MIRType::Slots);
- }
-
- public:
- INSTRUCTION_HEADER(StoreSlot)
- NAMED_OPERANDS((0, slots), (1, value))
-
- static MStoreSlot* New(TempAllocator& alloc, MDefinition* slots, uint32_t slot,
- MDefinition* value)
- {
- return new(alloc) MStoreSlot(slots, slot, value, false);
- }
- static MStoreSlot* NewBarriered(TempAllocator& alloc, MDefinition* slots, uint32_t slot,
- MDefinition* value)
- {
- return new(alloc) MStoreSlot(slots, slot, value, true);
- }
-
- uint32_t slot() const {
- return slot_;
- }
- MIRType slotType() const {
- return slotType_;
- }
- void setSlotType(MIRType slotType) {
- MOZ_ASSERT(slotType != MIRType::None);
- slotType_ = slotType;
- }
- bool needsBarrier() const {
- return needsBarrier_;
- }
- void setNeedsBarrier() {
- needsBarrier_ = true;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::DynamicSlot);
- }
- void printOpcode(GenericPrinter& out) const override;
-
- ALLOW_CLONE(MStoreSlot)
-};
-
-class MGetNameCache
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- private:
- explicit MGetNameCache(MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(GetNameCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, envObj))
-};
-
-class MCallGetIntrinsicValue : public MNullaryInstruction
-{
- CompilerPropertyName name_;
-
- explicit MCallGetIntrinsicValue(PropertyName* name)
- : MNullaryInstruction(classOpcode),
- name_(name)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(CallGetIntrinsicValue)
- TRIVIAL_NEW_WRAPPERS
-
- PropertyName* name() const {
- return name_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MSetPropertyInstruction : public MBinaryInstruction
-{
- CompilerPropertyName name_;
- bool strict_;
-
- protected:
- MSetPropertyInstruction(Opcode op, MDefinition* obj, MDefinition* value, PropertyName* name,
- bool strict)
- : MBinaryInstruction(op, obj, value),
- name_(name), strict_(strict)
- {}
-
- public:
- NAMED_OPERANDS((0, object), (1, value))
- PropertyName* name() const {
- return name_;
- }
- bool strict() const {
- return strict_;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MSetElementInstruction
- : public MTernaryInstruction
-{
- bool strict_;
- protected:
- MSetElementInstruction(Opcode op, MDefinition* object, MDefinition* index, MDefinition* value,
- bool strict)
- : MTernaryInstruction(op, object, index, value),
- strict_(strict)
- {
- }
-
- public:
- NAMED_OPERANDS((0, object), (1, index), (2, value))
- bool strict() const {
- return strict_;
- }
-};
-
-class MDeleteProperty
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- CompilerPropertyName name_;
- bool strict_;
-
- protected:
- MDeleteProperty(MDefinition* val, PropertyName* name, bool strict)
- : MUnaryInstruction(classOpcode, val),
- name_(name),
- strict_(strict)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(DeleteProperty)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-
- PropertyName* name() const {
- return name_;
- }
- bool strict() const {
- return strict_;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-class MDeleteElement
- : public MBinaryInstruction,
- public BoxInputsPolicy::Data
-{
- bool strict_;
-
- MDeleteElement(MDefinition* value, MDefinition* index, bool strict)
- : MBinaryInstruction(classOpcode, value, index),
- strict_(strict)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(DeleteElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value), (1, index))
-
- bool strict() const {
- return strict_;
- }
-};
-
// Note: This uses CallSetElementPolicy to always box its second input,
// ensuring we don't need two LIR instructions to lower this.
class MCallSetProperty
: public MSetPropertyInstruction,
public CallSetElementPolicy::Data
{
MCallSetProperty(MDefinition* obj, MDefinition* value, PropertyName* name, bool strict)
: MSetPropertyInstruction(classOpcode, obj, value, name, strict)
@@ -12084,119 +3837,16 @@ class MCallSetProperty
INSTRUCTION_HEADER(CallSetProperty)
TRIVIAL_NEW_WRAPPERS
bool possiblyCalls() const override {
return true;
}
};
-class MSetPropertyCache
- : public MTernaryInstruction,
- public MixPolicy<SingleObjectPolicy, CacheIdPolicy<1>, NoFloatPolicy<2>>::Data
-{
- bool strict_ : 1;
- bool needsPostBarrier_ : 1;
- bool needsTypeBarrier_ : 1;
- bool guardHoles_ : 1;
-
- MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value, bool strict,
- bool needsPostBarrier, bool typeBarrier, bool guardHoles)
- : MTernaryInstruction(classOpcode, obj, id, value),
- strict_(strict),
- needsPostBarrier_(needsPostBarrier),
- needsTypeBarrier_(typeBarrier),
- guardHoles_(guardHoles)
- {
- }
-
- public:
- INSTRUCTION_HEADER(SetPropertyCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, idval), (2, value))
-
- bool needsPostBarrier() const {
- return needsPostBarrier_;
- }
- bool needsTypeBarrier() const {
- return needsTypeBarrier_;
- }
-
- bool guardHoles() const {
- return guardHoles_;
- }
-
- bool strict() const {
- return strict_;
- }
-};
-
-class MCallGetProperty
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- CompilerPropertyName name_;
- bool idempotent_;
-
- MCallGetProperty(MDefinition* value, PropertyName* name)
- : MUnaryInstruction(classOpcode, value), name_(name),
- idempotent_(false)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(CallGetProperty)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-
- PropertyName* name() const {
- return name_;
- }
-
- // Constructors need to perform a GetProp on the function prototype.
- // Since getters cannot be set on the prototype, fetching is non-effectful.
- // The operation may be safely repeated in case of bailout.
- void setIdempotent() {
- idempotent_ = true;
- }
- AliasSet getAliasSet() const override {
- if (!idempotent_)
- return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(name_);
- }
-};
-
-// Inline call to handle lhs[rhs]. The first input is a Value so that this
-// instruction can handle both objects and strings.
-class MCallGetElement
- : public MBinaryInstruction,
- public BoxInputsPolicy::Data
-{
- MCallGetElement(MDefinition* lhs, MDefinition* rhs)
- : MBinaryInstruction(classOpcode, lhs, rhs)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(CallGetElement)
- TRIVIAL_NEW_WRAPPERS
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
class MCallSetElement
: public MSetElementInstruction,
public CallSetElementPolicy::Data
{
MCallSetElement(MDefinition* object, MDefinition* index, MDefinition* value, bool strict)
: MSetElementInstruction(classOpcode, object, index, value, strict)
{
}
@@ -12205,61 +3855,16 @@ class MCallSetElement
INSTRUCTION_HEADER(CallSetElement)
TRIVIAL_NEW_WRAPPERS
bool possiblyCalls() const override {
return true;
}
};
-class MCallInitElementArray
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
-{
- MCallInitElementArray(MDefinition* obj, MDefinition* index, MDefinition* val)
- : MTernaryInstruction(classOpcode, obj, index, val)
- {
- MOZ_ASSERT(index->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(CallInitElementArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, index), (2, value))
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
-class MSetDOMProperty
- : public MBinaryInstruction,
- public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
-{
- const JSJitSetterOp func_;
-
- MSetDOMProperty(const JSJitSetterOp func, MDefinition* obj, MDefinition* val)
- : MBinaryInstruction(classOpcode, obj, val),
- func_(func)
- { }
-
- public:
- INSTRUCTION_HEADER(SetDOMProperty)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, value))
-
- JSJitSetterOp fun() const {
- return func_;
- }
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
class MGetDOMProperty
: public MVariadicInstruction,
public ObjectPolicy<0>::Data
{
const JSJitInfo* info_;
protected:
MGetDOMProperty(Opcode op, const JSJitInfo* jitinfo)
@@ -12407,549 +4012,16 @@ class MGetDOMMember : public MGetDOMProp
bool congruentTo(const MDefinition* ins) const override {
if (!ins->isGetDOMMember())
return false;
return MGetDOMProperty::congruentTo(ins->toGetDOMMember());
}
};
-class MStringLength
- : public MUnaryInstruction,
- public StringPolicy<0>::Data
-{
- explicit MStringLength(MDefinition* string)
- : MUnaryInstruction(classOpcode, string)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
- public:
- INSTRUCTION_HEADER(StringLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, string))
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- // The string |length| property is immutable, so there is no
- // implicit dependency.
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MStringLength)
-};
-
-// Inlined assembly for Math.floor(double | float32) -> int32.
-class MFloor
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- explicit MFloor(MDefinition* num)
- : MUnaryInstruction(classOpcode, num)
- {
- setResultType(MIRType::Int32);
- specialization_ = MIRType::Double;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Floor)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool isFloat32Commutative() const override {
- return true;
- }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- void computeRange(TempAllocator& alloc) override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MFloor)
-};
-
-// Inlined assembly version for Math.ceil(double | float32) -> int32.
-class MCeil
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- explicit MCeil(MDefinition* num)
- : MUnaryInstruction(classOpcode, num)
- {
- setResultType(MIRType::Int32);
- specialization_ = MIRType::Double;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Ceil)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool isFloat32Commutative() const override {
- return true;
- }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- void computeRange(TempAllocator& alloc) override;
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MCeil)
-};
-
-// Inlined version of Math.round(double | float32) -> int32.
-class MRound
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- explicit MRound(MDefinition* num)
- : MUnaryInstruction(classOpcode, num)
- {
- setResultType(MIRType::Int32);
- specialization_ = MIRType::Double;
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(Round)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool isFloat32Commutative() const override {
- return true;
- }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MRound)
-};
-
-// NearbyInt rounds the floating-point input to the nearest integer, according
-// to the RoundingMode.
-class MNearbyInt
- : public MUnaryInstruction,
- public FloatingPointPolicy<0>::Data
-{
- RoundingMode roundingMode_;
-
- explicit MNearbyInt(MDefinition* num, MIRType resultType, RoundingMode roundingMode)
- : MUnaryInstruction(classOpcode, num),
- roundingMode_(roundingMode)
- {
- MOZ_ASSERT(HasAssemblerSupport(roundingMode));
-
- MOZ_ASSERT(IsFloatingPointType(resultType));
- setResultType(resultType);
- specialization_ = resultType;
-
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(NearbyInt)
- TRIVIAL_NEW_WRAPPERS
-
- static bool HasAssemblerSupport(RoundingMode mode) {
- return Assembler::HasRoundInstruction(mode);
- }
-
- RoundingMode roundingMode() const { return roundingMode_; }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool isFloat32Commutative() const override {
- return true;
- }
- void trySpecializeFloat32(TempAllocator& alloc) override;
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- return true;
- }
-#endif
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) &&
- ins->toNearbyInt()->roundingMode() == roundingMode_;
- }
-
- void printOpcode(GenericPrinter& out) const override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
-
- bool canRecoverOnBailout() const override {
- switch (roundingMode_) {
- case RoundingMode::Up:
- case RoundingMode::Down:
- return true;
- default:
- return false;
- }
- }
-
- ALLOW_CLONE(MNearbyInt)
-};
-
-class MGetIteratorCache
- : public MUnaryInstruction,
- public BoxExceptPolicy<0, MIRType::Object>::Data
-{
- explicit MGetIteratorCache(MDefinition* val)
- : MUnaryInstruction(classOpcode, val)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(GetIteratorCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-};
-
-class MIteratorMore
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MIteratorMore(MDefinition* iter)
- : MUnaryInstruction(classOpcode, iter)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(IteratorMore)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, iterator))
-
-};
-
-class MIsNoIter
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- explicit MIsNoIter(MDefinition* def)
- : MUnaryInstruction(classOpcode, def)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsNoIter)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MIteratorEnd
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MIteratorEnd(MDefinition* iter)
- : MUnaryInstruction(classOpcode, iter)
- { }
-
- public:
- INSTRUCTION_HEADER(IteratorEnd)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, iterator))
-
-};
-
-// Implementation for 'in' operator using instruction cache
-class MInCache
- : public MBinaryInstruction,
- public MixPolicy<CacheIdPolicy<0>, ObjectPolicy<1> >::Data
-{
- MInCache(MDefinition* key, MDefinition* obj)
- : MBinaryInstruction(classOpcode, key, obj)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(InCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, key), (1, object))
-};
-
-
-// Test whether the index is in the array bounds or a hole.
-class MInArray
- : public MQuaternaryInstruction,
- public ObjectPolicy<3>::Data
-{
- bool needsHoleCheck_;
- bool needsNegativeIntCheck_;
-
- MInArray(MDefinition* elements, MDefinition* index,
- MDefinition* initLength, MDefinition* object,
- bool needsHoleCheck)
- : MQuaternaryInstruction(classOpcode, elements, index, initLength, object),
- needsHoleCheck_(needsHoleCheck),
- needsNegativeIntCheck_(true)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- MOZ_ASSERT(elements->type() == MIRType::Elements);
- MOZ_ASSERT(index->type() == MIRType::Int32);
- MOZ_ASSERT(initLength->type() == MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(InArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, initLength), (3, object))
-
- bool needsHoleCheck() const {
- return needsHoleCheck_;
- }
- bool needsNegativeIntCheck() const {
- return needsNegativeIntCheck_;
- }
- void collectRangeInfoPreTrunc() override;
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::Element);
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isInArray())
- return false;
- const MInArray* other = ins->toInArray();
- if (needsHoleCheck() != other->needsHoleCheck())
- return false;
- if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
- return false;
- return congruentIfOperandsEqual(other);
- }
-};
-
-class MHasOwnCache
- : public MBinaryInstruction,
- public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
-{
- MHasOwnCache(MDefinition* obj, MDefinition* id)
- : MBinaryInstruction(classOpcode, obj, id)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(HasOwnCache)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value), (1, idval))
-};
-
-// Implementation for instanceof operator with specific rhs.
-class MInstanceOf
- : public MUnaryInstruction,
- public InstanceOfPolicy::Data
-{
- CompilerObject protoObj_;
-
- MInstanceOf(MDefinition* obj, JSObject* proto)
- : MUnaryInstruction(classOpcode, obj),
- protoObj_(proto)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(InstanceOf)
- TRIVIAL_NEW_WRAPPERS
-
- JSObject* prototypeObject() {
- return protoObj_;
- }
-
- bool appendRoots(MRootList& roots) const override {
- return roots.append(protoObj_);
- }
-};
-
-// Implementation for instanceof operator with unknown rhs.
-class MCallInstanceOf
- : public MBinaryInstruction,
- public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
-{
- MCallInstanceOf(MDefinition* obj, MDefinition* proto)
- : MBinaryInstruction(classOpcode, obj, proto)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(CallInstanceOf)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MArgumentsLength : public MNullaryInstruction
-{
- MArgumentsLength()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(ArgumentsLength)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- // Arguments |length| cannot be mutated by Ion Code.
- return AliasSet::None();
- }
-
- void computeRange(TempAllocator& alloc) override;
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
-
- bool canRecoverOnBailout() const override {
- return true;
- }
-};
-
-// This MIR instruction is used to get an argument from the actual arguments.
-class MGetFrameArgument
- : public MUnaryInstruction,
- public IntPolicy<0>::Data
-{
- bool scriptHasSetArg_;
-
- MGetFrameArgument(MDefinition* idx, bool scriptHasSetArg)
- : MUnaryInstruction(classOpcode, idx),
- scriptHasSetArg_(scriptHasSetArg)
- {
- setResultType(MIRType::Value);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(GetFrameArgument)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, index))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- // If the script doesn't have any JSOP_SETARG ops, then this instruction is never
- // aliased.
- if (scriptHasSetArg_)
- return AliasSet::Load(AliasSet::FrameArgument);
- return AliasSet::None();
- }
-};
-
-class MNewTarget : public MNullaryInstruction
-{
- MNewTarget() : MNullaryInstruction(classOpcode) {
- setResultType(MIRType::Value);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(NewTarget)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// This MIR instruction is used to set an argument value in the frame.
-class MSetFrameArgument
- : public MUnaryInstruction,
- public NoFloatPolicy<0>::Data
-{
- uint32_t argno_;
-
- MSetFrameArgument(uint32_t argno, MDefinition* value)
- : MUnaryInstruction(classOpcode, value),
- argno_(argno)
- {
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(SetFrameArgument)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-
- uint32_t argno() const {
- return argno_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return false;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::FrameArgument);
- }
-};
-
class MRestCommon
{
unsigned numFormals_;
CompilerGCPointer<ArrayObject*> templateObject_;
protected:
MRestCommon(unsigned numFormals, ArrayObject* templateObject)
: numFormals_(numFormals),
@@ -12960,287 +4032,16 @@ class MRestCommon
unsigned numFormals() const {
return numFormals_;
}
ArrayObject* templateObject() const {
return templateObject_;
}
};
-class MRest
- : public MUnaryInstruction,
- public MRestCommon,
- public IntPolicy<0>::Data
-{
- MRest(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* numActuals,
- unsigned numFormals, ArrayObject* templateObject)
- : MUnaryInstruction(classOpcode, numActuals),
- MRestCommon(numFormals, templateObject)
- {
- setResultType(MIRType::Object);
- setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
- }
-
- public:
- INSTRUCTION_HEADER(Rest)
- TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
- NAMED_OPERANDS((0, numActuals))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool possiblyCalls() const override {
- return true;
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObject());
- }
-};
-
-class MFilterTypeSet
- : public MUnaryInstruction,
- public FilterTypeSetPolicy::Data
-{
- MFilterTypeSet(MDefinition* def, TemporaryTypeSet* types)
- : MUnaryInstruction(classOpcode, def)
- {
- MOZ_ASSERT(!types->unknown());
- setResultType(types->getKnownMIRType());
- setResultTypeSet(types);
- }
-
- public:
- INSTRUCTION_HEADER(FilterTypeSet)
- TRIVIAL_NEW_WRAPPERS
-
- bool congruentTo(const MDefinition* def) const override {
- return false;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- virtual bool neverHoist() const override {
- return resultTypeSet()->empty();
- }
- void computeRange(TempAllocator& alloc) override;
-
- bool isFloat32Commutative() const override {
- return IsFloatingPointType(type());
- }
-
- bool canProduceFloat32() const override;
- bool canConsumeFloat32(MUse* operand) const override;
- void trySpecializeFloat32(TempAllocator& alloc) override;
-};
-
-// Given a value, guard that the value is in a particular TypeSet, then returns
-// that value.
-class MTypeBarrier
- : public MUnaryInstruction,
- public TypeBarrierPolicy::Data
-{
- BarrierKind barrierKind_;
-
- MTypeBarrier(MDefinition* def, TemporaryTypeSet* types,
- BarrierKind kind = BarrierKind::TypeSet)
- : MUnaryInstruction(classOpcode, def),
- barrierKind_(kind)
- {
- MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
-
- MOZ_ASSERT(!types->unknown());
- setResultType(types->getKnownMIRType());
- setResultTypeSet(types);
-
- setGuard();
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(TypeBarrier)
- TRIVIAL_NEW_WRAPPERS
-
- void printOpcode(GenericPrinter& out) const override;
- bool congruentTo(const MDefinition* def) const override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- virtual bool neverHoist() const override {
- return resultTypeSet()->empty();
- }
- BarrierKind barrierKind() const {
- return barrierKind_;
- }
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- bool alwaysBails() const {
- // If mirtype of input doesn't agree with mirtype of barrier,
- // we will definitely bail.
- MIRType type = resultTypeSet()->getKnownMIRType();
- if (type == MIRType::Value)
- return false;
- if (input()->type() == MIRType::Value)
- return false;
- if (input()->type() == MIRType::ObjectOrNull) {
- // The ObjectOrNull optimization is only performed when the
- // barrier's type is MIRType::Null.
- MOZ_ASSERT(type == MIRType::Null);
- return false;
- }
- return input()->type() != type;
- }
-
- ALLOW_CLONE(MTypeBarrier)
-};
-
-// Like MTypeBarrier, guard that the value is in the given type set. This is
-// used before property writes to ensure the value being written is represented
-// in the property types for the object.
-class MMonitorTypes
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- const TemporaryTypeSet* typeSet_;
- BarrierKind barrierKind_;
-
- MMonitorTypes(MDefinition* def, const TemporaryTypeSet* types, BarrierKind kind)
- : MUnaryInstruction(classOpcode, def),
- typeSet_(types),
- barrierKind_(kind)
- {
- MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
-
- setGuard();
- MOZ_ASSERT(!types->unknown());
- }
-
- public:
- INSTRUCTION_HEADER(MonitorTypes)
- TRIVIAL_NEW_WRAPPERS
-
- const TemporaryTypeSet* typeSet() const {
- return typeSet_;
- }
- BarrierKind barrierKind() const {
- return barrierKind_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-// Given a value being written to another object, update the generational store
-// buffer if the value is in the nursery and object is in the tenured heap.
-class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Data
-{
- MPostWriteBarrier(MDefinition* obj, MDefinition* value)
- : MBinaryInstruction(classOpcode, obj, value)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(PostWriteBarrier)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, value))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- // During lowering, values that neither have object nor value MIR type
- // are ignored, thus Float32 can show up at this point without any issue.
- return use == getUseFor(1);
- }
-#endif
-
- ALLOW_CLONE(MPostWriteBarrier)
-};
-
-// Given a value being written to another object's elements at the specified
-// index, update the generational store buffer if the value is in the nursery
-// and object is in the tenured heap.
-class MPostWriteElementBarrier : public MTernaryInstruction
- , public MixPolicy<ObjectPolicy<0>, IntPolicy<2>>::Data
-{
- MPostWriteElementBarrier(MDefinition* obj, MDefinition* value, MDefinition* index)
- : MTernaryInstruction(classOpcode, obj, value, index)
- {
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(PostWriteElementBarrier)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, value), (2, index))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
-#ifdef DEBUG
- bool isConsistentFloat32Use(MUse* use) const override {
- // During lowering, values that neither have object nor value MIR type
- // are ignored, thus Float32 can show up at this point without any issue.
- return use == getUseFor(1);
- }
-#endif
-
- ALLOW_CLONE(MPostWriteElementBarrier)
-};
-
-class MNewNamedLambdaObject : public MNullaryInstruction
-{
- CompilerGCPointer<LexicalEnvironmentObject*> templateObj_;
-
- explicit MNewNamedLambdaObject(LexicalEnvironmentObject* templateObj)
- : MNullaryInstruction(classOpcode),
- templateObj_(templateObj)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(NewNamedLambdaObject)
- TRIVIAL_NEW_WRAPPERS
-
- LexicalEnvironmentObject* templateObj() {
- return templateObj_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(templateObj_);
- }
-};
-
-class MNewCallObjectBase : public MUnaryInstruction
- , public SingleObjectPolicy::Data
-{
- protected:
- MNewCallObjectBase(Opcode op, MConstant* templateObj)
- : MUnaryInstruction(op, templateObj)
- {
- setResultType(MIRType::Object);
- }
-
- public:
- CallObject* templateObject() const {
- return &getOperand(0)->toConstant()->toObject().as<CallObject>();
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
class MNewCallObject : public MNewCallObjectBase
{
public:
INSTRUCTION_HEADER(NewCallObject)
TRIVIAL_NEW_WRAPPERS
explicit MNewCallObject(MConstant* templateObj)
: MNewCallObjectBase(classOpcode, templateObj)
@@ -13473,717 +4274,16 @@ class MResumePoint final :
MStoresToRecoverList::iterator storesEnd() const {
return stores_.end();
}
virtual void dump(GenericPrinter& out) const override;
virtual void dump() const override;
};
-class MIsCallable
- : public MUnaryInstruction,
- public BoxExceptPolicy<0, MIRType::Object>::Data
-{
- explicit MIsCallable(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- MOZ_ASSERT(object->type() == MIRType::Object || object->type() == MIRType::Value);
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsCallable)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MIsConstructor
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- public:
- explicit MIsConstructor(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsConstructor)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MIsObject
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MIsObject(MDefinition* object)
- : MUnaryInstruction(classOpcode, object)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
- public:
- INSTRUCTION_HEADER(IsObject)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MHasClass
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- const Class* class_;
-
- MHasClass(MDefinition* object, const Class* clasp)
- : MUnaryInstruction(classOpcode, object)
- , class_(clasp)
- {
- MOZ_ASSERT(object->type() == MIRType::Object ||
- (object->type() == MIRType::Value && object->mightBeType(MIRType::Object)));
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(HasClass)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- const Class* getClass() const {
- return class_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!ins->isHasClass())
- return false;
- if (getClass() != ins->toHasClass()->getClass())
- return false;
- return congruentIfOperandsEqual(ins);
- }
-};
-
-// Note: we might call a proxy trap, so this instruction is effectful.
-class MIsArray
- : public MUnaryInstruction,
- public BoxExceptPolicy<0, MIRType::Object>::Data
-{
- explicit MIsArray(MDefinition* value)
- : MUnaryInstruction(classOpcode, value)
- {
- setResultType(MIRType::Boolean);
- }
-
- public:
- INSTRUCTION_HEADER(IsArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-};
-
-class MIsTypedArray
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MIsTypedArray(MDefinition* value)
- : MUnaryInstruction(classOpcode, value)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsTypedArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MObjectClassToString
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MObjectClassToString(MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj)
- {
- setMovable();
- setResultType(MIRType::String);
- }
-
- public:
- INSTRUCTION_HEADER(ObjectClassToString)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-};
-
-class MCheckReturn
- : public MBinaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MCheckReturn(MDefinition* retVal, MDefinition* thisVal)
- : MBinaryInstruction(classOpcode, retVal, thisVal)
- {
- setGuard();
- setResultType(MIRType::Value);
- setResultTypeSet(retVal->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(CheckReturn)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, returnValue), (1, thisValue))
-
-};
-
-// Increase the warm-up counter of the provided script upon execution and test if
-// the warm-up counter surpasses the threshold. Upon hit it will recompile the
-// outermost script (i.e. not the inlined script).
-class MRecompileCheck : public MNullaryInstruction
-{
- public:
- enum RecompileCheckType {
- RecompileCheck_OptimizationLevel,
- RecompileCheck_Inlining
- };
-
- private:
- JSScript* script_;
- uint32_t recompileThreshold_;
- bool forceRecompilation_;
- bool increaseWarmUpCounter_;
-
- MRecompileCheck(JSScript* script, uint32_t recompileThreshold, RecompileCheckType type)
- : MNullaryInstruction(classOpcode),
- script_(script),
- recompileThreshold_(recompileThreshold)
- {
- switch (type) {
- case RecompileCheck_OptimizationLevel:
- forceRecompilation_ = false;
- increaseWarmUpCounter_ = true;
- break;
- case RecompileCheck_Inlining:
- forceRecompilation_ = true;
- increaseWarmUpCounter_ = false;
- break;
- default:
- MOZ_CRASH("Unexpected recompile check type");
- }
-
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(RecompileCheck)
- TRIVIAL_NEW_WRAPPERS
-
- JSScript* script() const {
- return script_;
- }
-
- uint32_t recompileThreshold() const {
- return recompileThreshold_;
- }
-
- bool forceRecompilation() const {
- return forceRecompilation_;
- }
-
- bool increaseWarmUpCounter() const {
- return increaseWarmUpCounter_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MAtomicIsLockFree
- : public MUnaryInstruction,
- public ConvertToInt32Policy<0>::Data
-{
- explicit MAtomicIsLockFree(MDefinition* value)
- : MUnaryInstruction(classOpcode, value)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(AtomicIsLockFree)
- TRIVIAL_NEW_WRAPPERS
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
- bool canRecoverOnBailout() const override {
- return true;
- }
-
- ALLOW_CLONE(MAtomicIsLockFree)
-};
-
-// This applies to an object that is known to be a TypedArray, it bails out
-// if the obj does not map a SharedArrayBuffer.
-
-class MGuardSharedTypedArray
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MGuardSharedTypedArray(MDefinition* obj)
- : MUnaryInstruction(classOpcode, obj)
- {
- setGuard();
- setMovable();
- }
-
-public:
- INSTRUCTION_HEADER(GuardSharedTypedArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MCompareExchangeTypedArrayElement
- : public MQuaternaryInstruction,
- public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3>>::Data
-{
- Scalar::Type arrayType_;
-
- explicit MCompareExchangeTypedArrayElement(MDefinition* elements, MDefinition* index,
- Scalar::Type arrayType, MDefinition* oldval,
- MDefinition* newval)
- : MQuaternaryInstruction(classOpcode, elements, index, oldval, newval),
- arrayType_(arrayType)
- {
- setGuard(); // Not removable
- }
-
- public:
- INSTRUCTION_HEADER(CompareExchangeTypedArrayElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, oldval), (3, newval))
-
- bool isByteArray() const {
- return (arrayType_ == Scalar::Int8 ||
- arrayType_ == Scalar::Uint8);
- }
- int oldvalOperand() {
- return 2;
- }
- Scalar::Type arrayType() const {
- return arrayType_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-};
-
-class MAtomicExchangeTypedArrayElement
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>>::Data
-{
- Scalar::Type arrayType_;
-
- MAtomicExchangeTypedArrayElement(MDefinition* elements, MDefinition* index, MDefinition* value,
- Scalar::Type arrayType)
- : MTernaryInstruction(classOpcode, elements, index, value),
- arrayType_(arrayType)
- {
- MOZ_ASSERT(arrayType <= Scalar::Uint32);
- setGuard(); // Not removable
- }
-
- public:
- INSTRUCTION_HEADER(AtomicExchangeTypedArrayElement)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value))
-
- bool isByteArray() const {
- return (arrayType_ == Scalar::Int8 ||
- arrayType_ == Scalar::Uint8);
- }
- Scalar::Type arrayType() const {
- return arrayType_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-};
-
-class MAtomicTypedArrayElementBinop
- : public MTernaryInstruction,
- public MixPolicy< ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2> >::Data
-{
- private:
- AtomicOp op_;
- Scalar::Type arrayType_;
-
- protected:
- explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition* elements, MDefinition* index,
- Scalar::Type arrayType, MDefinition* value)
- : MTernaryInstruction(classOpcode, elements, index, value),
- op_(op),
- arrayType_(arrayType)
- {
- setGuard(); // Not removable
- }
-
- public:
- INSTRUCTION_HEADER(AtomicTypedArrayElementBinop)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, elements), (1, index), (2, value))
-
- bool isByteArray() const {
- return (arrayType_ == Scalar::Int8 ||
- arrayType_ == Scalar::Uint8);
- }
- AtomicOp operation() const {
- return op_;
- }
- Scalar::Type arrayType() const {
- return arrayType_;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::UnboxedElement);
- }
-};
-
-class MDebugger : public MNullaryInstruction
-{
- MDebugger()
- : MNullaryInstruction(classOpcode)
- { }
-
- public:
- INSTRUCTION_HEADER(Debugger)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MCheckIsObj
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- uint8_t checkKind_;
-
- MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
- : MUnaryInstruction(classOpcode, toCheck),
- checkKind_(checkKind)
- {
- setResultType(MIRType::Value);
- setResultTypeSet(toCheck->resultTypeSet());
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(CheckIsObj)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, checkValue))
-
- uint8_t checkKind() const { return checkKind_; }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MCheckIsCallable
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- uint8_t checkKind_;
-
- MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
- : MUnaryInstruction(classOpcode, toCheck),
- checkKind_(checkKind)
- {
- setResultType(MIRType::Value);
- setResultTypeSet(toCheck->resultTypeSet());
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(CheckIsCallable)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, checkValue))
-
- uint8_t checkKind() const { return checkKind_; }
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-};
-
-class MCheckObjCoercible
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MCheckObjCoercible(MDefinition* toCheck)
- : MUnaryInstruction(classOpcode, toCheck)
- {
- setGuard();
- setResultType(MIRType::Value);
- setResultTypeSet(toCheck->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(CheckObjCoercible)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, checkValue))
-};
-
-class MDebugCheckSelfHosted
- : public MUnaryInstruction,
- public BoxInputsPolicy::Data
-{
- explicit MDebugCheckSelfHosted(MDefinition* toCheck)
- : MUnaryInstruction(classOpcode, toCheck)
- {
- setGuard();
- setResultType(MIRType::Value);
- setResultTypeSet(toCheck->resultTypeSet());
- }
-
- public:
- INSTRUCTION_HEADER(DebugCheckSelfHosted)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, checkValue))
-
-};
-
-class MFinishBoundFunctionInit
- : public MTernaryInstruction,
- public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2>>::Data
-{
- MFinishBoundFunctionInit(MDefinition* bound, MDefinition* target, MDefinition* argCount)
- : MTernaryInstruction(classOpcode, bound, target, argCount)
- { }
-
- public:
- INSTRUCTION_HEADER(FinishBoundFunctionInit)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, bound), (1, target), (2, argCount))
-};
-
-class MIsPackedArray
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MIsPackedArray(MDefinition* array)
- : MUnaryInstruction(classOpcode, array)
- {
- setResultType(MIRType::Boolean);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(IsPackedArray)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, array))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-class MGetPrototypeOf
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MGetPrototypeOf(MDefinition* target)
- : MUnaryInstruction(classOpcode, target)
- {
- setResultType(MIRType::Value);
- setGuard(); // May throw if target is a proxy.
- }
-
- public:
- INSTRUCTION_HEADER(GetPrototypeOf)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, target))
-};
-
-// Flips the input's sign bit, independently of the rest of the number's
-// payload. Note this is different from multiplying by minus-one, which has
-// side-effects for e.g. NaNs.
-class MWasmNeg
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmNeg(MDefinition* op, MIRType type)
- : MUnaryInstruction(classOpcode, op)
- {
- setResultType(type);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(WasmNeg)
- TRIVIAL_NEW_WRAPPERS
-};
-
-class MWasmLoadTls
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- uint32_t offset_;
- AliasSet aliases_;
-
- explicit MWasmLoadTls(MDefinition* tlsPointer, uint32_t offset, MIRType type, AliasSet aliases)
- : MUnaryInstruction(classOpcode, tlsPointer),
- offset_(offset),
- aliases_(aliases)
- {
- // Different Tls data have different alias classes and only those classes are allowed.
- MOZ_ASSERT(aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
- aliases_.flags() == AliasSet::None().flags());
-
- // The only types supported at the moment.
- MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32);
-
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(WasmLoadTls)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, tlsPtr))
-
- uint32_t offset() const {
- return offset_;
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return op() == ins->op() &&
- offset() == ins->toWasmLoadTls()->offset() &&
- type() == ins->type();
- }
-
- HashNumber valueHash() const override {
- return addU32ToHash(HashNumber(op()), offset());
- }
-
- AliasSet getAliasSet() const override {
- return aliases_;
- }
-};
-
-class MWasmBoundsCheck
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- wasm::BytecodeOffset bytecodeOffset_;
-
- explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit,
- wasm::BytecodeOffset bytecodeOffset)
- : MBinaryInstruction(classOpcode, index, boundsCheckLimit),
- bytecodeOffset_(bytecodeOffset)
- {
- // Bounds check is effectful: it throws for OOB.
- setGuard();
- }
-
- public:
- INSTRUCTION_HEADER(WasmBoundsCheck)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, index), (1, boundsCheckLimit))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool isRedundant() const {
- return !isGuard();
- }
-
- void setRedundant() {
- setNotGuard();
- }
-
- wasm::BytecodeOffset bytecodeOffset() const {
- return bytecodeOffset_;
- }
-};
-
-class MWasmAddOffset
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- uint32_t offset_;
- wasm::BytecodeOffset bytecodeOffset_;
-
- MWasmAddOffset(MDefinition* base, uint32_t offset, wasm::BytecodeOffset bytecodeOffset)
- : MUnaryInstruction(classOpcode, base),
- offset_(offset),
- bytecodeOffset_(bytecodeOffset)
- {
- setGuard();
- setResultType(MIRType::Int32);
- }
-
- public:
- INSTRUCTION_HEADER(WasmAddOffset)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, base))
-
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- uint32_t offset() const {
- return offset_;
- }
- wasm::BytecodeOffset bytecodeOffset() const {
- return bytecodeOffset_;
- }
-};
-
class MWasmLoad
: public MVariadicInstruction, // memoryBase is nullptr on some platforms
public NoTypePolicy::Data
{
wasm::MemoryAccessDesc access_;
explicit MWasmLoad(const wasm::MemoryAccessDesc& access, MIRType resultType)
: MVariadicInstruction(classOpcode),
@@ -14578,91 +4678,16 @@ class MAsmJSAtomicBinopHeap
MDefinition* tls() const { return getOperand(2); }
MDefinition* memoryBase() const { return getOperand(3); }
AliasSet getAliasSet() const override {
return AliasSet::Store(AliasSet::WasmHeap);
}
};
-class MWasmLoadGlobalVar
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant, MDefinition* tlsPtr)
- : MUnaryInstruction(classOpcode, tlsPtr),
- globalDataOffset_(globalDataOffset), isConstant_(isConstant)
- {
- MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
- setResultType(type);
- setMovable();
- }
-
- unsigned globalDataOffset_;
- bool isConstant_;
-
- public:
- INSTRUCTION_HEADER(WasmLoadGlobalVar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, tlsPtr))
-
- unsigned globalDataOffset() const { return globalDataOffset_; }
-
- HashNumber valueHash() const override;
- bool congruentTo(const MDefinition* ins) const override;
- MDefinition* foldsTo(TempAllocator& alloc) override;
-
- AliasSet getAliasSet() const override {
- return isConstant_ ? AliasSet::None() : AliasSet::Load(AliasSet::WasmGlobalVar);
- }
-
- AliasType mightAlias(const MDefinition* def) const override;
-};
-
-class MWasmStoreGlobalVar
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmStoreGlobalVar(unsigned globalDataOffset, MDefinition* value, MDefinition* tlsPtr)
- : MBinaryInstruction(classOpcode, value, tlsPtr),
- globalDataOffset_(globalDataOffset)
- { }
-
- unsigned globalDataOffset_;
-
- public:
- INSTRUCTION_HEADER(WasmStoreGlobalVar)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, value), (1, tlsPtr))
-
- unsigned globalDataOffset() const { return globalDataOffset_; }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::WasmGlobalVar);
- }
-};
-
-class MWasmParameter : public MNullaryInstruction
-{
- ABIArg abi_;
-
- MWasmParameter(ABIArg abi, MIRType mirType)
- : MNullaryInstruction(classOpcode),
- abi_(abi)
- {
- setResultType(mirType);
- }
-
- public:
- INSTRUCTION_HEADER(WasmParameter)
- TRIVIAL_NEW_WRAPPERS
-
- ABIArg abi() const { return abi_; }
-};
-
class MWasmReturn
: public MAryControlInstruction<1, 0>,
public NoTypePolicy::Data
{
explicit MWasmReturn(MDefinition* ins)
: MAryControlInstruction(classOpcode)
{
initOperand(0, ins);
@@ -14681,40 +4706,16 @@ class MWasmReturnVoid
: MAryControlInstruction(classOpcode)
{ }
public:
INSTRUCTION_HEADER(WasmReturnVoid)
TRIVIAL_NEW_WRAPPERS
};
-class MWasmStackArg
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmStackArg(uint32_t spOffset, MDefinition* ins)
- : MUnaryInstruction(classOpcode, ins),
- spOffset_(spOffset)
- {}
-
- uint32_t spOffset_;
-
- public:
- INSTRUCTION_HEADER(WasmStackArg)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, arg))
-
- uint32_t spOffset() const {
- return spOffset_;
- }
- void incrementOffset(uint32_t inc) {
- spOffset_ += inc;
- }
-};
-
class MWasmCall final
: public MVariadicInstruction,
public NoTypePolicy::Data
{
wasm::CallSiteDesc desc_;
wasm::CalleeDesc callee_;
FixedList<AnyRegister> argRegs_;
uint32_t spIncrement_;
@@ -14774,123 +4775,16 @@ class MWasmCall final
return true;
}
const ABIArg& instanceArg() const {
return instanceArg_;
}
};
-class MWasmSelect
- : public MTernaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmSelect(MDefinition* trueExpr, MDefinition* falseExpr, MDefinition *condExpr)
- : MTernaryInstruction(classOpcode, trueExpr, falseExpr, condExpr)
- {
- MOZ_ASSERT(condExpr->type() == MIRType::Int32);
- MOZ_ASSERT(trueExpr->type() == falseExpr->type());
- setResultType(trueExpr->type());
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(WasmSelect)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, trueExpr), (1, falseExpr), (2, condExpr))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- ALLOW_CLONE(MWasmSelect)
-};
-
-class MWasmReinterpret
- : public MUnaryInstruction,
- public NoTypePolicy::Data
-{
- MWasmReinterpret(MDefinition* val, MIRType toType)
- : MUnaryInstruction(classOpcode, val)
- {
- switch (val->type()) {
- case MIRType::Int32: MOZ_ASSERT(toType == MIRType::Float32); break;
- case MIRType::Float32: MOZ_ASSERT(toType == MIRType::Int32); break;
- case MIRType::Double: MOZ_ASSERT(toType == MIRType::Int64); break;
- case MIRType::Int64: MOZ_ASSERT(toType == MIRType::Double); break;
- default: MOZ_CRASH("unexpected reinterpret conversion");
- }
- setMovable();
- setResultType(toType);
- }
-
- public:
- INSTRUCTION_HEADER(WasmReinterpret)
- TRIVIAL_NEW_WRAPPERS
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
-
- ALLOW_CLONE(MWasmReinterpret)
-};
-
-class MRotate
- : public MBinaryInstruction,
- public NoTypePolicy::Data
-{
- bool isLeftRotate_;
-
- MRotate(MDefinition* input, MDefinition* count, MIRType type, bool isLeftRotate)
- : MBinaryInstruction(classOpcode, input, count), isLeftRotate_(isLeftRotate)
- {
- setMovable();
- setResultType(type);
- }
-
- public:
- INSTRUCTION_HEADER(Rotate)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, input), (1, count))
-
- AliasSet getAliasSet() const override {
- return AliasSet::None();
- }
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins) && ins->toRotate()->isLeftRotate() == isLeftRotate_;
- }
-
- bool isLeftRotate() const {
- return isLeftRotate_;
- }
-
- ALLOW_CLONE(MRotate)
-};
-
-class MUnknownValue : public MNullaryInstruction
-{
- protected:
- MUnknownValue()
- : MNullaryInstruction(classOpcode)
- {
- setResultType(MIRType::Value);
- }
-
- public:
- INSTRUCTION_HEADER(UnknownValue)
- TRIVIAL_NEW_WRAPPERS
-};
-
#undef INSTRUCTION_HEADER
void MUse::init(MDefinition* producer, MNode* consumer)
{
MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer");
MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer");
initUnchecked(producer, consumer);
}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/MIRInstruction.h
@@ -0,0 +1,10052 @@
+/* -*- 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 MIR_INSTRUCTION_H
+#define MIR_INSTRUCTION_H
+
+// Generates an LSnapshot without further effect.
+class MStart : public MNullaryInstruction
+{
+ MStart()
+ : MNullaryInstruction(classOpcode)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(Start)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// 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()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Pointer);
+ }
+
+ public:
+ INSTRUCTION_HEADER(OsrEntry)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// 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()
+ : MNullaryInstruction(classOpcode)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(Nop)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MNop)
+};
+
+// A constant js::Value.
+class MConstant : public MNullaryInstruction
+{
+ struct Payload {
+ union {
+ bool b;
+ int32_t i32;
+ int64_t i64;
+ float f;
+ double d;
+ JSString* str;
+ JS::Symbol* sym;
+ JSObject* obj;
+ uint64_t asBits;
+ };
+ Payload() : asBits(0) {}
+ };
+
+ Payload payload_;
+
+ static_assert(sizeof(Payload) == sizeof(uint64_t),
+ "asBits must be big enough for all payload bits");
+
+#ifdef DEBUG
+ void assertInitializedPayload() const;
+#else
+ void assertInitializedPayload() const {}
+#endif
+
+ protected:
+ MConstant(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints);
+ explicit MConstant(JSObject* obj);
+ explicit MConstant(float f);
+ explicit MConstant(int64_t i);
+
+ public:
+ INSTRUCTION_HEADER(Constant)
+ static MConstant* New(TempAllocator& alloc, const Value& v,
+ CompilerConstraintList* constraints = nullptr);
+ static MConstant* New(TempAllocator::Fallible alloc, const Value& v,
+ CompilerConstraintList* constraints = nullptr);
+ static MConstant* New(TempAllocator& alloc, const Value& v, MIRType type);
+ static MConstant* NewFloat32(TempAllocator& alloc, double d);
+ static MConstant* NewInt64(TempAllocator& alloc, int64_t i);
+ static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
+ static MConstant* Copy(TempAllocator& alloc, MConstant* src) {
+ return new(alloc) MConstant(*src);
+ }
+
+ // Try to convert this constant to boolean, similar to js::ToBoolean.
+ // Returns false if the type is MIRType::Magic*.
+ bool MOZ_MUST_USE valueToBoolean(bool* res) const;
+
+ // Like valueToBoolean, but returns the result directly instead of using
+ // an outparam. Should not be used if this constant might be a magic value.
+ bool valueToBooleanInfallible() const {
+ bool res;
+ MOZ_ALWAYS_TRUE(valueToBoolean(&res));
+ return res;
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ HashNumber valueHash() const override;
+ bool congruentTo(const MDefinition* ins) const override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool updateForReplacement(MDefinition* def) override {
+ MConstant* c = def->toConstant();
+ // During constant folding, we don't want to replace a float32
+ // value by a double value.
+ if (type() == MIRType::Float32)
+ return c->type() == MIRType::Float32;
+ if (type() == MIRType::Double)
+ return c->type() != MIRType::Float32;
+ return true;
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+ bool needTruncation(TruncateKind kind) override;
+ void truncate() override;
+
+ bool canProduceFloat32() const override;
+
+ ALLOW_CLONE(MConstant)
+
+ bool equals(const MConstant* other) const {
+ assertInitializedPayload();
+ return type() == other->type() && payload_.asBits == other->payload_.asBits;
+ }
+
+ bool toBoolean() const {
+ MOZ_ASSERT(type() == MIRType::Boolean);
+ return payload_.b;
+ }
+ int32_t toInt32() const {
+ MOZ_ASSERT(type() == MIRType::Int32);
+ return payload_.i32;
+ }
+ int64_t toInt64() const {
+ MOZ_ASSERT(type() == MIRType::Int64);
+ return payload_.i64;
+ }
+ bool isInt32(int32_t i) const {
+ return type() == MIRType::Int32 && payload_.i32 == i;
+ }
+ const double& toDouble() const {
+ MOZ_ASSERT(type() == MIRType::Double);
+ return payload_.d;
+ }
+ const float& toFloat32() const {
+ MOZ_ASSERT(type() == MIRType::Float32);
+ return payload_.f;
+ }
+ JSString* toString() const {
+ MOZ_ASSERT(type() == MIRType::String);
+ return payload_.str;
+ }
+ JS::Symbol* toSymbol() const {
+ MOZ_ASSERT(type() == MIRType::Symbol);
+ return payload_.sym;
+ }
+ JSObject& toObject() const {
+ MOZ_ASSERT(type() == MIRType::Object);
+ return *payload_.obj;
+ }
+ JSObject* toObjectOrNull() const {
+ if (type() == MIRType::Object)
+ return payload_.obj;
+ MOZ_ASSERT(type() == MIRType::Null);
+ return nullptr;
+ }
+
+ bool isTypeRepresentableAsDouble() const {
+ return IsTypeRepresentableAsDouble(type());
+ }
+ double numberToDouble() const {
+ MOZ_ASSERT(isTypeRepresentableAsDouble());
+ if (type() == MIRType::Int32)
+ return toInt32();
+ if (type() == MIRType::Double)
+ return toDouble();
+ return toFloat32();
+ }
+
+ // Convert this constant to a js::Value. Float32 constants will be stored
+ // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
+ // all constants can be represented by js::Value (wasm supports int64).
+ Value toJSValue() const;
+
+ bool appendRoots(MRootList& roots) const override;
+};
+
+// Floating-point value as created by wasm. Just a constant value, used to
+// effectively inhibite all the MIR optimizations. This uses the same LIR nodes
+// as a MConstant of the same type would.
+class MWasmFloatConstant : public MNullaryInstruction
+{
+ union {
+ float f32_;
+ double f64_;
+ uint64_t bits_;
+ } u;
+
+ explicit MWasmFloatConstant(MIRType type)
+ : MNullaryInstruction(classOpcode)
+ {
+ u.bits_ = 0;
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmFloatConstant)
+
+ static MWasmFloatConstant* NewDouble(TempAllocator& alloc, double d) {
+ auto* ret = new(alloc) MWasmFloatConstant(MIRType::Double);
+ ret->u.f64_ = d;
+ return ret;
+ }
+
+ static MWasmFloatConstant* NewFloat32(TempAllocator& alloc, float f) {
+ auto* ret = new(alloc) MWasmFloatConstant(MIRType::Float32);
+ ret->u.f32_ = f;
+ return ret;
+ }
+
+ HashNumber valueHash() const override;
+ bool congruentTo(const MDefinition* ins) const override;
+ AliasSet getAliasSet() const override { return AliasSet::None(); }
+
+ const double& toDouble() const {
+ MOZ_ASSERT(type() == MIRType::Double);
+ return u.f64_;
+ }
+ const float& toFloat32() const {
+ MOZ_ASSERT(type() == MIRType::Float32);
+ return u.f32_;
+ }
+};
+
+// A constant SIMD value.
+class MSimdConstant
+ : public MNullaryInstruction
+{
+ SimdConstant value_;
+
+ protected:
+ MSimdConstant(const SimdConstant& v, MIRType type)
+ : MNullaryInstruction(classOpcode),
+ value_(v)
+ {
+ MOZ_ASSERT(IsSimdType(type));
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdConstant)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isSimdConstant())
+ return false;
+ // Bool32x4 and Int32x4 share the same underlying SimdConstant representation.
+ if (type() != ins->type())
+ return false;
+ return value() == ins->toSimdConstant()->value();
+ }
+
+ const SimdConstant& value() const {
+ return value_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MSimdConstant)
+};
+
+class MParameter : public MNullaryInstruction
+{
+ int32_t index_;
+
+ MParameter(int32_t index, TemporaryTypeSet* types)
+ : MNullaryInstruction(classOpcode),
+ index_(index)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(types);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Parameter)
+ TRIVIAL_NEW_WRAPPERS
+
+ static const int32_t THIS_SLOT = -1;
+ int32_t index() const {
+ return index_;
+ }
+ void printOpcode(GenericPrinter& out) const override;
+
+ HashNumber valueHash() const override;
+ bool congruentTo(const MDefinition* ins) const override;
+};
+
+class MCallee : public MNullaryInstruction
+{
+ public:
+ MCallee()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Callee)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MIsConstructing : public MNullaryInstruction
+{
+ public:
+ MIsConstructing()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsConstructing)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MNewArrayCopyOnWrite : public MNullaryInstruction
+{
+ CompilerGCPointer<ArrayObject*> templateObject_;
+ gc::InitialHeap initialHeap_;
+
+ MNewArrayCopyOnWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
+ ArrayObject* templateObject, gc::InitialHeap initialHeap)
+ : MNullaryInstruction(classOpcode),
+ templateObject_(templateObject),
+ initialHeap_(initialHeap)
+ {
+ MOZ_ASSERT(!templateObject->isSingleton());
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewArrayCopyOnWrite)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ ArrayObject* templateObject() const {
+ return templateObject_;
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject_);
+ }
+};
+
+class MNewTypedObject : public MNullaryInstruction
+{
+ CompilerGCPointer<InlineTypedObject*> templateObject_;
+ gc::InitialHeap initialHeap_;
+
+ MNewTypedObject(TempAllocator& alloc, CompilerConstraintList* constraints,
+ InlineTypedObject* templateObject,
+ gc::InitialHeap initialHeap)
+ : MNullaryInstruction(classOpcode),
+ templateObject_(templateObject),
+ initialHeap_(initialHeap)
+ {
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewTypedObject)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ InlineTypedObject* templateObject() const {
+ return templateObject_;
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject_);
+ }
+};
+
+class MBail : public MNullaryInstruction
+{
+ protected:
+ explicit MBail(BailoutKind kind)
+ : MNullaryInstruction(classOpcode)
+ {
+ bailoutKind_ = kind;
+ setGuard();
+ }
+
+ private:
+ BailoutKind bailoutKind_;
+
+ public:
+ INSTRUCTION_HEADER(Bail)
+
+ static MBail*
+ New(TempAllocator& alloc, BailoutKind kind) {
+ return new(alloc) MBail(kind);
+ }
+ static MBail*
+ New(TempAllocator& alloc) {
+ return new(alloc) MBail(Bailout_Inevitable);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ BailoutKind bailoutKind() const {
+ return bailoutKind_;
+ }
+};
+
+// This class serve as a way to force the encoding of a snapshot, even if there
+// is no resume point using it. This is useful to run MAssertRecoveredOnBailout
+// assertions.
+class MEncodeSnapshot : public MNullaryInstruction
+{
+ protected:
+ MEncodeSnapshot()
+ : MNullaryInstruction(classOpcode)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(EncodeSnapshot)
+
+ static MEncodeSnapshot*
+ New(TempAllocator& alloc) {
+ return new(alloc) MEncodeSnapshot();
+ }
+};
+
+class MRunOncePrologue
+ : public MNullaryInstruction
+{
+ protected:
+ MRunOncePrologue()
+ : MNullaryInstruction(classOpcode)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(RunOncePrologue)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+// Inline implementation of Math.random().
+class MRandom : public MNullaryInstruction
+{
+ MRandom()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Double);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Random)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+ bool canRecoverOnBailout() const override {
+#ifdef JS_MORE_DETERMINISTIC
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ ALLOW_CLONE(MRandom)
+};
+
+class MNullarySharedStub
+ : public MNullaryInstruction
+{
+ explicit MNullarySharedStub()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(NullarySharedStub)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// Check the current frame for over-recursion past the global stack limit.
+class MCheckOverRecursed
+ : public MNullaryInstruction
+{
+ MCheckOverRecursed()
+ : MNullaryInstruction(classOpcode)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(CheckOverRecursed)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Check whether we need to fire the interrupt handler.
+class MInterruptCheck : public MNullaryInstruction
+{
+ MInterruptCheck()
+ : MNullaryInstruction(classOpcode)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(InterruptCheck)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Unconditionally throw an uninitialized let error.
+class MThrowRuntimeLexicalError : public MNullaryInstruction
+{
+ unsigned errorNumber_;
+
+ explicit MThrowRuntimeLexicalError(unsigned errorNumber)
+ : MNullaryInstruction(classOpcode),
+ errorNumber_(errorNumber)
+ {
+ setGuard();
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ThrowRuntimeLexicalError)
+ TRIVIAL_NEW_WRAPPERS
+
+ unsigned errorNumber() const {
+ return errorNumber_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// In the prologues of global and eval scripts, check for redeclarations.
+class MGlobalNameConflictsCheck : public MNullaryInstruction
+{
+ MGlobalNameConflictsCheck()
+ : MNullaryInstruction(classOpcode)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GlobalNameConflictsCheck)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MDefLexical
+ : public MNullaryInstruction
+{
+ CompilerPropertyName name_; // Target name to be defined.
+ unsigned attrs_; // Attributes to be set.
+
+ private:
+ MDefLexical(PropertyName* name, unsigned attrs)
+ : MNullaryInstruction(classOpcode),
+ name_(name),
+ attrs_(attrs)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(DefLexical)
+ TRIVIAL_NEW_WRAPPERS
+
+ PropertyName* name() const {
+ return name_;
+ }
+ unsigned attrs() const {
+ return attrs_;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MRegExp : public MNullaryInstruction
+{
+ CompilerGCPointer<RegExpObject*> source_;
+ bool mustClone_;
+ bool hasShared_;
+
+ MRegExp(TempAllocator& alloc, CompilerConstraintList* constraints, RegExpObject* source,
+ bool hasShared)
+ : MNullaryInstruction(classOpcode),
+ source_(source),
+ mustClone_(true),
+ hasShared_(hasShared)
+ {
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, source));
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExp)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ void setDoNotClone() {
+ mustClone_ = false;
+ }
+ bool mustClone() const {
+ return mustClone_;
+ }
+ bool hasShared() const {
+ return hasShared_;
+ }
+ RegExpObject* source() const {
+ return source_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(source_);
+ }
+};
+
+class MClassConstructor : public MNullaryInstruction
+{
+ jsbytecode* pc_;
+
+ explicit MClassConstructor(jsbytecode* pc)
+ : MNullaryInstruction(classOpcode),
+ pc_(pc)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ClassConstructor)
+ TRIVIAL_NEW_WRAPPERS
+
+ jsbytecode* pc() const {
+ return pc_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// A constant value for some object's typed array elements.
+class MConstantElements : public MNullaryInstruction
+{
+ SharedMem<void*> value_;
+
+ protected:
+ explicit MConstantElements(SharedMem<void*> v)
+ : MNullaryInstruction(classOpcode),
+ value_(v)
+ {
+ setResultType(MIRType::Elements);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ConstantElements)
+ TRIVIAL_NEW_WRAPPERS
+
+ SharedMem<void*> value() const {
+ return value_;
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ HashNumber valueHash() const override {
+ return (HashNumber)(size_t) value_.asValue();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return ins->isConstantElements() && ins->toConstantElements()->value() == value();
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MConstantElements)
+};
+
+class MCallGetIntrinsicValue : public MNullaryInstruction
+{
+ CompilerPropertyName name_;
+
+ explicit MCallGetIntrinsicValue(PropertyName* name)
+ : MNullaryInstruction(classOpcode),
+ name_(name)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallGetIntrinsicValue)
+ TRIVIAL_NEW_WRAPPERS
+
+ PropertyName* name() const {
+ return name_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MArgumentsLength : public MNullaryInstruction
+{
+ MArgumentsLength()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ArgumentsLength)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ // Arguments |length| cannot be mutated by Ion Code.
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MNewTarget : public MNullaryInstruction
+{
+ MNewTarget() : MNullaryInstruction(classOpcode) {
+ setResultType(MIRType::Value);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewTarget)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MNewNamedLambdaObject : public MNullaryInstruction
+{
+ CompilerGCPointer<LexicalEnvironmentObject*> templateObj_;
+
+ explicit MNewNamedLambdaObject(LexicalEnvironmentObject* templateObj)
+ : MNullaryInstruction(classOpcode),
+ templateObj_(templateObj)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewNamedLambdaObject)
+ TRIVIAL_NEW_WRAPPERS
+
+ LexicalEnvironmentObject* templateObj() {
+ return templateObj_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObj_);
+ }
+};
+
+// Increase the warm-up counter of the provided script upon execution and test if
+// the warm-up counter surpasses the threshold. Upon hit it will recompile the
+// outermost script (i.e. not the inlined script).
+class MRecompileCheck : public MNullaryInstruction
+{
+ public:
+ enum RecompileCheckType {
+ RecompileCheck_OptimizationLevel,
+ RecompileCheck_Inlining
+ };
+
+ private:
+ JSScript* script_;
+ uint32_t recompileThreshold_;
+ bool forceRecompilation_;
+ bool increaseWarmUpCounter_;
+
+ MRecompileCheck(JSScript* script, uint32_t recompileThreshold, RecompileCheckType type)
+ : MNullaryInstruction(classOpcode),
+ script_(script),
+ recompileThreshold_(recompileThreshold)
+ {
+ switch (type) {
+ case RecompileCheck_OptimizationLevel:
+ forceRecompilation_ = false;
+ increaseWarmUpCounter_ = true;
+ break;
+ case RecompileCheck_Inlining:
+ forceRecompilation_ = true;
+ increaseWarmUpCounter_ = false;
+ break;
+ default:
+ MOZ_CRASH("Unexpected recompile check type");
+ }
+
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(RecompileCheck)
+ TRIVIAL_NEW_WRAPPERS
+
+ JSScript* script() const {
+ return script_;
+ }
+
+ uint32_t recompileThreshold() const {
+ return recompileThreshold_;
+ }
+
+ bool forceRecompilation() const {
+ return forceRecompilation_;
+ }
+
+ bool increaseWarmUpCounter() const {
+ return increaseWarmUpCounter_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MDebugger : public MNullaryInstruction
+{
+ MDebugger()
+ : MNullaryInstruction(classOpcode)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(Debugger)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MWasmParameter : public MNullaryInstruction
+{
+ ABIArg abi_;
+
+ MWasmParameter(ABIArg abi, MIRType mirType)
+ : MNullaryInstruction(classOpcode),
+ abi_(abi)
+ {
+ setResultType(mirType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmParameter)
+ TRIVIAL_NEW_WRAPPERS
+
+ ABIArg abi() const { return abi_; }
+};
+
+class MUnknownValue : public MNullaryInstruction
+{
+ protected:
+ MUnknownValue()
+ : MNullaryInstruction(classOpcode)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(UnknownValue)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// Truncation barrier. This is intended for protecting its input against
+// follow-up truncation optimizations.
+class MLimitedTruncate
+ : public MUnaryInstruction,
+ public ConvertToInt32Policy<0>::Data
+{
+ public:
+ TruncateKind truncate_;
+ TruncateKind truncateLimit_;
+
+ protected:
+ MLimitedTruncate(MDefinition* input, TruncateKind limit)
+ : MUnaryInstruction(classOpcode, input),
+ truncate_(NoTruncate),
+ truncateLimit_(limit)
+ {
+ setResultType(MIRType::Int32);
+ setResultTypeSet(input->resultTypeSet());
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LimitedTruncate)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+ bool needTruncation(TruncateKind kind) override;
+ TruncateKind operandTruncateKind(size_t index) const override;
+ TruncateKind truncateKind() const {
+ return truncate_;
+ }
+ void setTruncateKind(TruncateKind kind) {
+ truncate_ = kind;
+ }
+};
+
+// Generic constructor of SIMD values with identical lanes.
+class MSimdSplat
+ : public MUnaryInstruction,
+ public SimdScalarPolicy<0>::Data
+{
+ protected:
+ MSimdSplat(MDefinition* v, MIRType type)
+ : MUnaryInstruction(classOpcode, v)
+ {
+ MOZ_ASSERT(IsSimdType(type));
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdSplat)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return SimdTypeToLaneType(type()) == MIRType::Float32;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MSimdSplat)
+};
+
+// Converts all lanes of a given vector into the type of another vector
+class MSimdConvert
+ : public MUnaryInstruction,
+ public SimdPolicy<0>::Data
+{
+ // When either fromType or toType is an integer vector, should it be treated
+ // as signed or unsigned. Note that we don't support int-int conversions -
+ // use MSimdReinterpretCast for that.
+ SimdSign sign_;
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ MSimdConvert(MDefinition* obj, MIRType toType, SimdSign sign,
+ wasm::BytecodeOffset bytecodeOffset)
+ : MUnaryInstruction(classOpcode, obj), sign_(sign), bytecodeOffset_(bytecodeOffset)
+ {
+ MIRType fromType = obj->type();
+ MOZ_ASSERT(IsSimdType(fromType));
+ MOZ_ASSERT(IsSimdType(toType));
+ // All conversions are int <-> float, so signedness is required.
+ MOZ_ASSERT(sign != SimdSign::NotApplicable);
+
+ setResultType(toType);
+ specialization_ = fromType; // expects fromType as input
+
+ setMovable();
+ if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) {
+ // Does the extra range check => do not remove
+ setGuard();
+ }
+ }
+
+ static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign,
+ wasm::BytecodeOffset bytecodeOffset)
+ {
+ return new (alloc) MSimdConvert(obj, toType, sign, bytecodeOffset);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdConvert)
+
+ // Create a MSimdConvert instruction and add it to the basic block.
+ // Possibly create and add an equivalent sequence of instructions instead if
+ // the current target doesn't support the requested conversion directly.
+ // Return the inserted MInstruction that computes the converted value.
+ static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
+ MIRType toType, SimdSign sign,
+ wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset());
+
+ SimdSign signedness() const {
+ return sign_;
+ }
+ wasm::BytecodeOffset bytecodeOffset() const {
+ return bytecodeOffset_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ const MSimdConvert* other = ins->toSimdConvert();
+ return sign_ == other->sign_;
+ }
+ ALLOW_CLONE(MSimdConvert)
+};
+
+// Casts bits of a vector input to another SIMD type (doesn't generate code).
+class MSimdReinterpretCast
+ : public MUnaryInstruction,
+ public SimdPolicy<0>::Data
+{
+ MSimdReinterpretCast(MDefinition* obj, MIRType toType)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ MIRType fromType = obj->type();
+ MOZ_ASSERT(IsSimdType(fromType));
+ MOZ_ASSERT(IsSimdType(toType));
+ setMovable();
+ setResultType(toType);
+ specialization_ = fromType; // expects fromType as input
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdReinterpretCast)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ ALLOW_CLONE(MSimdReinterpretCast)
+};
+
+// Extracts a lane element from a given vector type, given by its lane symbol.
+//
+// For integer SIMD types, a SimdSign must be provided so the lane value can be
+// converted to a scalar correctly.
+class MSimdExtractElement
+ : public MUnaryInstruction,
+ public SimdPolicy<0>::Data
+{
+ protected:
+ unsigned lane_;
+ SimdSign sign_;
+
+ MSimdExtractElement(MDefinition* obj, MIRType laneType, unsigned lane, SimdSign sign)
+ : MUnaryInstruction(classOpcode, obj), lane_(lane), sign_(sign)
+ {
+ MIRType vecType = obj->type();
+ MOZ_ASSERT(IsSimdType(vecType));
+ MOZ_ASSERT(lane < SimdTypeToLength(vecType));
+ MOZ_ASSERT(!IsSimdType(laneType));
+ MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(vecType),
+ "Signedness must be specified for integer SIMD extractLanes");
+ // The resulting type should match the lane type.
+ // Allow extracting boolean lanes directly into an Int32 (for wasm).
+ // Allow extracting Uint32 lanes into a double.
+ //
+ // We also allow extracting Uint32 lanes into a MIRType::Int32. This is
+ // equivalent to extracting the Uint32 lane to a double and then
+ // applying MTruncateToInt32, but it bypasses the conversion to/from
+ // double.
+ MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType ||
+ (IsBooleanSimdType(vecType) && laneType == MIRType::Int32) ||
+ (vecType == MIRType::Int32x4 && laneType == MIRType::Double &&
+ sign == SimdSign::Unsigned));
+
+ setMovable();
+ specialization_ = vecType;
+ setResultType(laneType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdExtractElement)
+ TRIVIAL_NEW_WRAPPERS
+
+ unsigned lane() const {
+ return lane_;
+ }
+
+ SimdSign signedness() const {
+ return sign_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isSimdExtractElement())
+ return false;
+ const MSimdExtractElement* other = ins->toSimdExtractElement();
+ if (other->lane_ != lane_ || other->sign_ != sign_)
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ ALLOW_CLONE(MSimdExtractElement)
+};
+
+// Returns true if all lanes are true.
+class MSimdAllTrue
+ : public MUnaryInstruction,
+ public SimdPolicy<0>::Data
+{
+ protected:
+ explicit MSimdAllTrue(MDefinition* obj, MIRType result)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ MIRType simdType = obj->type();
+ MOZ_ASSERT(IsBooleanSimdType(simdType));
+ MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
+ setResultType(result);
+ specialization_ = simdType;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdAllTrue)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ ALLOW_CLONE(MSimdAllTrue)
+};
+
+// Returns true if any lane is true.
+class MSimdAnyTrue
+ : public MUnaryInstruction,
+ public SimdPolicy<0>::Data
+{
+ protected:
+ explicit MSimdAnyTrue(MDefinition* obj, MIRType result)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ MIRType simdType = obj->type();
+ MOZ_ASSERT(IsBooleanSimdType(simdType));
+ MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
+ setResultType(result);
+ specialization_ = simdType;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdAnyTrue)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ ALLOW_CLONE(MSimdAnyTrue)
+};
+
+// Applies a swizzle operation to the input, putting the input lanes as
+// indicated in the output register's lanes. This implements the SIMD.js
+// "swizzle" function, that takes one vector and an array of lane indexes.
+class MSimdSwizzle
+ : public MUnaryInstruction,
+ public MSimdShuffleBase,
+ public NoTypePolicy::Data
+{
+ protected:
+ MSimdSwizzle(MDefinition* obj, const uint8_t lanes[])
+ : MUnaryInstruction(classOpcode, obj), MSimdShuffleBase(lanes, obj->type())
+ {
+ for (unsigned i = 0; i < arity_; i++)
+ MOZ_ASSERT(lane(i) < arity_);
+ setResultType(obj->type());
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdSwizzle)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isSimdSwizzle())
+ return false;
+ const MSimdSwizzle* other = ins->toSimdSwizzle();
+ return sameLanes(other) && congruentIfOperandsEqual(other);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MSimdSwizzle)
+};
+
+class MSimdUnaryArith
+ : public MUnaryInstruction,
+ public SimdSameAsReturnedTypePolicy<0>::Data
+{
+ public:
+ enum Operation {
+#define OP_LIST_(OP) OP,
+ FOREACH_FLOAT_SIMD_UNOP(OP_LIST_)
+ neg,
+ not_
+#undef OP_LIST_
+ };
+
+ static const char* OperationName(Operation op) {
+ switch (op) {
+ case abs: return "abs";
+ case neg: return "neg";
+ case not_: return "not";
+ case reciprocalApproximation: return "reciprocalApproximation";
+ case reciprocalSqrtApproximation: return "reciprocalSqrtApproximation";
+ case sqrt: return "sqrt";
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ private:
+ Operation operation_;
+
+ MSimdUnaryArith(MDefinition* def, Operation op)
+ : MUnaryInstruction(classOpcode, def), operation_(op)
+ {
+ MIRType type = def->type();
+ MOZ_ASSERT(IsSimdType(type));
+ MOZ_ASSERT_IF(IsIntegerSimdType(type), op == neg || op == not_);
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdUnaryArith)
+ TRIVIAL_NEW_WRAPPERS
+
+ Operation operation() const { return operation_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) && ins->toSimdUnaryArith()->operation() == operation();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdUnaryArith);
+};
+
+// Deep clone a constant JSObject.
+class MCloneLiteral
+ : public MUnaryInstruction,
+ public ObjectPolicy<0>::Data
+{
+ protected:
+ explicit MCloneLiteral(MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CloneLiteral)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MNewArray
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ // Number of elements to allocate for the array.
+ uint32_t length_;
+
+ // Heap where the array should be allocated.
+ gc::InitialHeap initialHeap_;
+
+ // Whether values written to this array should be converted to double first.
+ bool convertDoubleElements_;
+
+ jsbytecode* pc_;
+
+ bool vmCall_;
+
+ MNewArray(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t length,
+ MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc,
+ bool vmCall = false);
+
+ public:
+ INSTRUCTION_HEADER(NewArray)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ static MNewArray* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
+ uint32_t length, MConstant* templateConst,
+ gc::InitialHeap initialHeap, jsbytecode* pc)
+ {
+ return new(alloc) MNewArray(alloc, constraints, length, templateConst, initialHeap, pc,
+ true);
+ }
+
+ uint32_t length() const {
+ return length_;
+ }
+
+ JSObject* templateObject() const {
+ return getOperand(0)->toConstant()->toObjectOrNull();
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ jsbytecode* pc() const {
+ return pc_;
+ }
+
+ bool isVMCall() const {
+ return vmCall_;
+ }
+
+ bool convertDoubleElements() const {
+ return convertDoubleElements_;
+ }
+
+ // 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 override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ // The template object can safely be used in the recover instruction
+ // because it can never be mutated by any other function execution.
+ return templateObject() != nullptr;
+ }
+};
+
+class MNewArrayDynamicLength
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ CompilerObject templateObject_;
+ gc::InitialHeap initialHeap_;
+
+ MNewArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
+ JSObject* templateObject, gc::InitialHeap initialHeap,
+ MDefinition* length)
+ : MUnaryInstruction(classOpcode, length),
+ templateObject_(templateObject),
+ initialHeap_(initialHeap)
+ {
+ setGuard(); // Need to throw if length is negative.
+ setResultType(MIRType::Object);
+ if (!templateObject->isSingleton())
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewArrayDynamicLength)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+ NAMED_OPERANDS((0, length))
+
+ JSObject* templateObject() const {
+ return templateObject_;
+ }
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject_);
+ }
+};
+
+class MNewTypedArray
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ gc::InitialHeap initialHeap_;
+
+ MNewTypedArray(TempAllocator& alloc, CompilerConstraintList* constraints,
+ MConstant* templateConst, gc::InitialHeap initialHeap)
+ : MUnaryInstruction(classOpcode, templateConst),
+ initialHeap_(initialHeap)
+ {
+ MOZ_ASSERT(!templateObject()->isSingleton());
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewTypedArray)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ TypedArrayObject* templateObject() const {
+ return &getOperand(0)->toConstant()->toObject().as<TypedArrayObject>();
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MNewTypedArrayDynamicLength
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ CompilerObject templateObject_;
+ gc::InitialHeap initialHeap_;
+
+ MNewTypedArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
+ JSObject* templateObject, gc::InitialHeap initialHeap,
+ MDefinition* length)
+ : MUnaryInstruction(classOpcode, length),
+ templateObject_(templateObject),
+ initialHeap_(initialHeap)
+ {
+ setGuard(); // Need to throw if length is negative.
+ setResultType(MIRType::Object);
+ if (!templateObject->isSingleton())
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewTypedArrayDynamicLength)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ MDefinition* length() const {
+ return getOperand(0);
+ }
+ JSObject* templateObject() const {
+ return templateObject_;
+ }
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject_);
+ }
+};
+
+class MNewObject
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ public:
+ enum Mode { ObjectLiteral, ObjectCreate };
+
+ private:
+ gc::InitialHeap initialHeap_;
+ Mode mode_;
+ bool vmCall_;
+
+ MNewObject(TempAllocator& alloc, CompilerConstraintList* constraints, MConstant* templateConst,
+ gc::InitialHeap initialHeap, Mode mode, bool vmCall = false)
+ : MUnaryInstruction(classOpcode, templateConst),
+ initialHeap_(initialHeap),
+ mode_(mode),
+ vmCall_(vmCall)
+ {
+ MOZ_ASSERT_IF(mode != ObjectLiteral, templateObject());
+ setResultType(MIRType::Object);
+
+ if (JSObject* obj = templateObject())
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, obj));
+
+ // The constant is kept separated in a MConstant, this way we can safely
+ // mark it during GC if we recover the object allocation. Otherwise, by
+ // making it emittedAtUses, we do not produce register allocations for
+ // it and inline its content inside the code produced by the
+ // CodeGenerator.
+ if (templateConst->toConstant()->type() == MIRType::Object)
+ templateConst->setEmittedAtUses();
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewObject)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ static MNewObject* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
+ MConstant* templateConst, gc::InitialHeap initialHeap,
+ Mode mode)
+ {
+ return new(alloc) MNewObject(alloc, constraints, templateConst, initialHeap, mode, true);
+ }
+
+ Mode mode() const {
+ return mode_;
+ }
+
+ JSObject* templateObject() const {
+ return getOperand(0)->toConstant()->toObjectOrNull();
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ bool isVMCall() const {
+ return vmCall_;
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ // The template object can safely be used in the recover instruction
+ // because it can never be mutated by any other function execution.
+ return templateObject() != nullptr;
+ }
+};
+
+
+class MNewIterator
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ public:
+ enum Type {
+ ArrayIterator,
+ StringIterator,
+ };
+
+private:
+ Type type_;
+
+ MNewIterator(TempAllocator& alloc, CompilerConstraintList* constraints,
+ MConstant* templateConst, Type type)
+ : MUnaryInstruction(classOpcode, templateConst),
+ type_(type)
+ {
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
+ templateConst->setEmittedAtUses();
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewIterator)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ Type type() const {
+ return type_;
+ }
+
+ JSObject* templateObject() {
+ return getOperand(0)->toConstant()->toObjectOrNull();
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MTypedObjectDescr
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ private:
+ explicit MTypedObjectDescr(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypedObjectDescr)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+// Generic way for constructing a SIMD object in IonMonkey, this instruction
+// takes as argument a SIMD instruction and returns a new SIMD object which
+// corresponds to the MIRType of its operand.
+class MSimdBox
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ protected:
+ CompilerGCPointer<InlineTypedObject*> templateObject_;
+ SimdType simdType_;
+ gc::InitialHeap initialHeap_;
+
+ MSimdBox(TempAllocator& alloc,
+ CompilerConstraintList* constraints,
+ MDefinition* op,
+ InlineTypedObject* templateObject,
+ SimdType simdType,
+ gc::InitialHeap initialHeap)
+ : MUnaryInstruction(classOpcode, op),
+ templateObject_(templateObject),
+ simdType_(simdType),
+ initialHeap_(initialHeap)
+ {
+ MOZ_ASSERT(IsSimdType(op->type()));
+ setMovable();
+ setResultType(MIRType::Object);
+ if (constraints)
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdBox)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ InlineTypedObject* templateObject() const {
+ return templateObject_;
+ }
+
+ SimdType simdType() const {
+ return simdType_;
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ const MSimdBox* box = ins->toSimdBox();
+ if (box->simdType() != simdType())
+ return false;
+ MOZ_ASSERT(box->templateObject() == templateObject());
+ if (box->initialHeap() != initialHeap())
+ return false;
+ return true;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject_);
+ }
+};
+
+class MSimdUnbox
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ protected:
+ SimdType simdType_;
+
+ MSimdUnbox(MDefinition* op, SimdType simdType)
+ : MUnaryInstruction(classOpcode, op),
+ simdType_(simdType)
+ {
+ MIRType type = SimdTypeToMIRType(simdType);
+ MOZ_ASSERT(IsSimdType(type));
+ setGuard();
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdUnbox)
+ TRIVIAL_NEW_WRAPPERS
+ ALLOW_CLONE(MSimdUnbox)
+
+ SimdType simdType() const { return simdType_; }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ return ins->toSimdUnbox()->simdType() == simdType();
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+};
+
+class MAssertRecoveredOnBailout
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ protected:
+ bool mustBeRecovered_;
+
+ MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered)
+ : MUnaryInstruction(classOpcode, ins), mustBeRecovered_(mustBeRecovered)
+ {
+ setResultType(MIRType::Value);
+ setRecoveredOnBailout();
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(AssertRecoveredOnBailout)
+ TRIVIAL_NEW_WRAPPERS
+
+ // Needed to assert that float32 instructions are correctly recovered.
+ bool canConsumeFloat32(MUse* use) const override { return true; }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MAssertFloat32
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ protected:
+ bool mustBeFloat32_;
+
+ MAssertFloat32(MDefinition* value, bool mustBeFloat32)
+ : MUnaryInstruction(classOpcode, value), mustBeFloat32_(mustBeFloat32)
+ {
+ }
+
+ public:
+ INSTRUCTION_HEADER(AssertFloat32)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool canConsumeFloat32(MUse* use) const override { return true; }
+
+ bool mustBeFloat32() const { return mustBeFloat32_; }
+};
+
+// Takes a typed value and returns an untyped value.
+class MBox
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ MBox(TempAllocator& alloc, MDefinition* ins)
+ : MUnaryInstruction(classOpcode, ins)
+ {
+ setResultType(MIRType::Value);
+ if (ins->resultTypeSet()) {
+ setResultTypeSet(ins->resultTypeSet());
+ } else if (ins->type() != MIRType::Value) {
+ TypeSet::Type ntype = ins->type() == MIRType::Object
+ ? TypeSet::AnyObjectType()
+ : TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type()));
+ setResultTypeSet(alloc.lifoAlloc()->new_<TemporaryTypeSet>(alloc.lifoAlloc(), ntype));
+ }
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Box)
+ static MBox* New(TempAllocator& alloc, MDefinition* ins)
+ {
+ // Cannot box a box.
+ MOZ_ASSERT(ins->type() != MIRType::Value);
+
+ return new(alloc) MBox(alloc, ins);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MBox)
+};
+
+// 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 final : public MUnaryInstruction, public BoxInputsPolicy::Data
+{
+ 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.
+ };
+
+ private:
+ Mode mode_;
+ BailoutKind bailoutKind_;
+
+ MUnbox(MDefinition* ins, MIRType type, Mode mode, BailoutKind kind, TempAllocator& alloc)
+ : MUnaryInstruction(classOpcode, ins),
+ mode_(mode)
+ {
+ // Only allow unboxing a non MIRType::Value when input and output types
+ // don't match. This is often used to force a bailout. Boxing happens
+ // during type analysis.
+ MOZ_ASSERT_IF(ins->type() != MIRType::Value, type != ins->type());
+
+ MOZ_ASSERT(type == MIRType::Boolean ||
+ type == MIRType::Int32 ||
+ type == MIRType::Double ||
+ type == MIRType::String ||
+ type == MIRType::Symbol ||
+ type == MIRType::Object);
+
+ TemporaryTypeSet* resultSet = ins->resultTypeSet();
+ if (resultSet && type == MIRType::Object)
+ resultSet = resultSet->cloneObjectsOnly(alloc.lifoAlloc());
+
+ setResultType(type);
+ setResultTypeSet(resultSet);
+ setMovable();
+
+ if (mode_ == TypeBarrier || mode_ == Fallible)
+ setGuard();
+
+ bailoutKind_ = kind;
+ }
+ public:
+ INSTRUCTION_HEADER(Unbox)
+ static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode)
+ {
+ // Unless we were given a specific BailoutKind, pick a default based on
+ // the type we expect.
+ BailoutKind kind;
+ switch (type) {
+ case MIRType::Boolean:
+ kind = Bailout_NonBooleanInput;
+ break;
+ case MIRType::Int32:
+ kind = Bailout_NonInt32Input;
+ break;
+ case MIRType::Double:
+ kind = Bailout_NonNumericInput; // Int32s are fine too
+ break;
+ case MIRType::String:
+ kind = Bailout_NonStringInput;
+ break;
+ case MIRType::Symbol:
+ kind = Bailout_NonSymbolInput;
+ break;
+ case MIRType::Object:
+ kind = Bailout_NonObjectInput;
+ break;
+ default:
+ MOZ_CRASH("Given MIRType cannot be unboxed.");
+ }
+
+ return new(alloc) MUnbox(ins, type, mode, kind, alloc);
+ }
+
+ static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode,
+ BailoutKind kind)
+ {
+ return new(alloc) MUnbox(ins, type, mode, kind, alloc);
+ }
+
+ Mode mode() const {
+ return mode_;
+ }
+ BailoutKind bailoutKind() const {
+ // If infallible, no bailout should be generated.
+ MOZ_ASSERT(fallible());
+ return bailoutKind_;
+ }
+ bool fallible() const {
+ return mode() != Infallible;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isUnbox() || ins->toUnbox()->mode() != mode())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void printOpcode(GenericPrinter& out) const override;
+ void makeInfallible() {
+ // Should only be called if we're already Infallible or TypeBarrier
+ MOZ_ASSERT(mode() != Fallible);
+ mode_ = Infallible;
+ }
+
+ ALLOW_CLONE(MUnbox)
+};
+
+class MGuardObject
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MGuardObject(MDefinition* ins)
+ : MUnaryInstruction(classOpcode, ins)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ setResultTypeSet(ins->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardObject)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MGuardString
+ : public MUnaryInstruction,
+ public StringPolicy<0>::Data
+{
+ explicit MGuardString(MDefinition* ins)
+ : MUnaryInstruction(classOpcode, ins)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardString)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MPolyInlineGuard
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MPolyInlineGuard(MDefinition* ins)
+ : MUnaryInstruction(classOpcode, ins)
+ {
+ setGuard();
+ setResultType(MIRType::Object);
+ setResultTypeSet(ins->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(PolyInlineGuard)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MAssertRange
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ // This is the range checked by the assertion. Don't confuse this with the
+ // range_ member or the range() accessor. Since MAssertRange doesn't return
+ // a value, it doesn't use those.
+ const Range* assertedRange_;
+
+ MAssertRange(MDefinition* ins, const Range* assertedRange)
+ : MUnaryInstruction(classOpcode, ins), assertedRange_(assertedRange)
+ {
+ setGuard();
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(AssertRange)
+ TRIVIAL_NEW_WRAPPERS
+
+ const Range* assertedRange() const {
+ return assertedRange_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+};
+
+// Caller-side allocation of |this| for |new|:
+// Given a templateobject, construct |this| for JSOP_NEW
+class MCreateThisWithTemplate
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ gc::InitialHeap initialHeap_;
+
+ MCreateThisWithTemplate(TempAllocator& alloc, CompilerConstraintList* constraints,
+ MConstant* templateConst, gc::InitialHeap initialHeap)
+ : MUnaryInstruction(classOpcode, templateConst),
+ initialHeap_(initialHeap)
+ {
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
+ }
+
+ public:
+ INSTRUCTION_HEADER(CreateThisWithTemplate)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+
+ // Template for |this|, provided by TI.
+ JSObject* templateObject() const {
+ return &getOperand(0)->toConstant()->toObject();
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ // Although creation of |this| modifies global state, it is safely repeatable.
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override;
+};
+
+// Eager initialization of arguments object.
+class MCreateArgumentsObject
+ : public MUnaryInstruction,
+ public ObjectPolicy<0>::Data
+{
+ CompilerGCPointer<ArgumentsObject*> templateObj_;
+
+ MCreateArgumentsObject(MDefinition* callObj, ArgumentsObject* templateObj)
+ : MUnaryInstruction(classOpcode, callObj),
+ templateObj_(templateObj)
+ {
+ setResultType(MIRType::Object);
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CreateArgumentsObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getCallObject))
+
+ ArgumentsObject* templateObject() const {
+ return templateObj_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObj_);
+ }
+};
+
+class MGetArgumentsObjectArg
+ : public MUnaryInstruction,
+ public ObjectPolicy<0>::Data
+{
+ size_t argno_;
+
+ MGetArgumentsObjectArg(MDefinition* argsObject, size_t argno)
+ : MUnaryInstruction(classOpcode, argsObject),
+ argno_(argno)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetArgumentsObjectArg)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getArgsObject))
+
+ size_t argno() const {
+ return argno_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::Any);
+ }
+};
+
+// Converts a uint32 to a double (coming from wasm).
+class MWasmUnsignedToDouble
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MWasmUnsignedToDouble(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::Double);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmUnsignedToDouble)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Converts a uint32 to a float32 (coming from wasm).
+class MWasmUnsignedToFloat32
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MWasmUnsignedToFloat32(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::Float32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmUnsignedToFloat32)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool canProduceFloat32() const override { return true; }
+};
+
+class MWrapInt64ToInt32
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool bottomHalf_;
+
+ explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf = true)
+ : MUnaryInstruction(classOpcode, def),
+ bottomHalf_(bottomHalf)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WrapInt64ToInt32)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isWrapInt64ToInt32())
+ return false;
+ if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool bottomHalf() const {
+ return bottomHalf_;
+ }
+};
+
+class MExtendInt32ToInt64
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool isUnsigned_;
+
+ MExtendInt32ToInt64(MDefinition* def, bool isUnsigned)
+ : MUnaryInstruction(classOpcode, def),
+ isUnsigned_(isUnsigned)
+ {
+ setResultType(MIRType::Int64);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ExtendInt32ToInt64)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool isUnsigned() const { return isUnsigned_; }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isExtendInt32ToInt64())
+ return false;
+ if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_)
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MWasmTruncateToInt64
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool isUnsigned_;
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ MWasmTruncateToInt64(MDefinition* def, bool isUnsigned, wasm::BytecodeOffset bytecodeOffset)
+ : MUnaryInstruction(classOpcode, def),
+ isUnsigned_(isUnsigned),
+ bytecodeOffset_(bytecodeOffset)
+ {
+ setResultType(MIRType::Int64);
+ setGuard(); // neither removable nor movable because of possible side-effects.
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmTruncateToInt64)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool isUnsigned() const { return isUnsigned_; }
+ wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) &&
+ ins->toWasmTruncateToInt64()->isUnsigned() == isUnsigned_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Truncate a value to an int32, with wasm semantics: this will trap when the
+// value is out of range.
+class MWasmTruncateToInt32
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool isUnsigned_;
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ explicit MWasmTruncateToInt32(MDefinition* def, bool isUnsigned,
+ wasm::BytecodeOffset bytecodeOffset)
+ : MUnaryInstruction(classOpcode, def),
+ isUnsigned_(isUnsigned), bytecodeOffset_(bytecodeOffset)
+ {
+ setResultType(MIRType::Int32);
+ setGuard(); // neither removable nor movable because of possible side-effects.
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmTruncateToInt32)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool isUnsigned() const {
+ return isUnsigned_;
+ }
+ wasm::BytecodeOffset bytecodeOffset() const {
+ return bytecodeOffset_;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) &&
+ ins->toWasmTruncateToInt32()->isUnsigned() == isUnsigned_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MInt64ToFloatingPoint
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool isUnsigned_;
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ MInt64ToFloatingPoint(MDefinition* def, MIRType type, wasm::BytecodeOffset bytecodeOffset,
+ bool isUnsigned)
+ : MUnaryInstruction(classOpcode, def),
+ isUnsigned_(isUnsigned),
+ bytecodeOffset_(bytecodeOffset)
+ {
+ MOZ_ASSERT(IsFloatingPointType(type));
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Int64ToFloatingPoint)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool isUnsigned() const { return isUnsigned_; }
+ wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isInt64ToFloatingPoint())
+ return false;
+ if (ins->toInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_)
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ 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,
+ public ToInt32Policy::Data
+{
+ bool canBeNegativeZero_;
+ MacroAssembler::IntConversionInputKind conversion_;
+
+ explicit MToInt32(MDefinition* def, MacroAssembler::IntConversionInputKind conversion =
+ MacroAssembler::IntConversion_Any)
+ : MUnaryInstruction(classOpcode, def),
+ canBeNegativeZero_(true),
+ conversion_(conversion)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+
+ // An object might have "valueOf", which means it is effectful.
+ // ToNumber(symbol) throws.
+ if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToInt32)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ // this only has backwards information flow.
+ void analyzeEdgeCasesBackward() override;
+
+ bool canBeNegativeZero() const {
+ return canBeNegativeZero_;
+ }
+ void setCanBeNegativeZero(bool negativeZero) {
+ canBeNegativeZero_ = negativeZero;
+ }
+
+ MacroAssembler::IntConversionInputKind conversion() const {
+ return conversion_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isToInt32() || ins->toToInt32()->conversion() != conversion())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+ void collectRangeInfoPreTrunc() override;
+
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override { return true; }
+#endif
+
+ ALLOW_CLONE(MToInt32)
+};
+
+// Converts a value or typed input to a truncated int32, for use with bitwise
+// operations. This is an infallible ValueToECMAInt32.
+class MTruncateToInt32
+ : public MUnaryInstruction,
+ public ToInt32Policy::Data
+{
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ explicit MTruncateToInt32(MDefinition* def,
+ wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
+ : MUnaryInstruction(classOpcode, def),
+ bytecodeOffset_(bytecodeOffset)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+
+ // An object might have "valueOf", which means it is effectful.
+ // ToInt32(symbol) throws.
+ if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TruncateToInt32)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+ TruncateKind operandTruncateKind(size_t index) const override;
+# ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return input()->type() < MIRType::Symbol;
+ }
+
+ wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
+
+ ALLOW_CLONE(MTruncateToInt32)
+};
+
+// Converts any type to a string
+class MToString :
+ public MUnaryInstruction,
+ public ToStringPolicy::Data
+{
+ explicit MToString(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::String);
+ setMovable();
+
+ // Objects might override toString and Symbols throw. We bailout in
+ // those cases and run side-effects in baseline instead.
+ if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToString)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool fallible() const {
+ return input()->mightBeType(MIRType::Object) ||
+ input()->mightBeType(MIRType::Symbol);
+ }
+
+ ALLOW_CLONE(MToString)
+};
+
+// Converts any type to an object, throwing on null or undefined.
+class MToObject :
+ public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MToObject(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::Object);
+ setGuard(); // Throws on null or undefined.
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToObject)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MToObject)
+};
+
+// Converts any type to an object or null value, throwing on undefined.
+class MToObjectOrNull :
+ public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MToObjectOrNull(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::ObjectOrNull);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToObjectOrNull)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MToObjectOrNull)
+};
+
+class MBitNot
+ : public MUnaryInstruction,
+ public BitwisePolicy::Data
+{
+ protected:
+ explicit MBitNot(MDefinition* input)
+ : MUnaryInstruction(classOpcode, input)
+ {
+ specialization_ = MIRType::None;
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(BitNot)
+ TRIVIAL_NEW_WRAPPERS
+
+ static MBitNot* NewInt32(TempAllocator& alloc, MDefinition* input);
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void setSpecialization(MIRType type) {
+ specialization_ = type;
+ setResultType(type);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ if (specialization_ == MIRType::None)
+ return AliasSet::Store(AliasSet::Any);
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return specialization_ != MIRType::None;
+ }
+
+ ALLOW_CLONE(MBitNot)
+};
+
+class MTypeOf
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ MIRType inputType_;
+ bool inputMaybeCallableOrEmulatesUndefined_;
+
+ MTypeOf(MDefinition* def, MIRType inputType)
+ : MUnaryInstruction(classOpcode, def), inputType_(inputType),
+ inputMaybeCallableOrEmulatesUndefined_(true)
+ {
+ setResultType(MIRType::String);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypeOf)
+ TRIVIAL_NEW_WRAPPERS
+
+ MIRType inputType() const {
+ return inputType_;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints);
+
+ bool inputMaybeCallableOrEmulatesUndefined() const {
+ return inputMaybeCallableOrEmulatesUndefined_;
+ }
+ void markInputNotCallableOrEmulatesUndefined() {
+ inputMaybeCallableOrEmulatesUndefined_ = false;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isTypeOf())
+ return false;
+ if (inputType() != ins->toTypeOf()->inputType())
+ return false;
+ if (inputMaybeCallableOrEmulatesUndefined() !=
+ ins->toTypeOf()->inputMaybeCallableOrEmulatesUndefined())
+ {
+ return false;
+ }
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MToAsync
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MToAsync(MDefinition* unwrapped)
+ : MUnaryInstruction(classOpcode, unwrapped)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToAsync)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MToAsyncGen
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MToAsyncGen(MDefinition* unwrapped)
+ : MUnaryInstruction(classOpcode, unwrapped)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToAsyncGen)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MToAsyncIter
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MToAsyncIter(MDefinition* unwrapped)
+ : MUnaryInstruction(classOpcode, unwrapped)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToAsyncIter)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MToId
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MToId(MDefinition* index)
+ : MUnaryInstruction(classOpcode, index)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ToId)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MSignExtendInt32
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ public:
+ enum Mode {
+ Byte,
+ Half
+ };
+
+ private:
+ Mode mode_;
+
+ MSignExtendInt32(MDefinition* op, Mode mode)
+ : MUnaryInstruction(classOpcode, op), mode_(mode)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SignExtendInt32)
+ TRIVIAL_NEW_WRAPPERS
+
+ Mode mode() const { return mode_; }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ return ins->isSignExtendInt32() && ins->toSignExtendInt32()->mode_ == mode_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MSignExtendInt32)
+};
+
+class MSignExtendInt64
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ public:
+ enum Mode {
+ Byte,
+ Half,
+ Word
+ };
+
+ private:
+ Mode mode_;
+
+ MSignExtendInt64(MDefinition* op, Mode mode)
+ : MUnaryInstruction(classOpcode, op), mode_(mode)
+ {
+ setResultType(MIRType::Int64);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SignExtendInt64)
+ TRIVIAL_NEW_WRAPPERS
+
+ Mode mode() const { return mode_; }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ return ins->isSignExtendInt64() && ins->toSignExtendInt64()->mode_ == mode_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MSignExtendInt64)
+};
+
+class MAbs
+ : public MUnaryInstruction,
+ public ArithPolicy::Data
+{
+ bool implicitTruncate_;
+
+ MAbs(MDefinition* num, MIRType type)
+ : MUnaryInstruction(classOpcode, num),
+ implicitTruncate_(false)
+ {
+ MOZ_ASSERT(IsNumberType(type));
+ setResultType(type);
+ setMovable();
+ specialization_ = type;
+ }
+
+ public:
+ INSTRUCTION_HEADER(Abs)
+ TRIVIAL_NEW_WRAPPERS
+
+ static MAbs* NewWasm(TempAllocator& alloc, MDefinition* num, MIRType type) {
+ auto* ins = new(alloc) MAbs(num, type);
+ if (type == MIRType::Int32)
+ ins->implicitTruncate_ = true;
+ return ins;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ bool fallible() const;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+ bool isFloat32Commutative() const override { return true; }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MAbs)
+};
+
+class MClz
+ : public MUnaryInstruction
+ , public BitwisePolicy::Data
+{
+ bool operandIsNeverZero_;
+
+ explicit MClz(MDefinition* num, MIRType type)
+ : MUnaryInstruction(classOpcode, num),
+ operandIsNeverZero_(false)
+ {
+ MOZ_ASSERT(IsIntType(type));
+ MOZ_ASSERT(IsNumberType(num->type()));
+ specialization_ = type;
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Clz)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, num))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool operandIsNeverZero() const {
+ return operandIsNeverZero_;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void computeRange(TempAllocator& alloc) override;
+ void collectRangeInfoPreTrunc() override;
+};
+
+class MCtz
+ : public MUnaryInstruction
+ , public BitwisePolicy::Data
+{
+ bool operandIsNeverZero_;
+
+ explicit MCtz(MDefinition* num, MIRType type)
+ : MUnaryInstruction(classOpcode, num),
+ operandIsNeverZero_(false)
+ {
+ MOZ_ASSERT(IsIntType(type));
+ MOZ_ASSERT(IsNumberType(num->type()));
+ specialization_ = type;
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Ctz)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, num))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool operandIsNeverZero() const {
+ return operandIsNeverZero_;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void computeRange(TempAllocator& alloc) override;
+ void collectRangeInfoPreTrunc() override;
+};
+
+class MPopcnt
+ : public MUnaryInstruction
+ , public BitwisePolicy::Data
+{
+ explicit MPopcnt(MDefinition* num, MIRType type)
+ : MUnaryInstruction(classOpcode, num)
+ {
+ MOZ_ASSERT(IsNumberType(num->type()));
+ MOZ_ASSERT(IsIntType(type));
+ specialization_ = type;
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Popcnt)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, num))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void computeRange(TempAllocator& alloc) override;
+};
+
+// Inline implementation of Math.sqrt().
+class MSqrt
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ MSqrt(MDefinition* num, MIRType type)
+ : MUnaryInstruction(classOpcode, num)
+ {
+ setResultType(type);
+ specialization_ = type;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Sqrt)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+
+ bool isFloat32Commutative() const override { return true; }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MSqrt)
+};
+
+// Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
+class MPowHalf
+ : public MUnaryInstruction,
+ public DoublePolicy<0>::Data
+{
+ bool operandIsNeverNegativeInfinity_;
+ bool operandIsNeverNegativeZero_;
+ bool operandIsNeverNaN_;
+
+ explicit MPowHalf(MDefinition* input)
+ : MUnaryInstruction(classOpcode, input),
+ operandIsNeverNegativeInfinity_(false),
+ operandIsNeverNegativeZero_(false),
+ operandIsNeverNaN_(false)
+ {
+ setResultType(MIRType::Double);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(PowHalf)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ bool operandIsNeverNegativeInfinity() const {
+ return operandIsNeverNegativeInfinity_;
+ }
+ bool operandIsNeverNegativeZero() const {
+ return operandIsNeverNegativeZero_;
+ }
+ bool operandIsNeverNaN() const {
+ return operandIsNeverNaN_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void collectRangeInfoPreTrunc() override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MPowHalf)
+};
+
+class MMathFunction
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ public:
+ enum Function {
+ Log,
+ Sin,
+ Cos,
+ Exp,
+ Tan,
+ ACos,
+ ASin,
+ ATan,
+ Log10,
+ Log2,
+ Log1P,
+ ExpM1,
+ CosH,
+ SinH,
+ TanH,
+ ACosH,
+ ASinH,
+ ATanH,
+ Sign,
+ Trunc,
+ Cbrt,
+ Floor,
+ Ceil,
+ Round
+ };
+
+ private:
+ Function function_;
+ const MathCache* cache_;
+
+ // A nullptr cache means this function will neither access nor update the cache.
+ MMathFunction(MDefinition* input, Function function, const MathCache* cache)
+ : MUnaryInstruction(classOpcode, input), function_(function), cache_(cache)
+ {
+ setResultType(MIRType::Double);
+ specialization_ = MIRType::Double;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(MathFunction)
+ TRIVIAL_NEW_WRAPPERS
+
+ Function function() const {
+ return function_;
+ }
+ const MathCache* cache() const {
+ return cache_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isMathFunction())
+ return false;
+ if (ins->toMathFunction()->function() != function())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ static const char* FunctionName(Function function);
+
+ bool isFloat32Commutative() const override {
+ return function_ == Floor || function_ == Ceil || function_ == Round;
+ }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+ void computeRange(TempAllocator& alloc) override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ if (input()->type() == MIRType::SinCosDouble)
+ return false;
+ switch(function_) {
+ case Sin:
+ case Log:
+ case Ceil:
+ case Floor:
+ case Round:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ALLOW_CLONE(MMathFunction)
+};
+
+class MFromCharCode
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ explicit MFromCharCode(MDefinition* code)
+ : MUnaryInstruction(classOpcode, code)
+ {
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(FromCharCode)
+ TRIVIAL_NEW_WRAPPERS
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MFromCharCode)
+};
+
+class MFromCodePoint
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ explicit MFromCodePoint(MDefinition* codePoint)
+ : MUnaryInstruction(classOpcode, codePoint)
+ {
+ setGuard(); // throws on invalid code point
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(FromCodePoint)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ ALLOW_CLONE(MFromCodePoint)
+};
+
+class MStringConvertCase
+ : public MUnaryInstruction,
+ public StringPolicy<0>::Data
+{
+ public:
+ enum Mode { LowerCase, UpperCase };
+
+ private:
+ Mode mode_;
+
+ MStringConvertCase(MDefinition* string, Mode mode)
+ : MUnaryInstruction(classOpcode, string), mode_(mode)
+ {
+ setResultType(MIRType::String);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(StringConvertCase)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, string))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) && ins->toStringConvertCase()->mode() == mode();
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ Mode mode() const {
+ return mode_;
+ }
+};
+
+class MSinCos
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ const MathCache* cache_;
+
+ MSinCos(MDefinition *input, const MathCache *cache)
+ : MUnaryInstruction(classOpcode, input),
+ cache_(cache)
+ {
+ setResultType(MIRType::SinCosDouble);
+ specialization_ = MIRType::Double;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SinCos)
+
+ static MSinCos *New(TempAllocator &alloc, MDefinition *input, const MathCache *cache)
+ {
+ return new (alloc) MSinCos(input, cache);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition *ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ const MathCache* cache() const {
+ return cache_;
+ }
+};
+
+// Returns the value to use as |this| value. See also ComputeThis and
+// BoxNonStrictThis in Interpreter.h.
+class MComputeThis
+ : public MUnaryInstruction,
+ public BoxPolicy<0>::Data
+{
+ explicit MComputeThis(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ComputeThis)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ // Note: don't override getAliasSet: the thisValue hook can be effectful.
+};
+
+// Load an arrow function's |new.target| value.
+class MArrowNewTarget
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MArrowNewTarget(MDefinition* callee)
+ : MUnaryInstruction(classOpcode, callee)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ArrowNewTarget)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, callee))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ // An arrow function's lexical |this| value is immutable.
+ return AliasSet::None();
+ }
+};
+
+// The goal of a Beta node is to split a def at a conditionally taken
+// branch, so that uses dominated by it have a different name.
+class MBeta
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ // This is the range induced by a comparison and branch in a preceding
+ // block. Note that this does not reflect any range constraints from
+ // the input value itself, so this value may differ from the range()
+ // range after it is computed.
+ const Range* comparison_;
+
+ MBeta(MDefinition* val, const Range* comp)
+ : MUnaryInstruction(classOpcode, val),
+ comparison_(comp)
+ {
+ setResultType(val->type());
+ setResultTypeSet(val->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(Beta)
+ TRIVIAL_NEW_WRAPPERS
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+};
+
+// If input evaluates to false (i.e. it's NaN, 0 or -0), 0 is returned, else the input is returned
+class MNaNToZero
+ : public MUnaryInstruction,
+ public DoublePolicy<0>::Data
+{
+ bool operandIsNeverNaN_;
+ bool operandIsNeverNegativeZero_;
+
+ explicit MNaNToZero(MDefinition* input)
+ : MUnaryInstruction(classOpcode, input),
+ operandIsNeverNaN_(false),
+ operandIsNeverNegativeZero_(false)
+ {
+ setResultType(MIRType::Double);
+ setMovable();
+ }
+ public:
+ INSTRUCTION_HEADER(NaNToZero)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool operandIsNeverNaN() const {
+ return operandIsNeverNaN_;
+ }
+
+ bool operandIsNeverNegativeZero() const {
+ return operandIsNeverNegativeZero_;
+ }
+
+ void collectRangeInfoPreTrunc() override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MNaNToZero)
+};
+
+// MIR representation of a Value on the OSR BaselineFrame.
+// The Value is indexed off of OsrFrameReg.
+class MOsrValue
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ ptrdiff_t frameOffset_;
+
+ MOsrValue(MOsrEntry* entry, ptrdiff_t frameOffset)
+ : MUnaryInstruction(classOpcode, entry),
+ frameOffset_(frameOffset)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(OsrValue)
+ TRIVIAL_NEW_WRAPPERS
+
+ ptrdiff_t frameOffset() const {
+ return frameOffset_;
+ }
+
+ MOsrEntry* entry() {
+ return getOperand(0)->toOsrEntry();
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame.
+// The pointer is indexed off of OsrFrameReg.
+class MOsrEnvironmentChain
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ explicit MOsrEnvironmentChain(MOsrEntry* entry)
+ : MUnaryInstruction(classOpcode, entry)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(OsrEnvironmentChain)
+ TRIVIAL_NEW_WRAPPERS
+
+ MOsrEntry* entry() {
+ return getOperand(0)->toOsrEntry();
+ }
+};
+
+// MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame.
+// The pointer is indexed off of OsrFrameReg.
+class MOsrArgumentsObject
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ explicit MOsrArgumentsObject(MOsrEntry* entry)
+ : MUnaryInstruction(classOpcode, entry)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(OsrArgumentsObject)
+ TRIVIAL_NEW_WRAPPERS
+
+ MOsrEntry* entry() {
+ return getOperand(0)->toOsrEntry();
+ }
+};
+
+// MIR representation of the return value on the OSR BaselineFrame.
+// The Value is indexed off of OsrFrameReg.
+class MOsrReturnValue
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ explicit MOsrReturnValue(MOsrEntry* entry)
+ : MUnaryInstruction(classOpcode, entry)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(OsrReturnValue)
+ TRIVIAL_NEW_WRAPPERS
+
+ MOsrEntry* entry() {
+ return getOperand(0)->toOsrEntry();
+ }
+};
+
+class MUnarySharedStub
+ : public MUnaryInstruction,
+ public BoxPolicy<0>::Data
+{
+ explicit MUnarySharedStub(MDefinition* input)
+ : MUnaryInstruction(classOpcode, input)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(UnarySharedStub)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
+// it to baseline to throw at the correct pc.
+class MLexicalCheck
+ : public MUnaryInstruction,
+ public BoxPolicy<0>::Data
+{
+ BailoutKind kind_;
+ explicit MLexicalCheck(MDefinition* input, BailoutKind kind = Bailout_UninitializedLexical)
+ : MUnaryInstruction(classOpcode, input),
+ kind_(kind)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(input->resultTypeSet());
+ setMovable();
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LexicalCheck)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ BailoutKind bailoutKind() const {
+ return kind_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+};
+
+// If not defined, set a global variable to |undefined|.
+class MDefVar
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ CompilerPropertyName name_; // Target name to be defined.
+ unsigned attrs_; // Attributes to be set.
+
+ private:
+ MDefVar(PropertyName* name, unsigned attrs, MDefinition* envChain)
+ : MUnaryInstruction(classOpcode, envChain),
+ name_(name),
+ attrs_(attrs)
+ {
+ }
+
+ public:
+ INSTRUCTION_HEADER(DefVar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, environmentChain))
+
+ PropertyName* name() const {
+ return name_;
+ }
+ unsigned attrs() const {
+ return attrs_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MRegExpPrototypeOptimizable
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MRegExpPrototypeOptimizable(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExpPrototypeOptimizable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MGetFirstDollarIndex
+ : public MUnaryInstruction,
+ public StringPolicy<0>::Data
+{
+ explicit MGetFirstDollarIndex(MDefinition* str)
+ : MUnaryInstruction(classOpcode, str)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetFirstDollarIndex)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, str))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+};
+
+// Returns obj->slots.
+class MSlots
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MSlots(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Slots);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Slots)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ ALLOW_CLONE(MSlots)
+};
+
+// Returns obj->elements.
+class MElements
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool unboxed_;
+
+ explicit MElements(MDefinition* object, bool unboxed = false)
+ : MUnaryInstruction(classOpcode, object), unboxed_(unboxed)
+ {
+ setResultType(MIRType::Elements);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Elements)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool unboxed() const {
+ return unboxed_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) &&
+ ins->toElements()->unboxed() == unboxed();
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ ALLOW_CLONE(MElements)
+};
+
+// Passes through an object's elements, after ensuring it is entirely doubles.
+class MConvertElementsToDoubles
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MConvertElementsToDoubles(MDefinition* elements)
+ : MUnaryInstruction(classOpcode, elements)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Elements);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ConvertElementsToDoubles)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ // This instruction can read and write to the elements' contents.
+ // However, it is alright to hoist this from loops which explicitly
+ // read or write to the elements: such reads and writes will use double
+ // values and can be reordered freely wrt this conversion, except that
+ // definite double loads must follow the conversion. The latter
+ // property is ensured by chaining this instruction with the elements
+ // themselves, in the same manner as MBoundsCheck.
+ return AliasSet::None();
+ }
+};
+
+// Passes through an object, after ensuring its elements are not copy on write.
+class MMaybeCopyElementsForWrite
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool checkNative_;
+
+ explicit MMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
+ : MUnaryInstruction(classOpcode, object), checkNative_(checkNative)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ setResultTypeSet(object->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(MaybeCopyElementsForWrite)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool checkNative() const {
+ return checkNative_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) &&
+ checkNative() == ins->toMaybeCopyElementsForWrite()->checkNative();
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields);
+ }
+#ifdef DEBUG
+ bool needsResumePoint() const override {
+ // This instruction is idempotent and does not change observable
+ // behavior, so does not need its own resume point.
+ return false;
+ }
+#endif
+
+};
+
+// Load the initialized length from an elements header.
+class MInitializedLength
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MInitializedLength(MDefinition* elements)
+ : MUnaryInstruction(classOpcode, elements)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(InitializedLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MInitializedLength)
+};
+
+// Load the array length from an elements header.
+class MArrayLength
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MArrayLength(MDefinition* elements)
+ : MUnaryInstruction(classOpcode, elements)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ArrayLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MArrayLength)
+};
+
+// Read the length of a typed array.
+class MTypedArrayLength
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MTypedArrayLength(MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypedArrayLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::TypedArrayLength);
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+};
+
+// Load a typed array's elements vector.
+class MTypedArrayElements
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MTypedArrayElements(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Elements);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypedArrayElements)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ ALLOW_CLONE(MTypedArrayElements)
+};
+
+// Load a binary data object's "elements", which is just its opaque
+// binary data space. Eventually this should probably be
+// unified with `MTypedArrayElements`.
+class MTypedObjectElements
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool definitelyOutline_;
+
+ private:
+ explicit MTypedObjectElements(MDefinition* object, bool definitelyOutline)
+ : MUnaryInstruction(classOpcode, object),
+ definitelyOutline_(definitelyOutline)
+ {
+ setResultType(MIRType::Elements);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypedObjectElements)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool definitelyOutline() const {
+ return definitelyOutline_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isTypedObjectElements())
+ return false;
+ const MTypedObjectElements* other = ins->toTypedObjectElements();
+ if (other->definitelyOutline() != definitelyOutline())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+class MKeepAliveObject
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MKeepAliveObject(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::None);
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(KeepAliveObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+};
+
+// Perform !-operation
+class MNot
+ : public MUnaryInstruction,
+ public TestPolicy::Data
+{
+ bool operandMightEmulateUndefined_;
+ bool operandIsNeverNaN_;
+
+ explicit MNot(MDefinition* input, CompilerConstraintList* constraints = nullptr)
+ : MUnaryInstruction(classOpcode, input),
+ operandMightEmulateUndefined_(true),
+ operandIsNeverNaN_(false)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ if (constraints)
+ cacheOperandMightEmulateUndefined(constraints);
+ }
+
+ void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
+
+ public:
+ static MNot* NewInt32(TempAllocator& alloc, MDefinition* input) {
+ MOZ_ASSERT(input->type() == MIRType::Int32 || input->type() == MIRType::Int64);
+ auto* ins = new(alloc) MNot(input);
+ ins->setResultType(MIRType::Int32);
+ return ins;
+ }
+
+ INSTRUCTION_HEADER(Not)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ void markNoOperandEmulatesUndefined() {
+ operandMightEmulateUndefined_ = false;
+ }
+ bool operandMightEmulateUndefined() const {
+ return operandMightEmulateUndefined_;
+ }
+ bool operandIsNeverNaN() const {
+ return operandIsNeverNaN_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void collectRangeInfoPreTrunc() override;
+
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+ bool isFloat32Commutative() const override { return true; }
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+// Bailout if index < minimum.
+class MBoundsCheckLower
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ int32_t minimum_;
+ bool fallible_;
+
+ explicit MBoundsCheckLower(MDefinition* index)
+ : MUnaryInstruction(classOpcode, index), minimum_(0), fallible_(true)
+ {
+ setGuard();
+ setMovable();
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(BoundsCheckLower)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, index))
+
+ int32_t minimum() const {
+ return minimum_;
+ }
+ void setMinimum(int32_t n) {
+ minimum_ = n;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool fallible() const {
+ return fallible_;
+ }
+ void collectRangeInfoPreTrunc() override;
+};
+
+// Passes through an object, after ensuring it is converted from an unboxed
+// object to a native representation.
+class MConvertUnboxedObjectToNative
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ CompilerObjectGroup group_;
+
+ explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group)
+ : MUnaryInstruction(classOpcode, obj),
+ group_(group)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ConvertUnboxedObjectToNative)
+ NAMED_OPERANDS((0, object))
+
+ static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj,
+ ObjectGroup* group);
+
+ ObjectGroup* group() const {
+ return group_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ return ins->toConvertUnboxedObjectToNative()->group() == group();
+ }
+ AliasSet getAliasSet() const override {
+ // This instruction can read and write to all parts of the object, but
+ // is marked as non-effectful so it can be consolidated by LICM and GVN
+ // and avoid inhibiting other optimizations.
+ //
+ // This is valid to do because when unboxed objects might have a native
+ // group they can be converted to, we do not optimize accesses to the
+ // unboxed objects and do not guard on their group or shape (other than
+ // in this opcode).
+ //
+ // Later accesses can assume the object has a native representation
+ // and optimize accordingly. Those accesses cannot be reordered before
+ // this instruction, however. This is prevented by chaining this
+ // instruction with the object itself, in the same way as MBoundsCheck.
+ return AliasSet::None();
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(group_);
+ }
+};
+
+// Array.prototype.pop or Array.prototype.shift on a dense array.
+class MArrayPopShift
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ public:
+ enum Mode {
+ Pop,
+ Shift
+ };
+
+ private:
+ Mode mode_;
+ bool needsHoleCheck_;
+ bool maybeUndefined_;
+
+ MArrayPopShift(MDefinition* object, Mode mode,
+ bool needsHoleCheck, bool maybeUndefined)
+ : MUnaryInstruction(classOpcode, object), mode_(mode),
+ needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(ArrayPopShift)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool needsHoleCheck() const {
+ return needsHoleCheck_;
+ }
+ bool maybeUndefined() const {
+ return maybeUndefined_;
+ }
+ bool mode() const {
+ return mode_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
+ }
+
+ ALLOW_CLONE(MArrayPopShift)
+};
+
+// Load a value fallibly or infallibly from a statically known typed array.
+class MLoadTypedArrayElementStatic
+ : public MUnaryInstruction,
+ public ConvertToInt32Policy<0>::Data
+{
+ MLoadTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr,
+ int32_t offset = 0, bool needsBoundsCheck = true)
+ : MUnaryInstruction(classOpcode, ptr), someTypedArray_(someTypedArray), offset_(offset),
+ needsBoundsCheck_(needsBoundsCheck), fallible_(true)
+ {
+ int type = accessType();
+ if (type == Scalar::Float32)
+ setResultType(MIRType::Float32);
+ else if (type == Scalar::Float64)
+ setResultType(MIRType::Double);
+ else
+ setResultType(MIRType::Int32);
+ }
+
+ CompilerObject someTypedArray_;
+
+ // An offset to be encoded in the load instruction - taking advantage of the
+ // addressing modes. This is only non-zero when the access is proven to be
+ // within bounds.
+ int32_t offset_;
+ bool needsBoundsCheck_;
+ bool fallible_;
+
+ public:
+ INSTRUCTION_HEADER(LoadTypedArrayElementStatic)
+ TRIVIAL_NEW_WRAPPERS
+
+ Scalar::Type accessType() const {
+ return someTypedArray_->as<TypedArrayObject>().type();
+ }
+ SharedMem<void*> base() const;
+ size_t length() const;
+
+ MDefinition* ptr() const { return getOperand(0); }
+ int32_t offset() const { return offset_; }
+ void setOffset(int32_t offset) { offset_ = offset; }
+ bool congruentTo(const MDefinition* ins) const override;
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::UnboxedElement);
+ }
+
+ bool needsBoundsCheck() const { return needsBoundsCheck_; }
+ void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; }
+
+ bool fallible() const {
+ return fallible_;
+ }
+
+ void setInfallible() {
+ fallible_ = false;
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+ bool needTruncation(TruncateKind kind) override;
+ bool canProduceFloat32() const override { return accessType() == Scalar::Float32; }
+ void collectRangeInfoPreTrunc() override;
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(someTypedArray_);
+ }
+};
+
+// Clamp input to range [0, 255] for Uint8ClampedArray.
+class MClampToUint8
+ : public MUnaryInstruction,
+ public ClampPolicy::Data
+{
+ explicit MClampToUint8(MDefinition* input)
+ : MUnaryInstruction(classOpcode, input)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(ClampToUint8)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MClampToUint8)
+};
+
+class MLoadFixedSlot
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ size_t slot_;
+
+ protected:
+ MLoadFixedSlot(MDefinition* obj, size_t slot)
+ : MUnaryInstruction(classOpcode, obj), slot_(slot)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadFixedSlot)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ size_t slot() const {
+ return slot_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadFixedSlot())
+ return false;
+ if (slot() != ins->toLoadFixedSlot()->slot())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::FixedSlot);
+ }
+
+ AliasType mightAlias(const MDefinition* store) const override;
+
+ ALLOW_CLONE(MLoadFixedSlot)
+};
+
+class MLoadFixedSlotAndUnbox
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ size_t slot_;
+ MUnbox::Mode mode_;
+ BailoutKind bailoutKind_;
+ protected:
+ MLoadFixedSlotAndUnbox(MDefinition* obj, size_t slot, MUnbox::Mode mode, MIRType type,
+ BailoutKind kind)
+ : MUnaryInstruction(classOpcode, obj), slot_(slot), mode_(mode), bailoutKind_(kind)
+ {
+ setResultType(type);
+ setMovable();
+ if (mode_ == MUnbox::TypeBarrier || mode_ == MUnbox::Fallible)
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadFixedSlotAndUnbox)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ size_t slot() const {
+ return slot_;
+ }
+ MUnbox::Mode mode() const {
+ return mode_;
+ }
+ BailoutKind bailoutKind() const {
+ return bailoutKind_;
+ }
+ bool fallible() const {
+ return mode_ != MUnbox::Infallible;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadFixedSlotAndUnbox() ||
+ slot() != ins->toLoadFixedSlotAndUnbox()->slot() ||
+ mode() != ins->toLoadFixedSlotAndUnbox()->mode())
+ {
+ return false;
+ }
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::FixedSlot);
+ }
+
+ AliasType mightAlias(const MDefinition* store) const override;
+
+ ALLOW_CLONE(MLoadFixedSlotAndUnbox);
+};
+
+class MHomeObjectSuperBase
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MHomeObjectSuperBase(MDefinition* homeObject)
+ : MUnaryInstruction(classOpcode, homeObject)
+ {
+ setResultType(MIRType::Object);
+ setGuard(); // May throw if [[Prototype]] is null
+ }
+
+ public:
+ INSTRUCTION_HEADER(HomeObjectSuperBase)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, homeObject))
+};
+
+// Emit code to load a value from an object if it matches one of the receivers
+// observed by the baseline IC, else bails out.
+class MGetPropertyPolymorphic
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
+ CompilerPropertyName name_;
+
+ MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name)
+ : MUnaryInstruction(classOpcode, obj),
+ receivers_(alloc),
+ name_(name)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetPropertyPolymorphic)
+ NAMED_OPERANDS((0, object))
+
+ static MGetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, PropertyName* name) {
+ return new(alloc) MGetPropertyPolymorphic(alloc, obj, name);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isGetPropertyPolymorphic())
+ return false;
+ if (name() != ins->toGetPropertyPolymorphic()->name())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
+ PolymorphicEntry entry;
+ entry.receiver = receiver;
+ entry.shape = shape;
+ return receivers_.append(entry);
+ }
+ size_t numReceivers() const {
+ return receivers_.length();
+ }
+ const ReceiverGuard receiver(size_t i) const {
+ return receivers_[i].receiver;
+ }
+ Shape* shape(size_t i) const {
+ return receivers_[i].shape;
+ }
+ PropertyName* name() const {
+ return name_;
+ }
+ AliasSet getAliasSet() const override {
+ bool hasUnboxedLoad = false;
+ for (size_t i = 0; i < numReceivers(); i++) {
+ if (!shape(i)) {
+ hasUnboxedLoad = true;
+ break;
+ }
+ }
+ return AliasSet::Load(AliasSet::ObjectFields |
+ AliasSet::FixedSlot |
+ AliasSet::DynamicSlot |
+ (hasUnboxedLoad ? AliasSet::UnboxedElement : 0));
+ }
+
+ AliasType mightAlias(const MDefinition* store) const override;
+
+ bool appendRoots(MRootList& roots) const override;
+};
+
+class MBindNameCache
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ CompilerPropertyName name_;
+ CompilerScript script_;
+ jsbytecode* pc_;
+
+ MBindNameCache(MDefinition* envChain, PropertyName* name, JSScript* script, jsbytecode* pc)
+ : MUnaryInstruction(classOpcode, envChain), name_(name), script_(script), pc_(pc)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(BindNameCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, environmentChain))
+
+ PropertyName* name() const {
+ return name_;
+ }
+ JSScript* script() const {
+ return script_;
+ }
+ jsbytecode* pc() const {
+ return pc_;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ // Don't append the script, all scripts are added anyway.
+ return roots.append(name_);
+ }
+};
+
+class MCallBindVar
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MCallBindVar(MDefinition* envChain)
+ : MUnaryInstruction(classOpcode, envChain)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallBindVar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, environmentChain))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isCallBindVar())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Guard on an object's shape.
+class MGuardShape
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ CompilerShape shape_;
+ BailoutKind bailoutKind_;
+
+ MGuardShape(MDefinition* obj, Shape* shape, BailoutKind bailoutKind)
+ : MUnaryInstruction(classOpcode, obj),
+ shape_(shape),
+ bailoutKind_(bailoutKind)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ setResultTypeSet(obj->resultTypeSet());
+
+ // Disallow guarding on unboxed object shapes. The group is better to
+ // guard on, and guarding on the shape can interact badly with
+ // MConvertUnboxedObjectToNative.
+ MOZ_ASSERT(shape->getObjectClass() != &UnboxedPlainObject::class_);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardShape)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ const Shape* shape() const {
+ return shape_;
+ }
+ BailoutKind bailoutKind() const {
+ return bailoutKind_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isGuardShape())
+ return false;
+ if (shape() != ins->toGuardShape()->shape())
+ return false;
+ if (bailoutKind() != ins->toGuardShape()->bailoutKind())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(shape_);
+ }
+};
+
+// Bail if the object's shape or unboxed group is not in the input list.
+class MGuardReceiverPolymorphic
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ Vector<ReceiverGuard, 4, JitAllocPolicy> receivers_;
+
+ MGuardReceiverPolymorphic(TempAllocator& alloc, MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj),
+ receivers_(alloc)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ setResultTypeSet(obj->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardReceiverPolymorphic)
+ NAMED_OPERANDS((0, object))
+
+ static MGuardReceiverPolymorphic* New(TempAllocator& alloc, MDefinition* obj) {
+ return new(alloc) MGuardReceiverPolymorphic(alloc, obj);
+ }
+
+ MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver) {
+ return receivers_.append(receiver);
+ }
+ size_t numReceivers() const {
+ return receivers_.length();
+ }
+ const ReceiverGuard& receiver(size_t i) const {
+ return receivers_[i];
+ }
+
+ bool congruentTo(const MDefinition* ins) const override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ bool appendRoots(MRootList& roots) const override;
+
+};
+
+// Guard on an object's group, inclusively or exclusively.
+class MGuardObjectGroup
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ CompilerObjectGroup group_;
+ bool bailOnEquality_;
+ BailoutKind bailoutKind_;
+
+ MGuardObjectGroup(MDefinition* obj, ObjectGroup* group, bool bailOnEquality,
+ BailoutKind bailoutKind)
+ : MUnaryInstruction(classOpcode, obj),
+ group_(group),
+ bailOnEquality_(bailOnEquality),
+ bailoutKind_(bailoutKind)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+
+ // Unboxed groups which might be converted to natives can't be guarded
+ // on, due to MConvertUnboxedObjectToNative.
+ MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(),
+ !group->unboxedLayoutDontCheckGeneration().nativeGroup());
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardObjectGroup)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ const ObjectGroup* group() const {
+ return group_;
+ }
+ bool bailOnEquality() const {
+ return bailOnEquality_;
+ }
+ BailoutKind bailoutKind() const {
+ return bailoutKind_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isGuardObjectGroup())
+ return false;
+ if (group() != ins->toGuardObjectGroup()->group())
+ return false;
+ if (bailOnEquality() != ins->toGuardObjectGroup()->bailOnEquality())
+ return false;
+ if (bailoutKind() != ins->toGuardObjectGroup()->bailoutKind())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(group_);
+ }
+};
+
+// Guard on an object's class.
+class MGuardClass
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ const Class* class_;
+
+ MGuardClass(MDefinition* obj, const Class* clasp)
+ : MUnaryInstruction(classOpcode, obj),
+ class_(clasp)
+ {
+ setGuard();
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardClass)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ const Class* getClass() const {
+ return class_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isGuardClass())
+ return false;
+ if (getClass() != ins->toGuardClass()->getClass())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+
+ ALLOW_CLONE(MGuardClass)
+};
+
+// Guard on the presence or absence of an unboxed object's expando.
+class MGuardUnboxedExpando
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool requireExpando_;
+ BailoutKind bailoutKind_;
+
+ MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind)
+ : MUnaryInstruction(classOpcode, obj),
+ requireExpando_(requireExpando),
+ bailoutKind_(bailoutKind)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardUnboxedExpando)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool requireExpando() const {
+ return requireExpando_;
+ }
+ BailoutKind bailoutKind() const {
+ return bailoutKind_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando())
+ return false;
+ return true;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+// Load an unboxed plain object's expando.
+class MLoadUnboxedExpando
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ private:
+ explicit MLoadUnboxedExpando(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadUnboxedExpando)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+// Load from vp[slot] (slots that are not inline in an object).
+class MLoadSlot
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ uint32_t slot_;
+
+ MLoadSlot(MDefinition* slots, uint32_t slot)
+ : MUnaryInstruction(classOpcode, slots),
+ slot_(slot)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+ MOZ_ASSERT(slots->type() == MIRType::Slots);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadSlot)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, slots))
+
+ uint32_t slot() const {
+ return slot_;
+ }
+
+ HashNumber valueHash() const override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadSlot())
+ return false;
+ if (slot() != ins->toLoadSlot()->slot())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ MOZ_ASSERT(slots()->type() == MIRType::Slots);
+ return AliasSet::Load(AliasSet::DynamicSlot);
+ }
+ AliasType mightAlias(const MDefinition* store) const override;
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MLoadSlot)
+};
+
+// Inline call to access a function's environment (scope chain).
+class MFunctionEnvironment
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MFunctionEnvironment(MDefinition* function)
+ : MUnaryInstruction(classOpcode, function)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(FunctionEnvironment)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, function))
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ // A function's environment is fixed.
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Allocate a new LexicalEnvironmentObject.
+class MNewLexicalEnvironmentObject
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ CompilerGCPointer<LexicalScope*> scope_;
+
+ MNewLexicalEnvironmentObject(MDefinition* enclosing, LexicalScope* scope)
+ : MUnaryInstruction(classOpcode, enclosing),
+ scope_(scope)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewLexicalEnvironmentObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, enclosing))
+
+ LexicalScope* scope() const {
+ return scope_;
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(scope_);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Allocate a new LexicalEnvironmentObject from existing one
+class MCopyLexicalEnvironmentObject
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool copySlots_;
+
+ MCopyLexicalEnvironmentObject(MDefinition* env, bool copySlots)
+ : MUnaryInstruction(classOpcode, env),
+ copySlots_(copySlots)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CopyLexicalEnvironmentObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, env))
+
+ bool copySlots() const {
+ return copySlots_;
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields |
+ AliasSet::FixedSlot |
+ AliasSet::DynamicSlot);
+ }
+};
+
+class MHomeObject
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MHomeObject(MDefinition* function)
+ : MUnaryInstruction(classOpcode, function)
+ {
+ setResultType(MIRType::Object);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(HomeObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, function))
+
+ // A function's [[HomeObject]] is fixed.
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MGetNameCache
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ private:
+ explicit MGetNameCache(MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetNameCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, envObj))
+};
+
+class MDeleteProperty
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ CompilerPropertyName name_;
+ bool strict_;
+
+ protected:
+ MDeleteProperty(MDefinition* val, PropertyName* name, bool strict)
+ : MUnaryInstruction(classOpcode, val),
+ name_(name),
+ strict_(strict)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(DeleteProperty)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+
+ PropertyName* name() const {
+ return name_;
+ }
+ bool strict() const {
+ return strict_;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MCallGetProperty
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ CompilerPropertyName name_;
+ bool idempotent_;
+
+ MCallGetProperty(MDefinition* value, PropertyName* name)
+ : MUnaryInstruction(classOpcode, value), name_(name),
+ idempotent_(false)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallGetProperty)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+
+ PropertyName* name() const {
+ return name_;
+ }
+
+ // Constructors need to perform a GetProp on the function prototype.
+ // Since getters cannot be set on the prototype, fetching is non-effectful.
+ // The operation may be safely repeated in case of bailout.
+ void setIdempotent() {
+ idempotent_ = true;
+ }
+ AliasSet getAliasSet() const override {
+ if (!idempotent_)
+ return AliasSet::Store(AliasSet::Any);
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MStringLength
+ : public MUnaryInstruction,
+ public StringPolicy<0>::Data
+{
+ explicit MStringLength(MDefinition* string)
+ : MUnaryInstruction(classOpcode, string)
+ {
+ setResultType(MIRType::Int32);
+ setMovable();
+ }
+ public:
+ INSTRUCTION_HEADER(StringLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, string))
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ // The string |length| property is immutable, so there is no
+ // implicit dependency.
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MStringLength)
+};
+
+// Inlined assembly for Math.floor(double | float32) -> int32.
+class MFloor
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ explicit MFloor(MDefinition* num)
+ : MUnaryInstruction(classOpcode, num)
+ {
+ setResultType(MIRType::Int32);
+ specialization_ = MIRType::Double;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Floor)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool isFloat32Commutative() const override {
+ return true;
+ }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ void computeRange(TempAllocator& alloc) override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MFloor)
+};
+
+// Inlined assembly version for Math.ceil(double | float32) -> int32.
+class MCeil
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ explicit MCeil(MDefinition* num)
+ : MUnaryInstruction(classOpcode, num)
+ {
+ setResultType(MIRType::Int32);
+ specialization_ = MIRType::Double;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Ceil)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool isFloat32Commutative() const override {
+ return true;
+ }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ void computeRange(TempAllocator& alloc) override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MCeil)
+};
+
+// Inlined version of Math.round(double | float32) -> int32.
+class MRound
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ explicit MRound(MDefinition* num)
+ : MUnaryInstruction(classOpcode, num)
+ {
+ setResultType(MIRType::Int32);
+ specialization_ = MIRType::Double;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Round)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool isFloat32Commutative() const override {
+ return true;
+ }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MRound)
+};
+
+// NearbyInt rounds the floating-point input to the nearest integer, according
+// to the RoundingMode.
+class MNearbyInt
+ : public MUnaryInstruction,
+ public FloatingPointPolicy<0>::Data
+{
+ RoundingMode roundingMode_;
+
+ explicit MNearbyInt(MDefinition* num, MIRType resultType, RoundingMode roundingMode)
+ : MUnaryInstruction(classOpcode, num),
+ roundingMode_(roundingMode)
+ {
+ MOZ_ASSERT(HasAssemblerSupport(roundingMode));
+
+ MOZ_ASSERT(IsFloatingPointType(resultType));
+ setResultType(resultType);
+ specialization_ = resultType;
+
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(NearbyInt)
+ TRIVIAL_NEW_WRAPPERS
+
+ static bool HasAssemblerSupport(RoundingMode mode) {
+ return Assembler::HasRoundInstruction(mode);
+ }
+
+ RoundingMode roundingMode() const { return roundingMode_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool isFloat32Commutative() const override {
+ return true;
+ }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ return true;
+ }
+#endif
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) &&
+ ins->toNearbyInt()->roundingMode() == roundingMode_;
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+ bool canRecoverOnBailout() const override {
+ switch (roundingMode_) {
+ case RoundingMode::Up:
+ case RoundingMode::Down:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ALLOW_CLONE(MNearbyInt)
+};
+
+class MGetIteratorCache
+ : public MUnaryInstruction,
+ public BoxExceptPolicy<0, MIRType::Object>::Data
+{
+ explicit MGetIteratorCache(MDefinition* val)
+ : MUnaryInstruction(classOpcode, val)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetIteratorCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+};
+
+class MIteratorMore
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MIteratorMore(MDefinition* iter)
+ : MUnaryInstruction(classOpcode, iter)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(IteratorMore)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, iterator))
+
+};
+
+class MIsNoIter
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MIsNoIter(MDefinition* def)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsNoIter)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MIteratorEnd
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MIteratorEnd(MDefinition* iter)
+ : MUnaryInstruction(classOpcode, iter)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(IteratorEnd)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, iterator))
+
+};
+
+// Implementation for instanceof operator with specific rhs.
+class MInstanceOf
+ : public MUnaryInstruction,
+ public InstanceOfPolicy::Data
+{
+ CompilerObject protoObj_;
+
+ MInstanceOf(MDefinition* obj, JSObject* proto)
+ : MUnaryInstruction(classOpcode, obj),
+ protoObj_(proto)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(InstanceOf)
+ TRIVIAL_NEW_WRAPPERS
+
+ JSObject* prototypeObject() {
+ return protoObj_;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(protoObj_);
+ }
+};
+
+// This MIR instruction is used to get an argument from the actual arguments.
+class MGetFrameArgument
+ : public MUnaryInstruction,
+ public IntPolicy<0>::Data
+{
+ bool scriptHasSetArg_;
+
+ MGetFrameArgument(MDefinition* idx, bool scriptHasSetArg)
+ : MUnaryInstruction(classOpcode, idx),
+ scriptHasSetArg_(scriptHasSetArg)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetFrameArgument)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, index))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ // If the script doesn't have any JSOP_SETARG ops, then this instruction is never
+ // aliased.
+ if (scriptHasSetArg_)
+ return AliasSet::Load(AliasSet::FrameArgument);
+ return AliasSet::None();
+ }
+};
+
+// This MIR instruction is used to set an argument value in the frame.
+class MSetFrameArgument
+ : public MUnaryInstruction,
+ public NoFloatPolicy<0>::Data
+{
+ uint32_t argno_;
+
+ MSetFrameArgument(uint32_t argno, MDefinition* value)
+ : MUnaryInstruction(classOpcode, value),
+ argno_(argno)
+ {
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetFrameArgument)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+
+ uint32_t argno() const {
+ return argno_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return false;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::FrameArgument);
+ }
+};
+
+class MRest
+ : public MUnaryInstruction,
+ public MRestCommon,
+ public IntPolicy<0>::Data
+{
+ MRest(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* numActuals,
+ unsigned numFormals, ArrayObject* templateObject)
+ : MUnaryInstruction(classOpcode, numActuals),
+ MRestCommon(numFormals, templateObject)
+ {
+ setResultType(MIRType::Object);
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+ }
+
+ public:
+ INSTRUCTION_HEADER(Rest)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+ NAMED_OPERANDS((0, numActuals))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObject());
+ }
+};
+
+class MFilterTypeSet
+ : public MUnaryInstruction,
+ public FilterTypeSetPolicy::Data
+{
+ MFilterTypeSet(MDefinition* def, TemporaryTypeSet* types)
+ : MUnaryInstruction(classOpcode, def)
+ {
+ MOZ_ASSERT(!types->unknown());
+ setResultType(types->getKnownMIRType());
+ setResultTypeSet(types);
+ }
+
+ public:
+ INSTRUCTION_HEADER(FilterTypeSet)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* def) const override {
+ return false;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ virtual bool neverHoist() const override {
+ return resultTypeSet()->empty();
+ }
+ void computeRange(TempAllocator& alloc) override;
+
+ bool isFloat32Commutative() const override {
+ return IsFloatingPointType(type());
+ }
+
+ bool canProduceFloat32() const override;
+ bool canConsumeFloat32(MUse* operand) const override;
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+};
+
+// Given a value, guard that the value is in a particular TypeSet, then returns
+// that value.
+class MTypeBarrier
+ : public MUnaryInstruction,
+ public TypeBarrierPolicy::Data
+{
+ BarrierKind barrierKind_;
+
+ MTypeBarrier(MDefinition* def, TemporaryTypeSet* types,
+ BarrierKind kind = BarrierKind::TypeSet)
+ : MUnaryInstruction(classOpcode, def),
+ barrierKind_(kind)
+ {
+ MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
+
+ MOZ_ASSERT(!types->unknown());
+ setResultType(types->getKnownMIRType());
+ setResultTypeSet(types);
+
+ setGuard();
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(TypeBarrier)
+ TRIVIAL_NEW_WRAPPERS
+
+ void printOpcode(GenericPrinter& out) const override;
+ bool congruentTo(const MDefinition* def) const override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ virtual bool neverHoist() const override {
+ return resultTypeSet()->empty();
+ }
+ BarrierKind barrierKind() const {
+ return barrierKind_;
+ }
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ bool alwaysBails() const {
+ // If mirtype of input doesn't agree with mirtype of barrier,
+ // we will definitely bail.
+ MIRType type = resultTypeSet()->getKnownMIRType();
+ if (type == MIRType::Value)
+ return false;
+ if (input()->type() == MIRType::Value)
+ return false;
+ if (input()->type() == MIRType::ObjectOrNull) {
+ // The ObjectOrNull optimization is only performed when the
+ // barrier's type is MIRType::Null.
+ MOZ_ASSERT(type == MIRType::Null);
+ return false;
+ }
+ return input()->type() != type;
+ }
+
+ ALLOW_CLONE(MTypeBarrier)
+};
+
+// Like MTypeBarrier, guard that the value is in the given type set. This is
+// used before property writes to ensure the value being written is represented
+// in the property types for the object.
+class MMonitorTypes
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ const TemporaryTypeSet* typeSet_;
+ BarrierKind barrierKind_;
+
+ MMonitorTypes(MDefinition* def, const TemporaryTypeSet* types, BarrierKind kind)
+ : MUnaryInstruction(classOpcode, def),
+ typeSet_(types),
+ barrierKind_(kind)
+ {
+ MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
+
+ setGuard();
+ MOZ_ASSERT(!types->unknown());
+ }
+
+ public:
+ INSTRUCTION_HEADER(MonitorTypes)
+ TRIVIAL_NEW_WRAPPERS
+
+ const TemporaryTypeSet* typeSet() const {
+ return typeSet_;
+ }
+ BarrierKind barrierKind() const {
+ return barrierKind_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MNewCallObjectBase : public MUnaryInstruction
+ , public SingleObjectPolicy::Data
+{
+ protected:
+ MNewCallObjectBase(Opcode op, MConstant* templateObj)
+ : MUnaryInstruction(op, templateObj)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ CallObject* templateObject() const {
+ return &getOperand(0)->toConstant()->toObject().as<CallObject>();
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MIsCallable
+ : public MUnaryInstruction,
+ public BoxExceptPolicy<0, MIRType::Object>::Data
+{
+ explicit MIsCallable(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ MOZ_ASSERT(object->type() == MIRType::Object || object->type() == MIRType::Value);
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsCallable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MIsConstructor
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ public:
+ explicit MIsConstructor(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsConstructor)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MIsObject
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MIsObject(MDefinition* object)
+ : MUnaryInstruction(classOpcode, object)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+ public:
+ INSTRUCTION_HEADER(IsObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MHasClass
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ const Class* class_;
+
+ MHasClass(MDefinition* object, const Class* clasp)
+ : MUnaryInstruction(classOpcode, object)
+ , class_(clasp)
+ {
+ MOZ_ASSERT(object->type() == MIRType::Object ||
+ (object->type() == MIRType::Value && object->mightBeType(MIRType::Object)));
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(HasClass)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ const Class* getClass() const {
+ return class_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isHasClass())
+ return false;
+ if (getClass() != ins->toHasClass()->getClass())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+};
+
+// Note: we might call a proxy trap, so this instruction is effectful.
+class MIsArray
+ : public MUnaryInstruction,
+ public BoxExceptPolicy<0, MIRType::Object>::Data
+{
+ explicit MIsArray(MDefinition* value)
+ : MUnaryInstruction(classOpcode, value)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+};
+
+class MIsTypedArray
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MIsTypedArray(MDefinition* value)
+ : MUnaryInstruction(classOpcode, value)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsTypedArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MObjectClassToString
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MObjectClassToString(MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ObjectClassToString)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+};
+
+class MAtomicIsLockFree
+ : public MUnaryInstruction,
+ public ConvertToInt32Policy<0>::Data
+{
+ explicit MAtomicIsLockFree(MDefinition* value)
+ : MUnaryInstruction(classOpcode, value)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(AtomicIsLockFree)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MAtomicIsLockFree)
+};
+
+// This applies to an object that is known to be a TypedArray, it bails out
+// if the obj does not map a SharedArrayBuffer.
+
+class MGuardSharedTypedArray
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MGuardSharedTypedArray(MDefinition* obj)
+ : MUnaryInstruction(classOpcode, obj)
+ {
+ setGuard();
+ setMovable();
+ }
+
+public:
+ INSTRUCTION_HEADER(GuardSharedTypedArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MCheckIsObj
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ uint8_t checkKind_;
+
+ MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(classOpcode, toCheck),
+ checkKind_(checkKind)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckIsObj)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+
+ uint8_t checkKind() const { return checkKind_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MCheckIsCallable
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ uint8_t checkKind_;
+
+ MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(classOpcode, toCheck),
+ checkKind_(checkKind)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckIsCallable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+
+ uint8_t checkKind() const { return checkKind_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MCheckObjCoercible
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MCheckObjCoercible(MDefinition* toCheck)
+ : MUnaryInstruction(classOpcode, toCheck)
+ {
+ setGuard();
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckObjCoercible)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+};
+
+class MDebugCheckSelfHosted
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MDebugCheckSelfHosted(MDefinition* toCheck)
+ : MUnaryInstruction(classOpcode, toCheck)
+ {
+ setGuard();
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(DebugCheckSelfHosted)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+
+};
+
+class MIsPackedArray
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MIsPackedArray(MDefinition* array)
+ : MUnaryInstruction(classOpcode, array)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(IsPackedArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, array))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+class MGetPrototypeOf
+ : public MUnaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ explicit MGetPrototypeOf(MDefinition* target)
+ : MUnaryInstruction(classOpcode, target)
+ {
+ setResultType(MIRType::Value);
+ setGuard(); // May throw if target is a proxy.
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetPrototypeOf)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, target))
+};
+
+// Flips the input's sign bit, independently of the rest of the number's
+// payload. Note this is different from multiplying by minus-one, which has
+// side-effects for e.g. NaNs.
+class MWasmNeg
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmNeg(MDefinition* op, MIRType type)
+ : MUnaryInstruction(classOpcode, op)
+ {
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmNeg)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MWasmLoadTls
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ uint32_t offset_;
+ AliasSet aliases_;
+
+ explicit MWasmLoadTls(MDefinition* tlsPointer, uint32_t offset, MIRType type, AliasSet aliases)
+ : MUnaryInstruction(classOpcode, tlsPointer),
+ offset_(offset),
+ aliases_(aliases)
+ {
+ // Different Tls data have different alias classes and only those classes are allowed.
+ MOZ_ASSERT(aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
+ aliases_.flags() == AliasSet::None().flags());
+
+ // The only types supported at the moment.
+ MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32);
+
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmLoadTls)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, tlsPtr))
+
+ uint32_t offset() const {
+ return offset_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return op() == ins->op() &&
+ offset() == ins->toWasmLoadTls()->offset() &&
+ type() == ins->type();
+ }
+
+ HashNumber valueHash() const override {
+ return addU32ToHash(HashNumber(op()), offset());
+ }
+
+ AliasSet getAliasSet() const override {
+ return aliases_;
+ }
+};
+
+class MWasmAddOffset
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ uint32_t offset_;
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ MWasmAddOffset(MDefinition* base, uint32_t offset, wasm::BytecodeOffset bytecodeOffset)
+ : MUnaryInstruction(classOpcode, base),
+ offset_(offset),
+ bytecodeOffset_(bytecodeOffset)
+ {
+ setGuard();
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmAddOffset)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, base))
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ uint32_t offset() const {
+ return offset_;
+ }
+ wasm::BytecodeOffset bytecodeOffset() const {
+ return bytecodeOffset_;
+ }
+};
+
+class MWasmLoadGlobalVar
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant, MDefinition* tlsPtr)
+ : MUnaryInstruction(classOpcode, tlsPtr),
+ globalDataOffset_(globalDataOffset), isConstant_(isConstant)
+ {
+ MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
+ setResultType(type);
+ setMovable();
+ }
+
+ unsigned globalDataOffset_;
+ bool isConstant_;
+
+ public:
+ INSTRUCTION_HEADER(WasmLoadGlobalVar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, tlsPtr))
+
+ unsigned globalDataOffset() const { return globalDataOffset_; }
+
+ HashNumber valueHash() const override;
+ bool congruentTo(const MDefinition* ins) const override;
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ AliasSet getAliasSet() const override {
+ return isConstant_ ? AliasSet::None() : AliasSet::Load(AliasSet::WasmGlobalVar);
+ }
+
+ AliasType mightAlias(const MDefinition* def) const override;
+};
+
+class MWasmStackArg
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmStackArg(uint32_t spOffset, MDefinition* ins)
+ : MUnaryInstruction(classOpcode, ins),
+ spOffset_(spOffset)
+ {}
+
+ uint32_t spOffset_;
+
+ public:
+ INSTRUCTION_HEADER(WasmStackArg)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, arg))
+
+ uint32_t spOffset() const {
+ return spOffset_;
+ }
+ void incrementOffset(uint32_t inc) {
+ spOffset_ += inc;
+ }
+};
+
+class MWasmReinterpret
+ : public MUnaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmReinterpret(MDefinition* val, MIRType toType)
+ : MUnaryInstruction(classOpcode, val)
+ {
+ switch (val->type()) {
+ case MIRType::Int32: MOZ_ASSERT(toType == MIRType::Float32); break;
+ case MIRType::Float32: MOZ_ASSERT(toType == MIRType::Int32); break;
+ case MIRType::Double: MOZ_ASSERT(toType == MIRType::Int64); break;
+ case MIRType::Int64: MOZ_ASSERT(toType == MIRType::Double); break;
+ default: MOZ_CRASH("unexpected reinterpret conversion");
+ }
+ setMovable();
+ setResultType(toType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmReinterpret)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ ALLOW_CLONE(MWasmReinterpret)
+};
+
+// Store a value infallibly to a statically known typed array.
+class MStoreTypedArrayElementStatic :
+ public MBinaryInstruction,
+ public StoreUnboxedScalarBase,
+ public StoreTypedArrayElementStaticPolicy::Data
+{
+ MStoreTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr, MDefinition* v,
+ int32_t offset = 0, bool needsBoundsCheck = true)
+ : MBinaryInstruction(classOpcode, ptr, v),
+ StoreUnboxedScalarBase(someTypedArray->as<TypedArrayObject>().type()),
+ someTypedArray_(someTypedArray),
+ offset_(offset), needsBoundsCheck_(needsBoundsCheck)
+ {}
+
+ CompilerObject someTypedArray_;
+
+ // An offset to be encoded in the store instruction - taking advantage of the
+ // addressing modes. This is only non-zero when the access is proven to be
+ // within bounds.
+ int32_t offset_;
+ bool needsBoundsCheck_;
+
+ public:
+ INSTRUCTION_HEADER(StoreTypedArrayElementStatic)
+ TRIVIAL_NEW_WRAPPERS
+
+ Scalar::Type accessType() const {
+ return writeType();
+ }
+
+ SharedMem<void*> base() const;
+ size_t length() const;
+
+ MDefinition* ptr() const { return getOperand(0); }
+ MDefinition* value() const { return getOperand(1); }
+ bool needsBoundsCheck() const { return needsBoundsCheck_; }
+ void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; }
+ int32_t offset() const { return offset_; }
+ void setOffset(int32_t offset) { offset_ = offset; }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+ TruncateKind operandTruncateKind(size_t index) const override;
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return use == getUseFor(1) && accessType() == Scalar::Float32;
+ }
+ void collectRangeInfoPreTrunc() override;
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(someTypedArray_);
+ }
+};
+
+// Setting __proto__ in an object literal.
+class MMutateProto
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
+{
+ protected:
+ MMutateProto(MDefinition* obj, MDefinition* value)
+ : MBinaryInstruction(classOpcode, obj, value)
+ {
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(MutateProto)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getObject), (1, getValue))
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MInitPropGetterSetter
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
+{
+ CompilerPropertyName name_;
+
+ MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value)
+ : MBinaryInstruction(classOpcode, obj, value),
+ name_(name)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(InitPropGetterSetter)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, value))
+
+ PropertyName* name() const {
+ return name_;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MGetDynamicName
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >::Data
+{
+ protected:
+ MGetDynamicName(MDefinition* envChain, MDefinition* name)
+ : MBinaryInstruction(classOpcode, envChain, name)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetDynamicName)
+ NAMED_OPERANDS((0, getEnvironmentChain), (1, getName))
+
+ static MGetDynamicName*
+ New(TempAllocator& alloc, MDefinition* envChain, MDefinition* name) {
+ return new(alloc) MGetDynamicName(envChain, name);
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MCompare
+ : public MBinaryInstruction,
+ public ComparePolicy::Data
+{
+ 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
+ // Symbol compared to Boolean
+ // Object compared to Boolean
+ // Value compared to Boolean
+ Compare_Boolean,
+
+ // Int32 compared to Int32
+ // Boolean compared to Boolean
+ Compare_Int32,
+ Compare_Int32MaybeCoerceBoth,
+ Compare_Int32MaybeCoerceLHS,
+ Compare_Int32MaybeCoerceRHS,
+
+ // Int32 compared as unsigneds
+ Compare_UInt32,
+
+ // Int64 compared to Int64.
+ Compare_Int64,
+
+ // Int64 compared as unsigneds.
+ Compare_UInt64,
+
+ // Double compared to Double
+ Compare_Double,
+
+ Compare_DoubleMaybeCoerceLHS,
+ Compare_DoubleMaybeCoerceRHS,
+
+ // Float compared to Float
+ Compare_Float32,
+
+ // String compared to String
+ Compare_String,
+
+ // Symbol compared to Symbol
+ Compare_Symbol,
+
+ // 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_Bitwise,
+
+ // All other possible compares
+ Compare_Unknown
+ };
+
+ private:
+ CompareType compareType_;
+ JSOp jsop_;
+ bool operandMightEmulateUndefined_;
+ bool operandsAreNeverNaN_;
+
+ // When a floating-point comparison is converted to an integer comparison
+ // (when range analysis proves it safe), we need to convert the operands
+ // to integer as well.
+ bool truncateOperands_;
+
+ MCompare(MDefinition* left, MDefinition* right, JSOp jsop)
+ : MBinaryInstruction(classOpcode, left, right),
+ compareType_(Compare_Unknown),
+ jsop_(jsop),
+ operandMightEmulateUndefined_(true),
+ operandsAreNeverNaN_(false),
+ truncateOperands_(false)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ }
+
+ MCompare(MDefinition* left, MDefinition* right, JSOp jsop, CompareType compareType)
+ : MCompare(left, right, jsop)
+ {
+ MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
+ compareType == Compare_Int64 || compareType == Compare_UInt64 ||
+ compareType == Compare_Double || compareType == Compare_Float32);
+ compareType_ = compareType;
+ operandMightEmulateUndefined_ = false;
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Compare)
+ TRIVIAL_NEW_WRAPPERS
+
+ MOZ_MUST_USE bool tryFold(bool* result);
+ MOZ_MUST_USE bool evaluateConstantOperands(TempAllocator& alloc, bool* result);
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
+ bool* filtersNull);
+
+ CompareType compareType() const {
+ return compareType_;
+ }
+ bool isInt32Comparison() const {
+ return compareType() == Compare_Int32 ||
+ compareType() == Compare_Int32MaybeCoerceBoth ||
+ compareType() == Compare_Int32MaybeCoerceLHS ||
+ compareType() == Compare_Int32MaybeCoerceRHS;
+ }
+ bool isDoubleComparison() const {
+ return compareType() == Compare_Double ||
+ compareType() == Compare_DoubleMaybeCoerceLHS ||
+ compareType() == Compare_DoubleMaybeCoerceRHS;
+ }
+ bool isFloat32Comparison() const {
+ return compareType() == Compare_Float32;
+ }
+ bool isNumericComparison() const {
+ return isInt32Comparison() ||
+ isDoubleComparison() ||
+ isFloat32Comparison();
+ }
+ void setCompareType(CompareType type) {
+ compareType_ = type;
+ }
+ MIRType inputType();
+
+ JSOp jsop() const {
+ return jsop_;
+ }
+ void markNoOperandEmulatesUndefined() {
+ operandMightEmulateUndefined_ = false;
+ }
+ bool operandMightEmulateUndefined() const {
+ return operandMightEmulateUndefined_;
+ }
+ bool operandsAreNeverNaN() const {
+ return operandsAreNeverNaN_;
+ }
+ AliasSet getAliasSet() const override {
+ // Strict equality is never effectful.
+ if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
+ return AliasSet::None();
+ if (compareType_ == Compare_Unknown)
+ return AliasSet::Store(AliasSet::Any);
+ MOZ_ASSERT(compareType_ <= Compare_Bitwise);
+ return AliasSet::None();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+ void collectRangeInfoPreTrunc() override;
+
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+ bool isFloat32Commutative() const override { return true; }
+ bool needTruncation(TruncateKind kind) override;
+ void truncate() override;
+ TruncateKind operandTruncateKind(size_t index) const override;
+
+ static CompareType determineCompareType(JSOp op, MDefinition* left, MDefinition* right);
+ void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
+
+# ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ // Both sides of the compare can be Float32
+ return compareType_ == Compare_Float32;
+ }
+# endif
+
+ ALLOW_CLONE(MCompare)
+
+ protected:
+ MOZ_MUST_USE bool tryFoldEqualOperands(bool* result);
+ MOZ_MUST_USE bool tryFoldTypeOf(bool* result);
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ return compareType() == ins->toCompare()->compareType() &&
+ jsop() == ins->toCompare()->jsop();
+ }
+};
+
+// Caller-side allocation of |this| for |new|:
+// Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING).
+class MCreateThis
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
+{
+ explicit MCreateThis(MDefinition* callee, MDefinition* newTarget)
+ : MBinaryInstruction(classOpcode, callee, newTarget)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CreateThis)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getCallee), (1, getNewTarget))
+
+ // Although creation of |this| modifies global state, it is safely repeatable.
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MSetArgumentsObjectArg
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
+{
+ size_t argno_;
+
+ MSetArgumentsObjectArg(MDefinition* argsObj, size_t argno, MDefinition* value)
+ : MBinaryInstruction(classOpcode, argsObj, value),
+ argno_(argno)
+ {
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetArgumentsObjectArg)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getArgsObject), (1, getValue))
+
+ size_t argno() const {
+ return argno_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::Any);
+ }
+};
+
+// 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 MBinaryInstruction,
+ public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
+{
+ MReturnFromCtor(MDefinition* value, MDefinition* object)
+ : MBinaryInstruction(classOpcode, value, object)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ReturnFromCtor)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getValue), (1, getObject))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MBinaryBitwiseInstruction
+ : public MBinaryInstruction,
+ public BitwisePolicy::Data
+{
+ protected:
+ MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right, MIRType type)
+ : MBinaryInstruction(op, left, right), maskMatchesLeftRange(false),
+ maskMatchesRightRange(false)
+ {
+ MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
+ setResultType(type);
+ setMovable();
+ }
+
+ void specializeAs(MIRType type);
+ bool maskMatchesLeftRange;
+ bool maskMatchesRightRange;
+
+ public:
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ MDefinition* foldUnnecessaryBitop();
+ virtual MDefinition* foldIfZero(size_t operand) = 0;
+ virtual MDefinition* foldIfNegOne(size_t operand) = 0;
+ virtual MDefinition* foldIfEqual() = 0;
+ virtual MDefinition* foldIfAllBitsSet(size_t operand) = 0;
+ virtual void infer(BaselineInspector* inspector, jsbytecode* pc);
+ void collectRangeInfoPreTrunc() override;
+
+ void setInt32Specialization() {
+ specialization_ = MIRType::Int32;
+ setResultType(MIRType::Int32);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return binaryCongruentTo(ins);
+ }
+ AliasSet getAliasSet() const override {
+ if (specialization_ >= MIRType::Object)
+ return AliasSet::Store(AliasSet::Any);
+ return AliasSet::None();
+ }
+
+ TruncateKind operandTruncateKind(size_t index) const override;
+};
+
+class MBinaryArithInstruction
+ : public MBinaryInstruction,
+ public ArithPolicy::Data
+{
+ // Implicit truncate flag is set by the truncate backward range analysis
+ // optimization phase, and by wasm 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.
+ TruncateKind implicitTruncate_;
+
+ // Whether we must preserve NaN semantics, and in particular not fold
+ // (x op id) or (id op x) to x, or replace a division by a multiply of the
+ // exact reciprocal.
+ bool mustPreserveNaN_;
+
+ public:
+ MBinaryArithInstruction(Opcode op, MDefinition* left, MDefinition* right)
+ : MBinaryInstruction(op, left, right),
+ implicitTruncate_(NoTruncate),
+ mustPreserveNaN_(false)
+ {
+ specialization_ = MIRType::None;
+ setMovable();
+ }
+
+ static MBinaryArithInstruction* New(TempAllocator& alloc, Opcode op,
+ MDefinition* left, MDefinition* right);
+
+ bool constantDoubleResult(TempAllocator& alloc);
+
+ void setMustPreserveNaN(bool b) { mustPreserveNaN_ = b; }
+ bool mustPreserveNaN() const { return mustPreserveNaN_; }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void printOpcode(GenericPrinter& out) const override;
+
+ virtual double getIdentity() = 0;
+
+ void setSpecialization(MIRType type) {
+ specialization_ = type;
+ setResultType(type);
+ }
+ void setInt32Specialization() {
+ specialization_ = MIRType::Int32;
+ setResultType(MIRType::Int32);
+ }
+ void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
+
+ virtual void trySpecializeFloat32(TempAllocator& alloc) override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ const auto* other = static_cast<const MBinaryArithInstruction*>(ins);
+ return other->mustPreserveNaN_ == mustPreserveNaN_;
+ }
+ AliasSet getAliasSet() const override {
+ if (specialization_ >= MIRType::Object)
+ return AliasSet::Store(AliasSet::Any);
+ return AliasSet::None();
+ }
+
+ bool isTruncated() const {
+ return implicitTruncate_ == Truncate;
+ }
+ TruncateKind truncateKind() const {
+ return implicitTruncate_;
+ }
+ void setTruncateKind(TruncateKind kind) {
+ implicitTruncate_ = Max(implicitTruncate_, kind);
+ }
+};
+
+class MMinMax
+ : public MBinaryInstruction,
+ public ArithPolicy::Data
+{
+ bool isMax_;
+
+ MMinMax(MDefinition* left, MDefinition* right, MIRType type, bool isMax)
+ : MBinaryInstruction(classOpcode, left, right),
+ isMax_(isMax)
+ {
+ MOZ_ASSERT(IsNumberType(type));
+ setResultType(type);
+ setMovable();
+ specialization_ = type;
+ }
+
+ public:
+ INSTRUCTION_HEADER(MinMax)
+ TRIVIAL_NEW_WRAPPERS
+
+ static MMinMax* NewWasm(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type, bool isMax)
+ {
+ return New(alloc, left, right, type, isMax);
+ }
+
+ bool isMax() const {
+ return isMax_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!congruentIfOperandsEqual(ins))
+ return false;
+ const MMinMax* other = ins->toMinMax();
+ return other->isMax() == isMax();
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ void computeRange(TempAllocator& alloc) override;
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ bool isFloat32Commutative() const override { return true; }
+ void trySpecializeFloat32(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MMinMax)
+};
+
+class MCopySign
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ MCopySign(MDefinition* lhs, MDefinition* rhs, MIRType type)
+ : MBinaryInstruction(classOpcode, lhs, rhs)
+ {
+ setResultType(type);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CopySign)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MCopySign)
+};
+
+// Inline implementation of atan2 (arctangent of y/x).
+class MAtan2
+ : public MBinaryInstruction,
+ public MixPolicy<DoublePolicy<0>, DoublePolicy<1> >::Data
+{
+ MAtan2(MDefinition* y, MDefinition* x)
+ : MBinaryInstruction(classOpcode, y, x)
+ {
+ setResultType(MIRType::Double);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(Atan2)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, y), (1, x))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MAtan2)
+};
+
+// Inline implementation of Math.pow().
+class MPow
+ : public MBinaryInstruction,
+ public PowPolicy::Data
+{
+ MPow(MDefinition* input, MDefinition* power, MIRType powerType)
+ : MBinaryInstruction(classOpcode, input, power)
+ {
+ MOZ_ASSERT(powerType == MIRType::Double ||
+ powerType == MIRType::Int32 ||
+ powerType == MIRType::None);
+ specialization_ = powerType;
+ if (powerType == MIRType::None)
+ setResultType(MIRType::Value);
+ else
+ setResultType(MIRType::Double);
+ setMovable();
+ }
+
+ // Helpers for `foldsTo`
+ MDefinition* foldsConstant(TempAllocator &alloc);
+ MDefinition* foldsConstantPower(TempAllocator &alloc);
+
+ public:
+ INSTRUCTION_HEADER(Pow)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* input() const {
+ return lhs();
+ }
+ MDefinition* power() const {
+ return rhs();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ if (specialization_ == MIRType::None)
+ return AliasSet::Store(AliasSet::Any);
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return specialization_ != MIRType::None;
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MPow)
+};
+
+class MConcat
+ : public MBinaryInstruction,
+ public MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >::Data
+{
+ MConcat(MDefinition* left, MDefinition* right)
+ : MBinaryInstruction(classOpcode, left, right)
+ {
+ // At least one input should be definitely string
+ MOZ_ASSERT(left->type() == MIRType::String || right->type() == MIRType::String);
+
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Concat)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MConcat)
+};
+
+class MCharCodeAt
+ : public MBinaryInstruction,
+ public MixPolicy<StringPolicy<0>, IntPolicy<1> >::Data
+{
+ MCharCodeAt(MDefinition* str, MDefinition* index)
+ : MBinaryInstruction(classOpcode, str, index)
+ {
+ setMovable();
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CharCodeAt)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ // Strings are immutable, so there is no implicit dependency.
+ return AliasSet::None();
+ }
+
+ void computeRange(TempAllocator& alloc) override;
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ ALLOW_CLONE(MCharCodeAt)
+};
+
+class MStringSplit
+ : public MBinaryInstruction,
+ public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
+{
+ CompilerObjectGroup group_;
+
+ MStringSplit(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* string,
+ MDefinition* sep, ObjectGroup* group)
+ : MBinaryInstruction(classOpcode, string, sep),
+ group_(group)
+ {
+ setResultType(MIRType::Object);
+ TemporaryTypeSet* types = MakeSingletonTypeSet(alloc, constraints, group);
+ setResultTypeSet(types);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StringSplit)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+ NAMED_OPERANDS((0, string), (1, separator))
+
+ ObjectGroup* group() const {
+ return group_;
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ virtual AliasSet getAliasSet() const override {
+ // Although this instruction returns a new array, we don't have to mark
+ // it as store instruction, see also MNewArray.
+ return AliasSet::None();
+ }
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(group_);
+ }
+};
+
+// Replaces the datum in the given lane by a scalar value of the same type.
+class MSimdInsertElement
+ : public MBinaryInstruction,
+ public MixPolicy< SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
+{
+ private:
+ unsigned lane_;
+
+ MSimdInsertElement(MDefinition* vec, MDefinition* val, unsigned lane)
+ : MBinaryInstruction(classOpcode, vec, val), lane_(lane)
+ {
+ MIRType type = vec->type();
+ MOZ_ASSERT(IsSimdType(type));
+ MOZ_ASSERT(lane < SimdTypeToLength(type));
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdInsertElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, vector), (1, value))
+
+ unsigned lane() const {
+ return lane_;
+ }
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return use == getUseFor(1) && SimdTypeToLaneType(type()) == MIRType::Float32;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdInsertElement)
+};
+
+// Applies a shuffle operation to the inputs. The lane indexes select a source
+// lane from the concatenation of the two input vectors.
+class MSimdShuffle
+ : public MBinaryInstruction,
+ public MSimdShuffleBase,
+ public NoTypePolicy::Data
+{
+ MSimdShuffle(MDefinition* lhs, MDefinition* rhs, const uint8_t lanes[])
+ : MBinaryInstruction(classOpcode, lhs, rhs), MSimdShuffleBase(lanes, lhs->type())
+ {
+ MOZ_ASSERT(IsSimdType(lhs->type()));
+ MOZ_ASSERT(IsSimdType(rhs->type()));
+ MOZ_ASSERT(lhs->type() == rhs->type());
+ for (unsigned i = 0; i < arity_; i++)
+ MOZ_ASSERT(lane(i) < 2 * arity_);
+ setResultType(lhs->type());
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdShuffle)
+
+ static MInstruction* New(TempAllocator& alloc, MDefinition* lhs, MDefinition* rhs,
+ const uint8_t lanes[])
+ {
+ unsigned arity = SimdTypeToLength(lhs->type());
+
+ // Swap operands so that new lanes come from LHS in majority.
+ // In the balanced case, swap operands if needs be, in order to be able
+ // to do only one vshufps on x86.
+ unsigned lanesFromLHS = 0;
+ for (unsigned i = 0; i < arity; i++) {
+ if (lanes[i] < arity)
+ lanesFromLHS++;
+ }
+
+ if (lanesFromLHS < arity / 2 ||
+ (arity == 4 && lanesFromLHS == 2 && lanes[0] >= 4 && lanes[1] >= 4)) {
+ mozilla::Array<uint8_t, 16> newLanes;
+ for (unsigned i = 0; i < arity; i++)
+ newLanes[i] = (lanes[i] + arity) % (2 * arity);
+ return New(alloc, rhs, lhs, &newLanes[0]);
+ }
+
+ // If all lanes come from the same vector, just use swizzle instead.
+ if (lanesFromLHS == arity)
+ return MSimdSwizzle::New(alloc, lhs, lanes);
+
+ return new(alloc) MSimdShuffle(lhs, rhs, lanes);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isSimdShuffle())
+ return false;
+ const MSimdShuffle* other = ins->toSimdShuffle();
+ return sameLanes(other) && binaryCongruentTo(other);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ ALLOW_CLONE(MSimdShuffle)
+};
+
+// Compares each value of a SIMD vector to each corresponding lane's value of
+// another SIMD vector, and returns a boolean vector containing the results of
+// the comparison: all bits are set to 1 if the comparison is true, 0 otherwise.
+// When comparing integer vectors, a SimdSign must be provided to request signed
+// or unsigned comparison.
+class MSimdBinaryComp
+ : public MBinaryInstruction,
+ public SimdAllPolicy::Data
+{
+ public:
+ enum Operation {
+#define NAME_(x) x,
+ FOREACH_COMP_SIMD_OP(NAME_)
+#undef NAME_
+ };
+
+ static const char* OperationName(Operation op) {
+ switch (op) {
+#define NAME_(x) case x: return #x;
+ FOREACH_COMP_SIMD_OP(NAME_)
+#undef NAME_
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ private:
+ Operation operation_;
+ SimdSign sign_;
+
+ MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
+ : MBinaryInstruction(classOpcode, left, right), operation_(op), sign_(sign)
+ {
+ MOZ_ASSERT(left->type() == right->type());
+ MIRType opType = left->type();
+ MOZ_ASSERT(IsSimdType(opType));
+ MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(opType),
+ "Signedness must be specified for integer SIMD compares");
+ setResultType(MIRTypeToBooleanSimdType(opType));
+ specialization_ = opType;
+ setMovable();
+ if (op == equal || op == notEqual)
+ setCommutative();
+ }
+
+ static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ Operation op, SimdSign sign)
+ {
+ return new (alloc) MSimdBinaryComp(left, right, op, sign);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdBinaryComp)
+
+ // Create a MSimdBinaryComp or an equivalent sequence of instructions
+ // supported by the current target.
+ // Add all instructions to the basic block |addTo|.
+ static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
+ MDefinition* right, Operation op, SimdSign sign);
+
+ AliasSet getAliasSet() const override
+ {
+ return AliasSet::None();
+ }
+
+ Operation operation() const { return operation_; }
+ SimdSign signedness() const { return sign_; }
+ MIRType specialization() const { return specialization_; }
+
+ // Swap the operands and reverse the comparison predicate.
+ void reverse() {
+ switch (operation()) {
+ case greaterThan: operation_ = lessThan; break;
+ case greaterThanOrEqual: operation_ = lessThanOrEqual; break;
+ case lessThan: operation_ = greaterThan; break;
+ case lessThanOrEqual: operation_ = greaterThanOrEqual; break;
+ case equal:
+ case notEqual:
+ break;
+ default: MOZ_CRASH("Unexpected compare operation");
+ }
+ swapOperands();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ const MSimdBinaryComp* other = ins->toSimdBinaryComp();
+ return specialization_ == other->specialization() &&
+ operation_ == other->operation() &&
+ sign_ == other->signedness();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdBinaryComp)
+};
+
+class MSimdBinaryArith
+ : public MBinaryInstruction,
+ public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
+{
+ public:
+ enum Operation {
+#define OP_LIST_(OP) Op_##OP,
+ FOREACH_NUMERIC_SIMD_BINOP(OP_LIST_)
+ FOREACH_FLOAT_SIMD_BINOP(OP_LIST_)
+#undef OP_LIST_
+ };
+
+ static const char* OperationName(Operation op) {
+ switch (op) {
+#define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
+ FOREACH_NUMERIC_SIMD_BINOP(OP_CASE_LIST_)
+ FOREACH_FLOAT_SIMD_BINOP(OP_CASE_LIST_)
+#undef OP_CASE_LIST_
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ private:
+ Operation operation_;
+
+ MSimdBinaryArith(MDefinition* left, MDefinition* right, Operation op)
+ : MBinaryInstruction(classOpcode, left, right), operation_(op)
+ {
+ MOZ_ASSERT(left->type() == right->type());
+ MIRType type = left->type();
+ MOZ_ASSERT(IsSimdType(type));
+ MOZ_ASSERT_IF(IsIntegerSimdType(type), op == Op_add || op == Op_sub || op == Op_mul);
+ setResultType(type);
+ setMovable();
+ if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
+ setCommutative();
+ }
+
+ static MSimdBinaryArith* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ Operation op)
+ {
+ return new (alloc) MSimdBinaryArith(left, right, op);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdBinaryArith)
+
+ // Create an MSimdBinaryArith instruction and add it to the basic block. Possibly
+ // create and add an equivalent sequence of instructions instead if the
+ // current target doesn't support the requested shift operation directly.
+ static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
+ MDefinition* right, Operation op);
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ Operation operation() const { return operation_; }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ return operation_ == ins->toSimdBinaryArith()->operation();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdBinaryArith)
+};
+
+class MSimdBinarySaturating
+ : public MBinaryInstruction,
+ public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1>>::Data
+{
+ public:
+ enum Operation
+ {
+ add,
+ sub,
+ };
+
+ static const char* OperationName(Operation op)
+ {
+ switch (op) {
+ case add:
+ return "add";
+ case sub:
+ return "sub";
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ private:
+ Operation operation_;
+ SimdSign sign_;
+
+ MSimdBinarySaturating(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
+ : MBinaryInstruction(classOpcode, left, right)
+ , operation_(op)
+ , sign_(sign)
+ {
+ MOZ_ASSERT(left->type() == right->type());
+ MIRType type = left->type();
+ MOZ_ASSERT(type == MIRType::Int8x16 || type == MIRType::Int16x8);
+ setResultType(type);
+ setMovable();
+ if (op == add)
+ setCommutative();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdBinarySaturating)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override { return AliasSet::None(); }
+
+ Operation operation() const { return operation_; }
+ SimdSign signedness() const { return sign_; }
+
+ bool congruentTo(const MDefinition* ins) const override
+ {
+ if (!binaryCongruentTo(ins))
+ return false;
+ return operation_ == ins->toSimdBinarySaturating()->operation() &&
+ sign_ == ins->toSimdBinarySaturating()->signedness();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdBinarySaturating)
+};
+
+class MSimdBinaryBitwise
+ : public MBinaryInstruction,
+ public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
+{
+ public:
+ enum Operation {
+ and_,
+ or_,
+ xor_
+ };
+
+ static const char* OperationName(Operation op) {
+ switch (op) {
+ case and_: return "and";
+ case or_: return "or";
+ case xor_: return "xor";
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ private:
+ Operation operation_;
+
+ MSimdBinaryBitwise(MDefinition* left, MDefinition* right, Operation op)
+ : MBinaryInstruction(classOpcode, left, right), operation_(op)
+ {
+ MOZ_ASSERT(left->type() == right->type());
+ MIRType type = left->type();
+ MOZ_ASSERT(IsSimdType(type));
+ setResultType(type);
+ setMovable();
+ setCommutative();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdBinaryBitwise)
+ TRIVIAL_NEW_WRAPPERS
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ Operation operation() const { return operation_; }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ return operation_ == ins->toSimdBinaryBitwise()->operation();
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MSimdBinaryBitwise)
+};
+
+class MSimdShift
+ : public MBinaryInstruction,
+ public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
+{
+ public:
+ enum Operation {
+ lsh,
+ rsh,
+ ursh
+ };
+
+ private:
+ Operation operation_;
+
+ MSimdShift(MDefinition* left, MDefinition* right, Operation op)
+ : MBinaryInstruction(classOpcode, left, right), operation_(op)
+ {
+ MIRType type = left->type();
+ MOZ_ASSERT(IsIntegerSimdType(type));
+ setResultType(type);
+ setMovable();
+ }
+
+ static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ Operation op)
+ {
+ return new (alloc) MSimdShift(left, right, op);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdShift)
+
+ // Create an MSimdShift instruction and add it to the basic block. Possibly
+ // create and add an equivalent sequence of instructions instead if the
+ // current target doesn't support the requested shift operation directly.
+ // Return the inserted MInstruction that computes the shifted value.
+ static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
+ MDefinition* right, Operation op);
+
+ // Get the relevant right shift operation given the signedness of a type.
+ static Operation rshForSign(SimdSign sign) {
+ return sign == SimdSign::Unsigned ? ursh : rsh;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ Operation operation() const { return operation_; }
+
+ static const char* OperationName(Operation op) {
+ switch (op) {
+ case lsh: return "lsh";
+ case rsh: return "rsh-arithmetic";
+ case ursh: return "rsh-logical";
+ }
+ MOZ_CRASH("unexpected operation");
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!binaryCongruentTo(ins))
+ return false;
+ return operation_ == ins->toSimdShift()->operation();
+ }
+
+ ALLOW_CLONE(MSimdShift)
+};
+
+class MBinarySharedStub
+ : public MBinaryInstruction,
+ public MixPolicy<BoxPolicy<0>, BoxPolicy<1> >::Data
+{
+ protected:
+ explicit MBinarySharedStub(MDefinition* left, MDefinition* right)
+ : MBinaryInstruction(classOpcode, left, right)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(BinarySharedStub)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+class MDefFun
+ : public MBinaryInstruction,
+ public ObjectPolicy<0>::Data
+{
+ private:
+ MDefFun(MDefinition* fun, MDefinition* envChain)
+ : MBinaryInstruction(classOpcode, fun, envChain)
+ {}
+
+ public:
+ INSTRUCTION_HEADER(DefFun)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, fun), (1, environmentChain))
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MRegExpInstanceOptimizable
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
+{
+ explicit MRegExpInstanceOptimizable(MDefinition* object, MDefinition* proto)
+ : MBinaryInstruction(classOpcode, object, proto)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExpInstanceOptimizable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, proto))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MLambda
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ const LambdaFunctionInfo info_;
+
+ MLambda(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
+ MConstant* cst)
+ : MBinaryInstruction(classOpcode, envChain, cst),
+ info_(&cst->toObject().as<JSFunction>())
+ {
+ setResultType(MIRType::Object);
+ if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
+ }
+
+ public:
+ INSTRUCTION_HEADER(Lambda)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+ NAMED_OPERANDS((0, environmentChain))
+
+ MConstant* functionOperand() const {
+ return getOperand(1)->toConstant();
+ }
+ const LambdaFunctionInfo& info() const {
+ return info_;
+ }
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return info_.appendRoots(roots);
+ }
+};
+
+class MSetFunName
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
+{
+ uint8_t prefixKind_;
+
+ explicit MSetFunName(MDefinition* fun, MDefinition* name, uint8_t prefixKind)
+ : MBinaryInstruction(classOpcode, fun, name),
+ prefixKind_(prefixKind)
+ {
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetFunName)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, fun), (1, name))
+
+ uint8_t prefixKind() const {
+ return prefixKind_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+// If |elements| has the CONVERT_DOUBLE_ELEMENTS flag, convert value to
+// double. Else return the original value.
+class MMaybeToDoubleElement
+ : public MBinaryInstruction,
+ public IntPolicy<1>::Data
+{
+ MMaybeToDoubleElement(MDefinition* elements, MDefinition* value)
+ : MBinaryInstruction(classOpcode, elements, value)
+ {
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ setMovable();
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(MaybeToDoubleElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, value))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+// Store to the initialized length in an elements header. Note the input is an
+// *index*, one less than the desired length.
+class MSetInitializedLength
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ MSetInitializedLength(MDefinition* elements, MDefinition* index)
+ : MBinaryInstruction(classOpcode, elements, index)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(SetInitializedLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields);
+ }
+
+ ALLOW_CLONE(MSetInitializedLength)
+};
+
+// Store to the length in an elements header. Note the input is an *index*, one
+// less than the desired length.
+class MSetArrayLength
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ MSetArrayLength(MDefinition* elements, MDefinition* index)
+ : MBinaryInstruction(classOpcode, elements, index)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(SetArrayLength)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields);
+ }
+
+ // By default no, unless built as a recovered instruction.
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return isRecoveredOnBailout();
+ }
+};
+
+class MGetNextEntryForIterator
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
+{
+ public:
+ enum Mode {
+ Map,
+ Set
+ };
+
+ private:
+ Mode mode_;
+
+ explicit MGetNextEntryForIterator(MDefinition* iter, MDefinition* result, Mode mode)
+ : MBinaryInstruction(classOpcode, iter, result), mode_(mode)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetNextEntryForIterator)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, iter), (1, result))
+
+ Mode mode() const {
+ return mode_;
+ }
+};
+
+// Inlined version of the js::SetTypedObjectOffset() intrinsic.
+class MSetTypedObjectOffset
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ private:
+ MSetTypedObjectOffset(MDefinition* object, MDefinition* offset)
+ : MBinaryInstruction(classOpcode, object, offset)
+ {
+ MOZ_ASSERT(object->type() == MIRType::Object);
+ MOZ_ASSERT(offset->type() == MIRType::Int32);
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetTypedObjectOffset)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, offset))
+
+ AliasSet getAliasSet() const override {
+ // This affects the result of MTypedObjectElements,
+ // which is described as a load of ObjectFields.
+ return AliasSet::Store(AliasSet::ObjectFields);
+ }
+};
+
+// Bailout if index + minimum < 0 or index + maximum >= length. The length used
+// in a bounds check must not be negative, or the wrong result may be computed
+// (unsigned comparisons may be used).
+class MBoundsCheck
+ : public MBinaryInstruction,
+ public MixPolicy<IntPolicy<0>, IntPolicy<1>>::Data
+{
+ // Range over which to perform the bounds check, may be modified by GVN.
+ int32_t minimum_;
+ int32_t maximum_;
+ bool fallible_;
+
+ MBoundsCheck(MDefinition* index, MDefinition* length)
+ : MBinaryInstruction(classOpcode, index, length),
+ minimum_(0), maximum_(0), fallible_(true)
+ {
+ setGuard();
+ setMovable();
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(length->type() == MIRType::Int32);
+
+ // Returns the checked index.
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(BoundsCheck)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, index), (1, length))
+
+ int32_t minimum() const {
+ return minimum_;
+ }
+ void setMinimum(int32_t n) {
+ MOZ_ASSERT(fallible_);
+ minimum_ = n;
+ }
+ int32_t maximum() const {
+ return maximum_;
+ }
+ void setMaximum(int32_t n) {
+ MOZ_ASSERT(fallible_);
+ maximum_ = n;
+ }
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isBoundsCheck())
+ return false;
+ const MBoundsCheck* other = ins->toBoundsCheck();
+ if (minimum() != other->minimum() || maximum() != other->maximum())
+ return false;
+ if (fallible() != other->fallible())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ void computeRange(TempAllocator& alloc) override;
+ bool fallible() const {
+ return fallible_;
+ }
+ void collectRangeInfoPreTrunc() override;
+
+ ALLOW_CLONE(MBoundsCheck)
+};
+
+// Load a value from a dense array's element vector and does a hole check if the
+// array is not known to be packed.
+class MLoadElement
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool needsHoleCheck_;
+ bool loadDoubles_;
+ int32_t offsetAdjustment_;
+
+ MLoadElement(MDefinition* elements, MDefinition* index,
+ bool needsHoleCheck, bool loadDoubles, int32_t offsetAdjustment = 0)
+ : MBinaryInstruction(classOpcode, elements, index),
+ needsHoleCheck_(needsHoleCheck),
+ loadDoubles_(loadDoubles),
+ offsetAdjustment_(offsetAdjustment)
+ {
+ if (needsHoleCheck) {
+ // Uses may be optimized away based on this instruction's result
+ // type. This means it's invalid to DCE this instruction, as we
+ // have to invalidate when we read a hole.
+ setGuard();
+ }
+ setResultType(MIRType::Value);
+ setMovable();
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ bool needsHoleCheck() const {
+ return needsHoleCheck_;
+ }
+ bool loadDoubles() const {
+ return loadDoubles_;
+ }
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool fallible() const {
+ return needsHoleCheck();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadElement())
+ return false;
+ const MLoadElement* other = ins->toLoadElement();
+ if (needsHoleCheck() != other->needsHoleCheck())
+ return false;
+ if (loadDoubles() != other->loadDoubles())
+ return false;
+ if (offsetAdjustment() != other->offsetAdjustment())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ AliasType mightAlias(const MDefinition* store) const override;
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::Element);
+ }
+
+ ALLOW_CLONE(MLoadElement)
+};
+
+class MLoadUnboxedObjectOrNull
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ public:
+ enum NullBehavior {
+ HandleNull,
+ BailOnNull,
+ NullNotPossible
+ };
+
+ private:
+ NullBehavior nullBehavior_;
+ int32_t offsetAdjustment_;
+
+ MLoadUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
+ NullBehavior nullBehavior, int32_t offsetAdjustment)
+ : MBinaryInstruction(classOpcode, elements, index),
+ nullBehavior_(nullBehavior),
+ offsetAdjustment_(offsetAdjustment)
+ {
+ if (nullBehavior == BailOnNull) {
+ // Don't eliminate loads which bail out on a null pointer, for the
+ // same reason as MLoadElement.
+ setGuard();
+ }
+ setResultType(nullBehavior == HandleNull ? MIRType::Value : MIRType::Object);
+ setMovable();
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadUnboxedObjectOrNull)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ NullBehavior nullBehavior() const {
+ return nullBehavior_;
+ }
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool fallible() const {
+ return nullBehavior() == BailOnNull;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadUnboxedObjectOrNull())
+ return false;
+ const MLoadUnboxedObjectOrNull* other = ins->toLoadUnboxedObjectOrNull();
+ if (nullBehavior() != other->nullBehavior())
+ return false;
+ if (offsetAdjustment() != other->offsetAdjustment())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::UnboxedElement);
+ }
+ AliasType mightAlias(const MDefinition* store) const override;
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MLoadUnboxedObjectOrNull)
+};
+
+class MLoadUnboxedString
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ int32_t offsetAdjustment_;
+
+ MLoadUnboxedString(MDefinition* elements, MDefinition* index, int32_t offsetAdjustment = 0)
+ : MBinaryInstruction(classOpcode, elements, index),
+ offsetAdjustment_(offsetAdjustment)
+ {
+ setResultType(MIRType::String);
+ setMovable();
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadUnboxedString)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadUnboxedString())
+ return false;
+ const MLoadUnboxedString* other = ins->toLoadUnboxedString();
+ if (offsetAdjustment() != other->offsetAdjustment())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::UnboxedElement);
+ }
+
+ ALLOW_CLONE(MLoadUnboxedString)
+};
+
+// This instruction is used to load an element of a non-escaped inlined array.
+class MLoadElementFromState
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ MLoadElementFromState(MDefinition* array, MDefinition* index)
+ : MBinaryInstruction(classOpcode, array, index)
+ {
+ MOZ_ASSERT(array->isArgumentState());
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ setResultType(MIRType::Value);
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadElementFromState)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, array), (1, index));
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+// Array.prototype.push on a dense array. Returns the new array length.
+class MArrayPush
+ : public MBinaryInstruction,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
+{
+ MArrayPush(MDefinition* object, MDefinition* value)
+ : MBinaryInstruction(classOpcode, object, value)
+ {
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ArrayPush)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, value))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
+ }
+ void computeRange(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MArrayPush)
+};
+
+class MArrayJoin
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, StringPolicy<1> >::Data
+{
+ bool optimizeForArray_;
+
+ MArrayJoin(MDefinition* array, MDefinition* sep, bool optimizeForArray)
+ : MBinaryInstruction(classOpcode, array, sep),
+ optimizeForArray_(optimizeForArray)
+ {
+ setResultType(MIRType::String);
+ }
+ public:
+ INSTRUCTION_HEADER(ArrayJoin)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, array), (1, sep))
+
+ bool optimizeForArray() const {
+ return optimizeForArray_;
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ virtual AliasSet getAliasSet() const override {
+ // Array.join might coerce the elements of the Array to strings. This
+ // coercion might cause the evaluation of the some JavaScript code.
+ return AliasSet::Store(AliasSet::Any);
+ }
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+};
+
+// Load an unboxed scalar value from a typed array or other object.
+class MLoadUnboxedScalar
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ Scalar::Type storageType_;
+ Scalar::Type readType_;
+ unsigned numElems_; // used only for SIMD
+ bool requiresBarrier_;
+ int32_t offsetAdjustment_;
+ bool canonicalizeDoubles_;
+
+ MLoadUnboxedScalar(MDefinition* elements, MDefinition* index, Scalar::Type storageType,
+ MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier,
+ int32_t offsetAdjustment = 0, bool canonicalizeDoubles = true)
+ : MBinaryInstruction(classOpcode, elements, index),
+ storageType_(storageType),
+ readType_(storageType),
+ numElems_(1),
+ requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
+ offsetAdjustment_(offsetAdjustment),
+ canonicalizeDoubles_(canonicalizeDoubles)
+ {
+ setResultType(MIRType::Value);
+ if (requiresBarrier_)
+ setGuard(); // Not removable or movable
+ else
+ setMovable();
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadUnboxedScalar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index))
+
+ void setSimdRead(Scalar::Type type, unsigned numElems) {
+ readType_ = type;
+ numElems_ = numElems;
+ }
+ unsigned numElems() const {
+ return numElems_;
+ }
+ Scalar::Type readType() const {
+ return readType_;
+ }
+
+ Scalar::Type storageType() const {
+ return storageType_;
+ }
+ bool fallible() const {
+ // Bailout if the result does not fit in an int32.
+ return readType_ == Scalar::Uint32 && type() == MIRType::Int32;
+ }
+ bool requiresMemoryBarrier() const {
+ return requiresBarrier_;
+ }
+ bool canonicalizeDoubles() const {
+ return canonicalizeDoubles_;
+ }
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ void setOffsetAdjustment(int32_t offsetAdjustment) {
+ offsetAdjustment_ = offsetAdjustment;
+ }
+ AliasSet getAliasSet() const override {
+ // When a barrier is needed make the instruction effectful by
+ // giving it a "store" effect.
+ if (requiresBarrier_)
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ return AliasSet::Load(AliasSet::UnboxedElement);
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (requiresBarrier_)
+ return false;
+ if (!ins->isLoadUnboxedScalar())
+ return false;
+ const MLoadUnboxedScalar* other = ins->toLoadUnboxedScalar();
+ if (storageType_ != other->storageType_)
+ return false;
+ if (readType_ != other->readType_)
+ return false;
+ if (numElems_ != other->numElems_)
+ return false;
+ if (offsetAdjustment() != other->offsetAdjustment())
+ return false;
+ if (canonicalizeDoubles() != other->canonicalizeDoubles())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+
+ void printOpcode(GenericPrinter& out) const override;
+
+ void computeRange(TempAllocator& alloc) override;
+
+ bool canProduceFloat32() const override { return storageType_ == Scalar::Float32; }
+
+ ALLOW_CLONE(MLoadUnboxedScalar)
+};
+
+// Load a value from a typed array. Out-of-bounds accesses are handled in-line.
+class MLoadTypedArrayElementHole
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ Scalar::Type arrayType_;
+ bool allowDouble_;
+
+ MLoadTypedArrayElementHole(MDefinition* object, MDefinition* index, Scalar::Type arrayType, bool allowDouble)
+ : MBinaryInstruction(classOpcode, object, index),
+ arrayType_(arrayType), allowDouble_(allowDouble)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadTypedArrayElementHole)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, index))
+
+ Scalar::Type arrayType() const {
+ return arrayType_;
+ }
+ bool allowDouble() const {
+ return allowDouble_;
+ }
+ bool fallible() const {
+ return arrayType_ == Scalar::Uint32 && !allowDouble_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadTypedArrayElementHole())
+ return false;
+ const MLoadTypedArrayElementHole* other = ins->toLoadTypedArrayElementHole();
+ if (arrayType() != other->arrayType())
+ return false;
+ if (allowDouble() != other->allowDouble())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::UnboxedElement);
+ }
+ bool canProduceFloat32() const override { return arrayType_ == Scalar::Float32; }
+
+ ALLOW_CLONE(MLoadTypedArrayElementHole)
+};
+
+// Compute an "effective address", i.e., a compound computation of the form:
+// base + index * scale + displacement
+class MEffectiveAddress
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ MEffectiveAddress(MDefinition* base, MDefinition* index, Scale scale, int32_t displacement)
+ : MBinaryInstruction(classOpcode, base, index),
+ scale_(scale), displacement_(displacement)
+ {
+ MOZ_ASSERT(base->type() == MIRType::Int32);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ setMovable();
+ setResultType(MIRType::Int32);
+ }
+
+ Scale scale_;
+ int32_t displacement_;
+
+ public:
+ INSTRUCTION_HEADER(EffectiveAddress)
+ TRIVIAL_NEW_WRAPPERS
+
+ MDefinition* base() const {
+ return lhs();
+ }
+ MDefinition* index() const {
+ return rhs();
+ }
+ Scale scale() const {
+ return scale_;
+ }
+ int32_t displacement() const {
+ return displacement_;
+ }
+
+ ALLOW_CLONE(MEffectiveAddress)
+};
+
+class MStoreFixedSlot
+ : public MBinaryInstruction,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
+{
+ bool needsBarrier_;
+ size_t slot_;
+
+ MStoreFixedSlot(MDefinition* obj, MDefinition* rval, size_t slot, bool barrier)
+ : MBinaryInstruction(classOpcode, obj, rval),
+ needsBarrier_(barrier),
+ slot_(slot)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(StoreFixedSlot)
+ NAMED_OPERANDS((0, object), (1, value))
+
+ static MStoreFixedSlot* New(TempAllocator& alloc, MDefinition* obj, size_t slot,
+ MDefinition* rval)
+ {
+ return new(alloc) MStoreFixedSlot(obj, rval, slot, false);
+ }
+ static MStoreFixedSlot* NewBarriered(TempAllocator& alloc, MDefinition* obj, size_t slot,
+ MDefinition* rval)
+ {
+ return new(alloc) MStoreFixedSlot(obj, rval, slot, true);
+ }
+
+ size_t slot() const {
+ return slot_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::FixedSlot);
+ }
+ bool needsBarrier() const {
+ return needsBarrier_;
+ }
+ void setNeedsBarrier(bool needsBarrier = true) {
+ needsBarrier_ = needsBarrier;
+ }
+
+ ALLOW_CLONE(MStoreFixedSlot)
+};
+
+class MGetPropertyCache
+ : public MBinaryInstruction,
+ public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
+{
+ bool idempotent_ : 1;
+ bool monitoredResult_ : 1;
+
+ InlinePropertyTable* inlinePropertyTable_;
+
+ MGetPropertyCache(MDefinition* obj, MDefinition* id, bool monitoredResult)
+ : MBinaryInstruction(classOpcode, obj, id),
+ idempotent_(false),
+ monitoredResult_(monitoredResult),
+ inlinePropertyTable_(nullptr)
+ {
+ setResultType(MIRType::Value);
+
+ // The cache will invalidate if there are objects with e.g. lookup or
+ // resolve hooks on the proto chain. setGuard ensures this check is not
+ // eliminated.
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetPropertyCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value), (1, idval))
+
+ InlinePropertyTable* initInlinePropertyTable(TempAllocator& alloc, jsbytecode* pc) {
+ MOZ_ASSERT(inlinePropertyTable_ == nullptr);
+ inlinePropertyTable_ = new(alloc) InlinePropertyTable(alloc, pc);
+ return inlinePropertyTable_;
+ }
+
+ void clearInlinePropertyTable() {
+ inlinePropertyTable_ = nullptr;
+ }
+
+ InlinePropertyTable* propTable() const {
+ return inlinePropertyTable_;
+ }
+
+ bool idempotent() const {
+ return idempotent_;
+ }
+ void setIdempotent() {
+ idempotent_ = true;
+ setMovable();
+ }
+ bool monitoredResult() const {
+ return monitoredResult_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!idempotent_)
+ return false;
+ if (!ins->isGetPropertyCache())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ if (idempotent_) {
+ return AliasSet::Load(AliasSet::ObjectFields |
+ AliasSet::FixedSlot |
+ AliasSet::DynamicSlot);
+ }
+ return AliasSet::Store(AliasSet::Any);
+ }
+
+ bool allowDoubleResult() const;
+
+ bool appendRoots(MRootList& roots) const override {
+ if (inlinePropertyTable_)
+ return inlinePropertyTable_->appendRoots(roots);
+ return true;
+ }
+};
+
+// Emit code to store a value to an object's slots if its shape/group matches
+// one of the shapes/groups observed by the baseline IC, else bails out.
+class MSetPropertyPolymorphic
+ : public MBinaryInstruction,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
+{
+ Vector<PolymorphicEntry, 4, JitAllocPolicy> receivers_;
+ CompilerPropertyName name_;
+ bool needsBarrier_;
+
+ MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
+ PropertyName* name)
+ : MBinaryInstruction(classOpcode, obj, value),
+ receivers_(alloc),
+ name_(name),
+ needsBarrier_(false)
+ {
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetPropertyPolymorphic)
+ NAMED_OPERANDS((0, object), (1, value))
+
+ static MSetPropertyPolymorphic* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
+ PropertyName* name) {
+ return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name);
+ }
+
+ MOZ_MUST_USE bool addReceiver(const ReceiverGuard& receiver, Shape* shape) {
+ PolymorphicEntry entry;
+ entry.receiver = receiver;
+ entry.shape = shape;
+ return receivers_.append(entry);
+ }
+ size_t numReceivers() const {
+ return receivers_.length();
+ }
+ const ReceiverGuard& receiver(size_t i) const {
+ return receivers_[i].receiver;
+ }
+ Shape* shape(size_t i) const {
+ return receivers_[i].shape;
+ }
+ PropertyName* name() const {
+ return name_;
+ }
+ bool needsBarrier() const {
+ return needsBarrier_;
+ }
+ void setNeedsBarrier() {
+ needsBarrier_ = true;
+ }
+ AliasSet getAliasSet() const override {
+ bool hasUnboxedStore = false;
+ for (size_t i = 0; i < numReceivers(); i++) {
+ if (!shape(i)) {
+ hasUnboxedStore = true;
+ break;
+ }
+ }
+ return AliasSet::Store(AliasSet::ObjectFields |
+ AliasSet::FixedSlot |
+ AliasSet::DynamicSlot |
+ (hasUnboxedStore ? AliasSet::UnboxedElement : 0));
+ }
+ bool appendRoots(MRootList& roots) const override;
+};
+
+// Guard on an object's identity, inclusively or exclusively.
+class MGuardObjectIdentity
+ : public MBinaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool bailOnEquality_;
+
+ MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality)
+ : MBinaryInstruction(classOpcode, obj, expected),
+ bailOnEquality_(bailOnEquality)
+ {
+ setGuard();
+ setMovable();
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(GuardObjectIdentity)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, expected))
+
+ bool bailOnEquality() const {
+ return bailOnEquality_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isGuardObjectIdentity())
+ return false;
+ if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::ObjectFields);
+ }
+};
+
+// Store to vp[slot] (slots that are not inline in an object).
+class MStoreSlot
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >::Data
+{
+ uint32_t slot_;
+ MIRType slotType_;
+ bool needsBarrier_;
+
+ MStoreSlot(MDefinition* slots, uint32_t slot, MDefinition* value, bool barrier)
+ : MBinaryInstruction(classOpcode, slots, value),
+ slot_(slot),
+ slotType_(MIRType::Value),
+ needsBarrier_(barrier)
+ {
+ MOZ_ASSERT(slots->type() == MIRType::Slots);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreSlot)
+ NAMED_OPERANDS((0, slots), (1, value))
+
+ static MStoreSlot* New(TempAllocator& alloc, MDefinition* slots, uint32_t slot,
+ MDefinition* value)
+ {
+ return new(alloc) MStoreSlot(slots, slot, value, false);
+ }
+ static MStoreSlot* NewBarriered(TempAllocator& alloc, MDefinition* slots, uint32_t slot,
+ MDefinition* value)
+ {
+ return new(alloc) MStoreSlot(slots, slot, value, true);
+ }
+
+ uint32_t slot() const {
+ return slot_;
+ }
+ MIRType slotType() const {
+ return slotType_;
+ }
+ void setSlotType(MIRType slotType) {
+ MOZ_ASSERT(slotType != MIRType::None);
+ slotType_ = slotType;
+ }
+ bool needsBarrier() const {
+ return needsBarrier_;
+ }
+ void setNeedsBarrier() {
+ needsBarrier_ = true;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::DynamicSlot);
+ }
+ void printOpcode(GenericPrinter& out) const override;
+
+ ALLOW_CLONE(MStoreSlot)
+};
+
+class MSetPropertyInstruction : public MBinaryInstruction
+{
+ CompilerPropertyName name_;
+ bool strict_;
+
+ protected:
+ MSetPropertyInstruction(Opcode op, MDefinition* obj, MDefinition* value, PropertyName* name,
+ bool strict)
+ : MBinaryInstruction(op, obj, value),
+ name_(name), strict_(strict)
+ {}
+
+ public:
+ NAMED_OPERANDS((0, object), (1, value))
+ PropertyName* name() const {
+ return name_;
+ }
+ bool strict() const {
+ return strict_;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(name_);
+ }
+};
+
+class MDeleteElement
+ : public MBinaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ bool strict_;
+
+ MDeleteElement(MDefinition* value, MDefinition* index, bool strict)
+ : MBinaryInstruction(classOpcode, value, index),
+ strict_(strict)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(DeleteElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value), (1, index))
+
+ bool strict() const {
+ return strict_;
+ }
+};
+
+// Inline call to handle lhs[rhs]. The first input is a Value so that this
+// instruction can handle both objects and strings.
+class MCallGetElement
+ : public MBinaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ MCallGetElement(MDefinition* lhs, MDefinition* rhs)
+ : MBinaryInstruction(classOpcode, lhs, rhs)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallGetElement)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MSetDOMProperty
+ : public MBinaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
+{
+ const JSJitSetterOp func_;
+
+ MSetDOMProperty(const JSJitSetterOp func, MDefinition* obj, MDefinition* val)
+ : MBinaryInstruction(classOpcode, obj, val),
+ func_(func)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(SetDOMProperty)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, value))
+
+ JSJitSetterOp fun() const {
+ return func_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+// Implementation for 'in' operator using instruction cache
+class MInCache
+ : public MBinaryInstruction,
+ public MixPolicy<CacheIdPolicy<0>, ObjectPolicy<1> >::Data
+{
+ MInCache(MDefinition* key, MDefinition* obj)
+ : MBinaryInstruction(classOpcode, key, obj)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(InCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, key), (1, object))
+};
+
+class MHasOwnCache
+ : public MBinaryInstruction,
+ public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
+{
+ MHasOwnCache(MDefinition* obj, MDefinition* id)
+ : MBinaryInstruction(classOpcode, obj, id)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(HasOwnCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value), (1, idval))
+};
+
+// Implementation for instanceof operator with unknown rhs.
+class MCallInstanceOf
+ : public MBinaryInstruction,
+ public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
+{
+ MCallInstanceOf(MDefinition* obj, MDefinition* proto)
+ : MBinaryInstruction(classOpcode, obj, proto)
+ {
+ setResultType(MIRType::Boolean);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallInstanceOf)
+ TRIVIAL_NEW_WRAPPERS
+};
+
+// Given a value being written to another object, update the generational store
+// buffer if the value is in the nursery and object is in the tenured heap.
+class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Data
+{
+ MPostWriteBarrier(MDefinition* obj, MDefinition* value)
+ : MBinaryInstruction(classOpcode, obj, value)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(PostWriteBarrier)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, value))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ // During lowering, values that neither have object nor value MIR type
+ // are ignored, thus Float32 can show up at this point without any issue.
+ return use == getUseFor(1);
+ }
+#endif
+
+ ALLOW_CLONE(MPostWriteBarrier)
+};
+
+class MCheckReturn
+ : public MBinaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ explicit MCheckReturn(MDefinition* retVal, MDefinition* thisVal)
+ : MBinaryInstruction(classOpcode, retVal, thisVal)
+ {
+ setGuard();
+ setResultType(MIRType::Value);
+ setResultTypeSet(retVal->resultTypeSet());
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckReturn)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, returnValue), (1, thisValue))
+
+};
+
+class MWasmBoundsCheck
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ wasm::BytecodeOffset bytecodeOffset_;
+
+ explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit,
+ wasm::BytecodeOffset bytecodeOffset)
+ : MBinaryInstruction(classOpcode, index, boundsCheckLimit),
+ bytecodeOffset_(bytecodeOffset)
+ {
+ // Bounds check is effectful: it throws for OOB.
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmBoundsCheck)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, index), (1, boundsCheckLimit))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool isRedundant() const {
+ return !isGuard();
+ }
+
+ void setRedundant() {
+ setNotGuard();
+ }
+
+ wasm::BytecodeOffset bytecodeOffset() const {
+ return bytecodeOffset_;
+ }
+};
+
+class MWasmStoreGlobalVar
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmStoreGlobalVar(unsigned globalDataOffset, MDefinition* value, MDefinition* tlsPtr)
+ : MBinaryInstruction(classOpcode, value, tlsPtr),
+ globalDataOffset_(globalDataOffset)
+ { }
+
+ unsigned globalDataOffset_;
+
+ public:
+ INSTRUCTION_HEADER(WasmStoreGlobalVar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, value), (1, tlsPtr))
+
+ unsigned globalDataOffset() const { return globalDataOffset_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::WasmGlobalVar);
+ }
+};
+
+class MRotate
+ : public MBinaryInstruction,
+ public NoTypePolicy::Data
+{
+ bool isLeftRotate_;
+
+ MRotate(MDefinition* input, MDefinition* count, MIRType type, bool isLeftRotate)
+ : MBinaryInstruction(classOpcode, input, count), isLeftRotate_(isLeftRotate)
+ {
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Rotate)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, input), (1, count))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins) && ins->toRotate()->isLeftRotate() == isLeftRotate_;
+ }
+
+ bool isLeftRotate() const {
+ return isLeftRotate_;
+ }
+
+ ALLOW_CLONE(MRotate)
+};
+
+// Creates a new derived type object. At runtime, this is just a call
+// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
+// compile to particularly optimized code. However, using a distinct
+// MIR for creating derived type objects allows the compiler to
+// optimize ephemeral typed objects as would be created for a
+// reference like `a.b.c` -- here, the `a.b` will create an ephemeral
+// derived type object that aliases the memory of `a` itself. The
+// specific nature of `a.b` is revealed by using
+// `MNewDerivedTypedObject` rather than `MGetProperty` or what have
+// you. Moreover, the compiler knows that there are no side-effects,
+// so `MNewDerivedTypedObject` instructions can be reordered or pruned
+// as dead code.
+class MNewDerivedTypedObject
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>,
+ ObjectPolicy<1>,
+ IntPolicy<2> >::Data
+{
+ private:
+ TypedObjectPrediction prediction_;
+
+ MNewDerivedTypedObject(TypedObjectPrediction prediction,
+ MDefinition* type,
+ MDefinition* owner,
+ MDefinition* offset)
+ : MTernaryInstruction(classOpcode, type, owner, offset),
+ prediction_(prediction)
+ {
+ setMovable();
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(NewDerivedTypedObject)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, type), (1, owner), (2, offset))
+
+ TypedObjectPrediction prediction() const {
+ return prediction_;
+ }
+
+ virtual AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MInitElem
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
+{
+ MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
+ : MTernaryInstruction(classOpcode, obj, id, value)
+ {
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(InitElem)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getObject), (1, getId), (2, getValue))
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MInitElemGetterSetter
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >::Data
+{
+ MInitElemGetterSetter(MDefinition* obj, MDefinition* id, MDefinition* value)
+ : MTernaryInstruction(classOpcode, obj, id, value)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(InitElemGetterSetter)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, idValue), (2, value))
+
+};
+
+// fun.apply(self, arguments)
+class MApplyArgs
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
+{
+ protected:
+ // Monomorphic cache of single target from TI, or nullptr.
+ WrappedFunction* target_;
+
+ MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
+ : MTernaryInstruction(classOpcode, fun, argc, self),
+ target_(target)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ApplyArgs)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis))
+
+ // For TI-informed monomorphic callsites.
+ WrappedFunction* getSingleTarget() const {
+ return target_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ if (target_)
+ return target_->appendRoots(roots);
+ return true;
+ }
+};
+
+// fun.apply(fn, array)
+class MApplyArray
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
+{
+ protected:
+ // Monomorphic cache of single target from TI, or nullptr.
+ WrappedFunction* target_;
+
+ MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
+ : MTernaryInstruction(classOpcode, fun, elements, self),
+ target_(target)
+ {
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ApplyArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis))
+
+ // For TI-informed monomorphic callsites.
+ WrappedFunction* getSingleTarget() const {
+ return target_;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ bool appendRoots(MRootList& roots) const override {
+ if (target_)
+ return target_->appendRoots(roots);
+ return true;
+ }
+};
+
+// Caller-side allocation of |this| for |new|:
+// Given a prototype operand, construct |this| for JSOP_NEW.
+class MCreateThisWithProto
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >::Data
+{
+ MCreateThisWithProto(MDefinition* callee, MDefinition* newTarget, MDefinition* prototype)
+ : MTernaryInstruction(classOpcode, callee, newTarget, prototype)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CreateThisWithProto)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, getCallee), (1, getNewTarget), (2, getPrototype))
+
+ // Although creation of |this| modifies global state, it is safely repeatable.
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MSimdSelect
+ : public MTernaryInstruction,
+ public SimdSelectPolicy::Data
+{
+ MSimdSelect(MDefinition* mask, MDefinition* lhs, MDefinition* rhs)
+ : MTernaryInstruction(classOpcode, mask, lhs, rhs)
+ {
+ MOZ_ASSERT(IsBooleanSimdType(mask->type()));
+ MOZ_ASSERT(lhs->type() == lhs->type());
+ MIRType type = lhs->type();
+ MOZ_ASSERT(IsSimdType(type));
+ setResultType(type);
+ specialization_ = type;
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdSelect)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, mask))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ ALLOW_CLONE(MSimdSelect)
+};
+
+class MRegExpMatcher
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>,
+ StringPolicy<1>,
+ IntPolicy<2> >::Data
+{
+ private:
+
+ MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
+ : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
+ {
+ setMovable();
+ // May be object or null.
+ setResultType(MIRType::Value);
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExpMatcher)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MRegExpSearcher
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>,
+ StringPolicy<1>,
+ IntPolicy<2> >::Data
+{
+ private:
+
+ MRegExpSearcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
+ : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
+ {
+ setMovable();
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExpSearcher)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MRegExpTester
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>,
+ StringPolicy<1>,
+ IntPolicy<2> >::Data
+{
+ private:
+
+ MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
+ : MTernaryInstruction(classOpcode, regexp, string, lastIndex)
+ {
+ setMovable();
+ setResultType(MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(RegExpTester)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, regexp), (1, string), (2, lastIndex))
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+};
+
+class MStringReplace
+ : public MTernaryInstruction,
+ public MixPolicy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >::Data
+{
+ private:
+
+ bool isFlatReplacement_;
+
+ MStringReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
+ : MTernaryInstruction(classOpcode, string, pattern, replacement),
+ isFlatReplacement_(false)
+ {
+ setMovable();
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StringReplace)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, string), (1, pattern), (2, replacement))
+
+ void setFlatReplacement() {
+ MOZ_ASSERT(!isFlatReplacement_);
+ isFlatReplacement_ = true;
+ }
+
+ bool isFlatReplacement() const {
+ return isFlatReplacement_;
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isStringReplace())
+ return false;
+ if (isFlatReplacement_ != ins->toStringReplace()->isFlatReplacement())
+ return false;
+ return congruentIfOperandsEqual(ins);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ if (isFlatReplacement_) {
+ MOZ_ASSERT(!pattern()->isRegExp());
+ return true;
+ }
+ return false;
+ }
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+class MSubstr
+ : public MTernaryInstruction,
+ public MixPolicy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
+{
+ private:
+
+ MSubstr(MDefinition* string, MDefinition* begin, MDefinition* length)
+ : MTernaryInstruction(classOpcode, string, begin, length)
+ {
+ setResultType(MIRType::String);
+ }
+
+ public:
+ INSTRUCTION_HEADER(Substr)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, string), (1, begin), (2, length))
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
+class MLambdaArrow
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>::Data
+{
+ const LambdaFunctionInfo info_;
+
+ MLambdaArrow(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
+ MDefinition* newTarget, MConstant* cst)
+ : MTernaryInstruction(classOpcode, envChain, newTarget, cst),
+ info_(&cst->toObject().as<JSFunction>())
+ {
+ setResultType(MIRType::Object);
+ MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun));
+ if (!info().fun->isSingleton())
+ setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
+ }
+
+ public:
+ INSTRUCTION_HEADER(LambdaArrow)
+ TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
+ NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
+
+ MConstant* functionOperand() const {
+ return getOperand(2)->toConstant();
+ }
+ const LambdaFunctionInfo& info() const {
+ return info_;
+ }
+ MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+ bool canRecoverOnBailout() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return info_.appendRoots(roots);
+ }
+};
+
+class MSetDisjointTypedElements
+ : public MTernaryInstruction,
+ public NoTypePolicy::Data
+{
+ explicit MSetDisjointTypedElements(MDefinition* target, MDefinition* targetOffset,
+ MDefinition* source)
+ : MTernaryInstruction(classOpcode, target, targetOffset, source)
+ {
+ MOZ_ASSERT(target->type() == MIRType::Object);
+ MOZ_ASSERT(targetOffset->type() == MIRType::Int32);
+ MOZ_ASSERT(source->type() == MIRType::Object);
+ setResultType(MIRType::None);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetDisjointTypedElements)
+ NAMED_OPERANDS((0, target), (1, targetOffset), (2, source))
+
+ static MSetDisjointTypedElements*
+ New(TempAllocator& alloc, MDefinition* target, MDefinition* targetOffset,
+ MDefinition* source)
+ {
+ return new(alloc) MSetDisjointTypedElements(target, targetOffset, source);
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+
+ ALLOW_CLONE(MSetDisjointTypedElements)
+};
+
+// Load a value from the elements vector of a native object. If the index is
+// out-of-bounds, or the indexed slot has a hole, undefined is returned instead.
+class MLoadElementHole
+ : public MTernaryInstruction,
+ public SingleObjectPolicy::Data
+{
+ bool needsNegativeIntCheck_;
+ bool needsHoleCheck_;
+
+ MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
+ bool needsHoleCheck)
+ : MTernaryInstruction(classOpcode, elements, index, initLength),
+ needsNegativeIntCheck_(true),
+ needsHoleCheck_(needsHoleCheck)
+ {
+ setResultType(MIRType::Value);
+ setMovable();
+
+ // Set the guard flag to make sure we bail when we see a negative
+ // index. We can clear this flag (and needsNegativeIntCheck_) in
+ // collectRangeInfoPreTrunc.
+ setGuard();
+
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(initLength->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(LoadElementHole)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
+
+ bool needsNegativeIntCheck() const {
+ return needsNegativeIntCheck_;
+ }
+ bool needsHoleCheck() const {
+ return needsHoleCheck_;
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isLoadElementHole())
+ return false;
+ const MLoadElementHole* other = ins->toLoadElementHole();
+ if (needsHoleCheck() != other->needsHoleCheck())
+ return false;
+ if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::Element);
+ }
+ void collectRangeInfoPreTrunc() override;
+
+ ALLOW_CLONE(MLoadElementHole)
+};
+
+// Store a value to a dense array slots vector.
+class MStoreElement
+ : public MTernaryInstruction,
+ public MStoreElementCommon,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >::Data
+{
+ bool needsHoleCheck_;
+ int32_t offsetAdjustment_;
+
+ MStoreElement(MDefinition* elements, MDefinition* index, MDefinition* value,
+ bool needsHoleCheck, int32_t offsetAdjustment = 0)
+ : MTernaryInstruction(classOpcode, elements, index, value)
+ {
+ needsHoleCheck_ = needsHoleCheck;
+ offsetAdjustment_ = offsetAdjustment;
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::Element);
+ }
+ bool needsHoleCheck() const {
+ return needsHoleCheck_;
+ }
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool fallible() const {
+ return needsHoleCheck();
+ }
+
+ ALLOW_CLONE(MStoreElement)
+};
+
+// Store an unboxed object or null pointer to a vector.
+class MStoreUnboxedString
+ : public MTernaryInstruction,
+ public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
+{
+ int32_t offsetAdjustment_;
+ bool preBarrier_;
+
+ MStoreUnboxedString(MDefinition* elements, MDefinition* index, MDefinition* value,
+ int32_t offsetAdjustment = 0, bool preBarrier = true)
+ : MTernaryInstruction(classOpcode, elements, index, value),
+ offsetAdjustment_(offsetAdjustment),
+ preBarrier_(preBarrier)
+ {
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreUnboxedString)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value))
+
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool preBarrier() const {
+ return preBarrier_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+
+ ALLOW_CLONE(MStoreUnboxedString)
+};
+
+// Array.prototype.slice on a dense array.
+class MArraySlice
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
+{
+ CompilerObject templateObj_;
+ gc::InitialHeap initialHeap_;
+
+ MArraySlice(CompilerConstraintList* constraints, MDefinition* obj,
+ MDefinition* begin, MDefinition* end,
+ JSObject* templateObj, gc::InitialHeap initialHeap)
+ : MTernaryInstruction(classOpcode, obj, begin, end),
+ templateObj_(templateObj),
+ initialHeap_(initialHeap)
+ {
+ setResultType(MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(ArraySlice)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, begin), (2, end))
+
+ JSObject* templateObj() const {
+ return templateObj_;
+ }
+
+ gc::InitialHeap initialHeap() const {
+ return initialHeap_;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+ }
+ bool possiblyCalls() const override {
+ return true;
+ }
+ bool appendRoots(MRootList& roots) const override {
+ return roots.append(templateObj_);
+ }
+};
+
+// Store an unboxed scalar value to a typed array or other object.
+class MStoreUnboxedScalar
+ : public MTernaryInstruction,
+ public StoreUnboxedScalarBase,
+ public StoreUnboxedScalarPolicy::Data
+{
+ public:
+ enum TruncateInputKind {
+ DontTruncateInput,
+ TruncateInput
+ };
+
+ private:
+ Scalar::Type storageType_;
+
+ // Whether this store truncates out of range inputs, for use by range analysis.
+ TruncateInputKind truncateInput_;
+
+ bool requiresBarrier_;
+ int32_t offsetAdjustment_;
+ unsigned numElems_; // used only for SIMD
+
+ MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, MDefinition* value,
+ Scalar::Type storageType, TruncateInputKind truncateInput,
+ MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier,
+ int32_t offsetAdjustment = 0)
+ : MTernaryInstruction(classOpcode, elements, index, value),
+ StoreUnboxedScalarBase(storageType),
+ storageType_(storageType),
+ truncateInput_(truncateInput),
+ requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
+ offsetAdjustment_(offsetAdjustment),
+ numElems_(1)
+ {
+ if (requiresBarrier_)
+ setGuard(); // Not removable or movable
+ else
+ setMovable();
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreUnboxedScalar)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value))
+
+ void setSimdWrite(Scalar::Type writeType, unsigned numElems) {
+ MOZ_ASSERT(Scalar::isSimdType(writeType));
+ setWriteType(writeType);
+ numElems_ = numElems;
+ }
+ unsigned numElems() const {
+ return numElems_;
+ }
+ Scalar::Type storageType() const {
+ return storageType_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+ TruncateInputKind truncateInput() const {
+ return truncateInput_;
+ }
+ bool requiresMemoryBarrier() const {
+ return requiresBarrier_;
+ }
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ TruncateKind operandTruncateKind(size_t index) const override;
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return use == getUseFor(2) && writeType() == Scalar::Float32;
+ }
+
+ ALLOW_CLONE(MStoreUnboxedScalar)
+};
+
+class MGetPropSuperCache
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, CacheIdPolicy<2>>::Data
+{
+ MGetPropSuperCache(MDefinition* obj, MDefinition* receiver, MDefinition* id)
+ : MTernaryInstruction(classOpcode, obj, receiver, id)
+ {
+ setResultType(MIRType::Value);
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(GetPropSuperCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, receiver), (2, idval))
+};
+
+class MSetElementInstruction
+ : public MTernaryInstruction
+{
+ bool strict_;
+ protected:
+ MSetElementInstruction(Opcode op, MDefinition* object, MDefinition* index, MDefinition* value,
+ bool strict)
+ : MTernaryInstruction(op, object, index, value),
+ strict_(strict)
+ {
+ }
+
+ public:
+ NAMED_OPERANDS((0, object), (1, index), (2, value))
+ bool strict() const {
+ return strict_;
+ }
+};
+
+class MSetPropertyCache
+ : public MTernaryInstruction,
+ public MixPolicy<SingleObjectPolicy, CacheIdPolicy<1>, NoFloatPolicy<2>>::Data
+{
+ bool strict_ : 1;
+ bool needsPostBarrier_ : 1;
+ bool needsTypeBarrier_ : 1;
+ bool guardHoles_ : 1;
+
+ MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value, bool strict,
+ bool needsPostBarrier, bool typeBarrier, bool guardHoles)
+ : MTernaryInstruction(classOpcode, obj, id, value),
+ strict_(strict),
+ needsPostBarrier_(needsPostBarrier),
+ needsTypeBarrier_(typeBarrier),
+ guardHoles_(guardHoles)
+ {
+ }
+
+ public:
+ INSTRUCTION_HEADER(SetPropertyCache)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, idval), (2, value))
+
+ bool needsPostBarrier() const {
+ return needsPostBarrier_;
+ }
+ bool needsTypeBarrier() const {
+ return needsTypeBarrier_;
+ }
+
+ bool guardHoles() const {
+ return guardHoles_;
+ }
+
+ bool strict() const {
+ return strict_;
+ }
+};
+
+class MCallInitElementArray
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
+{
+ MCallInitElementArray(MDefinition* obj, MDefinition* index, MDefinition* val)
+ : MTernaryInstruction(classOpcode, obj, index, val)
+ {
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(CallInitElementArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, index), (2, value))
+
+ bool possiblyCalls() const override {
+ return true;
+ }
+};
+
+// Given a value being written to another object's elements at the specified
+// index, update the generational store buffer if the value is in the nursery
+// and object is in the tenured heap.
+class MPostWriteElementBarrier : public MTernaryInstruction
+ , public MixPolicy<ObjectPolicy<0>, IntPolicy<2>>::Data
+{
+ MPostWriteElementBarrier(MDefinition* obj, MDefinition* value, MDefinition* index)
+ : MTernaryInstruction(classOpcode, obj, value, index)
+ {
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(PostWriteElementBarrier)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, value), (2, index))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+#ifdef DEBUG
+ bool isConsistentFloat32Use(MUse* use) const override {
+ // During lowering, values that neither have object nor value MIR type
+ // are ignored, thus Float32 can show up at this point without any issue.
+ return use == getUseFor(1);
+ }
+#endif
+
+ ALLOW_CLONE(MPostWriteElementBarrier)
+};
+
+class MAtomicExchangeTypedArrayElement
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>>::Data
+{
+ Scalar::Type arrayType_;
+
+ MAtomicExchangeTypedArrayElement(MDefinition* elements, MDefinition* index, MDefinition* value,
+ Scalar::Type arrayType)
+ : MTernaryInstruction(classOpcode, elements, index, value),
+ arrayType_(arrayType)
+ {
+ MOZ_ASSERT(arrayType <= Scalar::Uint32);
+ setGuard(); // Not removable
+ }
+
+ public:
+ INSTRUCTION_HEADER(AtomicExchangeTypedArrayElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value))
+
+ bool isByteArray() const {
+ return (arrayType_ == Scalar::Int8 ||
+ arrayType_ == Scalar::Uint8);
+ }
+ Scalar::Type arrayType() const {
+ return arrayType_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+};
+
+class MAtomicTypedArrayElementBinop
+ : public MTernaryInstruction,
+ public MixPolicy< ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2> >::Data
+{
+ private:
+ AtomicOp op_;
+ Scalar::Type arrayType_;
+
+ protected:
+ explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition* elements, MDefinition* index,
+ Scalar::Type arrayType, MDefinition* value)
+ : MTernaryInstruction(classOpcode, elements, index, value),
+ op_(op),
+ arrayType_(arrayType)
+ {
+ setGuard(); // Not removable
+ }
+
+ public:
+ INSTRUCTION_HEADER(AtomicTypedArrayElementBinop)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value))
+
+ bool isByteArray() const {
+ return (arrayType_ == Scalar::Int8 ||
+ arrayType_ == Scalar::Uint8);
+ }
+ AtomicOp operation() const {
+ return op_;
+ }
+ Scalar::Type arrayType() const {
+ return arrayType_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+};
+
+class MFinishBoundFunctionInit
+ : public MTernaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2>>::Data
+{
+ MFinishBoundFunctionInit(MDefinition* bound, MDefinition* target, MDefinition* argCount)
+ : MTernaryInstruction(classOpcode, bound, target, argCount)
+ { }
+
+ public:
+ INSTRUCTION_HEADER(FinishBoundFunctionInit)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, bound), (1, target), (2, argCount))
+};
+
+class MWasmSelect
+ : public MTernaryInstruction,
+ public NoTypePolicy::Data
+{
+ MWasmSelect(MDefinition* trueExpr, MDefinition* falseExpr, MDefinition *condExpr)
+ : MTernaryInstruction(classOpcode, trueExpr, falseExpr, condExpr)
+ {
+ MOZ_ASSERT(condExpr->type() == MIRType::Int32);
+ MOZ_ASSERT(trueExpr->type() == falseExpr->type());
+ setResultType(trueExpr->type());
+ setMovable();
+ }
+
+ public:
+ INSTRUCTION_HEADER(WasmSelect)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, trueExpr), (1, falseExpr), (2, condExpr))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ ALLOW_CLONE(MWasmSelect)
+};
+
+// Generic constructor of SIMD valuesX4.
+class MSimdValueX4
+ : public MQuaternaryInstruction,
+ public MixPolicy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
+ SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
+{
+ protected:
+ MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w)
+ : MQuaternaryInstruction(classOpcode, x, y, z, w)
+ {
+ MOZ_ASSERT(IsSimdType(type));
+ MOZ_ASSERT(SimdTypeToLength(type) == 4);
+
+ setMovable();
+ setResultType(type);
+ }
+
+ public:
+ INSTRUCTION_HEADER(SimdValueX4)
+ TRIVIAL_NEW_WRAPPERS
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return SimdTypeToLaneType(type()) == MIRType::Float32;
+ }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+
+ bool congruentTo(const MDefinition* ins) const override {
+ return congruentIfOperandsEqual(ins);
+ }
+
+ MDefinition* foldsTo(TempAllocator& alloc) override;
+
+ ALLOW_CLONE(MSimdValueX4)
+};
+
+// Try to store a value to a dense array slots vector. May fail due to the object being frozen.
+// Cannot be used on an object that has extra indexed properties.
+class MFallibleStoreElement
+ : public MQuaternaryInstruction,
+ public MStoreElementCommon,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
+{
+ bool strict_;
+
+ MFallibleStoreElement(MDefinition* object, MDefinition* elements,
+ MDefinition* index, MDefinition* value,
+ bool strict)
+ : MQuaternaryInstruction(classOpcode, object, elements, index, value),
+ strict_(strict)
+ {
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(FallibleStoreElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
+ }
+ bool strict() const {
+ return strict_;
+ }
+
+ ALLOW_CLONE(MFallibleStoreElement)
+};
+
+
+// Store an unboxed object or null pointer to a v\ector.
+class MStoreUnboxedObjectOrNull
+ : public MQuaternaryInstruction,
+ public StoreUnboxedObjectOrNullPolicy::Data
+{
+ int32_t offsetAdjustment_;
+ bool preBarrier_;
+
+ MStoreUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
+ MDefinition* value, MDefinition* typedObj,
+ int32_t offsetAdjustment = 0, bool preBarrier = true)
+ : MQuaternaryInstruction(classOpcode, elements, index, value, typedObj),
+ offsetAdjustment_(offsetAdjustment),
+ preBarrier_(preBarrier)
+ {
+ MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(typedObj->type() == MIRType::Object);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreUnboxedObjectOrNull)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, value), (3, typedObj))
+
+ int32_t offsetAdjustment() const {
+ return offsetAdjustment_;
+ }
+ bool preBarrier() const {
+ return preBarrier_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+
+ // For StoreUnboxedObjectOrNullPolicy.
+ void setValue(MDefinition* def) {
+ replaceOperand(2, def);
+ }
+
+ ALLOW_CLONE(MStoreUnboxedObjectOrNull)
+};
+
+class MStoreTypedArrayElementHole
+ : public MQuaternaryInstruction,
+ public StoreUnboxedScalarBase,
+ public StoreTypedArrayHolePolicy::Data
+{
+ MStoreTypedArrayElementHole(MDefinition* elements, MDefinition* length, MDefinition* index,
+ MDefinition* value, Scalar::Type arrayType)
+ : MQuaternaryInstruction(classOpcode, elements, length, index, value),
+ StoreUnboxedScalarBase(arrayType)
+ {
+ setMovable();
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ MOZ_ASSERT(length->type() == MIRType::Int32);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreTypedArrayElementHole)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, length), (2, index), (3, value))
+
+ Scalar::Type arrayType() const {
+ MOZ_ASSERT(!Scalar::isSimdType(writeType()),
+ "arrayType == writeType iff the write type isn't SIMD");
+ return writeType();
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+ TruncateKind operandTruncateKind(size_t index) const override;
+
+ bool canConsumeFloat32(MUse* use) const override {
+ return use == getUseFor(3) && arrayType() == Scalar::Float32;
+ }
+
+ ALLOW_CLONE(MStoreTypedArrayElementHole)
+};
+
+// Test whether the index is in the array bounds or a hole.
+class MInArray
+ : public MQuaternaryInstruction,
+ public ObjectPolicy<3>::Data
+{
+ bool needsHoleCheck_;
+ bool needsNegativeIntCheck_;
+
+ MInArray(MDefinition* elements, MDefinition* index,
+ MDefinition* initLength, MDefinition* object,
+ bool needsHoleCheck)
+ : MQuaternaryInstruction(classOpcode, elements, index, initLength, object),
+ needsHoleCheck_(needsHoleCheck),
+ needsNegativeIntCheck_(true)
+ {
+ setResultType(MIRType::Boolean);
+ setMovable();
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ MOZ_ASSERT(initLength->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(InArray)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, initLength), (3, object))
+
+ bool needsHoleCheck() const {
+ return needsHoleCheck_;
+ }
+ bool needsNegativeIntCheck() const {
+ return needsNegativeIntCheck_;
+ }
+ void collectRangeInfoPreTrunc() override;
+ AliasSet getAliasSet() const override {
+ return AliasSet::Load(AliasSet::Element);
+ }
+ bool congruentTo(const MDefinition* ins) const override {
+ if (!ins->isInArray())
+ return false;
+ const MInArray* other = ins->toInArray();
+ if (needsHoleCheck() != other->needsHoleCheck())
+ return false;
+ if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
+ return false;
+ return congruentIfOperandsEqual(other);
+ }
+};
+
+class MCompareExchangeTypedArrayElement
+ : public MQuaternaryInstruction,
+ public MixPolicy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3>>::Data
+{
+ Scalar::Type arrayType_;
+
+ explicit MCompareExchangeTypedArrayElement(MDefinition* elements, MDefinition* index,
+ Scalar::Type arrayType, MDefinition* oldval,
+ MDefinition* newval)
+ : MQuaternaryInstruction(classOpcode, elements, index, oldval, newval),
+ arrayType_(arrayType)
+ {
+ setGuard(); // Not removable
+ }
+
+ public:
+ INSTRUCTION_HEADER(CompareExchangeTypedArrayElement)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, elements), (1, index), (2, oldval), (3, newval))
+
+ bool isByteArray() const {
+ return (arrayType_ == Scalar::Int8 ||
+ arrayType_ == Scalar::Uint8);
+ }
+ int oldvalOperand() {
+ return 2;
+ }
+ Scalar::Type arrayType() const {
+ return arrayType_;
+ }
+ AliasSet getAliasSet() const override {
+ return AliasSet::Store(AliasSet::UnboxedElement);
+ }
+};
+
+class MQuaternaryInstruction : public MAryInstruction<4>
+{
+ protected:
+ MQuaternaryInstruction(Opcode op,
+ MDefinition* first, MDefinition* second,
+ MDefinition* third, MDefinition* fourth)
+ : MAryInstruction(op)
+ {
+ initOperand(0, first);
+ initOperand(1, second);
+ initOperand(2, third);
+ initOperand(3, fourth);
+ }
+
+ HashNumber valueHash() const override;
+};
+
+// Like MStoreElement, but supports indexes >= initialized length. The downside
+// is that we cannot hoist the elements vector and bounds check, since this
+// instruction may update the (initialized) length and reallocate the elements
+// vector.
+class MStoreElementHole
+ : public MQuaternaryInstruction,
+ public MStoreElementCommon,
+ public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
+{
+ MStoreElementHole(MDefinition* object, MDefinition* elements,
+ MDefinition* index, MDefinition* value)
+ : MQuaternaryInstruction(classOpcode, object, elements, index, value)
+ {
+ MOZ_ASSERT(elements->type() == MIRType::Elements);
+ MOZ_ASSERT(index->type() == MIRType::Int32);
+ }
+
+ public:
+ INSTRUCTION_HEADER(StoreElementHole)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
+
+ AliasSet getAliasSet() const override {
+ // StoreElementHole can update the initialized length, the array length
+ // or reallocate obj->elements.
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
+ }
+
+ ALLOW_CLONE(MStoreElementHole)
+};
+
+#endif
\ No newline at end of file