tor-browser

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

WasmBCClass.h (76178B)


      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 *
      4 * Copyright 2016 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 // This is an INTERNAL header for Wasm baseline compiler: the compiler object
     20 // and its supporting types.
     21 
     22 #ifndef wasm_wasm_baseline_object_h
     23 #define wasm_wasm_baseline_object_h
     24 
     25 #include "jit/PerfSpewer.h"
     26 #include "wasm/WasmBCDefs.h"
     27 #include "wasm/WasmBCFrame.h"
     28 #include "wasm/WasmBCRegDefs.h"
     29 #include "wasm/WasmBCStk.h"
     30 #include "wasm/WasmConstants.h"
     31 
     32 namespace js {
     33 namespace wasm {
     34 
     35 // Container for a piece of out-of-line code, the slow path that supports an
     36 // operation.
     37 class OutOfLineCode;
     38 
     39 // Part of the inter-bytecode state for the boolean-evaluation-for-control
     40 // optimization.
     41 struct BranchState;
     42 
     43 // Representation of wasm local variables.
     44 using Local = BaseStackFrame::Local;
     45 
     46 // Bitset used for simple bounds check elimination.  Capping this at 64 locals
     47 // makes sense; even 32 locals would probably be OK in practice.
     48 //
     49 // For more information about BCE, see the block comment in WasmBCMemory.cpp.
     50 using BCESet = uint64_t;
     51 
     52 // Information stored in the control node for generating exception handling
     53 // landing pads.
     54 struct CatchInfo {
     55  uint32_t tagIndex;        // Index for the associated exception.
     56  NonAssertingLabel label;  // The entry label for the handler.
     57 
     58  explicit CatchInfo(uint32_t tagIndex_) : tagIndex(tagIndex_) {}
     59 };
     60 
     61 using CatchInfoVector = Vector<CatchInfo, 1, SystemAllocPolicy>;
     62 
     63 // Control node, representing labels and stack heights at join points.
     64 struct Control {
     65  NonAssertingLabel label;       // The "exit" label
     66  NonAssertingLabel otherLabel;  // Used for the "else" branch of if-then-else
     67  // and to allow delegate to jump to catches.
     68  StackHeight stackHeight;     // From BaseStackFrame
     69  uint32_t stackSize;          // Value stack height
     70  BCESet bceSafeOnEntry;       // Bounds check info flowing into the item
     71  BCESet bceSafeOnExit;        // Bounds check info flowing out of the item
     72  bool deadOnArrival;          // deadCode_ was set on entry to the region
     73  bool deadThenBranch;         // deadCode_ was set on exit from "then"
     74  size_t tryNoteIndex;         // For tracking try branch code ranges.
     75  CatchInfoVector catchInfos;  // Used for try-catch handlers.
     76  size_t loopBytecodeStart;    // For LT: bytecode offset of start of a loop.
     77  CodeOffset offsetOfCtrDec;   // For LT: masm offset of loop's counter decr.
     78 
     79  Control()
     80      : stackHeight(StackHeight::Invalid()),
     81        stackSize(UINT32_MAX),
     82        bceSafeOnEntry(0),
     83        bceSafeOnExit(~BCESet(0)),
     84        deadOnArrival(false),
     85        deadThenBranch(false),
     86        tryNoteIndex(0),
     87        loopBytecodeStart(UINTPTR_MAX),
     88        offsetOfCtrDec(CodeOffset()) {}
     89 
     90  Control(Control&&) = default;
     91  Control(const Control&) = delete;
     92 };
     93 
     94 // A vector of Nothing values, used for reading opcodes.
     95 class BaseNothingVector {
     96  mozilla::Nothing unused_;
     97 
     98 public:
     99  bool reserve(size_t size) { return true; }
    100  bool resize(size_t length) { return true; }
    101  mozilla::Nothing& operator[](size_t) { return unused_; }
    102  mozilla::Nothing& back() { return unused_; }
    103  size_t length() const { return 0; }
    104  bool append(mozilla::Nothing& nothing) { return true; }
    105  void infallibleAppend(mozilla::Nothing& nothing) {}
    106 };
    107 
    108 // The baseline compiler tracks values on a stack of its own -- it needs to scan
    109 // that stack for spilling -- and thus has no need for the values maintained by
    110 // the iterator.
    111 struct BaseCompilePolicy {
    112  using Value = mozilla::Nothing;
    113  using ValueVector = BaseNothingVector;
    114 
    115  // The baseline compiler uses the iterator's control stack, attaching
    116  // its own control information.
    117  using ControlItem = Control;
    118 };
    119 
    120 using BaseOpIter = OpIter<BaseCompilePolicy>;
    121 
    122 // Latent operation for boolean-evaluation-for-control optimization.
    123 enum class LatentOp { None, Compare, Eqz };
    124 
    125 // Encapsulate the checking needed for a memory access.
    126 struct AccessCheck {
    127  AccessCheck()
    128      : omitBoundsCheck(false),
    129        omitAlignmentCheck(false),
    130        onlyPointerAlignment(false) {}
    131 
    132  // If `omitAlignmentCheck` is true then we need check neither the
    133  // pointer nor the offset.  Otherwise, if `onlyPointerAlignment` is true
    134  // then we need check only the pointer.  Otherwise, check the sum of
    135  // pointer and offset.
    136 
    137  bool omitBoundsCheck;
    138  bool omitAlignmentCheck;
    139  bool onlyPointerAlignment;
    140 };
    141 
    142 // Encapsulate all the information about a function call.
    143 struct FunctionCall {
    144  FunctionCall(ABIKind abiKind, RestoreState restoreState)
    145      : abi(abiKind),
    146        restoreState(restoreState),
    147        abiKind(abiKind),
    148 #ifdef JS_CODEGEN_ARM
    149        hardFP(true),
    150 #endif
    151        frameAlignAdjustment(0),
    152        stackArgAreaSize(0) {
    153    // The system ABI preserves the instance register (as it's in a
    154    // non-volatile register) and realm. We just need to reload the HeapReg in
    155    // case the memory has been moved.
    156    MOZ_ASSERT_IF(abiKind == ABIKind::System,
    157                  restoreState == RestoreState::None ||
    158                      restoreState == RestoreState::PinnedRegs);
    159    if (abiKind == ABIKind::System) {
    160      // Builtin calls use the system hardFP setting on ARM32.
    161 #if defined(JS_CODEGEN_ARM)
    162      hardFP = ARMFlags::UseHardFpABI();
    163      abi.setUseHardFp(hardFP);
    164 #endif
    165    } else {
    166 #if defined(JS_CODEGEN_ARM)
    167      MOZ_ASSERT(hardFP, "The WASM ABI passes FP arguments in registers");
    168 #endif
    169    }
    170  }
    171 
    172  ABIArgGenerator abi;
    173  RestoreState restoreState;
    174  ABIKind abiKind;
    175 #ifdef JS_CODEGEN_ARM
    176  bool hardFP;
    177 #endif
    178  size_t frameAlignAdjustment;
    179  size_t stackArgAreaSize;
    180 };
    181 
    182 enum class PreBarrierKind {
    183  // No pre-write barrier is required because the previous value is undefined.
    184  None,
    185  // Perform a pre-write barrier to mark the previous value if an incremental
    186  // GC is underway.
    187  Normal,
    188 };
    189 
    190 enum class PostBarrierKind {
    191  // No post barrier.
    192  None,
    193  // Add a store buffer entry if the new value requires it, but do not attempt
    194  // to remove a pre-existing entry.
    195  Imprecise,
    196  // Remove an existing store buffer entry if the new value does not require
    197  // one. This is required to preserve invariants with HeapPtr when used for
    198  // movable storage.
    199  Precise,
    200  // Add a store buffer entry for the entire cell (e.g. the entire struct or
    201  // array who now has a field pointing into the nursery).
    202  WholeCell,
    203 };
    204 
    205 struct BranchIfRefSubtypeRegisters {
    206  RegPtr superSTV;
    207  RegI32 scratch1;
    208  RegI32 scratch2;
    209 };
    210 
    211 //////////////////////////////////////////////////////////////////////////////
    212 //
    213 // Wasm baseline compiler proper.
    214 //
    215 // This is a struct and not a class because there is no real benefit to hiding
    216 // anything, and because many static functions that are wrappers for masm
    217 // methods need to reach into it and would otherwise have to be declared as
    218 // friends.
    219 //
    220 // (Members generally have a '_' suffix but some don't because they are
    221 // referenced everywhere and it would be tedious to spell that out.)
    222 
    223 struct BaseCompiler final {
    224  ///////////////////////////////////////////////////////////////////////////
    225  //
    226  // Private types
    227 
    228  using LabelVector = Vector<NonAssertingLabel, 8, SystemAllocPolicy>;
    229 
    230  ///////////////////////////////////////////////////////////////////////////
    231  //
    232  // Read-only and write-once members.
    233 
    234  // Static compilation environment.
    235  const CodeMetadata& codeMeta_;
    236  const CompilerEnvironment& compilerEnv_;
    237  const FuncCompileInput& func_;
    238  const ValTypeVector& locals_;
    239 
    240  // Information about the locations of locals, this is set up during
    241  // initialization and read-only after that.
    242  BaseStackFrame::LocalVector localInfo_;
    243 
    244  // On specific platforms we sometimes need to use specific registers.
    245  const SpecificRegs specific_;
    246 
    247  // SigD and SigF are single-entry parameter lists for f64 and f32, these are
    248  // created during initialization.
    249  ValTypeVector SigD_;
    250  ValTypeVector SigF_;
    251 
    252  // Where to go to to return, bound as compilation ends.
    253  NonAssertingLabel returnLabel_;
    254 
    255  // Prologue and epilogue offsets, initialized during prologue and epilogue
    256  // generation and only used by the caller.
    257  FuncOffsets offsets_;
    258 
    259  // We call this address from the breakable point when the (per-module) debug
    260  // stub pointer in the Instance is not null.
    261  NonAssertingLabel perFunctionDebugStub_;
    262  uint32_t previousBreakablePoint_;
    263 
    264  // BaselineCompileFunctions() "lends" us the StkVector to use in this
    265  // BaseCompiler object, and that is installed in |stk_| in our constructor.
    266  // This is so as to avoid having to malloc/free the vector's contents at
    267  // each creation/destruction of a BaseCompiler object.  It does however mean
    268  // that we need to hold on to a reference to BaselineCompileFunctions()'s
    269  // vector, so we can swap (give) its contents back when this BaseCompiler
    270  // object is destroyed.  This significantly reduces the heap turnover of the
    271  // baseline compiler.  See bug 1532592.
    272  StkVector& stkSource_;
    273 
    274  ///////////////////////////////////////////////////////////////////////////
    275  //
    276  // Output-only data structures.
    277 
    278  // Bump allocator for temporary memory, used for the value stack and
    279  // out-of-line code blobs.  Bump-allocated memory is not freed until the end
    280  // of the compilation.
    281  TempAllocator::Fallible alloc_;
    282 
    283  // Machine code emitter.
    284  MacroAssembler& masm;
    285 
    286  // Perf spewer for annotated JIT code while profiling.
    287  WasmBaselinePerfSpewer perfSpewer_;
    288 
    289  ///////////////////////////////////////////////////////////////////////////
    290  //
    291  // Compilation state.
    292 
    293  // Decoder for this function, used for misc error reporting.
    294  Decoder& decoder_;
    295 
    296  // Opcode reader.
    297  BaseOpIter iter_;
    298 
    299  // Register allocator.
    300  BaseRegAlloc ra;
    301 
    302  // Stack frame abstraction.
    303  BaseStackFrame fr;
    304 
    305  // Latent out of line support code for some operations, code for these will be
    306  // emitted at the end of compilation.
    307  Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
    308 
    309  // The stack maps for this compilation.
    310  StackMaps* stackMaps_;
    311 
    312  // Stack map state.  This keeps track of live pointer slots and allows precise
    313  // stack maps to be generated at safe points.
    314  StackMapGenerator stackMapGenerator_;
    315 
    316  // Wasm value stack.  This maps values on the wasm stack to values in the
    317  // running code and their locations.
    318  //
    319  // The value stack facilitates on-the-fly register allocation and the use of
    320  // immediates in instructions.  It tracks latent constants, latent references
    321  // to locals, register contents, and values that have been flushed to the CPU
    322  // stack.
    323  //
    324  // The stack can be flushed to the CPU stack using sync().
    325  //
    326  // The stack is a StkVector rather than a StkVector& since constantly
    327  // dereferencing a StkVector& has been shown to add 0.5% or more to the
    328  // compiler's dynamic instruction count.
    329  StkVector stk_;
    330 
    331  // Flag indicating that the compiler is currently in a dead code region.
    332  bool deadCode_;
    333 
    334  // Store previously finished note to know if we need to insert a nop in
    335  // finishTryNote.
    336  size_t mostRecentFinishedTryNoteIndex_;
    337 
    338  ///////////////////////////////////////////////////////////////////////////
    339  //
    340  // State for bounds check elimination.
    341 
    342  // Locals that have been bounds checked and not updated since
    343  BCESet bceSafe_;
    344 
    345  ///////////////////////////////////////////////////////////////////////////
    346  //
    347  // State for boolean-evaluation-for-control.
    348 
    349  // Latent operation for branch (seen next)
    350  LatentOp latentOp_;
    351 
    352  // Operand type, if latentOp_ is true
    353  ValType latentType_;
    354 
    355  // Comparison operator, if latentOp_ == Compare, int types
    356  Assembler::Condition latentIntCmp_;
    357 
    358  // Comparison operator, if latentOp_ == Compare, float types
    359  Assembler::DoubleCondition latentDoubleCmp_;
    360 
    361  ///////////////////////////////////////////////////////////////////////////
    362  //
    363  // Main compilation API.
    364  //
    365  // A client will create a compiler object, and then call init(),
    366  // emitFunction(), and finish() in that order.
    367 
    368  BaseCompiler(const CodeMetadata& codeMetadata,
    369               const CompilerEnvironment& compilerEnv,
    370               const FuncCompileInput& func, const ValTypeVector& locals,
    371               const RegisterOffsets& trapExitLayout,
    372               size_t trapExitLayoutNumWords, Decoder& decoder,
    373               StkVector& stkSource, TempAllocator* alloc, MacroAssembler* masm,
    374               StackMaps* stackMaps);
    375  ~BaseCompiler();
    376 
    377  [[nodiscard]] bool init();
    378  [[nodiscard]] bool emitFunction();
    379  [[nodiscard]] FuncOffsets finish();
    380 
    381  //////////////////////////////////////////////////////////////////////////////
    382  //
    383  // Sundry accessor abstractions and convenience predicates.
    384  //
    385  // WasmBaselineObject-inl.h.
    386 
    387  inline const FuncType& funcType() const;
    388  inline bool usesMemory() const;
    389  inline bool usesSharedMemory(uint32_t memoryIndex) const;
    390  inline bool isMem32(uint32_t memoryIndex) const;
    391  inline bool isMem64(uint32_t memoryIndex) const;
    392  inline bool hugeMemoryEnabled(uint32_t memoryIndex) const;
    393  inline uint32_t instanceOffsetOfMemoryBase(uint32_t memoryIndex) const;
    394  inline uint32_t instanceOffsetOfBoundsCheckLimit(uint32_t memoryIndex,
    395                                                   unsigned byteSize) const;
    396 
    397  // The casts are used by some of the ScratchRegister implementations.
    398  operator MacroAssembler&() const { return masm; }
    399  operator BaseRegAlloc&() { return ra; }
    400 
    401  //////////////////////////////////////////////////////////////////////////////
    402  //
    403  // Locals.
    404  //
    405  // WasmBaselineObject-inl.h.
    406 
    407  // Assert that the local at the given index has the given type, and return a
    408  // reference to the Local.
    409  inline const Local& localFromSlot(uint32_t slot, MIRType type);
    410 
    411  //////////////////////////////////////////////////////////////////////////////
    412  //
    413  // Out of line code management.
    414 
    415  [[nodiscard]] OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool);
    416  [[nodiscard]] bool generateOutOfLineCode();
    417 
    418  /////////////////////////////////////////////////////////////////////////////
    419  //
    420  // Layering in the compiler (briefly).
    421  //
    422  // At the lowest layers are abstractions for registers (managed by the
    423  // BaseRegAlloc and the wrappers below) and the stack frame (managed by the
    424  // BaseStackFrame).
    425  //
    426  // The registers and frame are in turn used by the value abstraction, which is
    427  // implemented by the Stk type and backed by the value stack.  Values may be
    428  // stored in registers, in the frame, or may be latent constants, and the
    429  // value stack handles storage mostly transparently in its push and pop
    430  // routines.
    431  //
    432  // In turn, the pop routines bring values into registers so that we can
    433  // compute on them, and the push routines move values to the stack (where they
    434  // may still reside in registers until the registers are needed or the value
    435  // must be in memory).
    436  //
    437  // Routines for managing parameters and results (for blocks or calls) may also
    438  // manipulate the stack directly.
    439  //
    440  // At the top are the code generators: methods that use the poppers and
    441  // pushers and other utilities to move values into place, and that emit code
    442  // to compute on those values or change control flow.
    443 
    444  /////////////////////////////////////////////////////////////////////////////
    445  //
    446  // Register management.  These are simply strongly-typed wrappers that
    447  // delegate to the register allocator.
    448 
    449  inline bool isAvailableI32(RegI32 r);
    450  inline bool isAvailableI64(RegI64 r);
    451  inline bool isAvailableRef(RegRef r);
    452  inline bool isAvailablePtr(RegPtr r);
    453  inline bool isAvailableF32(RegF32 r);
    454  inline bool isAvailableF64(RegF64 r);
    455 #ifdef ENABLE_WASM_SIMD
    456  inline bool isAvailableV128(RegV128 r);
    457 #endif
    458 
    459  // Allocate any register
    460  [[nodiscard]] inline RegI32 needI32();
    461  [[nodiscard]] inline RegI64 needI64();
    462  [[nodiscard]] inline RegRef needRef();
    463  [[nodiscard]] inline RegPtr needPtr();
    464  [[nodiscard]] inline RegF32 needF32();
    465  [[nodiscard]] inline RegF64 needF64();
    466 #ifdef ENABLE_WASM_SIMD
    467  [[nodiscard]] inline RegV128 needV128();
    468 #endif
    469 
    470  // Allocate a specific register
    471  inline void needI32(RegI32 specific);
    472  inline void needI64(RegI64 specific);
    473  inline void needRef(RegRef specific);
    474  inline void needPtr(RegPtr specific);
    475  inline void needF32(RegF32 specific);
    476  inline void needF64(RegF64 specific);
    477 #ifdef ENABLE_WASM_SIMD
    478  inline void needV128(RegV128 specific);
    479 #endif
    480 
    481  template <typename RegType>
    482  inline RegType need();
    483 
    484  // Just a shorthand.
    485  inline void need2xI32(RegI32 r0, RegI32 r1);
    486  inline void need2xI64(RegI64 r0, RegI64 r1);
    487 
    488  // Get a register but do not sync the stack to free one up.  This will crash
    489  // if no register is available.
    490  inline void needI32NoSync(RegI32 r);
    491 
    492 #if defined(JS_CODEGEN_ARM)
    493  // Allocate a specific register pair (even-odd register numbers).
    494  [[nodiscard]] inline RegI64 needI64Pair();
    495 #endif
    496 
    497  inline void freeAny(AnyReg r);
    498  inline void freeI32(RegI32 r);
    499  inline void freeI64(RegI64 r);
    500  inline void freeRef(RegRef r);
    501  inline void freePtr(RegPtr r);
    502  inline void freeF32(RegF32 r);
    503  inline void freeF64(RegF64 r);
    504 #ifdef ENABLE_WASM_SIMD
    505  inline void freeV128(RegV128 r);
    506 #endif
    507 
    508  template <typename RegType>
    509  inline void free(RegType r);
    510 
    511  // Free r if it is not invalid.
    512  inline void maybeFree(RegI32 r);
    513  inline void maybeFree(RegI64 r);
    514  inline void maybeFree(RegF32 r);
    515  inline void maybeFree(RegF64 r);
    516  inline void maybeFree(RegRef r);
    517  inline void maybeFree(RegPtr r);
    518 #ifdef ENABLE_WASM_SIMD
    519  inline void maybeFree(RegV128 r);
    520 #endif
    521 
    522  // On 64-bit systems, `except` must equal r and this is a no-op.  On 32-bit
    523  // systems, `except` must equal the high or low part of a pair and the other
    524  // part of the pair is freed.
    525  inline void freeI64Except(RegI64 r, RegI32 except);
    526 
    527  // Return the 32-bit low part of the 64-bit register, do not free anything.
    528  inline RegI32 fromI64(RegI64 r);
    529 
    530  // If r is valid, return fromI64(r), otherwise an invalid RegI32.
    531  inline RegI32 maybeFromI64(RegI64 r);
    532 
    533 #ifdef JS_PUNBOX64
    534  // On 64-bit systems, reinterpret r as 64-bit.
    535  inline RegI64 fromI32(RegI32 r);
    536 #endif
    537 
    538  // Widen r to 64 bits; this may allocate another register to form a pair.
    539  // Note this does not generate code for sign/zero extension.
    540  inline RegI64 widenI32(RegI32 r);
    541 
    542  // Narrow r to 32 bits; this may free part of a pair.  Note this does not
    543  // generate code to canonicalize the value on 64-bit systems.
    544  inline RegI32 narrowI64(RegI64 r);
    545  inline RegI32 narrowRef(RegRef r);
    546 
    547  // Return the 32-bit low part of r.
    548  inline RegI32 lowPart(RegI64 r);
    549 
    550  // On 64-bit systems, return an invalid register.  On 32-bit systems, return
    551  // the low part of a pair.
    552  inline RegI32 maybeHighPart(RegI64 r);
    553 
    554  //////////////////////////////////////////////////////////////////////////////
    555  //
    556  // Values and value stack: Low-level methods for moving Stk values of specific
    557  // kinds to registers.
    558 
    559  inline void loadConstI32(const Stk& src, RegI32 dest);
    560  inline void loadMemI32(const Stk& src, RegI32 dest);
    561  inline void loadLocalI32(const Stk& src, RegI32 dest);
    562  inline void loadRegisterI32(const Stk& src, RegI32 dest);
    563  inline void loadConstI64(const Stk& src, RegI64 dest);
    564  inline void loadMemI64(const Stk& src, RegI64 dest);
    565  inline void loadLocalI64(const Stk& src, RegI64 dest);
    566  inline void loadRegisterI64(const Stk& src, RegI64 dest);
    567  inline void loadConstRef(const Stk& src, RegRef dest);
    568  inline void loadMemRef(const Stk& src, RegRef dest);
    569  inline void loadLocalRef(const Stk& src, RegRef dest);
    570  inline void loadRegisterRef(const Stk& src, RegRef dest);
    571  inline void loadConstF64(const Stk& src, RegF64 dest);
    572  inline void loadMemF64(const Stk& src, RegF64 dest);
    573  inline void loadLocalF64(const Stk& src, RegF64 dest);
    574  inline void loadRegisterF64(const Stk& src, RegF64 dest);
    575  inline void loadConstF32(const Stk& src, RegF32 dest);
    576  inline void loadMemF32(const Stk& src, RegF32 dest);
    577  inline void loadLocalF32(const Stk& src, RegF32 dest);
    578  inline void loadRegisterF32(const Stk& src, RegF32 dest);
    579 #ifdef ENABLE_WASM_SIMD
    580  inline void loadConstV128(const Stk& src, RegV128 dest);
    581  inline void loadMemV128(const Stk& src, RegV128 dest);
    582  inline void loadLocalV128(const Stk& src, RegV128 dest);
    583  inline void loadRegisterV128(const Stk& src, RegV128 dest);
    584 #endif
    585 
    586  //////////////////////////////////////////////////////////////////////////
    587  //
    588  // Values and value stack: Mid-level routines for moving Stk values of any
    589  // kind to registers.
    590 
    591  inline void loadI32(const Stk& src, RegI32 dest);
    592  inline void loadI64(const Stk& src, RegI64 dest);
    593 #if !defined(JS_PUNBOX64)
    594  inline void loadI64Low(const Stk& src, RegI32 dest);
    595  inline void loadI64High(const Stk& src, RegI32 dest);
    596 #endif
    597  inline void loadF64(const Stk& src, RegF64 dest);
    598  inline void loadF32(const Stk& src, RegF32 dest);
    599 #ifdef ENABLE_WASM_SIMD
    600  inline void loadV128(const Stk& src, RegV128 dest);
    601 #endif
    602  inline void loadRef(const Stk& src, RegRef dest);
    603 
    604  //////////////////////////////////////////////////////////////////////
    605  //
    606  // Value stack: stack management.
    607 
    608  // Flush all local and register value stack elements to memory.
    609  inline void sync();
    610 
    611  // Save a register on the value stack temporarily.
    612  void saveTempPtr(const RegPtr& r);
    613 
    614  // Restore a temporarily saved register from the value stack.
    615  void restoreTempPtr(const RegPtr& r);
    616 
    617  // This is an optimization used to avoid calling sync for setLocal: if the
    618  // local does not exist unresolved on the value stack then we can skip the
    619  // sync.
    620  inline bool hasLocal(uint32_t slot);
    621 
    622  // Sync the local if necessary. (This currently syncs everything if a sync is
    623  // needed at all.)
    624  inline void syncLocal(uint32_t slot);
    625 
    626  // Return the amount of execution stack consumed by the top numval
    627  // values on the value stack.
    628  inline size_t stackConsumed(size_t numval);
    629 
    630  // Drop one value off the stack, possibly also moving the physical stack
    631  // pointer.
    632  inline void dropValue();
    633 
    634 #ifdef DEBUG
    635  // Check that we're not leaking registers by comparing the
    636  // state of the stack + available registers with the set of
    637  // all available registers.
    638 
    639  // Call this between opcodes.
    640  void performRegisterLeakCheck();
    641 
    642  // This can be called at any point, really, but typically just after
    643  // performRegisterLeakCheck().
    644  void assertStackInvariants() const;
    645 
    646  // Count the number of memory references on the value stack.
    647  inline size_t countMemRefsOnStk();
    648 
    649  // Check if there are any live registers on the value stack.
    650  inline bool hasLiveRegsOnStk();
    651 
    652  // Print the stack to stderr.
    653  void showStack(const char* who) const;
    654 #endif
    655 
    656  //////////////////////////////////////////////////////////////////////
    657  //
    658  // Value stack: pushers of values.
    659 
    660  // Push a register onto the value stack.
    661  inline void pushAny(AnyReg r);
    662  inline void pushI32(RegI32 r);
    663  inline void pushI64(RegI64 r);
    664  inline void pushRef(RegRef r);
    665  inline void pushPtr(RegPtr r);
    666  inline void pushF64(RegF64 r);
    667  inline void pushF32(RegF32 r);
    668 #ifdef ENABLE_WASM_SIMD
    669  inline void pushV128(RegV128 r);
    670 #endif
    671 
    672  // Template variation of the foregoing, for use by templated emitters.
    673  template <typename RegType>
    674  inline void push(RegType item);
    675 
    676  // Push a constant value onto the stack.  pushI32 can also take uint32_t, and
    677  // pushI64 can take uint64_t; the semantics are the same.  Appropriate sign
    678  // extension for a 32-bit value on a 64-bit architecture happens when the
    679  // value is popped, see the definition of moveImm32.
    680  inline void pushI32(int32_t v);
    681  inline void pushI64(int64_t v);
    682  inline void pushRef(intptr_t v);
    683  inline void pushPtr(intptr_t v);
    684  inline void pushF64(double v);
    685  inline void pushF32(float v);
    686 #ifdef ENABLE_WASM_SIMD
    687  inline void pushV128(V128 v);
    688 #endif
    689  inline void pushConstRef(intptr_t v);
    690 
    691  // Push the local slot onto the stack.  The slot will not be read here; it
    692  // will be read when it is consumed, or when a side effect to the slot forces
    693  // its value to be saved.
    694  inline void pushLocalI32(uint32_t slot);
    695  inline void pushLocalI64(uint32_t slot);
    696  inline void pushLocalRef(uint32_t slot);
    697  inline void pushLocalF64(uint32_t slot);
    698  inline void pushLocalF32(uint32_t slot);
    699 #ifdef ENABLE_WASM_SIMD
    700  inline void pushLocalV128(uint32_t slot);
    701 #endif
    702 
    703  // Push an U32 as an I64, zero-extending it in the process
    704  inline void pushU32AsI64(RegI32 rs);
    705 
    706  //////////////////////////////////////////////////////////////////////
    707  //
    708  // Value stack: poppers and peekers of values.
    709 
    710  // Pop some value off the stack.
    711  inline AnyReg popAny();
    712  inline AnyReg popAny(AnyReg specific);
    713 
    714  // Call only from other popI32() variants.  v must be the stack top.  May pop
    715  // the CPU stack.
    716  inline void popI32(const Stk& v, RegI32 dest);
    717 
    718  [[nodiscard]] inline RegI32 popI32();
    719  inline RegI32 popI32(RegI32 specific);
    720 
    721 #ifdef ENABLE_WASM_SIMD
    722  // Call only from other popV128() variants.  v must be the stack top.  May pop
    723  // the CPU stack.
    724  inline void popV128(const Stk& v, RegV128 dest);
    725 
    726  [[nodiscard]] inline RegV128 popV128();
    727  inline RegV128 popV128(RegV128 specific);
    728 #endif
    729 
    730  // Call only from other popI64() variants.  v must be the stack top.  May pop
    731  // the CPU stack.
    732  inline void popI64(const Stk& v, RegI64 dest);
    733 
    734  [[nodiscard]] inline RegI64 popI64();
    735  inline RegI64 popI64(RegI64 specific);
    736 
    737  // Call only from other popRef() variants.  v must be the stack top.  May pop
    738  // the CPU stack.
    739  inline void popRef(const Stk& v, RegRef dest);
    740 
    741  inline RegRef popRef(RegRef specific);
    742  [[nodiscard]] inline RegRef popRef();
    743 
    744  // Call only from other popPtr() variants.  v must be the stack top.  May pop
    745  // the CPU stack.
    746  inline void popPtr(const Stk& v, RegPtr dest);
    747 
    748  inline RegPtr popPtr(RegPtr specific);
    749  [[nodiscard]] inline RegPtr popPtr();
    750 
    751  // Call only from other popF64() variants.  v must be the stack top.  May pop
    752  // the CPU stack.
    753  inline void popF64(const Stk& v, RegF64 dest);
    754 
    755  [[nodiscard]] inline RegF64 popF64();
    756  inline RegF64 popF64(RegF64 specific);
    757 
    758  // Call only from other popF32() variants.  v must be the stack top.  May pop
    759  // the CPU stack.
    760  inline void popF32(const Stk& v, RegF32 dest);
    761 
    762  [[nodiscard]] inline RegF32 popF32();
    763  inline RegF32 popF32(RegF32 specific);
    764 
    765  // Templated variation of the foregoing, for use by templated emitters.
    766  template <typename RegType>
    767  inline RegType pop();
    768 
    769  // Constant poppers will return true and pop the value if the stack top is a
    770  // constant of the appropriate type; otherwise pop nothing and return false.
    771  [[nodiscard]] inline bool hasConst() const;
    772  [[nodiscard]] inline bool popConst(int32_t* c);
    773  [[nodiscard]] inline bool popConst(int64_t* c);
    774  [[nodiscard]] inline bool peekConst(int32_t* c);
    775  [[nodiscard]] inline bool peekConst(int64_t* c);
    776  [[nodiscard]] inline bool peek2xConst(int32_t* c0, int32_t* c1);
    777  [[nodiscard]] inline bool popConstPositivePowerOfTwo(int32_t* c,
    778                                                       uint_fast8_t* power,
    779                                                       int32_t cutoff);
    780  [[nodiscard]] inline bool popConstPositivePowerOfTwo(int64_t* c,
    781                                                       uint_fast8_t* power,
    782                                                       int64_t cutoff);
    783 
    784  // Shorthand: Pop r1, then r0.
    785  inline void pop2xI32(RegI32* r0, RegI32* r1);
    786  inline void pop2xI64(RegI64* r0, RegI64* r1);
    787  inline void pop2xF32(RegF32* r0, RegF32* r1);
    788  inline void pop2xF64(RegF64* r0, RegF64* r1);
    789 #ifdef ENABLE_WASM_SIMD
    790  inline void pop2xV128(RegV128* r0, RegV128* r1);
    791 #endif
    792  inline void pop2xRef(RegRef* r0, RegRef* r1);
    793 
    794  // Pop to a specific register
    795  inline RegI32 popI32ToSpecific(RegI32 specific);
    796  inline RegI64 popI64ToSpecific(RegI64 specific);
    797 
    798 #ifdef JS_CODEGEN_ARM
    799  // Pop an I64 as a valid register pair.
    800  inline RegI64 popI64Pair();
    801 #endif
    802 
    803  // Pop an I64 but narrow it and return the narrowed part.
    804  inline RegI32 popI64ToI32();
    805  inline RegI32 popI64ToSpecificI32(RegI32 specific);
    806 
    807  // Pop an I32 or I64 as an I64. The value is zero extended out to 64-bits.
    808  inline RegI64 popAddressToInt64(AddressType addressType);
    809 
    810  // Pop an I32 or I64 as an I32. The value is clamped to UINT32_MAX to ensure
    811  // that it trips bounds checks.
    812  inline RegI32 popTableAddressToClampedInt32(AddressType addressType);
    813 
    814  // A combined push/pop that replaces an I32 or I64 on the stack with a clamped
    815  // I32, which will trip bounds checks if out of I32 range.
    816  inline void replaceTableAddressWithClampedInt32(AddressType addressType);
    817 
    818  // Pop the stack until it has the desired size, but do not move the physical
    819  // stack pointer.
    820  inline void popValueStackTo(uint32_t stackSize);
    821 
    822  // Pop the given number of elements off the value stack, but do not move
    823  // the physical stack pointer.
    824  inline void popValueStackBy(uint32_t items);
    825 
    826  // Peek into the stack at relativeDepth from the top.
    827  inline Stk& peek(uint32_t relativeDepth);
    828 
    829  // Peek the reference value at the specified depth and load it into a
    830  // register.
    831  inline void peekRefAt(uint32_t depth, RegRef dest);
    832 
    833  // Peek at the value on the top of the stack and return true if it is a Local
    834  // of any type.
    835  [[nodiscard]] inline bool peekLocal(uint32_t* local);
    836 
    837  ////////////////////////////////////////////////////////////////////////////
    838  //
    839  // Block parameters and results.
    840  //
    841  // Blocks may have multiple parameters and multiple results.  Blocks can also
    842  // be the target of branches: the entry for loops, and the exit for
    843  // non-loops.
    844  //
    845  // Passing multiple values to a non-branch target (i.e., the entry of a
    846  // "block") falls out naturally: any items on the value stack can flow
    847  // directly from one block to another.
    848  //
    849  // However, for branch targets, we need to allocate well-known locations for
    850  // the branch values.  The approach taken in the baseline compiler is to
    851  // allocate registers to the top N values (currently N=1), and then stack
    852  // locations for the rest.
    853  //
    854 
    855  // Types of result registers that interest us for result-manipulating
    856  // functions.
    857  enum class ResultRegKind {
    858    // General and floating result registers.
    859    All,
    860 
    861    // General result registers only.
    862    OnlyGPRs
    863  };
    864 
    865  // This is a flag ultimately intended for popBlockResults() that specifies how
    866  // the CPU stack should be handled after the result values have been
    867  // processed.
    868  enum class ContinuationKind {
    869    // Adjust the stack for a fallthrough: do nothing.
    870    Fallthrough,
    871 
    872    // Adjust the stack for a jump: make the stack conform to the
    873    // expected stack at the target
    874    Jump
    875  };
    876 
    877  // TODO: It's definitely disputable whether the result register management is
    878  // hot enough to warrant inlining at the outermost level.
    879 
    880  inline void needResultRegisters(ResultType type, ResultRegKind which);
    881 #ifdef JS_64BIT
    882  inline void widenInt32ResultRegisters(ResultType type);
    883 #endif
    884  inline void freeResultRegisters(ResultType type, ResultRegKind which);
    885  inline void needIntegerResultRegisters(ResultType type);
    886  inline void freeIntegerResultRegisters(ResultType type);
    887  inline void needResultRegisters(ResultType type);
    888  inline void freeResultRegisters(ResultType type);
    889  void assertResultRegistersAvailable(ResultType type);
    890  inline void captureResultRegisters(ResultType type);
    891  inline void captureCallResultRegisters(ResultType type);
    892 
    893  void popRegisterResults(ABIResultIter& iter);
    894  void popStackResults(ABIResultIter& iter, StackHeight stackBase);
    895 
    896  void popBlockResults(ResultType type, StackHeight stackBase,
    897                       ContinuationKind kind);
    898 
    899  // This function is similar to popBlockResults, but additionally handles the
    900  // implicit exception pointer that is pushed to the value stack on entry to
    901  // a catch handler by dropping it appropriately.
    902  void popCatchResults(ResultType type, StackHeight stackBase);
    903 
    904  Stk captureStackResult(const ABIResult& result, StackHeight resultsBase,
    905                         uint32_t stackResultBytes);
    906 
    907  [[nodiscard]] bool pushResults(ResultType type, StackHeight resultsBase);
    908  [[nodiscard]] bool pushBlockResults(ResultType type);
    909 
    910  // A combination of popBlockResults + pushBlockResults, used when entering a
    911  // block with a control-flow join (loops) or split (if) to shuffle the
    912  // fallthrough block parameters into the locations expected by the
    913  // continuation.
    914  //
    915  // This function should only be called when entering a block with a
    916  // control-flow join at the entry, where there are no live temporaries in
    917  // the current block.
    918  [[nodiscard]] bool topBlockParams(ResultType type);
    919 
    920  // A combination of popBlockResults + pushBlockResults, used before branches
    921  // where we don't know the target (br_if / br_table).  If and when the branch
    922  // is taken, the stack results will be shuffled down into place.  For br_if
    923  // that has fallthrough, the parameters for the untaken branch flow through to
    924  // the continuation.
    925  [[nodiscard]] bool topBranchParams(ResultType type, StackHeight* height);
    926 
    927  // Conditional branches with fallthrough are preceded by a topBranchParams, so
    928  // we know that there are no stack results that need to be materialized.  In
    929  // that case, we can just shuffle the whole block down before popping the
    930  // stack.
    931  void shuffleStackResultsBeforeBranch(StackHeight srcHeight,
    932                                       StackHeight destHeight, ResultType type);
    933 
    934  // If in debug mode, adds LeaveFrame breakpoint.
    935  bool insertDebugCollapseFrame();
    936 
    937  //////////////////////////////////////////////////////////////////////
    938  //
    939  // Stack maps
    940 
    941  // Various methods for creating a stackmap.  Stackmaps are indexed by the
    942  // lowest address of the instruction immediately *after* the instruction of
    943  // interest.  In practice that means either: the return point of a call, the
    944  // instruction immediately after a trap instruction (the "resume"
    945  // instruction), or the instruction immediately following a no-op (when
    946  // debugging is enabled).
    947 
    948  // Create a vanilla stackmap.
    949  [[nodiscard]] bool createStackMap(const char* who);
    950 
    951  // Create a stackmap as vanilla, but for a custom assembler offset.
    952  [[nodiscard]] bool createStackMap(const char* who,
    953                                    CodeOffset assemblerOffset);
    954 
    955  // Create a stack map as vanilla, and note the presence of a ref-typed
    956  // DebugFrame on the stack.
    957  [[nodiscard]] bool createStackMap(
    958      const char* who, HasDebugFrameWithLiveRefs debugFrameWithLiveRefs);
    959 
    960  ////////////////////////////////////////////////////////////
    961  //
    962  // Control stack
    963 
    964  inline void initControl(Control& item, ResultType params);
    965  inline Control& controlItem();
    966  inline Control& controlItem(uint32_t relativeDepth);
    967  inline Control& controlOutermost();
    968  inline LabelKind controlKind(uint32_t relativeDepth);
    969 
    970  ////////////////////////////////////////////////////////////
    971  //
    972  // Debugger API
    973 
    974  // Insert a breakpoint almost anywhere.  This will create a call, with all the
    975  // overhead that entails.
    976  void insertBreakablePoint(CallSiteKind kind);
    977 
    978  // Insert code at the end of a function for breakpoint filtering.
    979  void insertPerFunctionDebugStub();
    980 
    981  // Debugger API used at the return point: shuffle register return values off
    982  // to memory for the debugger to see; and get them back again.
    983  void saveRegisterReturnValues(const ResultType& resultType);
    984  void restoreRegisterReturnValues(const ResultType& resultType);
    985 
    986  //////////////////////////////////////////////////////////////////////
    987  //
    988  // Function prologue and epilogue.
    989 
    990  // Set up and tear down frame, execute prologue and epilogue.
    991  [[nodiscard]] bool beginFunction();
    992  [[nodiscard]] bool endFunction();
    993 
    994  // Move return values to memory before returning, as appropriate
    995  void popStackReturnValues(const ResultType& resultType);
    996 
    997  //////////////////////////////////////////////////////////////////////
    998  //
    999  // Calls.
   1000 
   1001  void beginCall(FunctionCall& call);
   1002  void endCall(FunctionCall& call, size_t stackSpace);
   1003 
   1004  void startCallArgs(size_t stackArgAreaSizeUnaligned, FunctionCall* call);
   1005  ABIArg reservePointerArgument(FunctionCall* call);
   1006  void passArg(ValType type, const Stk& arg, FunctionCall* call);
   1007 
   1008  // A flag passed to emitCallArgs, describing how the value stack is laid out.
   1009  enum class CalleeOnStack {
   1010    // After the arguments to the call, there is a callee pushed onto value
   1011    // stack.  This is only the case for callIndirect.  To get the arguments to
   1012    // the call, emitCallArgs has to reach one element deeper into the value
   1013    // stack, to skip the callee.
   1014    True,
   1015 
   1016    // No callee on the stack.
   1017    False
   1018  };
   1019  // The typename T for emitCallArgs can be one of the following:
   1020  // NormalCallResults, TailCallResults, or NoCallResults.
   1021  template <typename T>
   1022  [[nodiscard]] bool emitCallArgs(const ValTypeVector& argTypes, T results,
   1023                                  FunctionCall* baselineCall,
   1024                                  CalleeOnStack calleeOnStack);
   1025 
   1026  [[nodiscard]] bool pushStackResultsForWasmCall(const ResultType& type,
   1027                                                 RegPtr temp,
   1028                                                 StackResultsLoc* loc);
   1029  void popStackResultsAfterWasmCall(const StackResultsLoc& results,
   1030                                    uint32_t stackArgBytes);
   1031 
   1032  void pushBuiltinCallResult(const FunctionCall& call, MIRType type);
   1033  [[nodiscard]] bool pushWasmCallResults(const FunctionCall& call,
   1034                                         ResultType type,
   1035                                         const StackResultsLoc& loc);
   1036 
   1037  // Precondition for the call*() methods: sync()
   1038  CodeOffset callDefinition(uint32_t funcIndex, const FunctionCall& call);
   1039  CodeOffset callSymbolic(SymbolicAddress callee, const FunctionCall& call);
   1040  bool callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
   1041                    const Stk& indexVal, const FunctionCall& call,
   1042                    bool tailCall, CodeOffset* fastCallOffset,
   1043                    CodeOffset* slowCallOffset);
   1044  CodeOffset callImport(unsigned instanceDataOffset, const FunctionCall& call);
   1045  bool updateCallRefMetrics(size_t callRefIndex);
   1046  bool callRef(const Stk& calleeRef, const FunctionCall& call,
   1047               mozilla::Maybe<size_t> callRefIndex, CodeOffset* fastCallOffset,
   1048               CodeOffset* slowCallOffset);
   1049  void returnCallRef(const Stk& calleeRef, const FunctionCall& call,
   1050                     const FuncType& funcType);
   1051  CodeOffset builtinCall(SymbolicAddress builtin, const FunctionCall& call);
   1052  CodeOffset builtinInstanceMethodCall(const SymbolicAddressSignature& builtin,
   1053                                       const ABIArg& instanceArg,
   1054                                       const FunctionCall& call);
   1055 
   1056  // Helpers to pick up the returned value from the return register.
   1057  inline RegI32 captureReturnedI32();
   1058  inline RegI64 captureReturnedI64();
   1059  inline RegF32 captureReturnedF32(const FunctionCall& call);
   1060  inline RegF64 captureReturnedF64(const FunctionCall& call);
   1061 #ifdef ENABLE_WASM_SIMD
   1062  inline RegV128 captureReturnedV128(const FunctionCall& call);
   1063 #endif
   1064  inline RegRef captureReturnedRef();
   1065 
   1066  //////////////////////////////////////////////////////////////////////
   1067  //
   1068  // Register-to-register moves.  These emit nothing if src == dest.
   1069 
   1070  inline void moveI32(RegI32 src, RegI32 dest);
   1071  inline void moveI64(RegI64 src, RegI64 dest);
   1072  inline void moveRef(RegRef src, RegRef dest);
   1073  inline void movePtr(RegPtr src, RegPtr dest);
   1074  inline void moveF64(RegF64 src, RegF64 dest);
   1075  inline void moveF32(RegF32 src, RegF32 dest);
   1076 #ifdef ENABLE_WASM_SIMD
   1077  inline void moveV128(RegV128 src, RegV128 dest);
   1078 #endif
   1079 
   1080  template <typename RegType>
   1081  inline void move(RegType src, RegType dest);
   1082 
   1083  //////////////////////////////////////////////////////////////////////
   1084  //
   1085  // Immediate-to-register moves.
   1086  //
   1087  // The compiler depends on moveImm32() clearing the high bits of a 64-bit
   1088  // register on 64-bit systems except MIPS64 And LoongArch64 where high bits
   1089  // are sign extended from lower bits, see doc block "64-bit GPRs carrying
   1090  // 32-bit values" in MacroAssembler.h.
   1091 
   1092  inline void moveImm32(int32_t v, RegI32 dest);
   1093  inline void moveImm64(int64_t v, RegI64 dest);
   1094  inline void moveImmRef(intptr_t v, RegRef dest);
   1095 
   1096  //////////////////////////////////////////////////////////////////////
   1097  //
   1098  // Sundry low-level code generators.
   1099 
   1100  // Decrement the per-instance function hotness counter by `step` and request
   1101  // optimized compilation for this function if the updated counter is negative
   1102  // when regarded as an int32_t.  The amount to decrement is to be filled in
   1103  // later by ::patchHotnessCheck.
   1104  [[nodiscard]] Maybe<CodeOffset> addHotnessCheck();
   1105 
   1106  // Patch in the counter decrement for a hotness check, using the offset
   1107  // previously obtained from ::addHotnessCheck. We require 1 <= `step` <= 127.
   1108  void patchHotnessCheck(CodeOffset offset, uint32_t step);
   1109 
   1110  // Check the interrupt flag, trap if it is set.
   1111  [[nodiscard]] bool addInterruptCheck();
   1112 
   1113  // Check that the value is not zero, trap if it is.
   1114  void checkDivideByZero(RegI32 rhs);
   1115  void checkDivideByZero(RegI64 r);
   1116 
   1117  // Check that a signed division will not overflow, trap or flush-to-zero if it
   1118  // will according to `zeroOnOverflow`.
   1119  void checkDivideSignedOverflow(RegI32 rhs, RegI32 srcDest, Label* done,
   1120                                 bool zeroOnOverflow);
   1121  void checkDivideSignedOverflow(RegI64 rhs, RegI64 srcDest, Label* done,
   1122                                 bool zeroOnOverflow);
   1123 
   1124  // Emit a jump table to be used by tableSwitch()
   1125  void jumpTable(const LabelVector& labels, Label* theTable);
   1126 
   1127  // Emit a table switch, `theTable` is the jump table.
   1128  void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode);
   1129 
   1130  // Compare i64 and set an i32 boolean result according to the condition.
   1131  inline void cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs,
   1132                       RegI32 dest);
   1133 
   1134  // Round floating to integer.
   1135  [[nodiscard]] inline bool supportsRoundInstruction(RoundingMode mode);
   1136  inline void roundF32(RoundingMode roundingMode, RegF32 f0);
   1137  inline void roundF64(RoundingMode roundingMode, RegF64 f0);
   1138 
   1139  // These are just wrappers around assembler functions, but without
   1140  // type-specific names, and using our register abstractions for better type
   1141  // discipline.
   1142  inline void branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs,
   1143                       Label* l);
   1144  inline void branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs,
   1145                       Label* l);
   1146  inline void branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs,
   1147                       Label* l);
   1148  inline void branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l);
   1149  inline void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs,
   1150                       Label* l);
   1151  inline void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l);
   1152  inline void branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs,
   1153                       Label* l);
   1154 
   1155  // Helpers for accessing Instance::baselineScratchWords_.  Note that Word
   1156  // and I64 versions of these routines access the same area and it is up to
   1157  // the caller to use it in some way which makes sense.
   1158 
   1159  // Store/load `r`, a machine word, to/from the `index`th scratch storage
   1160  // slot in the current Instance.  `instancePtr` must point at the current
   1161  // Instance; it will not be modified.  For ::stashWord, `r` must not be the
   1162  // same as `instancePtr`.
   1163  void stashWord(RegPtr instancePtr, size_t index, RegPtr r);
   1164  void unstashWord(RegPtr instancePtr, size_t index, RegPtr r);
   1165 
   1166 #ifdef JS_CODEGEN_X86
   1167  // Store r in instance scratch storage after first loading the instance from
   1168  // the frame into the regForInstance.  regForInstance must be neither of the
   1169  // registers in r.
   1170  void stashI64(RegPtr regForInstance, RegI64 r);
   1171 
   1172  // Load r from the instance scratch storage after first loading the instance
   1173  // from the frame into the regForInstance.  regForInstance can be one of the
   1174  // registers in r.
   1175  void unstashI64(RegPtr regForInstance, RegI64 r);
   1176 #endif
   1177 
   1178  //////////////////////////////////////////////////////////////////////
   1179  //
   1180  // Code generators for actual operations.
   1181 
   1182  template <typename RegType, typename IntType>
   1183  void quotientOrRemainder(RegType rs, RegType rsd, RegType reserved,
   1184                           IsUnsigned isUnsigned, ZeroOnOverflow zeroOnOverflow,
   1185                           bool isConst, IntType c,
   1186                           void (*operate)(MacroAssembler&, RegType, RegType,
   1187                                           RegType, IsUnsigned));
   1188 
   1189  [[nodiscard]] bool truncateF32ToI32(RegF32 src, RegI32 dest,
   1190                                      TruncFlags flags);
   1191  [[nodiscard]] bool truncateF64ToI32(RegF64 src, RegI32 dest,
   1192                                      TruncFlags flags);
   1193 
   1194 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT
   1195  [[nodiscard]] RegF64 needTempForFloatingToI64(TruncFlags flags);
   1196  [[nodiscard]] bool truncateF32ToI64(RegF32 src, RegI64 dest, TruncFlags flags,
   1197                                      RegF64 temp);
   1198  [[nodiscard]] bool truncateF64ToI64(RegF64 src, RegI64 dest, TruncFlags flags,
   1199                                      RegF64 temp);
   1200 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
   1201 
   1202 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
   1203  [[nodiscard]] RegI32 needConvertI64ToFloatTemp(ValType to, bool isUnsigned);
   1204  void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp);
   1205  void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp);
   1206 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
   1207 
   1208  //////////////////////////////////////////////////////////////////////
   1209  //
   1210  // Global variable access.
   1211 
   1212  Address addressOfGlobalVar(const GlobalDesc& global, RegPtr tmp);
   1213 
   1214  //////////////////////////////////////////////////////////////////////
   1215  //
   1216  // Table access.
   1217 
   1218  Address addressOfTableField(uint32_t tableIndex, uint32_t fieldOffset,
   1219                              RegPtr instance);
   1220  void loadTableLength(uint32_t tableIndex, RegPtr instance, RegI32 length);
   1221  void loadTableElements(uint32_t tableIndex, RegPtr instance, RegPtr elements);
   1222 
   1223  //////////////////////////////////////////////////////////////////////
   1224  //
   1225  // Heap access.
   1226 
   1227  void bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check,
   1228                     uint32_t local);
   1229  void bceLocalIsUpdated(uint32_t local);
   1230 
   1231  // Fold offsets into ptr and bounds check as necessary.  The instance will be
   1232  // valid in cases where it's needed.
   1233  template <typename RegAddressType>
   1234  void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check,
   1235                           RegPtr instance, RegAddressType ptr);
   1236 
   1237  void branchAddNoOverflow(uint64_t offset, RegI32 ptr, Label* ok);
   1238  void branchTestLowZero(RegI32 ptr, Imm32 mask, Label* ok);
   1239  void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize,
   1240                                    RegPtr instance, RegI32 ptr, Label* ok);
   1241  void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize,
   1242                                 RegPtr instance, RegI32 ptr, Label* ok);
   1243 
   1244  void branchAddNoOverflow(uint64_t offset, RegI64 ptr, Label* ok);
   1245  void branchTestLowZero(RegI64 ptr, Imm32 mask, Label* ok);
   1246  void boundsCheck4GBOrLargerAccess(uint32_t memoryIndex, unsigned byteSize,
   1247                                    RegPtr instance, RegI64 ptr, Label* ok);
   1248  void boundsCheckBelow4GBAccess(uint32_t memoryIndex, unsigned byteSize,
   1249                                 RegPtr instance, RegI64 ptr, Label* ok);
   1250 
   1251  // Some consumers depend on the returned Address not incorporating instance,
   1252  // as instance may be the scratch register.
   1253  template <typename RegAddressType>
   1254  Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
   1255                                    AccessCheck* check, RegPtr instance,
   1256                                    RegAddressType ptr);
   1257 
   1258  template <typename RegAddressType>
   1259  void computeEffectiveAddress(MemoryAccessDesc* access);
   1260 
   1261  [[nodiscard]] bool needInstanceForAccess(const MemoryAccessDesc* access,
   1262                                           const AccessCheck& check);
   1263 
   1264  // ptr and dest may be the same iff dest is I32.
   1265  // This may destroy ptr even if ptr and dest are not the same.
   1266  void executeLoad(MemoryAccessDesc* access, AccessCheck* check,
   1267                   RegPtr instance, RegPtr memoryBase, RegI32 ptr, AnyReg dest,
   1268                   RegI32 temp);
   1269  void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
   1270            RegPtr memoryBase, RegI32 ptr, AnyReg dest, RegI32 temp);
   1271  void load(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
   1272            RegPtr memoryBase, RegI64 ptr, AnyReg dest, RegI64 temp);
   1273 
   1274  template <typename RegType>
   1275  void doLoadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type);
   1276 
   1277  void loadCommon(MemoryAccessDesc* access, AccessCheck check, ValType type);
   1278 
   1279  // ptr and src must not be the same register.
   1280  // This may destroy ptr and src.
   1281  void executeStore(MemoryAccessDesc* access, AccessCheck* check,
   1282                    RegPtr instance, RegPtr memoryBase, RegI32 ptr, AnyReg src,
   1283                    RegI32 temp);
   1284  void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
   1285             RegPtr memoryBase, RegI32 ptr, AnyReg src, RegI32 temp);
   1286  void store(MemoryAccessDesc* access, AccessCheck* check, RegPtr instance,
   1287             RegPtr memoryBase, RegI64 ptr, AnyReg src, RegI64 temp);
   1288 
   1289  template <typename RegType>
   1290  void doStoreCommon(MemoryAccessDesc* access, AccessCheck check,
   1291                     ValType resultType);
   1292 
   1293  void storeCommon(MemoryAccessDesc* access, AccessCheck check,
   1294                   ValType resultType);
   1295 
   1296  void atomicLoad(MemoryAccessDesc* access, ValType type);
   1297 #if !defined(JS_64BIT)
   1298  template <typename RegAddressType>
   1299  void atomicLoad64(MemoryAccessDesc* desc);
   1300 #endif
   1301 
   1302  void atomicStore(MemoryAccessDesc* access, ValType type);
   1303 
   1304  void atomicRMW(MemoryAccessDesc* access, ValType type, AtomicOp op);
   1305  template <typename RegAddressType>
   1306  void atomicRMW32(MemoryAccessDesc* access, ValType type, AtomicOp op);
   1307  template <typename RegAddressType>
   1308  void atomicRMW64(MemoryAccessDesc* access, ValType type, AtomicOp op);
   1309 
   1310  void atomicXchg(MemoryAccessDesc* access, ValType type);
   1311  template <typename RegAddressType>
   1312  void atomicXchg64(MemoryAccessDesc* access, WantResult wantResult);
   1313  template <typename RegAddressType>
   1314  void atomicXchg32(MemoryAccessDesc* access, ValType type);
   1315 
   1316  void atomicCmpXchg(MemoryAccessDesc* access, ValType type);
   1317  template <typename RegAddressType>
   1318  void atomicCmpXchg32(MemoryAccessDesc* access, ValType type);
   1319  template <typename RegAddressType>
   1320  void atomicCmpXchg64(MemoryAccessDesc* access, ValType type);
   1321 
   1322  template <typename RegType>
   1323  RegType popConstMemoryAccess(MemoryAccessDesc* access, AccessCheck* check);
   1324  template <typename RegType>
   1325  RegType popMemoryAccess(MemoryAccessDesc* access, AccessCheck* check);
   1326 
   1327  void pushHeapBase(uint32_t memoryIndex);
   1328 
   1329  ////////////////////////////////////////////////////////////////////////////
   1330  //
   1331  // Platform-specific popping and register targeting.
   1332 
   1333  // The simple popping methods pop values into targeted registers; the caller
   1334  // can free registers using standard functions.  These are always called
   1335  // popXForY where X says something about types and Y something about the
   1336  // operation being targeted.
   1337 
   1338  RegI32 needRotate64Temp();
   1339  void popAndAllocateForDivAndRemI32(RegI32* r0, RegI32* r1, RegI32* reserved);
   1340  void popAndAllocateForMulI64(RegI64* r0, RegI64* r1, RegI32* temp);
   1341 #ifndef RABALDR_INT_DIV_I64_CALLOUT
   1342  void popAndAllocateForDivAndRemI64(RegI64* r0, RegI64* r1, RegI64* reserved,
   1343                                     IsRemainder isRemainder);
   1344 #endif
   1345  RegI32 popI32RhsForShift();
   1346  RegI32 popI32RhsForShiftI64();
   1347  RegI64 popI64RhsForShift();
   1348  RegI32 popI32RhsForRotate();
   1349  RegI64 popI64RhsForRotate();
   1350  void popI32ForSignExtendI64(RegI64* r0);
   1351  void popI64ForSignExtendI64(RegI64* r0);
   1352 
   1353  ////////////////////////////////////////////////////////////
   1354  //
   1355  // Sundry helpers.
   1356 
   1357  // Retrieve the current bytecodeOffset.
   1358  inline BytecodeOffset bytecodeOffset() const;
   1359 
   1360  // Get a trap site description for a trap that would occur in the current
   1361  // opcode.
   1362  inline TrapSiteDesc trapSiteDesc() const;
   1363 
   1364  // Generate a trap instruction for the current bytecodeOffset.
   1365  inline void trap(Trap t) const;
   1366 
   1367  // Abstracted helper for throwing, used for throw, rethrow, and rethrowing
   1368  // at the end of a series of catch blocks (if none matched the exception).
   1369  [[nodiscard]] bool throwFrom(RegRef exn);
   1370 
   1371  // Load the specified tag object from the Instance.
   1372  void loadTag(RegPtr instance, uint32_t tagIndex, RegRef tagDst);
   1373 
   1374  // Load the pending exception state from the Instance and then reset it.
   1375  void consumePendingException(RegPtr instance, RegRef* exnDst, RegRef* tagDst);
   1376 
   1377  [[nodiscard]] bool startTryNote(size_t* tryNoteIndex);
   1378  void finishTryNote(size_t tryNoteIndex);
   1379 
   1380  ////////////////////////////////////////////////////////////
   1381  //
   1382  // Barriers support.
   1383 
   1384  // This emits a GC pre-write barrier.  The pre-barrier is needed when we
   1385  // replace a member field with a new value, and the previous field value
   1386  // might have no other referents, and incremental GC is ongoing. The field
   1387  // might belong to an object or be a stack slot or a register or a heap
   1388  // allocated value.
   1389  //
   1390  // let obj = { field: previousValue };
   1391  // obj.field = newValue; // previousValue must be marked with a pre-barrier.
   1392  //
   1393  // The `valueAddr` is the address of the location that we are about to
   1394  // update.  This function preserves that register.
   1395  void emitPreBarrier(RegPtr valueAddr);
   1396 
   1397  // These emit GC post-write barriers. The post-barrier is needed when we
   1398  // replace a member field with a new value, the new value is in the nursery,
   1399  // and the containing object is a tenured object. The field (or the entire
   1400  // containing object) must then be added to the store buffer so that the
   1401  // nursery can be correctly collected. The field might belong to an object or
   1402  // be a stack slot or a register or a heap allocated value.
   1403  //
   1404  // For the difference between 'precise' and 'imprecise', look at the
   1405  // documentation on PostBarrierKind.
   1406 
   1407  // Emits a post-write barrier that creates a whole-cell store buffer entry.
   1408  // See above for details.
   1409  //
   1410  // - `object` is a pointer to the object that contains the field. This
   1411  //   register is preserved by this function.
   1412  // - `value` is the value that was stored in the field. This register is
   1413  //   preserved by this function.
   1414  // - `temp` is consumed by this function.
   1415  [[nodiscard]] bool emitPostBarrierWholeCell(RegRef object, RegRef value,
   1416                                              RegPtr temp);
   1417 
   1418  // Emits a post-write barrier of type WasmAnyRefEdge, imprecisely. See above
   1419  // for details.
   1420  //
   1421  // - `object` is a pointer to the object that contains the field. It is used,
   1422  //   if present, to skip adding a store buffer entry when the containing
   1423  //   object is in the nursery. This register is preserved by this function.
   1424  // - `valueAddr` is the address of the location that we are writing to. This
   1425  //   register is consumed by this function.
   1426  // - `value` is the value that was stored in the field. This register is
   1427  //   preserved by this function.
   1428  [[nodiscard]] bool emitPostBarrierEdgeImprecise(
   1429      const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef value);
   1430 
   1431  // Emits a post-write barrier of type WasmAnyRefEdge, precisely. See above for
   1432  // details.
   1433  //
   1434  // - `object` is a pointer to the object that contains the field. It is used,
   1435  //   if present, to skip adding a store buffer entry when the containing
   1436  //   object is in the nursery. This register is preserved by this function.
   1437  // - `valueAddr` is the address of the location that we are writing to. This
   1438  //   register is consumed by this function.
   1439  // - `prevValue` is the value that existed in the field before `value` was
   1440  //   stored. This register is consumed by this function.
   1441  // - `value` is the value that was stored in the field. This register is
   1442  //   preserved by this function.
   1443  [[nodiscard]] bool emitPostBarrierEdgePrecise(
   1444      const mozilla::Maybe<RegRef>& object, RegPtr valueAddr, RegRef prevValue,
   1445      RegRef value);
   1446 
   1447  // Emits a store to a JS object pointer at the address `valueAddr`, which is
   1448  // inside the GC cell `object`.
   1449  //
   1450  // Preserves `object` and `value`. Consumes `valueAddr`.
   1451  [[nodiscard]] bool emitBarrieredStore(const mozilla::Maybe<RegRef>& object,
   1452                                        RegPtr valueAddr, RegRef value,
   1453                                        PreBarrierKind preBarrierKind,
   1454                                        PostBarrierKind postBarrierKind);
   1455 
   1456  // Emits a store of nullptr to a JS object pointer at the address valueAddr.
   1457  // Preserves `valueAddr`.
   1458  void emitBarrieredClear(RegPtr valueAddr);
   1459 
   1460  ////////////////////////////////////////////////////////////
   1461  //
   1462  // Machinery for optimized conditional branches.  See comments in the
   1463  // implementation.
   1464 
   1465  void setLatentCompare(Assembler::Condition compareOp, ValType operandType);
   1466  void setLatentCompare(Assembler::DoubleCondition compareOp,
   1467                        ValType operandType);
   1468  void setLatentEqz(ValType operandType);
   1469  bool hasLatentOp() const;
   1470  void resetLatentOp();
   1471  // Jump to the given branch, passing results, if the condition, `cond`
   1472  // matches between `lhs` and `rhs.
   1473  template <typename Cond, typename Lhs, typename Rhs>
   1474  [[nodiscard]] bool jumpConditionalWithResults(BranchState* b, Cond cond,
   1475                                                Lhs lhs, Rhs rhs);
   1476  // Jump to the given branch, passing results, if the WasmGcObject, `object`,
   1477  // is a subtype of `destType`.
   1478  [[nodiscard]] bool jumpConditionalWithResults(BranchState* b, RegRef object,
   1479                                                MaybeRefType sourceType,
   1480                                                RefType destType,
   1481                                                bool onSuccess);
   1482  template <typename Cond>
   1483  [[nodiscard]] bool sniffConditionalControlCmp(Cond compareOp,
   1484                                                ValType operandType);
   1485  [[nodiscard]] bool sniffConditionalControlEqz(ValType operandType);
   1486  void emitBranchSetup(BranchState* b);
   1487  [[nodiscard]] bool emitBranchPerform(BranchState* b);
   1488 
   1489  //////////////////////////////////////////////////////////////////////
   1490 
   1491  [[nodiscard]] bool emitBody();
   1492  [[nodiscard]] bool emitBlock();
   1493  [[nodiscard]] bool emitLoop();
   1494  [[nodiscard]] bool emitIf();
   1495  [[nodiscard]] bool emitElse();
   1496  // Used for common setup for catch and catch_all.
   1497  void emitCatchSetup(LabelKind kind, Control& tryCatch,
   1498                      const ResultType& resultType);
   1499 
   1500  [[nodiscard]] bool emitTry();
   1501  [[nodiscard]] bool emitTryTable();
   1502  [[nodiscard]] bool emitCatch();
   1503  [[nodiscard]] bool emitCatchAll();
   1504  [[nodiscard]] bool emitDelegate();
   1505  [[nodiscard]] bool emitThrow();
   1506  [[nodiscard]] bool emitThrowRef();
   1507  [[nodiscard]] bool emitRethrow();
   1508  [[nodiscard]] bool emitEnd();
   1509  [[nodiscard]] bool emitBr();
   1510  [[nodiscard]] bool emitBrIf();
   1511  [[nodiscard]] bool emitBrTable();
   1512  [[nodiscard]] bool emitDrop();
   1513  [[nodiscard]] bool emitReturn();
   1514  [[nodiscard]] bool emitCall();
   1515  [[nodiscard]] bool emitReturnCall();
   1516  [[nodiscard]] bool emitCallIndirect();
   1517  [[nodiscard]] bool emitReturnCallIndirect();
   1518  [[nodiscard]] bool emitUnaryMathBuiltinCall(SymbolicAddress callee,
   1519                                              ValType operandType);
   1520  [[nodiscard]] bool emitGetLocal();
   1521  [[nodiscard]] bool emitSetLocal();
   1522  [[nodiscard]] bool emitTeeLocal();
   1523  [[nodiscard]] bool emitGetGlobal();
   1524  [[nodiscard]] bool emitSetGlobal();
   1525  [[nodiscard]] RegPtr maybeLoadMemoryBaseForAccess(
   1526      RegPtr instance, const MemoryAccessDesc* access);
   1527  [[nodiscard]] RegPtr maybeLoadInstanceForAccess(
   1528      const MemoryAccessDesc* access, const AccessCheck& check);
   1529  [[nodiscard]] RegPtr maybeLoadInstanceForAccess(
   1530      const MemoryAccessDesc* access, const AccessCheck& check,
   1531      RegPtr specific);
   1532  [[nodiscard]] bool emitLoad(ValType type, Scalar::Type viewType);
   1533  [[nodiscard]] bool emitStore(ValType resultType, Scalar::Type viewType);
   1534  [[nodiscard]] bool emitSelect(bool typed);
   1535 
   1536  template <bool isSetLocal>
   1537  [[nodiscard]] bool emitSetOrTeeLocal(uint32_t slot);
   1538 
   1539  [[nodiscard]] bool endBlock(ResultType type);
   1540  [[nodiscard]] bool endIfThen(ResultType type);
   1541  [[nodiscard]] bool endIfThenElse(ResultType type);
   1542  [[nodiscard]] bool endTryCatch(ResultType type);
   1543  [[nodiscard]] bool endTryTable(ResultType type);
   1544 
   1545  void doReturn(ContinuationKind kind);
   1546 
   1547  void emitCompareI32(Assembler::Condition compareOp, ValType compareType);
   1548  void emitCompareI64(Assembler::Condition compareOp, ValType compareType);
   1549  void emitCompareF32(Assembler::DoubleCondition compareOp,
   1550                      ValType compareType);
   1551  void emitCompareF64(Assembler::DoubleCondition compareOp,
   1552                      ValType compareType);
   1553  void emitCompareRef(Assembler::Condition compareOp, ValType compareType);
   1554 
   1555  template <typename CompilerType>
   1556  inline CompilerType& selectCompiler();
   1557 
   1558  template <typename SourceType, typename DestType>
   1559  inline void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs,
   1560                                  DestType rd));
   1561 
   1562  template <typename SourceType, typename DestType, typename TempType>
   1563  inline void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs,
   1564                                  DestType rd, TempType temp));
   1565 
   1566  template <typename SourceType, typename DestType, typename ImmType>
   1567  inline void emitUnop(ImmType immediate, void (*op)(MacroAssembler&, ImmType,
   1568                                                     SourceType, DestType));
   1569 
   1570  template <typename CompilerType, typename RegType>
   1571  inline void emitUnop(void (*op)(CompilerType& compiler, RegType rsd));
   1572 
   1573  template <typename RegType, typename TempType>
   1574  inline void emitUnop(void (*op)(BaseCompiler& bc, RegType rsd, TempType rt),
   1575                       TempType (*getSpecializedTemp)(BaseCompiler& bc));
   1576 
   1577  template <typename CompilerType, typename RhsType, typename LhsDestType>
   1578  inline void emitBinop(void (*op)(CompilerType& masm, RhsType src,
   1579                                   LhsDestType srcDest));
   1580 
   1581  template <typename RhsDestType, typename LhsType>
   1582  inline void emitBinop(void (*op)(MacroAssembler& masm, RhsDestType src,
   1583                                   LhsType srcDest, RhsDestOp));
   1584 
   1585  template <typename RhsType, typename LhsDestType, typename TempType>
   1586  inline void emitBinop(void (*)(MacroAssembler& masm, RhsType rs,
   1587                                 LhsDestType rsd, TempType temp));
   1588 
   1589  template <typename RhsType, typename LhsDestType, typename TempType1,
   1590            typename TempType2>
   1591  inline void emitBinop(void (*)(MacroAssembler& masm, RhsType rs,
   1592                                 LhsDestType rsd, TempType1 temp1,
   1593                                 TempType2 temp2));
   1594 
   1595  template <typename RhsType, typename LhsDestType, typename ImmType>
   1596  inline void emitBinop(ImmType immediate, void (*op)(MacroAssembler&, ImmType,
   1597                                                      RhsType, LhsDestType));
   1598 
   1599  template <typename RhsType, typename LhsDestType, typename ImmType,
   1600            typename TempType1, typename TempType2>
   1601  inline void emitBinop(ImmType immediate,
   1602                        void (*op)(MacroAssembler&, ImmType, RhsType,
   1603                                   LhsDestType, TempType1 temp1,
   1604                                   TempType2 temp2));
   1605 
   1606  template <typename CompilerType1, typename CompilerType2, typename RegType,
   1607            typename ImmType>
   1608  inline void emitBinop(void (*op)(CompilerType1& compiler1, RegType rs,
   1609                                   RegType rd),
   1610                        void (*opConst)(CompilerType2& compiler2, ImmType c,
   1611                                        RegType rd),
   1612                        RegType (BaseCompiler::*rhsPopper)() = nullptr);
   1613 
   1614  template <typename CompilerType, typename ValType>
   1615  inline void emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1,
   1616                                     ValType srcDest));
   1617 
   1618  template <typename CompilerType, typename ValType>
   1619  inline void emitTernary(void (*op)(CompilerType&, ValType src0, ValType src1,
   1620                                     ValType srcDest, ValType temp));
   1621 
   1622  template <typename CompilerType, typename ValType>
   1623  inline void emitTernaryResultLast(void (*op)(CompilerType&, ValType src0,
   1624                                               ValType src1, ValType srcDest));
   1625 
   1626  template <typename R>
   1627  [[nodiscard]] inline bool emitInstanceCallOp(
   1628      const SymbolicAddressSignature& fn, R reader);
   1629 
   1630  template <typename A1, typename R>
   1631  [[nodiscard]] inline bool emitInstanceCallOp(
   1632      const SymbolicAddressSignature& fn, R reader);
   1633 
   1634  template <typename A1, typename A2, typename R>
   1635  [[nodiscard]] inline bool emitInstanceCallOp(
   1636      const SymbolicAddressSignature& fn, R reader);
   1637 
   1638  void emitMultiplyI64();
   1639  void emitQuotientI32();
   1640  void emitQuotientU32();
   1641  void emitRemainderI32();
   1642  void emitRemainderU32();
   1643 #ifdef RABALDR_INT_DIV_I64_CALLOUT
   1644  [[nodiscard]] bool emitDivOrModI64BuiltinCall(SymbolicAddress callee,
   1645                                                ValType operandType);
   1646 #else
   1647  void emitQuotientI64();
   1648  void emitQuotientU64();
   1649  void emitRemainderI64();
   1650  void emitRemainderU64();
   1651 #endif
   1652  void emitRotrI64();
   1653  void emitRotlI64();
   1654  void emitEqzI32();
   1655  void emitEqzI64();
   1656  template <TruncFlags flags>
   1657  [[nodiscard]] bool emitTruncateF32ToI32();
   1658  template <TruncFlags flags>
   1659  [[nodiscard]] bool emitTruncateF64ToI32();
   1660 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
   1661  [[nodiscard]] bool emitConvertFloatingToInt64Callout(SymbolicAddress callee,
   1662                                                       ValType operandType,
   1663                                                       ValType resultType);
   1664 #else
   1665  template <TruncFlags flags>
   1666  [[nodiscard]] bool emitTruncateF32ToI64();
   1667  template <TruncFlags flags>
   1668  [[nodiscard]] bool emitTruncateF64ToI64();
   1669 #endif
   1670  void emitExtendI64_8();
   1671  void emitExtendI64_16();
   1672  void emitExtendI64_32();
   1673  void emitExtendI32ToI64();
   1674  void emitExtendU32ToI64();
   1675 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
   1676  [[nodiscard]] bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee,
   1677                                                       ValType operandType,
   1678                                                       ValType resultType);
   1679 #else
   1680  void emitConvertU64ToF32();
   1681  void emitConvertU64ToF64();
   1682 #endif
   1683  void emitRound(RoundingMode roundingMode, ValType operandType);
   1684 
   1685  // Generate a call to the instance function denoted by `builtin`, passing as
   1686  // args the top elements of the compiler's value stack and optionally an
   1687  // Instance* too.  The relationship between the top of stack and arg
   1688  // ordering is as follows.  If the value stack looks like this:
   1689  //
   1690  //   A  <- least recently pushed
   1691  //   B
   1692  //   C  <- most recently pushed
   1693  //
   1694  // then the called function is expected to have signature [if an Instance*
   1695  // is also to be passed]:
   1696  //
   1697  //   static Instance::foo(Instance*, A, B, C)
   1698  //
   1699  // and the SymbolicAddressSignature::argTypes array will be
   1700  //
   1701  //   {_PTR, _A, _B, _C, _END}  // _PTR is for the Instance*
   1702  //
   1703  // (see WasmBuiltins.cpp).  In short, the most recently pushed value is the
   1704  // rightmost argument to the function.
   1705  [[nodiscard]] bool emitInstanceCall(const SymbolicAddressSignature& builtin);
   1706 
   1707  [[nodiscard]] bool emitMemoryGrow();
   1708  [[nodiscard]] bool emitMemorySize();
   1709 
   1710  [[nodiscard]] bool emitRefFunc();
   1711  [[nodiscard]] bool emitRefNull();
   1712  [[nodiscard]] bool emitRefIsNull();
   1713  [[nodiscard]] bool emitRefAsNonNull();
   1714  [[nodiscard]] bool emitBrOnNull();
   1715  [[nodiscard]] bool emitBrOnNonNull();
   1716  [[nodiscard]] bool emitCallRef();
   1717  [[nodiscard]] bool emitReturnCallRef();
   1718 
   1719  [[nodiscard]] bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
   1720  [[nodiscard]] bool emitAtomicLoad(ValType type, Scalar::Type viewType);
   1721  [[nodiscard]] bool emitAtomicRMW(ValType type, Scalar::Type viewType,
   1722                                   AtomicOp op);
   1723  [[nodiscard]] bool emitAtomicStore(ValType type, Scalar::Type viewType);
   1724  [[nodiscard]] bool emitWait(ValType type, uint32_t byteSize);
   1725  [[nodiscard]] bool atomicWait(ValType type, MemoryAccessDesc* access);
   1726  [[nodiscard]] bool emitNotify();
   1727  [[nodiscard]] bool atomicNotify(MemoryAccessDesc* access);
   1728  [[nodiscard]] bool emitFence();
   1729  [[nodiscard]] bool emitAtomicXchg(ValType type, Scalar::Type viewType);
   1730  [[nodiscard]] bool emitMemInit();
   1731  [[nodiscard]] bool emitMemCopy();
   1732  [[nodiscard]] bool memCopyCall(uint32_t dstMemIndex, uint32_t srcMemIndex);
   1733  void memCopyInlineM32();
   1734  [[nodiscard]] bool emitTableCopy();
   1735  [[nodiscard]] bool emitDataOrElemDrop(bool isData);
   1736  [[nodiscard]] bool emitMemFill();
   1737  [[nodiscard]] bool memFillCall(uint32_t memoryIndex);
   1738  void memFillInlineM32();
   1739  [[nodiscard]] bool emitTableInit();
   1740  [[nodiscard]] bool emitTableFill();
   1741  [[nodiscard]] bool emitMemDiscard();
   1742  [[nodiscard]] bool emitTableGet();
   1743  [[nodiscard]] bool emitTableGrow();
   1744  [[nodiscard]] bool emitTableSet();
   1745  [[nodiscard]] bool emitTableSize();
   1746 
   1747  void emitTableBoundsCheck(uint32_t tableIndex, RegI32 address,
   1748                            RegPtr instance);
   1749  [[nodiscard]] bool emitTableGetAnyRef(uint32_t tableIndex);
   1750  [[nodiscard]] bool emitTableSetAnyRef(uint32_t tableIndex);
   1751 
   1752  [[nodiscard]] bool emitStructNew();
   1753  [[nodiscard]] bool emitStructNewDefault();
   1754  [[nodiscard]] bool emitStructGet(FieldWideningOp wideningOp);
   1755  [[nodiscard]] bool emitStructSet();
   1756  [[nodiscard]] bool emitArrayNew();
   1757  [[nodiscard]] bool emitArrayNewFixed();
   1758  [[nodiscard]] bool emitArrayNewDefault();
   1759  [[nodiscard]] bool emitArrayNewData();
   1760  [[nodiscard]] bool emitArrayNewElem();
   1761  [[nodiscard]] bool emitArrayInitData();
   1762  [[nodiscard]] bool emitArrayInitElem();
   1763  [[nodiscard]] bool emitArrayGet(FieldWideningOp wideningOp);
   1764  [[nodiscard]] bool emitArraySet();
   1765  [[nodiscard]] bool emitArrayLen();
   1766  [[nodiscard]] bool emitArrayCopy();
   1767  [[nodiscard]] bool emitArrayFill();
   1768  [[nodiscard]] bool emitRefI31();
   1769  [[nodiscard]] bool emitI31Get(FieldWideningOp wideningOp);
   1770  [[nodiscard]] bool emitRefTest(bool nullable);
   1771  [[nodiscard]] bool emitRefCast(bool nullable);
   1772  [[nodiscard]] bool emitBrOnCastCommon(bool onSuccess,
   1773                                        uint32_t labelRelativeDepth,
   1774                                        const ResultType& labelType,
   1775                                        MaybeRefType sourceType,
   1776                                        RefType destType);
   1777  [[nodiscard]] bool emitBrOnCast(bool onSuccess);
   1778  [[nodiscard]] bool emitAnyConvertExtern();
   1779  [[nodiscard]] bool emitExternConvertAny();
   1780 
   1781  // Utility classes/methods to add trap information related to
   1782  // null pointer dereferences/accesses.
   1783  struct NoNullCheck {
   1784    static void emitNullCheck(BaseCompiler* bc, RegRef rp) {}
   1785    static void emitTrapSite(BaseCompiler* bc, FaultingCodeOffset fco,
   1786                             TrapMachineInsn tmi) {}
   1787  };
   1788  struct SignalNullCheck {
   1789    static void emitNullCheck(BaseCompiler* bc, RegRef rp);
   1790    static void emitTrapSite(BaseCompiler* bc, FaultingCodeOffset fco,
   1791                             TrapMachineInsn tmi);
   1792  };
   1793 
   1794  // Load a pointer to the AllocSite for current bytecode offset
   1795  RegPtr loadAllocSiteInstanceData(uint32_t allocSiteIndex);
   1796 
   1797  // Gets alloc site allociated with current instruction
   1798  [[nodiscard]] bool readAllocSiteIndex(uint32_t* index);
   1799 
   1800  // Load a pointer to the SuperTypeVector for a given type index
   1801  RegPtr loadSuperTypeVector(uint32_t typeIndex);
   1802 
   1803  // Emits allocation code for a GC struct. The struct may have an out-of-line
   1804  // data area; if so, `isOutlineStruct` will be true and `outlineBase` will be
   1805  // allocated and must be freed.
   1806  template <bool ZeroFields>
   1807  bool emitStructAlloc(uint32_t typeIndex, RegRef* object,
   1808                       bool* isOutlineStruct, RegPtr* outlineBase,
   1809                       uint32_t allocSiteIndex);
   1810  // Emits allocation code for a dynamically-sized GC array.
   1811  template <bool ZeroFields>
   1812  bool emitArrayAlloc(uint32_t typeIndex, RegRef object, RegI32 numElements,
   1813                      uint32_t elemSize, uint32_t allocSiteIndex);
   1814  // Emits allocation code for a fixed-size GC array.
   1815  template <bool ZeroFields>
   1816  bool emitArrayAllocFixed(uint32_t typeIndex, RegRef object,
   1817                           uint32_t numElements, uint32_t elemSize,
   1818                           uint32_t allocSiteIndex);
   1819 
   1820  template <typename NullCheckPolicy>
   1821  RegPtr emitGcArrayGetData(RegRef rp);
   1822  template <typename NullCheckPolicy>
   1823  RegI32 emitGcArrayGetNumElements(RegRef rp);
   1824  void emitGcArrayBoundsCheck(RegI32 index, RegI32 numElements);
   1825  template <typename T, typename NullCheckPolicy>
   1826  void emitGcGet(StorageType type, FieldWideningOp wideningOp, const T& src);
   1827  template <typename T, typename NullCheckPolicy>
   1828  void emitGcSetScalar(const T& dst, StorageType type, AnyReg value);
   1829 
   1830  BranchIfRefSubtypeRegisters allocRegistersForBranchIfRefSubtype(
   1831      RefType destType);
   1832  void freeRegistersForBranchIfRefSubtype(
   1833      const BranchIfRefSubtypeRegisters& regs);
   1834 
   1835  // Write `value` to wasm struct `object`, at `areaBase + areaOffset`.  The
   1836  // caller must decide on the in- vs out-of-lineness before the call and set
   1837  // the latter two accordingly; this routine does not take that into account.
   1838  // The value in `object` is unmodified, but `areaBase` and `value` may get
   1839  // trashed.
   1840  template <typename NullCheckPolicy>
   1841  [[nodiscard]] bool emitGcStructSet(RegRef object, RegPtr areaBase,
   1842                                     uint32_t areaOffset, StorageType type,
   1843                                     AnyReg value,
   1844                                     PreBarrierKind preBarrierKind);
   1845 
   1846  [[nodiscard]] bool emitGcArraySet(RegRef object, RegPtr data, RegI32 index,
   1847                                    const ArrayType& array, AnyReg value,
   1848                                    PreBarrierKind preBarrierKind,
   1849                                    PostBarrierKind postBarrierKind);
   1850 
   1851 #ifdef ENABLE_WASM_SIMD
   1852  void emitVectorAndNot();
   1853 #  ifdef ENABLE_WASM_RELAXED_SIMD
   1854  void emitDotI8x16I7x16AddS();
   1855 #  endif
   1856 
   1857  void loadSplat(MemoryAccessDesc* access);
   1858  void loadZero(MemoryAccessDesc* access);
   1859  void loadExtend(MemoryAccessDesc* access, Scalar::Type viewType);
   1860  void loadLane(MemoryAccessDesc* access, uint32_t laneIndex);
   1861  void storeLane(MemoryAccessDesc* access, uint32_t laneIndex);
   1862 
   1863  [[nodiscard]] bool emitLoadSplat(Scalar::Type viewType);
   1864  [[nodiscard]] bool emitLoadZero(Scalar::Type viewType);
   1865  [[nodiscard]] bool emitLoadExtend(Scalar::Type viewType);
   1866  [[nodiscard]] bool emitLoadLane(uint32_t laneSize);
   1867  [[nodiscard]] bool emitStoreLane(uint32_t laneSize);
   1868  [[nodiscard]] bool emitVectorShuffle();
   1869  [[nodiscard]] bool emitVectorLaneSelect();
   1870 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
   1871  [[nodiscard]] bool emitVectorShiftRightI64x2();
   1872 #  endif
   1873 #endif
   1874  [[nodiscard]] bool emitCallBuiltinModuleFunc();
   1875 };
   1876 
   1877 }  // namespace wasm
   1878 }  // namespace js
   1879 
   1880 #endif  // wasm_wasm_baseline_object_h