Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/*
* JavaScript "portable baseline interpreter": an interpreter that is
* capable of running ICs, but without any native code.
*
* See the [SMDOC] in vm/PortableBaselineInterpret.h for a high-level
* overview.
*/
#include "vm/PortableBaselineInterpret.h"
#include "mozilla/Maybe.h"
#include <algorithm>
#include "fdlibm.h"
#include "jsapi.h"
#include "builtin/DataViewObject.h"
#include "builtin/MapObject.h"
#include "builtin/Object.h"
#include "builtin/RegExp.h"
#include "builtin/String.h"
#include "debugger/DebugAPI.h"
#include "jit/BaselineFrame.h"
#include "jit/BaselineIC.h"
#include "jit/BaselineJIT.h"
#include "jit/CacheIR.h"
#include "jit/CacheIRCompiler.h"
#include "jit/CacheIRReader.h"
#include "jit/JitFrames.h"
#include "jit/JitScript.h"
#include "jit/JSJitFrameIter.h"
#include "jit/VMFunctions.h"
#include "proxy/DeadObjectProxy.h"
#include "proxy/DOMProxy.h"
#include "util/Unicode.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/DateObject.h"
#include "vm/EnvironmentObject.h"
#include "vm/EqualityOperations.h"
#include "vm/GeneratorObject.h"
#include "vm/Interpreter.h"
#include "vm/Iteration.h"
#include "vm/JitActivation.h"
#include "vm/JSObject.h"
#include "vm/JSScript.h"
#include "vm/Opcodes.h"
#include "vm/PlainObject.h"
#include "vm/Shape.h"
#include "vm/TypeofEqOperand.h" // TypeofEqOperand
#include "debugger/DebugAPI-inl.h"
#include "jit/BaselineFrame-inl.h"
#include "jit/JitScript-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/PlainObject-inl.h"
namespace js {
namespace pbl {
using namespace js::jit;
/*
* Debugging: enable `TRACE_INTERP` for an extremely detailed dump of
* what PBL is doing at every opcode step.
*/
// #define TRACE_INTERP
#ifdef TRACE_INTERP
# define TRACE_PRINTF(...) \
do { \
printf(__VA_ARGS__); \
fflush(stdout); \
} while (0)
#else
# define TRACE_PRINTF(...) \
do { \
} while (0)
#endif
#define PBL_HYBRID_ICS_DEFAULT true
// Whether we are using the "hybrid" strategy for ICs (see the [SMDOC]
// in PortableBaselineInterpret.h for more). This is currently a
// constant, but may become configurable in the future.
static const bool kHybridICsInterp = PBL_HYBRID_ICS_DEFAULT;
// Whether to compile interpreter dispatch loops using computed gotos
// or direct switches.
#if !defined(__wasi__) && !defined(TRACE_INTERP)
# define ENABLE_COMPUTED_GOTO_DISPATCH
#endif
// Whether to compile in interrupt checks in the main interpreter loop.
#ifndef __wasi__
// On WASI, with a single thread, there is no possibility for an
// interrupt to come asynchronously.
# define ENABLE_INTERRUPT_CHECKS
#endif
// Whether to compile in coverage counting in the main interpreter loop.
#ifndef __wasi__
# define ENABLE_COVERAGE
#endif
/*
* -----------------------------------------------
* Stack handling
* -----------------------------------------------
*/
// Large enough for an exit frame.
static const size_t kStackMargin = 1024;
/*
* A 64-bit value on the auxiliary stack. May either be a raw uint64_t
* or a `Value` (JS NaN-boxed value).
*/
struct StackVal {
uint64_t value;
explicit StackVal(uint64_t v) : value(v) {}
explicit StackVal(const Value& v) : value(v.asRawBits()) {}
uint64_t asUInt64() const { return value; }
Value asValue() const { return Value::fromRawBits(value); }
};
/*
* A native-pointer-sized value on the auxiliary stack. This is
* separate from the above because we support running on 32-bit
* systems as well! May either be a `void*` (or cast to a
* `CalleeToken`, which is a typedef for a `void*`), or a `uint32_t`,
* which always fits in a native pointer width on our supported
* platforms. (See static_assert below.)
*/
struct StackValNative {
static_assert(sizeof(uintptr_t) >= sizeof(uint32_t),
"Must be at least a 32-bit system to use PBL.");
uintptr_t value;
explicit StackValNative(void* v) : value(reinterpret_cast<uintptr_t>(v)) {}
explicit StackValNative(uint32_t v) : value(v) {}
void* asVoidPtr() const { return reinterpret_cast<void*>(value); }
CalleeToken asCalleeToken() const {
return reinterpret_cast<CalleeToken>(value);
}
};
// Assert that the stack alignment is no more than the size of a
// StackValNative -- we rely on this when setting up call frames.
static_assert(JitStackAlignment <= sizeof(StackValNative));
#define PUSH(val) *--sp = (val)
#define POP() (*sp++)
#define POPN(n) sp += (n)
#define PUSHNATIVE(val) \
do { \
StackValNative* nativeSP = reinterpret_cast<StackValNative*>(sp); \
*--nativeSP = (val); \
sp = reinterpret_cast<StackVal*>(nativeSP); \
} while (0)
#define POPNNATIVE(n) \
sp = reinterpret_cast<StackVal*>(reinterpret_cast<StackValNative*>(sp) + (n))
/*
* Helper class to manage the auxiliary stack and push/pop frames.
*/
struct Stack {
StackVal* fp;
StackVal* base;
StackVal* top;
StackVal* unwindingSP;
StackVal* unwindingFP;
explicit Stack(PortableBaselineStack& pbs)
: fp(reinterpret_cast<StackVal*>(pbs.top)),
base(reinterpret_cast<StackVal*>(pbs.base)),
top(reinterpret_cast<StackVal*>(pbs.top)),
unwindingSP(nullptr),
unwindingFP(nullptr) {}
MOZ_ALWAYS_INLINE bool check(StackVal* sp, size_t size, bool margin = true) {
return reinterpret_cast<uintptr_t>(base) + size +
(margin ? kStackMargin : 0) <=
reinterpret_cast<uintptr_t>(sp);
}
[[nodiscard]] MOZ_ALWAYS_INLINE StackVal* allocate(StackVal* sp,
size_t size) {
if (!check(sp, size, false)) {
return nullptr;
}
sp = reinterpret_cast<StackVal*>(reinterpret_cast<uintptr_t>(sp) - size);
return sp;
}
uint32_t frameSize(StackVal* sp, BaselineFrame* curFrame) const {
return sizeof(StackVal) * (reinterpret_cast<StackVal*>(fp) - sp);
}
[[nodiscard]] MOZ_ALWAYS_INLINE BaselineFrame* pushFrame(StackVal* sp,
JSContext* cx,
JSObject* envChain) {
TRACE_PRINTF("pushFrame: sp = %p fp = %p\n", sp, fp);
if (sp == base) {
return nullptr;
}
PUSHNATIVE(StackValNative(fp));
fp = sp;
TRACE_PRINTF("pushFrame: new fp = %p\n", fp);
BaselineFrame* frame =
reinterpret_cast<BaselineFrame*>(allocate(sp, BaselineFrame::Size()));
if (!frame) {
return nullptr;
}
frame->setFlags(BaselineFrame::Flags::RUNNING_IN_INTERPRETER);
frame->setEnvironmentChain(envChain);
JSScript* script = frame->script();
frame->setICScript(script->jitScript()->icScript());
frame->setInterpreterFieldsForPrologue(script);
#ifdef DEBUG
frame->setDebugFrameSize(0);
#endif
return frame;
}
StackVal* popFrame() {
StackVal* newTOS =
reinterpret_cast<StackVal*>(reinterpret_cast<StackValNative*>(fp) + 1);
fp = reinterpret_cast<StackVal*>(
reinterpret_cast<StackValNative*>(fp)->asVoidPtr());
MOZ_ASSERT(fp);
TRACE_PRINTF("popFrame: fp = %p\n", fp);
return newTOS;
}
void setFrameSize(StackVal* sp, BaselineFrame* prevFrame) {
#ifdef DEBUG
MOZ_ASSERT(fp != nullptr);
uintptr_t frameSize =
reinterpret_cast<uintptr_t>(fp) - reinterpret_cast<uintptr_t>(sp);
MOZ_ASSERT(reinterpret_cast<uintptr_t>(fp) >=
reinterpret_cast<uintptr_t>(sp));
TRACE_PRINTF("pushExitFrame: fp = %p cur() = %p -> frameSize = %d\n", fp,
sp, int(frameSize));
MOZ_ASSERT(frameSize >= BaselineFrame::Size());
prevFrame->setDebugFrameSize(frameSize);
#endif
}
[[nodiscard]] MOZ_ALWAYS_INLINE StackVal* pushExitFrame(
StackVal* sp, BaselineFrame* prevFrame) {
uint8_t* prevFP =
reinterpret_cast<uint8_t*>(prevFrame) + BaselineFrame::Size();
TRACE_PRINTF(
"pushExitFrame: prevFrame = %p sp = %p BaselineFrame::Size() = %d -> "
"computed prevFP = %p actual fp = %p\n",
prevFrame, sp, int(BaselineFrame::Size()), prevFP, fp);
MOZ_ASSERT(reinterpret_cast<StackVal*>(prevFP) == fp);
setFrameSize(sp, prevFrame);
if (!check(sp, sizeof(StackVal) * 4, false)) {
return nullptr;
}
PUSHNATIVE(StackValNative(
MakeFrameDescriptorForJitCall(FrameType::BaselineJS, 0)));
PUSHNATIVE(StackValNative(nullptr)); // fake return address.
PUSHNATIVE(StackValNative(prevFP));
StackVal* exitFP = sp;
fp = exitFP;
TRACE_PRINTF(" -> fp = %p\n", fp);
PUSHNATIVE(StackValNative(uint32_t(ExitFrameType::Bare)));
return exitFP;
}
void popExitFrame(StackVal* fp) {
StackVal* prevFP = reinterpret_cast<StackVal*>(
reinterpret_cast<StackValNative*>(fp)->asVoidPtr());
MOZ_ASSERT(prevFP);
this->fp = prevFP;
TRACE_PRINTF("popExitFrame: fp -> %p\n", fp);
}
BaselineFrame* frameFromFP() {
return reinterpret_cast<BaselineFrame*>(reinterpret_cast<uintptr_t>(fp) -
BaselineFrame::Size());
}
static HandleValue handle(StackVal* sp) {
return HandleValue::fromMarkedLocation(reinterpret_cast<Value*>(sp));
}
static MutableHandleValue handleMut(StackVal* sp) {
return MutableHandleValue::fromMarkedLocation(reinterpret_cast<Value*>(sp));
}
};
/*
* -----------------------------------------------
* Interpreter state
* -----------------------------------------------
*/
struct ICRegs {
static const int kMaxICVals = 16;
// Values can be split across two OR'd halves: unboxed bits and
// tags. We mostly rely on the CacheIRWriter/Reader typed OperandId
// system to ensure "type safety" in CacheIR w.r.t. unboxing: the
// existence of an ObjOperandId implies that the value is unboxed,
// so `icVals` contains a pointer (reinterpret-casted to a
// `uint64_t`) and `icTags` contains the tag bits. An operator that
// requires a tagged Value can OR the two together (this corresponds
// to `useValueRegister` rather than `useRegister` in the native
// baseline compiler).
uint64_t icVals[kMaxICVals];
uint64_t icTags[kMaxICVals]; // Shifted tags.
int extraArgs;
};
struct State {
RootedValue value0;
RootedValue value1;
RootedValue value2;
RootedValue value3;
RootedValue res;
RootedObject obj0;
RootedObject obj1;
RootedObject obj2;
RootedString str0;
RootedString str1;
RootedString str2;
RootedScript script0;
Rooted<PropertyName*> name0;
Rooted<jsid> id0;
Rooted<JSAtom*> atom0;
RootedFunction fun0;
Rooted<Scope*> scope0;
explicit State(JSContext* cx)
: value0(cx),
value1(cx),
value2(cx),
value3(cx),
res(cx),
obj0(cx),
obj1(cx),
obj2(cx),
str0(cx),
str1(cx),
str2(cx),
script0(cx),
name0(cx),
id0(cx),
atom0(cx),
fun0(cx),
scope0(cx) {}
};
/*
* -----------------------------------------------
* RAII helpers for pushing exit frames.
*
* (See [SMDOC] in PortableBaselineInterpret.h for more.)
* -----------------------------------------------
*/
class VMFrameManager {
JSContext* cx;
BaselineFrame* frame;
friend class VMFrame;
public:
VMFrameManager(JSContext*& cx_, BaselineFrame* frame_)
: cx(cx_), frame(frame_) {
// Once the manager exists, we need to create an exit frame to
// have access to the cx (unless the caller promises it is not
// calling into the rest of the runtime).
cx_ = nullptr;
}
void switchToFrame(BaselineFrame* frame) { this->frame = frame; }
// Provides the JSContext, but *only* if no calls into the rest of
// the runtime (that may invoke a GC or stack walk) occur. Avoids
// the overhead of pushing an exit frame.
JSContext* cxForLocalUseOnly() const { return cx; }
};
class VMFrame {
JSContext* cx;
Stack& stack;
StackVal* exitFP;
void* prevSavedStack;
public:
VMFrame(VMFrameManager& mgr, Stack& stack_, StackVal* sp)
: cx(mgr.cx), stack(stack_) {
exitFP = stack.pushExitFrame(sp, mgr.frame);
if (!exitFP) {
return;
}
cx->activation()->asJit()->setJSExitFP(reinterpret_cast<uint8_t*>(exitFP));
prevSavedStack = cx->portableBaselineStack().top;
cx->portableBaselineStack().top = reinterpret_cast<void*>(spBelowFrame());
}
StackVal* spBelowFrame() {
return reinterpret_cast<StackVal*>(reinterpret_cast<uintptr_t>(exitFP) -
sizeof(StackValNative));
}
~VMFrame() {
stack.popExitFrame(exitFP);
cx->portableBaselineStack().top = prevSavedStack;
}
JSContext* getCx() const { return cx; }
operator JSContext*() const { return cx; }
bool success() const { return exitFP != nullptr; }
};
#define PUSH_EXIT_FRAME_OR_RET(value, init_sp) \
VMFrame cx(ctx.frameMgr, ctx.stack, init_sp); \
if (!cx.success()) { \
return value; \
} \
StackVal* sp = cx.spBelowFrame(); /* shadow the definition */ \
(void)sp; /* avoid unused-variable warnings */
#define PUSH_IC_FRAME() \
ctx.error = PBIResult::Error; \
PUSH_EXIT_FRAME_OR_RET(IC_ERROR_SENTINEL(), ctx.sp())
#define PUSH_FALLBACK_IC_FRAME() \
ctx.error = PBIResult::Error; \
PUSH_EXIT_FRAME_OR_RET(IC_ERROR_SENTINEL(), sp)
#define PUSH_EXIT_FRAME() \
frame->interpreterPC() = pc; \
SYNCSP(); \
PUSH_EXIT_FRAME_OR_RET(PBIResult::Error, sp)
/*
* -----------------------------------------------
* IC Interpreter
* -----------------------------------------------
*/
// Bundled state for passing to ICs, in order to reduce the number of
// arguments and hence make the call more ABI-efficient. (On some
// platforms, e.g. Wasm on Wasmtime on x86-64, we have as few as four
// register arguments available before args go through the stack.)
struct ICCtx {
BaselineFrame* frame;
VMFrameManager frameMgr;
State& state;
ICRegs icregs;
Stack& stack;
StackVal* sp_;
PBIResult error;
uint64_t arg2;
ICCtx(JSContext* cx, BaselineFrame* frame_, State& state_, Stack& stack_)
: frame(frame_),
frameMgr(cx, frame_),
state(state_),
icregs(),
stack(stack_),
sp_(nullptr),
error(PBIResult::Ok),
arg2(0) {}
StackVal* sp() { return sp_; }
};
#define IC_ERROR_SENTINEL() (JS::MagicValue(JS_GENERIC_MAGIC).asRawBits())
// Universal signature for an IC stub function.
typedef uint64_t (*ICStubFunc)(uint64_t arg0, uint64_t arg1, ICStub* stub,
ICCtx& ctx);
#define PBL_CALL_IC(jitcode, ctx, stubvalue, result, arg0, arg1, arg2value, \
hasarg2) \
do { \
ctx.arg2 = arg2value; \
ICStubFunc func = reinterpret_cast<ICStubFunc>(jitcode); \
result = func(arg0, arg1, stubvalue, ctx); \
} while (0)
typedef PBIResult (*PBIFunc)(JSContext* cx_, State& state, Stack& stack,
StackVal* sp, JSObject* envChain, Value* ret,
jsbytecode* pc, ImmutableScriptData* isd,
jsbytecode* restartEntryPC,
BaselineFrame* restartFrame,
StackVal* restartEntryFrame,
PBIResult restartCode);
static uint64_t CallNextIC(uint64_t arg0, uint64_t arg1, ICStub* stub,
ICCtx& ctx);
static double DoubleMinMax(bool isMax, double first, double second) {
if (std::isnan(first) || std::isnan(second)) {
return JS::GenericNaN();
} else if (first == 0 && second == 0) {
// -0 and 0 compare as equal, but we have to distinguish
// them here: min(-0, 0) = -0, max(-0, 0) = 0.
bool firstPos = !std::signbit(first);
bool secondPos = !std::signbit(second);
bool sign = isMax ? (firstPos || secondPos) : (firstPos && secondPos);
return sign ? 0.0 : -0.0;
} else {
return isMax ? ((first >= second) ? first : second)
: ((first <= second) ? first : second);
}
}
// Interpreter for CacheIR.
uint64_t ICInterpretOps(uint64_t arg0, uint64_t arg1, ICStub* stub,
ICCtx& ctx) {
{
#define DECLARE_CACHEOP_CASE(name) __label__ cacheop_##name
#ifdef ENABLE_COMPUTED_GOTO_DISPATCH
# define CACHEOP_CASE(name) cacheop_##name : CACHEOP_TRACE(name)
# define CACHEOP_CASE_FALLTHROUGH(name) CACHEOP_CASE(name)
# define DISPATCH_CACHEOP() \
cacheop = cacheIRReader.readOp(); \
goto* addresses[long(cacheop)];
#else // ENABLE_COMPUTED_GOTO_DISPATCH
# define CACHEOP_CASE(name) \
case CacheOp::name: \
cacheop_##name : CACHEOP_TRACE(name)
# define CACHEOP_CASE_FALLTHROUGH(name) \
[[fallthrough]]; \
CACHEOP_CASE(name)
# define DISPATCH_CACHEOP() \
cacheop = cacheIRReader.readOp(); \
goto dispatch;
#endif // !ENABLE_COMPUTED_GOTO_DISPATCH
#define READ_REG(index) ctx.icregs.icVals[(index)]
#define READ_VALUE_REG(index) \
Value::fromRawBits(ctx.icregs.icVals[(index)] | ctx.icregs.icTags[(index)])
#define WRITE_REG(index, value, tag) \
do { \
ctx.icregs.icVals[(index)] = (value); \
ctx.icregs.icTags[(index)] = uint64_t(JSVAL_TAG_##tag) << JSVAL_TAG_SHIFT; \
} while (0)
#define WRITE_VALUE_REG(index, value) \
do { \
ctx.icregs.icVals[(index)] = (value).asRawBits(); \
ctx.icregs.icTags[(index)] = 0; \
} while (0)
DECLARE_CACHEOP_CASE(ReturnFromIC);
DECLARE_CACHEOP_CASE(GuardToObject);
DECLARE_CACHEOP_CASE(GuardIsNullOrUndefined);
DECLARE_CACHEOP_CASE(GuardIsNull);
DECLARE_CACHEOP_CASE(GuardIsUndefined);
DECLARE_CACHEOP_CASE(GuardIsNotUninitializedLexical);
DECLARE_CACHEOP_CASE(GuardToBoolean);
DECLARE_CACHEOP_CASE(GuardToString);
DECLARE_CACHEOP_CASE(GuardToSymbol);
DECLARE_CACHEOP_CASE(GuardToBigInt);
DECLARE_CACHEOP_CASE(GuardIsNumber);
DECLARE_CACHEOP_CASE(GuardToInt32);
DECLARE_CACHEOP_CASE(GuardToNonGCThing);
DECLARE_CACHEOP_CASE(GuardBooleanToInt32);
DECLARE_CACHEOP_CASE(GuardToInt32Index);
DECLARE_CACHEOP_CASE(Int32ToIntPtr);
DECLARE_CACHEOP_CASE(GuardToInt32ModUint32);
DECLARE_CACHEOP_CASE(GuardNonDoubleType);
DECLARE_CACHEOP_CASE(GuardShape);
DECLARE_CACHEOP_CASE(GuardFuse);
DECLARE_CACHEOP_CASE(GuardProto);
DECLARE_CACHEOP_CASE(GuardNullProto);
DECLARE_CACHEOP_CASE(GuardClass);
DECLARE_CACHEOP_CASE(GuardAnyClass);
DECLARE_CACHEOP_CASE(GuardGlobalGeneration);
DECLARE_CACHEOP_CASE(HasClassResult);
DECLARE_CACHEOP_CASE(GuardCompartment);
DECLARE_CACHEOP_CASE(GuardIsExtensible);
DECLARE_CACHEOP_CASE(GuardIsNativeObject);
DECLARE_CACHEOP_CASE(GuardIsProxy);
DECLARE_CACHEOP_CASE(GuardIsNotProxy);
DECLARE_CACHEOP_CASE(GuardIsNotArrayBufferMaybeShared);
DECLARE_CACHEOP_CASE(GuardIsTypedArray);
DECLARE_CACHEOP_CASE(GuardHasProxyHandler);
DECLARE_CACHEOP_CASE(GuardIsNotDOMProxy);
DECLARE_CACHEOP_CASE(GuardSpecificObject);
DECLARE_CACHEOP_CASE(GuardObjectIdentity);
DECLARE_CACHEOP_CASE(GuardSpecificFunction);
DECLARE_CACHEOP_CASE(GuardFunctionScript);
DECLARE_CACHEOP_CASE(GuardSpecificAtom);
DECLARE_CACHEOP_CASE(GuardSpecificSymbol);
DECLARE_CACHEOP_CASE(GuardSpecificInt32);
DECLARE_CACHEOP_CASE(GuardNoDenseElements);
DECLARE_CACHEOP_CASE(GuardStringToIndex);
DECLARE_CACHEOP_CASE(GuardStringToInt32);
DECLARE_CACHEOP_CASE(GuardStringToNumber);
DECLARE_CACHEOP_CASE(BooleanToNumber);
DECLARE_CACHEOP_CASE(GuardHasGetterSetter);
DECLARE_CACHEOP_CASE(GuardInt32IsNonNegative);
DECLARE_CACHEOP_CASE(GuardDynamicSlotIsSpecificObject);
DECLARE_CACHEOP_CASE(GuardDynamicSlotIsNotObject);
DECLARE_CACHEOP_CASE(GuardFixedSlotValue);
DECLARE_CACHEOP_CASE(GuardDynamicSlotValue);
DECLARE_CACHEOP_CASE(LoadFixedSlot);
DECLARE_CACHEOP_CASE(LoadDynamicSlot);
DECLARE_CACHEOP_CASE(GuardNoAllocationMetadataBuilder);
DECLARE_CACHEOP_CASE(GuardFunctionHasJitEntry);
DECLARE_CACHEOP_CASE(GuardFunctionHasNoJitEntry);
DECLARE_CACHEOP_CASE(GuardFunctionIsNonBuiltinCtor);
DECLARE_CACHEOP_CASE(GuardFunctionIsConstructor);
DECLARE_CACHEOP_CASE(GuardNotClassConstructor);
DECLARE_CACHEOP_CASE(GuardArrayIsPacked);
DECLARE_CACHEOP_CASE(GuardArgumentsObjectFlags);
DECLARE_CACHEOP_CASE(LoadObject);
DECLARE_CACHEOP_CASE(LoadProtoObject);
DECLARE_CACHEOP_CASE(LoadProto);
DECLARE_CACHEOP_CASE(LoadEnclosingEnvironment);
DECLARE_CACHEOP_CASE(LoadWrapperTarget);
DECLARE_CACHEOP_CASE(LoadValueTag);
DECLARE_CACHEOP_CASE(LoadArgumentFixedSlot);
DECLARE_CACHEOP_CASE(LoadArgumentDynamicSlot);
DECLARE_CACHEOP_CASE(TruncateDoubleToUInt32);
DECLARE_CACHEOP_CASE(MegamorphicLoadSlotResult);
DECLARE_CACHEOP_CASE(MegamorphicLoadSlotByValueResult);
DECLARE_CACHEOP_CASE(MegamorphicSetElement);
DECLARE_CACHEOP_CASE(StoreFixedSlot);
DECLARE_CACHEOP_CASE(StoreDynamicSlot);
DECLARE_CACHEOP_CASE(AddAndStoreFixedSlot);
DECLARE_CACHEOP_CASE(AddAndStoreDynamicSlot);
DECLARE_CACHEOP_CASE(AllocateAndStoreDynamicSlot);
DECLARE_CACHEOP_CASE(StoreDenseElement);
DECLARE_CACHEOP_CASE(StoreDenseElementHole);
DECLARE_CACHEOP_CASE(ArrayPush);
DECLARE_CACHEOP_CASE(IsObjectResult);
DECLARE_CACHEOP_CASE(Int32MinMax);
DECLARE_CACHEOP_CASE(StoreTypedArrayElement);
DECLARE_CACHEOP_CASE(CallInt32ToString);
DECLARE_CACHEOP_CASE(CallScriptedFunction);
DECLARE_CACHEOP_CASE(CallNativeFunction);
DECLARE_CACHEOP_CASE(MetaScriptedThisShape);
DECLARE_CACHEOP_CASE(LoadFixedSlotResult);
DECLARE_CACHEOP_CASE(LoadDynamicSlotResult);
DECLARE_CACHEOP_CASE(LoadDenseElementResult);
DECLARE_CACHEOP_CASE(LoadInt32ArrayLengthResult);
DECLARE_CACHEOP_CASE(LoadInt32ArrayLength);
DECLARE_CACHEOP_CASE(LoadArgumentsObjectArgResult);
DECLARE_CACHEOP_CASE(LinearizeForCharAccess);
DECLARE_CACHEOP_CASE(LoadStringCharResult);
DECLARE_CACHEOP_CASE(LoadStringCharCodeResult);
DECLARE_CACHEOP_CASE(LoadStringLengthResult);
DECLARE_CACHEOP_CASE(LoadObjectResult);
DECLARE_CACHEOP_CASE(LoadStringResult);
DECLARE_CACHEOP_CASE(LoadSymbolResult);
DECLARE_CACHEOP_CASE(LoadInt32Result);
DECLARE_CACHEOP_CASE(LoadDoubleResult);
DECLARE_CACHEOP_CASE(LoadBigIntResult);
DECLARE_CACHEOP_CASE(LoadBooleanResult);
DECLARE_CACHEOP_CASE(LoadInt32Constant);
DECLARE_CACHEOP_CASE(LoadConstantStringResult);
DECLARE_CACHEOP_CASE(Int32AddResult);
DECLARE_CACHEOP_CASE(Int32SubResult);
DECLARE_CACHEOP_CASE(Int32MulResult);
DECLARE_CACHEOP_CASE(Int32DivResult);
DECLARE_CACHEOP_CASE(Int32ModResult);
DECLARE_CACHEOP_CASE(Int32BitOrResult);
DECLARE_CACHEOP_CASE(Int32BitXorResult);
DECLARE_CACHEOP_CASE(Int32BitAndResult);
DECLARE_CACHEOP_CASE(Int32PowResult);
DECLARE_CACHEOP_CASE(Int32IncResult);
DECLARE_CACHEOP_CASE(LoadInt32TruthyResult);
DECLARE_CACHEOP_CASE(LoadStringTruthyResult);
DECLARE_CACHEOP_CASE(LoadObjectTruthyResult);
DECLARE_CACHEOP_CASE(LoadValueResult);
DECLARE_CACHEOP_CASE(LoadOperandResult);
DECLARE_CACHEOP_CASE(CallStringConcatResult);
DECLARE_CACHEOP_CASE(CompareStringResult);
DECLARE_CACHEOP_CASE(CompareInt32Result);
DECLARE_CACHEOP_CASE(CompareNullUndefinedResult);
DECLARE_CACHEOP_CASE(AssertPropertyLookup);
DECLARE_CACHEOP_CASE(GuardIsFixedLengthTypedArray);
DECLARE_CACHEOP_CASE(GuardIndexIsNotDenseElement);
DECLARE_CACHEOP_CASE(LoadFixedSlotTypedResult);
DECLARE_CACHEOP_CASE(LoadDenseElementHoleResult);
DECLARE_CACHEOP_CASE(LoadDenseElementExistsResult);
DECLARE_CACHEOP_CASE(LoadTypedArrayElementExistsResult);
DECLARE_CACHEOP_CASE(LoadDenseElementHoleExistsResult);
DECLARE_CACHEOP_CASE(LoadTypedArrayElementResult);
DECLARE_CACHEOP_CASE(RegExpFlagResult);
DECLARE_CACHEOP_CASE(GuardNumberToIntPtrIndex);
DECLARE_CACHEOP_CASE(CallRegExpMatcherResult);
DECLARE_CACHEOP_CASE(CallRegExpSearcherResult);
DECLARE_CACHEOP_CASE(RegExpSearcherLastLimitResult);
DECLARE_CACHEOP_CASE(RegExpHasCaptureGroupsResult);
DECLARE_CACHEOP_CASE(RegExpBuiltinExecMatchResult);
DECLARE_CACHEOP_CASE(RegExpBuiltinExecTestResult);
DECLARE_CACHEOP_CASE(CallSubstringKernelResult);
DECLARE_CACHEOP_CASE(StringReplaceStringResult);
DECLARE_CACHEOP_CASE(StringSplitStringResult);
DECLARE_CACHEOP_CASE(RegExpPrototypeOptimizableResult);
DECLARE_CACHEOP_CASE(RegExpInstanceOptimizableResult);
DECLARE_CACHEOP_CASE(GetFirstDollarIndexResult);
DECLARE_CACHEOP_CASE(StringToAtom);
DECLARE_CACHEOP_CASE(GuardTagNotEqual);
DECLARE_CACHEOP_CASE(IdToStringOrSymbol);
DECLARE_CACHEOP_CASE(MegamorphicStoreSlot);
DECLARE_CACHEOP_CASE(MegamorphicHasPropResult);
DECLARE_CACHEOP_CASE(ObjectToIteratorResult);
DECLARE_CACHEOP_CASE(ArrayJoinResult);
DECLARE_CACHEOP_CASE(ObjectKeysResult);
DECLARE_CACHEOP_CASE(PackedArrayPopResult);
DECLARE_CACHEOP_CASE(PackedArrayShiftResult);
DECLARE_CACHEOP_CASE(PackedArraySliceResult);
DECLARE_CACHEOP_CASE(IsArrayResult);
DECLARE_CACHEOP_CASE(IsPackedArrayResult);
DECLARE_CACHEOP_CASE(IsCallableResult);
DECLARE_CACHEOP_CASE(IsConstructorResult);
DECLARE_CACHEOP_CASE(IsCrossRealmArrayConstructorResult);
DECLARE_CACHEOP_CASE(IsTypedArrayResult);
DECLARE_CACHEOP_CASE(IsTypedArrayConstructorResult);
DECLARE_CACHEOP_CASE(ArrayBufferViewByteOffsetInt32Result);
DECLARE_CACHEOP_CASE(ArrayBufferViewByteOffsetDoubleResult);
DECLARE_CACHEOP_CASE(TypedArrayByteLengthInt32Result);
DECLARE_CACHEOP_CASE(TypedArrayByteLengthDoubleResult);
DECLARE_CACHEOP_CASE(TypedArrayElementSizeResult);
DECLARE_CACHEOP_CASE(NewStringIteratorResult);
DECLARE_CACHEOP_CASE(NewRegExpStringIteratorResult);
DECLARE_CACHEOP_CASE(ObjectCreateResult);
DECLARE_CACHEOP_CASE(NewArrayFromLengthResult);
DECLARE_CACHEOP_CASE(NewTypedArrayFromArrayBufferResult);
DECLARE_CACHEOP_CASE(NewTypedArrayFromArrayResult);
DECLARE_CACHEOP_CASE(NewTypedArrayFromLengthResult);
DECLARE_CACHEOP_CASE(StringFromCharCodeResult);
DECLARE_CACHEOP_CASE(StringFromCodePointResult);
DECLARE_CACHEOP_CASE(StringIncludesResult);
DECLARE_CACHEOP_CASE(StringIndexOfResult);
DECLARE_CACHEOP_CASE(StringLastIndexOfResult);
DECLARE_CACHEOP_CASE(StringStartsWithResult);
DECLARE_CACHEOP_CASE(StringEndsWithResult);
DECLARE_CACHEOP_CASE(StringToLowerCaseResult);
DECLARE_CACHEOP_CASE(StringToUpperCaseResult);
DECLARE_CACHEOP_CASE(StringTrimResult);
DECLARE_CACHEOP_CASE(StringTrimStartResult);
DECLARE_CACHEOP_CASE(StringTrimEndResult);
DECLARE_CACHEOP_CASE(MathAbsInt32Result);
DECLARE_CACHEOP_CASE(MathAbsNumberResult);
DECLARE_CACHEOP_CASE(MathClz32Result);
DECLARE_CACHEOP_CASE(MathSignInt32Result);
DECLARE_CACHEOP_CASE(MathSignNumberResult);
DECLARE_CACHEOP_CASE(MathSignNumberToInt32Result);
DECLARE_CACHEOP_CASE(MathImulResult);
DECLARE_CACHEOP_CASE(MathSqrtNumberResult);
DECLARE_CACHEOP_CASE(MathFRoundNumberResult);
DECLARE_CACHEOP_CASE(MathRandomResult);
DECLARE_CACHEOP_CASE(MathHypot2NumberResult);
DECLARE_CACHEOP_CASE(MathHypot3NumberResult);
DECLARE_CACHEOP_CASE(MathHypot4NumberResult);
DECLARE_CACHEOP_CASE(MathAtan2NumberResult);
DECLARE_CACHEOP_CASE(MathFloorNumberResult);
DECLARE_CACHEOP_CASE(MathCeilNumberResult);
DECLARE_CACHEOP_CASE(MathTruncNumberResult);
DECLARE_CACHEOP_CASE(MathCeilToInt32Result);
DECLARE_CACHEOP_CASE(MathFloorToInt32Result);
DECLARE_CACHEOP_CASE(MathTruncToInt32Result);
DECLARE_CACHEOP_CASE(MathRoundToInt32Result);
DECLARE_CACHEOP_CASE(NumberMinMax);
DECLARE_CACHEOP_CASE(Int32MinMaxArrayResult);
DECLARE_CACHEOP_CASE(NumberMinMaxArrayResult);
DECLARE_CACHEOP_CASE(MathFunctionNumberResult);
DECLARE_CACHEOP_CASE(NumberParseIntResult);
DECLARE_CACHEOP_CASE(DoubleParseIntResult);
DECLARE_CACHEOP_CASE(ObjectToStringResult);
DECLARE_CACHEOP_CASE(CallNativeSetter);
DECLARE_CACHEOP_CASE(CallSetArrayLength);
DECLARE_CACHEOP_CASE(CallNumberToString);
DECLARE_CACHEOP_CASE(Int32ToStringWithBaseResult);
DECLARE_CACHEOP_CASE(BooleanToString);
DECLARE_CACHEOP_CASE(BindFunctionResult);
DECLARE_CACHEOP_CASE(SpecializedBindFunctionResult);
DECLARE_CACHEOP_CASE(CallGetSparseElementResult);
DECLARE_CACHEOP_CASE(LoadArgumentsObjectLengthResult);
DECLARE_CACHEOP_CASE(LoadArgumentsObjectLength);
DECLARE_CACHEOP_CASE(LoadBoundFunctionNumArgs);
DECLARE_CACHEOP_CASE(LoadBoundFunctionTarget);
DECLARE_CACHEOP_CASE(LoadArrayBufferByteLengthInt32Result);
DECLARE_CACHEOP_CASE(LoadArrayBufferByteLengthDoubleResult);
DECLARE_CACHEOP_CASE(LinearizeForCodePointAccess);
DECLARE_CACHEOP_CASE(LoadArrayBufferViewLengthInt32Result);
DECLARE_CACHEOP_CASE(LoadArrayBufferViewLengthDoubleResult);
DECLARE_CACHEOP_CASE(LoadStringAtResult);
DECLARE_CACHEOP_CASE(LoadStringCodePointResult);
DECLARE_CACHEOP_CASE(CallNativeGetterResult);
DECLARE_CACHEOP_CASE(LoadUndefinedResult);
DECLARE_CACHEOP_CASE(LoadDoubleConstant);
DECLARE_CACHEOP_CASE(LoadBooleanConstant);
DECLARE_CACHEOP_CASE(LoadUndefined);
DECLARE_CACHEOP_CASE(LoadConstantString);
DECLARE_CACHEOP_CASE(LoadInstanceOfObjectResult);
DECLARE_CACHEOP_CASE(LoadTypeOfObjectResult);
DECLARE_CACHEOP_CASE(DoubleAddResult);
DECLARE_CACHEOP_CASE(DoubleSubResult);
DECLARE_CACHEOP_CASE(DoubleMulResult);
DECLARE_CACHEOP_CASE(DoubleDivResult);
DECLARE_CACHEOP_CASE(DoubleModResult);
DECLARE_CACHEOP_CASE(DoublePowResult);
DECLARE_CACHEOP_CASE(Int32LeftShiftResult);
DECLARE_CACHEOP_CASE(Int32RightShiftResult);
DECLARE_CACHEOP_CASE(Int32URightShiftResult);
DECLARE_CACHEOP_CASE(Int32NotResult);
DECLARE_CACHEOP_CASE(LoadDoubleTruthyResult);
DECLARE_CACHEOP_CASE(NewPlainObjectResult);
DECLARE_CACHEOP_CASE(NewArrayObjectResult);
DECLARE_CACHEOP_CASE(CompareObjectResult);
DECLARE_CACHEOP_CASE(CompareSymbolResult);
DECLARE_CACHEOP_CASE(CompareDoubleResult);
DECLARE_CACHEOP_CASE(IndirectTruncateInt32Result);
DECLARE_CACHEOP_CASE(CallScriptedSetter);
DECLARE_CACHEOP_CASE(CallBoundScriptedFunction);
DECLARE_CACHEOP_CASE(CallScriptedGetterResult);
// Define the computed-goto table regardless of dispatch strategy so
// we don't get unused-label errors. (We need some of the labels
// even without this for the predict-next mechanism, so we can't
// conditionally elide labels either.)
static const void* const addresses[long(CacheOp::NumOpcodes)] = {
#define OP(name, ...) &&cacheop_##name,
CACHE_IR_OPS(OP)
#undef OP
};
(void)addresses;
#define CACHEOP_TRACE(name) \
TRACE_PRINTF("cacheop (frame %p stub %p): " #name "\n", ctx.frame, cstub);
#define FAIL_IC() goto next_ic;
// We set a fixed bound on the number of icVals which is smaller than what IC
// generators may use. As a result we can't evaluate an IC if it defines too
// many values. Note that we don't need to check this when reading from icVals
// because we should have bailed out before the earlier write which defined the
// same value. Similarly, we don't need to check writes to locations which we've
// just read from.
#define BOUNDSCHECK(resultId) \
if (resultId.id() >= ICRegs::kMaxICVals) FAIL_IC();
#define PREDICT_NEXT(name) \
if (cacheIRReader.peekOp() == CacheOp::name) { \
cacheIRReader.readOp(); \
cacheop = CacheOp::name; \
goto cacheop_##name; \
}
#define PREDICT_RETURN() \
if (cacheIRReader.peekOp() == CacheOp::ReturnFromIC) { \
TRACE_PRINTF("stub successful, predicted return\n"); \
return retValue; \
}
ICCacheIRStub* cstub = stub->toCacheIRStub();
const CacheIRStubInfo* stubInfo = cstub->stubInfo();
CacheIRReader cacheIRReader(stubInfo);
uint64_t retValue = 0;
CacheOp cacheop;
WRITE_VALUE_REG(0, Value::fromRawBits(arg0));
WRITE_VALUE_REG(1, Value::fromRawBits(arg1));
WRITE_VALUE_REG(2, Value::fromRawBits(ctx.arg2));
DISPATCH_CACHEOP();
#ifndef ENABLE_COMPUTED_GOTO_DISPATCH
dispatch:
switch (cacheop)
#endif
{
CACHEOP_CASE(ReturnFromIC) {
TRACE_PRINTF("stub successful!\n");
return retValue;
}
CACHEOP_CASE(GuardToObject) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
TRACE_PRINTF("GuardToObject: icVal %" PRIx64 "\n",
READ_REG(inputId.id()));
if (!v.isObject()) {
FAIL_IC();
}
WRITE_REG(inputId.id(), reinterpret_cast<uint64_t>(&v.toObject()),
OBJECT);
PREDICT_NEXT(GuardShape);
PREDICT_NEXT(GuardSpecificFunction);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNullOrUndefined) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isNullOrUndefined()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNull) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isNull()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsUndefined) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isUndefined()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNotUninitializedLexical) {
ValOperandId valId = cacheIRReader.valOperandId();
Value val = READ_VALUE_REG(valId.id());
if (val == MagicValue(JS_UNINITIALIZED_LEXICAL)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToBoolean) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isBoolean()) {
FAIL_IC();
}
WRITE_REG(inputId.id(), v.toBoolean() ? 1 : 0, BOOLEAN);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToString) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isString()) {
FAIL_IC();
}
WRITE_REG(inputId.id(), reinterpret_cast<uint64_t>(v.toString()),
STRING);
PREDICT_NEXT(GuardToString);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToSymbol) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isSymbol()) {
FAIL_IC();
}
WRITE_REG(inputId.id(), reinterpret_cast<uint64_t>(v.toSymbol()),
SYMBOL);
PREDICT_NEXT(GuardSpecificSymbol);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToBigInt) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isBigInt()) {
FAIL_IC();
}
WRITE_REG(inputId.id(), reinterpret_cast<uint64_t>(v.toBigInt()),
BIGINT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNumber) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
if (!v.isNumber()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToInt32) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value v = READ_VALUE_REG(inputId.id());
TRACE_PRINTF("GuardToInt32 (%d): icVal %" PRIx64 "\n", inputId.id(),
READ_REG(inputId.id()));
if (!v.isInt32()) {
FAIL_IC();
}
// N.B.: we don't need to unbox because the low 32 bits are
// already the int32 itself, and we are careful when using
// `Int32Operand`s to only use those bits.
PREDICT_NEXT(GuardToInt32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToNonGCThing) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value input = READ_VALUE_REG(inputId.id());
if (input.isGCThing()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardBooleanToInt32) {
ValOperandId inputId = cacheIRReader.valOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
Value v = READ_VALUE_REG(inputId.id());
if (!v.isBoolean()) {
FAIL_IC();
}
WRITE_REG(resultId.id(), v.toBoolean() ? 1 : 0, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToInt32Index) {
ValOperandId inputId = cacheIRReader.valOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
Value val = READ_VALUE_REG(inputId.id());
if (val.isInt32()) {
WRITE_REG(resultId.id(), val.toInt32(), INT32);
DISPATCH_CACHEOP();
} else if (val.isDouble()) {
double doubleVal = val.toDouble();
if (int32_t(doubleVal) == doubleVal) {
WRITE_REG(resultId.id(), int32_t(doubleVal), INT32);
DISPATCH_CACHEOP();
}
}
FAIL_IC();
}
CACHEOP_CASE(Int32ToIntPtr) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
IntPtrOperandId resultId = cacheIRReader.intPtrOperandId();
BOUNDSCHECK(resultId);
int32_t input = int32_t(READ_REG(inputId.id()));
// Note that this must sign-extend to pointer width:
WRITE_REG(resultId.id(), intptr_t(input), OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardToInt32ModUint32) {
ValOperandId inputId = cacheIRReader.valOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
Value input = READ_VALUE_REG(inputId.id());
if (input.isInt32()) {
WRITE_REG(resultId.id(), input.toInt32(), INT32);
DISPATCH_CACHEOP();
} else if (input.isDouble()) {
double doubleVal = input.toDouble();
// Accept any double that fits in an int64_t but truncate the top 32
// bits.
if (doubleVal >= double(INT64_MIN) &&
doubleVal <= double(INT64_MAX)) {
WRITE_REG(resultId.id(), int64_t(doubleVal), INT32);
DISPATCH_CACHEOP();
}
}
FAIL_IC();
}
CACHEOP_CASE(GuardNonDoubleType) {
ValOperandId inputId = cacheIRReader.valOperandId();
ValueType type = cacheIRReader.valueType();
Value val = READ_VALUE_REG(inputId.id());
switch (type) {
case ValueType::String:
if (!val.isString()) {
FAIL_IC();
}
break;
case ValueType::Symbol:
if (!val.isSymbol()) {
FAIL_IC();
}
break;
case ValueType::BigInt:
if (!val.isBigInt()) {
FAIL_IC();
}
break;
case ValueType::Int32:
if (!val.isInt32()) {
FAIL_IC();
}
break;
case ValueType::Boolean:
if (!val.isBoolean()) {
FAIL_IC();
}
break;
case ValueType::Undefined:
if (!val.isUndefined()) {
FAIL_IC();
}
break;
case ValueType::Null:
if (!val.isNull()) {
FAIL_IC();
}
break;
default:
MOZ_CRASH("Unexpected type");
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardShape) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t shapeOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uintptr_t expectedShape = stubInfo->getStubRawWord(cstub, shapeOffset);
if (reinterpret_cast<uintptr_t>(obj->shape()) != expectedShape) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFuse) {
RealmFuses::FuseIndex fuseIndex = cacheIRReader.realmFuseIndex();
if (!ctx.frameMgr.cxForLocalUseOnly()
->realm()
->realmFuses.getFuseByIndex(fuseIndex)
->intact()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardProto) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t protoOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSObject* proto = reinterpret_cast<JSObject*>(
stubInfo->getStubRawWord(cstub, protoOffset));
if (obj->staticPrototype() != proto) {
FAIL_IC();
}
PREDICT_NEXT(LoadProto);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardNullProto) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->taggedProto().raw()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardClass) {
ObjOperandId objId = cacheIRReader.objOperandId();
GuardClassKind kind = cacheIRReader.guardClassKind();
JSObject* object = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
switch (kind) {
case GuardClassKind::Array:
if (object->getClass() != &ArrayObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::PlainObject:
if (object->getClass() != &PlainObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::FixedLengthArrayBuffer:
if (object->getClass() != &FixedLengthArrayBufferObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::ResizableArrayBuffer:
if (object->getClass() != &ResizableArrayBufferObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::FixedLengthSharedArrayBuffer:
if (object->getClass() !=
&FixedLengthSharedArrayBufferObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::GrowableSharedArrayBuffer:
if (object->getClass() !=
&GrowableSharedArrayBufferObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::FixedLengthDataView:
if (object->getClass() != &FixedLengthDataViewObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::ResizableDataView:
if (object->getClass() != &ResizableDataViewObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::MappedArguments:
if (object->getClass() != &MappedArgumentsObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::UnmappedArguments:
if (object->getClass() != &UnmappedArgumentsObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::WindowProxy:
if (object->getClass() != ctx.frameMgr.cxForLocalUseOnly()
->runtime()
->maybeWindowProxyClass()) {
FAIL_IC();
}
break;
case GuardClassKind::JSFunction:
if (!object->is<JSFunction>()) {
FAIL_IC();
}
break;
case GuardClassKind::Set:
if (object->getClass() != &SetObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::Map:
if (object->getClass() != &MapObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::BoundFunction:
if (object->getClass() != &BoundFunctionObject::class_) {
FAIL_IC();
}
break;
case GuardClassKind::Date:
if (object->getClass() != &DateObject::class_) {
FAIL_IC();
}
break;
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardAnyClass) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t claspOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSClass* clasp = reinterpret_cast<JSClass*>(
stubInfo->getStubRawWord(cstub, claspOffset));
if (obj->getClass() != clasp) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardGlobalGeneration) {
uint32_t expectedOffset = cacheIRReader.stubOffset();
uint32_t generationAddrOffset = cacheIRReader.stubOffset();
uint32_t expected = stubInfo->getStubRawInt32(cstub, expectedOffset);
uint32_t* generationAddr = reinterpret_cast<uint32_t*>(
stubInfo->getStubRawWord(cstub, generationAddrOffset));
if (*generationAddr != expected) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(HasClassResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t claspOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSClass* clasp = reinterpret_cast<JSClass*>(
stubInfo->getStubRawWord(cstub, claspOffset));
retValue = BooleanValue(obj->getClass() == clasp).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardCompartment) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t globalOffset = cacheIRReader.stubOffset();
uint32_t compartmentOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSObject* global = reinterpret_cast<JSObject*>(
stubInfo->getStubRawWord(cstub, globalOffset));
JS::Compartment* compartment = reinterpret_cast<JS::Compartment*>(
stubInfo->getStubRawWord(cstub, compartmentOffset));
if (IsDeadProxyObject(global)) {
FAIL_IC();
}
if (obj->compartment() != compartment) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsExtensible) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->nonProxyIsExtensible()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNativeObject) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!obj->is<NativeObject>()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsProxy) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!obj->is<ProxyObject>()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNotProxy) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->is<ProxyObject>()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNotArrayBufferMaybeShared) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
const JSClass* clasp = obj->getClass();
if (clasp == &FixedLengthArrayBufferObject::class_ ||
clasp == &FixedLengthSharedArrayBufferObject::class_ ||
clasp == &ResizableArrayBufferObject::class_ ||
clasp == &GrowableSharedArrayBufferObject::class_) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsTypedArray) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!IsTypedArrayClass(obj->getClass())) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsFixedLengthTypedArray) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!IsFixedLengthTypedArrayClass(obj->getClass())) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardHasProxyHandler) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t handlerOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
BaseProxyHandler* handler = reinterpret_cast<BaseProxyHandler*>(
stubInfo->getStubRawWord(cstub, handlerOffset));
if (obj->as<ProxyObject>().handler() != handler) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIsNotDOMProxy) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->as<ProxyObject>().handler()->family() ==
GetDOMProxyHandlerFamily()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardSpecificObject) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t expectedOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSObject* expected = reinterpret_cast<JSObject*>(
stubInfo->getStubRawWord(cstub, expectedOffset));
if (obj != expected) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardObjectIdentity) {
ObjOperandId obj1Id = cacheIRReader.objOperandId();
ObjOperandId obj2Id = cacheIRReader.objOperandId();
JSObject* obj1 = reinterpret_cast<JSObject*>(READ_REG(obj1Id.id()));
JSObject* obj2 = reinterpret_cast<JSObject*>(READ_REG(obj2Id.id()));
if (obj1 != obj2) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardSpecificFunction) {
ObjOperandId funId = cacheIRReader.objOperandId();
uint32_t expectedOffset = cacheIRReader.stubOffset();
uint32_t nargsAndFlagsOffset = cacheIRReader.stubOffset();
(void)nargsAndFlagsOffset; // Unused.
uintptr_t expected = stubInfo->getStubRawWord(cstub, expectedOffset);
if (expected != READ_REG(funId.id())) {
FAIL_IC();
}
PREDICT_NEXT(LoadArgumentFixedSlot);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFunctionScript) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t expectedOffset = cacheIRReader.stubOffset();
uint32_t nargsAndFlagsOffset = cacheIRReader.stubOffset();
JSFunction* fun = reinterpret_cast<JSFunction*>(READ_REG(objId.id()));
BaseScript* expected = reinterpret_cast<BaseScript*>(
stubInfo->getStubRawWord(cstub, expectedOffset));
(void)nargsAndFlagsOffset;
if (!fun->hasBaseScript() || fun->baseScript() != expected) {
FAIL_IC();
}
PREDICT_NEXT(CallScriptedFunction);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardSpecificAtom) {
StringOperandId strId = cacheIRReader.stringOperandId();
uint32_t expectedOffset = cacheIRReader.stubOffset();
uintptr_t expected = stubInfo->getStubRawWord(cstub, expectedOffset);
if (expected != READ_REG(strId.id())) {
// TODO: BaselineCacheIRCompiler also checks for equal strings
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardSpecificSymbol) {
SymbolOperandId symId = cacheIRReader.symbolOperandId();
uint32_t expectedOffset = cacheIRReader.stubOffset();
uintptr_t expected = stubInfo->getStubRawWord(cstub, expectedOffset);
if (expected != READ_REG(symId.id())) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardSpecificInt32) {
Int32OperandId numId = cacheIRReader.int32OperandId();
int32_t expected = cacheIRReader.int32Immediate();
if (expected != int32_t(READ_REG(numId.id()))) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardNoDenseElements) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->as<NativeObject>().getDenseInitializedLength() != 0) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardStringToIndex) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
int32_t result;
if (str->hasIndexValue()) {
uint32_t index = str->getIndexValue();
MOZ_ASSERT(index <= INT32_MAX);
result = index;
} else {
result = GetIndexFromString(str);
if (result < 0) {
FAIL_IC();
}
}
WRITE_REG(resultId.id(), result, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardStringToInt32) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
int32_t result;
// Use indexed value as fast path if possible.
if (str->hasIndexValue()) {
uint32_t index = str->getIndexValue();
MOZ_ASSERT(index <= INT32_MAX);
result = index;
} else {
if (!GetInt32FromStringPure(ctx.frameMgr.cxForLocalUseOnly(), str,
&result)) {
FAIL_IC();
}
}
WRITE_REG(resultId.id(), result, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardStringToNumber) {
StringOperandId strId = cacheIRReader.stringOperandId();
NumberOperandId resultId = cacheIRReader.numberOperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
Value result;
// Use indexed value as fast path if possible.
if (str->hasIndexValue()) {
uint32_t index = str->getIndexValue();
MOZ_ASSERT(index <= INT32_MAX);
result = Int32Value(index);
} else {
double value;
if (!StringToNumberPure(ctx.frameMgr.cxForLocalUseOnly(), str,
&value)) {
FAIL_IC();
}
result = DoubleValue(value);
}
WRITE_VALUE_REG(resultId.id(), result);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(BooleanToNumber) {
BooleanOperandId booleanId = cacheIRReader.booleanOperandId();
NumberOperandId resultId = cacheIRReader.numberOperandId();
BOUNDSCHECK(resultId);
uint64_t boolean = READ_REG(booleanId.id());
MOZ_ASSERT((boolean & ~1) == 0);
WRITE_VALUE_REG(resultId.id(), Int32Value(boolean));
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardHasGetterSetter) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t idOffset = cacheIRReader.stubOffset();
uint32_t getterSetterOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
jsid id = jsid::fromRawBits(stubInfo->getStubRawWord(cstub, idOffset));
GetterSetter* getterSetter = reinterpret_cast<GetterSetter*>(
stubInfo->getStubRawWord(cstub, getterSetterOffset));
if (!ObjectHasGetterSetterPure(ctx.frameMgr.cxForLocalUseOnly(), obj,
id, getterSetter)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardInt32IsNonNegative) {
Int32OperandId indexId = cacheIRReader.int32OperandId();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardDynamicSlotIsSpecificObject) {
ObjOperandId objId = cacheIRReader.objOperandId();
ObjOperandId expectedId = cacheIRReader.objOperandId();
uint32_t slotOffset = cacheIRReader.stubOffset();
JSObject* expected =
reinterpret_cast<JSObject*>(READ_REG(expectedId.id()));
uintptr_t slot = stubInfo->getStubRawInt32(cstub, slotOffset);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
HeapSlot* slots = nobj->getSlotsUnchecked();
// Note that unlike similar opcodes, GuardDynamicSlotIsSpecificObject
// takes a slot index rather than a byte offset.
Value actual = slots[slot];
if (actual != ObjectValue(*expected)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardDynamicSlotIsNotObject) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t slotOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t slot = stubInfo->getStubRawInt32(cstub, slotOffset);
NativeObject* nobj = &obj->as<NativeObject>();
HeapSlot* slots = nobj->getSlotsUnchecked();
// Note that unlike similar opcodes, GuardDynamicSlotIsNotObject takes a
// slot index rather than a byte offset.
Value actual = slots[slot];
if (actual.isObject()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFixedSlotValue) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
uint32_t valOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
Value val =
Value::fromRawBits(stubInfo->getStubRawInt64(cstub, valOffset));
GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>(
reinterpret_cast<uintptr_t>(obj) + offset);
Value actual = slot->get();
if (actual != val) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardDynamicSlotValue) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
uint32_t valOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
Value val =
Value::fromRawBits(stubInfo->getStubRawInt64(cstub, valOffset));
NativeObject* nobj = &obj->as<NativeObject>();
HeapSlot* slots = nobj->getSlotsUnchecked();
Value actual = slots[offset / sizeof(Value)];
if (actual != val) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadFixedSlot) {
ValOperandId resultId = cacheIRReader.valOperandId();
BOUNDSCHECK(resultId);
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>(
reinterpret_cast<uintptr_t>(obj) + offset);
Value actual = slot->get();
WRITE_VALUE_REG(resultId.id(), actual);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDynamicSlot) {
ValOperandId resultId = cacheIRReader.valOperandId();
BOUNDSCHECK(resultId);
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t slotOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t slot = stubInfo->getStubRawInt32(cstub, slotOffset);
NativeObject* nobj = &obj->as<NativeObject>();
HeapSlot* slots = nobj->getSlotsUnchecked();
// Note that unlike similar opcodes, LoadDynamicSlot takes a slot index
// rather than a byte offset.
Value actual = slots[slot];
WRITE_VALUE_REG(resultId.id(), actual);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardNoAllocationMetadataBuilder) {
uint32_t builderAddrOffset = cacheIRReader.stubOffset();
uintptr_t builderAddr =
stubInfo->getStubRawWord(cstub, builderAddrOffset);
if (*reinterpret_cast<uintptr_t*>(builderAddr) != 0) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFunctionHasJitEntry) {
ObjOperandId funId = cacheIRReader.objOperandId();
JSObject* fun = reinterpret_cast<JSObject*>(READ_REG(funId.id()));
uint16_t flags = FunctionFlags::HasJitEntryFlags();
if (!fun->as<JSFunction>().flags().hasFlags(flags)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFunctionHasNoJitEntry) {
ObjOperandId funId = cacheIRReader.objOperandId();
JSObject* fun = reinterpret_cast<JSObject*>(READ_REG(funId.id()));
uint16_t flags = FunctionFlags::HasJitEntryFlags();
if (fun->as<JSFunction>().flags().hasFlags(flags)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFunctionIsNonBuiltinCtor) {
ObjOperandId funId = cacheIRReader.objOperandId();
JSObject* fun = reinterpret_cast<JSObject*>(READ_REG(funId.id()));
if (!fun->as<JSFunction>().isNonBuiltinConstructor()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardFunctionIsConstructor) {
ObjOperandId funId = cacheIRReader.objOperandId();
JSObject* fun = reinterpret_cast<JSObject*>(READ_REG(funId.id()));
if (!fun->as<JSFunction>().isConstructor()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardNotClassConstructor) {
ObjOperandId funId = cacheIRReader.objOperandId();
JSObject* fun = reinterpret_cast<JSObject*>(READ_REG(funId.id()));
if (fun->as<JSFunction>().isClassConstructor()) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardArrayIsPacked) {
ObjOperandId arrayId = cacheIRReader.objOperandId();
JSObject* array = reinterpret_cast<JSObject*>(READ_REG(arrayId.id()));
if (!IsPackedArray(array)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardArgumentsObjectFlags) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint8_t flags = cacheIRReader.readByte();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (obj->as<ArgumentsObject>().hasFlags(flags)) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadObject) {
ObjOperandId resultId = cacheIRReader.objOperandId();
BOUNDSCHECK(resultId);
uint32_t objOffset = cacheIRReader.stubOffset();
intptr_t obj = stubInfo->getStubRawWord(cstub, objOffset);
WRITE_REG(resultId.id(), obj, OBJECT);
PREDICT_NEXT(GuardShape);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadProtoObject) {
ObjOperandId resultId = cacheIRReader.objOperandId();
BOUNDSCHECK(resultId);
uint32_t protoObjOffset = cacheIRReader.stubOffset();
ObjOperandId receiverObjId = cacheIRReader.objOperandId();
(void)receiverObjId;
intptr_t obj = stubInfo->getStubRawWord(cstub, protoObjOffset);
WRITE_REG(resultId.id(), obj, OBJECT);
PREDICT_NEXT(GuardShape);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadProto) {
ObjOperandId objId = cacheIRReader.objOperandId();
ObjOperandId resultId = cacheIRReader.objOperandId();
BOUNDSCHECK(resultId);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
WRITE_REG(resultId.id(),
reinterpret_cast<uint64_t>(nobj->staticPrototype()), OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadEnclosingEnvironment) {
ObjOperandId objId = cacheIRReader.objOperandId();
ObjOperandId resultId = cacheIRReader.objOperandId();
BOUNDSCHECK(resultId);
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSObject* env = &obj->as<EnvironmentObject>().enclosingEnvironment();
WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(env), OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadWrapperTarget) {
ObjOperandId objId = cacheIRReader.objOperandId();
ObjOperandId resultId = cacheIRReader.objOperandId();
bool fallible = cacheIRReader.readBool();
BOUNDSCHECK(resultId);
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSObject* target = obj->as<ProxyObject>().private_().toObjectOrNull();
if (fallible && !target) {
FAIL_IC();
}
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(target), OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadValueTag) {
ValOperandId valId = cacheIRReader.valOperandId();
ValueTagOperandId resultId = cacheIRReader.valueTagOperandId();
BOUNDSCHECK(resultId);
Value val = READ_VALUE_REG(valId.id());
WRITE_REG(resultId.id(), val.asRawBits() >> JSVAL_TAG_SHIFT, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArgumentFixedSlot) {
ValOperandId resultId = cacheIRReader.valOperandId();
BOUNDSCHECK(resultId);
uint8_t slotIndex = cacheIRReader.readByte();
StackVal* sp = ctx.sp();
Value val = sp[slotIndex].asValue();
TRACE_PRINTF(" -> slot %d: val %" PRIx64 "\n", int(slotIndex),
val.asRawBits());
WRITE_VALUE_REG(resultId.id(), val);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArgumentDynamicSlot) {
ValOperandId resultId = cacheIRReader.valOperandId();
BOUNDSCHECK(resultId);
Int32OperandId argcId = cacheIRReader.int32OperandId();
uint8_t slotIndex = cacheIRReader.readByte();
int32_t argc = int32_t(READ_REG(argcId.id()));
StackVal* sp = ctx.sp();
Value val = sp[slotIndex + argc].asValue();
WRITE_VALUE_REG(resultId.id(), val);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(TruncateDoubleToUInt32) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
Value input = READ_VALUE_REG(inputId.id());
WRITE_REG(resultId.id(), JS::ToInt32(input.toNumber()), INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MegamorphicLoadSlotResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t nameOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
jsid name =
jsid::fromRawBits(stubInfo->getStubRawWord(cstub, nameOffset));
if (!obj->shape()->isNative()) {
FAIL_IC();
}
Value result;
if (!GetNativeDataPropertyPureWithCacheLookup(
ctx.frameMgr.cxForLocalUseOnly(), obj, name, nullptr,
&result)) {
FAIL_IC();
}
retValue = result.asRawBits();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MegamorphicLoadSlotByValueResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ValOperandId idId = cacheIRReader.valOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
Value id = READ_VALUE_REG(idId.id());
if (!obj->shape()->isNative()) {
FAIL_IC();
}
Value values[2] = {id};
if (!GetNativeDataPropertyByValuePure(ctx.frameMgr.cxForLocalUseOnly(),
obj, nullptr, values)) {
FAIL_IC();
}
retValue = values[1].asRawBits();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MegamorphicSetElement) {
ObjOperandId objId = cacheIRReader.objOperandId();
ValOperandId idId = cacheIRReader.valOperandId();
ValOperandId rhsId = cacheIRReader.valOperandId();
bool strict = cacheIRReader.readBool();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
Value id = READ_VALUE_REG(idId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> obj0(&ctx.state.obj0, obj);
ReservedRooted<Value> value0(&ctx.state.value0, id);
ReservedRooted<Value> value1(&ctx.state.value1, rhs);
if (!SetElementMegamorphic<false>(cx, obj0, value0, value1, strict)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StoreFixedSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
ValOperandId rhsId = cacheIRReader.valOperandId();
uintptr_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>(
reinterpret_cast<uintptr_t>(nobj) + offset);
Value val = READ_VALUE_REG(rhsId.id());
slot->set(val);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StoreDynamicSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
ValOperandId rhsId = cacheIRReader.valOperandId();
uint32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
HeapSlot* slots = nobj->getSlotsUnchecked();
Value val = READ_VALUE_REG(rhsId.id());
size_t dynSlot = offset / sizeof(Value);
size_t slot = dynSlot + nobj->numFixedSlots();
slots[dynSlot].set(nobj, HeapSlot::Slot, slot, val);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(AddAndStoreFixedSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
ValOperandId rhsId = cacheIRReader.valOperandId();
uint32_t newShapeOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
int32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
Value rhs = READ_VALUE_REG(rhsId.id());
Shape* newShape = reinterpret_cast<Shape*>(
stubInfo->getStubRawWord(cstub, newShapeOffset));
obj->setShape(newShape);
GCPtr<Value>* slot = reinterpret_cast<GCPtr<Value>*>(
reinterpret_cast<uintptr_t>(obj) + offset);
slot->init(rhs);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(AddAndStoreDynamicSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
ValOperandId rhsId = cacheIRReader.valOperandId();
uint32_t newShapeOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
int32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
Value rhs = READ_VALUE_REG(rhsId.id());
Shape* newShape = reinterpret_cast<Shape*>(
stubInfo->getStubRawWord(cstub, newShapeOffset));
NativeObject* nobj = &obj->as<NativeObject>();
obj->setShape(newShape);
HeapSlot* slots = nobj->getSlotsUnchecked();
size_t dynSlot = offset / sizeof(Value);
size_t slot = dynSlot + nobj->numFixedSlots();
slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(AllocateAndStoreDynamicSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
ValOperandId rhsId = cacheIRReader.valOperandId();
uint32_t newShapeOffset = cacheIRReader.stubOffset();
uint32_t numNewSlotsOffset = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
int32_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
Value rhs = READ_VALUE_REG(rhsId.id());
Shape* newShape = reinterpret_cast<Shape*>(
stubInfo->getStubRawWord(cstub, newShapeOffset));
int32_t numNewSlots =
stubInfo->getStubRawInt32(cstub, numNewSlotsOffset);
NativeObject* nobj = &obj->as<NativeObject>();
// We have to (re)allocate dynamic slots. Do this first, as it's the
// only fallible operation here. Note that growSlotsPure is fallible but
// does not GC. Otherwise this is the same as AddAndStoreDynamicSlot
// above.
if (!NativeObject::growSlotsPure(ctx.frameMgr.cxForLocalUseOnly(), nobj,
numNewSlots)) {
FAIL_IC();
}
obj->setShape(newShape);
HeapSlot* slots = nobj->getSlotsUnchecked();
size_t dynSlot = offset / sizeof(Value);
size_t slot = dynSlot + nobj->numFixedSlots();
slots[dynSlot].init(nobj, HeapSlot::Slot, slot, rhs);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StoreDenseElement) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
ValOperandId rhsId = cacheIRReader.valOperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
FAIL_IC();
}
HeapSlot* slot = &elems->elements()[index];
if (slot->get().isMagic()) {
FAIL_IC();
}
Value val = READ_VALUE_REG(rhsId.id());
slot->set(nobj, HeapSlot::Element, index + elems->numShiftedElements(),
val);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StoreDenseElementHole) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
ValOperandId rhsId = cacheIRReader.valOperandId();
bool handleAdd = cacheIRReader.readBool();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t index = uint32_t(READ_REG(indexId.id()));
Value rhs = READ_VALUE_REG(rhsId.id());
NativeObject* nobj = &obj->as<NativeObject>();
uint32_t initLength = nobj->getDenseInitializedLength();
if (index < initLength) {
nobj->setDenseElement(index, rhs);
} else if (!handleAdd || index > initLength) {
FAIL_IC();
} else {
if (index >= nobj->getDenseCapacity()) {
if (!NativeObject::addDenseElementPure(
ctx.frameMgr.cxForLocalUseOnly(), nobj)) {
FAIL_IC();
}
}
nobj->setDenseInitializedLength(initLength + 1);
// Baseline always updates the length field by directly accessing its
// offset in ObjectElements. If the object is not an ArrayObject then
// this field is never read, so it's okay to skip the update here in
// that case.
if (nobj->is<ArrayObject>()) {
ArrayObject* aobj = &nobj->as<ArrayObject>();
uint32_t len = aobj->length();
if (len <= index) {
aobj->setLength(len + 1);
}
}
nobj->initDenseElement(index, rhs);
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ArrayPush) {
ObjOperandId objId = cacheIRReader.objOperandId();
ValOperandId rhsId = cacheIRReader.valOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
Value rhs = READ_VALUE_REG(rhsId.id());
ArrayObject* aobj = &obj->as<ArrayObject>();
uint32_t initLength = aobj->getDenseInitializedLength();
if (aobj->length() != initLength) {
FAIL_IC();
}
if (initLength >= aobj->getDenseCapacity()) {
if (!NativeObject::addDenseElementPure(
ctx.frameMgr.cxForLocalUseOnly(), aobj)) {
FAIL_IC();
}
}
aobj->setDenseInitializedLength(initLength + 1);
aobj->setLength(initLength + 1);
aobj->initDenseElement(initLength, rhs);
retValue = Int32Value(initLength + 1).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsObjectResult) {
ValOperandId inputId = cacheIRReader.valOperandId();
Value val = READ_VALUE_REG(inputId.id());
retValue = BooleanValue(val.isObject()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32MinMax) {
bool isMax = cacheIRReader.readBool();
Int32OperandId firstId = cacheIRReader.int32OperandId();
Int32OperandId secondId = cacheIRReader.int32OperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
int32_t lhs = int32_t(READ_REG(firstId.id()));
int32_t rhs = int32_t(READ_REG(secondId.id()));
int32_t result = ((lhs > rhs) ^ isMax) ? rhs : lhs;
WRITE_REG(resultId.id(), result, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StoreTypedArrayElement) {
ObjOperandId objId = cacheIRReader.objOperandId();
Scalar::Type elementType = cacheIRReader.scalarType();
IntPtrOperandId indexId = cacheIRReader.intPtrOperandId();
uint32_t rhsId = cacheIRReader.rawOperandId();
bool handleOOB = cacheIRReader.readBool();
ArrayBufferViewKind kind = cacheIRReader.arrayBufferViewKind();
(void)kind;
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uintptr_t index = uintptr_t(READ_REG(indexId.id()));
uint64_t rhs = READ_REG(rhsId);
if (obj->as<TypedArrayObject>().length().isNothing()) {
FAIL_IC();
}
if (index >= obj->as<TypedArrayObject>().length().value()) {
if (!handleOOB) {
FAIL_IC();
}
} else {
Value v;
switch (elementType) {
case Scalar::Int8:
case Scalar::Uint8:
case Scalar::Int16:
case Scalar::Uint16:
case Scalar::Int32:
case Scalar::Uint32:
case Scalar::Uint8Clamped:
v = Int32Value(rhs);
break;
case Scalar::Float16:
case Scalar::Float32:
case Scalar::Float64:
v = Value::fromRawBits(rhs);
MOZ_ASSERT(v.isNumber());
break;
case Scalar::BigInt64:
case Scalar::BigUint64:
v = BigIntValue(reinterpret_cast<JS::BigInt*>(rhs));
break;
case Scalar::MaxTypedArrayViewType:
case Scalar::Int64:
case Scalar::Simd128:
MOZ_CRASH("Unsupported TypedArray type");
}
// SetTypedArrayElement doesn't do anything that can actually GC or
// need a new context when the value can only be Int32, Double, or
// BigInt, as the above switch statement enforces.
FakeRooted<TypedArrayObject*> obj0(nullptr,
&obj->as<TypedArrayObject>());
FakeRooted<Value> value0(nullptr, v);
ObjectOpResult result;
MOZ_ASSERT(elementType == obj0->type());
MOZ_ALWAYS_TRUE(SetTypedArrayElement(ctx.frameMgr.cxForLocalUseOnly(),
obj0, index, value0, result));
MOZ_ALWAYS_TRUE(result.ok());
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadTypedArrayElementExistsResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
IntPtrOperandId indexId = cacheIRReader.intPtrOperandId();
ArrayBufferViewKind kind = cacheIRReader.arrayBufferViewKind();
(void)kind;
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uintptr_t index = uintptr_t(READ_REG(indexId.id()));
if (obj->as<TypedArrayObject>().length().isNothing()) {
FAIL_IC();
}
retValue =
BooleanValue(index < obj->as<TypedArrayObject>().length().value())
.asRawBits();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadTypedArrayElementResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
IntPtrOperandId indexId = cacheIRReader.intPtrOperandId();
Scalar::Type elementType = cacheIRReader.scalarType();
bool handleOOB = cacheIRReader.readBool();
bool forceDoubleForUint32 = cacheIRReader.readBool();
ArrayBufferViewKind kind = cacheIRReader.arrayBufferViewKind();
(void)kind;
(void)elementType;
(void)handleOOB;
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uintptr_t index = uintptr_t(READ_REG(indexId.id()));
if (obj->as<TypedArrayObject>().length().isNothing()) {
FAIL_IC();
}
if (index >= obj->as<TypedArrayObject>().length().value()) {
FAIL_IC();
}
Value v;
if (!obj->as<TypedArrayObject>().getElementPure(index, &v)) {
FAIL_IC();
}
if (forceDoubleForUint32) {
if (v.isInt32()) {
v.setNumber(v.toInt32());
}
}
retValue = v.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallInt32ToString) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
int32_t input = int32_t(READ_REG(inputId.id()));
JSLinearString* str =
Int32ToStringPure(ctx.frameMgr.cxForLocalUseOnly(), input);
if (str) {
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(str), STRING);
} else {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallScriptedFunction)
CACHEOP_CASE_FALLTHROUGH(CallNativeFunction) {
bool isNative = cacheop == CacheOp::CallNativeFunction;
TRACE_PRINTF("CallScriptedFunction / CallNativeFunction (native: %d)\n",
isNative);
ObjOperandId calleeId = cacheIRReader.objOperandId();
Int32OperandId argcId = cacheIRReader.int32OperandId();
CallFlags flags = cacheIRReader.callFlags();
uint32_t argcFixed = cacheIRReader.uint32Immediate();
bool ignoresRv = false;
if (isNative) {
ignoresRv = cacheIRReader.readBool();
}
TRACE_PRINTF("isConstructing = %d needsUninitializedThis = %d\n",
int(flags.isConstructing()),
int(flags.needsUninitializedThis()));
JSFunction* callee =
reinterpret_cast<JSFunction*>(READ_REG(calleeId.id()));
uint32_t argc = uint32_t(READ_REG(argcId.id()));
(void)argcFixed;
if (!isNative) {
if (!callee->hasBaseScript() ||
!callee->baseScript()->hasBytecode() ||
!callee->baseScript()->hasJitScript()) {
FAIL_IC();
}
}
// For now, fail any different-realm cases.
if (!flags.isSameRealm()) {
TRACE_PRINTF("failing: not same realm\n");
FAIL_IC();
}
// And support only "standard" arg formats.
if (flags.getArgFormat() != CallFlags::Standard) {
TRACE_PRINTF("failing: not standard arg format\n");
FAIL_IC();
}
// Fail constructing on a non-constructor callee.
if (flags.isConstructing() && !callee->isConstructor()) {
TRACE_PRINTF("failing: constructing a non-constructor\n");
FAIL_IC();
}
// Handle arg-underflow (but only for scripted targets).
uint32_t undefArgs = (!isNative && (argc < callee->nargs()))
? (callee->nargs() - argc)
: 0;
uint32_t extra = 1 + flags.isConstructing() + isNative;
uint32_t totalArgs = argc + undefArgs + extra;
StackVal* origArgs = ctx.sp();
{
PUSH_IC_FRAME();
if (!ctx.stack.check(sp, sizeof(StackVal) * (totalArgs + 6))) {
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
// Create `this` if we are constructing and this is a
// scripted function.
Value thisVal;
// Force JIT scripts to stick around, so we don't have to
// fail the IC after GC'ing. This is critical, because
// `stub` is not rooted (we don't have a BaselineStub frame
// in PBL, only an exit frame directly below a baseline
// function frame), so we cannot fall back to the next stub
// once we pass this point.
AutoKeepJitScripts keepJitScripts(cx);
if (flags.isConstructing() && !isNative) {
if (flags.needsUninitializedThis()) {
thisVal = MagicValue(JS_UNINITIALIZED_LEXICAL);
} else {
ReservedRooted<JSObject*> calleeObj(&ctx.state.obj0, callee);
ReservedRooted<JSObject*> newTargetRooted(
&ctx.state.obj1, &origArgs[0].asValue().toObject());
ReservedRooted<Value> result(&ctx.state.value0);
if (!CreateThisFromIC(cx, calleeObj, newTargetRooted, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
thisVal = result;
// `callee` may have moved.
callee = &calleeObj->as<JSFunction>();
}
}
// This will not be an Exit frame but a BaselineStub frame, so
// replace the ExitFrameType with the ICStub pointer.
POPNNATIVE(1);
PUSHNATIVE(StackValNative(cstub));
// `origArgs` is (in index order, i.e. increasing address order)
// - normal, scripted: arg[argc-1] ... arg[0] thisv
// - ctor, scripted: newTarget arg[argc-1] ... arg[0] thisv
// - normal, native: arg[argc-1] ... arg[0] thisv callee
// - ctor, native: newTarget arg[argc-1] ... arg[0] thisv callee
//
// and we need to push them in reverse order -- from sp
// upward (in increasing address order) -- with args filled
// in with `undefined` if fewer than the number of formals.
// Push args: newTarget if constructing, extra undef's added
// if underflow, then original args, and `callee` if
// native. Replace `this` if constructing.
if (flags.isConstructing()) {
PUSH(origArgs[0]);
origArgs++;
}
for (uint32_t i = 0; i < undefArgs; i++) {
PUSH(StackVal(UndefinedValue()));
}
for (uint32_t i = 0; i < argc + 1 + isNative; i++) {
PUSH(origArgs[i]);
}
if (flags.isConstructing() && !isNative) {
sp[0] = StackVal(thisVal);
}
Value* args = reinterpret_cast<Value*>(sp);
if (isNative) {
PUSHNATIVE(StackValNative(argc));
PUSHNATIVE(
StackValNative(MakeFrameDescriptor(FrameType::BaselineStub)));
// We *also* need an exit frame (the native baseline
// execution would invoke a trampoline here).
StackVal* trampolinePrevFP = ctx.stack.fp;
PUSHNATIVE(StackValNative(nullptr)); // fake return address.
PUSHNATIVE(StackValNative(ctx.stack.fp));
ctx.stack.fp = sp;
PUSHNATIVE(StackValNative(
uint32_t(flags.isConstructing() ? ExitFrameType::ConstructNative
: ExitFrameType::CallNative)));
cx.getCx()->activation()->asJit()->setJSExitFP(
reinterpret_cast<uint8_t*>(ctx.stack.fp));
cx.getCx()->portableBaselineStack().top =
reinterpret_cast<void*>(sp);
JSNative native = ignoresRv
? callee->jitInfo()->ignoresReturnValueMethod
: callee->native();
bool success = native(cx, argc, args);
ctx.stack.fp = trampolinePrevFP;
POPNNATIVE(4);
if (!success) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = args[0].asRawBits();
} else {
TRACE_PRINTF("pushing callee: %p\n", callee);
PUSHNATIVE(
StackValNative(CalleeToToken(callee, flags.isConstructing())));
PUSHNATIVE(StackValNative(
MakeFrameDescriptorForJitCall(FrameType::BaselineStub, argc)));
JSScript* script = callee->nonLazyScript();
jsbytecode* pc = script->code();
ImmutableScriptData* isd = script->immutableScriptData();
PBIResult result;
Value ret;
result = PortableBaselineInterpret<false, kHybridICsInterp>(
cx, ctx.state, ctx.stack, sp,
/* envChain = */ nullptr, &ret, pc, isd, nullptr, nullptr,
nullptr, PBIResult::Ok);
if (result != PBIResult::Ok) {
ctx.error = result;
return IC_ERROR_SENTINEL();
}
if (flags.isConstructing() && !ret.isObject()) {
ret = args[0];
}
retValue = ret.asRawBits();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallScriptedGetterResult)
CACHEOP_CASE_FALLTHROUGH(CallScriptedSetter) {
bool isSetter = cacheop == CacheOp::CallScriptedSetter;
ObjOperandId receiverId = cacheIRReader.objOperandId();
uint32_t getterSetterOffset = cacheIRReader.stubOffset();
ValOperandId rhsId =
isSetter ? cacheIRReader.valOperandId() : ValOperandId();
bool sameRealm = cacheIRReader.readBool();
uint32_t nargsAndFlagsOffset = cacheIRReader.stubOffset();
(void)nargsAndFlagsOffset;
Value receiver = isSetter ? ObjectValue(*reinterpret_cast<JSObject*>(
READ_REG(receiverId.id())))
: READ_VALUE_REG(receiverId.id());
JSFunction* callee = reinterpret_cast<JSFunction*>(
stubInfo->getStubRawWord(cstub, getterSetterOffset));
Value rhs = isSetter ? READ_VALUE_REG(rhsId.id()) : UndefinedValue();
if (!sameRealm) {
FAIL_IC();
}
if (!callee->hasBaseScript() || !callee->baseScript()->hasBytecode() ||
!callee->baseScript()->hasJitScript()) {
FAIL_IC();
}
// For now, fail any arg-underflow case.
if (callee->nargs() != isSetter ? 1 : 0) {
TRACE_PRINTF(
"failing: getter/setter does not have exactly 0/1 arg (has %d "
"instead)\n",
int(callee->nargs()));
FAIL_IC();
}
{
PUSH_IC_FRAME();
if (!ctx.stack.check(sp, sizeof(StackVal) * 8)) {
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
// This will not be an Exit frame but a BaselineStub frame, so
// replace the ExitFrameType with the ICStub pointer.
POPNNATIVE(1);
PUSHNATIVE(StackValNative(cstub));
if (isSetter) {
// Push arg: value.
PUSH(StackVal(rhs));
}
TRACE_PRINTF("pushing receiver: %" PRIx64 "\n", receiver.asRawBits());
// Push thisv: receiver.
PUSH(StackVal(receiver));
TRACE_PRINTF("pushing callee: %p\n", callee);
PUSHNATIVE(StackValNative(
CalleeToToken(callee, /* isConstructing = */ false)));
PUSHNATIVE(StackValNative(MakeFrameDescriptorForJitCall(
FrameType::BaselineStub, /* argc = */ isSetter ? 1 : 0)));
JSScript* script = callee->nonLazyScript();
jsbytecode* pc = script->code();
ImmutableScriptData* isd = script->immutableScriptData();
PBIResult result;
Value ret;
result = PortableBaselineInterpret<false, kHybridICsInterp>(
cx, ctx.state, ctx.stack, sp, /* envChain = */ nullptr, &ret, pc,
isd, nullptr, nullptr, nullptr, PBIResult::Ok);
if (result != PBIResult::Ok) {
ctx.error = result;
return IC_ERROR_SENTINEL();
}
retValue = ret.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallBoundScriptedFunction) {
ObjOperandId calleeId = cacheIRReader.objOperandId();
ObjOperandId targetId = cacheIRReader.objOperandId();
Int32OperandId argcId = cacheIRReader.int32OperandId();
CallFlags flags = cacheIRReader.callFlags();
uint32_t numBoundArgs = cacheIRReader.uint32Immediate();
BoundFunctionObject* boundFunc =
reinterpret_cast<BoundFunctionObject*>(READ_REG(calleeId.id()));
JSFunction* callee = &boundFunc->getTarget()->as<JSFunction>();
uint32_t argc = uint32_t(READ_REG(argcId.id()));
(void)targetId;
if (!callee->hasBaseScript() || !callee->baseScript()->hasBytecode() ||
!callee->baseScript()->hasJitScript()) {
FAIL_IC();
}
// For now, fail any constructing or different-realm cases.
if (flags.isConstructing()) {
TRACE_PRINTF("failing: constructing\n");
FAIL_IC();
}
if (!flags.isSameRealm()) {
TRACE_PRINTF("failing: not same realm\n");
FAIL_IC();
}
// And support only "standard" arg formats.
if (flags.getArgFormat() != CallFlags::Standard) {
TRACE_PRINTF("failing: not standard arg format\n");
FAIL_IC();
}
uint32_t totalArgs = numBoundArgs + argc;
// For now, fail any arg-underflow case.
if (totalArgs < callee->nargs()) {
TRACE_PRINTF("failing: too few args\n");
FAIL_IC();
}
StackVal* origArgs = ctx.sp();
{
PUSH_IC_FRAME();
if (!ctx.stack.check(sp, sizeof(StackVal) * (totalArgs + 6))) {
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
// This will not be an Exit frame but a BaselineStub frame, so
// replace the ExitFrameType with the ICStub pointer.
POPNNATIVE(1);
PUSHNATIVE(StackValNative(cstub));
// Push args.
for (uint32_t i = 0; i < argc; i++) {
PUSH(origArgs[i]);
}
// Push bound args.
for (uint32_t i = 0; i < numBoundArgs; i++) {
PUSH(StackVal(boundFunc->getBoundArg(numBoundArgs - 1 - i)));
}
// Push bound `this`.
PUSH(StackVal(boundFunc->getBoundThis()));
TRACE_PRINTF("pushing callee: %p\n", callee);
PUSHNATIVE(StackValNative(
CalleeToToken(callee, /* isConstructing = */ false)));
PUSHNATIVE(StackValNative(MakeFrameDescriptorForJitCall(
FrameType::BaselineStub, totalArgs)));
JSScript* script = callee->nonLazyScript();
jsbytecode* pc = script->code();
ImmutableScriptData* isd = script->immutableScriptData();
PBIResult result;
Value ret;
result = PortableBaselineInterpret<false, kHybridICsInterp>(
cx, ctx.state, ctx.stack, sp, /* envChain = */ nullptr, &ret, pc,
isd, nullptr, nullptr, nullptr, PBIResult::Ok);
if (result != PBIResult::Ok) {
ctx.error = result;
return IC_ERROR_SENTINEL();
}
retValue = ret.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MetaScriptedThisShape) {
uint32_t thisShapeOffset = cacheIRReader.stubOffset();
// This op is only metadata for the Warp Transpiler and should be
// ignored.
(void)thisShapeOffset;
PREDICT_NEXT(CallScriptedFunction);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadFixedSlotResult)
CACHEOP_CASE_FALLTHROUGH(LoadFixedSlotTypedResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
if (cacheop == CacheOp::LoadFixedSlotTypedResult) {
// Type is unused here.
(void)cacheIRReader.valueType();
}
uintptr_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
Value* slot = reinterpret_cast<Value*>(
reinterpret_cast<uintptr_t>(nobj) + offset);
TRACE_PRINTF(
"LoadFixedSlotResult: obj %p offsetOffset %d offset %d slotPtr %p "
"slot %" PRIx64 "\n",
nobj, int(offsetOffset), int(offset), slot, slot->asRawBits());
retValue = slot->asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDynamicSlotResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t offsetOffset = cacheIRReader.stubOffset();
uintptr_t offset = stubInfo->getStubRawInt32(cstub, offsetOffset);
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
HeapSlot* slots = nobj->getSlotsUnchecked();
retValue = slots[offset / sizeof(Value)].get().asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDenseElementResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
FAIL_IC();
}
HeapSlot* slot = &elems->elements()[index];
Value val = slot->get();
if (val.isMagic()) {
FAIL_IC();
}
retValue = val.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDenseElementHoleResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
FAIL_IC();
}
HeapSlot* slot = &elems->elements()[index];
Value val = slot->get();
if (val.isMagic()) {
val.setUndefined();
}
retValue = val.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDenseElementExistsResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
FAIL_IC();
}
HeapSlot* slot = &elems->elements()[index];
Value val = slot->get();
if (val.isMagic()) {
FAIL_IC();
}
retValue = BooleanValue(true).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDenseElementHoleExistsResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
retValue = BooleanValue(false).asRawBits();
} else {
HeapSlot* slot = &elems->elements()[index];
Value val = slot->get();
retValue = BooleanValue(!val.isMagic()).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardIndexIsNotDenseElement) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
ObjectElements* elems = nobj->getElementsHeader();
int32_t index = int32_t(READ_REG(indexId.id()));
if (index < 0 || uint32_t(index) >= nobj->getDenseInitializedLength()) {
// OK -- not in the dense index range.
} else {
HeapSlot* slot = &elems->elements()[index];
Value val = slot->get();
if (!val.isMagic()) {
// Not a magic value -- not the hole, so guard fails.
FAIL_IC();
}
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInt32ArrayLengthResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(objId.id()));
uint32_t length = aobj->length();
if (length > uint32_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(length).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInt32ArrayLength) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(objId.id()));
uint32_t length = aobj->length();
if (length > uint32_t(INT32_MAX)) {
FAIL_IC();
}
WRITE_REG(resultId.id(), length, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArrayBufferByteLengthInt32Result) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferObject* abo =
reinterpret_cast<ArrayBufferObject*>(READ_REG(objId.id()));
size_t len = abo->byteLength();
if (len > size_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(int32_t(len)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArrayBufferByteLengthDoubleResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferObject* abo =
reinterpret_cast<ArrayBufferObject*>(READ_REG(objId.id()));
size_t len = abo->byteLength();
retValue = DoubleValue(double(len)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArrayBufferViewLengthInt32Result) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferViewObject* abvo =
reinterpret_cast<ArrayBufferViewObject*>(READ_REG(objId.id()));
size_t len = size_t(
abvo->getFixedSlot(ArrayBufferViewObject::LENGTH_SLOT).toPrivate());
if (len > size_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(int32_t(len)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArrayBufferViewLengthDoubleResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferViewObject* abvo =
reinterpret_cast<ArrayBufferViewObject*>(READ_REG(objId.id()));
size_t len = size_t(
abvo->getFixedSlot(ArrayBufferViewObject::LENGTH_SLOT).toPrivate());
retValue = DoubleValue(double(len)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArgumentsObjectArgResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
uint32_t index = uint32_t(READ_REG(indexId.id()));
ArgumentsObject* args = &obj->as<ArgumentsObject>();
if (index >= args->initialLength() || args->hasOverriddenElement()) {
FAIL_IC();
}
if (args->argIsForwarded(index)) {
FAIL_IC();
}
retValue = args->arg(index).asRawBits();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LinearizeForCharAccess) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
(void)indexId;
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(str), STRING);
if (str->isRope()) {
PUSH_IC_FRAME();
JSLinearString* result = LinearizeForCharAccess(cx, str);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(result), STRING);
}
PREDICT_NEXT(LoadStringCharResult);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LinearizeForCodePointAccess) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
(void)indexId;
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(str), STRING);
if (str->isRope()) {
PUSH_IC_FRAME();
JSLinearString* result = LinearizeForCharAccess(cx, str);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
WRITE_REG(resultId.id(), reinterpret_cast<uintptr_t>(result), STRING);
}
PREDICT_NEXT(LoadStringCodePointResult);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringCharResult)
CACHEOP_CASE_FALLTHROUGH(LoadStringAtResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
bool handleOOB = cacheIRReader.readBool();
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
int32_t index = int32_t(READ_REG(indexId.id()));
JSString* result = nullptr;
if (index < 0 || size_t(index) >= str->length()) {
if (handleOOB) {
if (cacheop == CacheOp::LoadStringCharResult) {
// Return an empty string.
retValue =
StringValue(ctx.frameMgr.cxForLocalUseOnly()->names().empty_)
.asRawBits();
} else {
// Return `undefined`.
retValue = UndefinedValue().asRawBits();
}
} else {
FAIL_IC();
}
} else {
char16_t c;
// Guaranteed to always work because this CacheIR op is
// always preceded by LinearizeForCharAccess.
MOZ_ALWAYS_TRUE(str->getChar(/* cx = */ nullptr, index, &c));
StaticStrings& sstr =
ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasUnit(c)) {
result = sstr.getUnit(c);
} else {
PUSH_IC_FRAME();
result = StringFromCharCode(cx, c);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringCharCodeResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
bool handleOOB = cacheIRReader.readBool();
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
int32_t index = int32_t(READ_REG(indexId.id()));
Value result;
if (index < 0 || size_t(index) >= str->length()) {
if (handleOOB) {
// Return NaN.
result = JS::NaNValue();
} else {
FAIL_IC();
}
} else {
char16_t c;
// Guaranteed to always work because this CacheIR op is
// always preceded by LinearizeForCharAccess.
MOZ_ALWAYS_TRUE(str->getChar(/* cx = */ nullptr, index, &c));
result = Int32Value(c);
}
retValue = result.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringCodePointResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
bool handleOOB = cacheIRReader.readBool();
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
int32_t index = int32_t(READ_REG(indexId.id()));
Value result;
if (index < 0 || size_t(index) >= str->length()) {
if (handleOOB) {
// Return undefined.
result = UndefinedValue();
} else {
FAIL_IC();
}
} else {
char32_t c;
// Guaranteed to be always work because this CacheIR op is
// always preceded by LinearizeForCharAccess.
MOZ_ALWAYS_TRUE(str->getCodePoint(/* cx = */ nullptr, index, &c));
result = Int32Value(c);
}
retValue = result.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringLengthResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
size_t length = str->length();
if (length > size_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(length).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadObjectResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
retValue =
ObjectValue(*reinterpret_cast<JSObject*>(READ_REG(objId.id())))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
retValue =
StringValue(reinterpret_cast<JSString*>(READ_REG(strId.id())))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadSymbolResult) {
SymbolOperandId symId = cacheIRReader.symbolOperandId();
retValue =
SymbolValue(reinterpret_cast<JS::Symbol*>(READ_REG(symId.id())))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInt32Result) {
Int32OperandId valId = cacheIRReader.int32OperandId();
retValue = Int32Value(READ_REG(valId.id())).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDoubleResult) {
NumberOperandId valId = cacheIRReader.numberOperandId();
Value val = READ_VALUE_REG(valId.id());
if (val.isInt32()) {
val = DoubleValue(val.toInt32());
}
retValue = val.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadBigIntResult) {
BigIntOperandId valId = cacheIRReader.bigIntOperandId();
retValue =
BigIntValue(reinterpret_cast<JS::BigInt*>(READ_REG(valId.id())))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadBooleanResult) {
bool val = cacheIRReader.readBool();
retValue = BooleanValue(val).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInt32Constant) {
uint32_t valOffset = cacheIRReader.stubOffset();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
uint32_t value = stubInfo->getStubRawInt32(cstub, valOffset);
WRITE_REG(resultId.id(), value, INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadConstantStringResult) {
uint32_t strOffset = cacheIRReader.stubOffset();
JSString* str = reinterpret_cast<JSString*>(
stubInfo->getStubRawWord(cstub, strOffset));
retValue = StringValue(str).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleAddResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue = DoubleValue(lhs.toNumber() + rhs.toNumber()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleSubResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue = DoubleValue(lhs.toNumber() - rhs.toNumber()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleMulResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue = DoubleValue(lhs.toNumber() * rhs.toNumber()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleDivResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue =
DoubleValue(NumberDiv(lhs.toNumber(), rhs.toNumber())).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleModResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue =
DoubleValue(NumberMod(lhs.toNumber(), rhs.toNumber())).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoublePowResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
Value rhs = READ_VALUE_REG(rhsId.id());
retValue =
DoubleValue(ecmaPow(lhs.toNumber(), rhs.toNumber())).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
#define INT32_OP(name, op, extra_check) \
CACHEOP_CASE(Int32##name##Result) { \
Int32OperandId lhsId = cacheIRReader.int32OperandId(); \
Int32OperandId rhsId = cacheIRReader.int32OperandId(); \
int64_t lhs = int64_t(int32_t(READ_REG(lhsId.id()))); \
int64_t rhs = int64_t(int32_t(READ_REG(rhsId.id()))); \
extra_check; \
int64_t result = lhs op rhs; \
if (result < INT32_MIN || result > INT32_MAX) { \
FAIL_IC(); \
} \
retValue = Int32Value(int32_t(result)).asRawBits(); \
PREDICT_RETURN(); \
DISPATCH_CACHEOP(); \
}
// clang-format off
INT32_OP(Add, +, {});
INT32_OP(Sub, -, {});
// clang-format on
INT32_OP(Mul, *, {
if (rhs * lhs == 0 && ((rhs < 0) ^ (lhs < 0))) {
FAIL_IC();
}
});
INT32_OP(Div, /, {
if (rhs == 0 || (lhs == INT32_MIN && rhs == -1)) {
FAIL_IC();
}
if (lhs == 0 && rhs < 0) {
FAIL_IC();
}
if (lhs % rhs != 0) {
FAIL_IC();
}
});
INT32_OP(Mod, %, {
if (rhs == 0 || (lhs == INT32_MIN && rhs == -1)) {
FAIL_IC();
}
if (lhs % rhs == 0 && lhs < 0) {
FAIL_IC();
}
});
// clang-format off
INT32_OP(BitOr, |, {});
INT32_OP(BitXor, ^, {});
INT32_OP(BitAnd, &, {});
// clang-format on
CACHEOP_CASE(Int32PowResult) {
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
int64_t lhs = int64_t(int32_t(READ_REG(lhsId.id())));
uint64_t rhs = uint64_t(int32_t(READ_REG(rhsId.id())));
int64_t result;
if (lhs == 1) {
result = 1;
} else if (rhs >= uint64_t(INT64_MIN)) {
FAIL_IC();
} else {
result = 1;
int64_t runningSquare = lhs;
while (rhs) {
if (rhs & 1) {
result *= runningSquare;
if (result > int64_t(INT32_MAX)) {
FAIL_IC();
}
}
rhs >>= 1;
if (rhs == 0) {
break;
}
runningSquare *= runningSquare;
if (runningSquare > int64_t(INT32_MAX)) {
FAIL_IC();
}
}
}
retValue = Int32Value(int32_t(result)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32IncResult) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
int64_t value = int64_t(int32_t(READ_REG(inputId.id())));
value++;
if (value > INT32_MAX) {
FAIL_IC();
}
retValue = Int32Value(int32_t(value)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32LeftShiftResult) {
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
int32_t lhs = int32_t(READ_REG(lhsId.id()));
int32_t rhs = int32_t(READ_REG(rhsId.id()));
int32_t result = lhs << (rhs & 0x1F);
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32RightShiftResult) {
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
int32_t lhs = int32_t(READ_REG(lhsId.id()));
int32_t rhs = int32_t(READ_REG(rhsId.id()));
int32_t result = lhs >> (rhs & 0x1F);
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32URightShiftResult) {
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
bool forceDouble = cacheIRReader.readBool();
(void)forceDouble;
uint32_t lhs = uint32_t(READ_REG(lhsId.id()));
int32_t rhs = int32_t(READ_REG(rhsId.id()));
uint32_t result = lhs >> (rhs & 0x1F);
retValue = (result >= 0x80000000)
? DoubleValue(double(result)).asRawBits()
: Int32Value(int32_t(result)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32NotResult) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
int32_t input = int32_t(READ_REG(inputId.id()));
retValue = Int32Value(~input).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInt32TruthyResult) {
ValOperandId inputId = cacheIRReader.valOperandId();
int32_t val = int32_t(READ_REG(inputId.id()));
retValue = BooleanValue(val != 0).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDoubleTruthyResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
// NaN is falsy, not truthy.
retValue = BooleanValue(input != 0.0 && !std::isnan(input)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadStringTruthyResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSLinearString*>(READ_REG(strId.id()));
retValue = BooleanValue(str->length() > 0).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadObjectTruthyResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
const JSClass* cls = obj->getClass();
if (cls->isProxyObject()) {
FAIL_IC();
}
retValue = BooleanValue(!cls->emulatesUndefined()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadValueResult) {
uint32_t valOffset = cacheIRReader.stubOffset();
retValue = stubInfo->getStubRawInt64(cstub, valOffset);
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadOperandResult) {
ValOperandId inputId = cacheIRReader.valOperandId();
retValue = READ_REG(inputId.id());
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallStringConcatResult) {
StringOperandId lhsId = cacheIRReader.stringOperandId();
StringOperandId rhsId = cacheIRReader.stringOperandId();
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> lhs(
&ctx.state.str0,
reinterpret_cast<JSString*>(READ_REG(lhsId.id())));
ReservedRooted<JSString*> rhs(
&ctx.state.str1,
reinterpret_cast<JSString*>(READ_REG(rhsId.id())));
JSString* result =
ConcatStrings<CanGC>(ctx.frameMgr.cxForLocalUseOnly(), lhs, rhs);
if (result) {
retValue = StringValue(result).asRawBits();
} else {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareStringResult) {
JSOp op = cacheIRReader.jsop();
StringOperandId lhsId = cacheIRReader.stringOperandId();
StringOperandId rhsId = cacheIRReader.stringOperandId();
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> lhs(
&ctx.state.str0,
reinterpret_cast<JSString*>(READ_REG(lhsId.id())));
ReservedRooted<JSString*> rhs(
&ctx.state.str1,
reinterpret_cast<JSString*>(READ_REG(rhsId.id())));
bool result;
if (lhs == rhs) {
// If operands point to the same instance, the strings are trivially
// equal.
result = op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
op == JSOp::Ge;
} else {
switch (op) {
case JSOp::Eq:
case JSOp::StrictEq:
if (lhs->isAtom() && rhs->isAtom()) {
result = false;
break;
}
if (lhs->length() != rhs->length()) {
result = false;
break;
}
if (!StringsEqual<EqualityKind::Equal>(cx, lhs, rhs, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
case JSOp::Ne:
case JSOp::StrictNe:
if (lhs->isAtom() && rhs->isAtom()) {
result = true;
break;
}
if (lhs->length() != rhs->length()) {
result = true;
break;
}
if (!StringsEqual<EqualityKind::NotEqual>(cx, lhs, rhs,
&result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
case JSOp::Lt:
if (!StringsCompare<ComparisonKind::LessThan>(cx, lhs, rhs,
&result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
case JSOp::Ge:
if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>(
cx, lhs, rhs, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
case JSOp::Le:
if (!StringsCompare<ComparisonKind::GreaterThanOrEqual>(
cx, /* N.B. swapped order */ rhs, lhs, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
case JSOp::Gt:
if (!StringsCompare<ComparisonKind::LessThan>(
cx, /* N.B. swapped order */ rhs, lhs, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
break;
default:
MOZ_CRASH("bad opcode");
}
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareInt32Result) {
JSOp op = cacheIRReader.jsop();
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
int64_t lhs = int64_t(int32_t(READ_REG(lhsId.id())));
int64_t rhs = int64_t(int32_t(READ_REG(rhsId.id())));
TRACE_PRINTF("lhs (%d) = %" PRIi64 " rhs (%d) = %" PRIi64 "\n",
lhsId.id(), lhs, rhsId.id(), rhs);
bool result;
switch (op) {
case JSOp::Eq:
case JSOp::StrictEq:
result = lhs == rhs;
break;
case JSOp::Ne:
case JSOp::StrictNe:
result = lhs != rhs;
break;
case JSOp::Lt:
result = lhs < rhs;
break;
case JSOp::Le:
result = lhs <= rhs;
break;
case JSOp::Gt:
result = lhs > rhs;
break;
case JSOp::Ge:
result = lhs >= rhs;
break;
default:
MOZ_CRASH("Unexpected opcode");
}
retValue = BooleanValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareDoubleResult) {
JSOp op = cacheIRReader.jsop();
NumberOperandId lhsId = cacheIRReader.numberOperandId();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
double lhs = READ_VALUE_REG(lhsId.id()).toNumber();
double rhs = READ_VALUE_REG(rhsId.id()).toNumber();
bool result;
switch (op) {
case JSOp::Eq:
case JSOp::StrictEq:
result = lhs == rhs;
break;
case JSOp::Ne:
case JSOp::StrictNe:
result = lhs != rhs;
break;
case JSOp::Lt:
result = lhs < rhs;
break;
case JSOp::Le:
result = lhs <= rhs;
break;
case JSOp::Gt:
result = lhs > rhs;
break;
case JSOp::Ge:
result = lhs >= rhs;
break;
default:
MOZ_CRASH("Unexpected opcode");
}
retValue = BooleanValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareNullUndefinedResult) {
JSOp op = cacheIRReader.jsop();
bool isUndefined = cacheIRReader.readBool();
ValOperandId inputId = cacheIRReader.valOperandId();
Value val = READ_VALUE_REG(inputId.id());
if (val.isObject() && val.toObject().getClass()->isProxyObject()) {
FAIL_IC();
}
bool result;
switch (op) {
case JSOp::Eq:
result = val.isUndefined() || val.isNull() ||
(val.isObject() &&
val.toObject().getClass()->emulatesUndefined());
break;
case JSOp::Ne:
result = !(val.isUndefined() || val.isNull() ||
(val.isObject() &&
val.toObject().getClass()->emulatesUndefined()));
break;
case JSOp::StrictEq:
result = isUndefined ? val.isUndefined() : val.isNull();
break;
case JSOp::StrictNe:
result = !(isUndefined ? val.isUndefined() : val.isNull());
break;
default:
MOZ_CRASH("bad opcode");
}
retValue = BooleanValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareObjectResult) {
JSOp op = cacheIRReader.jsop();
ObjOperandId lhsId = cacheIRReader.objOperandId();
ObjOperandId rhsId = cacheIRReader.objOperandId();
(void)op;
JSObject* lhs = reinterpret_cast<JSObject*>(READ_REG(lhsId.id()));
JSObject* rhs = reinterpret_cast<JSObject*>(READ_REG(rhsId.id()));
switch (op) {
case JSOp::Eq:
case JSOp::StrictEq:
retValue = BooleanValue(lhs == rhs).asRawBits();
break;
case JSOp::Ne:
case JSOp::StrictNe:
retValue = BooleanValue(lhs != rhs).asRawBits();
break;
default:
FAIL_IC();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CompareSymbolResult) {
JSOp op = cacheIRReader.jsop();
SymbolOperandId lhsId = cacheIRReader.symbolOperandId();
SymbolOperandId rhsId = cacheIRReader.symbolOperandId();
(void)op;
JS::Symbol* lhs = reinterpret_cast<JS::Symbol*>(READ_REG(lhsId.id()));
JS::Symbol* rhs = reinterpret_cast<JS::Symbol*>(READ_REG(rhsId.id()));
switch (op) {
case JSOp::Eq:
case JSOp::StrictEq:
retValue = BooleanValue(lhs == rhs).asRawBits();
break;
case JSOp::Ne:
case JSOp::StrictNe:
retValue = BooleanValue(lhs != rhs).asRawBits();
break;
default:
FAIL_IC();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(AssertPropertyLookup) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t idOffset = cacheIRReader.stubOffset();
uint32_t slotOffset = cacheIRReader.stubOffset();
// Debug-only assertion; we can ignore.
(void)objId;
(void)idOffset;
(void)slotOffset;
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathSqrtNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
retValue = NumberValue(sqrt(input)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathAbsInt32Result) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
int32_t input = int32_t(READ_REG(inputId.id()));
if (input == INT32_MIN) {
FAIL_IC();
}
if (input < 0) {
input = -input;
}
retValue = Int32Value(input).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathAbsNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
retValue = DoubleValue(fabs(input)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathClz32Result) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
int32_t input = int32_t(READ_REG(inputId.id()));
int32_t result =
(input == 0) ? 32 : mozilla::CountLeadingZeroes32(input);
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathSignInt32Result) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
int32_t input = int32_t(READ_REG(inputId.id()));
int32_t result = (input == 0) ? 0 : ((input > 0) ? 1 : -1);
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathSignNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
double result = 0;
if (std::isnan(input)) {
result = JS::GenericNaN();
} else if (input == 0 && std::signbit(input)) {
result = -0.0;
} else if (input == 0) {
result = 0;
} else if (input > 0) {
result = 1;
} else {
result = -1;
}
retValue = DoubleValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathSignNumberToInt32Result) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
int32_t result = 0;
if (std::isnan(input) || (input == 0.0 && std::signbit(input))) {
FAIL_IC();
} else if (input == 0) {
result = 0;
} else if (input > 0) {
result = 1;
} else {
result = -1;
}
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathImulResult) {
Int32OperandId lhsId = cacheIRReader.int32OperandId();
Int32OperandId rhsId = cacheIRReader.int32OperandId();
int32_t lhs = int32_t(READ_REG(lhsId.id()));
int32_t rhs = int32_t(READ_REG(rhsId.id()));
int32_t result = lhs * rhs;
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathFRoundNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
retValue = DoubleValue(double(float(input))).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathRandomResult) {
uint32_t rngOffset = cacheIRReader.stubOffset();
auto* rng = reinterpret_cast<mozilla::non_crypto::XorShift128PlusRNG*>(
stubInfo->getStubRawWord(cstub, rngOffset));
retValue = DoubleValue(rng->nextDouble()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathHypot2NumberResult) {
NumberOperandId firstId = cacheIRReader.numberOperandId();
double first = READ_VALUE_REG(firstId.id()).toNumber();
NumberOperandId secondId = cacheIRReader.numberOperandId();
double second = READ_VALUE_REG(secondId.id()).toNumber();
retValue = DoubleValue(ecmaHypot(first, second)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathHypot3NumberResult) {
NumberOperandId firstId = cacheIRReader.numberOperandId();
double first = READ_VALUE_REG(firstId.id()).toNumber();
NumberOperandId secondId = cacheIRReader.numberOperandId();
double second = READ_VALUE_REG(secondId.id()).toNumber();
NumberOperandId thirdId = cacheIRReader.numberOperandId();
double third = READ_VALUE_REG(thirdId.id()).toNumber();
retValue = DoubleValue(hypot3(first, second, third)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathHypot4NumberResult) {
NumberOperandId firstId = cacheIRReader.numberOperandId();
double first = READ_VALUE_REG(firstId.id()).toNumber();
NumberOperandId secondId = cacheIRReader.numberOperandId();
double second = READ_VALUE_REG(secondId.id()).toNumber();
NumberOperandId thirdId = cacheIRReader.numberOperandId();
double third = READ_VALUE_REG(thirdId.id()).toNumber();
NumberOperandId fourthId = cacheIRReader.numberOperandId();
double fourth = READ_VALUE_REG(fourthId.id()).toNumber();
retValue =
DoubleValue(hypot4(first, second, third, fourth)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathAtan2NumberResult) {
NumberOperandId lhsId = cacheIRReader.numberOperandId();
double lhs = READ_VALUE_REG(lhsId.id()).toNumber();
NumberOperandId rhsId = cacheIRReader.numberOperandId();
double rhs = READ_VALUE_REG(rhsId.id()).toNumber();
retValue = DoubleValue(ecmaAtan2(lhs, rhs)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathFloorNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
double result = fdlibm_floor(input);
retValue = DoubleValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathCeilNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
double result = fdlibm_ceil(input);
retValue = DoubleValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathTruncNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
double result = fdlibm_trunc(input);
retValue = DoubleValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathFloorToInt32Result) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
if (input == 0.0 && std::signbit(input)) {
FAIL_IC();
}
double result = fdlibm_floor(input);
int32_t intResult = int32_t(result);
if (double(intResult) != result) {
FAIL_IC();
}
retValue = Int32Value(intResult).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathCeilToInt32Result) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
if (input > -1.0 && std::signbit(input)) {
FAIL_IC();
}
double result = fdlibm_ceil(input);
int32_t intResult = int32_t(result);
if (double(intResult) != result) {
FAIL_IC();
}
retValue = Int32Value(intResult).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathTruncToInt32Result) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
if (input == 0.0 && std::signbit(input)) {
FAIL_IC();
}
double result = fdlibm_trunc(input);
int32_t intResult = int32_t(result);
if (double(intResult) != result) {
FAIL_IC();
}
retValue = Int32Value(intResult).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathRoundToInt32Result) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
if (input == 0.0 && std::signbit(input)) {
FAIL_IC();
}
int32_t intResult = int32_t(input);
if (double(intResult) != input) {
FAIL_IC();
}
retValue = Int32Value(intResult).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NumberMinMax) {
bool isMax = cacheIRReader.readBool();
NumberOperandId firstId = cacheIRReader.numberOperandId();
NumberOperandId secondId = cacheIRReader.numberOperandId();
NumberOperandId resultId = cacheIRReader.numberOperandId();
BOUNDSCHECK(resultId);
double first = READ_VALUE_REG(firstId.id()).toNumber();
double second = READ_VALUE_REG(secondId.id()).toNumber();
double result = DoubleMinMax(isMax, first, second);
WRITE_VALUE_REG(resultId.id(), DoubleValue(result));
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32MinMaxArrayResult) {
ObjOperandId arrayId = cacheIRReader.objOperandId();
bool isMax = cacheIRReader.readBool();
// ICs that use this opcode depend on implicit unboxing due to
// type-overload on ObjOperandId when a value is loaded
// directly from an argument slot. We explicitly unbox here.
NativeObject* nobj = reinterpret_cast<NativeObject*>(
&READ_VALUE_REG(arrayId.id()).toObject());
uint32_t len = nobj->getDenseInitializedLength();
if (len == 0) {
FAIL_IC();
}
ObjectElements* elems = nobj->getElementsHeader();
int32_t accum = 0;
for (uint32_t i = 0; i < len; i++) {
HeapSlot* slot = &elems->elements()[i];
Value val = slot->get();
if (!val.isInt32()) {
FAIL_IC();
}
int32_t valInt = val.toInt32();
if (i > 0) {
accum = isMax ? ((valInt > accum) ? valInt : accum)
: ((valInt < accum) ? valInt : accum);
} else {
accum = valInt;
}
}
retValue = Int32Value(accum).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NumberMinMaxArrayResult) {
ObjOperandId arrayId = cacheIRReader.objOperandId();
bool isMax = cacheIRReader.readBool();
// ICs that use this opcode depend on implicit unboxing due to
// type-overload on ObjOperandId when a value is loaded
// directly from an argument slot. We explicitly unbox here.
NativeObject* nobj = reinterpret_cast<NativeObject*>(
&READ_VALUE_REG(arrayId.id()).toObject());
uint32_t len = nobj->getDenseInitializedLength();
if (len == 0) {
FAIL_IC();
}
ObjectElements* elems = nobj->getElementsHeader();
double accum = 0;
for (uint32_t i = 0; i < len; i++) {
HeapSlot* slot = &elems->elements()[i];
Value val = slot->get();
if (!val.isNumber()) {
FAIL_IC();
}
double valDouble = val.toNumber();
if (i > 0) {
accum = DoubleMinMax(isMax, accum, valDouble);
} else {
accum = valDouble;
}
}
retValue = DoubleValue(accum).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MathFunctionNumberResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
UnaryMathFunction fun = cacheIRReader.unaryMathFunction();
double input = READ_VALUE_REG(inputId.id()).toNumber();
auto funPtr = GetUnaryMathFunctionPtr(fun);
retValue = DoubleValue(funPtr(input)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NumberParseIntResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId radixId = cacheIRReader.int32OperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
int32_t radix = int32_t(READ_REG(radixId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<Value> result(&ctx.state.value0);
if (!NumberParseInt(cx, str0, radix, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = result.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(DoubleParseIntResult) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
double input = READ_VALUE_REG(inputId.id()).toNumber();
if (std::isnan(input)) {
FAIL_IC();
}
int32_t result = int32_t(input);
if (double(result) != input) {
FAIL_IC();
}
retValue = Int32Value(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardTagNotEqual) {
ValueTagOperandId lhsId = cacheIRReader.valueTagOperandId();
ValueTagOperandId rhsId = cacheIRReader.valueTagOperandId();
int32_t lhs = int32_t(READ_REG(lhsId.id()));
int32_t rhs = int32_t(READ_REG(rhsId.id()));
if (lhs == rhs) {
FAIL_IC();
}
if (JSValueTag(lhs) <= JSVAL_TAG_INT32 ||
JSValueTag(rhs) <= JSVAL_TAG_INT32) {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GuardNumberToIntPtrIndex) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
bool supportOOB = cacheIRReader.readBool();
(void)supportOOB;
IntPtrOperandId resultId = cacheIRReader.intPtrOperandId();
BOUNDSCHECK(resultId);
double input = READ_VALUE_REG(inputId.id()).toNumber();
// For simplicity, support only uint32 range for now. This
// covers 32-bit and 64-bit systems.
if (input < 0.0 || input >= (uint64_t(1) << 32)) {
FAIL_IC();
}
uintptr_t result = static_cast<uintptr_t>(input);
// Convert back and compare to detect rounded fractional
// parts.
if (static_cast<double>(result) != input) {
FAIL_IC();
}
WRITE_REG(resultId.id(), uint64_t(result), OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadTypeOfObjectResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
const JSClass* cls = obj->getClass();
if (cls->isProxyObject()) {
FAIL_IC();
}
if (obj->is<JSFunction>()) {
retValue =
StringValue(ctx.frameMgr.cxForLocalUseOnly()->names().function)
.asRawBits();
} else if (cls->emulatesUndefined()) {
retValue =
StringValue(ctx.frameMgr.cxForLocalUseOnly()->names().undefined)
.asRawBits();
} else {
retValue =
StringValue(ctx.frameMgr.cxForLocalUseOnly()->names().object)
.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(PackedArrayPopResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(objId.id()));
ObjectElements* elements = aobj->getElementsHeader();
if (!elements->isPacked() || elements->hasNonwritableArrayLength() ||
elements->isNotExtensible() || elements->maybeInIteration()) {
FAIL_IC();
}
size_t len = aobj->length();
if (len != aobj->getDenseInitializedLength()) {
FAIL_IC();
}
if (len == 0) {
retValue = UndefinedValue().asRawBits();
} else {
HeapSlot* slot = &elements->elements()[len - 1];
retValue = slot->get().asRawBits();
len--;
aobj->setLength(len);
aobj->setDenseInitializedLength(len);
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(PackedArrayShiftResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(objId.id()));
ObjectElements* elements = aobj->getElementsHeader();
if (!elements->isPacked() || elements->hasNonwritableArrayLength() ||
elements->isNotExtensible() || elements->maybeInIteration()) {
FAIL_IC();
}
size_t len = aobj->length();
if (len != aobj->getDenseInitializedLength()) {
FAIL_IC();
}
if (len == 0) {
retValue = UndefinedValue().asRawBits();
} else {
HeapSlot* slot = &elements->elements()[0];
retValue = slot->get().asRawBits();
ArrayShiftMoveElements(aobj);
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(PackedArraySliceResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
ObjOperandId arrayId = cacheIRReader.objOperandId();
Int32OperandId beginId = cacheIRReader.int32OperandId();
Int32OperandId endId = cacheIRReader.int32OperandId();
(void)templateObjectOffset;
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(arrayId.id()));
int32_t begin = int32_t(READ_REG(beginId.id()));
int32_t end = int32_t(READ_REG(endId.id()));
if (!aobj->getElementsHeader()->isPacked()) {
FAIL_IC();
}
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> arr(&ctx.state.obj0, aobj);
JSObject* ret = ArraySliceDense(cx, arr, begin, end, nullptr);
if (!ret) {
FAIL_IC();
}
retValue = ObjectValue(*ret).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsPackedArrayResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!obj->is<ArrayObject>()) {
retValue = BooleanValue(false).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
ArrayObject* aobj =
reinterpret_cast<ArrayObject*>(READ_REG(objId.id()));
if (aobj->length() != aobj->getDenseInitializedLength()) {
retValue = BooleanValue(false).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
retValue = BooleanValue(aobj->denseElementsArePacked()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArgumentsObjectLengthResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArgumentsObject* obj =
reinterpret_cast<ArgumentsObject*>(READ_REG(objId.id()));
if (obj->hasOverriddenLength()) {
FAIL_IC();
}
retValue = Int32Value(obj->initialLength()).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadArgumentsObjectLength) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
ArgumentsObject* obj =
reinterpret_cast<ArgumentsObject*>(READ_REG(objId.id()));
if (obj->hasOverriddenLength()) {
FAIL_IC();
}
WRITE_REG(resultId.id(), obj->initialLength(), INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ObjectToIteratorResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t enumeratorsAddr = cacheIRReader.stubOffset();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
(void)enumeratorsAddr;
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> rootedObj(&ctx.state.obj0, obj);
auto* iter = GetIterator(cx, rootedObj);
if (!iter) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*iter).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadUndefinedResult) {
retValue = UndefinedValue().asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadDoubleConstant) {
uint32_t valOffset = cacheIRReader.stubOffset();
NumberOperandId resultId = cacheIRReader.numberOperandId();
BOUNDSCHECK(resultId);
WRITE_VALUE_REG(
resultId.id(),
Value::fromRawBits(stubInfo->getStubRawInt64(cstub, valOffset)));
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadBooleanConstant) {
bool val = cacheIRReader.readBool();
BooleanOperandId resultId = cacheIRReader.booleanOperandId();
BOUNDSCHECK(resultId);
WRITE_REG(resultId.id(), val ? 1 : 0, BOOLEAN);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadUndefined) {
ValOperandId resultId = cacheIRReader.numberOperandId();
BOUNDSCHECK(resultId);
WRITE_VALUE_REG(resultId.id(), UndefinedValue());
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadConstantString) {
uint32_t valOffset = cacheIRReader.stubOffset();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
JSString* str = reinterpret_cast<JSString*>(
stubInfo->getStubRawWord(cstub, valOffset));
WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(str), STRING);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewPlainObjectResult) {
uint32_t numFixedSlots = cacheIRReader.uint32Immediate();
uint32_t numDynamicSlots = cacheIRReader.uint32Immediate();
gc::AllocKind allocKind = cacheIRReader.allocKind();
uint32_t shapeOffset = cacheIRReader.stubOffset();
uint32_t siteOffset = cacheIRReader.stubOffset();
(void)numFixedSlots;
(void)numDynamicSlots;
SharedShape* shape = reinterpret_cast<SharedShape*>(
stubInfo->getStubRawWord(cstub, shapeOffset));
gc::AllocSite* site = reinterpret_cast<gc::AllocSite*>(
stubInfo->getStubRawWord(cstub, siteOffset));
{
PUSH_IC_FRAME();
Rooted<SharedShape*> rootedShape(cx, shape);
auto* result =
NewPlainObjectBaselineFallback(cx, rootedShape, allocKind, site);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewArrayObjectResult) {
uint32_t arrayLength = cacheIRReader.uint32Immediate();
uint32_t shapeOffset = cacheIRReader.stubOffset();
uint32_t siteOffset = cacheIRReader.stubOffset();
(void)shapeOffset;
gc::AllocSite* site = reinterpret_cast<gc::AllocSite*>(
stubInfo->getStubRawWord(cstub, siteOffset));
gc::AllocKind allocKind = GuessArrayGCKind(arrayLength);
MOZ_ASSERT(
CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_));
allocKind = ForegroundToBackgroundAllocKind(allocKind);
{
PUSH_IC_FRAME();
auto* result =
NewArrayObjectBaselineFallback(cx, arrayLength, allocKind, site);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewArrayFromLengthResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
Int32OperandId lengthId = cacheIRReader.int32OperandId();
uint32_t siteOffset = cacheIRReader.stubOffset();
ArrayObject* templateObject = reinterpret_cast<ArrayObject*>(
stubInfo->getStubRawWord(cstub, templateObjectOffset));
gc::AllocSite* site = reinterpret_cast<gc::AllocSite*>(
stubInfo->getStubRawWord(cstub, siteOffset));
int32_t length = int32_t(READ_REG(lengthId.id()));
{
PUSH_IC_FRAME();
Rooted<ArrayObject*> templateObjectRooted(cx, templateObject);
auto* result =
ArrayConstructorOneArg(cx, templateObjectRooted, length, site);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewTypedArrayFromLengthResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
Int32OperandId lengthId = cacheIRReader.int32OperandId();
TypedArrayObject* templateObject = reinterpret_cast<TypedArrayObject*>(
stubInfo->getStubRawWord(cstub, templateObjectOffset));
int32_t length = int32_t(READ_REG(lengthId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> templateObjectRooted(&ctx.state.obj0,
templateObject);
auto* result = NewTypedArrayWithTemplateAndLength(
cx, templateObjectRooted, length);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewTypedArrayFromArrayBufferResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
ObjOperandId bufferId = cacheIRReader.objOperandId();
ValOperandId byteOffsetId = cacheIRReader.valOperandId();
ValOperandId lengthId = cacheIRReader.valOperandId();
TypedArrayObject* templateObject = reinterpret_cast<TypedArrayObject*>(
stubInfo->getStubRawWord(cstub, templateObjectOffset));
JSObject* buffer = reinterpret_cast<JSObject*>(READ_REG(bufferId.id()));
Value byteOffset = READ_VALUE_REG(byteOffsetId.id());
Value length = READ_VALUE_REG(lengthId.id());
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> templateObjectRooted(&ctx.state.obj0,
templateObject);
ReservedRooted<JSObject*> bufferRooted(&ctx.state.obj1, buffer);
ReservedRooted<Value> byteOffsetRooted(&ctx.state.value0, byteOffset);
ReservedRooted<Value> lengthRooted(&ctx.state.value1, length);
auto* result = NewTypedArrayWithTemplateAndBuffer(
cx, templateObjectRooted, bufferRooted, byteOffsetRooted,
lengthRooted);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewTypedArrayFromArrayResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
ObjOperandId arrayId = cacheIRReader.objOperandId();
TypedArrayObject* templateObject = reinterpret_cast<TypedArrayObject*>(
stubInfo->getStubRawWord(cstub, templateObjectOffset));
JSObject* array = reinterpret_cast<JSObject*>(READ_REG(arrayId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> templateObjectRooted(&ctx.state.obj0,
templateObject);
ReservedRooted<JSObject*> arrayRooted(&ctx.state.obj1, array);
auto* result = NewTypedArrayWithTemplateAndArray(
cx, templateObjectRooted, arrayRooted);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ObjectToStringResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
{
PUSH_IC_FRAME();
auto* result = ObjectClassToString(cx, obj);
if (!result) {
FAIL_IC();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallNativeGetterResult) {
ValOperandId receiverId = cacheIRReader.valOperandId();
uint32_t getterOffset = cacheIRReader.stubOffset();
bool sameRealm = cacheIRReader.readBool();
uint32_t nargsAndFlagsOffset = cacheIRReader.stubOffset();
(void)sameRealm;
(void)nargsAndFlagsOffset;
Value receiver = READ_VALUE_REG(receiverId.id());
JSFunction* getter = reinterpret_cast<JSFunction*>(
stubInfo->getStubRawWord(cstub, getterOffset));
{
PUSH_IC_FRAME();
ReservedRooted<JSFunction*> getterRooted(&ctx.state.fun0, getter);
ReservedRooted<Value> receiverRooted(&ctx.state.value0, receiver);
ReservedRooted<Value> resultRooted(&ctx.state.value1);
if (!CallNativeGetter(cx, getterRooted, receiverRooted,
&resultRooted)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = resultRooted.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallNativeSetter) {
ValOperandId receiverId = cacheIRReader.valOperandId();
uint32_t setterOffset = cacheIRReader.stubOffset();
ObjOperandId rhsId = cacheIRReader.objOperandId();
bool sameRealm = cacheIRReader.readBool();
uint32_t nargsAndFlagsOffset = cacheIRReader.stubOffset();
(void)sameRealm;
(void)nargsAndFlagsOffset;
JSObject* receiver =
reinterpret_cast<JSObject*>(READ_REG(receiverId.id()));
Value rhs = READ_VALUE_REG(rhsId.id());
JSFunction* setter = reinterpret_cast<JSFunction*>(
stubInfo->getStubRawWord(cstub, setterOffset));
{
PUSH_IC_FRAME();
ReservedRooted<JSFunction*> setterRooted(&ctx.state.fun0, setter);
ReservedRooted<JSObject*> receiverRooted(&ctx.state.obj0, receiver);
ReservedRooted<Value> rhsRooted(&ctx.state.value1, rhs);
if (!CallNativeSetter(cx, setterRooted, receiverRooted, rhsRooted)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadInstanceOfObjectResult) {
ValOperandId lhsId = cacheIRReader.valOperandId();
ObjOperandId protoId = cacheIRReader.objOperandId();
Value lhs = READ_VALUE_REG(lhsId.id());
JSObject* rhsProto =
reinterpret_cast<JSObject*>(READ_REG(protoId.id()));
if (!lhs.isObject()) {
retValue = BooleanValue(false).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
JSObject* lhsObj = &lhs.toObject();
bool result = false;
while (true) {
TaggedProto proto = lhsObj->taggedProto();
if (proto.isDynamic()) {
FAIL_IC();
}
JSObject* protoObj = proto.toObjectOrNull();
if (!protoObj) {
result = false;
break;
}
if (protoObj == rhsProto) {
result = true;
break;
}
lhsObj = protoObj;
}
retValue = BooleanValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringFromCharCodeResult) {
Int32OperandId codeId = cacheIRReader.int32OperandId();
uint32_t code = uint32_t(READ_REG(codeId.id()));
StaticStrings& sstr = ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasUnit(code)) {
retValue = StringValue(sstr.getUnit(code)).asRawBits();
} else {
PUSH_IC_FRAME();
auto* result = StringFromCharCode(cx, code);
if (!result) {
FAIL_IC();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringFromCodePointResult) {
Int32OperandId codeId = cacheIRReader.int32OperandId();
uint32_t code = uint32_t(READ_REG(codeId.id()));
if (code > unicode::NonBMPMax) {
FAIL_IC();
}
StaticStrings& sstr = ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasUnit(code)) {
retValue = StringValue(sstr.getUnit(code)).asRawBits();
} else {
PUSH_IC_FRAME();
auto* result = StringFromCodePoint(cx, code);
if (!result) {
FAIL_IC();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringIncludesResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId searchStrId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* searchStr =
reinterpret_cast<JSString*>(READ_REG(searchStrId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, searchStr);
bool result = false;
if (!StringIncludes(cx, str0, str1, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringIndexOfResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId searchStrId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* searchStr =
reinterpret_cast<JSString*>(READ_REG(searchStrId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, searchStr);
int32_t result = 0;
if (!StringIndexOf(cx, str0, str1, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = Int32Value(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringLastIndexOfResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId searchStrId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* searchStr =
reinterpret_cast<JSString*>(READ_REG(searchStrId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, searchStr);
int32_t result = 0;
if (!StringLastIndexOf(cx, str0, str1, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = Int32Value(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringStartsWithResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId searchStrId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* searchStr =
reinterpret_cast<JSString*>(READ_REG(searchStrId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, searchStr);
bool result = false;
if (!StringStartsWith(cx, str0, str1, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringEndsWithResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId searchStrId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* searchStr =
reinterpret_cast<JSString*>(READ_REG(searchStrId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, searchStr);
bool result = false;
if (!StringEndsWith(cx, str0, str1, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringToLowerCaseResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = StringToLowerCase(cx, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringToUpperCaseResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = StringToUpperCase(cx, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringTrimResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = StringTrim(cx, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringTrimStartResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = StringTrimStart(cx, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringTrimEndResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = StringTrimEnd(cx, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallSubstringKernelResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
Int32OperandId beginId = cacheIRReader.int32OperandId();
Int32OperandId lengthId = cacheIRReader.int32OperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
int32_t begin = int32_t(READ_REG(beginId.id()));
int32_t length = int32_t(READ_REG(lengthId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
auto* result = SubstringKernel(cx, str0, begin, length);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringReplaceStringResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId patternId = cacheIRReader.stringOperandId();
StringOperandId replacementId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* pattern =
reinterpret_cast<JSString*>(READ_REG(patternId.id()));
JSString* replacement =
reinterpret_cast<JSString*>(READ_REG(replacementId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, pattern);
ReservedRooted<JSString*> str2(&ctx.state.str2, replacement);
auto* result = StringReplace(cx, str0, str1, str2);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringSplitStringResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
StringOperandId separatorId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSString* separator =
reinterpret_cast<JSString*>(READ_REG(separatorId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSString*> str0(&ctx.state.str0, str);
ReservedRooted<JSString*> str1(&ctx.state.str1, separator);
auto* result = StringSplitString(cx, str0, str1, INT32_MAX);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(StringToAtom) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
JSAtom* result =
AtomizeStringNoGC(ctx.frameMgr.cxForLocalUseOnly(), str);
if (!result) {
FAIL_IC();
}
WRITE_REG(strId.id(), reinterpret_cast<uint64_t>(result), STRING);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IdToStringOrSymbol) {
ValOperandId resultId = cacheIRReader.valOperandId();
ValOperandId idId = cacheIRReader.valOperandId();
BOUNDSCHECK(resultId);
Value id = READ_VALUE_REG(idId.id());
if (id.isString() || id.isSymbol()) {
WRITE_VALUE_REG(resultId.id(), id);
} else if (id.isInt32()) {
int32_t idInt = id.toInt32();
StaticStrings& sstr =
ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasInt(idInt)) {
WRITE_VALUE_REG(resultId.id(), StringValue(sstr.getInt(idInt)));
} else {
PUSH_IC_FRAME();
auto* result = Int32ToStringPure(cx, idInt);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
WRITE_VALUE_REG(resultId.id(), StringValue(result));
}
} else {
FAIL_IC();
}
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewStringIteratorResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
(void)templateObjectOffset;
{
PUSH_IC_FRAME();
auto* result = NewStringIterator(cx);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsArrayResult) {
ValOperandId valId = cacheIRReader.valOperandId();
Value val = READ_VALUE_REG(valId.id());
if (!val.isObject()) {
retValue = BooleanValue(false).asRawBits();
} else {
JSObject* obj = &val.toObject();
if (obj->getClass()->isProxyObject()) {
FAIL_IC();
}
retValue = BooleanValue(obj->is<ArrayObject>()).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsCallableResult) {
ValOperandId valId = cacheIRReader.valOperandId();
Value val = READ_VALUE_REG(valId.id());
if (!val.isObject()) {
retValue = BooleanValue(false).asRawBits();
} else {
JSObject* obj = &val.toObject();
if (obj->getClass()->isProxyObject()) {
FAIL_IC();
}
bool callable =
obj->is<JSFunction>() || obj->getClass()->getCall() != nullptr;
retValue = BooleanValue(callable).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsConstructorResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
bool ctor = obj->isConstructor();
retValue = BooleanValue(ctor).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsCrossRealmArrayConstructorResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
bool result =
obj->shape()->realm() !=
ctx.frameMgr.cxForLocalUseOnly()->realm() &&
obj->is<JSFunction>() && obj->as<JSFunction>().isNativeFun() &&
obj->as<JSFunction>().nativeUnchecked() == &js::ArrayConstructor;
retValue = BooleanValue(result).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsTypedArrayResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
bool isPossiblyWrapped = cacheIRReader.readBool();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (IsTypedArrayClass(obj->getClass())) {
retValue = BooleanValue(true).asRawBits();
} else if (isPossiblyWrapped) {
PUSH_IC_FRAME();
bool result;
if (!IsPossiblyWrappedTypedArray(cx, obj, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
} else {
retValue = BooleanValue(false).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IsTypedArrayConstructorResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
retValue = BooleanValue(IsTypedArrayConstructor(obj)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ArrayBufferViewByteOffsetInt32Result) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferViewObject* abvo =
reinterpret_cast<ArrayBufferViewObject*>(READ_REG(objId.id()));
size_t byteOffset =
size_t(abvo->getFixedSlot(ArrayBufferViewObject::BYTEOFFSET_SLOT)
.toPrivate());
if (byteOffset > size_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(int32_t(byteOffset)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ArrayBufferViewByteOffsetDoubleResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ArrayBufferViewObject* abvo =
reinterpret_cast<ArrayBufferViewObject*>(READ_REG(objId.id()));
size_t byteOffset =
size_t(abvo->getFixedSlot(ArrayBufferViewObject::BYTEOFFSET_SLOT)
.toPrivate());
retValue = DoubleValue(double(byteOffset)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(TypedArrayByteLengthInt32Result) {
ObjOperandId objId = cacheIRReader.objOperandId();
TypedArrayObject* tao =
reinterpret_cast<TypedArrayObject*>(READ_REG(objId.id()));
if (!tao->length()) {
FAIL_IC();
}
size_t length = *tao->length() * tao->bytesPerElement();
if (length > size_t(INT32_MAX)) {
FAIL_IC();
}
retValue = Int32Value(int32_t(length)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(TypedArrayByteLengthDoubleResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
TypedArrayObject* tao =
reinterpret_cast<TypedArrayObject*>(READ_REG(objId.id()));
if (!tao->length()) {
FAIL_IC();
}
size_t length = *tao->length() * tao->bytesPerElement();
retValue = DoubleValue(double(length)).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(TypedArrayElementSizeResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
TypedArrayObject* tao =
reinterpret_cast<TypedArrayObject*>(READ_REG(objId.id()));
retValue = Int32Value(int32_t(tao->bytesPerElement())).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MegamorphicStoreSlot) {
ObjOperandId objId = cacheIRReader.objOperandId();
uint32_t nameOffset = cacheIRReader.stubOffset();
ValOperandId valId = cacheIRReader.valOperandId();
bool strict = cacheIRReader.readBool();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
jsid id =
jsid::fromRawBits(stubInfo->getStubRawWord(cstub, nameOffset));
Value val = READ_VALUE_REG(valId.id());
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> objRooted(&ctx.state.obj0, obj);
ReservedRooted<jsid> idRooted(&ctx.state.id0, id);
ReservedRooted<Value> valRooted(&ctx.state.value0, val);
if (!SetPropertyMegamorphic<false>(cx, objRooted, idRooted, valRooted,
strict)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(MegamorphicHasPropResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
ValOperandId valId = cacheIRReader.valOperandId();
bool hasOwn = cacheIRReader.readBool();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
if (!obj->is<NativeObject>()) {
FAIL_IC();
}
Value val[2] = {READ_VALUE_REG(valId.id()), UndefinedValue()};
{
PUSH_IC_FRAME();
bool ok =
hasOwn
? HasNativeDataPropertyPure<true>(cx, obj, nullptr, &val[0])
: HasNativeDataPropertyPure<false>(cx, obj, nullptr, &val[0]);
if (!ok) {
FAIL_IC();
}
retValue = val[1].asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ArrayJoinResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
StringOperandId sepId = cacheIRReader.stringOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
JSString* sep = reinterpret_cast<JSString*>(READ_REG(sepId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> obj0(&ctx.state.obj0, obj);
ReservedRooted<JSString*> str0(&ctx.state.str0, sep);
auto* result = ArrayJoin(cx, obj0, str0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallSetArrayLength) {
ObjOperandId objId = cacheIRReader.objOperandId();
bool strict = cacheIRReader.readBool();
ValOperandId rhsId = cacheIRReader.valOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
Value rhs = READ_VALUE_REG(rhsId.id());
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> obj0(&ctx.state.obj0, obj);
ReservedRooted<Value> value0(&ctx.state.value0, rhs);
if (!SetArrayLength(cx, obj0, value0, strict)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ObjectKeysResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
JSObject* obj = reinterpret_cast<JSObject*>(READ_REG(objId.id()));
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> obj0(&ctx.state.obj0, obj);
auto* result = ObjectKeys(cx, obj0);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(ObjectCreateResult) {
uint32_t templateOffset = cacheIRReader.stubOffset();
PlainObject* templateObj = reinterpret_cast<PlainObject*>(
stubInfo->getStubRawWord(cstub, templateOffset));
{
PUSH_IC_FRAME();
Rooted<PlainObject*> templateRooted(cx, templateObj);
auto* result = ObjectCreateWithTemplate(cx, templateRooted);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallNumberToString) {
NumberOperandId inputId = cacheIRReader.numberOperandId();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
double input = READ_VALUE_REG(inputId.id()).toNumber();
{
PUSH_IC_FRAME();
auto* result = NumberToStringPure(cx, input);
if (!result) {
FAIL_IC();
}
WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(result), STRING);
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(Int32ToStringWithBaseResult) {
Int32OperandId inputId = cacheIRReader.int32OperandId();
Int32OperandId baseId = cacheIRReader.int32OperandId();
int32_t input = int32_t(READ_REG(inputId.id()));
int32_t base = int32_t(READ_REG(baseId.id()));
if (base < 2 || base > 36) {
FAIL_IC();
}
{
PUSH_IC_FRAME();
auto* result = Int32ToStringWithBase(cx, input, base, true);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = StringValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(BooleanToString) {
BooleanOperandId inputId = cacheIRReader.booleanOperandId();
StringOperandId resultId = cacheIRReader.stringOperandId();
BOUNDSCHECK(resultId);
bool input = READ_REG(inputId.id()) != 0;
auto& names = ctx.frameMgr.cxForLocalUseOnly()->names();
JSString* result = input ? names.true_ : names.false_;
WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(result), STRING);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(IndirectTruncateInt32Result) {
Int32OperandId valId = cacheIRReader.int32OperandId();
int32_t value = int32_t(READ_REG(valId.id()));
retValue = Int32Value(value).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(GetFirstDollarIndexResult) {
StringOperandId strId = cacheIRReader.stringOperandId();
JSString* str = reinterpret_cast<JSString*>(READ_REG(strId.id()));
int32_t result = 0;
{
PUSH_IC_FRAME();
if (!GetFirstDollarIndexRaw(cx, str, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = Int32Value(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadBoundFunctionNumArgs) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId resultId = cacheIRReader.int32OperandId();
BOUNDSCHECK(resultId);
BoundFunctionObject* obj =
reinterpret_cast<BoundFunctionObject*>(READ_REG(objId.id()));
WRITE_REG(resultId.id(), obj->numBoundArgs(), INT32);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(LoadBoundFunctionTarget) {
ObjOperandId objId = cacheIRReader.objOperandId();
ObjOperandId resultId = cacheIRReader.objOperandId();
BOUNDSCHECK(resultId);
BoundFunctionObject* obj =
reinterpret_cast<BoundFunctionObject*>(READ_REG(objId.id()));
WRITE_REG(resultId.id(), reinterpret_cast<uint64_t>(obj->getTarget()),
OBJECT);
DISPATCH_CACHEOP();
}
CACHEOP_CASE(BindFunctionResult)
CACHEOP_CASE_FALLTHROUGH(SpecializedBindFunctionResult) {
ObjOperandId targetId = cacheIRReader.objOperandId();
uint32_t argc = cacheIRReader.uint32Immediate();
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
JSObject* target = reinterpret_cast<JSObject*>(READ_REG(targetId.id()));
BoundFunctionObject* templateObject =
(cacheop == CacheOp::SpecializedBindFunctionResult)
? reinterpret_cast<BoundFunctionObject*>(
stubInfo->getStubRawWord(cstub, templateObjectOffset))
: nullptr;
StackVal* origArgs = ctx.sp();
{
PUSH_IC_FRAME();
for (uint32_t i = 0; i < argc; i++) {
PUSH(origArgs[i]);
}
Value* args = reinterpret_cast<Value*>(sp);
ReservedRooted<JSObject*> targetRooted(&ctx.state.obj0, target);
BoundFunctionObject* result;
if (cacheop == CacheOp::BindFunctionResult) {
result = BoundFunctionObject::functionBindImpl(cx, targetRooted,
args, argc, nullptr);
} else {
Rooted<BoundFunctionObject*> templateObjectRooted(cx,
templateObject);
result = BoundFunctionObject::functionBindSpecializedBaseline(
cx, targetRooted, args, argc, templateObjectRooted);
}
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallRegExpMatcherResult)
CACHEOP_CASE_FALLTHROUGH(CallRegExpSearcherResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
StringOperandId inputId = cacheIRReader.stringOperandId();
Int32OperandId lastIndexId = cacheIRReader.int32OperandId();
uint32_t stubOffset = cacheIRReader.stubOffset();
JSObject* regexp = reinterpret_cast<JSObject*>(READ_REG(regexpId.id()));
JSString* input = reinterpret_cast<JSString*>(READ_REG(inputId.id()));
int32_t lastIndex = int32_t(READ_REG(lastIndexId.id()));
(void)stubOffset;
{
PUSH_IC_FRAME();
ReservedRooted<JSObject*> regexpRooted(&ctx.state.obj0, regexp);
ReservedRooted<JSString*> inputRooted(&ctx.state.str0, input);
if (cacheop == CacheOp::CallRegExpMatcherResult) {
ReservedRooted<Value> result(&ctx.state.value0, UndefinedValue());
if (!RegExpMatcherRaw(cx, regexpRooted, inputRooted, lastIndex,
nullptr, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = result.asRawBits();
} else {
int32_t result = 0;
if (!RegExpSearcherRaw(cx, regexpRooted, inputRooted, lastIndex,
nullptr, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = Int32Value(result).asRawBits();
}
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpSearcherLastLimitResult) {
uint32_t lastLimit =
ctx.frameMgr.cxForLocalUseOnly()->regExpSearcherLastLimit;
retValue = Int32Value(lastLimit).asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpHasCaptureGroupsResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
StringOperandId inputId = cacheIRReader.stringOperandId();
RegExpObject* regexp =
reinterpret_cast<RegExpObject*>(READ_REG(regexpId.id()));
JSString* input = reinterpret_cast<JSString*>(READ_REG(inputId.id()));
{
PUSH_IC_FRAME();
Rooted<RegExpObject*> regexpRooted(cx, regexp);
ReservedRooted<JSString*> inputRooted(&ctx.state.str0, input);
bool result = false;
if (!RegExpHasCaptureGroups(cx, regexpRooted, inputRooted, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpBuiltinExecMatchResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
StringOperandId inputId = cacheIRReader.stringOperandId();
uint32_t stubOffset = cacheIRReader.stubOffset();
RegExpObject* regexp =
reinterpret_cast<RegExpObject*>(READ_REG(regexpId.id()));
JSString* input = reinterpret_cast<JSString*>(READ_REG(inputId.id()));
(void)stubOffset;
{
PUSH_IC_FRAME();
Rooted<RegExpObject*> regexpRooted(cx, regexp);
ReservedRooted<JSString*> inputRooted(&ctx.state.str0, input);
ReservedRooted<Value> output(&ctx.state.value0, UndefinedValue());
if (!RegExpBuiltinExecMatchFromJit(cx, regexpRooted, inputRooted,
nullptr, &output)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = output.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpBuiltinExecTestResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
StringOperandId inputId = cacheIRReader.stringOperandId();
uint32_t stubOffset = cacheIRReader.stubOffset();
RegExpObject* regexp =
reinterpret_cast<RegExpObject*>(READ_REG(regexpId.id()));
JSString* input = reinterpret_cast<JSString*>(READ_REG(inputId.id()));
(void)stubOffset;
{
PUSH_IC_FRAME();
Rooted<RegExpObject*> regexpRooted(cx, regexp);
ReservedRooted<JSString*> inputRooted(&ctx.state.str0, input);
bool result = false;
if (!RegExpBuiltinExecTestFromJit(cx, regexpRooted, inputRooted,
&result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = BooleanValue(result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpFlagResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
uint32_t flagsMask = cacheIRReader.uint32Immediate();
RegExpObject* regexp =
reinterpret_cast<RegExpObject*>(READ_REG(regexpId.id()));
JS::RegExpFlags flags = regexp->getFlags();
retValue = BooleanValue((uint32_t(flags.value()) & flagsMask) != 0)
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpPrototypeOptimizableResult) {
ObjOperandId protoId = cacheIRReader.objOperandId();
JSObject* proto = reinterpret_cast<JSObject*>(READ_REG(protoId.id()));
retValue = BooleanValue(RegExpPrototypeOptimizableRaw(
ctx.frameMgr.cxForLocalUseOnly(), proto))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(RegExpInstanceOptimizableResult) {
ObjOperandId regexpId = cacheIRReader.objOperandId();
ObjOperandId protoId = cacheIRReader.objOperandId();
JSObject* regexp = reinterpret_cast<JSObject*>(READ_REG(regexpId.id()));
JSObject* proto = reinterpret_cast<JSObject*>(READ_REG(protoId.id()));
retValue =
BooleanValue(RegExpInstanceOptimizableRaw(
ctx.frameMgr.cxForLocalUseOnly(), regexp, proto))
.asRawBits();
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(NewRegExpStringIteratorResult) {
uint32_t templateObjectOffset = cacheIRReader.stubOffset();
(void)templateObjectOffset;
{
PUSH_IC_FRAME();
auto* result = NewRegExpStringIterator(cx);
if (!result) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = ObjectValue(*result).asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
CACHEOP_CASE(CallGetSparseElementResult) {
ObjOperandId objId = cacheIRReader.objOperandId();
Int32OperandId indexId = cacheIRReader.int32OperandId();
NativeObject* nobj =
reinterpret_cast<NativeObject*>(READ_REG(objId.id()));
int32_t index = int32_t(READ_REG(indexId.id()));
{
PUSH_IC_FRAME();
Rooted<NativeObject*> nobjRooted(cx, nobj);
ReservedRooted<Value> result(&ctx.state.value0, UndefinedValue());
if (!GetSparseElementHelper(cx, nobjRooted, index, &result)) {
ctx.error = PBIResult::Error;
return IC_ERROR_SENTINEL();
}
retValue = result.asRawBits();
}
PREDICT_RETURN();
DISPATCH_CACHEOP();
}
#undef PREDICT_NEXT
#ifndef ENABLE_COMPUTED_GOTO_DISPATCH
default:
TRACE_PRINTF("unknown CacheOp\n");
FAIL_IC();
#endif
}
}
#define CACHEOP_UNIMPL(name, ...) \
cacheop_##name : __attribute__((unused)); \
TRACE_PRINTF("unknown CacheOp: " #name "\n"); \
FAIL_IC();
CACHE_IR_OPS(CACHEOP_UNIMPL)
#undef CACHEOP_UNIMPL
next_ic:
TRACE_PRINTF("IC failed; next IC\n");
return CallNextIC(arg0, arg1, stub, ctx);
}
static MOZ_NEVER_INLINE uint64_t CallNextIC(uint64_t arg0, uint64_t arg1,
ICStub* stub, ICCtx& ctx) {
stub = stub->maybeNext();
MOZ_ASSERT(stub);
uint64_t result;
PBL_CALL_IC(stub->rawJitCode(), ctx, stub, result, arg0, arg1, ctx.arg2,
true);
return result;
}
/*
* -----------------------------------------------
* IC callsite logic, and fallback stubs
* -----------------------------------------------
*/
#define DEFINE_IC(kind, arity, fallback_body) \
static uint64_t MOZ_NEVER_INLINE IC##kind##Fallback( \
uint64_t arg0, uint64_t arg1, ICStub* stub, ICCtx& ctx) { \
uint64_t retValue = 0; \
uint64_t arg2 = ctx.arg2; \
(void)arg2; \
ICFallbackStub* fallback = stub->toFallbackStub(); \
StackVal* sp = ctx.sp(); \
fallback_body; \
retValue = ctx.state.res.asRawBits(); \
ctx.state.res = UndefinedValue(); \
return retValue; \
error: \
ctx.error = PBIResult::Error; \
return IC_ERROR_SENTINEL(); \
}
#define DEFINE_IC_ALIAS(kind, target) \
static uint64_t MOZ_NEVER_INLINE IC##kind##Fallback( \
uint64_t arg0, uint64_t arg1, ICStub* stub, ICCtx& ctx) { \
return IC##target##Fallback(arg0, arg1, stub, ctx); \
}
#define IC_LOAD_VAL(state_elem, index) \
ReservedRooted<Value> state_elem(&ctx.state.state_elem, \
Value::fromRawBits(arg##index))
#define IC_LOAD_OBJ(state_elem, index) \
ReservedRooted<JSObject*> state_elem( \
&ctx.state.state_elem, reinterpret_cast<JSObject*>(arg##index))
DEFINE_IC(TypeOf, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoTypeOfFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(TypeOfEq, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoTypeOfEqFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetName, 1, {
IC_LOAD_OBJ(obj0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoGetNameFallback(cx, ctx.frame, fallback, obj0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(Call, 1, {
uint32_t argc = uint32_t(arg0);
uint32_t totalArgs =
argc + ctx.icregs.extraArgs; // this, callee, (constructing?), func args
Value* args = reinterpret_cast<Value*>(&sp[0]);
TRACE_PRINTF("Call fallback: argc %d totalArgs %d args %p\n", argc, totalArgs,
args);
// Reverse values on the stack.
std::reverse(args, args + totalArgs);
{
PUSH_FALLBACK_IC_FRAME();
if (!DoCallFallback(cx, ctx.frame, fallback, argc, args, &ctx.state.res)) {
std::reverse(args, args + totalArgs);
goto error;
}
}
});
DEFINE_IC_ALIAS(CallConstructing, Call);
DEFINE_IC(SpreadCall, 1, {
uint32_t argc = uint32_t(arg0);
uint32_t totalArgs =
argc + ctx.icregs.extraArgs; // this, callee, (constructing?), func args
Value* args = reinterpret_cast<Value*>(&sp[0]);
TRACE_PRINTF("Call fallback: argc %d totalArgs %d args %p\n", argc, totalArgs,
args);
// Reverse values on the stack.
std::reverse(args, args + totalArgs);
{
PUSH_FALLBACK_IC_FRAME();
if (!DoSpreadCallFallback(cx, ctx.frame, fallback, args, &ctx.state.res)) {
std::reverse(args, args + totalArgs);
goto error;
}
}
});
DEFINE_IC_ALIAS(SpreadCallConstructing, SpreadCall);
DEFINE_IC(UnaryArith, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoUnaryArithFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(BinaryArith, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoBinaryArithFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(ToBool, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoToBoolFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(Compare, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoCompareFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(InstanceOf, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoInstanceOfFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(In, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoInFallback(cx, ctx.frame, fallback, value0, value1, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(BindName, 1, {
IC_LOAD_OBJ(obj0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoBindNameFallback(cx, ctx.frame, fallback, obj0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(SetProp, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoSetPropFallback(cx, ctx.frame, fallback, nullptr, value0, value1)) {
goto error;
}
});
DEFINE_IC(NewObject, 0, {
PUSH_FALLBACK_IC_FRAME();
if (!DoNewObjectFallback(cx, ctx.frame, fallback, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetProp, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoGetPropFallback(cx, ctx.frame, fallback, &value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetPropSuper, 2, {
IC_LOAD_VAL(value0, 1);
IC_LOAD_VAL(value1, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoGetPropSuperFallback(cx, ctx.frame, fallback, value0, &value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetElem, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoGetElemFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetElemSuper, 3, {
IC_LOAD_VAL(value0, 0); // receiver
IC_LOAD_VAL(value1, 1); // obj (lhs)
IC_LOAD_VAL(value2, 2); // key (rhs)
PUSH_FALLBACK_IC_FRAME();
if (!DoGetElemSuperFallback(cx, ctx.frame, fallback, value1, value2, value0,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(NewArray, 0, {
PUSH_FALLBACK_IC_FRAME();
if (!DoNewArrayFallback(cx, ctx.frame, fallback, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(LazyConstant, 0, {
PUSH_FALLBACK_IC_FRAME();
if (!DoLazyConstantFallback(cx, ctx.frame, fallback, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(SetElem, 3, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
IC_LOAD_VAL(value2, 2);
PUSH_FALLBACK_IC_FRAME();
if (!DoSetElemFallback(cx, ctx.frame, fallback, nullptr, value0, value1,
value2)) {
goto error;
}
});
DEFINE_IC(HasOwn, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoHasOwnFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(CheckPrivateField, 2, {
IC_LOAD_VAL(value0, 0);
IC_LOAD_VAL(value1, 1);
PUSH_FALLBACK_IC_FRAME();
if (!DoCheckPrivateFieldFallback(cx, ctx.frame, fallback, value0, value1,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(GetIterator, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoGetIteratorFallback(cx, ctx.frame, fallback, value0, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(ToPropertyKey, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoToPropertyKeyFallback(cx, ctx.frame, fallback, value0,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(OptimizeSpreadCall, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoOptimizeSpreadCallFallback(cx, ctx.frame, fallback, value0,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(OptimizeGetIterator, 1, {
IC_LOAD_VAL(value0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoOptimizeGetIteratorFallback(cx, ctx.frame, fallback, value0,
&ctx.state.res)) {
goto error;
}
});
DEFINE_IC(Rest, 0, {
PUSH_FALLBACK_IC_FRAME();
if (!DoRestFallback(cx, ctx.frame, fallback, &ctx.state.res)) {
goto error;
}
});
DEFINE_IC(CloseIter, 1, {
IC_LOAD_OBJ(obj0, 0);
PUSH_FALLBACK_IC_FRAME();
if (!DoCloseIterFallback(cx, ctx.frame, fallback, obj0)) {
goto error;
}
});
uint8_t* GetPortableFallbackStub(BaselineICFallbackKind kind) {
switch (kind) {
#define _(ty) \
case BaselineICFallbackKind::ty: \
return reinterpret_cast<uint8_t*>(&IC##ty##Fallback);
IC_BASELINE_FALLBACK_CODE_KIND_LIST(_)
#undef _
case BaselineICFallbackKind::Count:
MOZ_CRASH("Invalid kind");
}
}
uint8_t* GetICInterpreter() {
return reinterpret_cast<uint8_t*>(&ICInterpretOps);
}
/*
* -----------------------------------------------
* Main JSOp interpreter
* -----------------------------------------------
*/
static EnvironmentObject& getEnvironmentFromCoordinate(
BaselineFrame* frame, EnvironmentCoordinate ec) {
JSObject* env = frame->environmentChain();
for (unsigned i = ec.hops(); i; i--) {
if (env->is<EnvironmentObject>()) {
env = &env->as<EnvironmentObject>().enclosingEnvironment();
} else {
MOZ_ASSERT(env->is<DebugEnvironmentProxy>());
env = &env->as<DebugEnvironmentProxy>().enclosingEnvironment();
}
}
return env->is<EnvironmentObject>()
? env->as<EnvironmentObject>()
: env->as<DebugEnvironmentProxy>().environment();
}
#ifndef __wasi__
# define DEBUG_CHECK() \
if (frame->isDebuggee()) { \
TRACE_PRINTF( \
"Debug check: frame is debuggee, checking for debug script\n"); \
if (frame->script()->hasDebugScript()) { \
goto debug; \
} \
}
#else
# define DEBUG_CHECK()
#endif
#define LABEL(op) (&&label_##op)
#ifdef ENABLE_COMPUTED_GOTO_DISPATCH
# define CASE(op) label_##op:
# define DISPATCH() \
DEBUG_CHECK(); \
goto* addresses[*pc]
#else
# define CASE(op) label_##op : case JSOp::op:
# define DISPATCH() \
DEBUG_CHECK(); \
goto dispatch
#endif
#define ADVANCE(delta) pc += (delta);
#define ADVANCE_AND_DISPATCH(delta) \
ADVANCE(delta); \
DISPATCH();
#define END_OP(op) ADVANCE_AND_DISPATCH(JSOpLength_##op);
#define VIRTPUSH(value) PUSH(value)
#define VIRTPOP() POP()
#define VIRTSP(index) sp[(index)]
#define VIRTSPWRITE(index, value) sp[(index)] = (value)
#define SYNCSP()
#define SETLOCAL(i, value) frame->unaliasedLocal(i) = value
#define GETLOCAL(i) frame->unaliasedLocal(i)
#define IC_SET_ARG_FROM_STACK(index, stack_index) \
ic_arg##index = sp[(stack_index)].asUInt64();
#define IC_POP_ARG(index) ic_arg##index = (*sp++).asUInt64();
#define IC_SET_VAL_ARG(index, expr) ic_arg##index = (expr).asRawBits();
#define IC_SET_OBJ_ARG(index, expr) \
ic_arg##index = reinterpret_cast<uint64_t>(expr);
#define IC_ZERO_ARG(index) ic_arg##index = 0;
#define IC_PUSH_RESULT() VIRTPUSH(StackVal(ic_ret));
#if !defined(TRACE_INTERP)
# define PREDICT_NEXT(op) \
if (JSOp(*pc) == JSOp::op) { \
DEBUG_CHECK(); \
goto label_##op; \
}
#else
# define PREDICT_NEXT(op)
#endif
#ifdef ENABLE_COVERAGE
# define COUNT_COVERAGE_PC(PC) \
if (frame->script()->hasScriptCounts()) { \
PCCounts* counts = frame->script()->maybeGetPCCounts(PC); \
MOZ_ASSERT(counts); \
counts->numExec()++; \
}
# define COUNT_COVERAGE_MAIN() \
{ \
jsbytecode* main = frame->script()->main(); \
if (!BytecodeIsJumpTarget(JSOp(*main))) COUNT_COVERAGE_PC(main); \
}
#else
# define COUNT_COVERAGE_PC(PC) ;
# define COUNT_COVERAGE_MAIN() ;
#endif
#define NEXT_IC() icEntry++
#define INVOKE_IC(kind, hasarg2) \
ctx.sp_ = sp; \
frame->interpreterPC() = pc; \
frame->interpreterICEntry() = icEntry; \
SYNCSP(); \
PBL_CALL_IC(icEntry->firstStub()->rawJitCode(), ctx, icEntry->firstStub(), \
ic_ret, ic_arg0, ic_arg1, ic_arg2, hasarg2); \
if (ic_ret == IC_ERROR_SENTINEL()) { \
ic_result = ctx.error; \
goto ic_fail; \
} \
NEXT_IC();
#define INVOKE_IC_AND_PUSH(kind, hasarg2) \
INVOKE_IC(kind, hasarg2); \
VIRTPUSH(StackVal(ic_ret));
#define VIRTPOPN(n) \
SYNCSP(); \
POPN(n);
#define SPHANDLE(index) \
({ \
SYNCSP(); \
Stack::handle(&sp[(index)]); \
})
#define SPHANDLEMUT(index) \
({ \
SYNCSP(); \
Stack::handleMut(&sp[(index)]); \
})
template <bool IsRestart, bool HybridICs>
PBIResult PortableBaselineInterpret(
JSContext* cx_, State& state, Stack& stack, StackVal* sp,
JSObject* envChain, Value* ret, jsbytecode* pc, ImmutableScriptData* isd,
jsbytecode* restartEntryPC, BaselineFrame* restartFrame,
StackVal* restartEntryFrame, PBIResult restartCode) {
#define RESTART(code) \
if (!IsRestart) { \
TRACE_PRINTF("Restarting (code %d sp %p fp %p)\n", int(code), sp, \
ctx.stack.fp); \
SYNCSP(); \
restartCode = code; \
goto restart; \
}
#define GOTO_ERROR() \
do { \
SYNCSP(); \
RESTART(PBIResult::Error); \
goto error; \
} while (0)
// Update local state when we switch to a new script with a new PC.
#define RESET_PC(new_pc, new_script) \
pc = new_pc; \
entryPC = new_script->code(); \
isd = new_script->immutableScriptData(); \
icEntries = frame->icScript()->icEntries(); \
icEntry = frame->interpreterICEntry(); \
argsObjAliasesFormals = frame->script()->argsObjAliasesFormals(); \
resumeOffsets = isd->resumeOffsets().data();
#define OPCODE_LABEL(op, ...) LABEL(op),
#define TRAILING_LABEL(v) LABEL(default),
static const void* const addresses[EnableInterruptsPseudoOpcode + 1] = {
FOR_EACH_OPCODE(OPCODE_LABEL)
FOR_EACH_TRAILING_UNUSED_OPCODE(TRAILING_LABEL)};
#undef OPCODE_LABEL
#undef TRAILING_LABEL
BaselineFrame* frame = restartFrame;
StackVal* entryFrame = restartEntryFrame;
jsbytecode* entryPC = restartEntryPC;
if (!IsRestart) {
PUSHNATIVE(StackValNative(nullptr)); // Fake return address.
frame = stack.pushFrame(sp, cx_, envChain);
MOZ_ASSERT(frame); // safety: stack margin.
sp = reinterpret_cast<StackVal*>(frame);
// Save the entry frame so that when unwinding, we know when to
// return from this C++ frame.
entryFrame = sp;
// Save the entry PC so that we can compute offsets locally.
entryPC = pc;
}
bool from_unwind = false;
uint32_t nfixed = frame->script()->nfixed();
bool argsObjAliasesFormals = frame->script()->argsObjAliasesFormals();
PBIResult ic_result = PBIResult::Ok;
uint64_t ic_arg0 = 0, ic_arg1 = 0, ic_arg2 = 0, ic_ret = 0;
ICCtx ctx(cx_, frame, state, stack);
auto* icEntries = frame->icScript()->icEntries();
auto* icEntry = icEntries;
const uint32_t* resumeOffsets = isd->resumeOffsets().data();
if (IsRestart) {
ic_result = restartCode;
TRACE_PRINTF(
"Enter from restart: sp = %p ctx.stack.fp = %p ctx.frame = %p\n", sp,
ctx.stack.fp, ctx.frame);
goto ic_fail;
} else {
AutoCheckRecursionLimit recursion(ctx.frameMgr.cxForLocalUseOnly());
if (!recursion.checkDontReport(ctx.frameMgr.cxForLocalUseOnly())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
return PBIResult::Error;
}
}
// Check max stack depth once, so we don't need to check it
// otherwise below for ordinary stack-manipulation opcodes (just for
// exit frames).
if (!ctx.stack.check(sp, sizeof(StackVal) * frame->script()->nslots())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
return PBIResult::Error;
}
SYNCSP();
sp -= nfixed;
for (uint32_t i = 0; i < nfixed; i++) {
sp[i] = StackVal(UndefinedValue());
}
ret->setUndefined();
// Check if we are being debugged, and set a flag in the frame if so. This
// flag must be set before calling InitFunctionEnvironmentObjects.
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("Script is debuggee\n");
frame->setIsDebuggee();
}
if (CalleeTokenIsFunction(frame->calleeToken())) {
JSFunction* func = CalleeTokenToFunction(frame->calleeToken());
frame->setEnvironmentChain(func->environment());
if (func->needsFunctionEnvironmentObjects()) {
PUSH_EXIT_FRAME();
if (!js::InitFunctionEnvironmentObjects(cx, frame)) {
GOTO_ERROR();
}
TRACE_PRINTF("callee is func %p; created environment object: %p\n", func,
frame->environmentChain());
}
}
// The debug prologue can't run until the function environment is set up.
if (frame->script()->isDebuggee()) {
PUSH_EXIT_FRAME();
if (!DebugPrologue(cx, frame)) {
GOTO_ERROR();
}
}
if (!frame->script()->hasScriptCounts()) {
if (ctx.frameMgr.cxForLocalUseOnly()->realm()->collectCoverageForDebug()) {
PUSH_EXIT_FRAME();
if (!frame->script()->initScriptCounts(cx)) {
GOTO_ERROR();
}
}
}
COUNT_COVERAGE_MAIN();
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
TRACE_PRINTF("Entering: sp = %p fp = %p frame = %p, script = %p, pc = %p\n",
sp, ctx.stack.fp, frame, frame->script(), pc);
TRACE_PRINTF("nslots = %d nfixed = %d\n", int(frame->script()->nslots()),
int(frame->script()->nfixed()));
while (true) {
DEBUG_CHECK();
#if !defined(ENABLE_COMPUTED_GOTO_DISPATCH) || !defined(__wasi__)
dispatch:
#endif
#ifdef TRACE_INTERP
{
JSOp op = JSOp(*pc);
printf("sp[0] = %" PRIx64 " sp[1] = %" PRIx64 " sp[2] = %" PRIx64 "\n",
sp[0].asUInt64(), sp[1].asUInt64(), sp[2].asUInt64());
printf("script = %p pc = %p: %s (ic %d entry %p) pending = %d\n",
frame->script(), pc, CodeName(op), (int)(icEntry - icEntries),
icEntry, ctx.frameMgr.cxForLocalUseOnly()->isExceptionPending());
printf("sp = %p fp = %p\n", sp, ctx.stack.fp);
printf("TOS tag: %d\n", int(sp[0].asValue().asRawBits() >> 47));
fflush(stdout);
}
#endif
#ifdef ENABLE_COMPUTED_GOTO_DISPATCH
goto* addresses[*pc];
#else
(void)addresses; // Avoid unused-local error. We keep the table
// itself to avoid warnings (see note in IC
// interpreter above).
switch (JSOp(*pc))
#endif
{
CASE(Nop) { END_OP(Nop); }
CASE(NopIsAssignOp) { END_OP(NopIsAssignOp); }
CASE(Undefined) {
VIRTPUSH(StackVal(UndefinedValue()));
END_OP(Undefined);
}
CASE(Null) {
VIRTPUSH(StackVal(NullValue()));
END_OP(Null);
}
CASE(False) {
VIRTPUSH(StackVal(BooleanValue(false)));
END_OP(False);
}
CASE(True) {
VIRTPUSH(StackVal(BooleanValue(true)));
END_OP(True);
}
CASE(Int32) {
VIRTPUSH(StackVal(Int32Value(GET_INT32(pc))));
END_OP(Int32);
}
CASE(Zero) {
VIRTPUSH(StackVal(Int32Value(0)));
END_OP(Zero);
}
CASE(One) {
VIRTPUSH(StackVal(Int32Value(1)));
END_OP(One);
}
CASE(Int8) {
VIRTPUSH(StackVal(Int32Value(GET_INT8(pc))));
END_OP(Int8);
}
CASE(Uint16) {
VIRTPUSH(StackVal(Int32Value(GET_UINT16(pc))));
END_OP(Uint16);
}
CASE(Uint24) {
VIRTPUSH(StackVal(Int32Value(GET_UINT24(pc))));
END_OP(Uint24);
}
CASE(Double) {
VIRTPUSH(StackVal(GET_INLINE_VALUE(pc)));
END_OP(Double);
}
CASE(BigInt) {
VIRTPUSH(StackVal(JS::BigIntValue(frame->script()->getBigInt(pc))));
END_OP(BigInt);
}
CASE(String) {
VIRTPUSH(StackVal(StringValue(frame->script()->getString(pc))));
END_OP(String);
}
CASE(Symbol) {
VIRTPUSH(StackVal(SymbolValue(
ctx.frameMgr.cxForLocalUseOnly()->wellKnownSymbols().get(
GET_UINT8(pc)))));
END_OP(Symbol);
}
CASE(Void) {
VIRTSPWRITE(0, StackVal(JS::UndefinedValue()));
END_OP(Void);
}
CASE(Typeof)
CASE(TypeofExpr) {
static_assert(JSOpLength_Typeof == JSOpLength_TypeofExpr);
if (HybridICs) {
SYNCSP();
VIRTSPWRITE(
0,
StackVal(StringValue(TypeOfOperation(
SPHANDLE(0), ctx.frameMgr.cxForLocalUseOnly()->runtime()))));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(TypeOf, false);
}
END_OP(Typeof);
}
CASE(TypeofEq) {
if (HybridICs) {
TypeofEqOperand operand =
TypeofEqOperand::fromRawValue(GET_UINT8(pc));
bool result = js::TypeOfValue(SPHANDLE(0)) == operand.type();
if (operand.compareOp() == JSOp::Ne) {
result = !result;
}
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(TypeOfEq, false);
}
END_OP(TypeofEq);
}
CASE(Pos) {
if (VIRTSP(0).asValue().isNumber()) {
// Nothing!
NEXT_IC();
END_OP(Pos);
} else {
goto generic_unary;
}
}
CASE(Neg) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != 0 && i != INT32_MIN) {
VIRTSPWRITE(0, StackVal(Int32Value(-i)));
NEXT_IC();
END_OP(Neg);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(-v.toNumber())));
NEXT_IC();
END_OP(Neg);
}
goto generic_unary;
}
CASE(Inc) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != INT32_MAX) {
VIRTSPWRITE(0, StackVal(Int32Value(i + 1)));
NEXT_IC();
END_OP(Inc);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(v.toNumber() + 1)));
NEXT_IC();
END_OP(Inc);
}
goto generic_unary;
}
CASE(Dec) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i != INT32_MIN) {
VIRTSPWRITE(0, StackVal(Int32Value(i - 1)));
NEXT_IC();
END_OP(Dec);
}
}
if (v.isNumber()) {
VIRTSPWRITE(0, StackVal(NumberValue(v.toNumber() - 1)));
NEXT_IC();
END_OP(Dec);
}
goto generic_unary;
}
CASE(BitNot) {
Value v = VIRTSP(0).asValue();
if (v.isInt32()) {
int32_t i = v.toInt32();
VIRTSPWRITE(0, StackVal(Int32Value(~i)));
NEXT_IC();
END_OP(Inc);
}
goto generic_unary;
}
CASE(ToNumeric) {
if (VIRTSP(0).asValue().isNumeric()) {
NEXT_IC();
} else if (HybridICs) {
SYNCSP();
MutableHandleValue val = SPHANDLEMUT(0);
PUSH_EXIT_FRAME();
if (!ToNumeric(cx, val)) {
GOTO_ERROR();
}
NEXT_IC();
} else {
goto generic_unary;
}
END_OP(ToNumeric);
}
generic_unary:;
{
static_assert(JSOpLength_Pos == JSOpLength_Neg);
static_assert(JSOpLength_Pos == JSOpLength_BitNot);
static_assert(JSOpLength_Pos == JSOpLength_Inc);
static_assert(JSOpLength_Pos == JSOpLength_Dec);
static_assert(JSOpLength_Pos == JSOpLength_ToNumeric);
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(UnaryArith, false);
END_OP(Pos);
}
CASE(Not) {
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
VIRTSPWRITE(0, StackVal(BooleanValue(!v.toBoolean())));
NEXT_IC();
} else if (HybridICs) {
SYNCSP();
VIRTSPWRITE(0, StackVal(BooleanValue(!ToBoolean(SPHANDLE(0)))));
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
VIRTPUSH(
StackVal(BooleanValue(!Value::fromRawBits(ic_ret).toBoolean())));
}
END_OP(Not);
}
CASE(And) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
NEXT_IC();
} else {
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (!result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_And);
}
DISPATCH();
}
CASE(Or) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
NEXT_IC();
} else {
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_Or);
}
DISPATCH();
}
CASE(JumpIfTrue) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
VIRTPOP();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
VIRTPOP();
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_JumpIfTrue);
}
DISPATCH();
}
CASE(JumpIfFalse) {
bool result;
Value v = VIRTSP(0).asValue();
if (v.isBoolean()) {
result = v.toBoolean();
VIRTPOP();
NEXT_IC();
} else if (HybridICs) {
result = ToBoolean(SPHANDLE(0));
VIRTPOP();
NEXT_IC();
} else {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(ToBool, false);
result = Value::fromRawBits(ic_ret).toBoolean();
}
int32_t jumpOffset = GET_JUMP_OFFSET(pc);
if (!result) {
ADVANCE(jumpOffset);
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
} else {
ADVANCE(JSOpLength_JumpIfFalse);
}
DISPATCH();
}
CASE(Add) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t result = lhs + rhs;
if (result >= int64_t(INT32_MIN) && result <= int64_t(INT32_MAX)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(result))));
NEXT_IC();
END_OP(Add);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs + rhs)));
NEXT_IC();
END_OP(Add);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!AddOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Add);
}
goto generic_binary;
}
CASE(Sub) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t result = lhs - rhs;
if (result >= int64_t(INT32_MIN) && result <= int64_t(INT32_MAX)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(result))));
NEXT_IC();
END_OP(Sub);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs - rhs)));
NEXT_IC();
END_OP(Add);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!SubOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Sub);
}
goto generic_binary;
}
CASE(Mul) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
int64_t product = lhs * rhs;
if (product >= int64_t(INT32_MIN) &&
product <= int64_t(INT32_MAX) &&
(product != 0 || !((lhs < 0) ^ (rhs < 0)))) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(product))));
NEXT_IC();
END_OP(Mul);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(lhs * rhs)));
NEXT_IC();
END_OP(Mul);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!MulOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Mul);
}
goto generic_binary;
}
CASE(Div) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(NumberDiv(lhs, rhs))));
NEXT_IC();
END_OP(Div);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!DivOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Div);
}
goto generic_binary;
}
CASE(Mod) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int64_t lhs = v1.toInt32();
int64_t rhs = v0.toInt32();
if (lhs > 0 && rhs > 0) {
int64_t mod = lhs % rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(mod))));
NEXT_IC();
END_OP(Mod);
}
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(DoubleValue(NumberMod(lhs, rhs))));
NEXT_IC();
END_OP(Mod);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!ModOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Mod);
}
goto generic_binary;
}
CASE(Pow) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(NumberValue(ecmaPow(lhs, rhs))));
NEXT_IC();
END_OP(Pow);
}
MutableHandleValue lhs = SPHANDLEMUT(1);
MutableHandleValue rhs = SPHANDLEMUT(0);
MutableHandleValue result = SPHANDLEMUT(1);
{
PUSH_EXIT_FRAME();
if (!PowOperation(cx, lhs, rhs, result)) {
GOTO_ERROR();
}
}
VIRTPOP();
NEXT_IC();
END_OP(Pow);
}
goto generic_binary;
}
CASE(BitOr) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs | rhs)));
NEXT_IC();
END_OP(BitOr);
}
}
goto generic_binary;
}
CASE(BitAnd) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs & rhs)));
NEXT_IC();
END_OP(BitAnd);
}
}
goto generic_binary;
}
CASE(BitXor) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(Int32Value(lhs ^ rhs)));
NEXT_IC();
END_OP(BitXor);
}
}
goto generic_binary;
}
CASE(Lsh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
// Unsigned to avoid undefined behavior on left-shift overflow
// (see comment in BitLshOperation in Interpreter.cpp).
uint32_t lhs = uint32_t(v1.toInt32());
uint32_t rhs = uint32_t(v0.toInt32());
VIRTPOP();
rhs &= 31;
VIRTSPWRITE(0, StackVal(Int32Value(int32_t(lhs << rhs))));
NEXT_IC();
END_OP(Lsh);
}
}
goto generic_binary;
}
CASE(Rsh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
int32_t lhs = v1.toInt32();
int32_t rhs = v0.toInt32();
VIRTPOP();
rhs &= 31;
VIRTSPWRITE(0, StackVal(Int32Value(lhs >> rhs)));
NEXT_IC();
END_OP(Rsh);
}
}
goto generic_binary;
}
CASE(Ursh) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
uint32_t lhs = uint32_t(v1.toInt32());
int32_t rhs = v0.toInt32();
VIRTPOP();
rhs &= 31;
uint32_t result = lhs >> rhs;
StackVal stackResult(0);
if (result <= uint32_t(INT32_MAX)) {
stackResult = StackVal(Int32Value(int32_t(result)));
} else {
stackResult = StackVal(NumberValue(double(result)));
}
VIRTSPWRITE(0, stackResult);
NEXT_IC();
END_OP(Ursh);
}
}
goto generic_binary;
}
generic_binary:;
{
static_assert(JSOpLength_BitOr == JSOpLength_BitXor);
static_assert(JSOpLength_BitOr == JSOpLength_BitAnd);
static_assert(JSOpLength_BitOr == JSOpLength_Lsh);
static_assert(JSOpLength_BitOr == JSOpLength_Rsh);
static_assert(JSOpLength_BitOr == JSOpLength_Ursh);
static_assert(JSOpLength_BitOr == JSOpLength_Add);
static_assert(JSOpLength_BitOr == JSOpLength_Sub);
static_assert(JSOpLength_BitOr == JSOpLength_Mul);
static_assert(JSOpLength_BitOr == JSOpLength_Div);
static_assert(JSOpLength_BitOr == JSOpLength_Mod);
static_assert(JSOpLength_BitOr == JSOpLength_Pow);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BinaryArith, false);
END_OP(Div);
}
CASE(Eq) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v0.toInt32() == v1.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs == rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
if (v0.isNumber() && v1.isNumber()) {
bool result = v0.toNumber() == v1.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
}
goto generic_cmp;
}
CASE(Ne) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v0.toInt32() != v1.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ne);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs != rhs;
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ne);
}
if (v0.isNumber() && v1.isNumber()) {
bool result = v0.toNumber() != v1.toNumber();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Eq);
}
}
goto generic_cmp;
}
CASE(Lt) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() < v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Lt);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs < rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Lt);
}
}
goto generic_cmp;
}
CASE(Le) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() <= v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Le);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs <= rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Le);
}
}
goto generic_cmp;
}
CASE(Gt) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() > v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Gt);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs > rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Gt);
}
}
goto generic_cmp;
}
CASE(Ge) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
if (v0.isInt32() && v1.isInt32()) {
bool result = v1.toInt32() >= v0.toInt32();
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ge);
}
if (v0.isNumber() && v1.isNumber()) {
double lhs = v1.toNumber();
double rhs = v0.toNumber();
bool result = lhs >= rhs;
if (std::isnan(lhs) || std::isnan(rhs)) {
result = false;
}
VIRTPOP();
VIRTSPWRITE(0, StackVal(BooleanValue(result)));
NEXT_IC();
END_OP(Ge);
}
}
goto generic_cmp;
}
CASE(StrictEq)
CASE(StrictNe) {
if (HybridICs) {
Value v0 = VIRTSP(0).asValue();
Value v1 = VIRTSP(1).asValue();
bool result;
HandleValue lval = SPHANDLE(1);
HandleValue rval = SPHANDLE(0);
if (v0.isString() && v1.isString()) {
PUSH_EXIT_FRAME();
if (!js::StrictlyEqual(cx, lval, rval, &result)) {
GOTO_ERROR();
}
} else {
if (!js::StrictlyEqual(nullptr, lval, rval, &result)) {
GOTO_ERROR();
}
}
VIRTPOP();
VIRTSPWRITE(0,
StackVal(BooleanValue(
(JSOp(*pc) == JSOp::StrictEq) ? result : !result)));
NEXT_IC();
END_OP(StrictEq);
} else {
goto generic_cmp;
}
}
generic_cmp:;
{
static_assert(JSOpLength_Eq == JSOpLength_Ne);
static_assert(JSOpLength_Eq == JSOpLength_StrictEq);
static_assert(JSOpLength_Eq == JSOpLength_StrictNe);
static_assert(JSOpLength_Eq == JSOpLength_Lt);
static_assert(JSOpLength_Eq == JSOpLength_Gt);
static_assert(JSOpLength_Eq == JSOpLength_Le);
static_assert(JSOpLength_Eq == JSOpLength_Ge);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(Compare, false);
END_OP(Eq);
}
CASE(Instanceof) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(InstanceOf, false);
END_OP(Instanceof);
}
CASE(In) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(In, false);
END_OP(In);
}
CASE(ToPropertyKey) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(ToPropertyKey, false);
END_OP(ToPropertyKey);
}
CASE(ToString) {
if (VIRTSP(0).asValue().isString()) {
END_OP(ToString);
}
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
if (JSString* result = ToStringSlow<NoGC>(
ctx.frameMgr.cxForLocalUseOnly(), value0)) {
VIRTPUSH(StackVal(StringValue(result)));
} else {
{
PUSH_EXIT_FRAME();
result = ToString<CanGC>(cx, value0);
if (!result) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(StringValue(result)));
}
}
END_OP(ToString);
}
CASE(IsNullOrUndefined) {
Value v = VIRTSP(0).asValue();
bool result = v.isNull() || v.isUndefined();
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsNullOrUndefined);
}
CASE(GlobalThis) {
VIRTPUSH(StackVal(ObjectValue(*ctx.frameMgr.cxForLocalUseOnly()
->global()
->lexicalEnvironment()
.thisObject())));
END_OP(GlobalThis);
}
CASE(NonSyntacticGlobalThis) {
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
ReservedRooted<Value> value0(&state.value0);
{
PUSH_EXIT_FRAME();
js::GetNonSyntacticGlobalThis(cx, obj0, &value0);
}
VIRTPUSH(StackVal(value0));
}
END_OP(NonSyntacticGlobalThis);
}
CASE(NewTarget) {
VIRTPUSH(StackVal(frame->newTarget()));
END_OP(NewTarget);
}
CASE(DynamicImport) {
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // options
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // specifier
JSObject* promise;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
promise = StartDynamicModuleImport(cx, script0, value1, value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
}
END_OP(DynamicImport);
}
CASE(ImportMeta) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(ImportMeta);
}
CASE(NewInit) {
if (HybridICs) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
obj = NewObjectOperation(cx, script0, pc);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewInit);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewObject, false);
END_OP(NewInit);
}
}
CASE(NewObject) {
if (HybridICs) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
obj = NewObjectOperation(cx, script0, pc);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewObject);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewObject, false);
END_OP(NewObject);
}
}
CASE(Object) {
VIRTPUSH(StackVal(ObjectValue(*frame->script()->getObject(pc))));
END_OP(Object);
}
CASE(ObjWithProto) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
JSObject* obj;
{
PUSH_EXIT_FRAME();
obj = ObjectWithProtoOperation(cx, value0);
if (!obj) {
GOTO_ERROR();
}
}
VIRTSPWRITE(0, StackVal(ObjectValue(*obj)));
}
END_OP(ObjWithProto);
}
CASE(InitElem)
CASE(InitHiddenElem)
CASE(InitLockedElem)
CASE(InitElemInc)
CASE(SetElem)
CASE(StrictSetElem) {
static_assert(JSOpLength_InitElem == JSOpLength_InitHiddenElem);
static_assert(JSOpLength_InitElem == JSOpLength_InitLockedElem);
static_assert(JSOpLength_InitElem == JSOpLength_InitElemInc);
static_assert(JSOpLength_InitElem == JSOpLength_SetElem);
static_assert(JSOpLength_InitElem == JSOpLength_StrictSetElem);
StackVal val = VIRTSP(0);
IC_POP_ARG(2);
IC_POP_ARG(1);
IC_SET_ARG_FROM_STACK(0, 0);
if (JSOp(*pc) == JSOp::SetElem || JSOp(*pc) == JSOp::StrictSetElem) {
VIRTSPWRITE(0, val);
}
INVOKE_IC(SetElem, true);
if (JSOp(*pc) == JSOp::InitElemInc) {
VIRTPUSH(
StackVal(Int32Value(Value::fromRawBits(ic_arg1).toInt32() + 1)));
}
END_OP(InitElem);
}
CASE(InitPropGetter)
CASE(InitHiddenPropGetter)
CASE(InitPropSetter)
CASE(InitHiddenPropSetter) {
static_assert(JSOpLength_InitPropGetter ==
JSOpLength_InitHiddenPropGetter);
static_assert(JSOpLength_InitPropGetter == JSOpLength_InitPropSetter);
static_assert(JSOpLength_InitPropGetter ==
JSOpLength_InitHiddenPropSetter);
{
ReservedRooted<JSObject*> obj1(
&state.obj1,
&VIRTPOP().asValue().toObject()); // val
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTSP(0).asValue().toObject()); // obj; leave on stack
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
{
PUSH_EXIT_FRAME();
if (!InitPropGetterSetterOperation(cx, pc, obj0, name0, obj1)) {
GOTO_ERROR();
}
}
}
END_OP(InitPropGetter);
}
CASE(InitElemGetter)
CASE(InitHiddenElemGetter)
CASE(InitElemSetter)
CASE(InitHiddenElemSetter) {
static_assert(JSOpLength_InitElemGetter ==
JSOpLength_InitHiddenElemGetter);
static_assert(JSOpLength_InitElemGetter == JSOpLength_InitElemSetter);
static_assert(JSOpLength_InitElemGetter ==
JSOpLength_InitHiddenElemSetter);
{
ReservedRooted<JSObject*> obj1(
&state.obj1,
&VIRTPOP().asValue().toObject()); // val
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // idval
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTSP(0).asValue().toObject()); // obj; leave on stack
{
PUSH_EXIT_FRAME();
if (!InitElemGetterSetterOperation(cx, pc, obj0, value0, obj1)) {
GOTO_ERROR();
}
}
}
END_OP(InitElemGetter);
}
CASE(GetProp)
CASE(GetBoundName) {
static_assert(JSOpLength_GetProp == JSOpLength_GetBoundName);
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetProp, false);
END_OP(GetProp);
}
CASE(GetPropSuper) {
IC_POP_ARG(0);
IC_POP_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetPropSuper, false);
END_OP(GetPropSuper);
}
CASE(GetElem) {
if (HybridICs && VIRTSP(1).asValue().isString()) {
HandleValue lhs = SPHANDLE(1);
HandleValue rhs = SPHANDLE(0);
uint32_t index;
if (IsDefinitelyIndex(rhs, &index)) {
JSString* str = lhs.toString();
if (index < str->length() && str->isLinear()) {
JSLinearString* linear = &str->asLinear();
char16_t c = linear->latin1OrTwoByteChar(index);
StaticStrings& sstr =
ctx.frameMgr.cxForLocalUseOnly()->staticStrings();
if (sstr.hasUnit(c)) {
VIRTPOP();
VIRTSPWRITE(0, StackVal(StringValue(sstr.getUnit(c))));
NEXT_IC();
END_OP(GetElem);
}
}
}
}
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetElem, false);
END_OP(GetElem);
}
CASE(GetElemSuper) {
// N.B.: second and third args are out of order! See the saga at
// an echo of that issue.
IC_POP_ARG(1);
IC_POP_ARG(2);
IC_POP_ARG(0);
INVOKE_IC_AND_PUSH(GetElemSuper, true);
END_OP(GetElemSuper);
}
CASE(DelProp) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelPropOperation<false>(cx, value0, name0, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(DelProp);
}
CASE(StrictDelProp) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelPropOperation<true>(cx, value0, name0, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(StrictDelProp);
}
CASE(DelElem) {
{
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelElemOperation<false>(cx, value0, value1, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(DelElem);
}
CASE(StrictDelElem) {
{
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
bool res = false;
{
PUSH_EXIT_FRAME();
if (!DelElemOperation<true>(cx, value0, value1, &res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(res)));
}
END_OP(StrictDelElem);
}
CASE(HasOwn) {
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(HasOwn, false);
END_OP(HasOwn);
}
CASE(CheckPrivateField) {
IC_SET_ARG_FROM_STACK(1, 0);
IC_SET_ARG_FROM_STACK(0, 1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(CheckPrivateField, false);
END_OP(CheckPrivateField);
}
CASE(NewPrivateName) {
{
ReservedRooted<JSAtom*> atom0(&state.atom0,
frame->script()->getAtom(pc));
JS::Symbol* symbol;
{
PUSH_EXIT_FRAME();
symbol = NewPrivateName(cx, atom0);
if (!symbol) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(SymbolValue(symbol)));
}
END_OP(NewPrivateName);
}
CASE(SuperBase) {
JSFunction& superEnvFunc =
VIRTPOP().asValue().toObject().as<JSFunction>();
MOZ_ASSERT(superEnvFunc.allowSuperProperty());
MOZ_ASSERT(superEnvFunc.baseScript()->needsHomeObject());
const Value& homeObjVal = superEnvFunc.getExtendedSlot(
FunctionExtended::METHOD_HOMEOBJECT_SLOT);
JSObject* homeObj = &homeObjVal.toObject();
JSObject* superBase = HomeObjectSuperBase(homeObj);
VIRTPUSH(StackVal(ObjectOrNullValue(superBase)));
END_OP(SuperBase);
}
CASE(SetPropSuper)
CASE(StrictSetPropSuper) {
// stack signature: receiver, lval, rval => rval
static_assert(JSOpLength_SetPropSuper == JSOpLength_StrictSetPropSuper);
bool strict = JSOp(*pc) == JSOp::StrictSetPropSuper;
{
ReservedRooted<Value> value2(&state.value2,
VIRTPOP().asValue()); // rval
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // lval
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // recevier
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
{
PUSH_EXIT_FRAME();
// SetPropertySuper(cx, lval, receiver, name, rval, strict)
// (N.B.: lval and receiver are transposed!)
if (!SetPropertySuper(cx, value1, value0, name0, value2, strict)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value2));
}
END_OP(SetPropSuper);
}
CASE(SetElemSuper)
CASE(StrictSetElemSuper) {
// stack signature: receiver, key, lval, rval => rval
static_assert(JSOpLength_SetElemSuper == JSOpLength_StrictSetElemSuper);
bool strict = JSOp(*pc) == JSOp::StrictSetElemSuper;
{
ReservedRooted<Value> value3(&state.value3,
VIRTPOP().asValue()); // rval
ReservedRooted<Value> value2(&state.value2,
VIRTPOP().asValue()); // lval
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // index
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // receiver
{
PUSH_EXIT_FRAME();
// SetElementSuper(cx, lval, receiver, index, rval, strict)
// (N.B.: lval, receiver and index are rotated!)
if (!SetElementSuper(cx, value2, value0, value1, value3, strict)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value3)); // value
}
END_OP(SetElemSuper);
}
CASE(Iter) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetIterator, false);
END_OP(Iter);
}
CASE(MoreIter) {
// iter => iter, name
Value v = IteratorMore(&VIRTSP(0).asValue().toObject());
VIRTPUSH(StackVal(v));
END_OP(MoreIter);
}
CASE(IsNoIter) {
// iter => iter, bool
bool result = VIRTSP(0).asValue().isMagic(JS_NO_ITER_VALUE);
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsNoIter);
}
CASE(EndIter) {
// iter, interval =>
VIRTPOP();
CloseIterator(&VIRTPOP().asValue().toObject());
END_OP(EndIter);
}
CASE(CloseIter) {
IC_SET_OBJ_ARG(0, &VIRTPOP().asValue().toObject());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC(CloseIter, false);
END_OP(CloseIter);
}
CASE(CheckIsObj) {
if (!VIRTSP(0).asValue().isObject()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(
js::ThrowCheckIsObject(cx, js::CheckIsObjectKind(GET_UINT8(pc))));
/* abandon frame; error handler will re-establish sp */
GOTO_ERROR();
}
END_OP(CheckIsObj);
}
CASE(CheckObjCoercible) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
if (value0.isNullOrUndefined()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowObjectCoercible(cx, value0));
/* abandon frame; error handler will re-establish sp */
GOTO_ERROR();
}
}
END_OP(CheckObjCoercible);
}
CASE(ToAsyncIter) {
// iter, next => asynciter
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // next
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // iter
JSObject* result;
{
PUSH_EXIT_FRAME();
result = CreateAsyncFromSyncIterator(cx, obj0, value0);
if (!result) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*result)));
}
END_OP(ToAsyncIter);
}
CASE(MutateProto) {
// obj, protoVal => obj
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
{
PUSH_EXIT_FRAME();
if (!MutatePrototype(cx, obj0.as<PlainObject>(), value0)) {
GOTO_ERROR();
}
}
}
END_OP(MutateProto);
}
CASE(NewArray) {
if (HybridICs) {
ArrayObject* obj;
{
PUSH_EXIT_FRAME();
uint32_t length = GET_UINT32(pc);
obj = NewArrayOperation(cx, length);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
NEXT_IC();
END_OP(NewArray);
} else {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(NewArray, false);
END_OP(NewArray);
}
}
CASE(InitElemArray) {
// array, val => array
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
{
PUSH_EXIT_FRAME();
InitElemArrayOperation(cx, pc, obj0.as<ArrayObject>(), value0);
}
}
END_OP(InitElemArray);
}
CASE(Hole) {
VIRTPUSH(StackVal(MagicValue(JS_ELEMENTS_HOLE)));
END_OP(Hole);
}
CASE(RegExp) {
JSObject* obj;
{
PUSH_EXIT_FRAME();
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->script()->getRegExp(pc));
obj = CloneRegExpObject(cx, obj0.as<RegExpObject>());
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
END_OP(RegExp);
}
CASE(Lambda) {
{
ReservedRooted<JSFunction*> fun0(&state.fun0,
frame->script()->getFunction(pc));
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
JSObject* res;
{
PUSH_EXIT_FRAME();
res = js::Lambda(cx, fun0, obj0);
if (!res) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*res)));
}
END_OP(Lambda);
}
CASE(SetFunName) {
// fun, name => fun
{
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // name
ReservedRooted<JSFunction*> fun0(
&state.fun0, &VIRTSP(0).asValue().toObject().as<JSFunction>());
FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(pc));
{
PUSH_EXIT_FRAME();
if (!SetFunctionName(cx, fun0, value0, prefixKind)) {
GOTO_ERROR();
}
}
}
END_OP(SetFunName);
}
CASE(InitHomeObject) {
// fun, homeObject => fun
{
ReservedRooted<JSObject*> obj0(
&state.obj0, &VIRTPOP().asValue().toObject()); // homeObject
ReservedRooted<JSFunction*> fun0(
&state.fun0, &VIRTSP(0).asValue().toObject().as<JSFunction>());
MOZ_ASSERT(fun0->allowSuperProperty());
MOZ_ASSERT(obj0->is<PlainObject>() || obj0->is<JSFunction>());
fun0->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT,
ObjectValue(*obj0));
}
END_OP(InitHomeObject);
}
CASE(CheckClassHeritage) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
{
PUSH_EXIT_FRAME();
if (!CheckClassHeritageOperation(cx, value0)) {
GOTO_ERROR();
}
}
}
END_OP(CheckClassHeritage);
}
CASE(FunWithProto) {
// proto => obj
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // proto
ReservedRooted<JSObject*> obj1(&state.obj1,
frame->environmentChain());
ReservedRooted<JSFunction*> fun0(&state.fun0,
frame->script()->getFunction(pc));
JSObject* obj;
{
PUSH_EXIT_FRAME();
obj = FunWithProtoOperation(cx, fun0, obj1, obj0);
if (!obj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*obj)));
}
END_OP(FunWithProto);
}
CASE(BuiltinObject) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(BuiltinObject);
}
CASE(Call)
CASE(CallIgnoresRv)
CASE(CallContent)
CASE(CallIter)
CASE(CallContentIter)
CASE(Eval)
CASE(StrictEval)
CASE(SuperCall)
CASE(New)
CASE(NewContent) {
static_assert(JSOpLength_Call == JSOpLength_CallIgnoresRv);
static_assert(JSOpLength_Call == JSOpLength_CallContent);
static_assert(JSOpLength_Call == JSOpLength_CallIter);
static_assert(JSOpLength_Call == JSOpLength_CallContentIter);
static_assert(JSOpLength_Call == JSOpLength_Eval);
static_assert(JSOpLength_Call == JSOpLength_StrictEval);
static_assert(JSOpLength_Call == JSOpLength_SuperCall);
static_assert(JSOpLength_Call == JSOpLength_New);
static_assert(JSOpLength_Call == JSOpLength_NewContent);
JSOp op = JSOp(*pc);
bool constructing = (op == JSOp::New || op == JSOp::NewContent ||
op == JSOp::SuperCall);
uint32_t argc = GET_ARGC(pc);
do {
{
// CallArgsFromSp would be called with
// - numValues = argc + 2 + constructing
// - stackSlots = argc + constructing
// - sp = vp + numValues
// CallArgs::create then gets
// - argc_ = stackSlots - constructing = argc
// - argv_ = sp - stackSlots = vp + 2
// our arguments are in reverse order compared to what CallArgs
// expects so we should subtract any array subscripts from (sp +
// stackSlots - 1)
StackVal* firstArg = sp + argc + constructing - 1;
// callee is argv_[-2] -> sp + argc + constructing + 1
// this is argv_[-1] -> sp + argc + constructing
// newTarget is argv_[argc_] -> sp + constructing - 1
// but this/newTarget are only used when constructing is 1 so we can
// simplify this is argv_[-1] -> sp + argc + 1 newTarget is
// argv_[argc_] -> sp
HandleValue callee = Stack::handle(firstArg + 2);
if (!callee.isObject() || !callee.toObject().is<JSFunction>()) {
TRACE_PRINTF("missed fastpath: not a function\n");
break;
}
ReservedRooted<JSFunction*> func(
&state.fun0, &callee.toObject().as<JSFunction>());
if (!func->hasBaseScript() || !func->isInterpreted()) {
TRACE_PRINTF("missed fastpath: not an interpreted script\n");
break;
}
if (!constructing && func->isClassConstructor()) {
TRACE_PRINTF(
"missed fastpath: constructor called without `new`\n");
break;
}
if (constructing && !func->isConstructor()) {
TRACE_PRINTF(
"missed fastpath: constructing with a non-constructor\n");
break;
}
if (!func->baseScript()->hasBytecode()) {
TRACE_PRINTF("missed fastpath: no bytecode\n");
break;
}
ReservedRooted<JSScript*> calleeScript(
&state.script0, func->baseScript()->asJSScript());
if (!calleeScript->hasJitScript()) {
TRACE_PRINTF("missed fastpath: no jit-script\n");
break;
}
if (ctx.frameMgr.cxForLocalUseOnly()->realm() !=
calleeScript->realm()) {
TRACE_PRINTF("missed fastpath: mismatched realm\n");
break;
}
if (argc < func->nargs()) {
TRACE_PRINTF("missed fastpath: not enough arguments\n");
break;
}
// Fast-path: function, interpreted, has JitScript, same realm, no
// argument underflow.
// Include newTarget in the args if it exists; exclude callee
uint32_t totalArgs = argc + 1 + constructing;
StackVal* origArgs = sp;
TRACE_PRINTF(
"Call fastpath: argc = %d origArgs = %p callee = %" PRIx64 "\n",
argc, origArgs, callee.get().asRawBits());
if (!ctx.stack.check(sp, sizeof(StackVal) * (totalArgs + 3))) {
TRACE_PRINTF("missed fastpath: would cause stack overrun\n");
break;
}
if (constructing) {
MutableHandleValue thisv = Stack::handleMut(firstArg + 1);
if (!thisv.isObject()) {
HandleValue newTarget = Stack::handle(firstArg - argc);
ReservedRooted<JSObject*> obj0(&state.obj0,
&newTarget.toObject());
PUSH_EXIT_FRAME();
// CreateThis might discard the JitScript but we're counting on
// it continuing to exist while we evaluate the fastpath.
AutoKeepJitScripts keepJitScript(cx);
if (!CreateThis(cx, func, obj0, GenericObject, thisv)) {
GOTO_ERROR();
}
TRACE_PRINTF("created %" PRIx64 "\n", thisv.get().asRawBits());
}
}
// 0. Save current PC and interpreter IC pointer in
// current frame, so we can retrieve them later.
frame->interpreterPC() = pc;
frame->interpreterICEntry() = icEntry;
// 1. Push a baseline stub frame. Don't use the frame manager
// -- we don't want the frame to be auto-freed when we leave
// this scope, and we don't want to shadow `sp`.
StackVal* exitFP = ctx.stack.pushExitFrame(sp, frame);
MOZ_ASSERT(exitFP); // safety: stack margin.
sp = exitFP;
TRACE_PRINTF("exit frame at %p\n", exitFP);
// 2. Modify exit code to nullptr (this is where ICStubReg is
// normally saved; the tracing code can skip if null).
PUSHNATIVE(StackValNative(nullptr));
// 3. Push args in proper order (they are reversed in our
// downward-growth stack compared to what the calling
// convention expects).
for (uint32_t i = 0; i < totalArgs; i++) {
VIRTPUSH(origArgs[i]);
}
// 4. Push inter-frame content: callee token, descriptor for
// above.
PUSHNATIVE(StackValNative(CalleeToToken(func, constructing)));
PUSHNATIVE(StackValNative(
MakeFrameDescriptorForJitCall(FrameType::BaselineStub, argc)));
// 5. Push fake return address, set script, push baseline frame.
PUSHNATIVE(StackValNative(nullptr));
BaselineFrame* newFrame =
ctx.stack.pushFrame(sp, ctx.frameMgr.cxForLocalUseOnly(),
/* envChain = */ func->environment());
MOZ_ASSERT(newFrame); // safety: stack margin.
TRACE_PRINTF("callee frame at %p\n", newFrame);
frame = newFrame;
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
// 6. Set up PC and SP for callee.
sp = reinterpret_cast<StackVal*>(frame);
RESET_PC(calleeScript->code(), calleeScript);
// 7. Check callee stack space for max stack depth.
if (!stack.check(sp, sizeof(StackVal) * calleeScript->nslots())) {
PUSH_EXIT_FRAME();
ReportOverRecursed(ctx.frameMgr.cxForLocalUseOnly());
GOTO_ERROR();
}
// 8. Push local slots, and set return value to `undefined` by
// default.
uint32_t nfixed = calleeScript->nfixed();
for (uint32_t i = 0; i < nfixed; i++) {
VIRTPUSH(StackVal(UndefinedValue()));
}
ret->setUndefined();
// 9. Initialize environment objects.
if (func->needsFunctionEnvironmentObjects()) {
PUSH_EXIT_FRAME();
if (!js::InitFunctionEnvironmentObjects(cx, frame)) {
GOTO_ERROR();
}
}
// 10. Set debug flag, if appropriate.
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("Script is debuggee\n");
frame->setIsDebuggee();
PUSH_EXIT_FRAME();
if (!DebugPrologue(cx, frame)) {
GOTO_ERROR();
}
}
// 11. Check for interrupts.
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
// 12. Initialize coverage tables, if needed.
if (!frame->script()->hasScriptCounts()) {
if (ctx.frameMgr.cxForLocalUseOnly()
->realm()
->collectCoverageForDebug()) {
PUSH_EXIT_FRAME();
if (!frame->script()->initScriptCounts(cx)) {
GOTO_ERROR();
}
}
}
COUNT_COVERAGE_MAIN();
}
// Everything is switched to callee context now -- dispatch!
DISPATCH();
} while (0);
// Slow path: use the IC!
ic_arg0 = argc;
ctx.icregs.extraArgs = 2 + constructing;
INVOKE_IC(Call, false);
VIRTPOPN(argc + 2 + constructing);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(Call);
}
CASE(SpreadCall)
CASE(SpreadEval)
CASE(StrictSpreadEval) {
static_assert(JSOpLength_SpreadCall == JSOpLength_SpreadEval);
static_assert(JSOpLength_SpreadCall == JSOpLength_StrictSpreadEval);
ic_arg0 = 1;
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
ctx.icregs.extraArgs = 2;
INVOKE_IC(SpreadCall, false);
VIRTPOPN(3);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(SpreadCall);
}
CASE(SpreadSuperCall)
CASE(SpreadNew) {
static_assert(JSOpLength_SpreadSuperCall == JSOpLength_SpreadNew);
ic_arg0 = 1;
ctx.icregs.extraArgs = 3;
INVOKE_IC(SpreadCall, false);
VIRTPOPN(4);
VIRTPUSH(StackVal(Value::fromRawBits(ic_ret)));
END_OP(SpreadSuperCall);
}
CASE(OptimizeSpreadCall) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(OptimizeSpreadCall, false);
END_OP(OptimizeSpreadCall);
}
CASE(OptimizeGetIterator) {
IC_POP_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(OptimizeGetIterator, false);
END_OP(OptimizeGetIterator);
}
CASE(ImplicitThis) {
{
ReservedRooted<JSObject*> env(&state.obj0,
&VIRTSP(0).asValue().toObject());
VIRTPOP();
PUSH_EXIT_FRAME();
ImplicitThisOperation(cx, env, &state.res);
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(ImplicitThis);
}
CASE(CallSiteObj) {
JSObject* cso = frame->script()->getObject(pc);
MOZ_ASSERT(!cso->as<ArrayObject>().isExtensible());
MOZ_ASSERT(cso->as<ArrayObject>().containsPure(
ctx.frameMgr.cxForLocalUseOnly()->names().raw));
VIRTPUSH(StackVal(ObjectValue(*cso)));
END_OP(CallSiteObj);
}
CASE(IsConstructing) {
VIRTPUSH(StackVal(MagicValue(JS_IS_CONSTRUCTING)));
END_OP(IsConstructing);
}
CASE(SuperFun) {
JSObject* superEnvFunc = &VIRTPOP().asValue().toObject();
JSObject* superFun = SuperFunOperation(superEnvFunc);
VIRTPUSH(StackVal(ObjectOrNullValue(superFun)));
END_OP(SuperFun);
}
CASE(CheckThis) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx));
GOTO_ERROR();
}
END_OP(CheckThis);
}
CASE(CheckThisReinit) {
if (!VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowInitializedThis(cx));
GOTO_ERROR();
}
END_OP(CheckThisReinit);
}
CASE(Generator) {
JSObject* generator;
{
PUSH_EXIT_FRAME();
generator = CreateGeneratorFromFrame(cx, frame);
if (!generator) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*generator)));
END_OP(Generator);
}
CASE(InitialYield) {
// gen => rval, gen, resumeKind
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTSP(0).asValue().toObject());
uint32_t frameSize = ctx.stack.frameSize(sp, frame);
{
PUSH_EXIT_FRAME();
if (!NormalSuspend(cx, obj0, frame, frameSize, pc)) {
GOTO_ERROR();
}
}
frame->setReturnValue(VIRTSP(0).asValue());
goto do_return;
}
CASE(Await)
CASE(Yield) {
// rval1, gen => rval2, gen, resumeKind
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTPOP().asValue().toObject());
uint32_t frameSize = ctx.stack.frameSize(sp, frame);
{
PUSH_EXIT_FRAME();
if (!NormalSuspend(cx, obj0, frame, frameSize, pc)) {
GOTO_ERROR();
}
}
frame->setReturnValue(VIRTSP(0).asValue());
goto do_return;
}
CASE(FinalYieldRval) {
// gen =>
ReservedRooted<JSObject*> obj0(&state.obj0,
&VIRTPOP().asValue().toObject());
{
PUSH_EXIT_FRAME();
if (!FinalSuspend(cx, obj0, pc)) {
GOTO_ERROR();
}
}
goto do_return;
}
CASE(IsGenClosing) {
bool result = VIRTSP(0).asValue() == MagicValue(JS_GENERATOR_CLOSING);
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(IsGenClosing);
}
CASE(AsyncAwait) {
// value, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
PUSH_EXIT_FRAME();
promise = AsyncFunctionAwait(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncAwait);
}
CASE(AsyncResolve) {
// value, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
PUSH_EXIT_FRAME();
promise = AsyncFunctionResolve(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncResolve);
}
CASE(AsyncReject) {
// reason, gen => promise
JSObject* promise;
{
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // stack
ReservedRooted<Value> value1(&state.value1,
VIRTPOP().asValue()); // reason
PUSH_EXIT_FRAME();
promise = AsyncFunctionReject(
cx, obj0.as<AsyncFunctionGeneratorObject>(), value1, value0);
if (!promise) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*promise)));
END_OP(AsyncReject);
}
CASE(CanSkipAwait) {
// value => value, can_skip
bool result = false;
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
PUSH_EXIT_FRAME();
if (!CanSkipAwait(cx, value0, &result)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(BooleanValue(result)));
END_OP(CanSkipAwait);
}
CASE(MaybeExtractAwaitValue) {
// value, can_skip => value_or_resolved, can_skip
{
Value can_skip = VIRTPOP().asValue();
ReservedRooted<Value> value0(&state.value0,
VIRTPOP().asValue()); // value
if (can_skip.toBoolean()) {
PUSH_EXIT_FRAME();
if (!ExtractAwaitValue(cx, value0, &value0)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value0));
VIRTPUSH(StackVal(can_skip));
}
END_OP(MaybeExtractAwaitValue);
}
CASE(ResumeKind) {
GeneratorResumeKind resumeKind = ResumeKindFromPC(pc);
VIRTPUSH(StackVal(Int32Value(int32_t(resumeKind))));
END_OP(ResumeKind);
}
CASE(CheckResumeKind) {
// rval, gen, resumeKind => rval
{
GeneratorResumeKind resumeKind =
IntToResumeKind(VIRTPOP().asValue().toInt32());
ReservedRooted<JSObject*> obj0(
&state.obj0,
&VIRTPOP().asValue().toObject()); // gen
ReservedRooted<Value> value0(&state.value0,
VIRTSP(0).asValue()); // rval
if (resumeKind != GeneratorResumeKind::Next) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(GeneratorThrowOrReturn(
cx, frame, obj0.as<AbstractGeneratorObject>(), value0,
resumeKind));
GOTO_ERROR();
}
}
END_OP(CheckResumeKind);
}
CASE(Resume) {
SYNCSP();
Value gen = VIRTSP(2).asValue();
Value* callerSP = reinterpret_cast<Value*>(sp);
{
ReservedRooted<Value> value0(&state.value0);
ReservedRooted<JSObject*> obj0(&state.obj0, &gen.toObject());
{
PUSH_EXIT_FRAME();
TRACE_PRINTF("Going to C++ interp for Resume\n");
if (!InterpretResume(cx, obj0, callerSP, &value0)) {
GOTO_ERROR();
}
}
VIRTPOPN(2);
VIRTSPWRITE(0, StackVal(value0));
}
END_OP(Resume);
}
CASE(JumpTarget) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
COUNT_COVERAGE_PC(pc);
END_OP(JumpTarget);
}
CASE(LoopHead) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
COUNT_COVERAGE_PC(pc);
END_OP(LoopHead);
}
CASE(AfterYield) {
int32_t icIndex = GET_INT32(pc);
icEntry = icEntries + icIndex;
if (frame->script()->isDebuggee()) {
TRACE_PRINTF("doing DebugAfterYield\n");
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
if (DebugAPI::hasAnyBreakpointsOrStepMode(script0) &&
!HandleDebugTrap(cx, frame, pc)) {
TRACE_PRINTF("HandleDebugTrap returned error\n");
GOTO_ERROR();
}
if (!DebugAfterYield(cx, frame)) {
TRACE_PRINTF("DebugAfterYield returned error\n");
GOTO_ERROR();
}
}
COUNT_COVERAGE_PC(pc);
END_OP(AfterYield);
}
CASE(Goto) {
ADVANCE(GET_JUMP_OFFSET(pc));
PREDICT_NEXT(JumpTarget);
PREDICT_NEXT(LoopHead);
DISPATCH();
}
CASE(Coalesce) {
if (!VIRTSP(0).asValue().isNullOrUndefined()) {
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
} else {
END_OP(Coalesce);
}
}
CASE(Case) {
bool cond = VIRTPOP().asValue().toBoolean();
if (cond) {
VIRTPOP();
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
} else {
END_OP(Case);
}
}
CASE(Default) {
VIRTPOP();
ADVANCE(GET_JUMP_OFFSET(pc));
DISPATCH();
}
CASE(TableSwitch) {
int32_t len = GET_JUMP_OFFSET(pc);
int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
Value v = VIRTPOP().asValue();
int32_t i = 0;
if (v.isInt32()) {
i = v.toInt32();
} else if (!v.isDouble() ||
!mozilla::NumberEqualsInt32(v.toDouble(), &i)) {
ADVANCE(len);
DISPATCH();
}
if (i >= low && i <= high) {
uint32_t idx = uint32_t(i) - uint32_t(low);
uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
pc = entryPC + resumeOffsets[firstResumeIndex + idx];
DISPATCH();
}
ADVANCE(len);
DISPATCH();
}
CASE(Return) {
frame->setReturnValue(VIRTPOP().asValue());
goto do_return;
}
CASE(GetRval) {
VIRTPUSH(StackVal(frame->returnValue()));
END_OP(GetRval);
}
CASE(SetRval) {
frame->setReturnValue(VIRTPOP().asValue());
END_OP(SetRval);
}
do_return:
CASE(RetRval) {
SYNCSP();
bool ok = true;
if (frame->isDebuggee() && !from_unwind) {
TRACE_PRINTF("doing DebugEpilogueOnBaselineReturn\n");
PUSH_EXIT_FRAME();
ok = DebugEpilogueOnBaselineReturn(cx, frame, pc);
}
from_unwind = false;
uint32_t argc = frame->numActualArgs();
sp = ctx.stack.popFrame();
// If FP is higher than the entry frame now, return; otherwise,
// do an inline state update.
if (stack.fp > entryFrame) {
*ret = frame->returnValue();
TRACE_PRINTF("ret = %" PRIx64 "\n", ret->asRawBits());
return ok ? PBIResult::Ok : PBIResult::Error;
} else {
TRACE_PRINTF("Return fastpath\n");
Value ret = frame->returnValue();
TRACE_PRINTF("ret = %" PRIx64 "\n", ret.asRawBits());
// Pop exit frame as well.
sp = ctx.stack.popFrame();
// Pop fake return address and descriptor.
POPNNATIVE(2);
// Set PC, frame, and current script.
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" sp -> %p, fp -> %p, frame -> %p\n", sp, ctx.stack.fp,
frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
// Adjust caller's stack to complete the call op that PC still points
// to in that frame (pop args, push return value).
JSOp op = JSOp(*pc);
bool constructing = (op == JSOp::New || op == JSOp::NewContent ||
op == JSOp::SuperCall);
// Fix-up return value; EnterJit would do this if we hadn't bypassed
// it.
if (constructing && ret.isPrimitive()) {
ret = sp[argc + constructing].asValue();
TRACE_PRINTF("updated ret = %" PRIx64 "\n", ret.asRawBits());
}
// Pop args -- this is 1 more than how many are pushed in the
// `totalArgs` count during the call fastpath because it includes
// the callee.
VIRTPOPN(argc + 2 + constructing);
// Push return value.
VIRTPUSH(StackVal(ret));
if (!ok) {
GOTO_ERROR();
}
// Advance past call instruction, and advance past IC.
NEXT_IC();
ADVANCE(JSOpLength_Call);
DISPATCH();
}
}
CASE(CheckReturn) {
Value thisval = VIRTPOP().asValue();
// inlined version of frame->checkReturn(thisval, result)
// (js/src/vm/Stack.cpp).
HandleValue retVal = frame->returnValue();
if (retVal.isObject()) {
VIRTPUSH(StackVal(retVal));
} else if (!retVal.isUndefined()) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN,
JSDVG_IGNORE_STACK, retVal,
nullptr));
GOTO_ERROR();
} else if (thisval.isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx));
GOTO_ERROR();
} else {
VIRTPUSH(StackVal(thisval));
}
END_OP(CheckReturn);
}
CASE(Throw) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowOperation(cx, value0));
GOTO_ERROR();
}
END_OP(Throw);
}
CASE(ThrowWithStack) {
{
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
ReservedRooted<Value> value1(&state.value1, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowWithStackOperation(cx, value1, value0));
GOTO_ERROR();
}
END_OP(ThrowWithStack);
}
CASE(ThrowMsg) {
{
PUSH_EXIT_FRAME();
MOZ_ALWAYS_FALSE(ThrowMsgOperation(cx, GET_UINT8(pc)));
GOTO_ERROR();
}
END_OP(ThrowMsg);
}
CASE(ThrowSetConst) {
{
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, script0, pc);
GOTO_ERROR();
}
END_OP(ThrowSetConst);
}
CASE(Try)
CASE(TryDestructuring) {
static_assert(JSOpLength_Try == JSOpLength_TryDestructuring);
END_OP(Try);
}
CASE(Exception) {
{
PUSH_EXIT_FRAME();
if (!GetAndClearException(cx, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(Exception);
}
CASE(ExceptionAndStack) {
{
ReservedRooted<Value> value0(&state.value0);
{
PUSH_EXIT_FRAME();
if (!cx.getCx()->getPendingExceptionStack(&value0)) {
GOTO_ERROR();
}
if (!GetAndClearException(cx, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
VIRTPUSH(StackVal(value0));
state.res.setUndefined();
}
END_OP(ExceptionAndStack);
}
CASE(Finally) {
#ifdef ENABLE_INTERRUPT_CHECKS
if (ctx.frameMgr.cxForLocalUseOnly()->hasAnyPendingInterrupt()) {
PUSH_EXIT_FRAME();
if (!InterruptCheck(cx)) {
GOTO_ERROR();
}
}
#endif
END_OP(Finally);
}
CASE(Uninitialized) {
VIRTPUSH(StackVal(MagicValue(JS_UNINITIALIZED_LEXICAL)));
END_OP(Uninitialized);
}
CASE(InitLexical) {
uint32_t i = GET_LOCALNO(pc);
frame->unaliasedLocal(i) = VIRTSP(0).asValue();
END_OP(InitLexical);
}
CASE(InitAliasedLexical) {
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
obj.setAliasedBinding(ec, VIRTSP(0).asValue());
END_OP(InitAliasedLexical);
}
CASE(CheckLexical) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script0,
pc);
GOTO_ERROR();
}
END_OP(CheckLexical);
}
CASE(CheckAliasedLexical) {
if (VIRTSP(0).asValue().isMagic(JS_UNINITIALIZED_LEXICAL)) {
PUSH_EXIT_FRAME();
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, script0,
pc);
GOTO_ERROR();
}
END_OP(CheckAliasedLexical);
}
CASE(BindUnqualifiedGName) {
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindUnqualifiedGName);
}
CASE(BindName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindName);
}
CASE(BindUnqualifiedName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(BindName, false);
END_OP(BindUnqualifiedName);
}
CASE(GetGName) {
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetName, false);
END_OP(GetGName);
}
CASE(GetName) {
IC_SET_OBJ_ARG(0, frame->environmentChain());
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(GetName, false);
END_OP(GetName);
}
CASE(GetArg) {
unsigned i = GET_ARGNO(pc);
if (argsObjAliasesFormals) {
VIRTPUSH(StackVal(frame->argsObj().arg(i)));
} else {
VIRTPUSH(StackVal(frame->argv()[i]));
}
END_OP(GetArg);
}
CASE(GetFrameArg) {
uint32_t i = GET_ARGNO(pc);
VIRTPUSH(StackVal(frame->argv()[i]));
END_OP(GetFrameArg);
}
CASE(GetLocal) {
uint32_t i = GET_LOCALNO(pc);
TRACE_PRINTF(" -> local: %d\n", int(i));
VIRTPUSH(StackVal(GETLOCAL(i)));
END_OP(GetLocal);
}
CASE(ArgumentsLength) {
VIRTPUSH(StackVal(Int32Value(frame->numActualArgs())));
END_OP(ArgumentsLength);
}
CASE(GetActualArg) {
MOZ_ASSERT(!frame->script()->needsArgsObj());
uint32_t index = VIRTSP(0).asValue().toInt32();
VIRTSPWRITE(0, StackVal(frame->unaliasedActual(index)));
END_OP(GetActualArg);
}
CASE(GetAliasedVar)
CASE(GetAliasedDebugVar) {
static_assert(JSOpLength_GetAliasedVar ==
JSOpLength_GetAliasedDebugVar);
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
VIRTPUSH(StackVal(obj.aliasedBinding(ec)));
END_OP(GetAliasedVar);
}
CASE(GetImport) {
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
ReservedRooted<Value> value0(&state.value0);
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
{
PUSH_EXIT_FRAME();
if (!GetImportOperation(cx, obj0, script0, pc, &value0)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(value0));
}
END_OP(GetImport);
}
CASE(GetIntrinsic) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(LazyConstant, false);
END_OP(GetIntrinsic);
}
CASE(Callee) {
VIRTPUSH(StackVal(frame->calleev()));
END_OP(Callee);
}
CASE(EnvCallee) {
uint8_t numHops = GET_UINT8(pc);
JSObject* env = &frame->environmentChain()->as<EnvironmentObject>();
for (unsigned i = 0; i < numHops; i++) {
env = &env->as<EnvironmentObject>().enclosingEnvironment();
}
VIRTPUSH(StackVal(ObjectValue(env->as<CallObject>().callee())));
END_OP(EnvCallee);
}
CASE(SetProp)
CASE(StrictSetProp)
CASE(SetName)
CASE(StrictSetName)
CASE(SetGName)
CASE(StrictSetGName) {
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetProp);
static_assert(JSOpLength_SetProp == JSOpLength_SetName);
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetName);
static_assert(JSOpLength_SetProp == JSOpLength_SetGName);
static_assert(JSOpLength_SetProp == JSOpLength_StrictSetGName);
IC_POP_ARG(1);
IC_POP_ARG(0);
IC_ZERO_ARG(2);
VIRTPUSH(StackVal(ic_arg1));
INVOKE_IC(SetProp, false);
END_OP(SetProp);
}
CASE(InitProp)
CASE(InitHiddenProp)
CASE(InitLockedProp) {
static_assert(JSOpLength_InitProp == JSOpLength_InitHiddenProp);
static_assert(JSOpLength_InitProp == JSOpLength_InitLockedProp);
IC_POP_ARG(1);
IC_SET_ARG_FROM_STACK(0, 0);
IC_ZERO_ARG(2);
INVOKE_IC(SetProp, false);
END_OP(InitProp);
}
CASE(InitGLexical) {
IC_SET_ARG_FROM_STACK(1, 0);
IC_SET_OBJ_ARG(
0,
&ctx.frameMgr.cxForLocalUseOnly()->global()->lexicalEnvironment());
IC_ZERO_ARG(2);
INVOKE_IC(SetProp, false);
END_OP(InitGLexical);
}
CASE(SetArg) {
unsigned i = GET_ARGNO(pc);
if (argsObjAliasesFormals) {
frame->argsObj().setArg(i, VIRTSP(0).asValue());
} else {
frame->argv()[i] = VIRTSP(0).asValue();
}
END_OP(SetArg);
}
CASE(SetLocal) {
uint32_t i = GET_LOCALNO(pc);
TRACE_PRINTF(" -> local: %d\n", int(i));
SETLOCAL(i, VIRTSP(0).asValue());
END_OP(SetLocal);
}
CASE(SetAliasedVar) {
EnvironmentCoordinate ec = EnvironmentCoordinate(pc);
EnvironmentObject& obj = getEnvironmentFromCoordinate(frame, ec);
MOZ_ASSERT(!IsUninitializedLexical(obj.aliasedBinding(ec)));
obj.setAliasedBinding(ec, VIRTSP(0).asValue());
END_OP(SetAliasedVar);
}
CASE(SetIntrinsic) {
{
ReservedRooted<Value> value0(&state.value0, VIRTSP(0).asValue());
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
{
PUSH_EXIT_FRAME();
if (!SetIntrinsicOperation(cx, script0, pc, value0)) {
GOTO_ERROR();
}
}
}
END_OP(SetIntrinsic);
}
CASE(PushLexicalEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
{
PUSH_EXIT_FRAME();
if (!frame->pushLexicalEnvironment(cx, scope0.as<LexicalScope>())) {
GOTO_ERROR();
}
}
}
END_OP(PushLexicalEnv);
}
CASE(PopLexicalEnv) {
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebugLeaveThenPopLexicalEnv\n");
PUSH_EXIT_FRAME();
if (!DebugLeaveThenPopLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
}
END_OP(PopLexicalEnv);
}
CASE(DebugLeaveLexicalEnv) {
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebugLeaveLexicalEnv\n");
PUSH_EXIT_FRAME();
if (!DebugLeaveLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
}
END_OP(DebugLeaveLexicalEnv);
}
CASE(RecreateLexicalEnv) {
{
PUSH_EXIT_FRAME();
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebuggeeRecreateLexicalEnv\n");
if (!DebuggeeRecreateLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
if (!frame->recreateLexicalEnvironment<false>(cx)) {
GOTO_ERROR();
}
}
}
END_OP(RecreateLexicalEnv);
}
CASE(FreshenLexicalEnv) {
{
PUSH_EXIT_FRAME();
if (frame->isDebuggee()) {
TRACE_PRINTF("doing DebuggeeFreshenLexicalEnv\n");
if (!DebuggeeFreshenLexicalEnv(cx, frame, pc)) {
GOTO_ERROR();
}
} else {
if (!frame->freshenLexicalEnvironment<false>(cx)) {
GOTO_ERROR();
}
}
}
END_OP(FreshenLexicalEnv);
}
CASE(PushClassBodyEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
PUSH_EXIT_FRAME();
if (!frame->pushClassBodyEnvironment(cx,
scope0.as<ClassBodyScope>())) {
GOTO_ERROR();
}
}
END_OP(PushClassBodyEnv);
}
CASE(PushVarEnv) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
PUSH_EXIT_FRAME();
if (!frame->pushVarEnvironment(cx, scope0)) {
GOTO_ERROR();
}
}
END_OP(PushVarEnv);
}
CASE(EnterWith) {
{
ReservedRooted<Scope*> scope0(&state.scope0,
frame->script()->getScope(pc));
ReservedRooted<Value> value0(&state.value0, VIRTPOP().asValue());
PUSH_EXIT_FRAME();
if (!EnterWithOperation(cx, frame, value0, scope0.as<WithScope>())) {
GOTO_ERROR();
}
}
END_OP(EnterWith);
}
CASE(LeaveWith) {
frame->popOffEnvironmentChain<WithEnvironmentObject>();
END_OP(LeaveWith);
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
CASE(AddDisposable) {
{
ReservedRooted<JSObject*> env(&state.obj0, frame->environmentChain());
ReservedRooted<JS::Value> needsClosure(&state.value0,
VIRTPOP().asValue());
ReservedRooted<JS::Value> method(&state.value1, VIRTPOP().asValue());
ReservedRooted<JS::Value> val(&state.value2, VIRTPOP().asValue());
UsingHint hint = UsingHint(GET_UINT8(pc));
PUSH_EXIT_FRAME();
if (!AddDisposableResourceToCapability(
cx, env, val, method, needsClosure.toBoolean(), hint)) {
GOTO_ERROR();
}
}
END_OP(AddDisposable);
}
CASE(TakeDisposeCapability) {
{
ReservedRooted<JSObject*> env(&state.obj0, frame->environmentChain());
JS::Value maybeDisposables =
env->as<DisposableEnvironmentObject>().getDisposables();
MOZ_ASSERT(maybeDisposables.isObject() ||
maybeDisposables.isUndefined());
if (maybeDisposables.isUndefined()) {
VIRTPUSH(StackVal(UndefinedValue()));
} else {
VIRTPUSH(StackVal(maybeDisposables));
env->as<DisposableEnvironmentObject>().clearDisposables();
}
}
END_OP(TakeDisposeCapability);
}
CASE(CreateSuppressedError) {
ErrorObject* errorObj;
{
ReservedRooted<JS::Value> error(&state.value0, VIRTPOP().asValue());
ReservedRooted<JS::Value> suppressed(&state.value1,
VIRTPOP().asValue());
PUSH_EXIT_FRAME();
errorObj = CreateSuppressedError(cx, error, suppressed);
if (!errorObj) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(ObjectValue(*errorObj)));
END_OP(CreateSuppressedError);
}
#endif
CASE(BindVar) {
JSObject* varObj;
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
PUSH_EXIT_FRAME();
varObj = BindVarOperation(cx, obj0);
}
VIRTPUSH(StackVal(ObjectValue(*varObj)));
END_OP(BindVar);
}
CASE(GlobalOrEvalDeclInstantiation) {
GCThingIndex lastFun = GET_GCTHING_INDEX(pc);
{
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
ReservedRooted<JSScript*> script0(&state.script0, frame->script());
PUSH_EXIT_FRAME();
if (!GlobalOrEvalDeclInstantiation(cx, obj0, script0, lastFun)) {
GOTO_ERROR();
}
}
END_OP(GlobalOrEvalDeclInstantiation);
}
CASE(DelName) {
{
ReservedRooted<PropertyName*> name0(&state.name0,
frame->script()->getName(pc));
ReservedRooted<JSObject*> obj0(&state.obj0,
frame->environmentChain());
PUSH_EXIT_FRAME();
if (!DeleteNameOperation(cx, name0, obj0, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(DelName);
}
CASE(Arguments) {
{
PUSH_EXIT_FRAME();
if (!NewArgumentsObject(cx, frame, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(Arguments);
}
CASE(Rest) {
IC_ZERO_ARG(0);
IC_ZERO_ARG(1);
IC_ZERO_ARG(2);
INVOKE_IC_AND_PUSH(Rest, false);
END_OP(Rest);
}
CASE(FunctionThis) {
{
PUSH_EXIT_FRAME();
if (!js::GetFunctionThis(cx, frame, &state.res)) {
GOTO_ERROR();
}
}
VIRTPUSH(StackVal(state.res));
state.res.setUndefined();
END_OP(FunctionThis);
}
CASE(Pop) {
VIRTPOP();
END_OP(Pop);
}
CASE(PopN) {
SYNCSP();
uint32_t n = GET_UINT16(pc);
VIRTPOPN(n);
END_OP(PopN);
}
CASE(Dup) {
StackVal value = VIRTSP(0);
VIRTPUSH(value);
END_OP(Dup);
}
CASE(Dup2) {
StackVal value1 = VIRTSP(0);
StackVal value2 = VIRTSP(1);
VIRTPUSH(value2);
VIRTPUSH(value1);
END_OP(Dup2);
}
CASE(DupAt) {
unsigned i = GET_UINT24(pc);
StackVal value = VIRTSP(i);
VIRTPUSH(value);
END_OP(DupAt);
}
CASE(Swap) {
StackVal v0 = VIRTSP(0);
StackVal v1 = VIRTSP(1);
VIRTSPWRITE(0, v1);
VIRTSPWRITE(1, v0);
END_OP(Swap);
}
CASE(Pick) {
unsigned i = GET_UINT8(pc);
SYNCSP();
StackVal tmp = sp[i];
memmove(&sp[1], &sp[0], sizeof(StackVal) * i);
VIRTSPWRITE(0, tmp);
END_OP(Pick);
}
CASE(Unpick) {
unsigned i = GET_UINT8(pc);
StackVal tmp = VIRTSP(0);
SYNCSP();
memmove(&sp[0], &sp[1], sizeof(StackVal) * i);
sp[i] = tmp;
END_OP(Unpick);
}
CASE(DebugCheckSelfHosted) {
HandleValue val = SPHANDLE(0);
{
PUSH_EXIT_FRAME();
if (!Debug_CheckSelfHosted(cx, val)) {
GOTO_ERROR();
}
}
END_OP(DebugCheckSelfHosted);
}
CASE(Lineno) { END_OP(Lineno); }
CASE(NopDestructuring) { END_OP(NopDestructuring); }
CASE(ForceInterpreter) { END_OP(ForceInterpreter); }
CASE(Debugger) {
{
PUSH_EXIT_FRAME();
if (!OnDebuggerStatement(cx, frame)) {
GOTO_ERROR();
}
}
END_OP(Debugger);
}
label_default:
#ifndef ENABLE_COMPUTED_GOTO_DISPATCH
default:
#endif
MOZ_CRASH("Bad opcode");
}
}
restart:
// This is a `goto` target so that we exit any on-stack exit frames
// before restarting, to match previous behavior.
return PortableBaselineInterpret<true, HybridICs>(
ctx.frameMgr.cxForLocalUseOnly(), ctx.state, ctx.stack, sp, envChain, ret,
pc, isd, entryPC, frame, entryFrame, restartCode);
error:
TRACE_PRINTF("HandleException: frame %p\n", frame);
{
ResumeFromException rfe;
{
PUSH_EXIT_FRAME();
HandleException(&rfe);
}
switch (rfe.kind) {
case ExceptionResumeKind::EntryFrame:
TRACE_PRINTF(" -> Return from entry frame\n");
frame->setReturnValue(MagicValue(JS_ION_ERROR));
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
goto unwind_error;
case ExceptionResumeKind::Catch:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
TRACE_PRINTF(" -> catch to pc %p\n", pc);
goto unwind;
case ExceptionResumeKind::Finally:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
sp = reinterpret_cast<StackVal*>(rfe.stackPointer);
TRACE_PRINTF(" -> finally to pc %p\n", pc);
VIRTPUSH(StackVal(rfe.exception));
VIRTPUSH(StackVal(rfe.exceptionStack));
VIRTPUSH(StackVal(BooleanValue(true)));
ctx.stack.unwindingSP = sp;
ctx.stack.unwindingFP = ctx.stack.fp;
goto unwind;
case ExceptionResumeKind::ForcedReturnBaseline:
RESET_PC(frame->interpreterPC(), frame->script());
ctx.stack.fp = reinterpret_cast<StackVal*>(rfe.framePointer);
ctx.stack.unwindingSP = reinterpret_cast<StackVal*>(rfe.stackPointer);
ctx.stack.unwindingFP = reinterpret_cast<StackVal*>(rfe.framePointer);
TRACE_PRINTF(" -> forced return\n");
goto unwind_ret;
case ExceptionResumeKind::ForcedReturnIon:
MOZ_CRASH(
"Unexpected ForcedReturnIon exception-resume kind in Portable "
"Baseline");
case ExceptionResumeKind::Bailout:
MOZ_CRASH(
"Unexpected Bailout exception-resume kind in Portable Baseline");
case ExceptionResumeKind::WasmInterpEntry:
MOZ_CRASH(
"Unexpected WasmInterpEntry exception-resume kind in Portable "
"Baseline");
case ExceptionResumeKind::WasmCatch:
MOZ_CRASH(
"Unexpected WasmCatch exception-resume kind in Portable "
"Baseline");
}
}
DISPATCH();
ic_fail:
RESTART(ic_result);
switch (ic_result) {
case PBIResult::Ok:
MOZ_CRASH("Unreachable: ic_result must be an error if we reach ic_fail");
case PBIResult::Error:
goto error;
case PBIResult::Unwind:
goto unwind;
case PBIResult::UnwindError:
goto unwind_error;
case PBIResult::UnwindRet:
goto unwind_ret;
}
unwind:
TRACE_PRINTF("unwind: fp = %p entryFrame = %p\n", ctx.stack.fp, entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
TRACE_PRINTF(" -> returning\n");
return PBIResult::Unwind;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
DISPATCH();
unwind_error:
TRACE_PRINTF("unwind_error: fp = %p entryFrame = %p\n", ctx.stack.fp,
entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::UnwindError;
}
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) ==
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::Error;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
goto error;
unwind_ret:
TRACE_PRINTF("unwind_ret: fp = %p entryFrame = %p\n", ctx.stack.fp,
entryFrame);
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) >
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
return PBIResult::UnwindRet;
}
if (reinterpret_cast<uintptr_t>(ctx.stack.unwindingFP) ==
reinterpret_cast<uintptr_t>(entryFrame) + BaselineFrame::Size()) {
*ret = frame->returnValue();
return PBIResult::Ok;
}
sp = ctx.stack.unwindingSP;
ctx.stack.fp = ctx.stack.unwindingFP;
frame = reinterpret_cast<BaselineFrame*>(
reinterpret_cast<uintptr_t>(ctx.stack.fp) - BaselineFrame::Size());
TRACE_PRINTF(" -> setting sp to %p, frame to %p\n", sp, frame);
ctx.frameMgr.switchToFrame(frame);
ctx.frame = frame;
RESET_PC(frame->interpreterPC(), frame->script());
from_unwind = true;
goto do_return;
#ifndef __wasi__
debug:;
{
TRACE_PRINTF("hit debug point\n");
PUSH_EXIT_FRAME();
if (!HandleDebugTrap(cx, frame, pc)) {
TRACE_PRINTF("HandleDebugTrap returned error\n");
goto error;
}
RESET_PC(frame->interpreterPC(), frame->script());
TRACE_PRINTF("HandleDebugTrap done\n");
}
goto dispatch;
#endif
}
/*
* -----------------------------------------------
* Entry point
* -----------------------------------------------
*/
bool PortableBaselineTrampoline(JSContext* cx, size_t argc, Value* argv,
size_t numFormals, size_t numActuals,
CalleeToken calleeToken, JSObject* envChain,
Value* result) {
State state(cx);
Stack stack(cx->portableBaselineStack());
StackVal* sp = stack.top;
TRACE_PRINTF("Trampoline: calleeToken %p env %p\n", calleeToken, envChain);
// Expected stack frame:
// - argN
// - ...
// - arg1
// - this
// - calleeToken
// - descriptor
// - "return address" (nullptr for top frame)
// `argc` is the number of args *including* `this` (`N + 1`
// above). `numFormals` is the minimum `N`; if less, we need to push
// `UndefinedValue`s above. We need to pass an argc (including
// `this`) accoundint for the extra undefs in the descriptor's argc.
//
// If constructing, there is an additional `newTarget` at the end.
//
// Note that `callee`, which is in the stack signature for a `Call`
// JSOp, does *not* appear in this count: it is separately passed in
// the `calleeToken`.
bool constructing = CalleeTokenIsConstructing(calleeToken);
size_t numCalleeActuals = std::max(numActuals, numFormals);
size_t numUndefs = numCalleeActuals - numActuals;
// N.B.: we already checked the stack in
// PortableBaselineInterpreterStackCheck; we don't do it here
// because we can't push an exit frame if we don't have an entry
// frame, and we need a full activation to produce the backtrace
// from ReportOverRecursed.
if (constructing) {
PUSH(StackVal(argv[argc]));
}
for (size_t i = 0; i < numUndefs; i++) {
PUSH(StackVal(UndefinedValue()));
}
for (size_t i = 0; i < argc; i++) {
PUSH(StackVal(argv[argc - 1 - i]));
}
PUSHNATIVE(StackValNative(calleeToken));
PUSHNATIVE(StackValNative(
MakeFrameDescriptorForJitCall(FrameType::CppToJSJit, numActuals)));
JSScript* script = ScriptFromCalleeToken(calleeToken);
jsbytecode* pc = script->code();
ImmutableScriptData* isd = script->immutableScriptData();
PBIResult ret;
ret = PortableBaselineInterpret<false, kHybridICsInterp>(
cx, state, stack, sp, envChain, result, pc, isd, nullptr, nullptr,
nullptr, PBIResult::Ok);
switch (ret) {
case PBIResult::Ok:
case PBIResult::UnwindRet:
TRACE_PRINTF("PBI returned Ok/UnwindRet with result %" PRIx64 "\n",
result->asRawBits());
break;
case PBIResult::Error:
case PBIResult::UnwindError:
TRACE_PRINTF("PBI returned Error/UnwindError\n");
return false;
case PBIResult::Unwind:
MOZ_CRASH("Should not unwind out of top / entry frame");
}
return true;
}
MethodStatus CanEnterPortableBaselineInterpreter(JSContext* cx,
RunState& state) {
if (!JitOptions.portableBaselineInterpreter) {
return MethodStatus::Method_CantCompile;
}
if (state.script()->hasJitScript()) {
return MethodStatus::Method_Compiled;
}
if (state.script()->hasForceInterpreterOp()) {
return MethodStatus::Method_CantCompile;
}
if (state.script()->isAsync() || state.script()->isGenerator()) {
return MethodStatus::Method_CantCompile;
}
if (cx->runtime()->geckoProfiler().enabled()) {
return MethodStatus::Method_CantCompile;
}
if (state.isInvoke()) {
InvokeState& invoke = *state.asInvoke();
if (TooManyActualArguments(invoke.args().length())) {
return MethodStatus::Method_CantCompile;
}
} else {
if (state.asExecute()->isDebuggerEval()) {
return MethodStatus::Method_CantCompile;
}
}
if (state.script()->getWarmUpCount() <=
JitOptions.portableBaselineInterpreterWarmUpThreshold) {
return MethodStatus::Method_Skipped;
}
if (!cx->zone()->ensureJitZoneExists(cx)) {
return MethodStatus::Method_Error;
}
AutoKeepJitScripts keepJitScript(cx);
if (!state.script()->ensureHasJitScript(cx, keepJitScript)) {
return MethodStatus::Method_Error;
}
state.script()->updateJitCodeRaw(cx->runtime());
return MethodStatus::Method_Compiled;
}
bool PortablebaselineInterpreterStackCheck(JSContext* cx, RunState& state,
size_t numActualArgs) {
auto& pbs = cx->portableBaselineStack();
StackVal* base = reinterpret_cast<StackVal*>(pbs.base);
StackVal* top = reinterpret_cast<StackVal*>(pbs.top);
ssize_t margin = kStackMargin / sizeof(StackVal);
ssize_t needed = numActualArgs + state.script()->nslots() + margin;
return (top - base) >= needed;
}
} // namespace pbl
} // namespace js