tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MacroAssembler.cpp (386905B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "jit/MacroAssembler-inl.h"
      8 
      9 #include "mozilla/FloatingPoint.h"
     10 #include "mozilla/Latin1.h"
     11 #include "mozilla/MathAlgorithms.h"
     12 #include "mozilla/XorShift128PlusRNG.h"
     13 
     14 #include <algorithm>
     15 #include <limits>
     16 #include <utility>
     17 
     18 #include "jit/AtomicOp.h"
     19 #include "jit/AtomicOperations.h"
     20 #include "jit/Bailouts.h"
     21 #include "jit/BaselineFrame.h"
     22 #include "jit/BaselineJIT.h"
     23 #include "jit/JitFrames.h"
     24 #include "jit/JitOptions.h"
     25 #include "jit/JitRuntime.h"
     26 #include "jit/JitScript.h"
     27 #include "jit/MoveEmitter.h"
     28 #include "jit/ReciprocalMulConstants.h"
     29 #include "jit/SharedICHelpers.h"
     30 #include "jit/SharedICRegisters.h"
     31 #include "jit/Simulator.h"
     32 #include "jit/VMFunctions.h"
     33 #include "js/Conversions.h"
     34 #include "js/friend/DOMProxy.h"  // JS::ExpandoAndGeneration
     35 #include "js/GCAPI.h"            // JS::AutoCheckCannotGC
     36 #include "js/ScalarType.h"       // js::Scalar::Type
     37 #include "util/Unicode.h"
     38 #include "vm/ArgumentsObject.h"
     39 #include "vm/ArrayBufferViewObject.h"
     40 #include "vm/BoundFunctionObject.h"
     41 #include "vm/DateObject.h"
     42 #include "vm/DateTime.h"
     43 #include "vm/Float16.h"
     44 #include "vm/FunctionFlags.h"  // js::FunctionFlags
     45 #include "vm/Iteration.h"
     46 #include "vm/JSContext.h"
     47 #include "vm/JSFunction.h"
     48 #include "vm/StringType.h"
     49 #include "vm/TypedArrayObject.h"
     50 #include "wasm/WasmBuiltins.h"
     51 #include "wasm/WasmCodegenConstants.h"
     52 #include "wasm/WasmCodegenTypes.h"
     53 #include "wasm/WasmInstance.h"
     54 #include "wasm/WasmInstanceData.h"
     55 #include "wasm/WasmMemory.h"
     56 #include "wasm/WasmTypeDef.h"
     57 #include "wasm/WasmValidate.h"
     58 
     59 #include "jit/TemplateObject-inl.h"
     60 #include "vm/BytecodeUtil-inl.h"
     61 #include "vm/Interpreter-inl.h"
     62 #include "vm/JSObject-inl.h"
     63 #include "wasm/WasmGcObject-inl.h"
     64 
     65 using namespace js;
     66 using namespace js::jit;
     67 
     68 using JS::GenericNaN;
     69 
     70 using mozilla::CheckedInt;
     71 
     72 TrampolinePtr MacroAssembler::preBarrierTrampoline(MIRType type) {
     73  const JitRuntime* rt = runtime()->jitRuntime();
     74  return rt->preBarrier(type);
     75 }
     76 
     77 template <typename T>
     78 void MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType,
     79                                            FloatRegister value, const T& dest,
     80                                            Register temp,
     81                                            LiveRegisterSet volatileLiveRegs) {
     82  switch (arrayType) {
     83    case Scalar::Float16:
     84      storeFloat16(value, dest, temp, volatileLiveRegs);
     85      break;
     86    case Scalar::Float32: {
     87      if (value.isDouble()) {
     88        ScratchFloat32Scope fpscratch(*this);
     89        convertDoubleToFloat32(value, fpscratch);
     90        storeFloat32(fpscratch, dest);
     91      } else {
     92        MOZ_ASSERT(value.isSingle());
     93        storeFloat32(value, dest);
     94      }
     95      break;
     96    }
     97    case Scalar::Float64:
     98      MOZ_ASSERT(value.isDouble());
     99      storeDouble(value, dest);
    100      break;
    101    default:
    102      MOZ_CRASH("Invalid typed array type");
    103  }
    104 }
    105 
    106 template void MacroAssembler::storeToTypedFloatArray(
    107    Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest,
    108    Register temp, LiveRegisterSet volatileLiveRegs);
    109 template void MacroAssembler::storeToTypedFloatArray(
    110    Scalar::Type arrayType, FloatRegister value, const Address& dest,
    111    Register temp, LiveRegisterSet volatileLiveRegs);
    112 
    113 void MacroAssembler::boxUint32(Register source, ValueOperand dest,
    114                               Uint32Mode mode, Label* fail) {
    115  switch (mode) {
    116    // Fail if the value does not fit in an int32.
    117    case Uint32Mode::FailOnDouble: {
    118      branchTest32(Assembler::Signed, source, source, fail);
    119      tagValue(JSVAL_TYPE_INT32, source, dest);
    120      break;
    121    }
    122    case Uint32Mode::ForceDouble: {
    123      // Always convert the value to double.
    124      ScratchDoubleScope fpscratch(*this);
    125      convertUInt32ToDouble(source, fpscratch);
    126      boxDouble(fpscratch, dest, fpscratch);
    127      break;
    128    }
    129  }
    130 }
    131 
    132 template <typename T>
    133 void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src,
    134                                        AnyRegister dest, Register temp1,
    135                                        Register temp2, Label* fail,
    136                                        LiveRegisterSet volatileLiveRegs) {
    137  switch (arrayType) {
    138    case Scalar::Int8:
    139      load8SignExtend(src, dest.gpr());
    140      break;
    141    case Scalar::Uint8:
    142    case Scalar::Uint8Clamped:
    143      load8ZeroExtend(src, dest.gpr());
    144      break;
    145    case Scalar::Int16:
    146      load16SignExtend(src, dest.gpr());
    147      break;
    148    case Scalar::Uint16:
    149      load16ZeroExtend(src, dest.gpr());
    150      break;
    151    case Scalar::Int32:
    152      load32(src, dest.gpr());
    153      break;
    154    case Scalar::Uint32:
    155      if (dest.isFloat()) {
    156        load32(src, temp1);
    157        convertUInt32ToDouble(temp1, dest.fpu());
    158      } else {
    159        load32(src, dest.gpr());
    160 
    161        // Bail out if the value doesn't fit into a signed int32 value. This
    162        // is what allows MLoadUnboxedScalar to have a type() of
    163        // MIRType::Int32 for UInt32 array loads.
    164        branchTest32(Assembler::Signed, dest.gpr(), dest.gpr(), fail);
    165      }
    166      break;
    167    case Scalar::Float16:
    168      loadFloat16(src, dest.fpu(), temp1, temp2, volatileLiveRegs);
    169      canonicalizeFloat(dest.fpu());
    170      break;
    171    case Scalar::Float32:
    172      loadFloat32(src, dest.fpu());
    173      canonicalizeFloat(dest.fpu());
    174      break;
    175    case Scalar::Float64:
    176      loadDouble(src, dest.fpu());
    177      canonicalizeDouble(dest.fpu());
    178      break;
    179    case Scalar::BigInt64:
    180    case Scalar::BigUint64:
    181    default:
    182      MOZ_CRASH("Invalid typed array type");
    183  }
    184 }
    185 
    186 template void MacroAssembler::loadFromTypedArray(
    187    Scalar::Type arrayType, const Address& src, AnyRegister dest,
    188    Register temp1, Register temp2, Label* fail,
    189    LiveRegisterSet volatileLiveRegs);
    190 template void MacroAssembler::loadFromTypedArray(
    191    Scalar::Type arrayType, const BaseIndex& src, AnyRegister dest,
    192    Register temp1, Register temp2, Label* fail,
    193    LiveRegisterSet volatileLiveRegs);
    194 
    195 void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType,
    196                                        const BaseIndex& src,
    197                                        const ValueOperand& dest,
    198                                        Uint32Mode uint32Mode, Register temp,
    199                                        Label* fail,
    200                                        LiveRegisterSet volatileLiveRegs) {
    201  switch (arrayType) {
    202    case Scalar::Int8:
    203    case Scalar::Uint8:
    204    case Scalar::Uint8Clamped:
    205    case Scalar::Int16:
    206    case Scalar::Uint16:
    207    case Scalar::Int32:
    208      loadFromTypedArray(arrayType, src, AnyRegister(dest.scratchReg()),
    209                         InvalidReg, InvalidReg, nullptr, LiveRegisterSet{});
    210      tagValue(JSVAL_TYPE_INT32, dest.scratchReg(), dest);
    211      break;
    212    case Scalar::Uint32:
    213      load32(src, dest.scratchReg());
    214      boxUint32(dest.scratchReg(), dest, uint32Mode, fail);
    215      break;
    216    case Scalar::Float16: {
    217      ScratchDoubleScope dscratch(*this);
    218      FloatRegister fscratch = dscratch.asSingle();
    219      loadFromTypedArray(arrayType, src, AnyRegister(fscratch),
    220                         dest.scratchReg(), temp, nullptr, volatileLiveRegs);
    221      convertFloat32ToDouble(fscratch, dscratch);
    222      boxDouble(dscratch, dest, dscratch);
    223      break;
    224    }
    225    case Scalar::Float32: {
    226      ScratchDoubleScope dscratch(*this);
    227      FloatRegister fscratch = dscratch.asSingle();
    228      loadFromTypedArray(arrayType, src, AnyRegister(fscratch), InvalidReg,
    229                         InvalidReg, nullptr, LiveRegisterSet{});
    230      convertFloat32ToDouble(fscratch, dscratch);
    231      boxDouble(dscratch, dest, dscratch);
    232      break;
    233    }
    234    case Scalar::Float64: {
    235      ScratchDoubleScope fpscratch(*this);
    236      loadFromTypedArray(arrayType, src, AnyRegister(fpscratch), InvalidReg,
    237                         InvalidReg, nullptr, LiveRegisterSet{});
    238      boxDouble(fpscratch, dest, fpscratch);
    239      break;
    240    }
    241    case Scalar::BigInt64:
    242    case Scalar::BigUint64:
    243    default:
    244      MOZ_CRASH("Invalid typed array type");
    245  }
    246 }
    247 
    248 void MacroAssembler::loadFromTypedBigIntArray(Scalar::Type arrayType,
    249                                              const BaseIndex& src,
    250                                              const ValueOperand& dest,
    251                                              Register bigInt,
    252                                              Register64 temp) {
    253  MOZ_ASSERT(Scalar::isBigIntType(arrayType));
    254 
    255  load64(src, temp);
    256  initializeBigInt64(arrayType, bigInt, temp);
    257  tagValue(JSVAL_TYPE_BIGINT, bigInt, dest);
    258 }
    259 
    260 // Inlined version of gc::CheckAllocatorState that checks the bare essentials
    261 // and bails for anything that cannot be handled with our jit allocators.
    262 void MacroAssembler::checkAllocatorState(Register temp, gc::AllocKind allocKind,
    263                                         Label* fail) {
    264  // Don't execute the inline path if GC probes are built in.
    265 #ifdef JS_GC_PROBES
    266  jump(fail);
    267 #endif
    268 
    269 #ifdef JS_GC_ZEAL
    270  // Don't execute the inline path if gc zeal or tracing are active.
    271  const uint32_t* ptrZealModeBits = runtime()->addressOfGCZealModeBits();
    272  branch32(Assembler::NotEqual, AbsoluteAddress(ptrZealModeBits), Imm32(0),
    273           fail);
    274 #endif
    275 
    276  // If the zone has a realm with an object allocation metadata hook, emit a
    277  // guard for this. Note that IC stubs and some other trampolines can be shared
    278  // across realms, so we don't bake in a realm pointer.
    279  if (gc::IsObjectAllocKind(allocKind) &&
    280      realm()->zone()->hasRealmWithAllocMetadataBuilder()) {
    281    loadJSContext(temp);
    282    loadPtr(Address(temp, JSContext::offsetOfRealm()), temp);
    283    branchPtr(Assembler::NotEqual,
    284              Address(temp, Realm::offsetOfAllocationMetadataBuilder()),
    285              ImmWord(0), fail);
    286  }
    287 }
    288 
    289 bool MacroAssembler::shouldNurseryAllocate(gc::AllocKind allocKind,
    290                                           gc::Heap initialHeap) {
    291  // Note that Ion elides barriers on writes to objects known to be in the
    292  // nursery, so any allocation that can be made into the nursery must be made
    293  // into the nursery, even if the nursery is disabled. At runtime these will
    294  // take the out-of-line path, which is required to insert a barrier for the
    295  // initializing writes.
    296  return IsNurseryAllocable(allocKind) && initialHeap != gc::Heap::Tenured;
    297 }
    298 
    299 // Inline version of Nursery::allocateObject. If the object has dynamic slots,
    300 // this fills in the slots_ pointer.
    301 void MacroAssembler::nurseryAllocateObject(Register result, Register temp,
    302                                           gc::AllocKind allocKind,
    303                                           size_t nDynamicSlots, Label* fail,
    304                                           const AllocSiteInput& allocSite) {
    305  MOZ_ASSERT(IsNurseryAllocable(allocKind));
    306 
    307  // Currently the JIT does not nursery allocate foreground finalized
    308  // objects. This is allowed for objects that support this and have the
    309  // JSCLASS_SKIP_NURSERY_FINALIZE class flag set. It's hard to assert that here
    310  // though so disallow all foreground finalized objects for now.
    311  MOZ_ASSERT(!IsForegroundFinalized(allocKind));
    312 
    313  // We still need to allocate in the nursery, per the comment in
    314  // shouldNurseryAllocate; however, we need to insert into the
    315  // mallocedBuffers set, so bail to do the nursery allocation in the
    316  // interpreter.
    317  if (nDynamicSlots >= Nursery::MaxNurseryBufferSize / sizeof(Value)) {
    318    jump(fail);
    319    return;
    320  }
    321 
    322  // Check whether this allocation site needs pretenuring. This dynamic check
    323  // only happens for baseline code.
    324  if (allocSite.is<Register>()) {
    325    Register site = allocSite.as<Register>();
    326    branchTestPtr(Assembler::NonZero,
    327                  Address(site, gc::AllocSite::offsetOfScriptAndState()),
    328                  Imm32(gc::AllocSite::LONG_LIVED_BIT), fail);
    329  }
    330 
    331  // No explicit check for nursery.isEnabled() is needed, as the comparison
    332  // with the nursery's end will always fail in such cases.
    333  CompileZone* zone = realm()->zone();
    334  size_t thingSize = gc::Arena::thingSize(allocKind);
    335  size_t totalSize = thingSize;
    336  if (nDynamicSlots) {
    337    totalSize += ObjectSlots::allocSize(nDynamicSlots);
    338  }
    339  MOZ_ASSERT(totalSize < INT32_MAX);
    340  MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0);
    341 
    342  bumpPointerAllocate(result, temp, fail, zone, JS::TraceKind::Object,
    343                      totalSize, allocSite);
    344 
    345  if (nDynamicSlots) {
    346    store32(Imm32(nDynamicSlots),
    347            Address(result, thingSize + ObjectSlots::offsetOfCapacity()));
    348    store32(
    349        Imm32(0),
    350        Address(result, thingSize + ObjectSlots::offsetOfDictionarySlotSpan()));
    351    store64(Imm64(ObjectSlots::NoUniqueIdInDynamicSlots),
    352            Address(result, thingSize + ObjectSlots::offsetOfMaybeUniqueId()));
    353    computeEffectiveAddress(
    354        Address(result, thingSize + ObjectSlots::offsetOfSlots()), temp);
    355    storePtr(temp, Address(result, NativeObject::offsetOfSlots()));
    356  }
    357 }
    358 
    359 // Inlined version of FreeSpan::allocate. This does not fill in slots_.
    360 void MacroAssembler::freeListAllocate(Register result, Register temp,
    361                                      gc::AllocKind allocKind, Label* fail) {
    362  CompileZone* zone = realm()->zone();
    363  int thingSize = int(gc::Arena::thingSize(allocKind));
    364 
    365  Label fallback;
    366  Label success;
    367 
    368  // Load the first and last offsets of |zone|'s free list for |allocKind|.
    369  // If there is no room remaining in the span, fall back to get the next one.
    370  gc::FreeSpan** ptrFreeList = zone->addressOfFreeList(allocKind);
    371  loadPtr(AbsoluteAddress(ptrFreeList), temp);
    372  load16ZeroExtend(Address(temp, js::gc::FreeSpan::offsetOfFirst()), result);
    373  load16ZeroExtend(Address(temp, js::gc::FreeSpan::offsetOfLast()), temp);
    374  branch32(Assembler::AboveOrEqual, result, temp, &fallback);
    375 
    376  // Bump the offset for the next allocation.
    377  add32(Imm32(thingSize), result);
    378  loadPtr(AbsoluteAddress(ptrFreeList), temp);
    379  store16(result, Address(temp, js::gc::FreeSpan::offsetOfFirst()));
    380  sub32(Imm32(thingSize), result);
    381  addPtr(temp, result);  // Turn the offset into a pointer.
    382  jump(&success);
    383 
    384  bind(&fallback);
    385  // If there are no free spans left, we bail to finish the allocation. The
    386  // interpreter will call the GC allocator to set up a new arena to allocate
    387  // from, after which we can resume allocating in the jit.
    388  branchTest32(Assembler::Zero, result, result, fail);
    389  loadPtr(AbsoluteAddress(ptrFreeList), temp);
    390  addPtr(temp, result);  // Turn the offset into a pointer.
    391  Push(result);
    392  // Update the free list to point to the next span (which may be empty).
    393  load32(Address(result, 0), result);
    394  store32(result, Address(temp, js::gc::FreeSpan::offsetOfFirst()));
    395  Pop(result);
    396 
    397  bind(&success);
    398 
    399  if (runtime()->geckoProfiler().enabled()) {
    400    uint32_t* countAddress = zone->addressOfTenuredAllocCount();
    401    movePtr(ImmPtr(countAddress), temp);
    402    add32(Imm32(1), Address(temp, 0));
    403  }
    404 }
    405 
    406 // Inlined equivalent of gc::AllocateObject, without failure case handling.
    407 void MacroAssembler::allocateObject(Register result, Register temp,
    408                                    gc::AllocKind allocKind,
    409                                    uint32_t nDynamicSlots,
    410                                    gc::Heap initialHeap, Label* fail,
    411                                    const AllocSiteInput& allocSite) {
    412  MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
    413 
    414  checkAllocatorState(temp, allocKind, fail);
    415 
    416  if (shouldNurseryAllocate(allocKind, initialHeap)) {
    417    MOZ_ASSERT(initialHeap == gc::Heap::Default);
    418    return nurseryAllocateObject(result, temp, allocKind, nDynamicSlots, fail,
    419                                 allocSite);
    420  }
    421 
    422  // Fall back to calling into the VM to allocate objects in the tenured heap
    423  // that have dynamic slots.
    424  if (nDynamicSlots) {
    425    jump(fail);
    426    return;
    427  }
    428 
    429  return freeListAllocate(result, temp, allocKind, fail);
    430 }
    431 
    432 void MacroAssembler::createGCObject(Register obj, Register temp,
    433                                    const TemplateObject& templateObj,
    434                                    gc::Heap initialHeap, Label* fail,
    435                                    bool initContents /* = true */,
    436                                    const AllocSiteInput& allocSite) {
    437  gc::AllocKind allocKind = templateObj.getAllocKind();
    438  MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
    439 
    440  uint32_t nDynamicSlots = 0;
    441  if (templateObj.isNativeObject()) {
    442    const TemplateNativeObject& ntemplate =
    443        templateObj.asTemplateNativeObject();
    444    nDynamicSlots = ntemplate.numDynamicSlots();
    445  }
    446 
    447  allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail,
    448                 allocSite);
    449  initGCThing(obj, temp, templateObj, initContents);
    450 }
    451 
    452 void MacroAssembler::createPlainGCObject(
    453    Register result, Register shape, Register temp, Register temp2,
    454    uint32_t numFixedSlots, uint32_t numDynamicSlots, gc::AllocKind allocKind,
    455    gc::Heap initialHeap, Label* fail, const AllocSiteInput& allocSite,
    456    bool initContents /* = true */) {
    457  MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
    458  MOZ_ASSERT(shape != temp, "shape can overlap with temp2, but not temp");
    459 
    460  // Allocate object.
    461  allocateObject(result, temp, allocKind, numDynamicSlots, initialHeap, fail,
    462                 allocSite);
    463 
    464  // Initialize shape field.
    465  storePtr(shape, Address(result, JSObject::offsetOfShape()));
    466 
    467  // If the object has dynamic slots, allocateObject will initialize
    468  // the slots field. If not, we must initialize it now.
    469  if (numDynamicSlots == 0) {
    470    storePtr(ImmPtr(emptyObjectSlots),
    471             Address(result, NativeObject::offsetOfSlots()));
    472  }
    473 
    474  // Initialize elements field.
    475  storePtr(ImmPtr(emptyObjectElements),
    476           Address(result, NativeObject::offsetOfElements()));
    477 
    478  // Initialize fixed slots.
    479  if (initContents) {
    480    fillSlotsWithUndefined(Address(result, NativeObject::getFixedSlotOffset(0)),
    481                           temp, 0, numFixedSlots);
    482  }
    483 
    484  // Initialize dynamic slots.
    485  if (numDynamicSlots > 0) {
    486    loadPtr(Address(result, NativeObject::offsetOfSlots()), temp2);
    487    fillSlotsWithUndefined(Address(temp2, 0), temp, 0, numDynamicSlots);
    488  }
    489 }
    490 
    491 void MacroAssembler::createArrayWithFixedElements(
    492    Register result, Register shape, Register temp, Register dynamicSlotsTemp,
    493    uint32_t arrayLength, uint32_t arrayCapacity, uint32_t numUsedDynamicSlots,
    494    uint32_t numDynamicSlots, gc::AllocKind allocKind, gc::Heap initialHeap,
    495    Label* fail, const AllocSiteInput& allocSite) {
    496  MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
    497  MOZ_ASSERT(shape != temp, "shape can overlap with temp2, but not temp");
    498  MOZ_ASSERT(result != temp);
    499 
    500  // This only supports allocating arrays with fixed elements and does not
    501  // support any dynamic elements.
    502  MOZ_ASSERT(arrayCapacity >= arrayLength);
    503  MOZ_ASSERT(gc::GetGCKindSlots(allocKind) >=
    504             arrayCapacity + ObjectElements::VALUES_PER_HEADER);
    505 
    506  MOZ_ASSERT(numUsedDynamicSlots <= numDynamicSlots);
    507 
    508  // Allocate object.
    509  allocateObject(result, temp, allocKind, numDynamicSlots, initialHeap, fail,
    510                 allocSite);
    511 
    512  // Initialize shape field.
    513  storePtr(shape, Address(result, JSObject::offsetOfShape()));
    514 
    515  // If the object has dynamic slots, allocateObject will initialize
    516  // the slots field. If not, we must initialize it now.
    517  if (numDynamicSlots == 0) {
    518    storePtr(ImmPtr(emptyObjectSlots),
    519             Address(result, NativeObject::offsetOfSlots()));
    520  }
    521 
    522  // Initialize elements pointer for fixed (inline) elements.
    523  computeEffectiveAddress(
    524      Address(result, NativeObject::offsetOfFixedElements()), temp);
    525  storePtr(temp, Address(result, NativeObject::offsetOfElements()));
    526 
    527  // Initialize elements header.
    528  store32(Imm32(ObjectElements::FIXED),
    529          Address(temp, ObjectElements::offsetOfFlags()));
    530  store32(Imm32(0), Address(temp, ObjectElements::offsetOfInitializedLength()));
    531  store32(Imm32(arrayCapacity),
    532          Address(temp, ObjectElements::offsetOfCapacity()));
    533  store32(Imm32(arrayLength), Address(temp, ObjectElements::offsetOfLength()));
    534 
    535  // Initialize dynamic slots.
    536  if (numUsedDynamicSlots > 0) {
    537    MOZ_ASSERT(dynamicSlotsTemp != temp);
    538    MOZ_ASSERT(dynamicSlotsTemp != InvalidReg);
    539    loadPtr(Address(result, NativeObject::offsetOfSlots()), dynamicSlotsTemp);
    540    fillSlotsWithUndefined(Address(dynamicSlotsTemp, 0), temp, 0,
    541                           numUsedDynamicSlots);
    542  }
    543 }
    544 
    545 void MacroAssembler::createFunctionClone(Register result, Register canonical,
    546                                         Register envChain, Register temp,
    547                                         gc::AllocKind allocKind, Label* fail,
    548                                         const AllocSiteInput& allocSite) {
    549  MOZ_ASSERT(allocKind == gc::AllocKind::FUNCTION ||
    550             allocKind == gc::AllocKind::FUNCTION_EXTENDED);
    551  MOZ_ASSERT(result != temp);
    552 
    553  // Allocate object.
    554  size_t numDynamicSlots = 0;
    555  gc::Heap initialHeap = gc::Heap::Default;
    556  allocateObject(result, temp, allocKind, numDynamicSlots, initialHeap, fail,
    557                 allocSite);
    558 
    559  // Initialize shape field.
    560  loadPtr(Address(canonical, JSObject::offsetOfShape()), temp);
    561  storePtr(temp, Address(result, JSObject::offsetOfShape()));
    562 
    563  // Initialize dynamic slots and elements pointers.
    564  storePtr(ImmPtr(emptyObjectSlots),
    565           Address(result, NativeObject::offsetOfSlots()));
    566  storePtr(ImmPtr(emptyObjectElements),
    567           Address(result, NativeObject::offsetOfElements()));
    568 
    569  // Initialize FlagsAndArgCountSlot.
    570  storeValue(Address(canonical, JSFunction::offsetOfFlagsAndArgCount()),
    571             Address(result, JSFunction::offsetOfFlagsAndArgCount()), temp);
    572 
    573  // Initialize NativeFuncOrInterpretedEnvSlot.
    574  storeValue(JSVAL_TYPE_OBJECT, envChain,
    575             Address(result, JSFunction::offsetOfEnvironment()));
    576 
    577 #ifdef DEBUG
    578  // The new function must be allocated in the nursery if the nursery is
    579  // enabled. Assert no post-barrier is needed.
    580  Label ok;
    581  branchPtrInNurseryChunk(Assembler::Equal, result, temp, &ok);
    582  branchPtrInNurseryChunk(Assembler::NotEqual, envChain, temp, &ok);
    583  assumeUnreachable("Missing post write barrier in createFunctionClone");
    584  bind(&ok);
    585 #endif
    586 
    587  // Initialize NativeJitInfoOrInterpretedScriptSlot. This is a BaseScript*
    588  // pointer stored as PrivateValue.
    589  loadPrivate(Address(canonical, JSFunction::offsetOfJitInfoOrScript()), temp);
    590  storePrivateValue(temp,
    591                    Address(result, JSFunction::offsetOfJitInfoOrScript()));
    592 
    593  // Initialize AtomSlot.
    594  storeValue(Address(canonical, JSFunction::offsetOfAtom()),
    595             Address(result, JSFunction::offsetOfAtom()), temp);
    596 
    597  // Initialize extended slots.
    598  if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
    599    for (size_t i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++) {
    600      Address addr(result, FunctionExtended::offsetOfExtendedSlot(i));
    601      storeValue(UndefinedValue(), addr);
    602    }
    603  }
    604 }
    605 
    606 // Inline version of Nursery::allocateString.
    607 void MacroAssembler::nurseryAllocateString(Register result, Register temp,
    608                                           gc::AllocKind allocKind,
    609                                           Label* fail) {
    610  MOZ_ASSERT(IsNurseryAllocable(allocKind));
    611 
    612  // No explicit check for nursery.isEnabled() is needed, as the comparison
    613  // with the nursery's end will always fail in such cases.
    614 
    615  CompileZone* zone = realm()->zone();
    616  size_t thingSize = gc::Arena::thingSize(allocKind);
    617  bumpPointerAllocate(result, temp, fail, zone, JS::TraceKind::String,
    618                      thingSize);
    619 }
    620 
    621 // Inline version of Nursery::allocateBigInt.
    622 void MacroAssembler::nurseryAllocateBigInt(Register result, Register temp,
    623                                           Label* fail) {
    624  MOZ_ASSERT(IsNurseryAllocable(gc::AllocKind::BIGINT));
    625 
    626  // No explicit check for nursery.isEnabled() is needed, as the comparison
    627  // with the nursery's end will always fail in such cases.
    628 
    629  CompileZone* zone = realm()->zone();
    630  size_t thingSize = gc::Arena::thingSize(gc::AllocKind::BIGINT);
    631 
    632  bumpPointerAllocate(result, temp, fail, zone, JS::TraceKind::BigInt,
    633                      thingSize);
    634 }
    635 
    636 static bool IsNurseryAllocEnabled(CompileZone* zone, JS::TraceKind kind) {
    637  switch (kind) {
    638    case JS::TraceKind::Object:
    639      return zone->allocNurseryObjects();
    640    case JS::TraceKind::String:
    641      return zone->allocNurseryStrings();
    642    case JS::TraceKind::BigInt:
    643      return zone->allocNurseryBigInts();
    644    default:
    645      MOZ_CRASH("Bad nursery allocation kind");
    646  }
    647 }
    648 
    649 // This function handles nursery allocations for JS. For wasm, see
    650 // MacroAssembler::wasmBumpPointerAllocate.
    651 void MacroAssembler::bumpPointerAllocate(Register result, Register temp,
    652                                         Label* fail, CompileZone* zone,
    653                                         JS::TraceKind traceKind, uint32_t size,
    654                                         const AllocSiteInput& allocSite) {
    655  MOZ_ASSERT(size >= gc::MinCellSize);
    656 
    657  uint32_t totalSize = size + Nursery::nurseryCellHeaderSize();
    658  MOZ_ASSERT(totalSize < INT32_MAX, "Nursery allocation too large");
    659  MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0);
    660 
    661  // We know statically whether nursery allocation is enable for a particular
    662  // kind because we discard JIT code when this changes.
    663  if (!IsNurseryAllocEnabled(zone, traceKind)) {
    664    jump(fail);
    665    return;
    666  }
    667 
    668  // Use a relative 32 bit offset to the Nursery position_ to currentEnd_ to
    669  // avoid 64-bit immediate loads.
    670  void* posAddr = zone->addressOfNurseryPosition();
    671  int32_t endOffset = Nursery::offsetOfCurrentEndFromPosition();
    672 
    673  movePtr(ImmPtr(posAddr), temp);
    674  loadPtr(Address(temp, 0), result);
    675  addPtr(Imm32(totalSize), result);
    676  branchPtr(Assembler::Below, Address(temp, endOffset), result, fail);
    677  storePtr(result, Address(temp, 0));
    678  subPtr(Imm32(size), result);
    679 
    680  if (allocSite.is<gc::CatchAllAllocSite>()) {
    681    // No allocation site supplied. This is the case when called from Warp, or
    682    // from places that don't support pretenuring.
    683    gc::CatchAllAllocSite siteKind = allocSite.as<gc::CatchAllAllocSite>();
    684    gc::AllocSite* site = zone->catchAllAllocSite(traceKind, siteKind);
    685    uintptr_t headerWord = gc::NurseryCellHeader::MakeValue(site, traceKind);
    686    storePtr(ImmWord(headerWord),
    687             Address(result, -js::Nursery::nurseryCellHeaderSize()));
    688 
    689    if (traceKind != JS::TraceKind::Object ||
    690        runtime()->geckoProfiler().enabled()) {
    691      // Update the catch all allocation site, which his is used to calculate
    692      // nursery allocation counts so we can determine whether to disable
    693      // nursery allocation of strings and bigints.
    694      uint32_t* countAddress = site->nurseryAllocCountAddress();
    695      CheckedInt<int32_t> counterOffset =
    696          (CheckedInt<uintptr_t>(uintptr_t(countAddress)) -
    697           CheckedInt<uintptr_t>(uintptr_t(posAddr)))
    698              .toChecked<int32_t>();
    699      if (counterOffset.isValid()) {
    700        add32(Imm32(1), Address(temp, counterOffset.value()));
    701      } else {
    702        movePtr(ImmPtr(countAddress), temp);
    703        add32(Imm32(1), Address(temp, 0));
    704      }
    705    }
    706  } else {
    707    // Update allocation site and store pointer in the nursery cell header. This
    708    // is only used from baseline.
    709    Register site = allocSite.as<Register>();
    710    updateAllocSite(temp, result, zone, site);
    711    // See NurseryCellHeader::MakeValue.
    712    orPtr(Imm32(int32_t(traceKind)), site);
    713    storePtr(site, Address(result, -js::Nursery::nurseryCellHeaderSize()));
    714  }
    715 }
    716 
    717 // Update the allocation site in the same way as Nursery::allocateCell.
    718 void MacroAssembler::updateAllocSite(Register temp, Register result,
    719                                     CompileZone* zone, Register site) {
    720  Label done;
    721 
    722  add32(Imm32(1), Address(site, gc::AllocSite::offsetOfNurseryAllocCount()));
    723 
    724  branch32(Assembler::NotEqual,
    725           Address(site, gc::AllocSite::offsetOfNurseryAllocCount()),
    726           Imm32(js::gc::NormalSiteAttentionThreshold), &done);
    727 
    728  loadPtr(AbsoluteAddress(zone->addressOfNurseryAllocatedSites()), temp);
    729  storePtr(temp, Address(site, gc::AllocSite::offsetOfNextNurseryAllocated()));
    730  storePtr(site, AbsoluteAddress(zone->addressOfNurseryAllocatedSites()));
    731 
    732  bind(&done);
    733 }
    734 
    735 // Inlined equivalent of gc::AllocateString, jumping to fail if nursery
    736 // allocation requested but unsuccessful.
    737 void MacroAssembler::allocateString(Register result, Register temp,
    738                                    gc::AllocKind allocKind,
    739                                    gc::Heap initialHeap, Label* fail) {
    740  MOZ_ASSERT(allocKind == gc::AllocKind::STRING ||
    741             allocKind == gc::AllocKind::FAT_INLINE_STRING);
    742 
    743  checkAllocatorState(temp, allocKind, fail);
    744 
    745  if (shouldNurseryAllocate(allocKind, initialHeap)) {
    746    MOZ_ASSERT(initialHeap == gc::Heap::Default);
    747    return nurseryAllocateString(result, temp, allocKind, fail);
    748  }
    749 
    750  freeListAllocate(result, temp, allocKind, fail);
    751 }
    752 
    753 void MacroAssembler::newGCString(Register result, Register temp,
    754                                 gc::Heap initialHeap, Label* fail) {
    755  allocateString(result, temp, js::gc::AllocKind::STRING, initialHeap, fail);
    756 }
    757 
    758 void MacroAssembler::newGCFatInlineString(Register result, Register temp,
    759                                          gc::Heap initialHeap, Label* fail) {
    760  allocateString(result, temp, js::gc::AllocKind::FAT_INLINE_STRING,
    761                 initialHeap, fail);
    762 }
    763 
    764 void MacroAssembler::newGCBigInt(Register result, Register temp,
    765                                 gc::Heap initialHeap, Label* fail) {
    766  constexpr gc::AllocKind allocKind = gc::AllocKind::BIGINT;
    767 
    768  checkAllocatorState(temp, allocKind, fail);
    769 
    770  if (shouldNurseryAllocate(allocKind, initialHeap)) {
    771    MOZ_ASSERT(initialHeap == gc::Heap::Default);
    772    return nurseryAllocateBigInt(result, temp, fail);
    773  }
    774 
    775  freeListAllocate(result, temp, allocKind, fail);
    776 }
    777 
    778 void MacroAssembler::preserveWrapper(Register wrapper, Register scratchSuccess,
    779                                     Register scratch2,
    780                                     const LiveRegisterSet& liveRegs) {
    781  Label done, abiCall;
    782  CompileZone* zone = realm()->zone();
    783 
    784  loadPtr(AbsoluteAddress(zone->zone()->addressOfPreservedWrappersCount()),
    785          scratchSuccess);
    786  branchPtr(Assembler::Equal,
    787            AbsoluteAddress(zone->zone()->addressOfPreservedWrappersCapacity()),
    788            scratchSuccess, &abiCall);
    789  loadPtr(AbsoluteAddress(zone->zone()->addressOfPreservedWrappers()),
    790          scratch2);
    791 
    792  storePtr(wrapper, BaseIndex(scratch2, scratchSuccess, ScalePointer));
    793  addPtr(Imm32(1), scratchSuccess);
    794  storePtr(scratchSuccess,
    795           AbsoluteAddress(zone->zone()->addressOfPreservedWrappersCount()));
    796  move32(Imm32(1), scratchSuccess);
    797 
    798  jump(&done);
    799  bind(&abiCall);
    800  LiveRegisterSet save;
    801  save.set() = RegisterSet::Intersect(liveRegs.set(), RegisterSet::Volatile());
    802  PushRegsInMask(save);
    803 
    804  using Fn = bool (*)(JSContext* cx, JSObject* wrapper);
    805  setupUnalignedABICall(scratch2);
    806  loadJSContext(scratch2);
    807  passABIArg(scratch2);
    808  passABIArg(wrapper);
    809  callWithABI<Fn, js::jit::PreserveWrapper>();
    810  storeCallBoolResult(scratchSuccess);
    811 
    812  MOZ_ASSERT(!save.has(scratchSuccess));
    813  PopRegsInMask(save);
    814  bind(&done);
    815 }
    816 
    817 void MacroAssembler::copySlotsFromTemplate(
    818    Register obj, const TemplateNativeObject& templateObj, uint32_t start,
    819    uint32_t end) {
    820  uint32_t nfixed = std::min(templateObj.numFixedSlots(), end);
    821  for (unsigned i = start; i < nfixed; i++) {
    822    // Template objects are not exposed to script and therefore immutable.
    823    // However, regexp template objects are sometimes used directly (when
    824    // the cloning is not observable), and therefore we can end up with a
    825    // non-zero lastIndex. Detect this case here and just substitute 0, to
    826    // avoid racing with the main thread updating this slot.
    827    Value v;
    828    if (templateObj.isRegExpObject() && i == RegExpObject::lastIndexSlot()) {
    829      v = Int32Value(0);
    830    } else {
    831      v = templateObj.getSlot(i);
    832    }
    833    storeValue(v, Address(obj, NativeObject::getFixedSlotOffset(i)));
    834  }
    835 }
    836 
    837 void MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp,
    838                                                uint32_t start, uint32_t end,
    839                                                const Value& v) {
    840  MOZ_ASSERT(v.isUndefined() || IsUninitializedLexical(v));
    841 
    842  if (start >= end) {
    843    return;
    844  }
    845 
    846 #ifdef JS_NUNBOX32
    847  // We only have a single spare register, so do the initialization as two
    848  // strided writes of the tag and body.
    849  Address addr = base;
    850  move32(Imm32(v.toNunboxPayload()), temp);
    851  for (unsigned i = start; i < end; ++i, addr.offset += sizeof(GCPtr<Value>)) {
    852    store32(temp, ToPayload(addr));
    853  }
    854 
    855  addr = base;
    856  move32(Imm32(v.toNunboxTag()), temp);
    857  for (unsigned i = start; i < end; ++i, addr.offset += sizeof(GCPtr<Value>)) {
    858    store32(temp, ToType(addr));
    859  }
    860 #else
    861  moveValue(v, ValueOperand(temp));
    862  for (uint32_t i = start; i < end; ++i, base.offset += sizeof(GCPtr<Value>)) {
    863    storePtr(temp, base);
    864  }
    865 #endif
    866 }
    867 
    868 void MacroAssembler::fillSlotsWithUndefined(Address base, Register temp,
    869                                            uint32_t start, uint32_t end) {
    870  fillSlotsWithConstantValue(base, temp, start, end, UndefinedValue());
    871 }
    872 
    873 void MacroAssembler::fillSlotsWithUninitialized(Address base, Register temp,
    874                                                uint32_t start, uint32_t end) {
    875  fillSlotsWithConstantValue(base, temp, start, end,
    876                             MagicValue(JS_UNINITIALIZED_LEXICAL));
    877 }
    878 
    879 static std::pair<uint32_t, uint32_t> FindStartOfUninitializedAndUndefinedSlots(
    880    const TemplateNativeObject& templateObj, uint32_t nslots) {
    881  MOZ_ASSERT(nslots == templateObj.slotSpan());
    882  MOZ_ASSERT(nslots > 0);
    883 
    884  uint32_t first = nslots;
    885  for (; first != 0; --first) {
    886    if (templateObj.getSlot(first - 1) != UndefinedValue()) {
    887      break;
    888    }
    889  }
    890  uint32_t startOfUndefined = first;
    891 
    892  if (first != 0 && IsUninitializedLexical(templateObj.getSlot(first - 1))) {
    893    for (; first != 0; --first) {
    894      if (!IsUninitializedLexical(templateObj.getSlot(first - 1))) {
    895        break;
    896      }
    897    }
    898  }
    899  uint32_t startOfUninitialized = first;
    900 
    901  return {startOfUninitialized, startOfUndefined};
    902 }
    903 
    904 void MacroAssembler::initTypedArraySlots(
    905    Register obj, Register length, Register temp1, Register temp2, Label* fail,
    906    const FixedLengthTypedArrayObject* templateObj) {
    907  MOZ_ASSERT(!templateObj->hasBuffer());
    908  MOZ_ASSERT(obj != length);
    909  MOZ_ASSERT(obj != temp1);
    910  MOZ_ASSERT(obj != temp2);
    911  MOZ_ASSERT(length != temp1);
    912  MOZ_ASSERT(length != temp2);
    913  MOZ_ASSERT(temp1 != temp2);
    914 
    915  constexpr size_t dataSlotOffset = ArrayBufferViewObject::dataOffset();
    916  constexpr size_t dataOffset = dataSlotOffset + sizeof(HeapSlot);
    917 
    918  static_assert(
    919      FixedLengthTypedArrayObject::FIXED_DATA_START ==
    920          FixedLengthTypedArrayObject::DATA_SLOT + 1,
    921      "fixed inline element data assumed to begin after the data slot");
    922 
    923  static_assert(
    924      FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT ==
    925          JSObject::MAX_BYTE_SIZE - dataOffset,
    926      "typed array inline buffer is limited by the maximum object byte size");
    927 
    928  MOZ_ASSERT(templateObj->tenuredSizeOfThis() >= dataOffset);
    929 
    930  // Capacity for inline elements.
    931  size_t inlineCapacity = templateObj->tenuredSizeOfThis() - dataOffset;
    932  movePtr(ImmWord(inlineCapacity), temp2);
    933 
    934  // Ensure volatile |obj| and |length| are saved across the call.
    935  if (obj.volatile_()) {
    936    Push(obj);
    937  }
    938  if (length.volatile_()) {
    939    Push(length);
    940  }
    941  // Allocate a buffer on the heap to store the data elements.
    942  using Fn =
    943      void (*)(JSContext*, FixedLengthTypedArrayObject*, int32_t, size_t);
    944  setupUnalignedABICall(temp1);
    945  loadJSContext(temp1);
    946  passABIArg(temp1);
    947  passABIArg(obj);
    948  passABIArg(length);
    949  passABIArg(temp2);
    950  callWithABI<Fn, AllocateAndInitTypedArrayBuffer>();
    951  if (length.volatile_()) {
    952    Pop(length);
    953  }
    954  if (obj.volatile_()) {
    955    Pop(obj);
    956  }
    957 
    958  // Fail when data slot is UndefinedValue.
    959  branchTestUndefined(Assembler::Equal, Address(obj, dataSlotOffset), fail);
    960 }
    961 
    962 void MacroAssembler::initTypedArraySlotsInline(
    963    Register obj, Register temp,
    964    const FixedLengthTypedArrayObject* templateObj) {
    965  MOZ_ASSERT(!templateObj->hasBuffer());
    966 
    967  constexpr size_t dataSlotOffset = ArrayBufferViewObject::dataOffset();
    968  constexpr size_t dataOffset = dataSlotOffset + sizeof(HeapSlot);
    969 
    970  static_assert(
    971      FixedLengthTypedArrayObject::FIXED_DATA_START ==
    972          FixedLengthTypedArrayObject::DATA_SLOT + 1,
    973      "fixed inline element data assumed to begin after the data slot");
    974 
    975  static_assert(
    976      FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT ==
    977          JSObject::MAX_BYTE_SIZE - dataOffset,
    978      "typed array inline buffer is limited by the maximum object byte size");
    979 
    980  // Initialise data elements to zero.
    981  size_t nbytes = templateObj->byteLength();
    982  MOZ_ASSERT(nbytes <= FixedLengthTypedArrayObject::INLINE_BUFFER_LIMIT);
    983 
    984  MOZ_ASSERT(dataOffset + nbytes <= templateObj->tenuredSizeOfThis());
    985  MOZ_ASSERT(templateObj->tenuredSizeOfThis() > dataOffset,
    986             "enough inline capacity to tag with ZeroLengthArrayData");
    987 
    988  // Store data elements inside the remaining JSObject slots.
    989  computeEffectiveAddress(Address(obj, dataOffset), temp);
    990  storePrivateValue(temp, Address(obj, dataSlotOffset));
    991 
    992  // Write enough zero pointers into fixed data to zero every
    993  // element.  (This zeroes past the end of a byte count that's
    994  // not a multiple of pointer size.  That's okay, because fixed
    995  // data is a count of 8-byte HeapSlots (i.e. <= pointer size),
    996  // and we won't inline unless the desired memory fits in that
    997  // space.)
    998  static_assert(sizeof(HeapSlot) == 8, "Assumed 8 bytes alignment");
    999 
   1000  size_t numZeroPointers = ((nbytes + 7) & ~0x7) / sizeof(char*);
   1001  for (size_t i = 0; i < numZeroPointers; i++) {
   1002    storePtr(ImmWord(0), Address(obj, dataOffset + i * sizeof(char*)));
   1003  }
   1004 
   1005 #ifdef DEBUG
   1006  if (nbytes == 0) {
   1007    store8(Imm32(ArrayBufferViewObject::ZeroLengthArrayData),
   1008           Address(obj, dataOffset));
   1009  }
   1010 #endif
   1011 }
   1012 
   1013 void MacroAssembler::initGCSlots(Register obj, Register temp,
   1014                                 const TemplateNativeObject& templateObj) {
   1015  MOZ_ASSERT(!templateObj.isArrayObject());
   1016 
   1017  // Slots of non-array objects are required to be initialized.
   1018  // Use the values currently in the template object.
   1019  uint32_t nslots = templateObj.slotSpan();
   1020  if (nslots == 0) {
   1021    return;
   1022  }
   1023 
   1024  uint32_t nfixed = templateObj.numUsedFixedSlots();
   1025  uint32_t ndynamic = templateObj.numDynamicSlots();
   1026 
   1027  // Attempt to group slot writes such that we minimize the amount of
   1028  // duplicated data we need to embed in code and load into registers. In
   1029  // general, most template object slots will be undefined except for any
   1030  // reserved slots. Since reserved slots come first, we split the object
   1031  // logically into independent non-UndefinedValue writes to the head and
   1032  // duplicated writes of UndefinedValue to the tail. For the majority of
   1033  // objects, the "tail" will be the entire slot range.
   1034  //
   1035  // The template object may be a CallObject, in which case we need to
   1036  // account for uninitialized lexical slots as well as undefined
   1037  // slots. Uninitialized lexical slots appears in CallObjects if the function
   1038  // has parameter expressions, in which case closed over parameters have
   1039  // TDZ. Uninitialized slots come before undefined slots in CallObjects.
   1040  auto [startOfUninitialized, startOfUndefined] =
   1041      FindStartOfUninitializedAndUndefinedSlots(templateObj, nslots);
   1042  MOZ_ASSERT(startOfUninitialized <= nfixed);  // Reserved slots must be fixed.
   1043  MOZ_ASSERT(startOfUndefined >= startOfUninitialized);
   1044  MOZ_ASSERT_IF(!templateObj.isCallObject() &&
   1045                    !templateObj.isBlockLexicalEnvironmentObject(),
   1046                startOfUninitialized == startOfUndefined);
   1047 
   1048  // Copy over any preserved reserved slots.
   1049  copySlotsFromTemplate(obj, templateObj, 0, startOfUninitialized);
   1050 
   1051  // Fill the rest of the fixed slots with undefined and uninitialized.
   1052  size_t offset = NativeObject::getFixedSlotOffset(startOfUninitialized);
   1053  fillSlotsWithUninitialized(Address(obj, offset), temp, startOfUninitialized,
   1054                             std::min(startOfUndefined, nfixed));
   1055 
   1056  if (startOfUndefined < nfixed) {
   1057    offset = NativeObject::getFixedSlotOffset(startOfUndefined);
   1058    fillSlotsWithUndefined(Address(obj, offset), temp, startOfUndefined,
   1059                           nfixed);
   1060  }
   1061 
   1062  if (ndynamic) {
   1063    // We are short one register to do this elegantly. Borrow the obj
   1064    // register briefly for our slots base address.
   1065    push(obj);
   1066    loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
   1067 
   1068    // Fill uninitialized slots if necessary. Otherwise initialize all
   1069    // slots to undefined.
   1070    if (startOfUndefined > nfixed) {
   1071      MOZ_ASSERT(startOfUninitialized != startOfUndefined);
   1072      fillSlotsWithUninitialized(Address(obj, 0), temp, 0,
   1073                                 startOfUndefined - nfixed);
   1074      size_t offset = (startOfUndefined - nfixed) * sizeof(Value);
   1075      fillSlotsWithUndefined(Address(obj, offset), temp,
   1076                             startOfUndefined - nfixed, ndynamic);
   1077    } else {
   1078      fillSlotsWithUndefined(Address(obj, 0), temp, 0, ndynamic);
   1079    }
   1080 
   1081    pop(obj);
   1082  }
   1083 }
   1084 
   1085 void MacroAssembler::initGCThing(Register obj, Register temp,
   1086                                 const TemplateObject& templateObj,
   1087                                 bool initContents) {
   1088  // Fast initialization of an empty object returned by allocateObject().
   1089 
   1090  storePtr(ImmGCPtr(templateObj.shape()),
   1091           Address(obj, JSObject::offsetOfShape()));
   1092 
   1093  if (templateObj.isNativeObject()) {
   1094    const TemplateNativeObject& ntemplate =
   1095        templateObj.asTemplateNativeObject();
   1096    MOZ_ASSERT(!ntemplate.hasDynamicElements());
   1097 
   1098    // If the object has dynamic slots, the slots member has already been
   1099    // filled in.
   1100    if (ntemplate.numDynamicSlots() == 0) {
   1101      storePtr(ImmPtr(emptyObjectSlots),
   1102               Address(obj, NativeObject::offsetOfSlots()));
   1103    }
   1104 
   1105    if (ntemplate.isArrayObject()) {
   1106      // Can't skip initializing reserved slots.
   1107      MOZ_ASSERT(initContents);
   1108 
   1109      int elementsOffset = NativeObject::offsetOfFixedElements();
   1110 
   1111      computeEffectiveAddress(Address(obj, elementsOffset), temp);
   1112      storePtr(temp, Address(obj, NativeObject::offsetOfElements()));
   1113 
   1114      // Fill in the elements header.
   1115      store32(
   1116          Imm32(ntemplate.getDenseCapacity()),
   1117          Address(obj, elementsOffset + ObjectElements::offsetOfCapacity()));
   1118      store32(Imm32(ntemplate.getDenseInitializedLength()),
   1119              Address(obj, elementsOffset +
   1120                               ObjectElements::offsetOfInitializedLength()));
   1121      store32(Imm32(ntemplate.getArrayLength()),
   1122              Address(obj, elementsOffset + ObjectElements::offsetOfLength()));
   1123      store32(Imm32(ObjectElements::FIXED),
   1124              Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
   1125    } else if (ntemplate.isArgumentsObject()) {
   1126      // The caller will initialize the reserved slots.
   1127      MOZ_ASSERT(!initContents);
   1128      storePtr(ImmPtr(emptyObjectElements),
   1129               Address(obj, NativeObject::offsetOfElements()));
   1130    } else {
   1131      // If the target type could be a TypedArray that maps shared memory
   1132      // then this would need to store emptyObjectElementsShared in that case.
   1133      MOZ_ASSERT(!ntemplate.isSharedMemory());
   1134 
   1135      // Can't skip initializing reserved slots.
   1136      MOZ_ASSERT(initContents);
   1137 
   1138      storePtr(ImmPtr(emptyObjectElements),
   1139               Address(obj, NativeObject::offsetOfElements()));
   1140 
   1141      initGCSlots(obj, temp, ntemplate);
   1142    }
   1143  } else {
   1144    MOZ_CRASH("Unknown object");
   1145  }
   1146 
   1147 #ifdef JS_GC_PROBES
   1148  AllocatableRegisterSet regs(RegisterSet::Volatile());
   1149  LiveRegisterSet save(regs.asLiveSet());
   1150  PushRegsInMask(save);
   1151 
   1152  regs.takeUnchecked(obj);
   1153  Register temp2 = regs.takeAnyGeneral();
   1154 
   1155  using Fn = void (*)(JSObject* obj);
   1156  setupUnalignedABICall(temp2);
   1157  passABIArg(obj);
   1158  callWithABI<Fn, TraceCreateObject>();
   1159 
   1160  PopRegsInMask(save);
   1161 #endif
   1162 }
   1163 
   1164 static size_t StringCharsByteLength(const JSOffThreadAtom* str) {
   1165  CharEncoding encoding =
   1166      str->hasLatin1Chars() ? CharEncoding::Latin1 : CharEncoding::TwoByte;
   1167  size_t encodingSize = encoding == CharEncoding::Latin1
   1168                            ? sizeof(JS::Latin1Char)
   1169                            : sizeof(char16_t);
   1170  return str->length() * encodingSize;
   1171 }
   1172 
   1173 bool MacroAssembler::canCompareStringCharsInline(const JSOffThreadAtom* str) {
   1174  // Limit the number of inline instructions used for character comparisons. Use
   1175  // the same instruction limit for both encodings, i.e. two-byte uses half the
   1176  // limit of Latin-1 strings.
   1177  constexpr size_t ByteLengthCompareCutoff = 32;
   1178 
   1179  size_t byteLength = StringCharsByteLength(str);
   1180  return 0 < byteLength && byteLength <= ByteLengthCompareCutoff;
   1181 }
   1182 
   1183 template <typename T, typename CharT>
   1184 static inline T CopyCharacters(const CharT* chars) {
   1185  T value = 0;
   1186  std::memcpy(&value, chars, sizeof(T));
   1187  return value;
   1188 }
   1189 
   1190 template <typename T>
   1191 static inline T CopyCharacters(const JSOffThreadAtom* str, size_t index) {
   1192  JS::AutoCheckCannotGC nogc;
   1193 
   1194  if (str->hasLatin1Chars()) {
   1195    MOZ_ASSERT(index + sizeof(T) / sizeof(JS::Latin1Char) <= str->length());
   1196    return CopyCharacters<T>(str->latin1Chars(nogc) + index);
   1197  }
   1198 
   1199  MOZ_ASSERT(sizeof(T) >= sizeof(char16_t));
   1200  MOZ_ASSERT(index + sizeof(T) / sizeof(char16_t) <= str->length());
   1201  return CopyCharacters<T>(str->twoByteChars(nogc) + index);
   1202 }
   1203 
   1204 void MacroAssembler::branchIfNotStringCharsEquals(Register stringChars,
   1205                                                  const JSOffThreadAtom* str,
   1206                                                  Label* label) {
   1207  CharEncoding encoding =
   1208      str->hasLatin1Chars() ? CharEncoding::Latin1 : CharEncoding::TwoByte;
   1209  size_t encodingSize = encoding == CharEncoding::Latin1
   1210                            ? sizeof(JS::Latin1Char)
   1211                            : sizeof(char16_t);
   1212  size_t byteLength = StringCharsByteLength(str);
   1213 
   1214  size_t pos = 0;
   1215  for (size_t stride : {8, 4, 2, 1}) {
   1216    while (byteLength >= stride) {
   1217      Address addr(stringChars, pos * encodingSize);
   1218      switch (stride) {
   1219        case 8: {
   1220          auto x = CopyCharacters<uint64_t>(str, pos);
   1221          branch64(Assembler::NotEqual, addr, Imm64(x), label);
   1222          break;
   1223        }
   1224        case 4: {
   1225          auto x = CopyCharacters<uint32_t>(str, pos);
   1226          branch32(Assembler::NotEqual, addr, Imm32(x), label);
   1227          break;
   1228        }
   1229        case 2: {
   1230          auto x = CopyCharacters<uint16_t>(str, pos);
   1231          branch16(Assembler::NotEqual, addr, Imm32(x), label);
   1232          break;
   1233        }
   1234        case 1: {
   1235          auto x = CopyCharacters<uint8_t>(str, pos);
   1236          branch8(Assembler::NotEqual, addr, Imm32(x), label);
   1237          break;
   1238        }
   1239      }
   1240 
   1241      byteLength -= stride;
   1242      pos += stride / encodingSize;
   1243    }
   1244 
   1245    // Prefer a single comparison for trailing bytes instead of doing
   1246    // multiple consecutive comparisons.
   1247    //
   1248    // For example when comparing against the string "example", emit two
   1249    // four-byte comparisons against "exam" and "mple" instead of doing
   1250    // three comparisons against "exam", "pl", and finally "e".
   1251    if (pos > 0 && byteLength > stride / 2) {
   1252      MOZ_ASSERT(stride == 8 || stride == 4);
   1253 
   1254      size_t prev = pos - (stride - byteLength) / encodingSize;
   1255      Address addr(stringChars, prev * encodingSize);
   1256      switch (stride) {
   1257        case 8: {
   1258          auto x = CopyCharacters<uint64_t>(str, prev);
   1259          branch64(Assembler::NotEqual, addr, Imm64(x), label);
   1260          break;
   1261        }
   1262        case 4: {
   1263          auto x = CopyCharacters<uint32_t>(str, prev);
   1264          branch32(Assembler::NotEqual, addr, Imm32(x), label);
   1265          break;
   1266        }
   1267      }
   1268 
   1269      // Break from the loop, because we've finished the complete string.
   1270      break;
   1271    }
   1272  }
   1273 }
   1274 
   1275 void MacroAssembler::loadStringCharsForCompare(Register input,
   1276                                               const JSOffThreadAtom* str,
   1277                                               Register stringChars,
   1278                                               Label* fail) {
   1279  CharEncoding encoding =
   1280      str->hasLatin1Chars() ? CharEncoding::Latin1 : CharEncoding::TwoByte;
   1281 
   1282  // Take the slow path when the string is a rope or has a different character
   1283  // representation.
   1284  branchIfRope(input, fail);
   1285  if (encoding == CharEncoding::Latin1) {
   1286    branchTwoByteString(input, fail);
   1287  } else {
   1288    JS::AutoCheckCannotGC nogc;
   1289    if (mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) {
   1290      branchLatin1String(input, fail);
   1291    } else {
   1292      // This case was already handled in the caller.
   1293 #ifdef DEBUG
   1294      Label ok;
   1295      branchTwoByteString(input, &ok);
   1296      assumeUnreachable("Unexpected Latin-1 string");
   1297      bind(&ok);
   1298 #endif
   1299    }
   1300  }
   1301 
   1302 #ifdef DEBUG
   1303  {
   1304    size_t length = str->length();
   1305    MOZ_ASSERT(length > 0);
   1306 
   1307    Label ok;
   1308    branch32(Assembler::AboveOrEqual,
   1309             Address(input, JSString::offsetOfLength()), Imm32(length), &ok);
   1310    assumeUnreachable("Input mustn't be smaller than search string");
   1311    bind(&ok);
   1312  }
   1313 #endif
   1314 
   1315  // Load the input string's characters.
   1316  loadStringChars(input, stringChars, encoding);
   1317 }
   1318 
   1319 void MacroAssembler::compareStringChars(JSOp op, Register stringChars,
   1320                                        const JSOffThreadAtom* str,
   1321                                        Register output) {
   1322  MOZ_ASSERT(IsEqualityOp(op));
   1323 
   1324  size_t byteLength = StringCharsByteLength(str);
   1325 
   1326  // Prefer a single compare-and-set instruction if possible.
   1327  if (byteLength == 1 || byteLength == 2 || byteLength == 4 ||
   1328      byteLength == 8) {
   1329    auto cond = JSOpToCondition(op, /* isSigned = */ false);
   1330 
   1331    Address addr(stringChars, 0);
   1332    switch (byteLength) {
   1333      case 8: {
   1334        auto x = CopyCharacters<uint64_t>(str, 0);
   1335        cmp64Set(cond, addr, Imm64(x), output);
   1336        break;
   1337      }
   1338      case 4: {
   1339        auto x = CopyCharacters<uint32_t>(str, 0);
   1340        cmp32Set(cond, addr, Imm32(x), output);
   1341        break;
   1342      }
   1343      case 2: {
   1344        auto x = CopyCharacters<uint16_t>(str, 0);
   1345        cmp16Set(cond, addr, Imm32(x), output);
   1346        break;
   1347      }
   1348      case 1: {
   1349        auto x = CopyCharacters<uint8_t>(str, 0);
   1350        cmp8Set(cond, addr, Imm32(x), output);
   1351        break;
   1352      }
   1353    }
   1354  } else {
   1355    Label setNotEqualResult;
   1356    branchIfNotStringCharsEquals(stringChars, str, &setNotEqualResult);
   1357 
   1358    // Falls through if both strings are equal.
   1359 
   1360    Label done;
   1361    move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
   1362    jump(&done);
   1363 
   1364    bind(&setNotEqualResult);
   1365    move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
   1366 
   1367    bind(&done);
   1368  }
   1369 }
   1370 
   1371 void MacroAssembler::compareStrings(JSOp op, Register left, Register right,
   1372                                    Register result, Label* fail) {
   1373  MOZ_ASSERT(left != result);
   1374  MOZ_ASSERT(right != result);
   1375  MOZ_ASSERT(IsEqualityOp(op) || IsRelationalOp(op));
   1376 
   1377  Label notPointerEqual;
   1378  // If operands point to the same instance, the strings are trivially equal.
   1379  branchPtr(Assembler::NotEqual, left, right,
   1380            IsEqualityOp(op) ? &notPointerEqual : fail);
   1381  move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
   1382               op == JSOp::Ge),
   1383         result);
   1384 
   1385  if (IsEqualityOp(op)) {
   1386    Label done;
   1387    jump(&done);
   1388 
   1389    bind(&notPointerEqual);
   1390 
   1391    Label leftIsNotAtom;
   1392    Label setNotEqualResult;
   1393    // Atoms cannot be equal to each other if they point to different strings.
   1394    Imm32 atomBit(JSString::ATOM_BIT);
   1395    branchTest32(Assembler::Zero, Address(left, JSString::offsetOfFlags()),
   1396                 atomBit, &leftIsNotAtom);
   1397    branchTest32(Assembler::NonZero, Address(right, JSString::offsetOfFlags()),
   1398                 atomBit, &setNotEqualResult);
   1399 
   1400    bind(&leftIsNotAtom);
   1401    // Strings of different length can never be equal.
   1402    loadStringLength(left, result);
   1403    branch32(Assembler::Equal, Address(right, JSString::offsetOfLength()),
   1404             result, fail);
   1405 
   1406    bind(&setNotEqualResult);
   1407    move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), result);
   1408 
   1409    bind(&done);
   1410  }
   1411 }
   1412 
   1413 void MacroAssembler::loadStringChars(Register str, Register dest,
   1414                                     CharEncoding encoding) {
   1415  MOZ_ASSERT(str != dest);
   1416 
   1417  if (JitOptions.spectreStringMitigations) {
   1418    if (encoding == CharEncoding::Latin1) {
   1419      // If the string is a rope, zero the |str| register. The code below
   1420      // depends on str->flags so this should block speculative execution.
   1421      movePtr(ImmWord(0), dest);
   1422      test32MovePtr(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   1423                    Imm32(JSString::LINEAR_BIT), dest, str);
   1424    } else {
   1425      // If we're loading TwoByte chars, there's an additional risk:
   1426      // if the string has Latin1 chars, we could read out-of-bounds. To
   1427      // prevent this, we check both the Linear and Latin1 bits. We don't
   1428      // have a scratch register, so we use these flags also to block
   1429      // speculative execution, similar to the use of 0 above.
   1430      MOZ_ASSERT(encoding == CharEncoding::TwoByte);
   1431      static constexpr uint32_t Mask =
   1432          JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT;
   1433      static_assert(Mask < 2048,
   1434                    "Mask should be a small, near-null value to ensure we "
   1435                    "block speculative execution when it's used as string "
   1436                    "pointer");
   1437      move32(Imm32(Mask), dest);
   1438      and32(Address(str, JSString::offsetOfFlags()), dest);
   1439      cmp32MovePtr(Assembler::NotEqual, dest, Imm32(JSString::LINEAR_BIT), dest,
   1440                   str);
   1441    }
   1442  }
   1443 
   1444  // Load the inline chars.
   1445  computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()),
   1446                          dest);
   1447 
   1448  // If it's not an inline string, load the non-inline chars. Use a
   1449  // conditional move to prevent speculative execution.
   1450  test32LoadPtr(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   1451                Imm32(JSString::INLINE_CHARS_BIT),
   1452                Address(str, JSString::offsetOfNonInlineChars()), dest);
   1453 }
   1454 
   1455 void MacroAssembler::loadNonInlineStringChars(Register str, Register dest,
   1456                                              CharEncoding encoding) {
   1457  MOZ_ASSERT(str != dest);
   1458 
   1459  if (JitOptions.spectreStringMitigations) {
   1460    // If the string is a rope, has inline chars, or has a different
   1461    // character encoding, set str to a near-null value to prevent
   1462    // speculative execution below (when reading str->nonInlineChars).
   1463 
   1464    static constexpr uint32_t Mask = JSString::LINEAR_BIT |
   1465                                     JSString::INLINE_CHARS_BIT |
   1466                                     JSString::LATIN1_CHARS_BIT;
   1467    static_assert(Mask < 2048,
   1468                  "Mask should be a small, near-null value to ensure we "
   1469                  "block speculative execution when it's used as string "
   1470                  "pointer");
   1471 
   1472    uint32_t expectedBits = JSString::LINEAR_BIT;
   1473    if (encoding == CharEncoding::Latin1) {
   1474      expectedBits |= JSString::LATIN1_CHARS_BIT;
   1475    }
   1476 
   1477    move32(Imm32(Mask), dest);
   1478    and32(Address(str, JSString::offsetOfFlags()), dest);
   1479 
   1480    cmp32MovePtr(Assembler::NotEqual, dest, Imm32(expectedBits), dest, str);
   1481  }
   1482 
   1483  loadPtr(Address(str, JSString::offsetOfNonInlineChars()), dest);
   1484 }
   1485 
   1486 void MacroAssembler::storeNonInlineStringChars(Register chars, Register str) {
   1487  MOZ_ASSERT(chars != str);
   1488  storePtr(chars, Address(str, JSString::offsetOfNonInlineChars()));
   1489 }
   1490 
   1491 void MacroAssembler::loadInlineStringCharsForStore(Register str,
   1492                                                   Register dest) {
   1493  computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()),
   1494                          dest);
   1495 }
   1496 
   1497 void MacroAssembler::loadInlineStringChars(Register str, Register dest,
   1498                                           CharEncoding encoding) {
   1499  MOZ_ASSERT(str != dest);
   1500 
   1501  if (JitOptions.spectreStringMitigations) {
   1502    // Making this Spectre-safe is a bit complicated: using
   1503    // computeEffectiveAddress and then zeroing the output register if
   1504    // non-inline is not sufficient: when the index is very large, it would
   1505    // allow reading |nullptr + index|. Just fall back to loadStringChars
   1506    // for now.
   1507    loadStringChars(str, dest, encoding);
   1508  } else {
   1509    computeEffectiveAddress(
   1510        Address(str, JSInlineString::offsetOfInlineStorage()), dest);
   1511  }
   1512 }
   1513 
   1514 void MacroAssembler::loadRopeLeftChild(Register str, Register dest) {
   1515  MOZ_ASSERT(str != dest);
   1516 
   1517  if (JitOptions.spectreStringMitigations) {
   1518    // Zero the output register if the input was not a rope.
   1519    movePtr(ImmWord(0), dest);
   1520    test32LoadPtr(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   1521                  Imm32(JSString::LINEAR_BIT),
   1522                  Address(str, JSRope::offsetOfLeft()), dest);
   1523  } else {
   1524    loadPtr(Address(str, JSRope::offsetOfLeft()), dest);
   1525  }
   1526 }
   1527 
   1528 void MacroAssembler::loadRopeRightChild(Register str, Register dest) {
   1529  MOZ_ASSERT(str != dest);
   1530 
   1531  if (JitOptions.spectreStringMitigations) {
   1532    // Zero the output register if the input was not a rope.
   1533    movePtr(ImmWord(0), dest);
   1534    test32LoadPtr(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   1535                  Imm32(JSString::LINEAR_BIT),
   1536                  Address(str, JSRope::offsetOfRight()), dest);
   1537  } else {
   1538    loadPtr(Address(str, JSRope::offsetOfRight()), dest);
   1539  }
   1540 }
   1541 
   1542 void MacroAssembler::storeRopeChildren(Register left, Register right,
   1543                                       Register str) {
   1544  storePtr(left, Address(str, JSRope::offsetOfLeft()));
   1545  storePtr(right, Address(str, JSRope::offsetOfRight()));
   1546 }
   1547 
   1548 void MacroAssembler::loadDependentStringBase(Register str, Register dest) {
   1549  MOZ_ASSERT(str != dest);
   1550 
   1551  if (JitOptions.spectreStringMitigations) {
   1552    // If the string is not a dependent string, zero the |str| register.
   1553    // The code below loads str->base so this should block speculative
   1554    // execution.
   1555    movePtr(ImmWord(0), dest);
   1556    test32MovePtr(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   1557                  Imm32(JSString::DEPENDENT_BIT), dest, str);
   1558  }
   1559 
   1560  loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
   1561 }
   1562 
   1563 void MacroAssembler::storeDependentStringBase(Register base, Register str) {
   1564  storePtr(base, Address(str, JSDependentString::offsetOfBase()));
   1565 }
   1566 
   1567 void MacroAssembler::branchIfMaybeSplitSurrogatePair(Register leftChild,
   1568                                                     Register index,
   1569                                                     Register scratch,
   1570                                                     Label* maybeSplit,
   1571                                                     Label* notSplit) {
   1572  // If |index| is the last character of the left child and the left child
   1573  // is a two-byte string, it's possible that a surrogate pair is split
   1574  // between the left and right child of a rope.
   1575 
   1576  // Can't be a split surrogate when the left child is a Latin-1 string.
   1577  branchLatin1String(leftChild, notSplit);
   1578 
   1579  // Can't be a split surrogate when |index + 1| is in the left child.
   1580  add32(Imm32(1), index, scratch);
   1581  branch32(Assembler::Above, Address(leftChild, JSString::offsetOfLength()),
   1582           scratch, notSplit);
   1583 
   1584  // Jump to |maybeSplit| if the left child is another rope.
   1585  branchIfRope(leftChild, maybeSplit);
   1586 
   1587  // Load the character at |index|.
   1588  loadStringChars(leftChild, scratch, CharEncoding::TwoByte);
   1589  loadChar(scratch, index, scratch, CharEncoding::TwoByte);
   1590 
   1591  // Jump to |maybeSplit| if the last character is a lead surrogate.
   1592  branchIfLeadSurrogate(scratch, scratch, maybeSplit);
   1593 }
   1594 
   1595 void MacroAssembler::loadRopeChild(CharKind kind, Register str, Register index,
   1596                                   Register output, Register maybeScratch,
   1597                                   Label* isLinear, Label* splitSurrogate) {
   1598  // This follows JSString::getChar.
   1599  branchIfNotRope(str, isLinear);
   1600 
   1601  loadRopeLeftChild(str, output);
   1602 
   1603  Label loadedChild;
   1604  if (kind == CharKind::CharCode) {
   1605    // Check if |index| is contained in the left child.
   1606    branch32(Assembler::Above, Address(output, JSString::offsetOfLength()),
   1607             index, &loadedChild);
   1608  } else {
   1609    MOZ_ASSERT(maybeScratch != InvalidReg);
   1610 
   1611    // Check if |index| is contained in the left child.
   1612    Label loadRight;
   1613    branch32(Assembler::BelowOrEqual,
   1614             Address(output, JSString::offsetOfLength()), index, &loadRight);
   1615    {
   1616      // Handle possible split surrogate pairs.
   1617      branchIfMaybeSplitSurrogatePair(output, index, maybeScratch,
   1618                                      splitSurrogate, &loadedChild);
   1619      jump(&loadedChild);
   1620    }
   1621    bind(&loadRight);
   1622  }
   1623 
   1624  // The index must be in the rightChild.
   1625  loadRopeRightChild(str, output);
   1626 
   1627  bind(&loadedChild);
   1628 }
   1629 
   1630 void MacroAssembler::branchIfCanLoadStringChar(CharKind kind, Register str,
   1631                                               Register index, Register scratch,
   1632                                               Register maybeScratch,
   1633                                               Label* label) {
   1634  Label splitSurrogate;
   1635  loadRopeChild(kind, str, index, scratch, maybeScratch, label,
   1636                &splitSurrogate);
   1637 
   1638  // Branch if the left resp. right side is linear.
   1639  branchIfNotRope(scratch, label);
   1640 
   1641  if (kind == CharKind::CodePoint) {
   1642    bind(&splitSurrogate);
   1643  }
   1644 }
   1645 
   1646 void MacroAssembler::branchIfNotCanLoadStringChar(CharKind kind, Register str,
   1647                                                  Register index,
   1648                                                  Register scratch,
   1649                                                  Register maybeScratch,
   1650                                                  Label* label) {
   1651  Label done;
   1652  loadRopeChild(kind, str, index, scratch, maybeScratch, &done, label);
   1653 
   1654  // Branch if the left or right side is another rope.
   1655  branchIfRope(scratch, label);
   1656 
   1657  bind(&done);
   1658 }
   1659 
   1660 void MacroAssembler::loadStringChar(CharKind kind, Register str, Register index,
   1661                                    Register output, Register scratch1,
   1662                                    Register scratch2, Label* fail) {
   1663  MOZ_ASSERT(str != output);
   1664  MOZ_ASSERT(str != index);
   1665  MOZ_ASSERT(index != output);
   1666  MOZ_ASSERT_IF(kind == CharKind::CodePoint, index != scratch1);
   1667  MOZ_ASSERT(output != scratch1);
   1668  MOZ_ASSERT(output != scratch2);
   1669 
   1670  // Use scratch1 for the index (adjusted below).
   1671  if (index != scratch1) {
   1672    move32(index, scratch1);
   1673  }
   1674  movePtr(str, output);
   1675 
   1676  // This follows JSString::getChar.
   1677  Label notRope;
   1678  branchIfNotRope(str, &notRope);
   1679 
   1680  loadRopeLeftChild(str, output);
   1681 
   1682  // Check if the index is contained in the leftChild.
   1683  Label loadedChild, notInLeft;
   1684  spectreBoundsCheck32(scratch1, Address(output, JSString::offsetOfLength()),
   1685                       scratch2, &notInLeft);
   1686  if (kind == CharKind::CodePoint) {
   1687    branchIfMaybeSplitSurrogatePair(output, scratch1, scratch2, fail,
   1688                                    &loadedChild);
   1689  }
   1690  jump(&loadedChild);
   1691 
   1692  // The index must be in the rightChild.
   1693  // index -= rope->leftChild()->length()
   1694  bind(&notInLeft);
   1695  sub32(Address(output, JSString::offsetOfLength()), scratch1);
   1696  loadRopeRightChild(str, output);
   1697 
   1698  // If the left or right side is another rope, give up.
   1699  bind(&loadedChild);
   1700  branchIfRope(output, fail);
   1701 
   1702  bind(&notRope);
   1703 
   1704  Label isLatin1, done;
   1705  branchLatin1String(output, &isLatin1);
   1706  {
   1707    loadStringChars(output, scratch2, CharEncoding::TwoByte);
   1708 
   1709    if (kind == CharKind::CharCode) {
   1710      loadChar(scratch2, scratch1, output, CharEncoding::TwoByte);
   1711    } else {
   1712      // Load the first character.
   1713      addToCharPtr(scratch2, scratch1, CharEncoding::TwoByte);
   1714      loadChar(Address(scratch2, 0), output, CharEncoding::TwoByte);
   1715 
   1716      // If the first character isn't a lead surrogate, go to |done|.
   1717      branchIfNotLeadSurrogate(output, &done);
   1718 
   1719      // branchIfMaybeSplitSurrogatePair ensures that the surrogate pair can't
   1720      // split between two rope children. So if |index + 1 < str.length|, then
   1721      // |index| and |index + 1| are in the same rope child.
   1722      //
   1723      // NB: We use the non-adjusted |index| and |str| inputs, because |output|
   1724      // was overwritten and no longer contains the rope child.
   1725 
   1726      // If |index + 1| is a valid index into |str|.
   1727      add32(Imm32(1), index, scratch1);
   1728      spectreBoundsCheck32(scratch1, Address(str, JSString::offsetOfLength()),
   1729                           InvalidReg, &done);
   1730 
   1731      // Then load the next character at |scratch2 + sizeof(char16_t)|.
   1732      loadChar(Address(scratch2, sizeof(char16_t)), scratch1,
   1733               CharEncoding::TwoByte);
   1734 
   1735      // If the next character isn't a trail surrogate, go to |done|.
   1736      branchIfNotTrailSurrogate(scratch1, scratch2, &done);
   1737 
   1738      // Inlined unicode::UTF16Decode(char16_t, char16_t).
   1739      lshift32(Imm32(10), output);
   1740      add32(Imm32(unicode::NonBMPMin - (unicode::LeadSurrogateMin << 10) -
   1741                  unicode::TrailSurrogateMin),
   1742            scratch1);
   1743      add32(scratch1, output);
   1744    }
   1745 
   1746    jump(&done);
   1747  }
   1748  bind(&isLatin1);
   1749  {
   1750    loadStringChars(output, scratch2, CharEncoding::Latin1);
   1751    loadChar(scratch2, scratch1, output, CharEncoding::Latin1);
   1752  }
   1753 
   1754  bind(&done);
   1755 }
   1756 
   1757 void MacroAssembler::loadStringChar(Register str, int32_t index,
   1758                                    Register output, Register scratch1,
   1759                                    Register scratch2, Label* fail) {
   1760  MOZ_ASSERT(str != output);
   1761  MOZ_ASSERT(output != scratch1);
   1762  MOZ_ASSERT(output != scratch2);
   1763 
   1764  if (index == 0) {
   1765    movePtr(str, scratch1);
   1766 
   1767    // This follows JSString::getChar.
   1768    Label notRope;
   1769    branchIfNotRope(str, &notRope);
   1770 
   1771    loadRopeLeftChild(str, scratch1);
   1772 
   1773    // Rope children can't be empty, so the index can't be in the right side.
   1774 
   1775    // If the left side is another rope, give up.
   1776    branchIfRope(scratch1, fail);
   1777 
   1778    bind(&notRope);
   1779 
   1780    Label isLatin1, done;
   1781    branchLatin1String(scratch1, &isLatin1);
   1782    loadStringChars(scratch1, scratch2, CharEncoding::TwoByte);
   1783    loadChar(Address(scratch2, 0), output, CharEncoding::TwoByte);
   1784    jump(&done);
   1785 
   1786    bind(&isLatin1);
   1787    loadStringChars(scratch1, scratch2, CharEncoding::Latin1);
   1788    loadChar(Address(scratch2, 0), output, CharEncoding::Latin1);
   1789 
   1790    bind(&done);
   1791  } else {
   1792    move32(Imm32(index), scratch1);
   1793    loadStringChar(str, scratch1, output, scratch1, scratch2, fail);
   1794  }
   1795 }
   1796 
   1797 void MacroAssembler::loadStringIndexValue(Register str, Register dest,
   1798                                          Label* fail) {
   1799  MOZ_ASSERT(str != dest);
   1800 
   1801  load32(Address(str, JSString::offsetOfFlags()), dest);
   1802 
   1803  // Does not have a cached index value.
   1804  branchTest32(Assembler::Zero, dest, Imm32(JSString::INDEX_VALUE_BIT), fail);
   1805 
   1806  // Extract the index.
   1807  rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
   1808 }
   1809 
   1810 void MacroAssembler::loadChar(Register chars, Register index, Register dest,
   1811                              CharEncoding encoding, int32_t offset /* = 0 */) {
   1812  if (encoding == CharEncoding::Latin1) {
   1813    loadChar(BaseIndex(chars, index, TimesOne, offset), dest, encoding);
   1814  } else {
   1815    loadChar(BaseIndex(chars, index, TimesTwo, offset), dest, encoding);
   1816  }
   1817 }
   1818 
   1819 void MacroAssembler::addToCharPtr(Register chars, Register index,
   1820                                  CharEncoding encoding) {
   1821  if (encoding == CharEncoding::Latin1) {
   1822    static_assert(sizeof(char) == 1,
   1823                  "Latin-1 string index shouldn't need scaling");
   1824    addPtr(index, chars);
   1825  } else {
   1826    computeEffectiveAddress(BaseIndex(chars, index, TimesTwo), chars);
   1827  }
   1828 }
   1829 
   1830 void MacroAssembler::branchIfNotLeadSurrogate(Register src, Label* label) {
   1831  branch32(Assembler::Below, src, Imm32(unicode::LeadSurrogateMin), label);
   1832  branch32(Assembler::Above, src, Imm32(unicode::LeadSurrogateMax), label);
   1833 }
   1834 
   1835 void MacroAssembler::branchSurrogate(Assembler::Condition cond, Register src,
   1836                                     Register scratch, Label* label,
   1837                                     SurrogateChar surrogateChar) {
   1838  // For TrailSurrogateMin ≤ x ≤ TrailSurrogateMax and
   1839  // LeadSurrogateMin ≤ x ≤ LeadSurrogateMax, the following equations hold.
   1840  //
   1841  //    SurrogateMin ≤ x ≤ SurrogateMax
   1842  // <> SurrogateMin ≤ x ≤ SurrogateMin + 2^10 - 1
   1843  // <> ((x - SurrogateMin) >>> 10) = 0    where >>> is an unsigned-shift
   1844  // See Hacker's Delight, section 4-1 for details.
   1845  //
   1846  //    ((x - SurrogateMin) >>> 10) = 0
   1847  // <> floor((x - SurrogateMin) / 1024) = 0
   1848  // <> floor((x / 1024) - (SurrogateMin / 1024)) = 0
   1849  // <> floor(x / 1024) = SurrogateMin / 1024
   1850  // <> floor(x / 1024) * 1024 = SurrogateMin
   1851  // <> (x >>> 10) << 10 = SurrogateMin
   1852  // <> x & ~(2^10 - 1) = SurrogateMin
   1853 
   1854  constexpr char16_t SurrogateMask = 0xFC00;
   1855  char16_t SurrogateMin = surrogateChar == SurrogateChar::Lead
   1856                              ? unicode::LeadSurrogateMin
   1857                              : unicode::TrailSurrogateMin;
   1858 
   1859  and32(Imm32(SurrogateMask), src, scratch);
   1860  branch32(cond, scratch, Imm32(SurrogateMin), label);
   1861 }
   1862 
   1863 void MacroAssembler::loadStringFromUnit(Register unit, Register dest,
   1864                                        const StaticStrings& staticStrings) {
   1865  movePtr(ImmPtr(&staticStrings.unitStaticTable), dest);
   1866  loadPtr(BaseIndex(dest, unit, ScalePointer), dest);
   1867 }
   1868 
   1869 void MacroAssembler::loadLengthTwoString(Register c1, Register c2,
   1870                                         Register dest,
   1871                                         const StaticStrings& staticStrings) {
   1872  // Compute (toSmallCharTable[c1] << SMALL_CHAR_BITS) + toSmallCharTable[c2]
   1873  // to obtain the index into `StaticStrings::length2StaticTable`.
   1874  static_assert(sizeof(StaticStrings::SmallChar) == 1);
   1875 
   1876  movePtr(ImmPtr(&StaticStrings::toSmallCharTable.storage), dest);
   1877  load8ZeroExtend(BaseIndex(dest, c1, Scale::TimesOne), c1);
   1878  load8ZeroExtend(BaseIndex(dest, c2, Scale::TimesOne), c2);
   1879 
   1880  lshift32(Imm32(StaticStrings::SMALL_CHAR_BITS), c1);
   1881  add32(c2, c1);
   1882 
   1883  // Look up the string from the computed index.
   1884  movePtr(ImmPtr(&staticStrings.length2StaticTable), dest);
   1885  loadPtr(BaseIndex(dest, c1, ScalePointer), dest);
   1886 }
   1887 
   1888 void MacroAssembler::lookupStaticString(Register ch, Register dest,
   1889                                        const StaticStrings& staticStrings) {
   1890  MOZ_ASSERT(ch != dest);
   1891 
   1892  movePtr(ImmPtr(&staticStrings.unitStaticTable), dest);
   1893  loadPtr(BaseIndex(dest, ch, ScalePointer), dest);
   1894 }
   1895 
   1896 void MacroAssembler::lookupStaticString(Register ch, Register dest,
   1897                                        const StaticStrings& staticStrings,
   1898                                        Label* fail) {
   1899  MOZ_ASSERT(ch != dest);
   1900 
   1901  boundsCheck32PowerOfTwo(ch, StaticStrings::UNIT_STATIC_LIMIT, fail);
   1902  movePtr(ImmPtr(&staticStrings.unitStaticTable), dest);
   1903  loadPtr(BaseIndex(dest, ch, ScalePointer), dest);
   1904 }
   1905 
   1906 void MacroAssembler::lookupStaticString(Register ch1, Register ch2,
   1907                                        Register dest,
   1908                                        const StaticStrings& staticStrings,
   1909                                        Label* fail) {
   1910  MOZ_ASSERT(ch1 != dest);
   1911  MOZ_ASSERT(ch2 != dest);
   1912 
   1913  branch32(Assembler::AboveOrEqual, ch1,
   1914           Imm32(StaticStrings::SMALL_CHAR_TABLE_SIZE), fail);
   1915  branch32(Assembler::AboveOrEqual, ch2,
   1916           Imm32(StaticStrings::SMALL_CHAR_TABLE_SIZE), fail);
   1917 
   1918  movePtr(ImmPtr(&StaticStrings::toSmallCharTable.storage), dest);
   1919  load8ZeroExtend(BaseIndex(dest, ch1, Scale::TimesOne), ch1);
   1920  load8ZeroExtend(BaseIndex(dest, ch2, Scale::TimesOne), ch2);
   1921 
   1922  branch32(Assembler::Equal, ch1, Imm32(StaticStrings::INVALID_SMALL_CHAR),
   1923           fail);
   1924  branch32(Assembler::Equal, ch2, Imm32(StaticStrings::INVALID_SMALL_CHAR),
   1925           fail);
   1926 
   1927  lshift32(Imm32(StaticStrings::SMALL_CHAR_BITS), ch1);
   1928  add32(ch2, ch1);
   1929 
   1930  // Look up the string from the computed index.
   1931  movePtr(ImmPtr(&staticStrings.length2StaticTable), dest);
   1932  loadPtr(BaseIndex(dest, ch1, ScalePointer), dest);
   1933 }
   1934 
   1935 void MacroAssembler::lookupStaticIntString(Register integer, Register dest,
   1936                                           Register scratch,
   1937                                           const StaticStrings& staticStrings,
   1938                                           Label* fail) {
   1939  MOZ_ASSERT(integer != scratch);
   1940 
   1941  boundsCheck32PowerOfTwo(integer, StaticStrings::INT_STATIC_LIMIT, fail);
   1942  movePtr(ImmPtr(&staticStrings.intStaticTable), scratch);
   1943  loadPtr(BaseIndex(scratch, integer, ScalePointer), dest);
   1944 }
   1945 
   1946 void MacroAssembler::loadInt32ToStringWithBase(
   1947    Register input, Register base, Register dest, Register scratch1,
   1948    Register scratch2, const StaticStrings& staticStrings,
   1949    const LiveRegisterSet& volatileRegs, bool lowerCase, Label* fail) {
   1950 #ifdef DEBUG
   1951  Label baseBad, baseOk;
   1952  branch32(Assembler::LessThan, base, Imm32(2), &baseBad);
   1953  branch32(Assembler::LessThanOrEqual, base, Imm32(36), &baseOk);
   1954  bind(&baseBad);
   1955  assumeUnreachable("base must be in range [2, 36]");
   1956  bind(&baseOk);
   1957 #endif
   1958 
   1959  // Compute |"0123456789abcdefghijklmnopqrstuvwxyz"[r]|.
   1960  auto toChar = [this, base, lowerCase](Register r) {
   1961 #ifdef DEBUG
   1962    Label ok;
   1963    branch32(Assembler::Below, r, base, &ok);
   1964    assumeUnreachable("bad digit");
   1965    bind(&ok);
   1966 #else
   1967    // Silence unused lambda capture warning.
   1968    (void)base;
   1969 #endif
   1970 
   1971    Label done;
   1972    add32(Imm32('0'), r);
   1973    branch32(Assembler::BelowOrEqual, r, Imm32('9'), &done);
   1974    add32(Imm32((lowerCase ? 'a' : 'A') - '0' - 10), r);
   1975    bind(&done);
   1976  };
   1977 
   1978  // Perform a "unit" lookup when |unsigned(input) < unsigned(base)|.
   1979  Label lengthTwo, done;
   1980  branch32(Assembler::AboveOrEqual, input, base, &lengthTwo);
   1981  {
   1982    move32(input, scratch1);
   1983    toChar(scratch1);
   1984 
   1985    loadStringFromUnit(scratch1, dest, staticStrings);
   1986 
   1987    jump(&done);
   1988  }
   1989  bind(&lengthTwo);
   1990 
   1991  // Compute |base * base|.
   1992  move32(base, scratch1);
   1993  mul32(scratch1, scratch1);
   1994 
   1995  // Perform a "length2" lookup when |unsigned(input) < unsigned(base * base)|.
   1996  branch32(Assembler::AboveOrEqual, input, scratch1, fail);
   1997  {
   1998    // Compute |scratch1 = input / base| and |scratch2 = input % base|.
   1999    flexibleDivMod32(input, base, scratch1, scratch2, true, volatileRegs);
   2000 
   2001    // Compute the digits of the divisor and remainder.
   2002    toChar(scratch1);
   2003    toChar(scratch2);
   2004 
   2005    // Look up the 2-character digit string in the small-char table.
   2006    loadLengthTwoString(scratch1, scratch2, dest, staticStrings);
   2007  }
   2008  bind(&done);
   2009 }
   2010 
   2011 void MacroAssembler::loadInt32ToStringWithBase(
   2012    Register input, int32_t base, Register dest, Register scratch1,
   2013    Register scratch2, const StaticStrings& staticStrings, bool lowerCase,
   2014    Label* fail) {
   2015  MOZ_ASSERT(2 <= base && base <= 36, "base must be in range [2, 36]");
   2016 
   2017  // Compute |"0123456789abcdefghijklmnopqrstuvwxyz"[r]|.
   2018  auto toChar = [this, base, lowerCase](Register r) {
   2019 #ifdef DEBUG
   2020    Label ok;
   2021    branch32(Assembler::Below, r, Imm32(base), &ok);
   2022    assumeUnreachable("bad digit");
   2023    bind(&ok);
   2024 #endif
   2025 
   2026    if (base <= 10) {
   2027      add32(Imm32('0'), r);
   2028    } else {
   2029      Label done;
   2030      add32(Imm32('0'), r);
   2031      branch32(Assembler::BelowOrEqual, r, Imm32('9'), &done);
   2032      add32(Imm32((lowerCase ? 'a' : 'A') - '0' - 10), r);
   2033      bind(&done);
   2034    }
   2035  };
   2036 
   2037  // Perform a "unit" lookup when |unsigned(input) < unsigned(base)|.
   2038  Label lengthTwo, done;
   2039  branch32(Assembler::AboveOrEqual, input, Imm32(base), &lengthTwo);
   2040  {
   2041    move32(input, scratch1);
   2042    toChar(scratch1);
   2043 
   2044    loadStringFromUnit(scratch1, dest, staticStrings);
   2045 
   2046    jump(&done);
   2047  }
   2048  bind(&lengthTwo);
   2049 
   2050  // Perform a "length2" lookup when |unsigned(input) < unsigned(base * base)|.
   2051  branch32(Assembler::AboveOrEqual, input, Imm32(base * base), fail);
   2052  {
   2053    // Compute |scratch1 = input / base| and |scratch2 = input % base|.
   2054    if (mozilla::IsPowerOfTwo(uint32_t(base))) {
   2055      uint32_t shift = mozilla::FloorLog2(base);
   2056 
   2057      rshift32(Imm32(shift), input, scratch1);
   2058      and32(Imm32((uint32_t(1) << shift) - 1), input, scratch2);
   2059    } else {
   2060      // The following code matches CodeGenerator::visitUDivOrModConstant()
   2061      // for x86-shared. Also see Hacker's Delight 2nd edition, chapter 10-8
   2062      // "Unsigned Division by 7" for the case when |rmc.multiplier| exceeds
   2063      // UINT32_MAX and we need to adjust the shift amount.
   2064 
   2065      auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(
   2066          uint32_t(base));
   2067 
   2068      // We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
   2069      mulHighUnsigned32(Imm32(rmc.multiplier), input, scratch1);
   2070 
   2071      if (rmc.multiplier > UINT32_MAX) {
   2072        // M >= 2^32 and shift == 0 is impossible, as d >= 2 implies that
   2073        // ((M * n) >> (32 + shift)) >= n > floor(n/d) whenever n >= d,
   2074        // contradicting the proof of correctness in computeDivisionConstants.
   2075        MOZ_ASSERT(rmc.shiftAmount > 0);
   2076        MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 33));
   2077 
   2078        // Compute |t = (n - q) / 2|.
   2079        move32(input, scratch2);
   2080        sub32(scratch1, scratch2);
   2081        rshift32(Imm32(1), scratch2);
   2082 
   2083        // Compute |t = (n - q) / 2 + q = (n + q) / 2|.
   2084        add32(scratch2, scratch1);
   2085 
   2086        // Finish the computation |q = floor(n / d)|.
   2087        rshift32(Imm32(rmc.shiftAmount - 1), scratch1);
   2088      } else {
   2089        rshift32(Imm32(rmc.shiftAmount), scratch1);
   2090      }
   2091 
   2092      // Compute the remainder from |r = n - q * d|.
   2093      move32(scratch1, dest);
   2094      mul32(Imm32(base), dest);
   2095      move32(input, scratch2);
   2096      sub32(dest, scratch2);
   2097    }
   2098 
   2099    // Compute the digits of the divisor and remainder.
   2100    toChar(scratch1);
   2101    toChar(scratch2);
   2102 
   2103    // Look up the 2-character digit string in the small-char table.
   2104    loadLengthTwoString(scratch1, scratch2, dest, staticStrings);
   2105  }
   2106  bind(&done);
   2107 }
   2108 
   2109 void MacroAssembler::loadBigIntDigits(Register bigInt, Register digits) {
   2110  MOZ_ASSERT(digits != bigInt);
   2111 
   2112  // Load the inline digits.
   2113  computeEffectiveAddress(Address(bigInt, BigInt::offsetOfInlineDigits()),
   2114                          digits);
   2115 
   2116  // If inline digits aren't used, load the heap digits. Use a conditional move
   2117  // to prevent speculative execution.
   2118  cmp32LoadPtr(Assembler::Above, Address(bigInt, BigInt::offsetOfLength()),
   2119               Imm32(int32_t(BigInt::inlineDigitsLength())),
   2120               Address(bigInt, BigInt::offsetOfHeapDigits()), digits);
   2121 }
   2122 
   2123 void MacroAssembler::loadBigInt64(Register bigInt, Register64 dest) {
   2124  // This code follows the implementation of |BigInt::toUint64()|. We're also
   2125  // using it for inline callers of |BigInt::toInt64()|, which works, because
   2126  // all supported Jit architectures use a two's complement representation for
   2127  // int64 values, which means the WrapToSigned call in toInt64() is a no-op.
   2128 
   2129  Label done, nonZero;
   2130 
   2131  branchIfBigIntIsNonZero(bigInt, &nonZero);
   2132  {
   2133    move64(Imm64(0), dest);
   2134    jump(&done);
   2135  }
   2136  bind(&nonZero);
   2137 
   2138 #ifdef JS_PUNBOX64
   2139  Register digits = dest.reg;
   2140 #else
   2141  Register digits = dest.high;
   2142 #endif
   2143 
   2144  loadBigIntDigits(bigInt, digits);
   2145 
   2146 #if JS_PUNBOX64
   2147  // Load the first digit into the destination register.
   2148  load64(Address(digits, 0), dest);
   2149 #else
   2150  // Load the first digit into the destination register's low value.
   2151  load32(Address(digits, 0), dest.low);
   2152 
   2153  // And conditionally load the second digit into the high value register.
   2154  Label twoDigits, digitsDone;
   2155  branch32(Assembler::Above, Address(bigInt, BigInt::offsetOfLength()),
   2156           Imm32(1), &twoDigits);
   2157  {
   2158    move32(Imm32(0), dest.high);
   2159    jump(&digitsDone);
   2160  }
   2161  {
   2162    bind(&twoDigits);
   2163    load32(Address(digits, sizeof(BigInt::Digit)), dest.high);
   2164  }
   2165  bind(&digitsDone);
   2166 #endif
   2167 
   2168  branchTest32(Assembler::Zero, Address(bigInt, BigInt::offsetOfFlags()),
   2169               Imm32(BigInt::signBitMask()), &done);
   2170  neg64(dest);
   2171 
   2172  bind(&done);
   2173 }
   2174 
   2175 void MacroAssembler::loadBigIntDigit(Register bigInt, Register dest) {
   2176  Label done, nonZero;
   2177  branchIfBigIntIsNonZero(bigInt, &nonZero);
   2178  {
   2179    movePtr(ImmWord(0), dest);
   2180    jump(&done);
   2181  }
   2182  bind(&nonZero);
   2183 
   2184  loadBigIntDigits(bigInt, dest);
   2185 
   2186  // Load the first digit into the destination register.
   2187  loadPtr(Address(dest, 0), dest);
   2188 
   2189  bind(&done);
   2190 }
   2191 
   2192 void MacroAssembler::loadBigIntDigit(Register bigInt, Register dest,
   2193                                     Label* fail) {
   2194  MOZ_ASSERT(bigInt != dest);
   2195 
   2196  branch32(Assembler::Above, Address(bigInt, BigInt::offsetOfLength()),
   2197           Imm32(1), fail);
   2198 
   2199  static_assert(BigInt::inlineDigitsLength() > 0,
   2200                "Single digit BigInts use inline storage");
   2201 
   2202  // Load the first inline digit into the destination register.
   2203  movePtr(ImmWord(0), dest);
   2204  cmp32LoadPtr(Assembler::NotEqual, Address(bigInt, BigInt::offsetOfLength()),
   2205               Imm32(0), Address(bigInt, BigInt::offsetOfInlineDigits()), dest);
   2206 }
   2207 
   2208 void MacroAssembler::loadBigIntPtr(Register bigInt, Register dest,
   2209                                   Label* fail) {
   2210  loadBigIntDigit(bigInt, dest, fail);
   2211 
   2212  // BigInt digits are stored as unsigned numbers. Take the failure path when
   2213  // the digit can't be stored in intptr_t.
   2214 
   2215  Label nonNegative, done;
   2216  branchIfBigIntIsNonNegative(bigInt, &nonNegative);
   2217  {
   2218    // Negate |dest| when the BigInt is negative.
   2219    negPtr(dest);
   2220 
   2221    // Test after negating to handle INTPTR_MIN correctly.
   2222    branchTestPtr(Assembler::NotSigned, dest, dest, fail);
   2223    jump(&done);
   2224  }
   2225  bind(&nonNegative);
   2226  branchTestPtr(Assembler::Signed, dest, dest, fail);
   2227  bind(&done);
   2228 }
   2229 
   2230 void MacroAssembler::initializeBigInt64(Scalar::Type type, Register bigInt,
   2231                                        Register64 val, Register64 temp) {
   2232  MOZ_ASSERT(Scalar::isBigIntType(type));
   2233 
   2234  store32(Imm32(0), Address(bigInt, BigInt::offsetOfFlags()));
   2235 
   2236  Label done, nonZero;
   2237  branch64(Assembler::NotEqual, val, Imm64(0), &nonZero);
   2238  {
   2239    store32(Imm32(0), Address(bigInt, BigInt::offsetOfLength()));
   2240    jump(&done);
   2241  }
   2242  bind(&nonZero);
   2243 
   2244  if (type == Scalar::BigInt64) {
   2245    // Copy the input when we're not allowed to clobber it.
   2246    if (temp != Register64::Invalid()) {
   2247      move64(val, temp);
   2248      val = temp;
   2249    }
   2250 
   2251    // Set the sign-bit for negative values and then continue with the two's
   2252    // complement.
   2253    Label isPositive;
   2254    branch64(Assembler::GreaterThan, val, Imm64(0), &isPositive);
   2255    {
   2256      store32(Imm32(BigInt::signBitMask()),
   2257              Address(bigInt, BigInt::offsetOfFlags()));
   2258      neg64(val);
   2259    }
   2260    bind(&isPositive);
   2261  }
   2262 
   2263  store32(Imm32(1), Address(bigInt, BigInt::offsetOfLength()));
   2264 
   2265  static_assert(sizeof(BigInt::Digit) == sizeof(uintptr_t),
   2266                "BigInt Digit size matches uintptr_t, so there's a single "
   2267                "store on 64-bit and up to two stores on 32-bit");
   2268 
   2269 #ifndef JS_PUNBOX64
   2270  Label singleDigit;
   2271  branchTest32(Assembler::Zero, val.high, val.high, &singleDigit);
   2272  store32(Imm32(2), Address(bigInt, BigInt::offsetOfLength()));
   2273  bind(&singleDigit);
   2274 
   2275  // We can perform a single store64 on 32-bit platforms, because inline
   2276  // storage can store at least two 32-bit integers.
   2277  static_assert(BigInt::inlineDigitsLength() >= 2,
   2278                "BigInt inline storage can store at least two digits");
   2279 #endif
   2280 
   2281  store64(val, Address(bigInt, js::BigInt::offsetOfInlineDigits()));
   2282 
   2283  bind(&done);
   2284 }
   2285 
   2286 void MacroAssembler::initializeBigIntPtr(Register bigInt, Register val) {
   2287  store32(Imm32(0), Address(bigInt, BigInt::offsetOfFlags()));
   2288 
   2289  Label done, nonZero;
   2290  branchTestPtr(Assembler::NonZero, val, val, &nonZero);
   2291  {
   2292    store32(Imm32(0), Address(bigInt, BigInt::offsetOfLength()));
   2293    jump(&done);
   2294  }
   2295  bind(&nonZero);
   2296 
   2297  // Set the sign-bit for negative values and then continue with the two's
   2298  // complement.
   2299  Label isPositive;
   2300  branchTestPtr(Assembler::NotSigned, val, val, &isPositive);
   2301  {
   2302    store32(Imm32(BigInt::signBitMask()),
   2303            Address(bigInt, BigInt::offsetOfFlags()));
   2304    negPtr(val);
   2305  }
   2306  bind(&isPositive);
   2307 
   2308  store32(Imm32(1), Address(bigInt, BigInt::offsetOfLength()));
   2309 
   2310  static_assert(sizeof(BigInt::Digit) == sizeof(uintptr_t),
   2311                "BigInt Digit size matches uintptr_t");
   2312 
   2313  storePtr(val, Address(bigInt, js::BigInt::offsetOfInlineDigits()));
   2314 
   2315  bind(&done);
   2316 }
   2317 
   2318 void MacroAssembler::copyBigIntWithInlineDigits(Register src, Register dest,
   2319                                                Register temp,
   2320                                                gc::Heap initialHeap,
   2321                                                Label* fail) {
   2322  branch32(Assembler::Above, Address(src, BigInt::offsetOfLength()),
   2323           Imm32(int32_t(BigInt::inlineDigitsLength())), fail);
   2324 
   2325  newGCBigInt(dest, temp, initialHeap, fail);
   2326 
   2327  // Copy the sign-bit, but not any of the other bits used by the GC.
   2328  load32(Address(src, BigInt::offsetOfFlags()), temp);
   2329  and32(Imm32(BigInt::signBitMask()), temp);
   2330  store32(temp, Address(dest, BigInt::offsetOfFlags()));
   2331 
   2332  // Copy the length.
   2333  load32(Address(src, BigInt::offsetOfLength()), temp);
   2334  store32(temp, Address(dest, BigInt::offsetOfLength()));
   2335 
   2336  // Copy the digits.
   2337  Address srcDigits(src, js::BigInt::offsetOfInlineDigits());
   2338  Address destDigits(dest, js::BigInt::offsetOfInlineDigits());
   2339 
   2340  for (size_t i = 0; i < BigInt::inlineDigitsLength(); i++) {
   2341    static_assert(sizeof(BigInt::Digit) == sizeof(uintptr_t),
   2342                  "BigInt Digit size matches uintptr_t");
   2343 
   2344    loadPtr(srcDigits, temp);
   2345    storePtr(temp, destDigits);
   2346 
   2347    srcDigits = Address(src, srcDigits.offset + sizeof(BigInt::Digit));
   2348    destDigits = Address(dest, destDigits.offset + sizeof(BigInt::Digit));
   2349  }
   2350 }
   2351 
   2352 void MacroAssembler::compareBigIntAndInt32(JSOp op, Register bigInt,
   2353                                           Register int32, Register scratch1,
   2354                                           Register scratch2, Label* ifTrue,
   2355                                           Label* ifFalse) {
   2356  MOZ_ASSERT(IsLooseEqualityOp(op) || IsRelationalOp(op));
   2357 
   2358  static_assert(std::is_same_v<BigInt::Digit, uintptr_t>,
   2359                "BigInt digit can be loaded in a pointer-sized register");
   2360  static_assert(sizeof(BigInt::Digit) >= sizeof(uint32_t),
   2361                "BigInt digit stores at least an uint32");
   2362 
   2363  // Test for too large numbers.
   2364  //
   2365  // If the unsigned value of the BigInt can't be expressed in an uint32/uint64,
   2366  // the result of the comparison is a constant.
   2367  if (op == JSOp::Eq || op == JSOp::Ne) {
   2368    Label* tooLarge = op == JSOp::Eq ? ifFalse : ifTrue;
   2369    branch32(Assembler::GreaterThan,
   2370             Address(bigInt, BigInt::offsetOfDigitLength()), Imm32(1),
   2371             tooLarge);
   2372  } else {
   2373    Label doCompare;
   2374    branch32(Assembler::LessThanOrEqual,
   2375             Address(bigInt, BigInt::offsetOfDigitLength()), Imm32(1),
   2376             &doCompare);
   2377 
   2378    // Still need to take the sign-bit into account for relational operations.
   2379    if (op == JSOp::Lt || op == JSOp::Le) {
   2380      branchIfBigIntIsNegative(bigInt, ifTrue);
   2381      jump(ifFalse);
   2382    } else {
   2383      branchIfBigIntIsNegative(bigInt, ifFalse);
   2384      jump(ifTrue);
   2385    }
   2386 
   2387    bind(&doCompare);
   2388  }
   2389 
   2390  // Test for mismatched signs and, if the signs are equal, load |abs(x)| in
   2391  // |scratch1| and |abs(y)| in |scratch2| and then compare the unsigned numbers
   2392  // against each other.
   2393  {
   2394    // Jump to |ifTrue| resp. |ifFalse| if the BigInt is strictly less than
   2395    // resp. strictly greater than the int32 value, depending on the comparison
   2396    // operator.
   2397    Label* greaterThan;
   2398    Label* lessThan;
   2399    if (op == JSOp::Eq) {
   2400      greaterThan = ifFalse;
   2401      lessThan = ifFalse;
   2402    } else if (op == JSOp::Ne) {
   2403      greaterThan = ifTrue;
   2404      lessThan = ifTrue;
   2405    } else if (op == JSOp::Lt || op == JSOp::Le) {
   2406      greaterThan = ifFalse;
   2407      lessThan = ifTrue;
   2408    } else {
   2409      MOZ_ASSERT(op == JSOp::Gt || op == JSOp::Ge);
   2410      greaterThan = ifTrue;
   2411      lessThan = ifFalse;
   2412    }
   2413 
   2414    // BigInt digits are always stored as an unsigned number.
   2415    loadBigIntDigit(bigInt, scratch1);
   2416 
   2417    // Load the int32 into |scratch2| and negate it for negative numbers.
   2418    move32(int32, scratch2);
   2419 
   2420    Label isNegative, doCompare;
   2421    branchIfBigIntIsNegative(bigInt, &isNegative);
   2422    branch32(Assembler::LessThan, int32, Imm32(0), greaterThan);
   2423    jump(&doCompare);
   2424 
   2425    // We rely on |neg32(INT32_MIN)| staying INT32_MIN, because we're using an
   2426    // unsigned comparison below.
   2427    bind(&isNegative);
   2428    branch32(Assembler::GreaterThanOrEqual, int32, Imm32(0), lessThan);
   2429    neg32(scratch2);
   2430 
   2431    // Not all supported platforms (e.g. MIPS64) zero-extend 32-bit operations,
   2432    // so we need to explicitly clear any high 32-bits.
   2433    move32ZeroExtendToPtr(scratch2, scratch2);
   2434 
   2435    // Reverse the relational comparator for negative numbers.
   2436    // |-x < -y| <=> |+x > +y|.
   2437    // |-x ≤ -y| <=> |+x ≥ +y|.
   2438    // |-x > -y| <=> |+x < +y|.
   2439    // |-x ≥ -y| <=> |+x ≤ +y|.
   2440    JSOp reversed = ReverseCompareOp(op);
   2441    if (reversed != op) {
   2442      branchPtr(JSOpToCondition(reversed, /* isSigned = */ false), scratch1,
   2443                scratch2, ifTrue);
   2444      jump(ifFalse);
   2445    }
   2446 
   2447    bind(&doCompare);
   2448    branchPtr(JSOpToCondition(op, /* isSigned = */ false), scratch1, scratch2,
   2449              ifTrue);
   2450  }
   2451 }
   2452 
   2453 void MacroAssembler::compareBigIntAndInt32(JSOp op, Register bigInt,
   2454                                           Imm32 int32, Register scratch,
   2455                                           Label* ifTrue, Label* ifFalse) {
   2456  MOZ_ASSERT(IsLooseEqualityOp(op) || IsRelationalOp(op));
   2457 
   2458  static_assert(std::is_same_v<BigInt::Digit, uintptr_t>,
   2459                "BigInt digit can be loaded in a pointer-sized register");
   2460  static_assert(sizeof(BigInt::Digit) >= sizeof(uint32_t),
   2461                "BigInt digit stores at least an uint32");
   2462 
   2463  // Comparison against zero doesn't require loading any BigInt digits.
   2464  if (int32.value == 0) {
   2465    switch (op) {
   2466      case JSOp::Eq:
   2467        branchIfBigIntIsZero(bigInt, ifTrue);
   2468        break;
   2469      case JSOp::Ne:
   2470        branchIfBigIntIsNonZero(bigInt, ifTrue);
   2471        break;
   2472      case JSOp::Lt:
   2473        branchIfBigIntIsNegative(bigInt, ifTrue);
   2474        break;
   2475      case JSOp::Le:
   2476        branchIfBigIntIsZero(bigInt, ifTrue);
   2477        branchIfBigIntIsNegative(bigInt, ifTrue);
   2478        break;
   2479      case JSOp::Gt:
   2480        branchIfBigIntIsZero(bigInt, ifFalse);
   2481        branchIfBigIntIsNonNegative(bigInt, ifTrue);
   2482        break;
   2483      case JSOp::Ge:
   2484        branchIfBigIntIsNonNegative(bigInt, ifTrue);
   2485        break;
   2486      default:
   2487        MOZ_CRASH("bad comparison operator");
   2488    }
   2489 
   2490    // Fall through to the false case.
   2491    return;
   2492  }
   2493 
   2494  // Jump to |ifTrue| resp. |ifFalse| if the BigInt is strictly less than
   2495  // resp. strictly greater than the int32 value, depending on the comparison
   2496  // operator.
   2497  Label* greaterThan;
   2498  Label* lessThan;
   2499  if (op == JSOp::Eq) {
   2500    greaterThan = ifFalse;
   2501    lessThan = ifFalse;
   2502  } else if (op == JSOp::Ne) {
   2503    greaterThan = ifTrue;
   2504    lessThan = ifTrue;
   2505  } else if (op == JSOp::Lt || op == JSOp::Le) {
   2506    greaterThan = ifFalse;
   2507    lessThan = ifTrue;
   2508  } else {
   2509    MOZ_ASSERT(op == JSOp::Gt || op == JSOp::Ge);
   2510    greaterThan = ifTrue;
   2511    lessThan = ifFalse;
   2512  }
   2513 
   2514  // Test for mismatched signs.
   2515  if (int32.value > 0) {
   2516    branchIfBigIntIsNegative(bigInt, lessThan);
   2517  } else {
   2518    branchIfBigIntIsNonNegative(bigInt, greaterThan);
   2519  }
   2520 
   2521  // Both signs are equal, load |abs(x)| in |scratch| and then compare the
   2522  // unsigned numbers against each other.
   2523  //
   2524  // If the unsigned value of the BigInt can't be expressed in an uint32/uint64,
   2525  // the result of the comparison is a constant.
   2526  Label* tooLarge = int32.value > 0 ? greaterThan : lessThan;
   2527  loadBigIntDigit(bigInt, scratch, tooLarge);
   2528 
   2529  // Use the unsigned value of the immediate.
   2530  ImmWord uint32 = ImmWord(mozilla::Abs(int32.value));
   2531 
   2532  // Reverse the relational comparator for negative numbers.
   2533  // |-x < -y| <=> |+x > +y|.
   2534  // |-x ≤ -y| <=> |+x ≥ +y|.
   2535  // |-x > -y| <=> |+x < +y|.
   2536  // |-x ≥ -y| <=> |+x ≤ +y|.
   2537  if (int32.value < 0) {
   2538    op = ReverseCompareOp(op);
   2539  }
   2540 
   2541  branchPtr(JSOpToCondition(op, /* isSigned = */ false), scratch, uint32,
   2542            ifTrue);
   2543 }
   2544 
   2545 void MacroAssembler::equalBigInts(Register left, Register right, Register temp1,
   2546                                  Register temp2, Register temp3,
   2547                                  Register temp4, Label* notSameSign,
   2548                                  Label* notSameLength, Label* notSameDigit) {
   2549  MOZ_ASSERT(left != temp1);
   2550  MOZ_ASSERT(right != temp1);
   2551  MOZ_ASSERT(right != temp2);
   2552 
   2553  // Jump to |notSameSign| when the sign aren't the same.
   2554  load32(Address(left, BigInt::offsetOfFlags()), temp1);
   2555  xor32(Address(right, BigInt::offsetOfFlags()), temp1);
   2556  branchTest32(Assembler::NonZero, temp1, Imm32(BigInt::signBitMask()),
   2557               notSameSign);
   2558 
   2559  // Jump to |notSameLength| when the digits length is different.
   2560  load32(Address(right, BigInt::offsetOfLength()), temp1);
   2561  branch32(Assembler::NotEqual, Address(left, BigInt::offsetOfLength()), temp1,
   2562           notSameLength);
   2563 
   2564  // Both BigInts have the same sign and the same number of digits. Loop
   2565  // over each digit, starting with the left-most one, and break from the
   2566  // loop when the first non-matching digit was found.
   2567 
   2568  loadBigIntDigits(left, temp2);
   2569  loadBigIntDigits(right, temp3);
   2570 
   2571  static_assert(sizeof(BigInt::Digit) == sizeof(void*),
   2572                "BigInt::Digit is pointer sized");
   2573 
   2574  computeEffectiveAddress(BaseIndex(temp2, temp1, ScalePointer), temp2);
   2575  computeEffectiveAddress(BaseIndex(temp3, temp1, ScalePointer), temp3);
   2576 
   2577  Label start, loop;
   2578  jump(&start);
   2579  bind(&loop);
   2580 
   2581  subPtr(Imm32(sizeof(BigInt::Digit)), temp2);
   2582  subPtr(Imm32(sizeof(BigInt::Digit)), temp3);
   2583 
   2584  loadPtr(Address(temp3, 0), temp4);
   2585  branchPtr(Assembler::NotEqual, Address(temp2, 0), temp4, notSameDigit);
   2586 
   2587  bind(&start);
   2588  branchSub32(Assembler::NotSigned, Imm32(1), temp1, &loop);
   2589 
   2590  // No different digits were found, both BigInts are equal to each other.
   2591 }
   2592 
   2593 void MacroAssembler::typeOfObject(Register obj, Register scratch, Label* slow,
   2594                                  Label* isObject, Label* isCallable,
   2595                                  Label* isUndefined) {
   2596  loadObjClassUnsafe(obj, scratch);
   2597 
   2598  // Proxies can emulate undefined and have complex isCallable behavior.
   2599  branchTestClassIsProxy(true, scratch, slow);
   2600 
   2601  // JSFunctions are always callable.
   2602  branchTestClassIsFunction(Assembler::Equal, scratch, isCallable);
   2603 
   2604  // Objects that emulate undefined.
   2605  Address flags(scratch, JSClass::offsetOfFlags());
   2606  branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED),
   2607               isUndefined);
   2608 
   2609  // Handle classes with a call hook.
   2610  branchPtr(Assembler::Equal, Address(scratch, offsetof(JSClass, cOps)),
   2611            ImmPtr(nullptr), isObject);
   2612 
   2613  loadPtr(Address(scratch, offsetof(JSClass, cOps)), scratch);
   2614  branchPtr(Assembler::Equal, Address(scratch, offsetof(JSClassOps, call)),
   2615            ImmPtr(nullptr), isObject);
   2616 
   2617  jump(isCallable);
   2618 }
   2619 
   2620 void MacroAssembler::isCallableOrConstructor(bool isCallable, Register obj,
   2621                                             Register output, Label* isProxy) {
   2622  MOZ_ASSERT(obj != output);
   2623 
   2624  Label notFunction, hasCOps, done;
   2625  loadObjClassUnsafe(obj, output);
   2626 
   2627  // An object is callable iff:
   2628  //   is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
   2629  // An object is constructor iff:
   2630  //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
   2631  //   (getClass()->cOps && getClass()->cOps->construct)).
   2632  branchTestClassIsFunction(Assembler::NotEqual, output, &notFunction);
   2633  if (isCallable) {
   2634    move32(Imm32(1), output);
   2635  } else {
   2636    static_assert(mozilla::IsPowerOfTwo(uint32_t(FunctionFlags::CONSTRUCTOR)),
   2637                  "FunctionFlags::CONSTRUCTOR has only one bit set");
   2638 
   2639    load32(Address(obj, JSFunction::offsetOfFlagsAndArgCount()), output);
   2640    rshift32(Imm32(mozilla::FloorLog2(uint32_t(FunctionFlags::CONSTRUCTOR))),
   2641             output);
   2642    and32(Imm32(1), output);
   2643  }
   2644  jump(&done);
   2645 
   2646  bind(&notFunction);
   2647 
   2648  if (!isCallable) {
   2649    // For bound functions, we need to check the isConstructor flag.
   2650    Label notBoundFunction;
   2651    branchPtr(Assembler::NotEqual, output, ImmPtr(&BoundFunctionObject::class_),
   2652              &notBoundFunction);
   2653 
   2654    static_assert(BoundFunctionObject::IsConstructorFlag == 0b1,
   2655                  "AND operation results in boolean value");
   2656    unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()), output);
   2657    and32(Imm32(BoundFunctionObject::IsConstructorFlag), output);
   2658    jump(&done);
   2659 
   2660    bind(&notBoundFunction);
   2661  }
   2662 
   2663  // Just skim proxies off. Their notion of isCallable()/isConstructor() is
   2664  // more complicated.
   2665  branchTestClassIsProxy(true, output, isProxy);
   2666 
   2667  branchPtr(Assembler::NonZero, Address(output, offsetof(JSClass, cOps)),
   2668            ImmPtr(nullptr), &hasCOps);
   2669  move32(Imm32(0), output);
   2670  jump(&done);
   2671 
   2672  bind(&hasCOps);
   2673  loadPtr(Address(output, offsetof(JSClass, cOps)), output);
   2674  size_t opsOffset =
   2675      isCallable ? offsetof(JSClassOps, call) : offsetof(JSClassOps, construct);
   2676  cmpPtrSet(Assembler::NonZero, Address(output, opsOffset), ImmPtr(nullptr),
   2677            output);
   2678 
   2679  bind(&done);
   2680 }
   2681 
   2682 void MacroAssembler::loadJSContext(Register dest) {
   2683  movePtr(ImmPtr(runtime()->mainContextPtr()), dest);
   2684 }
   2685 
   2686 static const uint8_t* ContextRealmPtr(CompileRuntime* rt) {
   2687  return (static_cast<const uint8_t*>(rt->mainContextPtr()) +
   2688          JSContext::offsetOfRealm());
   2689 }
   2690 
   2691 void MacroAssembler::loadGlobalObjectData(Register dest) {
   2692  loadPtr(AbsoluteAddress(ContextRealmPtr(runtime())), dest);
   2693  loadPtr(Address(dest, Realm::offsetOfActiveGlobal()), dest);
   2694  loadPrivate(Address(dest, GlobalObject::offsetOfGlobalDataSlot()), dest);
   2695 }
   2696 
   2697 void MacroAssembler::switchToRealm(Register realm) {
   2698  storePtr(realm, AbsoluteAddress(ContextRealmPtr(runtime())));
   2699 }
   2700 
   2701 void MacroAssembler::loadRealmFuse(RealmFuses::FuseIndex index, Register dest) {
   2702  // Load Realm pointer
   2703  loadPtr(AbsoluteAddress(ContextRealmPtr(runtime())), dest);
   2704  loadPtr(Address(dest, RealmFuses::offsetOfFuseWordRelativeToRealm(index)),
   2705          dest);
   2706 }
   2707 
   2708 void MacroAssembler::loadRuntimeFuse(RuntimeFuses::FuseIndex index,
   2709                                     Register dest) {
   2710  loadPtr(AbsoluteAddress(runtime()->addressOfRuntimeFuse(index)), dest);
   2711 }
   2712 
   2713 void MacroAssembler::guardRuntimeFuse(RuntimeFuses::FuseIndex index,
   2714                                      Label* fail) {
   2715  AbsoluteAddress addr(runtime()->addressOfRuntimeFuse(index));
   2716  branchPtr(Assembler::NotEqual, addr, ImmWord(0), fail);
   2717 }
   2718 
   2719 void MacroAssembler::switchToRealm(const void* realm, Register scratch) {
   2720  MOZ_ASSERT(realm);
   2721 
   2722  movePtr(ImmPtr(realm), scratch);
   2723  switchToRealm(scratch);
   2724 }
   2725 
   2726 void MacroAssembler::switchToObjectRealm(Register obj, Register scratch) {
   2727  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   2728  loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
   2729  loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
   2730  switchToRealm(scratch);
   2731 }
   2732 
   2733 void MacroAssembler::switchToBaselineFrameRealm(Register scratch) {
   2734  Address envChain(FramePointer,
   2735                   BaselineFrame::reverseOffsetOfEnvironmentChain());
   2736  loadPtr(envChain, scratch);
   2737  switchToObjectRealm(scratch, scratch);
   2738 }
   2739 
   2740 void MacroAssembler::switchToWasmInstanceRealm(Register scratch1,
   2741                                               Register scratch2) {
   2742  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()), scratch1);
   2743  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfRealm()), scratch2);
   2744  storePtr(scratch2, Address(scratch1, JSContext::offsetOfRealm()));
   2745 }
   2746 
   2747 template <typename ValueType>
   2748 void MacroAssembler::storeLocalAllocSite(ValueType value, Register scratch) {
   2749  loadPtr(AbsoluteAddress(ContextRealmPtr(runtime())), scratch);
   2750  storePtr(value, Address(scratch, JS::Realm::offsetOfLocalAllocSite()));
   2751 }
   2752 
   2753 template void MacroAssembler::storeLocalAllocSite(Register, Register);
   2754 template void MacroAssembler::storeLocalAllocSite(ImmWord, Register);
   2755 template void MacroAssembler::storeLocalAllocSite(ImmPtr, Register);
   2756 
   2757 void MacroAssembler::debugAssertContextRealm(const void* realm,
   2758                                             Register scratch) {
   2759 #ifdef DEBUG
   2760  Label ok;
   2761  movePtr(ImmPtr(realm), scratch);
   2762  branchPtr(Assembler::Equal, AbsoluteAddress(ContextRealmPtr(runtime())),
   2763            scratch, &ok);
   2764  assumeUnreachable("Unexpected context realm");
   2765  bind(&ok);
   2766 #endif
   2767 }
   2768 
   2769 void MacroAssembler::setIsCrossRealmArrayConstructor(Register obj,
   2770                                                     Register output) {
   2771 #ifdef DEBUG
   2772  Label notProxy;
   2773  branchTestObjectIsProxy(false, obj, output, &notProxy);
   2774  assumeUnreachable("Unexpected proxy in setIsCrossRealmArrayConstructor");
   2775  bind(&notProxy);
   2776 #endif
   2777 
   2778  // The object's realm must not be cx->realm.
   2779  Label isFalse, done;
   2780  loadPtr(Address(obj, JSObject::offsetOfShape()), output);
   2781  loadPtr(Address(output, Shape::offsetOfBaseShape()), output);
   2782  loadPtr(Address(output, BaseShape::offsetOfRealm()), output);
   2783  branchPtr(Assembler::Equal, AbsoluteAddress(ContextRealmPtr(runtime())),
   2784            output, &isFalse);
   2785 
   2786  // The object must be a function.
   2787  branchTestObjIsFunction(Assembler::NotEqual, obj, output, obj, &isFalse);
   2788 
   2789  // The function must be the ArrayConstructor native.
   2790  branchPtr(Assembler::NotEqual,
   2791            Address(obj, JSFunction::offsetOfNativeOrEnv()),
   2792            ImmPtr(js::ArrayConstructor), &isFalse);
   2793 
   2794  move32(Imm32(1), output);
   2795  jump(&done);
   2796 
   2797  bind(&isFalse);
   2798  move32(Imm32(0), output);
   2799 
   2800  bind(&done);
   2801 }
   2802 
   2803 void MacroAssembler::guardObjectHasSameRealm(Register obj, Register scratch,
   2804                                             Label* fail) {
   2805  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   2806  loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
   2807  loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
   2808  branchPtr(Assembler::NotEqual, AbsoluteAddress(ContextRealmPtr(runtime())),
   2809            scratch, fail);
   2810 }
   2811 
   2812 void MacroAssembler::setIsDefinitelyTypedArrayConstructor(Register obj,
   2813                                                          Register output) {
   2814  Label isFalse, isTrue, done;
   2815 
   2816  // The object must be a function. (Wrappers are not supported.)
   2817  branchTestObjIsFunction(Assembler::NotEqual, obj, output, obj, &isFalse);
   2818 
   2819  // Load the native into |output|.
   2820  loadPtr(Address(obj, JSFunction::offsetOfNativeOrEnv()), output);
   2821 
   2822  auto branchIsTypedArrayCtor = [&](Scalar::Type type) {
   2823    // The function must be a TypedArrayConstructor native (from any realm).
   2824    JSNative constructor = TypedArrayConstructorNative(type);
   2825    branchPtr(Assembler::Equal, output, ImmPtr(constructor), &isTrue);
   2826  };
   2827 
   2828 #define TYPED_ARRAY_CONSTRUCTOR_NATIVE(_, T, N) \
   2829  branchIsTypedArrayCtor(Scalar::N);
   2830  JS_FOR_EACH_TYPED_ARRAY(TYPED_ARRAY_CONSTRUCTOR_NATIVE)
   2831 #undef TYPED_ARRAY_CONSTRUCTOR_NATIVE
   2832 
   2833  // Falls through to the false case.
   2834 
   2835  bind(&isFalse);
   2836  move32(Imm32(0), output);
   2837  jump(&done);
   2838 
   2839  bind(&isTrue);
   2840  move32(Imm32(1), output);
   2841 
   2842  bind(&done);
   2843 }
   2844 
   2845 void MacroAssembler::loadMegamorphicCache(Register dest) {
   2846  movePtr(ImmPtr(runtime()->addressOfMegamorphicCache()), dest);
   2847 }
   2848 void MacroAssembler::loadMegamorphicSetPropCache(Register dest) {
   2849  movePtr(ImmPtr(runtime()->addressOfMegamorphicSetPropCache()), dest);
   2850 }
   2851 
   2852 void MacroAssembler::tryFastAtomize(Register str, Register scratch,
   2853                                    Register output, Label* fail) {
   2854  Label found, done, notAtomRef;
   2855 
   2856  branchTest32(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
   2857               Imm32(JSString::ATOM_REF_BIT), &notAtomRef);
   2858  loadPtr(Address(str, JSAtomRefString::offsetOfAtom()), output);
   2859  jump(&done);
   2860  bind(&notAtomRef);
   2861 
   2862  uintptr_t cachePtr = uintptr_t(runtime()->addressOfStringToAtomCache());
   2863  void* offset = (void*)(cachePtr + StringToAtomCache::offsetOfLastLookups());
   2864  movePtr(ImmPtr(offset), scratch);
   2865 
   2866  static_assert(StringToAtomCache::NumLastLookups == 2);
   2867  size_t stringOffset = StringToAtomCache::LastLookup::offsetOfString();
   2868  size_t lookupSize = sizeof(StringToAtomCache::LastLookup);
   2869  branchPtr(Assembler::Equal, Address(scratch, stringOffset), str, &found);
   2870  branchPtr(Assembler::NotEqual, Address(scratch, lookupSize + stringOffset),
   2871            str, fail);
   2872  addPtr(Imm32(lookupSize), scratch);
   2873 
   2874  // We found a hit in the lastLookups_ array! Load the associated atom
   2875  // and jump back up to our usual atom handling code
   2876  bind(&found);
   2877  size_t atomOffset = StringToAtomCache::LastLookup::offsetOfAtom();
   2878  loadPtr(Address(scratch, atomOffset), output);
   2879  bind(&done);
   2880 }
   2881 
   2882 void MacroAssembler::loadAtomHash(Register id, Register outHash, Label* done) {
   2883  Label doneInner, fatInline;
   2884  if (!done) {
   2885    done = &doneInner;
   2886  }
   2887  move32(Imm32(JSString::FAT_INLINE_MASK), outHash);
   2888  and32(Address(id, JSString::offsetOfFlags()), outHash);
   2889 
   2890  branch32(Assembler::Equal, outHash, Imm32(JSString::FAT_INLINE_MASK),
   2891           &fatInline);
   2892  load32(Address(id, NormalAtom::offsetOfHash()), outHash);
   2893  jump(done);
   2894  bind(&fatInline);
   2895  load32(Address(id, FatInlineAtom::offsetOfHash()), outHash);
   2896  jump(done);
   2897  bind(&doneInner);
   2898 }
   2899 
   2900 void MacroAssembler::loadAtomOrSymbolAndHash(ValueOperand value, Register outId,
   2901                                             Register outHash,
   2902                                             Label* cacheMiss) {
   2903  Label isString, isSymbol, isNull, isUndefined, done, nonAtom, atom;
   2904 
   2905  {
   2906    ScratchTagScope tag(*this, value);
   2907    splitTagForTest(value, tag);
   2908    branchTestString(Assembler::Equal, tag, &isString);
   2909    branchTestSymbol(Assembler::Equal, tag, &isSymbol);
   2910    branchTestNull(Assembler::Equal, tag, &isNull);
   2911    branchTestUndefined(Assembler::NotEqual, tag, cacheMiss);
   2912  }
   2913 
   2914  const JSAtomState& names = runtime()->names();
   2915  movePropertyKey(NameToId(names.undefined), outId);
   2916  move32(Imm32(names.undefined->hash()), outHash);
   2917  jump(&done);
   2918 
   2919  bind(&isNull);
   2920  movePropertyKey(NameToId(names.null), outId);
   2921  move32(Imm32(names.null->hash()), outHash);
   2922  jump(&done);
   2923 
   2924  bind(&isSymbol);
   2925  unboxSymbol(value, outId);
   2926  load32(Address(outId, JS::Symbol::offsetOfHash()), outHash);
   2927  orPtr(Imm32(PropertyKey::SymbolTypeTag), outId);
   2928  jump(&done);
   2929 
   2930  bind(&isString);
   2931  unboxString(value, outId);
   2932  branchTest32(Assembler::Zero, Address(outId, JSString::offsetOfFlags()),
   2933               Imm32(JSString::ATOM_BIT), &nonAtom);
   2934 
   2935  bind(&atom);
   2936  loadAtomHash(outId, outHash, &done);
   2937 
   2938  bind(&nonAtom);
   2939  tryFastAtomize(outId, outHash, outId, cacheMiss);
   2940  jump(&atom);
   2941 
   2942  bind(&done);
   2943 }
   2944 
   2945 void MacroAssembler::emitExtractValueFromMegamorphicCacheEntry(
   2946    Register obj, Register entry, Register scratch1, Register scratch2,
   2947    ValueOperand output, Label* cacheHit, Label* cacheMiss,
   2948    Label* cacheHitGetter) {
   2949  Label isMissing, dynamicSlot, protoLoopHead, protoLoopTail;
   2950 
   2951  // scratch2 = entry->hopsAndKind_
   2952  load8ZeroExtend(
   2953      Address(entry, MegamorphicCache::Entry::offsetOfHopsAndKind()), scratch2);
   2954  // if (scratch2 == NumHopsForMissingProperty) goto isMissing
   2955  branch32(Assembler::Equal, scratch2,
   2956           Imm32(MegamorphicCache::Entry::NumHopsForMissingProperty),
   2957           &isMissing);
   2958 
   2959  if (cacheHitGetter) {
   2960    // Here we're going to set scratch1 to 0 for a data property and 1 for a
   2961    // getter and scratch2 to the number of hops
   2962    Label dataProperty;
   2963 
   2964    // if (scratch2 & NonDataPropertyFlag == 0) goto dataProperty
   2965    move32(Imm32(0), scratch1);
   2966    branchTest32(Assembler::Zero, scratch2,
   2967                 Imm32(MegamorphicCache::Entry::NonDataPropertyFlag),
   2968                 &dataProperty);
   2969 
   2970    // if (scratch2 > NonDataPropertyFlag | MaxHopsForAccessorProperty) goto
   2971    // cacheMiss
   2972    branch32(Assembler::GreaterThan, scratch2,
   2973             Imm32(MegamorphicCache::Entry::NonDataPropertyFlag |
   2974                   MegamorphicCache::Entry::MaxHopsForAccessorProperty),
   2975             cacheMiss);
   2976 
   2977    and32(Imm32(~MegamorphicCache::Entry::NonDataPropertyFlag), scratch2);
   2978    move32(Imm32(1), scratch1);
   2979 
   2980    bind(&dataProperty);
   2981  } else {
   2982    // if (scratch2 & NonDataPropertyFlag) goto cacheMiss
   2983    branchTest32(Assembler::NonZero, scratch2,
   2984                 Imm32(MegamorphicCache::Entry::NonDataPropertyFlag),
   2985                 cacheMiss);
   2986  }
   2987 
   2988  // NOTE: Where this is called, `output` can actually alias `obj`, and before
   2989  // the last cacheMiss branch above we can't write to `obj`, so we can't
   2990  // use `output`'s scratch register there. However a cache miss is impossible
   2991  // now, so we're free to use `output` as we like.
   2992  Register outputScratch = output.scratchReg();
   2993  if (!outputScratch.aliases(obj)) {
   2994    // We're okay with paying this very slight extra cost to avoid a potential
   2995    // footgun of writing to what callers understand as only an input register.
   2996    movePtr(obj, outputScratch);
   2997  }
   2998  branchTest32(Assembler::Zero, scratch2, scratch2, &protoLoopTail);
   2999  bind(&protoLoopHead);
   3000  loadObjProto(outputScratch, outputScratch);
   3001  branchSub32(Assembler::NonZero, Imm32(1), scratch2, &protoLoopHead);
   3002  bind(&protoLoopTail);
   3003 
   3004  // entry = entry->slotOffset()
   3005  load32(Address(entry, MegamorphicCacheEntry::offsetOfSlotOffset()), entry);
   3006 
   3007  // scratch2 = slotOffset.offset()
   3008  rshift32(Imm32(TaggedSlotOffset::OffsetShift), entry, scratch2);
   3009 
   3010  // if (!slotOffset.isFixedSlot()) goto dynamicSlot
   3011  branchTest32(Assembler::Zero, entry, Imm32(TaggedSlotOffset::IsFixedSlotFlag),
   3012               &dynamicSlot);
   3013  // output = outputScratch[scratch2]
   3014  loadValue(BaseIndex(outputScratch, scratch2, TimesOne), output);
   3015  if (cacheHitGetter) {
   3016    branchTest32(Assembler::NonZero, scratch1, scratch1, cacheHitGetter);
   3017  }
   3018  jump(cacheHit);
   3019 
   3020  bind(&dynamicSlot);
   3021  // output = outputScratch->slots_[scratch2]
   3022  loadPtr(Address(outputScratch, NativeObject::offsetOfSlots()), outputScratch);
   3023  loadValue(BaseIndex(outputScratch, scratch2, TimesOne), output);
   3024  if (cacheHitGetter) {
   3025    branchTest32(Assembler::NonZero, scratch1, scratch1, cacheHitGetter);
   3026  }
   3027  jump(cacheHit);
   3028 
   3029  bind(&isMissing);
   3030  // output = undefined
   3031  moveValue(UndefinedValue(), output);
   3032  jump(cacheHit);
   3033 }
   3034 
   3035 template <typename IdOperandType>
   3036 void MacroAssembler::emitMegamorphicCacheLookupByValueCommon(
   3037    IdOperandType id, Register obj, Register scratch1, Register scratch2,
   3038    Register outEntryPtr, Label* cacheMiss, Label* cacheMissWithEntry) {
   3039  // A lot of this code is shared with emitMegamorphicCacheLookup. It would
   3040  // be nice to be able to avoid the duplication here, but due to a few
   3041  // differences like taking the id in a ValueOperand instead of being able
   3042  // to bake it in as an immediate, and only needing a Register for the output
   3043  // value, it seemed more awkward to read once it was deduplicated.
   3044 
   3045  // outEntryPtr = obj->shape()
   3046  loadPtr(Address(obj, JSObject::offsetOfShape()), outEntryPtr);
   3047 
   3048  movePtr(outEntryPtr, scratch2);
   3049 
   3050  // outEntryPtr = (outEntryPtr >> 3) ^ (outEntryPtr >> 13) + idHash
   3051  rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift1), outEntryPtr);
   3052  rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift2), scratch2);
   3053  xorPtr(scratch2, outEntryPtr);
   3054 
   3055  if constexpr (std::is_same<IdOperandType, ValueOperand>::value) {
   3056    loadAtomOrSymbolAndHash(id, scratch1, scratch2, cacheMiss);
   3057  } else {
   3058    static_assert(std::is_same<IdOperandType, Register>::value);
   3059    movePtr(id, scratch1);
   3060    loadAtomHash(scratch1, scratch2, nullptr);
   3061  }
   3062  addPtr(scratch2, outEntryPtr);
   3063 
   3064  // outEntryPtr %= MegamorphicCache::NumEntries
   3065  constexpr size_t cacheSize = MegamorphicCache::NumEntries;
   3066  static_assert(mozilla::IsPowerOfTwo(cacheSize));
   3067  size_t cacheMask = cacheSize - 1;
   3068  and32(Imm32(cacheMask), outEntryPtr);
   3069 
   3070  loadMegamorphicCache(scratch2);
   3071  // outEntryPtr = &scratch2->entries_[outEntryPtr]
   3072  constexpr size_t entrySize = sizeof(MegamorphicCache::Entry);
   3073  static_assert(sizeof(void*) == 4 || entrySize == 24);
   3074  if constexpr (sizeof(void*) == 4) {
   3075    mul32(Imm32(entrySize), outEntryPtr);
   3076    computeEffectiveAddress(BaseIndex(scratch2, outEntryPtr, TimesOne,
   3077                                      MegamorphicCache::offsetOfEntries()),
   3078                            outEntryPtr);
   3079  } else {
   3080    computeEffectiveAddress(BaseIndex(outEntryPtr, outEntryPtr, TimesTwo),
   3081                            outEntryPtr);
   3082    computeEffectiveAddress(BaseIndex(scratch2, outEntryPtr, TimesEight,
   3083                                      MegamorphicCache::offsetOfEntries()),
   3084                            outEntryPtr);
   3085  }
   3086 
   3087  // if (outEntryPtr->key_ != scratch1) goto cacheMissWithEntry
   3088  branchPtr(Assembler::NotEqual,
   3089            Address(outEntryPtr, MegamorphicCache::Entry::offsetOfKey()),
   3090            scratch1, cacheMissWithEntry);
   3091  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch1);
   3092 
   3093  // if (outEntryPtr->shape_ != scratch1) goto cacheMissWithEntry
   3094  branchPtr(Assembler::NotEqual,
   3095            Address(outEntryPtr, MegamorphicCache::Entry::offsetOfShape()),
   3096            scratch1, cacheMissWithEntry);
   3097 
   3098  // scratch2 = scratch2->generation_
   3099  load16ZeroExtend(Address(scratch2, MegamorphicCache::offsetOfGeneration()),
   3100                   scratch2);
   3101  load16ZeroExtend(
   3102      Address(outEntryPtr, MegamorphicCache::Entry::offsetOfGeneration()),
   3103      scratch1);
   3104  // if (outEntryPtr->generation_ != scratch2) goto cacheMissWithEntry
   3105  branch32(Assembler::NotEqual, scratch1, scratch2, cacheMissWithEntry);
   3106 }
   3107 
   3108 void MacroAssembler::emitMegamorphicCacheLookup(
   3109    PropertyKey id, Register obj, Register scratch1, Register scratch2,
   3110    Register outEntryPtr, ValueOperand output, Label* cacheHit,
   3111    Label* cacheHitGetter) {
   3112  Label cacheMiss, isMissing, dynamicSlot, protoLoopHead, protoLoopTail;
   3113 
   3114  // scratch1 = obj->shape()
   3115  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch1);
   3116 
   3117  movePtr(scratch1, outEntryPtr);
   3118  movePtr(scratch1, scratch2);
   3119 
   3120  // outEntryPtr = (scratch1 >> 3) ^ (scratch1 >> 13) + hash(id)
   3121  rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift1), outEntryPtr);
   3122  rshiftPtr(Imm32(MegamorphicCache::ShapeHashShift2), scratch2);
   3123  xorPtr(scratch2, outEntryPtr);
   3124  addPtr(Imm32(HashPropertyKeyThreadSafe(id)), outEntryPtr);
   3125 
   3126  // outEntryPtr %= MegamorphicCache::NumEntries
   3127  constexpr size_t cacheSize = MegamorphicCache::NumEntries;
   3128  static_assert(mozilla::IsPowerOfTwo(cacheSize));
   3129  size_t cacheMask = cacheSize - 1;
   3130  and32(Imm32(cacheMask), outEntryPtr);
   3131 
   3132  loadMegamorphicCache(scratch2);
   3133  // outEntryPtr = &scratch2->entries_[outEntryPtr]
   3134  constexpr size_t entrySize = sizeof(MegamorphicCache::Entry);
   3135  static_assert(sizeof(void*) == 4 || entrySize == 24);
   3136  if constexpr (sizeof(void*) == 4) {
   3137    mul32(Imm32(entrySize), outEntryPtr);
   3138    computeEffectiveAddress(BaseIndex(scratch2, outEntryPtr, TimesOne,
   3139                                      MegamorphicCache::offsetOfEntries()),
   3140                            outEntryPtr);
   3141  } else {
   3142    computeEffectiveAddress(BaseIndex(outEntryPtr, outEntryPtr, TimesTwo),
   3143                            outEntryPtr);
   3144    computeEffectiveAddress(BaseIndex(scratch2, outEntryPtr, TimesEight,
   3145                                      MegamorphicCache::offsetOfEntries()),
   3146                            outEntryPtr);
   3147  }
   3148 
   3149  // if (outEntryPtr->shape_ != scratch1) goto cacheMiss
   3150  branchPtr(Assembler::NotEqual,
   3151            Address(outEntryPtr, MegamorphicCache::Entry::offsetOfShape()),
   3152            scratch1, &cacheMiss);
   3153 
   3154  // if (outEntryPtr->key_ != id) goto cacheMiss
   3155  movePropertyKey(id, scratch1);
   3156  branchPtr(Assembler::NotEqual,
   3157            Address(outEntryPtr, MegamorphicCache::Entry::offsetOfKey()),
   3158            scratch1, &cacheMiss);
   3159 
   3160  // scratch2 = scratch2->generation_
   3161  load16ZeroExtend(Address(scratch2, MegamorphicCache::offsetOfGeneration()),
   3162                   scratch2);
   3163  load16ZeroExtend(
   3164      Address(outEntryPtr, MegamorphicCache::Entry::offsetOfGeneration()),
   3165      scratch1);
   3166  // if (outEntryPtr->generation_ != scratch2) goto cacheMiss
   3167  branch32(Assembler::NotEqual, scratch1, scratch2, &cacheMiss);
   3168 
   3169  emitExtractValueFromMegamorphicCacheEntry(obj, outEntryPtr, scratch1,
   3170                                            scratch2, output, cacheHit,
   3171                                            &cacheMiss, cacheHitGetter);
   3172 
   3173  bind(&cacheMiss);
   3174 }
   3175 
   3176 template <typename IdOperandType>
   3177 void MacroAssembler::emitMegamorphicCacheLookupByValue(
   3178    IdOperandType id, Register obj, Register scratch1, Register scratch2,
   3179    Register outEntryPtr, ValueOperand output, Label* cacheHit,
   3180    Label* cacheHitGetter) {
   3181  Label cacheMiss, cacheMissWithEntry;
   3182  emitMegamorphicCacheLookupByValueCommon(id, obj, scratch1, scratch2,
   3183                                          outEntryPtr, &cacheMiss,
   3184                                          &cacheMissWithEntry);
   3185  emitExtractValueFromMegamorphicCacheEntry(
   3186      obj, outEntryPtr, scratch1, scratch2, output, cacheHit,
   3187      &cacheMissWithEntry, cacheHitGetter);
   3188  bind(&cacheMiss);
   3189  xorPtr(outEntryPtr, outEntryPtr);
   3190  bind(&cacheMissWithEntry);
   3191 }
   3192 
   3193 template void MacroAssembler::emitMegamorphicCacheLookupByValue<ValueOperand>(
   3194    ValueOperand id, Register obj, Register scratch1, Register scratch2,
   3195    Register outEntryPtr, ValueOperand output, Label* cacheHit,
   3196    Label* cacheHitGetter);
   3197 
   3198 template void MacroAssembler::emitMegamorphicCacheLookupByValue<Register>(
   3199    Register id, Register obj, Register scratch1, Register scratch2,
   3200    Register outEntryPtr, ValueOperand output, Label* cacheHit,
   3201    Label* cacheHitGetter);
   3202 
   3203 void MacroAssembler::emitMegamorphicCacheLookupExists(
   3204    ValueOperand id, Register obj, Register scratch1, Register scratch2,
   3205    Register outEntryPtr, Register output, Label* cacheHit, bool hasOwn) {
   3206  Label cacheMiss, cacheMissWithEntry, cacheHitFalse;
   3207  emitMegamorphicCacheLookupByValueCommon(id, obj, scratch1, scratch2,
   3208                                          outEntryPtr, &cacheMiss,
   3209                                          &cacheMissWithEntry);
   3210 
   3211  // scratch1 = outEntryPtr->hopsAndKind_
   3212  load8ZeroExtend(
   3213      Address(outEntryPtr, MegamorphicCache::Entry::offsetOfHopsAndKind()),
   3214      scratch1);
   3215 
   3216  branch32(Assembler::Equal, scratch1,
   3217           Imm32(MegamorphicCache::Entry::NumHopsForMissingProperty),
   3218           &cacheHitFalse);
   3219  branchTest32(Assembler::NonZero, scratch1,
   3220               Imm32(MegamorphicCache::Entry::NonDataPropertyFlag),
   3221               &cacheMissWithEntry);
   3222 
   3223  if (hasOwn) {
   3224    branch32(Assembler::NotEqual, scratch1, Imm32(0), &cacheHitFalse);
   3225  }
   3226 
   3227  move32(Imm32(1), output);
   3228  jump(cacheHit);
   3229 
   3230  bind(&cacheHitFalse);
   3231  xor32(output, output);
   3232  jump(cacheHit);
   3233 
   3234  bind(&cacheMiss);
   3235  xorPtr(outEntryPtr, outEntryPtr);
   3236  bind(&cacheMissWithEntry);
   3237 }
   3238 
   3239 void MacroAssembler::extractCurrentIndexAndKindFromIterator(Register iterator,
   3240                                                            Register outIndex,
   3241                                                            Register outKind) {
   3242  // Load iterator object
   3243  Address nativeIterAddr(iterator,
   3244                         PropertyIteratorObject::offsetOfIteratorSlot());
   3245  loadPrivate(nativeIterAddr, outIndex);
   3246 
   3247  // Load the property count into outKind.
   3248  load32(Address(outIndex, NativeIterator::offsetOfPropertyCount()), outKind);
   3249 
   3250  // We need two bits of wiggle room in a u32 here for the logic below.
   3251  static_assert(NativeIterator::PropCountLimit <= 1 << 30);
   3252 
   3253  // Shift up the property count on 64 bit. Ultimately we want
   3254  // sizeof(IteratorProperty) * count + sizeof(PropertyIndex) * cursor.
   3255  // If we shift up our count to be on the same scale as cursor right now,
   3256  // we can do this all with one register.
   3257  static_assert(sizeof(IteratorProperty) == sizeof(PropertyIndex) ||
   3258                sizeof(IteratorProperty) == sizeof(PropertyIndex) * 2);
   3259  if constexpr (sizeof(IteratorProperty) > sizeof(PropertyIndex)) {
   3260    lshift32(Imm32(1), outKind);
   3261  }
   3262 
   3263  // Add the current cursor. This is a uint32_t which has already been
   3264  // incremented in iteration to index the *next* property, so we'll want to
   3265  // keep that in mind in our final address calculation.
   3266  add32(Address(outIndex, NativeIterator::offsetOfPropertyCursor()), outKind);
   3267 
   3268  // outKind holds the offset in u32's to our PropertyIndex, so just multiply
   3269  // by four, add it to the offset of the first property, and subtract a
   3270  // PropertyIndex since we know we already incremented.
   3271  load32(BaseIndex(outIndex, outKind, Scale::TimesFour,
   3272                   NativeIterator::offsetOfFirstProperty() -
   3273                       int32_t(sizeof(PropertyIndex))),
   3274         outIndex);
   3275 
   3276  // Extract kind.
   3277  rshift32(Imm32(PropertyIndex::KindShift), outIndex, outKind);
   3278 
   3279  // Extract index.
   3280  and32(Imm32(PropertyIndex::IndexMask), outIndex);
   3281 }
   3282 
   3283 void MacroAssembler::extractIndexAndKindFromIteratorByIterIndex(
   3284    Register iterator, Register inIndex, Register outKind, Register outIndex) {
   3285  // Load iterator object
   3286  Address nativeIterAddr(iterator,
   3287                         PropertyIteratorObject::offsetOfIteratorSlot());
   3288  loadPrivate(nativeIterAddr, outIndex);
   3289 
   3290  // Load the property count into outKind.
   3291  load32(Address(outIndex, NativeIterator::offsetOfPropertyCount()), outKind);
   3292 
   3293  // We need two bits of wiggle room in a u32 here for the logic below.
   3294  static_assert(NativeIterator::PropCountLimit <= 1 << 30);
   3295 
   3296  // Shift up the property count on 64 bit. Ultimately we want
   3297  // sizeof(IteratorProperty) * count + sizeof(PropertyIndex) * cursor.
   3298  // If we shift up our count to be on the same scale as cursor right now,
   3299  // we can do this all with one register.
   3300  static_assert(sizeof(IteratorProperty) == sizeof(PropertyIndex) ||
   3301                sizeof(IteratorProperty) == sizeof(PropertyIndex) * 2);
   3302  if constexpr (sizeof(IteratorProperty) > sizeof(PropertyIndex)) {
   3303    lshift32(Imm32(1), outKind);
   3304  }
   3305 
   3306  // Add the index
   3307  add32(inIndex, outKind);
   3308 
   3309  // outKind holds the offset in u32's to our PropertyIndex, so just multiply
   3310  // by four and add it to the offset of the first property
   3311  load32(BaseIndex(outIndex, outKind, Scale::TimesFour,
   3312                   NativeIterator::offsetOfFirstProperty()),
   3313         outIndex);
   3314 
   3315  // Extract kind.
   3316  rshift32(Imm32(PropertyIndex::KindShift), outIndex, outKind);
   3317 
   3318  // Extract index.
   3319  and32(Imm32(PropertyIndex::IndexMask), outIndex);
   3320 }
   3321 
   3322 template <typename IdType>
   3323 void MacroAssembler::emitMegamorphicCachedSetSlot(
   3324    IdType id, Register obj, Register scratch1,
   3325 #ifndef JS_CODEGEN_X86  // See MegamorphicSetElement in LIROps.yaml
   3326    Register scratch2, Register scratch3,
   3327 #endif
   3328    ValueOperand value, const LiveRegisterSet& liveRegs, Label* cacheHit,
   3329    void (*emitPreBarrier)(MacroAssembler&, const Address&, MIRType)) {
   3330  Label cacheMiss, dynamicSlot, doAdd, doSet, doAddDynamic, doSetDynamic;
   3331 
   3332 #ifdef JS_CODEGEN_X86
   3333  pushValue(value);
   3334  Register scratch2 = value.typeReg();
   3335  Register scratch3 = value.payloadReg();
   3336 #endif
   3337 
   3338  // outEntryPtr = obj->shape()
   3339  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch3);
   3340 
   3341  movePtr(scratch3, scratch2);
   3342 
   3343  // scratch3 = (scratch3 >> 3) ^ (scratch3 >> 13) + idHash
   3344  rshiftPtr(Imm32(MegamorphicSetPropCache::ShapeHashShift1), scratch3);
   3345  rshiftPtr(Imm32(MegamorphicSetPropCache::ShapeHashShift2), scratch2);
   3346  xorPtr(scratch2, scratch3);
   3347 
   3348  if constexpr (std::is_same<IdType, ValueOperand>::value) {
   3349    loadAtomOrSymbolAndHash(id, scratch1, scratch2, &cacheMiss);
   3350    addPtr(scratch2, scratch3);
   3351  } else {
   3352    static_assert(std::is_same<IdType, PropertyKey>::value);
   3353    addPtr(Imm32(HashPropertyKeyThreadSafe(id)), scratch3);
   3354    movePropertyKey(id, scratch1);
   3355  }
   3356 
   3357  // scratch3 %= MegamorphicSetPropCache::NumEntries
   3358  constexpr size_t cacheSize = MegamorphicSetPropCache::NumEntries;
   3359  static_assert(mozilla::IsPowerOfTwo(cacheSize));
   3360  size_t cacheMask = cacheSize - 1;
   3361  and32(Imm32(cacheMask), scratch3);
   3362 
   3363  loadMegamorphicSetPropCache(scratch2);
   3364  // scratch3 = &scratch2->entries_[scratch3]
   3365  constexpr size_t entrySize = sizeof(MegamorphicSetPropCache::Entry);
   3366  mul32(Imm32(entrySize), scratch3);
   3367  computeEffectiveAddress(BaseIndex(scratch2, scratch3, TimesOne,
   3368                                    MegamorphicSetPropCache::offsetOfEntries()),
   3369                          scratch3);
   3370 
   3371  // if (scratch3->key_ != scratch1) goto cacheMiss
   3372  branchPtr(Assembler::NotEqual,
   3373            Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfKey()),
   3374            scratch1, &cacheMiss);
   3375 
   3376  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch1);
   3377  // if (scratch3->shape_ != scratch1) goto cacheMiss
   3378  branchPtr(Assembler::NotEqual,
   3379            Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfShape()),
   3380            scratch1, &cacheMiss);
   3381 
   3382  // scratch2 = scratch2->generation_
   3383  load16ZeroExtend(
   3384      Address(scratch2, MegamorphicSetPropCache::offsetOfGeneration()),
   3385      scratch2);
   3386  load16ZeroExtend(
   3387      Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfGeneration()),
   3388      scratch1);
   3389  // if (scratch3->generation_ != scratch2) goto cacheMiss
   3390  branch32(Assembler::NotEqual, scratch1, scratch2, &cacheMiss);
   3391 
   3392  // scratch2 = entry->slotOffset()
   3393  load32(
   3394      Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfSlotOffset()),
   3395      scratch2);
   3396 
   3397  // scratch1 = slotOffset.offset()
   3398  rshift32(Imm32(TaggedSlotOffset::OffsetShift), scratch2, scratch1);
   3399 
   3400  Address afterShapePtr(scratch3,
   3401                        MegamorphicSetPropCache::Entry::offsetOfAfterShape());
   3402 
   3403  // if (!slotOffset.isFixedSlot()) goto dynamicSlot
   3404  branchTest32(Assembler::Zero, scratch2,
   3405               Imm32(TaggedSlotOffset::IsFixedSlotFlag), &dynamicSlot);
   3406 
   3407  // Calculate slot address in scratch1. Jump to doSet if scratch3 == nullptr,
   3408  // else jump (or fall-through) to doAdd.
   3409  addPtr(obj, scratch1);
   3410  branchPtr(Assembler::Equal, afterShapePtr, ImmPtr(nullptr), &doSet);
   3411  jump(&doAdd);
   3412 
   3413  bind(&dynamicSlot);
   3414  branchPtr(Assembler::Equal, afterShapePtr, ImmPtr(nullptr), &doSetDynamic);
   3415 
   3416  Address slotAddr(scratch1, 0);
   3417 
   3418  // If entry->newCapacity_ is nonzero, we need to grow the slots on the
   3419  // object. Otherwise just jump straight to a dynamic add.
   3420  load16ZeroExtend(
   3421      Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfNewCapacity()),
   3422      scratch2);
   3423  branchTest32(Assembler::Zero, scratch2, scratch2, &doAddDynamic);
   3424 
   3425  LiveRegisterSet save;
   3426  save.set() = RegisterSet::Intersect(liveRegs.set(), RegisterSet::Volatile());
   3427  save.addUnchecked(scratch1);   // Used as call temp below.
   3428  save.takeUnchecked(scratch2);  // Used for the return value.
   3429  PushRegsInMask(save);
   3430 
   3431  using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount);
   3432  setupUnalignedABICall(scratch1);
   3433  loadJSContext(scratch1);
   3434  passABIArg(scratch1);
   3435  passABIArg(obj);
   3436  passABIArg(scratch2);
   3437  callWithABI<Fn, NativeObject::growSlotsPure>();
   3438  storeCallPointerResult(scratch2);
   3439 
   3440  MOZ_ASSERT(!save.has(scratch2));
   3441  PopRegsInMask(save);
   3442 
   3443  branchIfFalseBool(scratch2, &cacheMiss);
   3444 
   3445  bind(&doAddDynamic);
   3446  addPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
   3447 
   3448  bind(&doAdd);
   3449  // scratch3 = entry->afterShape()
   3450  loadPtr(
   3451      Address(scratch3, MegamorphicSetPropCache::Entry::offsetOfAfterShape()),
   3452      scratch3);
   3453 
   3454  storeObjShape(scratch3, obj,
   3455                [emitPreBarrier](MacroAssembler& masm, const Address& addr) {
   3456                  emitPreBarrier(masm, addr, MIRType::Shape);
   3457                });
   3458 #ifdef JS_CODEGEN_X86
   3459  popValue(value);
   3460 #endif
   3461  storeValue(value, slotAddr);
   3462  jump(cacheHit);
   3463 
   3464  bind(&doSetDynamic);
   3465  addPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
   3466  bind(&doSet);
   3467  guardedCallPreBarrier(slotAddr, MIRType::Value);
   3468 
   3469 #ifdef JS_CODEGEN_X86
   3470  popValue(value);
   3471 #endif
   3472  storeValue(value, slotAddr);
   3473  jump(cacheHit);
   3474 
   3475  bind(&cacheMiss);
   3476 #ifdef JS_CODEGEN_X86
   3477  popValue(value);
   3478 #endif
   3479 }
   3480 
   3481 template void MacroAssembler::emitMegamorphicCachedSetSlot<PropertyKey>(
   3482    PropertyKey id, Register obj, Register scratch1,
   3483 #ifndef JS_CODEGEN_X86  // See MegamorphicSetElement in LIROps.yaml
   3484    Register scratch2, Register scratch3,
   3485 #endif
   3486    ValueOperand value, const LiveRegisterSet& liveRegs, Label* cacheHit,
   3487    void (*emitPreBarrier)(MacroAssembler&, const Address&, MIRType));
   3488 
   3489 template void MacroAssembler::emitMegamorphicCachedSetSlot<ValueOperand>(
   3490    ValueOperand id, Register obj, Register scratch1,
   3491 #ifndef JS_CODEGEN_X86  // See MegamorphicSetElement in LIROps.yaml
   3492    Register scratch2, Register scratch3,
   3493 #endif
   3494    ValueOperand value, const LiveRegisterSet& liveRegs, Label* cacheHit,
   3495    void (*emitPreBarrier)(MacroAssembler&, const Address&, MIRType));
   3496 
   3497 void MacroAssembler::guardNonNegativeIntPtrToInt32(Register reg, Label* fail) {
   3498 #ifdef DEBUG
   3499  Label ok;
   3500  branchTestPtr(Assembler::NotSigned, reg, reg, &ok);
   3501  assumeUnreachable("Unexpected negative value");
   3502  bind(&ok);
   3503 #endif
   3504 
   3505 #ifdef JS_64BIT
   3506  branchPtr(Assembler::Above, reg, Imm32(INT32_MAX), fail);
   3507 #endif
   3508 }
   3509 
   3510 void MacroAssembler::loadArrayBufferByteLengthIntPtr(Register obj,
   3511                                                     Register output) {
   3512  Address slotAddr(obj, ArrayBufferObject::offsetOfByteLengthSlot());
   3513  loadPrivate(slotAddr, output);
   3514 }
   3515 
   3516 void MacroAssembler::loadArrayBufferViewByteOffsetIntPtr(Register obj,
   3517                                                         Register output) {
   3518  Address slotAddr(obj, ArrayBufferViewObject::byteOffsetOffset());
   3519  loadPrivate(slotAddr, output);
   3520 }
   3521 
   3522 void MacroAssembler::loadArrayBufferViewLengthIntPtr(Register obj,
   3523                                                     Register output) {
   3524  Address slotAddr(obj, ArrayBufferViewObject::lengthOffset());
   3525  loadPrivate(slotAddr, output);
   3526 }
   3527 
   3528 void MacroAssembler::loadGrowableSharedArrayBufferByteLengthIntPtr(
   3529    Synchronization sync, Register obj, Register output) {
   3530  // Load the SharedArrayRawBuffer.
   3531  loadPrivate(Address(obj, SharedArrayBufferObject::rawBufferOffset()), output);
   3532 
   3533  memoryBarrierBefore(sync);
   3534 
   3535  // Load the byteLength of the SharedArrayRawBuffer into |output|.
   3536  static_assert(sizeof(mozilla::Atomic<size_t>) == sizeof(size_t));
   3537  loadPtr(Address(output, SharedArrayRawBuffer::offsetOfByteLength()), output);
   3538 
   3539  memoryBarrierAfter(sync);
   3540 }
   3541 
   3542 void MacroAssembler::loadResizableArrayBufferViewLengthIntPtr(
   3543    ResizableArrayBufferView view, Synchronization sync, Register obj,
   3544    Register output, Register scratch) {
   3545  // Inline implementation of ArrayBufferViewObject::length(), when the input is
   3546  // guaranteed to be a resizable arraybuffer view object.
   3547 
   3548  loadArrayBufferViewLengthIntPtr(obj, output);
   3549 
   3550  Label done;
   3551  branchPtr(Assembler::NotEqual, output, ImmWord(0), &done);
   3552 
   3553  // Load obj->elements in |scratch|.
   3554  loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
   3555 
   3556  // If backed by non-shared memory, detached and out-of-bounds both return
   3557  // zero, so we're done here.
   3558  branchTest32(Assembler::Zero,
   3559               Address(scratch, ObjectElements::offsetOfFlags()),
   3560               Imm32(ObjectElements::SHARED_MEMORY), &done);
   3561 
   3562  // Load the auto-length slot.
   3563  unboxBoolean(Address(obj, ArrayBufferViewObject::autoLengthOffset()),
   3564               scratch);
   3565 
   3566  // If non-auto length, there's nothing to do.
   3567  branchTest32(Assembler::Zero, scratch, scratch, &done);
   3568 
   3569  // Load bufferByteLength into |output|.
   3570  {
   3571    // Resizable TypedArrays are guaranteed to have an ArrayBuffer.
   3572    unboxObject(Address(obj, ArrayBufferViewObject::bufferOffset()), output);
   3573 
   3574    // Load the byte length from the raw-buffer of growable SharedArrayBuffers.
   3575    loadGrowableSharedArrayBufferByteLengthIntPtr(sync, output, output);
   3576  }
   3577 
   3578  // Load the byteOffset into |scratch|.
   3579  loadArrayBufferViewByteOffsetIntPtr(obj, scratch);
   3580 
   3581  // Compute the accessible byte length |bufferByteLength - byteOffset|.
   3582  subPtr(scratch, output);
   3583 
   3584  if (view == ResizableArrayBufferView::TypedArray) {
   3585    // Compute the array length from the byte length.
   3586    resizableTypedArrayElementShiftBy(obj, output, scratch);
   3587  }
   3588 
   3589  bind(&done);
   3590 }
   3591 
   3592 void MacroAssembler::dateFillLocalTimeSlots(
   3593    Register obj, Register scratch, const LiveRegisterSet& volatileRegs) {
   3594  // Inline implementation of the cache check from
   3595  // DateObject::fillLocalTimeSlots(). Only used when no realm time zone
   3596  // override is active.
   3597 
   3598  Label callVM, done;
   3599 
   3600  // Check if the cache is already populated.
   3601  branchTestUndefined(Assembler::Equal,
   3602                      Address(obj, DateObject::offsetOfLocalTimeSlot()),
   3603                      &callVM);
   3604 
   3605  unboxInt32(Address(obj, DateObject::offsetOfTimeZoneCacheKeySlot()), scratch);
   3606 
   3607  branch32(Assembler::Equal,
   3608           AbsoluteAddress(DateTimeInfo::addressOfUTCToLocalOffsetSeconds()),
   3609           scratch, &done);
   3610 
   3611  bind(&callVM);
   3612  {
   3613    PushRegsInMask(volatileRegs);
   3614 
   3615    using Fn = void (*)(DateObject*);
   3616    setupUnalignedABICall(scratch);
   3617    passABIArg(obj);
   3618    callWithABI<Fn, jit::DateFillLocalTimeSlots>();
   3619 
   3620    PopRegsInMask(volatileRegs);
   3621  }
   3622 
   3623  bind(&done);
   3624 }
   3625 
   3626 void MacroAssembler::udiv32ByConstant(Register src, uint32_t divisor,
   3627                                      Register dest) {
   3628  auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(divisor);
   3629  MOZ_ASSERT(rmc.multiplier <= UINT32_MAX, "division needs scratch register");
   3630 
   3631  // We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
   3632  mulHighUnsigned32(Imm32(rmc.multiplier), src, dest);
   3633 
   3634  // Finish the computation |q = floor(n / d)|.
   3635  rshift32(Imm32(rmc.shiftAmount), dest);
   3636 }
   3637 
   3638 void MacroAssembler::umod32ByConstant(Register src, uint32_t divisor,
   3639                                      Register dest, Register scratch) {
   3640  MOZ_ASSERT(dest != scratch);
   3641 
   3642  auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(divisor);
   3643  MOZ_ASSERT(rmc.multiplier <= UINT32_MAX, "division needs scratch register");
   3644 
   3645  if (src != dest) {
   3646    move32(src, dest);
   3647  }
   3648 
   3649  // We first compute |q = (M * n) >> 32), where M = rmc.multiplier.
   3650  mulHighUnsigned32(Imm32(rmc.multiplier), dest, scratch);
   3651 
   3652  // Finish the computation |q = floor(n / d)|.
   3653  rshift32(Imm32(rmc.shiftAmount), scratch);
   3654 
   3655  // Compute the remainder from |r = n - q * d|.
   3656  mul32(Imm32(divisor), scratch);
   3657  sub32(scratch, dest);
   3658 }
   3659 
   3660 template <typename GetTimeFn>
   3661 void MacroAssembler::dateTimeFromSecondsIntoYear(ValueOperand secondsIntoYear,
   3662                                                 ValueOperand output,
   3663                                                 Register scratch1,
   3664                                                 Register scratch2,
   3665                                                 GetTimeFn getTimeFn) {
   3666 #ifdef DEBUG
   3667  Label okValue;
   3668  branchTestInt32(Assembler::Equal, secondsIntoYear, &okValue);
   3669  branchTestNaNValue(Assembler::Equal, secondsIntoYear, scratch1, &okValue);
   3670  assumeUnreachable("secondsIntoYear is an int32 or NaN");
   3671  bind(&okValue);
   3672 #endif
   3673 
   3674  moveValue(secondsIntoYear, output);
   3675 
   3676  Label done;
   3677  fallibleUnboxInt32(secondsIntoYear, scratch1, &done);
   3678 
   3679 #ifdef DEBUG
   3680  Label okInt;
   3681  branchTest32(Assembler::NotSigned, scratch1, scratch1, &okInt);
   3682  assumeUnreachable("secondsIntoYear is an unsigned int32");
   3683  bind(&okInt);
   3684 #endif
   3685 
   3686  getTimeFn(scratch1, scratch1, scratch2);
   3687 
   3688  tagValue(JSVAL_TYPE_INT32, scratch1, output);
   3689 
   3690  bind(&done);
   3691 }
   3692 
   3693 void MacroAssembler::dateHoursFromSecondsIntoYear(ValueOperand secondsIntoYear,
   3694                                                  ValueOperand output,
   3695                                                  Register scratch1,
   3696                                                  Register scratch2) {
   3697  // Inline implementation of seconds-into-year to local hours computation from
   3698  // date_getHours.
   3699 
   3700  // Compute `(yearSeconds / SecondsPerHour) % HoursPerDay`.
   3701  auto hoursFromSecondsIntoYear = [this](Register src, Register dest,
   3702                                         Register scratch) {
   3703    udiv32ByConstant(src, SecondsPerHour, dest);
   3704    umod32ByConstant(dest, HoursPerDay, dest, scratch);
   3705  };
   3706 
   3707  dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
   3708                              hoursFromSecondsIntoYear);
   3709 }
   3710 
   3711 void MacroAssembler::dateMinutesFromSecondsIntoYear(
   3712    ValueOperand secondsIntoYear, ValueOperand output, Register scratch1,
   3713    Register scratch2) {
   3714  // Inline implementation of seconds-into-year to local minutes computation
   3715  // from date_getMinutes.
   3716 
   3717  // Compute `(yearSeconds / SecondsPerMinute) % MinutesPerHour`.
   3718  auto minutesFromSecondsIntoYear = [this](Register src, Register dest,
   3719                                           Register scratch) {
   3720    udiv32ByConstant(src, SecondsPerMinute, dest);
   3721    umod32ByConstant(dest, MinutesPerHour, dest, scratch);
   3722  };
   3723 
   3724  dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
   3725                              minutesFromSecondsIntoYear);
   3726 }
   3727 
   3728 void MacroAssembler::dateSecondsFromSecondsIntoYear(
   3729    ValueOperand secondsIntoYear, ValueOperand output, Register scratch1,
   3730    Register scratch2) {
   3731  // Inline implementation of seconds-into-year to local seconds computation
   3732  // from date_getSeconds.
   3733 
   3734  // Compute `yearSeconds % SecondsPerMinute`.
   3735  auto secondsFromSecondsIntoYear = [this](Register src, Register dest,
   3736                                           Register scratch) {
   3737    umod32ByConstant(src, SecondsPerMinute, dest, scratch);
   3738  };
   3739 
   3740  dateTimeFromSecondsIntoYear(secondsIntoYear, output, scratch1, scratch2,
   3741                              secondsFromSecondsIntoYear);
   3742 }
   3743 
   3744 void MacroAssembler::computeImplicitThis(Register env, ValueOperand output,
   3745                                         Label* slowPath) {
   3746  // Inline implementation of ComputeImplicitThis.
   3747 
   3748  Register scratch = output.scratchReg();
   3749  MOZ_ASSERT(scratch != env);
   3750 
   3751  loadObjClassUnsafe(env, scratch);
   3752 
   3753  // Go to the slow path for possible debug environment proxies.
   3754  branchTestClassIsProxy(true, scratch, slowPath);
   3755 
   3756  // WithEnvironmentObjects have an actual implicit |this|.
   3757  Label nonWithEnv, done;
   3758  branchPtr(Assembler::NotEqual, scratch,
   3759            ImmPtr(&WithEnvironmentObject::class_), &nonWithEnv);
   3760  {
   3761    if (JitOptions.spectreObjectMitigations) {
   3762      spectreZeroRegister(Assembler::NotEqual, scratch, env);
   3763    }
   3764 
   3765    loadValue(Address(env, WithEnvironmentObject::offsetOfThisSlot()), output);
   3766 
   3767    jump(&done);
   3768  }
   3769  bind(&nonWithEnv);
   3770 
   3771  // The implicit |this| is |undefined| for all environment types except
   3772  // WithEnvironmentObject.
   3773  moveValue(JS::UndefinedValue(), output);
   3774 
   3775  bind(&done);
   3776 }
   3777 
   3778 void MacroAssembler::loadDOMExpandoValueGuardGeneration(
   3779    Register obj, ValueOperand output,
   3780    JS::ExpandoAndGeneration* expandoAndGeneration, uint64_t generation,
   3781    Label* fail) {
   3782  loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()),
   3783          output.scratchReg());
   3784  loadValue(Address(output.scratchReg(),
   3785                    js::detail::ProxyReservedSlots::offsetOfPrivateSlot()),
   3786            output);
   3787 
   3788  // Guard the ExpandoAndGeneration* matches the proxy's ExpandoAndGeneration
   3789  // privateSlot.
   3790  branchTestValue(Assembler::NotEqual, output,
   3791                  PrivateValue(expandoAndGeneration), fail);
   3792 
   3793  // Guard expandoAndGeneration->generation matches the expected generation.
   3794  Address generationAddr(output.payloadOrValueReg(),
   3795                         JS::ExpandoAndGeneration::offsetOfGeneration());
   3796  branch64(Assembler::NotEqual, generationAddr, Imm64(generation), fail);
   3797 
   3798  // Load expandoAndGeneration->expando into the output Value register.
   3799  loadValue(Address(output.payloadOrValueReg(),
   3800                    JS::ExpandoAndGeneration::offsetOfExpando()),
   3801            output);
   3802 }
   3803 
   3804 void MacroAssembler::loadJitActivation(Register dest) {
   3805  loadJSContext(dest);
   3806  loadPtr(Address(dest, offsetof(JSContext, activation_)), dest);
   3807 }
   3808 
   3809 void MacroAssembler::loadBaselineCompileQueue(Register dest) {
   3810  loadPtr(AbsoluteAddress(ContextRealmPtr(runtime())), dest);
   3811  computeEffectiveAddress(Address(dest, Realm::offsetOfBaselineCompileQueue()),
   3812                          dest);
   3813 }
   3814 
   3815 void MacroAssembler::guardSpecificAtom(Register str, JSOffThreadAtom* atom,
   3816                                       Register scratch,
   3817                                       const LiveRegisterSet& volatileRegs,
   3818                                       Label* fail) {
   3819  Label done, notCachedAtom;
   3820  branchPtr(Assembler::Equal, str, ImmGCPtr(atom), &done);
   3821 
   3822  // The pointers are not equal, so if the input string is also an atom it
   3823  // must be a different string.
   3824  branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
   3825               Imm32(JSString::ATOM_BIT), fail);
   3826 
   3827  // Try to do a cheap atomize on the string and repeat the above test
   3828  tryFastAtomize(str, scratch, scratch, &notCachedAtom);
   3829  branchPtr(Assembler::Equal, scratch, ImmGCPtr(atom), &done);
   3830  jump(fail);
   3831  bind(&notCachedAtom);
   3832 
   3833  // Check the length.
   3834  branch32(Assembler::NotEqual, Address(str, JSString::offsetOfLength()),
   3835           Imm32(atom->length()), fail);
   3836 
   3837  // Compare short atoms using inline assembly.
   3838  if (canCompareStringCharsInline(atom)) {
   3839    // Pure two-byte strings can't be equal to Latin-1 strings.
   3840    if (atom->hasTwoByteChars()) {
   3841      JS::AutoCheckCannotGC nogc;
   3842      if (!mozilla::IsUtf16Latin1(atom->twoByteRange(nogc))) {
   3843        branchLatin1String(str, fail);
   3844      }
   3845    }
   3846 
   3847    // Call into the VM when the input is a rope or has a different encoding.
   3848    Label vmCall;
   3849 
   3850    // Load the input string's characters.
   3851    Register stringChars = scratch;
   3852    loadStringCharsForCompare(str, atom, stringChars, &vmCall);
   3853 
   3854    // Start comparing character by character.
   3855    branchIfNotStringCharsEquals(stringChars, atom, fail);
   3856 
   3857    // Falls through if both strings are equal.
   3858    jump(&done);
   3859 
   3860    bind(&vmCall);
   3861  }
   3862 
   3863  // We have a non-atomized string with the same length. Call a helper
   3864  // function to do the comparison.
   3865  PushRegsInMask(volatileRegs);
   3866 
   3867  using Fn = bool (*)(JSString* str1, JSString* str2);
   3868  setupUnalignedABICall(scratch);
   3869  movePtr(ImmGCPtr(atom), scratch);
   3870  passABIArg(scratch);
   3871  passABIArg(str);
   3872  callWithABI<Fn, EqualStringsHelperPure>();
   3873  storeCallPointerResult(scratch);
   3874 
   3875  MOZ_ASSERT(!volatileRegs.has(scratch));
   3876  PopRegsInMask(volatileRegs);
   3877  branchIfFalseBool(scratch, fail);
   3878 
   3879  bind(&done);
   3880 }
   3881 
   3882 void MacroAssembler::guardStringToInt32(Register str, Register output,
   3883                                        Register scratch,
   3884                                        LiveRegisterSet volatileRegs,
   3885                                        Label* fail) {
   3886  Label vmCall, done;
   3887  // Use indexed value as fast path if possible.
   3888  loadStringIndexValue(str, output, &vmCall);
   3889  jump(&done);
   3890  {
   3891    bind(&vmCall);
   3892 
   3893    // Reserve space for holding the result int32_t of the call. Use
   3894    // pointer-size to avoid misaligning the stack on 64-bit platforms.
   3895    reserveStack(sizeof(uintptr_t));
   3896    moveStackPtrTo(output);
   3897 
   3898    volatileRegs.takeUnchecked(scratch);
   3899    if (output.volatile_()) {
   3900      volatileRegs.addUnchecked(output);
   3901    }
   3902    PushRegsInMask(volatileRegs);
   3903 
   3904    using Fn = bool (*)(JSContext* cx, JSString* str, int32_t* result);
   3905    setupUnalignedABICall(scratch);
   3906    loadJSContext(scratch);
   3907    passABIArg(scratch);
   3908    passABIArg(str);
   3909    passABIArg(output);
   3910    callWithABI<Fn, GetInt32FromStringPure>();
   3911    storeCallPointerResult(scratch);
   3912 
   3913    PopRegsInMask(volatileRegs);
   3914 
   3915    Label ok;
   3916    branchIfTrueBool(scratch, &ok);
   3917    {
   3918      // OOM path, recovered by GetInt32FromStringPure.
   3919      //
   3920      // Use addToStackPtr instead of freeStack as freeStack tracks stack height
   3921      // flow-insensitively, and using it twice would confuse the stack height
   3922      // tracking.
   3923      addToStackPtr(Imm32(sizeof(uintptr_t)));
   3924      jump(fail);
   3925    }
   3926    bind(&ok);
   3927    load32(Address(output, 0), output);
   3928    freeStack(sizeof(uintptr_t));
   3929  }
   3930  bind(&done);
   3931 }
   3932 
   3933 void MacroAssembler::generateBailoutTail(Register scratch,
   3934                                         Register bailoutInfo) {
   3935  Label bailoutFailed;
   3936  branchIfFalseBool(ReturnReg, &bailoutFailed);
   3937 
   3938  // Finish bailing out to Baseline.
   3939  {
   3940    // Prepare a register set for use in this case.
   3941    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   3942    MOZ_ASSERT_IF(!IsHiddenSP(getStackPointer()),
   3943                  !regs.has(AsRegister(getStackPointer())));
   3944    regs.take(bailoutInfo);
   3945 
   3946    Register temp = regs.takeAny();
   3947 
   3948 #ifdef DEBUG
   3949    // Assert the stack pointer points to the JitFrameLayout header. Copying
   3950    // starts here.
   3951    Label ok;
   3952    loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, incomingStack)),
   3953            temp);
   3954    branchStackPtr(Assembler::Equal, temp, &ok);
   3955    assumeUnreachable("Unexpected stack pointer value");
   3956    bind(&ok);
   3957 #endif
   3958 
   3959    Register copyCur = regs.takeAny();
   3960    Register copyEnd = regs.takeAny();
   3961 
   3962    // Copy data onto stack.
   3963    loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, copyStackTop)),
   3964            copyCur);
   3965    loadPtr(
   3966        Address(bailoutInfo, offsetof(BaselineBailoutInfo, copyStackBottom)),
   3967        copyEnd);
   3968    {
   3969      Label copyLoop;
   3970      Label endOfCopy;
   3971      bind(&copyLoop);
   3972      branchPtr(Assembler::BelowOrEqual, copyCur, copyEnd, &endOfCopy);
   3973      subPtr(Imm32(sizeof(uintptr_t)), copyCur);
   3974      subFromStackPtr(Imm32(sizeof(uintptr_t)));
   3975      loadPtr(Address(copyCur, 0), temp);
   3976      storePtr(temp, Address(getStackPointer(), 0));
   3977      jump(&copyLoop);
   3978      bind(&endOfCopy);
   3979    }
   3980 
   3981    loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)),
   3982            FramePointer);
   3983 
   3984    // Enter exit frame for the FinishBailoutToBaseline call.
   3985    push(FrameDescriptor(FrameType::BaselineJS));
   3986    push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
   3987    push(FramePointer);
   3988    // No GC things to mark on the stack, push a bare token.
   3989    loadJSContext(scratch);
   3990    enterFakeExitFrame(scratch, scratch, ExitFrameType::Bare);
   3991 
   3992    // Save needed values onto stack temporarily.
   3993    push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
   3994 
   3995    // Call a stub to free allocated memory and create arguments objects.
   3996    using Fn = bool (*)(BaselineBailoutInfo* bailoutInfoArg);
   3997    setupUnalignedABICall(temp);
   3998    passABIArg(bailoutInfo);
   3999    callWithABI<Fn, FinishBailoutToBaseline>(
   4000        ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
   4001    branchIfFalseBool(ReturnReg, exceptionLabel());
   4002 
   4003    // Restore values where they need to be and resume execution.
   4004    AllocatableGeneralRegisterSet enterRegs(GeneralRegisterSet::All());
   4005    MOZ_ASSERT(!enterRegs.has(FramePointer));
   4006    Register jitcodeReg = enterRegs.takeAny();
   4007 
   4008    pop(jitcodeReg);
   4009 
   4010    // Discard exit frame.
   4011    addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter()));
   4012 
   4013    jump(jitcodeReg);
   4014  }
   4015 
   4016  bind(&bailoutFailed);
   4017  {
   4018    // jit::Bailout or jit::InvalidationBailout failed and returned false. The
   4019    // Ion frame has already been discarded and the stack pointer points to the
   4020    // JitFrameLayout header. Turn it into an ExitFrameLayout, similar to
   4021    // EnsureUnwoundJitExitFrame, and call the exception handler.
   4022    loadJSContext(scratch);
   4023    enterFakeExitFrame(scratch, scratch, ExitFrameType::UnwoundJit);
   4024    jump(exceptionLabel());
   4025  }
   4026 }
   4027 
   4028 void MacroAssembler::loadJitCodeRaw(Register func, Register dest) {
   4029  static_assert(BaseScript::offsetOfJitCodeRaw() ==
   4030                    SelfHostedLazyScript::offsetOfJitCodeRaw(),
   4031                "SelfHostedLazyScript and BaseScript must use same layout for "
   4032                "jitCodeRaw_");
   4033  static_assert(
   4034      BaseScript::offsetOfJitCodeRaw() == wasm::JumpTableJitEntryOffset,
   4035      "Wasm exported functions jit entries must use same layout for "
   4036      "jitCodeRaw_");
   4037  loadPrivate(Address(func, JSFunction::offsetOfJitInfoOrScript()), dest);
   4038  loadPtr(Address(dest, BaseScript::offsetOfJitCodeRaw()), dest);
   4039 }
   4040 
   4041 void MacroAssembler::loadJitCodeRawNoIon(Register func, Register dest,
   4042                                         Register scratch) {
   4043  // This is used when calling a trial-inlined script using a private
   4044  // ICScript to collect callsite-specific CacheIR. Ion doesn't use
   4045  // the baseline ICScript, so we want to enter at the highest
   4046  // available non-Ion tier.
   4047 
   4048  Label useJitCodeRaw, done;
   4049  loadPrivate(Address(func, JSFunction::offsetOfJitInfoOrScript()), dest);
   4050  branchIfScriptHasNoJitScript(dest, &useJitCodeRaw);
   4051  loadJitScript(dest, scratch);
   4052 
   4053  // If we have an IonScript, jitCodeRaw_ will point to it, so we have
   4054  // to load the baseline entry out of the BaselineScript.
   4055  branchPtr(Assembler::BelowOrEqual,
   4056            Address(scratch, JitScript::offsetOfIonScript()),
   4057            ImmPtr(IonCompilingScriptPtr), &useJitCodeRaw);
   4058  loadPtr(Address(scratch, JitScript::offsetOfBaselineScript()), scratch);
   4059 
   4060 #ifdef DEBUG
   4061  // If we have an IonScript, we must also have a BaselineScript.
   4062  Label hasBaselineScript;
   4063  branchPtr(Assembler::Above, scratch, ImmPtr(BaselineCompilingScriptPtr),
   4064            &hasBaselineScript);
   4065  assumeUnreachable("JitScript has IonScript without BaselineScript");
   4066  bind(&hasBaselineScript);
   4067 #endif
   4068 
   4069  loadPtr(Address(scratch, BaselineScript::offsetOfMethod()), scratch);
   4070  loadPtr(Address(scratch, JitCode::offsetOfCode()), dest);
   4071  jump(&done);
   4072 
   4073  // If there's no IonScript, we can just use jitCodeRaw_.
   4074  bind(&useJitCodeRaw);
   4075  loadPtr(Address(dest, BaseScript::offsetOfJitCodeRaw()), dest);
   4076  bind(&done);
   4077 }
   4078 
   4079 void MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest) {
   4080  if (framePtr != dest) {
   4081    movePtr(framePtr, dest);
   4082  }
   4083  subPtr(Imm32(BaselineFrame::Size()), dest);
   4084 }
   4085 
   4086 void MacroAssembler::handleFailure() {
   4087  // Re-entry code is irrelevant because the exception will leave the
   4088  // running function and never come back
   4089  TrampolinePtr excTail = runtime()->jitRuntime()->getExceptionTail();
   4090  jump(excTail);
   4091 }
   4092 
   4093 void MacroAssembler::assumeUnreachable(const char* output) {
   4094 #ifdef JS_MASM_VERBOSE
   4095  if (!IsCompilingWasm()) {
   4096    AllocatableRegisterSet regs(RegisterSet::Volatile());
   4097    LiveRegisterSet save(regs.asLiveSet());
   4098    PushRegsInMask(save);
   4099    Register temp = regs.takeAnyGeneral();
   4100 
   4101    using Fn = void (*)(const char* output);
   4102    setupUnalignedABICall(temp);
   4103    movePtr(ImmPtr(output), temp);
   4104    passABIArg(temp);
   4105    callWithABI<Fn, AssumeUnreachable>(ABIType::General,
   4106                                       CheckUnsafeCallWithABI::DontCheckOther);
   4107 
   4108    PopRegsInMask(save);
   4109  }
   4110 #endif
   4111 
   4112  breakpoint();
   4113 }
   4114 
   4115 void MacroAssembler::printf(const char* output) {
   4116 #ifdef JS_MASM_VERBOSE
   4117  AllocatableRegisterSet regs(RegisterSet::Volatile());
   4118  LiveRegisterSet save(regs.asLiveSet());
   4119  PushRegsInMask(save);
   4120 
   4121  Register temp = regs.takeAnyGeneral();
   4122 
   4123  using Fn = void (*)(const char* output);
   4124  setupUnalignedABICall(temp);
   4125  movePtr(ImmPtr(output), temp);
   4126  passABIArg(temp);
   4127  callWithABI<Fn, Printf0>();
   4128 
   4129  PopRegsInMask(save);
   4130 #endif
   4131 }
   4132 
   4133 void MacroAssembler::printf(const char* output, Register value) {
   4134 #ifdef JS_MASM_VERBOSE
   4135  AllocatableRegisterSet regs(RegisterSet::Volatile());
   4136  LiveRegisterSet save(regs.asLiveSet());
   4137  PushRegsInMask(save);
   4138 
   4139  regs.takeUnchecked(value);
   4140 
   4141  Register temp = regs.takeAnyGeneral();
   4142 
   4143  using Fn = void (*)(const char* output, uintptr_t value);
   4144  setupUnalignedABICall(temp);
   4145  movePtr(ImmPtr(output), temp);
   4146  passABIArg(temp);
   4147  passABIArg(value);
   4148  callWithABI<Fn, Printf1>();
   4149 
   4150  PopRegsInMask(save);
   4151 #endif
   4152 }
   4153 
   4154 void MacroAssembler::convertInt32ValueToDouble(ValueOperand val) {
   4155  Label done;
   4156  branchTestInt32(Assembler::NotEqual, val, &done);
   4157  ScratchDoubleScope fpscratch(*this);
   4158  convertInt32ToDouble(val.payloadOrValueReg(), fpscratch);
   4159  boxDouble(fpscratch, val, fpscratch);
   4160  bind(&done);
   4161 }
   4162 
   4163 void MacroAssembler::convertValueToFloatingPoint(
   4164    ValueOperand value, FloatRegister output, Register maybeTemp,
   4165    LiveRegisterSet volatileLiveRegs, Label* fail,
   4166    FloatingPointType outputType) {
   4167  Label isDouble, isInt32OrBool, isNull, done;
   4168 
   4169  {
   4170    ScratchTagScope tag(*this, value);
   4171    splitTagForTest(value, tag);
   4172 
   4173    branchTestDouble(Assembler::Equal, tag, &isDouble);
   4174    branchTestInt32(Assembler::Equal, tag, &isInt32OrBool);
   4175    branchTestBoolean(Assembler::Equal, tag, &isInt32OrBool);
   4176    branchTestNull(Assembler::Equal, tag, &isNull);
   4177    branchTestUndefined(Assembler::NotEqual, tag, fail);
   4178  }
   4179 
   4180  // fall-through: undefined
   4181  if (outputType == FloatingPointType::Float16 ||
   4182      outputType == FloatingPointType::Float32) {
   4183    loadConstantFloat32(float(GenericNaN()), output);
   4184  } else {
   4185    loadConstantDouble(GenericNaN(), output);
   4186  }
   4187  jump(&done);
   4188 
   4189  bind(&isNull);
   4190  if (outputType == FloatingPointType::Float16 ||
   4191      outputType == FloatingPointType::Float32) {
   4192    loadConstantFloat32(0.0f, output);
   4193  } else {
   4194    loadConstantDouble(0.0, output);
   4195  }
   4196  jump(&done);
   4197 
   4198  bind(&isInt32OrBool);
   4199  if (outputType == FloatingPointType::Float16) {
   4200    convertInt32ToFloat16(value.payloadOrValueReg(), output, maybeTemp,
   4201                          volatileLiveRegs);
   4202  } else if (outputType == FloatingPointType::Float32) {
   4203    convertInt32ToFloat32(value.payloadOrValueReg(), output);
   4204  } else {
   4205    convertInt32ToDouble(value.payloadOrValueReg(), output);
   4206  }
   4207  jump(&done);
   4208 
   4209  // On some non-multiAlias platforms, unboxDouble may use the scratch register,
   4210  // so do not merge code paths here.
   4211  bind(&isDouble);
   4212  if ((outputType == FloatingPointType::Float16 ||
   4213       outputType == FloatingPointType::Float32) &&
   4214      hasMultiAlias()) {
   4215    ScratchDoubleScope tmp(*this);
   4216    unboxDouble(value, tmp);
   4217 
   4218    if (outputType == FloatingPointType::Float16) {
   4219      convertDoubleToFloat16(tmp, output, maybeTemp, volatileLiveRegs);
   4220    } else {
   4221      convertDoubleToFloat32(tmp, output);
   4222    }
   4223  } else {
   4224    FloatRegister tmp = output.asDouble();
   4225    unboxDouble(value, tmp);
   4226 
   4227    if (outputType == FloatingPointType::Float16) {
   4228      convertDoubleToFloat16(tmp, output, maybeTemp, volatileLiveRegs);
   4229    } else if (outputType == FloatingPointType::Float32) {
   4230      convertDoubleToFloat32(tmp, output);
   4231    }
   4232  }
   4233 
   4234  bind(&done);
   4235 }
   4236 
   4237 void MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest,
   4238                                           bool widenFloatToDouble,
   4239                                           bool compilingWasm,
   4240                                           wasm::BytecodeOffset callOffset) {
   4241  ScratchDoubleScope fpscratch(*this);
   4242  if (widenFloatToDouble) {
   4243    convertFloat32ToDouble(src, fpscratch);
   4244    src = fpscratch;
   4245  }
   4246  MOZ_ASSERT(src.isDouble());
   4247 
   4248  if (compilingWasm) {
   4249    Push(InstanceReg);
   4250    int32_t framePushedAfterInstance = framePushed();
   4251 
   4252    setupWasmABICall(wasm::SymbolicAddress::ToInt32);
   4253    passABIArg(src, ABIType::Float64);
   4254 
   4255    int32_t instanceOffset = framePushed() - framePushedAfterInstance;
   4256    callWithABI(callOffset, wasm::SymbolicAddress::ToInt32,
   4257                mozilla::Some(instanceOffset));
   4258    storeCallInt32Result(dest);
   4259 
   4260    Pop(InstanceReg);
   4261  } else {
   4262    using Fn = int32_t (*)(double);
   4263    setupUnalignedABICall(dest);
   4264    passABIArg(src, ABIType::Float64);
   4265    callWithABI<Fn, JS::ToInt32>(ABIType::General,
   4266                                 CheckUnsafeCallWithABI::DontCheckOther);
   4267    storeCallInt32Result(dest);
   4268  }
   4269 }
   4270 
   4271 void MacroAssembler::convertValueToInt32(ValueOperand value, FloatRegister temp,
   4272                                         Register output, Label* fail,
   4273                                         bool negativeZeroCheck,
   4274                                         IntConversionInputKind conversion) {
   4275  Label done, isInt32, isBool, isDouble, isString;
   4276 
   4277  {
   4278    ScratchTagScope tag(*this, value);
   4279    splitTagForTest(value, tag);
   4280 
   4281    branchTestInt32(Equal, tag, &isInt32);
   4282    branchTestDouble(Equal, tag, &isDouble);
   4283    if (conversion == IntConversionInputKind::Any) {
   4284      branchTestBoolean(Equal, tag, &isBool);
   4285      branchTestNull(Assembler::NotEqual, tag, fail);
   4286    } else {
   4287      jump(fail);
   4288    }
   4289  }
   4290 
   4291  // The value is null - just emit 0.
   4292  if (conversion == IntConversionInputKind::Any) {
   4293    move32(Imm32(0), output);
   4294    jump(&done);
   4295  }
   4296 
   4297  // Try converting double into integer.
   4298  {
   4299    bind(&isDouble);
   4300    unboxDouble(value, temp);
   4301    convertDoubleToInt32(temp, output, fail, negativeZeroCheck);
   4302    jump(&done);
   4303  }
   4304 
   4305  // Just unbox a bool, the result is 0 or 1.
   4306  if (conversion == IntConversionInputKind::Any) {
   4307    bind(&isBool);
   4308    unboxBoolean(value, output);
   4309    jump(&done);
   4310  }
   4311 
   4312  // Integers can be unboxed.
   4313  {
   4314    bind(&isInt32);
   4315    unboxInt32(value, output);
   4316  }
   4317 
   4318  bind(&done);
   4319 }
   4320 
   4321 void MacroAssembler::truncateValueToInt32(
   4322    ValueOperand value, Label* handleStringEntry, Label* handleStringRejoin,
   4323    Label* truncateDoubleSlow, Register stringReg, FloatRegister temp,
   4324    Register output, Label* fail) {
   4325  Label done, isInt32, isBool, isDouble, isNull, isString;
   4326 
   4327  bool handleStrings = handleStringEntry && handleStringRejoin;
   4328 
   4329  // |output| needs to be different from |stringReg| to load string indices.
   4330  MOZ_ASSERT_IF(handleStrings, stringReg != output);
   4331 
   4332  {
   4333    ScratchTagScope tag(*this, value);
   4334    splitTagForTest(value, tag);
   4335 
   4336    branchTestInt32(Equal, tag, &isInt32);
   4337    branchTestDouble(Equal, tag, &isDouble);
   4338    branchTestBoolean(Equal, tag, &isBool);
   4339    branchTestNull(Equal, tag, &isNull);
   4340    if (handleStrings) {
   4341      branchTestString(Equal, tag, &isString);
   4342    }
   4343    branchTestUndefined(Assembler::NotEqual, tag, fail);
   4344  }
   4345 
   4346  // The value is null or undefined in truncation contexts - just emit 0.
   4347  {
   4348    bind(&isNull);
   4349    move32(Imm32(0), output);
   4350    jump(&done);
   4351  }
   4352 
   4353  // First try loading a string index. If that fails, try converting a string
   4354  // into a double, then jump to the double case.
   4355  Label handleStringIndex;
   4356  if (handleStrings) {
   4357    bind(&isString);
   4358    unboxString(value, stringReg);
   4359    loadStringIndexValue(stringReg, output, handleStringEntry);
   4360    jump(&done);
   4361  }
   4362 
   4363  // Try converting double into integer.
   4364  {
   4365    bind(&isDouble);
   4366    unboxDouble(value, temp);
   4367 
   4368    if (handleStrings) {
   4369      bind(handleStringRejoin);
   4370    }
   4371    branchTruncateDoubleMaybeModUint32(
   4372        temp, output, truncateDoubleSlow ? truncateDoubleSlow : fail);
   4373    jump(&done);
   4374  }
   4375 
   4376  // Just unbox a bool, the result is 0 or 1.
   4377  {
   4378    bind(&isBool);
   4379    unboxBoolean(value, output);
   4380    jump(&done);
   4381  }
   4382 
   4383  // Integers can be unboxed.
   4384  {
   4385    bind(&isInt32);
   4386    unboxInt32(value, output);
   4387  }
   4388 
   4389  bind(&done);
   4390 }
   4391 
   4392 void MacroAssembler::clampValueToUint8(ValueOperand value,
   4393                                       Label* handleStringEntry,
   4394                                       Label* handleStringRejoin,
   4395                                       Register stringReg, FloatRegister temp,
   4396                                       Register output, Label* fail) {
   4397  Label done, isInt32, isBool, isDouble, isNull, isString;
   4398  {
   4399    ScratchTagScope tag(*this, value);
   4400    splitTagForTest(value, tag);
   4401 
   4402    branchTestInt32(Equal, tag, &isInt32);
   4403    branchTestDouble(Equal, tag, &isDouble);
   4404    branchTestBoolean(Equal, tag, &isBool);
   4405    branchTestNull(Equal, tag, &isNull);
   4406    branchTestString(Equal, tag, &isString);
   4407    branchTestUndefined(Assembler::NotEqual, tag, fail);
   4408  }
   4409 
   4410  // The value is null or undefined in truncation contexts - just emit 0.
   4411  {
   4412    bind(&isNull);
   4413    move32(Imm32(0), output);
   4414    jump(&done);
   4415  }
   4416 
   4417  // Try converting a string into a double, then jump to the double case.
   4418  {
   4419    bind(&isString);
   4420    unboxString(value, stringReg);
   4421    jump(handleStringEntry);
   4422  }
   4423 
   4424  // Try converting double into integer.
   4425  {
   4426    bind(&isDouble);
   4427    unboxDouble(value, temp);
   4428    bind(handleStringRejoin);
   4429    clampDoubleToUint8(temp, output);
   4430    jump(&done);
   4431  }
   4432 
   4433  // Just unbox a bool, the result is 0 or 1.
   4434  {
   4435    bind(&isBool);
   4436    unboxBoolean(value, output);
   4437    jump(&done);
   4438  }
   4439 
   4440  // Integers can be unboxed.
   4441  {
   4442    bind(&isInt32);
   4443    unboxInt32(value, output);
   4444    clampIntToUint8(output);
   4445  }
   4446 
   4447  bind(&done);
   4448 }
   4449 
   4450 void MacroAssembler::finish() {
   4451  if (failureLabel_.used()) {
   4452    bind(&failureLabel_);
   4453    handleFailure();
   4454  }
   4455 
   4456  MacroAssemblerSpecific::finish();
   4457 
   4458  MOZ_RELEASE_ASSERT(
   4459      size() <= MaxCodeBytesPerProcess,
   4460      "AssemblerBuffer should ensure we don't exceed MaxCodeBytesPerProcess");
   4461 
   4462  if (bytesNeeded() > MaxCodeBytesPerProcess) {
   4463    setOOM();
   4464  }
   4465 }
   4466 
   4467 void MacroAssembler::link(JitCode* code) {
   4468  MOZ_ASSERT(!oom());
   4469  linkProfilerCallSites(code);
   4470 }
   4471 
   4472 MacroAssembler::AutoProfilerCallInstrumentation::
   4473    AutoProfilerCallInstrumentation(MacroAssembler& masm) {
   4474  if (!masm.emitProfilingInstrumentation_) {
   4475    return;
   4476  }
   4477 
   4478  Register reg = CallTempReg0;
   4479  Register reg2 = CallTempReg1;
   4480  masm.push(reg);
   4481  masm.push(reg2);
   4482 
   4483  CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg);
   4484  masm.loadJSContext(reg2);
   4485  masm.loadPtr(Address(reg2, offsetof(JSContext, profilingActivation_)), reg2);
   4486  masm.storePtr(reg,
   4487                Address(reg2, JitActivation::offsetOfLastProfilingCallSite()));
   4488 
   4489  masm.appendProfilerCallSite(label);
   4490 
   4491  masm.pop(reg2);
   4492  masm.pop(reg);
   4493 }
   4494 
   4495 void MacroAssembler::linkProfilerCallSites(JitCode* code) {
   4496  for (size_t i = 0; i < profilerCallSites_.length(); i++) {
   4497    CodeOffset offset = profilerCallSites_[i];
   4498    CodeLocationLabel location(code, offset);
   4499    PatchDataWithValueCheck(location, ImmPtr(location.raw()),
   4500                            ImmPtr((void*)-1));
   4501  }
   4502 }
   4503 
   4504 void MacroAssembler::alignJitStackBasedOnNArgs(Register nargs,
   4505                                               bool countIncludesThis) {
   4506  // The stack should already be aligned to the size of a value.
   4507  assertStackAlignment(sizeof(Value), 0);
   4508 
   4509  static_assert(JitStackValueAlignment == 1 || JitStackValueAlignment == 2,
   4510                "JitStackValueAlignment is either 1 or 2.");
   4511  if (JitStackValueAlignment == 1) {
   4512    return;
   4513  }
   4514  // A jit frame is composed of the following:
   4515  //
   4516  // [padding?] [argN] .. [arg1] [this] [[argc] [callee] [descr] [raddr]]
   4517  //                                    \________JitFrameLayout_________/
   4518  // (The stack grows this way --->)
   4519  //
   4520  // We want to ensure that |raddr|, the return address, is 16-byte aligned.
   4521  // (Note: if 8-byte alignment was sufficient, we would have already
   4522  // returned above.)
   4523 
   4524  // JitFrameLayout does not affect the alignment, so we can ignore it.
   4525  static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
   4526                "JitFrameLayout doesn't affect stack alignment");
   4527 
   4528  // Therefore, we need to ensure that |this| is aligned.
   4529  // This implies that |argN| must be aligned if N is even,
   4530  // and offset by |sizeof(Value)| if N is odd.
   4531 
   4532  // Depending on the context of the caller, it may be easier to pass in a
   4533  // register that has already been modified to include |this|. If that is the
   4534  // case, we want to flip the direction of the test.
   4535  Assembler::Condition condition =
   4536      countIncludesThis ? Assembler::NonZero : Assembler::Zero;
   4537 
   4538  Label alignmentIsOffset, end;
   4539  branchTestPtr(condition, nargs, Imm32(1), &alignmentIsOffset);
   4540 
   4541  // |argN| should be aligned to 16 bytes.
   4542  andToStackPtr(Imm32(~(JitStackAlignment - 1)));
   4543  jump(&end);
   4544 
   4545  // |argN| should be offset by 8 bytes from 16-byte alignment.
   4546  // We already know that it is 8-byte aligned, so the only possibilities are:
   4547  // a) It is 16-byte aligned, and we must offset it by 8 bytes.
   4548  // b) It is not 16-byte aligned, and therefore already has the right offset.
   4549  // Therefore, we test to see if it is 16-byte aligned, and adjust it if it is.
   4550  bind(&alignmentIsOffset);
   4551  branchTestStackPtr(Assembler::NonZero, Imm32(JitStackAlignment - 1), &end);
   4552  subFromStackPtr(Imm32(sizeof(Value)));
   4553 
   4554  bind(&end);
   4555 }
   4556 
   4557 void MacroAssembler::alignJitStackBasedOnNArgs(uint32_t argc,
   4558                                               bool countIncludesThis) {
   4559  // The stack should already be aligned to the size of a value.
   4560  assertStackAlignment(sizeof(Value), 0);
   4561 
   4562  static_assert(JitStackValueAlignment == 1 || JitStackValueAlignment == 2,
   4563                "JitStackValueAlignment is either 1 or 2.");
   4564  if (JitStackValueAlignment == 1) {
   4565    return;
   4566  }
   4567 
   4568  // See above for full explanation.
   4569  uint32_t nArgs = argc + !countIncludesThis;
   4570  if (nArgs % 2 == 0) {
   4571    // |argN| should be 16-byte aligned
   4572    andToStackPtr(Imm32(~(JitStackAlignment - 1)));
   4573  } else {
   4574    // |argN| must be 16-byte aligned if argc is even,
   4575    // and offset by 8 if argc is odd.
   4576    Label end;
   4577    branchTestStackPtr(Assembler::NonZero, Imm32(JitStackAlignment - 1), &end);
   4578    subFromStackPtr(Imm32(sizeof(Value)));
   4579    bind(&end);
   4580    assertStackAlignment(JitStackAlignment, sizeof(Value));
   4581  }
   4582 }
   4583 
   4584 // ===============================================================
   4585 
   4586 MacroAssembler::MacroAssembler(TempAllocator& alloc,
   4587                               CompileRuntime* maybeRuntime,
   4588                               CompileRealm* maybeRealm)
   4589    : maybeRuntime_(maybeRuntime),
   4590      maybeRealm_(maybeRealm),
   4591      framePushed_(0),
   4592      abiArgs_(/* This will be overwritten for every ABI call, the initial value
   4593                  doesn't matter */
   4594               ABIKind::System),
   4595 #ifdef DEBUG
   4596      inCall_(false),
   4597 #endif
   4598      dynamicAlignment_(false),
   4599      emitProfilingInstrumentation_(false) {
   4600  moveResolver_.setAllocator(alloc);
   4601 }
   4602 
   4603 StackMacroAssembler::StackMacroAssembler(JSContext* cx, TempAllocator& alloc)
   4604    : MacroAssembler(alloc, CompileRuntime::get(cx->runtime()),
   4605                     CompileRealm::get(cx->realm())) {}
   4606 
   4607 OffThreadMacroAssembler::OffThreadMacroAssembler(TempAllocator& alloc,
   4608                                                 CompileRealm* realm)
   4609    : MacroAssembler(alloc, realm->runtime(), realm) {
   4610  MOZ_ASSERT(CurrentThreadIsOffThreadCompiling());
   4611 }
   4612 
   4613 WasmMacroAssembler::WasmMacroAssembler(TempAllocator& alloc, bool limitedSize)
   4614    : MacroAssembler(alloc) {
   4615 #if defined(JS_CODEGEN_ARM64)
   4616  // Stubs + builtins + the baseline compiler all require the native SP,
   4617  // not the PSP.
   4618  SetStackPointer64(sp);
   4619 #endif
   4620  if (!limitedSize) {
   4621    setUnlimitedBuffer();
   4622  }
   4623 }
   4624 
   4625 bool MacroAssembler::icBuildOOLFakeExitFrame(void* fakeReturnAddr,
   4626                                             AutoSaveLiveRegisters& save) {
   4627  return buildOOLFakeExitFrame(fakeReturnAddr);
   4628 }
   4629 
   4630 #ifndef JS_CODEGEN_ARM64
   4631 void MacroAssembler::subFromStackPtr(Register reg) {
   4632  subPtr(reg, getStackPointer());
   4633 }
   4634 #endif  // JS_CODEGEN_ARM64
   4635 
   4636 //{{{ check_macroassembler_style
   4637 // ===============================================================
   4638 // Stack manipulation functions.
   4639 
   4640 void MacroAssembler::PushRegsInMask(LiveGeneralRegisterSet set) {
   4641  PushRegsInMask(LiveRegisterSet(set.set(), FloatRegisterSet()));
   4642 }
   4643 
   4644 void MacroAssembler::PopRegsInMask(LiveRegisterSet set) {
   4645  PopRegsInMaskIgnore(set, LiveRegisterSet());
   4646 }
   4647 
   4648 void MacroAssembler::PopRegsInMask(LiveGeneralRegisterSet set) {
   4649  PopRegsInMask(LiveRegisterSet(set.set(), FloatRegisterSet()));
   4650 }
   4651 
   4652 void MacroAssembler::Push(PropertyKey key, Register scratchReg) {
   4653  if (key.isGCThing()) {
   4654    // If we're pushing a gcthing, then we can't just push the tagged key
   4655    // value since the GC won't have any idea that the push instruction
   4656    // carries a reference to a gcthing.  Need to unpack the pointer,
   4657    // push it using ImmGCPtr, and then rematerialize the PropertyKey at
   4658    // runtime.
   4659 
   4660    if (key.isString()) {
   4661      JSString* str = key.toString();
   4662      MOZ_ASSERT((uintptr_t(str) & PropertyKey::TypeMask) == 0);
   4663      static_assert(PropertyKey::StringTypeTag == 0,
   4664                    "need to orPtr StringTypeTag if it's not 0");
   4665      Push(ImmGCPtr(str));
   4666    } else {
   4667      MOZ_ASSERT(key.isSymbol());
   4668      movePropertyKey(key, scratchReg);
   4669      Push(scratchReg);
   4670    }
   4671  } else {
   4672    MOZ_ASSERT(key.isInt());
   4673    Push(ImmWord(key.asRawBits()));
   4674  }
   4675 }
   4676 
   4677 void MacroAssembler::moveValue(const TypedOrValueRegister& src,
   4678                               const ValueOperand& dest) {
   4679  if (src.hasValue()) {
   4680    moveValue(src.valueReg(), dest);
   4681    return;
   4682  }
   4683 
   4684  MIRType type = src.type();
   4685  AnyRegister reg = src.typedReg();
   4686 
   4687  if (!IsFloatingPointType(type)) {
   4688    tagValue(ValueTypeFromMIRType(type), reg.gpr(), dest);
   4689    return;
   4690  }
   4691 
   4692  ScratchDoubleScope scratch(*this);
   4693  FloatRegister freg = reg.fpu();
   4694  if (type == MIRType::Float32) {
   4695    convertFloat32ToDouble(freg, scratch);
   4696    freg = scratch;
   4697  }
   4698  boxDouble(freg, dest, scratch);
   4699 }
   4700 
   4701 void MacroAssembler::movePropertyKey(PropertyKey key, Register dest) {
   4702  if (key.isGCThing()) {
   4703    // See comment in |Push(PropertyKey, ...)| above for an explanation.
   4704    if (key.isString()) {
   4705      JSString* str = key.toString();
   4706      MOZ_ASSERT((uintptr_t(str) & PropertyKey::TypeMask) == 0);
   4707      static_assert(PropertyKey::StringTypeTag == 0,
   4708                    "need to orPtr StringTypeTag tag if it's not 0");
   4709      movePtr(ImmGCPtr(str), dest);
   4710    } else {
   4711      MOZ_ASSERT(key.isSymbol());
   4712      JS::Symbol* sym = key.toSymbol();
   4713      movePtr(ImmGCPtr(sym), dest);
   4714      orPtr(Imm32(PropertyKey::SymbolTypeTag), dest);
   4715    }
   4716  } else {
   4717    MOZ_ASSERT(key.isInt());
   4718    movePtr(ImmWord(key.asRawBits()), dest);
   4719  }
   4720 }
   4721 
   4722 void MacroAssembler::Push(TypedOrValueRegister v) {
   4723  if (v.hasValue()) {
   4724    Push(v.valueReg());
   4725  } else if (IsFloatingPointType(v.type())) {
   4726    FloatRegister reg = v.typedReg().fpu();
   4727    if (v.type() == MIRType::Float32) {
   4728      ScratchDoubleScope fpscratch(*this);
   4729      convertFloat32ToDouble(reg, fpscratch);
   4730      PushBoxed(fpscratch);
   4731    } else {
   4732      PushBoxed(reg);
   4733    }
   4734  } else {
   4735    Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
   4736  }
   4737 }
   4738 
   4739 void MacroAssembler::Push(const ConstantOrRegister& v) {
   4740  if (v.constant()) {
   4741    Push(v.value());
   4742  } else {
   4743    Push(v.reg());
   4744  }
   4745 }
   4746 
   4747 void MacroAssembler::Push(const Address& addr) {
   4748  push(addr);
   4749  framePushed_ += sizeof(uintptr_t);
   4750 }
   4751 
   4752 void MacroAssembler::Push(const ValueOperand& val) {
   4753  pushValue(val);
   4754  framePushed_ += sizeof(Value);
   4755 }
   4756 
   4757 void MacroAssembler::Push(const Value& val) {
   4758  pushValue(val);
   4759  framePushed_ += sizeof(Value);
   4760 }
   4761 
   4762 void MacroAssembler::Push(JSValueType type, Register reg) {
   4763  pushValue(type, reg);
   4764  framePushed_ += sizeof(Value);
   4765 }
   4766 
   4767 void MacroAssembler::Push(const Register64 reg) {
   4768 #if JS_BITS_PER_WORD == 64
   4769  Push(reg.reg);
   4770 #else
   4771  MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Big-endian not supported.");
   4772  Push(reg.high);
   4773  Push(reg.low);
   4774 #endif
   4775 }
   4776 
   4777 void MacroAssembler::Pop(const Register64 reg) {
   4778 #if JS_BITS_PER_WORD == 64
   4779  Pop(reg.reg);
   4780 #else
   4781  MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Big-endian not supported.");
   4782  Pop(reg.low);
   4783  Pop(reg.high);
   4784 #endif
   4785 }
   4786 
   4787 void MacroAssembler::PushEmptyRooted(VMFunctionData::RootType rootType) {
   4788  switch (rootType) {
   4789    case VMFunctionData::RootNone:
   4790      MOZ_CRASH("Handle must have root type");
   4791    case VMFunctionData::RootObject:
   4792    case VMFunctionData::RootString:
   4793    case VMFunctionData::RootCell:
   4794    case VMFunctionData::RootBigInt:
   4795      Push(ImmPtr(nullptr));
   4796      break;
   4797    case VMFunctionData::RootValue:
   4798      Push(UndefinedValue());
   4799      break;
   4800    case VMFunctionData::RootId:
   4801      Push(ImmWord(JS::PropertyKey::Void().asRawBits()));
   4802      break;
   4803  }
   4804 }
   4805 
   4806 void MacroAssembler::adjustStack(int amount) {
   4807  if (amount > 0) {
   4808    freeStack(amount);
   4809  } else if (amount < 0) {
   4810    reserveStack(-amount);
   4811  }
   4812 }
   4813 
   4814 void MacroAssembler::freeStack(uint32_t amount) {
   4815  MOZ_ASSERT(amount <= framePushed_);
   4816  if (amount) {
   4817    addToStackPtr(Imm32(amount));
   4818  }
   4819  framePushed_ -= amount;
   4820 }
   4821 
   4822 void MacroAssembler::reserveVMFunctionOutParamSpace(const VMFunctionData& f) {
   4823  switch (f.outParam) {
   4824    case Type_Handle:
   4825      PushEmptyRooted(f.outParamRootType);
   4826      break;
   4827 
   4828    case Type_Value:
   4829    case Type_Double:
   4830    case Type_Pointer:
   4831    case Type_Int32:
   4832    case Type_Bool:
   4833      reserveStack(f.sizeOfOutParamStackSlot());
   4834      break;
   4835 
   4836    case Type_Void:
   4837      break;
   4838 
   4839    case Type_Cell:
   4840      MOZ_CRASH("Unexpected outparam type");
   4841  }
   4842 }
   4843 
   4844 void MacroAssembler::loadVMFunctionOutParam(const VMFunctionData& f,
   4845                                            const Address& addr) {
   4846  switch (f.outParam) {
   4847    case Type_Handle:
   4848      switch (f.outParamRootType) {
   4849        case VMFunctionData::RootNone:
   4850          MOZ_CRASH("Handle must have root type");
   4851        case VMFunctionData::RootObject:
   4852        case VMFunctionData::RootString:
   4853        case VMFunctionData::RootCell:
   4854        case VMFunctionData::RootBigInt:
   4855        case VMFunctionData::RootId:
   4856          loadPtr(addr, ReturnReg);
   4857          break;
   4858        case VMFunctionData::RootValue:
   4859          loadValue(addr, JSReturnOperand);
   4860          break;
   4861      }
   4862      break;
   4863 
   4864    case Type_Value:
   4865      loadValue(addr, JSReturnOperand);
   4866      break;
   4867 
   4868    case Type_Int32:
   4869      load32(addr, ReturnReg);
   4870      break;
   4871 
   4872    case Type_Bool:
   4873      load8ZeroExtend(addr, ReturnReg);
   4874      break;
   4875 
   4876    case Type_Double:
   4877      loadDouble(addr, ReturnDoubleReg);
   4878      break;
   4879 
   4880    case Type_Pointer:
   4881      loadPtr(addr, ReturnReg);
   4882      break;
   4883 
   4884    case Type_Void:
   4885      break;
   4886 
   4887    case Type_Cell:
   4888      MOZ_CRASH("Unexpected outparam type");
   4889  }
   4890 }
   4891 
   4892 // ===============================================================
   4893 // ABI function calls.
   4894 void MacroAssembler::setupABICallHelper(ABIKind kind) {
   4895 #ifdef DEBUG
   4896  MOZ_ASSERT(!inCall_);
   4897  inCall_ = true;
   4898 #endif
   4899 
   4900 #ifdef JS_SIMULATOR
   4901  signature_ = 0;
   4902 #endif
   4903 
   4904  // Reinitialize the ABIArg generator.
   4905  abiArgs_ = ABIArgGenerator(kind);
   4906 
   4907 #if defined(JS_CODEGEN_ARM)
   4908  if (kind != ABIKind::Wasm) {
   4909    // On ARM, we need to know what ABI we are using.
   4910    abiArgs_.setUseHardFp(ARMFlags::UseHardFpABI());
   4911  }
   4912 #endif
   4913 }
   4914 
   4915 void MacroAssembler::setupNativeABICall() {
   4916  setupABICallHelper(ABIKind::System);
   4917 }
   4918 
   4919 void MacroAssembler::setupWasmABICall(wasm::SymbolicAddress builtin) {
   4920  MOZ_ASSERT(IsCompilingWasm(), "non-wasm should use setupAlignedABICall");
   4921  setupABICallHelper(wasm::ABIForBuiltin(builtin));
   4922  dynamicAlignment_ = false;
   4923 }
   4924 
   4925 void MacroAssembler::setupUnalignedABICallDontSaveRestoreSP() {
   4926  andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
   4927  setFramePushed(0);  // Required for aligned callWithABI.
   4928  setupAlignedABICall();
   4929 }
   4930 
   4931 void MacroAssembler::setupAlignedABICall() {
   4932  MOZ_ASSERT(!IsCompilingWasm(), "wasm should use setupWasmABICall");
   4933  setupNativeABICall();
   4934  dynamicAlignment_ = false;
   4935 }
   4936 
   4937 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   4938 void MacroAssembler::wasmCheckUnsafeCallWithABIPre() {
   4939  // Set the JSContext::inUnsafeCallWithABI flag.
   4940  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()),
   4941          ABINonArgReturnReg0);
   4942  Address flagAddr(ABINonArgReturnReg0,
   4943                   JSContext::offsetOfInUnsafeCallWithABI());
   4944  store32(Imm32(1), flagAddr);
   4945 }
   4946 
   4947 void MacroAssembler::wasmCheckUnsafeCallWithABIPost() {
   4948  // Check JSContext::inUnsafeCallWithABI was cleared as expected.
   4949  Label ok;
   4950  // InstanceReg is invariant in the system ABI, so we can use it here.
   4951  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()),
   4952          ABINonArgReturnReg0);
   4953  Address flagAddr(ABINonArgReturnReg0,
   4954                   JSContext::offsetOfInUnsafeCallWithABI());
   4955  branch32(Assembler::Equal, flagAddr, Imm32(0), &ok);
   4956  assumeUnreachable("callWithABI: callee did not use AutoUnsafeCallWithABI");
   4957  bind(&ok);
   4958 }
   4959 #endif  // JS_CHECK_UNSAFE_CALL_WITH_ABI
   4960 
   4961 void MacroAssembler::passABIArg(const MoveOperand& from, ABIType type) {
   4962  MOZ_ASSERT(inCall_);
   4963  appendSignatureType(type);
   4964 
   4965  ABIArg arg;
   4966  MoveOp::Type moveType;
   4967  switch (type) {
   4968    case ABIType::Float32:
   4969      arg = abiArgs_.next(MIRType::Float32);
   4970      moveType = MoveOp::FLOAT32;
   4971      break;
   4972    case ABIType::Float64:
   4973      arg = abiArgs_.next(MIRType::Double);
   4974      moveType = MoveOp::DOUBLE;
   4975      break;
   4976    case ABIType::General:
   4977      arg = abiArgs_.next(MIRType::Pointer);
   4978      moveType = MoveOp::GENERAL;
   4979      break;
   4980    default:
   4981      MOZ_CRASH("Unexpected argument type");
   4982  }
   4983 
   4984  MoveOperand to(*this, arg);
   4985  if (from == to) {
   4986    return;
   4987  }
   4988 
   4989  if (oom()) {
   4990    return;
   4991  }
   4992  propagateOOM(moveResolver_.addMove(from, to, moveType));
   4993 }
   4994 
   4995 void MacroAssembler::passABIArg(Register64 reg) {
   4996  MOZ_ASSERT(inCall_);
   4997  appendSignatureType(ABIType::Int64);
   4998 
   4999  ABIArg arg = abiArgs_.next(MIRType::Int64);
   5000  MoveOperand to(*this, arg);
   5001 
   5002  auto addMove = [&](const MoveOperand& from, const MoveOperand& to) {
   5003    if (from == to) {
   5004      return;
   5005    }
   5006    if (oom()) {
   5007      return;
   5008    }
   5009    propagateOOM(moveResolver_.addMove(from, to, MoveOp::GENERAL));
   5010  };
   5011 
   5012 #ifdef JS_PUNBOX64
   5013  addMove(MoveOperand(reg.reg), to);
   5014 #else
   5015  if (to.isMemory()) {
   5016    Address addr(to.base(), to.disp());
   5017    addMove(MoveOperand(reg.high), MoveOperand(HighWord(addr)));
   5018    addMove(MoveOperand(reg.low), MoveOperand(LowWord(addr)));
   5019  } else if (to.isGeneralRegPair()) {
   5020    addMove(MoveOperand(reg.high), MoveOperand(to.oddReg()));
   5021    addMove(MoveOperand(reg.low), MoveOperand(to.evenReg()));
   5022  } else {
   5023    MOZ_CRASH("Unsupported move operand");
   5024  }
   5025 #endif
   5026 }
   5027 
   5028 void MacroAssembler::callWithABINoProfiler(void* fun, ABIType result,
   5029                                           CheckUnsafeCallWithABI check) {
   5030  appendSignatureType(result);
   5031 #ifdef JS_SIMULATOR
   5032  fun = Simulator::RedirectNativeFunction(fun, signature());
   5033 #endif
   5034 
   5035  uint32_t stackAdjust;
   5036  callWithABIPre(&stackAdjust);
   5037 
   5038 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   5039  if (check == CheckUnsafeCallWithABI::Check) {
   5040    // Set the JSContext::inUnsafeCallWithABI flag.
   5041    push(ReturnReg);
   5042    loadJSContext(ReturnReg);
   5043    Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
   5044    store32(Imm32(1), flagAddr);
   5045    pop(ReturnReg);
   5046    // On arm64, SP may be < PSP now (that's OK).
   5047    // eg testcase: tests/bug1375074.js
   5048  }
   5049 #endif
   5050 
   5051  call(ImmPtr(fun));
   5052 
   5053  callWithABIPost(stackAdjust, result);
   5054 
   5055 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   5056  if (check == CheckUnsafeCallWithABI::Check) {
   5057    // Check JSContext::inUnsafeCallWithABI was cleared as expected.
   5058    Label ok;
   5059    push(ReturnReg);
   5060    loadJSContext(ReturnReg);
   5061    Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
   5062    branch32(Assembler::Equal, flagAddr, Imm32(0), &ok);
   5063    assumeUnreachable("callWithABI: callee did not use AutoUnsafeCallWithABI");
   5064    bind(&ok);
   5065    pop(ReturnReg);
   5066    // On arm64, SP may be < PSP now (that's OK).
   5067    // eg testcase: tests/bug1375074.js
   5068  }
   5069 #endif
   5070 }
   5071 
   5072 CodeOffset MacroAssembler::callWithABI(wasm::BytecodeOffset bytecode,
   5073                                       wasm::SymbolicAddress imm,
   5074                                       mozilla::Maybe<int32_t> instanceOffset,
   5075                                       ABIType result) {
   5076  uint32_t stackAdjust;
   5077  callWithABIPre(&stackAdjust, /* callFromWasm = */ true);
   5078 
   5079  // The instance register is used in builtin thunks and must be set.
   5080  bool needsBuiltinThunk = wasm::NeedsBuiltinThunk(imm);
   5081 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   5082  // The builtin thunk exits the JIT activation, if we don't have one we must
   5083  // use AutoUnsafeCallWithABI inside the builtin and check that here.
   5084  bool checkUnsafeCallWithABI = !needsBuiltinThunk;
   5085 #else
   5086  bool checkUnsafeCallWithABI = false;
   5087 #endif
   5088  if (needsBuiltinThunk || checkUnsafeCallWithABI) {
   5089    if (instanceOffset) {
   5090      loadPtr(Address(getStackPointer(), *instanceOffset + stackAdjust),
   5091              InstanceReg);
   5092    } else {
   5093      MOZ_CRASH("callWithABI missing instanceOffset");
   5094    }
   5095  }
   5096 
   5097 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   5098  if (checkUnsafeCallWithABI) {
   5099    wasmCheckUnsafeCallWithABIPre();
   5100  }
   5101 #endif
   5102 
   5103  CodeOffset raOffset = call(
   5104      wasm::CallSiteDesc(bytecode.offset(), wasm::CallSiteKind::Symbolic), imm);
   5105 
   5106  callWithABIPost(stackAdjust, result);
   5107 
   5108 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   5109  if (checkUnsafeCallWithABI) {
   5110    wasmCheckUnsafeCallWithABIPost();
   5111  }
   5112 #endif
   5113 
   5114  return raOffset;
   5115 }
   5116 
   5117 void MacroAssembler::callDebugWithABI(wasm::SymbolicAddress imm,
   5118                                      ABIType result) {
   5119  uint32_t stackAdjust;
   5120  callWithABIPre(&stackAdjust, /* callFromWasm = */ false);
   5121  call(imm);
   5122  callWithABIPost(stackAdjust, result);
   5123 }
   5124 
   5125 // ===============================================================
   5126 // Exit frame footer.
   5127 
   5128 void MacroAssembler::linkExitFrame(Register cxreg, Register scratch) {
   5129  loadPtr(Address(cxreg, JSContext::offsetOfActivation()), scratch);
   5130  storeStackPtr(Address(scratch, JitActivation::offsetOfPackedExitFP()));
   5131 }
   5132 
   5133 // ===============================================================
   5134 // Simple value-shuffling helpers, to hide MoveResolver verbosity
   5135 // in common cases.
   5136 
   5137 void MacroAssembler::moveRegPair(Register src0, Register src1, Register dst0,
   5138                                 Register dst1, MoveOp::Type type) {
   5139  MoveResolver& moves = moveResolver();
   5140  if (src0 != dst0) {
   5141    propagateOOM(moves.addMove(MoveOperand(src0), MoveOperand(dst0), type));
   5142  }
   5143  if (src1 != dst1) {
   5144    propagateOOM(moves.addMove(MoveOperand(src1), MoveOperand(dst1), type));
   5145  }
   5146  propagateOOM(moves.resolve());
   5147  if (oom()) {
   5148    return;
   5149  }
   5150 
   5151  MoveEmitter emitter(*this);
   5152  emitter.emit(moves);
   5153  emitter.finish();
   5154 }
   5155 
   5156 // ===============================================================
   5157 // Arithmetic functions
   5158 
   5159 void MacroAssembler::pow32(Register base, Register power, Register dest,
   5160                           Register temp1, Register temp2, Label* onOver) {
   5161  // Inline int32-specialized implementation of js::powi with overflow
   5162  // detection.
   5163 
   5164  move32(Imm32(1), dest);  // result = 1
   5165 
   5166  // x^y where x == 1 returns 1 for any y.
   5167  Label done;
   5168  branch32(Assembler::Equal, base, Imm32(1), &done);
   5169 
   5170  // x^y where y < 0 returns a non-int32 value for any x != 1. Except when y is
   5171  // large enough so that the result is no longer representable as a double with
   5172  // fractional parts. We can't easily determine when y is too large, so we bail
   5173  // here.
   5174  // Note: it's important for this condition to match the code in CacheIR.cpp
   5175  // (CanAttachInt32Pow) to prevent failure loops.
   5176  branchTest32(Assembler::Signed, power, power, onOver);
   5177 
   5178  move32(base, temp1);   // runningSquare = x
   5179  move32(power, temp2);  // n = y
   5180 
   5181  Label start;
   5182  jump(&start);
   5183 
   5184  Label loop;
   5185  bind(&loop);
   5186 
   5187  // runningSquare *= runningSquare
   5188  branchMul32(Assembler::Overflow, temp1, temp1, onOver);
   5189 
   5190  bind(&start);
   5191 
   5192  // if ((n & 1) != 0) result *= runningSquare
   5193  Label even;
   5194  branchTest32(Assembler::Zero, temp2, Imm32(1), &even);
   5195  branchMul32(Assembler::Overflow, temp1, dest, onOver);
   5196  bind(&even);
   5197 
   5198  // n >>= 1
   5199  // if (n == 0) return result
   5200  branchRshift32(Assembler::NonZero, Imm32(1), temp2, &loop);
   5201 
   5202  bind(&done);
   5203 }
   5204 
   5205 void MacroAssembler::powPtr(Register base, Register power, Register dest,
   5206                            Register temp1, Register temp2, Label* onOver) {
   5207  // Inline intptr-specialized implementation of BigInt::pow with overflow
   5208  // detection.
   5209 
   5210  // Negative exponents are disallowed for any BigInts.
   5211  branchTestPtr(Assembler::Signed, power, power, onOver);
   5212 
   5213  movePtr(ImmWord(1), dest);  // result = 1
   5214 
   5215  // x^y where x == 1 returns 1 for any y.
   5216  Label done;
   5217  branchPtr(Assembler::Equal, base, ImmWord(1), &done);
   5218 
   5219  // x^y where x == -1 returns 1 for even y, and -1 for odd y.
   5220  Label notNegativeOne;
   5221  branchPtr(Assembler::NotEqual, base, ImmWord(-1), &notNegativeOne);
   5222  test32MovePtr(Assembler::NonZero, power, Imm32(1), base, dest);
   5223  jump(&done);
   5224  bind(&notNegativeOne);
   5225 
   5226  // x ** y with |x| > 1 and y >= DigitBits can't be pointer-sized.
   5227  branchPtr(Assembler::GreaterThanOrEqual, power, Imm32(BigInt::DigitBits),
   5228            onOver);
   5229 
   5230  movePtr(base, temp1);   // runningSquare = x
   5231  movePtr(power, temp2);  // n = y
   5232 
   5233  Label start;
   5234  jump(&start);
   5235 
   5236  Label loop;
   5237  bind(&loop);
   5238 
   5239  // runningSquare *= runningSquare
   5240  branchMulPtr(Assembler::Overflow, temp1, temp1, onOver);
   5241 
   5242  bind(&start);
   5243 
   5244  // if ((n & 1) != 0) result *= runningSquare
   5245  Label even;
   5246  branchTest32(Assembler::Zero, temp2, Imm32(1), &even);
   5247  branchMulPtr(Assembler::Overflow, temp1, dest, onOver);
   5248  bind(&even);
   5249 
   5250  // n >>= 1
   5251  // if (n == 0) return result
   5252  branchRshift32(Assembler::NonZero, Imm32(1), temp2, &loop);
   5253 
   5254  bind(&done);
   5255 }
   5256 
   5257 void MacroAssembler::signInt32(Register input, Register output) {
   5258  MOZ_ASSERT(input != output);
   5259 
   5260  rshift32Arithmetic(Imm32(31), input, output);
   5261  or32(Imm32(1), output);
   5262  cmp32Move32(Assembler::Equal, input, Imm32(0), input, output);
   5263 }
   5264 
   5265 void MacroAssembler::signDouble(FloatRegister input, FloatRegister output) {
   5266  MOZ_ASSERT(input != output);
   5267 
   5268  Label done, zeroOrNaN, negative;
   5269  loadConstantDouble(0.0, output);
   5270  branchDouble(Assembler::DoubleEqualOrUnordered, input, output, &zeroOrNaN);
   5271  branchDouble(Assembler::DoubleLessThan, input, output, &negative);
   5272 
   5273  loadConstantDouble(1.0, output);
   5274  jump(&done);
   5275 
   5276  bind(&negative);
   5277  loadConstantDouble(-1.0, output);
   5278  jump(&done);
   5279 
   5280  bind(&zeroOrNaN);
   5281  moveDouble(input, output);
   5282 
   5283  bind(&done);
   5284 }
   5285 
   5286 void MacroAssembler::signDoubleToInt32(FloatRegister input, Register output,
   5287                                       FloatRegister temp, Label* fail) {
   5288  MOZ_ASSERT(input != temp);
   5289 
   5290  Label done, zeroOrNaN, negative;
   5291  loadConstantDouble(0.0, temp);
   5292  branchDouble(Assembler::DoubleEqualOrUnordered, input, temp, &zeroOrNaN);
   5293  branchDouble(Assembler::DoubleLessThan, input, temp, &negative);
   5294 
   5295  move32(Imm32(1), output);
   5296  jump(&done);
   5297 
   5298  bind(&negative);
   5299  move32(Imm32(-1), output);
   5300  jump(&done);
   5301 
   5302  // Fail for NaN and negative zero.
   5303  bind(&zeroOrNaN);
   5304  branchDouble(Assembler::DoubleUnordered, input, input, fail);
   5305 
   5306  // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0
   5307  // is -Infinity instead of Infinity.
   5308  loadConstantDouble(1.0, temp);
   5309  divDouble(input, temp);
   5310  branchDouble(Assembler::DoubleLessThan, temp, input, fail);
   5311  move32(Imm32(0), output);
   5312 
   5313  bind(&done);
   5314 }
   5315 
   5316 void MacroAssembler::randomDouble(Register rng, FloatRegister dest,
   5317                                  Register64 temp0, Register64 temp1) {
   5318  using mozilla::non_crypto::XorShift128PlusRNG;
   5319 
   5320  static_assert(
   5321      sizeof(XorShift128PlusRNG) == 2 * sizeof(uint64_t),
   5322      "Code below assumes XorShift128PlusRNG contains two uint64_t values");
   5323 
   5324  Address state0Addr(rng, XorShift128PlusRNG::offsetOfState0());
   5325  Address state1Addr(rng, XorShift128PlusRNG::offsetOfState1());
   5326 
   5327  Register64 s0Reg = temp0;
   5328  Register64 s1Reg = temp1;
   5329 
   5330  // uint64_t s1 = mState[0];
   5331  load64(state0Addr, s1Reg);
   5332 
   5333  // s1 ^= s1 << 23;
   5334  move64(s1Reg, s0Reg);
   5335  lshift64(Imm32(23), s1Reg);
   5336  xor64(s0Reg, s1Reg);
   5337 
   5338  // s1 ^= s1 >> 17
   5339  move64(s1Reg, s0Reg);
   5340  rshift64(Imm32(17), s1Reg);
   5341  xor64(s0Reg, s1Reg);
   5342 
   5343  // const uint64_t s0 = mState[1];
   5344  load64(state1Addr, s0Reg);
   5345 
   5346  // mState[0] = s0;
   5347  store64(s0Reg, state0Addr);
   5348 
   5349  // s1 ^= s0
   5350  xor64(s0Reg, s1Reg);
   5351 
   5352  // s1 ^= s0 >> 26
   5353  rshift64(Imm32(26), s0Reg);
   5354  xor64(s0Reg, s1Reg);
   5355 
   5356  // mState[1] = s1
   5357  store64(s1Reg, state1Addr);
   5358 
   5359  // s1 += mState[0]
   5360  load64(state0Addr, s0Reg);
   5361  add64(s0Reg, s1Reg);
   5362 
   5363  // See comment in XorShift128PlusRNG::nextDouble().
   5364  static constexpr int MantissaBits =
   5365      mozilla::FloatingPoint<double>::kExponentShift + 1;
   5366  static constexpr double ScaleInv = double(1) / (1ULL << MantissaBits);
   5367 
   5368  and64(Imm64((1ULL << MantissaBits) - 1), s1Reg);
   5369 
   5370  // Note: we know s1Reg isn't signed after the and64 so we can use the faster
   5371  // convertInt64ToDouble instead of convertUInt64ToDouble.
   5372  convertInt64ToDouble(s1Reg, dest);
   5373 
   5374  // dest *= ScaleInv
   5375  mulDoublePtr(ImmPtr(&ScaleInv), s0Reg.scratchReg(), dest);
   5376 }
   5377 
   5378 void MacroAssembler::roundFloat32(FloatRegister src, FloatRegister dest) {
   5379  MOZ_ASSERT(HasRoundInstruction(RoundingMode::Up));
   5380  MOZ_ASSERT(src != dest);
   5381 
   5382  nearbyIntFloat32(RoundingMode::Up, src, dest);
   5383 
   5384  ScratchFloat32Scope scratch(*this);
   5385  loadConstantFloat32(-0.5f, scratch);
   5386  addFloat32(dest, scratch);
   5387 
   5388  Label done;
   5389  branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, scratch, src, &done);
   5390  {
   5391    loadConstantFloat32(1.0f, scratch);
   5392    subFloat32(scratch, dest);
   5393  }
   5394  bind(&done);
   5395 }
   5396 
   5397 void MacroAssembler::roundDouble(FloatRegister src, FloatRegister dest) {
   5398  MOZ_ASSERT(HasRoundInstruction(RoundingMode::Up));
   5399  MOZ_ASSERT(src != dest);
   5400 
   5401  nearbyIntDouble(RoundingMode::Up, src, dest);
   5402 
   5403  ScratchDoubleScope scratch(*this);
   5404  loadConstantDouble(-0.5, scratch);
   5405  addDouble(dest, scratch);
   5406 
   5407  Label done;
   5408  branchDouble(Assembler::DoubleLessThanOrEqualOrUnordered, scratch, src,
   5409               &done);
   5410  {
   5411    loadConstantDouble(1.0, scratch);
   5412    subDouble(scratch, dest);
   5413  }
   5414  bind(&done);
   5415 }
   5416 
   5417 void MacroAssembler::sameValueDouble(FloatRegister left, FloatRegister right,
   5418                                     FloatRegister temp, Register dest) {
   5419  Label nonEqual, isSameValue, isNotSameValue;
   5420  branchDouble(Assembler::DoubleNotEqualOrUnordered, left, right, &nonEqual);
   5421  {
   5422    // First, test for being equal to 0.0, which also includes -0.0.
   5423    loadConstantDouble(0.0, temp);
   5424    branchDouble(Assembler::DoubleNotEqual, left, temp, &isSameValue);
   5425 
   5426    // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0
   5427    // is -Infinity instead of Infinity.
   5428    Label isNegInf;
   5429    loadConstantDouble(1.0, temp);
   5430    divDouble(left, temp);
   5431    branchDouble(Assembler::DoubleLessThan, temp, left, &isNegInf);
   5432    {
   5433      loadConstantDouble(1.0, temp);
   5434      divDouble(right, temp);
   5435      branchDouble(Assembler::DoubleGreaterThan, temp, right, &isSameValue);
   5436      jump(&isNotSameValue);
   5437    }
   5438    bind(&isNegInf);
   5439    {
   5440      loadConstantDouble(1.0, temp);
   5441      divDouble(right, temp);
   5442      branchDouble(Assembler::DoubleLessThan, temp, right, &isSameValue);
   5443      jump(&isNotSameValue);
   5444    }
   5445  }
   5446  bind(&nonEqual);
   5447  {
   5448    // Test if both values are NaN.
   5449    branchDouble(Assembler::DoubleOrdered, left, left, &isNotSameValue);
   5450    branchDouble(Assembler::DoubleOrdered, right, right, &isNotSameValue);
   5451  }
   5452 
   5453  Label done;
   5454  bind(&isSameValue);
   5455  move32(Imm32(1), dest);
   5456  jump(&done);
   5457 
   5458  bind(&isNotSameValue);
   5459  move32(Imm32(0), dest);
   5460 
   5461  bind(&done);
   5462 }
   5463 
   5464 void MacroAssembler::minMaxArrayInt32(Register array, Register result,
   5465                                      Register temp1, Register temp2,
   5466                                      Register temp3, bool isMax, Label* fail) {
   5467  // array must be a packed array. Load its elements.
   5468  Register elements = temp1;
   5469  loadPtr(Address(array, NativeObject::offsetOfElements()), elements);
   5470 
   5471  // Load the length and guard that it is non-zero.
   5472  Address lengthAddr(elements, ObjectElements::offsetOfInitializedLength());
   5473  load32(lengthAddr, temp3);
   5474  branchTest32(Assembler::Zero, temp3, temp3, fail);
   5475 
   5476  // Compute the address of the last element.
   5477  Register elementsEnd = temp2;
   5478  BaseObjectElementIndex elementsEndAddr(elements, temp3,
   5479                                         -int32_t(sizeof(Value)));
   5480  computeEffectiveAddress(elementsEndAddr, elementsEnd);
   5481 
   5482  // Load the first element into result.
   5483  fallibleUnboxInt32(Address(elements, 0), result, fail);
   5484 
   5485  Label loop, done;
   5486  bind(&loop);
   5487 
   5488  // Check whether we're done.
   5489  branchPtr(Assembler::Equal, elements, elementsEnd, &done);
   5490 
   5491  // If not, advance to the next element and load it.
   5492  addPtr(Imm32(sizeof(Value)), elements);
   5493  fallibleUnboxInt32(Address(elements, 0), temp3, fail);
   5494 
   5495  // Update result if necessary.
   5496  if (isMax) {
   5497    max32(result, temp3, result);
   5498  } else {
   5499    min32(result, temp3, result);
   5500  }
   5501 
   5502  jump(&loop);
   5503  bind(&done);
   5504 }
   5505 
   5506 void MacroAssembler::minMaxArrayNumber(Register array, FloatRegister result,
   5507                                       FloatRegister floatTemp, Register temp1,
   5508                                       Register temp2, bool isMax,
   5509                                       Label* fail) {
   5510  // array must be a packed array. Load its elements.
   5511  Register elements = temp1;
   5512  loadPtr(Address(array, NativeObject::offsetOfElements()), elements);
   5513 
   5514  // Load the length and check if the array is empty.
   5515  Label isEmpty;
   5516  Address lengthAddr(elements, ObjectElements::offsetOfInitializedLength());
   5517  load32(lengthAddr, temp2);
   5518  branchTest32(Assembler::Zero, temp2, temp2, &isEmpty);
   5519 
   5520  // Compute the address of the last element.
   5521  Register elementsEnd = temp2;
   5522  BaseObjectElementIndex elementsEndAddr(elements, temp2,
   5523                                         -int32_t(sizeof(Value)));
   5524  computeEffectiveAddress(elementsEndAddr, elementsEnd);
   5525 
   5526  // Load the first element into result.
   5527  ensureDouble(Address(elements, 0), result, fail);
   5528 
   5529  Label loop, done;
   5530  bind(&loop);
   5531 
   5532  // Check whether we're done.
   5533  branchPtr(Assembler::Equal, elements, elementsEnd, &done);
   5534 
   5535  // If not, advance to the next element and load it into floatTemp.
   5536  addPtr(Imm32(sizeof(Value)), elements);
   5537  ensureDouble(Address(elements, 0), floatTemp, fail);
   5538 
   5539  // Update result if necessary.
   5540  if (isMax) {
   5541    maxDouble(floatTemp, result, /* handleNaN = */ true);
   5542  } else {
   5543    minDouble(floatTemp, result, /* handleNaN = */ true);
   5544  }
   5545  jump(&loop);
   5546 
   5547  // With no arguments, min/max return +Infinity/-Infinity respectively.
   5548  bind(&isEmpty);
   5549  if (isMax) {
   5550    loadConstantDouble(mozilla::NegativeInfinity<double>(), result);
   5551  } else {
   5552    loadConstantDouble(mozilla::PositiveInfinity<double>(), result);
   5553  }
   5554 
   5555  bind(&done);
   5556 }
   5557 
   5558 void MacroAssembler::loadRegExpLastIndex(Register regexp, Register string,
   5559                                         Register lastIndex,
   5560                                         Label* notFoundZeroLastIndex) {
   5561  Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
   5562  Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
   5563  Address stringLength(string, JSString::offsetOfLength());
   5564 
   5565  Label notGlobalOrSticky, loadedLastIndex;
   5566 
   5567  branchTest32(Assembler::Zero, flagsSlot,
   5568               Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
   5569               &notGlobalOrSticky);
   5570  {
   5571    // It's a global or sticky regular expression. Emit the following code:
   5572    //
   5573    //   lastIndex = regexp.lastIndex
   5574    //   if lastIndex > string.length:
   5575    //     jump to notFoundZeroLastIndex (skip the regexp match/test operation)
   5576    //
   5577    // The `notFoundZeroLastIndex` code should set regexp.lastIndex to 0 and
   5578    // treat this as a not-found result.
   5579    //
   5580    // See steps 5-8 in js::RegExpBuiltinExec.
   5581    //
   5582    // Earlier guards must have ensured regexp.lastIndex is a non-negative
   5583    // integer.
   5584 #ifdef DEBUG
   5585    {
   5586      Label ok;
   5587      branchTestInt32(Assembler::Equal, lastIndexSlot, &ok);
   5588      assumeUnreachable("Expected int32 value for lastIndex");
   5589      bind(&ok);
   5590    }
   5591 #endif
   5592    unboxInt32(lastIndexSlot, lastIndex);
   5593 #ifdef DEBUG
   5594    {
   5595      Label ok;
   5596      branchTest32(Assembler::NotSigned, lastIndex, lastIndex, &ok);
   5597      assumeUnreachable("Expected non-negative lastIndex");
   5598      bind(&ok);
   5599    }
   5600 #endif
   5601    branch32(Assembler::Below, stringLength, lastIndex, notFoundZeroLastIndex);
   5602    jump(&loadedLastIndex);
   5603  }
   5604 
   5605  bind(&notGlobalOrSticky);
   5606  move32(Imm32(0), lastIndex);
   5607 
   5608  bind(&loadedLastIndex);
   5609 }
   5610 
   5611 void MacroAssembler::loadAndClearRegExpSearcherLastLimit(Register result,
   5612                                                         Register scratch) {
   5613  MOZ_ASSERT(result != scratch);
   5614 
   5615  loadJSContext(scratch);
   5616 
   5617  Address limitField(scratch, JSContext::offsetOfRegExpSearcherLastLimit());
   5618  load32(limitField, result);
   5619 
   5620 #ifdef DEBUG
   5621  Label ok;
   5622  branch32(Assembler::NotEqual, result, Imm32(RegExpSearcherLastLimitSentinel),
   5623           &ok);
   5624  assumeUnreachable("Unexpected sentinel for regExpSearcherLastLimit");
   5625  bind(&ok);
   5626  store32(Imm32(RegExpSearcherLastLimitSentinel), limitField);
   5627 #endif
   5628 }
   5629 
   5630 void MacroAssembler::loadParsedRegExpShared(Register regexp, Register result,
   5631                                            Label* unparsed) {
   5632  Address sharedSlot(regexp, RegExpObject::offsetOfShared());
   5633  branchTestUndefined(Assembler::Equal, sharedSlot, unparsed);
   5634  unboxNonDouble(sharedSlot, result, JSVAL_TYPE_PRIVATE_GCTHING);
   5635 
   5636  static_assert(sizeof(RegExpShared::Kind) == sizeof(uint32_t));
   5637  branch32(Assembler::Equal, Address(result, RegExpShared::offsetOfKind()),
   5638           Imm32(int32_t(RegExpShared::Kind::Unparsed)), unparsed);
   5639 }
   5640 
   5641 // ===============================================================
   5642 // Branch functions
   5643 
   5644 void MacroAssembler::loadFunctionLength(Register func,
   5645                                        Register funFlagsAndArgCount,
   5646                                        Register output, Label* slowPath) {
   5647 #ifdef DEBUG
   5648  {
   5649    // These flags should already have been checked by caller.
   5650    Label ok;
   5651    uint32_t FlagsToCheck =
   5652        FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH;
   5653    branchTest32(Assembler::Zero, funFlagsAndArgCount, Imm32(FlagsToCheck),
   5654                 &ok);
   5655    assumeUnreachable("The function flags should already have been checked.");
   5656    bind(&ok);
   5657  }
   5658 #endif  // DEBUG
   5659 
   5660  // NOTE: `funFlagsAndArgCount` and `output` must be allowed to alias.
   5661 
   5662  // Load the target function's length.
   5663  Label isInterpreted, lengthLoaded;
   5664  branchTest32(Assembler::NonZero, funFlagsAndArgCount,
   5665               Imm32(FunctionFlags::BASESCRIPT), &isInterpreted);
   5666  {
   5667    // The length property of a native function stored with the flags.
   5668    rshift32(Imm32(JSFunction::ArgCountShift), funFlagsAndArgCount, output);
   5669    jump(&lengthLoaded);
   5670  }
   5671  bind(&isInterpreted);
   5672  {
   5673    // Load the length property of an interpreted function.
   5674    loadPrivate(Address(func, JSFunction::offsetOfJitInfoOrScript()), output);
   5675    loadPtr(Address(output, JSScript::offsetOfSharedData()), output);
   5676    branchTestPtr(Assembler::Zero, output, output, slowPath);
   5677    loadPtr(Address(output, SharedImmutableScriptData::offsetOfISD()), output);
   5678    load16ZeroExtend(Address(output, ImmutableScriptData::offsetOfFunLength()),
   5679                     output);
   5680  }
   5681  bind(&lengthLoaded);
   5682 }
   5683 
   5684 void MacroAssembler::loadFunctionName(Register func, Register output,
   5685                                      ImmGCPtr emptyString, Label* slowPath) {
   5686  MOZ_ASSERT(func != output);
   5687 
   5688  // Get the JSFunction flags.
   5689  load32(Address(func, JSFunction::offsetOfFlagsAndArgCount()), output);
   5690 
   5691  // If the name was previously resolved, the name property may be shadowed.
   5692  // If the function is an accessor with lazy name, AtomSlot contains the
   5693  // unprefixed name.
   5694  branchTest32(
   5695      Assembler::NonZero, output,
   5696      Imm32(FunctionFlags::RESOLVED_NAME | FunctionFlags::LAZY_ACCESSOR_NAME),
   5697      slowPath);
   5698 
   5699  Label noName, done;
   5700  branchTest32(Assembler::NonZero, output,
   5701               Imm32(FunctionFlags::HAS_GUESSED_ATOM), &noName);
   5702 
   5703  Address atomAddr(func, JSFunction::offsetOfAtom());
   5704  branchTestUndefined(Assembler::Equal, atomAddr, &noName);
   5705  unboxString(atomAddr, output);
   5706  jump(&done);
   5707 
   5708  {
   5709    bind(&noName);
   5710 
   5711    // An absent name property defaults to the empty string.
   5712    movePtr(emptyString, output);
   5713  }
   5714 
   5715  bind(&done);
   5716 }
   5717 
   5718 void MacroAssembler::assertFunctionIsExtended(Register func) {
   5719 #ifdef DEBUG
   5720  Label extended;
   5721  branchTestFunctionFlags(func, FunctionFlags::EXTENDED, Assembler::NonZero,
   5722                          &extended);
   5723  assumeUnreachable("Function is not extended");
   5724  bind(&extended);
   5725 #endif
   5726 }
   5727 
   5728 void MacroAssembler::branchTestType(Condition cond, Register tag,
   5729                                    JSValueType type, Label* label) {
   5730  switch (type) {
   5731    case JSVAL_TYPE_DOUBLE:
   5732      branchTestDouble(cond, tag, label);
   5733      break;
   5734    case JSVAL_TYPE_INT32:
   5735      branchTestInt32(cond, tag, label);
   5736      break;
   5737    case JSVAL_TYPE_BOOLEAN:
   5738      branchTestBoolean(cond, tag, label);
   5739      break;
   5740    case JSVAL_TYPE_UNDEFINED:
   5741      branchTestUndefined(cond, tag, label);
   5742      break;
   5743    case JSVAL_TYPE_NULL:
   5744      branchTestNull(cond, tag, label);
   5745      break;
   5746    case JSVAL_TYPE_MAGIC:
   5747      branchTestMagic(cond, tag, label);
   5748      break;
   5749    case JSVAL_TYPE_STRING:
   5750      branchTestString(cond, tag, label);
   5751      break;
   5752    case JSVAL_TYPE_SYMBOL:
   5753      branchTestSymbol(cond, tag, label);
   5754      break;
   5755    case JSVAL_TYPE_BIGINT:
   5756      branchTestBigInt(cond, tag, label);
   5757      break;
   5758    case JSVAL_TYPE_OBJECT:
   5759      branchTestObject(cond, tag, label);
   5760      break;
   5761    default:
   5762      MOZ_CRASH("Unexpected value type");
   5763  }
   5764 }
   5765 
   5766 void MacroAssembler::branchTestObjShapeListImpl(
   5767    Register obj, Register shapeElements, size_t itemSize,
   5768    Register shapeScratch, Register endScratch, Register spectreScratch,
   5769    Label* fail) {
   5770  bool needSpectreMitigations = spectreScratch != InvalidReg;
   5771 
   5772  Label done;
   5773 
   5774  // Load the object's shape pointer into shapeScratch, and prepare to compare
   5775  // it with the shapes in the list. The shapes are stored as private values so
   5776  // we can compare directly.
   5777  loadPtr(Address(obj, JSObject::offsetOfShape()), shapeScratch);
   5778 
   5779  // Compute end pointer.
   5780  Address lengthAddr(shapeElements,
   5781                     ObjectElements::offsetOfInitializedLength());
   5782  load32(lengthAddr, endScratch);
   5783  branch32(Assembler::Equal, endScratch, Imm32(0), fail);
   5784  BaseObjectElementIndex endPtrAddr(shapeElements, endScratch);
   5785  computeEffectiveAddress(endPtrAddr, endScratch);
   5786 
   5787  Label loop;
   5788  bind(&loop);
   5789 
   5790  // Compare the object's shape with a shape from the list. Note that on 64-bit
   5791  // this includes the tag bits, but on 32-bit we only compare the low word of
   5792  // the value. This is fine because the list of shapes is never exposed and the
   5793  // tag is guaranteed to be PrivateGCThing.
   5794  if (needSpectreMitigations) {
   5795    move32(Imm32(0), spectreScratch);
   5796  }
   5797  branchPtr(Assembler::Equal, Address(shapeElements, 0), shapeScratch, &done);
   5798  if (needSpectreMitigations) {
   5799    spectreMovePtr(Assembler::Equal, spectreScratch, obj);
   5800  }
   5801 
   5802  // Advance to next shape and loop if not finished.
   5803  addPtr(Imm32(itemSize), shapeElements);
   5804  branchPtr(Assembler::Below, shapeElements, endScratch, &loop);
   5805 
   5806  jump(fail);
   5807  bind(&done);
   5808 }
   5809 
   5810 void MacroAssembler::branchTestObjShapeList(
   5811    Register obj, Register shapeElements, Register shapeScratch,
   5812    Register endScratch, Register spectreScratch, Label* fail) {
   5813  branchTestObjShapeListImpl(obj, shapeElements, sizeof(Value), shapeScratch,
   5814                             endScratch, spectreScratch, fail);
   5815 }
   5816 
   5817 void MacroAssembler::branchTestObjShapeListSetOffset(
   5818    Register obj, Register shapeElements, Register offset,
   5819    Register shapeScratch, Register endScratch, Register spectreScratch,
   5820    Label* fail) {
   5821  branchTestObjShapeListImpl(obj, shapeElements, 2 * sizeof(Value),
   5822                             shapeScratch, endScratch, spectreScratch, fail);
   5823 
   5824  // The shapeElements register points to the matched shape (if found).
   5825  // The corresponding offset is saved in the array as the next value.
   5826  load32(Address(shapeElements, sizeof(Value)), offset);
   5827 }
   5828 
   5829 void MacroAssembler::branchTestObjCompartment(Condition cond, Register obj,
   5830                                              const Address& compartment,
   5831                                              Register scratch, Label* label) {
   5832  MOZ_ASSERT(obj != scratch);
   5833  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5834  loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
   5835  loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
   5836  loadPtr(Address(scratch, Realm::offsetOfCompartment()), scratch);
   5837  branchPtr(cond, compartment, scratch, label);
   5838 }
   5839 
   5840 void MacroAssembler::branchTestObjCompartment(
   5841    Condition cond, Register obj, const JS::Compartment* compartment,
   5842    Register scratch, Label* label) {
   5843  MOZ_ASSERT(obj != scratch);
   5844  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5845  loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
   5846  loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
   5847  loadPtr(Address(scratch, Realm::offsetOfCompartment()), scratch);
   5848  branchPtr(cond, scratch, ImmPtr(compartment), label);
   5849 }
   5850 
   5851 void MacroAssembler::branchIfNonNativeObj(Register obj, Register scratch,
   5852                                          Label* label) {
   5853  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5854  branchTest32(Assembler::Zero,
   5855               Address(scratch, Shape::offsetOfImmutableFlags()),
   5856               Imm32(Shape::isNativeBit()), label);
   5857 }
   5858 
   5859 void MacroAssembler::branchIfObjectNotExtensible(Register obj, Register scratch,
   5860                                                 Label* label) {
   5861  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5862 
   5863  // Spectre-style checks are not needed here because we do not interpret data
   5864  // based on this check.
   5865  static_assert(sizeof(ObjectFlags) == sizeof(uint32_t));
   5866  load32(Address(scratch, Shape::offsetOfObjectFlags()), scratch);
   5867  branchTest32(Assembler::NonZero, scratch,
   5868               Imm32(uint32_t(ObjectFlag::NotExtensible)), label);
   5869 }
   5870 
   5871 void MacroAssembler::branchTestObjectNeedsProxyResultValidation(
   5872    Condition cond, Register obj, Register scratch, Label* label) {
   5873  MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
   5874 
   5875  Label done;
   5876  Label* doValidation = cond == NonZero ? label : &done;
   5877  Label* skipValidation = cond == NonZero ? &done : label;
   5878 
   5879  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5880  branchTest32(Assembler::Zero,
   5881               Address(scratch, Shape::offsetOfImmutableFlags()),
   5882               Imm32(Shape::isNativeBit()), doValidation);
   5883  static_assert(sizeof(ObjectFlags) == sizeof(uint32_t));
   5884  load32(Address(scratch, Shape::offsetOfObjectFlags()), scratch);
   5885  branchTest32(Assembler::NonZero, scratch,
   5886               Imm32(uint32_t(ObjectFlag::NeedsProxyGetSetResultValidation)),
   5887               doValidation);
   5888 
   5889  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   5890  loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
   5891  loadPtr(Address(scratch, BaseShape::offsetOfClasp()), scratch);
   5892  loadPtr(Address(scratch, offsetof(JSClass, cOps)), scratch);
   5893  branchTestPtr(Assembler::Zero, scratch, scratch, skipValidation);
   5894  loadPtr(Address(scratch, offsetof(JSClassOps, resolve)), scratch);
   5895  branchTestPtr(Assembler::NonZero, scratch, scratch, doValidation);
   5896  bind(&done);
   5897 }
   5898 
   5899 void MacroAssembler::wasmTrap(wasm::Trap trap,
   5900                              const wasm::TrapSiteDesc& trapSiteDesc) {
   5901  FaultingCodeOffset fco = wasmTrapInstruction();
   5902  MOZ_ASSERT_IF(!oom(),
   5903                currentOffset() - fco.get() == WasmTrapInstructionLength);
   5904 
   5905  append(trap, wasm::TrapMachineInsn::OfficialUD, fco.get(), trapSiteDesc);
   5906 }
   5907 
   5908 uint32_t MacroAssembler::wasmReserveStackChecked(uint32_t amount, Label* fail) {
   5909  Register scratch1 = ABINonArgReg0;
   5910  Register scratch2 = ABINonArgReg1;
   5911  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()), scratch2);
   5912 
   5913  if (amount > MAX_UNCHECKED_LEAF_FRAME_SIZE) {
   5914    // The frame is large.  Don't bump sp until after the stack limit check so
   5915    // that the trap handler isn't called with a wild sp.
   5916    moveStackPtrTo(scratch1);
   5917    branchPtr(Assembler::Below, scratch1, Imm32(amount), fail);
   5918    subPtr(Imm32(amount), scratch1);
   5919    branchPtr(Assembler::AboveOrEqual,
   5920              Address(scratch2, JSContext::offsetOfWasm() +
   5921                                    wasm::Context::offsetOfStackLimit()),
   5922              scratch1, fail);
   5923    reserveStack(amount);
   5924    // The stack amount was reserved after branching to the fail label.
   5925    return 0;
   5926  }
   5927 
   5928  reserveStack(amount);
   5929  branchStackPtrRhs(Assembler::AboveOrEqual,
   5930                    Address(scratch2, JSContext::offsetOfWasm() +
   5931                                          wasm::Context::offsetOfStackLimit()),
   5932                    fail);
   5933  // The stack amount was reserved before branching to the fail label.
   5934  return amount;
   5935 }
   5936 
   5937 static void MoveDataBlock(MacroAssembler& masm, Register base, int32_t from,
   5938                          int32_t to, uint32_t size) {
   5939  MOZ_ASSERT(base != masm.getStackPointer());
   5940  if (from == to || size == 0) {
   5941    return;  // noop
   5942  }
   5943 
   5944 #ifdef JS_CODEGEN_ARM64
   5945  vixl::UseScratchRegisterScope temps(&masm);
   5946  const Register scratch = temps.AcquireX().asUnsized();
   5947 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_X86)
   5948  static constexpr Register scratch = ABINonArgReg0;
   5949  masm.push(scratch);
   5950 #elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \
   5951    defined(JS_CODEGEN_RISCV64)
   5952  UseScratchRegisterScope temps(masm);
   5953  Register scratch = temps.Acquire();
   5954 #elif !defined(JS_CODEGEN_NONE)
   5955  const Register scratch = ScratchReg;
   5956 #else
   5957  const Register scratch = InvalidReg;
   5958 #endif
   5959 
   5960  if (to < from) {
   5961    for (uint32_t i = 0; i < size; i += sizeof(void*)) {
   5962      masm.loadPtr(Address(base, from + i), scratch);
   5963      masm.storePtr(scratch, Address(base, to + i));
   5964    }
   5965  } else {
   5966    for (uint32_t i = size; i > 0;) {
   5967      i -= sizeof(void*);
   5968      masm.loadPtr(Address(base, from + i), scratch);
   5969      masm.storePtr(scratch, Address(base, to + i));
   5970    }
   5971  }
   5972 
   5973 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_X86)
   5974  masm.pop(scratch);
   5975 #endif
   5976 }
   5977 
   5978 struct ReturnCallTrampolineData {
   5979 #ifdef JS_CODEGEN_ARM
   5980  uint32_t trampolineOffset;
   5981 #else
   5982  CodeLabel trampoline;
   5983 #endif
   5984 };
   5985 
   5986 static ReturnCallTrampolineData MakeReturnCallTrampoline(MacroAssembler& masm) {
   5987  uint32_t savedPushed = masm.framePushed();
   5988 
   5989  ReturnCallTrampolineData data;
   5990 
   5991  {
   5992 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
   5993    AutoForbidPoolsAndNops afp(&masm, 1);
   5994 #elif defined(JS_CODEGEN_RISCV64)
   5995    BlockTrampolinePoolScope block_trampoline_pool(&masm, 1);
   5996 #endif
   5997 
   5998    // Build simple trampoline code: load the instance slot from the frame,
   5999    // restore FP, and return to prevous caller.
   6000 #ifdef JS_CODEGEN_ARM
   6001    data.trampolineOffset = masm.currentOffset();
   6002 #else
   6003    masm.bind(&data.trampoline);
   6004 #endif
   6005 
   6006    masm.setFramePushed(AlignBytes(
   6007        wasm::FrameWithInstances::sizeOfInstanceFieldsAndShadowStack(),
   6008        WasmStackAlignment));
   6009 
   6010    masm.wasmMarkCallAsSlow();
   6011  }
   6012 
   6013  masm.loadPtr(
   6014      Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
   6015      InstanceReg);
   6016  masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
   6017  masm.moveToStackPtr(FramePointer);
   6018 #ifdef JS_CODEGEN_ARM64
   6019  masm.pop(FramePointer, lr);
   6020  masm.append(wasm::CodeRangeUnwindInfo::UseFpLr, masm.currentOffset());
   6021  masm.Mov(PseudoStackPointer64, vixl::sp);
   6022  masm.abiret();
   6023 #elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64)
   6024  masm.loadPtr(Address(FramePointer, wasm::Frame::returnAddressOffset()), ra);
   6025  masm.loadPtr(Address(FramePointer, wasm::Frame::callerFPOffset()),
   6026               FramePointer);
   6027  masm.append(wasm::CodeRangeUnwindInfo::UseFpLr, masm.currentOffset());
   6028  masm.addToStackPtr(Imm32(sizeof(wasm::Frame)));
   6029  masm.abiret();
   6030 #else
   6031  masm.pop(FramePointer);
   6032  masm.append(wasm::CodeRangeUnwindInfo::UseFp, masm.currentOffset());
   6033  masm.ret();
   6034 #endif
   6035 
   6036  masm.append(wasm::CodeRangeUnwindInfo::Normal, masm.currentOffset());
   6037  masm.setFramePushed(savedPushed);
   6038  return data;
   6039 }
   6040 
   6041 // CollapseWasmFrame methods merge frames fields: callee parameters, instance
   6042 // slots, and caller RA. See the diagram below. The C0 is the previous caller,
   6043 // the C1 is the caller of the return call, and the C2 is the callee.
   6044 //
   6045 //    +-------------------+          +--------------------+
   6046 //    |C0 instance slots  |          |C0 instance slots   |
   6047 //    +-------------------+ -+       +--------------------+ -+
   6048 //    |   RA              |  |       |   RA               |  |
   6049 //    +-------------------+  | C0    +--------------------+  |C0
   6050 //    |   FP              |  v       |   FP               |  v
   6051 //    +-------------------+          +--------------------+
   6052 //    |C0 private frame   |          |C0 private frame    |
   6053 //    +-------------------+          +--------------------+
   6054 //    |C1 results area    |          |C1/C2 results area  |
   6055 //    +-------------------+          +--------------------+
   6056 //    |C1 parameters      |          |? trampoline frame  |
   6057 //    +-------------------+          +--------------------+
   6058 //    |C1 instance slots  |          |C2 parameters       |
   6059 //    +-------------------+ -+       +--------------------+
   6060 //    |C0 RA              |  |       |C2 instance slots’  |
   6061 //    +-------------------+  | C1    +--------------------+ -+
   6062 //    |C0 FP              |  v       |C0 RA’              |  |
   6063 //    +-------------------+          +--------------------+  | C2
   6064 //    |C1 private frame   |          |C0 FP’              |  v
   6065 //    +-------------------+          +--------------------+ <= start of C2
   6066 //    |C2 parameters      |
   6067 //    +-------------------+
   6068 //    |C2 instance slots  |
   6069 //    +-------------------+ <= call C2
   6070 //
   6071 // The C2 parameters are moved in place of the C1 parameters, and the
   6072 // C1 frame data is removed. The instance slots, return address, and
   6073 // frame pointer to the C0 callsite are saved or adjusted.
   6074 //
   6075 // For cross-instance calls, the trampoline frame will be introduced
   6076 // if the C0 callsite has no ability to restore instance registers and realm.
   6077 
   6078 static void CollapseWasmFrameFast(MacroAssembler& masm,
   6079                                  const ReturnCallAdjustmentInfo& retCallInfo) {
   6080  uint32_t framePushedAtStart = masm.framePushed();
   6081  static_assert(sizeof(wasm::Frame) == 2 * sizeof(void*));
   6082 
   6083  // The instance slots + stack arguments are expected to be padded and
   6084  // aligned to the WasmStackAlignment boundary. There is no data expected
   6085  // in the padded region, such as results stack area or locals, to avoid
   6086  // unwanted stack growth.
   6087  uint32_t newSlotsAndStackArgBytes =
   6088      AlignBytes(retCallInfo.newSlotsAndStackArgBytes, WasmStackAlignment);
   6089  uint32_t oldSlotsAndStackArgBytes =
   6090      AlignBytes(retCallInfo.oldSlotsAndStackArgBytes, WasmStackAlignment);
   6091 
   6092  static constexpr Register tempForCaller = WasmTailCallInstanceScratchReg;
   6093  static constexpr Register tempForFP = WasmTailCallFPScratchReg;
   6094  static constexpr Register tempForRA = WasmTailCallRAScratchReg;
   6095 #ifndef JS_USE_LINK_REGISTER
   6096  masm.push(tempForRA);
   6097 #endif
   6098 
   6099  // Load the FP, RA, and instance slots into registers to preserve them while
   6100  // the new frame is collapsed over the current one.
   6101  masm.loadPtr(Address(FramePointer, wasm::Frame::callerFPOffset()), tempForFP);
   6102  masm.loadPtr(Address(FramePointer, wasm::Frame::returnAddressOffset()),
   6103               tempForRA);
   6104  masm.append(wasm::CodeRangeUnwindInfo::RestoreFpRa, masm.currentOffset());
   6105  bool copyCallerSlot = oldSlotsAndStackArgBytes != newSlotsAndStackArgBytes;
   6106  if (copyCallerSlot) {
   6107    masm.loadPtr(
   6108        Address(FramePointer, wasm::FrameWithInstances::callerInstanceOffset()),
   6109        tempForCaller);
   6110  }
   6111 
   6112  // Copy parameters data, ignoring shadow data and instance slots.
   6113  // Make all offsets relative to the FramePointer.
   6114  int32_t newArgSrc = -framePushedAtStart;
   6115  int32_t newArgDest =
   6116      sizeof(wasm::Frame) + oldSlotsAndStackArgBytes - newSlotsAndStackArgBytes;
   6117  const uint32_t SlotsSize =
   6118      wasm::FrameWithInstances::sizeOfInstanceFieldsAndShadowStack();
   6119  MoveDataBlock(masm, FramePointer, newArgSrc + SlotsSize,
   6120                newArgDest + SlotsSize,
   6121                retCallInfo.newSlotsAndStackArgBytes - SlotsSize);
   6122 
   6123  // Copy caller instance slots from the current frame.
   6124  if (copyCallerSlot) {
   6125    masm.storePtr(
   6126        tempForCaller,
   6127        Address(FramePointer, newArgDest + WasmCallerInstanceOffsetBeforeCall));
   6128  }
   6129 
   6130  // Store current instance as the new callee instance slot.
   6131  masm.storePtr(
   6132      InstanceReg,
   6133      Address(FramePointer, newArgDest + WasmCalleeInstanceOffsetBeforeCall));
   6134 
   6135 #ifdef JS_USE_LINK_REGISTER
   6136  // RA is already in its place, just move stack.
   6137  masm.addToStackPtr(Imm32(framePushedAtStart + newArgDest));
   6138 #else
   6139  // Push RA to new frame: store RA, restore temp, and move stack.
   6140  int32_t newFrameOffset = newArgDest - sizeof(wasm::Frame);
   6141  masm.storePtr(tempForRA,
   6142                Address(FramePointer,
   6143                        newFrameOffset + wasm::Frame::returnAddressOffset()));
   6144  // Restore tempForRA, but keep RA on top of the stack.
   6145  // There is no non-locking exchange instruction between register and memory.
   6146  // Using tempForCaller as scratch register.
   6147  masm.loadPtr(Address(masm.getStackPointer(), 0), tempForCaller);
   6148  masm.storePtr(tempForRA, Address(masm.getStackPointer(), 0));
   6149  masm.mov(tempForCaller, tempForRA);
   6150  masm.append(wasm::CodeRangeUnwindInfo::RestoreFp, masm.currentOffset());
   6151  masm.addToStackPtr(Imm32(framePushedAtStart + newFrameOffset +
   6152                           wasm::Frame::returnAddressOffset() + sizeof(void*)));
   6153 #endif
   6154 
   6155  masm.movePtr(tempForFP, FramePointer);
   6156  // Setting framePushed to pre-collapse state, to properly set that in the
   6157  // following code.
   6158  masm.setFramePushed(framePushedAtStart);
   6159 }
   6160 
   6161 static void CollapseWasmFrameSlow(MacroAssembler& masm,
   6162                                  const ReturnCallAdjustmentInfo& retCallInfo,
   6163                                  wasm::CallSiteDesc desc,
   6164                                  ReturnCallTrampolineData data) {
   6165  uint32_t framePushedAtStart = masm.framePushed();
   6166  static constexpr Register tempForCaller = WasmTailCallInstanceScratchReg;
   6167  static constexpr Register tempForFP = WasmTailCallFPScratchReg;
   6168  static constexpr Register tempForRA = WasmTailCallRAScratchReg;
   6169 
   6170  static_assert(sizeof(wasm::Frame) == 2 * sizeof(void*));
   6171 
   6172  // The hidden frame will "break" after wasm::Frame data fields.
   6173  // Calculate sum of wasm stack alignment before and after the break as
   6174  // the size to reserve.
   6175  const uint32_t HiddenFrameAfterSize =
   6176      AlignBytes(wasm::FrameWithInstances::sizeOfInstanceFieldsAndShadowStack(),
   6177                 WasmStackAlignment);
   6178  const uint32_t HiddenFrameSize =
   6179      AlignBytes(sizeof(wasm::Frame), WasmStackAlignment) +
   6180      HiddenFrameAfterSize;
   6181 
   6182  // If it is not slow, prepare two frame: one is regular wasm frame, and
   6183  // another one is hidden. The hidden frame contains one instance slots
   6184  // for unwind and recovering pinned registers.
   6185  // The instance slots + stack arguments are expected to be padded and
   6186  // aligned to the WasmStackAlignment boundary. There is no data expected
   6187  // in the padded region, such as results stack area or locals, to avoid
   6188  // unwanted stack growth.
   6189  // The Hidden frame will be inserted with this constraint too.
   6190  uint32_t newSlotsAndStackArgBytes =
   6191      AlignBytes(retCallInfo.newSlotsAndStackArgBytes, WasmStackAlignment);
   6192  uint32_t oldSlotsAndStackArgBytes =
   6193      AlignBytes(retCallInfo.oldSlotsAndStackArgBytes, WasmStackAlignment);
   6194 
   6195  // Make all offsets relative to the FramePointer.
   6196  int32_t newArgSrc = -framePushedAtStart;
   6197  int32_t newArgDest = sizeof(wasm::Frame) + oldSlotsAndStackArgBytes -
   6198                       HiddenFrameSize - newSlotsAndStackArgBytes;
   6199  int32_t hiddenFrameArgsDest =
   6200      sizeof(wasm::Frame) + oldSlotsAndStackArgBytes - HiddenFrameAfterSize;
   6201 
   6202  // It will be possible to overwrite data (on the top of the stack) due to
   6203  // the added hidden frame, reserve needed space.
   6204  uint32_t reserved = newArgDest - int32_t(sizeof(void*)) < newArgSrc
   6205                          ? newArgSrc - newArgDest + sizeof(void*)
   6206                          : 0;
   6207  masm.reserveStack(reserved);
   6208 
   6209 #ifndef JS_USE_LINK_REGISTER
   6210  masm.push(tempForRA);
   6211 #endif
   6212 
   6213  // Load FP, RA and instance slots to preserve them from being overwritten.
   6214  masm.loadPtr(Address(FramePointer, wasm::Frame::callerFPOffset()), tempForFP);
   6215  masm.loadPtr(Address(FramePointer, wasm::Frame::returnAddressOffset()),
   6216               tempForRA);
   6217  masm.append(wasm::CodeRangeUnwindInfo::RestoreFpRa, masm.currentOffset());
   6218  masm.loadPtr(
   6219      Address(FramePointer, newArgSrc + WasmCallerInstanceOffsetBeforeCall),
   6220      tempForCaller);
   6221 
   6222  // Copy parameters data, ignoring shadow data and instance slots.
   6223  const uint32_t SlotsSize =
   6224      wasm::FrameWithInstances::sizeOfInstanceFieldsAndShadowStack();
   6225  MoveDataBlock(masm, FramePointer, newArgSrc + SlotsSize,
   6226                newArgDest + SlotsSize,
   6227                retCallInfo.newSlotsAndStackArgBytes - SlotsSize);
   6228 
   6229  // Form hidden frame for trampoline.
   6230  int32_t newFPOffset = hiddenFrameArgsDest - sizeof(wasm::Frame);
   6231  masm.storePtr(
   6232      tempForRA,
   6233      Address(FramePointer, newFPOffset + wasm::Frame::returnAddressOffset()));
   6234 
   6235  // Copy original FP.
   6236  masm.storePtr(
   6237      tempForFP,
   6238      Address(FramePointer, newFPOffset + wasm::Frame::callerFPOffset()));
   6239 
   6240  // Set up instance slots.
   6241  masm.storePtr(
   6242      tempForCaller,
   6243      Address(FramePointer,
   6244              newFPOffset + wasm::FrameWithInstances::calleeInstanceOffset()));
   6245  masm.storePtr(
   6246      tempForCaller,
   6247      Address(FramePointer, newArgDest + WasmCallerInstanceOffsetBeforeCall));
   6248  masm.storePtr(
   6249      InstanceReg,
   6250      Address(FramePointer, newArgDest + WasmCalleeInstanceOffsetBeforeCall));
   6251 
   6252 #ifdef JS_CODEGEN_ARM
   6253  // ARM has no CodeLabel -- calculate PC directly.
   6254  masm.mov(pc, tempForRA);
   6255  masm.computeEffectiveAddress(
   6256      Address(tempForRA,
   6257              int32_t(data.trampolineOffset - masm.currentOffset() - 4)),
   6258      tempForRA);
   6259  masm.append(desc, CodeOffset(data.trampolineOffset));
   6260 #else
   6261 
   6262 #  if defined(JS_CODEGEN_MIPS64)
   6263  // intermediate values in ra can break the unwinder.
   6264  masm.mov(&data.trampoline, ScratchRegister);
   6265  // thus, modify ra in only one instruction.
   6266  masm.mov(ScratchRegister, tempForRA);
   6267 #  elif defined(JS_CODEGEN_LOONG64)
   6268  // intermediate values in ra can break the unwinder.
   6269  masm.mov(&data.trampoline, SavedScratchRegister);
   6270  // thus, modify ra in only one instruction.
   6271  masm.mov(SavedScratchRegister, tempForRA);
   6272 #  else
   6273  masm.mov(&data.trampoline, tempForRA);
   6274 #  endif
   6275 
   6276  masm.addCodeLabel(data.trampoline);
   6277  // Add slow trampoline callsite description, to be annotated in
   6278  // stack/frame iterators.
   6279  masm.append(desc, *data.trampoline.target());
   6280 #endif
   6281 
   6282 #ifdef JS_USE_LINK_REGISTER
   6283  masm.freeStack(reserved);
   6284  // RA is already in its place, just move stack.
   6285  masm.addToStackPtr(Imm32(framePushedAtStart + newArgDest));
   6286 #else
   6287  // Push RA to new frame: store RA, restore temp, and move stack.
   6288  int32_t newFrameOffset = newArgDest - sizeof(wasm::Frame);
   6289  masm.storePtr(tempForRA,
   6290                Address(FramePointer,
   6291                        newFrameOffset + wasm::Frame::returnAddressOffset()));
   6292  // Restore tempForRA, but keep RA on top of the stack.
   6293  // There is no non-locking exchange instruction between register and memory.
   6294  // Using tempForCaller as scratch register.
   6295  masm.loadPtr(Address(masm.getStackPointer(), 0), tempForCaller);
   6296  masm.storePtr(tempForRA, Address(masm.getStackPointer(), 0));
   6297  masm.mov(tempForCaller, tempForRA);
   6298  masm.append(wasm::CodeRangeUnwindInfo::RestoreFp, masm.currentOffset());
   6299  masm.addToStackPtr(Imm32(framePushedAtStart + newFrameOffset +
   6300                           wasm::Frame::returnAddressOffset() + reserved +
   6301                           sizeof(void*)));
   6302 #endif
   6303 
   6304  // Point FramePointer to hidden frame.
   6305  masm.computeEffectiveAddress(Address(FramePointer, newFPOffset),
   6306                               FramePointer);
   6307  // Setting framePushed to pre-collapse state, to properly set that in the
   6308  // following code.
   6309  masm.setFramePushed(framePushedAtStart);
   6310 }
   6311 
   6312 void MacroAssembler::wasmCollapseFrameFast(
   6313    const ReturnCallAdjustmentInfo& retCallInfo) {
   6314  CollapseWasmFrameFast(*this, retCallInfo);
   6315 }
   6316 
   6317 void MacroAssembler::wasmCollapseFrameSlow(
   6318    const ReturnCallAdjustmentInfo& retCallInfo, wasm::CallSiteDesc desc) {
   6319  static constexpr Register temp1 = ABINonArgReg1;
   6320  static constexpr Register temp2 = ABINonArgReg3;
   6321 
   6322  // Check if RA has slow marker. If there is no marker, generate a trampoline
   6323  // frame to restore register state when this tail call returns.
   6324 
   6325  Label slow, done;
   6326  loadPtr(Address(FramePointer, wasm::Frame::returnAddressOffset()), temp1);
   6327  wasmCheckSlowCallsite(temp1, &slow, temp1, temp2);
   6328  CollapseWasmFrameFast(*this, retCallInfo);
   6329  jump(&done);
   6330  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6331 
   6332  ReturnCallTrampolineData data = MakeReturnCallTrampoline(*this);
   6333 
   6334  bind(&slow);
   6335  CollapseWasmFrameSlow(*this, retCallInfo, desc, data);
   6336 
   6337  bind(&done);
   6338 }
   6339 
   6340 CodeOffset MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc,
   6341                                          const wasm::CalleeDesc& callee) {
   6342  storePtr(InstanceReg,
   6343           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6344 
   6345  // Load the callee, before the caller's registers are clobbered.
   6346  uint32_t instanceDataOffset = callee.importInstanceDataOffset();
   6347  loadPtr(
   6348      Address(InstanceReg, wasm::Instance::offsetInData(
   6349                               instanceDataOffset +
   6350                               offsetof(wasm::FuncImportInstanceData, code))),
   6351      ABINonArgReg0);
   6352 
   6353 #if !defined(JS_CODEGEN_NONE) && !defined(JS_CODEGEN_WASM32)
   6354  static_assert(ABINonArgReg0 != InstanceReg, "by constraint");
   6355 #endif
   6356 
   6357  // Switch to the callee's realm.
   6358  loadPtr(
   6359      Address(InstanceReg, wasm::Instance::offsetInData(
   6360                               instanceDataOffset +
   6361                               offsetof(wasm::FuncImportInstanceData, realm))),
   6362      ABINonArgReg1);
   6363  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()), ABINonArgReg2);
   6364  storePtr(ABINonArgReg1, Address(ABINonArgReg2, JSContext::offsetOfRealm()));
   6365 
   6366  // Switch to the callee's instance and pinned registers and make the call.
   6367  loadPtr(Address(InstanceReg,
   6368                  wasm::Instance::offsetInData(
   6369                      instanceDataOffset +
   6370                      offsetof(wasm::FuncImportInstanceData, instance))),
   6371          InstanceReg);
   6372 
   6373  storePtr(InstanceReg,
   6374           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6375  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6376 
   6377  return wasmMarkedSlowCall(desc, ABINonArgReg0);
   6378 }
   6379 
   6380 CodeOffset MacroAssembler::wasmReturnCallImport(
   6381    const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
   6382    const ReturnCallAdjustmentInfo& retCallInfo) {
   6383  storePtr(InstanceReg,
   6384           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6385 
   6386  // Load the callee, before the caller's registers are clobbered.
   6387  uint32_t instanceDataOffset = callee.importInstanceDataOffset();
   6388  loadPtr(
   6389      Address(InstanceReg, wasm::Instance::offsetInData(
   6390                               instanceDataOffset +
   6391                               offsetof(wasm::FuncImportInstanceData, code))),
   6392      ABINonArgReg0);
   6393 
   6394 #if !defined(JS_CODEGEN_NONE) && !defined(JS_CODEGEN_WASM32)
   6395  static_assert(ABINonArgReg0 != InstanceReg, "by constraint");
   6396 #endif
   6397 
   6398  // Switch to the callee's realm.
   6399  loadPtr(
   6400      Address(InstanceReg, wasm::Instance::offsetInData(
   6401                               instanceDataOffset +
   6402                               offsetof(wasm::FuncImportInstanceData, realm))),
   6403      ABINonArgReg1);
   6404  loadPtr(Address(InstanceReg, wasm::Instance::offsetOfCx()), ABINonArgReg2);
   6405  storePtr(ABINonArgReg1, Address(ABINonArgReg2, JSContext::offsetOfRealm()));
   6406 
   6407  // Switch to the callee's instance and pinned registers and make the call.
   6408  loadPtr(Address(InstanceReg,
   6409                  wasm::Instance::offsetInData(
   6410                      instanceDataOffset +
   6411                      offsetof(wasm::FuncImportInstanceData, instance))),
   6412          InstanceReg);
   6413 
   6414  storePtr(InstanceReg,
   6415           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6416  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6417 
   6418  wasm::CallSiteDesc stubDesc(desc.lineOrBytecode(),
   6419                              wasm::CallSiteKind::ReturnStub);
   6420  wasmCollapseFrameSlow(retCallInfo, stubDesc);
   6421  jump(ABINonArgReg0);
   6422  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6423  return CodeOffset(currentOffset());
   6424 }
   6425 
   6426 CodeOffset MacroAssembler::wasmReturnCall(
   6427    const wasm::CallSiteDesc& desc, uint32_t funcDefIndex,
   6428    const ReturnCallAdjustmentInfo& retCallInfo) {
   6429  wasmCollapseFrameFast(retCallInfo);
   6430  CodeOffset offset = farJumpWithPatch();
   6431  append(desc, offset, funcDefIndex);
   6432  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6433  return offset;
   6434 }
   6435 
   6436 CodeOffset MacroAssembler::wasmCallBuiltinInstanceMethod(
   6437    const wasm::CallSiteDesc& desc, const ABIArg& instanceArg,
   6438    wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
   6439    wasm::Trap failureTrap) {
   6440  MOZ_ASSERT(instanceArg != ABIArg());
   6441  MOZ_ASSERT_IF(!wasm::NeedsBuiltinThunk(builtin),
   6442                failureMode == wasm::FailureMode::Infallible ||
   6443                    failureTrap != wasm::Trap::ThrowReported);
   6444 
   6445  // Instance methods take the instance as the first argument. This is in
   6446  // addition to the builtin thunk (if any) requiring the InstanceReg to be
   6447  // set.
   6448  if (instanceArg.kind() == ABIArg::GPR) {
   6449    movePtr(InstanceReg, instanceArg.gpr());
   6450  } else if (instanceArg.kind() == ABIArg::Stack) {
   6451    storePtr(InstanceReg,
   6452             Address(getStackPointer(), instanceArg.offsetFromArgBase()));
   6453  } else {
   6454    MOZ_CRASH("Unknown abi passing style for pointer");
   6455  }
   6456 
   6457 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   6458  // The builtin thunk exits the JIT activation, if we don't have one we must
   6459  // use AutoUnsafeCallWithABI inside the builtin and check that here.
   6460  bool checkUnsafeCallWithABI = !wasm::NeedsBuiltinThunk(builtin);
   6461  if (checkUnsafeCallWithABI) {
   6462    wasmCheckUnsafeCallWithABIPre();
   6463  }
   6464 #endif
   6465 
   6466  CodeOffset ret = call(desc, builtin);
   6467 
   6468 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   6469  if (checkUnsafeCallWithABI) {
   6470    wasmCheckUnsafeCallWithABIPost();
   6471  }
   6472 #endif
   6473 
   6474  wasmTrapOnFailedInstanceCall(ReturnReg, failureMode, failureTrap,
   6475                               desc.toTrapSiteDesc());
   6476  return ret;
   6477 }
   6478 
   6479 void MacroAssembler::wasmTrapOnFailedInstanceCall(
   6480    Register resultRegister, wasm::FailureMode failureMode,
   6481    wasm::Trap failureTrap, const wasm::TrapSiteDesc& trapSiteDesc) {
   6482  Label noTrap;
   6483  switch (failureMode) {
   6484    case wasm::FailureMode::Infallible:
   6485      MOZ_ASSERT(failureTrap == wasm::Trap::Limit);
   6486      return;
   6487    case wasm::FailureMode::FailOnNegI32:
   6488      branchTest32(Assembler::NotSigned, resultRegister, resultRegister,
   6489                   &noTrap);
   6490      break;
   6491    case wasm::FailureMode::FailOnMaxI32:
   6492      branchPtr(Assembler::NotEqual, resultRegister,
   6493                ImmWord(uintptr_t(INT32_MAX)), &noTrap);
   6494      break;
   6495    case wasm::FailureMode::FailOnNullPtr:
   6496      branchTestPtr(Assembler::NonZero, resultRegister, resultRegister,
   6497                    &noTrap);
   6498      break;
   6499    case wasm::FailureMode::FailOnInvalidRef:
   6500      branchPtr(Assembler::NotEqual, resultRegister,
   6501                ImmWord(uintptr_t(wasm::AnyRef::invalid().forCompiledCode())),
   6502                &noTrap);
   6503      break;
   6504  }
   6505  wasmTrap(failureTrap, trapSiteDesc);
   6506  bind(&noTrap);
   6507 }
   6508 
   6509 CodeOffset MacroAssembler::asmCallIndirect(const wasm::CallSiteDesc& desc,
   6510                                           const wasm::CalleeDesc& callee) {
   6511  MOZ_ASSERT(callee.which() == wasm::CalleeDesc::AsmJSTable);
   6512 
   6513  const Register scratch = WasmTableCallScratchReg0;
   6514  const Register index = WasmTableCallIndexReg;
   6515 
   6516  // Optimization opportunity: when offsetof(FunctionTableElem, code) == 0, as
   6517  // it is at present, we can probably generate better code here by folding
   6518  // the address computation into the load.
   6519 
   6520  static_assert(sizeof(wasm::FunctionTableElem) == 8 ||
   6521                    sizeof(wasm::FunctionTableElem) == 16,
   6522                "elements of function tables are two words");
   6523 
   6524  // asm.js tables require no signature check, and have had their index
   6525  // masked into range and thus need no bounds check.
   6526  loadPtr(
   6527      Address(InstanceReg, wasm::Instance::offsetInData(
   6528                               callee.tableFunctionBaseInstanceDataOffset())),
   6529      scratch);
   6530  if (sizeof(wasm::FunctionTableElem) == 8) {
   6531    computeEffectiveAddress(BaseIndex(scratch, index, TimesEight), scratch);
   6532  } else {
   6533    lshift32(Imm32(4), index);
   6534    addPtr(index, scratch);
   6535  }
   6536  loadPtr(Address(scratch, offsetof(wasm::FunctionTableElem, code)), scratch);
   6537  storePtr(InstanceReg,
   6538           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6539  storePtr(InstanceReg,
   6540           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6541  return call(desc, scratch);
   6542 }
   6543 
   6544 // In principle, call_indirect requires an expensive context switch to the
   6545 // callee's instance and realm before the call and an almost equally expensive
   6546 // switch back to the caller's ditto after.  However, if the caller's instance
   6547 // is the same as the callee's instance then no context switch is required, and
   6548 // it only takes a compare-and-branch at run-time to test this - all values are
   6549 // in registers already.  We therefore generate two call paths, one for the fast
   6550 // call without the context switch (which additionally avoids a null check) and
   6551 // one for the slow call with the context switch.
   6552 
   6553 void MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc,
   6554                                      const wasm::CalleeDesc& callee,
   6555                                      Label* nullCheckFailedLabel,
   6556                                      CodeOffset* fastCallOffset,
   6557                                      CodeOffset* slowCallOffset) {
   6558  static_assert(sizeof(wasm::FunctionTableElem) == 2 * sizeof(void*),
   6559                "Exactly two pointers or index scaling won't work correctly");
   6560  MOZ_ASSERT(callee.which() == wasm::CalleeDesc::WasmTable);
   6561 
   6562  const int shift = sizeof(wasm::FunctionTableElem) == 8 ? 3 : 4;
   6563  const Register calleeScratch = WasmTableCallScratchReg0;
   6564  const Register index = WasmTableCallIndexReg;
   6565 
   6566  // Write the functype-id into the ABI functype-id register.
   6567 
   6568  const wasm::CallIndirectId callIndirectId = callee.wasmTableSigId();
   6569  switch (callIndirectId.kind()) {
   6570    case wasm::CallIndirectIdKind::Global:
   6571      loadPtr(Address(InstanceReg, wasm::Instance::offsetInData(
   6572                                       callIndirectId.instanceDataOffset() +
   6573                                       offsetof(wasm::TypeDefInstanceData,
   6574                                                superTypeVector))),
   6575              WasmTableCallSigReg);
   6576      break;
   6577    case wasm::CallIndirectIdKind::Immediate:
   6578      move32(Imm32(callIndirectId.immediate()), WasmTableCallSigReg);
   6579      break;
   6580    case wasm::CallIndirectIdKind::AsmJS:
   6581    case wasm::CallIndirectIdKind::None:
   6582      break;
   6583  }
   6584 
   6585  // Load the base pointer of the table and compute the address of the callee in
   6586  // the table.
   6587 
   6588  loadPtr(
   6589      Address(InstanceReg, wasm::Instance::offsetInData(
   6590                               callee.tableFunctionBaseInstanceDataOffset())),
   6591      calleeScratch);
   6592  shiftIndex32AndAdd(index, shift, calleeScratch);
   6593 
   6594  // Load the callee instance and decide whether to take the fast path or the
   6595  // slow path.
   6596 
   6597  Label fastCall;
   6598  Label done;
   6599  const Register newInstanceTemp = WasmTableCallScratchReg1;
   6600  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, instance)),
   6601          newInstanceTemp);
   6602  branchPtr(Assembler::Equal, InstanceReg, newInstanceTemp, &fastCall);
   6603 
   6604  // Slow path: Save context, check for null, setup new context, call, restore
   6605  // context.
   6606  //
   6607  // TODO: The slow path could usefully be out-of-line and the test above would
   6608  // just fall through to the fast path.  This keeps the fast-path code dense,
   6609  // and has correct static prediction for the branch (forward conditional
   6610  // branches predicted not taken, normally).
   6611 
   6612  storePtr(InstanceReg,
   6613           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6614  movePtr(newInstanceTemp, InstanceReg);
   6615  storePtr(InstanceReg,
   6616           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6617 
   6618 #ifdef WASM_HAS_HEAPREG
   6619  // Use the null pointer exception resulting from loading HeapReg from a null
   6620  // instance to handle a call to a null slot.
   6621  MOZ_ASSERT(nullCheckFailedLabel == nullptr);
   6622  loadWasmPinnedRegsFromInstance(mozilla::Some(desc.toTrapSiteDesc()));
   6623 #else
   6624  MOZ_ASSERT(nullCheckFailedLabel != nullptr);
   6625  branchTestPtr(Assembler::Zero, InstanceReg, InstanceReg,
   6626                nullCheckFailedLabel);
   6627 
   6628  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6629 #endif
   6630  switchToWasmInstanceRealm(index, WasmTableCallScratchReg1);
   6631 
   6632  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, code)),
   6633          calleeScratch);
   6634 
   6635  *slowCallOffset = wasmMarkedSlowCall(desc, calleeScratch);
   6636 
   6637  // Restore registers and realm and join up with the fast path.
   6638 
   6639  loadPtr(Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
   6640          InstanceReg);
   6641  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6642  switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
   6643  jump(&done);
   6644 
   6645  // Fast path: just load the code pointer and go.  The instance and heap
   6646  // register are the same as in the caller, and nothing will be null.
   6647  //
   6648  // (In particular, the code pointer will not be null: if it were, the instance
   6649  // would have been null, and then it would not have been equivalent to our
   6650  // current instance.  So no null check is needed on the fast path.)
   6651 
   6652  bind(&fastCall);
   6653 
   6654  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, code)),
   6655          calleeScratch);
   6656 
   6657  // We use a different type of call site for the fast call since the instance
   6658  // slots in the frame do not have valid values.
   6659 
   6660  wasm::CallSiteDesc newDesc(desc.lineOrBytecode(),
   6661                             wasm::CallSiteKind::IndirectFast);
   6662  *fastCallOffset = call(newDesc, calleeScratch);
   6663 
   6664  bind(&done);
   6665 }
   6666 
   6667 void MacroAssembler::wasmReturnCallIndirect(
   6668    const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
   6669    Label* nullCheckFailedLabel, const ReturnCallAdjustmentInfo& retCallInfo) {
   6670  static_assert(sizeof(wasm::FunctionTableElem) == 2 * sizeof(void*),
   6671                "Exactly two pointers or index scaling won't work correctly");
   6672  MOZ_ASSERT(callee.which() == wasm::CalleeDesc::WasmTable);
   6673 
   6674  const int shift = sizeof(wasm::FunctionTableElem) == 8 ? 3 : 4;
   6675  const Register calleeScratch = WasmTableCallScratchReg0;
   6676  const Register index = WasmTableCallIndexReg;
   6677 
   6678  // Write the functype-id into the ABI functype-id register.
   6679 
   6680  const wasm::CallIndirectId callIndirectId = callee.wasmTableSigId();
   6681  switch (callIndirectId.kind()) {
   6682    case wasm::CallIndirectIdKind::Global:
   6683      loadPtr(Address(InstanceReg, wasm::Instance::offsetInData(
   6684                                       callIndirectId.instanceDataOffset() +
   6685                                       offsetof(wasm::TypeDefInstanceData,
   6686                                                superTypeVector))),
   6687              WasmTableCallSigReg);
   6688      break;
   6689    case wasm::CallIndirectIdKind::Immediate:
   6690      move32(Imm32(callIndirectId.immediate()), WasmTableCallSigReg);
   6691      break;
   6692    case wasm::CallIndirectIdKind::AsmJS:
   6693    case wasm::CallIndirectIdKind::None:
   6694      break;
   6695  }
   6696 
   6697  // Load the base pointer of the table and compute the address of the callee in
   6698  // the table.
   6699 
   6700  loadPtr(
   6701      Address(InstanceReg, wasm::Instance::offsetInData(
   6702                               callee.tableFunctionBaseInstanceDataOffset())),
   6703      calleeScratch);
   6704  shiftIndex32AndAdd(index, shift, calleeScratch);
   6705 
   6706  // Load the callee instance and decide whether to take the fast path or the
   6707  // slow path.
   6708 
   6709  Label fastCall;
   6710  Label done;
   6711  const Register newInstanceTemp = WasmTableCallScratchReg1;
   6712  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, instance)),
   6713          newInstanceTemp);
   6714  branchPtr(Assembler::Equal, InstanceReg, newInstanceTemp, &fastCall);
   6715 
   6716  // Slow path: Save context, check for null, setup new context.
   6717 
   6718  storePtr(InstanceReg,
   6719           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6720  movePtr(newInstanceTemp, InstanceReg);
   6721 
   6722 #ifdef WASM_HAS_HEAPREG
   6723  // Use the null pointer exception resulting from loading HeapReg from a null
   6724  // instance to handle a call to a null slot.
   6725  MOZ_ASSERT(nullCheckFailedLabel == nullptr);
   6726  loadWasmPinnedRegsFromInstance(mozilla::Some(desc.toTrapSiteDesc()));
   6727 #else
   6728  MOZ_ASSERT(nullCheckFailedLabel != nullptr);
   6729  branchTestPtr(Assembler::Zero, InstanceReg, InstanceReg,
   6730                nullCheckFailedLabel);
   6731 
   6732  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6733 #endif
   6734  switchToWasmInstanceRealm(index, WasmTableCallScratchReg1);
   6735 
   6736  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, code)),
   6737          calleeScratch);
   6738 
   6739  wasm::CallSiteDesc stubDesc(desc.lineOrBytecode(),
   6740                              wasm::CallSiteKind::ReturnStub);
   6741  wasmCollapseFrameSlow(retCallInfo, stubDesc);
   6742  jump(calleeScratch);
   6743  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6744 
   6745  // Fast path: just load the code pointer and go.
   6746 
   6747  bind(&fastCall);
   6748 
   6749  loadPtr(Address(calleeScratch, offsetof(wasm::FunctionTableElem, code)),
   6750          calleeScratch);
   6751 
   6752  wasmCollapseFrameFast(retCallInfo);
   6753  jump(calleeScratch);
   6754  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6755 }
   6756 
   6757 void MacroAssembler::wasmCallRef(const wasm::CallSiteDesc& desc,
   6758                                 const wasm::CalleeDesc& callee,
   6759                                 CodeOffset* fastCallOffset,
   6760                                 CodeOffset* slowCallOffset) {
   6761  MOZ_ASSERT(callee.which() == wasm::CalleeDesc::FuncRef);
   6762  const Register calleeScratch = WasmCallRefCallScratchReg0;
   6763  const Register calleeFnObj = WasmCallRefReg;
   6764 
   6765  // Load from the function's WASM_INSTANCE_SLOT extended slot, and decide
   6766  // whether to take the fast path or the slow path. Register this load
   6767  // instruction to be source of a trap -- null pointer check.
   6768 
   6769  Label fastCall;
   6770  Label done;
   6771  const Register newInstanceTemp = WasmCallRefCallScratchReg1;
   6772  size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
   6773      FunctionExtended::WASM_INSTANCE_SLOT);
   6774  static_assert(FunctionExtended::WASM_INSTANCE_SLOT < wasm::NullPtrGuardSize);
   6775  FaultingCodeOffset fco =
   6776      loadPtr(Address(calleeFnObj, instanceSlotOffset), newInstanceTemp);
   6777  append(wasm::Trap::NullPointerDereference, wasm::TrapMachineInsnForLoadWord(),
   6778         fco.get(), desc.toTrapSiteDesc());
   6779  branchPtr(Assembler::Equal, InstanceReg, newInstanceTemp, &fastCall);
   6780 
   6781  storePtr(InstanceReg,
   6782           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6783  movePtr(newInstanceTemp, InstanceReg);
   6784  storePtr(InstanceReg,
   6785           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6786 
   6787  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6788  switchToWasmInstanceRealm(WasmCallRefCallScratchReg0,
   6789                            WasmCallRefCallScratchReg1);
   6790 
   6791  // Get funcUncheckedCallEntry() from the function's
   6792  // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
   6793  size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
   6794      FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
   6795  loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
   6796 
   6797  *slowCallOffset = wasmMarkedSlowCall(desc, calleeScratch);
   6798 
   6799  // Restore registers and realm and back to this caller's.
   6800  loadPtr(Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
   6801          InstanceReg);
   6802  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6803  switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
   6804  jump(&done);
   6805 
   6806  // Fast path: just load WASM_FUNC_UNCHECKED_ENTRY_SLOT value and go.
   6807  // The instance and pinned registers are the same as in the caller.
   6808 
   6809  bind(&fastCall);
   6810 
   6811  loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
   6812 
   6813  // We use a different type of call site for the fast call since the instance
   6814  // slots in the frame do not have valid values.
   6815 
   6816  wasm::CallSiteDesc newDesc(desc.lineOrBytecode(),
   6817                             wasm::CallSiteKind::FuncRefFast);
   6818  *fastCallOffset = call(newDesc, calleeScratch);
   6819 
   6820  bind(&done);
   6821 }
   6822 
   6823 void MacroAssembler::wasmReturnCallRef(
   6824    const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
   6825    const ReturnCallAdjustmentInfo& retCallInfo) {
   6826  MOZ_ASSERT(callee.which() == wasm::CalleeDesc::FuncRef);
   6827  const Register calleeScratch = WasmCallRefCallScratchReg0;
   6828  const Register calleeFnObj = WasmCallRefReg;
   6829 
   6830  // Load from the function's WASM_INSTANCE_SLOT extended slot, and decide
   6831  // whether to take the fast path or the slow path. Register this load
   6832  // instruction to be source of a trap -- null pointer check.
   6833 
   6834  Label fastCall;
   6835  Label done;
   6836  const Register newInstanceTemp = WasmCallRefCallScratchReg1;
   6837  size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
   6838      FunctionExtended::WASM_INSTANCE_SLOT);
   6839  static_assert(FunctionExtended::WASM_INSTANCE_SLOT < wasm::NullPtrGuardSize);
   6840  FaultingCodeOffset fco =
   6841      loadPtr(Address(calleeFnObj, instanceSlotOffset), newInstanceTemp);
   6842  append(wasm::Trap::NullPointerDereference, wasm::TrapMachineInsnForLoadWord(),
   6843         fco.get(), desc.toTrapSiteDesc());
   6844  branchPtr(Assembler::Equal, InstanceReg, newInstanceTemp, &fastCall);
   6845 
   6846  storePtr(InstanceReg,
   6847           Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
   6848  movePtr(newInstanceTemp, InstanceReg);
   6849  storePtr(InstanceReg,
   6850           Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
   6851 
   6852  loadWasmPinnedRegsFromInstance(mozilla::Nothing());
   6853  switchToWasmInstanceRealm(WasmCallRefCallScratchReg0,
   6854                            WasmCallRefCallScratchReg1);
   6855 
   6856  // Get funcUncheckedCallEntry() from the function's
   6857  // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
   6858  size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
   6859      FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
   6860  loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
   6861 
   6862  wasm::CallSiteDesc stubDesc(desc.lineOrBytecode(),
   6863                              wasm::CallSiteKind::ReturnStub);
   6864  wasmCollapseFrameSlow(retCallInfo, stubDesc);
   6865  jump(calleeScratch);
   6866  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6867 
   6868  // Fast path: just load WASM_FUNC_UNCHECKED_ENTRY_SLOT value and go.
   6869  // The instance and pinned registers are the same as in the caller.
   6870 
   6871  bind(&fastCall);
   6872 
   6873  loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
   6874 
   6875  wasmCollapseFrameFast(retCallInfo);
   6876  jump(calleeScratch);
   6877  append(wasm::CodeRangeUnwindInfo::Normal, currentOffset());
   6878 }
   6879 
   6880 void MacroAssembler::wasmBoundsCheckRange32(
   6881    Register index, Register length, Register limit, Register tmp,
   6882    const wasm::TrapSiteDesc& trapSiteDesc) {
   6883  Label ok;
   6884  Label fail;
   6885 
   6886  mov(index, tmp);
   6887  branchAdd32(Assembler::CarrySet, length, tmp, &fail);
   6888  branch32(Assembler::Above, tmp, limit, &fail);
   6889  jump(&ok);
   6890 
   6891  bind(&fail);
   6892  wasmTrap(wasm::Trap::OutOfBounds, trapSiteDesc);
   6893 
   6894  bind(&ok);
   6895 }
   6896 
   6897 void MacroAssembler::wasmClampTable64Address(Register64 address, Register out) {
   6898  Label oob;
   6899  Label ret;
   6900  branch64(Assembler::Above, address, Imm64(UINT32_MAX), &oob);
   6901  move64To32(address, out);
   6902  jump(&ret);
   6903  bind(&oob);
   6904  static_assert(wasm::MaxTableElemsRuntime < UINT32_MAX);
   6905  move32(Imm32(UINT32_MAX), out);
   6906  bind(&ret);
   6907 };
   6908 
   6909 BranchWasmRefIsSubtypeRegisters MacroAssembler::regsForBranchWasmRefIsSubtype(
   6910    wasm::RefType type) {
   6911  MOZ_ASSERT(type.isValid());
   6912  switch (type.hierarchy()) {
   6913    case wasm::RefTypeHierarchy::Any:
   6914      return BranchWasmRefIsSubtypeRegisters{
   6915          .needSuperSTV = type.isTypeRef(),
   6916          .needScratch1 = !type.isNone() && !type.isAny(),
   6917          .needScratch2 = type.isTypeRef() && !type.typeDef()->isFinal() &&
   6918                          type.typeDef()->subTypingDepth() >=
   6919                              wasm::MinSuperTypeVectorLength,
   6920      };
   6921    case wasm::RefTypeHierarchy::Func:
   6922      return BranchWasmRefIsSubtypeRegisters{
   6923          .needSuperSTV = type.isTypeRef(),
   6924          .needScratch1 = type.isTypeRef(),
   6925          .needScratch2 = type.isTypeRef() && !type.typeDef()->isFinal() &&
   6926                          type.typeDef()->subTypingDepth() >=
   6927                              wasm::MinSuperTypeVectorLength,
   6928      };
   6929    case wasm::RefTypeHierarchy::Extern:
   6930    case wasm::RefTypeHierarchy::Exn:
   6931      return BranchWasmRefIsSubtypeRegisters{
   6932          .needSuperSTV = false,
   6933          .needScratch1 = false,
   6934          .needScratch2 = false,
   6935      };
   6936    default:
   6937      MOZ_CRASH("unknown type hierarchy for cast");
   6938  }
   6939 }
   6940 
   6941 FaultingCodeOffset MacroAssembler::branchWasmRefIsSubtype(
   6942    Register ref, wasm::MaybeRefType sourceType, wasm::RefType destType,
   6943    Label* label, bool onSuccess, bool signalNullChecks, Register superSTV,
   6944    Register scratch1, Register scratch2) {
   6945  FaultingCodeOffset result = FaultingCodeOffset();
   6946  switch (destType.hierarchy()) {
   6947    case wasm::RefTypeHierarchy::Any: {
   6948      result = branchWasmRefIsSubtypeAny(
   6949          ref, sourceType.valueOr(wasm::RefType::any()), destType, label,
   6950          onSuccess, signalNullChecks, superSTV, scratch1, scratch2);
   6951    } break;
   6952    case wasm::RefTypeHierarchy::Func: {
   6953      branchWasmRefIsSubtypeFunc(ref, sourceType.valueOr(wasm::RefType::func()),
   6954                                 destType, label, onSuccess, superSTV, scratch1,
   6955                                 scratch2);
   6956    } break;
   6957    case wasm::RefTypeHierarchy::Extern: {
   6958      branchWasmRefIsSubtypeExtern(ref,
   6959                                   sourceType.valueOr(wasm::RefType::extern_()),
   6960                                   destType, label, onSuccess);
   6961    } break;
   6962    case wasm::RefTypeHierarchy::Exn: {
   6963      branchWasmRefIsSubtypeExn(ref, sourceType.valueOr(wasm::RefType::exn()),
   6964                                destType, label, onSuccess);
   6965    } break;
   6966    default:
   6967      MOZ_CRASH("unknown type hierarchy for wasm cast");
   6968  }
   6969  MOZ_ASSERT_IF(!signalNullChecks, !result.isValid());
   6970  return result;
   6971 }
   6972 
   6973 FaultingCodeOffset MacroAssembler::branchWasmRefIsSubtypeAny(
   6974    Register ref, wasm::RefType sourceType, wasm::RefType destType,
   6975    Label* label, bool onSuccess, bool signalNullChecks, Register superSTV,
   6976    Register scratch1, Register scratch2) {
   6977  MOZ_ASSERT(sourceType.isValid());
   6978  MOZ_ASSERT(destType.isValid());
   6979  MOZ_ASSERT(sourceType.isAnyHierarchy());
   6980  MOZ_ASSERT(destType.isAnyHierarchy());
   6981 
   6982  mozilla::DebugOnly<BranchWasmRefIsSubtypeRegisters> needs =
   6983      regsForBranchWasmRefIsSubtype(destType);
   6984  MOZ_ASSERT_IF(needs.value.needSuperSTV, superSTV != Register::Invalid());
   6985  MOZ_ASSERT_IF(needs.value.needScratch1, scratch1 != Register::Invalid());
   6986  MOZ_ASSERT_IF(needs.value.needScratch2, scratch2 != Register::Invalid());
   6987 
   6988  Label fallthrough;
   6989  Label* successLabel = onSuccess ? label : &fallthrough;
   6990  Label* failLabel = onSuccess ? &fallthrough : label;
   6991  Label* nullLabel = destType.isNullable() ? successLabel : failLabel;
   6992 
   6993  auto finishSuccess = [&]() {
   6994    if (successLabel != &fallthrough) {
   6995      jump(successLabel);
   6996    }
   6997    bind(&fallthrough);
   6998  };
   6999  auto finishFail = [&]() {
   7000    if (failLabel != &fallthrough) {
   7001      jump(failLabel);
   7002    }
   7003    bind(&fallthrough);
   7004  };
   7005 
   7006  // We can omit the null check, and catch nulls in signal handling, if the dest
   7007  // type is non-nullable and the cast process will attempt to load from the
   7008  // (null) ref. This logic must be kept in sync with the flow of checks below;
   7009  // this will be checked by fcoLogicCheck.
   7010  bool willLoadShape =
   7011      // Dest type is a GC object
   7012      (wasm::RefType::isSubTypeOf(destType, wasm::RefType::eq()) &&
   7013       !destType.isI31() && !destType.isNone()) &&
   7014      // Source type is not already known to be a GC object
   7015      !(wasm::RefType::isSubTypeOf(sourceType, wasm::RefType::struct_()) ||
   7016        wasm::RefType::isSubTypeOf(sourceType, wasm::RefType::array()));
   7017  bool willLoadSTV =
   7018      // Dest type is struct or array or concrete type
   7019      (wasm::RefType::isSubTypeOf(destType, wasm::RefType::struct_()) ||
   7020       wasm::RefType::isSubTypeOf(destType, wasm::RefType::array())) &&
   7021      !destType.isNone();
   7022  bool canOmitNullCheck = signalNullChecks && !destType.isNullable() &&
   7023                          (willLoadShape || willLoadSTV);
   7024 
   7025  FaultingCodeOffset fco = FaultingCodeOffset();
   7026  auto trackFCO = [&](FaultingCodeOffset newFco) {
   7027    if (signalNullChecks && !destType.isNullable() && !fco.isValid()) {
   7028      fco = newFco;
   7029    }
   7030  };
   7031  auto fcoLogicCheck = mozilla::DebugOnly(mozilla::MakeScopeExit([&]() {
   7032    // When we think we can omit the null check, we should get a valid FCO, and
   7033    // vice versa. These are asserted to match because:
   7034    //  - If we think we cannot omit the null check, but get a valid FCO, then
   7035    //    we wasted an optimization opportunity.
   7036    //  - If we think we *can* omit the null check, but do not get a valid FCO,
   7037    //    then we will segfault.
   7038    // We could ignore the former check, but better to be precise and ensure
   7039    // that we are getting the optimizations we expect.
   7040    MOZ_ASSERT_IF(signalNullChecks && fco.isValid(), canOmitNullCheck);
   7041    MOZ_ASSERT_IF(signalNullChecks && !fco.isValid(), !canOmitNullCheck);
   7042 
   7043    // We should never get a valid FCO if the caller doesn't expect signal
   7044    // handling. This simplifies life for the caller.
   7045    MOZ_ASSERT_IF(!signalNullChecks, !fco.isValid());
   7046  }));
   7047 
   7048  // -----------------------------------
   7049  // Actual test/cast logic begins here.
   7050 
   7051  // Check for null.
   7052  if (sourceType.isNullable() && !canOmitNullCheck) {
   7053    branchWasmAnyRefIsNull(true, ref, nullLabel);
   7054  }
   7055 
   7056  // The only value that can inhabit 'none' is null. So, early out if we got
   7057  // not-null.
   7058  if (destType.isNone()) {
   7059    finishFail();
   7060    MOZ_ASSERT(!willLoadShape && !willLoadSTV);
   7061    return fco;
   7062  }
   7063 
   7064  if (destType.isAny()) {
   7065    // No further checks for 'any'
   7066    finishSuccess();
   7067    MOZ_ASSERT(!willLoadShape && !willLoadSTV);
   7068    return fco;
   7069  }
   7070 
   7071  // 'type' is now 'eq' or lower, which currently will either be a gc object or
   7072  // an i31.
   7073 
   7074  // Check first for i31 values, and get them out of the way. i31 values are
   7075  // valid when casting to i31 or eq, and invalid otherwise.
   7076  if (destType.isI31() || destType.isEq()) {
   7077    branchWasmAnyRefIsI31(true, ref, successLabel);
   7078 
   7079    if (destType.isI31()) {
   7080      // No further checks for 'i31'
   7081      finishFail();
   7082      MOZ_ASSERT(!willLoadShape && !willLoadSTV);
   7083      return fco;
   7084    }
   7085  }
   7086 
   7087  // Then check for any kind of gc object.
   7088  MOZ_ASSERT(scratch1 != Register::Invalid());
   7089  if (!wasm::RefType::isSubTypeOf(sourceType, wasm::RefType::struct_()) &&
   7090      !wasm::RefType::isSubTypeOf(sourceType, wasm::RefType::array())) {
   7091    branchWasmAnyRefIsObjectOrNull(false, ref, failLabel);
   7092 
   7093    MOZ_ASSERT(willLoadShape);
   7094    trackFCO(branchObjectIsWasmGcObject(false, ref, scratch1, failLabel));
   7095  } else {
   7096    MOZ_ASSERT(!willLoadShape);
   7097  }
   7098 
   7099  if (destType.isEq()) {
   7100    // No further checks for 'eq'
   7101    finishSuccess();
   7102    MOZ_ASSERT(!willLoadSTV);
   7103    return fco;
   7104  }
   7105 
   7106  // 'type' is now 'struct', 'array', or a concrete type. (Bottom types and i31
   7107  // were handled above.)
   7108  //
   7109  // Casting to a concrete type only requires a simple check on the
   7110  // object's super type vector. Casting to an abstract type (struct, array)
   7111  // requires loading the object's superTypeVector->typeDef->kind, and checking
   7112  // that it is correct.
   7113 
   7114  MOZ_ASSERT(willLoadSTV);
   7115  trackFCO(
   7116      loadPtr(Address(ref, int32_t(WasmGcObject::offsetOfSuperTypeVector())),
   7117              scratch1));
   7118  if (destType.isTypeRef()) {
   7119    // Concrete type, do superTypeVector check.
   7120    branchWasmSTVIsSubtype(scratch1, superSTV, scratch2, destType.typeDef(),
   7121                           label, onSuccess);
   7122    bind(&fallthrough);
   7123    return fco;
   7124  }
   7125 
   7126  // Abstract type, do kind check
   7127  loadPtr(
   7128      Address(scratch1, int32_t(wasm::SuperTypeVector::offsetOfSelfTypeDef())),
   7129      scratch1);
   7130  load8ZeroExtend(Address(scratch1, int32_t(wasm::TypeDef::offsetOfKind())),
   7131                  scratch1);
   7132  branch32(onSuccess ? Assembler::Equal : Assembler::NotEqual, scratch1,
   7133           Imm32(int32_t(destType.typeDefKind())), label);
   7134  bind(&fallthrough);
   7135  return fco;
   7136 }
   7137 
   7138 void MacroAssembler::branchWasmRefIsSubtypeFunc(
   7139    Register ref, wasm::RefType sourceType, wasm::RefType destType,
   7140    Label* label, bool onSuccess, Register superSTV, Register scratch1,
   7141    Register scratch2) {
   7142  MOZ_ASSERT(sourceType.isValid());
   7143  MOZ_ASSERT(destType.isValid());
   7144  MOZ_ASSERT(sourceType.isFuncHierarchy());
   7145  MOZ_ASSERT(destType.isFuncHierarchy());
   7146 
   7147  mozilla::DebugOnly<BranchWasmRefIsSubtypeRegisters> needs =
   7148      regsForBranchWasmRefIsSubtype(destType);
   7149  MOZ_ASSERT_IF(needs.value.needSuperSTV, superSTV != Register::Invalid());
   7150  MOZ_ASSERT_IF(needs.value.needScratch1, scratch1 != Register::Invalid());
   7151  MOZ_ASSERT_IF(needs.value.needScratch2, scratch2 != Register::Invalid());
   7152 
   7153  Label fallthrough;
   7154  Label* successLabel = onSuccess ? label : &fallthrough;
   7155  Label* failLabel = onSuccess ? &fallthrough : label;
   7156  Label* nullLabel = destType.isNullable() ? successLabel : failLabel;
   7157 
   7158  auto finishSuccess = [&]() {
   7159    if (successLabel != &fallthrough) {
   7160      jump(successLabel);
   7161    }
   7162    bind(&fallthrough);
   7163  };
   7164  auto finishFail = [&]() {
   7165    if (failLabel != &fallthrough) {
   7166      jump(failLabel);
   7167    }
   7168    bind(&fallthrough);
   7169  };
   7170 
   7171  // Check for null.
   7172  if (sourceType.isNullable()) {
   7173    branchTestPtr(Assembler::Zero, ref, ref, nullLabel);
   7174  }
   7175 
   7176  // The only value that can inhabit 'nofunc' is null. So, early out if we got
   7177  // not-null.
   7178  if (destType.isNoFunc()) {
   7179    finishFail();
   7180    return;
   7181  }
   7182 
   7183  if (destType.isFunc()) {
   7184    // No further checks for 'func' (any func)
   7185    finishSuccess();
   7186    return;
   7187  }
   7188 
   7189  // In the func hierarchy, a supertype vector check is now sufficient for all
   7190  // remaining cases.
   7191  loadPrivate(Address(ref, int32_t(FunctionExtended::offsetOfWasmSTV())),
   7192              scratch1);
   7193  branchWasmSTVIsSubtype(scratch1, superSTV, scratch2, destType.typeDef(),
   7194                         label, onSuccess);
   7195  bind(&fallthrough);
   7196 }
   7197 
   7198 void MacroAssembler::branchWasmRefIsSubtypeExtern(Register ref,
   7199                                                  wasm::RefType sourceType,
   7200                                                  wasm::RefType destType,
   7201                                                  Label* label,
   7202                                                  bool onSuccess) {
   7203  MOZ_ASSERT(sourceType.isValid());
   7204  MOZ_ASSERT(destType.isValid());
   7205  MOZ_ASSERT(sourceType.isExternHierarchy());
   7206  MOZ_ASSERT(destType.isExternHierarchy());
   7207 
   7208  Label fallthrough;
   7209  Label* successLabel = onSuccess ? label : &fallthrough;
   7210  Label* failLabel = onSuccess ? &fallthrough : label;
   7211  Label* nullLabel = destType.isNullable() ? successLabel : failLabel;
   7212 
   7213  auto finishSuccess = [&]() {
   7214    if (successLabel != &fallthrough) {
   7215      jump(successLabel);
   7216    }
   7217    bind(&fallthrough);
   7218  };
   7219  auto finishFail = [&]() {
   7220    if (failLabel != &fallthrough) {
   7221      jump(failLabel);
   7222    }
   7223    bind(&fallthrough);
   7224  };
   7225 
   7226  // Check for null.
   7227  if (sourceType.isNullable()) {
   7228    branchTestPtr(Assembler::Zero, ref, ref, nullLabel);
   7229  }
   7230 
   7231  // The only value that can inhabit 'noextern' is null. So, early out if we got
   7232  // not-null.
   7233  if (destType.isNoExtern()) {
   7234    finishFail();
   7235    return;
   7236  }
   7237 
   7238  // There are no other possible types except externref, so succeed!
   7239  finishSuccess();
   7240 }
   7241 
   7242 void MacroAssembler::branchWasmRefIsSubtypeExn(Register ref,
   7243                                               wasm::RefType sourceType,
   7244                                               wasm::RefType destType,
   7245                                               Label* label, bool onSuccess) {
   7246  MOZ_ASSERT(sourceType.isValid());
   7247  MOZ_ASSERT(destType.isValid());
   7248  MOZ_ASSERT(sourceType.isExnHierarchy());
   7249  MOZ_ASSERT(destType.isExnHierarchy());
   7250 
   7251  Label fallthrough;
   7252  Label* successLabel = onSuccess ? label : &fallthrough;
   7253  Label* failLabel = onSuccess ? &fallthrough : label;
   7254  Label* nullLabel = destType.isNullable() ? successLabel : failLabel;
   7255 
   7256  auto finishSuccess = [&]() {
   7257    if (successLabel != &fallthrough) {
   7258      jump(successLabel);
   7259    }
   7260    bind(&fallthrough);
   7261  };
   7262  auto finishFail = [&]() {
   7263    if (failLabel != &fallthrough) {
   7264      jump(failLabel);
   7265    }
   7266    bind(&fallthrough);
   7267  };
   7268 
   7269  // Check for null.
   7270  if (sourceType.isNullable()) {
   7271    branchTestPtr(Assembler::Zero, ref, ref, nullLabel);
   7272  }
   7273 
   7274  // The only value that can inhabit 'noexn' is null. So, early out if we got
   7275  // not-null.
   7276  if (destType.isNoExn()) {
   7277    finishFail();
   7278    return;
   7279  }
   7280 
   7281  // There are no other possible types except exnref, so succeed!
   7282  finishSuccess();
   7283 }
   7284 
   7285 void MacroAssembler::branchWasmSTVIsSubtype(Register subSTV, Register superSTV,
   7286                                            Register scratch,
   7287                                            const wasm::TypeDef* destType,
   7288                                            Label* label, bool onSuccess) {
   7289  if (destType->isFinal()) {
   7290    // A final type cannot have subtypes, and therefore a simple equality check
   7291    // is sufficient.
   7292    MOZ_ASSERT(scratch == Register::Invalid());
   7293    branchPtr(onSuccess ? Assembler::Equal : Assembler::NotEqual, subSTV,
   7294              superSTV, label);
   7295    return;
   7296  }
   7297 
   7298  MOZ_ASSERT((destType->subTypingDepth() >= wasm::MinSuperTypeVectorLength) ==
   7299             (scratch != Register::Invalid()));
   7300 
   7301  Label fallthrough;
   7302  Label* successLabel = onSuccess ? label : &fallthrough;
   7303  Label* failLabel = onSuccess ? &fallthrough : label;
   7304 
   7305  // Emit a fast success path for if subSTV == superSTV.
   7306  // If they are unequal, they still may be subtypes.
   7307  branchPtr(Assembler::Equal, subSTV, superSTV, successLabel);
   7308 
   7309  // Emit a bounds check if the super type depth may be out-of-bounds.
   7310  if (destType->subTypingDepth() >= wasm::MinSuperTypeVectorLength) {
   7311    load32(Address(subSTV, wasm::SuperTypeVector::offsetOfLength()), scratch);
   7312    branch32(Assembler::BelowOrEqual, scratch,
   7313             Imm32(destType->subTypingDepth()), failLabel);
   7314  }
   7315 
   7316  // Load the `superTypeDepth` entry from subSTV. This will be `superSTV` if
   7317  // `subSTV` is indeed a subtype.
   7318  loadPtr(Address(subSTV, wasm::SuperTypeVector::offsetOfSTVInVector(
   7319                              destType->subTypingDepth())),
   7320          subSTV);
   7321 
   7322  // We succeed iff the entries are equal.
   7323  branchPtr(onSuccess ? Assembler::Equal : Assembler::NotEqual, subSTV,
   7324            superSTV, label);
   7325 
   7326  bind(&fallthrough);
   7327 }
   7328 
   7329 void MacroAssembler::branchWasmSTVIsSubtypeDynamicDepth(
   7330    Register subSTV, Register superSTV, Register superDepth, Register scratch,
   7331    Label* label, bool onSuccess) {
   7332  Label fallthrough;
   7333  Label* failed = onSuccess ? &fallthrough : label;
   7334 
   7335  // Bounds check of the super type vector
   7336  load32(Address(subSTV, wasm::SuperTypeVector::offsetOfLength()), scratch);
   7337  branch32(Assembler::BelowOrEqual, scratch, superDepth, failed);
   7338 
   7339  // Load `subSTV[superTypeDepth]`. This will be `superSTV` if `subSTV` is
   7340  // indeed a subtype.
   7341  loadPtr(BaseIndex(subSTV, superDepth, ScalePointer,
   7342                    offsetof(wasm::SuperTypeVector, types_)),
   7343          subSTV);
   7344 
   7345  // We succeed iff the entries are equal
   7346  branchPtr(onSuccess ? Assembler::Equal : Assembler::NotEqual, subSTV,
   7347            superSTV, label);
   7348 
   7349  bind(&fallthrough);
   7350 }
   7351 
   7352 void MacroAssembler::extractWasmAnyRefTag(Register src, Register dest) {
   7353  andPtr(Imm32(int32_t(wasm::AnyRef::TagMask)), src, dest);
   7354 }
   7355 
   7356 void MacroAssembler::untagWasmAnyRef(Register src, Register dest,
   7357                                     wasm::AnyRefTag tag) {
   7358  MOZ_ASSERT(tag != wasm::AnyRefTag::ObjectOrNull, "No untagging needed");
   7359  computeEffectiveAddress(Address(src, -int32_t(tag)), dest);
   7360 }
   7361 
   7362 void MacroAssembler::branchWasmAnyRefIsNull(bool isNull, Register src,
   7363                                            Label* label) {
   7364  branchTestPtr(isNull ? Assembler::Zero : Assembler::NonZero, src, src, label);
   7365 }
   7366 
   7367 void MacroAssembler::branchWasmAnyRefIsI31(bool isI31, Register src,
   7368                                           Label* label) {
   7369  branchTestPtr(isI31 ? Assembler::NonZero : Assembler::Zero, src,
   7370                Imm32(int32_t(wasm::AnyRefTag::I31)), label);
   7371 }
   7372 
   7373 void MacroAssembler::branchWasmAnyRefIsObjectOrNull(bool isObject, Register src,
   7374                                                    Label* label) {
   7375  branchTestPtr(isObject ? Assembler::Zero : Assembler::NonZero, src,
   7376                Imm32(int32_t(wasm::AnyRef::TagMask)), label);
   7377 }
   7378 
   7379 void MacroAssembler::branchWasmAnyRefIsJSString(bool isJSString, Register src,
   7380                                                Register temp, Label* label) {
   7381  extractWasmAnyRefTag(src, temp);
   7382  branch32(isJSString ? Assembler::Equal : Assembler::NotEqual, temp,
   7383           Imm32(int32_t(wasm::AnyRefTag::String)), label);
   7384 }
   7385 
   7386 void MacroAssembler::branchWasmAnyRefIsGCThing(bool isGCThing, Register src,
   7387                                               Label* label) {
   7388  Label fallthrough;
   7389  Label* isGCThingLabel = isGCThing ? label : &fallthrough;
   7390  Label* isNotGCThingLabel = isGCThing ? &fallthrough : label;
   7391 
   7392  // A null value or i31 value are not GC things.
   7393  branchWasmAnyRefIsNull(true, src, isNotGCThingLabel);
   7394  branchWasmAnyRefIsI31(true, src, isNotGCThingLabel);
   7395  jump(isGCThingLabel);
   7396  bind(&fallthrough);
   7397 }
   7398 
   7399 void MacroAssembler::branchWasmAnyRefIsNurseryCell(bool isNurseryCell,
   7400                                                   Register src, Register temp,
   7401                                                   Label* label) {
   7402  Label done;
   7403  branchWasmAnyRefIsGCThing(false, src, isNurseryCell ? &done : label);
   7404 
   7405  getWasmAnyRefGCThingChunk(src, temp);
   7406  branchPtr(isNurseryCell ? Assembler::NotEqual : Assembler::Equal,
   7407            Address(temp, gc::ChunkStoreBufferOffset), ImmWord(0), label);
   7408  bind(&done);
   7409 }
   7410 
   7411 void MacroAssembler::truncate32ToWasmI31Ref(Register src, Register dest) {
   7412  // This will either zero-extend or sign-extend the high 32-bits on 64-bit
   7413  // platforms (see comments on invariants in MacroAssembler.h). Either case
   7414  // is fine, as we won't use this bits.
   7415  //
   7416  // Move the payload of the integer over by 1 to make room for the tag. This
   7417  // will perform the truncation required by the spec.
   7418  lshift32(Imm32(1), src, dest);
   7419  // Add the i31 tag to the integer.
   7420  orPtr(Imm32(int32_t(wasm::AnyRefTag::I31)), dest);
   7421 #ifdef JS_64BIT
   7422  debugAssertCanonicalInt32(dest);
   7423 #endif
   7424 }
   7425 
   7426 void MacroAssembler::convertWasmI31RefTo32Signed(Register src, Register dest) {
   7427 #ifdef JS_64BIT
   7428  debugAssertCanonicalInt32(src);
   7429 #endif
   7430  // This will either zero-extend or sign-extend the high 32-bits on 64-bit
   7431  // platforms (see comments on invariants in MacroAssembler.h). Either case
   7432  // is fine, as we won't use this bits.
   7433  //
   7434  // Shift the payload back (clobbering the tag). This will sign-extend, giving
   7435  // us the unsigned behavior we want.
   7436  rshift32Arithmetic(Imm32(1), src, dest);
   7437 }
   7438 
   7439 void MacroAssembler::convertWasmI31RefTo32Unsigned(Register src,
   7440                                                   Register dest) {
   7441 #ifdef JS_64BIT
   7442  debugAssertCanonicalInt32(src);
   7443 #endif
   7444  // This will either zero-extend or sign-extend the high 32-bits on 64-bit
   7445  // platforms (see comments on invariants in MacroAssembler.h). Either case
   7446  // is fine, as we won't use this bits.
   7447  //
   7448  // Shift the payload back (clobbering the tag). This will zero-extend, giving
   7449  // us the unsigned behavior we want.
   7450  rshift32(Imm32(1), src, dest);
   7451 }
   7452 
   7453 void MacroAssembler::branchValueConvertsToWasmAnyRefInline(
   7454    ValueOperand src, Register scratchInt, FloatRegister scratchFloat,
   7455    Label* label) {
   7456  // We can convert objects, strings, 31-bit integers and null without boxing.
   7457  Label checkInt32;
   7458  Label checkDouble;
   7459  Label fallthrough;
   7460  {
   7461    ScratchTagScope tag(*this, src);
   7462    splitTagForTest(src, tag);
   7463    branchTestObject(Assembler::Equal, tag, label);
   7464    branchTestString(Assembler::Equal, tag, label);
   7465    branchTestNull(Assembler::Equal, tag, label);
   7466    branchTestInt32(Assembler::Equal, tag, &checkInt32);
   7467    branchTestDouble(Assembler::Equal, tag, &checkDouble);
   7468  }
   7469  jump(&fallthrough);
   7470 
   7471  bind(&checkInt32);
   7472  {
   7473    unboxInt32(src, scratchInt);
   7474    branch32(Assembler::GreaterThan, scratchInt,
   7475             Imm32(wasm::AnyRef::MaxI31Value), &fallthrough);
   7476    branch32(Assembler::LessThan, scratchInt, Imm32(wasm::AnyRef::MinI31Value),
   7477             &fallthrough);
   7478    jump(label);
   7479  }
   7480 
   7481  bind(&checkDouble);
   7482  {
   7483    unboxDouble(src, scratchFloat);
   7484    convertDoubleToInt32(scratchFloat, scratchInt, &fallthrough,
   7485                         /*negativeZeroCheck=*/false);
   7486    branch32(Assembler::GreaterThan, scratchInt,
   7487             Imm32(wasm::AnyRef::MaxI31Value), &fallthrough);
   7488    branch32(Assembler::LessThan, scratchInt, Imm32(wasm::AnyRef::MinI31Value),
   7489             &fallthrough);
   7490    jump(label);
   7491  }
   7492 
   7493  bind(&fallthrough);
   7494 }
   7495 
   7496 void MacroAssembler::convertValueToWasmAnyRef(ValueOperand src, Register dest,
   7497                                              FloatRegister scratchFloat,
   7498                                              Label* oolConvert) {
   7499  Label doubleValue, int32Value, nullValue, stringValue, objectValue, done;
   7500  {
   7501    ScratchTagScope tag(*this, src);
   7502    splitTagForTest(src, tag);
   7503    branchTestObject(Assembler::Equal, tag, &objectValue);
   7504    branchTestString(Assembler::Equal, tag, &stringValue);
   7505    branchTestNull(Assembler::Equal, tag, &nullValue);
   7506    branchTestInt32(Assembler::Equal, tag, &int32Value);
   7507    branchTestDouble(Assembler::Equal, tag, &doubleValue);
   7508    jump(oolConvert);
   7509  }
   7510 
   7511  bind(&doubleValue);
   7512  {
   7513    unboxDouble(src, scratchFloat);
   7514    convertDoubleToInt32(scratchFloat, dest, oolConvert,
   7515                         /*negativeZeroCheck=*/false);
   7516    branch32(Assembler::GreaterThan, dest, Imm32(wasm::AnyRef::MaxI31Value),
   7517             oolConvert);
   7518    branch32(Assembler::LessThan, dest, Imm32(wasm::AnyRef::MinI31Value),
   7519             oolConvert);
   7520    truncate32ToWasmI31Ref(dest, dest);
   7521    jump(&done);
   7522  }
   7523 
   7524  bind(&int32Value);
   7525  {
   7526    unboxInt32(src, dest);
   7527    branch32(Assembler::GreaterThan, dest, Imm32(wasm::AnyRef::MaxI31Value),
   7528             oolConvert);
   7529    branch32(Assembler::LessThan, dest, Imm32(wasm::AnyRef::MinI31Value),
   7530             oolConvert);
   7531    truncate32ToWasmI31Ref(dest, dest);
   7532    jump(&done);
   7533  }
   7534 
   7535  bind(&nullValue);
   7536  {
   7537    static_assert(wasm::AnyRef::NullRefValue == 0);
   7538    xorPtr(dest, dest);
   7539    jump(&done);
   7540  }
   7541 
   7542  bind(&stringValue);
   7543  {
   7544    unboxString(src, dest);
   7545    orPtr(Imm32((int32_t)wasm::AnyRefTag::String), dest);
   7546    jump(&done);
   7547  }
   7548 
   7549  bind(&objectValue);
   7550  {
   7551    unboxObject(src, dest);
   7552  }
   7553 
   7554  bind(&done);
   7555 }
   7556 
   7557 void MacroAssembler::convertObjectToWasmAnyRef(Register src, Register dest) {
   7558  // JS objects are represented without any tagging.
   7559  movePtr(src, dest);
   7560 }
   7561 
   7562 void MacroAssembler::convertStringToWasmAnyRef(Register src, Register dest) {
   7563  // JS strings require a tag.
   7564  orPtr(Imm32(int32_t(wasm::AnyRefTag::String)), src, dest);
   7565 }
   7566 
   7567 FaultingCodeOffset MacroAssembler::branchObjectIsWasmGcObject(bool isGcObject,
   7568                                                              Register src,
   7569                                                              Register scratch,
   7570                                                              Label* label) {
   7571  constexpr uint32_t ShiftedMask = (Shape::kindMask() << Shape::kindShift());
   7572  constexpr uint32_t ShiftedKind =
   7573      (uint32_t(Shape::Kind::WasmGC) << Shape::kindShift());
   7574  MOZ_ASSERT(src != scratch);
   7575 
   7576  FaultingCodeOffset fco =
   7577      loadPtr(Address(src, JSObject::offsetOfShape()), scratch);
   7578  load32(Address(scratch, Shape::offsetOfImmutableFlags()), scratch);
   7579  and32(Imm32(ShiftedMask), scratch);
   7580  branch32(isGcObject ? Assembler::Equal : Assembler::NotEqual, scratch,
   7581           Imm32(ShiftedKind), label);
   7582  return fco;
   7583 }
   7584 
   7585 void MacroAssembler::wasmNewStructObject(Register instance, Register result,
   7586                                         Register allocSite, Register temp,
   7587                                         size_t offsetOfTypeDefData,
   7588                                         Label* fail, gc::AllocKind allocKind,
   7589                                         bool zeroFields) {
   7590  MOZ_ASSERT(instance != result);
   7591 
   7592  // Don't execute the inline path if GC probes are built in.
   7593 #ifdef JS_GC_PROBES
   7594  jump(fail);
   7595 #endif
   7596 
   7597  // Don't execute the inline path if there is an allocation metadata builder
   7598  // on the realm.
   7599  branchPtr(
   7600      Assembler::NotEqual,
   7601      Address(instance, wasm::Instance::offsetOfAllocationMetadataBuilder()),
   7602      ImmWord(0), fail);
   7603 
   7604 #ifdef JS_GC_ZEAL
   7605  // Don't execute the inline path if gc zeal or tracing are active.
   7606  loadPtr(Address(instance, wasm::Instance::offsetOfAddressOfGCZealModeBits()),
   7607          temp);
   7608  load32(Address(temp, 0), temp);
   7609  branch32(Assembler::NotEqual, temp, Imm32(0), fail);
   7610 #endif
   7611 
   7612  // If the alloc site is long lived, immediately fall back to the OOL path,
   7613  // which will handle that.
   7614  branchTestPtr(Assembler::NonZero,
   7615                Address(allocSite, gc::AllocSite::offsetOfScriptAndState()),
   7616                Imm32(gc::AllocSite::LONG_LIVED_BIT), fail);
   7617 
   7618  size_t sizeBytes = gc::Arena::thingSize(allocKind);
   7619  wasmBumpPointerAllocate(instance, result, allocSite, temp, fail, sizeBytes);
   7620 
   7621  loadPtr(Address(instance, offsetOfTypeDefData +
   7622                                wasm::TypeDefInstanceData::offsetOfShape()),
   7623          temp);
   7624  storePtr(temp, Address(result, WasmArrayObject::offsetOfShape()));
   7625  loadPtr(Address(instance,
   7626                  offsetOfTypeDefData +
   7627                      wasm::TypeDefInstanceData::offsetOfSuperTypeVector()),
   7628          temp);
   7629  storePtr(temp, Address(result, WasmArrayObject::offsetOfSuperTypeVector()));
   7630 
   7631  if (zeroFields) {
   7632    static_assert(wasm::WasmStructObject_Size_ASSUMED % sizeof(void*) == 0);
   7633    MOZ_ASSERT(sizeBytes % sizeof(void*) == 0);
   7634    for (size_t i = wasm::WasmStructObject_Size_ASSUMED; i < sizeBytes;
   7635         i += sizeof(void*)) {
   7636      storePtr(ImmWord(0), Address(result, i));
   7637    }
   7638  }
   7639 }
   7640 
   7641 void MacroAssembler::wasmNewArrayObject(Register instance, Register result,
   7642                                        Register numElements,
   7643                                        Register allocSite, Register temp,
   7644                                        size_t offsetOfTypeDefData, Label* fail,
   7645                                        uint32_t elemSize, bool zeroFields) {
   7646  MOZ_ASSERT(instance != result);
   7647 
   7648  // Don't execute the inline path if GC probes are built in.
   7649 #ifdef JS_GC_PROBES
   7650  jump(fail);
   7651 #endif
   7652 
   7653  // Don't execute the inline path if there is an allocation metadata builder
   7654  // on the realm.
   7655  branchPtr(
   7656      Assembler::NotEqual,
   7657      Address(instance, wasm::Instance::offsetOfAllocationMetadataBuilder()),
   7658      ImmWord(0), fail);
   7659 
   7660 #ifdef JS_GC_ZEAL
   7661  // Don't execute the inline path if gc zeal or tracing are active.
   7662  loadPtr(Address(instance, wasm::Instance::offsetOfAddressOfGCZealModeBits()),
   7663          temp);
   7664  load32(Address(temp, 0), temp);
   7665  branch32(Assembler::NotEqual, temp, Imm32(0), fail);
   7666 #endif
   7667 
   7668  // If the alloc site is long lived, immediately fall back to the OOL path,
   7669  // which will handle that.
   7670  branchTestPtr(Assembler::NonZero,
   7671                Address(allocSite, gc::AllocSite::offsetOfScriptAndState()),
   7672                Imm32(gc::AllocSite::LONG_LIVED_BIT), fail);
   7673 
   7674  // Ensure that the numElements is small enough to fit in inline storage.
   7675  branch32(Assembler::Above, numElements,
   7676           Imm32(WasmArrayObject::maxInlineElementsForElemSize(elemSize)),
   7677           fail);
   7678 
   7679  // Push numElements for later; numElements will be used as a temp in the
   7680  // meantime. Make sure that all exit paths pop the value again!
   7681  Label popAndFail;
   7682 #ifdef JS_CODEGEN_ARM64
   7683  // On arm64, we must maintain 16-alignment of both the actual and pseudo stack
   7684  // pointers.
   7685  push(numElements, xzr);
   7686  syncStackPtr();
   7687 #else
   7688  push(numElements);
   7689 #endif
   7690 
   7691  // Compute the size of the allocation in bytes. The final size must correspond
   7692  // to an AllocKind. See WasmArrayObject::calcStorageBytes and
   7693  // WasmArrayObject::allocKindForIL.
   7694 
   7695  // Compute the size of all array element data.
   7696  mul32(Imm32(elemSize), numElements);
   7697  // Add the data header.
   7698  add32(Imm32(sizeof(WasmArrayObject::DataHeader)), numElements);
   7699  // Round up to gc::CellAlignBytes to play nice with the GC and to simplify the
   7700  // zeroing logic below.
   7701  add32(Imm32(gc::CellAlignBytes - 1), numElements);
   7702  and32(Imm32(~int32_t(gc::CellAlignBytes - 1)), numElements);
   7703  // Add the size of the WasmArrayObject to get the full allocation size.
   7704  static_assert(WasmArrayObject_MaxInlineBytes + sizeof(WasmArrayObject) <
   7705                INT32_MAX);
   7706  add32(Imm32(sizeof(WasmArrayObject)), numElements);
   7707  // Per gc::slotsToAllocKindBytes, subtract sizeof(NativeObject),
   7708  // divide by sizeof(js::Value), then look up the final AllocKind-based
   7709  // allocation size from a table.
   7710  movePtr(wasm::SymbolicAddress::SlotsToAllocKindBytesTable, temp);
   7711  move32ZeroExtendToPtr(numElements, numElements);
   7712  subPtr(Imm32(sizeof(NativeObject)), numElements);
   7713  static_assert(sizeof(js::Value) == 8);
   7714  rshiftPtr(Imm32(3), numElements);
   7715  static_assert(sizeof(gc::slotsToAllocKindBytes[0]) == 4);
   7716  load32(BaseIndex(temp, numElements, Scale::TimesFour), numElements);
   7717 
   7718  wasmBumpPointerAllocateDynamic(instance, result, allocSite,
   7719                                 /*size=*/numElements, temp, &popAndFail);
   7720 
   7721  // Initialize the shape and STV
   7722  loadPtr(Address(instance, offsetOfTypeDefData +
   7723                                wasm::TypeDefInstanceData::offsetOfShape()),
   7724          temp);
   7725  storePtr(temp, Address(result, WasmArrayObject::offsetOfShape()));
   7726  loadPtr(Address(instance,
   7727                  offsetOfTypeDefData +
   7728                      wasm::TypeDefInstanceData::offsetOfSuperTypeVector()),
   7729          temp);
   7730  storePtr(temp, Address(result, WasmArrayObject::offsetOfSuperTypeVector()));
   7731 
   7732  // Store inline data header and data pointer
   7733  storePtr(ImmWord(WasmArrayObject::DataIsIL),
   7734           Address(result, WasmArrayObject::offsetOfInlineStorage()));
   7735  computeEffectiveAddress(
   7736      Address(result, WasmArrayObject::offsetOfInlineArrayData()), temp);
   7737  // temp now points at the base of the array data; this will be used later
   7738  storePtr(temp, Address(result, WasmArrayObject::offsetOfData()));
   7739  // numElements will be saved to the array object later; for now we want to
   7740  // continue using numElements as a temp.
   7741 
   7742  // Zero the array elements. This loop depends on the size of the array data
   7743  // being a multiple of the machine word size. This is currently always the
   7744  // case since WasmArrayObject::calcStorageBytes rounds up to
   7745  // gc::CellAlignBytes.
   7746  static_assert(gc::CellAlignBytes % sizeof(void*) == 0);
   7747  Label zeroed;
   7748  if (zeroFields) {
   7749    // numElements currently stores the total size of the allocation. temp
   7750    // points at the base of the inline array data. We will zero the memory by
   7751    // advancing numElements to the end of the allocation, then counting down
   7752    // toward temp, zeroing one word at a time. The following aliases make this
   7753    // clearer.
   7754    Register current = numElements;
   7755    Register inlineArrayData = temp;
   7756 
   7757    // We first need to update current to actually point at the end of the
   7758    // allocation. We can compute this from the data pointer, since the data
   7759    // pointer points at a known offset within the array.
   7760    //
   7761    // It is easier to understand the code below as first subtracting the offset
   7762    // (to get back to the start of the allocation), then adding the total size
   7763    // of the allocation (using Scale::TimesOne).
   7764    computeEffectiveAddress(
   7765        BaseIndex(inlineArrayData, current, Scale::TimesOne,
   7766                  -int32_t(WasmArrayObject::offsetOfInlineArrayData())),
   7767        current);
   7768 
   7769    // Exit immediately if the array has zero elements.
   7770    branchPtr(Assembler::Equal, current, inlineArrayData, &zeroed);
   7771 
   7772    // Loop, counting down until current == inlineArrayData.
   7773    Label loop;
   7774    bind(&loop);
   7775    subPtr(Imm32(sizeof(void*)), current);
   7776    storePtr(ImmWord(0), Address(current, 0));
   7777    branchPtr(Assembler::NotEqual, current, inlineArrayData, &loop);
   7778  }
   7779  bind(&zeroed);
   7780 
   7781  // Finally, store the actual numElements in the array object.
   7782 #ifdef JS_CODEGEN_ARM64
   7783  pop(xzr, numElements);
   7784  syncStackPtr();
   7785 #else
   7786  pop(numElements);
   7787 #endif
   7788  store32(numElements, Address(result, WasmArrayObject::offsetOfNumElements()));
   7789 
   7790  Label done;
   7791  jump(&done);
   7792 
   7793  bind(&popAndFail);
   7794 #ifdef JS_CODEGEN_ARM64
   7795  pop(xzr, numElements);
   7796  syncStackPtr();
   7797 #else
   7798  pop(numElements);
   7799 #endif
   7800  jump(fail);
   7801 
   7802  bind(&done);
   7803 }
   7804 
   7805 void MacroAssembler::wasmNewArrayObjectFixed(
   7806    Register instance, Register result, Register allocSite, Register temp1,
   7807    Register temp2, size_t offsetOfTypeDefData, Label* fail,
   7808    uint32_t numElements, uint32_t storageBytes, bool zeroFields) {
   7809  MOZ_ASSERT(storageBytes <= WasmArrayObject_MaxInlineBytes);
   7810  MOZ_ASSERT(instance != result);
   7811 
   7812  // Don't execute the inline path if GC probes are built in.
   7813 #ifdef JS_GC_PROBES
   7814  jump(fail);
   7815 #endif
   7816 
   7817  // Don't execute the inline path if there is an allocation metadata builder
   7818  // on the realm.
   7819  branchPtr(
   7820      Assembler::NotEqual,
   7821      Address(instance, wasm::Instance::offsetOfAllocationMetadataBuilder()),
   7822      ImmWord(0), fail);
   7823 
   7824 #ifdef JS_GC_ZEAL
   7825  // Don't execute the inline path if gc zeal or tracing are active.
   7826  loadPtr(Address(instance, wasm::Instance::offsetOfAddressOfGCZealModeBits()),
   7827          temp1);
   7828  load32(Address(temp1, 0), temp1);
   7829  branch32(Assembler::NotEqual, temp1, Imm32(0), fail);
   7830 #endif
   7831 
   7832  // If the alloc site is long lived, immediately fall back to the OOL path,
   7833  // which will handle that.
   7834  branchTestPtr(Assembler::NonZero,
   7835                Address(allocSite, gc::AllocSite::offsetOfScriptAndState()),
   7836                Imm32(gc::AllocSite::LONG_LIVED_BIT), fail);
   7837 
   7838  gc::AllocKind allocKind = WasmArrayObject::allocKindForIL(storageBytes);
   7839  uint32_t totalSize = gc::Arena::thingSize(allocKind);
   7840  wasmBumpPointerAllocate(instance, result, allocSite, temp1, fail, totalSize);
   7841 
   7842  loadPtr(Address(instance, offsetOfTypeDefData +
   7843                                wasm::TypeDefInstanceData::offsetOfShape()),
   7844          temp1);
   7845  loadPtr(Address(instance,
   7846                  offsetOfTypeDefData +
   7847                      wasm::TypeDefInstanceData::offsetOfSuperTypeVector()),
   7848          temp2);
   7849  storePtr(temp1, Address(result, WasmArrayObject::offsetOfShape()));
   7850  storePtr(temp2, Address(result, WasmArrayObject::offsetOfSuperTypeVector()));
   7851  store32(Imm32(numElements),
   7852          Address(result, WasmArrayObject::offsetOfNumElements()));
   7853 
   7854  // Store inline data header and data pointer
   7855  storePtr(ImmWord(WasmArrayObject::DataIsIL),
   7856           Address(result, WasmArrayObject::offsetOfInlineStorage()));
   7857  computeEffectiveAddress(
   7858      Address(result, WasmArrayObject::offsetOfInlineArrayData()), temp2);
   7859  // temp2 now points at the base of the array data; this will be used later
   7860  storePtr(temp2, Address(result, WasmArrayObject::offsetOfData()));
   7861 
   7862  if (zeroFields) {
   7863    MOZ_ASSERT(storageBytes % sizeof(void*) == 0);
   7864 
   7865    // Advance temp1 to the end of the allocation
   7866    // (note that temp2 is already past the data header)
   7867    Label done;
   7868    computeEffectiveAddress(
   7869        Address(temp2, -sizeof(WasmArrayObject::DataHeader) + storageBytes),
   7870        temp1);
   7871    branchPtr(Assembler::Equal, temp1, temp2, &done);
   7872 
   7873    // Count temp2 down toward temp1, zeroing one word at a time
   7874    Label loop;
   7875    bind(&loop);
   7876    subPtr(Imm32(sizeof(void*)), temp1);
   7877    storePtr(ImmWord(0), Address(temp1, 0));
   7878    branchPtr(Assembler::NotEqual, temp1, temp2, &loop);
   7879 
   7880    bind(&done);
   7881  }
   7882 }
   7883 
   7884 void MacroAssembler::wasmBumpPointerAllocate(Register instance, Register result,
   7885                                             Register allocSite, Register temp1,
   7886                                             Label* fail, uint32_t size) {
   7887  MOZ_ASSERT(size >= gc::MinCellSize);
   7888 
   7889  uint32_t totalSize = size + Nursery::nurseryCellHeaderSize();
   7890  MOZ_ASSERT(totalSize < INT32_MAX, "Nursery allocation too large");
   7891  MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0);
   7892 
   7893  int32_t endOffset = Nursery::offsetOfCurrentEndFromPosition();
   7894 
   7895  // Bail to OOL code if the alloc site needs to be pushed onto the active
   7896  // list.
   7897  load32(Address(allocSite, gc::AllocSite::offsetOfNurseryAllocCount()), temp1);
   7898  branch32(Assembler::Equal, temp1,
   7899           Imm32(js::gc::NormalSiteAttentionThreshold - 1), fail);
   7900 
   7901  // Bump allocate in the nursery, bailing if there is not enough room.
   7902  loadPtr(Address(instance, wasm::Instance::offsetOfAddressOfNurseryPosition()),
   7903          temp1);
   7904  loadPtr(Address(temp1, 0), result);
   7905  addPtr(Imm32(totalSize), result);
   7906  branchPtr(Assembler::Below, Address(temp1, endOffset), result, fail);
   7907  storePtr(result, Address(temp1, 0));
   7908  subPtr(Imm32(size), result);
   7909 
   7910  // Increment the alloc count in the allocation site and store pointer in the
   7911  // nursery cell header. See NurseryCellHeader::MakeValue.
   7912  add32(Imm32(1),
   7913        Address(allocSite, gc::AllocSite::offsetOfNurseryAllocCount()));
   7914  // Because JS::TraceKind::Object is zero, there is no need to explicitly set
   7915  // it in the nursery cell header.
   7916  static_assert(int(JS::TraceKind::Object) == 0);
   7917  storePtr(allocSite, Address(result, -js::Nursery::nurseryCellHeaderSize()));
   7918 }
   7919 
   7920 void MacroAssembler::wasmBumpPointerAllocateDynamic(
   7921    Register instance, Register result, Register allocSite, Register size,
   7922    Register temp1, Label* fail) {
   7923 #ifdef DEBUG
   7924  // Replaces MOZ_ASSERT(size >= gc::MinCellSize);
   7925  Label ok1;
   7926  branch32(Assembler::AboveOrEqual, size, Imm32(gc::MinCellSize), &ok1);
   7927  breakpoint();
   7928  bind(&ok1);
   7929 
   7930  Label ok2;
   7931  branch32(Assembler::BelowOrEqual, size, Imm32(JSObject::MAX_BYTE_SIZE), &ok2);
   7932  breakpoint();
   7933  bind(&ok2);
   7934 #endif
   7935 
   7936  int32_t endOffset = Nursery::offsetOfCurrentEndFromPosition();
   7937 
   7938  // Bail to OOL code if the alloc site needs to be initialized.
   7939  load32(Address(allocSite, gc::AllocSite::offsetOfNurseryAllocCount()), temp1);
   7940  branch32(Assembler::Equal, temp1,
   7941           Imm32(js::gc::NormalSiteAttentionThreshold - 1), fail);
   7942 
   7943  // Bump allocate in the nursery, bailing if there is not enough room.
   7944  loadPtr(Address(instance, wasm::Instance::offsetOfAddressOfNurseryPosition()),
   7945          temp1);
   7946  loadPtr(Address(temp1, 0), result);
   7947  computeEffectiveAddress(BaseIndex(result, size, Scale::TimesOne,
   7948                                    Nursery::nurseryCellHeaderSize()),
   7949                          result);
   7950  branchPtr(Assembler::Below, Address(temp1, endOffset), result, fail);
   7951  storePtr(result, Address(temp1, 0));
   7952  subPtr(size, result);
   7953 
   7954  // Increment the alloc count in the allocation site and store pointer in the
   7955  // nursery cell header. See NurseryCellHeader::MakeValue.
   7956 
   7957  add32(Imm32(1),
   7958        Address(allocSite, gc::AllocSite::offsetOfNurseryAllocCount()));
   7959  // Because JS::TraceKind::Object is zero, there is no need to explicitly set
   7960  // it in the nursery cell header.
   7961  static_assert(int(JS::TraceKind::Object) == 0);
   7962  storePtr(allocSite, Address(result, -js::Nursery::nurseryCellHeaderSize()));
   7963 }
   7964 
   7965 // Unboxing is branchy and contorted because of Spectre mitigations - we don't
   7966 // have enough scratch registers.  Were it not for the spectre mitigations in
   7967 // branchTestObjClass, the branch nest below would be restructured significantly
   7968 // by inverting branches and using fewer registers.
   7969 
   7970 // Unbox an anyref in src (clobbering src in the process) and then re-box it as
   7971 // a Value in *dst.  See the definition of AnyRef for a discussion of pointer
   7972 // representation.
   7973 void MacroAssembler::convertWasmAnyRefToValue(Register instance, Register src,
   7974                                              ValueOperand dst,
   7975                                              Register scratch) {
   7976  MOZ_ASSERT(src != scratch);
   7977 #if JS_BITS_PER_WORD == 32
   7978  MOZ_ASSERT(dst.typeReg() != scratch);
   7979  MOZ_ASSERT(dst.payloadReg() != scratch);
   7980 #else
   7981  MOZ_ASSERT(dst.valueReg() != scratch);
   7982 #endif
   7983 
   7984  Label isI31, isObjectOrNull, isObject, isWasmValueBox, done;
   7985 
   7986  // Check for if this is an i31 value first
   7987  branchTestPtr(Assembler::NonZero, src, Imm32(int32_t(wasm::AnyRefTag::I31)),
   7988                &isI31);
   7989  // Then check for the object or null tag
   7990  branchTestPtr(Assembler::Zero, src, Imm32(wasm::AnyRef::TagMask),
   7991                &isObjectOrNull);
   7992 
   7993  // If we're not i31, object, or null, we must be a string
   7994  untagWasmAnyRef(src, src, wasm::AnyRefTag::String);
   7995  moveValue(TypedOrValueRegister(MIRType::String, AnyRegister(src)), dst);
   7996  jump(&done);
   7997 
   7998  // This is an i31 value, convert to an int32 JS value
   7999  bind(&isI31);
   8000  convertWasmI31RefTo32Signed(src, src);
   8001  moveValue(TypedOrValueRegister(MIRType::Int32, AnyRegister(src)), dst);
   8002  jump(&done);
   8003 
   8004  // Check for the null value
   8005  bind(&isObjectOrNull);
   8006  branchTestPtr(Assembler::NonZero, src, src, &isObject);
   8007  moveValue(NullValue(), dst);
   8008  jump(&done);
   8009 
   8010  // Otherwise we must be a non-null object. We next to check if it's storing a
   8011  // boxed value
   8012  bind(&isObject);
   8013  // The type test will clear src if the test fails, so store early.
   8014  moveValue(TypedOrValueRegister(MIRType::Object, AnyRegister(src)), dst);
   8015  // Spectre mitigations: see comment above about efficiency.
   8016  branchTestObjClass(Assembler::Equal, src,
   8017                     Address(instance, wasm::Instance::offsetOfValueBoxClass()),
   8018                     scratch, src, &isWasmValueBox);
   8019  jump(&done);
   8020 
   8021  // This is a boxed JS value, unbox it.
   8022  bind(&isWasmValueBox);
   8023  loadValue(Address(src, wasm::AnyRef::valueBoxOffsetOfValue()), dst);
   8024 
   8025  bind(&done);
   8026 }
   8027 
   8028 void MacroAssembler::convertWasmAnyRefToValue(Register instance, Register src,
   8029                                              const Address& dst,
   8030                                              Register scratch) {
   8031  MOZ_ASSERT(src != scratch);
   8032 
   8033  Label isI31, isObjectOrNull, isObject, isWasmValueBox, done;
   8034 
   8035  // Check for if this is an i31 value first
   8036  branchTestPtr(Assembler::NonZero, src, Imm32(int32_t(wasm::AnyRefTag::I31)),
   8037                &isI31);
   8038  // Then check for the object or null tag
   8039  branchTestPtr(Assembler::Zero, src, Imm32(wasm::AnyRef::TagMask),
   8040                &isObjectOrNull);
   8041 
   8042  // If we're not i31, object, or null, we must be a string
   8043  andPtr(Imm32(int32_t(~wasm::AnyRef::TagMask)), src);
   8044  storeValue(JSVAL_TYPE_STRING, src, dst);
   8045  jump(&done);
   8046 
   8047  // This is an i31 value, convert to an int32 JS value
   8048  bind(&isI31);
   8049  convertWasmI31RefTo32Signed(src, src);
   8050  storeValue(JSVAL_TYPE_INT32, src, dst);
   8051  jump(&done);
   8052 
   8053  // Check for the null value
   8054  bind(&isObjectOrNull);
   8055  branchTestPtr(Assembler::NonZero, src, src, &isObject);
   8056  storeValue(NullValue(), dst);
   8057  jump(&done);
   8058 
   8059  // Otherwise we must be a non-null object. We next to check if it's storing a
   8060  // boxed value
   8061  bind(&isObject);
   8062  // The type test will clear src if the test fails, so store early.
   8063  storeValue(JSVAL_TYPE_OBJECT, src, dst);
   8064  // Spectre mitigations: see comment above about efficiency.
   8065  branchTestObjClass(Assembler::Equal, src,
   8066                     Address(instance, wasm::Instance::offsetOfValueBoxClass()),
   8067                     scratch, src, &isWasmValueBox);
   8068  jump(&done);
   8069 
   8070  // This is a boxed JS value, unbox it.
   8071  bind(&isWasmValueBox);
   8072  copy64(Address(src, wasm::AnyRef::valueBoxOffsetOfValue()), dst, scratch);
   8073 
   8074  bind(&done);
   8075 }
   8076 
   8077 void MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc) {
   8078  CodeOffset offset = nopPatchableToCall();
   8079  append(desc, offset);
   8080 }
   8081 
   8082 // Given a cell and the chunk containing that cell, load the word containing
   8083 // the mark bits for that cell, and compute the bitIndex for a particular mark
   8084 // color.
   8085 void MacroAssembler::loadMarkBits(Register cell, Register chunk,
   8086                                  Register markWord, Register bitIndex,
   8087                                  Register temp, gc::ColorBit color) {
   8088  MOZ_ASSERT(temp != bitIndex);
   8089  MOZ_ASSERT(temp != chunk);
   8090  MOZ_ASSERT(chunk != bitIndex);
   8091 
   8092  // Determine the bit index and store in bitIndex.
   8093  //
   8094  // bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
   8095  //        static_cast<uint32_t>(colorBit);
   8096  static_assert(gc::CellBytesPerMarkBit == 8,
   8097                "Calculation below relies on this");
   8098  andPtr(Imm32(gc::ChunkMask), cell, bitIndex);
   8099  rshiftPtr(Imm32(3), bitIndex);
   8100  if (int32_t(color) != 0) {
   8101    addPtr(Imm32(int32_t(color)), bitIndex);
   8102  }
   8103 
   8104  static_assert(gc::ChunkMarkBitmap::BitsPerWord == JS_BITS_PER_WORD,
   8105                "Calculation below relies on this");
   8106 
   8107  // Load the mark word
   8108  //
   8109  // word = chunk.bitmap[bit / WordBits];
   8110 
   8111  // Fold the adjustment for the fact that arenas don't start at the beginning
   8112  // of the chunk into the offset to the chunk bitmap.
   8113  const size_t firstArenaAdjustment =
   8114      gc::ChunkMarkBitmap::FirstThingAdjustmentBits / CHAR_BIT;
   8115  const intptr_t offset =
   8116      intptr_t(gc::ChunkMarkBitmapOffset) - intptr_t(firstArenaAdjustment);
   8117 
   8118  uint8_t shift = mozilla::FloorLog2Size(JS_BITS_PER_WORD);
   8119  rshiftPtr(Imm32(shift), bitIndex, temp);
   8120  loadPtr(BaseIndex(chunk, temp, ScalePointer, offset), markWord);
   8121 }
   8122 
   8123 void MacroAssembler::emitPreBarrierFastPath(MIRType type, Register temp1,
   8124                                            Register temp2, Register temp3,
   8125                                            Label* noBarrier) {
   8126  MOZ_ASSERT(temp1 != PreBarrierReg);
   8127  MOZ_ASSERT(temp2 != PreBarrierReg);
   8128  MOZ_ASSERT(temp3 != PreBarrierReg);
   8129 
   8130 #ifdef JS_CODEGEN_X64
   8131  MOZ_ASSERT(temp3 == rcx);
   8132 #elif JS_CODEGEN_X86
   8133  MOZ_ASSERT(temp3 == ecx);
   8134 #endif
   8135 
   8136  // Load the GC thing in temp1.
   8137  if (type == MIRType::Value) {
   8138    unboxGCThingForGCBarrier(Address(PreBarrierReg, 0), temp1);
   8139  } else if (type == MIRType::WasmAnyRef) {
   8140    unboxWasmAnyRefGCThingForGCBarrier(Address(PreBarrierReg, 0), temp1);
   8141  } else {
   8142    MOZ_ASSERT(type == MIRType::Object || type == MIRType::String ||
   8143               type == MIRType::Shape);
   8144    loadPtr(Address(PreBarrierReg, 0), temp1);
   8145  }
   8146 
   8147 #ifdef DEBUG
   8148  // The caller should have checked for null pointers.
   8149  Label nonZero;
   8150  branchTestPtr(Assembler::NonZero, temp1, temp1, &nonZero);
   8151  assumeUnreachable("JIT pre-barrier: unexpected nullptr");
   8152  bind(&nonZero);
   8153 #endif
   8154 
   8155  // Load the chunk address in temp2.
   8156  andPtr(Imm32(int32_t(~gc::ChunkMask)), temp1, temp2);
   8157 
   8158  // If the GC thing is in the nursery, we don't need to barrier it.
   8159  if (type == MIRType::Value || type == MIRType::Object ||
   8160      type == MIRType::String || type == MIRType::WasmAnyRef) {
   8161    branchPtr(Assembler::NotEqual, Address(temp2, gc::ChunkStoreBufferOffset),
   8162              ImmWord(0), noBarrier);
   8163  } else {
   8164 #ifdef DEBUG
   8165    Label isTenured;
   8166    branchPtr(Assembler::Equal, Address(temp2, gc::ChunkStoreBufferOffset),
   8167              ImmWord(0), &isTenured);
   8168    assumeUnreachable("JIT pre-barrier: unexpected nursery pointer");
   8169    bind(&isTenured);
   8170 #endif
   8171  }
   8172 
   8173  // Check if the cell is marked black.
   8174  // Load the bitindex into temp3 and the bitmap word into temp2.
   8175  loadMarkBits(temp1, temp2, temp2, temp3, temp1, gc::ColorBit::BlackBit);
   8176 
   8177  // Load the mask in temp1.
   8178  // mask = uintptr_t(1) << (bit % WordBits);
   8179  andPtr(Imm32(gc::ChunkMarkBitmap::BitsPerWord - 1), temp3);
   8180  move32(Imm32(1), temp1);
   8181  lshiftPtr(temp3, temp1);
   8182 
   8183  // No barrier is needed if the bit is set, |word & mask != 0|.
   8184  branchTestPtr(Assembler::NonZero, temp2, temp1, noBarrier);
   8185 }
   8186 
   8187 void MacroAssembler::emitValueReadBarrierFastPath(
   8188    ValueOperand value, Register cell, Register temp1, Register temp2,
   8189    Register temp3, Register temp4, Label* barrier) {
   8190  Label done;
   8191 
   8192  // No barrier needed for non-GC types
   8193  branchTestGCThing(Assembler::NotEqual, value, &done);
   8194 
   8195  // Load the GC thing in `cell`.
   8196  unboxGCThingForGCBarrier(value, cell);
   8197 
   8198  // Load the chunk address.
   8199  Register chunk = temp1;
   8200  andPtr(Imm32(int32_t(~gc::ChunkMask)), cell, chunk);
   8201 
   8202  // If the GC thing is in the nursery, we don't need to barrier it.
   8203  branchPtr(Assembler::NotEqual, Address(chunk, gc::ChunkStoreBufferOffset),
   8204            ImmWord(0), &done);
   8205 
   8206  // Load the mark word and bit index for the black bit.
   8207  Register markWord = temp2;
   8208  Register bitIndex = temp3;
   8209  loadMarkBits(cell, chunk, markWord, bitIndex, temp4, gc::ColorBit::BlackBit);
   8210 
   8211  // The mask for the black bit is 1 << (bitIndex % WordBits).
   8212  Register mask = temp4;
   8213  andPtr(Imm32(gc::ChunkMarkBitmap::BitsPerWord - 1), bitIndex);
   8214  move32(Imm32(1), mask);
   8215  flexibleLshiftPtr(bitIndex, mask);
   8216 
   8217  // No barrier is needed if the black bit is set.
   8218  branchTestPtr(Assembler::NonZero, markWord, mask, &done);
   8219 
   8220  // The bit index for the gray bit is bitIndex + 1. If the black bit
   8221  // is any bit except the highest bit of the mark word, we can reuse
   8222  // the mark word we've already loaded, and simply shift the mask by
   8223  // 1.
   8224  Label noMaskOverflow;
   8225  lshiftPtr(Imm32(1), mask);
   8226  branchTestPtr(Assembler::NonZero, mask, mask, &noMaskOverflow);
   8227 
   8228  // If the black bit was the high bit of the mark word, we need to load
   8229  // a new mark word. In this case the mask for the gray bit will always
   8230  // be 1 (the lowest bit of the next word).
   8231  loadMarkBits(cell, chunk, markWord, bitIndex, temp4,
   8232               gc::ColorBit::GrayOrBlackBit);
   8233  move32(Imm32(1), mask);
   8234  bind(&noMaskOverflow);
   8235 
   8236  // If the gray bit is set, then we *do* need a barrier.
   8237  branchTestPtr(Assembler::NonZero, markWord, mask, barrier);
   8238 
   8239  // Otherwise, we don't need a barrier unless we're in the middle of
   8240  // an incremental GC.
   8241  branchTestNeedsIncrementalBarrierAnyZone(Assembler::NonZero, barrier, temp1);
   8242  bind(&done);
   8243 }
   8244 
   8245 // ========================================================================
   8246 // JS atomic operations.
   8247 
   8248 void MacroAssembler::atomicIsLockFreeJS(Register value, Register output) {
   8249  // Keep this in sync with isLockfreeJS() in jit/AtomicOperations.h.
   8250  static_assert(AtomicOperations::isLockfreeJS(1));  // Implementation artifact
   8251  static_assert(AtomicOperations::isLockfreeJS(2));  // Implementation artifact
   8252  static_assert(AtomicOperations::isLockfreeJS(4));  // Spec requirement
   8253  static_assert(AtomicOperations::isLockfreeJS(8));  // Implementation artifact
   8254 
   8255  Label done;
   8256  move32(Imm32(1), output);
   8257  branch32(Assembler::Equal, value, Imm32(8), &done);
   8258  branch32(Assembler::Equal, value, Imm32(4), &done);
   8259  branch32(Assembler::Equal, value, Imm32(2), &done);
   8260  branch32(Assembler::Equal, value, Imm32(1), &done);
   8261  move32(Imm32(0), output);
   8262  bind(&done);
   8263 }
   8264 
   8265 // ========================================================================
   8266 // Spectre Mitigations.
   8267 
   8268 void MacroAssembler::spectreMaskIndex32(Register index, Register length,
   8269                                        Register output) {
   8270  MOZ_ASSERT(JitOptions.spectreIndexMasking);
   8271  MOZ_ASSERT(length != output);
   8272  MOZ_ASSERT(index != output);
   8273 
   8274  move32(Imm32(0), output);
   8275  cmp32Move32(Assembler::Below, index, length, index, output);
   8276 }
   8277 
   8278 void MacroAssembler::spectreMaskIndex32(Register index, const Address& length,
   8279                                        Register output) {
   8280  MOZ_ASSERT(JitOptions.spectreIndexMasking);
   8281  MOZ_ASSERT(index != length.base);
   8282  MOZ_ASSERT(length.base != output);
   8283  MOZ_ASSERT(index != output);
   8284 
   8285  move32(Imm32(0), output);
   8286  cmp32Move32(Assembler::Below, index, length, index, output);
   8287 }
   8288 
   8289 void MacroAssembler::spectreMaskIndexPtr(Register index, Register length,
   8290                                         Register output) {
   8291  MOZ_ASSERT(JitOptions.spectreIndexMasking);
   8292  MOZ_ASSERT(length != output);
   8293  MOZ_ASSERT(index != output);
   8294 
   8295  movePtr(ImmWord(0), output);
   8296  cmpPtrMovePtr(Assembler::Below, index, length, index, output);
   8297 }
   8298 
   8299 void MacroAssembler::spectreMaskIndexPtr(Register index, const Address& length,
   8300                                         Register output) {
   8301  MOZ_ASSERT(JitOptions.spectreIndexMasking);
   8302  MOZ_ASSERT(index != length.base);
   8303  MOZ_ASSERT(length.base != output);
   8304  MOZ_ASSERT(index != output);
   8305 
   8306  movePtr(ImmWord(0), output);
   8307  cmpPtrMovePtr(Assembler::Below, index, length, index, output);
   8308 }
   8309 
   8310 void MacroAssembler::boundsCheck32PowerOfTwo(Register index, uint32_t length,
   8311                                             Label* failure) {
   8312  MOZ_ASSERT(mozilla::IsPowerOfTwo(length));
   8313  branch32(Assembler::AboveOrEqual, index, Imm32(length), failure);
   8314 
   8315  // Note: it's fine to clobber the input register, as this is a no-op: it
   8316  // only affects speculative execution.
   8317  if (JitOptions.spectreIndexMasking) {
   8318    and32(Imm32(length - 1), index);
   8319  }
   8320 }
   8321 
   8322 void MacroAssembler::loadWasmPinnedRegsFromInstance(
   8323    const wasm::MaybeTrapSiteDesc& trapSiteDesc) {
   8324 #ifdef WASM_HAS_HEAPREG
   8325  static_assert(wasm::Instance::offsetOfMemory0Base() < 4096,
   8326                "We count only on the low page being inaccessible");
   8327  FaultingCodeOffset fco = loadPtr(
   8328      Address(InstanceReg, wasm::Instance::offsetOfMemory0Base()), HeapReg);
   8329  if (trapSiteDesc) {
   8330    append(wasm::Trap::IndirectCallToNull, wasm::TrapMachineInsnForLoadWord(),
   8331           fco.get(), *trapSiteDesc);
   8332  }
   8333 #else
   8334  MOZ_ASSERT(!trapSiteDesc);
   8335 #endif
   8336 }
   8337 
   8338 //}}} check_macroassembler_style
   8339 
   8340 #ifdef JS_64BIT
   8341 void MacroAssembler::debugAssertCanonicalInt32(Register r) {
   8342 #  ifdef DEBUG
   8343  if (!js::jit::JitOptions.lessDebugCode) {
   8344 #    if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
   8345    Label ok;
   8346    branchPtr(Assembler::BelowOrEqual, r, ImmWord(UINT32_MAX), &ok);
   8347    breakpoint();
   8348    bind(&ok);
   8349 #    elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \
   8350        defined(JS_CODEGEN_RISCV64)
   8351    Label ok;
   8352    UseScratchRegisterScope temps(*this);
   8353    Register scratch = temps.Acquire();
   8354    move32SignExtendToPtr(r, scratch);
   8355    branchPtr(Assembler::Equal, r, scratch, &ok);
   8356    breakpoint();
   8357    bind(&ok);
   8358 #    else
   8359    MOZ_CRASH("IMPLEMENT ME");
   8360 #    endif
   8361  }
   8362 #  endif
   8363 }
   8364 #endif
   8365 
   8366 void MacroAssembler::memoryBarrierBefore(Synchronization sync) {
   8367  memoryBarrier(sync.barrierBefore);
   8368 }
   8369 
   8370 void MacroAssembler::memoryBarrierAfter(Synchronization sync) {
   8371  memoryBarrier(sync.barrierAfter);
   8372 }
   8373 
   8374 void MacroAssembler::convertDoubleToFloat16(FloatRegister src,
   8375                                            FloatRegister dest, Register temp,
   8376                                            LiveRegisterSet volatileLiveRegs) {
   8377  if (MacroAssembler::SupportsFloat64To16()) {
   8378    convertDoubleToFloat16(src, dest);
   8379 
   8380    // Float16 is currently passed as Float32, so expand again to Float32.
   8381    convertFloat16ToFloat32(dest, dest);
   8382    return;
   8383  }
   8384 
   8385  LiveRegisterSet save = volatileLiveRegs;
   8386  save.takeUnchecked(dest);
   8387  save.takeUnchecked(dest.asDouble());
   8388  save.takeUnchecked(temp);
   8389 
   8390  PushRegsInMask(save);
   8391 
   8392  using Fn = float (*)(double);
   8393  setupUnalignedABICall(temp);
   8394  passABIArg(src, ABIType::Float64);
   8395  callWithABI<Fn, jit::RoundFloat16ToFloat32>(ABIType::Float32);
   8396  storeCallFloatResult(dest);
   8397 
   8398  PopRegsInMask(save);
   8399 }
   8400 
   8401 void MacroAssembler::convertDoubleToFloat16(FloatRegister src,
   8402                                            FloatRegister dest, Register temp1,
   8403                                            Register temp2) {
   8404  MOZ_ASSERT(MacroAssembler::SupportsFloat64To16() ||
   8405             MacroAssembler::SupportsFloat32To16());
   8406  MOZ_ASSERT(src != dest);
   8407 
   8408  if (MacroAssembler::SupportsFloat64To16()) {
   8409    convertDoubleToFloat16(src, dest);
   8410 
   8411    // Float16 is currently passed as Float32, so expand again to Float32.
   8412    convertFloat16ToFloat32(dest, dest);
   8413    return;
   8414  }
   8415 
   8416  using Float32 = mozilla::FloatingPoint<float>;
   8417 
   8418 #ifdef DEBUG
   8419  static auto float32Bits = [](float16 f16) {
   8420    // Cast to float and reinterpret to bit representation.
   8421    return mozilla::BitwiseCast<Float32::Bits>(static_cast<float>(f16));
   8422  };
   8423 
   8424  static auto nextExponent = [](float16 f16, int32_t direction) {
   8425    constexpr auto kSignificandWidth = Float32::kSignificandWidth;
   8426 
   8427    // Shift out mantissa and then adjust exponent.
   8428    auto bits = float32Bits(f16);
   8429    return ((bits >> kSignificandWidth) + direction) << kSignificandWidth;
   8430  };
   8431 #endif
   8432 
   8433  // Float32 larger or equals to |overflow| are infinity (or NaN) in Float16.
   8434  constexpr uint32_t overflow = 0x4780'0000;
   8435  MOZ_ASSERT(overflow == nextExponent(std::numeric_limits<float16>::max(), 1));
   8436 
   8437  // Float32 smaller than |underflow| are zero in Float16.
   8438  constexpr uint32_t underflow = 0x3300'0000;
   8439  MOZ_ASSERT(underflow ==
   8440             nextExponent(std::numeric_limits<float16>::denorm_min(), -1));
   8441 
   8442  // Float32 larger or equals to |normal| are normal numbers in Float16.
   8443  constexpr uint32_t normal = 0x3880'0000;
   8444  MOZ_ASSERT(normal == float32Bits(std::numeric_limits<float16>::min()));
   8445 
   8446  // There are five possible cases to consider:
   8447  // 1. Non-finite (infinity and NaN)
   8448  // 2. Overflow to infinity
   8449  // 3. Normal numbers
   8450  // 4. Denormal numbers
   8451  // 5. Underflow to zero
   8452  //
   8453  // Cases 1-2 and 4-5 don't need separate code paths, so we only need to be
   8454  // concerned about incorrect double rounding for cases 3-4.
   8455  //
   8456  // Double rounding:
   8457  //
   8458  // Conversion from float64 -> float32 -> float16 can introduce double rounding
   8459  // errors when compared to a direct conversion float64 -> float16.
   8460  //
   8461  // Number of bits in the exponent and mantissa. These are relevant below.
   8462  //
   8463  //       exponent  mantissa
   8464  // -----------------------
   8465  // f16 |  5        10
   8466  // f32 |  8        23
   8467  // f64 | 11        52
   8468  //
   8469  // Examples:
   8470  //
   8471  // Input (f64): 0.0000610649585723877
   8472  // Bits (f64):  3f10'0200'0000'0000
   8473  // Bits (f32):  3880'1000
   8474  // Bits (f16):  0400
   8475  //
   8476  // Ignore the three left-most nibbles of the f64 bits (those are the sign and
   8477  // exponent). Shift the f64 mantissa right by (52 - 23) = 29 bits. The bits
   8478  // of the f32 mantissa are therefore 00'1000. Converting from f32 to f16 will
   8479  // right shift the mantissa by (23 - 10) = 13 bits. `001000 >> 13` is all
   8480  // zero. Directly converting from f64 to f16 right shifts the f64 mantissa by
   8481  // (52 - 10) = 42 bits. `0'0200'0000'0000 >> 42` is also all zero. So in this
   8482  // case no double rounding did take place.
   8483  //
   8484  // Input (f64): 0.00006106495857238771
   8485  // Bits (f64):  3f10'0200'0000'0001
   8486  // Bits (f32):  3880'1000
   8487  // Bits (f16):  0401
   8488  //
   8489  // The f64 to f32 conversion returns the same result 3880'1000 as in the first
   8490  // example, but the direct f64 to f16 conversion must return 0401. Let's look
   8491  // at the binary representation of the mantissa.
   8492  //
   8493  // Mantissa of 3f10'0200'0000'0001 in binary representation:
   8494  //
   8495  //                                          Low 32-bits
   8496  //                           __________________|__________________
   8497  //                          /                                     |
   8498  // 0000 0000 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
   8499  //            |               |
   8500  //            |               GRS
   8501  //            |               001
   8502  //            |
   8503  //            GRS  (G)uard bit
   8504  //            011  (R)ound bit
   8505  //                 (S)ticky bit
   8506  //
   8507  // The guard, round, and sticky bits control when to round: If the round bit
   8508  // is one and at least one of guard or sticky is one, then round up. The
   8509  // sticky bit is the or-ed value of all bits right of the round bit.
   8510  //
   8511  // When rounding to float16, GRS is 011, so we have to round up, whereas when
   8512  // rounding to float32, GRS is 001, so no rounding takes place.
   8513  //
   8514  // Mantissa of 3880'1000 in binary representation:
   8515  //
   8516  // e000 0000 0001 0000 0000 0000
   8517  //             |
   8518  //             GRS
   8519  //             010
   8520  //
   8521  // The round bit is set, but neither the guard nor sticky bit is set, so no
   8522  // rounding takes place for the f32 -> f16 conversion. We can attempt to
   8523  // recover the missing sticky bit from the f64 -> f16 conversion by looking at
   8524  // the low 32-bits of the f64 mantissa. If at least one bit is set in the
   8525  // low 32-bits (and the MSB is zero), then add one to the f32 mantissa.
   8526  // Modified mantissa now looks like:
   8527  //
   8528  // e000 0000 0001 0000 0000 0001
   8529  //             |
   8530  //             GRS
   8531  //             011
   8532  //
   8533  // GRS is now 011, so we round up and get the correctly rounded result 0401.
   8534  //
   8535  // Input (f64): 0.00006112456321716307
   8536  // Bits (f64):  3f10'05ff'ffff'ffff
   8537  // Bits (f32):  3880'3000
   8538  // Bits (f16):  0401
   8539  //
   8540  //                                          Low 32-bits
   8541  //                           __________________|__________________
   8542  //                          /                                     |
   8543  // 0000 0000 0101 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
   8544  //            |               |
   8545  //            |               GRS
   8546  //            |               111
   8547  //            |
   8548  //            GRS
   8549  //            101
   8550  //
   8551  // When rounding to float16, GRS is 101, so we don't round, whereas when
   8552  // rounding to float32, GRS is 111, so we have to round up.
   8553  //
   8554  // Mantissa of 3880'3000 in binary representation:
   8555  //
   8556  // e000 0000 0011 0000 0000 0000
   8557  //             |
   8558  //             GRS
   8559  //             110
   8560  //
   8561  // The guard and sticky bits are set, so the float32 -> float16 conversion
   8562  // incorrectly rounds up when compared to the direct float64 -> float16
   8563  // conversion. To avoid rounding twice we subtract one if the MSB of the low
   8564  // 32-bits of the f64 mantissa is set. Modified mantissa now looks like:
   8565  //
   8566  // e000 0000 0010 1111 1111 1111
   8567  //             |
   8568  //             GRS
   8569  //             101
   8570  //
   8571  // GRS is now 101, so we don't round and get the correct result 0401.
   8572  //
   8573  // Approach used to avoid double rounding:
   8574  //
   8575  // 1. For normal numbers, inspect the f32 mantissa and if its round bit is set
   8576  // and the sticky bits are all zero, then possibly adjust the f32 mantissa
   8577  // depending on the low 32-bits of the f64 mantissa.
   8578  //
   8579  // 2. For denormal numbers, possibly adjust the f32 mantissa if the round and
   8580  // sticky bits are all zero.
   8581 
   8582  // First round to float32 and reinterpret to bit representation.
   8583  convertDoubleToFloat32(src, dest);
   8584  moveFloat32ToGPR(dest, temp1);
   8585 
   8586  // Mask off sign bit to simplify range checks.
   8587  and32(Imm32(~Float32::kSignBit), temp1);
   8588 
   8589  Label done;
   8590 
   8591  // No changes necessary for underflow or overflow, including zero and
   8592  // non-finite numbers.
   8593  branch32(Assembler::Below, temp1, Imm32(underflow), &done);
   8594  branch32(Assembler::AboveOrEqual, temp1, Imm32(overflow), &done);
   8595 
   8596  // Compute 0x1000 for normal and 0x0000 for denormal numbers.
   8597  cmp32Set(Assembler::AboveOrEqual, temp1, Imm32(normal), temp2);
   8598  lshift32(Imm32(12), temp2);
   8599 
   8600  // Look at the last thirteen bits of the mantissa which will be shifted out
   8601  // when converting from float32 to float16. (The round and sticky bits.)
   8602  //
   8603  // Normal numbers: If the round bit is set and sticky bits are zero, then
   8604  // adjust the float32 mantissa.
   8605  // Denormal numbers: If all bits are zero, then adjust the mantissa.
   8606  and32(Imm32(0x1fff), temp1);
   8607  branch32(Assembler::NotEqual, temp1, temp2, &done);
   8608  {
   8609 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
   8610    // x86 can use SIMD instructions to avoid GPR<>XMM register moves.
   8611    ScratchSimd128Scope scratch(*this);
   8612 
   8613    int32_t one[] = {1, 0, 0, 0};
   8614    loadConstantSimd128(SimdConstant::CreateX4(one), scratch);
   8615 
   8616    // 1. If the low 32-bits of |src| are all zero, then set |scratch| to 0.
   8617    // 2. If the MSB of the low 32-bits is set, then set |scratch| to -1.
   8618    // 3. Otherwise set |scratch| to 1.
   8619    vpsignd(Operand(src), scratch, scratch);
   8620 
   8621    // Add |scratch| to the mantissa.
   8622    vpaddd(Operand(scratch), dest, dest);
   8623 #else
   8624    // Determine in which direction to round. When the low 32-bits are all zero,
   8625    // then we don't have to round.
   8626    moveLowDoubleToGPR(src, temp2);
   8627    branch32(Assembler::Equal, temp2, Imm32(0), &done);
   8628 
   8629    // Load either -1 or +1 into |temp2|.
   8630    rshift32Arithmetic(Imm32(31), temp2);
   8631    or32(Imm32(1), temp2);
   8632 
   8633    // Add or subtract one to the mantissa.
   8634    moveFloat32ToGPR(dest, temp1);
   8635    add32(temp2, temp1);
   8636    moveGPRToFloat32(temp1, dest);
   8637 #endif
   8638  }
   8639 
   8640  bind(&done);
   8641 
   8642  // Perform the actual float16 conversion.
   8643  convertFloat32ToFloat16(dest, dest);
   8644 
   8645  // Float16 is currently passed as Float32, so expand again to Float32.
   8646  convertFloat16ToFloat32(dest, dest);
   8647 }
   8648 
   8649 void MacroAssembler::convertFloat32ToFloat16(FloatRegister src,
   8650                                             FloatRegister dest, Register temp,
   8651                                             LiveRegisterSet volatileLiveRegs) {
   8652  if (MacroAssembler::SupportsFloat32To16()) {
   8653    convertFloat32ToFloat16(src, dest);
   8654 
   8655    // Float16 is currently passed as Float32, so expand again to Float32.
   8656    convertFloat16ToFloat32(dest, dest);
   8657    return;
   8658  }
   8659 
   8660  LiveRegisterSet save = volatileLiveRegs;
   8661  save.takeUnchecked(dest);
   8662  save.takeUnchecked(dest.asDouble());
   8663  save.takeUnchecked(temp);
   8664 
   8665  PushRegsInMask(save);
   8666 
   8667  using Fn = float (*)(float);
   8668  setupUnalignedABICall(temp);
   8669  passABIArg(src, ABIType::Float32);
   8670  callWithABI<Fn, jit::RoundFloat16ToFloat32>(ABIType::Float32);
   8671  storeCallFloatResult(dest);
   8672 
   8673  PopRegsInMask(save);
   8674 }
   8675 
   8676 void MacroAssembler::convertInt32ToFloat16(Register src, FloatRegister dest,
   8677                                           Register temp,
   8678                                           LiveRegisterSet volatileLiveRegs) {
   8679  if (MacroAssembler::SupportsFloat32To16()) {
   8680    convertInt32ToFloat16(src, dest);
   8681 
   8682    // Float16 is currently passed as Float32, so expand again to Float32.
   8683    convertFloat16ToFloat32(dest, dest);
   8684    return;
   8685  }
   8686 
   8687  LiveRegisterSet save = volatileLiveRegs;
   8688  save.takeUnchecked(dest);
   8689  save.takeUnchecked(dest.asDouble());
   8690  save.takeUnchecked(temp);
   8691 
   8692  PushRegsInMask(save);
   8693 
   8694  using Fn = float (*)(int32_t);
   8695  setupUnalignedABICall(temp);
   8696  passABIArg(src);
   8697  callWithABI<Fn, jit::RoundFloat16ToFloat32>(ABIType::Float32);
   8698  storeCallFloatResult(dest);
   8699 
   8700  PopRegsInMask(save);
   8701 }
   8702 
   8703 template <typename T>
   8704 void MacroAssembler::loadFloat16(const T& src, FloatRegister dest,
   8705                                 Register temp1, Register temp2,
   8706                                 LiveRegisterSet volatileLiveRegs) {
   8707  if (MacroAssembler::SupportsFloat32To16()) {
   8708    loadFloat16(src, dest, temp1);
   8709 
   8710    // Float16 is currently passed as Float32, so expand again to Float32.
   8711    convertFloat16ToFloat32(dest, dest);
   8712    return;
   8713  }
   8714 
   8715  load16ZeroExtend(src, temp1);
   8716 
   8717  LiveRegisterSet save = volatileLiveRegs;
   8718  save.takeUnchecked(dest);
   8719  save.takeUnchecked(dest.asDouble());
   8720  save.takeUnchecked(temp1);
   8721  save.takeUnchecked(temp2);
   8722 
   8723  PushRegsInMask(save);
   8724 
   8725  using Fn = float (*)(int32_t);
   8726  setupUnalignedABICall(temp2);
   8727  passABIArg(temp1);
   8728  callWithABI<Fn, jit::Float16ToFloat32>(ABIType::Float32);
   8729  storeCallFloatResult(dest);
   8730 
   8731  PopRegsInMask(save);
   8732 }
   8733 
   8734 template void MacroAssembler::loadFloat16(const Address& src,
   8735                                          FloatRegister dest, Register temp1,
   8736                                          Register temp2,
   8737                                          LiveRegisterSet volatileLiveRegs);
   8738 
   8739 template void MacroAssembler::loadFloat16(const BaseIndex& src,
   8740                                          FloatRegister dest, Register temp1,
   8741                                          Register temp2,
   8742                                          LiveRegisterSet volatileLiveRegs);
   8743 
   8744 template <typename T>
   8745 void MacroAssembler::storeFloat16(FloatRegister src, const T& dest,
   8746                                  Register temp,
   8747                                  LiveRegisterSet volatileLiveRegs) {
   8748  ScratchFloat32Scope fpscratch(*this);
   8749 
   8750  if (src.isDouble()) {
   8751    if (MacroAssembler::SupportsFloat64To16()) {
   8752      convertDoubleToFloat16(src, fpscratch);
   8753      storeFloat16(fpscratch, dest, temp);
   8754      return;
   8755    }
   8756 
   8757    convertDoubleToFloat16(src, fpscratch, temp, volatileLiveRegs);
   8758    src = fpscratch;
   8759  }
   8760  MOZ_ASSERT(src.isSingle());
   8761 
   8762  if (MacroAssembler::SupportsFloat32To16()) {
   8763    convertFloat32ToFloat16(src, fpscratch);
   8764    storeFloat16(fpscratch, dest, temp);
   8765    return;
   8766  }
   8767 
   8768  moveFloat16ToGPR(src, temp, volatileLiveRegs);
   8769  store16(temp, dest);
   8770 }
   8771 
   8772 template void MacroAssembler::storeFloat16(FloatRegister src,
   8773                                           const Address& dest, Register temp,
   8774                                           LiveRegisterSet volatileLiveRegs);
   8775 
   8776 template void MacroAssembler::storeFloat16(FloatRegister src,
   8777                                           const BaseIndex& dest, Register temp,
   8778                                           LiveRegisterSet volatileLiveRegs);
   8779 
   8780 void MacroAssembler::moveFloat16ToGPR(FloatRegister src, Register dest,
   8781                                      LiveRegisterSet volatileLiveRegs) {
   8782  if (MacroAssembler::SupportsFloat32To16()) {
   8783    ScratchFloat32Scope fpscratch(*this);
   8784 
   8785    // Float16 is currently passed as Float32, so first narrow to Float16.
   8786    convertFloat32ToFloat16(src, fpscratch);
   8787 
   8788    moveFloat16ToGPR(fpscratch, dest);
   8789    return;
   8790  }
   8791 
   8792  LiveRegisterSet save = volatileLiveRegs;
   8793  save.takeUnchecked(dest);
   8794 
   8795  PushRegsInMask(save);
   8796 
   8797  using Fn = int32_t (*)(float);
   8798  setupUnalignedABICall(dest);
   8799  passABIArg(src, ABIType::Float32);
   8800  callWithABI<Fn, jit::Float32ToFloat16>();
   8801  storeCallInt32Result(dest);
   8802 
   8803  PopRegsInMask(save);
   8804 }
   8805 
   8806 void MacroAssembler::moveGPRToFloat16(Register src, FloatRegister dest,
   8807                                      Register temp,
   8808                                      LiveRegisterSet volatileLiveRegs) {
   8809  if (MacroAssembler::SupportsFloat32To16()) {
   8810    moveGPRToFloat16(src, dest);
   8811 
   8812    // Float16 is currently passed as Float32, so expand again to Float32.
   8813    convertFloat16ToFloat32(dest, dest);
   8814    return;
   8815  }
   8816 
   8817  LiveRegisterSet save = volatileLiveRegs;
   8818  save.takeUnchecked(dest);
   8819  save.takeUnchecked(dest.asDouble());
   8820  save.takeUnchecked(temp);
   8821 
   8822  PushRegsInMask(save);
   8823 
   8824  using Fn = float (*)(int32_t);
   8825  setupUnalignedABICall(temp);
   8826  passABIArg(src);
   8827  callWithABI<Fn, jit::Float16ToFloat32>(ABIType::Float32);
   8828  storeCallFloatResult(dest);
   8829 
   8830  PopRegsInMask(save);
   8831 }
   8832 
   8833 void MacroAssembler::debugAssertIsObject(const ValueOperand& val) {
   8834 #ifdef DEBUG
   8835  Label ok;
   8836  branchTestObject(Assembler::Equal, val, &ok);
   8837  assumeUnreachable("Expected an object!");
   8838  bind(&ok);
   8839 #endif
   8840 }
   8841 
   8842 void MacroAssembler::debugAssertObjHasFixedSlots(Register obj,
   8843                                                 Register scratch) {
   8844 #ifdef DEBUG
   8845  Label hasFixedSlots;
   8846  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   8847  branchTest32(Assembler::NonZero,
   8848               Address(scratch, Shape::offsetOfImmutableFlags()),
   8849               Imm32(NativeShape::fixedSlotsMask()), &hasFixedSlots);
   8850  assumeUnreachable("Expected a fixed slot");
   8851  bind(&hasFixedSlots);
   8852 #endif
   8853 }
   8854 
   8855 void MacroAssembler::debugAssertObjectHasClass(Register obj, Register scratch,
   8856                                               const JSClass* clasp) {
   8857 #ifdef DEBUG
   8858  Label done;
   8859  branchTestObjClassNoSpectreMitigations(Assembler::Equal, obj, clasp, scratch,
   8860                                         &done);
   8861  assumeUnreachable("Class check failed");
   8862  bind(&done);
   8863 #endif
   8864 }
   8865 
   8866 void MacroAssembler::debugAssertGCThingIsTenured(Register ptr, Register temp) {
   8867 #ifdef DEBUG
   8868  Label done;
   8869  branchPtrInNurseryChunk(Assembler::NotEqual, ptr, temp, &done);
   8870  assumeUnreachable("Expected a tenured pointer");
   8871  bind(&done);
   8872 #endif
   8873 }
   8874 
   8875 void MacroAssembler::branchArrayIsNotPacked(Register array, Register temp1,
   8876                                            Register temp2, Label* label) {
   8877  loadPtr(Address(array, NativeObject::offsetOfElements()), temp1);
   8878 
   8879  // Test length == initializedLength.
   8880  Address initLength(temp1, ObjectElements::offsetOfInitializedLength());
   8881  load32(Address(temp1, ObjectElements::offsetOfLength()), temp2);
   8882  branch32(Assembler::NotEqual, initLength, temp2, label);
   8883 
   8884  // Test the NON_PACKED flag.
   8885  Address flags(temp1, ObjectElements::offsetOfFlags());
   8886  branchTest32(Assembler::NonZero, flags, Imm32(ObjectElements::NON_PACKED),
   8887               label);
   8888 }
   8889 
   8890 void MacroAssembler::setIsPackedArray(Register obj, Register output,
   8891                                      Register temp) {
   8892  // Ensure it's an ArrayObject.
   8893  Label notPackedArray;
   8894  branchTestObjClass(Assembler::NotEqual, obj, &ArrayObject::class_, temp, obj,
   8895                     &notPackedArray);
   8896 
   8897  branchArrayIsNotPacked(obj, temp, output, &notPackedArray);
   8898 
   8899  Label done;
   8900  move32(Imm32(1), output);
   8901  jump(&done);
   8902 
   8903  bind(&notPackedArray);
   8904  move32(Imm32(0), output);
   8905 
   8906  bind(&done);
   8907 }
   8908 
   8909 void MacroAssembler::packedArrayPop(Register array, ValueOperand output,
   8910                                    Register temp1, Register temp2,
   8911                                    Label* fail) {
   8912  // Load obj->elements in temp1.
   8913  loadPtr(Address(array, NativeObject::offsetOfElements()), temp1);
   8914 
   8915  // Check flags.
   8916  static constexpr uint32_t UnhandledFlags =
   8917      ObjectElements::Flags::NON_PACKED |
   8918      ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH |
   8919      ObjectElements::Flags::NOT_EXTENSIBLE |
   8920      ObjectElements::Flags::MAYBE_IN_ITERATION;
   8921  Address flags(temp1, ObjectElements::offsetOfFlags());
   8922  branchTest32(Assembler::NonZero, flags, Imm32(UnhandledFlags), fail);
   8923 
   8924  // Load length in temp2. Ensure length == initializedLength.
   8925  Address lengthAddr(temp1, ObjectElements::offsetOfLength());
   8926  Address initLengthAddr(temp1, ObjectElements::offsetOfInitializedLength());
   8927  load32(lengthAddr, temp2);
   8928  branch32(Assembler::NotEqual, initLengthAddr, temp2, fail);
   8929 
   8930  // Result is |undefined| if length == 0.
   8931  Label notEmpty, done;
   8932  branchTest32(Assembler::NonZero, temp2, temp2, &notEmpty);
   8933  {
   8934    moveValue(UndefinedValue(), output);
   8935    jump(&done);
   8936  }
   8937 
   8938  bind(&notEmpty);
   8939 
   8940  // Load the last element.
   8941  sub32(Imm32(1), temp2);
   8942  BaseObjectElementIndex elementAddr(temp1, temp2);
   8943  loadValue(elementAddr, output);
   8944 
   8945  // Pre-barrier the element because we're removing it from the array.
   8946  EmitPreBarrier(*this, elementAddr, MIRType::Value);
   8947 
   8948  // Update length and initializedLength.
   8949  store32(temp2, lengthAddr);
   8950  store32(temp2, initLengthAddr);
   8951 
   8952  bind(&done);
   8953 }
   8954 
   8955 void MacroAssembler::packedArrayShift(Register array, ValueOperand output,
   8956                                      Register temp1, Register temp2,
   8957                                      LiveRegisterSet volatileRegs,
   8958                                      Label* fail) {
   8959  // Load obj->elements in temp1.
   8960  loadPtr(Address(array, NativeObject::offsetOfElements()), temp1);
   8961 
   8962  // Check flags.
   8963  static constexpr uint32_t UnhandledFlags =
   8964      ObjectElements::Flags::NON_PACKED |
   8965      ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH |
   8966      ObjectElements::Flags::NOT_EXTENSIBLE |
   8967      ObjectElements::Flags::MAYBE_IN_ITERATION;
   8968  Address flags(temp1, ObjectElements::offsetOfFlags());
   8969  branchTest32(Assembler::NonZero, flags, Imm32(UnhandledFlags), fail);
   8970 
   8971  // Load length in temp2. Ensure length == initializedLength.
   8972  Address lengthAddr(temp1, ObjectElements::offsetOfLength());
   8973  Address initLengthAddr(temp1, ObjectElements::offsetOfInitializedLength());
   8974  load32(lengthAddr, temp2);
   8975  branch32(Assembler::NotEqual, initLengthAddr, temp2, fail);
   8976 
   8977  // Result is |undefined| if length == 0.
   8978  Label notEmpty, done;
   8979  branchTest32(Assembler::NonZero, temp2, temp2, &notEmpty);
   8980  {
   8981    moveValue(UndefinedValue(), output);
   8982    jump(&done);
   8983  }
   8984 
   8985  bind(&notEmpty);
   8986 
   8987  // Load the first element.
   8988  Address elementAddr(temp1, 0);
   8989  loadValue(elementAddr, output);
   8990 
   8991  // Move the other elements and update the initializedLength/length. This will
   8992  // also trigger pre-barriers.
   8993  {
   8994    // Ensure output is in volatileRegs. Don't preserve temp1 and temp2.
   8995    volatileRegs.takeUnchecked(temp1);
   8996    volatileRegs.takeUnchecked(temp2);
   8997    if (output.hasVolatileReg()) {
   8998      volatileRegs.addUnchecked(output);
   8999    }
   9000 
   9001    PushRegsInMask(volatileRegs);
   9002 
   9003    using Fn = void (*)(ArrayObject* arr);
   9004    setupUnalignedABICall(temp1);
   9005    passABIArg(array);
   9006    callWithABI<Fn, ArrayShiftMoveElements>();
   9007 
   9008    PopRegsInMask(volatileRegs);
   9009  }
   9010 
   9011  bind(&done);
   9012 }
   9013 
   9014 void MacroAssembler::loadArgumentsObjectElement(Register obj, Register index,
   9015                                                ValueOperand output,
   9016                                                Register temp, Label* fail) {
   9017  Register temp2 = output.scratchReg();
   9018 
   9019  // Get initial length value.
   9020  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), temp);
   9021 
   9022  // Ensure no overridden elements.
   9023  branchTest32(Assembler::NonZero, temp,
   9024               Imm32(ArgumentsObject::ELEMENT_OVERRIDDEN_BIT), fail);
   9025 
   9026  // Bounds check.
   9027  rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), temp);
   9028  spectreBoundsCheck32(index, temp, temp2, fail);
   9029 
   9030  // Load ArgumentsData.
   9031  loadPrivate(Address(obj, ArgumentsObject::getDataSlotOffset()), temp);
   9032 
   9033  // Guard the argument is not a FORWARD_TO_CALL_SLOT MagicValue.
   9034  BaseValueIndex argValue(temp, index, ArgumentsData::offsetOfArgs());
   9035  branchTestMagic(Assembler::Equal, argValue, fail);
   9036  loadValue(argValue, output);
   9037 }
   9038 
   9039 void MacroAssembler::loadArgumentsObjectElementHole(Register obj,
   9040                                                    Register index,
   9041                                                    ValueOperand output,
   9042                                                    Register temp,
   9043                                                    Label* fail) {
   9044  Register temp2 = output.scratchReg();
   9045 
   9046  // Get initial length value.
   9047  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), temp);
   9048 
   9049  // Ensure no overridden elements.
   9050  branchTest32(Assembler::NonZero, temp,
   9051               Imm32(ArgumentsObject::ELEMENT_OVERRIDDEN_BIT), fail);
   9052 
   9053  // Bounds check.
   9054  Label outOfBounds, done;
   9055  rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), temp);
   9056  spectreBoundsCheck32(index, temp, temp2, &outOfBounds);
   9057 
   9058  // Load ArgumentsData.
   9059  loadPrivate(Address(obj, ArgumentsObject::getDataSlotOffset()), temp);
   9060 
   9061  // Guard the argument is not a FORWARD_TO_CALL_SLOT MagicValue.
   9062  BaseValueIndex argValue(temp, index, ArgumentsData::offsetOfArgs());
   9063  branchTestMagic(Assembler::Equal, argValue, fail);
   9064  loadValue(argValue, output);
   9065  jump(&done);
   9066 
   9067  bind(&outOfBounds);
   9068  branch32(Assembler::LessThan, index, Imm32(0), fail);
   9069  moveValue(UndefinedValue(), output);
   9070 
   9071  bind(&done);
   9072 }
   9073 
   9074 void MacroAssembler::loadArgumentsObjectElementExists(
   9075    Register obj, Register index, Register output, Register temp, Label* fail) {
   9076  // Ensure the index is non-negative.
   9077  branch32(Assembler::LessThan, index, Imm32(0), fail);
   9078 
   9079  // Get initial length value.
   9080  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), temp);
   9081 
   9082  // Ensure no overridden or deleted elements.
   9083  branchTest32(Assembler::NonZero, temp,
   9084               Imm32(ArgumentsObject::ELEMENT_OVERRIDDEN_BIT), fail);
   9085 
   9086  // Compare index against the length.
   9087  rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), temp);
   9088  cmp32Set(Assembler::LessThan, index, temp, output);
   9089 }
   9090 
   9091 void MacroAssembler::loadArgumentsObjectLength(Register obj, Register output,
   9092                                               Label* fail) {
   9093  // Get initial length value.
   9094  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()),
   9095             output);
   9096 
   9097  // Test if length has been overridden.
   9098  branchTest32(Assembler::NonZero, output,
   9099               Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), fail);
   9100 
   9101  // Shift out arguments length and return it.
   9102  rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), output);
   9103 }
   9104 
   9105 void MacroAssembler::loadArgumentsObjectLength(Register obj, Register output) {
   9106  // Get initial length value.
   9107  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()),
   9108             output);
   9109 
   9110 #ifdef DEBUG
   9111  // Assert length hasn't been overridden.
   9112  Label ok;
   9113  branchTest32(Assembler::Zero, output,
   9114               Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), &ok);
   9115  assumeUnreachable("arguments object length has been overridden");
   9116  bind(&ok);
   9117 #endif
   9118 
   9119  // Shift out arguments length and return it.
   9120  rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), output);
   9121 }
   9122 
   9123 void MacroAssembler::branchTestArgumentsObjectFlags(Register obj, Register temp,
   9124                                                    uint32_t flags,
   9125                                                    Condition cond,
   9126                                                    Label* label) {
   9127  MOZ_ASSERT((flags & ~ArgumentsObject::PACKED_BITS_MASK) == 0);
   9128 
   9129  // Get initial length value.
   9130  unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), temp);
   9131 
   9132  // Test flags.
   9133  branchTest32(cond, temp, Imm32(flags), label);
   9134 }
   9135 
   9136 static constexpr bool ValidateSizeRange(Scalar::Type from, Scalar::Type to) {
   9137  for (Scalar::Type type = from; type < to; type = Scalar::Type(type + 1)) {
   9138    if (TypedArrayElemSize(type) != TypedArrayElemSize(from)) {
   9139      return false;
   9140    }
   9141  }
   9142  return true;
   9143 }
   9144 
   9145 void MacroAssembler::typedArrayElementSize(Register obj, Register output) {
   9146  static_assert(std::end(TypedArrayObject::fixedLengthClasses) ==
   9147                        std::begin(TypedArrayObject::immutableClasses) &&
   9148                    std::end(TypedArrayObject::immutableClasses) ==
   9149                        std::begin(TypedArrayObject::resizableClasses),
   9150                "TypedArray classes are in contiguous memory");
   9151 
   9152  // Constexpr subtraction requires using elements of the same array, so we have
   9153  // to use `std::end` instead of `std::begin`. We still get the right results,
   9154  // because the classes are in contiguous memory, as asserted above.
   9155  constexpr ptrdiff_t diffFirstImmutableToFirstFixedLength =
   9156      std::end(TypedArrayObject::fixedLengthClasses) -
   9157      std::begin(TypedArrayObject::fixedLengthClasses);
   9158  constexpr ptrdiff_t diffFirstResizableToFirstImmutable =
   9159      std::end(TypedArrayObject::immutableClasses) -
   9160      std::begin(TypedArrayObject::immutableClasses);
   9161  constexpr ptrdiff_t diffFirstResizableToFirstFixedLength =
   9162      diffFirstResizableToFirstImmutable + diffFirstImmutableToFirstFixedLength;
   9163 
   9164  loadObjClassUnsafe(obj, output);
   9165 
   9166  // Map immutable and resizable to fixed-length TypedArray classes.
   9167  Label fixedLength, immutable;
   9168  branchPtr(Assembler::Below, output,
   9169            ImmPtr(std::end(TypedArrayObject::fixedLengthClasses)),
   9170            &fixedLength);
   9171  branchPtr(Assembler::Below, output,
   9172            ImmPtr(std::end(TypedArrayObject::immutableClasses)), &immutable);
   9173  {
   9174    // NB: constexpr evaluation doesn't allow overflow, so the difference is
   9175    // guaranteed to fit into an int32.
   9176    constexpr int32_t diff = static_cast<int32_t>(
   9177        diffFirstResizableToFirstFixedLength * sizeof(JSClass));
   9178 
   9179    subPtr(Imm32(diff), output);
   9180    jump(&fixedLength);
   9181  }
   9182  bind(&immutable);
   9183  {
   9184    // NB: constexpr evaluation doesn't allow overflow, so the difference is
   9185    // guaranteed to fit into an int32.
   9186    constexpr int32_t diff = static_cast<int32_t>(
   9187        diffFirstImmutableToFirstFixedLength * sizeof(JSClass));
   9188 
   9189    subPtr(Imm32(diff), output);
   9190  }
   9191  bind(&fixedLength);
   9192 
   9193 #ifdef DEBUG
   9194  Label invalidClass, validClass;
   9195  branchPtr(Assembler::Below, output,
   9196            ImmPtr(std::begin(TypedArrayObject::fixedLengthClasses)),
   9197            &invalidClass);
   9198  branchPtr(Assembler::Below, output,
   9199            ImmPtr(std::end(TypedArrayObject::fixedLengthClasses)),
   9200            &validClass);
   9201  bind(&invalidClass);
   9202  assumeUnreachable("value isn't a valid FixedLengthTypedArray class");
   9203  bind(&validClass);
   9204 #endif
   9205 
   9206  auto classForType = [](Scalar::Type type) {
   9207    MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
   9208    return &TypedArrayObject::fixedLengthClasses[type];
   9209  };
   9210 
   9211  Label one, two, four, eight, done;
   9212 
   9213  static_assert(ValidateSizeRange(Scalar::Int8, Scalar::Int16),
   9214                "element size is one in [Int8, Int16)");
   9215  branchPtr(Assembler::Below, output, ImmPtr(classForType(Scalar::Int16)),
   9216            &one);
   9217 
   9218  static_assert(ValidateSizeRange(Scalar::Int16, Scalar::Int32),
   9219                "element size is two in [Int16, Int32)");
   9220  branchPtr(Assembler::Below, output, ImmPtr(classForType(Scalar::Int32)),
   9221            &two);
   9222 
   9223  static_assert(ValidateSizeRange(Scalar::Int32, Scalar::Float64),
   9224                "element size is four in [Int32, Float64)");
   9225  branchPtr(Assembler::Below, output, ImmPtr(classForType(Scalar::Float64)),
   9226            &four);
   9227 
   9228  static_assert(ValidateSizeRange(Scalar::Float64, Scalar::Uint8Clamped),
   9229                "element size is eight in [Float64, Uint8Clamped)");
   9230  branchPtr(Assembler::Below, output,
   9231            ImmPtr(classForType(Scalar::Uint8Clamped)), &eight);
   9232 
   9233  static_assert(ValidateSizeRange(Scalar::Uint8Clamped, Scalar::BigInt64),
   9234                "element size is one in [Uint8Clamped, BigInt64)");
   9235  branchPtr(Assembler::Below, output, ImmPtr(classForType(Scalar::BigInt64)),
   9236            &one);
   9237 
   9238  static_assert(ValidateSizeRange(Scalar::BigInt64, Scalar::Float16),
   9239                "element size is eight in [BigInt64, Float16)");
   9240  branchPtr(Assembler::Below, output, ImmPtr(classForType(Scalar::Float16)),
   9241            &eight);
   9242 
   9243  static_assert(
   9244      ValidateSizeRange(Scalar::Float16, Scalar::MaxTypedArrayViewType),
   9245      "element size is two in [Float16, MaxTypedArrayViewType)");
   9246  jump(&two);
   9247 
   9248  bind(&eight);
   9249  move32(Imm32(8), output);
   9250  jump(&done);
   9251 
   9252  bind(&four);
   9253  move32(Imm32(4), output);
   9254  jump(&done);
   9255 
   9256  bind(&two);
   9257  move32(Imm32(2), output);
   9258  jump(&done);
   9259 
   9260  bind(&one);
   9261  move32(Imm32(1), output);
   9262 
   9263  bind(&done);
   9264 }
   9265 
   9266 void MacroAssembler::resizableTypedArrayElementShiftBy(Register obj,
   9267                                                       Register output,
   9268                                                       Register scratch) {
   9269  loadObjClassUnsafe(obj, scratch);
   9270 
   9271 #ifdef DEBUG
   9272  Label invalidClass, validClass;
   9273  branchPtr(Assembler::Below, scratch,
   9274            ImmPtr(std::begin(TypedArrayObject::resizableClasses)),
   9275            &invalidClass);
   9276  branchPtr(Assembler::Below, scratch,
   9277            ImmPtr(std::end(TypedArrayObject::resizableClasses)), &validClass);
   9278  bind(&invalidClass);
   9279  assumeUnreachable("value isn't a valid ResizableLengthTypedArray class");
   9280  bind(&validClass);
   9281 #endif
   9282 
   9283  auto classForType = [](Scalar::Type type) {
   9284    MOZ_ASSERT(type < Scalar::MaxTypedArrayViewType);
   9285    return &TypedArrayObject::resizableClasses[type];
   9286  };
   9287 
   9288  Label zero, one, two, three;
   9289 
   9290  static_assert(ValidateSizeRange(Scalar::Int8, Scalar::Int16),
   9291                "element shift is zero in [Int8, Int16)");
   9292  branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Int16)),
   9293            &zero);
   9294 
   9295  static_assert(ValidateSizeRange(Scalar::Int16, Scalar::Int32),
   9296                "element shift is one in [Int16, Int32)");
   9297  branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Int32)),
   9298            &one);
   9299 
   9300  static_assert(ValidateSizeRange(Scalar::Int32, Scalar::Float64),
   9301                "element shift is two in [Int32, Float64)");
   9302  branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Float64)),
   9303            &two);
   9304 
   9305  static_assert(ValidateSizeRange(Scalar::Float64, Scalar::Uint8Clamped),
   9306                "element shift is three in [Float64, Uint8Clamped)");
   9307  branchPtr(Assembler::Below, scratch,
   9308            ImmPtr(classForType(Scalar::Uint8Clamped)), &three);
   9309 
   9310  static_assert(ValidateSizeRange(Scalar::Uint8Clamped, Scalar::BigInt64),
   9311                "element shift is zero in [Uint8Clamped, BigInt64)");
   9312  branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::BigInt64)),
   9313            &zero);
   9314 
   9315  static_assert(ValidateSizeRange(Scalar::BigInt64, Scalar::Float16),
   9316                "element shift is three in [BigInt64, Float16)");
   9317  branchPtr(Assembler::Below, scratch, ImmPtr(classForType(Scalar::Float16)),
   9318            &three);
   9319 
   9320  static_assert(
   9321      ValidateSizeRange(Scalar::Float16, Scalar::MaxTypedArrayViewType),
   9322      "element shift is one in [Float16, MaxTypedArrayViewType)");
   9323  jump(&one);
   9324 
   9325  bind(&three);
   9326  rshiftPtr(Imm32(3), output);
   9327  jump(&zero);
   9328 
   9329  bind(&two);
   9330  rshiftPtr(Imm32(2), output);
   9331  jump(&zero);
   9332 
   9333  bind(&one);
   9334  rshiftPtr(Imm32(1), output);
   9335 
   9336  bind(&zero);
   9337 }
   9338 
   9339 void MacroAssembler::branchIfClassIsNotTypedArray(Register clasp,
   9340                                                  Label* notTypedArray) {
   9341  // Inline implementation of IsTypedArrayClass().
   9342 
   9343  const auto* firstTypedArrayClass =
   9344      std::begin(TypedArrayObject::fixedLengthClasses);
   9345  const auto* lastTypedArrayClass =
   9346      std::prev(std::end(TypedArrayObject::resizableClasses));
   9347  MOZ_ASSERT(std::end(TypedArrayObject::fixedLengthClasses) ==
   9348                     std::begin(TypedArrayObject::immutableClasses) &&
   9349                 std::end(TypedArrayObject::immutableClasses) ==
   9350                     std::begin(TypedArrayObject::resizableClasses),
   9351             "TypedArray classes are in contiguous memory");
   9352 
   9353  branchPtr(Assembler::Below, clasp, ImmPtr(firstTypedArrayClass),
   9354            notTypedArray);
   9355  branchPtr(Assembler::Above, clasp, ImmPtr(lastTypedArrayClass),
   9356            notTypedArray);
   9357 }
   9358 
   9359 void MacroAssembler::branchIfClassIsNotNonResizableTypedArray(
   9360    Register clasp, Label* notTypedArray) {
   9361  // Inline implementation of IsFixedLengthTypedArrayClass() and
   9362  // IsImmutableTypedArrayClass().
   9363 
   9364  const auto* firstTypedArrayClass =
   9365      std::begin(TypedArrayObject::fixedLengthClasses);
   9366  const auto* lastTypedArrayClass =
   9367      std::prev(std::end(TypedArrayObject::immutableClasses));
   9368  MOZ_ASSERT(std::end(TypedArrayObject::fixedLengthClasses) ==
   9369                 std::begin(TypedArrayObject::immutableClasses),
   9370             "TypedArray classes are in contiguous memory");
   9371 
   9372  branchPtr(Assembler::Below, clasp, ImmPtr(firstTypedArrayClass),
   9373            notTypedArray);
   9374  branchPtr(Assembler::Above, clasp, ImmPtr(lastTypedArrayClass),
   9375            notTypedArray);
   9376 }
   9377 
   9378 void MacroAssembler::branchIfClassIsNotResizableTypedArray(
   9379    Register clasp, Label* notTypedArray) {
   9380  // Inline implementation of IsResizableTypedArrayClass().
   9381 
   9382  const auto* firstTypedArrayClass =
   9383      std::begin(TypedArrayObject::resizableClasses);
   9384  const auto* lastTypedArrayClass =
   9385      std::prev(std::end(TypedArrayObject::resizableClasses));
   9386 
   9387  branchPtr(Assembler::Below, clasp, ImmPtr(firstTypedArrayClass),
   9388            notTypedArray);
   9389  branchPtr(Assembler::Above, clasp, ImmPtr(lastTypedArrayClass),
   9390            notTypedArray);
   9391 }
   9392 
   9393 void MacroAssembler::branchIfIsNotArrayBuffer(Register obj, Register temp,
   9394                                              Label* label) {
   9395  Label ok;
   9396 
   9397  loadObjClassUnsafe(obj, temp);
   9398 
   9399  branchPtr(Assembler::Equal, temp,
   9400            ImmPtr(&FixedLengthArrayBufferObject::class_), &ok);
   9401  branchPtr(Assembler::Equal, temp, ImmPtr(&ResizableArrayBufferObject::class_),
   9402            &ok);
   9403  branchPtr(Assembler::NotEqual, temp,
   9404            ImmPtr(&ImmutableArrayBufferObject::class_), label);
   9405 
   9406  bind(&ok);
   9407 
   9408  if (JitOptions.spectreObjectMitigations) {
   9409    spectreZeroRegister(Assembler::NotEqual, temp, obj);
   9410  }
   9411 }
   9412 
   9413 void MacroAssembler::branchIfIsNotSharedArrayBuffer(Register obj, Register temp,
   9414                                                    Label* label) {
   9415  Label ok;
   9416 
   9417  loadObjClassUnsafe(obj, temp);
   9418 
   9419  branchPtr(Assembler::Equal, temp,
   9420            ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &ok);
   9421  branchPtr(Assembler::NotEqual, temp,
   9422            ImmPtr(&GrowableSharedArrayBufferObject::class_), label);
   9423 
   9424  bind(&ok);
   9425 
   9426  if (JitOptions.spectreObjectMitigations) {
   9427    spectreZeroRegister(Assembler::NotEqual, temp, obj);
   9428  }
   9429 }
   9430 
   9431 void MacroAssembler::branchIfIsArrayBufferMaybeShared(Register obj,
   9432                                                      Register temp,
   9433                                                      Label* label) {
   9434  loadObjClassUnsafe(obj, temp);
   9435 
   9436  branchPtr(Assembler::Equal, temp,
   9437            ImmPtr(&FixedLengthArrayBufferObject::class_), label);
   9438  branchPtr(Assembler::Equal, temp,
   9439            ImmPtr(&FixedLengthSharedArrayBufferObject::class_), label);
   9440  branchPtr(Assembler::Equal, temp, ImmPtr(&ResizableArrayBufferObject::class_),
   9441            label);
   9442  branchPtr(Assembler::Equal, temp,
   9443            ImmPtr(&GrowableSharedArrayBufferObject::class_), label);
   9444  branchPtr(Assembler::Equal, temp, ImmPtr(&ImmutableArrayBufferObject::class_),
   9445            label);
   9446 }
   9447 
   9448 void MacroAssembler::branchIfHasDetachedArrayBuffer(BranchIfDetached branchIf,
   9449                                                    Register obj, Register temp,
   9450                                                    Label* label) {
   9451  // Inline implementation of ArrayBufferViewObject::hasDetachedBuffer().
   9452 
   9453  // TODO: The data-slot of detached views is set to undefined, which would be
   9454  // a faster way to detect detached buffers.
   9455 
   9456  // auto cond = branchIf == BranchIfDetached::Yes ? Assembler::Equal
   9457  //                                               : Assembler::NotEqual;
   9458  // branchTestUndefined(cond, Address(obj,
   9459  //                     ArrayBufferViewObject::dataOffset()), label);
   9460 
   9461  Label done;
   9462  Label* ifNotDetached = branchIf == BranchIfDetached::Yes ? &done : label;
   9463  Condition detachedCond =
   9464      branchIf == BranchIfDetached::Yes ? Assembler::NonZero : Assembler::Zero;
   9465 
   9466  // Load obj->elements in temp.
   9467  loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
   9468 
   9469  // Shared buffers can't be detached.
   9470  branchTest32(Assembler::NonZero,
   9471               Address(temp, ObjectElements::offsetOfFlags()),
   9472               Imm32(ObjectElements::SHARED_MEMORY), ifNotDetached);
   9473 
   9474  // An ArrayBufferView with a null/true buffer has never had its buffer
   9475  // exposed, so nothing can possibly detach it.
   9476  fallibleUnboxObject(Address(obj, ArrayBufferViewObject::bufferOffset()), temp,
   9477                      ifNotDetached);
   9478 
   9479  // Load the ArrayBuffer flags and branch if the detached flag is (not) set.
   9480  unboxInt32(Address(temp, ArrayBufferObject::offsetOfFlagsSlot()), temp);
   9481  branchTest32(detachedCond, temp, Imm32(ArrayBufferObject::DETACHED), label);
   9482 
   9483  if (branchIf == BranchIfDetached::Yes) {
   9484    bind(&done);
   9485  }
   9486 }
   9487 
   9488 void MacroAssembler::branchIfResizableArrayBufferViewOutOfBounds(Register obj,
   9489                                                                 Register temp,
   9490                                                                 Label* label) {
   9491  // Implementation of ArrayBufferViewObject::isOutOfBounds().
   9492 
   9493  Label done;
   9494 
   9495  loadArrayBufferViewLengthIntPtr(obj, temp);
   9496  branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done);
   9497 
   9498  loadArrayBufferViewByteOffsetIntPtr(obj, temp);
   9499  branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done);
   9500 
   9501  loadPrivate(Address(obj, ArrayBufferViewObject::initialLengthOffset()), temp);
   9502  branchPtr(Assembler::NotEqual, temp, ImmWord(0), label);
   9503 
   9504  loadPrivate(Address(obj, ArrayBufferViewObject::initialByteOffsetOffset()),
   9505              temp);
   9506  branchPtr(Assembler::NotEqual, temp, ImmWord(0), label);
   9507 
   9508  bind(&done);
   9509 }
   9510 
   9511 void MacroAssembler::branchIfResizableArrayBufferViewInBounds(Register obj,
   9512                                                              Register temp,
   9513                                                              Label* label) {
   9514  // Implementation of ArrayBufferViewObject::isOutOfBounds().
   9515 
   9516  Label done;
   9517 
   9518  loadArrayBufferViewLengthIntPtr(obj, temp);
   9519  branchPtr(Assembler::NotEqual, temp, ImmWord(0), label);
   9520 
   9521  loadArrayBufferViewByteOffsetIntPtr(obj, temp);
   9522  branchPtr(Assembler::NotEqual, temp, ImmWord(0), label);
   9523 
   9524  loadPrivate(Address(obj, ArrayBufferViewObject::initialLengthOffset()), temp);
   9525  branchPtr(Assembler::NotEqual, temp, ImmWord(0), &done);
   9526 
   9527  loadPrivate(Address(obj, ArrayBufferViewObject::initialByteOffsetOffset()),
   9528              temp);
   9529  branchPtr(Assembler::Equal, temp, ImmWord(0), label);
   9530 
   9531  bind(&done);
   9532 }
   9533 
   9534 void MacroAssembler::branchIfNativeIteratorNotReusable(Register ni,
   9535                                                       Label* notReusable) {
   9536  // See NativeIterator::isReusable.
   9537  Address flagsAddr(ni, NativeIterator::offsetOfFlags());
   9538 
   9539 #ifdef DEBUG
   9540  Label niIsInitialized;
   9541  branchTest32(Assembler::NonZero, flagsAddr,
   9542               Imm32(NativeIterator::Flags::Initialized), &niIsInitialized);
   9543  assumeUnreachable(
   9544      "Expected a NativeIterator that's been completely "
   9545      "initialized");
   9546  bind(&niIsInitialized);
   9547 #endif
   9548 
   9549  branchTest32(Assembler::NonZero, flagsAddr,
   9550               Imm32(NativeIterator::Flags::NotReusable), notReusable);
   9551 }
   9552 
   9553 static void LoadNativeIterator(MacroAssembler& masm, Register obj,
   9554                               Register dest) {
   9555  MOZ_ASSERT(obj != dest);
   9556 
   9557 #ifdef DEBUG
   9558  // Assert we have a PropertyIteratorObject.
   9559  Label ok;
   9560  masm.branchTestObjClass(Assembler::Equal, obj,
   9561                          &PropertyIteratorObject::class_, dest, obj, &ok);
   9562  masm.assumeUnreachable("Expected PropertyIteratorObject!");
   9563  masm.bind(&ok);
   9564 #endif
   9565 
   9566  // Load NativeIterator object.
   9567  Address slotAddr(obj, PropertyIteratorObject::offsetOfIteratorSlot());
   9568  masm.loadPrivate(slotAddr, dest);
   9569 }
   9570 
   9571 // The ShapeCachePtr may be used to cache an iterator for for-in. Return that
   9572 // iterator in |dest| if:
   9573 // - the shape cache pointer exists and stores a native iterator
   9574 // - the iterator is reusable
   9575 // - the iterated object has no dense elements
   9576 // - the shapes of each object on the proto chain of |obj| match the cached
   9577 //   shapes
   9578 // - the proto chain has no dense elements
   9579 // Otherwise, jump to |failure|.
   9580 void MacroAssembler::maybeLoadIteratorFromShape(Register obj, Register dest,
   9581                                                Register temp, Register temp2,
   9582                                                Register temp3, Label* failure,
   9583                                                bool exclusive) {
   9584  // Register usage:
   9585  // obj: always contains the input object
   9586  // temp: walks the obj->shape->baseshape->proto->shape->... chain
   9587  // temp2: points to the native iterator. Incremented to walk the shapes array.
   9588  // temp3: scratch space
   9589  // dest: stores the resulting PropertyIteratorObject on success
   9590 
   9591  Label success;
   9592  Register shapeAndProto = temp;
   9593  Register nativeIterator = temp2;
   9594 
   9595  // Load ShapeCache from shape.
   9596  loadPtr(Address(obj, JSObject::offsetOfShape()), shapeAndProto);
   9597  loadPtr(Address(shapeAndProto, Shape::offsetOfCachePtr()), dest);
   9598 
   9599  // Check if it's an iterator.
   9600  andPtr(Imm32(ShapeCachePtr::MASK), dest, temp3);
   9601  branch32(Assembler::NotEqual, temp3, Imm32(ShapeCachePtr::ITERATOR), failure);
   9602 
   9603  // If we've cached an iterator, |obj| must be a native object.
   9604 #ifdef DEBUG
   9605  Label nonNative;
   9606  branchIfNonNativeObj(obj, temp3, &nonNative);
   9607 #endif
   9608 
   9609  // Verify that |obj| has no dense elements.
   9610  loadPtr(Address(obj, NativeObject::offsetOfElements()), temp3);
   9611  branch32(Assembler::NotEqual,
   9612           Address(temp3, ObjectElements::offsetOfInitializedLength()),
   9613           Imm32(0), failure);
   9614 
   9615  // Clear tag bits from iterator object. |dest| is now valid.
   9616  // Load the native iterator and verify that it's reusable.
   9617  andPtr(Imm32(~ShapeCachePtr::MASK), dest);
   9618  LoadNativeIterator(*this, dest, nativeIterator);
   9619 
   9620  if (exclusive) {
   9621    branchIfNativeIteratorNotReusable(nativeIterator, failure);
   9622  }
   9623 
   9624  Label skipIndices;
   9625  load32(Address(nativeIterator, NativeIterator::offsetOfPropertyCount()),
   9626         temp3);
   9627  branchTest32(Assembler::Zero,
   9628               Address(nativeIterator, NativeIterator::offsetOfFlags()),
   9629               Imm32(NativeIterator::Flags::IndicesAllocated), &skipIndices);
   9630 
   9631  computeEffectiveAddress(BaseIndex(nativeIterator, temp3, Scale::TimesFour),
   9632                          nativeIterator);
   9633 
   9634  bind(&skipIndices);
   9635  computeEffectiveAddress(BaseIndex(nativeIterator, temp3, ScalePointer,
   9636                                    NativeIterator::offsetOfFirstProperty()),
   9637                          nativeIterator);
   9638 
   9639  Register expectedProtoShape = nativeIterator;
   9640 
   9641  // We have to compare the shapes in the native iterator with the shapes on the
   9642  // proto chain to ensure the cached iterator is still valid.
   9643 
   9644  // Loop over the proto chain. At the head of the loop, |shape| is the shape of
   9645  // the current object, and |iteratorShapes| points to the expected shape of
   9646  // its proto.
   9647  Label protoLoop;
   9648  bind(&protoLoop);
   9649 
   9650  // Load the proto. If the proto is null, then we're done.
   9651  loadPtr(Address(shapeAndProto, Shape::offsetOfBaseShape()), shapeAndProto);
   9652  loadPtr(Address(shapeAndProto, BaseShape::offsetOfProto()), shapeAndProto);
   9653  branchPtr(Assembler::Equal, shapeAndProto, ImmPtr(nullptr), &success);
   9654 
   9655 #ifdef DEBUG
   9656  // We have guarded every shape up until this point, so we know that the proto
   9657  // is a native object.
   9658  branchIfNonNativeObj(shapeAndProto, temp3, &nonNative);
   9659 #endif
   9660 
   9661  // Verify that the proto has no dense elements.
   9662  loadPtr(Address(shapeAndProto, NativeObject::offsetOfElements()), temp3);
   9663  branch32(Assembler::NotEqual,
   9664           Address(temp3, ObjectElements::offsetOfInitializedLength()),
   9665           Imm32(0), failure);
   9666 
   9667  // Compare the shape of the proto to the expected shape.
   9668  loadPtr(Address(shapeAndProto, JSObject::offsetOfShape()), shapeAndProto);
   9669  loadPtr(Address(expectedProtoShape, 0), temp3);
   9670  branchPtr(Assembler::NotEqual, shapeAndProto, temp3, failure);
   9671 
   9672  // Increment |iteratorShapes| and jump back to the top of the loop.
   9673  addPtr(Imm32(sizeof(Shape*)), expectedProtoShape);
   9674  jump(&protoLoop);
   9675 
   9676 #ifdef DEBUG
   9677  bind(&nonNative);
   9678  assumeUnreachable("Expected NativeObject in maybeLoadIteratorFromShape");
   9679 #endif
   9680 
   9681  bind(&success);
   9682 }
   9683 
   9684 void MacroAssembler::iteratorMore(Register obj, ValueOperand output,
   9685                                  Register temp) {
   9686  Label done;
   9687  Register outputScratch = output.scratchReg();
   9688  LoadNativeIterator(*this, obj, outputScratch);
   9689 
   9690  // If propertyCursor_ < propertiesEnd_, load the next string and advance
   9691  // the cursor.  Otherwise return MagicValue(JS_NO_ITER_VALUE).
   9692  Label iterDone, restart;
   9693  bind(&restart);
   9694  Address cursorAddr(outputScratch, NativeIterator::offsetOfPropertyCursor());
   9695  Address cursorEndAddr(outputScratch, NativeIterator::offsetOfPropertyCount());
   9696  load32(cursorAddr, temp);
   9697  branch32(Assembler::BelowOrEqual, cursorEndAddr, temp, &iterDone);
   9698 
   9699  // Get next string.
   9700  BaseIndex propAddr(outputScratch, temp, ScalePointer,
   9701                     NativeIterator::offsetOfFirstProperty());
   9702  loadPtr(propAddr, temp);
   9703 
   9704  // Increase the cursor.
   9705  addPtr(Imm32(1), cursorAddr);
   9706 
   9707  // Check if the property has been deleted while iterating. Skip it if so.
   9708  branchTestPtr(Assembler::NonZero, temp,
   9709                Imm32(uint32_t(IteratorProperty::DeletedBit)), &restart);
   9710 
   9711  tagValue(JSVAL_TYPE_STRING, temp, output);
   9712  jump(&done);
   9713 
   9714  bind(&iterDone);
   9715  moveValue(MagicValue(JS_NO_ITER_VALUE), output);
   9716 
   9717  bind(&done);
   9718 }
   9719 
   9720 void MacroAssembler::iteratorLength(Register obj, Register output) {
   9721  LoadNativeIterator(*this, obj, output);
   9722  load32(Address(output, NativeIterator::offsetOfOwnPropertyCount()), output);
   9723 }
   9724 
   9725 void MacroAssembler::iteratorLoadElement(Register obj, Register index,
   9726                                         Register output) {
   9727  LoadNativeIterator(*this, obj, output);
   9728  loadPtr(BaseIndex(output, index, ScalePointer,
   9729                    NativeIterator::offsetOfFirstProperty()),
   9730          output);
   9731  andPtr(Imm32(int32_t(~IteratorProperty::DeletedBit)), output);
   9732 }
   9733 
   9734 void MacroAssembler::iteratorLoadElement(Register obj, int32_t index,
   9735                                         Register output) {
   9736  LoadNativeIterator(*this, obj, output);
   9737  loadPtr(Address(output, index * sizeof(IteratorProperty) +
   9738                              NativeIterator::offsetOfFirstProperty()),
   9739          output);
   9740  andPtr(Imm32(int32_t(~IteratorProperty::DeletedBit)), output);
   9741 }
   9742 
   9743 void MacroAssembler::iteratorClose(Register obj, Register temp1, Register temp2,
   9744                                   Register temp3) {
   9745  LoadNativeIterator(*this, obj, temp1);
   9746 
   9747  Address flagsAddr(temp1, NativeIterator::offsetOfFlags());
   9748 
   9749  // The shared iterator used for for-in with null/undefined is immutable and
   9750  // unlinked. See NativeIterator::isEmptyIteratorSingleton.
   9751  Label done;
   9752  branchTest32(Assembler::NonZero, flagsAddr,
   9753               Imm32(NativeIterator::Flags::IsEmptyIteratorSingleton), &done);
   9754 
   9755  // Clear objectBeingIterated.
   9756  Address iterObjAddr(temp1, NativeIterator::offsetOfObjectBeingIterated());
   9757  guardedCallPreBarrierAnyZone(iterObjAddr, MIRType::Object, temp2);
   9758  storePtr(ImmPtr(nullptr), iterObjAddr);
   9759 
   9760  // Reset property cursor.
   9761  store32(Imm32(0), Address(temp1, NativeIterator::offsetOfPropertyCursor()));
   9762 
   9763  // Clear deleted bits (only if we have unvisited deletions)
   9764  Label clearDeletedLoopStart, clearDeletedLoopEnd;
   9765  branchTest32(Assembler::Zero, flagsAddr,
   9766               Imm32(NativeIterator::Flags::HasUnvisitedPropertyDeletion),
   9767               &clearDeletedLoopEnd);
   9768 
   9769  load32(Address(temp1, NativeIterator::offsetOfPropertyCount()), temp3);
   9770 
   9771  computeEffectiveAddress(BaseIndex(temp1, temp3, ScalePointer,
   9772                                    NativeIterator::offsetOfFirstProperty()),
   9773                          temp3);
   9774  computeEffectiveAddress(
   9775      Address(temp1, NativeIterator::offsetOfFirstProperty()), temp2);
   9776 
   9777  bind(&clearDeletedLoopStart);
   9778  and32(Imm32(~uint32_t(IteratorProperty::DeletedBit)), Address(temp2, 0));
   9779  addPtr(Imm32(sizeof(IteratorProperty)), temp2);
   9780  branchPtr(Assembler::Below, temp2, temp3, &clearDeletedLoopStart);
   9781 
   9782  bind(&clearDeletedLoopEnd);
   9783 
   9784  // Clear active and unvisited deletions bits
   9785  and32(Imm32(~(NativeIterator::Flags::Active |
   9786                NativeIterator::Flags::HasUnvisitedPropertyDeletion)),
   9787        flagsAddr);
   9788 
   9789  // Unlink from the iterator list.
   9790  const Register next = temp2;
   9791  const Register prev = temp3;
   9792  loadPtr(Address(temp1, NativeIterator::offsetOfNext()), next);
   9793  loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), prev);
   9794  storePtr(prev, Address(next, NativeIterator::offsetOfPrev()));
   9795  storePtr(next, Address(prev, NativeIterator::offsetOfNext()));
   9796 #ifdef DEBUG
   9797  storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfNext()));
   9798  storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfPrev()));
   9799 #endif
   9800 
   9801  bind(&done);
   9802 }
   9803 
   9804 void MacroAssembler::registerIterator(Register enumeratorsList, Register iter,
   9805                                      Register temp) {
   9806  // iter->next = list
   9807  storePtr(enumeratorsList, Address(iter, NativeIterator::offsetOfNext()));
   9808 
   9809  // iter->prev = list->prev
   9810  loadPtr(Address(enumeratorsList, NativeIterator::offsetOfPrev()), temp);
   9811  storePtr(temp, Address(iter, NativeIterator::offsetOfPrev()));
   9812 
   9813  // list->prev->next = iter
   9814  storePtr(iter, Address(temp, NativeIterator::offsetOfNext()));
   9815 
   9816  // list->prev = iter
   9817  storePtr(iter, Address(enumeratorsList, NativeIterator::offsetOfPrev()));
   9818 }
   9819 
   9820 void MacroAssembler::prepareOOBStoreElement(Register object, Register index,
   9821                                            Register elements,
   9822                                            Register maybeTemp, Label* failure,
   9823                                            LiveRegisterSet volatileLiveRegs) {
   9824  Address length(elements, ObjectElements::offsetOfLength());
   9825  Address initLength(elements, ObjectElements::offsetOfInitializedLength());
   9826  Address capacity(elements, ObjectElements::offsetOfCapacity());
   9827  Address flags(elements, ObjectElements::offsetOfFlags());
   9828 
   9829  // If index < capacity, we can add a dense element inline. If not, we
   9830  // need to allocate more elements.
   9831  Label allocElement, enoughCapacity;
   9832  spectreBoundsCheck32(index, capacity, maybeTemp, &allocElement);
   9833  jump(&enoughCapacity);
   9834 
   9835  bind(&allocElement);
   9836 
   9837  // We currently only support storing one past the current capacity.
   9838  // We could add support for stores beyond that point by calling a different
   9839  // function, but then we'd have to think carefully about when to go sparse.
   9840  branch32(Assembler::NotEqual, capacity, index, failure);
   9841 
   9842  volatileLiveRegs.takeUnchecked(elements);
   9843  if (maybeTemp != InvalidReg) {
   9844    volatileLiveRegs.takeUnchecked(maybeTemp);
   9845  }
   9846  PushRegsInMask(volatileLiveRegs);
   9847 
   9848  // Use `elements` as a scratch register because we're about to reallocate it.
   9849  using Fn = bool (*)(JSContext* cx, NativeObject* obj);
   9850  setupUnalignedABICall(elements);
   9851  loadJSContext(elements);
   9852  passABIArg(elements);
   9853  passABIArg(object);
   9854  callWithABI<Fn, NativeObject::addDenseElementPure>();
   9855  storeCallPointerResult(elements);
   9856 
   9857  PopRegsInMask(volatileLiveRegs);
   9858  branchIfFalseBool(elements, failure);
   9859 
   9860  // Load the reallocated elements pointer.
   9861  loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
   9862 
   9863  bind(&enoughCapacity);
   9864 
   9865  // If our caller couldn't give us a temp register, use `object`.
   9866  Register temp;
   9867  if (maybeTemp == InvalidReg) {
   9868    push(object);
   9869    temp = object;
   9870  } else {
   9871    temp = maybeTemp;
   9872  }
   9873 
   9874  // Load the index of the first uninitialized element into `temp`.
   9875  load32(initLength, temp);
   9876 
   9877  // If it is not `index`, mark this elements array as non-packed.
   9878  Label noHoles, loop, done;
   9879  branch32(Assembler::Equal, temp, index, &noHoles);
   9880  or32(Imm32(ObjectElements::NON_PACKED), flags);
   9881 
   9882  // Loop over intermediate elements and fill them with the magic hole value.
   9883  bind(&loop);
   9884  storeValue(MagicValue(JS_ELEMENTS_HOLE), BaseValueIndex(elements, temp));
   9885  add32(Imm32(1), temp);
   9886  branch32(Assembler::NotEqual, temp, index, &loop);
   9887 
   9888  bind(&noHoles);
   9889 
   9890  // The new initLength is index + 1. Update it.
   9891  add32(Imm32(1), temp);
   9892  store32(temp, initLength);
   9893 
   9894  // If necessary, update length as well.
   9895  branch32(Assembler::Above, length, temp, &done);
   9896  store32(temp, length);
   9897  bind(&done);
   9898 
   9899  if (maybeTemp == InvalidReg) {
   9900    pop(object);
   9901  }
   9902 }
   9903 
   9904 void MacroAssembler::toHashableNonGCThing(ValueOperand value,
   9905                                          ValueOperand result,
   9906                                          FloatRegister tempFloat) {
   9907  // Inline implementation of |HashableValue::setValue()|.
   9908 
   9909 #ifdef DEBUG
   9910  Label ok;
   9911  branchTestGCThing(Assembler::NotEqual, value, &ok);
   9912  assumeUnreachable("Unexpected GC thing");
   9913  bind(&ok);
   9914 #endif
   9915 
   9916  Label useInput, done;
   9917  branchTestDouble(Assembler::NotEqual, value, &useInput);
   9918  {
   9919    Register int32 = result.scratchReg();
   9920    unboxDouble(value, tempFloat);
   9921 
   9922    // Normalize int32-valued doubles to int32 and negative zero to +0.
   9923    Label canonicalize;
   9924    convertDoubleToInt32(tempFloat, int32, &canonicalize, false);
   9925    {
   9926      tagValue(JSVAL_TYPE_INT32, int32, result);
   9927      jump(&done);
   9928    }
   9929    bind(&canonicalize);
   9930    {
   9931      // Normalize the sign bit of a NaN.
   9932      branchDouble(Assembler::DoubleOrdered, tempFloat, tempFloat, &useInput);
   9933      moveValue(JS::NaNValue(), result);
   9934      jump(&done);
   9935    }
   9936  }
   9937 
   9938  bind(&useInput);
   9939  moveValue(value, result);
   9940 
   9941  bind(&done);
   9942 }
   9943 
   9944 void MacroAssembler::toHashableValue(ValueOperand value, ValueOperand result,
   9945                                     FloatRegister tempFloat,
   9946                                     Label* atomizeString, Label* tagString) {
   9947  // Inline implementation of |HashableValue::setValue()|.
   9948 
   9949  ScratchTagScope tag(*this, value);
   9950  splitTagForTest(value, tag);
   9951 
   9952  Label notString, useInput, done;
   9953  branchTestString(Assembler::NotEqual, tag, &notString);
   9954  {
   9955    ScratchTagScopeRelease _(&tag);
   9956 
   9957    Register str = result.scratchReg();
   9958    unboxString(value, str);
   9959 
   9960    branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
   9961                 Imm32(JSString::ATOM_BIT), &useInput);
   9962 
   9963    jump(atomizeString);
   9964    bind(tagString);
   9965 
   9966    tagValue(JSVAL_TYPE_STRING, str, result);
   9967    jump(&done);
   9968  }
   9969  bind(&notString);
   9970  branchTestDouble(Assembler::NotEqual, tag, &useInput);
   9971  {
   9972    ScratchTagScopeRelease _(&tag);
   9973 
   9974    Register int32 = result.scratchReg();
   9975    unboxDouble(value, tempFloat);
   9976 
   9977    Label canonicalize;
   9978    convertDoubleToInt32(tempFloat, int32, &canonicalize, false);
   9979    {
   9980      tagValue(JSVAL_TYPE_INT32, int32, result);
   9981      jump(&done);
   9982    }
   9983    bind(&canonicalize);
   9984    {
   9985      branchDouble(Assembler::DoubleOrdered, tempFloat, tempFloat, &useInput);
   9986      moveValue(JS::NaNValue(), result);
   9987      jump(&done);
   9988    }
   9989  }
   9990 
   9991  bind(&useInput);
   9992  moveValue(value, result);
   9993 
   9994  bind(&done);
   9995 }
   9996 
   9997 void MacroAssembler::scrambleHashCode(Register result) {
   9998  // Inline implementation of |mozilla::ScrambleHashCode()|.
   9999 
  10000  mul32(Imm32(mozilla::kGoldenRatioU32), result);
  10001 }
  10002 
  10003 void MacroAssembler::hashAndScrambleValue(ValueOperand value, Register result,
  10004                                          Register temp) {
  10005  // Inline implementation of:
  10006  // mozilla::ScrambleHashCode(mozilla::HashGeneric(v.asRawBits()))
  10007  // Note that this uses the raw bits, which will change if a GC thing moves.
  10008  // This function should only be used for non-GC things, or in cases where
  10009  // moving GC things are handled specially (eg WeakMapObject).
  10010 
  10011  // uint32_t v1 = static_cast<uint32_t>(aValue);
  10012 #ifdef JS_PUNBOX64
  10013  move64To32(value.toRegister64(), result);
  10014 #else
  10015  move32(value.payloadReg(), result);
  10016 #endif
  10017 
  10018  // uint32_t v2 = static_cast<uint32_t>(static_cast<uint64_t>(aValue) >> 32);
  10019 #ifdef JS_PUNBOX64
  10020  auto r64 = Register64(temp);
  10021  move64(value.toRegister64(), r64);
  10022  rshift64Arithmetic(Imm32(32), r64);
  10023 #else
  10024  move32(value.typeReg(), temp);
  10025 #endif
  10026 
  10027  // mozilla::WrappingMultiply(kGoldenRatioU32, RotateLeft5(aHash) ^ aValue);
  10028  // with |aHash = 0| and |aValue = v1|.
  10029  mul32(Imm32(mozilla::kGoldenRatioU32), result);
  10030 
  10031  // mozilla::WrappingMultiply(kGoldenRatioU32, RotateLeft5(aHash) ^ aValue);
  10032  // with |aHash = <above hash>| and |aValue = v2|.
  10033  rotateLeft(Imm32(5), result, result);
  10034  xor32(temp, result);
  10035 
  10036  // Combine |mul32| and |scrambleHashCode| by directly multiplying with
  10037  // |kGoldenRatioU32 * kGoldenRatioU32|.
  10038  //
  10039  // mul32(Imm32(mozilla::kGoldenRatioU32), result);
  10040  //
  10041  // scrambleHashCode(result);
  10042  mul32(Imm32(mozilla::kGoldenRatioU32 * mozilla::kGoldenRatioU32), result);
  10043 }
  10044 
  10045 void MacroAssembler::prepareHashNonGCThing(ValueOperand value, Register result,
  10046                                           Register temp) {
  10047  // Inline implementation of |OrderedHashTableImpl::prepareHash()| and
  10048  // |mozilla::HashGeneric(v.asRawBits())|.
  10049 
  10050 #ifdef DEBUG
  10051  Label ok;
  10052  branchTestGCThing(Assembler::NotEqual, value, &ok);
  10053  assumeUnreachable("Unexpected GC thing");
  10054  bind(&ok);
  10055 #endif
  10056 
  10057  hashAndScrambleValue(value, result, temp);
  10058 }
  10059 
  10060 void MacroAssembler::prepareHashString(Register str, Register result,
  10061                                       Register temp) {
  10062  // Inline implementation of |OrderedHashTableImpl::prepareHash()| and
  10063  // |JSAtom::hash()|.
  10064 
  10065 #ifdef DEBUG
  10066  Label ok;
  10067  branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
  10068               Imm32(JSString::ATOM_BIT), &ok);
  10069  assumeUnreachable("Unexpected non-atom string");
  10070  bind(&ok);
  10071 #endif
  10072 
  10073 #ifdef JS_64BIT
  10074  static_assert(FatInlineAtom::offsetOfHash() == NormalAtom::offsetOfHash());
  10075  load32(Address(str, NormalAtom::offsetOfHash()), result);
  10076 #else
  10077  move32(Imm32(JSString::FAT_INLINE_MASK), temp);
  10078  and32(Address(str, JSString::offsetOfFlags()), temp);
  10079 
  10080  // Set |result| to 1 for FatInlineAtoms.
  10081  move32(Imm32(0), result);
  10082  cmp32Set(Assembler::Equal, temp, Imm32(JSString::FAT_INLINE_MASK), result);
  10083 
  10084  // Use a computed load for branch-free code.
  10085 
  10086  static_assert(FatInlineAtom::offsetOfHash() > NormalAtom::offsetOfHash());
  10087 
  10088  constexpr size_t offsetDiff =
  10089      FatInlineAtom::offsetOfHash() - NormalAtom::offsetOfHash();
  10090  static_assert(mozilla::IsPowerOfTwo(offsetDiff));
  10091 
  10092  uint8_t shift = mozilla::FloorLog2Size(offsetDiff);
  10093  if (IsShiftInScaleRange(shift)) {
  10094    load32(
  10095        BaseIndex(str, result, ShiftToScale(shift), NormalAtom::offsetOfHash()),
  10096        result);
  10097  } else {
  10098    lshift32(Imm32(shift), result);
  10099    load32(BaseIndex(str, result, TimesOne, NormalAtom::offsetOfHash()),
  10100           result);
  10101  }
  10102 #endif
  10103 
  10104  scrambleHashCode(result);
  10105 }
  10106 
  10107 void MacroAssembler::prepareHashSymbol(Register sym, Register result) {
  10108  // Inline implementation of |OrderedHashTableImpl::prepareHash()| and
  10109  // |Symbol::hash()|.
  10110 
  10111  load32(Address(sym, JS::Symbol::offsetOfHash()), result);
  10112 
  10113  scrambleHashCode(result);
  10114 }
  10115 
  10116 void MacroAssembler::prepareHashBigInt(Register bigInt, Register result,
  10117                                       Register temp1, Register temp2,
  10118                                       Register temp3) {
  10119  // Inline implementation of |OrderedHashTableImpl::prepareHash()| and
  10120  // |BigInt::hash()|.
  10121 
  10122  // Inline implementation of |mozilla::AddU32ToHash()|.
  10123  auto addU32ToHash = [&](auto toAdd) {
  10124    rotateLeft(Imm32(5), result, result);
  10125    xor32(toAdd, result);
  10126    mul32(Imm32(mozilla::kGoldenRatioU32), result);
  10127  };
  10128 
  10129  move32(Imm32(0), result);
  10130 
  10131  // Inline |mozilla::HashBytes()|.
  10132 
  10133  load32(Address(bigInt, BigInt::offsetOfLength()), temp1);
  10134  loadBigIntDigits(bigInt, temp2);
  10135 
  10136  Label start, loop;
  10137  jump(&start);
  10138  bind(&loop);
  10139 
  10140  {
  10141    // Compute |AddToHash(AddToHash(hash, data), sizeof(Digit))|.
  10142 #if defined(JS_CODEGEN_MIPS64)
  10143    // Hash the lower 32-bits.
  10144    addU32ToHash(Address(temp2, 0));
  10145 
  10146    // Hash the upper 32-bits.
  10147    addU32ToHash(Address(temp2, sizeof(int32_t)));
  10148 #elif JS_PUNBOX64
  10149    // Use a single 64-bit load on non-MIPS64 platforms.
  10150    loadPtr(Address(temp2, 0), temp3);
  10151 
  10152    // Hash the lower 32-bits.
  10153    addU32ToHash(temp3);
  10154 
  10155    // Hash the upper 32-bits.
  10156    rshiftPtr(Imm32(32), temp3);
  10157    addU32ToHash(temp3);
  10158 #else
  10159    addU32ToHash(Address(temp2, 0));
  10160 #endif
  10161  }
  10162  addPtr(Imm32(sizeof(BigInt::Digit)), temp2);
  10163 
  10164  bind(&start);
  10165  branchSub32(Assembler::NotSigned, Imm32(1), temp1, &loop);
  10166 
  10167  // Compute |mozilla::AddToHash(h, isNegative())|.
  10168  {
  10169    static_assert(mozilla::IsPowerOfTwo(BigInt::signBitMask()));
  10170 
  10171    load32(Address(bigInt, BigInt::offsetOfFlags()), temp1);
  10172    and32(Imm32(BigInt::signBitMask()), temp1);
  10173    rshift32(Imm32(mozilla::FloorLog2(BigInt::signBitMask())), temp1);
  10174 
  10175    addU32ToHash(temp1);
  10176  }
  10177 
  10178  scrambleHashCode(result);
  10179 }
  10180 
  10181 void MacroAssembler::prepareHashObject(Register setObj, ValueOperand value,
  10182                                       Register result, Register temp1,
  10183                                       Register temp2, Register temp3,
  10184                                       Register temp4) {
  10185 #ifdef JS_PUNBOX64
  10186  // Inline implementation of |OrderedHashTableImpl::prepareHash()| and
  10187  // |HashCodeScrambler::scramble(v.asRawBits())|.
  10188 
  10189  // Load |HashCodeScrambler*|. If the object has no buffer yet this will be
  10190  // nullptr. In this case we use 0 as hash number because the hash won't be
  10191  // used in MacroAssembler::orderedHashTableLookup when there are no entries.
  10192  Label done;
  10193  static_assert(MapObject::offsetOfHashCodeScrambler() ==
  10194                SetObject::offsetOfHashCodeScrambler());
  10195  loadPrivate(Address(setObj, SetObject::offsetOfHashCodeScrambler()), temp1);
  10196  move32(Imm32(0), result);
  10197  branchTestPtr(Assembler::Zero, temp1, temp1, &done);
  10198 
  10199  // Load |HashCodeScrambler::mK0| and |HashCodeScrambler::mK1|.
  10200  auto k0 = Register64(temp1);
  10201  auto k1 = Register64(temp2);
  10202  load64(Address(temp1, mozilla::HashCodeScrambler::offsetOfMK1()), k1);
  10203  load64(Address(temp1, mozilla::HashCodeScrambler::offsetOfMK0()), k0);
  10204 
  10205  // Hash numbers are 32-bit values, so only hash the lower double-word.
  10206  static_assert(sizeof(mozilla::HashNumber) == 4);
  10207  move32To64ZeroExtend(value.valueReg(), Register64(result));
  10208 
  10209  // Inline implementation of |SipHasher::sipHash()|.
  10210  auto m = Register64(result);
  10211  auto v0 = Register64(temp3);
  10212  auto v1 = Register64(temp4);
  10213  auto v2 = k0;
  10214  auto v3 = k1;
  10215 
  10216  auto sipRound = [&]() {
  10217    // mV0 = WrappingAdd(mV0, mV1);
  10218    add64(v1, v0);
  10219 
  10220    // mV1 = RotateLeft(mV1, 13);
  10221    rotateLeft64(Imm32(13), v1, v1, InvalidReg);
  10222 
  10223    // mV1 ^= mV0;
  10224    xor64(v0, v1);
  10225 
  10226    // mV0 = RotateLeft(mV0, 32);
  10227    rotateLeft64(Imm32(32), v0, v0, InvalidReg);
  10228 
  10229    // mV2 = WrappingAdd(mV2, mV3);
  10230    add64(v3, v2);
  10231 
  10232    // mV3 = RotateLeft(mV3, 16);
  10233    rotateLeft64(Imm32(16), v3, v3, InvalidReg);
  10234 
  10235    // mV3 ^= mV2;
  10236    xor64(v2, v3);
  10237 
  10238    // mV0 = WrappingAdd(mV0, mV3);
  10239    add64(v3, v0);
  10240 
  10241    // mV3 = RotateLeft(mV3, 21);
  10242    rotateLeft64(Imm32(21), v3, v3, InvalidReg);
  10243 
  10244    // mV3 ^= mV0;
  10245    xor64(v0, v3);
  10246 
  10247    // mV2 = WrappingAdd(mV2, mV1);
  10248    add64(v1, v2);
  10249 
  10250    // mV1 = RotateLeft(mV1, 17);
  10251    rotateLeft64(Imm32(17), v1, v1, InvalidReg);
  10252 
  10253    // mV1 ^= mV2;
  10254    xor64(v2, v1);
  10255 
  10256    // mV2 = RotateLeft(mV2, 32);
  10257    rotateLeft64(Imm32(32), v2, v2, InvalidReg);
  10258  };
  10259 
  10260  // 1. Initialization.
  10261  // mV0 = aK0 ^ UINT64_C(0x736f6d6570736575);
  10262  move64(Imm64(0x736f6d6570736575), v0);
  10263  xor64(k0, v0);
  10264 
  10265  // mV1 = aK1 ^ UINT64_C(0x646f72616e646f6d);
  10266  move64(Imm64(0x646f72616e646f6d), v1);
  10267  xor64(k1, v1);
  10268 
  10269  // mV2 = aK0 ^ UINT64_C(0x6c7967656e657261);
  10270  MOZ_ASSERT(v2 == k0);
  10271  xor64(Imm64(0x6c7967656e657261), v2);
  10272 
  10273  // mV3 = aK1 ^ UINT64_C(0x7465646279746573);
  10274  MOZ_ASSERT(v3 == k1);
  10275  xor64(Imm64(0x7465646279746573), v3);
  10276 
  10277  // 2. Compression.
  10278  // mV3 ^= aM;
  10279  xor64(m, v3);
  10280 
  10281  // sipRound();
  10282  sipRound();
  10283 
  10284  // mV0 ^= aM;
  10285  xor64(m, v0);
  10286 
  10287  // 3. Finalization.
  10288  // mV2 ^= 0xff;
  10289  xor64(Imm64(0xff), v2);
  10290 
  10291  // for (int i = 0; i < 3; i++) sipRound();
  10292  for (int i = 0; i < 3; i++) {
  10293    sipRound();
  10294  }
  10295 
  10296  // return mV0 ^ mV1 ^ mV2 ^ mV3;
  10297  xor64(v1, v0);
  10298  xor64(v2, v3);
  10299  xor64(v3, v0);
  10300 
  10301  move64To32(v0, result);
  10302 
  10303  scrambleHashCode(result);
  10304 
  10305  bind(&done);
  10306 #else
  10307  MOZ_CRASH("Not implemented");
  10308 #endif
  10309 }
  10310 
  10311 void MacroAssembler::prepareHashValue(Register setObj, ValueOperand value,
  10312                                      Register result, Register temp1,
  10313                                      Register temp2, Register temp3,
  10314                                      Register temp4) {
  10315  Label isString, isObject, isSymbol, isBigInt;
  10316  {
  10317    ScratchTagScope tag(*this, value);
  10318    splitTagForTest(value, tag);
  10319 
  10320    branchTestString(Assembler::Equal, tag, &isString);
  10321    branchTestObject(Assembler::Equal, tag, &isObject);
  10322    branchTestSymbol(Assembler::Equal, tag, &isSymbol);
  10323    branchTestBigInt(Assembler::Equal, tag, &isBigInt);
  10324  }
  10325 
  10326  Label done;
  10327  {
  10328    prepareHashNonGCThing(value, result, temp1);
  10329    jump(&done);
  10330  }
  10331  bind(&isString);
  10332  {
  10333    unboxString(value, temp1);
  10334    prepareHashString(temp1, result, temp2);
  10335    jump(&done);
  10336  }
  10337  bind(&isObject);
  10338  {
  10339    prepareHashObject(setObj, value, result, temp1, temp2, temp3, temp4);
  10340    jump(&done);
  10341  }
  10342  bind(&isSymbol);
  10343  {
  10344    unboxSymbol(value, temp1);
  10345    prepareHashSymbol(temp1, result);
  10346    jump(&done);
  10347  }
  10348  bind(&isBigInt);
  10349  {
  10350    unboxBigInt(value, temp1);
  10351    prepareHashBigInt(temp1, result, temp2, temp3, temp4);
  10352 
  10353    // Fallthrough to |done|.
  10354  }
  10355 
  10356  bind(&done);
  10357 }
  10358 
  10359 template <typename TableObject>
  10360 void MacroAssembler::orderedHashTableLookup(Register setOrMapObj,
  10361                                            ValueOperand value, Register hash,
  10362                                            Register entryTemp, Register temp1,
  10363                                            Register temp2, Register temp3,
  10364                                            Register temp4, Label* found,
  10365                                            IsBigInt isBigInt) {
  10366  // Inline implementation of |OrderedHashTableImpl::lookup()|.
  10367 
  10368  MOZ_ASSERT_IF(isBigInt == IsBigInt::No, temp3 == InvalidReg);
  10369  MOZ_ASSERT_IF(isBigInt == IsBigInt::No, temp4 == InvalidReg);
  10370 
  10371 #ifdef DEBUG
  10372  Label ok;
  10373  if (isBigInt == IsBigInt::No) {
  10374    branchTestBigInt(Assembler::NotEqual, value, &ok);
  10375    assumeUnreachable("Unexpected BigInt");
  10376  } else if (isBigInt == IsBigInt::Yes) {
  10377    branchTestBigInt(Assembler::Equal, value, &ok);
  10378    assumeUnreachable("Unexpected non-BigInt");
  10379  }
  10380  bind(&ok);
  10381 #endif
  10382 
  10383  // Jump to notFound if the hash table has no entries and may not have a
  10384  // buffer. Check this before calling Assert{Map,Set}ObjectHash because |hash|
  10385  // may be 0 when there's no hash code scrambler.
  10386  Label notFound;
  10387  unboxInt32(Address(setOrMapObj, TableObject::offsetOfLiveCount()), temp1);
  10388  branchTest32(Assembler::Zero, temp1, temp1, &notFound);
  10389 
  10390 #ifdef DEBUG
  10391  PushRegsInMask(LiveRegisterSet(RegisterSet::Volatile()));
  10392 
  10393  pushValue(value);
  10394  moveStackPtrTo(temp2);
  10395 
  10396  setupUnalignedABICall(temp1);
  10397  loadJSContext(temp1);
  10398  passABIArg(temp1);
  10399  passABIArg(setOrMapObj);
  10400  passABIArg(temp2);
  10401  passABIArg(hash);
  10402 
  10403  if constexpr (std::is_same_v<TableObject, SetObject>) {
  10404    using Fn =
  10405        void (*)(JSContext*, SetObject*, const Value*, mozilla::HashNumber);
  10406    callWithABI<Fn, jit::AssertSetObjectHash>();
  10407  } else {
  10408    static_assert(std::is_same_v<TableObject, MapObject>);
  10409    using Fn =
  10410        void (*)(JSContext*, MapObject*, const Value*, mozilla::HashNumber);
  10411    callWithABI<Fn, jit::AssertMapObjectHash>();
  10412  }
  10413 
  10414  popValue(value);
  10415  PopRegsInMask(LiveRegisterSet(RegisterSet::Volatile()));
  10416 #endif
  10417 
  10418  // Determine the bucket by computing |hash >> object->hashShift|. The hash
  10419  // shift is stored as PrivateUint32Value.
  10420  move32(hash, entryTemp);
  10421  unboxInt32(Address(setOrMapObj, TableObject::offsetOfHashShift()), temp2);
  10422  flexibleRshift32(temp2, entryTemp);
  10423 
  10424  loadPrivate(Address(setOrMapObj, TableObject::offsetOfHashTable()), temp2);
  10425  loadPtr(BaseIndex(temp2, entryTemp, ScalePointer), entryTemp);
  10426 
  10427  // Search for a match in this bucket.
  10428  Label start, loop;
  10429  jump(&start);
  10430  bind(&loop);
  10431  {
  10432    // Inline implementation of |HashableValue::operator==|.
  10433 
  10434    static_assert(TableObject::Table::offsetOfImplDataElement() == 0,
  10435                  "offsetof(Data, element) is 0");
  10436    auto keyAddr = Address(entryTemp, TableObject::Table::offsetOfEntryKey());
  10437 
  10438    if (isBigInt == IsBigInt::No) {
  10439      // Two HashableValues are equal if they have equal bits.
  10440      branch64(Assembler::Equal, keyAddr, value.toRegister64(), found);
  10441    } else {
  10442 #ifdef JS_PUNBOX64
  10443      auto key = ValueOperand(temp1);
  10444 #else
  10445      auto key = ValueOperand(temp1, temp2);
  10446 #endif
  10447 
  10448      loadValue(keyAddr, key);
  10449 
  10450      // Two HashableValues are equal if they have equal bits.
  10451      branch64(Assembler::Equal, key.toRegister64(), value.toRegister64(),
  10452               found);
  10453 
  10454      // BigInt values are considered equal if they represent the same
  10455      // mathematical value.
  10456      Label next;
  10457      fallibleUnboxBigInt(key, temp2, &next);
  10458      if (isBigInt == IsBigInt::Yes) {
  10459        unboxBigInt(value, temp1);
  10460      } else {
  10461        fallibleUnboxBigInt(value, temp1, &next);
  10462      }
  10463      equalBigInts(temp1, temp2, temp3, temp4, temp1, temp2, &next, &next,
  10464                   &next);
  10465      jump(found);
  10466      bind(&next);
  10467    }
  10468  }
  10469  loadPtr(Address(entryTemp, TableObject::Table::offsetOfImplDataChain()),
  10470          entryTemp);
  10471  bind(&start);
  10472  branchTestPtr(Assembler::NonZero, entryTemp, entryTemp, &loop);
  10473 
  10474  bind(&notFound);
  10475 }
  10476 
  10477 void MacroAssembler::setObjectHas(Register setObj, ValueOperand value,
  10478                                  Register hash, Register result,
  10479                                  Register temp1, Register temp2,
  10480                                  Register temp3, Register temp4,
  10481                                  IsBigInt isBigInt) {
  10482  Label found;
  10483  orderedHashTableLookup<SetObject>(setObj, value, hash, result, temp1, temp2,
  10484                                    temp3, temp4, &found, isBigInt);
  10485 
  10486  Label done;
  10487  move32(Imm32(0), result);
  10488  jump(&done);
  10489 
  10490  bind(&found);
  10491  move32(Imm32(1), result);
  10492  bind(&done);
  10493 }
  10494 
  10495 void MacroAssembler::mapObjectHas(Register mapObj, ValueOperand value,
  10496                                  Register hash, Register result,
  10497                                  Register temp1, Register temp2,
  10498                                  Register temp3, Register temp4,
  10499                                  IsBigInt isBigInt) {
  10500  Label found;
  10501  orderedHashTableLookup<MapObject>(mapObj, value, hash, result, temp1, temp2,
  10502                                    temp3, temp4, &found, isBigInt);
  10503 
  10504  Label done;
  10505  move32(Imm32(0), result);
  10506  jump(&done);
  10507 
  10508  bind(&found);
  10509  move32(Imm32(1), result);
  10510  bind(&done);
  10511 }
  10512 
  10513 void MacroAssembler::mapObjectGet(Register mapObj, ValueOperand value,
  10514                                  Register hash, ValueOperand result,
  10515                                  Register temp1, Register temp2,
  10516                                  Register temp3, Register temp4,
  10517                                  Register temp5, IsBigInt isBigInt) {
  10518  Label found;
  10519  orderedHashTableLookup<MapObject>(mapObj, value, hash, temp1, temp2, temp3,
  10520                                    temp4, temp5, &found, isBigInt);
  10521 
  10522  Label done;
  10523  moveValue(UndefinedValue(), result);
  10524  jump(&done);
  10525 
  10526  // |temp1| holds the found entry.
  10527  bind(&found);
  10528  loadValue(Address(temp1, MapObject::Table::Entry::offsetOfValue()), result);
  10529 
  10530  bind(&done);
  10531 }
  10532 
  10533 template <typename TableObject>
  10534 void MacroAssembler::loadOrderedHashTableCount(Register setOrMapObj,
  10535                                               Register result) {
  10536  // Inline implementation of |OrderedHashTableImpl::count()|.
  10537 
  10538  // Load the live count, stored as PrivateUint32Value.
  10539  unboxInt32(Address(setOrMapObj, TableObject::offsetOfLiveCount()), result);
  10540 }
  10541 
  10542 void MacroAssembler::loadSetObjectSize(Register setObj, Register result) {
  10543  loadOrderedHashTableCount<SetObject>(setObj, result);
  10544 }
  10545 
  10546 void MacroAssembler::loadMapObjectSize(Register mapObj, Register result) {
  10547  loadOrderedHashTableCount<MapObject>(mapObj, result);
  10548 }
  10549 
  10550 void MacroAssembler::prepareHashMFBT(Register hashCode, bool alreadyScrambled) {
  10551  // Inline implementation of |mozilla::HashTable::prepareHash()|.
  10552  static_assert(sizeof(HashNumber) == sizeof(uint32_t));
  10553 
  10554  // In some cases scrambling can be more efficiently folded into the
  10555  // computation of the hash itself.
  10556  if (!alreadyScrambled) {
  10557    // HashNumber keyHash = ScrambleHashCode(aInputHash);
  10558    scrambleHashCode(hashCode);
  10559  }
  10560 
  10561  const mozilla::HashNumber RemovedKey = mozilla::detail::kHashTableRemovedKey;
  10562  const mozilla::HashNumber CollisionBit =
  10563      mozilla::detail::kHashTableCollisionBit;
  10564 
  10565  // Avoid reserved hash codes:
  10566  // if (!isLiveHash(keyHash)) {
  10567  Label isLive;
  10568  branch32(Assembler::Above, hashCode, Imm32(RemovedKey), &isLive);
  10569  //   keyHash -= (sRemovedKey + 1);
  10570  sub32(Imm32(RemovedKey + 1), hashCode);
  10571  bind(&isLive);
  10572 
  10573  // return keyHash & ~sCollisionBit;
  10574  and32(Imm32(~CollisionBit), hashCode);
  10575 }
  10576 
  10577 template <typename Table>
  10578 void MacroAssembler::computeHash1MFBT(Register hashTable, Register hashCode,
  10579                                      Register hash1, Register scratch) {
  10580  // Inline implementation of |mozilla::HashTable::hash1|
  10581  // return aHash0 >> hashShift();
  10582  move32(hashCode, hash1);
  10583  load8ZeroExtend(Address(hashTable, Table::offsetOfHashShift()), scratch);
  10584  flexibleRshift32(scratch, hash1);
  10585 }
  10586 
  10587 template void MacroAssembler::computeHash1MFBT<WeakMapObject::Map>(
  10588    Register hashTable, Register hashCode, Register hash1, Register scratch);
  10589 
  10590 template <typename Table>
  10591 void MacroAssembler::computeHash2MFBT(Register hashTable, Register hashCode,
  10592                                      Register hash2, Register sizeMask,
  10593                                      Register scratch) {
  10594  // Inline implementation of |mozilla::detail::HashTable::hash2|
  10595 
  10596  // Load hashShift into sizeMask
  10597  load8ZeroExtend(Address(hashTable, Table::offsetOfHashShift()), sizeMask);
  10598 
  10599  //   uint32_t sizeLog2 = kHashNumberBits - hashShift();
  10600  move32(Imm32(kHashNumberBits), scratch);
  10601  sub32(sizeMask, scratch);
  10602 
  10603  //   DoubleHash dh = {((aCurKeyHash << sizeLog2) >> hashShift()) | 1,
  10604  move32(hashCode, hash2);
  10605  flexibleLshift32(scratch, hash2);
  10606  flexibleRshift32(sizeMask, hash2);
  10607  or32(Imm32(1), hash2);
  10608 
  10609  // sizeMask = (HashNumber(1) << sizeLog2) - 1};
  10610  move32(Imm32(1), sizeMask);
  10611  flexibleLshift32(scratch, sizeMask);
  10612  sub32(Imm32(1), sizeMask);
  10613 }
  10614 
  10615 template void MacroAssembler::computeHash2MFBT<WeakMapObject::Map>(
  10616    Register hashTable, Register hashCode, Register hash2, Register sizeMask,
  10617    Register scratch);
  10618 
  10619 void MacroAssembler::applyDoubleHashMFBT(Register hash1, Register hash2,
  10620                                         Register sizeMask) {
  10621  // Inline implementation of |mozilla::detail::HashTable::applyDoubleHash|
  10622 
  10623  // return WrappingSubtract(aHash1, aDoubleHash.mHash2) & aDoubleHash.mSizeMask
  10624  sub32(hash2, hash1);
  10625  and32(sizeMask, hash1);
  10626 }
  10627 
  10628 template <typename Table>
  10629 void MacroAssembler::checkForMatchMFBT(Register hashTable, Register hashIndex,
  10630                                       Register hashCode, Register scratch,
  10631                                       Register scratch2, Label* missing,
  10632                                       Label* collision) {
  10633  // Helper for inline implementation of |mozilla::detail::HashTable::lookup|
  10634  // The following code is used twice in |lookup|:
  10635  //
  10636  //  Slot slot = slotForIndex(h1);
  10637  //  if (slot.isFree()) {
  10638  //    <not found>
  10639  //  }
  10640  //  if (slot.matchHash(aKeyHash) && match(slot.get(), aLookup)) {
  10641  //    <found>
  10642  //  }
  10643  //
  10644  // To reduce register pressure, we do some inlining and reorder some
  10645  // intermediate computation. We inline the following functions:
  10646  //
  10647  // Slot slotForIndex(HashNumber aIndex) const {
  10648  //   auto hashes = reinterpret_cast<HashNumber*>(mTable);
  10649  //   auto entries = reinterpret_cast<Entry*>(&hashes[capacity()]);
  10650  //   return Slot(&entries[aIndex], &hashes[aIndex]);
  10651  // }
  10652  //
  10653  // uint32_t rawCapacity() { return 1u << (kHashNumberBits - hashShift()); }
  10654  //
  10655  // bool isFree() const { return *mKeyHash == Entry::sFreeKey; }
  10656  //
  10657  // bool matchHash(HashNumber hn) {
  10658  //   return (*mKeyHash & ~Entry::sCollisionBit) == hn;
  10659  // }
  10660  //
  10661  // Reordered, we implement:
  10662  //
  10663  // auto hashes = reinterpret_cast<HashNumber*>(mTable);
  10664  // HashNumber hashInTable = hashes[hashIndex];
  10665  // if (hashInTable == Entry::sFreeKey) {
  10666  //   <jump to missing label>
  10667  // }
  10668  // if (hashInTable & ~CollisionBit != hashCode) {
  10669  //   <jump to collision label>
  10670  // }
  10671  // auto entries = hashes[capacity()];
  10672  // Entry* entry = entries[hashIndex]
  10673  // <fall through to entry-specific match code>
  10674  const mozilla::HashNumber FreeKey = mozilla::detail::kHashTableFreeKey;
  10675  const mozilla::HashNumber CollisionBit =
  10676      mozilla::detail::kHashTableCollisionBit;
  10677 
  10678  Address tableAddr(hashTable, Table::offsetOfTable());
  10679  Address hashShiftAddr(hashTable, Table::offsetOfHashShift());
  10680 
  10681  // auto hashes = reinterpret_cast<HashNumber*>(mTable);
  10682  Register hashes = scratch;
  10683  loadPtr(tableAddr, scratch);
  10684 
  10685  // HashNumber hashInTable = hashes[hashIndex];
  10686  Register hashInTable = scratch2;
  10687  static_assert(sizeof(HashNumber) == 4);
  10688  load32(BaseIndex(hashes, hashIndex, Scale::TimesFour), hashInTable);
  10689 
  10690  // if (hashInTable == Entry::sFreeKey) {
  10691  //   <jump to missing label>
  10692  // }
  10693  branch32(Assembler::Equal, hashInTable, Imm32(FreeKey), missing);
  10694 
  10695  // if (hashInTable & ~CollisionBit != hashCode) {
  10696  //   <jump to collision label>
  10697  // }
  10698  and32(Imm32(~CollisionBit), hashInTable);
  10699  branch32(Assembler::NotEqual, hashInTable, hashCode, collision);
  10700 
  10701  // entries = hashes[capacity()]
  10702  // = hashes[1 << (kHashNumberBits - hashShift()]
  10703  // = &hashes + (1 << (kHashNumberBits - hashShift())) * sizeof(HashNumber)
  10704  // = &hashes + sizeof(HashNumber) << (kHashNumberBits - hashShift())
  10705  // = &hashes + sizeof(HashNumber) << (kHashNumberBits + -hashShift())
  10706  Register capacityOffset = scratch;
  10707  load8ZeroExtend(hashShiftAddr, scratch2);
  10708  neg32(scratch2);
  10709  add32(Imm32(kHashNumberBits), scratch2);
  10710  move32(Imm32(sizeof(mozilla::HashNumber)), capacityOffset);
  10711  flexibleLshift32(scratch2, capacityOffset);
  10712  Register entries = scratch2;
  10713  loadPtr(tableAddr, entries);
  10714  addPtr(capacityOffset, entries);
  10715 
  10716  // Load entries[hashIndex] into |scratch|
  10717  size_t EntrySize = sizeof(typename Table::Entry);
  10718  if (mozilla::IsPowerOfTwo(EntrySize)) {
  10719    uint32_t shift = mozilla::FloorLog2(EntrySize);
  10720    lshiftPtr(Imm32(shift), hashIndex, scratch);
  10721  } else {
  10722    // Note: this is provided as a fallback. Faster paths are possible for many
  10723    // non-power-of-two constants. If you add a use of this code that requires a
  10724    // non-power-of-two EntrySize, consider extending this code.
  10725    move32(hashIndex, scratch);
  10726    mulPtr(ImmWord(EntrySize), scratch);
  10727  }
  10728  computeEffectiveAddress(BaseIndex(entries, scratch, Scale::TimesOne),
  10729                          scratch);
  10730 }
  10731 
  10732 template void MacroAssembler::checkForMatchMFBT<WeakMapObject::Map>(
  10733    Register hashTable, Register hashIndex, Register hashCode, Register scratch,
  10734    Register scratch2, Label* missing, Label* collision);
  10735 
  10736 // Can't push large frames blindly on windows, so we must touch frame memory
  10737 // incrementally, with no more than 4096 - 1 bytes between touches.
  10738 //
  10739 // This is used across all platforms for simplicity.
  10740 void MacroAssembler::touchFrameValues(Register numStackValues,
  10741                                      Register scratch1, Register scratch2) {
  10742  const size_t FRAME_TOUCH_INCREMENT = 2048;
  10743  static_assert(FRAME_TOUCH_INCREMENT < 4096 - 1,
  10744                "Frame increment is too large");
  10745 
  10746  moveStackPtrTo(scratch2);
  10747 
  10748  lshiftPtr(Imm32(3), numStackValues, scratch1);
  10749  {
  10750    // Note: this loop needs to update the stack pointer register because older
  10751    // Linux kernels check the distance between the touched address and RSP.
  10752    // See bug 1839669 comment 47.
  10753    Label touchFrameLoop;
  10754    Label touchFrameLoopEnd;
  10755    bind(&touchFrameLoop);
  10756    branchSub32(Assembler::Signed, Imm32(FRAME_TOUCH_INCREMENT), scratch1,
  10757                &touchFrameLoopEnd);
  10758    subFromStackPtr(Imm32(FRAME_TOUCH_INCREMENT));
  10759    store32(Imm32(0), Address(getStackPointer(), 0));
  10760    jump(&touchFrameLoop);
  10761    bind(&touchFrameLoopEnd);
  10762  }
  10763 
  10764  moveToStackPtr(scratch2);
  10765 }
  10766 
  10767 #ifdef FUZZING_JS_FUZZILLI
  10768 void MacroAssembler::fuzzilliHashDouble(FloatRegister src, Register result,
  10769                                        Register temp) {
  10770  canonicalizeDouble(src);
  10771 
  10772 #  ifdef JS_PUNBOX64
  10773  Register64 r64(temp);
  10774 #  else
  10775  Register64 r64(temp, result);
  10776 #  endif
  10777 
  10778  moveDoubleToGPR64(src, r64);
  10779 
  10780 #  ifdef JS_PUNBOX64
  10781  // Move the high word into |result|.
  10782  move64(r64, Register64(result));
  10783  rshift64(Imm32(32), Register64(result));
  10784 #  endif
  10785 
  10786  // Add the high and low words of |r64|.
  10787  add32(temp, result);
  10788 }
  10789 
  10790 void MacroAssembler::fuzzilliStoreHash(Register value, Register temp1,
  10791                                       Register temp2) {
  10792  loadJSContext(temp1);
  10793 
  10794  // stats
  10795  Address addrExecHashInputs(temp1, offsetof(JSContext, executionHashInputs));
  10796  add32(Imm32(1), addrExecHashInputs);
  10797 
  10798  // hash
  10799  Address addrExecHash(temp1, offsetof(JSContext, executionHash));
  10800  load32(addrExecHash, temp2);
  10801  add32(value, temp2);
  10802  rotateLeft(Imm32(1), temp2, temp2);
  10803  store32(temp2, addrExecHash);
  10804 }
  10805 #endif
  10806 
  10807 namespace js {
  10808 namespace jit {
  10809 
  10810 #ifdef DEBUG
  10811 template <class RegisterType>
  10812 AutoGenericRegisterScope<RegisterType>::AutoGenericRegisterScope(
  10813    MacroAssembler& masm, RegisterType reg)
  10814    : RegisterType(reg), masm_(masm), released_(false) {
  10815  masm.debugTrackedRegisters_.add(reg);
  10816 }
  10817 
  10818 template AutoGenericRegisterScope<Register>::AutoGenericRegisterScope(
  10819    MacroAssembler& masm, Register reg);
  10820 template AutoGenericRegisterScope<FloatRegister>::AutoGenericRegisterScope(
  10821    MacroAssembler& masm, FloatRegister reg);
  10822 #endif  // DEBUG
  10823 
  10824 #ifdef DEBUG
  10825 template <class RegisterType>
  10826 AutoGenericRegisterScope<RegisterType>::~AutoGenericRegisterScope() {
  10827  if (!released_) {
  10828    release();
  10829  }
  10830 }
  10831 
  10832 template AutoGenericRegisterScope<Register>::~AutoGenericRegisterScope();
  10833 template AutoGenericRegisterScope<FloatRegister>::~AutoGenericRegisterScope();
  10834 
  10835 template <class RegisterType>
  10836 void AutoGenericRegisterScope<RegisterType>::release() {
  10837  MOZ_ASSERT(!released_);
  10838  released_ = true;
  10839  const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
  10840  masm_.debugTrackedRegisters_.take(reg);
  10841 }
  10842 
  10843 template void AutoGenericRegisterScope<Register>::release();
  10844 template void AutoGenericRegisterScope<FloatRegister>::release();
  10845 
  10846 template <class RegisterType>
  10847 void AutoGenericRegisterScope<RegisterType>::reacquire() {
  10848  MOZ_ASSERT(released_);
  10849  released_ = false;
  10850  const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
  10851  masm_.debugTrackedRegisters_.add(reg);
  10852 }
  10853 
  10854 template void AutoGenericRegisterScope<Register>::reacquire();
  10855 template void AutoGenericRegisterScope<FloatRegister>::reacquire();
  10856 
  10857 #endif  // DEBUG
  10858 
  10859 }  // namespace jit
  10860 
  10861 }  // namespace js