uint64_t ICInterpretOps()

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);
}