/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/WarpCacheIRTranspiler.h"
#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"
#include "jsmath.h"
#include "jit/AtomicOp.h"
#include "jit/CacheIR.h"
#include "jit/CacheIRCompiler.h"
#include "jit/CacheIROpsGenerated.h"
#include "jit/CacheIRReader.h"
#include "jit/LIR.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/WarpBuilder.h"
#include "jit/WarpBuilderShared.h"
#include "jit/WarpSnapshot.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "vm/BytecodeLocation.h"
#include "vm/TypeofEqOperand.h" // TypeofEqOperand
#include "wasm/WasmCode.h"
#include "gc/ObjectKind-inl.h"
#include "vm/NativeObject-inl.h"
#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
// The CacheIR transpiler generates MIR from Baseline CacheIR.
class MOZ_RAII WarpCacheIRTranspiler :
public WarpBuilderShared {
WarpBuilder* builder_;
BytecodeLocation loc_;
const CacheIRStubInfo* stubInfo_;
const uint8_t* stubData_;
// Vector mapping OperandId to corresponding MDefinition.
using MDefinitionStackVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
MDefinitionStackVector operands_;
CallInfo* callInfo_;
// Array mapping call arguments to OperandId.
using ArgumentKindArray =
mozilla::EnumeratedArray<ArgumentKind, OperandId,
size_t(ArgumentKind::NumKinds)>;
ArgumentKindArray argumentOperandIds_;
void setArgumentId(ArgumentKind kind, OperandId id) {
MOZ_ASSERT(kind != ArgumentKind::Callee);
MOZ_ASSERT(!argumentOperandIds_[kind].valid());
argumentOperandIds_[kind] = id;
}
void updateArgumentsFromOperands();
#ifdef DEBUG
// Used to assert that there is only one effectful instruction
// per stub. And that this instruction has a resume point.
MInstruction* effectful_ = nullptr;
bool pushedResult_ =
false;
#endif
inline void addUnchecked(MInstruction* ins) {
current->add(ins);
// If we have not set a more specific bailout kind, mark this instruction
// as transpiled CacheIR. If one of these instructions bails out, we
// expect to hit the baseline fallback stub and invalidate the Warp script
// in tryAttach.
if (ins->bailoutKind() == BailoutKind::Unknown) {
ins->setBailoutKind(BailoutKind::TranspiledCacheIR);
}
}
inline void add(MInstruction* ins) {
MOZ_ASSERT(!ins->isEffectful());
addUnchecked(ins);
}
inline void addEffectful(MInstruction* ins) {
MOZ_ASSERT(ins->isEffectful());
MOZ_ASSERT(!effectful_,
"Can only have one effectful instruction");
addUnchecked(ins);
#ifdef DEBUG
effectful_ = ins;
#endif
}
// Bypasses all checks in addEffectful. Use with caution!
inline void addEffectfulUnsafe(MInstruction* ins) {
MOZ_ASSERT(ins->isEffectful());
addUnchecked(ins);
}
[[nodiscard]]
bool resumeAfterUnchecked(MInstruction* ins) {
return WarpBuilderShared::resumeAfter(ins, loc_);
}
[[nodiscard]]
bool resumeAfter(MInstruction* ins) {
MOZ_ASSERT(effectful_ == ins);
return resumeAfterUnchecked(ins);
}
// CacheIR instructions writing to the IC's result register (the *Result
// instructions) must call this to push the result onto the virtual stack.
void pushResult(MDefinition* result) {
MOZ_ASSERT(!pushedResult_,
"Can't have more than one result");
current->push(result);
#ifdef DEBUG
pushedResult_ =
true;
#endif
}
MDefinition* getOperand(OperandId id)
const {
return operands_[id.id()]; }
void setOperand(OperandId id, MDefinition* def) { operands_[id.id()] = def; }
[[nodiscard]]
bool defineOperand(OperandId id, MDefinition* def) {
MOZ_ASSERT(id.id() == operands_.length());
return operands_.append(def);
}
uintptr_t readStubWord(uint32_t offset) {
return stubInfo_->getStubRawWord(stubData_, offset);
}
Shape* shapeStubField(uint32_t offset) {
return reinterpret_cast<Shape*>(readStubWord(offset));
}
GetterSetter* getterSetterStubField(uint32_t offset) {
return reinterpret_cast<GetterSetter*>(readStubWord(offset));
}
const JSClass* classStubField(uint32_t offset) {
return reinterpret_cast<
const JSClass*>(readStubWord(offset));
}
JSString* stringStubField(uint32_t offset) {
return reinterpret_cast<JSString*>(readStubWord(offset));
}
JS::Symbol* symbolStubField(uint32_t offset) {
return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
}
BaseScript* baseScriptStubField(uint32_t offset) {
return reinterpret_cast<BaseScript*>(readStubWord(offset));
}
const JSJitInfo* jitInfoStubField(uint32_t offset) {
return reinterpret_cast<
const JSJitInfo*>(readStubWord(offset));
}
JSNative jsnativeStubField(uint32_t offset) {
return reinterpret_cast<JSNative>(readStubWord(offset));
}
JS::ExpandoAndGeneration* expandoAndGenerationField(uint32_t offset) {
return reinterpret_cast<JS::ExpandoAndGeneration*>(readStubWord(offset));
}
const wasm::FuncExport* wasmFuncExportField(uint32_t offset) {
return reinterpret_cast<
const wasm::FuncExport*>(readStubWord(offset));
}
NativeIteratorListHead* nativeIteratorListHeadStubField(uint32_t offset) {
return reinterpret_cast<NativeIteratorListHead*>(readStubWord(offset));
}
size_t* fuseStubField(uint32_t offset) {
return reinterpret_cast<size_t*>(readStubWord(offset));
}
gc::Heap allocSiteInitialHeapField(uint32_t offset) {
uintptr_t word = readStubWord(offset);
MOZ_ASSERT(word == uintptr_t(gc::Heap::
Default) ||
word == uintptr_t(gc::Heap::Tenured));
return gc::Heap(word);
}
const void* rawPointerField(uint32_t offset) {
return reinterpret_cast<
const void*>(readStubWord(offset));
}
jsid idStubField(uint32_t offset) {
return jsid::fromRawBits(readStubWord(offset));
}
int32_t int32StubField(uint32_t offset) {
return static_cast<int32_t>(readStubWord(offset));
}
uint32_t uint32StubField(uint32_t offset) {
return static_cast<uint32_t>(readStubWord(offset));
}
uint64_t uint64StubField(uint32_t offset) {
return static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
}
Value valueStubField(uint32_t offset) {
uint64_t raw =
static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
Value val = Value::fromRawBits(raw);
MOZ_ASSERT_IF(val.isGCThing(), val.toGCThing()->isTenured());
return val;
}
double doubleStubField(uint32_t offset) {
uint64_t raw =
static_cast<uint64_t>(stubInfo_->getStubRawInt64(stubData_, offset));
return mozilla::BitwiseCast<
double>(raw);
}
// This must only be called when the caller knows the object is tenured and
// not a nursery index.
JSObject* tenuredObjectStubField(uint32_t offset) {
WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
return field.toObject();
}
// Returns either MConstant or MNurseryIndex. See WarpObjectField.
MInstruction* objectStubField(uint32_t offset);
const JSClass* classForGuardClassKind(GuardClassKind kind);
[[nodiscard]]
bool emitGuardTo(ValOperandId inputId, MIRType type);
[[nodiscard]]
bool emitToString(OperandId inputId, StringOperandId resultId);
template <
typename T>
[[nodiscard]]
bool emitDoubleBinaryArithResult(NumberOperandId lhsId,
NumberOperandId rhsId);
template <
typename T>
[[nodiscard]]
bool emitInt32BinaryArithResult(Int32OperandId lhsId,
Int32OperandId rhsId);
template <
typename T>
[[nodiscard]]
bool emitBigIntBinaryArithResult(BigIntOperandId lhsId,
BigIntOperandId rhsId);
template <
typename T>
[[nodiscard]]
bool emitBigIntBinaryArithEffectfulResult(
BigIntOperandId lhsId, BigIntOperandId rhsId);
template <
typename T>
[[nodiscard]]
bool emitBigIntUnaryArithResult(BigIntOperandId inputId);
template <
typename T>
[[nodiscard]]
bool emitBigIntPtrBinaryArith(IntPtrOperandId lhsId,
IntPtrOperandId rhsId,
IntPtrOperandId resultId);
[[nodiscard]]
bool emitCompareResult(JSOp op, OperandId lhsId,
OperandId rhsId,
MCompare::CompareType compareType);
[[nodiscard]]
bool emitTruthyResult(OperandId inputId);
[[nodiscard]]
bool emitNewIteratorResult(MNewIterator::Type type,
uint32_t templateObjectOffset);
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
[[nodiscard]] MInstruction* convertToBoolean(MDefinition* input);
bool emitAddAndStoreSlotShared(MAddAndStoreSlot::Kind kind,
ObjOperandId objId, uint32_t offsetOffset,
ValOperandId rhsId, uint32_t newShapeOffset);
MInstruction* emitTypedArrayLength(ArrayBufferViewKind viewKind,
MDefinition* obj);
MInstruction* emitDataViewLength(ArrayBufferViewKind viewKind,
MDefinition* obj);
void addDataViewData(ArrayBufferViewKind viewKind, MDefinition* obj,
Scalar::Type type, MDefinition** offset,
MInstruction** elements);
[[nodiscard]]
bool emitAtomicsBinaryOp(
ObjOperandId objId, IntPtrOperandId indexId, uint32_t valueId,
Scalar::Type elementType,
bool forEffect, ArrayBufferViewKind viewKind,
AtomicOp op);
[[nodiscard]]
bool emitLoadArgumentSlot(ValOperandId resultId,
uint32_t slotIndex);
// Calls are either Native (native function without a JitEntry),
// a DOM Native (native function with a JitInfo OpType::Method),
// or Scripted (scripted function or native function with a JitEntry).
enum class CallKind { Native, DOM, Scripted };
[[nodiscard]]
bool updateCallInfo(MDefinition* callee, CallFlags flags);
[[nodiscard]]
bool emitCallFunction(
ObjOperandId calleeId, Int32OperandId argcId,
mozilla::Maybe<ObjOperandId> thisObjId, CallFlags flags, CallKind kind,
mozilla::Maybe<uint32_t> siteOffset = mozilla::Nothing());
[[nodiscard]]
bool emitFunApplyArgsObj(WrappedFunction* wrappedTarget,
CallFlags flags);
MDefinition* convertWasmArg(MDefinition* arg, wasm::ValType::Kind kind);
WrappedFunction* maybeWrappedFunction(MDefinition* callee, CallKind kind,
uint16_t nargs, FunctionFlags flags);
WrappedFunction* maybeCallTarget(MDefinition* callee, CallKind kind);
bool maybeCreateThis(MDefinition* callee, CallFlags flags, CallKind kind);
[[nodiscard]]
bool emitCallGetterResult(CallKind kind,
ValOperandId receiverId,
uint32_t getterOffset,
bool sameRealm,
uint32_t nargsAndFlagsOffset);
[[nodiscard]]
bool emitCallSetter(CallKind kind, ObjOperandId receiverId,
uint32_t setterOffset, ValOperandId rhsId,
bool sameRealm,
uint32_t nargsAndFlagsOffset);
#ifndef JS_CODEGEN_X86
[[nodiscard]]
bool emitCallScriptedProxyGetShared(
MDefinition* target, MDefinition* receiver, MDefinition* handler,
MDefinition* id, MDefinition* trapDef, WrappedFunction* trap);
#endif
CACHE_IR_TRANSPILER_GENERATED
public:
WarpCacheIRTranspiler(WarpBuilder* builder, BytecodeLocation loc,
CallInfo* callInfo,
const WarpCacheIR* cacheIRSnapshot)
: WarpBuilderShared(builder->snapshot(), builder->mirGen(),
builder->currentBlock()),
builder_(builder),
loc_(loc),
stubInfo_(cacheIRSnapshot->stubInfo()),
stubData_(cacheIRSnapshot->stubData()),
callInfo_(callInfo) {}
[[nodiscard]]
bool transpile(std::initializer_list<MDefinition*> inputs);
};
bool WarpCacheIRTranspiler::transpile(
std::initializer_list<MDefinition*> inputs) {
if (!operands_.append(inputs.begin(), inputs.end())) {
return false;
}
CacheIRReader reader(stubInfo_);
do {
CacheOp op = reader.readOp();
switch (op) {
#define DEFINE_OP(op, ...) \
case CacheOp::op: \
if (!emit
##op(reader)) { \
return false; \
} \
break;
CACHE_IR_TRANSPILER_OPS(DEFINE_OP)
#undef DEFINE_OP
default:
fprintf(stderr,
"Unsupported op: %s\n", CacheIROpNames[size_t(op)]);
MOZ_CRASH(
"Unsupported op");
}
}
while (reader.more());
// Effectful instructions should have a resume point. We allow a limited
// number of exceptions:
// - MIonToWasmCall: Resumes after MInt64ToBigInt
// - MLoadUnboxedScalar: Resumes after MInt64ToBigInt
// - MAtomicTypedArrayElementBinop: Resumes after MInt64ToBigInt
// - MAtomicExchangeTypedArrayElement: Resumes after MInt64ToBigInt
// - MCompareExchangeTypedArrayElement: Resumes after MInt64ToBigInt
// - MResizableTypedArrayLength: Resumes after MPostIntPtrConversion
// - MResizableDataViewByteLength: Resumes after MPostIntPtrConversion
// - MGrowableSharedArrayBufferByteLength: Resumes after MPostIntPtrConversion
MOZ_ASSERT_IF(effectful_,
effectful_->resumePoint() || effectful_->isIonToWasmCall() ||
effectful_->isLoadUnboxedScalar() ||
effectful_->isAtomicTypedArrayElementBinop() ||
effectful_->isAtomicExchangeTypedArrayElement() ||
effectful_->isCompareExchangeTypedArrayElement() ||
effectful_->isResizableTypedArrayLength() ||
effectful_->isResizableDataViewByteLength() ||
effectful_->isGrowableSharedArrayBufferByteLength());
return true;
}
MInstruction* WarpCacheIRTranspiler::objectStubField(uint32_t offset) {
WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
if (field.isNurseryIndex()) {
auto* ins = MNurseryObject::
New(alloc(), field.toNurseryIndex());
add(ins);
return ins;
}
auto* ins = MConstant::NewObject(alloc(), field.toObject());
add(ins);
return ins;
}
bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
GuardClassKind kind) {
MDefinition* def = getOperand(objId);
MInstruction* ins;
if (kind == GuardClassKind::JSFunction) {
ins = MGuardToFunction::
New(alloc(), def);
}
else {
const JSClass* classp = classForGuardClassKind(kind);
ins = MGuardToClass::
New(alloc(), def, classp);
}
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardEitherClass(ObjOperandId objId,
GuardClassKind kind1,
GuardClassKind kind2) {
MDefinition* def = getOperand(objId);
// We don't yet need this case, so it's unsupported for now.
MOZ_ASSERT(kind1 != GuardClassKind::JSFunction &&
kind2 != GuardClassKind::JSFunction);
const JSClass* classp1 = classForGuardClassKind(kind1);
const JSClass* classp2 = classForGuardClassKind(kind2);
auto* ins = MGuardToEitherClass::
New(alloc(), def, classp1, classp2);
add(ins);
setOperand(objId, ins);
return true;
}
const JSClass* WarpCacheIRTranspiler::classForGuardClassKind(
GuardClassKind kind) {
switch (kind) {
case GuardClassKind::Array:
case GuardClassKind::PlainObject:
case GuardClassKind::FixedLengthArrayBuffer:
case GuardClassKind::ResizableArrayBuffer:
case GuardClassKind::FixedLengthSharedArrayBuffer:
case GuardClassKind::GrowableSharedArrayBuffer:
case GuardClassKind::FixedLengthDataView:
case GuardClassKind::ResizableDataView:
case GuardClassKind::MappedArguments:
case GuardClassKind::UnmappedArguments:
case GuardClassKind::Set:
case GuardClassKind::Map:
case GuardClassKind::BoundFunction:
case GuardClassKind::Date:
return ClassFor(kind);
case GuardClassKind::WindowProxy:
return mirGen().runtime->maybeWindowProxyClass();
case GuardClassKind::JSFunction:
break;
}
MOZ_CRASH(
"unexpected kind");
}
bool WarpCacheIRTranspiler::emitGuardAnyClass(ObjOperandId objId,
uint32_t claspOffset) {
MDefinition* def = getOperand(objId);
const JSClass* classp = classStubField(claspOffset);
auto* ins = MGuardToClass::
New(alloc(), def, classp);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardShape(ObjOperandId objId,
uint32_t shapeOffset) {
MDefinition* def = getOperand(objId);
// No read barrier is required because snapshot data is not weak and is traced
// as part of IonCompileTask.
Shape* shape = shapeStubField(shapeOffset);
auto* ins = MGuardShape::
New(alloc(), def, shape);
add(ins);
setOperand(objId, ins);
return true;
}
template <
auto FuseMember>
struct RealmFuseDependency final :
public CompilationDependency {
RealmFuseDependency();
virtual bool registerDependency(JSContext* cx, HandleScript script) override {
MOZ_ASSERT(checkDependency(cx));
return (cx->realm()->realmFuses.*FuseMember).addFuseDependency(cx, script);
}
virtual UniquePtr<CompilationDependency> clone() override {
return MakeUnique<RealmFuseDependency<FuseMember>>();
}
virtual bool checkDependency(JSContext* cx) override {
return (cx->realm()->realmFuses.*FuseMember).intact();
}
virtual bool operator==(CompilationDependency& dep) override {
return dep.type == type;
}
};
using GetIteratorDependency =
RealmFuseDependency<&RealmFuses::optimizeGetIteratorFuse>;
template <>
GetIteratorDependency::RealmFuseDependency()
: CompilationDependency(CompilationDependency::Type::GetIterator) {}
bool WarpCacheIRTranspiler::emitGuardFuse(RealmFuses::FuseIndex fuseIndex) {
if (fuseIndex != RealmFuses::FuseIndex::OptimizeGetIteratorFuse) {
auto* ins = MGuardFuse::
New(alloc(), fuseIndex);
add(ins);
return true;
}
// Register the compilation dependency.
GetIteratorDependency dep;
return mirGen().tracker.addDependency(dep);
}
bool WarpCacheIRTranspiler::emitGuardMultipleShapes(ObjOperandId objId,
uint32_t shapesOffset) {
MDefinition* def = getOperand(objId);
MInstruction* shapeList = objectStubField(shapesOffset);
auto* ins = MGuardMultipleShapes::
New(alloc(), def, shapeList);
if (builder_->info().inlineScriptTree()->hasSharedICScript()) {
ins->setBailoutKind(BailoutKind::MonomorphicInlinedStubFolding);
}
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
MDefinition* def = getOperand(objId);
auto* ins = MGuardNullProto::
New(alloc(), def);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsNativeObject(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsNativeObject::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsProxy(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsProxy::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsNotProxy(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsNotProxy::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsNotDOMProxy(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsNotDOMProxy::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardHasGetterSetter(
ObjOperandId objId, uint32_t idOffset, uint32_t getterSetterOffset) {
MDefinition* obj = getOperand(objId);
jsid id = idStubField(idOffset);
GetterSetter* gs = getterSetterStubField(getterSetterOffset);
auto* ins = MGuardHasGetterSetter::
New(alloc(), obj, id, gs);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitProxyGetResult(ObjOperandId objId,
uint32_t idOffset) {
MDefinition* obj = getOperand(objId);
jsid id = idStubField(idOffset);
auto* ins = MProxyGet::
New(alloc(), obj, id);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitProxyGetByValueResult(ObjOperandId objId,
ValOperandId idId) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
auto* ins = MProxyGetByValue::
New(alloc(), obj, id);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitProxyHasPropResult(ObjOperandId objId,
ValOperandId idId,
bool hasOwn) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
auto* ins = MProxyHasProp::
New(alloc(), obj, id, hasOwn);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitProxySet(ObjOperandId objId, uint32_t idOffset,
ValOperandId rhsId,
bool strict) {
MDefinition* obj = getOperand(objId);
jsid id = idStubField(idOffset);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MProxySet::
New(alloc(), obj, rhs, id, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitProxySetByValue(ObjOperandId objId,
ValOperandId idId,
ValOperandId rhsId,
bool strict) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MProxySetByValue::
New(alloc(), obj, id, rhs, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitCallSetArrayLength(ObjOperandId objId,
bool strict,
ValOperandId rhsId) {
MDefinition* obj = getOperand(objId);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MCallSetArrayLength::
New(alloc(), obj, rhs, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitCallDOMGetterResult(ObjOperandId objId,
uint32_t jitInfoOffset) {
MDefinition* obj = getOperand(objId);
const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
MInstruction* ins;
if (jitInfo->isAlwaysInSlot) {
ins = MGetDOMMember::
New(alloc(), jitInfo, obj, nullptr, nullptr);
}
else {
// TODO(post-Warp): realms, guard operands (movable?).
ins = MGetDOMProperty::
New(alloc(), jitInfo, DOMObjectKind::Native,
(JS::Realm*)mirGen().realm->realmPtr(), obj,
nullptr, nullptr);
}
if (!ins) {
return false;
}
if (ins->isEffectful()) {
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitCallDOMSetter(ObjOperandId objId,
uint32_t jitInfoOffset,
ValOperandId rhsId) {
MDefinition* obj = getOperand(objId);
const JSJitInfo* jitInfo = jitInfoStubField(jitInfoOffset);
MDefinition* value = getOperand(rhsId);
MOZ_ASSERT(jitInfo->type() == JSJitInfo::Setter);
auto* set =
MSetDOMProperty::
New(alloc(), jitInfo->setter, DOMObjectKind::Native,
(JS::Realm*)mirGen().realm->realmPtr(), obj, value);
addEffectful(set);
return resumeAfter(set);
}
bool WarpCacheIRTranspiler::emitLoadDOMExpandoValue(ObjOperandId objId,
ValOperandId resultId) {
MDefinition* proxy = getOperand(objId);
auto* ins = MLoadDOMExpandoValue::
New(alloc(), proxy);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueGuardGeneration(
ObjOperandId objId, uint32_t expandoAndGenerationOffset,
uint32_t generationOffset, ValOperandId resultId) {
MDefinition* proxy = getOperand(objId);
JS::ExpandoAndGeneration* expandoAndGeneration =
expandoAndGenerationField(expandoAndGenerationOffset);
uint64_t generation = uint64StubField(generationOffset);
auto* ins = MLoadDOMExpandoValueGuardGeneration::
New(
alloc(), proxy, expandoAndGeneration, generation);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadDOMExpandoValueIgnoreGeneration(
ObjOperandId objId, ValOperandId resultId) {
MDefinition* proxy = getOperand(objId);
auto* ins = MLoadDOMExpandoValueIgnoreGeneration::
New(alloc(), proxy);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardDOMExpandoMissingOrGuardShape(
ValOperandId expandoId, uint32_t shapeOffset) {
MDefinition* expando = getOperand(expandoId);
Shape* shape = shapeStubField(shapeOffset);
auto* ins = MGuardDOMExpandoMissingOrGuardShape::
New(alloc(), expando, shape);
add(ins);
setOperand(expandoId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotResult(ObjOperandId objId,
uint32_t nameOffset) {
MDefinition* obj = getOperand(objId);
PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();
auto* ins = MMegamorphicLoadSlot::
New(alloc(), obj, NameToId(name));
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotPermissiveResult(
ObjOperandId objId, uint32_t nameOffset) {
MDefinition* obj = getOperand(objId);
PropertyName* name = stringStubField(nameOffset)->asAtom().asPropertyName();
auto* ins = MMegamorphicLoadSlotPermissive::
New(alloc(), obj, NameToId(name));
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValueResult(
ObjOperandId objId, ValOperandId idId) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
auto* ins = MMegamorphicLoadSlotByValue::
New(alloc(), obj, id);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitMegamorphicLoadSlotByValuePermissiveResult(
ObjOperandId objId, ValOperandId idId) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
auto* ins = MMegamorphicLoadSlotByValuePermissive::
New(alloc(), obj, id);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitMegamorphicStoreSlot(ObjOperandId objId,
uint32_t idOffset,
ValOperandId rhsId,
bool strict) {
MDefinition* obj = getOperand(objId);
jsid id = idStubField(idOffset);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MMegamorphicStoreSlot::
New(alloc(), obj, rhs, id, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitMegamorphicHasPropResult(ObjOperandId objId,
ValOperandId idId,
bool hasOwn) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
auto* ins = MMegamorphicHasProp::
New(alloc(), obj, id, hasOwn);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitSmallObjectVariableKeyHasOwnResult(
StringOperandId idId, uint32_t propNamesOffset, uint32_t shapeOffset) {
MDefinition* id = getOperand(idId);
SharedShape* shape = &shapeStubField(shapeOffset)->asShared();
auto* ins = MSmallObjectVariableKeyHasProp::
New(alloc(), id, shape);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitMegamorphicSetElement(ObjOperandId objId,
ValOperandId idId,
ValOperandId rhsId,
bool strict) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MMegamorphicSetElement::
New(alloc(), obj, id, rhs, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitObjectToIteratorResult(
ObjOperandId objId, uint32_t enumeratorsAddrOffset) {
MDefinition* obj = getOperand(objId);
NativeIteratorListHead* enumeratorsAddr =
nativeIteratorListHeadStubField(enumeratorsAddrOffset);
auto* ins = MObjectToIterator::
New(alloc(), obj, enumeratorsAddr);
addEffectful(ins);
pushResult(ins);
if (!resumeAfter(ins)) {
return false;
}
return true;
}
bool WarpCacheIRTranspiler::emitValueToIteratorResult(ValOperandId valId) {
MDefinition* val = getOperand(valId);
auto* ins = MValueToIterator::
New(alloc(), val);
addEffectful(ins);
pushResult(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitGuardIsNotArrayBufferMaybeShared(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsNotArrayBufferMaybeShared::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsTypedArray::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsFixedLengthTypedArray(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsFixedLengthTypedArray::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsResizableTypedArray(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsResizableTypedArray::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId,
uint32_t handlerOffset) {
MDefinition* obj = getOperand(objId);
const void* handler = rawPointerField(handlerOffset);
auto* ins = MGuardHasProxyHandler::
New(alloc(), obj, handler);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
uint32_t protoOffset) {
MDefinition* def = getOperand(objId);
MDefinition* proto = objectStubField(protoOffset);
auto* ins = MGuardProto::
New(alloc(), def, proto);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsSpecificObject(
ObjOperandId objId, ObjOperandId expectedId, uint32_t slotOffset) {
size_t slotIndex = int32StubField(slotOffset);
MDefinition* obj = getOperand(objId);
MDefinition* expected = getOperand(expectedId);
auto* slots = MSlots::
New(alloc(), obj);
add(slots);
auto* load = MLoadDynamicSlot::
New(alloc(), slots, slotIndex);
add(load);
auto* unbox = MUnbox::
New(alloc(), load, MIRType::Object, MUnbox::Fallible);
add(unbox);
auto* guard = MGuardObjectIdentity::
New(alloc(), unbox, expected,
/* bailOnEquality = */ false);
add(guard);
return true;
}
bool WarpCacheIRTranspiler::emitLoadDynamicSlot(ValOperandId resultId,
ObjOperandId objId,
uint32_t slotOffset) {
size_t slotIndex = int32StubField(slotOffset);
MDefinition* obj = getOperand(objId);
auto* slots = MSlots::
New(alloc(), obj);
add(slots);
auto* load = MLoadDynamicSlot::
New(alloc(), slots, slotIndex);
add(load);
return defineOperand(resultId, load);
}
bool WarpCacheIRTranspiler::emitGuardDynamicSlotIsNotObject(
ObjOperandId objId, uint32_t slotOffset) {
size_t slotIndex = int32StubField(slotOffset);
MDefinition* obj = getOperand(objId);
auto* slots = MSlots::
New(alloc(), obj);
add(slots);
auto* load = MLoadDynamicSlot::
New(alloc(), slots, slotIndex);
add(load);
auto* guard = MGuardIsNotObject::
New(alloc(), load);
add(guard);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFixedSlotValue(ObjOperandId objId,
uint32_t offsetOffset,
uint32_t valOffset) {
MDefinition* obj = getOperand(objId);
size_t offset = int32StubField(offsetOffset);
Value val = valueStubField(valOffset);
uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
auto* load = MLoadFixedSlot::
New(alloc(), obj, slotIndex);
add(load);
auto* guard = MGuardValue::
New(alloc(), load, val);
add(guard);
return true;
}
bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId,
uint32_t offsetOffset,
uint32_t valOffset) {
MDefinition* obj = getOperand(objId);
size_t offset = int32StubField(offsetOffset);
Value val = valueStubField(valOffset);
size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
auto* slots = MSlots::
New(alloc(), obj);
add(slots);
auto* load = MLoadDynamicSlot::
New(alloc(), slots, slotIndex);
add(load);
auto* guard = MGuardValue::
New(alloc(), load, val);
add(guard);
return true;
}
bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ObjOperandId resultId,
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* load = MLoadScriptedProxyHandler::
New(alloc(), obj);
add(load);
return defineOperand(resultId, load);
}
bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId,
ValOperandId idId) {
MDefinition* id = getOperand(idId);
auto* ins = MIdToStringOrSymbol::
New(alloc(), id);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId,
uint32_t expectedOffset) {
MDefinition* str = getOperand(strId);
JSString* expected = stringStubField(expectedOffset);
auto* ins = MGuardSpecificAtom::
New(alloc(), str, &expected->asAtom());
add(ins);
setOperand(strId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId,
uint32_t expectedOffset) {
MDefinition* symbol = getOperand(symId);
JS::Symbol* expected = symbolStubField(expectedOffset);
auto* ins = MGuardSpecificSymbol::
New(alloc(), symbol, expected);
add(ins);
setOperand(symId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardSpecificInt32(Int32OperandId numId,
int32_t expected) {
MDefinition* num = getOperand(numId);
auto* ins = MGuardSpecificInt32::
New(alloc(), num, expected);
add(ins);
setOperand(numId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardSpecificValue(ValOperandId valId,
uint32_t expectedOffset) {
MDefinition* val = getOperand(valId);
Value expected = valueStubField(expectedOffset);
auto* ins = MGuardValue::
New(alloc(), val, expected);
add(ins);
setOperand(valId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
uint32_t expectedOffset) {
MDefinition* obj = getOperand(objId);
MDefinition* expected = objectStubField(expectedOffset);
auto* ins = MGuardObjectIdentity::
New(alloc(), obj, expected,
/* bailOnEquality = */ false);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
ObjOperandId objId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
MDefinition* obj = getOperand(objId);
MDefinition* expected = objectStubField(expectedOffset);
uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
uint16_t nargs = nargsAndFlags >> 16;
FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
auto* ins = MGuardSpecificFunction::
New(alloc(), obj, expected, nargs, flags);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFunctionScript(
ObjOperandId funId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
MDefinition* fun = getOperand(funId);
BaseScript* expected = baseScriptStubField(expectedOffset);
uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
uint16_t nargs = nargsAndFlags >> 16;
FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
auto* ins = MGuardFunctionScript::
New(alloc(), fun, expected, nargs, flags);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardStringToIndex(StringOperandId strId,
Int32OperandId resultId) {
MDefinition* str = getOperand(strId);
auto* ins = MGuardStringToIndex::
New(alloc(), str);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardStringToInt32(StringOperandId strId,
Int32OperandId resultId) {
MDefinition* str = getOperand(strId);
auto* ins = MGuardStringToInt32::
New(alloc(), str);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardStringToNumber(StringOperandId strId,
NumberOperandId resultId) {
MDefinition* str = getOperand(strId);
auto* ins = MGuardStringToDouble::
New(alloc(), str);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardNoDenseElements(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardNoDenseElements::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFunctionHasJitEntry(ObjOperandId funId) {
MDefinition* fun = getOperand(funId);
uint16_t expectedFlags = FunctionFlags::HasJitEntryFlags();
uint16_t unexpectedFlags = 0;
auto* ins =
MGuardFunctionFlags::
New(alloc(), fun, expectedFlags, unexpectedFlags);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFunctionHasNoJitEntry(ObjOperandId funId) {
MDefinition* fun = getOperand(funId);
uint16_t expectedFlags = 0;
uint16_t unexpectedFlags = FunctionFlags::HasJitEntryFlags();
auto* ins =
MGuardFunctionFlags::
New(alloc(), fun, expectedFlags, unexpectedFlags);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFunctionIsNonBuiltinCtor(
ObjOperandId funId) {
MDefinition* fun = getOperand(funId);
auto* ins = MGuardFunctionIsNonBuiltinCtor::
New(alloc(), fun);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardFunctionIsConstructor(ObjOperandId funId) {
MDefinition* fun = getOperand(funId);
uint16_t expectedFlags = FunctionFlags::CONSTRUCTOR;
uint16_t unexpectedFlags = 0;
auto* ins =
MGuardFunctionFlags::
New(alloc(), fun, expectedFlags, unexpectedFlags);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardNotClassConstructor(ObjOperandId funId) {
MDefinition* fun = getOperand(funId);
auto* ins =
MGuardFunctionKind::
New(alloc(), fun, FunctionFlags::ClassConstructor,
/*bailOnEquality=*/true);
add(ins);
setOperand(funId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardArrayIsPacked(ObjOperandId arrayId) {
MDefinition* array = getOperand(arrayId);
auto* ins = MGuardArrayIsPacked::
New(alloc(), array);
add(ins);
setOperand(arrayId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardArgumentsObjectFlags(ObjOperandId objId,
uint8_t flags) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardArgumentsObjectFlags::
New(alloc(), obj, flags);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardNonDoubleType(ValOperandId inputId,
ValueType type) {
switch (type) {
case ValueType::String:
case ValueType::Symbol:
case ValueType::BigInt:
case ValueType::Int32:
case ValueType::Boolean:
return emitGuardTo(inputId, MIRTypeFromValueType(JSValueType(type)));
case ValueType::Undefined:
return emitGuardIsUndefined(inputId);
case ValueType::Null:
return emitGuardIsNull(inputId);
case ValueType::
Double:
case ValueType::Magic:
case ValueType::PrivateGCThing:
case ValueType::Object:
#ifdef ENABLE_RECORD_TUPLE
case ValueType::ExtendedPrimitive:
#endif
break;
}
MOZ_CRASH(
"unexpected type");
}
bool WarpCacheIRTranspiler::emitGuardTo(ValOperandId inputId, MIRType type) {
MDefinition* def = getOperand(inputId);
if (def->type() == type) {
return true;
}
auto* ins = MUnbox::
New(alloc(), def, type, MUnbox::Fallible);
add(ins);
setOperand(inputId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardToObject(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::Object);
}
bool WarpCacheIRTranspiler::emitGuardToString(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::String);
}
bool WarpCacheIRTranspiler::emitGuardToSymbol(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::Symbol);
}
bool WarpCacheIRTranspiler::emitGuardToBigInt(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::BigInt);
}
bool WarpCacheIRTranspiler::emitGuardToBoolean(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::Boolean);
}
bool WarpCacheIRTranspiler::emitGuardToInt32(ValOperandId inputId) {
return emitGuardTo(inputId, MIRType::Int32);
}
bool WarpCacheIRTranspiler::emitGuardBooleanToInt32(ValOperandId inputId,
Int32OperandId resultId) {
if (!emitGuardTo(inputId, MIRType::Boolean)) {
return false;
}
MDefinition* input = getOperand(inputId);
MOZ_ASSERT(input->type() == MIRType::Boolean);
auto* ins = MBooleanToInt32::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardIsNumber(ValOperandId inputId) {
MDefinition* def = getOperand(inputId);
// No guard needed when the input is already a number type.
if (IsNumberType(def->type())) {
return true;
}
// MIRType::Double also implies int32 in Ion.
return emitGuardTo(inputId, MIRType::
Double);
}
bool WarpCacheIRTranspiler::emitGuardIsNullOrUndefined(ValOperandId inputId) {
MDefinition* input = getOperand(inputId);
if (input->type() == MIRType::Null || input->type() == MIRType::Undefined) {
return true;
}
auto* ins = MGuardNullOrUndefined::
New(alloc(), input);
add(ins);
setOperand(inputId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsNull(ValOperandId inputId) {
MDefinition* input = getOperand(inputId);
if (input->type() == MIRType::Null) {
return true;
}
auto* ins = MGuardValue::
New(alloc(), input, NullValue());
add(ins);
setOperand(inputId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsUndefined(ValOperandId inputId) {
MDefinition* input = getOperand(inputId);
if (input->type() == MIRType::Undefined) {
return true;
}
auto* ins = MGuardValue::
New(alloc(), input, UndefinedValue());
add(ins);
setOperand(inputId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsExtensible(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* ins = MGuardIsExtensible::
New(alloc(), obj);
add(ins);
setOperand(objId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardInt32IsNonNegative(
Int32OperandId indexId) {
MDefinition* index = getOperand(indexId);
auto* ins = MGuardInt32IsNonNegative::
New(alloc(), index);
add(ins);
setOperand(indexId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIndexIsNotDenseElement(
ObjOperandId objId, Int32OperandId indexId) {
MDefinition* obj = getOperand(objId);
MDefinition* index = getOperand(indexId);
auto* ins = MGuardIndexIsNotDenseElement::
New(alloc(), obj, index);
add(ins);
setOperand(indexId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIndexIsValidUpdateOrAdd(
ObjOperandId objId, Int32OperandId indexId) {
MDefinition* obj = getOperand(objId);
MDefinition* index = getOperand(indexId);
auto* ins = MGuardIndexIsValidUpdateOrAdd::
New(alloc(), obj, index);
add(ins);
setOperand(indexId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitCallAddOrUpdateSparseElementHelper(
ObjOperandId objId, Int32OperandId idId, ValOperandId rhsId,
bool strict) {
MDefinition* obj = getOperand(objId);
MDefinition* id = getOperand(idId);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MCallAddOrUpdateSparseElement::
New(alloc(), obj, id, rhs, strict);
addEffectful(ins);
return resumeAfter(ins);
}
bool WarpCacheIRTranspiler::emitGuardTagNotEqual(ValueTagOperandId lhsId,
ValueTagOperandId rhsId) {
MDefinition* lhs = getOperand(lhsId);
MDefinition* rhs = getOperand(rhsId);
auto* ins = MGuardTagNotEqual::
New(alloc(), lhs, rhs);
add(ins);
return true;
}
bool WarpCacheIRTranspiler::emitGuardToInt32Index(ValOperandId inputId,
Int32OperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins =
MToNumberInt32::
New(alloc(), input, IntConversionInputKind::NumbersOnly);
// ToPropertyKey(-0) is "0", so we can silently convert -0 to 0 here.
ins->setNeedsNegativeZeroCheck(
false);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitTruncateDoubleToUInt32(
NumberOperandId inputId, Int32OperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins = MTruncateToInt32::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitDoubleToUint8Clamped(NumberOperandId inputId,
Int32OperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins = MClampToUint8::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardToInt32ModUint32(ValOperandId valId,
Int32OperandId resultId) {
MDefinition* input = getOperand(valId);
auto* ins = MTruncateToInt32::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardToUint8Clamped(ValOperandId valId,
Int32OperandId resultId) {
MDefinition* input = getOperand(valId);
auto* ins = MClampToUint8::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitToString(OperandId inputId,
StringOperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins =
MToString::
New(alloc(), input, MToString::SideEffectHandling::Bailout);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitInt32ToIntPtr(Int32OperandId inputId,
IntPtrOperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins = MInt32ToIntPtr::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitGuardNumberToIntPtrIndex(
NumberOperandId inputId,
bool supportOOB, IntPtrOperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins = MGuardNumberToIntPtrIndex::
New(alloc(), input, supportOOB);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitCallInt32ToString(Int32OperandId inputId,
StringOperandId resultId) {
return emitToString(inputId, resultId);
}
bool WarpCacheIRTranspiler::emitCallNumberToString(NumberOperandId inputId,
StringOperandId resultId) {
return emitToString(inputId, resultId);
}
bool WarpCacheIRTranspiler::emitInt32ToStringWithBaseResult(
Int32OperandId inputId, Int32OperandId baseId) {
MDefinition* input = getOperand(inputId);
MDefinition* base = getOperand(baseId);
auto* guardedBase = MGuardInt32Range::
New(alloc(), base, 2, 36);
add(guardedBase);
// Use lower-case characters by default.
constexpr
bool lower =
true;
auto* ins = MInt32ToStringWithBase::
New(alloc(), input, guardedBase, lower);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitBooleanToString(BooleanOperandId inputId,
StringOperandId resultId) {
return emitToString(inputId, resultId);
}
bool WarpCacheIRTranspiler::emitBooleanToNumber(BooleanOperandId inputId,
NumberOperandId resultId) {
MDefinition* input = getOperand(inputId);
auto* ins = MToDouble::
New(alloc(), input);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitStringToAtom(StringOperandId strId) {
MDefinition* str = getOperand(strId);
auto* ins = MToHashableString::
New(alloc(), str);
add(ins);
setOperand(strId, ins);
return true;
}
bool WarpCacheIRTranspiler::emitLoadInt32Result(Int32OperandId valId) {
MDefinition* val = getOperand(valId);
MOZ_ASSERT(val->type() == MIRType::Int32);
pushResult(val);
return true;
}
bool WarpCacheIRTranspiler::emitLoadDoubleResult(NumberOperandId valId) {
MDefinition* val = getOperand(valId);
MOZ_ASSERT(IsNumberType(val->type()));
if (val->type() != MIRType::
Double) {
auto* ins = MToDouble::
New(alloc(), val);
add(ins);
val = ins;
}
pushResult(val);
return true;
}
bool WarpCacheIRTranspiler::emitLoadBigIntResult(BigIntOperandId valId) {
MDefinition* val = getOperand(valId);
MOZ_ASSERT(val->type() == MIRType::BigInt);
pushResult(val);
return true;
}
bool WarpCacheIRTranspiler::emitLoadObjectResult(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
MOZ_ASSERT(obj->type() == MIRType::Object);
pushResult(obj);
return true;
}
bool WarpCacheIRTranspiler::emitLoadStringResult(StringOperandId strId) {
MDefinition* str = getOperand(strId);
MOZ_ASSERT(str->type() == MIRType::String);
pushResult(str);
return true;
}
bool WarpCacheIRTranspiler::emitLoadSymbolResult(SymbolOperandId symId) {
MDefinition* sym = getOperand(symId);
MOZ_ASSERT(sym->type() == MIRType::Symbol);
pushResult(sym);
return true;
}
bool WarpCacheIRTranspiler::emitLoadUndefinedResult() {
pushResult(constant(UndefinedValue()));
return true;
}
bool WarpCacheIRTranspiler::emitLoadBooleanResult(
bool val) {
pushResult(constant(BooleanValue(val)));
return true;
}
bool WarpCacheIRTranspiler::emitLoadInt32Constant(uint32_t valOffset,
Int32OperandId resultId) {
int32_t val = int32StubField(valOffset);
auto* valConst = constant(Int32Value(val));
return defineOperand(resultId, valConst);
}
bool WarpCacheIRTranspiler::emitLoadDoubleConstant(uint32_t valOffset,
NumberOperandId resultId) {
double val = doubleStubField(valOffset);
auto* valConst = constant(DoubleValue(val));
return defineOperand(resultId, valConst);
}
bool WarpCacheIRTranspiler::emitLoadBooleanConstant(
bool val,
BooleanOperandId resultId) {
auto* valConst = constant(BooleanValue(val));
return defineOperand(resultId, valConst);
}
bool WarpCacheIRTranspiler::emitLoadUndefined(ValOperandId resultId) {
auto* valConst = constant(UndefinedValue());
return defineOperand(resultId, valConst);
}
bool WarpCacheIRTranspiler::emitLoadConstantString(uint32_t strOffset,
StringOperandId resultId) {
JSString* val = stringStubField(strOffset);
auto* valConst = constant(StringValue(val));
return defineOperand(resultId, valConst);
}
bool WarpCacheIRTranspiler::emitLoadConstantStringResult(uint32_t strOffset) {
JSString* val = stringStubField(strOffset);
auto* valConst = constant(StringValue(val));
pushResult(valConst);
return true;
}
bool WarpCacheIRTranspiler::emitLoadTypeOfObjectResult(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* typeOf = MTypeOf::
New(alloc(), obj);
add(typeOf);
auto* ins = MTypeOfName::
New(alloc(), typeOf);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitLoadTypeOfEqObjectResult(
ObjOperandId objId, TypeofEqOperand operand) {
MDefinition* obj = getOperand(objId);
auto* typeOf = MTypeOf::
New(alloc(), obj);
add(typeOf);
auto* typeInt = MConstant::
New(alloc(), Int32Value(operand.type()));
add(typeInt);
auto* ins = MCompare::
New(alloc(), typeOf, typeInt, operand.compareOp(),
MCompare::Compare_Int32);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
ObjOperandId objId, ObjOperandId resultId) {
MDefinition* env = getOperand(objId);
auto* ins = MEnclosingEnvironment::
New(alloc(), env);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId,
uint32_t objOffset) {
MInstruction* ins = objectStubField(objOffset);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadProtoObject(ObjOperandId resultId,
uint32_t objOffset,
ObjOperandId receiverObjId) {
MInstruction* ins = objectStubField(objOffset);
if (ins->isConstant()) {
MDefinition* receiverObj = getOperand(receiverObjId);
ins = MConstantProto::
New(alloc(), ins, receiverObj->skipObjectGuards());
add(ins);
}
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadProto(ObjOperandId objId,
ObjOperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* ins = MObjectStaticProto::
New(alloc(), obj);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadInstanceOfObjectResult(
ValOperandId lhsId, ObjOperandId protoId) {
MDefinition* lhs = getOperand(lhsId);
MDefinition* proto = getOperand(protoId);
auto* instanceOf = MInstanceOf::
New(alloc(), lhs, proto);
addEffectful(instanceOf);
pushResult(instanceOf);
return resumeAfter(instanceOf);
}
bool WarpCacheIRTranspiler::emitLoadValueTag(ValOperandId valId,
ValueTagOperandId resultId) {
MDefinition* val = getOperand(valId);
auto* ins = MLoadValueTag::
New(alloc(), val);
add(ins);
return defineOperand(resultId, ins);
}
bool WarpCacheIRTranspiler::emitLoadDynamicSlotResult(ObjOperandId objId,
uint32_t offsetOffset) {
int32_t offset = int32StubField(offsetOffset);
MDefinition* obj = getOperand(objId);
size_t slotIndex = NativeObject::getDynamicSlotIndexFromOffset(offset);
auto* slots = MSlots::
New(alloc(), obj);
add(slots);
auto* load = MLoadDynamicSlot::
New(alloc(), slots, slotIndex);
add(load);
pushResult(load);
return true;
}
bool WarpCacheIRTranspiler::emitLoadFixedSlot(ValOperandId resultId,
ObjOperandId objId,
uint32_t offsetOffset) {
MDefinition* obj = getOperand(objId);
size_t offset = int32StubField(offsetOffset);
uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
auto* load = MLoadFixedSlot::
New(alloc(), obj, slotIndex);
add(load);
return defineOperand(resultId, load);
}
bool WarpCacheIRTranspiler::emitLoadFixedSlotResult(ObjOperandId objId,
uint32_t offsetOffset) {
int32_t offset = int32StubField(offsetOffset);
MDefinition* obj = getOperand(objId);
uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
auto* load = MLoadFixedSlot::
New(alloc(), obj, slotIndex);
add(load);
pushResult(load);
return true;
}
bool WarpCacheIRTranspiler::emitLoadFixedSlotTypedResult(ObjOperandId objId,
uint32_t offsetOffset,
ValueType type) {
int32_t offset = int32StubField(offsetOffset);
MDefinition* obj = getOperand(objId);
uint32_t slotIndex = NativeObject::getFixedSlotIndexFromOffset(offset);
auto* load = MLoadFixedSlot::
New(alloc(), obj, slotIndex);
load->setResultType(MIRTypeFromValueType(JSValueType(type)));
add(load);
pushResult(load);
return true;
}
bool WarpCacheIRTranspiler::emitGuardIsNotUninitializedLexical(
ValOperandId valId) {
MDefinition* val = getOperand(valId);
auto* lexicalCheck = MLexicalCheck::
New(alloc(), val);
add(lexicalCheck);
if (snapshot().bailoutInfo().failedLexicalCheck()) {
lexicalCheck->setNotMovable();
}
setOperand(valId, lexicalCheck);
return true;
}
bool WarpCacheIRTranspiler::emitLoadInt32ArrayLengthResult(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* elements = MElements::
New(alloc(), obj);
add(elements);
auto* length = MArrayLength::
New(alloc(), elements);
add(length);
pushResult(length);
return true;
}
bool WarpCacheIRTranspiler::emitLoadInt32ArrayLength(ObjOperandId objId,
Int32OperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* elements = MElements::
New(alloc(), obj);
add(elements);
auto* length = MArrayLength::
New(alloc(), elements);
add(length);
return defineOperand(resultId, length);
}
bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgResult(
ObjOperandId objId, Int32OperandId indexId) {
MDefinition* obj = getOperand(objId);
MDefinition* index = getOperand(indexId);
auto* load = MLoadArgumentsObjectArg::
New(alloc(), obj, index);
add(load);
pushResult(load);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgHoleResult(
ObjOperandId objId, Int32OperandId indexId) {
MDefinition* obj = getOperand(objId);
MDefinition* index = getOperand(indexId);
auto* load = MLoadArgumentsObjectArgHole::
New(alloc(), obj, index);
add(load);
pushResult(load);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArgumentsObjectArgExistsResult(
ObjOperandId objId, Int32OperandId indexId) {
MDefinition* obj = getOperand(objId);
MDefinition* index = getOperand(indexId);
auto* ins = MInArgumentsObjectArg::
New(alloc(), obj, index);
add(ins);
pushResult(ins);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLengthResult(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* length = MArgumentsObjectLength::
New(alloc(), obj);
add(length);
pushResult(length);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArgumentsObjectLength(
ObjOperandId objId, Int32OperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* length = MArgumentsObjectLength::
New(alloc(), obj);
add(length);
return defineOperand(resultId, length);
}
bool WarpCacheIRTranspiler::emitLoadBoundFunctionNumArgs(
ObjOperandId objId, Int32OperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* numArgs = MBoundFunctionNumArgs::
New(alloc(), obj);
add(numArgs);
return defineOperand(resultId, numArgs);
}
bool WarpCacheIRTranspiler::emitLoadBoundFunctionTarget(ObjOperandId objId,
ObjOperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* target = MLoadFixedSlotAndUnbox::
New(
alloc(), obj, BoundFunctionObject::targetSlot(), MUnbox::Mode::Infallible,
MIRType::Object);
add(target);
return defineOperand(resultId, target);
}
bool WarpCacheIRTranspiler::emitLoadBoundFunctionArgument(
ObjOperandId objId, uint32_t index, ValOperandId resultId) {
MDefinition* obj = getOperand(objId);
auto* boundArgs = MLoadFixedSlotAndUnbox::
New(
alloc(), obj, BoundFunctionObject::firstInlineBoundArgSlot(),
MUnbox::Mode::Infallible, MIRType::Object);
add(boundArgs);
auto* elements = MElements::
New(alloc(), boundArgs);
add(elements);
auto argIndex = constant(Int32Value(index));
auto* load = MLoadElement::
New(alloc(), elements, argIndex);
add(load);
return defineOperand(resultId, load);
}
bool WarpCacheIRTranspiler::emitGuardBoundFunctionIsConstructor(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* guard = MGuardBoundFunctionIsConstructor::
New(alloc(), obj);
add(guard);
setOperand(objId, guard);
return true;
}
bool WarpCacheIRTranspiler::emitGuardObjectIdentity(ObjOperandId obj1Id,
ObjOperandId obj2Id) {
MDefinition* obj1 = getOperand(obj1Id);
MDefinition* obj2 = getOperand(obj2Id);
auto* guard = MGuardObjectIdentity::
New(alloc(), obj1, obj2,
/* bailOnEquality = */ false);
add(guard);
return true;
}
bool WarpCacheIRTranspiler::emitArrayFromArgumentsObjectResult(
ObjOperandId objId, uint32_t shapeOffset) {
MDefinition* obj = getOperand(objId);
Shape* shape = shapeStubField(shapeOffset);
MOZ_ASSERT(shape);
auto* array = MArrayFromArgumentsObject::
New(alloc(), obj, shape);
addEffectful(array);
pushResult(array);
return resumeAfter(array);
}
bool WarpCacheIRTranspiler::emitLoadFunctionLengthResult(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* length = MFunctionLength::
New(alloc(), obj);
add(length);
pushResult(length);
return true;
}
bool WarpCacheIRTranspiler::emitLoadFunctionNameResult(ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* name = MFunctionName::
New(alloc(), obj);
add(name);
pushResult(name);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthInt32Result(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* length = MArrayBufferByteLength::
New(alloc(), obj);
add(length);
auto* lengthInt32 = MNonNegativeIntPtrToInt32::
New(alloc(), length);
add(lengthInt32);
pushResult(lengthInt32);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArrayBufferByteLengthDoubleResult(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* length = MArrayBufferByteLength::
New(alloc(), obj);
add(length);
auto* lengthDouble = MIntPtrToDouble::
New(alloc(), length);
add(lengthDouble);
pushResult(lengthDouble);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthInt32Result(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
// Use a separate instruction for converting the length to Int32, so that we
// can fold the MArrayBufferViewLength instruction with length instructions
// added for bounds checks.
auto* length = MArrayBufferViewLength::
New(alloc(), obj);
add(length);
auto* lengthInt32 = MNonNegativeIntPtrToInt32::
New(alloc(), length);
add(lengthInt32);
pushResult(lengthInt32);
return true;
}
bool WarpCacheIRTranspiler::emitLoadArrayBufferViewLengthDoubleResult(
ObjOperandId objId) {
MDefinition* obj = getOperand(objId);
auto* length = MArrayBufferViewLength::
New(alloc(), obj);
add(length);
auto* lengthDouble = MIntPtrToDouble::
New(alloc(), length);
add(lengthDouble);
pushResult(lengthDouble);
--> --------------------
--> maximum size reached
--> --------------------