in js/src/vm/PortableBaselineInterpret.cpp [533:5468]
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(ConcatStringsResult);
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(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(ctx.frameMgr.cxForLocalUseOnly(), 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->setLengthToInitializedLength();
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(ConcatStringsResult) {
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->setDenseInitializedLength(len);
aobj->setLengthToInitializedLength();
}
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(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None);
MOZ_ASSERT(!IsFinalizedKind(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();
auto* result = StringToLowerCase(cx, str);
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();
auto* result = StringToUpperCase(cx, str);
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 && obj->is<WrapperObject>()) {
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<CanGC>(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(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);
}