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 /*
20  * [SMDOC] WebAssembly baseline compiler (RabaldrMonkey)
21  *
22  * General assumptions for 32-bit vs 64-bit code:
23  *
24  * - A 32-bit register can be extended in-place to a 64-bit register on 64-bit
25  *   systems.
26  *
27  * - Code that knows that Register64 has a '.reg' member on 64-bit systems and
28  *   '.high' and '.low' members on 32-bit systems, or knows the implications
29  *   thereof, is #ifdef JS_PUNBOX64.  All other code is #if(n)?def JS_64BIT.
30  *
31  *
32  * Coding standards:
33  *
34  * - In "small" code generating functions (eg emitMultiplyF64, emitQuotientI32,
35  *   and surrounding functions; most functions fall into this class) where the
36  *   meaning is obvious:
37  *
38  *   Old school:
39  *   - if there is a single source + destination register, it is called 'r'
40  *   - if there is one source and a different destination, they are called 'rs'
41  *     and 'rd'
42  *   - if there is one source + destination register and another source register
43  *     they are called 'r' and 'rs'
44  *   - if there are two source registers and a destination register they are
45  *     called 'rs0', 'rs1', and 'rd'.
46  *
47  *   The new thing:
48  *   - what is called 'r' in the old-school naming scheme is increasingly called
49  *     'rsd' in source+dest cases.
50  *
51  * - Generic temp registers are named /temp[0-9]?/ not /tmp[0-9]?/.
52  *
53  * - Registers can be named non-generically for their function ('rp' for the
54  *   'pointer' register and 'rv' for the 'value' register are typical) and those
55  *   names may or may not have an 'r' prefix.
56  *
57  * - "Larger" code generating functions make their own rules.
58  *
59  *
60  * General status notes:
61  *
62  * "FIXME" indicates a known or suspected bug.  Always has a bug#.
63  *
64  * "TODO" indicates an opportunity for a general improvement, with an additional
65  * tag to indicate the area of improvement.  Usually has a bug#.
66  *
67  * There are lots of machine dependencies here but they are pretty well isolated
68  * to a segment of the compiler.  Many dependencies will eventually be factored
69  * into the MacroAssembler layer and shared with other code generators.
70  *
71  *
72  * High-value compiler performance improvements:
73  *
74  * - (Bug 1316802) The specific-register allocator (the needI32(r), needI64(r)
75  *   etc methods) can avoid syncing the value stack if the specific register is
76  *   in use but there is a free register to shuffle the specific register into.
77  *   (This will also improve the generated code.)  The sync happens often enough
78  *   here to show up in profiles, because it is triggered by integer multiply
79  *   and divide.
80  *
81  *
82  * High-value code generation improvements:
83  *
84  * - (Bug 1316804) brTable pessimizes by always dispatching to code that pops
85  *   the stack and then jumps to the code for the target case.  If no cleanup is
86  *   needed we could just branch conditionally to the target; if the same amount
87  *   of cleanup is needed for all cases then the cleanup can be done before the
88  *   dispatch.  Both are highly likely.
89  *
90  * - (Bug 1316806) Register management around calls: At the moment we sync the
91  *   value stack unconditionally (this is simple) but there are probably many
92  *   common cases where we could instead save/restore live caller-saves
93  *   registers and perform parallel assignment into argument registers.  This
94  *   may be important if we keep some locals in registers.
95  *
96  * - (Bug 1316808) Allocate some locals to registers on machines where there are
97  *   enough registers.  This is probably hard to do well in a one-pass compiler
98  *   but it might be that just keeping register arguments and the first few
99  *   locals in registers is a viable strategy; another (more general) strategy
100  *   is caching locals in registers in straight-line code.  Such caching could
101  *   also track constant values in registers, if that is deemed valuable.  A
102  *   combination of techniques may be desirable: parameters and the first few
103  *   locals could be cached on entry to the function but not statically assigned
104  *   to registers throughout.
105  *
106  *   (On a large corpus of code it should be possible to compute, for every
107  *   signature comprising the types of parameters and locals, and using a static
108  *   weight for loops, a list in priority order of which parameters and locals
109  *   that should be assigned to registers.  Or something like that.  Wasm makes
110  *   this simple.  Static assignments are desirable because they are not flushed
111  *   to memory by the pre-block sync() call.)
112  */
113 
114 #include "wasm/WasmBaselineCompile.h"
115 
116 #include "mozilla/MathAlgorithms.h"
117 #include "mozilla/Maybe.h"
118 #include "mozilla/ScopeExit.h"
119 
120 #include <algorithm>
121 #include <utility>
122 
123 #include "jit/AtomicOp.h"
124 #include "jit/IonTypes.h"
125 #include "jit/JitAllocPolicy.h"
126 #include "jit/Label.h"
127 #include "jit/MIR.h"
128 #include "jit/RegisterAllocator.h"
129 #include "jit/Registers.h"
130 #include "jit/RegisterSets.h"
131 #if defined(JS_CODEGEN_ARM)
132 #  include "jit/arm/Assembler-arm.h"
133 #endif
134 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
135 #  include "jit/x86-shared/Architecture-x86-shared.h"
136 #  include "jit/x86-shared/Assembler-x86-shared.h"
137 #endif
138 #if defined(JS_CODEGEN_MIPS32)
139 #  include "jit/mips-shared/Assembler-mips-shared.h"
140 #  include "jit/mips32/Assembler-mips32.h"
141 #endif
142 #if defined(JS_CODEGEN_MIPS64)
143 #  include "jit/mips-shared/Assembler-mips-shared.h"
144 #  include "jit/mips64/Assembler-mips64.h"
145 #endif
146 #include "js/ScalarType.h"  // js::Scalar::Type
147 #include "util/Memory.h"
148 #include "wasm/TypedObject.h"
149 #include "wasm/WasmGC.h"
150 #include "wasm/WasmGenerator.h"
151 #include "wasm/WasmInstance.h"
152 #include "wasm/WasmOpIter.h"
153 #include "wasm/WasmSignalHandlers.h"
154 #include "wasm/WasmStubs.h"
155 #include "wasm/WasmValidate.h"
156 
157 #include "jit/MacroAssembler-inl.h"
158 
159 using mozilla::DebugOnly;
160 using mozilla::FloorLog2;
161 using mozilla::IsPowerOfTwo;
162 using mozilla::Maybe;
163 
164 namespace js {
165 namespace wasm {
166 
167 using namespace js::jit;
168 
169 using HandleNaNSpecially = bool;
170 using InvertBranch = bool;
171 using IsKnownNotZero = bool;
172 using IsUnsigned = bool;
173 using NeedsBoundsCheck = bool;
174 using WantResult = bool;
175 using ZeroOnOverflow = bool;
176 
177 class BaseStackFrame;
178 
179 // Two flags, useABI and interModule, control how calls are made.
180 //
181 // UseABI::Wasm implies that the Tls/Heap/Global registers are nonvolatile,
182 // except when InterModule::True is also set, when they are volatile.
183 //
184 // UseABI::Builtin implies that the Tls/Heap/Global registers are volatile.
185 // In this case, we require InterModule::False.  The calling convention
186 // is otherwise like UseABI::Wasm.
187 //
188 // UseABI::System implies that the Tls/Heap/Global registers are volatile.
189 // Additionally, the parameter passing mechanism may be slightly different from
190 // the UseABI::Wasm convention.
191 //
192 // When the Tls/Heap/Global registers are not volatile, the baseline compiler
193 // will restore the Tls register from its save slot before the call, since the
194 // baseline compiler uses the Tls register for other things.
195 //
196 // When those registers are volatile, the baseline compiler will reload them
197 // after the call (it will restore the Tls register from the save slot and load
198 // the other two from the Tls data).
199 
200 enum class UseABI { Wasm, Builtin, System };
201 enum class InterModule { False = false, True = true };
202 enum class RhsDestOp { True = true };
203 
204 #if defined(JS_CODEGEN_NONE)
205 #  define RABALDR_SCRATCH_I32
206 #  define RABALDR_SCRATCH_F32
207 #  define RABALDR_SCRATCH_F64
208 
209 static constexpr Register RabaldrScratchI32 = Register::Invalid();
210 static constexpr FloatRegister RabaldrScratchF32 = InvalidFloatReg;
211 static constexpr FloatRegister RabaldrScratchF64 = InvalidFloatReg;
212 #endif
213 
214 #ifdef JS_CODEGEN_ARM64
215 #  define RABALDR_CHUNKY_STACK
216 #  define RABALDR_SCRATCH_I32
217 #  define RABALDR_SCRATCH_F32
218 #  define RABALDR_SCRATCH_F64
219 #  define RABALDR_SCRATCH_V128
220 #  define RABALDR_SCRATCH_F32_ALIASES_F64
221 
222 static constexpr Register RabaldrScratchI32{Registers::x15};
223 
224 // Note, the float scratch regs cannot be registers that are used for parameter
225 // passing in any ABI we use.  Argregs tend to be low-numbered; register 30
226 // should be safe.
227 
228 static constexpr FloatRegister RabaldrScratchF32{FloatRegisters::s30,
229                                                  FloatRegisters::Single};
230 static constexpr FloatRegister RabaldrScratchF64{FloatRegisters::d30,
231                                                  FloatRegisters::Double};
232 #  ifdef ENABLE_WASM_SIMD
233 static constexpr FloatRegister RabaldrScratchV128{FloatRegisters::d30,
234                                                   FloatRegisters::Simd128};
235 #  endif
236 
237 static_assert(RabaldrScratchF32 != ScratchFloat32Reg_, "Too busy");
238 static_assert(RabaldrScratchF64 != ScratchDoubleReg_, "Too busy");
239 #  ifdef ENABLE_WASM_SIMD
240 static_assert(RabaldrScratchV128 != ScratchSimd128Reg, "Too busy");
241 #  endif
242 #endif
243 
244 #ifdef JS_CODEGEN_X86
245 // The selection of EBX here steps gingerly around: the need for EDX
246 // to be allocatable for multiply/divide; ECX to be allocatable for
247 // shift/rotate; EAX (= ReturnReg) to be allocatable as the result
248 // register; EBX not being one of the WasmTableCall registers; and
249 // needing a temp register for load/store that has a single-byte
250 // persona.
251 //
252 // The compiler assumes that RabaldrScratchI32 has a single-byte
253 // persona.  Code for 8-byte atomic operations assumes that
254 // RabaldrScratchI32 is in fact ebx.
255 
256 #  define RABALDR_SCRATCH_I32
257 static constexpr Register RabaldrScratchI32 = ebx;
258 
259 #  define RABALDR_INT_DIV_I64_CALLOUT
260 #endif
261 
262 #ifdef JS_CODEGEN_ARM
263 // We use our own scratch register, because the macro assembler uses
264 // the regular scratch register(s) pretty liberally.  We could
265 // work around that in several cases but the mess does not seem
266 // worth it yet.  CallTempReg2 seems safe.
267 
268 #  define RABALDR_SCRATCH_I32
269 static constexpr Register RabaldrScratchI32 = CallTempReg2;
270 
271 #  define RABALDR_INT_DIV_I64_CALLOUT
272 #  define RABALDR_I64_TO_FLOAT_CALLOUT
273 #  define RABALDR_FLOAT_TO_I64_CALLOUT
274 #endif
275 
276 #ifdef JS_CODEGEN_MIPS32
277 #  define RABALDR_SCRATCH_I32
278 static constexpr Register RabaldrScratchI32 = CallTempReg2;
279 
280 #  define RABALDR_INT_DIV_I64_CALLOUT
281 #  define RABALDR_I64_TO_FLOAT_CALLOUT
282 #  define RABALDR_FLOAT_TO_I64_CALLOUT
283 #endif
284 
285 #ifdef JS_CODEGEN_MIPS64
286 #  define RABALDR_SCRATCH_I32
287 static constexpr Register RabaldrScratchI32 = CallTempReg2;
288 #endif
289 
290 #ifdef RABALDR_SCRATCH_F32_ALIASES_F64
291 #  if !defined(RABALDR_SCRATCH_F32) || !defined(RABALDR_SCRATCH_F64)
292 #    error "Bad configuration"
293 #  endif
294 #endif
295 
296 template <MIRType t>
297 struct RegTypeOf {
298 #ifdef ENABLE_WASM_SIMD
299   static_assert(t == MIRType::Float32 || t == MIRType::Double ||
300                     t == MIRType::Simd128,
301                 "Float mask type");
302 #else
303   static_assert(t == MIRType::Float32 || t == MIRType::Double,
304                 "Float mask type");
305 #endif
306 };
307 
308 template <>
309 struct RegTypeOf<MIRType::Float32> {
310   static constexpr RegTypeName value = RegTypeName::Float32;
311 };
312 template <>
313 struct RegTypeOf<MIRType::Double> {
314   static constexpr RegTypeName value = RegTypeName::Float64;
315 };
316 #ifdef ENABLE_WASM_SIMD
317 template <>
318 struct RegTypeOf<MIRType::Simd128> {
319   static constexpr RegTypeName value = RegTypeName::Vector128;
320 };
321 #endif
322 
323 // The strongly typed register wrappers are especially useful to distinguish
324 // float registers from double registers, but they also clearly distinguish
325 // 32-bit registers from 64-bit register pairs on 32-bit systems.
326 
327 struct RegI32 : public Register {
RegI32js::wasm::RegI32328   RegI32() : Register(Register::Invalid()) {}
RegI32js::wasm::RegI32329   explicit RegI32(Register reg) : Register(reg) {
330     MOZ_ASSERT(reg != Invalid());
331   }
isInvalidjs::wasm::RegI32332   bool isInvalid() const { return *this == Invalid(); }
isValidjs::wasm::RegI32333   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegI32334   static RegI32 Invalid() { return RegI32(); }
335 };
336 
337 struct RegI64 : public Register64 {
RegI64js::wasm::RegI64338   RegI64() : Register64(Register64::Invalid()) {}
RegI64js::wasm::RegI64339   explicit RegI64(Register64 reg) : Register64(reg) {
340     MOZ_ASSERT(reg != Invalid());
341   }
isInvalidjs::wasm::RegI64342   bool isInvalid() const { return *this == Invalid(); }
isValidjs::wasm::RegI64343   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegI64344   static RegI64 Invalid() { return RegI64(); }
345 };
346 
347 // RegRef is for GC-pointers, for non GC-pointers use RegPtr
348 struct RegRef : public Register {
RegRefjs::wasm::RegRef349   RegRef() : Register(Register::Invalid()) {}
RegRefjs::wasm::RegRef350   explicit RegRef(Register reg) : Register(reg) {
351     MOZ_ASSERT(reg != Invalid());
352   }
isInvalidjs::wasm::RegRef353   bool isInvalid() const { return *this == Invalid(); }
isValidjs::wasm::RegRef354   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegRef355   static RegRef Invalid() { return RegRef(); }
356 };
357 
358 // RegPtr is for non GC-pointers, for GC-pointers use RegRef
359 struct RegPtr : public Register {
RegPtrjs::wasm::RegPtr360   RegPtr() : Register(Register::Invalid()) {}
RegPtrjs::wasm::RegPtr361   explicit RegPtr(Register reg) : Register(reg) {
362     MOZ_ASSERT(reg != Invalid());
363   }
isInvalidjs::wasm::RegPtr364   bool isInvalid() const { return *this == Invalid(); }
isValidjs::wasm::RegPtr365   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegPtr366   static RegPtr Invalid() { return RegPtr(); }
367 };
368 
369 struct RegF32 : public FloatRegister {
RegF32js::wasm::RegF32370   RegF32() : FloatRegister() {}
RegF32js::wasm::RegF32371   explicit RegF32(FloatRegister reg) : FloatRegister(reg) {
372     MOZ_ASSERT(isSingle());
373   }
isValidjs::wasm::RegF32374   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegF32375   static RegF32 Invalid() { return RegF32(); }
376 };
377 
378 struct RegF64 : public FloatRegister {
RegF64js::wasm::RegF64379   RegF64() : FloatRegister() {}
RegF64js::wasm::RegF64380   explicit RegF64(FloatRegister reg) : FloatRegister(reg) {
381     MOZ_ASSERT(isDouble());
382   }
isValidjs::wasm::RegF64383   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegF64384   static RegF64 Invalid() { return RegF64(); }
385 };
386 
387 #ifdef ENABLE_WASM_SIMD
388 struct RegV128 : public FloatRegister {
RegV128js::wasm::RegV128389   RegV128() : FloatRegister() {}
RegV128js::wasm::RegV128390   explicit RegV128(FloatRegister reg) : FloatRegister(reg) {
391     MOZ_ASSERT(isSimd128());
392   }
isValidjs::wasm::RegV128393   bool isValid() const { return !isInvalid(); }
Invalidjs::wasm::RegV128394   static RegV128 Invalid() { return RegV128(); }
395 };
396 #endif
397 
398 struct AnyReg {
399   union {
400     RegI32 i32_;
401     RegI64 i64_;
402     RegRef ref_;
403     RegF32 f32_;
404     RegF64 f64_;
405 #ifdef ENABLE_WASM_SIMD
406     RegV128 v128_;
407 #endif
408   };
409 
410   enum {
411     I32,
412     I64,
413     REF,
414     F32,
415     F64,
416 #ifdef ENABLE_WASM_SIMD
417     V128
418 #endif
419   } tag;
420 
AnyRegjs::wasm::AnyReg421   explicit AnyReg(RegI32 r) {
422     tag = I32;
423     i32_ = r;
424   }
AnyRegjs::wasm::AnyReg425   explicit AnyReg(RegI64 r) {
426     tag = I64;
427     i64_ = r;
428   }
AnyRegjs::wasm::AnyReg429   explicit AnyReg(RegF32 r) {
430     tag = F32;
431     f32_ = r;
432   }
AnyRegjs::wasm::AnyReg433   explicit AnyReg(RegF64 r) {
434     tag = F64;
435     f64_ = r;
436   }
437 #ifdef ENABLE_WASM_SIMD
AnyRegjs::wasm::AnyReg438   explicit AnyReg(RegV128 r) {
439     tag = V128;
440     v128_ = r;
441   }
442 #endif
AnyRegjs::wasm::AnyReg443   explicit AnyReg(RegRef r) {
444     tag = REF;
445     ref_ = r;
446   }
447 
i32js::wasm::AnyReg448   RegI32 i32() const {
449     MOZ_ASSERT(tag == I32);
450     return i32_;
451   }
i64js::wasm::AnyReg452   RegI64 i64() const {
453     MOZ_ASSERT(tag == I64);
454     return i64_;
455   }
f32js::wasm::AnyReg456   RegF32 f32() const {
457     MOZ_ASSERT(tag == F32);
458     return f32_;
459   }
f64js::wasm::AnyReg460   RegF64 f64() const {
461     MOZ_ASSERT(tag == F64);
462     return f64_;
463   }
464 #ifdef ENABLE_WASM_SIMD
v128js::wasm::AnyReg465   RegV128 v128() const {
466     MOZ_ASSERT(tag == V128);
467     return v128_;
468   }
469 #endif
refjs::wasm::AnyReg470   RegRef ref() const {
471     MOZ_ASSERT(tag == REF);
472     return ref_;
473   }
474 
anyjs::wasm::AnyReg475   AnyRegister any() const {
476     switch (tag) {
477       case F32:
478         return AnyRegister(f32_);
479       case F64:
480         return AnyRegister(f64_);
481 #ifdef ENABLE_WASM_SIMD
482       case V128:
483         return AnyRegister(v128_);
484 #endif
485       case I32:
486         return AnyRegister(i32_);
487       case I64:
488 #ifdef JS_PUNBOX64
489         return AnyRegister(i64_.reg);
490 #else
491         // The compiler is written so that this is never needed: any() is
492         // called on arbitrary registers for asm.js but asm.js does not have
493         // 64-bit ints.  For wasm, any() is called on arbitrary registers
494         // only on 64-bit platforms.
495         MOZ_CRASH("AnyReg::any() on 32-bit platform");
496 #endif
497       case REF:
498         MOZ_CRASH("AnyReg::any() not implemented for ref types");
499       default:
500         MOZ_CRASH();
501     }
502     // Work around GCC 5 analysis/warning bug.
503     MOZ_CRASH("AnyReg::any(): impossible case");
504   }
505 };
506 
507 // Platform-specific registers.
508 //
509 // All platforms must define struct SpecificRegs.  All 32-bit platforms must
510 // have an abiReturnRegI64 member in that struct.
511 
512 #if defined(JS_CODEGEN_X64)
513 struct SpecificRegs {
514   RegI32 eax, ecx, edx, edi, esi;
515   RegI64 rax, rcx, rdx;
516 
SpecificRegsjs::wasm::SpecificRegs517   SpecificRegs()
518       : eax(RegI32(js::jit::eax)),
519         ecx(RegI32(js::jit::ecx)),
520         edx(RegI32(js::jit::edx)),
521         edi(RegI32(js::jit::edi)),
522         esi(RegI32(js::jit::esi)),
523         rax(RegI64(Register64(js::jit::rax))),
524         rcx(RegI64(Register64(js::jit::rcx))),
525         rdx(RegI64(Register64(js::jit::rdx))) {}
526 };
527 #elif defined(JS_CODEGEN_X86)
528 struct SpecificRegs {
529   RegI32 eax, ecx, edx, edi, esi;
530   RegI64 ecx_ebx, edx_eax, abiReturnRegI64;
531 
SpecificRegsjs::wasm::SpecificRegs532   SpecificRegs()
533       : eax(RegI32(js::jit::eax)),
534         ecx(RegI32(js::jit::ecx)),
535         edx(RegI32(js::jit::edx)),
536         edi(RegI32(js::jit::edi)),
537         esi(RegI32(js::jit::esi)),
538         ecx_ebx(RegI64(Register64(js::jit::ecx, js::jit::ebx))),
539         edx_eax(RegI64(Register64(js::jit::edx, js::jit::eax))),
540         abiReturnRegI64(edx_eax) {}
541 };
542 #elif defined(JS_CODEGEN_ARM)
543 struct SpecificRegs {
544   RegI64 abiReturnRegI64;
545 
SpecificRegsjs::wasm::SpecificRegs546   SpecificRegs() : abiReturnRegI64(ReturnReg64) {}
547 };
548 #elif defined(JS_CODEGEN_ARM64)
549 struct SpecificRegs {};
550 #elif defined(JS_CODEGEN_MIPS32)
551 struct SpecificRegs {
552   RegI64 abiReturnRegI64;
553 
SpecificRegsjs::wasm::SpecificRegs554   SpecificRegs() : abiReturnRegI64(ReturnReg64) {}
555 };
556 #elif defined(JS_CODEGEN_MIPS64)
557 struct SpecificRegs {};
558 #else
559 struct SpecificRegs {
560 #  ifndef JS_64BIT
561   RegI64 abiReturnRegI64;
562 #  endif
563 
SpecificRegsjs::wasm::SpecificRegs564   SpecificRegs() { MOZ_CRASH("BaseCompiler porting interface: SpecificRegs"); }
565 };
566 #endif
567 
568 class BaseCompilerInterface {
569  public:
570   // Spill all spillable registers.
571   //
572   // TODO / OPTIMIZE (Bug 1316802): It's possible to do better here by
573   // spilling only enough registers to satisfy current needs.
574   virtual void sync() = 0;
575   virtual void saveTempPtr(RegPtr r) = 0;
576   virtual void restoreTempPtr(RegPtr r) = 0;
577 };
578 
579 // Register allocator.
580 
581 class BaseRegAlloc {
582   // Notes on float register allocation.
583   //
584   // The general rule in SpiderMonkey is that float registers can alias double
585   // registers, but there are predicates to handle exceptions to that rule:
586   // hasUnaliasedDouble() and hasMultiAlias().  The way aliasing actually
587   // works is platform dependent and exposed through the aliased(n, &r)
588   // predicate, etc.
589   //
590   //  - hasUnaliasedDouble(): on ARM VFPv3-D32 there are double registers that
591   //    cannot be treated as float.
592   //  - hasMultiAlias(): on ARM and MIPS a double register aliases two float
593   //    registers.
594   //
595   // On some platforms (x86, x64, ARM64) but not all (ARM)
596   // ScratchFloat32Register is the same as ScratchDoubleRegister.
597   //
598   // It's a basic invariant of the AllocatableRegisterSet that it deals
599   // properly with aliasing of registers: if s0 or s1 are allocated then d0 is
600   // not allocatable; if s0 and s1 are freed individually then d0 becomes
601   // allocatable.
602 
603   BaseCompilerInterface* bc;
604   AllocatableGeneralRegisterSet availGPR;
605   AllocatableFloatRegisterSet availFPU;
606 #ifdef DEBUG
607   // The registers available after removing ScratchReg, HeapReg, etc.
608   AllocatableGeneralRegisterSet allGPR;
609   AllocatableFloatRegisterSet allFPU;
610   uint32_t scratchTaken;
611 #endif
612 #ifdef JS_CODEGEN_X86
613   AllocatableGeneralRegisterSet singleByteRegs;
614 #endif
615 
hasGPR()616   bool hasGPR() { return !availGPR.empty(); }
617 
hasGPR64()618   bool hasGPR64() {
619 #ifdef JS_PUNBOX64
620     return !availGPR.empty();
621 #else
622     if (availGPR.empty()) {
623       return false;
624     }
625     Register r = allocGPR();
626     bool available = !availGPR.empty();
627     freeGPR(r);
628     return available;
629 #endif
630   }
631 
632   template <MIRType t>
hasFPU()633   bool hasFPU() {
634     return availFPU.hasAny<RegTypeOf<t>::value>();
635   }
636 
isAvailableGPR(Register r)637   bool isAvailableGPR(Register r) { return availGPR.has(r); }
638 
isAvailableFPU(FloatRegister r)639   bool isAvailableFPU(FloatRegister r) { return availFPU.has(r); }
640 
allocGPR(Register r)641   void allocGPR(Register r) {
642     MOZ_ASSERT(isAvailableGPR(r));
643     availGPR.take(r);
644   }
645 
allocGPR()646   Register allocGPR() {
647     MOZ_ASSERT(hasGPR());
648     return availGPR.takeAny();
649   }
650 
allocInt64(Register64 r)651   void allocInt64(Register64 r) {
652 #ifdef JS_PUNBOX64
653     allocGPR(r.reg);
654 #else
655     allocGPR(r.low);
656     allocGPR(r.high);
657 #endif
658   }
659 
allocInt64()660   Register64 allocInt64() {
661     MOZ_ASSERT(hasGPR64());
662 #ifdef JS_PUNBOX64
663     return Register64(availGPR.takeAny());
664 #else
665     Register high = availGPR.takeAny();
666     Register low = availGPR.takeAny();
667     return Register64(high, low);
668 #endif
669   }
670 
671 #ifdef JS_CODEGEN_ARM
672   // r12 is normally the ScratchRegister and r13 is always the stack pointer,
673   // so the highest possible pair has r10 as the even-numbered register.
674 
675   static constexpr uint32_t PAIR_LIMIT = 10;
676 
hasGPRPair()677   bool hasGPRPair() {
678     for (uint32_t i = 0; i <= PAIR_LIMIT; i += 2) {
679       if (isAvailableGPR(Register::FromCode(i)) &&
680           isAvailableGPR(Register::FromCode(i + 1))) {
681         return true;
682       }
683     }
684     return false;
685   }
686 
allocGPRPair(Register * low,Register * high)687   void allocGPRPair(Register* low, Register* high) {
688     MOZ_ASSERT(hasGPRPair());
689     for (uint32_t i = 0; i <= PAIR_LIMIT; i += 2) {
690       if (isAvailableGPR(Register::FromCode(i)) &&
691           isAvailableGPR(Register::FromCode(i + 1))) {
692         *low = Register::FromCode(i);
693         *high = Register::FromCode(i + 1);
694         allocGPR(*low);
695         allocGPR(*high);
696         return;
697       }
698     }
699     MOZ_CRASH("No pair");
700   }
701 #endif
702 
allocFPU(FloatRegister r)703   void allocFPU(FloatRegister r) {
704     MOZ_ASSERT(isAvailableFPU(r));
705     availFPU.take(r);
706   }
707 
708   template <MIRType t>
allocFPU()709   FloatRegister allocFPU() {
710     return availFPU.takeAny<RegTypeOf<t>::value>();
711   }
712 
freeGPR(Register r)713   void freeGPR(Register r) { availGPR.add(r); }
714 
freeInt64(Register64 r)715   void freeInt64(Register64 r) {
716 #ifdef JS_PUNBOX64
717     freeGPR(r.reg);
718 #else
719     freeGPR(r.low);
720     freeGPR(r.high);
721 #endif
722   }
723 
freeFPU(FloatRegister r)724   void freeFPU(FloatRegister r) { availFPU.add(r); }
725 
726  public:
BaseRegAlloc()727   explicit BaseRegAlloc()
728       : bc(nullptr),
729         availGPR(GeneralRegisterSet::All()),
730         availFPU(FloatRegisterSet::All())
731 #ifdef DEBUG
732         ,
733         scratchTaken(0)
734 #endif
735 #ifdef JS_CODEGEN_X86
736         ,
737         singleByteRegs(GeneralRegisterSet(Registers::SingleByteRegs))
738 #endif
739   {
740     RegisterAllocator::takeWasmRegisters(availGPR);
741 
742     // Allocate any private scratch registers.
743 #if defined(RABALDR_SCRATCH_I32)
744     if (RabaldrScratchI32 != RegI32::Invalid()) {
745       availGPR.take(RabaldrScratchI32);
746     }
747 #endif
748 
749 #ifdef RABALDR_SCRATCH_F32_ALIASES_F64
750     static_assert(RabaldrScratchF32 != InvalidFloatReg, "Float reg definition");
751     static_assert(RabaldrScratchF64 != InvalidFloatReg, "Float reg definition");
752 #endif
753 
754 #if defined(RABALDR_SCRATCH_F32) && !defined(RABALDR_SCRATCH_F32_ALIASES_F64)
755     if (RabaldrScratchF32 != RegF32::Invalid()) {
756       availFPU.take(RabaldrScratchF32);
757     }
758 #endif
759 
760 #if defined(RABALDR_SCRATCH_F64)
761 #  ifdef RABALDR_SCRATCH_F32_ALIASES_F64
762     MOZ_ASSERT(availFPU.has(RabaldrScratchF32));
763 #  endif
764     if (RabaldrScratchF64 != RegF64::Invalid()) {
765       availFPU.take(RabaldrScratchF64);
766     }
767 #  ifdef RABALDR_SCRATCH_F32_ALIASES_F64
768     MOZ_ASSERT(!availFPU.has(RabaldrScratchF32));
769 #  endif
770 #endif
771 
772 #ifdef DEBUG
773     allGPR = availGPR;
774     allFPU = availFPU;
775 #endif
776   }
777 
init(BaseCompilerInterface * bc)778   void init(BaseCompilerInterface* bc) { this->bc = bc; }
779 
780   enum class ScratchKind { I32 = 1, F32 = 2, F64 = 4, V128 = 8 };
781 
782 #ifdef DEBUG
isScratchRegisterTaken(ScratchKind s) const783   bool isScratchRegisterTaken(ScratchKind s) const {
784     return (scratchTaken & uint32_t(s)) != 0;
785   }
786 
setScratchRegisterTaken(ScratchKind s,bool state)787   void setScratchRegisterTaken(ScratchKind s, bool state) {
788     if (state) {
789       scratchTaken |= uint32_t(s);
790     } else {
791       scratchTaken &= ~uint32_t(s);
792     }
793   }
794 #endif
795 
796 #ifdef JS_CODEGEN_X86
isSingleByteI32(Register r)797   bool isSingleByteI32(Register r) { return singleByteRegs.has(r); }
798 #endif
799 
isAvailableI32(RegI32 r)800   bool isAvailableI32(RegI32 r) { return isAvailableGPR(r); }
801 
isAvailableI64(RegI64 r)802   bool isAvailableI64(RegI64 r) {
803 #ifdef JS_PUNBOX64
804     return isAvailableGPR(r.reg);
805 #else
806     return isAvailableGPR(r.low) && isAvailableGPR(r.high);
807 #endif
808   }
809 
isAvailableRef(RegRef r)810   bool isAvailableRef(RegRef r) { return isAvailableGPR(r); }
811 
isAvailablePtr(RegPtr r)812   bool isAvailablePtr(RegPtr r) { return isAvailableGPR(r); }
813 
isAvailableF32(RegF32 r)814   bool isAvailableF32(RegF32 r) { return isAvailableFPU(r); }
815 
isAvailableF64(RegF64 r)816   bool isAvailableF64(RegF64 r) { return isAvailableFPU(r); }
817 
818 #ifdef ENABLE_WASM_SIMD
isAvailableV128(RegV128 r)819   bool isAvailableV128(RegV128 r) { return isAvailableFPU(r); }
820 #endif
821 
822   // TODO / OPTIMIZE (Bug 1316802): Do not sync everything on allocation
823   // failure, only as much as we need.
824 
needI32()825   [[nodiscard]] RegI32 needI32() {
826     if (!hasGPR()) {
827       bc->sync();
828     }
829     return RegI32(allocGPR());
830   }
831 
needI32(RegI32 specific)832   void needI32(RegI32 specific) {
833     if (!isAvailableI32(specific)) {
834       bc->sync();
835     }
836     allocGPR(specific);
837   }
838 
needI64()839   [[nodiscard]] RegI64 needI64() {
840     if (!hasGPR64()) {
841       bc->sync();
842     }
843     return RegI64(allocInt64());
844   }
845 
needI64(RegI64 specific)846   void needI64(RegI64 specific) {
847     if (!isAvailableI64(specific)) {
848       bc->sync();
849     }
850     allocInt64(specific);
851   }
852 
needRef()853   [[nodiscard]] RegRef needRef() {
854     if (!hasGPR()) {
855       bc->sync();
856     }
857     return RegRef(allocGPR());
858   }
859 
needRef(RegRef specific)860   void needRef(RegRef specific) {
861     if (!isAvailableRef(specific)) {
862       bc->sync();
863     }
864     allocGPR(specific);
865   }
866 
needPtr()867   [[nodiscard]] RegPtr needPtr() {
868     if (!hasGPR()) {
869       bc->sync();
870     }
871     return RegPtr(allocGPR());
872   }
873 
needPtr(RegPtr specific)874   void needPtr(RegPtr specific) {
875     if (!isAvailablePtr(specific)) {
876       bc->sync();
877     }
878     allocGPR(specific);
879   }
880 
881   // Use when you need a register for a short time but explicitly want to avoid
882   // a full sync().
needTempPtr(RegPtr fallback,bool * saved)883   [[nodiscard]] RegPtr needTempPtr(RegPtr fallback, bool* saved) {
884     if (hasGPR()) {
885       *saved = false;
886       return RegPtr(allocGPR());
887     }
888     *saved = true;
889     bc->saveTempPtr(fallback);
890     MOZ_ASSERT(isAvailablePtr(fallback));
891     allocGPR(fallback);
892     return RegPtr(fallback);
893   }
894 
needF32()895   [[nodiscard]] RegF32 needF32() {
896     if (!hasFPU<MIRType::Float32>()) {
897       bc->sync();
898     }
899     return RegF32(allocFPU<MIRType::Float32>());
900   }
901 
needF32(RegF32 specific)902   void needF32(RegF32 specific) {
903     if (!isAvailableF32(specific)) {
904       bc->sync();
905     }
906     allocFPU(specific);
907   }
908 
needF64()909   [[nodiscard]] RegF64 needF64() {
910     if (!hasFPU<MIRType::Double>()) {
911       bc->sync();
912     }
913     return RegF64(allocFPU<MIRType::Double>());
914   }
915 
needF64(RegF64 specific)916   void needF64(RegF64 specific) {
917     if (!isAvailableF64(specific)) {
918       bc->sync();
919     }
920     allocFPU(specific);
921   }
922 
923 #ifdef ENABLE_WASM_SIMD
needV128()924   [[nodiscard]] RegV128 needV128() {
925     if (!hasFPU<MIRType::Simd128>()) {
926       bc->sync();
927     }
928     return RegV128(allocFPU<MIRType::Simd128>());
929   }
930 
needV128(RegV128 specific)931   void needV128(RegV128 specific) {
932     if (!isAvailableV128(specific)) {
933       bc->sync();
934     }
935     allocFPU(specific);
936   }
937 #endif
938 
freeI32(RegI32 r)939   void freeI32(RegI32 r) { freeGPR(r); }
940 
freeI64(RegI64 r)941   void freeI64(RegI64 r) { freeInt64(r); }
942 
freeRef(RegRef r)943   void freeRef(RegRef r) { freeGPR(r); }
944 
freePtr(RegPtr r)945   void freePtr(RegPtr r) { freeGPR(r); }
946 
freeF64(RegF64 r)947   void freeF64(RegF64 r) { freeFPU(r); }
948 
freeF32(RegF32 r)949   void freeF32(RegF32 r) { freeFPU(r); }
950 
951 #ifdef ENABLE_WASM_SIMD
freeV128(RegV128 r)952   void freeV128(RegV128 r) { freeFPU(r); }
953 #endif
954 
freeTempPtr(RegPtr r,bool saved)955   void freeTempPtr(RegPtr r, bool saved) {
956     freePtr(r);
957     if (saved) {
958       bc->restoreTempPtr(r);
959       MOZ_ASSERT(!isAvailablePtr(r));
960     }
961   }
962 
963 #ifdef JS_CODEGEN_ARM
needI64Pair()964   [[nodiscard]] RegI64 needI64Pair() {
965     if (!hasGPRPair()) {
966       bc->sync();
967     }
968     Register low, high;
969     allocGPRPair(&low, &high);
970     return RegI64(Register64(high, low));
971   }
972 #endif
973 
974 #ifdef DEBUG
975   friend class LeakCheck;
976 
977   class MOZ_RAII LeakCheck {
978    private:
979     const BaseRegAlloc& ra;
980     AllocatableGeneralRegisterSet knownGPR_;
981     AllocatableFloatRegisterSet knownFPU_;
982 
983    public:
LeakCheck(const BaseRegAlloc & ra)984     explicit LeakCheck(const BaseRegAlloc& ra) : ra(ra) {
985       knownGPR_ = ra.availGPR;
986       knownFPU_ = ra.availFPU;
987     }
988 
~LeakCheck()989     ~LeakCheck() {
990       MOZ_ASSERT(knownGPR_.bits() == ra.allGPR.bits());
991       MOZ_ASSERT(knownFPU_.bits() == ra.allFPU.bits());
992     }
993 
addKnownI32(RegI32 r)994     void addKnownI32(RegI32 r) { knownGPR_.add(r); }
995 
addKnownI64(RegI64 r)996     void addKnownI64(RegI64 r) {
997 #  ifdef JS_PUNBOX64
998       knownGPR_.add(r.reg);
999 #  else
1000       knownGPR_.add(r.high);
1001       knownGPR_.add(r.low);
1002 #  endif
1003     }
1004 
addKnownF32(RegF32 r)1005     void addKnownF32(RegF32 r) { knownFPU_.add(r); }
1006 
addKnownF64(RegF64 r)1007     void addKnownF64(RegF64 r) { knownFPU_.add(r); }
1008 
1009 #  ifdef ENABLE_WASM_SIMD
addKnownV128(RegV128 r)1010     void addKnownV128(RegV128 r) { knownFPU_.add(r); }
1011 #  endif
1012 
addKnownRef(RegRef r)1013     void addKnownRef(RegRef r) { knownGPR_.add(r); }
1014   };
1015 #endif
1016 };
1017 
1018 // Scratch register abstractions.
1019 //
1020 // We define our own scratch registers when the platform doesn't provide what we
1021 // need.  A notable use case is that we will need a private scratch register
1022 // when the platform masm uses its scratch register very frequently (eg, ARM).
1023 
1024 class BaseScratchRegister {
1025 #ifdef DEBUG
1026   BaseRegAlloc& ra;
1027   BaseRegAlloc::ScratchKind kind_;
1028 
1029  public:
BaseScratchRegister(BaseRegAlloc & ra,BaseRegAlloc::ScratchKind kind)1030   explicit BaseScratchRegister(BaseRegAlloc& ra, BaseRegAlloc::ScratchKind kind)
1031       : ra(ra), kind_(kind) {
1032     MOZ_ASSERT(!ra.isScratchRegisterTaken(kind_));
1033     ra.setScratchRegisterTaken(kind_, true);
1034   }
~BaseScratchRegister()1035   ~BaseScratchRegister() {
1036     MOZ_ASSERT(ra.isScratchRegisterTaken(kind_));
1037     ra.setScratchRegisterTaken(kind_, false);
1038   }
1039 #else
1040  public:
1041   explicit BaseScratchRegister(BaseRegAlloc& ra,
1042                                BaseRegAlloc::ScratchKind kind) {}
1043 #endif
1044 };
1045 
1046 #ifdef ENABLE_WASM_SIMD
1047 #  ifdef RABALDR_SCRATCH_V128
1048 class ScratchV128 : public BaseScratchRegister {
1049  public:
ScratchV128(BaseRegAlloc & ra)1050   explicit ScratchV128(BaseRegAlloc& ra)
1051       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::V128) {}
operator RegV128() const1052   operator RegV128() const { return RegV128(RabaldrScratchV128); }
1053 };
1054 #  else
1055 class ScratchV128 : public ScratchSimd128Scope {
1056  public:
ScratchV128(MacroAssembler & m)1057   explicit ScratchV128(MacroAssembler& m) : ScratchSimd128Scope(m) {}
operator RegV128() const1058   operator RegV128() const { return RegV128(FloatRegister(*this)); }
1059 };
1060 #  endif
1061 #endif
1062 
1063 #ifdef RABALDR_SCRATCH_F64
1064 class ScratchF64 : public BaseScratchRegister {
1065  public:
ScratchF64(BaseRegAlloc & ra)1066   explicit ScratchF64(BaseRegAlloc& ra)
1067       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F64) {}
operator RegF64() const1068   operator RegF64() const { return RegF64(RabaldrScratchF64); }
1069 };
1070 #else
1071 class ScratchF64 : public ScratchDoubleScope {
1072  public:
ScratchF64(MacroAssembler & m)1073   explicit ScratchF64(MacroAssembler& m) : ScratchDoubleScope(m) {}
operator RegF64() const1074   operator RegF64() const { return RegF64(FloatRegister(*this)); }
1075 };
1076 #endif
1077 
1078 #ifdef RABALDR_SCRATCH_F32
1079 class ScratchF32 : public BaseScratchRegister {
1080  public:
ScratchF32(BaseRegAlloc & ra)1081   explicit ScratchF32(BaseRegAlloc& ra)
1082       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F32) {}
operator RegF32() const1083   operator RegF32() const { return RegF32(RabaldrScratchF32); }
1084 };
1085 #else
1086 class ScratchF32 : public ScratchFloat32Scope {
1087  public:
ScratchF32(MacroAssembler & m)1088   explicit ScratchF32(MacroAssembler& m) : ScratchFloat32Scope(m) {}
operator RegF32() const1089   operator RegF32() const { return RegF32(FloatRegister(*this)); }
1090 };
1091 #endif
1092 
1093 #ifdef RABALDR_SCRATCH_I32
1094 template <class RegType>
1095 class ScratchGPR : public BaseScratchRegister {
1096  public:
ScratchGPR(BaseRegAlloc & ra)1097   explicit ScratchGPR(BaseRegAlloc& ra)
1098       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::I32) {}
operator RegType() const1099   operator RegType() const { return RegType(RabaldrScratchI32); }
1100 };
1101 #else
1102 template <class RegType>
1103 class ScratchGPR : public ScratchRegisterScope {
1104  public:
ScratchGPR(MacroAssembler & m)1105   explicit ScratchGPR(MacroAssembler& m) : ScratchRegisterScope(m) {}
operator RegType() const1106   operator RegType() const { return RegType(Register(*this)); }
1107 };
1108 #endif
1109 
1110 using ScratchI32 = ScratchGPR<RegI32>;
1111 using ScratchPtr = ScratchGPR<RegPtr>;
1112 using ScratchRef = ScratchGPR<RegRef>;
1113 
1114 #if defined(JS_CODEGEN_X86)
1115 // ScratchEBX is a mnemonic device: For some atomic ops we really need EBX,
1116 // no other register will do.  And we would normally have to allocate that
1117 // register using ScratchI32 since normally the scratch register is EBX.
1118 // But the whole point of ScratchI32 is to hide that relationship.  By using
1119 // the ScratchEBX alias, we document that at that point we require the
1120 // scratch register to be EBX.
1121 using ScratchEBX = ScratchI32;
1122 
1123 // ScratchI8 is a mnemonic device: For some ops we need a register with a
1124 // byte subregister.
1125 using ScratchI8 = ScratchI32;
1126 #endif
1127 
1128 // The stack frame.
1129 //
1130 // The stack frame has four parts ("below" means at lower addresses):
1131 //
1132 //  - the Frame element;
1133 //  - the Local area, including the DebugFrame element and possibly a spilled
1134 //    pointer to stack results, if any; allocated below the header with various
1135 //    forms of alignment;
1136 //  - the Dynamic area, comprising the temporary storage the compiler uses for
1137 //    register spilling, allocated below the Local area;
1138 //  - the Arguments area, comprising memory allocated for outgoing calls,
1139 //    allocated below the Dynamic area.
1140 //
1141 //                +==============================+
1142 //                |    Incoming stack arg        |
1143 //                |    ...                       |
1144 // -------------  +==============================+
1145 //                |    Frame (fixed size)        |
1146 // -------------  +==============================+ <-------------------- FP
1147 //         ^      |    DebugFrame (optional)     |    ^  ^             ^^
1148 //   localSize    |    Register arg local        |    |  |             ||
1149 //         |      |    ...                       |    |  |     framePushed
1150 //         |      |    Register stack result ptr?|    |  |             ||
1151 //         |      |    Non-arg local             |    |  |             ||
1152 //         |      |    ...                       |    |  |             ||
1153 //         |      |    (padding)                 |    |  |             ||
1154 //         |      |    Tls pointer               |    |  |             ||
1155 //         |      +------------------------------+    |  |             ||
1156 //         v      |    (padding)                 |    |  v             ||
1157 // -------------  +==============================+ currentStackHeight  ||
1158 //         ^      |    Dynamic (variable size)   |    |                ||
1159 //  dynamicSize   |    ...                       |    |                ||
1160 //         v      |    ...                       |    v                ||
1161 // -------------  |    (free space, sometimes)   | ---------           v|
1162 //                +==============================+ <----- SP not-during calls
1163 //                |    Arguments (sometimes)     |                      |
1164 //                |    ...                       |                      v
1165 //                +==============================+ <----- SP during calls
1166 //
1167 // The Frame is addressed off the stack pointer.  masm.framePushed() is always
1168 // correct, and masm.getStackPointer() + masm.framePushed() always addresses the
1169 // Frame, with the DebugFrame optionally below it.
1170 //
1171 // The Local area (including the DebugFrame and, if needed, the spilled value of
1172 // the stack results area pointer) is laid out by BaseLocalIter and is allocated
1173 // and deallocated by standard prologue and epilogue functions that manipulate
1174 // the stack pointer, but it is accessed via BaseStackFrame.
1175 //
1176 // The Dynamic area is maintained by and accessed via BaseStackFrame.  On some
1177 // systems (such as ARM64), the Dynamic memory may be allocated in chunks
1178 // because the SP needs a specific alignment, and in this case there will
1179 // normally be some free space directly above the SP.  The stack height does not
1180 // include the free space, it reflects the logically used space only.
1181 //
1182 // The Dynamic area is where space for stack results is allocated when calling
1183 // functions that return results on the stack.  If a function has stack results,
1184 // a pointer to the low address of the stack result area is passed as an
1185 // additional argument, according to the usual ABI.  See
1186 // ABIResultIter::HasStackResults.
1187 //
1188 // The Arguments area is allocated and deallocated via BaseStackFrame (see
1189 // comments later) but is accessed directly off the stack pointer.
1190 
1191 // BaseLocalIter iterates over a vector of types of locals and provides offsets
1192 // from the Frame address for those locals, and associated data.
1193 //
1194 // The implementation of BaseLocalIter is the property of the BaseStackFrame.
1195 // But it is also exposed for eg the debugger to use.
1196 
BaseLocalIter(const ValTypeVector & locals,const ArgTypeVector & args,bool debugEnabled)1197 BaseLocalIter::BaseLocalIter(const ValTypeVector& locals,
1198                              const ArgTypeVector& args, bool debugEnabled)
1199     : locals_(locals),
1200       args_(args),
1201       argsIter_(args_),
1202       index_(0),
1203       frameSize_(0),
1204       nextFrameSize_(debugEnabled ? DebugFrame::offsetOfFrame() : 0),
1205       frameOffset_(INT32_MAX),
1206       stackResultPointerOffset_(INT32_MAX),
1207       mirType_(MIRType::Undefined),
1208       done_(false) {
1209   MOZ_ASSERT(args.lengthWithoutStackResults() <= locals.length());
1210   settle();
1211 }
1212 
pushLocal(size_t nbytes)1213 int32_t BaseLocalIter::pushLocal(size_t nbytes) {
1214   MOZ_ASSERT(nbytes % 4 == 0 && nbytes <= 16);
1215   nextFrameSize_ = AlignBytes(frameSize_, nbytes) + nbytes;
1216   return nextFrameSize_;  // Locals grow down so capture base address.
1217 }
1218 
settle()1219 void BaseLocalIter::settle() {
1220   MOZ_ASSERT(!done_);
1221   frameSize_ = nextFrameSize_;
1222 
1223   if (!argsIter_.done()) {
1224     mirType_ = argsIter_.mirType();
1225     MIRType concreteType = mirType_;
1226     switch (mirType_) {
1227       case MIRType::StackResults:
1228         // The pointer to stack results is handled like any other argument:
1229         // either addressed in place if it is passed on the stack, or we spill
1230         // it in the frame if it's in a register.
1231         MOZ_ASSERT(args_.isSyntheticStackResultPointerArg(index_));
1232         concreteType = MIRType::Pointer;
1233         [[fallthrough]];
1234       case MIRType::Int32:
1235       case MIRType::Int64:
1236       case MIRType::Double:
1237       case MIRType::Float32:
1238       case MIRType::RefOrNull:
1239 #ifdef ENABLE_WASM_SIMD
1240       case MIRType::Simd128:
1241 #endif
1242         if (argsIter_->argInRegister()) {
1243           frameOffset_ = pushLocal(MIRTypeToSize(concreteType));
1244         } else {
1245           frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
1246         }
1247         break;
1248       default:
1249         MOZ_CRASH("Argument type");
1250     }
1251     if (mirType_ == MIRType::StackResults) {
1252       stackResultPointerOffset_ = frameOffset();
1253       // Advance past the synthetic stack result pointer argument and fall
1254       // through to the next case.
1255       argsIter_++;
1256       frameSize_ = nextFrameSize_;
1257       MOZ_ASSERT(argsIter_.done());
1258     } else {
1259       return;
1260     }
1261   }
1262 
1263   if (index_ < locals_.length()) {
1264     switch (locals_[index_].kind()) {
1265       case ValType::I32:
1266       case ValType::I64:
1267       case ValType::F32:
1268       case ValType::F64:
1269 #ifdef ENABLE_WASM_SIMD
1270       case ValType::V128:
1271 #endif
1272       case ValType::Ref:
1273         // TODO/AnyRef-boxing: With boxed immediates and strings, the
1274         // debugger must be made aware that AnyRef != Pointer.
1275         ASSERT_ANYREF_IS_JSOBJECT;
1276         mirType_ = ToMIRType(locals_[index_]);
1277         frameOffset_ = pushLocal(MIRTypeToSize(mirType_));
1278         break;
1279       default:
1280         MOZ_CRASH("Compiler bug: Unexpected local type");
1281     }
1282     return;
1283   }
1284 
1285   done_ = true;
1286 }
1287 
operator ++(int)1288 void BaseLocalIter::operator++(int) {
1289   MOZ_ASSERT(!done_);
1290   index_++;
1291   if (!argsIter_.done()) {
1292     argsIter_++;
1293   }
1294   settle();
1295 }
1296 
1297 // Abstraction of the height of the stack frame, to avoid type confusion.
1298 
1299 class StackHeight {
1300   friend class BaseStackFrameAllocator;
1301 
1302   uint32_t height;
1303 
1304  public:
StackHeight(uint32_t h)1305   explicit StackHeight(uint32_t h) : height(h) {}
Invalid()1306   static StackHeight Invalid() { return StackHeight(UINT32_MAX); }
isValid() const1307   bool isValid() const { return height != UINT32_MAX; }
operator ==(StackHeight rhs) const1308   bool operator==(StackHeight rhs) const {
1309     MOZ_ASSERT(isValid() && rhs.isValid());
1310     return height == rhs.height;
1311   }
operator !=(StackHeight rhs) const1312   bool operator!=(StackHeight rhs) const { return !(*this == rhs); }
1313 };
1314 
1315 // Abstraction for where multi-value results go on the machine stack.
1316 
1317 class StackResultsLoc {
1318   uint32_t bytes_;
1319   size_t count_;
1320   Maybe<uint32_t> height_;
1321 
1322  public:
StackResultsLoc()1323   StackResultsLoc() : bytes_(0), count_(0){};
StackResultsLoc(uint32_t bytes,size_t count,uint32_t height)1324   StackResultsLoc(uint32_t bytes, size_t count, uint32_t height)
1325       : bytes_(bytes), count_(count), height_(Some(height)) {
1326     MOZ_ASSERT(bytes != 0);
1327     MOZ_ASSERT(count != 0);
1328     MOZ_ASSERT(height != 0);
1329   }
1330 
bytes() const1331   uint32_t bytes() const { return bytes_; }
count() const1332   uint32_t count() const { return count_; }
height() const1333   uint32_t height() const { return height_.value(); }
1334 
hasStackResults() const1335   bool hasStackResults() const { return bytes() != 0; }
stackResults() const1336   StackResults stackResults() const {
1337     return hasStackResults() ? StackResults::HasStackResults
1338                              : StackResults::NoStackResults;
1339   }
1340 };
1341 
1342 // Abstraction of the baseline compiler's stack frame (except for the Frame /
1343 // DebugFrame parts).  See comments above for more.  Remember, "below" on the
1344 // stack means at lower addresses.
1345 //
1346 // The abstraction is split into two parts: BaseStackFrameAllocator is
1347 // responsible for allocating and deallocating space on the stack and for
1348 // performing computations that are affected by how the allocation is performed;
1349 // BaseStackFrame then provides a pleasant interface for stack frame management.
1350 
1351 class BaseStackFrameAllocator {
1352   MacroAssembler& masm;
1353 
1354 #ifdef RABALDR_CHUNKY_STACK
1355   // On platforms that require the stack pointer to be aligned on a boundary
1356   // greater than the typical stack item (eg, ARM64 requires 16-byte alignment
1357   // but items are 8 bytes), allocate stack memory in chunks, and use a
1358   // separate stack height variable to track the effective stack pointer
1359   // within the allocated area.  Effectively, there's a variable amount of
1360   // free space directly above the stack pointer.  See diagram above.
1361 
1362   // The following must be true in order for the stack height to be
1363   // predictable at control flow joins:
1364   //
1365   // - The Local area is always aligned according to WasmStackAlignment, ie,
1366   //   masm.framePushed() % WasmStackAlignment is zero after allocating
1367   //   locals.
1368   //
1369   // - ChunkSize is always a multiple of WasmStackAlignment.
1370   //
1371   // - Pushing and popping are always in units of ChunkSize (hence preserving
1372   //   alignment).
1373   //
1374   // - The free space on the stack (masm.framePushed() - currentStackHeight_)
1375   //   is a predictable (nonnegative) amount.
1376 
1377   // As an optimization, we pre-allocate some space on the stack, the size of
1378   // this allocation is InitialChunk and it must be a multiple of ChunkSize.
1379   // It is allocated as part of the function prologue and deallocated as part
1380   // of the epilogue, along with the locals.
1381   //
1382   // If ChunkSize is too large then we risk overflowing the stack on simple
1383   // recursions with few live values where stack overflow should not be a
1384   // risk; if it is too small we spend too much time adjusting the stack
1385   // pointer.
1386   //
1387   // Good values for ChunkSize are the subject of future empirical analysis;
1388   // eight words is just an educated guess.
1389 
1390   static constexpr uint32_t ChunkSize = 8 * sizeof(void*);
1391   static constexpr uint32_t InitialChunk = ChunkSize;
1392 
1393   // The current logical height of the frame is
1394   //   currentStackHeight_ = localSize_ + dynamicSize
1395   // where dynamicSize is not accounted for explicitly and localSize_ also
1396   // includes size for the DebugFrame.
1397   //
1398   // The allocated size of the frame, provided by masm.framePushed(), is usually
1399   // larger than currentStackHeight_, notably at the beginning of execution when
1400   // we've allocated InitialChunk extra space.
1401 
1402   uint32_t currentStackHeight_;
1403 #endif
1404 
1405   // Size of the Local area in bytes (stable after BaseCompiler::init() has
1406   // called BaseStackFrame::setupLocals(), which in turn calls
1407   // BaseStackFrameAllocator::setLocalSize()), always rounded to the proper
1408   // stack alignment.  The Local area is then allocated in beginFunction(),
1409   // following the allocation of the Header.  See onFixedStackAllocated()
1410   // below.
1411 
1412   uint32_t localSize_;
1413 
1414  protected:
1415   ///////////////////////////////////////////////////////////////////////////
1416   //
1417   // Initialization
1418 
BaseStackFrameAllocator(MacroAssembler & masm)1419   explicit BaseStackFrameAllocator(MacroAssembler& masm)
1420       : masm(masm),
1421 #ifdef RABALDR_CHUNKY_STACK
1422         currentStackHeight_(0),
1423 #endif
1424         localSize_(UINT32_MAX) {
1425   }
1426 
1427  protected:
1428   //////////////////////////////////////////////////////////////////////
1429   //
1430   // The Local area - the static part of the frame.
1431 
1432   // Record the size of the Local area, once it is known.
1433 
setLocalSize(uint32_t localSize)1434   void setLocalSize(uint32_t localSize) {
1435     MOZ_ASSERT(localSize == AlignBytes(localSize, sizeof(void*)),
1436                "localSize_ should be aligned to at least a pointer");
1437     MOZ_ASSERT(localSize_ == UINT32_MAX);
1438     localSize_ = localSize;
1439   }
1440 
1441   // Record the current stack height, after it has become stable in
1442   // beginFunction().  See also BaseStackFrame::onFixedStackAllocated().
1443 
onFixedStackAllocated()1444   void onFixedStackAllocated() {
1445     MOZ_ASSERT(localSize_ != UINT32_MAX);
1446 #ifdef RABALDR_CHUNKY_STACK
1447     currentStackHeight_ = localSize_;
1448 #endif
1449   }
1450 
1451  public:
1452   // The fixed amount of memory, in bytes, allocated on the stack below the
1453   // Header for purposes such as locals and other fixed values.  Includes all
1454   // necessary alignment, and on ARM64 also the initial chunk for the working
1455   // stack memory.
1456 
fixedAllocSize() const1457   uint32_t fixedAllocSize() const {
1458     MOZ_ASSERT(localSize_ != UINT32_MAX);
1459 #ifdef RABALDR_CHUNKY_STACK
1460     return localSize_ + InitialChunk;
1461 #else
1462     return localSize_;
1463 #endif
1464   }
1465 
1466 #ifdef RABALDR_CHUNKY_STACK
1467   // The allocated frame size is frequently larger than the logical stack
1468   // height; we round up to a chunk boundary, and special case the initial
1469   // chunk.
framePushedForHeight(uint32_t logicalHeight)1470   uint32_t framePushedForHeight(uint32_t logicalHeight) {
1471     if (logicalHeight <= fixedAllocSize()) {
1472       return fixedAllocSize();
1473     }
1474     return fixedAllocSize() +
1475            AlignBytes(logicalHeight - fixedAllocSize(), ChunkSize);
1476   }
1477 #endif
1478 
1479  protected:
1480   //////////////////////////////////////////////////////////////////////
1481   //
1482   // The Dynamic area - the dynamic part of the frame, for spilling and saving
1483   // intermediate values.
1484 
1485   // Offset off of sp_ for the slot at stack area location `offset`.
1486 
stackOffset(int32_t offset)1487   int32_t stackOffset(int32_t offset) {
1488     MOZ_ASSERT(offset > 0);
1489     return masm.framePushed() - offset;
1490   }
1491 
computeHeightWithStackResults(StackHeight stackBase,uint32_t stackResultBytes)1492   uint32_t computeHeightWithStackResults(StackHeight stackBase,
1493                                          uint32_t stackResultBytes) {
1494     MOZ_ASSERT(stackResultBytes);
1495     MOZ_ASSERT(currentStackHeight() >= stackBase.height);
1496     return stackBase.height + stackResultBytes;
1497   }
1498 
1499 #ifdef RABALDR_CHUNKY_STACK
pushChunkyBytes(uint32_t bytes)1500   void pushChunkyBytes(uint32_t bytes) {
1501     checkChunkyInvariants();
1502     uint32_t freeSpace = masm.framePushed() - currentStackHeight_;
1503     if (freeSpace < bytes) {
1504       uint32_t bytesToReserve = AlignBytes(bytes - freeSpace, ChunkSize);
1505       MOZ_ASSERT(bytesToReserve + freeSpace >= bytes);
1506       masm.reserveStack(bytesToReserve);
1507     }
1508     currentStackHeight_ += bytes;
1509     checkChunkyInvariants();
1510   }
1511 
popChunkyBytes(uint32_t bytes)1512   void popChunkyBytes(uint32_t bytes) {
1513     checkChunkyInvariants();
1514     currentStackHeight_ -= bytes;
1515     // Sometimes, popChunkyBytes() is used to pop a larger area, as when we drop
1516     // values consumed by a call, and we may need to drop several chunks.  But
1517     // never drop the initial chunk.  Crucially, the amount we drop is always an
1518     // integral number of chunks.
1519     uint32_t freeSpace = masm.framePushed() - currentStackHeight_;
1520     if (freeSpace >= ChunkSize) {
1521       uint32_t targetAllocSize = framePushedForHeight(currentStackHeight_);
1522       uint32_t amountToFree = masm.framePushed() - targetAllocSize;
1523       MOZ_ASSERT(amountToFree % ChunkSize == 0);
1524       if (amountToFree) {
1525         masm.freeStack(amountToFree);
1526       }
1527     }
1528     checkChunkyInvariants();
1529   }
1530 #endif
1531 
currentStackHeight() const1532   uint32_t currentStackHeight() const {
1533 #ifdef RABALDR_CHUNKY_STACK
1534     return currentStackHeight_;
1535 #else
1536     return masm.framePushed();
1537 #endif
1538   }
1539 
1540  private:
1541 #ifdef RABALDR_CHUNKY_STACK
checkChunkyInvariants()1542   void checkChunkyInvariants() {
1543     MOZ_ASSERT(masm.framePushed() >= fixedAllocSize());
1544     MOZ_ASSERT(masm.framePushed() >= currentStackHeight_);
1545     MOZ_ASSERT(masm.framePushed() == fixedAllocSize() ||
1546                masm.framePushed() - currentStackHeight_ < ChunkSize);
1547     MOZ_ASSERT((masm.framePushed() - localSize_) % ChunkSize == 0);
1548   }
1549 #endif
1550 
1551   // For a given stack height, return the appropriate size of the allocated
1552   // frame.
1553 
framePushedForHeight(StackHeight stackHeight)1554   uint32_t framePushedForHeight(StackHeight stackHeight) {
1555 #ifdef RABALDR_CHUNKY_STACK
1556     // A more complicated adjustment is needed.
1557     return framePushedForHeight(stackHeight.height);
1558 #else
1559     // The allocated frame size equals the stack height.
1560     return stackHeight.height;
1561 #endif
1562   }
1563 
1564  public:
1565   // The current height of the stack area, not necessarily zero-based, in a
1566   // type-safe way.
1567 
stackHeight() const1568   StackHeight stackHeight() const { return StackHeight(currentStackHeight()); }
1569 
1570   // Set the frame height to a previously recorded value.
1571 
setStackHeight(StackHeight amount)1572   void setStackHeight(StackHeight amount) {
1573 #ifdef RABALDR_CHUNKY_STACK
1574     currentStackHeight_ = amount.height;
1575     masm.setFramePushed(framePushedForHeight(amount));
1576     checkChunkyInvariants();
1577 #else
1578     masm.setFramePushed(amount.height);
1579 #endif
1580   }
1581 
1582   // The current height of the dynamic part of the stack area (ie, the backing
1583   // store for the evaluation stack), zero-based.
1584 
dynamicHeight() const1585   uint32_t dynamicHeight() const { return currentStackHeight() - localSize_; }
1586 
1587   // Before branching to an outer control label, pop the execution stack to
1588   // the level expected by that region, but do not update masm.framePushed()
1589   // as that will happen as compilation leaves the block.
1590   //
1591   // Note these operate directly on the stack pointer register.
1592 
popStackBeforeBranch(StackHeight destStackHeight,uint32_t stackResultBytes)1593   void popStackBeforeBranch(StackHeight destStackHeight,
1594                             uint32_t stackResultBytes) {
1595     uint32_t framePushedHere = masm.framePushed();
1596     StackHeight heightThere =
1597         StackHeight(destStackHeight.height + stackResultBytes);
1598     uint32_t framePushedThere = framePushedForHeight(heightThere);
1599     if (framePushedHere > framePushedThere) {
1600       masm.addToStackPtr(Imm32(framePushedHere - framePushedThere));
1601     }
1602   }
1603 
popStackBeforeBranch(StackHeight destStackHeight,ResultType type)1604   void popStackBeforeBranch(StackHeight destStackHeight, ResultType type) {
1605     popStackBeforeBranch(destStackHeight,
1606                          ABIResultIter::MeasureStackBytes(type));
1607   }
1608 
1609   // Given that there are |stackParamSize| bytes on the dynamic stack
1610   // corresponding to the stack results, return the stack height once these
1611   // parameters are popped.
1612 
stackResultsBase(uint32_t stackParamSize)1613   StackHeight stackResultsBase(uint32_t stackParamSize) {
1614     return StackHeight(currentStackHeight() - stackParamSize);
1615   }
1616 
1617   // For most of WebAssembly, adjacent instructions have fallthrough control
1618   // flow between them, which allows us to simply thread the current stack
1619   // height through the compiler.  There are two exceptions to this rule: when
1620   // leaving a block via dead code, and when entering the "else" arm of an "if".
1621   // In these cases, the stack height is the block entry height, plus any stack
1622   // values (results in the block exit case, parameters in the else entry case).
1623 
resetStackHeight(StackHeight destStackHeight,ResultType type)1624   void resetStackHeight(StackHeight destStackHeight, ResultType type) {
1625     uint32_t height = destStackHeight.height;
1626     height += ABIResultIter::MeasureStackBytes(type);
1627     setStackHeight(StackHeight(height));
1628   }
1629 
1630   // Return offset of stack result.
1631 
locateStackResult(const ABIResult & result,StackHeight stackBase,uint32_t stackResultBytes)1632   uint32_t locateStackResult(const ABIResult& result, StackHeight stackBase,
1633                              uint32_t stackResultBytes) {
1634     MOZ_ASSERT(result.onStack());
1635     MOZ_ASSERT(result.stackOffset() + result.size() <= stackResultBytes);
1636     uint32_t end = computeHeightWithStackResults(stackBase, stackResultBytes);
1637     return end - result.stackOffset();
1638   }
1639 
1640  public:
1641   //////////////////////////////////////////////////////////////////////
1642   //
1643   // The Argument area - for outgoing calls.
1644   //
1645   // We abstract these operations as an optimization: we can merge the freeing
1646   // of the argument area and dropping values off the stack after a call.  But
1647   // they always amount to manipulating the real stack pointer by some amount.
1648   //
1649   // Note that we do not update currentStackHeight_ for this; the frame does
1650   // not know about outgoing arguments.  But we do update framePushed(), so we
1651   // can still index into the frame below the outgoing arguments area.
1652 
1653   // This is always equivalent to a masm.reserveStack() call.
1654 
allocArgArea(size_t argSize)1655   void allocArgArea(size_t argSize) {
1656     if (argSize) {
1657       masm.reserveStack(argSize);
1658     }
1659   }
1660 
1661   // This frees the argument area allocated by allocArgArea(), and `argSize`
1662   // must be equal to the `argSize` argument to allocArgArea().  In addition
1663   // we drop some values from the frame, corresponding to the values that were
1664   // consumed by the call.
1665 
freeArgAreaAndPopBytes(size_t argSize,size_t dropSize)1666   void freeArgAreaAndPopBytes(size_t argSize, size_t dropSize) {
1667 #ifdef RABALDR_CHUNKY_STACK
1668     // Freeing the outgoing arguments and freeing the consumed values have
1669     // different semantics here, which is why the operation is split.
1670     if (argSize) {
1671       masm.freeStack(argSize);
1672     }
1673     popChunkyBytes(dropSize);
1674 #else
1675     if (argSize + dropSize) {
1676       masm.freeStack(argSize + dropSize);
1677     }
1678 #endif
1679   }
1680 };
1681 
1682 class BaseStackFrame final : public BaseStackFrameAllocator {
1683   MacroAssembler& masm;
1684 
1685   // The largest observed value of masm.framePushed(), ie, the size of the
1686   // stack frame.  Read this for its true value only when code generation is
1687   // finished.
1688   uint32_t maxFramePushed_;
1689 
1690   // Patch point where we check for stack overflow.
1691   CodeOffset stackAddOffset_;
1692 
1693   // Low byte offset of pointer to stack results, if any.
1694   Maybe<int32_t> stackResultsPtrOffset_;
1695 
1696   // The offset of TLS pointer.
1697   uint32_t tlsPointerOffset_;
1698 
1699   // Low byte offset of local area for true locals (not parameters).
1700   uint32_t varLow_;
1701 
1702   // High byte offset + 1 of local area for true locals.
1703   uint32_t varHigh_;
1704 
1705   // The stack pointer, cached for brevity.
1706   RegisterOrSP sp_;
1707 
1708  public:
BaseStackFrame(MacroAssembler & masm)1709   explicit BaseStackFrame(MacroAssembler& masm)
1710       : BaseStackFrameAllocator(masm),
1711         masm(masm),
1712         maxFramePushed_(0),
1713         stackAddOffset_(0),
1714         tlsPointerOffset_(UINT32_MAX),
1715         varLow_(UINT32_MAX),
1716         varHigh_(UINT32_MAX),
1717         sp_(masm.getStackPointer()) {}
1718 
1719   ///////////////////////////////////////////////////////////////////////////
1720   //
1721   // Stack management and overflow checking
1722 
1723   // This must be called once beginFunction has allocated space for the Header
1724   // (the Frame and DebugFrame) and the Local area, and will record the current
1725   // frame size for internal use by the stack abstractions.
1726 
onFixedStackAllocated()1727   void onFixedStackAllocated() {
1728     maxFramePushed_ = masm.framePushed();
1729     BaseStackFrameAllocator::onFixedStackAllocated();
1730   }
1731 
1732   // We won't know until after we've generated code how big the frame will be
1733   // (we may need arbitrary spill slots and outgoing param slots) so emit a
1734   // patchable add that is patched in endFunction().
1735   //
1736   // Note the platform scratch register may be used by branchPtr(), so
1737   // generally tmp must be something else.
1738 
checkStack(Register tmp,BytecodeOffset trapOffset)1739   void checkStack(Register tmp, BytecodeOffset trapOffset) {
1740     stackAddOffset_ = masm.sub32FromStackPtrWithPatch(tmp);
1741     Label ok;
1742     masm.branchPtr(Assembler::Below,
1743                    Address(WasmTlsReg, offsetof(wasm::TlsData, stackLimit)),
1744                    tmp, &ok);
1745     masm.wasmTrap(Trap::StackOverflow, trapOffset);
1746     masm.bind(&ok);
1747   }
1748 
patchCheckStack()1749   void patchCheckStack() {
1750     masm.patchSub32FromStackPtr(stackAddOffset_,
1751                                 Imm32(int32_t(maxFramePushed_)));
1752   }
1753 
1754   // Very large frames are implausible, probably an attack.
1755 
checkStackHeight()1756   bool checkStackHeight() {
1757     // 512KiB should be enough, considering how Rabaldr uses the stack and
1758     // what the standard limits are:
1759     //
1760     // - 1,000 parameters
1761     // - 50,000 locals
1762     // - 10,000 values on the eval stack (not an official limit)
1763     //
1764     // At sizeof(int64) bytes per slot this works out to about 480KiB.
1765     return maxFramePushed_ <= 512 * 1024;
1766   }
1767 
1768   ///////////////////////////////////////////////////////////////////////////
1769   //
1770   // Local area
1771 
1772   struct Local {
1773     // Type of the value.
1774     const MIRType type;
1775 
1776     // Byte offset from Frame "into" the locals, ie positive for true locals
1777     // and negative for incoming args that read directly from the arg area.
1778     // It assumes the stack is growing down and that locals are on the stack
1779     // at lower addresses than Frame, and is the offset from Frame of the
1780     // lowest-addressed byte of the local.
1781     const int32_t offs;
1782 
Localjs::wasm::BaseStackFrame::Local1783     Local(MIRType type, int32_t offs) : type(type), offs(offs) {}
1784 
isStackArgumentjs::wasm::BaseStackFrame::Local1785     bool isStackArgument() const { return offs < 0; }
1786   };
1787 
1788   // Profiling shows that the number of parameters and locals frequently
1789   // touches or exceeds 8.  So 16 seems like a reasonable starting point.
1790   using LocalVector = Vector<Local, 16, SystemAllocPolicy>;
1791 
1792   // Initialize `localInfo` based on the types of `locals` and `args`.
setupLocals(const ValTypeVector & locals,const ArgTypeVector & args,bool debugEnabled,LocalVector * localInfo)1793   [[nodiscard]] bool setupLocals(const ValTypeVector& locals,
1794                                  const ArgTypeVector& args, bool debugEnabled,
1795                                  LocalVector* localInfo) {
1796     if (!localInfo->reserve(locals.length())) {
1797       return false;
1798     }
1799 
1800     DebugOnly<uint32_t> index = 0;
1801     BaseLocalIter i(locals, args, debugEnabled);
1802     for (; !i.done() && i.index() < args.lengthWithoutStackResults(); i++) {
1803       MOZ_ASSERT(i.isArg());
1804       MOZ_ASSERT(i.index() == index);
1805       localInfo->infallibleEmplaceBack(i.mirType(), i.frameOffset());
1806       index++;
1807     }
1808 
1809     varLow_ = i.frameSize();
1810     for (; !i.done(); i++) {
1811       MOZ_ASSERT(!i.isArg());
1812       MOZ_ASSERT(i.index() == index);
1813       localInfo->infallibleEmplaceBack(i.mirType(), i.frameOffset());
1814       index++;
1815     }
1816     varHigh_ = i.frameSize();
1817 
1818     // Reserve an additional stack slot for the TLS pointer.
1819     const uint32_t pointerAlignedVarHigh = AlignBytes(varHigh_, sizeof(void*));
1820     const uint32_t localSize = pointerAlignedVarHigh + sizeof(void*);
1821     tlsPointerOffset_ = localSize;
1822 
1823     setLocalSize(AlignBytes(localSize, WasmStackAlignment));
1824 
1825     if (args.hasSyntheticStackResultPointerArg()) {
1826       stackResultsPtrOffset_ = Some(i.stackResultPointerOffset());
1827     }
1828 
1829     return true;
1830   }
1831 
1832   void zeroLocals(BaseRegAlloc* ra);
1833 
addressOfLocal(const Local & local,uint32_t additionalOffset=0)1834   Address addressOfLocal(const Local& local, uint32_t additionalOffset = 0) {
1835     if (local.isStackArgument()) {
1836       return Address(FramePointer,
1837                      stackArgumentOffsetFromFp(local) + additionalOffset);
1838     }
1839     return Address(sp_, localOffsetFromSp(local) + additionalOffset);
1840   }
1841 
loadLocalI32(const Local & src,RegI32 dest)1842   void loadLocalI32(const Local& src, RegI32 dest) {
1843     masm.load32(addressOfLocal(src), dest);
1844   }
1845 
1846 #ifndef JS_PUNBOX64
loadLocalI64Low(const Local & src,RegI32 dest)1847   void loadLocalI64Low(const Local& src, RegI32 dest) {
1848     masm.load32(addressOfLocal(src, INT64LOW_OFFSET), dest);
1849   }
1850 
loadLocalI64High(const Local & src,RegI32 dest)1851   void loadLocalI64High(const Local& src, RegI32 dest) {
1852     masm.load32(addressOfLocal(src, INT64HIGH_OFFSET), dest);
1853   }
1854 #endif
1855 
loadLocalI64(const Local & src,RegI64 dest)1856   void loadLocalI64(const Local& src, RegI64 dest) {
1857     masm.load64(addressOfLocal(src), dest);
1858   }
1859 
loadLocalRef(const Local & src,RegRef dest)1860   void loadLocalRef(const Local& src, RegRef dest) {
1861     masm.loadPtr(addressOfLocal(src), dest);
1862   }
1863 
loadLocalF64(const Local & src,RegF64 dest)1864   void loadLocalF64(const Local& src, RegF64 dest) {
1865     masm.loadDouble(addressOfLocal(src), dest);
1866   }
1867 
loadLocalF32(const Local & src,RegF32 dest)1868   void loadLocalF32(const Local& src, RegF32 dest) {
1869     masm.loadFloat32(addressOfLocal(src), dest);
1870   }
1871 
1872 #ifdef ENABLE_WASM_SIMD
loadLocalV128(const Local & src,RegV128 dest)1873   void loadLocalV128(const Local& src, RegV128 dest) {
1874     masm.loadUnalignedSimd128(addressOfLocal(src), dest);
1875   }
1876 #endif
1877 
storeLocalI32(RegI32 src,const Local & dest)1878   void storeLocalI32(RegI32 src, const Local& dest) {
1879     masm.store32(src, addressOfLocal(dest));
1880   }
1881 
storeLocalI64(RegI64 src,const Local & dest)1882   void storeLocalI64(RegI64 src, const Local& dest) {
1883     masm.store64(src, addressOfLocal(dest));
1884   }
1885 
storeLocalRef(RegRef src,const Local & dest)1886   void storeLocalRef(RegRef src, const Local& dest) {
1887     masm.storePtr(src, addressOfLocal(dest));
1888   }
1889 
storeLocalF64(RegF64 src,const Local & dest)1890   void storeLocalF64(RegF64 src, const Local& dest) {
1891     masm.storeDouble(src, addressOfLocal(dest));
1892   }
1893 
storeLocalF32(RegF32 src,const Local & dest)1894   void storeLocalF32(RegF32 src, const Local& dest) {
1895     masm.storeFloat32(src, addressOfLocal(dest));
1896   }
1897 
1898 #ifdef ENABLE_WASM_SIMD
storeLocalV128(RegV128 src,const Local & dest)1899   void storeLocalV128(RegV128 src, const Local& dest) {
1900     masm.storeUnalignedSimd128(src, addressOfLocal(dest));
1901   }
1902 #endif
1903 
1904   // Offset off of sp_ for `local`.
localOffsetFromSp(const Local & local)1905   int32_t localOffsetFromSp(const Local& local) {
1906     MOZ_ASSERT(!local.isStackArgument());
1907     return localOffset(local.offs);
1908   }
1909 
1910   // Offset off of frame pointer for `stack argument`.
stackArgumentOffsetFromFp(const Local & local)1911   int32_t stackArgumentOffsetFromFp(const Local& local) {
1912     MOZ_ASSERT(local.isStackArgument());
1913     return -local.offs;
1914   }
1915 
1916   // The incoming stack result area pointer is for stack results of the function
1917   // being compiled.
loadIncomingStackResultAreaPtr(RegPtr reg)1918   void loadIncomingStackResultAreaPtr(RegPtr reg) {
1919     const int32_t offset = stackResultsPtrOffset_.value();
1920     Address src = offset < 0 ? Address(FramePointer, -offset)
1921                              : Address(sp_, stackOffset(offset));
1922     masm.loadPtr(src, reg);
1923   }
1924 
storeIncomingStackResultAreaPtr(RegPtr reg)1925   void storeIncomingStackResultAreaPtr(RegPtr reg) {
1926     // If we get here, that means the pointer to the stack results area was
1927     // passed in as a register, and therefore it will be spilled below the
1928     // frame, so the offset is a positive height.
1929     MOZ_ASSERT(stackResultsPtrOffset_.value() > 0);
1930     masm.storePtr(reg,
1931                   Address(sp_, stackOffset(stackResultsPtrOffset_.value())));
1932   }
1933 
loadTlsPtr(Register dst)1934   void loadTlsPtr(Register dst) {
1935     masm.loadPtr(Address(sp_, stackOffset(tlsPointerOffset_)), dst);
1936   }
1937 
storeTlsPtr(Register tls)1938   void storeTlsPtr(Register tls) {
1939     masm.storePtr(tls, Address(sp_, stackOffset(tlsPointerOffset_)));
1940   }
1941 
getTlsPtrOffset()1942   int32_t getTlsPtrOffset() { return stackOffset(tlsPointerOffset_); }
1943 
1944   // An outgoing stack result area pointer is for stack results of callees of
1945   // the function being compiled.
computeOutgoingStackResultAreaPtr(const StackResultsLoc & results,RegPtr dest)1946   void computeOutgoingStackResultAreaPtr(const StackResultsLoc& results,
1947                                          RegPtr dest) {
1948     MOZ_ASSERT(results.height() <= masm.framePushed());
1949     uint32_t offsetFromSP = masm.framePushed() - results.height();
1950     masm.moveStackPtrTo(dest);
1951     if (offsetFromSP) {
1952       masm.addPtr(Imm32(offsetFromSP), dest);
1953     }
1954   }
1955 
1956  private:
1957   // Offset off of sp_ for a local with offset `offset` from Frame.
localOffset(int32_t offset)1958   int32_t localOffset(int32_t offset) { return masm.framePushed() - offset; }
1959 
1960  public:
1961   ///////////////////////////////////////////////////////////////////////////
1962   //
1963   // Dynamic area
1964 
1965   static constexpr size_t StackSizeOfPtr = ABIResult::StackSizeOfPtr;
1966   static constexpr size_t StackSizeOfInt64 = ABIResult::StackSizeOfInt64;
1967   static constexpr size_t StackSizeOfFloat = ABIResult::StackSizeOfFloat;
1968   static constexpr size_t StackSizeOfDouble = ABIResult::StackSizeOfDouble;
1969 #ifdef ENABLE_WASM_SIMD
1970   static constexpr size_t StackSizeOfV128 = ABIResult::StackSizeOfV128;
1971 #endif
1972 
1973   // Pushes the register `r` to the stack. This pushes the full 64-bit width on
1974   // 64-bit systems, and 32-bits otherwise.
pushGPR(Register r)1975   uint32_t pushGPR(Register r) {
1976     DebugOnly<uint32_t> stackBefore = currentStackHeight();
1977 #ifdef RABALDR_CHUNKY_STACK
1978     pushChunkyBytes(StackSizeOfPtr);
1979     masm.storePtr(r, Address(sp_, stackOffset(currentStackHeight())));
1980 #else
1981     masm.Push(r);
1982 #endif
1983     maxFramePushed_ = std::max(maxFramePushed_, masm.framePushed());
1984     MOZ_ASSERT(stackBefore + StackSizeOfPtr == currentStackHeight());
1985     return currentStackHeight();
1986   }
1987 
pushFloat32(FloatRegister r)1988   uint32_t pushFloat32(FloatRegister r) {
1989     DebugOnly<uint32_t> stackBefore = currentStackHeight();
1990 #ifdef RABALDR_CHUNKY_STACK
1991     pushChunkyBytes(StackSizeOfFloat);
1992     masm.storeFloat32(r, Address(sp_, stackOffset(currentStackHeight())));
1993 #else
1994     masm.Push(r);
1995 #endif
1996     maxFramePushed_ = std::max(maxFramePushed_, masm.framePushed());
1997     MOZ_ASSERT(stackBefore + StackSizeOfFloat == currentStackHeight());
1998     return currentStackHeight();
1999   }
2000 
2001 #ifdef ENABLE_WASM_SIMD
pushV128(RegV128 r)2002   uint32_t pushV128(RegV128 r) {
2003     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2004 #  ifdef RABALDR_CHUNKY_STACK
2005     pushChunkyBytes(StackSizeOfV128);
2006 #  else
2007     masm.adjustStack(-(int)StackSizeOfV128);
2008 #  endif
2009     masm.storeUnalignedSimd128(r,
2010                                Address(sp_, stackOffset(currentStackHeight())));
2011     maxFramePushed_ = std::max(maxFramePushed_, masm.framePushed());
2012     MOZ_ASSERT(stackBefore + StackSizeOfV128 == currentStackHeight());
2013     return currentStackHeight();
2014   }
2015 #endif
2016 
pushDouble(FloatRegister r)2017   uint32_t pushDouble(FloatRegister r) {
2018     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2019 #ifdef RABALDR_CHUNKY_STACK
2020     pushChunkyBytes(StackSizeOfDouble);
2021     masm.storeDouble(r, Address(sp_, stackOffset(currentStackHeight())));
2022 #else
2023     masm.Push(r);
2024 #endif
2025     maxFramePushed_ = std::max(maxFramePushed_, masm.framePushed());
2026     MOZ_ASSERT(stackBefore + StackSizeOfDouble == currentStackHeight());
2027     return currentStackHeight();
2028   }
2029 
2030   // Pops the stack into the register `r`. This pops the full 64-bit width on
2031   // 64-bit systems, and 32-bits otherwise.
popGPR(Register r)2032   void popGPR(Register r) {
2033     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2034 #ifdef RABALDR_CHUNKY_STACK
2035     masm.loadPtr(Address(sp_, stackOffset(currentStackHeight())), r);
2036     popChunkyBytes(StackSizeOfPtr);
2037 #else
2038     masm.Pop(r);
2039 #endif
2040     MOZ_ASSERT(stackBefore - StackSizeOfPtr == currentStackHeight());
2041   }
2042 
popFloat32(FloatRegister r)2043   void popFloat32(FloatRegister r) {
2044     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2045 #ifdef RABALDR_CHUNKY_STACK
2046     masm.loadFloat32(Address(sp_, stackOffset(currentStackHeight())), r);
2047     popChunkyBytes(StackSizeOfFloat);
2048 #else
2049     masm.Pop(r);
2050 #endif
2051     MOZ_ASSERT(stackBefore - StackSizeOfFloat == currentStackHeight());
2052   }
2053 
popDouble(FloatRegister r)2054   void popDouble(FloatRegister r) {
2055     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2056 #ifdef RABALDR_CHUNKY_STACK
2057     masm.loadDouble(Address(sp_, stackOffset(currentStackHeight())), r);
2058     popChunkyBytes(StackSizeOfDouble);
2059 #else
2060     masm.Pop(r);
2061 #endif
2062     MOZ_ASSERT(stackBefore - StackSizeOfDouble == currentStackHeight());
2063   }
2064 
2065 #ifdef ENABLE_WASM_SIMD
popV128(RegV128 r)2066   void popV128(RegV128 r) {
2067     DebugOnly<uint32_t> stackBefore = currentStackHeight();
2068     masm.loadUnalignedSimd128(Address(sp_, stackOffset(currentStackHeight())),
2069                               r);
2070 #  ifdef RABALDR_CHUNKY_STACK
2071     popChunkyBytes(StackSizeOfV128);
2072 #  else
2073     masm.adjustStack((int)StackSizeOfV128);
2074 #  endif
2075     MOZ_ASSERT(stackBefore - StackSizeOfV128 == currentStackHeight());
2076   }
2077 #endif
2078 
popBytes(size_t bytes)2079   void popBytes(size_t bytes) {
2080     if (bytes > 0) {
2081 #ifdef RABALDR_CHUNKY_STACK
2082       popChunkyBytes(bytes);
2083 #else
2084       masm.freeStack(bytes);
2085 #endif
2086     }
2087   }
2088 
loadStackI32(int32_t offset,RegI32 dest)2089   void loadStackI32(int32_t offset, RegI32 dest) {
2090     masm.load32(Address(sp_, stackOffset(offset)), dest);
2091   }
2092 
loadStackI64(int32_t offset,RegI64 dest)2093   void loadStackI64(int32_t offset, RegI64 dest) {
2094     masm.load64(Address(sp_, stackOffset(offset)), dest);
2095   }
2096 
2097 #ifndef JS_PUNBOX64
loadStackI64Low(int32_t offset,RegI32 dest)2098   void loadStackI64Low(int32_t offset, RegI32 dest) {
2099     masm.load32(Address(sp_, stackOffset(offset - INT64LOW_OFFSET)), dest);
2100   }
2101 
loadStackI64High(int32_t offset,RegI32 dest)2102   void loadStackI64High(int32_t offset, RegI32 dest) {
2103     masm.load32(Address(sp_, stackOffset(offset - INT64HIGH_OFFSET)), dest);
2104   }
2105 #endif
2106 
loadStackRef(int32_t offset,RegRef dest)2107   void loadStackRef(int32_t offset, RegRef dest) {
2108     masm.loadPtr(Address(sp_, stackOffset(offset)), dest);
2109   }
2110 
loadStackF64(int32_t offset,RegF64 dest)2111   void loadStackF64(int32_t offset, RegF64 dest) {
2112     masm.loadDouble(Address(sp_, stackOffset(offset)), dest);
2113   }
2114 
loadStackF32(int32_t offset,RegF32 dest)2115   void loadStackF32(int32_t offset, RegF32 dest) {
2116     masm.loadFloat32(Address(sp_, stackOffset(offset)), dest);
2117   }
2118 
2119 #ifdef ENABLE_WASM_SIMD
loadStackV128(int32_t offset,RegV128 dest)2120   void loadStackV128(int32_t offset, RegV128 dest) {
2121     masm.loadUnalignedSimd128(Address(sp_, stackOffset(offset)), dest);
2122   }
2123 #endif
2124 
prepareStackResultArea(StackHeight stackBase,uint32_t stackResultBytes)2125   uint32_t prepareStackResultArea(StackHeight stackBase,
2126                                   uint32_t stackResultBytes) {
2127     uint32_t end = computeHeightWithStackResults(stackBase, stackResultBytes);
2128     if (currentStackHeight() < end) {
2129       uint32_t bytes = end - currentStackHeight();
2130 #ifdef RABALDR_CHUNKY_STACK
2131       pushChunkyBytes(bytes);
2132 #else
2133       masm.reserveStack(bytes);
2134 #endif
2135       maxFramePushed_ = std::max(maxFramePushed_, masm.framePushed());
2136     }
2137     return end;
2138   }
2139 
finishStackResultArea(StackHeight stackBase,uint32_t stackResultBytes)2140   void finishStackResultArea(StackHeight stackBase, uint32_t stackResultBytes) {
2141     uint32_t end = computeHeightWithStackResults(stackBase, stackResultBytes);
2142     MOZ_ASSERT(currentStackHeight() >= end);
2143     popBytes(currentStackHeight() - end);
2144   }
2145 
2146   // |srcHeight| and |destHeight| are stack heights *including* |bytes|.
shuffleStackResultsTowardFP(uint32_t srcHeight,uint32_t destHeight,uint32_t bytes,Register temp)2147   void shuffleStackResultsTowardFP(uint32_t srcHeight, uint32_t destHeight,
2148                                    uint32_t bytes, Register temp) {
2149     MOZ_ASSERT(destHeight < srcHeight);
2150     MOZ_ASSERT(bytes % sizeof(uint32_t) == 0);
2151     uint32_t destOffset = stackOffset(destHeight) + bytes;
2152     uint32_t srcOffset = stackOffset(srcHeight) + bytes;
2153     while (bytes >= sizeof(intptr_t)) {
2154       destOffset -= sizeof(intptr_t);
2155       srcOffset -= sizeof(intptr_t);
2156       bytes -= sizeof(intptr_t);
2157       masm.loadPtr(Address(sp_, srcOffset), temp);
2158       masm.storePtr(temp, Address(sp_, destOffset));
2159     }
2160     if (bytes) {
2161       MOZ_ASSERT(bytes == sizeof(uint32_t));
2162       destOffset -= sizeof(uint32_t);
2163       srcOffset -= sizeof(uint32_t);
2164       masm.load32(Address(sp_, srcOffset), temp);
2165       masm.store32(temp, Address(sp_, destOffset));
2166     }
2167   }
2168 
2169   // Unlike the overload that operates on raw heights, |srcHeight| and
2170   // |destHeight| are stack heights *not including* |bytes|.
shuffleStackResultsTowardFP(StackHeight srcHeight,StackHeight destHeight,uint32_t bytes,Register temp)2171   void shuffleStackResultsTowardFP(StackHeight srcHeight,
2172                                    StackHeight destHeight, uint32_t bytes,
2173                                    Register temp) {
2174     MOZ_ASSERT(srcHeight.isValid());
2175     MOZ_ASSERT(destHeight.isValid());
2176     uint32_t src = computeHeightWithStackResults(srcHeight, bytes);
2177     uint32_t dest = computeHeightWithStackResults(destHeight, bytes);
2178     MOZ_ASSERT(src <= currentStackHeight());
2179     MOZ_ASSERT(dest <= currentStackHeight());
2180     shuffleStackResultsTowardFP(src, dest, bytes, temp);
2181   }
2182 
2183   // |srcHeight| and |destHeight| are stack heights *including* |bytes|.
shuffleStackResultsTowardSP(uint32_t srcHeight,uint32_t destHeight,uint32_t bytes,Register temp)2184   void shuffleStackResultsTowardSP(uint32_t srcHeight, uint32_t destHeight,
2185                                    uint32_t bytes, Register temp) {
2186     MOZ_ASSERT(destHeight > srcHeight);
2187     MOZ_ASSERT(bytes % sizeof(uint32_t) == 0);
2188     uint32_t destOffset = stackOffset(destHeight);
2189     uint32_t srcOffset = stackOffset(srcHeight);
2190     while (bytes >= sizeof(intptr_t)) {
2191       masm.loadPtr(Address(sp_, srcOffset), temp);
2192       masm.storePtr(temp, Address(sp_, destOffset));
2193       destOffset += sizeof(intptr_t);
2194       srcOffset += sizeof(intptr_t);
2195       bytes -= sizeof(intptr_t);
2196     }
2197     if (bytes) {
2198       MOZ_ASSERT(bytes == sizeof(uint32_t));
2199       masm.load32(Address(sp_, srcOffset), temp);
2200       masm.store32(temp, Address(sp_, destOffset));
2201     }
2202   }
2203 
2204   // Copy results from the top of the current stack frame to an area of memory,
2205   // and pop the stack accordingly.  `dest` is the address of the low byte of
2206   // that memory.
popStackResultsToMemory(Register dest,uint32_t bytes,Register temp)2207   void popStackResultsToMemory(Register dest, uint32_t bytes, Register temp) {
2208     MOZ_ASSERT(bytes <= currentStackHeight());
2209     MOZ_ASSERT(bytes % sizeof(uint32_t) == 0);
2210     uint32_t bytesToPop = bytes;
2211     uint32_t srcOffset = stackOffset(currentStackHeight());
2212     uint32_t destOffset = 0;
2213     while (bytes >= sizeof(intptr_t)) {
2214       masm.loadPtr(Address(sp_, srcOffset), temp);
2215       masm.storePtr(temp, Address(dest, destOffset));
2216       destOffset += sizeof(intptr_t);
2217       srcOffset += sizeof(intptr_t);
2218       bytes -= sizeof(intptr_t);
2219     }
2220     if (bytes) {
2221       MOZ_ASSERT(bytes == sizeof(uint32_t));
2222       masm.load32(Address(sp_, srcOffset), temp);
2223       masm.store32(temp, Address(dest, destOffset));
2224     }
2225     popBytes(bytesToPop);
2226   }
2227 
2228  private:
store32BitsToStack(int32_t imm,uint32_t destHeight,Register temp)2229   void store32BitsToStack(int32_t imm, uint32_t destHeight, Register temp) {
2230     masm.move32(Imm32(imm), temp);
2231     masm.store32(temp, Address(sp_, stackOffset(destHeight)));
2232   }
2233 
store64BitsToStack(int64_t imm,uint32_t destHeight,Register temp)2234   void store64BitsToStack(int64_t imm, uint32_t destHeight, Register temp) {
2235 #ifdef JS_PUNBOX64
2236     masm.move64(Imm64(imm), Register64(temp));
2237     masm.store64(Register64(temp), Address(sp_, stackOffset(destHeight)));
2238 #else
2239     union {
2240       int64_t i64;
2241       int32_t i32[2];
2242     } bits = {.i64 = imm};
2243     static_assert(sizeof(bits) == 8);
2244     store32BitsToStack(bits.i32[0], destHeight, temp);
2245     store32BitsToStack(bits.i32[1], destHeight - sizeof(int32_t), temp);
2246 #endif
2247   }
2248 
2249  public:
storeImmediatePtrToStack(intptr_t imm,uint32_t destHeight,Register temp)2250   void storeImmediatePtrToStack(intptr_t imm, uint32_t destHeight,
2251                                 Register temp) {
2252 #ifdef JS_PUNBOX64
2253     static_assert(StackSizeOfPtr == 8);
2254     store64BitsToStack(imm, destHeight, temp);
2255 #else
2256     static_assert(StackSizeOfPtr == 4);
2257     store32BitsToStack(int32_t(imm), destHeight, temp);
2258 #endif
2259   }
2260 
storeImmediateI64ToStack(int64_t imm,uint32_t destHeight,Register temp)2261   void storeImmediateI64ToStack(int64_t imm, uint32_t destHeight,
2262                                 Register temp) {
2263     store64BitsToStack(imm, destHeight, temp);
2264   }
2265 
storeImmediateF32ToStack(float imm,uint32_t destHeight,Register temp)2266   void storeImmediateF32ToStack(float imm, uint32_t destHeight, Register temp) {
2267     union {
2268       int32_t i32;
2269       float f32;
2270     } bits = {.f32 = imm};
2271     static_assert(sizeof(bits) == 4);
2272     // Do not store 4 bytes if StackSizeOfFloat == 8.  It's probably OK to do
2273     // so, but it costs little to store something predictable.
2274     if (StackSizeOfFloat == 4) {
2275       store32BitsToStack(bits.i32, destHeight, temp);
2276     } else {
2277       store64BitsToStack(uint32_t(bits.i32), destHeight, temp);
2278     }
2279   }
2280 
storeImmediateF64ToStack(double imm,uint32_t destHeight,Register temp)2281   void storeImmediateF64ToStack(double imm, uint32_t destHeight,
2282                                 Register temp) {
2283     union {
2284       int64_t i64;
2285       double f64;
2286     } bits = {.f64 = imm};
2287     static_assert(sizeof(bits) == 8);
2288     store64BitsToStack(bits.i64, destHeight, temp);
2289   }
2290 
2291 #ifdef ENABLE_WASM_SIMD
storeImmediateV128ToStack(V128 imm,uint32_t destHeight,Register temp)2292   void storeImmediateV128ToStack(V128 imm, uint32_t destHeight, Register temp) {
2293     union {
2294       int32_t i32[4];
2295       uint8_t bytes[16];
2296     } bits{};
2297     static_assert(sizeof(bits) == 16);
2298     memcpy(bits.bytes, imm.bytes, 16);
2299     for (unsigned i = 0; i < 4; i++) {
2300       store32BitsToStack(bits.i32[i], destHeight - i * sizeof(int32_t), temp);
2301     }
2302   }
2303 #endif
2304 };
2305 
zeroLocals(BaseRegAlloc * ra)2306 void BaseStackFrame::zeroLocals(BaseRegAlloc* ra) {
2307   MOZ_ASSERT(varLow_ != UINT32_MAX);
2308 
2309   if (varLow_ == varHigh_) {
2310     return;
2311   }
2312 
2313   static const uint32_t wordSize = sizeof(void*);
2314 
2315   // The adjustments to 'low' by the size of the item being stored compensates
2316   // for the fact that locals offsets are the offsets from Frame to the bytes
2317   // directly "above" the locals in the locals area.  See comment at Local.
2318 
2319   // On 64-bit systems we may have 32-bit alignment for the local area as it
2320   // may be preceded by parameters and prologue/debug data.
2321 
2322   uint32_t low = varLow_;
2323   if (low % wordSize) {
2324     masm.store32(Imm32(0), Address(sp_, localOffset(low + 4)));
2325     low += 4;
2326   }
2327   MOZ_ASSERT(low % wordSize == 0);
2328 
2329   const uint32_t high = AlignBytes(varHigh_, wordSize);
2330 
2331   // An UNROLL_LIMIT of 16 is chosen so that we only need an 8-bit signed
2332   // immediate to represent the offset in the store instructions in the loop
2333   // on x64.
2334 
2335   const uint32_t UNROLL_LIMIT = 16;
2336   const uint32_t initWords = (high - low) / wordSize;
2337   const uint32_t tailWords = initWords % UNROLL_LIMIT;
2338   const uint32_t loopHigh = high - (tailWords * wordSize);
2339 
2340   // With only one word to initialize, just store an immediate zero.
2341 
2342   if (initWords == 1) {
2343     masm.storePtr(ImmWord(0), Address(sp_, localOffset(low + wordSize)));
2344     return;
2345   }
2346 
2347   // For other cases, it's best to have a zero in a register.
2348   //
2349   // One can do more here with SIMD registers (store 16 bytes at a time) or
2350   // with instructions like STRD on ARM (store 8 bytes at a time), but that's
2351   // for another day.
2352 
2353   RegI32 zero = ra->needI32();
2354   masm.mov(ImmWord(0), zero);
2355 
2356   // For the general case we want to have a loop body of UNROLL_LIMIT stores
2357   // and then a tail of less than UNROLL_LIMIT stores.  When initWords is less
2358   // than 2*UNROLL_LIMIT the loop trip count is at most 1 and there is no
2359   // benefit to having the pointer calculations and the compare-and-branch.
2360   // So we completely unroll when we have initWords < 2 * UNROLL_LIMIT.  (In
2361   // this case we'll end up using 32-bit offsets on x64 for up to half of the
2362   // stores, though.)
2363 
2364   // Fully-unrolled case.
2365 
2366   if (initWords < 2 * UNROLL_LIMIT) {
2367     for (uint32_t i = low; i < high; i += wordSize) {
2368       masm.storePtr(zero, Address(sp_, localOffset(i + wordSize)));
2369     }
2370     ra->freeI32(zero);
2371     return;
2372   }
2373 
2374   // Unrolled loop with a tail. Stores will use negative offsets. That's OK
2375   // for x86 and ARM, at least.
2376 
2377   // Compute pointer to the highest-addressed slot on the frame.
2378   RegI32 p = ra->needI32();
2379   masm.computeEffectiveAddress(Address(sp_, localOffset(low + wordSize)), p);
2380 
2381   // Compute pointer to the lowest-addressed slot on the frame that will be
2382   // initialized by the loop body.
2383   RegI32 lim = ra->needI32();
2384   masm.computeEffectiveAddress(Address(sp_, localOffset(loopHigh + wordSize)),
2385                                lim);
2386 
2387   // The loop body.  Eventually we'll have p == lim and exit the loop.
2388   Label again;
2389   masm.bind(&again);
2390   for (uint32_t i = 0; i < UNROLL_LIMIT; ++i) {
2391     masm.storePtr(zero, Address(p, -(wordSize * i)));
2392   }
2393   masm.subPtr(Imm32(UNROLL_LIMIT * wordSize), p);
2394   masm.branchPtr(Assembler::LessThan, lim, p, &again);
2395 
2396   // The tail.
2397   for (uint32_t i = 0; i < tailWords; ++i) {
2398     masm.storePtr(zero, Address(p, -(wordSize * i)));
2399   }
2400 
2401   ra->freeI32(p);
2402   ra->freeI32(lim);
2403   ra->freeI32(zero);
2404 }
2405 
2406 // Value stack: stack elements
2407 
2408 struct Stk {
2409  private:
Stkjs::wasm::Stk2410   Stk() : kind_(Unknown), i64val_(0) {}
2411 
2412  public:
2413   enum Kind {
2414     // The Mem opcodes are all clustered at the beginning to
2415     // allow for a quick test within sync().
2416     MemI32,  // 32-bit integer stack value ("offs")
2417     MemI64,  // 64-bit integer stack value ("offs")
2418     MemF32,  // 32-bit floating stack value ("offs")
2419     MemF64,  // 64-bit floating stack value ("offs")
2420 #ifdef ENABLE_WASM_SIMD
2421     MemV128,  // 128-bit vector stack value ("offs")
2422 #endif
2423     MemRef,  // reftype (pointer wide) stack value ("offs")
2424 
2425     // The Local opcodes follow the Mem opcodes for a similar
2426     // quick test within hasLocal().
2427     LocalI32,  // Local int32 var ("slot")
2428     LocalI64,  // Local int64 var ("slot")
2429     LocalF32,  // Local float32 var ("slot")
2430     LocalF64,  // Local double var ("slot")
2431 #ifdef ENABLE_WASM_SIMD
2432     LocalV128,  // Local v128 var ("slot")
2433 #endif
2434     LocalRef,  // Local reftype (pointer wide) var ("slot")
2435 
2436     RegisterI32,  // 32-bit integer register ("i32reg")
2437     RegisterI64,  // 64-bit integer register ("i64reg")
2438     RegisterF32,  // 32-bit floating register ("f32reg")
2439     RegisterF64,  // 64-bit floating register ("f64reg")
2440 #ifdef ENABLE_WASM_SIMD
2441     RegisterV128,  // 128-bit vector register ("v128reg")
2442 #endif
2443     RegisterRef,  // reftype (pointer wide) register ("refReg")
2444 
2445     ConstI32,  // 32-bit integer constant ("i32val")
2446     ConstI64,  // 64-bit integer constant ("i64val")
2447     ConstF32,  // 32-bit floating constant ("f32val")
2448     ConstF64,  // 64-bit floating constant ("f64val")
2449 #ifdef ENABLE_WASM_SIMD
2450     ConstV128,  // 128-bit vector constant ("v128val")
2451 #endif
2452     ConstRef,  // reftype (pointer wide) constant ("refval")
2453 
2454     Unknown,
2455   };
2456 
2457   Kind kind_;
2458 
2459   static const Kind MemLast = MemRef;
2460   static const Kind LocalLast = LocalRef;
2461 
2462   union {
2463     RegI32 i32reg_;
2464     RegI64 i64reg_;
2465     RegRef refReg_;
2466     RegF32 f32reg_;
2467     RegF64 f64reg_;
2468 #ifdef ENABLE_WASM_SIMD
2469     RegV128 v128reg_;
2470 #endif
2471     int32_t i32val_;
2472     int64_t i64val_;
2473     intptr_t refval_;
2474     float f32val_;
2475     double f64val_;
2476 #ifdef ENABLE_WASM_SIMD
2477     V128 v128val_;
2478 #endif
2479     uint32_t slot_;
2480     uint32_t offs_;
2481   };
2482 
Stkjs::wasm::Stk2483   explicit Stk(RegI32 r) : kind_(RegisterI32), i32reg_(r) {}
Stkjs::wasm::Stk2484   explicit Stk(RegI64 r) : kind_(RegisterI64), i64reg_(r) {}
Stkjs::wasm::Stk2485   explicit Stk(RegRef r) : kind_(RegisterRef), refReg_(r) {}
Stkjs::wasm::Stk2486   explicit Stk(RegF32 r) : kind_(RegisterF32), f32reg_(r) {}
Stkjs::wasm::Stk2487   explicit Stk(RegF64 r) : kind_(RegisterF64), f64reg_(r) {}
2488 #ifdef ENABLE_WASM_SIMD
Stkjs::wasm::Stk2489   explicit Stk(RegV128 r) : kind_(RegisterV128), v128reg_(r) {}
2490 #endif
Stkjs::wasm::Stk2491   explicit Stk(int32_t v) : kind_(ConstI32), i32val_(v) {}
Stkjs::wasm::Stk2492   explicit Stk(uint32_t v) : kind_(ConstI32), i32val_(int32_t(v)) {}
Stkjs::wasm::Stk2493   explicit Stk(int64_t v) : kind_(ConstI64), i64val_(v) {}
Stkjs::wasm::Stk2494   explicit Stk(float v) : kind_(ConstF32), f32val_(v) {}
Stkjs::wasm::Stk2495   explicit Stk(double v) : kind_(ConstF64), f64val_(v) {}
2496 #ifdef ENABLE_WASM_SIMD
Stkjs::wasm::Stk2497   explicit Stk(V128 v) : kind_(ConstV128), v128val_(v) {}
2498 #endif
Stkjs::wasm::Stk2499   explicit Stk(Kind k, uint32_t v) : kind_(k), slot_(v) {
2500     MOZ_ASSERT(k > MemLast && k <= LocalLast);
2501   }
StkRefjs::wasm::Stk2502   static Stk StkRef(intptr_t v) {
2503     Stk s;
2504     s.kind_ = ConstRef;
2505     s.refval_ = v;
2506     return s;
2507   }
StackResultjs::wasm::Stk2508   static Stk StackResult(ValType type, uint32_t offs) {
2509     Kind k;
2510     switch (type.kind()) {
2511       case ValType::I32:
2512         k = Stk::MemI32;
2513         break;
2514       case ValType::I64:
2515         k = Stk::MemI64;
2516         break;
2517       case ValType::V128:
2518 #ifdef ENABLE_WASM_SIMD
2519         k = Stk::MemV128;
2520         break;
2521 #else
2522         MOZ_CRASH("No SIMD");
2523 #endif
2524       case ValType::F32:
2525         k = Stk::MemF32;
2526         break;
2527       case ValType::F64:
2528         k = Stk::MemF64;
2529         break;
2530       case ValType::Rtt:
2531       case ValType::Ref:
2532         k = Stk::MemRef;
2533         break;
2534     }
2535     Stk s;
2536     s.setOffs(k, offs);
2537     return s;
2538   }
2539 
setOffsjs::wasm::Stk2540   void setOffs(Kind k, uint32_t v) {
2541     MOZ_ASSERT(k <= MemLast);
2542     kind_ = k;
2543     offs_ = v;
2544   }
2545 
kindjs::wasm::Stk2546   Kind kind() const { return kind_; }
isMemjs::wasm::Stk2547   bool isMem() const { return kind_ <= MemLast; }
2548 
i32regjs::wasm::Stk2549   RegI32 i32reg() const {
2550     MOZ_ASSERT(kind_ == RegisterI32);
2551     return i32reg_;
2552   }
i64regjs::wasm::Stk2553   RegI64 i64reg() const {
2554     MOZ_ASSERT(kind_ == RegisterI64);
2555     return i64reg_;
2556   }
refRegjs::wasm::Stk2557   RegRef refReg() const {
2558     MOZ_ASSERT(kind_ == RegisterRef);
2559     return refReg_;
2560   }
f32regjs::wasm::Stk2561   RegF32 f32reg() const {
2562     MOZ_ASSERT(kind_ == RegisterF32);
2563     return f32reg_;
2564   }
f64regjs::wasm::Stk2565   RegF64 f64reg() const {
2566     MOZ_ASSERT(kind_ == RegisterF64);
2567     return f64reg_;
2568   }
2569 #ifdef ENABLE_WASM_SIMD
v128regjs::wasm::Stk2570   RegV128 v128reg() const {
2571     MOZ_ASSERT(kind_ == RegisterV128);
2572     return v128reg_;
2573   }
2574 #endif
i32valjs::wasm::Stk2575   int32_t i32val() const {
2576     MOZ_ASSERT(kind_ == ConstI32);
2577     return i32val_;
2578   }
i64valjs::wasm::Stk2579   int64_t i64val() const {
2580     MOZ_ASSERT(kind_ == ConstI64);
2581     return i64val_;
2582   }
refvaljs::wasm::Stk2583   intptr_t refval() const {
2584     MOZ_ASSERT(kind_ == ConstRef);
2585     return refval_;
2586   }
2587 
2588   // For these two, use an out-param instead of simply returning, to
2589   // use the normal stack and not the x87 FP stack (which has effect on
2590   // NaNs with the signaling bit set).
2591 
f32valjs::wasm::Stk2592   void f32val(float* out) const {
2593     MOZ_ASSERT(kind_ == ConstF32);
2594     *out = f32val_;
2595   }
f64valjs::wasm::Stk2596   void f64val(double* out) const {
2597     MOZ_ASSERT(kind_ == ConstF64);
2598     *out = f64val_;
2599   }
2600 
2601 #ifdef ENABLE_WASM_SIMD
2602   // For SIMD, do the same as for floats since we're using float registers to
2603   // hold vectors; this is just conservative.
v128valjs::wasm::Stk2604   void v128val(V128* out) const {
2605     MOZ_ASSERT(kind_ == ConstV128);
2606     *out = v128val_;
2607   }
2608 #endif
2609 
slotjs::wasm::Stk2610   uint32_t slot() const {
2611     MOZ_ASSERT(kind_ > MemLast && kind_ <= LocalLast);
2612     return slot_;
2613   }
offsjs::wasm::Stk2614   uint32_t offs() const {
2615     MOZ_ASSERT(isMem());
2616     return offs_;
2617   }
2618 };
2619 
2620 using StkVector = Vector<Stk, 0, SystemAllocPolicy>;
2621 
2622 // MachineStackTracker, used for stack-slot pointerness tracking.
2623 
2624 class MachineStackTracker {
2625   // Simulates the machine's stack, with one bool per word.  Index zero in
2626   // this vector corresponds to the highest address in the machine stack.  The
2627   // last entry corresponds to what SP currently points at.  This all assumes
2628   // a grow-down stack.
2629   //
2630   // numPtrs_ contains the number of "true" values in vec_, and is therefore
2631   // redundant.  But it serves as a constant-time way to detect the common
2632   // case where vec_ holds no "true" values.
2633   size_t numPtrs_;
2634   Vector<bool, 64, SystemAllocPolicy> vec_;
2635 
2636  public:
MachineStackTracker()2637   MachineStackTracker() : numPtrs_(0) {}
2638 
~MachineStackTracker()2639   ~MachineStackTracker() {
2640 #ifdef DEBUG
2641     size_t n = 0;
2642     for (bool b : vec_) {
2643       n += (b ? 1 : 0);
2644     }
2645     MOZ_ASSERT(n == numPtrs_);
2646 #endif
2647   }
2648 
2649   // Clone this MachineStackTracker, writing the result at |dst|.
cloneTo(MachineStackTracker * dst)2650   [[nodiscard]] bool cloneTo(MachineStackTracker* dst) {
2651     MOZ_ASSERT(dst->vec_.empty());
2652     if (!dst->vec_.appendAll(vec_)) {
2653       return false;
2654     }
2655     dst->numPtrs_ = numPtrs_;
2656     return true;
2657   }
2658 
2659   // Notionally push |n| non-pointers on the stack.
pushNonGCPointers(size_t n)2660   [[nodiscard]] bool pushNonGCPointers(size_t n) {
2661     return vec_.appendN(false, n);
2662   }
2663 
2664   // Mark the stack slot |offsetFromSP| up from the bottom as holding a
2665   // pointer.
setGCPointer(size_t offsetFromSP)2666   void setGCPointer(size_t offsetFromSP) {
2667     // offsetFromSP == 0 denotes the most recently pushed item, == 1 the
2668     // second most recently pushed item, etc.
2669     MOZ_ASSERT(offsetFromSP < vec_.length());
2670 
2671     size_t offsetFromTop = vec_.length() - 1 - offsetFromSP;
2672     numPtrs_ = numPtrs_ + 1 - (vec_[offsetFromTop] ? 1 : 0);
2673     vec_[offsetFromTop] = true;
2674   }
2675 
2676   // Query the pointerness of the slot |offsetFromSP| up from the bottom.
isGCPointer(size_t offsetFromSP)2677   bool isGCPointer(size_t offsetFromSP) {
2678     MOZ_ASSERT(offsetFromSP < vec_.length());
2679 
2680     size_t offsetFromTop = vec_.length() - 1 - offsetFromSP;
2681     return vec_[offsetFromTop];
2682   }
2683 
2684   // Return the number of words tracked by this MachineStackTracker.
length()2685   size_t length() { return vec_.length(); }
2686 
2687   // Return the number of pointer-typed words tracked by this
2688   // MachineStackTracker.
numPtrs()2689   size_t numPtrs() {
2690     MOZ_ASSERT(numPtrs_ <= length());
2691     return numPtrs_;
2692   }
2693 
2694   // Discard all contents, but (per mozilla::Vector::clear semantics) don't
2695   // free or reallocate any dynamic storage associated with |vec_|.
clear()2696   void clear() {
2697     vec_.clear();
2698     numPtrs_ = 0;
2699   }
2700 };
2701 
2702 // StackMapGenerator, which carries all state needed to create stackmaps.
2703 
2704 enum class HasDebugFrame { No, Yes };
2705 
2706 struct StackMapGenerator {
2707  private:
2708   // --- These are constant for the life of the function's compilation ---
2709 
2710   // For generating stackmaps, we'll need to know the offsets of registers
2711   // as saved by the trap exit stub.
2712   const MachineState& trapExitLayout_;
2713   const size_t trapExitLayoutNumWords_;
2714 
2715   // Completed stackmaps are added here
2716   StackMaps* stackMaps_;
2717 
2718   // So as to be able to get current offset when creating stackmaps
2719   const MacroAssembler& masm_;
2720 
2721  public:
2722   // --- These are constant once we've completed beginFunction() ---
2723 
2724   // The number of words of arguments passed to this function in memory.
2725   size_t numStackArgWords;
2726 
2727   MachineStackTracker machineStackTracker;  // tracks machine stack pointerness
2728 
2729   // This holds masm.framePushed at entry to the function's body.  It is a
2730   // Maybe because createStackMap needs to know whether or not we're still
2731   // in the prologue.  It makes a Nothing-to-Some transition just once per
2732   // function.
2733   Maybe<uint32_t> framePushedAtEntryToBody;
2734 
2735   // --- These can change at any point ---
2736 
2737   // This holds masm.framePushed at it would be be for a function call
2738   // instruction, but excluding the stack area used to pass arguments in
2739   // memory.  That is, for an upcoming function call, this will hold
2740   //
2741   //   masm.framePushed() at the call instruction -
2742   //      StackArgAreaSizeUnaligned(argumentTypes)
2743   //
2744   // This value denotes the lowest-addressed stack word covered by the current
2745   // function's stackmap.  Words below this point form the highest-addressed
2746   // area of the callee's stackmap.  Note that all alignment padding above the
2747   // arguments-in-memory themselves belongs to the caller's stackmap, which
2748   // is why this is defined in terms of StackArgAreaSizeUnaligned() rather than
2749   // StackArgAreaSizeAligned().
2750   //
2751   // When not inside a function call setup/teardown sequence, it is Nothing.
2752   // It can make Nothing-to/from-Some transitions arbitrarily as we progress
2753   // through the function body.
2754   Maybe<uint32_t> framePushedExcludingOutboundCallArgs;
2755 
2756   // The number of memory-resident, ref-typed entries on the containing
2757   // BaseCompiler::stk_.
2758   size_t memRefsOnStk;
2759 
2760   // This is a copy of machineStackTracker that is used only within individual
2761   // calls to createStackMap. It is here only to avoid possible heap allocation
2762   // costs resulting from making it local to createStackMap().
2763   MachineStackTracker augmentedMst;
2764 
StackMapGeneratorjs::wasm::StackMapGenerator2765   StackMapGenerator(StackMaps* stackMaps, const MachineState& trapExitLayout,
2766                     const size_t trapExitLayoutNumWords,
2767                     const MacroAssembler& masm)
2768       : trapExitLayout_(trapExitLayout),
2769         trapExitLayoutNumWords_(trapExitLayoutNumWords),
2770         stackMaps_(stackMaps),
2771         masm_(masm),
2772         numStackArgWords(0),
2773         memRefsOnStk(0) {}
2774 
2775   // At the beginning of a function, we may have live roots in registers (as
2776   // arguments) at the point where we perform a stack overflow check.  This
2777   // method generates the "extra" stackmap entries to describe that, in the
2778   // case that the check fails and we wind up calling into the wasm exit
2779   // stub, as generated by GenerateTrapExit().
2780   //
2781   // The resulting map must correspond precisely with the stack layout
2782   // created for the integer registers as saved by (code generated by)
2783   // GenerateTrapExit().  To do that we use trapExitLayout_ and
2784   // trapExitLayoutNumWords_, which together comprise a description of the
2785   // layout and are created by GenerateTrapExitMachineState().
generateStackmapEntriesForTrapExitjs::wasm::StackMapGenerator2786   [[nodiscard]] bool generateStackmapEntriesForTrapExit(
2787       const ArgTypeVector& args, ExitStubMapVector* extras) {
2788     return GenerateStackmapEntriesForTrapExit(args, trapExitLayout_,
2789                                               trapExitLayoutNumWords_, extras);
2790   }
2791 
2792   // Creates a stackmap associated with the instruction denoted by
2793   // |assemblerOffset|, incorporating pointers from the current operand
2794   // stack |stk|, incorporating possible extra pointers in |extra| at the
2795   // lower addressed end, and possibly with the associated frame having a
2796   // DebugFrame as indicated by |debugFrame|.
createStackMapjs::wasm::StackMapGenerator2797   [[nodiscard]] bool createStackMap(const char* who,
2798                                     const ExitStubMapVector& extras,
2799                                     uint32_t assemblerOffset,
2800                                     HasDebugFrame debugFrame,
2801                                     const StkVector& stk) {
2802     size_t countedPointers = machineStackTracker.numPtrs() + memRefsOnStk;
2803 #ifndef DEBUG
2804     // An important optimization.  If there are obviously no pointers, as
2805     // we expect in the majority of cases, exit quickly.
2806     if (countedPointers == 0 && debugFrame == HasDebugFrame::No) {
2807       // We can skip creating the map if there are no |true| elements in
2808       // |extras|.
2809       bool extrasHasRef = false;
2810       for (bool b : extras) {
2811         if (b) {
2812           extrasHasRef = true;
2813           break;
2814         }
2815       }
2816       if (!extrasHasRef) {
2817         return true;
2818       }
2819     }
2820 #else
2821     // In the debug case, create the stackmap regardless, and cross-check
2822     // the pointer-counting below.  We expect the final map to have
2823     // |countedPointers| in total.  This doesn't include those in the
2824     // DebugFrame, but they do not appear in the map's bitmap.  Note that
2825     // |countedPointers| is debug-only from this point onwards.
2826     for (bool b : extras) {
2827       countedPointers += (b ? 1 : 0);
2828     }
2829 #endif
2830 
2831     // Start with the frame-setup map, and add operand-stack information to
2832     // that.  augmentedMst holds live data only within individual calls to
2833     // createStackMap.
2834     augmentedMst.clear();
2835     if (!machineStackTracker.cloneTo(&augmentedMst)) {
2836       return false;
2837     }
2838 
2839     // At this point, augmentedMst only contains entries covering the
2840     // incoming argument area (if any) and for the area allocated by this
2841     // function's prologue.  We now need to calculate how far the machine's
2842     // stack pointer is below where it was at the start of the body.  But we
2843     // must take care not to include any words pushed as arguments to an
2844     // upcoming function call, since those words "belong" to the stackmap of
2845     // the callee, not to the stackmap of this function.  Note however that
2846     // any alignment padding pushed prior to pushing the args *does* belong to
2847     // this function.
2848     //
2849     // That padding is taken into account at the point where
2850     // framePushedExcludingOutboundCallArgs is set, viz, in startCallArgs(),
2851     // and comprises two components:
2852     //
2853     // * call->frameAlignAdjustment
2854     // * the padding applied to the stack arg area itself.  That is:
2855     //   StackArgAreaSize(argTys) - StackArgAreaSizeUnpadded(argTys)
2856     Maybe<uint32_t> framePushedExcludingArgs;
2857     if (framePushedAtEntryToBody.isNothing()) {
2858       // Still in the prologue.  framePushedExcludingArgs remains Nothing.
2859       MOZ_ASSERT(framePushedExcludingOutboundCallArgs.isNothing());
2860     } else {
2861       // In the body.
2862       MOZ_ASSERT(masm_.framePushed() >= framePushedAtEntryToBody.value());
2863       if (framePushedExcludingOutboundCallArgs.isSome()) {
2864         // In the body, and we've potentially pushed some args onto the stack.
2865         // We must ignore them when sizing the stackmap.
2866         MOZ_ASSERT(masm_.framePushed() >=
2867                    framePushedExcludingOutboundCallArgs.value());
2868         MOZ_ASSERT(framePushedExcludingOutboundCallArgs.value() >=
2869                    framePushedAtEntryToBody.value());
2870         framePushedExcludingArgs =
2871             Some(framePushedExcludingOutboundCallArgs.value());
2872       } else {
2873         // In the body, but not with call args on the stack.  The stackmap
2874         // must be sized so as to extend all the way "down" to
2875         // masm_.framePushed().
2876         framePushedExcludingArgs = Some(masm_.framePushed());
2877       }
2878     }
2879 
2880     if (framePushedExcludingArgs.isSome()) {
2881       uint32_t bodyPushedBytes =
2882           framePushedExcludingArgs.value() - framePushedAtEntryToBody.value();
2883       MOZ_ASSERT(0 == bodyPushedBytes % sizeof(void*));
2884       if (!augmentedMst.pushNonGCPointers(bodyPushedBytes / sizeof(void*))) {
2885         return false;
2886       }
2887     }
2888 
2889     // Scan the operand stack, marking pointers in the just-added new
2890     // section.
2891     MOZ_ASSERT_IF(framePushedAtEntryToBody.isNothing(), stk.empty());
2892     MOZ_ASSERT_IF(framePushedExcludingArgs.isNothing(), stk.empty());
2893 
2894     for (const Stk& v : stk) {
2895 #ifndef DEBUG
2896       // We don't track roots in registers, per rationale below, so if this
2897       // doesn't hold, something is seriously wrong, and we're likely to get a
2898       // GC-related crash.
2899       MOZ_RELEASE_ASSERT(v.kind() != Stk::RegisterRef);
2900       if (v.kind() != Stk::MemRef) {
2901         continue;
2902       }
2903 #else
2904       // Take the opportunity to check everything we reasonably can about
2905       // operand stack elements.
2906       switch (v.kind()) {
2907         case Stk::MemI32:
2908         case Stk::MemI64:
2909         case Stk::MemF32:
2910         case Stk::MemF64:
2911         case Stk::ConstI32:
2912         case Stk::ConstI64:
2913         case Stk::ConstF32:
2914         case Stk::ConstF64:
2915 #  ifdef ENABLE_WASM_SIMD
2916         case Stk::MemV128:
2917         case Stk::ConstV128:
2918 #  endif
2919           // All of these have uninteresting type.
2920           continue;
2921         case Stk::LocalI32:
2922         case Stk::LocalI64:
2923         case Stk::LocalF32:
2924         case Stk::LocalF64:
2925 #  ifdef ENABLE_WASM_SIMD
2926         case Stk::LocalV128:
2927 #  endif
2928           // These also have uninteresting type.  Check that they live in the
2929           // section of stack set up by beginFunction().  The unguarded use of
2930           // |value()| here is safe due to the assertion above this loop.
2931           MOZ_ASSERT(v.offs() <= framePushedAtEntryToBody.value());
2932           continue;
2933         case Stk::RegisterI32:
2934         case Stk::RegisterI64:
2935         case Stk::RegisterF32:
2936         case Stk::RegisterF64:
2937 #  ifdef ENABLE_WASM_SIMD
2938         case Stk::RegisterV128:
2939 #  endif
2940           // These also have uninteresting type, but more to the point: all
2941           // registers holding live values should have been flushed to the
2942           // machine stack immediately prior to the instruction to which this
2943           // stackmap pertains.  So these can't happen.
2944           MOZ_CRASH("createStackMap: operand stack has Register-non-Ref");
2945         case Stk::MemRef:
2946           // This is the only case we care about.  We'll handle it after the
2947           // switch.
2948           break;
2949         case Stk::LocalRef:
2950           // We need the stackmap to mention this pointer, but it should
2951           // already be in the machineStackTracker section created by
2952           // beginFunction().
2953           MOZ_ASSERT(v.offs() <= framePushedAtEntryToBody.value());
2954           continue;
2955         case Stk::ConstRef:
2956           // This can currently only be a null pointer.
2957           MOZ_ASSERT(v.refval() == 0);
2958           continue;
2959         case Stk::RegisterRef:
2960           // This can't happen, per rationale above.
2961           MOZ_CRASH("createStackMap: operand stack contains RegisterRef");
2962         default:
2963           MOZ_CRASH("createStackMap: unknown operand stack element");
2964       }
2965 #endif
2966       // v.offs() holds masm.framePushed() at the point immediately after it
2967       // was pushed on the stack.  Since it's still on the stack,
2968       // masm.framePushed() can't be less.
2969       MOZ_ASSERT(v.offs() <= framePushedExcludingArgs.value());
2970       uint32_t offsFromMapLowest = framePushedExcludingArgs.value() - v.offs();
2971       MOZ_ASSERT(0 == offsFromMapLowest % sizeof(void*));
2972       augmentedMst.setGCPointer(offsFromMapLowest / sizeof(void*));
2973     }
2974 
2975     // Create the final StackMap.  The initial map is zeroed out, so there's
2976     // no need to write zero bits in it.
2977     const uint32_t extraWords = extras.length();
2978     const uint32_t augmentedMstWords = augmentedMst.length();
2979     const uint32_t numMappedWords = extraWords + augmentedMstWords;
2980     StackMap* stackMap = StackMap::create(numMappedWords);
2981     if (!stackMap) {
2982       return false;
2983     }
2984 
2985     {
2986       // First the exit stub extra words, if any.
2987       uint32_t i = 0;
2988       for (bool b : extras) {
2989         if (b) {
2990           stackMap->setBit(i);
2991         }
2992         i++;
2993       }
2994     }
2995     // Followed by the "main" part of the map.
2996     for (uint32_t i = 0; i < augmentedMstWords; i++) {
2997       if (augmentedMst.isGCPointer(i)) {
2998         stackMap->setBit(extraWords + i);
2999       }
3000     }
3001 
3002     stackMap->setExitStubWords(extraWords);
3003 
3004     // Record in the map, how far down from the highest address the Frame* is.
3005     // Take the opportunity to check that we haven't marked any part of the
3006     // Frame itself as a pointer.
3007     stackMap->setFrameOffsetFromTop(numStackArgWords +
3008                                     sizeof(Frame) / sizeof(void*));
3009 #ifdef DEBUG
3010     for (uint32_t i = 0; i < sizeof(Frame) / sizeof(void*); i++) {
3011       MOZ_ASSERT(stackMap->getBit(stackMap->numMappedWords -
3012                                   stackMap->frameOffsetFromTop + i) == 0);
3013     }
3014 #endif
3015 
3016     // Note the presence of a DebugFrame, if any.
3017     if (debugFrame == HasDebugFrame::Yes) {
3018       stackMap->setHasDebugFrame();
3019     }
3020 
3021     // Add the completed map to the running collection thereof.
3022     if (!stackMaps_->add((uint8_t*)(uintptr_t)assemblerOffset, stackMap)) {
3023       stackMap->destroy();
3024       return false;
3025     }
3026 
3027 #ifdef DEBUG
3028     {
3029       // Crosscheck the map pointer counting.
3030       uint32_t nw = stackMap->numMappedWords;
3031       uint32_t np = 0;
3032       for (uint32_t i = 0; i < nw; i++) {
3033         np += stackMap->getBit(i);
3034       }
3035       MOZ_ASSERT(size_t(np) == countedPointers);
3036     }
3037 #endif
3038 
3039     return true;
3040   }
3041 };
3042 
3043 class BaseCompiler;
3044 
3045 static void ClzI64(BaseCompiler& bc, RegI64 rsd);
3046 static void CtzI64(BaseCompiler& bc, RegI64 rsd);
3047 static RegI32 PopcntTemp(BaseCompiler& bc);
3048 static void PopcntI32(BaseCompiler& bc, RegI32 rsd, RegI32 temp);
3049 static void PopcntI64(BaseCompiler& bc, RegI64 rsd, RegI32 temp);
3050 static void ShlI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3051 static void ShrI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3052 static void ShrUI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3053 static void MinF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd);
3054 static void MaxF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd);
3055 static void MinF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd);
3056 static void MaxF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd);
3057 static void ExtendI32_8(BaseCompiler& bc, RegI32 rsd);
3058 #ifdef ENABLE_WASM_SIMD
3059 static RegV128 BitselectV128(BaseCompiler& bc, RegV128 rs1, RegV128 rs2,
3060                              RegV128 rs3);
3061 #endif
3062 
3063 // The baseline compiler proper.
3064 
3065 class BaseCompiler final : public BaseCompilerInterface {
3066   friend void ClzI64(BaseCompiler& bc, RegI64 rsd);
3067   friend void CtzI64(BaseCompiler& bc, RegI64 rsd);
3068   friend RegI32 PopcntTemp(BaseCompiler& bc);
3069   friend void PopcntI32(BaseCompiler& bc, RegI32 rsd, RegI32 temp);
3070   friend void PopcntI64(BaseCompiler& bc, RegI64 rsd, RegI32 temp);
3071   friend void ShlI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3072   friend void ShrI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3073   friend void ShrUI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd);
3074   friend void MinF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd);
3075   friend void MaxF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd);
3076   friend void MinF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd);
3077   friend void MaxF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd);
3078   friend void ExtendI32_8(BaseCompiler& bc, RegI32 rsd);
3079 #ifdef ENABLE_WASM_SIMD
3080   friend RegV128 BitselectV128(BaseCompiler& bc, RegV128 rs1, RegV128 rs2,
3081                                RegV128 rs3);
3082 #endif
3083 
3084   using Local = BaseStackFrame::Local;
3085   using LabelVector = Vector<NonAssertingLabel, 8, SystemAllocPolicy>;
3086 
3087   // Bit set used for simple bounds check elimination.  Capping this at 64
3088   // locals makes sense; even 32 locals would probably be OK in practice.
3089   //
3090   // For more information about BCE, see the block comment above
3091   // popMemory32Access(), below.
3092 
3093   using BCESet = uint64_t;
3094 
3095   // Information stored in the control node for generating exception handling
3096   // landing pads.
3097 
3098   struct CatchInfo {
3099     uint32_t eventIndex;      // Index for the associated exception.
3100     NonAssertingLabel label;  // The entry label for the handler.
3101 
3102     static const uint32_t CATCH_ALL_INDEX = UINT32_MAX;
3103     static_assert(CATCH_ALL_INDEX > MaxEvents);
3104 
CatchInfojs::wasm::BaseCompiler::CatchInfo3105     explicit CatchInfo(uint32_t eventIndex_) : eventIndex(eventIndex_) {}
3106   };
3107 
3108   using CatchInfoVector = Vector<CatchInfo, 0, SystemAllocPolicy>;
3109 
3110   // Control node, representing labels and stack heights at join points.
3111 
3112   struct Control {
3113     NonAssertingLabel label;       // The "exit" label
3114     NonAssertingLabel otherLabel;  // Used for the "else" branch of if-then-else
3115                                    // and to allow delegate to jump to catches.
3116     StackHeight stackHeight;       // From BaseStackFrame
3117     uint32_t stackSize;            // Value stack height
3118     BCESet bceSafeOnEntry;         // Bounds check info flowing into the item
3119     BCESet bceSafeOnExit;          // Bounds check info flowing out of the item
3120     bool deadOnArrival;            // deadCode_ was set on entry to the region
3121     bool deadThenBranch;           // deadCode_ was set on exit from "then"
3122     size_t tryNoteIndex;           // For tracking try branch code ranges.
3123     CatchInfoVector catchInfos;    // Used for try-catch handlers.
3124 
Controljs::wasm::BaseCompiler::Control3125     Control()
3126         : stackHeight(StackHeight::Invalid()),
3127           stackSize(UINT32_MAX),
3128           bceSafeOnEntry(0),
3129           bceSafeOnExit(~BCESet(0)),
3130           deadOnArrival(false),
3131           deadThenBranch(false),
3132           tryNoteIndex(0) {}
3133   };
3134 
3135   class NothingVector {
3136     Nothing unused_;
3137 
3138    public:
resize(size_t length)3139     bool resize(size_t length) { return true; }
operator [](size_t)3140     Nothing& operator[](size_t) { return unused_; }
back()3141     Nothing& back() { return unused_; }
3142   };
3143 
3144   struct BaseCompilePolicy {
3145     // The baseline compiler tracks values on a stack of its own -- it
3146     // needs to scan that stack for spilling -- and thus has no need
3147     // for the values maintained by the iterator.
3148     using Value = Nothing;
3149     using ValueVector = NothingVector;
3150 
3151     // The baseline compiler uses the iterator's control stack, attaching
3152     // its own control information.
3153     using ControlItem = Control;
3154   };
3155 
3156   using BaseOpIter = OpIter<BaseCompilePolicy>;
3157 
3158   // The baseline compiler will use OOL code more sparingly than
3159   // Baldr since our code is not high performance and frills like
3160   // code density and branch prediction friendliness will be less
3161   // important.
3162 
3163   class OutOfLineCode : public TempObject {
3164    private:
3165     NonAssertingLabel entry_;
3166     NonAssertingLabel rejoin_;
3167     StackHeight stackHeight_;
3168 
3169    public:
OutOfLineCode()3170     OutOfLineCode() : stackHeight_(StackHeight::Invalid()) {}
3171 
entry()3172     Label* entry() { return &entry_; }
rejoin()3173     Label* rejoin() { return &rejoin_; }
3174 
setStackHeight(StackHeight stackHeight)3175     void setStackHeight(StackHeight stackHeight) {
3176       MOZ_ASSERT(!stackHeight_.isValid());
3177       stackHeight_ = stackHeight;
3178     }
3179 
bind(BaseStackFrame * fr,MacroAssembler * masm)3180     void bind(BaseStackFrame* fr, MacroAssembler* masm) {
3181       MOZ_ASSERT(stackHeight_.isValid());
3182       masm->bind(&entry_);
3183       fr->setStackHeight(stackHeight_);
3184     }
3185 
3186     // The generate() method must be careful about register use
3187     // because it will be invoked when there is a register
3188     // assignment in the BaseCompiler that does not correspond
3189     // to the available registers when the generated OOL code is
3190     // executed.  The register allocator *must not* be called.
3191     //
3192     // The best strategy is for the creator of the OOL object to
3193     // allocate all temps that the OOL code will need.
3194     //
3195     // Input, output, and temp registers are embedded in the OOL
3196     // object and are known to the code generator.
3197     //
3198     // Scratch registers are available to use in OOL code.
3199     //
3200     // All other registers must be explicitly saved and restored
3201     // by the OOL code before being used.
3202 
3203     virtual void generate(MacroAssembler* masm) = 0;
3204   };
3205 
3206   enum class LatentOp { None, Compare, Eqz };
3207 
3208   struct AccessCheck {
AccessCheckjs::wasm::BaseCompiler::AccessCheck3209     AccessCheck()
3210         : omitBoundsCheck(false),
3211           omitAlignmentCheck(false),
3212           onlyPointerAlignment(false) {}
3213 
3214     // If `omitAlignmentCheck` is true then we need check neither the
3215     // pointer nor the offset.  Otherwise, if `onlyPointerAlignment` is true
3216     // then we need check only the pointer.  Otherwise, check the sum of
3217     // pointer and offset.
3218 
3219     bool omitBoundsCheck;
3220     bool omitAlignmentCheck;
3221     bool onlyPointerAlignment;
3222   };
3223 
3224   const ModuleEnvironment& moduleEnv_;
3225   const CompilerEnvironment& compilerEnv_;
3226   BaseOpIter iter_;
3227   const FuncCompileInput& func_;
3228   size_t lastReadCallSite_;
3229   TempAllocator::Fallible alloc_;
3230   const ValTypeVector& locals_;  // Types of parameters and locals
3231   bool deadCode_;  // Flag indicating we should decode & discard the opcode
3232   BCESet
3233       bceSafe_;  // Locals that have been bounds checked and not updated since
3234   ValTypeVector SigD_;
3235   ValTypeVector SigF_;
3236   NonAssertingLabel returnLabel_;
3237 
3238   LatentOp latentOp_;   // Latent operation for branch (seen next)
3239   ValType latentType_;  // Operand type, if latentOp_ is true
3240   Assembler::Condition
3241       latentIntCmp_;  // Comparison operator, if latentOp_ == Compare, int types
3242   Assembler::DoubleCondition
3243       latentDoubleCmp_;  // Comparison operator, if latentOp_ == Compare, float
3244                          // types
3245 
3246   FuncOffsets offsets_;
3247   MacroAssembler& masm;  // No '_' suffix - too tedious...
3248   BaseRegAlloc ra;       // Ditto
3249   BaseStackFrame fr;
3250 
3251   StackMapGenerator stackMapGenerator_;
3252 
3253   BaseStackFrame::LocalVector localInfo_;
3254   Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
3255 
3256   // On specific platforms we sometimes need to use specific registers.
3257 
3258   SpecificRegs specific_;
3259 
3260   // There are more members scattered throughout.
3261 
3262  public:
3263   BaseCompiler(const ModuleEnvironment& moduleEnv,
3264                const CompilerEnvironment& compilerEnv,
3265                const FuncCompileInput& func, const ValTypeVector& locals,
3266                const MachineState& trapExitLayout,
3267                size_t trapExitLayoutNumWords, Decoder& decoder,
3268                StkVector& stkSource, TempAllocator* alloc, MacroAssembler* masm,
3269                StackMaps* stackMaps);
3270   ~BaseCompiler();
3271 
3272   [[nodiscard]] bool init();
3273 
3274   FuncOffsets finish();
3275 
3276   [[nodiscard]] bool emitFunction();
3277   void emitInitStackLocals();
3278 
funcType() const3279   const FuncType& funcType() const {
3280     return *moduleEnv_.funcs[func_.index].type;
3281   }
3282 
funcTypeId() const3283   const TypeIdDesc& funcTypeId() const {
3284     return *moduleEnv_.funcs[func_.index].typeId;
3285   }
3286 
3287   // Used by some of the ScratchRegister implementations.
operator MacroAssembler&() const3288   operator MacroAssembler&() const { return masm; }
operator BaseRegAlloc&()3289   operator BaseRegAlloc&() { return ra; }
3290 
usesSharedMemory() const3291   bool usesSharedMemory() const { return moduleEnv_.usesSharedMemory(); }
3292 
3293  private:
3294   ////////////////////////////////////////////////////////////
3295   //
3296   // Out of line code management.
3297 
addOutOfLineCode(OutOfLineCode * ool)3298   [[nodiscard]] OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool) {
3299     if (!ool || !outOfLine_.append(ool)) {
3300       return nullptr;
3301     }
3302     ool->setStackHeight(fr.stackHeight());
3303     return ool;
3304   }
3305 
generateOutOfLineCode()3306   [[nodiscard]] bool generateOutOfLineCode() {
3307     for (auto* ool : outOfLine_) {
3308       ool->bind(&fr, &masm);
3309       ool->generate(&masm);
3310     }
3311 
3312     return !masm.oom();
3313   }
3314 
3315   // Utility.
3316 
localFromSlot(uint32_t slot,MIRType type)3317   const Local& localFromSlot(uint32_t slot, MIRType type) {
3318     MOZ_ASSERT(localInfo_[slot].type == type);
3319     return localInfo_[slot];
3320   }
3321 
3322   ////////////////////////////////////////////////////////////
3323   //
3324   // High-level register management.
3325 
isAvailableI32(RegI32 r)3326   bool isAvailableI32(RegI32 r) { return ra.isAvailableI32(r); }
isAvailableI64(RegI64 r)3327   bool isAvailableI64(RegI64 r) { return ra.isAvailableI64(r); }
isAvailableRef(RegRef r)3328   bool isAvailableRef(RegRef r) { return ra.isAvailableRef(r); }
isAvailablePtr(RegPtr r)3329   bool isAvailablePtr(RegPtr r) { return ra.isAvailablePtr(r); }
isAvailableF32(RegF32 r)3330   bool isAvailableF32(RegF32 r) { return ra.isAvailableF32(r); }
isAvailableF64(RegF64 r)3331   bool isAvailableF64(RegF64 r) { return ra.isAvailableF64(r); }
3332 #ifdef ENABLE_WASM_SIMD
isAvailableV128(RegV128 r)3333   bool isAvailableV128(RegV128 r) { return ra.isAvailableV128(r); }
3334 #endif
3335 
needI32()3336   [[nodiscard]] RegI32 needI32() { return ra.needI32(); }
needI64()3337   [[nodiscard]] RegI64 needI64() { return ra.needI64(); }
needRef()3338   [[nodiscard]] RegRef needRef() { return ra.needRef(); }
needPtr()3339   [[nodiscard]] RegPtr needPtr() { return ra.needPtr(); }
needF32()3340   [[nodiscard]] RegF32 needF32() { return ra.needF32(); }
needF64()3341   [[nodiscard]] RegF64 needF64() { return ra.needF64(); }
3342 #ifdef ENABLE_WASM_SIMD
needV128()3343   [[nodiscard]] RegV128 needV128() { return ra.needV128(); }
3344 #endif
3345 
needI32(RegI32 specific)3346   void needI32(RegI32 specific) { ra.needI32(specific); }
needI64(RegI64 specific)3347   void needI64(RegI64 specific) { ra.needI64(specific); }
needRef(RegRef specific)3348   void needRef(RegRef specific) { ra.needRef(specific); }
needPtr(RegPtr specific)3349   void needPtr(RegPtr specific) { ra.needPtr(specific); }
needF32(RegF32 specific)3350   void needF32(RegF32 specific) { ra.needF32(specific); }
needF64(RegF64 specific)3351   void needF64(RegF64 specific) { ra.needF64(specific); }
3352 #ifdef ENABLE_WASM_SIMD
needV128(RegV128 specific)3353   void needV128(RegV128 specific) { ra.needV128(specific); }
3354 #endif
3355 
3356 #if defined(JS_CODEGEN_ARM)
needI64Pair()3357   [[nodiscard]] RegI64 needI64Pair() { return ra.needI64Pair(); }
3358 #endif
3359 
freeAny(AnyReg r)3360   void freeAny(AnyReg r) {
3361     switch (r.tag) {
3362       case AnyReg::I32:
3363         freeI32(r.i32());
3364         break;
3365       case AnyReg::I64:
3366         freeI64(r.i64());
3367         break;
3368       case AnyReg::REF:
3369         freeRef(r.ref());
3370         break;
3371       case AnyReg::F32:
3372         freeF32(r.f32());
3373         break;
3374       case AnyReg::F64:
3375         freeF64(r.f64());
3376         break;
3377 #ifdef ENABLE_WASM_SIMD
3378       case AnyReg::V128:
3379         freeV128(r.v128());
3380         break;
3381 #endif
3382       default:
3383         MOZ_CRASH();
3384     }
3385   }
freeI32(RegI32 r)3386   void freeI32(RegI32 r) { ra.freeI32(r); }
freeI64(RegI64 r)3387   void freeI64(RegI64 r) { ra.freeI64(r); }
freeRef(RegRef r)3388   void freeRef(RegRef r) { ra.freeRef(r); }
freePtr(RegPtr r)3389   void freePtr(RegPtr r) { ra.freePtr(r); }
freeF32(RegF32 r)3390   void freeF32(RegF32 r) { ra.freeF32(r); }
freeF64(RegF64 r)3391   void freeF64(RegF64 r) { ra.freeF64(r); }
3392 #ifdef ENABLE_WASM_SIMD
freeV128(RegV128 r)3393   void freeV128(RegV128 r) { ra.freeV128(r); }
3394 #endif
3395 
freeI64Except(RegI64 r,RegI32 except)3396   void freeI64Except(RegI64 r, RegI32 except) {
3397 #ifdef JS_PUNBOX64
3398     MOZ_ASSERT(r.reg == except);
3399 #else
3400     MOZ_ASSERT(r.high == except || r.low == except);
3401     freeI64(r);
3402     needI32(except);
3403 #endif
3404   }
3405 
maybeFree(RegI32 r)3406   void maybeFree(RegI32 r) {
3407     if (r.isValid()) {
3408       freeI32(r);
3409     }
3410   }
3411 
maybeFree(RegI64 r)3412   void maybeFree(RegI64 r) {
3413     if (r.isValid()) {
3414       freeI64(r);
3415     }
3416   }
3417 
maybeFree(RegF64 r)3418   void maybeFree(RegF64 r) {
3419     if (r.isValid()) {
3420       freeF64(r);
3421     }
3422   }
3423 
needI32NoSync(RegI32 r)3424   void needI32NoSync(RegI32 r) {
3425     MOZ_ASSERT(isAvailableI32(r));
3426     needI32(r);
3427   }
3428 
3429   // TODO / OPTIMIZE: need2xI32() can be optimized along with needI32()
3430   // to avoid sync(). (Bug 1316802)
3431 
need2xI32(RegI32 r0,RegI32 r1)3432   void need2xI32(RegI32 r0, RegI32 r1) {
3433     needI32(r0);
3434     needI32(r1);
3435   }
3436 
need2xI64(RegI64 r0,RegI64 r1)3437   void need2xI64(RegI64 r0, RegI64 r1) {
3438     needI64(r0);
3439     needI64(r1);
3440   }
3441 
fromI64(RegI64 r)3442   RegI32 fromI64(RegI64 r) { return RegI32(lowPart(r)); }
3443 
3444 #ifdef JS_PUNBOX64
fromI32(RegI32 r)3445   RegI64 fromI32(RegI32 r) { return RegI64(Register64(r)); }
3446 #endif
3447 
widenI32(RegI32 r)3448   RegI64 widenI32(RegI32 r) {
3449     MOZ_ASSERT(!isAvailableI32(r));
3450 #ifdef JS_PUNBOX64
3451     return fromI32(r);
3452 #else
3453     RegI32 high = needI32();
3454     return RegI64(Register64(high, r));
3455 #endif
3456   }
3457 
narrowI64(RegI64 r)3458   RegI32 narrowI64(RegI64 r) {
3459 #ifdef JS_PUNBOX64
3460     return RegI32(r.reg);
3461 #else
3462     freeI32(RegI32(r.high));
3463     return RegI32(r.low);
3464 #endif
3465   }
3466 
narrowRef(RegRef r)3467   RegI32 narrowRef(RegRef r) { return RegI32(r); }
3468 
lowPart(RegI64 r)3469   RegI32 lowPart(RegI64 r) {
3470 #ifdef JS_PUNBOX64
3471     return RegI32(r.reg);
3472 #else
3473     return RegI32(r.low);
3474 #endif
3475   }
3476 
maybeHighPart(RegI64 r)3477   RegI32 maybeHighPart(RegI64 r) {
3478 #ifdef JS_PUNBOX64
3479     return RegI32::Invalid();
3480 #else
3481     return RegI32(r.high);
3482 #endif
3483   }
3484 
maybeClearHighPart(RegI64 r)3485   void maybeClearHighPart(RegI64 r) {
3486 #if !defined(JS_PUNBOX64)
3487     moveImm32(0, RegI32(r.high));
3488 #endif
3489   }
3490 
moveI32(RegI32 src,RegI32 dest)3491   void moveI32(RegI32 src, RegI32 dest) {
3492     if (src != dest) {
3493       masm.move32(src, dest);
3494     }
3495   }
3496 
moveI64(RegI64 src,RegI64 dest)3497   void moveI64(RegI64 src, RegI64 dest) {
3498     if (src != dest) {
3499       masm.move64(src, dest);
3500     }
3501   }
3502 
moveRef(RegRef src,RegRef dest)3503   void moveRef(RegRef src, RegRef dest) {
3504     if (src != dest) {
3505       masm.movePtr(src, dest);
3506     }
3507   }
3508 
moveF64(RegF64 src,RegF64 dest)3509   void moveF64(RegF64 src, RegF64 dest) {
3510     if (src != dest) {
3511       masm.moveDouble(src, dest);
3512     }
3513   }
3514 
moveF32(RegF32 src,RegF32 dest)3515   void moveF32(RegF32 src, RegF32 dest) {
3516     if (src != dest) {
3517       masm.moveFloat32(src, dest);
3518     }
3519   }
3520 
3521 #ifdef ENABLE_WASM_SIMD
moveV128(RegV128 src,RegV128 dest)3522   void moveV128(RegV128 src, RegV128 dest) {
3523     if (src != dest) {
3524       masm.moveSimd128(src, dest);
3525     }
3526   }
3527 #endif
3528 
3529   ////////////////////////////////////////////////////////////////////////////
3530   //
3531   // Block parameters and results.
3532   //
3533   // Blocks may have multiple parameters and multiple results.  Blocks can also
3534   // be the target of branches: the entry for loops, and the exit for
3535   // non-loops.
3536   //
3537   // Passing multiple values to a non-branch target (i.e., the entry of a
3538   // "block") falls out naturally: any items on the value stack can flow
3539   // directly from one block to another.
3540   //
3541   // However, for branch targets, we need to allocate well-known locations for
3542   // the branch values.  The approach taken in the baseline compiler is to
3543   // allocate registers to the top N values (currently N=1), and then stack
3544   // locations for the rest.
3545   //
3546 
3547   enum class RegKind { All, OnlyGPRs };
3548 
needResultRegisters(ResultType type,RegKind which)3549   inline void needResultRegisters(ResultType type, RegKind which) {
3550     if (type.empty()) {
3551       return;
3552     }
3553 
3554     for (ABIResultIter iter(type); !iter.done(); iter.next()) {
3555       ABIResult result = iter.cur();
3556       // Register results are visited first; when we see a stack result we're
3557       // done.
3558       if (!result.inRegister()) {
3559         return;
3560       }
3561       switch (result.type().kind()) {
3562         case ValType::I32:
3563           needI32(RegI32(result.gpr()));
3564           break;
3565         case ValType::I64:
3566           needI64(RegI64(result.gpr64()));
3567           break;
3568         case ValType::V128:
3569 #ifdef ENABLE_WASM_SIMD
3570           if (which == RegKind::All) {
3571             needV128(RegV128(result.fpr()));
3572           }
3573           break;
3574 #else
3575           MOZ_CRASH("No SIMD support");
3576 #endif
3577         case ValType::F32:
3578           if (which == RegKind::All) {
3579             needF32(RegF32(result.fpr()));
3580           }
3581           break;
3582         case ValType::F64:
3583           if (which == RegKind::All) {
3584             needF64(RegF64(result.fpr()));
3585           }
3586           break;
3587         case ValType::Rtt:
3588         case ValType::Ref:
3589           needRef(RegRef(result.gpr()));
3590           break;
3591       }
3592     }
3593   }
3594 
3595 #ifdef JS_CODEGEN_X64
maskResultRegisters(ResultType type)3596   inline void maskResultRegisters(ResultType type) {
3597     MOZ_ASSERT(JitOptions.spectreIndexMasking);
3598 
3599     if (type.empty()) {
3600       return;
3601     }
3602 
3603     for (ABIResultIter iter(type); !iter.done(); iter.next()) {
3604       ABIResult result = iter.cur();
3605       if (result.inRegister() && result.type().kind() == ValType::I32) {
3606         masm.movl(result.gpr(), result.gpr());
3607       }
3608     }
3609   }
3610 #endif
3611 
freeResultRegisters(ResultType type,RegKind which)3612   inline void freeResultRegisters(ResultType type, RegKind which) {
3613     if (type.empty()) {
3614       return;
3615     }
3616 
3617     for (ABIResultIter iter(type); !iter.done(); iter.next()) {
3618       ABIResult result = iter.cur();
3619       // Register results are visited first; when we see a stack result we're
3620       // done.
3621       if (!result.inRegister()) {
3622         return;
3623       }
3624       switch (result.type().kind()) {
3625         case ValType::I32:
3626           freeI32(RegI32(result.gpr()));
3627           break;
3628         case ValType::I64:
3629           freeI64(RegI64(result.gpr64()));
3630           break;
3631         case ValType::V128:
3632 #ifdef ENABLE_WASM_SIMD
3633           if (which == RegKind::All) {
3634             freeV128(RegV128(result.fpr()));
3635           }
3636           break;
3637 #else
3638           MOZ_CRASH("No SIMD support");
3639 #endif
3640         case ValType::F32:
3641           if (which == RegKind::All) {
3642             freeF32(RegF32(result.fpr()));
3643           }
3644           break;
3645         case ValType::F64:
3646           if (which == RegKind::All) {
3647             freeF64(RegF64(result.fpr()));
3648           }
3649           break;
3650         case ValType::Rtt:
3651         case ValType::Ref:
3652           freeRef(RegRef(result.gpr()));
3653           break;
3654       }
3655     }
3656   }
3657 
needIntegerResultRegisters(ResultType type)3658   void needIntegerResultRegisters(ResultType type) {
3659     needResultRegisters(type, RegKind::OnlyGPRs);
3660   }
freeIntegerResultRegisters(ResultType type)3661   void freeIntegerResultRegisters(ResultType type) {
3662     freeResultRegisters(type, RegKind::OnlyGPRs);
3663   }
3664 
needResultRegisters(ResultType type)3665   void needResultRegisters(ResultType type) {
3666     needResultRegisters(type, RegKind::All);
3667   }
freeResultRegisters(ResultType type)3668   void freeResultRegisters(ResultType type) {
3669     freeResultRegisters(type, RegKind::All);
3670   }
3671 
assertResultRegistersAvailable(ResultType type)3672   void assertResultRegistersAvailable(ResultType type) {
3673 #ifdef DEBUG
3674     for (ABIResultIter iter(type); !iter.done(); iter.next()) {
3675       ABIResult result = iter.cur();
3676       if (!result.inRegister()) {
3677         return;
3678       }
3679       switch (result.type().kind()) {
3680         case ValType::I32:
3681           MOZ_ASSERT(isAvailableI32(RegI32(result.gpr())));
3682           break;
3683         case ValType::I64:
3684           MOZ_ASSERT(isAvailableI64(RegI64(result.gpr64())));
3685           break;
3686         case ValType::V128:
3687 #  ifdef ENABLE_WASM_SIMD
3688           MOZ_ASSERT(isAvailableV128(RegV128(result.fpr())));
3689           break;
3690 #  else
3691           MOZ_CRASH("No SIMD support");
3692 #  endif
3693         case ValType::F32:
3694           MOZ_ASSERT(isAvailableF32(RegF32(result.fpr())));
3695           break;
3696         case ValType::F64:
3697           MOZ_ASSERT(isAvailableF64(RegF64(result.fpr())));
3698           break;
3699         case ValType::Rtt:
3700         case ValType::Ref:
3701           MOZ_ASSERT(isAvailableRef(RegRef(result.gpr())));
3702           break;
3703       }
3704     }
3705 #endif
3706   }
3707 
captureResultRegisters(ResultType type)3708   void captureResultRegisters(ResultType type) {
3709     assertResultRegistersAvailable(type);
3710     needResultRegisters(type);
3711   }
3712 
captureCallResultRegisters(ResultType type)3713   void captureCallResultRegisters(ResultType type) {
3714     captureResultRegisters(type);
3715 #ifdef JS_CODEGEN_X64
3716     if (JitOptions.spectreIndexMasking) {
3717       maskResultRegisters(type);
3718     }
3719 #endif
3720   }
3721 
3722   ////////////////////////////////////////////////////////////
3723   //
3724   // Value stack and spilling.
3725   //
3726   // The value stack facilitates some on-the-fly register allocation
3727   // and immediate-constant use.  It tracks constants, latent
3728   // references to locals, register contents, and values on the CPU
3729   // stack.
3730   //
3731   // The stack can be flushed to memory using sync().  This is handy
3732   // to avoid problems with control flow and messy register usage
3733   // patterns.
3734 
3735   // This is the value stack actually used during compilation.  It is a
3736   // StkVector rather than a StkVector& since constantly dereferencing a
3737   // StkVector& adds about 0.5% or more to the compiler's dynamic instruction
3738   // count.
3739   StkVector stk_;
3740 
3741   // Max number of pushes onto the value stack for any opcode or emitter that
3742   // does not push a variable, unbounded amount (anything with multiple
3743   // results).  This includes also intermediate pushes such as values pushed as
3744   // parameters for builtin calls.
3745   //
3746   // This limit is set quite high on purpose, so as to avoid brittleness.  The
3747   // true max value is likely no more than four or five.
3748 
3749   static constexpr size_t MaxPushesPerOpcode = 10;
3750 
3751   // BaselineCompileFunctions() "lends" us the StkVector to use in this
3752   // BaseCompiler object, and that is installed in |stk_| in our constructor.
3753   // This is so as to avoid having to malloc/free the vector's contents at
3754   // each creation/destruction of a BaseCompiler object.  It does however mean
3755   // that we need to hold on to a reference to BaselineCompileFunctions()'s
3756   // vector, so we can swap (give) its contents back when this BaseCompiler
3757   // object is destroyed.  This significantly reduces the heap turnover of the
3758   // baseline compiler.  See bug 1532592.
3759   StkVector& stkSource_;
3760 
3761 #ifdef DEBUG
countMemRefsOnStk()3762   size_t countMemRefsOnStk() {
3763     size_t nRefs = 0;
3764     for (Stk& v : stk_) {
3765       if (v.kind() == Stk::MemRef) {
3766         nRefs++;
3767       }
3768     }
3769     return nRefs;
3770   }
3771 #endif
3772 
3773   template <typename T>
push(T item)3774   void push(T item) {
3775     // None of the single-arg Stk constructors create a Stk::MemRef, so
3776     // there's no need to increment stackMapGenerator_.memRefsOnStk here.
3777     stk_.infallibleEmplaceBack(Stk(item));
3778   }
3779 
pushConstRef(intptr_t v)3780   void pushConstRef(intptr_t v) { stk_.infallibleEmplaceBack(Stk::StkRef(v)); }
3781 
loadConstI32(const Stk & src,RegI32 dest)3782   void loadConstI32(const Stk& src, RegI32 dest) {
3783     moveImm32(src.i32val(), dest);
3784   }
3785 
loadMemI32(const Stk & src,RegI32 dest)3786   void loadMemI32(const Stk& src, RegI32 dest) {
3787     fr.loadStackI32(src.offs(), dest);
3788   }
3789 
loadLocalI32(const Stk & src,RegI32 dest)3790   void loadLocalI32(const Stk& src, RegI32 dest) {
3791     fr.loadLocalI32(localFromSlot(src.slot(), MIRType::Int32), dest);
3792   }
3793 
loadRegisterI32(const Stk & src,RegI32 dest)3794   void loadRegisterI32(const Stk& src, RegI32 dest) {
3795     moveI32(src.i32reg(), dest);
3796   }
3797 
loadConstI64(const Stk & src,RegI64 dest)3798   void loadConstI64(const Stk& src, RegI64 dest) {
3799     moveImm64(src.i64val(), dest);
3800   }
3801 
loadMemI64(const Stk & src,RegI64 dest)3802   void loadMemI64(const Stk& src, RegI64 dest) {
3803     fr.loadStackI64(src.offs(), dest);
3804   }
3805 
loadLocalI64(const Stk & src,RegI64 dest)3806   void loadLocalI64(const Stk& src, RegI64 dest) {
3807     fr.loadLocalI64(localFromSlot(src.slot(), MIRType::Int64), dest);
3808   }
3809 
loadRegisterI64(const Stk & src,RegI64 dest)3810   void loadRegisterI64(const Stk& src, RegI64 dest) {
3811     moveI64(src.i64reg(), dest);
3812   }
3813 
loadConstRef(const Stk & src,RegRef dest)3814   void loadConstRef(const Stk& src, RegRef dest) {
3815     moveImmRef(src.refval(), dest);
3816   }
3817 
loadMemRef(const Stk & src,RegRef dest)3818   void loadMemRef(const Stk& src, RegRef dest) {
3819     fr.loadStackRef(src.offs(), dest);
3820   }
3821 
loadLocalRef(const Stk & src,RegRef dest)3822   void loadLocalRef(const Stk& src, RegRef dest) {
3823     fr.loadLocalRef(localFromSlot(src.slot(), MIRType::RefOrNull), dest);
3824   }
3825 
loadRegisterRef(const Stk & src,RegRef dest)3826   void loadRegisterRef(const Stk& src, RegRef dest) {
3827     moveRef(src.refReg(), dest);
3828   }
3829 
loadConstF64(const Stk & src,RegF64 dest)3830   void loadConstF64(const Stk& src, RegF64 dest) {
3831     double d;
3832     src.f64val(&d);
3833     masm.loadConstantDouble(d, dest);
3834   }
3835 
loadMemF64(const Stk & src,RegF64 dest)3836   void loadMemF64(const Stk& src, RegF64 dest) {
3837     fr.loadStackF64(src.offs(), dest);
3838   }
3839 
loadLocalF64(const Stk & src,RegF64 dest)3840   void loadLocalF64(const Stk& src, RegF64 dest) {
3841     fr.loadLocalF64(localFromSlot(src.slot(), MIRType::Double), dest);
3842   }
3843 
loadRegisterF64(const Stk & src,RegF64 dest)3844   void loadRegisterF64(const Stk& src, RegF64 dest) {
3845     moveF64(src.f64reg(), dest);
3846   }
3847 
loadConstF32(const Stk & src,RegF32 dest)3848   void loadConstF32(const Stk& src, RegF32 dest) {
3849     float f;
3850     src.f32val(&f);
3851     masm.loadConstantFloat32(f, dest);
3852   }
3853 
loadMemF32(const Stk & src,RegF32 dest)3854   void loadMemF32(const Stk& src, RegF32 dest) {
3855     fr.loadStackF32(src.offs(), dest);
3856   }
3857 
loadLocalF32(const Stk & src,RegF32 dest)3858   void loadLocalF32(const Stk& src, RegF32 dest) {
3859     fr.loadLocalF32(localFromSlot(src.slot(), MIRType::Float32), dest);
3860   }
3861 
loadRegisterF32(const Stk & src,RegF32 dest)3862   void loadRegisterF32(const Stk& src, RegF32 dest) {
3863     moveF32(src.f32reg(), dest);
3864   }
3865 
3866 #ifdef ENABLE_WASM_SIMD
loadConstV128(const Stk & src,RegV128 dest)3867   void loadConstV128(const Stk& src, RegV128 dest) {
3868     V128 f;
3869     src.v128val(&f);
3870     masm.loadConstantSimd128(SimdConstant::CreateX16((int8_t*)f.bytes), dest);
3871   }
3872 
loadMemV128(const Stk & src,RegV128 dest)3873   void loadMemV128(const Stk& src, RegV128 dest) {
3874     fr.loadStackV128(src.offs(), dest);
3875   }
3876 
loadLocalV128(const Stk & src,RegV128 dest)3877   void loadLocalV128(const Stk& src, RegV128 dest) {
3878     fr.loadLocalV128(localFromSlot(src.slot(), MIRType::Simd128), dest);
3879   }
3880 
loadRegisterV128(const Stk & src,RegV128 dest)3881   void loadRegisterV128(const Stk& src, RegV128 dest) {
3882     moveV128(src.v128reg(), dest);
3883   }
3884 #endif
3885 
loadI32(const Stk & src,RegI32 dest)3886   void loadI32(const Stk& src, RegI32 dest) {
3887     switch (src.kind()) {
3888       case Stk::ConstI32:
3889         loadConstI32(src, dest);
3890         break;
3891       case Stk::MemI32:
3892         loadMemI32(src, dest);
3893         break;
3894       case Stk::LocalI32:
3895         loadLocalI32(src, dest);
3896         break;
3897       case Stk::RegisterI32:
3898         loadRegisterI32(src, dest);
3899         break;
3900       default:
3901         MOZ_CRASH("Compiler bug: Expected I32 on stack");
3902     }
3903   }
3904 
loadI64(const Stk & src,RegI64 dest)3905   void loadI64(const Stk& src, RegI64 dest) {
3906     switch (src.kind()) {
3907       case Stk::ConstI64:
3908         loadConstI64(src, dest);
3909         break;
3910       case Stk::MemI64:
3911         loadMemI64(src, dest);
3912         break;
3913       case Stk::LocalI64:
3914         loadLocalI64(src, dest);
3915         break;
3916       case Stk::RegisterI64:
3917         loadRegisterI64(src, dest);
3918         break;
3919       default:
3920         MOZ_CRASH("Compiler bug: Expected I64 on stack");
3921     }
3922   }
3923 
3924 #if !defined(JS_PUNBOX64)
loadI64Low(const Stk & src,RegI32 dest)3925   void loadI64Low(const Stk& src, RegI32 dest) {
3926     switch (src.kind()) {
3927       case Stk::ConstI64:
3928         moveImm32(int32_t(src.i64val()), dest);
3929         break;
3930       case Stk::MemI64:
3931         fr.loadStackI64Low(src.offs(), dest);
3932         break;
3933       case Stk::LocalI64:
3934         fr.loadLocalI64Low(localFromSlot(src.slot(), MIRType::Int64), dest);
3935         break;
3936       case Stk::RegisterI64:
3937         moveI32(RegI32(src.i64reg().low), dest);
3938         break;
3939       default:
3940         MOZ_CRASH("Compiler bug: Expected I64 on stack");
3941     }
3942   }
3943 
loadI64High(const Stk & src,RegI32 dest)3944   void loadI64High(const Stk& src, RegI32 dest) {
3945     switch (src.kind()) {
3946       case Stk::ConstI64:
3947         moveImm32(int32_t(src.i64val() >> 32), dest);
3948         break;
3949       case Stk::MemI64:
3950         fr.loadStackI64High(src.offs(), dest);
3951         break;
3952       case Stk::LocalI64:
3953         fr.loadLocalI64High(localFromSlot(src.slot(), MIRType::Int64), dest);
3954         break;
3955       case Stk::RegisterI64:
3956         moveI32(RegI32(src.i64reg().high), dest);
3957         break;
3958       default:
3959         MOZ_CRASH("Compiler bug: Expected I64 on stack");
3960     }
3961   }
3962 #endif
3963 
loadF64(const Stk & src,RegF64 dest)3964   void loadF64(const Stk& src, RegF64 dest) {
3965     switch (src.kind()) {
3966       case Stk::ConstF64:
3967         loadConstF64(src, dest);
3968         break;
3969       case Stk::MemF64:
3970         loadMemF64(src, dest);
3971         break;
3972       case Stk::LocalF64:
3973         loadLocalF64(src, dest);
3974         break;
3975       case Stk::RegisterF64:
3976         loadRegisterF64(src, dest);
3977         break;
3978       default:
3979         MOZ_CRASH("Compiler bug: expected F64 on stack");
3980     }
3981   }
3982 
loadF32(const Stk & src,RegF32 dest)3983   void loadF32(const Stk& src, RegF32 dest) {
3984     switch (src.kind()) {
3985       case Stk::ConstF32:
3986         loadConstF32(src, dest);
3987         break;
3988       case Stk::MemF32:
3989         loadMemF32(src, dest);
3990         break;
3991       case Stk::LocalF32:
3992         loadLocalF32(src, dest);
3993         break;
3994       case Stk::RegisterF32:
3995         loadRegisterF32(src, dest);
3996         break;
3997       default:
3998         MOZ_CRASH("Compiler bug: expected F32 on stack");
3999     }
4000   }
4001 
4002 #ifdef ENABLE_WASM_SIMD
loadV128(const Stk & src,RegV128 dest)4003   void loadV128(const Stk& src, RegV128 dest) {
4004     switch (src.kind()) {
4005       case Stk::ConstV128:
4006         loadConstV128(src, dest);
4007         break;
4008       case Stk::MemV128:
4009         loadMemV128(src, dest);
4010         break;
4011       case Stk::LocalV128:
4012         loadLocalV128(src, dest);
4013         break;
4014       case Stk::RegisterV128:
4015         loadRegisterV128(src, dest);
4016         break;
4017       default:
4018         MOZ_CRASH("Compiler bug: expected V128 on stack");
4019     }
4020   }
4021 #endif
4022 
loadRef(const Stk & src,RegRef dest)4023   void loadRef(const Stk& src, RegRef dest) {
4024     switch (src.kind()) {
4025       case Stk::ConstRef:
4026         loadConstRef(src, dest);
4027         break;
4028       case Stk::MemRef:
4029         loadMemRef(src, dest);
4030         break;
4031       case Stk::LocalRef:
4032         loadLocalRef(src, dest);
4033         break;
4034       case Stk::RegisterRef:
4035         loadRegisterRef(src, dest);
4036         break;
4037       default:
4038         MOZ_CRASH("Compiler bug: expected ref on stack");
4039     }
4040   }
4041 
4042   // Duplicate the reference at the specified depth and load it into a register.
dupRefAt(uint32_t depth,RegRef dest)4043   void dupRefAt(uint32_t depth, RegRef dest) {
4044     MOZ_ASSERT(depth < stk_.length());
4045     Stk& src = peek(stk_.length() - depth - 1);
4046     loadRef(src, dest);
4047   }
4048 
4049   // Flush all local and register value stack elements to memory.
4050   //
4051   // TODO / OPTIMIZE: As this is fairly expensive and causes worse
4052   // code to be emitted subsequently, it is useful to avoid calling
4053   // it.  (Bug 1316802)
4054   //
4055   // Some optimization has been done already.  Remaining
4056   // opportunities:
4057   //
4058   //  - It would be interesting to see if we can specialize it
4059   //    before calls with particularly simple signatures, or where
4060   //    we can do parallel assignment of register arguments, or
4061   //    similar.  See notes in emitCall().
4062   //
4063   //  - Operations that need specific registers: multiply, quotient,
4064   //    remainder, will tend to sync because the registers we need
4065   //    will tend to be allocated.  We may be able to avoid that by
4066   //    prioritizing registers differently (takeLast instead of
4067   //    takeFirst) but we may also be able to allocate an unused
4068   //    register on demand to free up one we need, thus avoiding the
4069   //    sync.  That type of fix would go into needI32().
4070 
sync()4071   void sync() final {
4072     size_t start = 0;
4073     size_t lim = stk_.length();
4074 
4075     for (size_t i = lim; i > 0; i--) {
4076       // Memory opcodes are first in the enum, single check against MemLast is
4077       // fine.
4078       if (stk_[i - 1].kind() <= Stk::MemLast) {
4079         start = i;
4080         break;
4081       }
4082     }
4083 
4084     for (size_t i = start; i < lim; i++) {
4085       Stk& v = stk_[i];
4086       switch (v.kind()) {
4087         case Stk::LocalI32: {
4088           ScratchI32 scratch(*this);
4089           loadLocalI32(v, scratch);
4090           uint32_t offs = fr.pushGPR(scratch);
4091           v.setOffs(Stk::MemI32, offs);
4092           break;
4093         }
4094         case Stk::RegisterI32: {
4095           uint32_t offs = fr.pushGPR(v.i32reg());
4096           freeI32(v.i32reg());
4097           v.setOffs(Stk::MemI32, offs);
4098           break;
4099         }
4100         case Stk::LocalI64: {
4101           ScratchI32 scratch(*this);
4102 #ifdef JS_PUNBOX64
4103           loadI64(v, fromI32(scratch));
4104           uint32_t offs = fr.pushGPR(scratch);
4105 #else
4106           fr.loadLocalI64High(localFromSlot(v.slot(), MIRType::Int64), scratch);
4107           fr.pushGPR(scratch);
4108           fr.loadLocalI64Low(localFromSlot(v.slot(), MIRType::Int64), scratch);
4109           uint32_t offs = fr.pushGPR(scratch);
4110 #endif
4111           v.setOffs(Stk::MemI64, offs);
4112           break;
4113         }
4114         case Stk::RegisterI64: {
4115 #ifdef JS_PUNBOX64
4116           uint32_t offs = fr.pushGPR(v.i64reg().reg);
4117           freeI64(v.i64reg());
4118 #else
4119           fr.pushGPR(v.i64reg().high);
4120           uint32_t offs = fr.pushGPR(v.i64reg().low);
4121           freeI64(v.i64reg());
4122 #endif
4123           v.setOffs(Stk::MemI64, offs);
4124           break;
4125         }
4126         case Stk::LocalF64: {
4127           ScratchF64 scratch(*this);
4128           loadF64(v, scratch);
4129           uint32_t offs = fr.pushDouble(scratch);
4130           v.setOffs(Stk::MemF64, offs);
4131           break;
4132         }
4133         case Stk::RegisterF64: {
4134           uint32_t offs = fr.pushDouble(v.f64reg());
4135           freeF64(v.f64reg());
4136           v.setOffs(Stk::MemF64, offs);
4137           break;
4138         }
4139         case Stk::LocalF32: {
4140           ScratchF32 scratch(*this);
4141           loadF32(v, scratch);
4142           uint32_t offs = fr.pushFloat32(scratch);
4143           v.setOffs(Stk::MemF32, offs);
4144           break;
4145         }
4146         case Stk::RegisterF32: {
4147           uint32_t offs = fr.pushFloat32(v.f32reg());
4148           freeF32(v.f32reg());
4149           v.setOffs(Stk::MemF32, offs);
4150           break;
4151         }
4152 #ifdef ENABLE_WASM_SIMD
4153         case Stk::LocalV128: {
4154           ScratchV128 scratch(*this);
4155           loadV128(v, scratch);
4156           uint32_t offs = fr.pushV128(scratch);
4157           v.setOffs(Stk::MemV128, offs);
4158           break;
4159         }
4160         case Stk::RegisterV128: {
4161           uint32_t offs = fr.pushV128(v.v128reg());
4162           freeV128(v.v128reg());
4163           v.setOffs(Stk::MemV128, offs);
4164           break;
4165         }
4166 #endif
4167         case Stk::LocalRef: {
4168           ScratchRef scratch(*this);
4169           loadLocalRef(v, scratch);
4170           uint32_t offs = fr.pushGPR(scratch);
4171           v.setOffs(Stk::MemRef, offs);
4172           stackMapGenerator_.memRefsOnStk++;
4173           break;
4174         }
4175         case Stk::RegisterRef: {
4176           uint32_t offs = fr.pushGPR(v.refReg());
4177           freeRef(v.refReg());
4178           v.setOffs(Stk::MemRef, offs);
4179           stackMapGenerator_.memRefsOnStk++;
4180           break;
4181         }
4182         default: {
4183           break;
4184         }
4185       }
4186     }
4187   }
4188 
saveTempPtr(RegPtr r)4189   void saveTempPtr(RegPtr r) final {
4190     MOZ_ASSERT(!ra.isAvailablePtr(r));
4191     fr.pushGPR(r);
4192     ra.freePtr(r);
4193     MOZ_ASSERT(ra.isAvailablePtr(r));
4194   }
4195 
restoreTempPtr(RegPtr r)4196   void restoreTempPtr(RegPtr r) final {
4197     MOZ_ASSERT(ra.isAvailablePtr(r));
4198     ra.needPtr(r);
4199     fr.popGPR(r);
4200     MOZ_ASSERT(!ra.isAvailablePtr(r));
4201   }
4202 
4203   // Various methods for creating a stackmap.  Stackmaps are indexed by the
4204   // lowest address of the instruction immediately *after* the instruction of
4205   // interest.  In practice that means either: the return point of a call, the
4206   // instruction immediately after a trap instruction (the "resume"
4207   // instruction), or the instruction immediately following a no-op (when
4208   // debugging is enabled).
4209 
4210   // Create a vanilla stackmap.
createStackMap(const char * who)4211   [[nodiscard]] bool createStackMap(const char* who) {
4212     const ExitStubMapVector noExtras;
4213     return createStackMap(who, noExtras, masm.currentOffset());
4214   }
4215 
4216   // Create a stackmap as vanilla, but for a custom assembler offset.
createStackMap(const char * who,CodeOffset assemblerOffset)4217   [[nodiscard]] bool createStackMap(const char* who,
4218                                     CodeOffset assemblerOffset) {
4219     const ExitStubMapVector noExtras;
4220     return createStackMap(who, noExtras, assemblerOffset.offset());
4221   }
4222 
4223   // The most general stackmap construction.
createStackMap(const char * who,const ExitStubMapVector & extras,uint32_t assemblerOffset)4224   [[nodiscard]] bool createStackMap(const char* who,
4225                                     const ExitStubMapVector& extras,
4226                                     uint32_t assemblerOffset) {
4227     auto debugFrame =
4228         compilerEnv_.debugEnabled() ? HasDebugFrame::Yes : HasDebugFrame::No;
4229     return stackMapGenerator_.createStackMap(who, extras, assemblerOffset,
4230                                              debugFrame, stk_);
4231   }
4232 
4233   // This is an optimization used to avoid calling sync() for
4234   // setLocal(): if the local does not exist unresolved on the stack
4235   // then we can skip the sync.
4236 
hasLocal(uint32_t slot)4237   bool hasLocal(uint32_t slot) {
4238     for (size_t i = stk_.length(); i > 0; i--) {
4239       // Memory opcodes are first in the enum, single check against MemLast is
4240       // fine.
4241       Stk::Kind kind = stk_[i - 1].kind();
4242       if (kind <= Stk::MemLast) {
4243         return false;
4244       }
4245 
4246       // Local opcodes follow memory opcodes in the enum, single check against
4247       // LocalLast is sufficient.
4248       if (kind <= Stk::LocalLast && stk_[i - 1].slot() == slot) {
4249         return true;
4250       }
4251     }
4252     return false;
4253   }
4254 
syncLocal(uint32_t slot)4255   void syncLocal(uint32_t slot) {
4256     if (hasLocal(slot)) {
4257       sync();  // TODO / OPTIMIZE: Improve this?  (Bug 1316817)
4258     }
4259   }
4260 
4261   // Push the register r onto the stack.
4262 
pushAny(AnyReg r)4263   void pushAny(AnyReg r) {
4264     switch (r.tag) {
4265       case AnyReg::I32: {
4266         pushI32(r.i32());
4267         break;
4268       }
4269       case AnyReg::I64: {
4270         pushI64(r.i64());
4271         break;
4272       }
4273       case AnyReg::F32: {
4274         pushF32(r.f32());
4275         break;
4276       }
4277       case AnyReg::F64: {
4278         pushF64(r.f64());
4279         break;
4280       }
4281 #ifdef ENABLE_WASM_SIMD
4282       case AnyReg::V128: {
4283         pushV128(r.v128());
4284         break;
4285       }
4286 #endif
4287       case AnyReg::REF: {
4288         pushRef(r.ref());
4289         break;
4290       }
4291     }
4292   }
4293 
pushI32(RegI32 r)4294   void pushI32(RegI32 r) {
4295     MOZ_ASSERT(!isAvailableI32(r));
4296     push(Stk(r));
4297   }
4298 
pushI64(RegI64 r)4299   void pushI64(RegI64 r) {
4300     MOZ_ASSERT(!isAvailableI64(r));
4301     push(Stk(r));
4302   }
4303 
pushRef(RegRef r)4304   void pushRef(RegRef r) {
4305     MOZ_ASSERT(!isAvailableRef(r));
4306     push(Stk(r));
4307   }
4308 
pushPtr(RegPtr r)4309   void pushPtr(RegPtr r) {
4310     MOZ_ASSERT(!isAvailablePtr(r));
4311 #ifdef JS_64BIT
4312     pushI64(RegI64(Register64(r)));
4313 #else
4314     pushI32(RegI32(r));
4315 #endif
4316   }
4317 
pushF64(RegF64 r)4318   void pushF64(RegF64 r) {
4319     MOZ_ASSERT(!isAvailableF64(r));
4320     push(Stk(r));
4321   }
4322 
pushF32(RegF32 r)4323   void pushF32(RegF32 r) {
4324     MOZ_ASSERT(!isAvailableF32(r));
4325     push(Stk(r));
4326   }
4327 
4328 #ifdef ENABLE_WASM_SIMD
pushV128(RegV128 r)4329   void pushV128(RegV128 r) {
4330     MOZ_ASSERT(!isAvailableV128(r));
4331     push(Stk(r));
4332   }
4333 #endif
4334 
4335   // Push the value onto the stack.  PushI32 can also take uint32_t, and PushI64
4336   // can take uint64_t; the semantics are the same.  Appropriate sign extension
4337   // for a 32-bit value on a 64-bit architecture happens when the value is
4338   // popped, see the definition of moveImm32 below.
4339 
pushI32(int32_t v)4340   void pushI32(int32_t v) { push(Stk(v)); }
4341 
pushI64(int64_t v)4342   void pushI64(int64_t v) { push(Stk(v)); }
4343 
pushRef(intptr_t v)4344   void pushRef(intptr_t v) { pushConstRef(v); }
4345 
pushF64(double v)4346   void pushF64(double v) { push(Stk(v)); }
4347 
pushF32(float v)4348   void pushF32(float v) { push(Stk(v)); }
4349 
4350 #ifdef ENABLE_WASM_SIMD
pushV128(V128 v)4351   void pushV128(V128 v) { push(Stk(v)); }
4352 #endif
4353 
4354   // Push the local slot onto the stack.  The slot will not be read
4355   // here; it will be read when it is consumed, or when a side
4356   // effect to the slot forces its value to be saved.
4357 
pushLocalI32(uint32_t slot)4358   void pushLocalI32(uint32_t slot) {
4359     stk_.infallibleEmplaceBack(Stk(Stk::LocalI32, slot));
4360   }
4361 
pushLocalI64(uint32_t slot)4362   void pushLocalI64(uint32_t slot) {
4363     stk_.infallibleEmplaceBack(Stk(Stk::LocalI64, slot));
4364   }
4365 
pushLocalRef(uint32_t slot)4366   void pushLocalRef(uint32_t slot) {
4367     stk_.infallibleEmplaceBack(Stk(Stk::LocalRef, slot));
4368   }
4369 
pushLocalF64(uint32_t slot)4370   void pushLocalF64(uint32_t slot) {
4371     stk_.infallibleEmplaceBack(Stk(Stk::LocalF64, slot));
4372   }
4373 
pushLocalF32(uint32_t slot)4374   void pushLocalF32(uint32_t slot) {
4375     stk_.infallibleEmplaceBack(Stk(Stk::LocalF32, slot));
4376   }
4377 
4378 #ifdef ENABLE_WASM_SIMD
pushLocalV128(uint32_t slot)4379   void pushLocalV128(uint32_t slot) {
4380     stk_.infallibleEmplaceBack(Stk(Stk::LocalV128, slot));
4381   }
4382 #endif
4383 
dupAny(AnyReg r)4384   AnyReg dupAny(AnyReg r) {
4385     switch (r.tag) {
4386       case AnyReg::I32: {
4387         RegI32 x = needI32();
4388         moveI32(r.i32(), x);
4389         return AnyReg(x);
4390       }
4391       case AnyReg::I64: {
4392         RegI64 x = needI64();
4393         moveI64(r.i64(), x);
4394         return AnyReg(x);
4395       }
4396       case AnyReg::F32: {
4397         RegF32 x = needF32();
4398         moveF32(r.f32(), x);
4399         return AnyReg(x);
4400       }
4401       case AnyReg::F64: {
4402         RegF64 x = needF64();
4403         moveF64(r.f64(), x);
4404         return AnyReg(x);
4405       }
4406 #ifdef ENABLE_WASM_SIMD
4407       case AnyReg::V128: {
4408         RegV128 x = needV128();
4409         moveV128(r.v128(), x);
4410         return AnyReg(x);
4411       }
4412 #endif
4413       case AnyReg::REF: {
4414         RegRef x = needRef();
4415         moveRef(r.ref(), x);
4416         return AnyReg(x);
4417       }
4418       default:
4419         MOZ_CRASH();
4420     }
4421   }
4422 
popAny(AnyReg specific)4423   AnyReg popAny(AnyReg specific) {
4424     switch (stk_.back().kind()) {
4425       case Stk::MemI32:
4426       case Stk::LocalI32:
4427       case Stk::RegisterI32:
4428       case Stk::ConstI32:
4429         return AnyReg(popI32(specific.i32()));
4430 
4431       case Stk::MemI64:
4432       case Stk::LocalI64:
4433       case Stk::RegisterI64:
4434       case Stk::ConstI64:
4435         return AnyReg(popI64(specific.i64()));
4436 
4437       case Stk::MemF32:
4438       case Stk::LocalF32:
4439       case Stk::RegisterF32:
4440       case Stk::ConstF32:
4441         return AnyReg(popF32(specific.f32()));
4442 
4443       case Stk::MemF64:
4444       case Stk::LocalF64:
4445       case Stk::RegisterF64:
4446       case Stk::ConstF64:
4447         return AnyReg(popF64(specific.f64()));
4448 
4449 #ifdef ENABLE_WASM_SIMD
4450       case Stk::MemV128:
4451       case Stk::LocalV128:
4452       case Stk::RegisterV128:
4453       case Stk::ConstV128:
4454         return AnyReg(popV128(specific.v128()));
4455 #endif
4456 
4457       case Stk::MemRef:
4458       case Stk::LocalRef:
4459       case Stk::RegisterRef:
4460       case Stk::ConstRef:
4461         return AnyReg(popRef(specific.ref()));
4462 
4463       case Stk::Unknown:
4464         MOZ_CRASH();
4465 
4466       default:
4467         MOZ_CRASH();
4468     }
4469   }
4470 
popAny()4471   AnyReg popAny() {
4472     switch (stk_.back().kind()) {
4473       case Stk::MemI32:
4474       case Stk::LocalI32:
4475       case Stk::RegisterI32:
4476       case Stk::ConstI32:
4477         return AnyReg(popI32());
4478 
4479       case Stk::MemI64:
4480       case Stk::LocalI64:
4481       case Stk::RegisterI64:
4482       case Stk::ConstI64:
4483         return AnyReg(popI64());
4484 
4485       case Stk::MemF32:
4486       case Stk::LocalF32:
4487       case Stk::RegisterF32:
4488       case Stk::ConstF32:
4489         return AnyReg(popF32());
4490 
4491       case Stk::MemF64:
4492       case Stk::LocalF64:
4493       case Stk::RegisterF64:
4494       case Stk::ConstF64:
4495         return AnyReg(popF64());
4496 
4497 #ifdef ENABLE_WASM_SIMD
4498       case Stk::MemV128:
4499       case Stk::LocalV128:
4500       case Stk::RegisterV128:
4501       case Stk::ConstV128:
4502         return AnyReg(popV128());
4503 #endif
4504 
4505       case Stk::MemRef:
4506       case Stk::LocalRef:
4507       case Stk::RegisterRef:
4508       case Stk::ConstRef:
4509         return AnyReg(popRef());
4510 
4511       case Stk::Unknown:
4512         MOZ_CRASH();
4513 
4514       default:
4515         MOZ_CRASH();
4516     }
4517   }
4518 
4519   // Call only from other popI32() variants.
4520   // v must be the stack top.  May pop the CPU stack.
4521 
popI32(const Stk & v,RegI32 dest)4522   void popI32(const Stk& v, RegI32 dest) {
4523     MOZ_ASSERT(&v == &stk_.back());
4524     switch (v.kind()) {
4525       case Stk::ConstI32:
4526         loadConstI32(v, dest);
4527         break;
4528       case Stk::LocalI32:
4529         loadLocalI32(v, dest);
4530         break;
4531       case Stk::MemI32:
4532         fr.popGPR(dest);
4533         break;
4534       case Stk::RegisterI32:
4535         loadRegisterI32(v, dest);
4536         break;
4537       default:
4538         MOZ_CRASH("Compiler bug: expected int on stack");
4539     }
4540   }
4541 
popI32()4542   [[nodiscard]] RegI32 popI32() {
4543     Stk& v = stk_.back();
4544     RegI32 r;
4545     if (v.kind() == Stk::RegisterI32) {
4546       r = v.i32reg();
4547     } else {
4548       popI32(v, (r = needI32()));
4549     }
4550     stk_.popBack();
4551     return r;
4552   }
4553 
popI32(RegI32 specific)4554   RegI32 popI32(RegI32 specific) {
4555     Stk& v = stk_.back();
4556 
4557     if (!(v.kind() == Stk::RegisterI32 && v.i32reg() == specific)) {
4558       needI32(specific);
4559       popI32(v, specific);
4560       if (v.kind() == Stk::RegisterI32) {
4561         freeI32(v.i32reg());
4562       }
4563     }
4564 
4565     stk_.popBack();
4566     return specific;
4567   }
4568 
4569 #ifdef ENABLE_WASM_SIMD
4570   // Call only from other popV128() variants.
4571   // v must be the stack top.  May pop the CPU stack.
4572 
popV128(const Stk & v,RegV128 dest)4573   void popV128(const Stk& v, RegV128 dest) {
4574     MOZ_ASSERT(&v == &stk_.back());
4575     switch (v.kind()) {
4576       case Stk::ConstV128:
4577         loadConstV128(v, dest);
4578         break;
4579       case Stk::LocalV128:
4580         loadLocalV128(v, dest);
4581         break;
4582       case Stk::MemV128:
4583         fr.popV128(dest);
4584         break;
4585       case Stk::RegisterV128:
4586         loadRegisterV128(v, dest);
4587         break;
4588       default:
4589         MOZ_CRASH("Compiler bug: expected int on stack");
4590     }
4591   }
4592 
popV128()4593   [[nodiscard]] RegV128 popV128() {
4594     Stk& v = stk_.back();
4595     RegV128 r;
4596     if (v.kind() == Stk::RegisterV128) {
4597       r = v.v128reg();
4598     } else {
4599       popV128(v, (r = needV128()));
4600     }
4601     stk_.popBack();
4602     return r;
4603   }
4604 
popV128(RegV128 specific)4605   RegV128 popV128(RegV128 specific) {
4606     Stk& v = stk_.back();
4607 
4608     if (!(v.kind() == Stk::RegisterV128 && v.v128reg() == specific)) {
4609       needV128(specific);
4610       popV128(v, specific);
4611       if (v.kind() == Stk::RegisterV128) {
4612         freeV128(v.v128reg());
4613       }
4614     }
4615 
4616     stk_.popBack();
4617     return specific;
4618   }
4619 #endif
4620 
4621   // Call only from other popI64() variants.
4622   // v must be the stack top.  May pop the CPU stack.
4623 
popI64(const Stk & v,RegI64 dest)4624   void popI64(const Stk& v, RegI64 dest) {
4625     MOZ_ASSERT(&v == &stk_.back());
4626     switch (v.kind()) {
4627       case Stk::ConstI64:
4628         loadConstI64(v, dest);
4629         break;
4630       case Stk::LocalI64:
4631         loadLocalI64(v, dest);
4632         break;
4633       case Stk::MemI64:
4634 #ifdef JS_PUNBOX64
4635         fr.popGPR(dest.reg);
4636 #else
4637         fr.popGPR(dest.low);
4638         fr.popGPR(dest.high);
4639 #endif
4640         break;
4641       case Stk::RegisterI64:
4642         loadRegisterI64(v, dest);
4643         break;
4644       default:
4645         MOZ_CRASH("Compiler bug: expected long on stack");
4646     }
4647   }
4648 
popI64()4649   [[nodiscard]] RegI64 popI64() {
4650     Stk& v = stk_.back();
4651     RegI64 r;
4652     if (v.kind() == Stk::RegisterI64) {
4653       r = v.i64reg();
4654     } else {
4655       popI64(v, (r = needI64()));
4656     }
4657     stk_.popBack();
4658     return r;
4659   }
4660 
4661   // Note, the stack top can be in one half of "specific" on 32-bit
4662   // systems.  We can optimize, but for simplicity, if the register
4663   // does not match exactly, then just force the stack top to memory
4664   // and then read it back in.
4665 
popI64(RegI64 specific)4666   RegI64 popI64(RegI64 specific) {
4667     Stk& v = stk_.back();
4668 
4669     if (!(v.kind() == Stk::RegisterI64 && v.i64reg() == specific)) {
4670       needI64(specific);
4671       popI64(v, specific);
4672       if (v.kind() == Stk::RegisterI64) {
4673         freeI64(v.i64reg());
4674       }
4675     }
4676 
4677     stk_.popBack();
4678     return specific;
4679   }
4680 
4681   // Call only from other popRef() variants.
4682   // v must be the stack top.  May pop the CPU stack.
4683 
popRef(const Stk & v,RegRef dest)4684   void popRef(const Stk& v, RegRef dest) {
4685     MOZ_ASSERT(&v == &stk_.back());
4686     switch (v.kind()) {
4687       case Stk::ConstRef:
4688         loadConstRef(v, dest);
4689         break;
4690       case Stk::LocalRef:
4691         loadLocalRef(v, dest);
4692         break;
4693       case Stk::MemRef:
4694         fr.popGPR(dest);
4695         break;
4696       case Stk::RegisterRef:
4697         loadRegisterRef(v, dest);
4698         break;
4699       default:
4700         MOZ_CRASH("Compiler bug: expected ref on stack");
4701     }
4702   }
4703 
popRef(RegRef specific)4704   RegRef popRef(RegRef specific) {
4705     Stk& v = stk_.back();
4706 
4707     if (!(v.kind() == Stk::RegisterRef && v.refReg() == specific)) {
4708       needRef(specific);
4709       popRef(v, specific);
4710       if (v.kind() == Stk::RegisterRef) {
4711         freeRef(v.refReg());
4712       }
4713     }
4714 
4715     stk_.popBack();
4716     if (v.kind() == Stk::MemRef) {
4717       stackMapGenerator_.memRefsOnStk--;
4718     }
4719     return specific;
4720   }
4721 
popRef()4722   [[nodiscard]] RegRef popRef() {
4723     Stk& v = stk_.back();
4724     RegRef r;
4725     if (v.kind() == Stk::RegisterRef) {
4726       r = v.refReg();
4727     } else {
4728       popRef(v, (r = needRef()));
4729     }
4730     stk_.popBack();
4731     if (v.kind() == Stk::MemRef) {
4732       stackMapGenerator_.memRefsOnStk--;
4733     }
4734     return r;
4735   }
4736 
4737   // Call only from other popPtr() variants.
4738   // v must be the stack top.  May pop the CPU stack.
4739 
popPtr(const Stk & v,RegPtr dest)4740   void popPtr(const Stk& v, RegPtr dest) {
4741 #ifdef JS_64BIT
4742     popI64(v, RegI64(Register64(dest)));
4743 #else
4744     popI32(v, RegI32(dest));
4745 #endif
4746   }
4747 
popPtr(RegPtr specific)4748   RegPtr popPtr(RegPtr specific) {
4749 #ifdef JS_64BIT
4750     return RegPtr(popI64(RegI64(Register64(specific))).reg);
4751 #else
4752     return RegPtr(popI32(RegI32(specific)));
4753 #endif
4754   }
4755 
popPtr()4756   [[nodiscard]] RegPtr popPtr() {
4757 #ifdef JS_64BIT
4758     return RegPtr(popI64().reg);
4759 #else
4760     return RegPtr(popI32());
4761 #endif
4762   }
4763 
4764   // Call only from other popF64() variants.
4765   // v must be the stack top.  May pop the CPU stack.
4766 
popF64(const Stk & v,RegF64 dest)4767   void popF64(const Stk& v, RegF64 dest) {
4768     MOZ_ASSERT(&v == &stk_.back());
4769     switch (v.kind()) {
4770       case Stk::ConstF64:
4771         loadConstF64(v, dest);
4772         break;
4773       case Stk::LocalF64:
4774         loadLocalF64(v, dest);
4775         break;
4776       case Stk::MemF64:
4777         fr.popDouble(dest);
4778         break;
4779       case Stk::RegisterF64:
4780         loadRegisterF64(v, dest);
4781         break;
4782       default:
4783         MOZ_CRASH("Compiler bug: expected double on stack");
4784     }
4785   }
4786 
popF64()4787   [[nodiscard]] RegF64 popF64() {
4788     Stk& v = stk_.back();
4789     RegF64 r;
4790     if (v.kind() == Stk::RegisterF64) {
4791       r = v.f64reg();
4792     } else {
4793       popF64(v, (r = needF64()));
4794     }
4795     stk_.popBack();
4796     return r;
4797   }
4798 
popF64(RegF64 specific)4799   RegF64 popF64(RegF64 specific) {
4800     Stk& v = stk_.back();
4801 
4802     if (!(v.kind() == Stk::RegisterF64 && v.f64reg() == specific)) {
4803       needF64(specific);
4804       popF64(v, specific);
4805       if (v.kind() == Stk::RegisterF64) {
4806         freeF64(v.f64reg());
4807       }
4808     }
4809 
4810     stk_.popBack();
4811     return specific;
4812   }
4813 
4814   // Call only from other popF32() variants.
4815   // v must be the stack top.  May pop the CPU stack.
4816 
popF32(const Stk & v,RegF32 dest)4817   void popF32(const Stk& v, RegF32 dest) {
4818     MOZ_ASSERT(&v == &stk_.back());
4819     switch (v.kind()) {
4820       case Stk::ConstF32:
4821         loadConstF32(v, dest);
4822         break;
4823       case Stk::LocalF32:
4824         loadLocalF32(v, dest);
4825         break;
4826       case Stk::MemF32:
4827         fr.popFloat32(dest);
4828         break;
4829       case Stk::RegisterF32:
4830         loadRegisterF32(v, dest);
4831         break;
4832       default:
4833         MOZ_CRASH("Compiler bug: expected float on stack");
4834     }
4835   }
4836 
popF32()4837   [[nodiscard]] RegF32 popF32() {
4838     Stk& v = stk_.back();
4839     RegF32 r;
4840     if (v.kind() == Stk::RegisterF32) {
4841       r = v.f32reg();
4842     } else {
4843       popF32(v, (r = needF32()));
4844     }
4845     stk_.popBack();
4846     return r;
4847   }
4848 
popF32(RegF32 specific)4849   RegF32 popF32(RegF32 specific) {
4850     Stk& v = stk_.back();
4851 
4852     if (!(v.kind() == Stk::RegisterF32 && v.f32reg() == specific)) {
4853       needF32(specific);
4854       popF32(v, specific);
4855       if (v.kind() == Stk::RegisterF32) {
4856         freeF32(v.f32reg());
4857       }
4858     }
4859 
4860     stk_.popBack();
4861     return specific;
4862   }
4863 
popConst(int32_t * c)4864   [[nodiscard]] bool popConst(int32_t* c) {
4865     Stk& v = stk_.back();
4866     if (v.kind() != Stk::ConstI32) {
4867       return false;
4868     }
4869     *c = v.i32val();
4870     stk_.popBack();
4871     return true;
4872   }
4873 
popConst(int64_t * c)4874   [[nodiscard]] bool popConst(int64_t* c) {
4875     Stk& v = stk_.back();
4876     if (v.kind() != Stk::ConstI64) {
4877       return false;
4878     }
4879     *c = v.i64val();
4880     stk_.popBack();
4881     return true;
4882   }
4883 
peekConst(int32_t * c)4884   [[nodiscard]] bool peekConst(int32_t* c) {
4885     Stk& v = stk_.back();
4886     if (v.kind() != Stk::ConstI32) {
4887       return false;
4888     }
4889     *c = v.i32val();
4890     return true;
4891   }
4892 
peekConst(int64_t * c)4893   [[nodiscard]] bool peekConst(int64_t* c) {
4894     Stk& v = stk_.back();
4895     if (v.kind() != Stk::ConstI64) {
4896       return false;
4897     }
4898     *c = v.i64val();
4899     return true;
4900   }
4901 
peek2xConst(int32_t * c0,int32_t * c1)4902   [[nodiscard]] bool peek2xConst(int32_t* c0, int32_t* c1) {
4903     MOZ_ASSERT(stk_.length() >= 2);
4904     const Stk& v0 = *(stk_.end() - 1);
4905     const Stk& v1 = *(stk_.end() - 2);
4906     if (v0.kind() != Stk::ConstI32 || v1.kind() != Stk::ConstI32) {
4907       return false;
4908     }
4909     *c0 = v0.i32val();
4910     *c1 = v1.i32val();
4911     return true;
4912   }
4913 
popConstPositivePowerOfTwo(int32_t * c,uint_fast8_t * power,int32_t cutoff)4914   [[nodiscard]] bool popConstPositivePowerOfTwo(int32_t* c, uint_fast8_t* power,
4915                                                 int32_t cutoff) {
4916     Stk& v = stk_.back();
4917     if (v.kind() != Stk::ConstI32) {
4918       return false;
4919     }
4920     *c = v.i32val();
4921     if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint32_t>(*c))) {
4922       return false;
4923     }
4924     *power = FloorLog2(*c);
4925     stk_.popBack();
4926     return true;
4927   }
4928 
popConstPositivePowerOfTwo(int64_t * c,uint_fast8_t * power,int64_t cutoff)4929   [[nodiscard]] bool popConstPositivePowerOfTwo(int64_t* c, uint_fast8_t* power,
4930                                                 int64_t cutoff) {
4931     Stk& v = stk_.back();
4932     if (v.kind() != Stk::ConstI64) {
4933       return false;
4934     }
4935     *c = v.i64val();
4936     if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(*c))) {
4937       return false;
4938     }
4939     *power = FloorLog2(*c);
4940     stk_.popBack();
4941     return true;
4942   }
4943 
peekLocalI32(uint32_t * local)4944   [[nodiscard]] bool peekLocalI32(uint32_t* local) {
4945     Stk& v = stk_.back();
4946     if (v.kind() != Stk::LocalI32) {
4947       return false;
4948     }
4949     *local = v.slot();
4950     return true;
4951   }
4952 
4953   // TODO / OPTIMIZE (Bug 1316818): At the moment we use the Wasm
4954   // inter-procedure ABI for block returns, which allocates ReturnReg as the
4955   // single block result register.  It is possible other choices would lead to
4956   // better register allocation, as ReturnReg is often first in the register set
4957   // and will be heavily wanted by the register allocator that uses takeFirst().
4958   //
4959   // Obvious options:
4960   //  - pick a register at the back of the register set
4961   //  - pick a random register per block (different blocks have
4962   //    different join regs)
4963 
popRegisterResults(ABIResultIter & iter)4964   void popRegisterResults(ABIResultIter& iter) {
4965     // Pop register results.  Note that in the single-value case, popping to a
4966     // register may cause a sync(); for multi-value we sync'd already.
4967     for (; !iter.done(); iter.next()) {
4968       const ABIResult& result = iter.cur();
4969       if (!result.inRegister()) {
4970         // TODO / OPTIMIZE: We sync here to avoid solving the general parallel
4971         // move problem in popStackResults.  However we could avoid syncing the
4972         // values that are going to registers anyway, if they are already in
4973         // registers.
4974         sync();
4975         break;
4976       }
4977       switch (result.type().kind()) {
4978         case ValType::I32:
4979           popI32(RegI32(result.gpr()));
4980           break;
4981         case ValType::I64:
4982           popI64(RegI64(result.gpr64()));
4983           break;
4984         case ValType::F32:
4985           popF32(RegF32(result.fpr()));
4986           break;
4987         case ValType::F64:
4988           popF64(RegF64(result.fpr()));
4989           break;
4990         case ValType::Rtt:
4991         case ValType::Ref:
4992           popRef(RegRef(result.gpr()));
4993           break;
4994         case ValType::V128:
4995 #ifdef ENABLE_WASM_SIMD
4996           popV128(RegV128(result.fpr()));
4997 #else
4998           MOZ_CRASH("No SIMD support");
4999 #endif
5000       }
5001     }
5002   }
5003 
popStackResults(ABIResultIter & iter,StackHeight stackBase)5004   void popStackResults(ABIResultIter& iter, StackHeight stackBase) {
5005     MOZ_ASSERT(!iter.done());
5006 
5007     // The iterator should be advanced beyond register results, and register
5008     // results should be popped already from the value stack.
5009     uint32_t alreadyPopped = iter.index();
5010 
5011     // At this point, only stack arguments are remaining.  Iterate through them
5012     // to measure how much stack space they will take up.
5013     for (; !iter.done(); iter.next()) {
5014       MOZ_ASSERT(iter.cur().onStack());
5015     }
5016 
5017     // Calculate the space needed to store stack results, in bytes.
5018     uint32_t stackResultBytes = iter.stackBytesConsumedSoFar();
5019     MOZ_ASSERT(stackResultBytes);
5020 
5021     // Compute the stack height including the stack results.  Note that it's
5022     // possible that this call expands the stack, for example if some of the
5023     // results are supplied by constants and so are not already on the machine
5024     // stack.
5025     uint32_t endHeight = fr.prepareStackResultArea(stackBase, stackResultBytes);
5026 
5027     // Find a free GPR to use when shuffling stack values.  If none is
5028     // available, push ReturnReg and restore it after we're done.
5029     bool saved = false;
5030     RegPtr temp = ra.needTempPtr(RegPtr(ReturnReg), &saved);
5031 
5032     // The sequence of Stk values is in the same order on the machine stack as
5033     // the result locations, but there is a complication: constant values are
5034     // not actually pushed on the machine stack.  (At this point registers and
5035     // locals have been spilled already.)  So, moving the Stk values into place
5036     // isn't simply a shuffle-down or shuffle-up operation.  There is a part of
5037     // the Stk sequence that shuffles toward the FP, a part that's already in
5038     // place, and a part that shuffles toward the SP.  After shuffling, we have
5039     // to materialize the constants.
5040 
5041     // Shuffle mem values toward the frame pointer, copying deepest values
5042     // first.  Stop when we run out of results, get to a register result, or
5043     // find a Stk value that is closer to the FP than the result.
5044     for (iter.switchToPrev(); !iter.done(); iter.prev()) {
5045       const ABIResult& result = iter.cur();
5046       if (!result.onStack()) {
5047         break;
5048       }
5049       MOZ_ASSERT(result.stackOffset() < stackResultBytes);
5050       uint32_t destHeight = endHeight - result.stackOffset();
5051       uint32_t stkBase = stk_.length() - (iter.count() - alreadyPopped);
5052       Stk& v = stk_[stkBase + iter.index()];
5053       if (v.isMem()) {
5054         uint32_t srcHeight = v.offs();
5055         if (srcHeight <= destHeight) {
5056           break;
5057         }
5058         fr.shuffleStackResultsTowardFP(srcHeight, destHeight, result.size(),
5059                                        temp);
5060       }
5061     }
5062 
5063     // Reset iterator and skip register results.
5064     for (iter.reset(); !iter.done(); iter.next()) {
5065       if (iter.cur().onStack()) {
5066         break;
5067       }
5068     }
5069 
5070     // Revisit top stack values, shuffling mem values toward the stack pointer,
5071     // copying shallowest values first.
5072     for (; !iter.done(); iter.next()) {
5073       const ABIResult& result = iter.cur();
5074       MOZ_ASSERT(result.onStack());
5075       MOZ_ASSERT(result.stackOffset() < stackResultBytes);
5076       uint32_t destHeight = endHeight - result.stackOffset();
5077       Stk& v = stk_[stk_.length() - (iter.index() - alreadyPopped) - 1];
5078       if (v.isMem()) {
5079         uint32_t srcHeight = v.offs();
5080         if (srcHeight >= destHeight) {
5081           break;
5082         }
5083         fr.shuffleStackResultsTowardSP(srcHeight, destHeight, result.size(),
5084                                        temp);
5085       }
5086     }
5087 
5088     // Reset iterator and skip register results, which are already popped off
5089     // the value stack.
5090     for (iter.reset(); !iter.done(); iter.next()) {
5091       if (iter.cur().onStack()) {
5092         break;
5093       }
5094     }
5095 
5096     // Materialize constants and pop the remaining items from the value stack.
5097     for (; !iter.done(); iter.next()) {
5098       const ABIResult& result = iter.cur();
5099       uint32_t resultHeight = endHeight - result.stackOffset();
5100       Stk& v = stk_.back();
5101       switch (v.kind()) {
5102         case Stk::ConstI32:
5103           fr.storeImmediatePtrToStack(uint32_t(v.i32val_), resultHeight, temp);
5104           break;
5105         case Stk::ConstF32:
5106           fr.storeImmediateF32ToStack(v.f32val_, resultHeight, temp);
5107           break;
5108         case Stk::ConstI64:
5109           fr.storeImmediateI64ToStack(v.i64val_, resultHeight, temp);
5110           break;
5111         case Stk::ConstF64:
5112           fr.storeImmediateF64ToStack(v.f64val_, resultHeight, temp);
5113           break;
5114 #ifdef ENABLE_WASM_SIMD
5115         case Stk::ConstV128:
5116           fr.storeImmediateV128ToStack(v.v128val_, resultHeight, temp);
5117           break;
5118 #endif
5119         case Stk::ConstRef:
5120           fr.storeImmediatePtrToStack(v.refval_, resultHeight, temp);
5121           break;
5122         case Stk::MemRef:
5123           // Update bookkeeping as we pop the Stk entry.
5124           stackMapGenerator_.memRefsOnStk--;
5125           break;
5126         default:
5127           MOZ_ASSERT(v.isMem());
5128           break;
5129       }
5130       stk_.popBack();
5131     }
5132 
5133     ra.freeTempPtr(temp, saved);
5134 
5135     // This will pop the stack if needed.
5136     fr.finishStackResultArea(stackBase, stackResultBytes);
5137   }
5138 
5139   enum class ContinuationKind { Fallthrough, Jump };
5140 
popBlockResults(ResultType type,StackHeight stackBase,ContinuationKind kind)5141   void popBlockResults(ResultType type, StackHeight stackBase,
5142                        ContinuationKind kind) {
5143     if (!type.empty()) {
5144       ABIResultIter iter(type);
5145       popRegisterResults(iter);
5146       if (!iter.done()) {
5147         popStackResults(iter, stackBase);
5148         // Because popStackResults might clobber the stack, it leaves the stack
5149         // pointer already in the right place for the continuation, whether the
5150         // continuation is a jump or fallthrough.
5151         return;
5152       }
5153     }
5154     // We get here if there are no stack results.  For a fallthrough, the stack
5155     // is already at the right height.  For a jump, we may need to pop the stack
5156     // pointer if the continuation's stack height is lower than the current
5157     // stack height.
5158     if (kind == ContinuationKind::Jump) {
5159       fr.popStackBeforeBranch(stackBase, type);
5160     }
5161   }
5162 
5163 #ifdef ENABLE_WASM_EXCEPTIONS
5164   // This function is similar to popBlockResults, but additionally handles the
5165   // implicit exception pointer that is pushed to the value stack on entry to
5166   // a catch handler by dropping it appropriately.
popCatchResults(ResultType type,StackHeight stackBase)5167   void popCatchResults(ResultType type, StackHeight stackBase) {
5168     if (!type.empty()) {
5169       ABIResultIter iter(type);
5170       popRegisterResults(iter);
5171       if (!iter.done()) {
5172         popStackResults(iter, stackBase);
5173         // Since popStackResults clobbers the stack, we only need to free the
5174         // exception off of the value stack.
5175         popValueStackBy(1);
5176       } else {
5177         // If there are no stack results, we have to adjust the stack by
5178         // dropping the exception reference that's now on the stack.
5179         dropValue();
5180       }
5181     } else {
5182       dropValue();
5183     }
5184     fr.popStackBeforeBranch(stackBase, type);
5185   }
5186 #endif
5187 
captureStackResult(const ABIResult & result,StackHeight resultsBase,uint32_t stackResultBytes)5188   Stk captureStackResult(const ABIResult& result, StackHeight resultsBase,
5189                          uint32_t stackResultBytes) {
5190     MOZ_ASSERT(result.onStack());
5191     uint32_t offs = fr.locateStackResult(result, resultsBase, stackResultBytes);
5192     return Stk::StackResult(result.type(), offs);
5193   }
5194 
pushResults(ResultType type,StackHeight resultsBase)5195   [[nodiscard]] bool pushResults(ResultType type, StackHeight resultsBase) {
5196     if (type.empty()) {
5197       return true;
5198     }
5199 
5200     if (type.length() > 1) {
5201       // Reserve extra space on the stack for all the values we'll push.
5202       // Multi-value push is not accounted for by the pre-sizing of the stack in
5203       // the decoding loop.
5204       //
5205       // Also make sure we leave headroom for other pushes that will occur after
5206       // pushing results, just to be safe.
5207       if (!stk_.reserve(stk_.length() + type.length() + MaxPushesPerOpcode)) {
5208         return false;
5209       }
5210     }
5211 
5212     // We need to push the results in reverse order, so first iterate through
5213     // all results to determine the locations of stack result types.
5214     ABIResultIter iter(type);
5215     while (!iter.done()) {
5216       iter.next();
5217     }
5218     uint32_t stackResultBytes = iter.stackBytesConsumedSoFar();
5219     for (iter.switchToPrev(); !iter.done(); iter.prev()) {
5220       const ABIResult& result = iter.cur();
5221       if (!result.onStack()) {
5222         break;
5223       }
5224       Stk v = captureStackResult(result, resultsBase, stackResultBytes);
5225       push(v);
5226       if (v.kind() == Stk::MemRef) {
5227         stackMapGenerator_.memRefsOnStk++;
5228       }
5229     }
5230 
5231     for (; !iter.done(); iter.prev()) {
5232       const ABIResult& result = iter.cur();
5233       MOZ_ASSERT(result.inRegister());
5234       switch (result.type().kind()) {
5235         case ValType::I32:
5236           pushI32(RegI32(result.gpr()));
5237           break;
5238         case ValType::I64:
5239           pushI64(RegI64(result.gpr64()));
5240           break;
5241         case ValType::V128:
5242 #ifdef ENABLE_WASM_SIMD
5243           pushV128(RegV128(result.fpr()));
5244           break;
5245 #else
5246           MOZ_CRASH("No SIMD support");
5247 #endif
5248         case ValType::F32:
5249           pushF32(RegF32(result.fpr()));
5250           break;
5251         case ValType::F64:
5252           pushF64(RegF64(result.fpr()));
5253           break;
5254         case ValType::Rtt:
5255         case ValType::Ref:
5256           pushRef(RegRef(result.gpr()));
5257           break;
5258       }
5259     }
5260 
5261     return true;
5262   }
5263 
pushBlockResults(ResultType type)5264   [[nodiscard]] bool pushBlockResults(ResultType type) {
5265     return pushResults(type, controlItem().stackHeight);
5266   }
5267 
5268   // A combination of popBlockResults + pushBlockResults, used when entering a
5269   // block with a control-flow join (loops) or split (if) to shuffle the
5270   // fallthrough block parameters into the locations expected by the
5271   // continuation.
topBlockParams(ResultType type)5272   [[nodiscard]] bool topBlockParams(ResultType type) {
5273     // This function should only be called when entering a block with a
5274     // control-flow join at the entry, where there are no live temporaries in
5275     // the current block.
5276     StackHeight base = controlItem().stackHeight;
5277     MOZ_ASSERT(fr.stackResultsBase(stackConsumed(type.length())) == base);
5278     popBlockResults(type, base, ContinuationKind::Fallthrough);
5279     return pushBlockResults(type);
5280   }
5281 
5282   // A combination of popBlockResults + pushBlockResults, used before branches
5283   // where we don't know the target (br_if / br_table).  If and when the branch
5284   // is taken, the stack results will be shuffled down into place.  For br_if
5285   // that has fallthrough, the parameters for the untaken branch flow through to
5286   // the continuation.
topBranchParams(ResultType type,StackHeight * height)5287   [[nodiscard]] bool topBranchParams(ResultType type, StackHeight* height) {
5288     if (type.empty()) {
5289       *height = fr.stackHeight();
5290       return true;
5291     }
5292     // There may be temporary values that need spilling; delay computation of
5293     // the stack results base until after the popRegisterResults(), which spills
5294     // if needed.
5295     ABIResultIter iter(type);
5296     popRegisterResults(iter);
5297     StackHeight base = fr.stackResultsBase(stackConsumed(iter.remaining()));
5298     if (!iter.done()) {
5299       popStackResults(iter, base);
5300     }
5301     if (!pushResults(type, base)) {
5302       return false;
5303     }
5304     *height = base;
5305     return true;
5306   }
5307 
5308   // Conditional branches with fallthrough are preceded by a topBranchParams, so
5309   // we know that there are no stack results that need to be materialized.  In
5310   // that case, we can just shuffle the whole block down before popping the
5311   // stack.
shuffleStackResultsBeforeBranch(StackHeight srcHeight,StackHeight destHeight,ResultType type)5312   void shuffleStackResultsBeforeBranch(StackHeight srcHeight,
5313                                        StackHeight destHeight,
5314                                        ResultType type) {
5315     uint32_t stackResultBytes = 0;
5316 
5317     if (ABIResultIter::HasStackResults(type)) {
5318       MOZ_ASSERT(stk_.length() >= type.length());
5319       ABIResultIter iter(type);
5320       for (; !iter.done(); iter.next()) {
5321 #ifdef DEBUG
5322         const ABIResult& result = iter.cur();
5323         const Stk& v = stk_[stk_.length() - iter.index() - 1];
5324         MOZ_ASSERT(v.isMem() == result.onStack());
5325 #endif
5326       }
5327 
5328       stackResultBytes = iter.stackBytesConsumedSoFar();
5329       MOZ_ASSERT(stackResultBytes > 0);
5330 
5331       if (srcHeight != destHeight) {
5332         // Find a free GPR to use when shuffling stack values.  If none
5333         // is available, push ReturnReg and restore it after we're done.
5334         bool saved = false;
5335         RegPtr temp = ra.needTempPtr(RegPtr(ReturnReg), &saved);
5336         fr.shuffleStackResultsTowardFP(srcHeight, destHeight, stackResultBytes,
5337                                        temp);
5338         ra.freeTempPtr(temp, saved);
5339       }
5340     }
5341 
5342     fr.popStackBeforeBranch(destHeight, stackResultBytes);
5343   }
5344 
5345   // Return the amount of execution stack consumed by the top numval
5346   // values on the value stack.
5347 
stackConsumed(size_t numval)5348   size_t stackConsumed(size_t numval) {
5349     size_t size = 0;
5350     MOZ_ASSERT(numval <= stk_.length());
5351     for (uint32_t i = stk_.length() - 1; numval > 0; numval--, i--) {
5352       Stk& v = stk_[i];
5353       switch (v.kind()) {
5354         case Stk::MemRef:
5355           size += BaseStackFrame::StackSizeOfPtr;
5356           break;
5357         case Stk::MemI32:
5358           size += BaseStackFrame::StackSizeOfPtr;
5359           break;
5360         case Stk::MemI64:
5361           size += BaseStackFrame::StackSizeOfInt64;
5362           break;
5363         case Stk::MemF64:
5364           size += BaseStackFrame::StackSizeOfDouble;
5365           break;
5366         case Stk::MemF32:
5367           size += BaseStackFrame::StackSizeOfFloat;
5368           break;
5369 #ifdef ENABLE_WASM_SIMD
5370         case Stk::MemV128:
5371           size += BaseStackFrame::StackSizeOfV128;
5372           break;
5373 #endif
5374         default:
5375           break;
5376       }
5377     }
5378     return size;
5379   }
5380 
popValueStackTo(uint32_t stackSize)5381   void popValueStackTo(uint32_t stackSize) {
5382     for (uint32_t i = stk_.length(); i > stackSize; i--) {
5383       Stk& v = stk_[i - 1];
5384       switch (v.kind()) {
5385         case Stk::RegisterI32:
5386           freeI32(v.i32reg());
5387           break;
5388         case Stk::RegisterI64:
5389           freeI64(v.i64reg());
5390           break;
5391         case Stk::RegisterF64:
5392           freeF64(v.f64reg());
5393           break;
5394         case Stk::RegisterF32:
5395           freeF32(v.f32reg());
5396           break;
5397 #ifdef ENABLE_WASM_SIMD
5398         case Stk::RegisterV128:
5399           freeV128(v.v128reg());
5400           break;
5401 #endif
5402         case Stk::RegisterRef:
5403           freeRef(v.refReg());
5404           break;
5405         case Stk::MemRef:
5406           stackMapGenerator_.memRefsOnStk--;
5407           break;
5408         default:
5409           break;
5410       }
5411     }
5412     stk_.shrinkTo(stackSize);
5413   }
5414 
popValueStackBy(uint32_t items)5415   void popValueStackBy(uint32_t items) {
5416     popValueStackTo(stk_.length() - items);
5417   }
5418 
dropValue()5419   void dropValue() {
5420     if (peek(0).isMem()) {
5421       fr.popBytes(stackConsumed(1));
5422     }
5423     popValueStackBy(1);
5424   }
5425 
5426   // Peek at the stack, for calls.
5427 
peek(uint32_t relativeDepth)5428   Stk& peek(uint32_t relativeDepth) {
5429     return stk_[stk_.length() - 1 - relativeDepth];
5430   }
5431 
5432 #ifdef DEBUG
5433   // Check that we're not leaking registers by comparing the
5434   // state of the stack + available registers with the set of
5435   // all available registers.
5436 
5437   // Call this between opcodes.
performRegisterLeakCheck()5438   void performRegisterLeakCheck() {
5439     BaseRegAlloc::LeakCheck check(ra);
5440     for (auto& item : stk_) {
5441       switch (item.kind_) {
5442         case Stk::RegisterI32:
5443           check.addKnownI32(item.i32reg());
5444           break;
5445         case Stk::RegisterI64:
5446           check.addKnownI64(item.i64reg());
5447           break;
5448         case Stk::RegisterF32:
5449           check.addKnownF32(item.f32reg());
5450           break;
5451         case Stk::RegisterF64:
5452           check.addKnownF64(item.f64reg());
5453           break;
5454 #  ifdef ENABLE_WASM_SIMD
5455         case Stk::RegisterV128:
5456           check.addKnownV128(item.v128reg());
5457           break;
5458 #  endif
5459         case Stk::RegisterRef:
5460           check.addKnownRef(item.refReg());
5461           break;
5462         default:
5463           break;
5464       }
5465     }
5466   }
5467 
assertStackInvariants() const5468   void assertStackInvariants() const {
5469     if (deadCode_) {
5470       // Nonlocal control flow can pass values in stack locations in a way that
5471       // isn't accounted for by the value stack.  In dead code, which occurs
5472       // after unconditional non-local control flow, there is no invariant to
5473       // assert.
5474       return;
5475     }
5476     size_t size = 0;
5477     for (const Stk& v : stk_) {
5478       switch (v.kind()) {
5479         case Stk::MemRef:
5480           size += BaseStackFrame::StackSizeOfPtr;
5481           break;
5482         case Stk::MemI32:
5483           size += BaseStackFrame::StackSizeOfPtr;
5484           break;
5485         case Stk::MemI64:
5486           size += BaseStackFrame::StackSizeOfInt64;
5487           break;
5488         case Stk::MemF64:
5489           size += BaseStackFrame::StackSizeOfDouble;
5490           break;
5491         case Stk::MemF32:
5492           size += BaseStackFrame::StackSizeOfFloat;
5493           break;
5494 #  ifdef ENABLE_WASM_SIMD
5495         case Stk::MemV128:
5496           size += BaseStackFrame::StackSizeOfV128;
5497           break;
5498 #  endif
5499         default:
5500           MOZ_ASSERT(!v.isMem());
5501           break;
5502       }
5503     }
5504     MOZ_ASSERT(size == fr.dynamicHeight());
5505   }
5506 
5507 #endif
5508 
5509   ////////////////////////////////////////////////////////////
5510   //
5511   // Control stack
5512 
initControl(Control & item,ResultType params)5513   void initControl(Control& item, ResultType params) {
5514     // Make sure the constructor was run properly
5515     MOZ_ASSERT(!item.stackHeight.isValid() && item.stackSize == UINT32_MAX);
5516 
5517     uint32_t paramCount = deadCode_ ? 0 : params.length();
5518     uint32_t stackParamSize = stackConsumed(paramCount);
5519     item.stackHeight = fr.stackResultsBase(stackParamSize);
5520     item.stackSize = stk_.length() - paramCount;
5521     item.deadOnArrival = deadCode_;
5522     item.bceSafeOnEntry = bceSafe_;
5523   }
5524 
controlItem()5525   Control& controlItem() { return iter_.controlItem(); }
5526 
controlItem(uint32_t relativeDepth)5527   Control& controlItem(uint32_t relativeDepth) {
5528     return iter_.controlItem(relativeDepth);
5529   }
5530 
controlOutermost()5531   Control& controlOutermost() { return iter_.controlOutermost(); }
5532 
5533   ////////////////////////////////////////////////////////////
5534   //
5535   // Labels
5536 
insertBreakablePoint(CallSiteDesc::Kind kind)5537   void insertBreakablePoint(CallSiteDesc::Kind kind) {
5538     fr.loadTlsPtr(WasmTlsReg);
5539     masm.nopPatchableToCall(CallSiteDesc(iter_.lastOpcodeOffset(), kind));
5540   }
5541 
5542   //////////////////////////////////////////////////////////////////////
5543   //
5544   // Function prologue and epilogue.
5545 
beginFunction()5546   [[nodiscard]] bool beginFunction() {
5547     JitSpew(JitSpew_Codegen, "# ========================================");
5548     JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
5549     JitSpew(JitSpew_Codegen,
5550             "# beginFunction: start of function prologue for index %d",
5551             (int)func_.index);
5552 
5553     // Make a start on the stackmap for this function.  Inspect the args so
5554     // as to determine which of them are both in-memory and pointer-typed, and
5555     // add entries to machineStackTracker as appropriate.
5556 
5557     ArgTypeVector args(funcType());
5558     size_t inboundStackArgBytes = StackArgAreaSizeUnaligned(args);
5559     MOZ_ASSERT(inboundStackArgBytes % sizeof(void*) == 0);
5560     stackMapGenerator_.numStackArgWords = inboundStackArgBytes / sizeof(void*);
5561 
5562     MOZ_ASSERT(stackMapGenerator_.machineStackTracker.length() == 0);
5563     if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers(
5564             stackMapGenerator_.numStackArgWords)) {
5565       return false;
5566     }
5567 
5568     // Identify GC-managed pointers passed on the stack.
5569     for (WasmABIArgIter i(args); !i.done(); i++) {
5570       ABIArg argLoc = *i;
5571       if (argLoc.kind() == ABIArg::Stack &&
5572           args[i.index()] == MIRType::RefOrNull) {
5573         uint32_t offset = argLoc.offsetFromArgBase();
5574         MOZ_ASSERT(offset < inboundStackArgBytes);
5575         MOZ_ASSERT(offset % sizeof(void*) == 0);
5576         stackMapGenerator_.machineStackTracker.setGCPointer(offset /
5577                                                             sizeof(void*));
5578       }
5579     }
5580 
5581     GenerateFunctionPrologue(masm, *moduleEnv_.funcs[func_.index].typeId,
5582                              compilerEnv_.mode() == CompileMode::Tier1
5583                                  ? Some(func_.index)
5584                                  : Nothing(),
5585                              &offsets_);
5586 
5587     // GenerateFunctionPrologue pushes exactly one wasm::Frame's worth of
5588     // stuff, and none of the values are GC pointers.  Hence:
5589     if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers(
5590             sizeof(Frame) / sizeof(void*))) {
5591       return false;
5592     }
5593 
5594     // Initialize DebugFrame fields before the stack overflow trap so that
5595     // we have the invariant that all observable Frames in a debugEnabled
5596     // Module have valid DebugFrames.
5597     if (compilerEnv_.debugEnabled()) {
5598 #ifdef JS_CODEGEN_ARM64
5599       static_assert(DebugFrame::offsetOfFrame() % WasmStackAlignment == 0,
5600                     "aligned");
5601 #endif
5602       masm.reserveStack(DebugFrame::offsetOfFrame());
5603       if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers(
5604               DebugFrame::offsetOfFrame() / sizeof(void*))) {
5605         return false;
5606       }
5607 
5608       masm.store32(
5609           Imm32(func_.index),
5610           Address(masm.getStackPointer(), DebugFrame::offsetOfFuncIndex()));
5611       masm.store32(Imm32(0), Address(masm.getStackPointer(),
5612                                      DebugFrame::offsetOfFlags()));
5613 
5614       // No need to initialize cachedReturnJSValue_ or any ref-typed spilled
5615       // register results, as they are traced if and only if a corresponding
5616       // flag (hasCachedReturnJSValue or hasSpilledRefRegisterResult) is set.
5617     }
5618 
5619     // Generate a stack-overflow check and its associated stackmap.
5620 
5621     fr.checkStack(ABINonArgReg0, BytecodeOffset(func_.lineOrBytecode));
5622 
5623     ExitStubMapVector extras;
5624     if (!stackMapGenerator_.generateStackmapEntriesForTrapExit(args, &extras)) {
5625       return false;
5626     }
5627     if (!createStackMap("stack check", extras, masm.currentOffset())) {
5628       return false;
5629     }
5630 
5631     size_t reservedBytes = fr.fixedAllocSize() - masm.framePushed();
5632     MOZ_ASSERT(0 == (reservedBytes % sizeof(void*)));
5633 
5634     masm.reserveStack(reservedBytes);
5635     fr.onFixedStackAllocated();
5636     if (!stackMapGenerator_.machineStackTracker.pushNonGCPointers(
5637             reservedBytes / sizeof(void*))) {
5638       return false;
5639     }
5640 
5641     // Locals are stack allocated.  Mark ref-typed ones in the stackmap
5642     // accordingly.
5643     for (const Local& l : localInfo_) {
5644       // Locals that are stack arguments were already added to the stackmap
5645       // before pushing the frame.
5646       if (l.type == MIRType::RefOrNull && !l.isStackArgument()) {
5647         uint32_t offs = fr.localOffsetFromSp(l);
5648         MOZ_ASSERT(0 == (offs % sizeof(void*)));
5649         stackMapGenerator_.machineStackTracker.setGCPointer(offs /
5650                                                             sizeof(void*));
5651       }
5652     }
5653 
5654     // Copy arguments from registers to stack.
5655     for (WasmABIArgIter i(args); !i.done(); i++) {
5656       if (args.isSyntheticStackResultPointerArg(i.index())) {
5657         // If there are stack results and the pointer to stack results
5658         // was passed in a register, store it to the stack.
5659         if (i->argInRegister()) {
5660           fr.storeIncomingStackResultAreaPtr(RegPtr(i->gpr()));
5661         }
5662         // If we're in a debug frame, copy the stack result pointer arg
5663         // to a well-known place.
5664         if (compilerEnv_.debugEnabled()) {
5665           Register target = ABINonArgReturnReg0;
5666           fr.loadIncomingStackResultAreaPtr(RegPtr(target));
5667           size_t debugFrameOffset =
5668               masm.framePushed() - DebugFrame::offsetOfFrame();
5669           size_t debugStackResultsPointerOffset =
5670               debugFrameOffset + DebugFrame::offsetOfStackResultsPointer();
5671           masm.storePtr(target, Address(masm.getStackPointer(),
5672                                         debugStackResultsPointerOffset));
5673         }
5674         continue;
5675       }
5676       if (!i->argInRegister()) {
5677         continue;
5678       }
5679       Local& l = localInfo_[args.naturalIndex(i.index())];
5680       switch (i.mirType()) {
5681         case MIRType::Int32:
5682           fr.storeLocalI32(RegI32(i->gpr()), l);
5683           break;
5684         case MIRType::Int64:
5685           fr.storeLocalI64(RegI64(i->gpr64()), l);
5686           break;
5687         case MIRType::RefOrNull: {
5688           DebugOnly<uint32_t> offs = fr.localOffsetFromSp(l);
5689           MOZ_ASSERT(0 == (offs % sizeof(void*)));
5690           fr.storeLocalRef(RegRef(i->gpr()), l);
5691           // We should have just visited this local in the preceding loop.
5692           MOZ_ASSERT(stackMapGenerator_.machineStackTracker.isGCPointer(
5693               offs / sizeof(void*)));
5694           break;
5695         }
5696         case MIRType::Double:
5697           fr.storeLocalF64(RegF64(i->fpu()), l);
5698           break;
5699         case MIRType::Float32:
5700           fr.storeLocalF32(RegF32(i->fpu()), l);
5701           break;
5702 #ifdef ENABLE_WASM_SIMD
5703         case MIRType::Simd128:
5704           fr.storeLocalV128(RegV128(i->fpu()), l);
5705           break;
5706 #endif
5707         default:
5708           MOZ_CRASH("Function argument type");
5709       }
5710     }
5711 
5712     fr.zeroLocals(&ra);
5713     fr.storeTlsPtr(WasmTlsReg);
5714 
5715     if (compilerEnv_.debugEnabled()) {
5716       insertBreakablePoint(CallSiteDesc::EnterFrame);
5717       if (!createStackMap("debug: enter-frame breakpoint")) {
5718         return false;
5719       }
5720     }
5721 
5722     JitSpew(JitSpew_Codegen,
5723             "# beginFunction: enter body with masm.framePushed = %u",
5724             masm.framePushed());
5725     MOZ_ASSERT(stackMapGenerator_.framePushedAtEntryToBody.isNothing());
5726     stackMapGenerator_.framePushedAtEntryToBody.emplace(masm.framePushed());
5727 
5728     return true;
5729   }
5730 
popStackReturnValues(const ResultType & resultType)5731   void popStackReturnValues(const ResultType& resultType) {
5732     uint32_t bytes = ABIResultIter::MeasureStackBytes(resultType);
5733     if (bytes == 0) {
5734       return;
5735     }
5736     Register target = ABINonArgReturnReg0;
5737     Register temp = ABINonArgReturnReg1;
5738     fr.loadIncomingStackResultAreaPtr(RegPtr(target));
5739     fr.popStackResultsToMemory(target, bytes, temp);
5740   }
5741 
saveRegisterReturnValues(const ResultType & resultType)5742   void saveRegisterReturnValues(const ResultType& resultType) {
5743     MOZ_ASSERT(compilerEnv_.debugEnabled());
5744     size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
5745     size_t registerResultIdx = 0;
5746     for (ABIResultIter i(resultType); !i.done(); i.next()) {
5747       const ABIResult result = i.cur();
5748       if (!result.inRegister()) {
5749 #ifdef DEBUG
5750         for (i.next(); !i.done(); i.next()) {
5751           MOZ_ASSERT(!i.cur().inRegister());
5752         }
5753 #endif
5754         break;
5755       }
5756 
5757       size_t resultOffset =
5758           DebugFrame::offsetOfRegisterResult(registerResultIdx);
5759       Address dest(masm.getStackPointer(), debugFrameOffset + resultOffset);
5760       switch (result.type().kind()) {
5761         case ValType::I32:
5762           masm.store32(RegI32(result.gpr()), dest);
5763           break;
5764         case ValType::I64:
5765           masm.store64(RegI64(result.gpr64()), dest);
5766           break;
5767         case ValType::F64:
5768           masm.storeDouble(RegF64(result.fpr()), dest);
5769           break;
5770         case ValType::F32:
5771           masm.storeFloat32(RegF32(result.fpr()), dest);
5772           break;
5773         case ValType::Rtt:
5774         case ValType::Ref: {
5775           uint32_t flag =
5776               DebugFrame::hasSpilledRegisterRefResultBitMask(registerResultIdx);
5777           // Tell Instance::traceFrame that we have a pointer to trace.
5778           masm.or32(Imm32(flag),
5779                     Address(masm.getStackPointer(),
5780                             debugFrameOffset + DebugFrame::offsetOfFlags()));
5781           masm.storePtr(RegRef(result.gpr()), dest);
5782           break;
5783         }
5784         case ValType::V128:
5785 #ifdef ENABLE_WASM_SIMD
5786           masm.storeUnalignedSimd128(RegV128(result.fpr()), dest);
5787           break;
5788 #else
5789           MOZ_CRASH("No SIMD support");
5790 #endif
5791       }
5792       registerResultIdx++;
5793     }
5794   }
5795 
restoreRegisterReturnValues(const ResultType & resultType)5796   void restoreRegisterReturnValues(const ResultType& resultType) {
5797     MOZ_ASSERT(compilerEnv_.debugEnabled());
5798     size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
5799     size_t registerResultIdx = 0;
5800     for (ABIResultIter i(resultType); !i.done(); i.next()) {
5801       const ABIResult result = i.cur();
5802       if (!result.inRegister()) {
5803 #ifdef DEBUG
5804         for (i.next(); !i.done(); i.next()) {
5805           MOZ_ASSERT(!i.cur().inRegister());
5806         }
5807 #endif
5808         break;
5809       }
5810       size_t resultOffset =
5811           DebugFrame::offsetOfRegisterResult(registerResultIdx++);
5812       Address src(masm.getStackPointer(), debugFrameOffset + resultOffset);
5813       switch (result.type().kind()) {
5814         case ValType::I32:
5815           masm.load32(src, RegI32(result.gpr()));
5816           break;
5817         case ValType::I64:
5818           masm.load64(src, RegI64(result.gpr64()));
5819           break;
5820         case ValType::F64:
5821           masm.loadDouble(src, RegF64(result.fpr()));
5822           break;
5823         case ValType::F32:
5824           masm.loadFloat32(src, RegF32(result.fpr()));
5825           break;
5826         case ValType::Rtt:
5827         case ValType::Ref:
5828           masm.loadPtr(src, RegRef(result.gpr()));
5829           break;
5830         case ValType::V128:
5831 #ifdef ENABLE_WASM_SIMD
5832           masm.loadUnalignedSimd128(src, RegV128(result.fpr()));
5833           break;
5834 #else
5835           MOZ_CRASH("No SIMD support");
5836 #endif
5837       }
5838     }
5839   }
5840 
endFunction()5841   [[nodiscard]] bool endFunction() {
5842     JitSpew(JitSpew_Codegen, "# endFunction: start of function epilogue");
5843 
5844     // Always branch to returnLabel_.
5845     masm.breakpoint();
5846 
5847     // Patch the add in the prologue so that it checks against the correct
5848     // frame size. Flush the constant pool in case it needs to be patched.
5849     masm.flush();
5850 
5851     // Precondition for patching.
5852     if (masm.oom()) {
5853       return false;
5854     }
5855 
5856     fr.patchCheckStack();
5857 
5858     masm.bind(&returnLabel_);
5859 
5860     ResultType resultType(ResultType::Vector(funcType().results()));
5861 
5862     popStackReturnValues(resultType);
5863 
5864     if (compilerEnv_.debugEnabled()) {
5865       // Store and reload the return value from DebugFrame::return so that
5866       // it can be clobbered, and/or modified by the debug trap.
5867       saveRegisterReturnValues(resultType);
5868       insertBreakablePoint(CallSiteDesc::Breakpoint);
5869       if (!createStackMap("debug: return-point breakpoint")) {
5870         return false;
5871       }
5872       insertBreakablePoint(CallSiteDesc::LeaveFrame);
5873       if (!createStackMap("debug: leave-frame breakpoint")) {
5874         return false;
5875       }
5876       restoreRegisterReturnValues(resultType);
5877     }
5878 
5879     // To satisy Tls extent invariant we need to reload WasmTlsReg because
5880     // baseline can clobber it.
5881     fr.loadTlsPtr(WasmTlsReg);
5882     GenerateFunctionEpilogue(masm, fr.fixedAllocSize(), &offsets_);
5883 
5884 #if defined(JS_ION_PERF)
5885     // FIXME - profiling code missing.  No bug for this.
5886 
5887     // Note the end of the inline code and start of the OOL code.
5888     // gen->perfSpewer().noteEndInlineCode(masm);
5889 #endif
5890 
5891     JitSpew(JitSpew_Codegen, "# endFunction: end of function epilogue");
5892     JitSpew(JitSpew_Codegen, "# endFunction: start of OOL code");
5893     if (!generateOutOfLineCode()) {
5894       return false;
5895     }
5896 
5897     offsets_.end = masm.currentOffset();
5898 
5899     if (!fr.checkStackHeight()) {
5900       return false;
5901     }
5902 
5903     JitSpew(JitSpew_Codegen, "# endFunction: end of OOL code for index %d",
5904             (int)func_.index);
5905     return !masm.oom();
5906   }
5907 
5908   //////////////////////////////////////////////////////////////////////
5909   //
5910   // Calls.
5911 
5912   struct FunctionCall {
FunctionCalljs::wasm::BaseCompiler::FunctionCall5913     explicit FunctionCall(uint32_t lineOrBytecode)
5914         : lineOrBytecode(lineOrBytecode),
5915           isInterModule(false),
5916           usesSystemAbi(false),
5917 #ifdef JS_CODEGEN_ARM
5918           hardFP(true),
5919 #endif
5920           frameAlignAdjustment(0),
5921           stackArgAreaSize(0) {
5922     }
5923 
5924     uint32_t lineOrBytecode;
5925     WasmABIArgGenerator abi;
5926     bool isInterModule;
5927     bool usesSystemAbi;
5928 #ifdef JS_CODEGEN_ARM
5929     bool hardFP;
5930 #endif
5931     size_t frameAlignAdjustment;
5932     size_t stackArgAreaSize;
5933   };
5934 
beginCall(FunctionCall & call,UseABI useABI,InterModule interModule)5935   void beginCall(FunctionCall& call, UseABI useABI, InterModule interModule) {
5936     MOZ_ASSERT_IF(useABI == UseABI::Builtin, interModule == InterModule::False);
5937 
5938     call.isInterModule = interModule == InterModule::True;
5939     call.usesSystemAbi = useABI == UseABI::System;
5940 
5941     if (call.usesSystemAbi) {
5942       // Call-outs need to use the appropriate system ABI.
5943 #if defined(JS_CODEGEN_ARM)
5944       call.hardFP = UseHardFpABI();
5945       call.abi.setUseHardFp(call.hardFP);
5946 #elif defined(JS_CODEGEN_MIPS32)
5947       call.abi.enforceO32ABI();
5948 #endif
5949     } else {
5950 #if defined(JS_CODEGEN_ARM)
5951       MOZ_ASSERT(call.hardFP,
5952                  "All private ABIs pass FP arguments in registers");
5953 #endif
5954     }
5955 
5956     // Use masm.framePushed() because the value we want here does not depend
5957     // on the height of the frame's stack area, but the actual size of the
5958     // allocated frame.
5959     call.frameAlignAdjustment = ComputeByteAlignment(
5960         masm.framePushed() + sizeof(Frame), JitStackAlignment);
5961   }
5962 
endCall(FunctionCall & call,size_t stackSpace)5963   void endCall(FunctionCall& call, size_t stackSpace) {
5964     size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
5965     fr.freeArgAreaAndPopBytes(adjustment, stackSpace);
5966 
5967     MOZ_ASSERT(
5968         stackMapGenerator_.framePushedExcludingOutboundCallArgs.isSome());
5969     stackMapGenerator_.framePushedExcludingOutboundCallArgs.reset();
5970 
5971     if (call.isInterModule) {
5972       fr.loadTlsPtr(WasmTlsReg);
5973       masm.loadWasmPinnedRegsFromTls();
5974       masm.switchToWasmTlsRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
5975     } else if (call.usesSystemAbi) {
5976       // On x86 there are no pinned registers, so don't waste time
5977       // reloading the Tls.
5978 #ifndef JS_CODEGEN_X86
5979       fr.loadTlsPtr(WasmTlsReg);
5980       masm.loadWasmPinnedRegsFromTls();
5981 #endif
5982     }
5983   }
5984 
startCallArgs(size_t stackArgAreaSizeUnaligned,FunctionCall * call)5985   void startCallArgs(size_t stackArgAreaSizeUnaligned, FunctionCall* call) {
5986     size_t stackArgAreaSizeAligned =
5987         AlignStackArgAreaSize(stackArgAreaSizeUnaligned);
5988     MOZ_ASSERT(stackArgAreaSizeUnaligned <= stackArgAreaSizeAligned);
5989 
5990     // Record the masm.framePushed() value at this point, before we push args
5991     // for the call, but including the alignment space placed above the args.
5992     // This defines the lower limit of the stackmap that will be created for
5993     // this call.
5994     MOZ_ASSERT(
5995         stackMapGenerator_.framePushedExcludingOutboundCallArgs.isNothing());
5996     stackMapGenerator_.framePushedExcludingOutboundCallArgs.emplace(
5997         // However much we've pushed so far
5998         masm.framePushed() +
5999         // Extra space we'll push to get the frame aligned
6000         call->frameAlignAdjustment +
6001         // Extra space we'll push to get the outbound arg area 16-aligned
6002         (stackArgAreaSizeAligned - stackArgAreaSizeUnaligned));
6003 
6004     call->stackArgAreaSize = stackArgAreaSizeAligned;
6005 
6006     size_t adjustment = call->stackArgAreaSize + call->frameAlignAdjustment;
6007     fr.allocArgArea(adjustment);
6008   }
6009 
reservePointerArgument(FunctionCall * call)6010   ABIArg reservePointerArgument(FunctionCall* call) {
6011     return call->abi.next(MIRType::Pointer);
6012   }
6013 
6014   // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place.
6015   // (Or it was, until Luke wandered through, but that can be fixed again.)
6016   // I'm not saying we should manually inline it, but we could hoist the
6017   // dispatch into the caller and have type-specific implementations of
6018   // passArg: passArgI32(), etc.  Then those might be inlined, at least in PGO
6019   // builds.
6020   //
6021   // The bulk of the work here (60%) is in the next() call, though.
6022   //
6023   // Notably, since next() is so expensive, StackArgAreaSizeUnaligned()
6024   // becomes expensive too.
6025   //
6026   // Somehow there could be a trick here where the sequence of argument types
6027   // (read from the input stream) leads to a cached entry for
6028   // StackArgAreaSizeUnaligned() and for how to pass arguments...
6029   //
6030   // But at least we could reduce the cost of StackArgAreaSizeUnaligned() by
6031   // first reading the argument types into a (reusable) vector, then we have
6032   // the outgoing size at low cost, and then we can pass args based on the
6033   // info we read.
6034 
passArg(ValType type,const Stk & arg,FunctionCall * call)6035   void passArg(ValType type, const Stk& arg, FunctionCall* call) {
6036     switch (type.kind()) {
6037       case ValType::I32: {
6038         ABIArg argLoc = call->abi.next(MIRType::Int32);
6039         if (argLoc.kind() == ABIArg::Stack) {
6040           ScratchI32 scratch(*this);
6041           loadI32(arg, scratch);
6042           masm.store32(scratch, Address(masm.getStackPointer(),
6043                                         argLoc.offsetFromArgBase()));
6044         } else {
6045           loadI32(arg, RegI32(argLoc.gpr()));
6046         }
6047         break;
6048       }
6049       case ValType::I64: {
6050         ABIArg argLoc = call->abi.next(MIRType::Int64);
6051         if (argLoc.kind() == ABIArg::Stack) {
6052           ScratchI32 scratch(*this);
6053 #ifdef JS_PUNBOX64
6054           loadI64(arg, fromI32(scratch));
6055           masm.storePtr(scratch, Address(masm.getStackPointer(),
6056                                          argLoc.offsetFromArgBase()));
6057 #else
6058           loadI64Low(arg, scratch);
6059           masm.store32(scratch, LowWord(Address(masm.getStackPointer(),
6060                                                 argLoc.offsetFromArgBase())));
6061           loadI64High(arg, scratch);
6062           masm.store32(scratch, HighWord(Address(masm.getStackPointer(),
6063                                                  argLoc.offsetFromArgBase())));
6064 #endif
6065         } else {
6066           loadI64(arg, RegI64(argLoc.gpr64()));
6067         }
6068         break;
6069       }
6070       case ValType::V128: {
6071 #ifdef ENABLE_WASM_SIMD
6072         ABIArg argLoc = call->abi.next(MIRType::Simd128);
6073         switch (argLoc.kind()) {
6074           case ABIArg::Stack: {
6075             ScratchV128 scratch(*this);
6076             loadV128(arg, scratch);
6077             masm.storeUnalignedSimd128(
6078                 (RegV128)scratch,
6079                 Address(masm.getStackPointer(), argLoc.offsetFromArgBase()));
6080             break;
6081           }
6082           case ABIArg::GPR: {
6083             MOZ_CRASH("Unexpected parameter passing discipline");
6084           }
6085           case ABIArg::FPU: {
6086             loadV128(arg, RegV128(argLoc.fpu()));
6087             break;
6088           }
6089 #  if defined(JS_CODEGEN_REGISTER_PAIR)
6090           case ABIArg::GPR_PAIR: {
6091             MOZ_CRASH("Unexpected parameter passing discipline");
6092           }
6093 #  endif
6094           case ABIArg::Uninitialized:
6095             MOZ_CRASH("Uninitialized ABIArg kind");
6096         }
6097         break;
6098 #else
6099         MOZ_CRASH("No SIMD support");
6100 #endif
6101       }
6102       case ValType::F64: {
6103         ABIArg argLoc = call->abi.next(MIRType::Double);
6104         switch (argLoc.kind()) {
6105           case ABIArg::Stack: {
6106             ScratchF64 scratch(*this);
6107             loadF64(arg, scratch);
6108             masm.storeDouble(scratch, Address(masm.getStackPointer(),
6109                                               argLoc.offsetFromArgBase()));
6110             break;
6111           }
6112 #if defined(JS_CODEGEN_REGISTER_PAIR)
6113           case ABIArg::GPR_PAIR: {
6114 #  if defined(JS_CODEGEN_ARM)
6115             ScratchF64 scratch(*this);
6116             loadF64(arg, scratch);
6117             masm.ma_vxfer(scratch, argLoc.evenGpr(), argLoc.oddGpr());
6118             break;
6119 #  elif defined(JS_CODEGEN_MIPS32)
6120             ScratchF64 scratch(*this);
6121             loadF64(arg, scratch);
6122             MOZ_ASSERT(MOZ_LITTLE_ENDIAN());
6123             masm.moveFromDoubleLo(scratch, argLoc.evenGpr());
6124             masm.moveFromDoubleHi(scratch, argLoc.oddGpr());
6125             break;
6126 #  else
6127             MOZ_CRASH("BaseCompiler platform hook: passArg F64 pair");
6128 #  endif
6129           }
6130 #endif
6131           case ABIArg::FPU: {
6132             loadF64(arg, RegF64(argLoc.fpu()));
6133             break;
6134           }
6135           case ABIArg::GPR: {
6136             MOZ_CRASH("Unexpected parameter passing discipline");
6137           }
6138           case ABIArg::Uninitialized:
6139             MOZ_CRASH("Uninitialized ABIArg kind");
6140         }
6141         break;
6142       }
6143       case ValType::F32: {
6144         ABIArg argLoc = call->abi.next(MIRType::Float32);
6145         switch (argLoc.kind()) {
6146           case ABIArg::Stack: {
6147             ScratchF32 scratch(*this);
6148             loadF32(arg, scratch);
6149             masm.storeFloat32(scratch, Address(masm.getStackPointer(),
6150                                                argLoc.offsetFromArgBase()));
6151             break;
6152           }
6153           case ABIArg::GPR: {
6154             ScratchF32 scratch(*this);
6155             loadF32(arg, scratch);
6156             masm.moveFloat32ToGPR(scratch, argLoc.gpr());
6157             break;
6158           }
6159           case ABIArg::FPU: {
6160             loadF32(arg, RegF32(argLoc.fpu()));
6161             break;
6162           }
6163 #if defined(JS_CODEGEN_REGISTER_PAIR)
6164           case ABIArg::GPR_PAIR: {
6165             MOZ_CRASH("Unexpected parameter passing discipline");
6166           }
6167 #endif
6168           case ABIArg::Uninitialized:
6169             MOZ_CRASH("Uninitialized ABIArg kind");
6170         }
6171         break;
6172       }
6173       case ValType::Rtt:
6174       case ValType::Ref: {
6175         ABIArg argLoc = call->abi.next(MIRType::RefOrNull);
6176         if (argLoc.kind() == ABIArg::Stack) {
6177           ScratchRef scratch(*this);
6178           loadRef(arg, scratch);
6179           masm.storePtr(scratch, Address(masm.getStackPointer(),
6180                                          argLoc.offsetFromArgBase()));
6181         } else {
6182           loadRef(arg, RegRef(argLoc.gpr()));
6183         }
6184         break;
6185       }
6186     }
6187   }
6188 
callDefinition(uint32_t funcIndex,const FunctionCall & call)6189   CodeOffset callDefinition(uint32_t funcIndex, const FunctionCall& call) {
6190     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Func);
6191     return masm.call(desc, funcIndex);
6192   }
6193 
callSymbolic(SymbolicAddress callee,const FunctionCall & call)6194   CodeOffset callSymbolic(SymbolicAddress callee, const FunctionCall& call) {
6195     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
6196     return masm.call(desc, callee);
6197   }
6198 
6199   // Precondition: sync()
6200 
callIndirect(uint32_t funcTypeIndex,uint32_t tableIndex,const Stk & indexVal,const FunctionCall & call)6201   CodeOffset callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
6202                           const Stk& indexVal, const FunctionCall& call) {
6203     const TypeIdDesc& funcTypeId = moduleEnv_.typeIds[funcTypeIndex];
6204     MOZ_ASSERT(funcTypeId.kind() != TypeIdDescKind::None);
6205 
6206     const TableDesc& table = moduleEnv_.tables[tableIndex];
6207 
6208     loadI32(indexVal, RegI32(WasmTableCallIndexReg));
6209 
6210     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
6211     CalleeDesc callee = CalleeDesc::wasmTable(table, funcTypeId);
6212     return masm.wasmCallIndirect(desc, callee, NeedsBoundsCheck(true));
6213   }
6214 
6215   // Precondition: sync()
6216 
callImport(unsigned globalDataOffset,const FunctionCall & call)6217   CodeOffset callImport(unsigned globalDataOffset, const FunctionCall& call) {
6218     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
6219     CalleeDesc callee = CalleeDesc::import(globalDataOffset);
6220     return masm.wasmCallImport(desc, callee);
6221   }
6222 
builtinCall(SymbolicAddress builtin,const FunctionCall & call)6223   CodeOffset builtinCall(SymbolicAddress builtin, const FunctionCall& call) {
6224     return callSymbolic(builtin, call);
6225   }
6226 
builtinInstanceMethodCall(const SymbolicAddressSignature & builtin,const ABIArg & instanceArg,const FunctionCall & call)6227   CodeOffset builtinInstanceMethodCall(const SymbolicAddressSignature& builtin,
6228                                        const ABIArg& instanceArg,
6229                                        const FunctionCall& call) {
6230     // Builtin method calls assume the TLS register has been set.
6231     fr.loadTlsPtr(WasmTlsReg);
6232 
6233     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
6234     return masm.wasmCallBuiltinInstanceMethod(
6235         desc, instanceArg, builtin.identity, builtin.failureMode);
6236   }
6237 
pushCallResults(const FunctionCall & call,ResultType type,const StackResultsLoc & loc)6238   [[nodiscard]] bool pushCallResults(const FunctionCall& call, ResultType type,
6239                                      const StackResultsLoc& loc) {
6240 #if defined(JS_CODEGEN_ARM)
6241     // pushResults currently bypasses special case code in captureReturnedFxx()
6242     // that converts GPR results to FPR results for systemABI+softFP.  If we
6243     // ever start using that combination for calls we need more code.  This
6244     // assert is stronger than we need - we only care about results in return
6245     // registers - but that's OK.
6246     MOZ_ASSERT(!call.usesSystemAbi || call.hardFP);
6247 #endif
6248     return pushResults(type, fr.stackResultsBase(loc.bytes()));
6249   }
6250 
6251   //////////////////////////////////////////////////////////////////////
6252   //
6253   // Sundry low-level code generators.
6254 
6255   // The compiler depends on moveImm32() clearing the high bits of a 64-bit
6256   // register on 64-bit systems except MIPS64 where high bits are sign extended
6257   // from lower bits, see doc block "64-bit GPRs carrying 32-bit values" in
6258   // MacroAssembler.h.
6259 
moveImm32(int32_t v,RegI32 dest)6260   void moveImm32(int32_t v, RegI32 dest) { masm.move32(Imm32(v), dest); }
6261 
moveImm64(int64_t v,RegI64 dest)6262   void moveImm64(int64_t v, RegI64 dest) { masm.move64(Imm64(v), dest); }
6263 
moveImmRef(intptr_t v,RegRef dest)6264   void moveImmRef(intptr_t v, RegRef dest) { masm.movePtr(ImmWord(v), dest); }
6265 
addInterruptCheck()6266   [[nodiscard]] bool addInterruptCheck() {
6267     ScratchI32 tmp(*this);
6268     fr.loadTlsPtr(tmp);
6269     masm.wasmInterruptCheck(tmp, bytecodeOffset());
6270     return createStackMap("addInterruptCheck");
6271   }
6272 
jumpTable(const LabelVector & labels,Label * theTable)6273   void jumpTable(const LabelVector& labels, Label* theTable) {
6274     // Flush constant pools to ensure that the table is never interrupted by
6275     // constant pool entries.
6276     masm.flush();
6277 
6278 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
6279     // Prevent nop sequences to appear in the jump table.
6280     AutoForbidNops afn(&masm);
6281 #endif
6282     masm.bind(theTable);
6283 
6284     for (const auto& label : labels) {
6285       CodeLabel cl;
6286       masm.writeCodePointer(&cl);
6287       cl.target()->bind(label.offset());
6288       masm.addCodeLabel(cl);
6289     }
6290   }
6291 
tableSwitch(Label * theTable,RegI32 switchValue,Label * dispatchCode)6292   void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode) {
6293     masm.bind(dispatchCode);
6294 
6295 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
6296     ScratchI32 scratch(*this);
6297     CodeLabel tableCl;
6298 
6299     masm.mov(&tableCl, scratch);
6300 
6301     tableCl.target()->bind(theTable->offset());
6302     masm.addCodeLabel(tableCl);
6303 
6304     masm.jmp(Operand(scratch, switchValue, ScalePointer));
6305 #elif defined(JS_CODEGEN_ARM)
6306     // Flush constant pools: offset must reflect the distance from the MOV
6307     // to the start of the table; as the address of the MOV is given by the
6308     // label, nothing must come between the bind() and the ma_mov().
6309     AutoForbidPoolsAndNops afp(&masm,
6310                                /* number of instructions in scope = */ 5);
6311 
6312     ScratchI32 scratch(*this);
6313 
6314     // Compute the offset from the ma_mov instruction to the jump table.
6315     Label here;
6316     masm.bind(&here);
6317     uint32_t offset = here.offset() - theTable->offset();
6318 
6319     // Read PC+8
6320     masm.ma_mov(pc, scratch);
6321 
6322     // ARM scratch register is required by ma_sub.
6323     ScratchRegisterScope arm_scratch(*this);
6324 
6325     // Compute the absolute table base pointer into `scratch`, offset by 8
6326     // to account for the fact that ma_mov read PC+8.
6327     masm.ma_sub(Imm32(offset + 8), scratch, arm_scratch);
6328 
6329     // Jump indirect via table element.
6330     masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc,
6331                 Offset, Assembler::Always);
6332 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
6333     ScratchI32 scratch(*this);
6334     CodeLabel tableCl;
6335 
6336     masm.ma_li(scratch, &tableCl);
6337 
6338     tableCl.target()->bind(theTable->offset());
6339     masm.addCodeLabel(tableCl);
6340 
6341     masm.branchToComputedAddress(BaseIndex(scratch, switchValue, ScalePointer));
6342 #elif defined(JS_CODEGEN_ARM64)
6343     AutoForbidPoolsAndNops afp(&masm,
6344                                /* number of instructions in scope = */ 4);
6345 
6346     ScratchI32 scratch(*this);
6347 
6348     ARMRegister s(scratch, 64);
6349     ARMRegister v(switchValue, 64);
6350     masm.Adr(s, theTable);
6351     masm.Add(s, s, Operand(v, vixl::LSL, 3));
6352     masm.Ldr(s, MemOperand(s, 0));
6353     masm.Br(s);
6354 #else
6355     MOZ_CRASH("BaseCompiler platform hook: tableSwitch");
6356 #endif
6357   }
6358 
captureReturnedI32()6359   RegI32 captureReturnedI32() {
6360     RegI32 r = RegI32(ReturnReg);
6361     MOZ_ASSERT(isAvailableI32(r));
6362     needI32(r);
6363 #if defined(JS_CODEGEN_X64)
6364     if (JitOptions.spectreIndexMasking) {
6365       masm.movl(r, r);
6366     }
6367 #endif
6368     return r;
6369   }
6370 
captureReturnedI64()6371   RegI64 captureReturnedI64() {
6372     RegI64 r = RegI64(ReturnReg64);
6373     MOZ_ASSERT(isAvailableI64(r));
6374     needI64(r);
6375     return r;
6376   }
6377 
captureReturnedF32(const FunctionCall & call)6378   RegF32 captureReturnedF32(const FunctionCall& call) {
6379     RegF32 r = RegF32(ReturnFloat32Reg);
6380     MOZ_ASSERT(isAvailableF32(r));
6381     needF32(r);
6382 #if defined(JS_CODEGEN_ARM)
6383     if (call.usesSystemAbi && !call.hardFP) {
6384       masm.ma_vxfer(ReturnReg, r);
6385     }
6386 #endif
6387     return r;
6388   }
6389 
captureReturnedF64(const FunctionCall & call)6390   RegF64 captureReturnedF64(const FunctionCall& call) {
6391     RegF64 r = RegF64(ReturnDoubleReg);
6392     MOZ_ASSERT(isAvailableF64(r));
6393     needF64(r);
6394 #if defined(JS_CODEGEN_ARM)
6395     if (call.usesSystemAbi && !call.hardFP) {
6396       masm.ma_vxfer(ReturnReg64.low, ReturnReg64.high, r);
6397     }
6398 #endif
6399     return r;
6400   }
6401 
6402 #ifdef ENABLE_WASM_SIMD
captureReturnedV128(const FunctionCall & call)6403   RegV128 captureReturnedV128(const FunctionCall& call) {
6404     RegV128 r = RegV128(ReturnSimd128Reg);
6405     MOZ_ASSERT(isAvailableV128(r));
6406     needV128(r);
6407     return r;
6408   }
6409 #endif
6410 
captureReturnedRef()6411   RegRef captureReturnedRef() {
6412     RegRef r = RegRef(ReturnReg);
6413     MOZ_ASSERT(isAvailableRef(r));
6414     needRef(r);
6415     return r;
6416   }
6417 
checkDivideByZeroI32(RegI32 rhs)6418   void checkDivideByZeroI32(RegI32 rhs) {
6419     Label nonZero;
6420     masm.branchTest32(Assembler::NonZero, rhs, rhs, &nonZero);
6421     trap(Trap::IntegerDivideByZero);
6422     masm.bind(&nonZero);
6423   }
6424 
checkDivideByZeroI64(RegI64 r)6425   void checkDivideByZeroI64(RegI64 r) {
6426     Label nonZero;
6427     ScratchI32 scratch(*this);
6428     masm.branchTest64(Assembler::NonZero, r, r, scratch, &nonZero);
6429     trap(Trap::IntegerDivideByZero);
6430     masm.bind(&nonZero);
6431   }
6432 
checkDivideSignedOverflowI32(RegI32 rhs,RegI32 srcDest,Label * done,bool zeroOnOverflow)6433   void checkDivideSignedOverflowI32(RegI32 rhs, RegI32 srcDest, Label* done,
6434                                     bool zeroOnOverflow) {
6435     Label notMin;
6436     masm.branch32(Assembler::NotEqual, srcDest, Imm32(INT32_MIN), &notMin);
6437     if (zeroOnOverflow) {
6438       masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notMin);
6439       moveImm32(0, srcDest);
6440       masm.jump(done);
6441     } else {
6442       masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notMin);
6443       trap(Trap::IntegerOverflow);
6444     }
6445     masm.bind(&notMin);
6446   }
6447 
checkDivideSignedOverflowI64(RegI64 rhs,RegI64 srcDest,Label * done,bool zeroOnOverflow)6448   void checkDivideSignedOverflowI64(RegI64 rhs, RegI64 srcDest, Label* done,
6449                                     bool zeroOnOverflow) {
6450     Label notmin;
6451     masm.branch64(Assembler::NotEqual, srcDest, Imm64(INT64_MIN), &notmin);
6452     masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), &notmin);
6453     if (zeroOnOverflow) {
6454       masm.xor64(srcDest, srcDest);
6455       masm.jump(done);
6456     } else {
6457       trap(Trap::IntegerOverflow);
6458     }
6459     masm.bind(&notmin);
6460   }
6461 
6462 #ifndef RABALDR_INT_DIV_I64_CALLOUT
quotientI64(RegI64 rhs,RegI64 srcDest,RegI64 reserved,IsUnsigned isUnsigned,bool isConst,int64_t c)6463   void quotientI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved,
6464                    IsUnsigned isUnsigned, bool isConst, int64_t c) {
6465     Label done;
6466 
6467     if (!isConst || c == 0) {
6468       checkDivideByZeroI64(rhs);
6469     }
6470 
6471     if (!isUnsigned && (!isConst || c == -1)) {
6472       checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
6473     }
6474 
6475 #  if defined(JS_CODEGEN_X64)
6476     // The caller must set up the following situation.
6477     MOZ_ASSERT(srcDest.reg == rax);
6478     MOZ_ASSERT(reserved == specific_.rdx);
6479     if (isUnsigned) {
6480       masm.xorq(rdx, rdx);
6481       masm.udivq(rhs.reg);
6482     } else {
6483       masm.cqo();
6484       masm.idivq(rhs.reg);
6485     }
6486 #  elif defined(JS_CODEGEN_MIPS64)
6487     if (isUnsigned) {
6488       masm.as_ddivu(srcDest.reg, rhs.reg);
6489     } else {
6490       masm.as_ddiv(srcDest.reg, rhs.reg);
6491     }
6492     masm.as_mflo(srcDest.reg);
6493 #  elif defined(JS_CODEGEN_ARM64)
6494     ARMRegister sd(srcDest.reg, 64);
6495     ARMRegister r(rhs.reg, 64);
6496     if (isUnsigned) {
6497       masm.Udiv(sd, sd, r);
6498     } else {
6499       masm.Sdiv(sd, sd, r);
6500     }
6501 #  else
6502     MOZ_CRASH("BaseCompiler platform hook: quotientI64");
6503 #  endif
6504     masm.bind(&done);
6505   }
6506 
remainderI64(RegI64 rhs,RegI64 srcDest,RegI64 reserved,IsUnsigned isUnsigned,bool isConst,int64_t c)6507   void remainderI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved,
6508                     IsUnsigned isUnsigned, bool isConst, int64_t c) {
6509     Label done;
6510 
6511     if (!isConst || c == 0) {
6512       checkDivideByZeroI64(rhs);
6513     }
6514 
6515     if (!isUnsigned && (!isConst || c == -1)) {
6516       checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
6517     }
6518 
6519 #  if defined(JS_CODEGEN_X64)
6520     // The caller must set up the following situation.
6521     MOZ_ASSERT(srcDest.reg == rax);
6522     MOZ_ASSERT(reserved == specific_.rdx);
6523 
6524     if (isUnsigned) {
6525       masm.xorq(rdx, rdx);
6526       masm.udivq(rhs.reg);
6527     } else {
6528       masm.cqo();
6529       masm.idivq(rhs.reg);
6530     }
6531     masm.movq(rdx, rax);
6532 #  elif defined(JS_CODEGEN_MIPS64)
6533     if (isUnsigned) {
6534       masm.as_ddivu(srcDest.reg, rhs.reg);
6535     } else {
6536       masm.as_ddiv(srcDest.reg, rhs.reg);
6537     }
6538     masm.as_mfhi(srcDest.reg);
6539 #  elif defined(JS_CODEGEN_ARM64)
6540     MOZ_ASSERT(reserved.isInvalid());
6541     ARMRegister sd(srcDest.reg, 64);
6542     ARMRegister r(rhs.reg, 64);
6543     ScratchI32 temp(*this);
6544     ARMRegister t(temp, 64);
6545     if (isUnsigned) {
6546       masm.Udiv(t, sd, r);
6547     } else {
6548       masm.Sdiv(t, sd, r);
6549     }
6550     masm.Mul(t, t, r);
6551     masm.Sub(sd, sd, t);
6552 #  else
6553     MOZ_CRASH("BaseCompiler platform hook: remainderI64");
6554 #  endif
6555     masm.bind(&done);
6556   }
6557 #endif  // RABALDR_INT_DIV_I64_CALLOUT
6558 
needRotate64Temp()6559   RegI32 needRotate64Temp() {
6560 #if defined(JS_CODEGEN_X86)
6561     return needI32();
6562 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) ||    \
6563     defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
6564     defined(JS_CODEGEN_MIPS64)
6565     return RegI32::Invalid();
6566 #else
6567     MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp");
6568 #endif
6569   }
6570 
6571   class OutOfLineTruncateCheckF32OrF64ToI32 : public OutOfLineCode {
6572     AnyReg src;
6573     RegI32 dest;
6574     TruncFlags flags;
6575     BytecodeOffset off;
6576 
6577    public:
OutOfLineTruncateCheckF32OrF64ToI32(AnyReg src,RegI32 dest,TruncFlags flags,BytecodeOffset off)6578     OutOfLineTruncateCheckF32OrF64ToI32(AnyReg src, RegI32 dest,
6579                                         TruncFlags flags, BytecodeOffset off)
6580         : src(src), dest(dest), flags(flags), off(off) {}
6581 
generate(MacroAssembler * masm)6582     virtual void generate(MacroAssembler* masm) override {
6583       if (src.tag == AnyReg::F32) {
6584         masm->oolWasmTruncateCheckF32ToI32(src.f32(), dest, flags, off,
6585                                            rejoin());
6586       } else if (src.tag == AnyReg::F64) {
6587         masm->oolWasmTruncateCheckF64ToI32(src.f64(), dest, flags, off,
6588                                            rejoin());
6589       } else {
6590         MOZ_CRASH("unexpected type");
6591       }
6592     }
6593   };
6594 
truncateF32ToI32(RegF32 src,RegI32 dest,TruncFlags flags)6595   [[nodiscard]] bool truncateF32ToI32(RegF32 src, RegI32 dest,
6596                                       TruncFlags flags) {
6597     BytecodeOffset off = bytecodeOffset();
6598     OutOfLineCode* ool =
6599         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32(
6600             AnyReg(src), dest, flags, off));
6601     if (!ool) {
6602       return false;
6603     }
6604     bool isSaturating = flags & TRUNC_SATURATING;
6605     if (flags & TRUNC_UNSIGNED) {
6606       masm.wasmTruncateFloat32ToUInt32(src, dest, isSaturating, ool->entry());
6607     } else {
6608       masm.wasmTruncateFloat32ToInt32(src, dest, isSaturating, ool->entry());
6609     }
6610     masm.bind(ool->rejoin());
6611     return true;
6612   }
6613 
truncateF64ToI32(RegF64 src,RegI32 dest,TruncFlags flags)6614   [[nodiscard]] bool truncateF64ToI32(RegF64 src, RegI32 dest,
6615                                       TruncFlags flags) {
6616     BytecodeOffset off = bytecodeOffset();
6617     OutOfLineCode* ool =
6618         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32(
6619             AnyReg(src), dest, flags, off));
6620     if (!ool) {
6621       return false;
6622     }
6623     bool isSaturating = flags & TRUNC_SATURATING;
6624     if (flags & TRUNC_UNSIGNED) {
6625       masm.wasmTruncateDoubleToUInt32(src, dest, isSaturating, ool->entry());
6626     } else {
6627       masm.wasmTruncateDoubleToInt32(src, dest, isSaturating, ool->entry());
6628     }
6629     masm.bind(ool->rejoin());
6630     return true;
6631   }
6632 
6633   class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode {
6634     AnyReg src;
6635     RegI64 dest;
6636     TruncFlags flags;
6637     BytecodeOffset off;
6638 
6639    public:
OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src,RegI64 dest,TruncFlags flags,BytecodeOffset off)6640     OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src, RegI64 dest,
6641                                         TruncFlags flags, BytecodeOffset off)
6642         : src(src), dest(dest), flags(flags), off(off) {}
6643 
generate(MacroAssembler * masm)6644     virtual void generate(MacroAssembler* masm) override {
6645       if (src.tag == AnyReg::F32) {
6646         masm->oolWasmTruncateCheckF32ToI64(src.f32(), dest, flags, off,
6647                                            rejoin());
6648       } else if (src.tag == AnyReg::F64) {
6649         masm->oolWasmTruncateCheckF64ToI64(src.f64(), dest, flags, off,
6650                                            rejoin());
6651       } else {
6652         MOZ_CRASH("unexpected type");
6653       }
6654     }
6655   };
6656 
6657 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT
needTempForFloatingToI64(TruncFlags flags)6658   [[nodiscard]] RegF64 needTempForFloatingToI64(TruncFlags flags) {
6659 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
6660     if (flags & TRUNC_UNSIGNED) {
6661       return needF64();
6662     }
6663 #  endif
6664     return RegF64::Invalid();
6665   }
6666 
truncateF32ToI64(RegF32 src,RegI64 dest,TruncFlags flags,RegF64 temp)6667   [[nodiscard]] bool truncateF32ToI64(RegF32 src, RegI64 dest, TruncFlags flags,
6668                                       RegF64 temp) {
6669     OutOfLineCode* ool =
6670         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
6671             AnyReg(src), dest, flags, bytecodeOffset()));
6672     if (!ool) {
6673       return false;
6674     }
6675     bool isSaturating = flags & TRUNC_SATURATING;
6676     if (flags & TRUNC_UNSIGNED) {
6677       masm.wasmTruncateFloat32ToUInt64(src, dest, isSaturating, ool->entry(),
6678                                        ool->rejoin(), temp);
6679     } else {
6680       masm.wasmTruncateFloat32ToInt64(src, dest, isSaturating, ool->entry(),
6681                                       ool->rejoin(), temp);
6682     }
6683     return true;
6684   }
6685 
truncateF64ToI64(RegF64 src,RegI64 dest,TruncFlags flags,RegF64 temp)6686   [[nodiscard]] bool truncateF64ToI64(RegF64 src, RegI64 dest, TruncFlags flags,
6687                                       RegF64 temp) {
6688     OutOfLineCode* ool =
6689         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
6690             AnyReg(src), dest, flags, bytecodeOffset()));
6691     if (!ool) {
6692       return false;
6693     }
6694     bool isSaturating = flags & TRUNC_SATURATING;
6695     if (flags & TRUNC_UNSIGNED) {
6696       masm.wasmTruncateDoubleToUInt64(src, dest, isSaturating, ool->entry(),
6697                                       ool->rejoin(), temp);
6698     } else {
6699       masm.wasmTruncateDoubleToInt64(src, dest, isSaturating, ool->entry(),
6700                                      ool->rejoin(), temp);
6701     }
6702     return true;
6703   }
6704 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
6705 
6706 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
needConvertI64ToFloatTemp(ValType to,bool isUnsigned)6707   RegI32 needConvertI64ToFloatTemp(ValType to, bool isUnsigned) {
6708     bool needs = false;
6709     if (to == ValType::F64) {
6710       needs = isUnsigned && masm.convertUInt64ToDoubleNeedsTemp();
6711     } else {
6712 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
6713       needs = true;
6714 #  endif
6715     }
6716     return needs ? needI32() : RegI32::Invalid();
6717   }
6718 
convertI64ToF32(RegI64 src,bool isUnsigned,RegF32 dest,RegI32 temp)6719   void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp) {
6720     if (isUnsigned) {
6721       masm.convertUInt64ToFloat32(src, dest, temp);
6722     } else {
6723       masm.convertInt64ToFloat32(src, dest);
6724     }
6725   }
6726 
convertI64ToF64(RegI64 src,bool isUnsigned,RegF64 dest,RegI32 temp)6727   void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp) {
6728     if (isUnsigned) {
6729       masm.convertUInt64ToDouble(src, dest, temp);
6730     } else {
6731       masm.convertInt64ToDouble(src, dest);
6732     }
6733   }
6734 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
6735 
cmp64Set(Assembler::Condition cond,RegI64 lhs,RegI64 rhs,RegI32 dest)6736   void cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs,
6737                 RegI32 dest) {
6738 #if defined(JS_PUNBOX64)
6739     masm.cmpPtrSet(cond, lhs.reg, rhs.reg, dest);
6740 #elif defined(JS_CODEGEN_MIPS32)
6741     masm.cmp64Set(cond, lhs, rhs, dest);
6742 #else
6743     // TODO / OPTIMIZE (Bug 1316822): This is pretty branchy, we should be
6744     // able to do better.
6745     Label done, condTrue;
6746     masm.branch64(cond, lhs, rhs, &condTrue);
6747     moveImm32(0, dest);
6748     masm.jump(&done);
6749     masm.bind(&condTrue);
6750     moveImm32(1, dest);
6751     masm.bind(&done);
6752 #endif
6753   }
6754 
supportsRoundInstruction(RoundingMode mode)6755   [[nodiscard]] bool supportsRoundInstruction(RoundingMode mode) {
6756     return Assembler::HasRoundInstruction(mode);
6757   }
6758 
roundF32(RoundingMode roundingMode,RegF32 f0)6759   void roundF32(RoundingMode roundingMode, RegF32 f0) {
6760     masm.nearbyIntFloat32(roundingMode, f0, f0);
6761   }
6762 
roundF64(RoundingMode roundingMode,RegF64 f0)6763   void roundF64(RoundingMode roundingMode, RegF64 f0) {
6764     masm.nearbyIntDouble(roundingMode, f0, f0);
6765   }
6766 
6767   //////////////////////////////////////////////////////////////////////
6768   //
6769   // Global variable access.
6770 
addressOfGlobalVar(const GlobalDesc & global,RegI32 tmp)6771   Address addressOfGlobalVar(const GlobalDesc& global, RegI32 tmp) {
6772     uint32_t globalToTlsOffset =
6773         offsetof(TlsData, globalArea) + global.offset();
6774     fr.loadTlsPtr(tmp);
6775     if (global.isIndirect()) {
6776       masm.loadPtr(Address(tmp, globalToTlsOffset), tmp);
6777       return Address(tmp, 0);
6778     }
6779     return Address(tmp, globalToTlsOffset);
6780   }
6781 
6782   //////////////////////////////////////////////////////////////////////
6783   //
6784   // Heap access.
6785 
bceCheckLocal(MemoryAccessDesc * access,AccessCheck * check,uint32_t local)6786   void bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check,
6787                      uint32_t local) {
6788     if (local >= sizeof(BCESet) * 8) {
6789       return;
6790     }
6791 
6792     uint32_t offsetGuardLimit =
6793         GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled());
6794 
6795     if ((bceSafe_ & (BCESet(1) << local)) &&
6796         access->offset() < offsetGuardLimit) {
6797       check->omitBoundsCheck = true;
6798     }
6799 
6800     // The local becomes safe even if the offset is beyond the guard limit.
6801     bceSafe_ |= (BCESet(1) << local);
6802   }
6803 
bceLocalIsUpdated(uint32_t local)6804   void bceLocalIsUpdated(uint32_t local) {
6805     if (local >= sizeof(BCESet) * 8) {
6806       return;
6807     }
6808 
6809     bceSafe_ &= ~(BCESet(1) << local);
6810   }
6811 
prepareMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)6812   void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check,
6813                            RegI32 tls, RegI32 ptr) {
6814     uint32_t offsetGuardLimit =
6815         GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled());
6816 
6817     // Fold offset if necessary for further computations.
6818     if (access->offset() >= offsetGuardLimit ||
6819         (access->isAtomic() && !check->omitAlignmentCheck &&
6820          !check->onlyPointerAlignment)) {
6821       Label ok;
6822       masm.branchAdd32(Assembler::CarryClear, Imm32(access->offset()), ptr,
6823                        &ok);
6824       masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset());
6825       masm.bind(&ok);
6826       access->clearOffset();
6827       check->onlyPointerAlignment = true;
6828     }
6829 
6830     // Alignment check if required.
6831 
6832     if (access->isAtomic() && !check->omitAlignmentCheck) {
6833       MOZ_ASSERT(check->onlyPointerAlignment);
6834       // We only care about the low pointer bits here.
6835       Label ok;
6836       masm.branchTest32(Assembler::Zero, ptr, Imm32(access->byteSize() - 1),
6837                         &ok);
6838       masm.wasmTrap(Trap::UnalignedAccess, bytecodeOffset());
6839       masm.bind(&ok);
6840     }
6841 
6842     // Ensure no tls if we don't need it.
6843 
6844     if (moduleEnv_.hugeMemoryEnabled()) {
6845       // We have HeapReg and no bounds checking and need load neither
6846       // memoryBase nor boundsCheckLimit from tls.
6847       MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
6848     }
6849 #ifdef JS_CODEGEN_ARM
6850     // We have HeapReg on ARM and don't need to load the memoryBase from tls.
6851     MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
6852 #endif
6853 
6854     // Bounds check if required.
6855 
6856     if (!moduleEnv_.hugeMemoryEnabled() && !check->omitBoundsCheck) {
6857       Label ok;
6858 #ifdef JS_64BIT
6859       // If the bounds check uses the full 64 bits of the bounds check limit,
6860       // then the index must be zero-extended to 64 bits before checking and
6861       // wrapped back to 32-bits after Spectre masking.  (And it's important
6862       // that the value we end up with has flowed through the Spectre mask.)
6863       //
6864       // If the memory's max size is known to be smaller than 64K pages exactly,
6865       // we can use a 32-bit check and avoid extension and wrapping.
6866       if (!moduleEnv_.memory->boundsCheckLimitIs32Bits() &&
6867           ArrayBufferObject::maxBufferByteLength() >= 0x100000000) {
6868         // Note, ptr and ptr64 are the same register.
6869         RegI64 ptr64 = fromI32(ptr);
6870 
6871         // In principle there may be non-zero bits in the upper bits of the
6872         // register; clear them.
6873 #  if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
6874         // The canonical value is zero-extended (see comment block "64-bit GPRs
6875         // carrying 32-bit values" in MacroAssembler.h); we already have that.
6876         masm.assertCanonicalInt32(ptr);
6877 #  else
6878         MOZ_CRASH("Platform code needed here");
6879 #  endif
6880 
6881         // Any Spectre mitigation will appear to update the ptr64 register.
6882         masm.wasmBoundsCheck64(
6883             Assembler::Below, ptr64,
6884             Address(tls, offsetof(TlsData, boundsCheckLimit)), &ok);
6885 
6886         // Restore the value to the canonical form for a 32-bit value in a
6887         // 64-bit register and/or the appropriate form for further use in the
6888         // indexing instruction.
6889 #  if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
6890         // The canonical value is zero-extended; we already have that.
6891 #  else
6892         MOZ_CRASH("Platform code needed here");
6893 #  endif
6894       } else {
6895         masm.wasmBoundsCheck32(
6896             Assembler::Below, ptr,
6897             Address(tls, offsetof(TlsData, boundsCheckLimit)), &ok);
6898       }
6899 #else
6900       masm.wasmBoundsCheck32(Assembler::Below, ptr,
6901                              Address(tls, offsetof(TlsData, boundsCheckLimit)),
6902                              &ok);
6903 #endif
6904       masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset());
6905       masm.bind(&ok);
6906     }
6907   }
6908 
6909 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) ||      \
6910     defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
6911     defined(JS_CODEGEN_MIPS64)
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)6912   BaseIndex prepareAtomicMemoryAccess(MemoryAccessDesc* access,
6913                                       AccessCheck* check, RegI32 tls,
6914                                       RegI32 ptr) {
6915     MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid());
6916     prepareMemoryAccess(access, check, tls, ptr);
6917     return BaseIndex(HeapReg, ptr, TimesOne, access->offset());
6918   }
6919 #elif defined(JS_CODEGEN_X86)
6920   // Some consumers depend on the address not retaining tls, as tls may be the
6921   // scratch register.
6922 
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)6923   Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
6924                                     AccessCheck* check, RegI32 tls,
6925                                     RegI32 ptr) {
6926     MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid());
6927     prepareMemoryAccess(access, check, tls, ptr);
6928     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
6929     return Address(ptr, access->offset());
6930   }
6931 #else
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)6932   Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
6933                                     AccessCheck* check, RegI32 tls,
6934                                     RegI32 ptr) {
6935     MOZ_CRASH("BaseCompiler platform hook: prepareAtomicMemoryAccess");
6936   }
6937 #endif
6938 
computeEffectiveAddress(MemoryAccessDesc * access)6939   void computeEffectiveAddress(MemoryAccessDesc* access) {
6940     if (access->offset()) {
6941       Label ok;
6942       RegI32 ptr = popI32();
6943       masm.branchAdd32(Assembler::CarryClear, Imm32(access->offset()), ptr,
6944                        &ok);
6945       masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset());
6946       masm.bind(&ok);
6947       access->clearOffset();
6948       pushI32(ptr);
6949     }
6950   }
6951 
needLoadTemps(const MemoryAccessDesc & access,RegI32 * temp1,RegI32 * temp2,RegI32 * temp3)6952   void needLoadTemps(const MemoryAccessDesc& access, RegI32* temp1,
6953                      RegI32* temp2, RegI32* temp3) {
6954 #if defined(JS_CODEGEN_ARM)
6955     if (IsUnaligned(access)) {
6956       switch (access.type()) {
6957         case Scalar::Float64:
6958           *temp3 = needI32();
6959           [[fallthrough]];
6960         case Scalar::Float32:
6961           *temp2 = needI32();
6962           [[fallthrough]];
6963         default:
6964           *temp1 = needI32();
6965           break;
6966       }
6967     }
6968 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
6969     *temp1 = needI32();
6970 #endif
6971   }
6972 
needTlsForAccess(const AccessCheck & check)6973   [[nodiscard]] bool needTlsForAccess(const AccessCheck& check) {
6974 #if defined(JS_CODEGEN_X86)
6975     // x86 requires Tls for memory base
6976     return true;
6977 #else
6978     return !moduleEnv_.hugeMemoryEnabled() && !check.omitBoundsCheck;
6979 #endif
6980   }
6981 
6982   // ptr and dest may be the same iff dest is I32.
6983   // This may destroy ptr even if ptr and dest are not the same.
load(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr,AnyReg dest,RegI32 temp1,RegI32 temp2,RegI32 temp3)6984   [[nodiscard]] bool load(MemoryAccessDesc* access, AccessCheck* check,
6985                           RegI32 tls, RegI32 ptr, AnyReg dest, RegI32 temp1,
6986                           RegI32 temp2, RegI32 temp3) {
6987     prepareMemoryAccess(access, check, tls, ptr);
6988 
6989 #if defined(JS_CODEGEN_X64)
6990     Operand srcAddr(HeapReg, ptr, TimesOne, access->offset());
6991 
6992     if (dest.tag == AnyReg::I64) {
6993       masm.wasmLoadI64(*access, srcAddr, dest.i64());
6994     } else {
6995       masm.wasmLoad(*access, srcAddr, dest.any());
6996     }
6997 #elif defined(JS_CODEGEN_X86)
6998     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
6999     Operand srcAddr(ptr, access->offset());
7000 
7001     if (dest.tag == AnyReg::I64) {
7002       MOZ_ASSERT(dest.i64() == specific_.abiReturnRegI64);
7003       masm.wasmLoadI64(*access, srcAddr, dest.i64());
7004     } else {
7005       // For 8 bit loads, this will generate movsbl or movzbl, so
7006       // there's no constraint on what the output register may be.
7007       masm.wasmLoad(*access, srcAddr, dest.any());
7008     }
7009 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7010     if (IsUnaligned(*access)) {
7011       switch (dest.tag) {
7012         case AnyReg::I64:
7013           masm.wasmUnalignedLoadI64(*access, HeapReg, ptr, ptr, dest.i64(),
7014                                     temp1);
7015           break;
7016         case AnyReg::F32:
7017           masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f32(),
7018                                    temp1, temp2, RegI32::Invalid());
7019           break;
7020         case AnyReg::F64:
7021           masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f64(),
7022                                    temp1, temp2, temp3);
7023           break;
7024         case AnyReg::I32:
7025           masm.wasmUnalignedLoad(*access, HeapReg, ptr, ptr, dest.i32(), temp1);
7026           break;
7027         default:
7028           MOZ_CRASH("Unexpected type");
7029       }
7030     } else {
7031       if (dest.tag == AnyReg::I64) {
7032         masm.wasmLoadI64(*access, HeapReg, ptr, ptr, dest.i64());
7033       } else {
7034         masm.wasmLoad(*access, HeapReg, ptr, ptr, dest.any());
7035       }
7036     }
7037 #elif defined(JS_CODEGEN_ARM)
7038     if (dest.tag == AnyReg::I64) {
7039       masm.wasmLoadI64(*access, HeapReg, ptr, ptr, dest.i64());
7040     } else {
7041       masm.wasmLoad(*access, HeapReg, ptr, ptr, dest.any());
7042     }
7043 #elif defined(JS_CODEGEN_ARM64)
7044     if (dest.tag == AnyReg::I64) {
7045       masm.wasmLoadI64(*access, HeapReg, ptr, dest.i64());
7046     } else {
7047       masm.wasmLoad(*access, HeapReg, ptr, dest.any());
7048     }
7049 #else
7050     MOZ_CRASH("BaseCompiler platform hook: load");
7051 #endif
7052 
7053     return true;
7054   }
7055 
needStoreTemp(const MemoryAccessDesc & access,ValType srcType)7056   RegI32 needStoreTemp(const MemoryAccessDesc& access, ValType srcType) {
7057 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7058     return needI32();
7059 #endif
7060     return RegI32::Invalid();
7061   }
7062 
7063   // ptr and src must not be the same register.
7064   // This may destroy ptr and src.
store(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr,AnyReg src,RegI32 temp)7065   [[nodiscard]] bool store(MemoryAccessDesc* access, AccessCheck* check,
7066                            RegI32 tls, RegI32 ptr, AnyReg src, RegI32 temp) {
7067     prepareMemoryAccess(access, check, tls, ptr);
7068 
7069     // Emit the store
7070 #if defined(JS_CODEGEN_X64)
7071     MOZ_ASSERT(temp.isInvalid());
7072     Operand dstAddr(HeapReg, ptr, TimesOne, access->offset());
7073 
7074     masm.wasmStore(*access, src.any(), dstAddr);
7075 #elif defined(JS_CODEGEN_X86)
7076     MOZ_ASSERT(temp.isInvalid());
7077     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
7078     Operand dstAddr(ptr, access->offset());
7079 
7080     if (access->type() == Scalar::Int64) {
7081       masm.wasmStoreI64(*access, src.i64(), dstAddr);
7082     } else {
7083       AnyRegister value;
7084       ScratchI8 scratch(*this);
7085       if (src.tag == AnyReg::I64) {
7086         if (access->byteSize() == 1 && !ra.isSingleByteI32(src.i64().low)) {
7087           masm.mov(src.i64().low, scratch);
7088           value = AnyRegister(scratch);
7089         } else {
7090           value = AnyRegister(src.i64().low);
7091         }
7092       } else if (access->byteSize() == 1 && !ra.isSingleByteI32(src.i32())) {
7093         masm.mov(src.i32(), scratch);
7094         value = AnyRegister(scratch);
7095       } else {
7096         value = src.any();
7097       }
7098 
7099       masm.wasmStore(*access, value, dstAddr);
7100     }
7101 #elif defined(JS_CODEGEN_ARM)
7102     MOZ_ASSERT(temp.isInvalid());
7103     if (access->type() == Scalar::Int64) {
7104       masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr);
7105     } else if (src.tag == AnyReg::I64) {
7106       masm.wasmStore(*access, AnyRegister(src.i64().low), HeapReg, ptr, ptr);
7107     } else {
7108       masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr);
7109     }
7110 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7111     if (IsUnaligned(*access)) {
7112       switch (src.tag) {
7113         case AnyReg::I64:
7114           masm.wasmUnalignedStoreI64(*access, src.i64(), HeapReg, ptr, ptr,
7115                                      temp);
7116           break;
7117         case AnyReg::F32:
7118           masm.wasmUnalignedStoreFP(*access, src.f32(), HeapReg, ptr, ptr,
7119                                     temp);
7120           break;
7121         case AnyReg::F64:
7122           masm.wasmUnalignedStoreFP(*access, src.f64(), HeapReg, ptr, ptr,
7123                                     temp);
7124           break;
7125         case AnyReg::I32:
7126           masm.wasmUnalignedStore(*access, src.i32(), HeapReg, ptr, ptr, temp);
7127           break;
7128         default:
7129           MOZ_CRASH("Unexpected type");
7130       }
7131     } else {
7132       if (src.tag == AnyReg::I64) {
7133         masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr);
7134       } else {
7135         masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr);
7136       }
7137     }
7138 #elif defined(JS_CODEGEN_ARM64)
7139     MOZ_ASSERT(temp.isInvalid());
7140     if (access->type() == Scalar::Int64) {
7141       masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr);
7142     } else {
7143       masm.wasmStore(*access, src.any(), HeapReg, ptr);
7144     }
7145 #else
7146     MOZ_CRASH("BaseCompiler platform hook: store");
7147 #endif
7148 
7149     return true;
7150   }
7151 
7152   template <size_t Count>
7153   struct Atomic32Temps : mozilla::Array<RegI32, Count> {
7154     // Allocate all temp registers if 'allocate' is not specified.
allocatejs::wasm::BaseCompiler::Atomic32Temps7155     void allocate(BaseCompiler* bc, size_t allocate = Count) {
7156       static_assert(Count != 0);
7157       for (size_t i = 0; i < allocate; ++i) {
7158         this->operator[](i) = bc->needI32();
7159       }
7160     }
maybeFreejs::wasm::BaseCompiler::Atomic32Temps7161     void maybeFree(BaseCompiler* bc) {
7162       for (size_t i = 0; i < Count; ++i) {
7163         bc->maybeFree(this->operator[](i));
7164       }
7165     }
7166   };
7167 
7168 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7169   using AtomicRMW32Temps = Atomic32Temps<3>;
7170 #else
7171   using AtomicRMW32Temps = Atomic32Temps<1>;
7172 #endif
7173 
7174   template <typename T>
atomicRMW32(const MemoryAccessDesc & access,T srcAddr,AtomicOp op,RegI32 rv,RegI32 rd,const AtomicRMW32Temps & temps)7175   void atomicRMW32(const MemoryAccessDesc& access, T srcAddr, AtomicOp op,
7176                    RegI32 rv, RegI32 rd, const AtomicRMW32Temps& temps) {
7177     switch (access.type()) {
7178       case Scalar::Uint8:
7179 #ifdef JS_CODEGEN_X86
7180       {
7181         RegI32 temp = temps[0];
7182         // The temp, if used, must be a byte register.
7183         MOZ_ASSERT(temp.isInvalid());
7184         ScratchI8 scratch(*this);
7185         if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) {
7186           temp = scratch;
7187         }
7188         masm.wasmAtomicFetchOp(access, op, rv, srcAddr, temp, rd);
7189         break;
7190       }
7191 #endif
7192       case Scalar::Uint16:
7193       case Scalar::Int32:
7194       case Scalar::Uint32:
7195 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7196         masm.wasmAtomicFetchOp(access, op, rv, srcAddr, temps[0], temps[1],
7197                                temps[2], rd);
7198 #else
7199         masm.wasmAtomicFetchOp(access, op, rv, srcAddr, temps[0], rd);
7200 #endif
7201         break;
7202       default: {
7203         MOZ_CRASH("Bad type for atomic operation");
7204       }
7205     }
7206   }
7207 
7208   // On x86, V is Address.  On other platforms, it is Register64.
7209   // T is BaseIndex or Address.
7210   template <typename T, typename V>
atomicRMW64(const MemoryAccessDesc & access,const T & srcAddr,AtomicOp op,V value,Register64 temp,Register64 rd)7211   void atomicRMW64(const MemoryAccessDesc& access, const T& srcAddr,
7212                    AtomicOp op, V value, Register64 temp, Register64 rd) {
7213     masm.wasmAtomicFetchOp64(access, op, value, srcAddr, temp, rd);
7214   }
7215 
7216 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7217   using AtomicCmpXchg32Temps = Atomic32Temps<3>;
7218 #else
7219   using AtomicCmpXchg32Temps = Atomic32Temps<0>;
7220 #endif
7221 
7222   template <typename T>
atomicCmpXchg32(const MemoryAccessDesc & access,T srcAddr,RegI32 rexpect,RegI32 rnew,RegI32 rd,const AtomicCmpXchg32Temps & temps)7223   void atomicCmpXchg32(const MemoryAccessDesc& access, T srcAddr,
7224                        RegI32 rexpect, RegI32 rnew, RegI32 rd,
7225                        const AtomicCmpXchg32Temps& temps) {
7226     switch (access.type()) {
7227       case Scalar::Uint8:
7228 #if defined(JS_CODEGEN_X86)
7229       {
7230         ScratchI8 scratch(*this);
7231         MOZ_ASSERT(rd == specific_.eax);
7232         if (!ra.isSingleByteI32(rnew)) {
7233           // The replacement value must have a byte persona.
7234           masm.movl(rnew, scratch);
7235           rnew = scratch;
7236         }
7237         masm.wasmCompareExchange(access, srcAddr, rexpect, rnew, rd);
7238         break;
7239       }
7240 #endif
7241       case Scalar::Uint16:
7242       case Scalar::Int32:
7243       case Scalar::Uint32:
7244 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7245         masm.wasmCompareExchange(access, srcAddr, rexpect, rnew, temps[0],
7246                                  temps[1], temps[2], rd);
7247 #else
7248         masm.wasmCompareExchange(access, srcAddr, rexpect, rnew, rd);
7249 #endif
7250         break;
7251       default:
7252         MOZ_CRASH("Bad type for atomic operation");
7253     }
7254   }
7255 
7256 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7257   using AtomicXchg32Temps = Atomic32Temps<3>;
7258 #else
7259   using AtomicXchg32Temps = Atomic32Temps<0>;
7260 #endif
7261 
7262   template <typename T>
atomicXchg32(const MemoryAccessDesc & access,T srcAddr,RegI32 rv,RegI32 rd,const AtomicXchg32Temps & temps)7263   void atomicXchg32(const MemoryAccessDesc& access, T srcAddr, RegI32 rv,
7264                     RegI32 rd, const AtomicXchg32Temps& temps) {
7265     switch (access.type()) {
7266       case Scalar::Uint8:
7267 #if defined(JS_CODEGEN_X86)
7268       {
7269         if (!ra.isSingleByteI32(rd)) {
7270           ScratchI8 scratch(*this);
7271           // The output register must have a byte persona.
7272           masm.wasmAtomicExchange(access, srcAddr, rv, scratch);
7273           masm.movl(scratch, rd);
7274         } else {
7275           masm.wasmAtomicExchange(access, srcAddr, rv, rd);
7276         }
7277         break;
7278       }
7279 #endif
7280       case Scalar::Uint16:
7281       case Scalar::Int32:
7282       case Scalar::Uint32:
7283 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7284         masm.wasmAtomicExchange(access, srcAddr, rv, temps[0], temps[1],
7285                                 temps[2], rd);
7286 #else
7287         masm.wasmAtomicExchange(access, srcAddr, rv, rd);
7288 #endif
7289         break;
7290       default:
7291         MOZ_CRASH("Bad type for atomic operation");
7292     }
7293   }
7294 
7295   ////////////////////////////////////////////////////////////
7296   //
7297   // Generally speaking, ABOVE this point there should be no
7298   // value stack manipulation (calls to popI32 etc).
7299   //
7300   ////////////////////////////////////////////////////////////
7301 
7302   ////////////////////////////////////////////////////////////
7303   //
7304   // Platform-specific popping and register targeting.
7305   //
7306   // These fall into two groups, popping methods for simple needs, and RAII
7307   // wrappers for more complex behavior.
7308 
7309   // The simple popping methods pop values into targeted registers; the caller
7310   // can free registers using standard functions.  These are always called
7311   // popXForY where X says something about types and Y something about the
7312   // operation being targeted.
7313 
pop2xI32ForMulDivI32(RegI32 * r0,RegI32 * r1,RegI32 * reserved)7314   void pop2xI32ForMulDivI32(RegI32* r0, RegI32* r1, RegI32* reserved) {
7315 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
7316     // r0 must be eax, and edx will be clobbered.
7317     need2xI32(specific_.eax, specific_.edx);
7318     *r1 = popI32();
7319     *r0 = popI32ToSpecific(specific_.eax);
7320     *reserved = specific_.edx;
7321 #else
7322     pop2xI32(r0, r1);
7323 #endif
7324   }
7325 
pop2xI64ForMulI64(RegI64 * r0,RegI64 * r1,RegI32 * temp,RegI64 * reserved)7326   void pop2xI64ForMulI64(RegI64* r0, RegI64* r1, RegI32* temp,
7327                          RegI64* reserved) {
7328 #if defined(JS_CODEGEN_X64)
7329     // r0 must be rax, and rdx will be clobbered.
7330     need2xI64(specific_.rax, specific_.rdx);
7331     *r1 = popI64();
7332     *r0 = popI64ToSpecific(specific_.rax);
7333     *reserved = specific_.rdx;
7334 #elif defined(JS_CODEGEN_X86)
7335     // As for x64, though edx is part of r0.
7336     need2xI32(specific_.eax, specific_.edx);
7337     *r1 = popI64();
7338     *r0 = popI64ToSpecific(specific_.edx_eax);
7339     *temp = needI32();
7340 #elif defined(JS_CODEGEN_MIPS64)
7341     pop2xI64(r0, r1);
7342 #elif defined(JS_CODEGEN_MIPS32)
7343     pop2xI64(r0, r1);
7344     *temp = needI32();
7345 #elif defined(JS_CODEGEN_ARM)
7346     pop2xI64(r0, r1);
7347     *temp = needI32();
7348 #elif defined(JS_CODEGEN_ARM64)
7349     pop2xI64(r0, r1);
7350 #else
7351     MOZ_CRASH("BaseCompiler porting interface: pop2xI64ForMulI64");
7352 #endif
7353   }
7354 
pop2xI64ForDivI64(RegI64 * r0,RegI64 * r1,RegI64 * reserved)7355   void pop2xI64ForDivI64(RegI64* r0, RegI64* r1, RegI64* reserved) {
7356 #if defined(JS_CODEGEN_X64)
7357     // r0 must be rax, and rdx will be clobbered.
7358     need2xI64(specific_.rax, specific_.rdx);
7359     *r1 = popI64();
7360     *r0 = popI64ToSpecific(specific_.rax);
7361     *reserved = specific_.rdx;
7362 #else
7363     pop2xI64(r0, r1);
7364 #endif
7365   }
7366 
popI32RhsForShift()7367   RegI32 popI32RhsForShift() {
7368 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
7369     // r1 must be ecx for a variable shift, unless BMI2 is available.
7370     if (!Assembler::HasBMI2()) {
7371       return popI32(specific_.ecx);
7372     }
7373 #endif
7374     RegI32 r = popI32();
7375 #if defined(JS_CODEGEN_ARM)
7376     masm.and32(Imm32(31), r);
7377 #endif
7378     return r;
7379   }
7380 
popI32RhsForShiftI64()7381   RegI32 popI32RhsForShiftI64() {
7382 #if defined(JS_CODEGEN_X86)
7383     // A limitation in the x86 masm requires ecx here
7384     return popI32(specific_.ecx);
7385 #elif defined(JS_CODEGEN_X64)
7386     if (!Assembler::HasBMI2()) {
7387       return popI32(specific_.ecx);
7388     }
7389     return popI32();
7390 #else
7391     return popI32();
7392 #endif
7393   }
7394 
popI64RhsForShift()7395   RegI64 popI64RhsForShift() {
7396 #if defined(JS_CODEGEN_X86)
7397     // r1 must be ecx for a variable shift.
7398     needI32(specific_.ecx);
7399     return popI64ToSpecific(widenI32(specific_.ecx));
7400 #else
7401 #  if defined(JS_CODEGEN_X64)
7402     // r1 must be rcx for a variable shift, unless BMI2 is available.
7403     if (!Assembler::HasBMI2()) {
7404       needI64(specific_.rcx);
7405       return popI64ToSpecific(specific_.rcx);
7406     }
7407 #  endif
7408     // No masking is necessary on 64-bit platforms, and on arm32 the masm
7409     // implementation masks.
7410     return popI64();
7411 #endif
7412   }
7413 
popI32RhsForRotate()7414   RegI32 popI32RhsForRotate() {
7415 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
7416     // r1 must be ecx for a variable rotate.
7417     return popI32(specific_.ecx);
7418 #else
7419     return popI32();
7420 #endif
7421   }
7422 
popI64RhsForRotate()7423   RegI64 popI64RhsForRotate() {
7424 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
7425     // r1 must be ecx for a variable rotate.
7426     needI32(specific_.ecx);
7427     return popI64ToSpecific(widenI32(specific_.ecx));
7428 #else
7429     return popI64();
7430 #endif
7431   }
7432 
popI32ForSignExtendI64(RegI64 * r0)7433   void popI32ForSignExtendI64(RegI64* r0) {
7434 #if defined(JS_CODEGEN_X86)
7435     // r0 must be edx:eax for cdq
7436     need2xI32(specific_.edx, specific_.eax);
7437     *r0 = specific_.edx_eax;
7438     popI32ToSpecific(specific_.eax);
7439 #else
7440     *r0 = widenI32(popI32());
7441 #endif
7442   }
7443 
popI64ForSignExtendI64(RegI64 * r0)7444   void popI64ForSignExtendI64(RegI64* r0) {
7445 #if defined(JS_CODEGEN_X86)
7446     // r0 must be edx:eax for cdq
7447     need2xI32(specific_.edx, specific_.eax);
7448     // Low on top, high underneath
7449     *r0 = popI64ToSpecific(specific_.edx_eax);
7450 #else
7451     *r0 = popI64();
7452 #endif
7453   }
7454 
7455   // The RAII wrappers are used because we sometimes have to free partial
7456   // registers, as when part of a register is the scratch register that has
7457   // been temporarily used, or not free a register at all, as when the
7458   // register is the same as the destination register (but only on some
7459   // platforms, not on all).  These are called PopX{32,64}Regs where X is the
7460   // operation being targeted.
7461 
7462   // Utility struct that holds the BaseCompiler and the destination, and frees
7463   // the destination if it has not been extracted.
7464 
7465   template <typename T>
7466   class PopBase {
7467     T rd_;
7468 
maybeFree(RegI32 r)7469     void maybeFree(RegI32 r) { bc->maybeFree(r); }
maybeFree(RegI64 r)7470     void maybeFree(RegI64 r) { bc->maybeFree(r); }
7471 
7472    protected:
7473     BaseCompiler* const bc;
7474 
setRd(T r)7475     void setRd(T r) {
7476       MOZ_ASSERT(rd_.isInvalid());
7477       rd_ = r;
7478     }
getRd() const7479     T getRd() const {
7480       MOZ_ASSERT(rd_.isValid());
7481       return rd_;
7482     }
7483 
7484    public:
PopBase(BaseCompiler * bc)7485     explicit PopBase(BaseCompiler* bc) : bc(bc) {}
~PopBase()7486     ~PopBase() { maybeFree(rd_); }
7487 
7488     // Take and clear the Rd - use this when pushing Rd.
takeRd()7489     T takeRd() {
7490       MOZ_ASSERT(rd_.isValid());
7491       T r = rd_;
7492       rd_ = T::Invalid();
7493       return r;
7494     }
7495   };
7496 
7497   friend class PopAtomicCmpXchg32Regs;
7498   class PopAtomicCmpXchg32Regs : public PopBase<RegI32> {
7499     using Base = PopBase<RegI32>;
7500     RegI32 rexpect, rnew;
7501     AtomicCmpXchg32Temps temps;
7502 
7503    public:
7504 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicCmpXchg32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType)7505     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
7506                                     Scalar::Type viewType)
7507         : Base(bc) {
7508       // For cmpxchg, the expected value and the result are both in eax.
7509       bc->needI32(bc->specific_.eax);
7510       if (type == ValType::I64) {
7511         rnew = bc->popI64ToI32();
7512         rexpect = bc->popI64ToSpecificI32(bc->specific_.eax);
7513       } else {
7514         rnew = bc->popI32();
7515         rexpect = bc->popI32ToSpecific(bc->specific_.eax);
7516       }
7517       setRd(rexpect);
7518     }
~PopAtomicCmpXchg32Regs()7519     ~PopAtomicCmpXchg32Regs() { bc->freeI32(rnew); }
7520 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
7521     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
7522                                     Scalar::Type viewType)
7523         : Base(bc) {
7524       if (type == ValType::I64) {
7525         rnew = bc->popI64ToI32();
7526         rexpect = bc->popI64ToI32();
7527       } else {
7528         rnew = bc->popI32();
7529         rexpect = bc->popI32();
7530       }
7531       setRd(bc->needI32());
7532     }
7533     ~PopAtomicCmpXchg32Regs() {
7534       bc->freeI32(rnew);
7535       bc->freeI32(rexpect);
7536     }
7537 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7538     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
7539                                     Scalar::Type viewType)
7540         : Base(bc) {
7541       if (type == ValType::I64) {
7542         rnew = bc->popI64ToI32();
7543         rexpect = bc->popI64ToI32();
7544       } else {
7545         rnew = bc->popI32();
7546         rexpect = bc->popI32();
7547       }
7548       if (Scalar::byteSize(viewType) < 4) {
7549         temps.allocate(bc);
7550       }
7551       setRd(bc->needI32());
7552     }
7553     ~PopAtomicCmpXchg32Regs() {
7554       bc->freeI32(rnew);
7555       bc->freeI32(rexpect);
7556       temps.maybeFree(bc);
7557     }
7558 #else
7559     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
7560                                     Scalar::Type viewType)
7561         : Base(bc) {
7562       MOZ_CRASH("BaseCompiler porting interface: PopAtomicCmpXchg32Regs");
7563     }
7564 #endif
7565 
7566     template <typename T>
atomicCmpXchg32(const MemoryAccessDesc & access,T srcAddr)7567     void atomicCmpXchg32(const MemoryAccessDesc& access, T srcAddr) {
7568       bc->atomicCmpXchg32(access, srcAddr, rexpect, rnew, getRd(), temps);
7569     }
7570   };
7571 
7572   friend class PopAtomicCmpXchg64Regs;
7573   class PopAtomicCmpXchg64Regs : public PopBase<RegI64> {
7574     using Base = PopBase<RegI64>;
7575     RegI64 rexpect, rnew;
7576 
7577    public:
7578 #ifdef JS_CODEGEN_X64
PopAtomicCmpXchg64Regs(BaseCompiler * bc)7579     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
7580       // For cmpxchg, the expected value and the result are both in rax.
7581       bc->needI64(bc->specific_.rax);
7582       rnew = bc->popI64();
7583       rexpect = bc->popI64ToSpecific(bc->specific_.rax);
7584       setRd(rexpect);
7585     }
~PopAtomicCmpXchg64Regs()7586     ~PopAtomicCmpXchg64Regs() { bc->freeI64(rnew); }
7587 #elif defined(JS_CODEGEN_X86)
7588     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
7589       // For cmpxchg8b, the expected value and the result are both in
7590       // edx:eax, and the replacement value is in ecx:ebx.  But we can't
7591       // allocate ebx here, so instead we allocate a temp to hold the low
7592       // word of 'new'.
7593       bc->needI64(bc->specific_.edx_eax);
7594       bc->needI32(bc->specific_.ecx);
7595 
7596       rnew = bc->popI64ToSpecific(
7597           RegI64(Register64(bc->specific_.ecx, bc->needI32())));
7598       rexpect = bc->popI64ToSpecific(bc->specific_.edx_eax);
7599       setRd(rexpect);
7600     }
7601     ~PopAtomicCmpXchg64Regs() { bc->freeI64(rnew); }
7602 #elif defined(JS_CODEGEN_ARM)
7603     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
7604       // The replacement value and the result must both be odd/even pairs.
7605       rnew = bc->popI64Pair();
7606       rexpect = bc->popI64();
7607       setRd(bc->needI64Pair());
7608     }
7609     ~PopAtomicCmpXchg64Regs() {
7610       bc->freeI64(rexpect);
7611       bc->freeI64(rnew);
7612     }
7613 #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
7614     defined(JS_CODEGEN_MIPS64)
7615     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
7616       rnew = bc->popI64();
7617       rexpect = bc->popI64();
7618       setRd(bc->needI64());
7619     }
7620     ~PopAtomicCmpXchg64Regs() {
7621       bc->freeI64(rexpect);
7622       bc->freeI64(rnew);
7623     }
7624 #else
7625     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
7626       MOZ_CRASH("BaseCompiler porting interface: PopAtomicCmpXchg64Regs");
7627     }
7628 #endif
7629 
7630 #ifdef JS_CODEGEN_X86
7631     template <typename T>
atomicCmpXchg64(const MemoryAccessDesc & access,T srcAddr,RegI32 ebx)7632     void atomicCmpXchg64(const MemoryAccessDesc& access, T srcAddr,
7633                          RegI32 ebx) {
7634       MOZ_ASSERT(ebx == js::jit::ebx);
7635       bc->masm.move32(rnew.low, ebx);
7636       bc->masm.wasmCompareExchange64(access, srcAddr, rexpect,
7637                                      bc->specific_.ecx_ebx, getRd());
7638     }
7639 #else
7640     template <typename T>
atomicCmpXchg64(const MemoryAccessDesc & access,T srcAddr)7641     void atomicCmpXchg64(const MemoryAccessDesc& access, T srcAddr) {
7642       bc->masm.wasmCompareExchange64(access, srcAddr, rexpect, rnew, getRd());
7643     }
7644 #endif
7645   };
7646 
7647 #ifndef JS_64BIT
7648   class PopAtomicLoad64Regs : public PopBase<RegI64> {
7649     using Base = PopBase<RegI64>;
7650 
7651    public:
7652 #  if defined(JS_CODEGEN_X86)
PopAtomicLoad64Regs(BaseCompiler * bc)7653     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
7654       // The result is in edx:eax, and we need ecx:ebx as a temp.  But we
7655       // can't reserve ebx yet, so we'll accept it as an argument to the
7656       // operation (below).
7657       bc->needI32(bc->specific_.ecx);
7658       bc->needI64(bc->specific_.edx_eax);
7659       setRd(bc->specific_.edx_eax);
7660     }
~PopAtomicLoad64Regs()7661     ~PopAtomicLoad64Regs() { bc->freeI32(bc->specific_.ecx); }
7662 #  elif defined(JS_CODEGEN_ARM)
7663     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
7664       setRd(bc->needI64Pair());
7665     }
7666 #  elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7667     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
7668       setRd(bc->needI64());
7669     }
7670 #  else
7671     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
7672       MOZ_CRASH("BaseCompiler porting interface: PopAtomicLoad64Regs");
7673     }
7674 #  endif
7675 
7676 #  ifdef JS_CODEGEN_X86
7677     template <typename T>
atomicLoad64(const MemoryAccessDesc & access,T srcAddr,RegI32 ebx)7678     void atomicLoad64(const MemoryAccessDesc& access, T srcAddr, RegI32 ebx) {
7679       MOZ_ASSERT(ebx == js::jit::ebx);
7680       bc->masm.wasmAtomicLoad64(access, srcAddr, bc->specific_.ecx_ebx,
7681                                 getRd());
7682     }
7683 #  else  // ARM, MIPS32
7684     template <typename T>
atomicLoad64(const MemoryAccessDesc & access,T srcAddr)7685     void atomicLoad64(const MemoryAccessDesc& access, T srcAddr) {
7686       bc->masm.wasmAtomicLoad64(access, srcAddr, RegI64::Invalid(), getRd());
7687     }
7688 #  endif
7689   };
7690 #endif  // JS_64BIT
7691 
7692   friend class PopAtomicRMW32Regs;
7693   class PopAtomicRMW32Regs : public PopBase<RegI32> {
7694     using Base = PopBase<RegI32>;
7695     RegI32 rv;
7696     AtomicRMW32Temps temps;
7697 
7698    public:
7699 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicRMW32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType,AtomicOp op)7700     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
7701                                 Scalar::Type viewType, AtomicOp op)
7702         : Base(bc) {
7703       bc->needI32(bc->specific_.eax);
7704       if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) {
7705         // We use xadd, so source and destination are the same.  Using
7706         // eax here is overconstraining, but for byte operations on x86
7707         // we do need something with a byte register.
7708         if (type == ValType::I64) {
7709           rv = bc->popI64ToSpecificI32(bc->specific_.eax);
7710         } else {
7711           rv = bc->popI32ToSpecific(bc->specific_.eax);
7712         }
7713         setRd(rv);
7714       } else {
7715         // We use a cmpxchg loop.  The output must be eax; the input
7716         // must be in a separate register since it may be used several
7717         // times.
7718         if (type == ValType::I64) {
7719           rv = bc->popI64ToI32();
7720         } else {
7721           rv = bc->popI32();
7722         }
7723         setRd(bc->specific_.eax);
7724 #  if defined(JS_CODEGEN_X86)
7725         // Single-byte is a special case handled very locally with
7726         // ScratchReg, see atomicRMW32 above.
7727         if (Scalar::byteSize(viewType) > 1) {
7728           temps.allocate(bc);
7729         }
7730 #  else
7731         temps.allocate(bc);
7732 #  endif
7733       }
7734     }
~PopAtomicRMW32Regs()7735     ~PopAtomicRMW32Regs() {
7736       if (rv != bc->specific_.eax) {
7737         bc->freeI32(rv);
7738       }
7739       temps.maybeFree(bc);
7740     }
7741 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
7742     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
7743                                 Scalar::Type viewType, AtomicOp op)
7744         : Base(bc) {
7745       rv = type == ValType::I64 ? bc->popI64ToI32() : bc->popI32();
7746       temps.allocate(bc);
7747       setRd(bc->needI32());
7748     }
7749     ~PopAtomicRMW32Regs() {
7750       bc->freeI32(rv);
7751       temps.maybeFree(bc);
7752     }
7753 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7754     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
7755                                 Scalar::Type viewType, AtomicOp op)
7756         : Base(bc) {
7757       rv = type == ValType::I64 ? bc->popI64ToI32() : bc->popI32();
7758       if (Scalar::byteSize(viewType) < 4) {
7759         temps.allocate(bc);
7760       }
7761 
7762       setRd(bc->needI32());
7763     }
7764     ~PopAtomicRMW32Regs() {
7765       bc->freeI32(rv);
7766       temps.maybeFree(bc);
7767     }
7768 #else
7769     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
7770                                 Scalar::Type viewType, AtomicOp op)
7771         : Base(bc) {
7772       MOZ_CRASH("BaseCompiler porting interface: PopAtomicRMW32Regs");
7773     }
7774 #endif
7775 
7776     template <typename T>
atomicRMW32(const MemoryAccessDesc & access,T srcAddr,AtomicOp op)7777     void atomicRMW32(const MemoryAccessDesc& access, T srcAddr, AtomicOp op) {
7778       bc->atomicRMW32(access, srcAddr, op, rv, getRd(), temps);
7779     }
7780   };
7781 
7782   friend class PopAtomicRMW64Regs;
7783   class PopAtomicRMW64Regs : public PopBase<RegI64> {
7784     using Base = PopBase<RegI64>;
7785 #if defined(JS_CODEGEN_X64)
7786     AtomicOp op;
7787 #endif
7788     RegI64 rv, temp;
7789 
7790    public:
7791 #if defined(JS_CODEGEN_X64)
PopAtomicRMW64Regs(BaseCompiler * bc,AtomicOp op)7792     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp op)
7793         : Base(bc), op(op) {
7794       if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) {
7795         // We use xaddq, so input and output must be the same register.
7796         rv = bc->popI64();
7797         setRd(rv);
7798       } else {
7799         // We use a cmpxchgq loop, so the output must be rax.
7800         bc->needI64(bc->specific_.rax);
7801         rv = bc->popI64();
7802         temp = bc->needI64();
7803         setRd(bc->specific_.rax);
7804       }
7805     }
~PopAtomicRMW64Regs()7806     ~PopAtomicRMW64Regs() {
7807       bc->maybeFree(temp);
7808       if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) {
7809         bc->freeI64(rv);
7810       }
7811     }
7812 #elif defined(JS_CODEGEN_X86)
7813     // We'll use cmpxchg8b, so rv must be in ecx:ebx, and rd must be
7814     // edx:eax.  But we can't reserve ebx here because we need it later, so
7815     // use a separate temp and set up ebx when we perform the operation.
7816     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
7817       bc->needI32(bc->specific_.ecx);
7818       bc->needI64(bc->specific_.edx_eax);
7819 
7820       temp = RegI64(Register64(bc->specific_.ecx, bc->needI32()));
7821       bc->popI64ToSpecific(temp);
7822 
7823       setRd(bc->specific_.edx_eax);
7824     }
7825     ~PopAtomicRMW64Regs() { bc->freeI64(temp); }
7826     RegI32 valueHigh() const { return RegI32(temp.high); }
7827     RegI32 valueLow() const { return RegI32(temp.low); }
7828 #elif defined(JS_CODEGEN_ARM)
7829     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
7830       // We use a ldrex/strexd loop so the temp and the output must be
7831       // odd/even pairs.
7832       rv = bc->popI64();
7833       temp = bc->needI64Pair();
7834       setRd(bc->needI64Pair());
7835     }
7836     ~PopAtomicRMW64Regs() {
7837       bc->freeI64(rv);
7838       bc->freeI64(temp);
7839     }
7840 #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
7841     defined(JS_CODEGEN_MIPS64)
7842     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
7843       rv = bc->popI64();
7844       temp = bc->needI64();
7845       setRd(bc->needI64());
7846     }
7847     ~PopAtomicRMW64Regs() {
7848       bc->freeI64(rv);
7849       bc->freeI64(temp);
7850     }
7851 #else
7852     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
7853       MOZ_CRASH("BaseCompiler porting interface: PopAtomicRMW64Regs");
7854     }
7855 #endif
7856 
7857 #ifdef JS_CODEGEN_X86
7858     template <typename T, typename V>
atomicRMW64(const MemoryAccessDesc & access,T srcAddr,AtomicOp op,const V & value,RegI32 ebx)7859     void atomicRMW64(const MemoryAccessDesc& access, T srcAddr, AtomicOp op,
7860                      const V& value, RegI32 ebx) {
7861       MOZ_ASSERT(ebx == js::jit::ebx);
7862       bc->atomicRMW64(access, srcAddr, op, value, bc->specific_.ecx_ebx,
7863                       getRd());
7864     }
7865 #else
7866     template <typename T>
atomicRMW64(const MemoryAccessDesc & access,T srcAddr,AtomicOp op)7867     void atomicRMW64(const MemoryAccessDesc& access, T srcAddr, AtomicOp op) {
7868       bc->atomicRMW64(access, srcAddr, op, rv, temp, getRd());
7869     }
7870 #endif
7871   };
7872 
7873   friend class PopAtomicXchg32Regs;
7874   class PopAtomicXchg32Regs : public PopBase<RegI32> {
7875     using Base = PopBase<RegI32>;
7876     RegI32 rv;
7877     AtomicXchg32Temps temps;
7878 
7879    public:
7880 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicXchg32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType)7881     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
7882                                  Scalar::Type viewType)
7883         : Base(bc) {
7884       // The xchg instruction reuses rv as rd.
7885       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
7886       setRd(rv);
7887     }
7888 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
7889     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
7890                                  Scalar::Type viewType)
7891         : Base(bc) {
7892       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
7893       setRd(bc->needI32());
7894     }
7895     ~PopAtomicXchg32Regs() { bc->freeI32(rv); }
7896 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7897     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
7898                                  Scalar::Type viewType)
7899         : Base(bc) {
7900       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
7901       if (Scalar::byteSize(viewType) < 4) {
7902         temps.allocate(bc);
7903       }
7904       setRd(bc->needI32());
7905     }
7906     ~PopAtomicXchg32Regs() {
7907       temps.maybeFree(bc);
7908       bc->freeI32(rv);
7909     }
7910 #else
7911     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
7912                                  Scalar::Type viewType)
7913         : Base(bc) {
7914       MOZ_CRASH("BaseCompiler porting interface: PopAtomicXchg32Regs");
7915     }
7916 #endif
7917 
7918     template <typename T>
atomicXchg32(const MemoryAccessDesc & access,T srcAddr)7919     void atomicXchg32(const MemoryAccessDesc& access, T srcAddr) {
7920       bc->atomicXchg32(access, srcAddr, rv, getRd(), temps);
7921     }
7922   };
7923 
7924   friend class PopAtomicXchg64Regs;
7925   class PopAtomicXchg64Regs : public PopBase<RegI64> {
7926     using Base = PopBase<RegI64>;
7927     RegI64 rv;
7928 
7929    public:
7930 #if defined(JS_CODEGEN_X64)
PopAtomicXchg64Regs(BaseCompiler * bc)7931     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7932       rv = bc->popI64();
7933       setRd(rv);
7934     }
7935 #elif defined(JS_CODEGEN_ARM64)
7936     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7937       rv = bc->popI64();
7938       setRd(bc->needI64());
7939     }
7940     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
7941 #elif defined(JS_CODEGEN_X86)
7942     // We'll use cmpxchg8b, so rv must be in ecx:ebx, and rd must be
7943     // edx:eax.  But we can't reserve ebx here because we need it later, so
7944     // use a separate temp and set up ebx when we perform the operation.
7945     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7946       bc->needI32(bc->specific_.ecx);
7947       bc->needI64(bc->specific_.edx_eax);
7948 
7949       rv = RegI64(Register64(bc->specific_.ecx, bc->needI32()));
7950       bc->popI64ToSpecific(rv);
7951 
7952       setRd(bc->specific_.edx_eax);
7953     }
7954     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
7955 #elif defined(JS_CODEGEN_ARM)
7956     // Both rv and rd must be odd/even pairs.
7957     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7958       rv = bc->popI64ToSpecific(bc->needI64Pair());
7959       setRd(bc->needI64Pair());
7960     }
7961     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
7962 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7963     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7964       rv = bc->popI64ToSpecific(bc->needI64());
7965       setRd(bc->needI64());
7966     }
7967     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
7968 #else
7969     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
7970       MOZ_CRASH("BaseCompiler porting interface: xchg64");
7971     }
7972 #endif
7973 
7974 #ifdef JS_CODEGEN_X86
7975     template <typename T>
atomicXchg64(const MemoryAccessDesc & access,T srcAddr,RegI32 ebx) const7976     void atomicXchg64(const MemoryAccessDesc& access, T srcAddr,
7977                       RegI32 ebx) const {
7978       MOZ_ASSERT(ebx == js::jit::ebx);
7979       bc->masm.move32(rv.low, ebx);
7980       bc->masm.wasmAtomicExchange64(access, srcAddr, bc->specific_.ecx_ebx,
7981                                     getRd());
7982     }
7983 #else
7984     template <typename T>
atomicXchg64(const MemoryAccessDesc & access,T srcAddr) const7985     void atomicXchg64(const MemoryAccessDesc& access, T srcAddr) const {
7986       bc->masm.wasmAtomicExchange64(access, srcAddr, rv, getRd());
7987     }
7988 #endif
7989   };
7990 
7991   ////////////////////////////////////////////////////////////
7992   //
7993   // Generally speaking, BELOW this point there should be no
7994   // platform dependencies.  We make very occasional exceptions
7995   // when it doesn't become messy and further abstraction is
7996   // not desirable.
7997   //
7998   ////////////////////////////////////////////////////////////
7999 
8000   ////////////////////////////////////////////////////////////
8001   //
8002   // Sundry wrappers.
8003 
pop2xI32(RegI32 * r0,RegI32 * r1)8004   void pop2xI32(RegI32* r0, RegI32* r1) {
8005     *r1 = popI32();
8006     *r0 = popI32();
8007   }
8008 
popI32ToSpecific(RegI32 specific)8009   RegI32 popI32ToSpecific(RegI32 specific) {
8010     freeI32(specific);
8011     return popI32(specific);
8012   }
8013 
pop2xI64(RegI64 * r0,RegI64 * r1)8014   void pop2xI64(RegI64* r0, RegI64* r1) {
8015     *r1 = popI64();
8016     *r0 = popI64();
8017   }
8018 
popI64ToSpecific(RegI64 specific)8019   RegI64 popI64ToSpecific(RegI64 specific) {
8020     freeI64(specific);
8021     return popI64(specific);
8022   }
8023 
8024 #ifdef JS_CODEGEN_ARM
popI64Pair()8025   RegI64 popI64Pair() {
8026     RegI64 r = needI64Pair();
8027     popI64ToSpecific(r);
8028     return r;
8029   }
8030 #endif
8031 
pop2xF32(RegF32 * r0,RegF32 * r1)8032   void pop2xF32(RegF32* r0, RegF32* r1) {
8033     *r1 = popF32();
8034     *r0 = popF32();
8035   }
8036 
pop2xF64(RegF64 * r0,RegF64 * r1)8037   void pop2xF64(RegF64* r0, RegF64* r1) {
8038     *r1 = popF64();
8039     *r0 = popF64();
8040   }
8041 
8042 #ifdef ENABLE_WASM_SIMD
pop2xV128(RegV128 * r0,RegV128 * r1)8043   void pop2xV128(RegV128* r0, RegV128* r1) {
8044     *r1 = popV128();
8045     *r0 = popV128();
8046   }
8047 #endif
8048 
pop2xRef(RegRef * r0,RegRef * r1)8049   void pop2xRef(RegRef* r0, RegRef* r1) {
8050     *r1 = popRef();
8051     *r0 = popRef();
8052   }
8053 
popI64ToI32()8054   RegI32 popI64ToI32() {
8055     RegI64 r = popI64();
8056     return narrowI64(r);
8057   }
8058 
popI64ToSpecificI32(RegI32 specific)8059   RegI32 popI64ToSpecificI32(RegI32 specific) {
8060     RegI64 rd = widenI32(specific);
8061     popI64ToSpecific(rd);
8062     return narrowI64(rd);
8063   }
8064 
pushU32AsI64(RegI32 rs)8065   void pushU32AsI64(RegI32 rs) {
8066     RegI64 rd = widenI32(rs);
8067     masm.move32To64ZeroExtend(rs, rd);
8068     pushI64(rd);
8069   }
8070 
8071   RegI32 popMemory32Access(MemoryAccessDesc* access, AccessCheck* check);
8072 
8073   void pushHeapBase();
8074 
8075   template <typename RegType>
8076   RegType pop();
8077   template <typename RegType>
8078   RegType need();
8079 
free(RegI32 r)8080   void free(RegI32 r) { freeI32(r); }
free(RegI64 r)8081   void free(RegI64 r) { freeI64(r); }
free(RegF32 r)8082   void free(RegF32 r) { freeF32(r); }
free(RegF64 r)8083   void free(RegF64 r) { freeF64(r); }
8084 #ifdef ENABLE_WASM_SIMD
free(RegV128 r)8085   void free(RegV128 r) { freeV128(r); }
8086 #endif
8087 
8088   ////////////////////////////////////////////////////////////
8089   //
8090   // Sundry helpers.
8091 
readCallSiteLineOrBytecode()8092   uint32_t readCallSiteLineOrBytecode() {
8093     if (!func_.callSiteLineNums.empty()) {
8094       return func_.callSiteLineNums[lastReadCallSite_++];
8095     }
8096     return iter_.lastOpcodeOffset();
8097   }
8098 
done() const8099   bool done() const { return iter_.done(); }
8100 
bytecodeOffset() const8101   BytecodeOffset bytecodeOffset() const { return iter_.bytecodeOffset(); }
8102 
trap(Trap t) const8103   void trap(Trap t) const { masm.wasmTrap(t, bytecodeOffset()); }
8104 
8105 #ifdef ENABLE_WASM_EXCEPTIONS
8106   // Abstracted helper for throwing, used for throw, rethrow, and rethrowing
8107   // at the end of a series of catch blocks (if none matched the exception).
throwFrom(RegRef exn,uint32_t lineOrBytecode)8108   [[nodiscard]] bool throwFrom(RegRef exn, uint32_t lineOrBytecode) {
8109     pushRef(exn);
8110 
8111     // ThrowException invokes a trap, and the rest is dead code.
8112     if (!emitInstanceCall(lineOrBytecode, SASigThrowException)) {
8113       return false;
8114     }
8115     freeRef(popRef());
8116 
8117     return true;
8118   }
8119 #endif
8120 
8121   ////////////////////////////////////////////////////////////
8122   //
8123   // Object support.
8124 
8125   // This emits a GC pre-write barrier.  The pre-barrier is needed when we
8126   // replace a member field with a new value, and the previous field value
8127   // might have no other referents, and incremental GC is ongoing. The field
8128   // might belong to an object or be a stack slot or a register or a heap
8129   // allocated value.
8130   //
8131   // let obj = { field: previousValue };
8132   // obj.field = newValue; // previousValue must be marked with a pre-barrier.
8133   //
8134   // The `valueAddr` is the address of the location that we are about to
8135   // update.  This function preserves that register.
8136 
emitPreBarrier(RegPtr valueAddr)8137   void emitPreBarrier(RegPtr valueAddr) {
8138     Label skipBarrier;
8139     ScratchPtr scratch(*this);
8140 
8141     fr.loadTlsPtr(scratch);
8142     EmitWasmPreBarrierGuard(masm, scratch, scratch, valueAddr, &skipBarrier);
8143 
8144     fr.loadTlsPtr(scratch);
8145 #ifdef JS_CODEGEN_ARM64
8146     // The prebarrier stub assumes the PseudoStackPointer is set up.  It is OK
8147     // to just move the sp to x28 here because x28 is not being used by the
8148     // baseline compiler and need not be saved or restored.
8149     MOZ_ASSERT(!GeneralRegisterSet::All().hasRegisterIndex(x28.asUnsized()));
8150     masm.Mov(x28, sp);
8151 #endif
8152     // The prebarrier call preserves all volatile registers
8153     EmitWasmPreBarrierCall(masm, scratch, scratch, valueAddr);
8154 
8155     masm.bind(&skipBarrier);
8156   }
8157 
8158   // This frees the register `valueAddr`.
8159 
emitPostBarrierCall(RegPtr valueAddr)8160   [[nodiscard]] bool emitPostBarrierCall(RegPtr valueAddr) {
8161     uint32_t bytecodeOffset = iter_.lastOpcodeOffset();
8162 
8163     // The `valueAddr` is a raw pointer to the cell within some GC object or
8164     // TLS area, and we guarantee that the GC will not run while the
8165     // postbarrier call is active, so push a uintptr_t value.
8166     pushPtr(valueAddr);
8167     return emitInstanceCall(bytecodeOffset, SASigPostBarrier);
8168   }
8169 
8170   // Emits a store to a JS object pointer at the address valueAddr, which is
8171   // inside the GC cell `object`. Preserves `object` and `value`.
emitBarrieredStore(const Maybe<RegRef> & object,RegPtr valueAddr,RegRef value)8172   [[nodiscard]] bool emitBarrieredStore(const Maybe<RegRef>& object,
8173                                         RegPtr valueAddr, RegRef value) {
8174     // TODO/AnyRef-boxing: With boxed immediates and strings, the write
8175     // barrier is going to have to be more complicated.
8176     ASSERT_ANYREF_IS_JSOBJECT;
8177 
8178     emitPreBarrier(valueAddr);  // Preserves valueAddr
8179     masm.storePtr(value, Address(valueAddr, 0));
8180 
8181     Label skipBarrier;
8182     sync();
8183 
8184     RegRef otherScratch = needRef();
8185     EmitWasmPostBarrierGuard(masm, object, otherScratch, value, &skipBarrier);
8186     freeRef(otherScratch);
8187 
8188     if (object) {
8189       pushRef(*object);
8190     }
8191     pushRef(value);
8192 
8193     // Consumes valueAddr
8194     if (!emitPostBarrierCall(valueAddr)) {
8195       return false;
8196     }
8197 
8198     // Consume all other operands as they may have been clobbered by the post
8199     // barrier call
8200     popRef(value);
8201     if (object) {
8202       popRef(*object);
8203     }
8204 
8205     masm.bind(&skipBarrier);
8206     return true;
8207   }
8208 
8209   ////////////////////////////////////////////////////////////
8210   //
8211   // Machinery for optimized conditional branches.
8212   //
8213   // To disable this optimization it is enough always to return false from
8214   // sniffConditionalControl{Cmp,Eqz}.
8215 
8216   struct BranchState {
8217     union {
8218       struct {
8219         RegI32 lhs;
8220         RegI32 rhs;
8221         int32_t imm;
8222         bool rhsImm;
8223       } i32;
8224       struct {
8225         RegI64 lhs;
8226         RegI64 rhs;
8227         int64_t imm;
8228         bool rhsImm;
8229       } i64;
8230       struct {
8231         RegF32 lhs;
8232         RegF32 rhs;
8233       } f32;
8234       struct {
8235         RegF64 lhs;
8236         RegF64 rhs;
8237       } f64;
8238     };
8239 
8240     Label* const label;             // The target of the branch, never NULL
8241     const StackHeight stackHeight;  // The stack base above which to place
8242                                     // stack-spilled block results, if
8243                                     // hasBlockResults().
8244     const bool invertBranch;        // If true, invert the sense of the branch
8245     const ResultType resultType;    // The result propagated along the edges
8246 
BranchStatejs::wasm::BaseCompiler::BranchState8247     explicit BranchState(Label* label)
8248         : label(label),
8249           stackHeight(StackHeight::Invalid()),
8250           invertBranch(false),
8251           resultType(ResultType::Empty()) {}
8252 
BranchStatejs::wasm::BaseCompiler::BranchState8253     BranchState(Label* label, bool invertBranch)
8254         : label(label),
8255           stackHeight(StackHeight::Invalid()),
8256           invertBranch(invertBranch),
8257           resultType(ResultType::Empty()) {}
8258 
BranchStatejs::wasm::BaseCompiler::BranchState8259     BranchState(Label* label, StackHeight stackHeight, bool invertBranch,
8260                 ResultType resultType)
8261         : label(label),
8262           stackHeight(stackHeight),
8263           invertBranch(invertBranch),
8264           resultType(resultType) {}
8265 
hasBlockResultsjs::wasm::BaseCompiler::BranchState8266     bool hasBlockResults() const { return stackHeight.isValid(); }
8267   };
8268 
setLatentCompare(Assembler::Condition compareOp,ValType operandType)8269   void setLatentCompare(Assembler::Condition compareOp, ValType operandType) {
8270     latentOp_ = LatentOp::Compare;
8271     latentType_ = operandType;
8272     latentIntCmp_ = compareOp;
8273   }
8274 
setLatentCompare(Assembler::DoubleCondition compareOp,ValType operandType)8275   void setLatentCompare(Assembler::DoubleCondition compareOp,
8276                         ValType operandType) {
8277     latentOp_ = LatentOp::Compare;
8278     latentType_ = operandType;
8279     latentDoubleCmp_ = compareOp;
8280   }
8281 
setLatentEqz(ValType operandType)8282   void setLatentEqz(ValType operandType) {
8283     latentOp_ = LatentOp::Eqz;
8284     latentType_ = operandType;
8285   }
8286 
hasLatentOp() const8287   bool hasLatentOp() const { return latentOp_ != LatentOp::None; }
8288 
resetLatentOp()8289   void resetLatentOp() { latentOp_ = LatentOp::None; }
8290 
branchTo(Assembler::DoubleCondition c,RegF64 lhs,RegF64 rhs,Label * l)8291   void branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs,
8292                 Label* l) {
8293     masm.branchDouble(c, lhs, rhs, l);
8294   }
8295 
branchTo(Assembler::DoubleCondition c,RegF32 lhs,RegF32 rhs,Label * l)8296   void branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs,
8297                 Label* l) {
8298     masm.branchFloat(c, lhs, rhs, l);
8299   }
8300 
branchTo(Assembler::Condition c,RegI32 lhs,RegI32 rhs,Label * l)8301   void branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs, Label* l) {
8302     masm.branch32(c, lhs, rhs, l);
8303   }
8304 
branchTo(Assembler::Condition c,RegI32 lhs,Imm32 rhs,Label * l)8305   void branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l) {
8306     masm.branch32(c, lhs, rhs, l);
8307   }
8308 
branchTo(Assembler::Condition c,RegI64 lhs,RegI64 rhs,Label * l)8309   void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, Label* l) {
8310     masm.branch64(c, lhs, rhs, l);
8311   }
8312 
branchTo(Assembler::Condition c,RegI64 lhs,Imm64 rhs,Label * l)8313   void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l) {
8314     masm.branch64(c, lhs, rhs, l);
8315   }
8316 
branchTo(Assembler::Condition c,RegRef lhs,ImmWord rhs,Label * l)8317   void branchTo(Assembler::Condition c, RegRef lhs, ImmWord rhs, Label* l) {
8318     masm.branchPtr(c, lhs, rhs, l);
8319   }
8320 
8321   // Emit a conditional branch that optionally and optimally cleans up the CPU
8322   // stack before we branch.
8323   //
8324   // Cond is either Assembler::Condition or Assembler::DoubleCondition.
8325   //
8326   // Lhs is RegI32, RegI64, or RegF32, RegF64, or RegRef.
8327   //
8328   // Rhs is either the same as Lhs, or an immediate expression compatible with
8329   // Lhs "when applicable".
8330 
8331   template <typename Cond, typename Lhs, typename Rhs>
jumpConditionalWithResults(BranchState * b,Cond cond,Lhs lhs,Rhs rhs)8332   [[nodiscard]] bool jumpConditionalWithResults(BranchState* b, Cond cond,
8333                                                 Lhs lhs, Rhs rhs) {
8334     if (b->hasBlockResults()) {
8335       StackHeight resultsBase(0);
8336       if (!topBranchParams(b->resultType, &resultsBase)) {
8337         return false;
8338       }
8339       if (b->stackHeight != resultsBase) {
8340         Label notTaken;
8341         branchTo(b->invertBranch ? cond : Assembler::InvertCondition(cond), lhs,
8342                  rhs, &notTaken);
8343 
8344         // Shuffle stack args.
8345         shuffleStackResultsBeforeBranch(resultsBase, b->stackHeight,
8346                                         b->resultType);
8347         masm.jump(b->label);
8348         masm.bind(&notTaken);
8349         return true;
8350       }
8351     }
8352 
8353     branchTo(b->invertBranch ? Assembler::InvertCondition(cond) : cond, lhs,
8354              rhs, b->label);
8355     return true;
8356   }
8357 
8358   // sniffConditionalControl{Cmp,Eqz} may modify the latentWhatever_ state in
8359   // the BaseCompiler so that a subsequent conditional branch can be compiled
8360   // optimally.  emitBranchSetup() and emitBranchPerform() will consume that
8361   // state.  If the latter methods are not called because deadCode_ is true
8362   // then the compiler MUST instead call resetLatentOp() to reset the state.
8363 
8364   template <typename Cond>
8365   [[nodiscard]] bool sniffConditionalControlCmp(Cond compareOp,
8366                                                 ValType operandType);
8367   [[nodiscard]] bool sniffConditionalControlEqz(ValType operandType);
8368   void emitBranchSetup(BranchState* b);
8369   [[nodiscard]] bool emitBranchPerform(BranchState* b);
8370 
8371   //////////////////////////////////////////////////////////////////////
8372 
8373   [[nodiscard]] bool emitBody();
8374   [[nodiscard]] bool emitBlock();
8375   [[nodiscard]] bool emitLoop();
8376   [[nodiscard]] bool emitIf();
8377   [[nodiscard]] bool emitElse();
8378 #ifdef ENABLE_WASM_EXCEPTIONS
8379   // Used for common setup for catch and catch_all.
8380   void emitCatchSetup(LabelKind kind, Control& tryCatch,
8381                       const ResultType& resultType);
8382   // Helper function used to generate landing pad code for the special
8383   // case in which `delegate` jumps to a function's body block.
8384   [[nodiscard]] bool emitBodyDelegateThrowPad();
8385 
8386   [[nodiscard]] bool emitTry();
8387   [[nodiscard]] bool emitCatch();
8388   [[nodiscard]] bool emitCatchAll();
8389   [[nodiscard]] bool emitDelegate();
8390   [[nodiscard]] bool emitThrow();
8391   [[nodiscard]] bool emitRethrow();
8392 #endif
8393   [[nodiscard]] bool emitEnd();
8394   [[nodiscard]] bool emitBr();
8395   [[nodiscard]] bool emitBrIf();
8396   [[nodiscard]] bool emitBrTable();
8397   [[nodiscard]] bool emitDrop();
8398   [[nodiscard]] bool emitReturn();
8399 
8400   enum class CalleeOnStack {
8401     // After the arguments to the call, there is a callee pushed onto value
8402     // stack.  This is only the case for callIndirect.  To get the arguments to
8403     // the call, emitCallArgs has to reach one element deeper into the value
8404     // stack, to skip the callee.
8405     True,
8406 
8407     // No callee on the stack.
8408     False
8409   };
8410 
8411   [[nodiscard]] bool emitCallArgs(const ValTypeVector& argTypes,
8412                                   const StackResultsLoc& results,
8413                                   FunctionCall* baselineCall,
8414                                   CalleeOnStack calleeOnStack);
8415 
8416   [[nodiscard]] bool emitCall();
8417   [[nodiscard]] bool emitCallIndirect();
8418   [[nodiscard]] bool emitUnaryMathBuiltinCall(SymbolicAddress callee,
8419                                               ValType operandType);
8420   [[nodiscard]] bool emitGetLocal();
8421   [[nodiscard]] bool emitSetLocal();
8422   [[nodiscard]] bool emitTeeLocal();
8423   [[nodiscard]] bool emitGetGlobal();
8424   [[nodiscard]] bool emitSetGlobal();
8425   [[nodiscard]] RegI32 maybeLoadTlsForAccess(const AccessCheck& check);
8426   [[nodiscard]] RegI32 maybeLoadTlsForAccess(const AccessCheck& check,
8427                                              RegI32 specific);
8428   [[nodiscard]] bool emitLoad(ValType type, Scalar::Type viewType);
8429   [[nodiscard]] bool loadCommon(MemoryAccessDesc* access, AccessCheck check,
8430                                 ValType type);
8431   [[nodiscard]] bool emitStore(ValType resultType, Scalar::Type viewType);
8432   [[nodiscard]] bool storeCommon(MemoryAccessDesc* access, AccessCheck check,
8433                                  ValType resultType);
8434   [[nodiscard]] bool emitSelect(bool typed);
8435 
8436   template <bool isSetLocal>
8437   [[nodiscard]] bool emitSetOrTeeLocal(uint32_t slot);
8438 
8439   [[nodiscard]] bool endBlock(ResultType type);
8440   [[nodiscard]] bool endIfThen(ResultType type);
8441   [[nodiscard]] bool endIfThenElse(ResultType type);
8442 #ifdef ENABLE_WASM_EXCEPTIONS
8443   [[nodiscard]] bool endTryCatch(ResultType type);
8444 #endif
8445 
8446   void doReturn(ContinuationKind kind);
8447   void pushReturnValueOfCall(const FunctionCall& call, MIRType type);
8448 
8449   [[nodiscard]] bool pushStackResultsForCall(const ResultType& type,
8450                                              RegPtr temp, StackResultsLoc* loc);
8451   void popStackResultsAfterCall(const StackResultsLoc& results,
8452                                 uint32_t stackArgBytes);
8453 
8454   void emitCompareI32(Assembler::Condition compareOp, ValType compareType);
8455   void emitCompareI64(Assembler::Condition compareOp, ValType compareType);
8456   void emitCompareF32(Assembler::DoubleCondition compareOp,
8457                       ValType compareType);
8458   void emitCompareF64(Assembler::DoubleCondition compareOp,
8459                       ValType compareType);
8460   void emitCompareRef(Assembler::Condition compareOp, ValType compareType);
8461 
8462   template <typename CompilerType>
8463   CompilerType& selectCompiler();
8464 
8465   template <typename SourceType, typename DestType>
8466   void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, DestType rd));
8467 
8468   template <typename SourceType, typename DestType, typename TempType>
8469   void emitUnop(void (*op)(MacroAssembler& masm, SourceType rs, DestType rd,
8470                            TempType temp));
8471 
8472   template <typename SourceType, typename DestType, typename ImmType>
8473   void emitUnop(ImmType immediate,
8474                 void (*op)(MacroAssembler&, ImmType, SourceType, DestType));
8475 
8476   template <typename CompilerType, typename RegType>
8477   void emitUnop(void (*op)(CompilerType& compiler, RegType rsd));
8478 
8479   template <typename RegType, typename TempType>
8480   void emitUnop(void (*op)(BaseCompiler& bc, RegType rsd, TempType rt),
8481                 TempType (*getSpecializedTemp)(BaseCompiler& bc));
8482 
8483   template <typename CompilerType, typename RhsType, typename LhsDestType>
8484   void emitBinop(void (*op)(CompilerType& masm, RhsType src,
8485                             LhsDestType srcDest));
8486 
8487   template <typename RhsDestType, typename LhsType>
8488   void emitBinop(void (*op)(MacroAssembler& masm, RhsDestType src,
8489                             LhsType srcDest, RhsDestOp));
8490 
8491   template <typename RhsType, typename LhsDestType, typename TempType>
8492   void emitBinop(void (*)(MacroAssembler& masm, RhsType rs, LhsDestType rsd,
8493                           TempType temp));
8494 
8495   template <typename RhsType, typename LhsDestType, typename TempType1,
8496             typename TempType2>
8497   void emitBinop(void (*)(MacroAssembler& masm, RhsType rs, LhsDestType rsd,
8498                           TempType1 temp1, TempType2 temp2));
8499 
8500   template <typename RhsType, typename LhsDestType, typename ImmType>
8501   void emitBinop(ImmType immediate,
8502                  void (*op)(MacroAssembler&, ImmType, RhsType, LhsDestType));
8503 
8504   template <typename RhsType, typename LhsDestType, typename ImmType,
8505             typename TempType1, typename TempType2>
8506   void emitBinop(ImmType immediate,
8507                  void (*op)(MacroAssembler&, ImmType, RhsType, LhsDestType,
8508                             TempType1 temp1, TempType2 temp2));
8509 
8510   template <typename CompilerType1, typename CompilerType2, typename RegType,
8511             typename ImmType>
8512   void emitBinop(void (*op)(CompilerType1& compiler1, RegType rs, RegType rd),
8513                  void (*opConst)(CompilerType2& compiler2, ImmType c,
8514                                  RegType rd),
8515                  RegType (BaseCompiler::*rhsPopper)() = nullptr);
8516 
8517   template <typename R>
8518   [[nodiscard]] bool emitInstanceCallOp(const SymbolicAddressSignature& fn,
8519                                         R reader);
8520 
8521   template <typename A1, typename R>
8522   [[nodiscard]] bool emitInstanceCallOp(const SymbolicAddressSignature& fn,
8523                                         R reader);
8524 
8525   template <typename A1, typename A2, typename R>
8526   [[nodiscard]] bool emitInstanceCallOp(const SymbolicAddressSignature& fn,
8527                                         R reader);
8528 
8529   void emitMultiplyI32();
8530   void emitMultiplyI64();
8531   void emitQuotientI32();
8532   void emitQuotientU32();
8533   void emitRemainderI32();
8534   void emitRemainderU32();
8535 #ifdef RABALDR_INT_DIV_I64_CALLOUT
8536   [[nodiscard]] bool emitDivOrModI64BuiltinCall(SymbolicAddress callee,
8537                                                 ValType operandType);
8538 #else
8539   void emitQuotientI64();
8540   void emitQuotientU64();
8541   void emitRemainderI64();
8542   void emitRemainderU64();
8543 #endif
8544   void emitRotrI64();
8545   void emitRotlI64();
8546   void emitEqzI32();
8547   void emitEqzI64();
8548   template <TruncFlags flags>
8549   [[nodiscard]] bool emitTruncateF32ToI32();
8550   template <TruncFlags flags>
8551   [[nodiscard]] bool emitTruncateF64ToI32();
8552 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8553   [[nodiscard]] bool emitConvertFloatingToInt64Callout(SymbolicAddress callee,
8554                                                        ValType operandType,
8555                                                        ValType resultType);
8556 #else
8557   template <TruncFlags flags>
8558   [[nodiscard]] bool emitTruncateF32ToI64();
8559   template <TruncFlags flags>
8560   [[nodiscard]] bool emitTruncateF64ToI64();
8561 #endif
8562   void emitExtendI64_8();
8563   void emitExtendI64_16();
8564   void emitExtendI64_32();
8565   void emitExtendI32ToI64();
8566   void emitExtendU32ToI64();
8567 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
8568   [[nodiscard]] bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee,
8569                                                        ValType operandType,
8570                                                        ValType resultType);
8571 #else
8572   void emitConvertU64ToF32();
8573   void emitConvertU64ToF64();
8574 #endif
8575   void emitRound(RoundingMode roundingMode, ValType operandType);
8576   [[nodiscard]] bool emitInstanceCall(uint32_t lineOrBytecode,
8577                                       const SymbolicAddressSignature& builtin);
8578   [[nodiscard]] bool emitMemoryGrow();
8579   [[nodiscard]] bool emitMemorySize();
8580 
8581   [[nodiscard]] bool emitRefFunc();
8582   [[nodiscard]] bool emitRefNull();
8583   [[nodiscard]] bool emitRefIsNull();
8584 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
8585   [[nodiscard]] bool emitRefAsNonNull();
8586   [[nodiscard]] bool emitBrOnNull();
8587 #endif
8588 
8589   [[nodiscard]] bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
8590   [[nodiscard]] bool emitAtomicLoad(ValType type, Scalar::Type viewType);
8591   [[nodiscard]] bool emitAtomicRMW(ValType type, Scalar::Type viewType,
8592                                    AtomicOp op);
8593   [[nodiscard]] bool emitAtomicStore(ValType type, Scalar::Type viewType);
8594   [[nodiscard]] bool emitWait(ValType type, uint32_t byteSize);
8595   [[nodiscard]] bool emitWake();
8596   [[nodiscard]] bool emitFence();
8597   [[nodiscard]] bool emitAtomicXchg(ValType type, Scalar::Type viewType);
8598   void emitAtomicXchg64(MemoryAccessDesc* access, WantResult wantResult);
8599   [[nodiscard]] bool emitMemInit();
8600   [[nodiscard]] bool emitMemCopy();
8601   [[nodiscard]] bool emitMemCopyCall(uint32_t lineOrBytecode);
8602   [[nodiscard]] bool emitMemCopyInline();
8603   [[nodiscard]] bool emitTableCopy();
8604   [[nodiscard]] bool emitDataOrElemDrop(bool isData);
8605   [[nodiscard]] bool emitMemFill();
8606   [[nodiscard]] bool emitMemFillCall(uint32_t lineOrBytecode);
8607   [[nodiscard]] bool emitMemFillInline();
8608   [[nodiscard]] bool emitTableInit();
8609   [[nodiscard]] bool emitTableFill();
8610   [[nodiscard]] bool emitTableGet();
8611   [[nodiscard]] bool emitTableGrow();
8612   [[nodiscard]] bool emitTableSet();
8613   [[nodiscard]] bool emitTableSize();
8614   [[nodiscard]] bool emitStructNewWithRtt();
8615   [[nodiscard]] bool emitStructNewDefaultWithRtt();
8616   [[nodiscard]] bool emitStructGet(FieldExtension extension);
8617   [[nodiscard]] bool emitStructSet();
8618   [[nodiscard]] bool emitArrayNewWithRtt();
8619   [[nodiscard]] bool emitArrayNewDefaultWithRtt();
8620   [[nodiscard]] bool emitArrayGet(FieldExtension extension);
8621   [[nodiscard]] bool emitArraySet();
8622   [[nodiscard]] bool emitArrayLen();
8623   [[nodiscard]] bool emitRttCanon();
8624   [[nodiscard]] bool emitRttSub();
8625   [[nodiscard]] bool emitRefTest();
8626   [[nodiscard]] bool emitRefCast();
8627   [[nodiscard]] bool emitBrOnCast();
8628 
8629   void emitGcNullCheck(RegRef rp);
8630   RegPtr emitGcArrayGetData(RegRef rp);
8631   RegI32 emitGcArrayGetLength(RegPtr rdata, bool adjustDataPointer);
8632   void emitGcArrayBoundsCheck(RegI32 index, RegI32 length);
8633   template <typename T>
8634   void emitGcGet(FieldType type, FieldExtension extension, const T& src);
8635   template <typename T>
8636   void emitGcSetScalar(const T& dst, FieldType type, AnyReg value);
8637   [[nodiscard]] bool emitGcStructSet(RegRef object, RegPtr data,
8638                                      const StructField& field, AnyReg value);
8639   [[nodiscard]] bool emitGcArraySet(RegRef object, RegPtr data, RegI32 index,
8640                                     const ArrayType& array, AnyReg value);
8641 
8642 #ifdef ENABLE_WASM_SIMD
8643   void emitVectorAndNot();
8644 
8645   [[nodiscard]] bool emitLoadSplat(Scalar::Type viewType);
8646   [[nodiscard]] bool emitLoadZero(Scalar::Type viewType);
8647   [[nodiscard]] bool emitLoadExtend(Scalar::Type viewType);
8648   [[nodiscard]] bool emitLoadLane(uint32_t laneSize);
8649   [[nodiscard]] bool emitStoreLane(uint32_t laneSize);
8650   [[nodiscard]] bool emitBitselect();
8651   [[nodiscard]] bool emitVectorShuffle();
8652 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
8653   [[nodiscard]] bool emitVectorShiftRightI64x2();
8654 #  endif
8655 #endif
8656 };
8657 
8658 // TODO: We want these to be inlined for sure; do we need an `inline` somewhere?
8659 
8660 template <>
need()8661 RegI32 BaseCompiler::need<RegI32>() {
8662   return needI32();
8663 }
8664 template <>
need()8665 RegI64 BaseCompiler::need<RegI64>() {
8666   return needI64();
8667 }
8668 template <>
need()8669 RegF32 BaseCompiler::need<RegF32>() {
8670   return needF32();
8671 }
8672 template <>
need()8673 RegF64 BaseCompiler::need<RegF64>() {
8674   return needF64();
8675 }
8676 
8677 template <>
pop()8678 RegI32 BaseCompiler::pop<RegI32>() {
8679   return popI32();
8680 }
8681 template <>
pop()8682 RegI64 BaseCompiler::pop<RegI64>() {
8683   return popI64();
8684 }
8685 template <>
pop()8686 RegF32 BaseCompiler::pop<RegF32>() {
8687   return popF32();
8688 }
8689 template <>
pop()8690 RegF64 BaseCompiler::pop<RegF64>() {
8691   return popF64();
8692 }
8693 
8694 #ifdef ENABLE_WASM_SIMD
8695 template <>
need()8696 RegV128 BaseCompiler::need<RegV128>() {
8697   return needV128();
8698 }
8699 template <>
pop()8700 RegV128 BaseCompiler::pop<RegV128>() {
8701   return popV128();
8702 }
8703 #endif
8704 
8705 template <>
selectCompiler()8706 BaseCompiler& BaseCompiler::selectCompiler<BaseCompiler>() {
8707   return *this;
8708 }
8709 
8710 template <>
selectCompiler()8711 MacroAssembler& BaseCompiler::selectCompiler<MacroAssembler>() {
8712   return masm;
8713 }
8714 
8715 template <typename SourceType, typename DestType>
emitUnop(void (* op)(MacroAssembler & masm,SourceType rs,DestType rd))8716 void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, SourceType rs,
8717                                        DestType rd)) {
8718   SourceType rs = pop<SourceType>();
8719   DestType rd = need<DestType>();
8720   op(masm, rs, rd);
8721   free(rs);
8722   push(rd);
8723 }
8724 
8725 // Specialize narrowing reuse.  Consumers may assume that rs.reg==rd on 64-bit
8726 // platforms, or rs.low==rd on 32-bit platforms.
8727 template <>
emitUnop(void (* op)(MacroAssembler & masm,RegI64 rs,RegI32 rd))8728 void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, RegI64 rs,
8729                                        RegI32 rd)) {
8730   RegI64 rs = pop<RegI64>();
8731   RegI32 rd = fromI64(rs);
8732   op(masm, rs, rd);
8733   freeI64Except(rs, rd);
8734   push(rd);
8735 }
8736 
8737 template <typename CompilerType, typename RegType>
emitUnop(void (* op)(CompilerType & compiler,RegType rsd))8738 void BaseCompiler::emitUnop(void (*op)(CompilerType& compiler, RegType rsd)) {
8739   RegType rsd = pop<RegType>();
8740   op(selectCompiler<CompilerType>(), rsd);
8741   push(rsd);
8742 }
8743 
8744 template <typename RegType, typename TempType>
emitUnop(void (* op)(BaseCompiler & bc,RegType rsd,TempType rt),TempType (* getSpecializedTemp)(BaseCompiler & bc))8745 void BaseCompiler::emitUnop(void (*op)(BaseCompiler& bc, RegType rsd,
8746                                        TempType rt),
8747                             TempType (*getSpecializedTemp)(BaseCompiler& bc)) {
8748   RegType rsd = pop<RegType>();
8749   TempType temp = getSpecializedTemp(*this);
8750   op(*this, rsd, temp);
8751   maybeFree(temp);
8752   push(rsd);
8753 }
8754 
8755 template <typename SourceType, typename DestType, typename TempType>
emitUnop(void (* op)(MacroAssembler & masm,SourceType rs,DestType rd,TempType temp))8756 void BaseCompiler::emitUnop(void (*op)(MacroAssembler& masm, SourceType rs,
8757                                        DestType rd, TempType temp)) {
8758   SourceType rs = pop<SourceType>();
8759   DestType rd = need<DestType>();
8760   TempType temp = need<TempType>();
8761   op(masm, rs, rd, temp);
8762   free(rs);
8763   free(temp);
8764   push(rd);
8765 }
8766 
8767 template <typename SourceType, typename DestType, typename ImmType>
emitUnop(ImmType immediate,void (* op)(MacroAssembler &,ImmType,SourceType,DestType))8768 void BaseCompiler::emitUnop(ImmType immediate,
8769                             void (*op)(MacroAssembler&, ImmType, SourceType,
8770                                        DestType)) {
8771   SourceType rs = pop<SourceType>();
8772   DestType rd = need<DestType>();
8773   op(masm, immediate, rs, rd);
8774   free(rs);
8775   push(rd);
8776 }
8777 
8778 template <typename CompilerType, typename RhsType, typename LhsDestType>
emitBinop(void (* op)(CompilerType & masm,RhsType src,LhsDestType srcDest))8779 void BaseCompiler::emitBinop(void (*op)(CompilerType& masm, RhsType src,
8780                                         LhsDestType srcDest)) {
8781   RhsType rs = pop<RhsType>();
8782   LhsDestType rsd = pop<LhsDestType>();
8783   op(selectCompiler<CompilerType>(), rs, rsd);
8784   free(rs);
8785   push(rsd);
8786 }
8787 
8788 template <typename RhsDestType, typename LhsType>
emitBinop(void (* op)(MacroAssembler & masm,RhsDestType src,LhsType srcDest,RhsDestOp))8789 void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsDestType src,
8790                                         LhsType srcDest, RhsDestOp)) {
8791   RhsDestType rsd = pop<RhsDestType>();
8792   LhsType rs = pop<LhsType>();
8793   op(masm, rsd, rs, RhsDestOp::True);
8794   free(rs);
8795   push(rsd);
8796 }
8797 
8798 template <typename RhsType, typename LhsDestType, typename TempType>
emitBinop(void (* op)(MacroAssembler & masm,RhsType rs,LhsDestType rsd,TempType temp))8799 void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsType rs,
8800                                         LhsDestType rsd, TempType temp)) {
8801   RhsType rs = pop<RhsType>();
8802   LhsDestType rsd = pop<LhsDestType>();
8803   TempType temp = need<TempType>();
8804   op(masm, rs, rsd, temp);
8805   free(rs);
8806   free(temp);
8807   push(rsd);
8808 }
8809 
8810 template <typename RhsType, typename LhsDestType, typename TempType1,
8811           typename TempType2>
emitBinop(void (* op)(MacroAssembler & masm,RhsType rs,LhsDestType rsd,TempType1 temp1,TempType2 temp2))8812 void BaseCompiler::emitBinop(void (*op)(MacroAssembler& masm, RhsType rs,
8813                                         LhsDestType rsd, TempType1 temp1,
8814                                         TempType2 temp2)) {
8815   RhsType rs = pop<RhsType>();
8816   LhsDestType rsd = pop<LhsDestType>();
8817   TempType1 temp1 = need<TempType1>();
8818   TempType2 temp2 = need<TempType2>();
8819   op(masm, rs, rsd, temp1, temp2);
8820   free(rs);
8821   free(temp1);
8822   free(temp2);
8823   push(rsd);
8824 }
8825 
8826 template <typename RhsType, typename LhsDestType, typename ImmType>
emitBinop(ImmType immediate,void (* op)(MacroAssembler &,ImmType,RhsType,LhsDestType))8827 void BaseCompiler::emitBinop(ImmType immediate,
8828                              void (*op)(MacroAssembler&, ImmType, RhsType,
8829                                         LhsDestType)) {
8830   RhsType rs = pop<RhsType>();
8831   LhsDestType rsd = pop<LhsDestType>();
8832   op(masm, immediate, rs, rsd);
8833   free(rs);
8834   push(rsd);
8835 }
8836 
8837 template <typename RhsType, typename LhsDestType, typename ImmType,
8838           typename TempType1, typename TempType2>
emitBinop(ImmType immediate,void (* op)(MacroAssembler &,ImmType,RhsType,LhsDestType,TempType1 temp1,TempType2 temp2))8839 void BaseCompiler::emitBinop(ImmType immediate,
8840                              void (*op)(MacroAssembler&, ImmType, RhsType,
8841                                         LhsDestType, TempType1 temp1,
8842                                         TempType2 temp2)) {
8843   RhsType rs = pop<RhsType>();
8844   LhsDestType rsd = pop<LhsDestType>();
8845   TempType1 temp1 = need<TempType1>();
8846   TempType2 temp2 = need<TempType2>();
8847   op(masm, immediate, rs, rsd, temp1, temp2);
8848   free(rs);
8849   free(temp1);
8850   free(temp2);
8851   push(rsd);
8852 }
8853 
8854 template <typename CompilerType1, typename CompilerType2, typename RegType,
8855           typename ImmType>
emitBinop(void (* op)(CompilerType1 & compiler,RegType rs,RegType rsd),void (* opConst)(CompilerType2 & compiler,ImmType c,RegType rsd),RegType (BaseCompiler::* rhsPopper)())8856 void BaseCompiler::emitBinop(void (*op)(CompilerType1& compiler, RegType rs,
8857                                         RegType rsd),
8858                              void (*opConst)(CompilerType2& compiler, ImmType c,
8859                                              RegType rsd),
8860                              RegType (BaseCompiler::*rhsPopper)()) {
8861   ImmType c;
8862   if (popConst(&c)) {
8863     RegType rsd = pop<RegType>();
8864     opConst(selectCompiler<CompilerType2>(), c, rsd);
8865     push(rsd);
8866   } else {
8867     RegType rs = rhsPopper ? (this->*rhsPopper)() : pop<RegType>();
8868     RegType rsd = pop<RegType>();
8869     op(selectCompiler<CompilerType1>(), rs, rsd);
8870     free(rs);
8871     push(rsd);
8872   }
8873 }
8874 
8875 template <typename R>
emitInstanceCallOp(const SymbolicAddressSignature & fn,R reader)8876 [[nodiscard]] bool BaseCompiler::emitInstanceCallOp(
8877     const SymbolicAddressSignature& fn, R reader) {
8878   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
8879   if (!reader()) {
8880     return false;
8881   }
8882   if (deadCode_) {
8883     return true;
8884   }
8885   return emitInstanceCall(lineOrBytecode, fn);
8886 }
8887 
8888 template <typename A1, typename R>
emitInstanceCallOp(const SymbolicAddressSignature & fn,R reader)8889 [[nodiscard]] bool BaseCompiler::emitInstanceCallOp(
8890     const SymbolicAddressSignature& fn, R reader) {
8891   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
8892   A1 arg = 0;
8893   if (!reader(&arg)) {
8894     return false;
8895   }
8896   if (deadCode_) {
8897     return true;
8898   }
8899   push(arg);
8900   return emitInstanceCall(lineOrBytecode, fn);
8901 }
8902 
8903 template <typename A1, typename A2, typename R>
emitInstanceCallOp(const SymbolicAddressSignature & fn,R reader)8904 [[nodiscard]] bool BaseCompiler::emitInstanceCallOp(
8905     const SymbolicAddressSignature& fn, R reader) {
8906   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
8907   A1 arg1 = 0;
8908   A2 arg2 = 0;
8909   if (!reader(&arg1, &arg2)) {
8910     return false;
8911   }
8912   if (deadCode_) {
8913     return true;
8914   }
8915   // Note order of arguments must be the same as for the reader.
8916   push(arg1);
8917   push(arg2);
8918   return emitInstanceCall(lineOrBytecode, fn);
8919 }
8920 
AddI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8921 static void AddI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8922   masm.add32(rs, rsd);
8923 }
8924 
AddImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8925 static void AddImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8926   masm.add32(Imm32(c), rsd);
8927 }
8928 
SubI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8929 static void SubI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8930   masm.sub32(rs, rsd);
8931 }
8932 
SubImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8933 static void SubImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8934   masm.sub32(Imm32(c), rsd);
8935 }
8936 
OrI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8937 static void OrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8938   masm.or32(rs, rsd);
8939 }
8940 
OrImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8941 static void OrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8942   masm.or32(Imm32(c), rsd);
8943 }
8944 
AndI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8945 static void AndI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8946   masm.and32(rs, rsd);
8947 }
8948 
AndImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8949 static void AndImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8950   masm.and32(Imm32(c), rsd);
8951 }
8952 
XorI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8953 static void XorI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8954   masm.xor32(rs, rsd);
8955 }
8956 
XorImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8957 static void XorImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8958   masm.xor32(Imm32(c), rsd);
8959 }
8960 
ClzI32(MacroAssembler & masm,RegI32 rsd)8961 static void ClzI32(MacroAssembler& masm, RegI32 rsd) {
8962   masm.clz32(rsd, rsd, IsKnownNotZero(false));
8963 }
8964 
CtzI32(MacroAssembler & masm,RegI32 rsd)8965 static void CtzI32(MacroAssembler& masm, RegI32 rsd) {
8966   masm.ctz32(rsd, rsd, IsKnownNotZero(false));
8967 }
8968 
8969 // Currently common to PopcntI32 and PopcntI64
PopcntTemp(BaseCompiler & bc)8970 static RegI32 PopcntTemp(BaseCompiler& bc) {
8971 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
8972   return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : bc.needI32();
8973 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
8974     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
8975   return bc.needI32();
8976 #else
8977   MOZ_CRASH("BaseCompiler platform hook: PopcntTemp");
8978 #endif
8979 }
8980 
PopcntI32(BaseCompiler & bc,RegI32 rsd,RegI32 temp)8981 static void PopcntI32(BaseCompiler& bc, RegI32 rsd, RegI32 temp) {
8982   bc.masm.popcnt32(rsd, rsd, temp);
8983 }
8984 
ShlI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8985 static void ShlI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8986   masm.lshift32(rs, rsd);
8987 }
8988 
ShlImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8989 static void ShlImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8990   masm.lshift32(Imm32(c & 31), rsd);
8991 }
8992 
ShrI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)8993 static void ShrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
8994   masm.rshift32Arithmetic(rs, rsd);
8995 }
8996 
ShrImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)8997 static void ShrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
8998   masm.rshift32Arithmetic(Imm32(c & 31), rsd);
8999 }
9000 
ShrUI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)9001 static void ShrUI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
9002   masm.rshift32(rs, rsd);
9003 }
9004 
ShrUImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)9005 static void ShrUImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
9006   masm.rshift32(Imm32(c & 31), rsd);
9007 }
9008 
RotlI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)9009 static void RotlI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
9010   masm.rotateLeft(rs, rsd, rsd);
9011 }
9012 
RotlImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)9013 static void RotlImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
9014   masm.rotateLeft(Imm32(c & 31), rsd, rsd);
9015 }
9016 
RotrI32(MacroAssembler & masm,RegI32 rs,RegI32 rsd)9017 static void RotrI32(MacroAssembler& masm, RegI32 rs, RegI32 rsd) {
9018   masm.rotateRight(rs, rsd, rsd);
9019 }
9020 
RotrImmI32(MacroAssembler & masm,int32_t c,RegI32 rsd)9021 static void RotrImmI32(MacroAssembler& masm, int32_t c, RegI32 rsd) {
9022   masm.rotateRight(Imm32(c & 31), rsd, rsd);
9023 }
9024 
EqzI32(MacroAssembler & masm,RegI32 rsd)9025 static void EqzI32(MacroAssembler& masm, RegI32 rsd) {
9026   masm.cmp32Set(Assembler::Equal, rsd, Imm32(0), rsd);
9027 }
9028 
WrapI64ToI32(MacroAssembler & masm,RegI64 rs,RegI32 rd)9029 static void WrapI64ToI32(MacroAssembler& masm, RegI64 rs, RegI32 rd) {
9030   masm.move64To32(rs, rd);
9031 }
9032 
AddI64(MacroAssembler & masm,RegI64 rs,RegI64 rsd)9033 static void AddI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) {
9034   masm.add64(rs, rsd);
9035 }
9036 
AddImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9037 static void AddImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9038   masm.add64(Imm64(c), rsd);
9039 }
9040 
SubI64(MacroAssembler & masm,RegI64 rs,RegI64 rsd)9041 static void SubI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) {
9042   masm.sub64(rs, rsd);
9043 }
9044 
SubImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9045 static void SubImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9046   masm.sub64(Imm64(c), rsd);
9047 }
9048 
OrI64(MacroAssembler & masm,RegI64 rs,RegI64 rsd)9049 static void OrI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) {
9050   masm.or64(rs, rsd);
9051 }
9052 
OrImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9053 static void OrImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9054   masm.or64(Imm64(c), rsd);
9055 }
9056 
AndI64(MacroAssembler & masm,RegI64 rs,RegI64 rsd)9057 static void AndI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) {
9058   masm.and64(rs, rsd);
9059 }
9060 
AndImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9061 static void AndImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9062   masm.and64(Imm64(c), rsd);
9063 }
9064 
XorI64(MacroAssembler & masm,RegI64 rs,RegI64 rsd)9065 static void XorI64(MacroAssembler& masm, RegI64 rs, RegI64 rsd) {
9066   masm.xor64(rs, rsd);
9067 }
9068 
XorImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9069 static void XorImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9070   masm.xor64(Imm64(c), rsd);
9071 }
9072 
ClzI64(BaseCompiler & bc,RegI64 rsd)9073 static void ClzI64(BaseCompiler& bc, RegI64 rsd) {
9074   bc.masm.clz64(rsd, bc.lowPart(rsd));
9075   bc.maybeClearHighPart(rsd);
9076 }
9077 
CtzI64(BaseCompiler & bc,RegI64 rsd)9078 static void CtzI64(BaseCompiler& bc, RegI64 rsd) {
9079   bc.masm.ctz64(rsd, bc.lowPart(rsd));
9080   bc.maybeClearHighPart(rsd);
9081 }
9082 
PopcntI64(BaseCompiler & bc,RegI64 rsd,RegI32 temp)9083 static void PopcntI64(BaseCompiler& bc, RegI64 rsd, RegI32 temp) {
9084   bc.masm.popcnt64(rsd, rsd, temp);
9085 }
9086 
ShlI64(BaseCompiler & bc,RegI64 rs,RegI64 rsd)9087 static void ShlI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) {
9088   bc.masm.lshift64(bc.lowPart(rs), rsd);
9089 }
9090 
ShlImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9091 static void ShlImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9092   masm.lshift64(Imm32(c & 63), rsd);
9093 }
9094 
ShrI64(BaseCompiler & bc,RegI64 rs,RegI64 rsd)9095 static void ShrI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) {
9096   bc.masm.rshift64Arithmetic(bc.lowPart(rs), rsd);
9097 }
9098 
ShrImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9099 static void ShrImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9100   masm.rshift64Arithmetic(Imm32(c & 63), rsd);
9101 }
9102 
ShrUI64(BaseCompiler & bc,RegI64 rs,RegI64 rsd)9103 static void ShrUI64(BaseCompiler& bc, RegI64 rs, RegI64 rsd) {
9104   bc.masm.rshift64(bc.lowPart(rs), rsd);
9105 }
9106 
ShrUImmI64(MacroAssembler & masm,int64_t c,RegI64 rsd)9107 static void ShrUImmI64(MacroAssembler& masm, int64_t c, RegI64 rsd) {
9108   masm.rshift64(Imm32(c & 63), rsd);
9109 }
9110 
EqzI64(MacroAssembler & masm,RegI64 rs,RegI32 rd)9111 static void EqzI64(MacroAssembler& masm, RegI64 rs, RegI32 rd) {
9112 #ifdef JS_PUNBOX64
9113   masm.cmpPtrSet(Assembler::Equal, rs.reg, ImmWord(0), rd);
9114 #else
9115   MOZ_ASSERT(rs.low == rd);
9116   masm.or32(rs.high, rs.low);
9117   masm.cmp32Set(Assembler::Equal, rs.low, Imm32(0), rd);
9118 #endif
9119 }
9120 
AddF64(MacroAssembler & masm,RegF64 rs,RegF64 rsd)9121 static void AddF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) {
9122   masm.addDouble(rs, rsd);
9123 }
9124 
SubF64(MacroAssembler & masm,RegF64 rs,RegF64 rsd)9125 static void SubF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) {
9126   masm.subDouble(rs, rsd);
9127 }
9128 
MulF64(MacroAssembler & masm,RegF64 rs,RegF64 rsd)9129 static void MulF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) {
9130   masm.mulDouble(rs, rsd);
9131 }
9132 
DivF64(MacroAssembler & masm,RegF64 rs,RegF64 rsd)9133 static void DivF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd) {
9134   masm.divDouble(rs, rsd);
9135 }
9136 
MinF64(BaseCompiler & bc,RegF64 rs,RegF64 rsd)9137 static void MinF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd) {
9138   // Convert signaling NaN to quiet NaNs.
9139   //
9140   // TODO / OPTIMIZE (bug 1316824): see comment in MinF32.
9141 #ifdef RABALDR_SCRATCH_F64
9142   ScratchF64 zero(bc.ra);
9143 #else
9144   ScratchF64 zero(bc.masm);
9145 #endif
9146   bc.masm.loadConstantDouble(0, zero);
9147   bc.masm.subDouble(zero, rsd);
9148   bc.masm.subDouble(zero, rs);
9149   bc.masm.minDouble(rs, rsd, HandleNaNSpecially(true));
9150 }
9151 
MaxF64(BaseCompiler & bc,RegF64 rs,RegF64 rsd)9152 static void MaxF64(BaseCompiler& bc, RegF64 rs, RegF64 rsd) {
9153   // Convert signaling NaN to quiet NaNs.
9154   //
9155   // TODO / OPTIMIZE (bug 1316824): see comment in MinF32.
9156 #ifdef RABALDR_SCRATCH_F64
9157   ScratchF64 zero(bc.ra);
9158 #else
9159   ScratchF64 zero(bc.masm);
9160 #endif
9161   bc.masm.loadConstantDouble(0, zero);
9162   bc.masm.subDouble(zero, rsd);
9163   bc.masm.subDouble(zero, rs);
9164   bc.masm.maxDouble(rs, rsd, HandleNaNSpecially(true));
9165 }
9166 
CopysignF64(MacroAssembler & masm,RegF64 rs,RegF64 rsd,RegI64 temp0,RegI64 temp1)9167 static void CopysignF64(MacroAssembler& masm, RegF64 rs, RegF64 rsd,
9168                         RegI64 temp0, RegI64 temp1) {
9169   masm.moveDoubleToGPR64(rsd, temp0);
9170   masm.moveDoubleToGPR64(rs, temp1);
9171   masm.and64(Imm64(INT64_MAX), temp0);
9172   masm.and64(Imm64(INT64_MIN), temp1);
9173   masm.or64(temp1, temp0);
9174   masm.moveGPR64ToDouble(temp0, rsd);
9175 }
9176 
AbsF64(MacroAssembler & masm,RegF64 rsd)9177 static void AbsF64(MacroAssembler& masm, RegF64 rsd) {
9178   masm.absDouble(rsd, rsd);
9179 }
9180 
NegateF64(MacroAssembler & masm,RegF64 rsd)9181 static void NegateF64(MacroAssembler& masm, RegF64 rsd) {
9182   masm.negateDouble(rsd);
9183 }
9184 
SqrtF64(MacroAssembler & masm,RegF64 rsd)9185 static void SqrtF64(MacroAssembler& masm, RegF64 rsd) {
9186   masm.sqrtDouble(rsd, rsd);
9187 }
9188 
AddF32(MacroAssembler & masm,RegF32 rs,RegF32 rsd)9189 static void AddF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) {
9190   masm.addFloat32(rs, rsd);
9191 }
9192 
SubF32(MacroAssembler & masm,RegF32 rs,RegF32 rsd)9193 static void SubF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) {
9194   masm.subFloat32(rs, rsd);
9195 }
9196 
MulF32(MacroAssembler & masm,RegF32 rs,RegF32 rsd)9197 static void MulF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) {
9198   masm.mulFloat32(rs, rsd);
9199 }
9200 
DivF32(MacroAssembler & masm,RegF32 rs,RegF32 rsd)9201 static void DivF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd) {
9202   masm.divFloat32(rs, rsd);
9203 }
9204 
MinF32(BaseCompiler & bc,RegF32 rs,RegF32 rsd)9205 static void MinF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd) {
9206   // Convert signaling NaN to quiet NaNs.
9207   //
9208   // TODO / OPTIMIZE (bug 1316824): Don't do this if one of the operands
9209   // is known to be a constant.
9210 #ifdef RABALDR_SCRATCH_F32
9211   ScratchF32 zero(bc.ra);
9212 #else
9213   ScratchF32 zero(bc.masm);
9214 #endif
9215   bc.masm.loadConstantFloat32(0.f, zero);
9216   bc.masm.subFloat32(zero, rsd);
9217   bc.masm.subFloat32(zero, rs);
9218   bc.masm.minFloat32(rs, rsd, HandleNaNSpecially(true));
9219 }
9220 
MaxF32(BaseCompiler & bc,RegF32 rs,RegF32 rsd)9221 static void MaxF32(BaseCompiler& bc, RegF32 rs, RegF32 rsd) {
9222   // Convert signaling NaN to quiet NaNs.
9223   //
9224   // TODO / OPTIMIZE (bug 1316824): see comment in MinF32.
9225 #ifdef RABALDR_SCRATCH_F32
9226   ScratchF32 zero(bc.ra);
9227 #else
9228   ScratchF32 zero(bc.masm);
9229 #endif
9230   bc.masm.loadConstantFloat32(0.f, zero);
9231   bc.masm.subFloat32(zero, rsd);
9232   bc.masm.subFloat32(zero, rs);
9233   bc.masm.maxFloat32(rs, rsd, HandleNaNSpecially(true));
9234 }
9235 
CopysignF32(MacroAssembler & masm,RegF32 rs,RegF32 rsd,RegI32 temp0,RegI32 temp1)9236 static void CopysignF32(MacroAssembler& masm, RegF32 rs, RegF32 rsd,
9237                         RegI32 temp0, RegI32 temp1) {
9238   masm.moveFloat32ToGPR(rsd, temp0);
9239   masm.moveFloat32ToGPR(rs, temp1);
9240   masm.and32(Imm32(INT32_MAX), temp0);
9241   masm.and32(Imm32(INT32_MIN), temp1);
9242   masm.or32(temp1, temp0);
9243   masm.moveGPRToFloat32(temp0, rsd);
9244 }
9245 
AbsF32(MacroAssembler & masm,RegF32 rsd)9246 static void AbsF32(MacroAssembler& masm, RegF32 rsd) {
9247   masm.absFloat32(rsd, rsd);
9248 }
9249 
NegateF32(MacroAssembler & masm,RegF32 rsd)9250 static void NegateF32(MacroAssembler& masm, RegF32 rsd) {
9251   masm.negateFloat(rsd);
9252 }
9253 
SqrtF32(MacroAssembler & masm,RegF32 rsd)9254 static void SqrtF32(MacroAssembler& masm, RegF32 rsd) {
9255   masm.sqrtFloat32(rsd, rsd);
9256 }
9257 
9258 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
ConvertI64ToF32(MacroAssembler & masm,RegI64 rs,RegF32 rd)9259 static void ConvertI64ToF32(MacroAssembler& masm, RegI64 rs, RegF32 rd) {
9260   masm.convertInt64ToFloat32(rs, rd);
9261 }
9262 
ConvertI64ToF64(MacroAssembler & masm,RegI64 rs,RegF64 rd)9263 static void ConvertI64ToF64(MacroAssembler& masm, RegI64 rs, RegF64 rd) {
9264   masm.convertInt64ToDouble(rs, rd);
9265 }
9266 #endif
9267 
ReinterpretF32AsI32(MacroAssembler & masm,RegF32 rs,RegI32 rd)9268 static void ReinterpretF32AsI32(MacroAssembler& masm, RegF32 rs, RegI32 rd) {
9269   masm.moveFloat32ToGPR(rs, rd);
9270 }
9271 
ReinterpretF64AsI64(MacroAssembler & masm,RegF64 rs,RegI64 rd)9272 static void ReinterpretF64AsI64(MacroAssembler& masm, RegF64 rs, RegI64 rd) {
9273   masm.moveDoubleToGPR64(rs, rd);
9274 }
9275 
ConvertF64ToF32(MacroAssembler & masm,RegF64 rs,RegF32 rd)9276 static void ConvertF64ToF32(MacroAssembler& masm, RegF64 rs, RegF32 rd) {
9277   masm.convertDoubleToFloat32(rs, rd);
9278 }
9279 
ConvertI32ToF32(MacroAssembler & masm,RegI32 rs,RegF32 rd)9280 static void ConvertI32ToF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) {
9281   masm.convertInt32ToFloat32(rs, rd);
9282 }
9283 
ConvertU32ToF32(MacroAssembler & masm,RegI32 rs,RegF32 rd)9284 static void ConvertU32ToF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) {
9285   masm.convertUInt32ToFloat32(rs, rd);
9286 }
9287 
ConvertF32ToF64(MacroAssembler & masm,RegF32 rs,RegF64 rd)9288 static void ConvertF32ToF64(MacroAssembler& masm, RegF32 rs, RegF64 rd) {
9289   masm.convertFloat32ToDouble(rs, rd);
9290 }
9291 
ConvertI32ToF64(MacroAssembler & masm,RegI32 rs,RegF64 rd)9292 static void ConvertI32ToF64(MacroAssembler& masm, RegI32 rs, RegF64 rd) {
9293   masm.convertInt32ToDouble(rs, rd);
9294 }
9295 
ConvertU32ToF64(MacroAssembler & masm,RegI32 rs,RegF64 rd)9296 static void ConvertU32ToF64(MacroAssembler& masm, RegI32 rs, RegF64 rd) {
9297   masm.convertUInt32ToDouble(rs, rd);
9298 }
9299 
ReinterpretI32AsF32(MacroAssembler & masm,RegI32 rs,RegF32 rd)9300 static void ReinterpretI32AsF32(MacroAssembler& masm, RegI32 rs, RegF32 rd) {
9301   masm.moveGPRToFloat32(rs, rd);
9302 }
9303 
ReinterpretI64AsF64(MacroAssembler & masm,RegI64 rs,RegF64 rd)9304 static void ReinterpretI64AsF64(MacroAssembler& masm, RegI64 rs, RegF64 rd) {
9305   masm.moveGPR64ToDouble(rs, rd);
9306 }
9307 
ExtendI32_8(BaseCompiler & bc,RegI32 rsd)9308 static void ExtendI32_8(BaseCompiler& bc, RegI32 rsd) {
9309 #ifdef JS_CODEGEN_X86
9310   if (!bc.ra.isSingleByteI32(rsd)) {
9311     ScratchI8 scratch(bc.ra);
9312     bc.masm.move32(rsd, scratch);
9313     bc.masm.move8SignExtend(scratch, rsd);
9314     return;
9315   }
9316 #endif
9317   bc.masm.move8SignExtend(rsd, rsd);
9318 }
9319 
ExtendI32_16(MacroAssembler & masm,RegI32 rsd)9320 static void ExtendI32_16(MacroAssembler& masm, RegI32 rsd) {
9321   masm.move16SignExtend(rsd, rsd);
9322 }
9323 
emitMultiplyI32()9324 void BaseCompiler::emitMultiplyI32() {
9325   RegI32 r, rs, reserved;
9326   pop2xI32ForMulDivI32(&r, &rs, &reserved);
9327   masm.mul32(rs, r);
9328   maybeFree(reserved);
9329   freeI32(rs);
9330   pushI32(r);
9331 }
9332 
emitMultiplyI64()9333 void BaseCompiler::emitMultiplyI64() {
9334   RegI64 r, rs, reserved;
9335   RegI32 temp;
9336   pop2xI64ForMulI64(&r, &rs, &temp, &reserved);
9337   masm.mul64(rs, r, temp);
9338   maybeFree(reserved);
9339   maybeFree(temp);
9340   freeI64(rs);
9341   pushI64(r);
9342 }
9343 
emitQuotientI32()9344 void BaseCompiler::emitQuotientI32() {
9345   int32_t c;
9346   uint_fast8_t power;
9347   if (popConstPositivePowerOfTwo(&c, &power, 0)) {
9348     if (power != 0) {
9349       RegI32 r = popI32();
9350       Label positive;
9351       masm.branchTest32(Assembler::NotSigned, r, r, &positive);
9352       masm.add32(Imm32(c - 1), r);
9353       masm.bind(&positive);
9354 
9355       masm.rshift32Arithmetic(Imm32(power & 31), r);
9356       pushI32(r);
9357     }
9358   } else {
9359     bool isConst = peekConst(&c);
9360     RegI32 r, rs, reserved;
9361     pop2xI32ForMulDivI32(&r, &rs, &reserved);
9362 
9363     if (!isConst || c == 0) {
9364       checkDivideByZeroI32(rs);
9365     }
9366 
9367     Label done;
9368     if (!isConst || c == -1) {
9369       checkDivideSignedOverflowI32(rs, r, &done, ZeroOnOverflow(false));
9370     }
9371     masm.quotient32(rs, r, IsUnsigned(false));
9372     masm.bind(&done);
9373 
9374     maybeFree(reserved);
9375     freeI32(rs);
9376     pushI32(r);
9377   }
9378 }
9379 
emitQuotientU32()9380 void BaseCompiler::emitQuotientU32() {
9381   int32_t c;
9382   uint_fast8_t power;
9383   if (popConstPositivePowerOfTwo(&c, &power, 0)) {
9384     if (power != 0) {
9385       RegI32 r = popI32();
9386       masm.rshift32(Imm32(power & 31), r);
9387       pushI32(r);
9388     }
9389   } else {
9390     bool isConst = peekConst(&c);
9391     RegI32 r, rs, reserved;
9392     pop2xI32ForMulDivI32(&r, &rs, &reserved);
9393 
9394     if (!isConst || c == 0) {
9395       checkDivideByZeroI32(rs);
9396     }
9397     masm.quotient32(rs, r, IsUnsigned(true));
9398 
9399     maybeFree(reserved);
9400     freeI32(rs);
9401     pushI32(r);
9402   }
9403 }
9404 
emitRemainderI32()9405 void BaseCompiler::emitRemainderI32() {
9406   int32_t c;
9407   uint_fast8_t power;
9408   if (popConstPositivePowerOfTwo(&c, &power, 1)) {
9409     RegI32 r = popI32();
9410     RegI32 temp = needI32();
9411     moveI32(r, temp);
9412 
9413     Label positive;
9414     masm.branchTest32(Assembler::NotSigned, temp, temp, &positive);
9415     masm.add32(Imm32(c - 1), temp);
9416     masm.bind(&positive);
9417 
9418     masm.rshift32Arithmetic(Imm32(power & 31), temp);
9419     masm.lshift32(Imm32(power & 31), temp);
9420     masm.sub32(temp, r);
9421     freeI32(temp);
9422 
9423     pushI32(r);
9424   } else {
9425     bool isConst = peekConst(&c);
9426     RegI32 r, rs, reserved;
9427     pop2xI32ForMulDivI32(&r, &rs, &reserved);
9428 
9429     if (!isConst || c == 0) {
9430       checkDivideByZeroI32(rs);
9431     }
9432 
9433     Label done;
9434     if (!isConst || c == -1) {
9435       checkDivideSignedOverflowI32(rs, r, &done, ZeroOnOverflow(true));
9436     }
9437     masm.remainder32(rs, r, IsUnsigned(false));
9438     masm.bind(&done);
9439 
9440     maybeFree(reserved);
9441     freeI32(rs);
9442     pushI32(r);
9443   }
9444 }
9445 
emitRemainderU32()9446 void BaseCompiler::emitRemainderU32() {
9447   int32_t c;
9448   uint_fast8_t power;
9449   if (popConstPositivePowerOfTwo(&c, &power, 1)) {
9450     RegI32 r = popI32();
9451     masm.and32(Imm32(c - 1), r);
9452     pushI32(r);
9453   } else {
9454     bool isConst = peekConst(&c);
9455     RegI32 r, rs, reserved;
9456     pop2xI32ForMulDivI32(&r, &rs, &reserved);
9457 
9458     if (!isConst || c == 0) {
9459       checkDivideByZeroI32(rs);
9460     }
9461     masm.remainder32(rs, r, IsUnsigned(true));
9462 
9463     maybeFree(reserved);
9464     freeI32(rs);
9465     pushI32(r);
9466   }
9467 }
9468 
9469 #ifndef RABALDR_INT_DIV_I64_CALLOUT
emitQuotientI64()9470 void BaseCompiler::emitQuotientI64() {
9471 #  ifdef JS_64BIT
9472   int64_t c;
9473   uint_fast8_t power;
9474   if (popConstPositivePowerOfTwo(&c, &power, 0)) {
9475     if (power != 0) {
9476       RegI64 r = popI64();
9477       Label positive;
9478       masm.branchTest64(Assembler::NotSigned, r, r, RegI32::Invalid(),
9479                         &positive);
9480       masm.add64(Imm64(c - 1), r);
9481       masm.bind(&positive);
9482 
9483       masm.rshift64Arithmetic(Imm32(power & 63), r);
9484       pushI64(r);
9485     }
9486   } else {
9487     bool isConst = peekConst(&c);
9488     RegI64 r, rs, reserved;
9489     pop2xI64ForDivI64(&r, &rs, &reserved);
9490     quotientI64(rs, r, reserved, IsUnsigned(false), isConst, c);
9491     maybeFree(reserved);
9492     freeI64(rs);
9493     pushI64(r);
9494   }
9495 #  else
9496   MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
9497 #  endif
9498 }
9499 
emitQuotientU64()9500 void BaseCompiler::emitQuotientU64() {
9501 #  ifdef JS_64BIT
9502   int64_t c;
9503   uint_fast8_t power;
9504   if (popConstPositivePowerOfTwo(&c, &power, 0)) {
9505     if (power != 0) {
9506       RegI64 r = popI64();
9507       masm.rshift64(Imm32(power & 63), r);
9508       pushI64(r);
9509     }
9510   } else {
9511     bool isConst = peekConst(&c);
9512     RegI64 r, rs, reserved;
9513     pop2xI64ForDivI64(&r, &rs, &reserved);
9514     quotientI64(rs, r, reserved, IsUnsigned(true), isConst, c);
9515     maybeFree(reserved);
9516     freeI64(rs);
9517     pushI64(r);
9518   }
9519 #  else
9520   MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
9521 #  endif
9522 }
9523 
emitRemainderI64()9524 void BaseCompiler::emitRemainderI64() {
9525 #  ifdef JS_64BIT
9526   int64_t c;
9527   uint_fast8_t power;
9528   if (popConstPositivePowerOfTwo(&c, &power, 1)) {
9529     RegI64 r = popI64();
9530     RegI64 temp = needI64();
9531     moveI64(r, temp);
9532 
9533     Label positive;
9534     masm.branchTest64(Assembler::NotSigned, temp, temp, RegI32::Invalid(),
9535                       &positive);
9536     masm.add64(Imm64(c - 1), temp);
9537     masm.bind(&positive);
9538 
9539     masm.rshift64Arithmetic(Imm32(power & 63), temp);
9540     masm.lshift64(Imm32(power & 63), temp);
9541     masm.sub64(temp, r);
9542     freeI64(temp);
9543 
9544     pushI64(r);
9545   } else {
9546     bool isConst = peekConst(&c);
9547     RegI64 r, rs, reserved;
9548     pop2xI64ForDivI64(&r, &rs, &reserved);
9549     remainderI64(rs, r, reserved, IsUnsigned(false), isConst, c);
9550     maybeFree(reserved);
9551     freeI64(rs);
9552     pushI64(r);
9553   }
9554 #  else
9555   MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
9556 #  endif
9557 }
9558 
emitRemainderU64()9559 void BaseCompiler::emitRemainderU64() {
9560 #  ifdef JS_64BIT
9561   int64_t c;
9562   uint_fast8_t power;
9563   if (popConstPositivePowerOfTwo(&c, &power, 1)) {
9564     RegI64 r = popI64();
9565     masm.and64(Imm64(c - 1), r);
9566     pushI64(r);
9567   } else {
9568     bool isConst = peekConst(&c);
9569     RegI64 r, rs, reserved;
9570     pop2xI64ForDivI64(&r, &rs, &reserved);
9571     remainderI64(rs, r, reserved, IsUnsigned(true), isConst, c);
9572     maybeFree(reserved);
9573     freeI64(rs);
9574     pushI64(r);
9575   }
9576 #  else
9577   MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
9578 #  endif
9579 }
9580 #endif  // RABALDR_INT_DIV_I64_CALLOUT
9581 
emitRotrI64()9582 void BaseCompiler::emitRotrI64() {
9583   int64_t c;
9584   if (popConst(&c)) {
9585     RegI64 r = popI64();
9586     RegI32 temp = needRotate64Temp();
9587     masm.rotateRight64(Imm32(c & 63), r, r, temp);
9588     maybeFree(temp);
9589     pushI64(r);
9590   } else {
9591     RegI64 rs = popI64RhsForRotate();
9592     RegI64 r = popI64();
9593     masm.rotateRight64(lowPart(rs), r, r, maybeHighPart(rs));
9594     freeI64(rs);
9595     pushI64(r);
9596   }
9597 }
9598 
emitRotlI64()9599 void BaseCompiler::emitRotlI64() {
9600   int64_t c;
9601   if (popConst(&c)) {
9602     RegI64 r = popI64();
9603     RegI32 temp = needRotate64Temp();
9604     masm.rotateLeft64(Imm32(c & 63), r, r, temp);
9605     maybeFree(temp);
9606     pushI64(r);
9607   } else {
9608     RegI64 rs = popI64RhsForRotate();
9609     RegI64 r = popI64();
9610     masm.rotateLeft64(lowPart(rs), r, r, maybeHighPart(rs));
9611     freeI64(rs);
9612     pushI64(r);
9613   }
9614 }
9615 
emitEqzI32()9616 void BaseCompiler::emitEqzI32() {
9617   if (sniffConditionalControlEqz(ValType::I32)) {
9618     return;
9619   }
9620   emitUnop(EqzI32);
9621 }
9622 
emitEqzI64()9623 void BaseCompiler::emitEqzI64() {
9624   if (sniffConditionalControlEqz(ValType::I64)) {
9625     return;
9626   }
9627   emitUnop(EqzI64);
9628 }
9629 
9630 template <TruncFlags flags>
emitTruncateF32ToI32()9631 bool BaseCompiler::emitTruncateF32ToI32() {
9632   RegF32 rs = popF32();
9633   RegI32 rd = needI32();
9634   if (!truncateF32ToI32(rs, rd, flags)) {
9635     return false;
9636   }
9637   freeF32(rs);
9638   pushI32(rd);
9639   return true;
9640 }
9641 
9642 template <TruncFlags flags>
emitTruncateF64ToI32()9643 bool BaseCompiler::emitTruncateF64ToI32() {
9644   RegF64 rs = popF64();
9645   RegI32 rd = needI32();
9646   if (!truncateF64ToI32(rs, rd, flags)) {
9647     return false;
9648   }
9649   freeF64(rs);
9650   pushI32(rd);
9651   return true;
9652 }
9653 
9654 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT
9655 template <TruncFlags flags>
emitTruncateF32ToI64()9656 bool BaseCompiler::emitTruncateF32ToI64() {
9657   RegF32 rs = popF32();
9658   RegI64 rd = needI64();
9659   RegF64 temp = needTempForFloatingToI64(flags);
9660   if (!truncateF32ToI64(rs, rd, flags, temp)) {
9661     return false;
9662   }
9663   maybeFree(temp);
9664   freeF32(rs);
9665   pushI64(rd);
9666   return true;
9667 }
9668 
9669 template <TruncFlags flags>
emitTruncateF64ToI64()9670 bool BaseCompiler::emitTruncateF64ToI64() {
9671   RegF64 rs = popF64();
9672   RegI64 rd = needI64();
9673   RegF64 temp = needTempForFloatingToI64(flags);
9674   if (!truncateF64ToI64(rs, rd, flags, temp)) {
9675     return false;
9676   }
9677   maybeFree(temp);
9678   freeF64(rs);
9679   pushI64(rd);
9680   return true;
9681 }
9682 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
9683 
emitExtendI64_8()9684 void BaseCompiler::emitExtendI64_8() {
9685   RegI64 r;
9686   popI64ForSignExtendI64(&r);
9687   masm.move8To64SignExtend(lowPart(r), r);
9688   pushI64(r);
9689 }
9690 
emitExtendI64_16()9691 void BaseCompiler::emitExtendI64_16() {
9692   RegI64 r;
9693   popI64ForSignExtendI64(&r);
9694   masm.move16To64SignExtend(lowPart(r), r);
9695   pushI64(r);
9696 }
9697 
emitExtendI64_32()9698 void BaseCompiler::emitExtendI64_32() {
9699   RegI64 r;
9700   popI64ForSignExtendI64(&r);
9701   masm.move32To64SignExtend(lowPart(r), r);
9702   pushI64(r);
9703 }
9704 
emitExtendI32ToI64()9705 void BaseCompiler::emitExtendI32ToI64() {
9706   RegI64 r;
9707   popI32ForSignExtendI64(&r);
9708   masm.move32To64SignExtend(lowPart(r), r);
9709   pushI64(r);
9710 }
9711 
emitExtendU32ToI64()9712 void BaseCompiler::emitExtendU32ToI64() {
9713   RegI32 rs = popI32();
9714   RegI64 rd = widenI32(rs);
9715   masm.move32To64ZeroExtend(rs, rd);
9716   pushI64(rd);
9717 }
9718 
9719 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
emitConvertU64ToF32()9720 void BaseCompiler::emitConvertU64ToF32() {
9721   RegI64 rs = popI64();
9722   RegF32 rd = needF32();
9723   RegI32 temp = needConvertI64ToFloatTemp(ValType::F32, IsUnsigned(true));
9724   convertI64ToF32(rs, IsUnsigned(true), rd, temp);
9725   maybeFree(temp);
9726   freeI64(rs);
9727   pushF32(rd);
9728 }
9729 
emitConvertU64ToF64()9730 void BaseCompiler::emitConvertU64ToF64() {
9731   RegI64 rs = popI64();
9732   RegF64 rd = needF64();
9733   RegI32 temp = needConvertI64ToFloatTemp(ValType::F64, IsUnsigned(true));
9734   convertI64ToF64(rs, IsUnsigned(true), rd, temp);
9735   maybeFree(temp);
9736   freeI64(rs);
9737   pushF64(rd);
9738 }
9739 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
9740 
9741 template <typename Cond>
sniffConditionalControlCmp(Cond compareOp,ValType operandType)9742 bool BaseCompiler::sniffConditionalControlCmp(Cond compareOp,
9743                                               ValType operandType) {
9744   MOZ_ASSERT(latentOp_ == LatentOp::None,
9745              "Latent comparison state not properly reset");
9746 
9747 #ifdef JS_CODEGEN_X86
9748   // On x86, latent i64 binary comparisons use too many registers: the
9749   // reserved join register and the lhs and rhs operands require six, but we
9750   // only have five.
9751   if (operandType == ValType::I64) {
9752     return false;
9753   }
9754 #endif
9755 
9756   // No optimization for pointer compares yet.
9757   if (operandType.isReference()) {
9758     return false;
9759   }
9760 
9761   OpBytes op{};
9762   iter_.peekOp(&op);
9763   switch (op.b0) {
9764     case uint16_t(Op::BrIf):
9765     case uint16_t(Op::If):
9766     case uint16_t(Op::SelectNumeric):
9767     case uint16_t(Op::SelectTyped):
9768       setLatentCompare(compareOp, operandType);
9769       return true;
9770     default:
9771       return false;
9772   }
9773 }
9774 
sniffConditionalControlEqz(ValType operandType)9775 bool BaseCompiler::sniffConditionalControlEqz(ValType operandType) {
9776   MOZ_ASSERT(latentOp_ == LatentOp::None,
9777              "Latent comparison state not properly reset");
9778 
9779   OpBytes op{};
9780   iter_.peekOp(&op);
9781   switch (op.b0) {
9782     case uint16_t(Op::BrIf):
9783     case uint16_t(Op::SelectNumeric):
9784     case uint16_t(Op::SelectTyped):
9785     case uint16_t(Op::If):
9786       setLatentEqz(operandType);
9787       return true;
9788     default:
9789       return false;
9790   }
9791 }
9792 
emitBranchSetup(BranchState * b)9793 void BaseCompiler::emitBranchSetup(BranchState* b) {
9794   // Avoid allocating operands to latentOp_ to result registers.
9795   if (b->hasBlockResults()) {
9796     needResultRegisters(b->resultType);
9797   }
9798 
9799   // Set up fields so that emitBranchPerform() need not switch on latentOp_.
9800   switch (latentOp_) {
9801     case LatentOp::None: {
9802       latentIntCmp_ = Assembler::NotEqual;
9803       latentType_ = ValType::I32;
9804       b->i32.lhs = popI32();
9805       b->i32.rhsImm = true;
9806       b->i32.imm = 0;
9807       break;
9808     }
9809     case LatentOp::Compare: {
9810       switch (latentType_.kind()) {
9811         case ValType::I32: {
9812           if (popConst(&b->i32.imm)) {
9813             b->i32.lhs = popI32();
9814             b->i32.rhsImm = true;
9815           } else {
9816             pop2xI32(&b->i32.lhs, &b->i32.rhs);
9817             b->i32.rhsImm = false;
9818           }
9819           break;
9820         }
9821         case ValType::I64: {
9822           pop2xI64(&b->i64.lhs, &b->i64.rhs);
9823           b->i64.rhsImm = false;
9824           break;
9825         }
9826         case ValType::F32: {
9827           pop2xF32(&b->f32.lhs, &b->f32.rhs);
9828           break;
9829         }
9830         case ValType::F64: {
9831           pop2xF64(&b->f64.lhs, &b->f64.rhs);
9832           break;
9833         }
9834         default: {
9835           MOZ_CRASH("Unexpected type for LatentOp::Compare");
9836         }
9837       }
9838       break;
9839     }
9840     case LatentOp::Eqz: {
9841       switch (latentType_.kind()) {
9842         case ValType::I32: {
9843           latentIntCmp_ = Assembler::Equal;
9844           b->i32.lhs = popI32();
9845           b->i32.rhsImm = true;
9846           b->i32.imm = 0;
9847           break;
9848         }
9849         case ValType::I64: {
9850           latentIntCmp_ = Assembler::Equal;
9851           b->i64.lhs = popI64();
9852           b->i64.rhsImm = true;
9853           b->i64.imm = 0;
9854           break;
9855         }
9856         default: {
9857           MOZ_CRASH("Unexpected type for LatentOp::Eqz");
9858         }
9859       }
9860       break;
9861     }
9862   }
9863 
9864   if (b->hasBlockResults()) {
9865     freeResultRegisters(b->resultType);
9866   }
9867 }
9868 
emitBranchPerform(BranchState * b)9869 bool BaseCompiler::emitBranchPerform(BranchState* b) {
9870   switch (latentType_.kind()) {
9871     case ValType::I32: {
9872       if (b->i32.rhsImm) {
9873         if (!jumpConditionalWithResults(b, latentIntCmp_, b->i32.lhs,
9874                                         Imm32(b->i32.imm))) {
9875           return false;
9876         }
9877       } else {
9878         if (!jumpConditionalWithResults(b, latentIntCmp_, b->i32.lhs,
9879                                         b->i32.rhs)) {
9880           return false;
9881         }
9882         freeI32(b->i32.rhs);
9883       }
9884       freeI32(b->i32.lhs);
9885       break;
9886     }
9887     case ValType::I64: {
9888       if (b->i64.rhsImm) {
9889         if (!jumpConditionalWithResults(b, latentIntCmp_, b->i64.lhs,
9890                                         Imm64(b->i64.imm))) {
9891           return false;
9892         }
9893       } else {
9894         if (!jumpConditionalWithResults(b, latentIntCmp_, b->i64.lhs,
9895                                         b->i64.rhs)) {
9896           return false;
9897         }
9898         freeI64(b->i64.rhs);
9899       }
9900       freeI64(b->i64.lhs);
9901       break;
9902     }
9903     case ValType::F32: {
9904       if (!jumpConditionalWithResults(b, latentDoubleCmp_, b->f32.lhs,
9905                                       b->f32.rhs)) {
9906         return false;
9907       }
9908       freeF32(b->f32.lhs);
9909       freeF32(b->f32.rhs);
9910       break;
9911     }
9912     case ValType::F64: {
9913       if (!jumpConditionalWithResults(b, latentDoubleCmp_, b->f64.lhs,
9914                                       b->f64.rhs)) {
9915         return false;
9916       }
9917       freeF64(b->f64.lhs);
9918       freeF64(b->f64.rhs);
9919       break;
9920     }
9921     default: {
9922       MOZ_CRASH("Unexpected type for LatentOp::Compare");
9923     }
9924   }
9925   resetLatentOp();
9926   return true;
9927 }
9928 
9929 // For blocks and loops and ifs:
9930 //
9931 //  - Sync the value stack before going into the block in order to simplify exit
9932 //    from the block: all exits from the block can assume that there are no
9933 //    live registers except the one carrying the exit value.
9934 //  - The block can accumulate a number of dead values on the stacks, so when
9935 //    branching out of the block or falling out at the end be sure to
9936 //    pop the appropriate stacks back to where they were on entry, while
9937 //    preserving the exit value.
9938 //  - A continue branch in a loop is much like an exit branch, but the branch
9939 //    value must not be preserved.
9940 //  - The exit value is always in a designated join register (type dependent).
9941 
emitBlock()9942 bool BaseCompiler::emitBlock() {
9943   ResultType params;
9944   if (!iter_.readBlock(&params)) {
9945     return false;
9946   }
9947 
9948   if (!deadCode_) {
9949     sync();  // Simplifies branching out from block
9950   }
9951 
9952   initControl(controlItem(), params);
9953 
9954   return true;
9955 }
9956 
endBlock(ResultType type)9957 bool BaseCompiler::endBlock(ResultType type) {
9958   Control& block = controlItem();
9959 
9960   if (deadCode_) {
9961     // Block does not fall through; reset stack.
9962     fr.resetStackHeight(block.stackHeight, type);
9963     popValueStackTo(block.stackSize);
9964   } else {
9965     // If the block label is used, we have a control join, so we need to shuffle
9966     // fallthrough values into place.  Otherwise if it's not a control join, we
9967     // can leave the value stack alone.
9968     MOZ_ASSERT(stk_.length() == block.stackSize + type.length());
9969     if (block.label.used()) {
9970       popBlockResults(type, block.stackHeight, ContinuationKind::Fallthrough);
9971     }
9972     block.bceSafeOnExit &= bceSafe_;
9973   }
9974 
9975   // Bind after cleanup: branches out will have popped the stack.
9976   if (block.label.used()) {
9977     masm.bind(&block.label);
9978     if (deadCode_) {
9979       captureResultRegisters(type);
9980       deadCode_ = false;
9981     }
9982     if (!pushBlockResults(type)) {
9983       return false;
9984     }
9985   }
9986 
9987   bceSafe_ = block.bceSafeOnExit;
9988 
9989   return true;
9990 }
9991 
emitLoop()9992 bool BaseCompiler::emitLoop() {
9993   ResultType params;
9994   if (!iter_.readLoop(&params)) {
9995     return false;
9996   }
9997 
9998   if (!deadCode_) {
9999     sync();  // Simplifies branching out from block
10000   }
10001 
10002   initControl(controlItem(), params);
10003   bceSafe_ = 0;
10004 
10005   if (!deadCode_) {
10006     // Loop entry is a control join, so shuffle the entry parameters into the
10007     // well-known locations.
10008     if (!topBlockParams(params)) {
10009       return false;
10010     }
10011     masm.nopAlign(CodeAlignment);
10012     masm.bind(&controlItem(0).label);
10013     // The interrupt check barfs if there are live registers.
10014     sync();
10015     if (!addInterruptCheck()) {
10016       return false;
10017     }
10018   }
10019 
10020   return true;
10021 }
10022 
10023 // The bodies of the "then" and "else" arms can be arbitrary sequences
10024 // of expressions, they push control and increment the nesting and can
10025 // even be targeted by jumps.  A branch to the "if" block branches to
10026 // the exit of the if, ie, it's like "break".  Consider:
10027 //
10028 //      (func (result i32)
10029 //       (if (i32.const 1)
10030 //           (begin (br 1) (unreachable))
10031 //           (begin (unreachable)))
10032 //       (i32.const 1))
10033 //
10034 // The branch causes neither of the unreachable expressions to be
10035 // evaluated.
10036 
emitIf()10037 bool BaseCompiler::emitIf() {
10038   ResultType params;
10039   Nothing unused_cond;
10040   if (!iter_.readIf(&params, &unused_cond)) {
10041     return false;
10042   }
10043 
10044   BranchState b(&controlItem().otherLabel, InvertBranch(true));
10045   if (!deadCode_) {
10046     needResultRegisters(params);
10047     emitBranchSetup(&b);
10048     freeResultRegisters(params);
10049     sync();
10050   } else {
10051     resetLatentOp();
10052   }
10053 
10054   initControl(controlItem(), params);
10055 
10056   if (!deadCode_) {
10057     // Because params can flow immediately to results in the case of an empty
10058     // "then" or "else" block, and the result of an if/then is a join in
10059     // general, we shuffle params eagerly to the result allocations.
10060     if (!topBlockParams(params)) {
10061       return false;
10062     }
10063     if (!emitBranchPerform(&b)) {
10064       return false;
10065     }
10066   }
10067 
10068   return true;
10069 }
10070 
endIfThen(ResultType type)10071 bool BaseCompiler::endIfThen(ResultType type) {
10072   Control& ifThen = controlItem();
10073 
10074   // The parameters to the "if" logically flow to both the "then" and "else"
10075   // blocks, but the "else" block is empty.  Since we know that the "if"
10076   // type-checks, that means that the "else" parameters are the "else" results,
10077   // and that the "if"'s result type is the same as its parameter type.
10078 
10079   if (deadCode_) {
10080     // "then" arm does not fall through; reset stack.
10081     fr.resetStackHeight(ifThen.stackHeight, type);
10082     popValueStackTo(ifThen.stackSize);
10083     if (!ifThen.deadOnArrival) {
10084       captureResultRegisters(type);
10085     }
10086   } else {
10087     MOZ_ASSERT(stk_.length() == ifThen.stackSize + type.length());
10088     // Assume we have a control join, so place results in block result
10089     // allocations.
10090     popBlockResults(type, ifThen.stackHeight, ContinuationKind::Fallthrough);
10091     MOZ_ASSERT(!ifThen.deadOnArrival);
10092   }
10093 
10094   if (ifThen.otherLabel.used()) {
10095     masm.bind(&ifThen.otherLabel);
10096   }
10097 
10098   if (ifThen.label.used()) {
10099     masm.bind(&ifThen.label);
10100   }
10101 
10102   if (!deadCode_) {
10103     ifThen.bceSafeOnExit &= bceSafe_;
10104   }
10105 
10106   deadCode_ = ifThen.deadOnArrival;
10107   if (!deadCode_) {
10108     if (!pushBlockResults(type)) {
10109       return false;
10110     }
10111   }
10112 
10113   bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry;
10114 
10115   return true;
10116 }
10117 
emitElse()10118 bool BaseCompiler::emitElse() {
10119   ResultType params, results;
10120   NothingVector unused_thenValues{};
10121 
10122   if (!iter_.readElse(&params, &results, &unused_thenValues)) {
10123     return false;
10124   }
10125 
10126   Control& ifThenElse = controlItem(0);
10127 
10128   // See comment in endIfThenElse, below.
10129 
10130   // Exit the "then" branch.
10131 
10132   ifThenElse.deadThenBranch = deadCode_;
10133 
10134   if (deadCode_) {
10135     fr.resetStackHeight(ifThenElse.stackHeight, results);
10136     popValueStackTo(ifThenElse.stackSize);
10137   } else {
10138     MOZ_ASSERT(stk_.length() == ifThenElse.stackSize + results.length());
10139     popBlockResults(results, ifThenElse.stackHeight, ContinuationKind::Jump);
10140     freeResultRegisters(results);
10141     MOZ_ASSERT(!ifThenElse.deadOnArrival);
10142   }
10143 
10144   if (!deadCode_) {
10145     masm.jump(&ifThenElse.label);
10146   }
10147 
10148   if (ifThenElse.otherLabel.used()) {
10149     masm.bind(&ifThenElse.otherLabel);
10150   }
10151 
10152   // Reset to the "else" branch.
10153 
10154   if (!deadCode_) {
10155     ifThenElse.bceSafeOnExit &= bceSafe_;
10156   }
10157 
10158   deadCode_ = ifThenElse.deadOnArrival;
10159   bceSafe_ = ifThenElse.bceSafeOnEntry;
10160 
10161   fr.resetStackHeight(ifThenElse.stackHeight, params);
10162 
10163   if (!deadCode_) {
10164     captureResultRegisters(params);
10165     if (!pushBlockResults(params)) {
10166       return false;
10167     }
10168   }
10169 
10170   return true;
10171 }
10172 
endIfThenElse(ResultType type)10173 bool BaseCompiler::endIfThenElse(ResultType type) {
10174   Control& ifThenElse = controlItem();
10175 
10176   // The expression type is not a reliable guide to what we'll find
10177   // on the stack, we could have (if E (i32.const 1) (unreachable))
10178   // in which case the "else" arm is AnyType but the type of the
10179   // full expression is I32.  So restore whatever's there, not what
10180   // we want to find there.  The "then" arm has the same constraint.
10181 
10182   if (deadCode_) {
10183     // "then" arm does not fall through; reset stack.
10184     fr.resetStackHeight(ifThenElse.stackHeight, type);
10185     popValueStackTo(ifThenElse.stackSize);
10186   } else {
10187     MOZ_ASSERT(stk_.length() == ifThenElse.stackSize + type.length());
10188     // Assume we have a control join, so place results in block result
10189     // allocations.
10190     popBlockResults(type, ifThenElse.stackHeight,
10191                     ContinuationKind::Fallthrough);
10192     ifThenElse.bceSafeOnExit &= bceSafe_;
10193     MOZ_ASSERT(!ifThenElse.deadOnArrival);
10194   }
10195 
10196   if (ifThenElse.label.used()) {
10197     masm.bind(&ifThenElse.label);
10198   }
10199 
10200   bool joinLive =
10201       !ifThenElse.deadOnArrival &&
10202       (!ifThenElse.deadThenBranch || !deadCode_ || ifThenElse.label.bound());
10203 
10204   if (joinLive) {
10205     // No values were provided by the "then" path, but capture the values
10206     // provided by the "else" path.
10207     if (deadCode_) {
10208       captureResultRegisters(type);
10209     }
10210     deadCode_ = false;
10211   }
10212 
10213   bceSafe_ = ifThenElse.bceSafeOnExit;
10214 
10215   if (!deadCode_) {
10216     if (!pushBlockResults(type)) {
10217       return false;
10218     }
10219   }
10220 
10221   return true;
10222 }
10223 
emitEnd()10224 bool BaseCompiler::emitEnd() {
10225   LabelKind kind;
10226   ResultType type;
10227   NothingVector unused_values{};
10228   if (!iter_.readEnd(&kind, &type, &unused_values, &unused_values)) {
10229     return false;
10230   }
10231 
10232   switch (kind) {
10233     case LabelKind::Body:
10234       if (!endBlock(type)) {
10235         return false;
10236       }
10237       doReturn(ContinuationKind::Fallthrough);
10238       // This is emitted here after `doReturn` to avoid being executed in the
10239       // normal return path of a function, and instead only when a `delegate`
10240       // jumps to it.
10241 #ifdef ENABLE_WASM_EXCEPTIONS
10242       if (!emitBodyDelegateThrowPad()) {
10243         return false;
10244       }
10245 #endif
10246       iter_.popEnd();
10247       MOZ_ASSERT(iter_.controlStackEmpty());
10248       return iter_.endFunction(iter_.end());
10249     case LabelKind::Block:
10250       if (!endBlock(type)) {
10251         return false;
10252       }
10253       break;
10254     case LabelKind::Loop:
10255       // The end of a loop isn't a branch target, so we can just leave its
10256       // results on the expression stack to be consumed by the outer block.
10257       break;
10258     case LabelKind::Then:
10259       if (!endIfThen(type)) {
10260         return false;
10261       }
10262       break;
10263     case LabelKind::Else:
10264       if (!endIfThenElse(type)) {
10265         return false;
10266       }
10267       break;
10268 #ifdef ENABLE_WASM_EXCEPTIONS
10269     case LabelKind::Try:
10270     case LabelKind::Catch:
10271     case LabelKind::CatchAll:
10272       if (!endTryCatch(type)) {
10273         return false;
10274       }
10275       break;
10276 #endif
10277   }
10278 
10279   iter_.popEnd();
10280 
10281   return true;
10282 }
10283 
emitBr()10284 bool BaseCompiler::emitBr() {
10285   uint32_t relativeDepth;
10286   ResultType type;
10287   NothingVector unused_values{};
10288   if (!iter_.readBr(&relativeDepth, &type, &unused_values)) {
10289     return false;
10290   }
10291 
10292   if (deadCode_) {
10293     return true;
10294   }
10295 
10296   Control& target = controlItem(relativeDepth);
10297   target.bceSafeOnExit &= bceSafe_;
10298 
10299   // Save any values in the designated join registers, as if the target block
10300   // returned normally.
10301 
10302   popBlockResults(type, target.stackHeight, ContinuationKind::Jump);
10303   masm.jump(&target.label);
10304 
10305   // The registers holding the join values are free for the remainder of this
10306   // block.
10307 
10308   freeResultRegisters(type);
10309 
10310   deadCode_ = true;
10311 
10312   return true;
10313 }
10314 
emitBrIf()10315 bool BaseCompiler::emitBrIf() {
10316   uint32_t relativeDepth;
10317   ResultType type;
10318   NothingVector unused_values{};
10319   Nothing unused_condition;
10320   if (!iter_.readBrIf(&relativeDepth, &type, &unused_values,
10321                       &unused_condition)) {
10322     return false;
10323   }
10324 
10325   if (deadCode_) {
10326     resetLatentOp();
10327     return true;
10328   }
10329 
10330   Control& target = controlItem(relativeDepth);
10331   target.bceSafeOnExit &= bceSafe_;
10332 
10333   BranchState b(&target.label, target.stackHeight, InvertBranch(false), type);
10334   emitBranchSetup(&b);
10335   return emitBranchPerform(&b);
10336 }
10337 
10338 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
emitBrOnNull()10339 bool BaseCompiler::emitBrOnNull() {
10340   MOZ_ASSERT(!hasLatentOp());
10341 
10342   uint32_t relativeDepth;
10343   ResultType type;
10344   NothingVector unused_values{};
10345   Nothing unused_condition;
10346   if (!iter_.readBrOnNull(&relativeDepth, &type, &unused_values,
10347                           &unused_condition)) {
10348     return false;
10349   }
10350 
10351   if (deadCode_) {
10352     return true;
10353   }
10354 
10355   Control& target = controlItem(relativeDepth);
10356   target.bceSafeOnExit &= bceSafe_;
10357 
10358   BranchState b(&target.label, target.stackHeight, InvertBranch(false), type);
10359   if (b.hasBlockResults()) {
10360     needResultRegisters(b.resultType);
10361   }
10362   RegRef rp = popRef();
10363   if (b.hasBlockResults()) {
10364     freeResultRegisters(b.resultType);
10365   }
10366   if (!jumpConditionalWithResults(&b, Assembler::Equal, rp,
10367                                   ImmWord(NULLREF_VALUE))) {
10368     return false;
10369   }
10370   pushRef(rp);
10371 
10372   return true;
10373 }
10374 #endif
10375 
emitBrTable()10376 bool BaseCompiler::emitBrTable() {
10377   Uint32Vector depths;
10378   uint32_t defaultDepth;
10379   ResultType branchParams;
10380   NothingVector unused_values{};
10381   Nothing unused_index;
10382   // N.B., `branchParams' gets set to the type of the default branch target.  In
10383   // the presence of subtyping, it could be that the different branch targets
10384   // have different types.  Here we rely on the assumption that the value
10385   // representations (e.g. Stk value types) of all branch target types are the
10386   // same, in the baseline compiler.  Notably, this means that all Ref types
10387   // should be represented the same.
10388   if (!iter_.readBrTable(&depths, &defaultDepth, &branchParams, &unused_values,
10389                          &unused_index)) {
10390     return false;
10391   }
10392 
10393   if (deadCode_) {
10394     return true;
10395   }
10396 
10397   // Don't use param registers for rc
10398   needIntegerResultRegisters(branchParams);
10399 
10400   // Table switch value always on top.
10401   RegI32 rc = popI32();
10402 
10403   freeIntegerResultRegisters(branchParams);
10404 
10405   StackHeight resultsBase(0);
10406   if (!topBranchParams(branchParams, &resultsBase)) {
10407     return false;
10408   }
10409 
10410   Label dispatchCode;
10411   masm.branch32(Assembler::Below, rc, Imm32(depths.length()), &dispatchCode);
10412 
10413   // This is the out-of-range stub.  rc is dead here but we don't need it.
10414 
10415   shuffleStackResultsBeforeBranch(
10416       resultsBase, controlItem(defaultDepth).stackHeight, branchParams);
10417   controlItem(defaultDepth).bceSafeOnExit &= bceSafe_;
10418   masm.jump(&controlItem(defaultDepth).label);
10419 
10420   // Emit stubs.  rc is dead in all of these but we don't need it.
10421   //
10422   // The labels in the vector are in the TempAllocator and will
10423   // be freed by and by.
10424   //
10425   // TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we
10426   // can, don't emit an intermediate stub.
10427 
10428   LabelVector stubs;
10429   if (!stubs.reserve(depths.length())) {
10430     return false;
10431   }
10432 
10433   for (uint32_t depth : depths) {
10434     stubs.infallibleEmplaceBack(NonAssertingLabel());
10435     masm.bind(&stubs.back());
10436     shuffleStackResultsBeforeBranch(resultsBase, controlItem(depth).stackHeight,
10437                                     branchParams);
10438     controlItem(depth).bceSafeOnExit &= bceSafe_;
10439     masm.jump(&controlItem(depth).label);
10440   }
10441 
10442   // Emit table.
10443 
10444   Label theTable;
10445   jumpTable(stubs, &theTable);
10446 
10447   // Emit indirect jump.  rc is live here.
10448 
10449   tableSwitch(&theTable, rc, &dispatchCode);
10450 
10451   deadCode_ = true;
10452 
10453   // Clean up.
10454 
10455   freeI32(rc);
10456   popValueStackBy(branchParams.length());
10457 
10458   return true;
10459 }
10460 
10461 #ifdef ENABLE_WASM_EXCEPTIONS
emitTry()10462 bool BaseCompiler::emitTry() {
10463   ResultType params;
10464   if (!iter_.readTry(&params)) {
10465     return false;
10466   }
10467 
10468   if (!deadCode_) {
10469     // Simplifies jumping out, but it is also necessary so that control
10470     // can re-enter the catch handler without restoring registers.
10471     sync();
10472   }
10473 
10474   initControl(controlItem(), params);
10475 
10476   if (!deadCode_) {
10477     // Be conservative for BCE due to complex control flow in try blocks.
10478     controlItem().bceSafeOnExit = 0;
10479     // Mark the beginning of the try block, the rest is filled in by catch.
10480     controlItem().tryNoteIndex = masm.wasmStartTry();
10481   }
10482 
10483   return true;
10484 }
10485 
emitCatchSetup(LabelKind kind,Control & tryCatch,const ResultType & resultType)10486 void BaseCompiler::emitCatchSetup(LabelKind kind, Control& tryCatch,
10487                                   const ResultType& resultType) {
10488   // Catch ends the try or last catch, so we finish this like endIfThen.
10489   if (deadCode_) {
10490     fr.resetStackHeight(tryCatch.stackHeight, resultType);
10491     popValueStackTo(tryCatch.stackSize);
10492   } else {
10493     // If the previous block is a catch, we need to handle the extra exception
10494     // reference on the stack (for rethrow) and thus the stack size is 1 more.
10495     MOZ_ASSERT(stk_.length() == tryCatch.stackSize + resultType.length() +
10496                                     (kind == LabelKind::Try ? 0 : 1));
10497     // Try jumps to the end of the try-catch block unless a throw is done.
10498     if (kind == LabelKind::Try) {
10499       popBlockResults(resultType, tryCatch.stackHeight, ContinuationKind::Jump);
10500     } else {
10501       popCatchResults(resultType, tryCatch.stackHeight);
10502     }
10503     MOZ_ASSERT(stk_.length() == tryCatch.stackSize);
10504     freeResultRegisters(resultType);
10505     MOZ_ASSERT(!tryCatch.deadOnArrival);
10506   }
10507 
10508   // Reset to this "catch" branch.
10509   deadCode_ = tryCatch.deadOnArrival;
10510 
10511   // We use the empty result type here because catch does *not* take the
10512   // try-catch block parameters.
10513   fr.resetStackHeight(tryCatch.stackHeight, ResultType::Empty());
10514 
10515   if (deadCode_) {
10516     return;
10517   }
10518 
10519   bceSafe_ = 0;
10520 
10521   // The end of the previous try/catch jumps to the join point.
10522   masm.jump(&tryCatch.label);
10523 
10524   // Note end of try block for finding the catch block target. This needs
10525   // to happen after the stack is reset to the correct height.
10526   if (kind == LabelKind::Try) {
10527     WasmTryNoteVector& tryNotes = masm.tryNotes();
10528     WasmTryNote& tryNote = tryNotes[controlItem().tryNoteIndex];
10529     tryNote.end = masm.currentOffset();
10530   }
10531 }
10532 
emitCatch()10533 bool BaseCompiler::emitCatch() {
10534   LabelKind kind;
10535   uint32_t eventIndex;
10536   ResultType paramType, resultType;
10537   NothingVector unused_tryValues{};
10538 
10539   if (!iter_.readCatch(&kind, &eventIndex, &paramType, &resultType,
10540                        &unused_tryValues)) {
10541     return false;
10542   }
10543 
10544   Control& tryCatch = controlItem();
10545 
10546   emitCatchSetup(kind, tryCatch, resultType);
10547 
10548   if (deadCode_) {
10549     return true;
10550   }
10551 
10552   // Construct info used for the exception landing pad.
10553   CatchInfo catchInfo(eventIndex);
10554   if (!tryCatch.catchInfos.emplaceBack(catchInfo)) {
10555     return false;
10556   }
10557 
10558   masm.bind(&tryCatch.catchInfos.back().label);
10559 
10560   // Extract the arguments in the exception package and push them.
10561   const ResultType params = moduleEnv_.events[eventIndex].resultType();
10562 
10563   uint32_t refCount = 0;
10564   for (uint32_t i = 0; i < params.length(); i++) {
10565     if (params[i].isReference()) {
10566       refCount++;
10567     }
10568   }
10569 
10570   const uint32_t dataOffset =
10571       NativeObject::getFixedSlotOffset(ArrayBufferObject::DATA_SLOT);
10572 
10573   // The code in the landing pad guarantees us that the exception reference
10574   // is live in this register.
10575   RegRef exn = RegRef(WasmExceptionReg);
10576   needRef(exn);
10577   RegRef values = needRef();
10578   RegRef refs = needRef();
10579 
10580   masm.unboxObject(Address(exn, WasmRuntimeExceptionObject::offsetOfValues()),
10581                    values);
10582   masm.unboxObject(Address(exn, WasmRuntimeExceptionObject::offsetOfRefs()),
10583                    refs);
10584 
10585 #  ifdef DEBUG
10586   Label ok;
10587   RegI32 scratch = needI32();
10588   masm.load32(Address(refs, NativeObject::offsetOfFixedElements() +
10589                                 ObjectElements::offsetOfLength()),
10590               scratch);
10591   masm.branch32(Assembler::Equal, scratch, Imm32(refCount), &ok);
10592   masm.assumeUnreachable("Array length should be equal to exn ref count.");
10593   masm.bind(&ok);
10594   freeI32(scratch);
10595 #  endif
10596   masm.loadPtr(Address(refs, NativeObject::offsetOfElements()), refs);
10597 
10598   // This reference is pushed onto the stack because a potential rethrow
10599   // may need to access it. It is always popped at the end of the block.
10600   pushRef(exn);
10601 
10602   masm.loadPtr(Address(values, dataOffset), values);
10603   size_t argOffset = 0;
10604   // The ref values have been pushed into the ArrayObject in a stacklike
10605   // fashion so we need to load them starting from the last element.
10606   int32_t refIndex = refCount - 1;
10607   for (uint32_t i = 0; i < params.length(); i++) {
10608     switch (params[i].kind()) {
10609       case ValType::I32: {
10610         RegI32 reg = needI32();
10611         masm.load32(Address(values, argOffset), reg);
10612         pushI32(reg);
10613         break;
10614       }
10615       case ValType::I64: {
10616         RegI64 reg = needI64();
10617         masm.load64(Address(values, argOffset), reg);
10618         pushI64(reg);
10619         break;
10620       }
10621       case ValType::F32: {
10622         RegF32 reg = needF32();
10623         masm.loadFloat32(Address(values, argOffset), reg);
10624         pushF32(reg);
10625         break;
10626       }
10627       case ValType::F64: {
10628         RegF64 reg = needF64();
10629         masm.loadDouble(Address(values, argOffset), reg);
10630         pushF64(reg);
10631         break;
10632       }
10633       case ValType::V128: {
10634 #  ifdef ENABLE_WASM_SIMD
10635         RegV128 reg = needV128();
10636         masm.loadUnalignedSimd128(Address(values, argOffset), reg);
10637         pushV128(reg);
10638         break;
10639 #  else
10640         MOZ_CRASH("No SIMD support");
10641 #  endif
10642       }
10643       case ValType::Rtt:
10644       case ValType::Ref: {
10645         // TODO/AnyRef-boxing: With boxed immediates and strings, this may need
10646         // to handle other kinds of values.
10647         ASSERT_ANYREF_IS_JSOBJECT;
10648 
10649         RegRef reg = needRef();
10650         NativeObject::elementsSizeMustNotOverflow();
10651         uint32_t offset = refIndex * sizeof(Value);
10652         masm.unboxObjectOrNull(Address(refs, offset), reg);
10653         pushRef(reg);
10654         refIndex--;
10655         break;
10656       }
10657     }
10658     if (!params[i].isReference()) {
10659       argOffset += SizeOf(params[i]);
10660     }
10661   }
10662   MOZ_ASSERT(refIndex == -1);
10663   freeRef(values);
10664   freeRef(refs);
10665 
10666   return true;
10667 }
10668 
emitCatchAll()10669 bool BaseCompiler::emitCatchAll() {
10670   LabelKind kind;
10671   ResultType paramType, resultType;
10672   NothingVector unused_tryValues{};
10673 
10674   if (!iter_.readCatchAll(&kind, &paramType, &resultType, &unused_tryValues)) {
10675     return false;
10676   }
10677 
10678   Control& tryCatch = controlItem();
10679 
10680   emitCatchSetup(kind, tryCatch, resultType);
10681 
10682   if (deadCode_) {
10683     return true;
10684   }
10685 
10686   CatchInfo catchInfo(CatchInfo::CATCH_ALL_INDEX);
10687   if (!tryCatch.catchInfos.emplaceBack(catchInfo)) {
10688     return false;
10689   }
10690 
10691   masm.bind(&tryCatch.catchInfos.back().label);
10692 
10693   // The code in the landing pad guarantees us that the exception reference
10694   // is live in this register.
10695   RegRef exn = RegRef(WasmExceptionReg);
10696   needRef(exn);
10697   // This reference is pushed onto the stack because a potential rethrow
10698   // may need to access it. It is always popped at the end of the block.
10699   pushRef(exn);
10700 
10701   return true;
10702 }
10703 
emitBodyDelegateThrowPad()10704 bool BaseCompiler::emitBodyDelegateThrowPad() {
10705   Control& block = controlItem();
10706 
10707   // Only emit a landing pad if a `delegate` has generated a jump to here.
10708   if (block.otherLabel.used()) {
10709     StackHeight savedHeight = fr.stackHeight();
10710     fr.setStackHeight(block.stackHeight);
10711     masm.bind(&block.otherLabel);
10712 
10713     // We can assume this is live because `delegate` received it from a throw.
10714     RegRef exn = RegRef(WasmExceptionReg);
10715     needRef(exn);
10716     if (!throwFrom(exn, readCallSiteLineOrBytecode())) {
10717       return false;
10718     }
10719     fr.setStackHeight(savedHeight);
10720   }
10721 
10722   return true;
10723 }
10724 
emitDelegate()10725 bool BaseCompiler::emitDelegate() {
10726   uint32_t relativeDepth;
10727   ResultType resultType;
10728   NothingVector unused_tryValues{};
10729 
10730   if (!iter_.readDelegate(&relativeDepth, &resultType, &unused_tryValues)) {
10731     return false;
10732   }
10733 
10734   Control& tryDelegate = controlItem();
10735   Control& target = controlItem(relativeDepth);
10736 
10737   // End the try branch like a plain catch block without exception ref handling.
10738   if (deadCode_) {
10739     fr.resetStackHeight(tryDelegate.stackHeight, resultType);
10740     popValueStackTo(tryDelegate.stackSize);
10741   } else {
10742     MOZ_ASSERT(stk_.length() == tryDelegate.stackSize + resultType.length());
10743     popBlockResults(resultType, tryDelegate.stackHeight,
10744                     ContinuationKind::Jump);
10745     freeResultRegisters(resultType);
10746     masm.jump(&tryDelegate.label);
10747     MOZ_ASSERT(!tryDelegate.deadOnArrival);
10748   }
10749 
10750   deadCode_ = tryDelegate.deadOnArrival;
10751 
10752   if (deadCode_) {
10753     return true;
10754   }
10755 
10756   // Create an exception landing pad that immediately branches to the landing
10757   // pad of the delegated try block.
10758   masm.bind(&tryDelegate.otherLabel);
10759 
10760   StackHeight savedHeight = fr.stackHeight();
10761   fr.setStackHeight(tryDelegate.stackHeight);
10762 
10763   WasmTryNoteVector& tryNotes = masm.tryNotes();
10764   WasmTryNote& tryNote = tryNotes[controlItem().tryNoteIndex];
10765   tryNote.end = masm.currentOffset();
10766   tryNote.entryPoint = tryNote.end;
10767   tryNote.framePushed = masm.framePushed();
10768 
10769   masm.jump(&target.otherLabel);
10770 
10771   fr.setStackHeight(savedHeight);
10772 
10773   // Where the try branch jumps to, if it's not dead.
10774   if (tryDelegate.label.used()) {
10775     masm.bind(&tryDelegate.label);
10776   }
10777 
10778   captureResultRegisters(resultType);
10779   bceSafe_ = tryDelegate.bceSafeOnExit;
10780 
10781   return pushBlockResults(resultType);
10782 }
10783 
endTryCatch(ResultType type)10784 bool BaseCompiler::endTryCatch(ResultType type) {
10785   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
10786 
10787   Control& tryCatch = controlItem();
10788   LabelKind tryKind = iter_.controlKind(0);
10789 
10790   if (deadCode_) {
10791     fr.resetStackHeight(tryCatch.stackHeight, type);
10792     popValueStackTo(tryCatch.stackSize);
10793   } else {
10794     // If the previous block is a catch, we must handle the extra exception
10795     // reference on the stack (for rethrow) and thus the stack size is 1 more.
10796     MOZ_ASSERT(stk_.length() == tryCatch.stackSize + type.length() +
10797                                     (tryKind == LabelKind::Try ? 0 : 1));
10798     // Assume we have a control join, so place results in block result
10799     // allocations and also handle the implicit exception reference if needed.
10800     if (tryKind == LabelKind::Try) {
10801       popBlockResults(type, tryCatch.stackHeight, ContinuationKind::Jump);
10802     } else {
10803       popCatchResults(type, tryCatch.stackHeight);
10804     }
10805     MOZ_ASSERT(stk_.length() == tryCatch.stackSize);
10806     // Since we will emit a landing pad after this and jump over it to get to
10807     // the control join, we free these here and re-capture at the join.
10808     freeResultRegisters(type);
10809     masm.jump(&tryCatch.label);
10810     MOZ_ASSERT(!tryCatch.bceSafeOnExit);
10811     MOZ_ASSERT(!tryCatch.deadOnArrival);
10812   }
10813 
10814   deadCode_ = tryCatch.deadOnArrival;
10815 
10816   if (deadCode_) {
10817     return true;
10818   }
10819 
10820   // Create landing pad for all catch handlers in this block.
10821   // When used for a catchless try block, this will generate a landing pad
10822   // with no handlers and only the fall-back rethrow.
10823   masm.bind(&tryCatch.otherLabel);
10824 
10825   // The stack height also needs to be set not for a block result, but for the
10826   // entry to the exception handlers. This is reset again below for the join.
10827   StackHeight prePadHeight = fr.stackHeight();
10828   fr.setStackHeight(tryCatch.stackHeight);
10829 
10830   WasmTryNoteVector& tryNotes = masm.tryNotes();
10831   WasmTryNote& tryNote = tryNotes[controlItem().tryNoteIndex];
10832   tryNote.entryPoint = masm.currentOffset();
10833   tryNote.framePushed = masm.framePushed();
10834 
10835   // If we are in a catchless try block, then there were no catch blocks to
10836   // mark the end of the try note, so we need to end it here.
10837   if (tryKind == LabelKind::Try) {
10838     tryNote.end = tryNote.entryPoint;
10839   }
10840 
10841   RegRef exn = RegRef(WasmExceptionReg);
10842   needRef(exn);
10843 
10844   // Explicitly restore the tls data in case the throw was across instances.
10845   fr.loadTlsPtr(WasmTlsReg);
10846   masm.loadWasmPinnedRegsFromTls();
10847   RegRef scratch = needRef();
10848   RegRef scratch2 = needRef();
10849   masm.switchToWasmTlsRealm(scratch, scratch2);
10850   freeRef(scratch2);
10851 
10852   // Make sure that the exception pointer is saved across the call.
10853   masm.movePtr(exn, scratch);
10854   pushRef(exn);
10855   pushRef(scratch);
10856 
10857   if (!emitInstanceCall(lineOrBytecode, SASigGetLocalExceptionIndex)) {
10858     return false;
10859   }
10860 
10861   // Prevent conflict with exn register when popping this result.
10862   needRef(exn);
10863   RegI32 index = popI32();
10864   freeRef(exn);
10865 
10866   // Ensure that the exception is materialized before branching.
10867   exn = popRef(RegRef(WasmExceptionReg));
10868 
10869   bool hasCatchAll = false;
10870   for (CatchInfo& info : tryCatch.catchInfos) {
10871     if (info.eventIndex != CatchInfo::CATCH_ALL_INDEX) {
10872       MOZ_ASSERT(!hasCatchAll);
10873       masm.branch32(Assembler::Equal, index, Imm32(info.eventIndex),
10874                     &info.label);
10875     } else {
10876       masm.jump(&info.label);
10877       hasCatchAll = true;
10878       // `catch_all` must be the last clause and we won't call throwFrom
10879       // below due to the catch_all, so we can free exn here.
10880       freeRef(exn);
10881     }
10882   }
10883   freeI32(index);
10884 
10885   // If none of the tag checks succeed and there is no catch_all,
10886   // then we rethrow the exception.
10887   if (!hasCatchAll && !throwFrom(exn, lineOrBytecode)) {
10888     return false;
10889   }
10890 
10891   // Reset stack height for join.
10892   fr.setStackHeight(prePadHeight);
10893 
10894   // Create join point.
10895   if (tryCatch.label.used()) {
10896     masm.bind(&tryCatch.label);
10897   }
10898 
10899   captureResultRegisters(type);
10900   deadCode_ = tryCatch.deadOnArrival;
10901   bceSafe_ = tryCatch.bceSafeOnExit;
10902 
10903   return pushBlockResults(type);
10904 }
10905 
emitThrow()10906 bool BaseCompiler::emitThrow() {
10907   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
10908   uint32_t exnIndex;
10909   NothingVector unused_argValues{};
10910 
10911   if (!iter_.readThrow(&exnIndex, &unused_argValues)) {
10912     return false;
10913   }
10914 
10915   if (deadCode_) {
10916     return true;
10917   }
10918 
10919   const ResultType& params = moduleEnv_.events[exnIndex].resultType();
10920 
10921   // Measure space we need for all the args to put in the exception.
10922   uint32_t exnBytes = 0;
10923   for (size_t i = 0; i < params.length(); i++) {
10924     if (!params[i].isReference()) {
10925       exnBytes += SizeOf(params[i]);
10926     }
10927   }
10928 
10929   // Create the new exception object that we will throw.
10930   pushI32(exnIndex);
10931   pushI32(exnBytes);
10932   if (!emitInstanceCall(lineOrBytecode, SASigExceptionNew)) {
10933     return false;
10934   }
10935 
10936   RegRef exn = popRef();
10937   // Create scratch register, to store the exception package values.
10938   RegRef scratch = needRef();
10939   const uint32_t dataOffset =
10940       NativeObject::getFixedSlotOffset(ArrayBufferObject::DATA_SLOT);
10941 
10942   Address exnValuesAddress(exn, WasmRuntimeExceptionObject::offsetOfValues());
10943   masm.unboxObject(exnValuesAddress, scratch);
10944   masm.loadPtr(Address(scratch, dataOffset), scratch);
10945 
10946   size_t argOffset = exnBytes;
10947   for (int32_t i = params.length() - 1; i >= 0; i--) {
10948     if (!params[i].isReference()) {
10949       argOffset -= SizeOf(params[i]);
10950     }
10951     switch (params[i].kind()) {
10952       case ValType::I32: {
10953         RegI32 reg = popI32();
10954         masm.store32(reg, Address(scratch, argOffset));
10955         freeI32(reg);
10956         break;
10957       }
10958       case ValType::I64: {
10959         RegI64 reg = popI64();
10960         masm.store64(reg, Address(scratch, argOffset));
10961         freeI64(reg);
10962         break;
10963       }
10964       case ValType::F32: {
10965         RegF32 reg = popF32();
10966         masm.storeFloat32(reg, Address(scratch, argOffset));
10967         freeF32(reg);
10968         break;
10969       }
10970       case ValType::F64: {
10971         RegF64 reg = popF64();
10972         masm.storeDouble(reg, Address(scratch, argOffset));
10973         freeF64(reg);
10974         break;
10975       }
10976       case ValType::V128: {
10977 #  ifdef ENABLE_WASM_SIMD
10978         RegV128 reg = popV128();
10979         masm.storeUnalignedSimd128(reg, Address(scratch, argOffset));
10980         freeV128(reg);
10981         break;
10982 #  else
10983         MOZ_CRASH("No SIMD support");
10984 #  endif
10985       }
10986       case ValType::Rtt:
10987       case ValType::Ref: {
10988         RegRef refArg = popRef();
10989 
10990         // Keep exn on the stack to preserve it across the call.
10991         RegRef tmp = needRef();
10992         moveRef(exn, tmp);
10993         pushRef(tmp);
10994 
10995         // Arguments to the instance call start here.
10996         pushRef(exn);
10997         pushRef(refArg);
10998 
10999         if (!emitInstanceCall(lineOrBytecode, SASigPushRefIntoExn)) {
11000           return false;
11001         }
11002 
11003         // The call result is checked by the instance call failure handling,
11004         // so we do not need to use the result here.
11005         freeI32(popI32());
11006 
11007         exn = popRef();
11008 
11009         // Restore scratch register contents that got clobbered.
11010         masm.unboxObject(exnValuesAddress, scratch);
11011         masm.loadPtr(Address(scratch, dataOffset), scratch);
11012         break;
11013       }
11014     }
11015   }
11016   MOZ_ASSERT(argOffset == 0);
11017   freeRef(scratch);
11018 
11019   deadCode_ = true;
11020 
11021   return throwFrom(exn, lineOrBytecode);
11022 }
11023 
emitRethrow()11024 bool BaseCompiler::emitRethrow() {
11025   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
11026 
11027   uint32_t relativeDepth;
11028   if (!iter_.readRethrow(&relativeDepth)) {
11029     return false;
11030   }
11031 
11032   if (deadCode_) {
11033     return true;
11034   }
11035 
11036   Control& tryCatch = controlItem(relativeDepth);
11037   RegRef exn = needRef();
11038   dupRefAt(tryCatch.stackSize, exn);
11039 
11040   return throwFrom(exn, lineOrBytecode);
11041 }
11042 #endif
11043 
emitDrop()11044 bool BaseCompiler::emitDrop() {
11045   if (!iter_.readDrop()) {
11046     return false;
11047   }
11048 
11049   if (deadCode_) {
11050     return true;
11051   }
11052 
11053   dropValue();
11054   return true;
11055 }
11056 
doReturn(ContinuationKind kind)11057 void BaseCompiler::doReturn(ContinuationKind kind) {
11058   if (deadCode_) {
11059     return;
11060   }
11061 
11062   StackHeight height = controlOutermost().stackHeight;
11063   ResultType type = ResultType::Vector(funcType().results());
11064   popBlockResults(type, height, kind);
11065   masm.jump(&returnLabel_);
11066   freeResultRegisters(type);
11067 }
11068 
emitReturn()11069 bool BaseCompiler::emitReturn() {
11070   NothingVector unused_values{};
11071   if (!iter_.readReturn(&unused_values)) {
11072     return false;
11073   }
11074 
11075   if (deadCode_) {
11076     return true;
11077   }
11078 
11079   doReturn(ContinuationKind::Jump);
11080   deadCode_ = true;
11081 
11082   return true;
11083 }
11084 
emitCallArgs(const ValTypeVector & argTypes,const StackResultsLoc & results,FunctionCall * baselineCall,CalleeOnStack calleeOnStack)11085 bool BaseCompiler::emitCallArgs(const ValTypeVector& argTypes,
11086                                 const StackResultsLoc& results,
11087                                 FunctionCall* baselineCall,
11088                                 CalleeOnStack calleeOnStack) {
11089   MOZ_ASSERT(!deadCode_);
11090 
11091   ArgTypeVector args(argTypes, results.stackResults());
11092   uint32_t naturalArgCount = argTypes.length();
11093   uint32_t abiArgCount = args.lengthWithStackResults();
11094   startCallArgs(StackArgAreaSizeUnaligned(args), baselineCall);
11095 
11096   // Args are deeper on the stack than the stack result area, if any.
11097   size_t argsDepth = results.count();
11098   // They're deeper than the callee too, for callIndirect.
11099   if (calleeOnStack == CalleeOnStack::True) {
11100     argsDepth++;
11101   }
11102 
11103   for (size_t i = 0; i < abiArgCount; ++i) {
11104     if (args.isNaturalArg(i)) {
11105       size_t naturalIndex = args.naturalIndex(i);
11106       size_t stackIndex = naturalArgCount - 1 - naturalIndex + argsDepth;
11107       passArg(argTypes[naturalIndex], peek(stackIndex), baselineCall);
11108     } else {
11109       // The synthetic stack result area pointer.
11110       ABIArg argLoc = baselineCall->abi.next(MIRType::Pointer);
11111       if (argLoc.kind() == ABIArg::Stack) {
11112         ScratchPtr scratch(*this);
11113         fr.computeOutgoingStackResultAreaPtr(results, scratch);
11114         masm.storePtr(scratch, Address(masm.getStackPointer(),
11115                                        argLoc.offsetFromArgBase()));
11116       } else {
11117         fr.computeOutgoingStackResultAreaPtr(results, RegPtr(argLoc.gpr()));
11118       }
11119     }
11120   }
11121 
11122   fr.loadTlsPtr(WasmTlsReg);
11123   return true;
11124 }
11125 
pushReturnValueOfCall(const FunctionCall & call,MIRType type)11126 void BaseCompiler::pushReturnValueOfCall(const FunctionCall& call,
11127                                          MIRType type) {
11128   switch (type) {
11129     case MIRType::Int32: {
11130       RegI32 rv = captureReturnedI32();
11131       pushI32(rv);
11132       break;
11133     }
11134     case MIRType::Int64: {
11135       RegI64 rv = captureReturnedI64();
11136       pushI64(rv);
11137       break;
11138     }
11139     case MIRType::Float32: {
11140       RegF32 rv = captureReturnedF32(call);
11141       pushF32(rv);
11142       break;
11143     }
11144     case MIRType::Double: {
11145       RegF64 rv = captureReturnedF64(call);
11146       pushF64(rv);
11147       break;
11148     }
11149 #ifdef ENABLE_WASM_SIMD
11150     case MIRType::Simd128: {
11151       RegV128 rv = captureReturnedV128(call);
11152       pushV128(rv);
11153       break;
11154     }
11155 #endif
11156     case MIRType::RefOrNull: {
11157       RegRef rv = captureReturnedRef();
11158       pushRef(rv);
11159       break;
11160     }
11161     default:
11162       // In particular, passing |type| as MIRType::Void or MIRType::Pointer to
11163       // this function is an error.
11164       MOZ_CRASH("Function return type");
11165   }
11166 }
11167 
pushStackResultsForCall(const ResultType & type,RegPtr temp,StackResultsLoc * loc)11168 bool BaseCompiler::pushStackResultsForCall(const ResultType& type, RegPtr temp,
11169                                            StackResultsLoc* loc) {
11170   if (!ABIResultIter::HasStackResults(type)) {
11171     return true;
11172   }
11173 
11174   // This method is the only one in the class that can increase stk_.length() by
11175   // an unbounded amount, so it's the only one that requires an allocation.
11176   // (The general case is handled in emitBody.)
11177   if (!stk_.reserve(stk_.length() + type.length())) {
11178     return false;
11179   }
11180 
11181   // Measure stack results.
11182   ABIResultIter i(type);
11183   size_t count = 0;
11184   for (; !i.done(); i.next()) {
11185     if (i.cur().onStack()) {
11186       count++;
11187     }
11188   }
11189   uint32_t bytes = i.stackBytesConsumedSoFar();
11190 
11191   // Reserve space for the stack results.
11192   StackHeight resultsBase = fr.stackHeight();
11193   uint32_t height = fr.prepareStackResultArea(resultsBase, bytes);
11194 
11195   // Push Stk values onto the value stack, and zero out Ref values.
11196   for (i.switchToPrev(); !i.done(); i.prev()) {
11197     const ABIResult& result = i.cur();
11198     if (result.onStack()) {
11199       Stk v = captureStackResult(result, resultsBase, bytes);
11200       push(v);
11201       if (v.kind() == Stk::MemRef) {
11202         stackMapGenerator_.memRefsOnStk++;
11203         fr.storeImmediatePtrToStack(intptr_t(0), v.offs(), temp);
11204       }
11205     }
11206   }
11207 
11208   *loc = StackResultsLoc(bytes, count, height);
11209 
11210   return true;
11211 }
11212 
11213 // After a call, some results may be written to the stack result locations that
11214 // are pushed on the machine stack after any stack args.  If there are stack
11215 // args and stack results, these results need to be shuffled down, as the args
11216 // are "consumed" by the call.
popStackResultsAfterCall(const StackResultsLoc & results,uint32_t stackArgBytes)11217 void BaseCompiler::popStackResultsAfterCall(const StackResultsLoc& results,
11218                                             uint32_t stackArgBytes) {
11219   if (results.bytes() != 0) {
11220     popValueStackBy(results.count());
11221     if (stackArgBytes != 0) {
11222       uint32_t srcHeight = results.height();
11223       MOZ_ASSERT(srcHeight >= stackArgBytes + results.bytes());
11224       uint32_t destHeight = srcHeight - stackArgBytes;
11225 
11226       fr.shuffleStackResultsTowardFP(srcHeight, destHeight, results.bytes(),
11227                                      ABINonArgReturnVolatileReg);
11228     }
11229   }
11230 }
11231 
11232 // For now, always sync() at the beginning of the call to easily save live
11233 // values.
11234 //
11235 // TODO / OPTIMIZE (Bug 1316806): We may be able to avoid a full sync(), since
11236 // all we want is to save live registers that won't be saved by the callee or
11237 // that we need for outgoing args - we don't need to sync the locals.  We can
11238 // just push the necessary registers, it'll be like a lightweight sync.
11239 //
11240 // Even some of the pushing may be unnecessary if the registers will be consumed
11241 // by the call, because then what we want is parallel assignment to the argument
11242 // registers or onto the stack for outgoing arguments.  A sync() is just
11243 // simpler.
11244 
emitCall()11245 bool BaseCompiler::emitCall() {
11246   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
11247 
11248   uint32_t funcIndex;
11249   NothingVector args_{};
11250   if (!iter_.readCall(&funcIndex, &args_)) {
11251     return false;
11252   }
11253 
11254   if (deadCode_) {
11255     return true;
11256   }
11257 
11258   sync();
11259 
11260   const FuncType& funcType = *moduleEnv_.funcs[funcIndex].type;
11261   bool import = moduleEnv_.funcIsImport(funcIndex);
11262 
11263   uint32_t numArgs = funcType.args().length();
11264   size_t stackArgBytes = stackConsumed(numArgs);
11265 
11266   ResultType resultType(ResultType::Vector(funcType.results()));
11267   StackResultsLoc results;
11268   if (!pushStackResultsForCall(resultType, RegPtr(ABINonArgReg0), &results)) {
11269     return false;
11270   }
11271 
11272   FunctionCall baselineCall(lineOrBytecode);
11273   beginCall(baselineCall, UseABI::Wasm,
11274             import ? InterModule::True : InterModule::False);
11275 
11276   if (!emitCallArgs(funcType.args(), results, &baselineCall,
11277                     CalleeOnStack::False)) {
11278     return false;
11279   }
11280 
11281   CodeOffset raOffset;
11282   if (import) {
11283     raOffset = callImport(moduleEnv_.funcImportGlobalDataOffsets[funcIndex],
11284                           baselineCall);
11285   } else {
11286     raOffset = callDefinition(funcIndex, baselineCall);
11287   }
11288 
11289   if (!createStackMap("emitCall", raOffset)) {
11290     return false;
11291   }
11292 
11293   popStackResultsAfterCall(results, stackArgBytes);
11294 
11295   endCall(baselineCall, stackArgBytes);
11296 
11297   popValueStackBy(numArgs);
11298 
11299   captureCallResultRegisters(resultType);
11300   return pushCallResults(baselineCall, resultType, results);
11301 }
11302 
emitCallIndirect()11303 bool BaseCompiler::emitCallIndirect() {
11304   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
11305 
11306   uint32_t funcTypeIndex;
11307   uint32_t tableIndex;
11308   Nothing callee_;
11309   NothingVector args_{};
11310   if (!iter_.readCallIndirect(&funcTypeIndex, &tableIndex, &callee_, &args_)) {
11311     return false;
11312   }
11313 
11314   if (deadCode_) {
11315     return true;
11316   }
11317 
11318   sync();
11319 
11320   const FuncType& funcType = moduleEnv_.types[funcTypeIndex].funcType();
11321 
11322   // Stack: ... arg1 .. argn callee
11323 
11324   uint32_t numArgs = funcType.args().length() + 1;
11325   size_t stackArgBytes = stackConsumed(numArgs);
11326 
11327   ResultType resultType(ResultType::Vector(funcType.results()));
11328   StackResultsLoc results;
11329   if (!pushStackResultsForCall(resultType, RegPtr(ABINonArgReg0), &results)) {
11330     return false;
11331   }
11332 
11333   FunctionCall baselineCall(lineOrBytecode);
11334   beginCall(baselineCall, UseABI::Wasm, InterModule::True);
11335 
11336   if (!emitCallArgs(funcType.args(), results, &baselineCall,
11337                     CalleeOnStack::True)) {
11338     return false;
11339   }
11340 
11341   const Stk& callee = peek(results.count());
11342   CodeOffset raOffset =
11343       callIndirect(funcTypeIndex, tableIndex, callee, baselineCall);
11344   if (!createStackMap("emitCallIndirect", raOffset)) {
11345     return false;
11346   }
11347 
11348   popStackResultsAfterCall(results, stackArgBytes);
11349 
11350   endCall(baselineCall, stackArgBytes);
11351 
11352   popValueStackBy(numArgs);
11353 
11354   captureCallResultRegisters(resultType);
11355   return pushCallResults(baselineCall, resultType, results);
11356 }
11357 
emitRound(RoundingMode roundingMode,ValType operandType)11358 void BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType) {
11359   if (operandType == ValType::F32) {
11360     RegF32 f0 = popF32();
11361     roundF32(roundingMode, f0);
11362     pushF32(f0);
11363   } else if (operandType == ValType::F64) {
11364     RegF64 f0 = popF64();
11365     roundF64(roundingMode, f0);
11366     pushF64(f0);
11367   } else {
11368     MOZ_CRASH("unexpected type");
11369   }
11370 }
11371 
emitUnaryMathBuiltinCall(SymbolicAddress callee,ValType operandType)11372 bool BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee,
11373                                             ValType operandType) {
11374   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
11375 
11376   Nothing operand_;
11377   if (!iter_.readUnary(operandType, &operand_)) {
11378     return false;
11379   }
11380 
11381   if (deadCode_) {
11382     return true;
11383   }
11384 
11385   RoundingMode roundingMode;
11386   if (IsRoundingFunction(callee, &roundingMode) &&
11387       supportsRoundInstruction(roundingMode)) {
11388     emitRound(roundingMode, operandType);
11389     return true;
11390   }
11391 
11392   sync();
11393 
11394   ValTypeVector& signature = operandType == ValType::F32 ? SigF_ : SigD_;
11395   ValType retType = operandType;
11396   uint32_t numArgs = signature.length();
11397   size_t stackSpace = stackConsumed(numArgs);
11398   StackResultsLoc noStackResults;
11399 
11400   FunctionCall baselineCall(lineOrBytecode);
11401   beginCall(baselineCall, UseABI::Builtin, InterModule::False);
11402 
11403   if (!emitCallArgs(signature, noStackResults, &baselineCall,
11404                     CalleeOnStack::False)) {
11405     return false;
11406   }
11407 
11408   CodeOffset raOffset = builtinCall(callee, baselineCall);
11409   if (!createStackMap("emitUnaryMathBuiltin[..]", raOffset)) {
11410     return false;
11411   }
11412 
11413   endCall(baselineCall, stackSpace);
11414 
11415   popValueStackBy(numArgs);
11416 
11417   pushReturnValueOfCall(baselineCall, ToMIRType(retType));
11418 
11419   return true;
11420 }
11421 
11422 #ifdef RABALDR_INT_DIV_I64_CALLOUT
emitDivOrModI64BuiltinCall(SymbolicAddress callee,ValType operandType)11423 bool BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee,
11424                                               ValType operandType) {
11425   MOZ_ASSERT(operandType == ValType::I64);
11426   MOZ_ASSERT(!deadCode_);
11427 
11428   sync();
11429 
11430   needI64(specific_.abiReturnRegI64);
11431 
11432   RegI64 rhs = popI64();
11433   RegI64 srcDest = popI64ToSpecific(specific_.abiReturnRegI64);
11434 
11435   Label done;
11436 
11437   checkDivideByZeroI64(rhs);
11438 
11439   if (callee == SymbolicAddress::DivI64) {
11440     checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
11441   } else if (callee == SymbolicAddress::ModI64) {
11442     checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
11443   }
11444 
11445   masm.setupWasmABICall();
11446   masm.passABIArg(srcDest.high);
11447   masm.passABIArg(srcDest.low);
11448   masm.passABIArg(rhs.high);
11449   masm.passABIArg(rhs.low);
11450   CodeOffset raOffset = masm.callWithABI(bytecodeOffset(), callee,
11451                                          mozilla::Some(fr.getTlsPtrOffset()));
11452   if (!createStackMap("emitDivOrModI64Bui[..]", raOffset)) {
11453     return false;
11454   }
11455 
11456   masm.bind(&done);
11457 
11458   freeI64(rhs);
11459   pushI64(srcDest);
11460   return true;
11461 }
11462 #endif  // RABALDR_INT_DIV_I64_CALLOUT
11463 
11464 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
emitConvertInt64ToFloatingCallout(SymbolicAddress callee,ValType operandType,ValType resultType)11465 bool BaseCompiler::emitConvertInt64ToFloatingCallout(SymbolicAddress callee,
11466                                                      ValType operandType,
11467                                                      ValType resultType) {
11468   sync();
11469 
11470   RegI64 input = popI64();
11471 
11472   FunctionCall call(0);
11473 
11474   masm.setupWasmABICall();
11475 #  ifdef JS_PUNBOX64
11476   MOZ_CRASH("BaseCompiler platform hook: emitConvertInt64ToFloatingCallout");
11477 #  else
11478   masm.passABIArg(input.high);
11479   masm.passABIArg(input.low);
11480 #  endif
11481   CodeOffset raOffset = masm.callWithABI(
11482       bytecodeOffset(), callee, mozilla::Some(fr.getTlsPtrOffset()),
11483       resultType == ValType::F32 ? MoveOp::FLOAT32 : MoveOp::DOUBLE);
11484   if (!createStackMap("emitConvertInt64To[..]", raOffset)) {
11485     return false;
11486   }
11487 
11488   freeI64(input);
11489 
11490   if (resultType == ValType::F32) {
11491     pushF32(captureReturnedF32(call));
11492   } else {
11493     pushF64(captureReturnedF64(call));
11494   }
11495 
11496   return true;
11497 }
11498 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
11499 
11500 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
11501 // `Callee` always takes a double, so a float32 input must be converted.
emitConvertFloatingToInt64Callout(SymbolicAddress callee,ValType operandType,ValType resultType)11502 bool BaseCompiler::emitConvertFloatingToInt64Callout(SymbolicAddress callee,
11503                                                      ValType operandType,
11504                                                      ValType resultType) {
11505   RegF64 doubleInput;
11506   if (operandType == ValType::F32) {
11507     doubleInput = needF64();
11508     RegF32 input = popF32();
11509     masm.convertFloat32ToDouble(input, doubleInput);
11510     freeF32(input);
11511   } else {
11512     doubleInput = popF64();
11513   }
11514 
11515   // We may need the value after the call for the ool check.
11516   RegF64 otherReg = needF64();
11517   moveF64(doubleInput, otherReg);
11518   pushF64(otherReg);
11519 
11520   sync();
11521 
11522   FunctionCall call(0);
11523 
11524   masm.setupWasmABICall();
11525   masm.passABIArg(doubleInput, MoveOp::DOUBLE);
11526   CodeOffset raOffset = masm.callWithABI(bytecodeOffset(), callee,
11527                                          mozilla::Some(fr.getTlsPtrOffset()));
11528   if (!createStackMap("emitConvertFloatin[..]", raOffset)) {
11529     return false;
11530   }
11531 
11532   freeF64(doubleInput);
11533 
11534   RegI64 rv = captureReturnedI64();
11535 
11536   RegF64 inputVal = popF64();
11537 
11538   TruncFlags flags = 0;
11539   if (callee == SymbolicAddress::TruncateDoubleToUint64) {
11540     flags |= TRUNC_UNSIGNED;
11541   }
11542   if (callee == SymbolicAddress::SaturatingTruncateDoubleToInt64 ||
11543       callee == SymbolicAddress::SaturatingTruncateDoubleToUint64) {
11544     flags |= TRUNC_SATURATING;
11545   }
11546 
11547   // If we're saturating, the callout will always produce the final result
11548   // value. Otherwise, the callout value will return 0x8000000000000000
11549   // and we need to produce traps.
11550   OutOfLineCode* ool = nullptr;
11551   if (!(flags & TRUNC_SATURATING)) {
11552     // The OOL check just succeeds or fails, it does not generate a value.
11553     ool = addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
11554         AnyReg(inputVal), rv, flags, bytecodeOffset()));
11555     if (!ool) {
11556       return false;
11557     }
11558 
11559     masm.branch64(Assembler::Equal, rv, Imm64(0x8000000000000000),
11560                   ool->entry());
11561     masm.bind(ool->rejoin());
11562   }
11563 
11564   pushI64(rv);
11565   freeF64(inputVal);
11566 
11567   return true;
11568 }
11569 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
11570 
emitGetLocal()11571 bool BaseCompiler::emitGetLocal() {
11572   uint32_t slot;
11573   if (!iter_.readGetLocal(locals_, &slot)) {
11574     return false;
11575   }
11576 
11577   if (deadCode_) {
11578     return true;
11579   }
11580 
11581   // Local loads are pushed unresolved, ie, they may be deferred
11582   // until needed, until they may be affected by a store, or until a
11583   // sync.  This is intended to reduce register pressure.
11584 
11585   switch (locals_[slot].kind()) {
11586     case ValType::I32:
11587       pushLocalI32(slot);
11588       break;
11589     case ValType::I64:
11590       pushLocalI64(slot);
11591       break;
11592     case ValType::V128:
11593 #ifdef ENABLE_WASM_SIMD
11594       pushLocalV128(slot);
11595       break;
11596 #else
11597       MOZ_CRASH("No SIMD support");
11598 #endif
11599     case ValType::F64:
11600       pushLocalF64(slot);
11601       break;
11602     case ValType::F32:
11603       pushLocalF32(slot);
11604       break;
11605     case ValType::Rtt:
11606     case ValType::Ref:
11607       pushLocalRef(slot);
11608       break;
11609   }
11610 
11611   return true;
11612 }
11613 
11614 template <bool isSetLocal>
emitSetOrTeeLocal(uint32_t slot)11615 bool BaseCompiler::emitSetOrTeeLocal(uint32_t slot) {
11616   if (deadCode_) {
11617     return true;
11618   }
11619 
11620   bceLocalIsUpdated(slot);
11621   switch (locals_[slot].kind()) {
11622     case ValType::I32: {
11623       RegI32 rv = popI32();
11624       syncLocal(slot);
11625       fr.storeLocalI32(rv, localFromSlot(slot, MIRType::Int32));
11626       if (isSetLocal) {
11627         freeI32(rv);
11628       } else {
11629         pushI32(rv);
11630       }
11631       break;
11632     }
11633     case ValType::I64: {
11634       RegI64 rv = popI64();
11635       syncLocal(slot);
11636       fr.storeLocalI64(rv, localFromSlot(slot, MIRType::Int64));
11637       if (isSetLocal) {
11638         freeI64(rv);
11639       } else {
11640         pushI64(rv);
11641       }
11642       break;
11643     }
11644     case ValType::F64: {
11645       RegF64 rv = popF64();
11646       syncLocal(slot);
11647       fr.storeLocalF64(rv, localFromSlot(slot, MIRType::Double));
11648       if (isSetLocal) {
11649         freeF64(rv);
11650       } else {
11651         pushF64(rv);
11652       }
11653       break;
11654     }
11655     case ValType::F32: {
11656       RegF32 rv = popF32();
11657       syncLocal(slot);
11658       fr.storeLocalF32(rv, localFromSlot(slot, MIRType::Float32));
11659       if (isSetLocal) {
11660         freeF32(rv);
11661       } else {
11662         pushF32(rv);
11663       }
11664       break;
11665     }
11666     case ValType::V128: {
11667 #ifdef ENABLE_WASM_SIMD
11668       RegV128 rv = popV128();
11669       syncLocal(slot);
11670       fr.storeLocalV128(rv, localFromSlot(slot, MIRType::Simd128));
11671       if (isSetLocal) {
11672         freeV128(rv);
11673       } else {
11674         pushV128(rv);
11675       }
11676       break;
11677 #else
11678       MOZ_CRASH("No SIMD support");
11679 #endif
11680     }
11681     case ValType::Rtt:
11682     case ValType::Ref: {
11683       RegRef rv = popRef();
11684       syncLocal(slot);
11685       fr.storeLocalRef(rv, localFromSlot(slot, MIRType::RefOrNull));
11686       if (isSetLocal) {
11687         freeRef(rv);
11688       } else {
11689         pushRef(rv);
11690       }
11691       break;
11692     }
11693   }
11694 
11695   return true;
11696 }
11697 
emitSetLocal()11698 bool BaseCompiler::emitSetLocal() {
11699   uint32_t slot;
11700   Nothing unused_value;
11701   if (!iter_.readSetLocal(locals_, &slot, &unused_value)) {
11702     return false;
11703   }
11704   return emitSetOrTeeLocal<true>(slot);
11705 }
11706 
emitTeeLocal()11707 bool BaseCompiler::emitTeeLocal() {
11708   uint32_t slot;
11709   Nothing unused_value;
11710   if (!iter_.readTeeLocal(locals_, &slot, &unused_value)) {
11711     return false;
11712   }
11713   return emitSetOrTeeLocal<false>(slot);
11714 }
11715 
emitGetGlobal()11716 bool BaseCompiler::emitGetGlobal() {
11717   uint32_t id;
11718   if (!iter_.readGetGlobal(&id)) {
11719     return false;
11720   }
11721 
11722   if (deadCode_) {
11723     return true;
11724   }
11725 
11726   const GlobalDesc& global = moduleEnv_.globals[id];
11727 
11728   if (global.isConstant()) {
11729     LitVal value = global.constantValue();
11730     switch (value.type().kind()) {
11731       case ValType::I32:
11732         pushI32(value.i32());
11733         break;
11734       case ValType::I64:
11735         pushI64(value.i64());
11736         break;
11737       case ValType::F32:
11738         pushF32(value.f32());
11739         break;
11740       case ValType::F64:
11741         pushF64(value.f64());
11742         break;
11743       case ValType::Ref:
11744         pushRef(intptr_t(value.ref().forCompiledCode()));
11745         break;
11746 #ifdef ENABLE_WASM_SIMD
11747       case ValType::V128:
11748         pushV128(value.v128());
11749         break;
11750 #endif
11751       default:
11752         MOZ_CRASH("Global constant type");
11753     }
11754     return true;
11755   }
11756 
11757   switch (global.type().kind()) {
11758     case ValType::I32: {
11759       RegI32 rv = needI32();
11760       ScratchI32 tmp(*this);
11761       masm.load32(addressOfGlobalVar(global, tmp), rv);
11762       pushI32(rv);
11763       break;
11764     }
11765     case ValType::I64: {
11766       RegI64 rv = needI64();
11767       ScratchI32 tmp(*this);
11768       masm.load64(addressOfGlobalVar(global, tmp), rv);
11769       pushI64(rv);
11770       break;
11771     }
11772     case ValType::F32: {
11773       RegF32 rv = needF32();
11774       ScratchI32 tmp(*this);
11775       masm.loadFloat32(addressOfGlobalVar(global, tmp), rv);
11776       pushF32(rv);
11777       break;
11778     }
11779     case ValType::F64: {
11780       RegF64 rv = needF64();
11781       ScratchI32 tmp(*this);
11782       masm.loadDouble(addressOfGlobalVar(global, tmp), rv);
11783       pushF64(rv);
11784       break;
11785     }
11786     case ValType::Ref: {
11787       RegRef rv = needRef();
11788       ScratchI32 tmp(*this);
11789       masm.loadPtr(addressOfGlobalVar(global, tmp), rv);
11790       pushRef(rv);
11791       break;
11792     }
11793 #ifdef ENABLE_WASM_SIMD
11794     case ValType::V128: {
11795       RegV128 rv = needV128();
11796       ScratchI32 tmp(*this);
11797       masm.loadUnalignedSimd128(addressOfGlobalVar(global, tmp), rv);
11798       pushV128(rv);
11799       break;
11800     }
11801 #endif
11802     default:
11803       MOZ_CRASH("Global variable type");
11804       break;
11805   }
11806   return true;
11807 }
11808 
emitSetGlobal()11809 bool BaseCompiler::emitSetGlobal() {
11810   uint32_t id;
11811   Nothing unused_value;
11812   if (!iter_.readSetGlobal(&id, &unused_value)) {
11813     return false;
11814   }
11815 
11816   if (deadCode_) {
11817     return true;
11818   }
11819 
11820   const GlobalDesc& global = moduleEnv_.globals[id];
11821 
11822   switch (global.type().kind()) {
11823     case ValType::I32: {
11824       RegI32 rv = popI32();
11825       ScratchI32 tmp(*this);
11826       masm.store32(rv, addressOfGlobalVar(global, tmp));
11827       freeI32(rv);
11828       break;
11829     }
11830     case ValType::I64: {
11831       RegI64 rv = popI64();
11832       ScratchI32 tmp(*this);
11833       masm.store64(rv, addressOfGlobalVar(global, tmp));
11834       freeI64(rv);
11835       break;
11836     }
11837     case ValType::F32: {
11838       RegF32 rv = popF32();
11839       ScratchI32 tmp(*this);
11840       masm.storeFloat32(rv, addressOfGlobalVar(global, tmp));
11841       freeF32(rv);
11842       break;
11843     }
11844     case ValType::F64: {
11845       RegF64 rv = popF64();
11846       ScratchI32 tmp(*this);
11847       masm.storeDouble(rv, addressOfGlobalVar(global, tmp));
11848       freeF64(rv);
11849       break;
11850     }
11851     case ValType::Ref: {
11852       RegPtr valueAddr(PreBarrierReg);
11853       needPtr(valueAddr);
11854       {
11855         ScratchI32 tmp(*this);
11856         masm.computeEffectiveAddress(addressOfGlobalVar(global, tmp),
11857                                      valueAddr);
11858       }
11859       RegRef rv = popRef();
11860       // emitBarrieredStore preserves rv
11861       if (!emitBarrieredStore(Nothing(), valueAddr, rv)) {
11862         return false;
11863       }
11864       freeRef(rv);
11865       break;
11866     }
11867 #ifdef ENABLE_WASM_SIMD
11868     case ValType::V128: {
11869       RegV128 rv = popV128();
11870       ScratchI32 tmp(*this);
11871       masm.storeUnalignedSimd128(rv, addressOfGlobalVar(global, tmp));
11872       freeV128(rv);
11873       break;
11874     }
11875 #endif
11876     default:
11877       MOZ_CRASH("Global variable type");
11878       break;
11879   }
11880   return true;
11881 }
11882 
11883 // Bounds check elimination.
11884 //
11885 // We perform BCE on two kinds of address expressions: on constant heap pointers
11886 // that are known to be in the heap or will be handled by the out-of-bounds trap
11887 // handler; and on local variables that have been checked in dominating code
11888 // without being updated since.
11889 //
11890 // For an access through a constant heap pointer + an offset we can eliminate
11891 // the bounds check if the sum of the address and offset is below the sum of the
11892 // minimum memory length and the offset guard length.
11893 //
11894 // For an access through a local variable + an offset we can eliminate the
11895 // bounds check if the local variable has already been checked and has not been
11896 // updated since, and the offset is less than the guard limit.
11897 //
11898 // To track locals for which we can eliminate checks we use a bit vector
11899 // bceSafe_ that has a bit set for those locals whose bounds have been checked
11900 // and which have not subsequently been set.  Initially this vector is zero.
11901 //
11902 // In straight-line code a bit is set when we perform a bounds check on an
11903 // access via the local and is reset when the variable is updated.
11904 //
11905 // In control flow, the bit vector is manipulated as follows.  Each ControlItem
11906 // has a value bceSafeOnEntry, which is the value of bceSafe_ on entry to the
11907 // item, and a value bceSafeOnExit, which is initially ~0.  On a branch (br,
11908 // brIf, brTable), we always AND the branch target's bceSafeOnExit with the
11909 // value of bceSafe_ at the branch point.  On exiting an item by falling out of
11910 // it, provided we're not in dead code, we AND the current value of bceSafe_
11911 // into the item's bceSafeOnExit.  Additional processing depends on the item
11912 // type:
11913 //
11914 //  - After a block, set bceSafe_ to the block's bceSafeOnExit.
11915 //
11916 //  - On loop entry, after pushing the ControlItem, set bceSafe_ to zero; the
11917 //    back edges would otherwise require us to iterate to a fixedpoint.
11918 //
11919 //  - After a loop, the bceSafe_ is left unchanged, because only fallthrough
11920 //    control flow will reach that point and the bceSafe_ value represents the
11921 //    correct state of the fallthrough path.
11922 //
11923 //  - Set bceSafe_ to the ControlItem's bceSafeOnEntry at both the 'then' branch
11924 //    and the 'else' branch.
11925 //
11926 //  - After an if-then-else, set bceSafe_ to the if-then-else's bceSafeOnExit.
11927 //
11928 //  - After an if-then, set bceSafe_ to the if-then's bceSafeOnExit AND'ed with
11929 //    the if-then's bceSafeOnEntry.
11930 //
11931 // Finally, when the debugger allows locals to be mutated we must disable BCE
11932 // for references via a local, by returning immediately from bceCheckLocal if
11933 // compilerEnv_.debugEnabled() is true.
11934 //
11935 //
11936 // Alignment check elimination.
11937 //
11938 // Alignment checks for atomic operations can be omitted if the pointer is a
11939 // constant and the pointer + offset is aligned.  Alignment checking that can't
11940 // be omitted can still be simplified by checking only the pointer if the offset
11941 // is aligned.
11942 //
11943 // (In addition, alignment checking of the pointer can be omitted if the pointer
11944 // has been checked in dominating code, but we don't do that yet.)
11945 
11946 // TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
11947 // code by not moving a constant address with a zero offset into a register.
11948 
popMemory32Access(MemoryAccessDesc * access,AccessCheck * check)11949 RegI32 BaseCompiler::popMemory32Access(MemoryAccessDesc* access,
11950                                        AccessCheck* check) {
11951   check->onlyPointerAlignment =
11952       (access->offset() & (access->byteSize() - 1)) == 0;
11953 
11954   int32_t addrTemp;
11955   if (popConst(&addrTemp)) {
11956     uint32_t addr = addrTemp;
11957 
11958     uint32_t offsetGuardLimit =
11959         GetMaxOffsetGuardLimit(moduleEnv_.hugeMemoryEnabled());
11960 
11961     uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
11962     uint64_t limit = moduleEnv_.memory->initialLength32() + offsetGuardLimit;
11963 
11964     check->omitBoundsCheck = ea < limit;
11965     check->omitAlignmentCheck = (ea & (access->byteSize() - 1)) == 0;
11966 
11967     // Fold the offset into the pointer if we can, as this is always
11968     // beneficial.
11969 
11970     if (ea <= UINT32_MAX) {
11971       addr = uint32_t(ea);
11972       access->clearOffset();
11973     }
11974 
11975     RegI32 r = needI32();
11976     moveImm32(int32_t(addr), r);
11977     return r;
11978   }
11979 
11980   uint32_t local;
11981   if (peekLocalI32(&local)) {
11982     bceCheckLocal(access, check, local);
11983   }
11984 
11985   return popI32();
11986 }
11987 
pushHeapBase()11988 void BaseCompiler::pushHeapBase() {
11989 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) || \
11990     defined(JS_CODEGEN_MIPS64)
11991   RegI64 heapBase = needI64();
11992   moveI64(RegI64(Register64(HeapReg)), heapBase);
11993   pushI64(heapBase);
11994 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
11995   RegI32 heapBase = needI32();
11996   moveI32(RegI32(HeapReg), heapBase);
11997   pushI32(heapBase);
11998 #elif defined(JS_CODEGEN_X86)
11999   RegI32 heapBase = needI32();
12000   fr.loadTlsPtr(heapBase);
12001   masm.loadPtr(Address(heapBase, offsetof(TlsData, memoryBase)), heapBase);
12002   pushI32(heapBase);
12003 #else
12004   MOZ_CRASH("BaseCompiler platform hook: pushHeapBase");
12005 #endif
12006 }
12007 
maybeLoadTlsForAccess(const AccessCheck & check)12008 RegI32 BaseCompiler::maybeLoadTlsForAccess(const AccessCheck& check) {
12009   RegI32 tls;
12010   if (needTlsForAccess(check)) {
12011     tls = needI32();
12012     fr.loadTlsPtr(tls);
12013   }
12014   return tls;
12015 }
12016 
maybeLoadTlsForAccess(const AccessCheck & check,RegI32 specific)12017 RegI32 BaseCompiler::maybeLoadTlsForAccess(const AccessCheck& check,
12018                                            RegI32 specific) {
12019   if (needTlsForAccess(check)) {
12020     fr.loadTlsPtr(specific);
12021     return specific;
12022   }
12023   return RegI32::Invalid();
12024 }
12025 
loadCommon(MemoryAccessDesc * access,AccessCheck check,ValType type)12026 bool BaseCompiler::loadCommon(MemoryAccessDesc* access, AccessCheck check,
12027                               ValType type) {
12028   RegI32 tls, temp1, temp2, temp3;
12029   needLoadTemps(*access, &temp1, &temp2, &temp3);
12030 
12031   switch (type.kind()) {
12032     case ValType::I32: {
12033       RegI32 rp = popMemory32Access(access, &check);
12034 #ifdef JS_CODEGEN_ARM
12035       RegI32 rv = IsUnaligned(*access) ? needI32() : rp;
12036 #else
12037       RegI32 rv = rp;
12038 #endif
12039       tls = maybeLoadTlsForAccess(check);
12040       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3)) {
12041         return false;
12042       }
12043       pushI32(rv);
12044       if (rp != rv) {
12045         freeI32(rp);
12046       }
12047       break;
12048     }
12049     case ValType::I64: {
12050       RegI64 rv;
12051       RegI32 rp;
12052 #ifdef JS_CODEGEN_X86
12053       rv = specific_.abiReturnRegI64;
12054       needI64(rv);
12055       rp = popMemory32Access(access, &check);
12056 #else
12057       rp = popMemory32Access(access, &check);
12058       rv = needI64();
12059 #endif
12060       tls = maybeLoadTlsForAccess(check);
12061       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3)) {
12062         return false;
12063       }
12064       pushI64(rv);
12065       freeI32(rp);
12066       break;
12067     }
12068     case ValType::F32: {
12069       RegI32 rp = popMemory32Access(access, &check);
12070       RegF32 rv = needF32();
12071       tls = maybeLoadTlsForAccess(check);
12072       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3)) {
12073         return false;
12074       }
12075       pushF32(rv);
12076       freeI32(rp);
12077       break;
12078     }
12079     case ValType::F64: {
12080       RegI32 rp = popMemory32Access(access, &check);
12081       RegF64 rv = needF64();
12082       tls = maybeLoadTlsForAccess(check);
12083       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3)) {
12084         return false;
12085       }
12086       pushF64(rv);
12087       freeI32(rp);
12088       break;
12089     }
12090 #ifdef ENABLE_WASM_SIMD
12091     case ValType::V128: {
12092       RegI32 rp = popMemory32Access(access, &check);
12093       RegV128 rv = needV128();
12094       tls = maybeLoadTlsForAccess(check);
12095       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3)) {
12096         return false;
12097       }
12098       pushV128(rv);
12099       freeI32(rp);
12100       break;
12101     }
12102 #endif
12103     default:
12104       MOZ_CRASH("load type");
12105       break;
12106   }
12107 
12108   maybeFree(tls);
12109   maybeFree(temp1);
12110   maybeFree(temp2);
12111   maybeFree(temp3);
12112 
12113   return true;
12114 }
12115 
emitLoad(ValType type,Scalar::Type viewType)12116 bool BaseCompiler::emitLoad(ValType type, Scalar::Type viewType) {
12117   LinearMemoryAddress<Nothing> addr;
12118   if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr)) {
12119     return false;
12120   }
12121 
12122   if (deadCode_) {
12123     return true;
12124   }
12125 
12126   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
12127   return loadCommon(&access, AccessCheck(), type);
12128 }
12129 
storeCommon(MemoryAccessDesc * access,AccessCheck check,ValType resultType)12130 bool BaseCompiler::storeCommon(MemoryAccessDesc* access, AccessCheck check,
12131                                ValType resultType) {
12132   RegI32 tls;
12133   RegI32 temp = needStoreTemp(*access, resultType);
12134 
12135   switch (resultType.kind()) {
12136     case ValType::I32: {
12137       RegI32 rv = popI32();
12138       RegI32 rp = popMemory32Access(access, &check);
12139       tls = maybeLoadTlsForAccess(check);
12140       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) {
12141         return false;
12142       }
12143       freeI32(rp);
12144       freeI32(rv);
12145       break;
12146     }
12147     case ValType::I64: {
12148       RegI64 rv = popI64();
12149       RegI32 rp = popMemory32Access(access, &check);
12150       tls = maybeLoadTlsForAccess(check);
12151       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) {
12152         return false;
12153       }
12154       freeI32(rp);
12155       freeI64(rv);
12156       break;
12157     }
12158     case ValType::F32: {
12159       RegF32 rv = popF32();
12160       RegI32 rp = popMemory32Access(access, &check);
12161       tls = maybeLoadTlsForAccess(check);
12162       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) {
12163         return false;
12164       }
12165       freeI32(rp);
12166       freeF32(rv);
12167       break;
12168     }
12169     case ValType::F64: {
12170       RegF64 rv = popF64();
12171       RegI32 rp = popMemory32Access(access, &check);
12172       tls = maybeLoadTlsForAccess(check);
12173       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) {
12174         return false;
12175       }
12176       freeI32(rp);
12177       freeF64(rv);
12178       break;
12179     }
12180 #ifdef ENABLE_WASM_SIMD
12181     case ValType::V128: {
12182       RegV128 rv = popV128();
12183       RegI32 rp = popMemory32Access(access, &check);
12184       tls = maybeLoadTlsForAccess(check);
12185       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) {
12186         return false;
12187       }
12188       freeI32(rp);
12189       freeV128(rv);
12190       break;
12191     }
12192 #endif
12193     default:
12194       MOZ_CRASH("store type");
12195       break;
12196   }
12197 
12198   maybeFree(tls);
12199   maybeFree(temp);
12200 
12201   return true;
12202 }
12203 
emitStore(ValType resultType,Scalar::Type viewType)12204 bool BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType) {
12205   LinearMemoryAddress<Nothing> addr;
12206   Nothing unused_value;
12207   if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr,
12208                        &unused_value)) {
12209     return false;
12210   }
12211 
12212   if (deadCode_) {
12213     return true;
12214   }
12215 
12216   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
12217   return storeCommon(&access, AccessCheck(), resultType);
12218 }
12219 
emitSelect(bool typed)12220 bool BaseCompiler::emitSelect(bool typed) {
12221   StackType type;
12222   Nothing unused_trueValue;
12223   Nothing unused_falseValue;
12224   Nothing unused_condition;
12225   if (!iter_.readSelect(typed, &type, &unused_trueValue, &unused_falseValue,
12226                         &unused_condition)) {
12227     return false;
12228   }
12229 
12230   if (deadCode_) {
12231     resetLatentOp();
12232     return true;
12233   }
12234 
12235   // I32 condition on top, then false, then true.
12236 
12237   Label done;
12238   BranchState b(&done);
12239   emitBranchSetup(&b);
12240 
12241   switch (type.valType().kind()) {
12242     case ValType::I32: {
12243       RegI32 r, rs;
12244       pop2xI32(&r, &rs);
12245       if (!emitBranchPerform(&b)) {
12246         return false;
12247       }
12248       moveI32(rs, r);
12249       masm.bind(&done);
12250       freeI32(rs);
12251       pushI32(r);
12252       break;
12253     }
12254     case ValType::I64: {
12255 #ifdef JS_CODEGEN_X86
12256       // There may be as many as four Int64 values in registers at a time: two
12257       // for the latent branch operands, and two for the true/false values we
12258       // normally pop before executing the branch.  On x86 this is one value
12259       // too many, so we need to generate more complicated code here, and for
12260       // simplicity's sake we do so even if the branch operands are not Int64.
12261       // However, the resulting control flow diamond is complicated since the
12262       // arms of the diamond will have to stay synchronized with respect to
12263       // their evaluation stack and regalloc state.  To simplify further, we
12264       // use a double branch and a temporary boolean value for now.
12265       RegI32 temp = needI32();
12266       moveImm32(0, temp);
12267       if (!emitBranchPerform(&b)) {
12268         return false;
12269       }
12270       moveImm32(1, temp);
12271       masm.bind(&done);
12272 
12273       Label trueValue;
12274       RegI64 r, rs;
12275       pop2xI64(&r, &rs);
12276       masm.branch32(Assembler::Equal, temp, Imm32(0), &trueValue);
12277       moveI64(rs, r);
12278       masm.bind(&trueValue);
12279       freeI32(temp);
12280       freeI64(rs);
12281       pushI64(r);
12282 #else
12283       RegI64 r, rs;
12284       pop2xI64(&r, &rs);
12285       if (!emitBranchPerform(&b)) {
12286         return false;
12287       }
12288       moveI64(rs, r);
12289       masm.bind(&done);
12290       freeI64(rs);
12291       pushI64(r);
12292 #endif
12293       break;
12294     }
12295     case ValType::F32: {
12296       RegF32 r, rs;
12297       pop2xF32(&r, &rs);
12298       if (!emitBranchPerform(&b)) {
12299         return false;
12300       }
12301       moveF32(rs, r);
12302       masm.bind(&done);
12303       freeF32(rs);
12304       pushF32(r);
12305       break;
12306     }
12307     case ValType::F64: {
12308       RegF64 r, rs;
12309       pop2xF64(&r, &rs);
12310       if (!emitBranchPerform(&b)) {
12311         return false;
12312       }
12313       moveF64(rs, r);
12314       masm.bind(&done);
12315       freeF64(rs);
12316       pushF64(r);
12317       break;
12318     }
12319 #ifdef ENABLE_WASM_SIMD
12320     case ValType::V128: {
12321       RegV128 r, rs;
12322       pop2xV128(&r, &rs);
12323       if (!emitBranchPerform(&b)) {
12324         return false;
12325       }
12326       moveV128(rs, r);
12327       masm.bind(&done);
12328       freeV128(rs);
12329       pushV128(r);
12330       break;
12331     }
12332 #endif
12333     case ValType::Ref: {
12334       RegRef r, rs;
12335       pop2xRef(&r, &rs);
12336       if (!emitBranchPerform(&b)) {
12337         return false;
12338       }
12339       moveRef(rs, r);
12340       masm.bind(&done);
12341       freeRef(rs);
12342       pushRef(r);
12343       break;
12344     }
12345     default: {
12346       MOZ_CRASH("select type");
12347     }
12348   }
12349 
12350   return true;
12351 }
12352 
emitCompareI32(Assembler::Condition compareOp,ValType compareType)12353 void BaseCompiler::emitCompareI32(Assembler::Condition compareOp,
12354                                   ValType compareType) {
12355   MOZ_ASSERT(compareType == ValType::I32);
12356 
12357   if (sniffConditionalControlCmp(compareOp, compareType)) {
12358     return;
12359   }
12360 
12361   int32_t c;
12362   if (popConst(&c)) {
12363     RegI32 r = popI32();
12364     masm.cmp32Set(compareOp, r, Imm32(c), r);
12365     pushI32(r);
12366   } else {
12367     RegI32 r, rs;
12368     pop2xI32(&r, &rs);
12369     masm.cmp32Set(compareOp, r, rs, r);
12370     freeI32(rs);
12371     pushI32(r);
12372   }
12373 }
12374 
emitCompareI64(Assembler::Condition compareOp,ValType compareType)12375 void BaseCompiler::emitCompareI64(Assembler::Condition compareOp,
12376                                   ValType compareType) {
12377   MOZ_ASSERT(compareType == ValType::I64);
12378 
12379   if (sniffConditionalControlCmp(compareOp, compareType)) {
12380     return;
12381   }
12382 
12383   RegI64 rs0, rs1;
12384   pop2xI64(&rs0, &rs1);
12385   RegI32 rd(fromI64(rs0));
12386   cmp64Set(compareOp, rs0, rs1, rd);
12387   freeI64(rs1);
12388   freeI64Except(rs0, rd);
12389   pushI32(rd);
12390 }
12391 
emitCompareF32(Assembler::DoubleCondition compareOp,ValType compareType)12392 void BaseCompiler::emitCompareF32(Assembler::DoubleCondition compareOp,
12393                                   ValType compareType) {
12394   MOZ_ASSERT(compareType == ValType::F32);
12395 
12396   if (sniffConditionalControlCmp(compareOp, compareType)) {
12397     return;
12398   }
12399 
12400   Label across;
12401   RegF32 rs0, rs1;
12402   pop2xF32(&rs0, &rs1);
12403   RegI32 rd = needI32();
12404   moveImm32(1, rd);
12405   masm.branchFloat(compareOp, rs0, rs1, &across);
12406   moveImm32(0, rd);
12407   masm.bind(&across);
12408   freeF32(rs0);
12409   freeF32(rs1);
12410   pushI32(rd);
12411 }
12412 
emitCompareF64(Assembler::DoubleCondition compareOp,ValType compareType)12413 void BaseCompiler::emitCompareF64(Assembler::DoubleCondition compareOp,
12414                                   ValType compareType) {
12415   MOZ_ASSERT(compareType == ValType::F64);
12416 
12417   if (sniffConditionalControlCmp(compareOp, compareType)) {
12418     return;
12419   }
12420 
12421   Label across;
12422   RegF64 rs0, rs1;
12423   pop2xF64(&rs0, &rs1);
12424   RegI32 rd = needI32();
12425   moveImm32(1, rd);
12426   masm.branchDouble(compareOp, rs0, rs1, &across);
12427   moveImm32(0, rd);
12428   masm.bind(&across);
12429   freeF64(rs0);
12430   freeF64(rs1);
12431   pushI32(rd);
12432 }
12433 
emitCompareRef(Assembler::Condition compareOp,ValType compareType)12434 void BaseCompiler::emitCompareRef(Assembler::Condition compareOp,
12435                                   ValType compareType) {
12436   MOZ_ASSERT(!sniffConditionalControlCmp(compareOp, compareType));
12437 
12438   RegRef rs1, rs2;
12439   pop2xRef(&rs1, &rs2);
12440   RegI32 rd = needI32();
12441   masm.cmpPtrSet(compareOp, rs1, rs2, rd);
12442   freeRef(rs1);
12443   freeRef(rs2);
12444   pushI32(rd);
12445 }
12446 
emitInstanceCall(uint32_t lineOrBytecode,const SymbolicAddressSignature & builtin)12447 bool BaseCompiler::emitInstanceCall(uint32_t lineOrBytecode,
12448                                     const SymbolicAddressSignature& builtin) {
12449   const MIRType* argTypes = builtin.argTypes;
12450   MOZ_ASSERT(argTypes[0] == MIRType::Pointer);
12451 
12452   sync();
12453 
12454   uint32_t numNonInstanceArgs = builtin.numArgs - 1 /* instance */;
12455   size_t stackSpace = stackConsumed(numNonInstanceArgs);
12456 
12457   FunctionCall baselineCall(lineOrBytecode);
12458   beginCall(baselineCall, UseABI::System, InterModule::True);
12459 
12460   ABIArg instanceArg = reservePointerArgument(&baselineCall);
12461 
12462   startCallArgs(StackArgAreaSizeUnaligned(builtin), &baselineCall);
12463   for (uint32_t i = 1; i < builtin.numArgs; i++) {
12464     ValType t;
12465     switch (argTypes[i]) {
12466       case MIRType::Int32:
12467         t = ValType::I32;
12468         break;
12469       case MIRType::Int64:
12470         t = ValType::I64;
12471         break;
12472       case MIRType::RefOrNull:
12473         t = RefType::extern_();
12474         break;
12475       case MIRType::Pointer:
12476         // Instance function args can now be uninterpreted pointers (eg, for
12477         // the cases PostBarrier and PostBarrierFilter) so we simply treat
12478         // them like the equivalently sized integer.
12479         t = ValType::hostPtr();
12480         break;
12481       default:
12482         MOZ_CRASH("Unexpected type");
12483     }
12484     passArg(t, peek(numNonInstanceArgs - i), &baselineCall);
12485   }
12486   CodeOffset raOffset =
12487       builtinInstanceMethodCall(builtin, instanceArg, baselineCall);
12488   if (!createStackMap("emitInstanceCall", raOffset)) {
12489     return false;
12490   }
12491 
12492   endCall(baselineCall, stackSpace);
12493 
12494   popValueStackBy(numNonInstanceArgs);
12495 
12496   // Note, many clients of emitInstanceCall currently assume that pushing the
12497   // result here does not destroy ReturnReg.
12498   //
12499   // Furthermore, clients assume that if builtin.retType != MIRType::None, the
12500   // callee will have returned a result and left it in ReturnReg for us to
12501   // find, and that that register will not be destroyed here (or above).
12502 
12503   // For the return type only, MIRType::None is used to indicate that the
12504   // call doesn't return a result, that is, returns a C/C++ "void".
12505 
12506   if (builtin.retType != MIRType::None) {
12507     pushReturnValueOfCall(baselineCall, builtin.retType);
12508   }
12509   return true;
12510 }
12511 
emitMemoryGrow()12512 bool BaseCompiler::emitMemoryGrow() {
12513   return emitInstanceCallOp(SASigMemoryGrow, [this]() -> bool {
12514     Nothing arg;
12515     return iter_.readMemoryGrow(&arg);
12516   });
12517 }
12518 
emitMemorySize()12519 bool BaseCompiler::emitMemorySize() {
12520   return emitInstanceCallOp(
12521       SASigMemorySize, [this]() -> bool { return iter_.readMemorySize(); });
12522 }
12523 
emitRefFunc()12524 bool BaseCompiler::emitRefFunc() {
12525   return emitInstanceCallOp<uint32_t>(SASigRefFunc,
12526                                       [this](uint32_t* funcIndex) -> bool {
12527                                         return iter_.readRefFunc(funcIndex);
12528                                       });
12529 }
12530 
emitRefNull()12531 bool BaseCompiler::emitRefNull() {
12532   RefType type;
12533   if (!iter_.readRefNull(&type)) {
12534     return false;
12535   }
12536 
12537   if (deadCode_) {
12538     return true;
12539   }
12540 
12541   pushRef(NULLREF_VALUE);
12542   return true;
12543 }
12544 
emitRefIsNull()12545 bool BaseCompiler::emitRefIsNull() {
12546   Nothing nothing;
12547   if (!iter_.readRefIsNull(&nothing)) {
12548     return false;
12549   }
12550 
12551   if (deadCode_) {
12552     return true;
12553   }
12554 
12555   RegRef r = popRef();
12556   RegI32 rd = narrowRef(r);
12557 
12558   masm.cmpPtrSet(Assembler::Equal, r, ImmWord(NULLREF_VALUE), rd);
12559   pushI32(rd);
12560   return true;
12561 }
12562 
12563 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
emitRefAsNonNull()12564 bool BaseCompiler::emitRefAsNonNull() {
12565   Nothing nothing;
12566   if (!iter_.readRefAsNonNull(&nothing)) {
12567     return false;
12568   }
12569 
12570   if (deadCode_) {
12571     return true;
12572   }
12573 
12574   RegRef rp = popRef();
12575   Label ok;
12576   masm.branchTestPtr(Assembler::NonZero, rp, rp, &ok);
12577   trap(Trap::NullPointerDereference);
12578   masm.bind(&ok);
12579   pushRef(rp);
12580 
12581   return true;
12582 }
12583 #endif
12584 
emitAtomicCmpXchg(ValType type,Scalar::Type viewType)12585 bool BaseCompiler::emitAtomicCmpXchg(ValType type, Scalar::Type viewType) {
12586   LinearMemoryAddress<Nothing> addr;
12587   Nothing unused{};
12588 
12589   if (!iter_.readAtomicCmpXchg(&addr, type, Scalar::byteSize(viewType), &unused,
12590                                &unused)) {
12591     return false;
12592   }
12593 
12594   if (deadCode_) {
12595     return true;
12596   }
12597 
12598   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset(),
12599                           Synchronization::Full());
12600 
12601   if (Scalar::byteSize(viewType) <= 4) {
12602     PopAtomicCmpXchg32Regs regs(this, type, viewType);
12603 
12604     AccessCheck check;
12605     RegI32 rp = popMemory32Access(&access, &check);
12606     RegI32 tls = maybeLoadTlsForAccess(check);
12607 
12608     auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12609     regs.atomicCmpXchg32(access, memaddr);
12610 
12611     maybeFree(tls);
12612     freeI32(rp);
12613 
12614     if (type == ValType::I64) {
12615       pushU32AsI64(regs.takeRd());
12616     } else {
12617       pushI32(regs.takeRd());
12618     }
12619 
12620     return true;
12621   }
12622 
12623   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
12624 
12625   PopAtomicCmpXchg64Regs regs(this);
12626 
12627   AccessCheck check;
12628   RegI32 rp = popMemory32Access(&access, &check);
12629 
12630 #ifdef JS_CODEGEN_X86
12631   ScratchEBX ebx(*this);
12632   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
12633   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12634   regs.atomicCmpXchg64(access, memaddr, ebx);
12635 #else
12636   RegI32 tls = maybeLoadTlsForAccess(check);
12637   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12638   regs.atomicCmpXchg64(access, memaddr);
12639   maybeFree(tls);
12640 #endif
12641 
12642   freeI32(rp);
12643 
12644   pushI64(regs.takeRd());
12645   return true;
12646 }
12647 
emitAtomicLoad(ValType type,Scalar::Type viewType)12648 bool BaseCompiler::emitAtomicLoad(ValType type, Scalar::Type viewType) {
12649   LinearMemoryAddress<Nothing> addr;
12650   if (!iter_.readAtomicLoad(&addr, type, Scalar::byteSize(viewType))) {
12651     return false;
12652   }
12653 
12654   if (deadCode_) {
12655     return true;
12656   }
12657 
12658   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset(),
12659                           Synchronization::Load());
12660 
12661   if (Scalar::byteSize(viewType) <= sizeof(void*)) {
12662     return loadCommon(&access, AccessCheck(), type);
12663   }
12664 
12665   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
12666 
12667 #if defined(JS_64BIT)
12668   MOZ_CRASH("Should not happen");
12669 #else
12670   PopAtomicLoad64Regs regs(this);
12671 
12672   AccessCheck check;
12673   RegI32 rp = popMemory32Access(&access, &check);
12674 
12675 #  ifdef JS_CODEGEN_X86
12676   ScratchEBX ebx(*this);
12677   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
12678   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12679   regs.atomicLoad64(access, memaddr, ebx);
12680 #  else
12681   RegI32 tls = maybeLoadTlsForAccess(check);
12682   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12683   regs.atomicLoad64(access, memaddr);
12684   maybeFree(tls);
12685 #  endif
12686 
12687   freeI32(rp);
12688 
12689   pushI64(regs.takeRd());
12690   return true;
12691 #endif  // JS_64BIT
12692 }
12693 
emitAtomicRMW(ValType type,Scalar::Type viewType,AtomicOp op)12694 bool BaseCompiler::emitAtomicRMW(ValType type, Scalar::Type viewType,
12695                                  AtomicOp op) {
12696   LinearMemoryAddress<Nothing> addr;
12697   Nothing unused_value;
12698   if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType),
12699                            &unused_value)) {
12700     return false;
12701   }
12702 
12703   if (deadCode_) {
12704     return true;
12705   }
12706 
12707   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset(),
12708                           Synchronization::Full());
12709 
12710   if (Scalar::byteSize(viewType) <= 4) {
12711     PopAtomicRMW32Regs regs(this, type, viewType, op);
12712 
12713     AccessCheck check;
12714     RegI32 rp = popMemory32Access(&access, &check);
12715     RegI32 tls = maybeLoadTlsForAccess(check);
12716 
12717     auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12718     regs.atomicRMW32(access, memaddr, op);
12719 
12720     maybeFree(tls);
12721     freeI32(rp);
12722 
12723     if (type == ValType::I64) {
12724       pushU32AsI64(regs.takeRd());
12725     } else {
12726       pushI32(regs.takeRd());
12727     }
12728     return true;
12729   }
12730 
12731   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
12732 
12733   PopAtomicRMW64Regs regs(this, op);
12734 
12735   AccessCheck check;
12736   RegI32 rp = popMemory32Access(&access, &check);
12737 
12738 #ifdef JS_CODEGEN_X86
12739   ScratchEBX ebx(*this);
12740   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
12741 
12742   fr.pushGPR(regs.valueHigh());
12743   fr.pushGPR(regs.valueLow());
12744   Address value(esp, 0);
12745 
12746   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12747   regs.atomicRMW64(access, memaddr, op, value, ebx);
12748 
12749   fr.popBytes(8);
12750 #else
12751   RegI32 tls = maybeLoadTlsForAccess(check);
12752   auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12753   regs.atomicRMW64(access, memaddr, op);
12754   maybeFree(tls);
12755 #endif
12756 
12757   freeI32(rp);
12758 
12759   pushI64(regs.takeRd());
12760   return true;
12761 }
12762 
emitAtomicStore(ValType type,Scalar::Type viewType)12763 bool BaseCompiler::emitAtomicStore(ValType type, Scalar::Type viewType) {
12764   LinearMemoryAddress<Nothing> addr;
12765   Nothing unused_value;
12766   if (!iter_.readAtomicStore(&addr, type, Scalar::byteSize(viewType),
12767                              &unused_value)) {
12768     return false;
12769   }
12770 
12771   if (deadCode_) {
12772     return true;
12773   }
12774 
12775   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset(),
12776                           Synchronization::Store());
12777 
12778   if (Scalar::byteSize(viewType) <= sizeof(void*)) {
12779     return storeCommon(&access, AccessCheck(), type);
12780   }
12781 
12782   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
12783 
12784 #ifdef JS_64BIT
12785   MOZ_CRASH("Should not happen");
12786 #else
12787   emitAtomicXchg64(&access, WantResult(false));
12788   return true;
12789 #endif
12790 }
12791 
emitAtomicXchg(ValType type,Scalar::Type viewType)12792 bool BaseCompiler::emitAtomicXchg(ValType type, Scalar::Type viewType) {
12793   LinearMemoryAddress<Nothing> addr;
12794   Nothing unused_value;
12795   if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType),
12796                            &unused_value)) {
12797     return false;
12798   }
12799 
12800   if (deadCode_) {
12801     return true;
12802   }
12803 
12804   AccessCheck check;
12805   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset(),
12806                           Synchronization::Full());
12807 
12808   if (Scalar::byteSize(viewType) <= 4) {
12809     PopAtomicXchg32Regs regs(this, type, viewType);
12810     RegI32 rp = popMemory32Access(&access, &check);
12811     RegI32 tls = maybeLoadTlsForAccess(check);
12812 
12813     auto memaddr = prepareAtomicMemoryAccess(&access, &check, tls, rp);
12814     regs.atomicXchg32(access, memaddr);
12815 
12816     maybeFree(tls);
12817     freeI32(rp);
12818 
12819     if (type == ValType::I64) {
12820       pushU32AsI64(regs.takeRd());
12821     } else {
12822       pushI32(regs.takeRd());
12823     }
12824     return true;
12825   }
12826 
12827   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
12828 
12829   emitAtomicXchg64(&access, WantResult(true));
12830   return true;
12831 }
12832 
emitAtomicXchg64(MemoryAccessDesc * access,WantResult wantResult)12833 void BaseCompiler::emitAtomicXchg64(MemoryAccessDesc* access,
12834                                     WantResult wantResult) {
12835   PopAtomicXchg64Regs regs(this);
12836 
12837   AccessCheck check;
12838   RegI32 rp = popMemory32Access(access, &check);
12839 
12840 #ifdef JS_CODEGEN_X86
12841   ScratchEBX ebx(*this);
12842   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
12843   auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp);
12844   regs.atomicXchg64(*access, memaddr, ebx);
12845 #else
12846   RegI32 tls = maybeLoadTlsForAccess(check);
12847   auto memaddr = prepareAtomicMemoryAccess(access, &check, tls, rp);
12848   regs.atomicXchg64(*access, memaddr);
12849   maybeFree(tls);
12850 #endif
12851 
12852   freeI32(rp);
12853 
12854   if (wantResult) {
12855     pushI64(regs.takeRd());
12856   }
12857 }
12858 
emitWait(ValType type,uint32_t byteSize)12859 bool BaseCompiler::emitWait(ValType type, uint32_t byteSize) {
12860   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
12861 
12862   Nothing nothing;
12863   LinearMemoryAddress<Nothing> addr;
12864   if (!iter_.readWait(&addr, type, byteSize, &nothing, &nothing)) {
12865     return false;
12866   }
12867 
12868   if (deadCode_) {
12869     return true;
12870   }
12871 
12872   switch (type.kind()) {
12873     case ValType::I32: {
12874       RegI64 timeout = popI64();
12875       RegI32 val = popI32();
12876 
12877       MemoryAccessDesc access(Scalar::Int32, addr.align, addr.offset,
12878                               bytecodeOffset());
12879       computeEffectiveAddress(&access);
12880 
12881       pushI32(val);
12882       pushI64(timeout);
12883 
12884       if (!emitInstanceCall(lineOrBytecode, SASigWaitI32)) {
12885         return false;
12886       }
12887       break;
12888     }
12889     case ValType::I64: {
12890       RegI64 timeout = popI64();
12891       RegI64 val = popI64();
12892 
12893       MemoryAccessDesc access(Scalar::Int64, addr.align, addr.offset,
12894                               bytecodeOffset());
12895       computeEffectiveAddress(&access);
12896 
12897       pushI64(val);
12898       pushI64(timeout);
12899 
12900       if (!emitInstanceCall(lineOrBytecode, SASigWaitI64)) {
12901         return false;
12902       }
12903       break;
12904     }
12905     default:
12906       MOZ_CRASH();
12907   }
12908 
12909   return true;
12910 }
12911 
emitWake()12912 bool BaseCompiler::emitWake() {
12913   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
12914 
12915   Nothing nothing;
12916   LinearMemoryAddress<Nothing> addr;
12917   if (!iter_.readWake(&addr, &nothing)) {
12918     return false;
12919   }
12920 
12921   if (deadCode_) {
12922     return true;
12923   }
12924 
12925   RegI32 count = popI32();
12926 
12927   MemoryAccessDesc access(Scalar::Int32, addr.align, addr.offset,
12928                           bytecodeOffset());
12929   computeEffectiveAddress(&access);
12930 
12931   pushI32(count);
12932 
12933   return emitInstanceCall(lineOrBytecode, SASigWake);
12934 }
12935 
emitFence()12936 bool BaseCompiler::emitFence() {
12937   if (!iter_.readFence()) {
12938     return false;
12939   }
12940   if (deadCode_) {
12941     return true;
12942   }
12943 
12944   masm.memoryBarrier(MembarFull);
12945   return true;
12946 }
12947 
emitMemCopy()12948 bool BaseCompiler::emitMemCopy() {
12949   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
12950 
12951   uint32_t dstMemOrTableIndex = 0;
12952   uint32_t srcMemOrTableIndex = 0;
12953   Nothing nothing;
12954   if (!iter_.readMemOrTableCopy(true, &dstMemOrTableIndex, &nothing,
12955                                 &srcMemOrTableIndex, &nothing, &nothing)) {
12956     return false;
12957   }
12958 
12959   if (deadCode_) {
12960     return true;
12961   }
12962 
12963   int32_t signedLength;
12964   if (MacroAssembler::SupportsFastUnalignedAccesses() &&
12965       peekConst(&signedLength) && signedLength != 0 &&
12966       uint32_t(signedLength) <= MaxInlineMemoryCopyLength) {
12967     return emitMemCopyInline();
12968   }
12969 
12970   return emitMemCopyCall(lineOrBytecode);
12971 }
12972 
emitMemCopyCall(uint32_t lineOrBytecode)12973 bool BaseCompiler::emitMemCopyCall(uint32_t lineOrBytecode) {
12974   pushHeapBase();
12975   return emitInstanceCall(lineOrBytecode, usesSharedMemory()
12976                                               ? SASigMemCopyShared32
12977                                               : SASigMemCopy32);
12978 }
12979 
emitMemCopyInline()12980 bool BaseCompiler::emitMemCopyInline() {
12981   MOZ_ASSERT(MaxInlineMemoryCopyLength != 0);
12982 
12983   int32_t signedLength;
12984   MOZ_ALWAYS_TRUE(popConst(&signedLength));
12985   uint32_t length = signedLength;
12986   MOZ_ASSERT(length != 0 && length <= MaxInlineMemoryCopyLength);
12987 
12988   RegI32 src = popI32();
12989   RegI32 dest = popI32();
12990 
12991   // Compute the number of copies of each width we will need to do
12992   size_t remainder = length;
12993 #ifdef JS_64BIT
12994   size_t numCopies8 = remainder / sizeof(uint64_t);
12995   remainder %= sizeof(uint64_t);
12996 #endif
12997   size_t numCopies4 = remainder / sizeof(uint32_t);
12998   remainder %= sizeof(uint32_t);
12999   size_t numCopies2 = remainder / sizeof(uint16_t);
13000   remainder %= sizeof(uint16_t);
13001   size_t numCopies1 = remainder;
13002 
13003   // Load all source bytes onto the value stack from low to high using the
13004   // widest transfer width we can for the system. We will trap without writing
13005   // anything if any source byte is out-of-bounds.
13006   bool omitBoundsCheck = false;
13007   size_t offset = 0;
13008 
13009 #ifdef JS_64BIT
13010   for (uint32_t i = 0; i < numCopies8; i++) {
13011     RegI32 temp = needI32();
13012     moveI32(src, temp);
13013     pushI32(temp);
13014 
13015     MemoryAccessDesc access(Scalar::Int64, 1, offset, bytecodeOffset());
13016     AccessCheck check;
13017     check.omitBoundsCheck = omitBoundsCheck;
13018     if (!loadCommon(&access, check, ValType::I64)) {
13019       return false;
13020     }
13021 
13022     offset += sizeof(uint64_t);
13023     omitBoundsCheck = true;
13024   }
13025 #endif
13026 
13027   for (uint32_t i = 0; i < numCopies4; i++) {
13028     RegI32 temp = needI32();
13029     moveI32(src, temp);
13030     pushI32(temp);
13031 
13032     MemoryAccessDesc access(Scalar::Uint32, 1, offset, bytecodeOffset());
13033     AccessCheck check;
13034     check.omitBoundsCheck = omitBoundsCheck;
13035     if (!loadCommon(&access, check, ValType::I32)) {
13036       return false;
13037     }
13038 
13039     offset += sizeof(uint32_t);
13040     omitBoundsCheck = true;
13041   }
13042 
13043   if (numCopies2) {
13044     RegI32 temp = needI32();
13045     moveI32(src, temp);
13046     pushI32(temp);
13047 
13048     MemoryAccessDesc access(Scalar::Uint16, 1, offset, bytecodeOffset());
13049     AccessCheck check;
13050     check.omitBoundsCheck = omitBoundsCheck;
13051     if (!loadCommon(&access, check, ValType::I32)) {
13052       return false;
13053     }
13054 
13055     offset += sizeof(uint16_t);
13056     omitBoundsCheck = true;
13057   }
13058 
13059   if (numCopies1) {
13060     RegI32 temp = needI32();
13061     moveI32(src, temp);
13062     pushI32(temp);
13063 
13064     MemoryAccessDesc access(Scalar::Uint8, 1, offset, bytecodeOffset());
13065     AccessCheck check;
13066     check.omitBoundsCheck = omitBoundsCheck;
13067     if (!loadCommon(&access, check, ValType::I32)) {
13068       return false;
13069     }
13070   }
13071 
13072   // Store all source bytes from the value stack to the destination from
13073   // high to low. We will trap without writing anything on the first store
13074   // if any dest byte is out-of-bounds.
13075   offset = length;
13076   omitBoundsCheck = false;
13077 
13078   if (numCopies1) {
13079     offset -= sizeof(uint8_t);
13080 
13081     RegI32 value = popI32();
13082     RegI32 temp = needI32();
13083     moveI32(dest, temp);
13084     pushI32(temp);
13085     pushI32(value);
13086 
13087     MemoryAccessDesc access(Scalar::Uint8, 1, offset, bytecodeOffset());
13088     AccessCheck check;
13089     if (!storeCommon(&access, check, ValType::I32)) {
13090       return false;
13091     }
13092 
13093     omitBoundsCheck = true;
13094   }
13095 
13096   if (numCopies2) {
13097     offset -= sizeof(uint16_t);
13098 
13099     RegI32 value = popI32();
13100     RegI32 temp = needI32();
13101     moveI32(dest, temp);
13102     pushI32(temp);
13103     pushI32(value);
13104 
13105     MemoryAccessDesc access(Scalar::Uint16, 1, offset, bytecodeOffset());
13106     AccessCheck check;
13107     check.omitBoundsCheck = omitBoundsCheck;
13108     if (!storeCommon(&access, check, ValType::I32)) {
13109       return false;
13110     }
13111 
13112     omitBoundsCheck = true;
13113   }
13114 
13115   for (uint32_t i = 0; i < numCopies4; i++) {
13116     offset -= sizeof(uint32_t);
13117 
13118     RegI32 value = popI32();
13119     RegI32 temp = needI32();
13120     moveI32(dest, temp);
13121     pushI32(temp);
13122     pushI32(value);
13123 
13124     MemoryAccessDesc access(Scalar::Uint32, 1, offset, bytecodeOffset());
13125     AccessCheck check;
13126     check.omitBoundsCheck = omitBoundsCheck;
13127     if (!storeCommon(&access, check, ValType::I32)) {
13128       return false;
13129     }
13130 
13131     omitBoundsCheck = true;
13132   }
13133 
13134 #ifdef JS_64BIT
13135   for (uint32_t i = 0; i < numCopies8; i++) {
13136     offset -= sizeof(uint64_t);
13137 
13138     RegI64 value = popI64();
13139     RegI32 temp = needI32();
13140     moveI32(dest, temp);
13141     pushI32(temp);
13142     pushI64(value);
13143 
13144     MemoryAccessDesc access(Scalar::Int64, 1, offset, bytecodeOffset());
13145     AccessCheck check;
13146     check.omitBoundsCheck = omitBoundsCheck;
13147     if (!storeCommon(&access, check, ValType::I64)) {
13148       return false;
13149     }
13150 
13151     omitBoundsCheck = true;
13152   }
13153 #endif
13154 
13155   freeI32(dest);
13156   freeI32(src);
13157   return true;
13158 }
13159 
emitTableCopy()13160 bool BaseCompiler::emitTableCopy() {
13161   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
13162 
13163   uint32_t dstMemOrTableIndex = 0;
13164   uint32_t srcMemOrTableIndex = 0;
13165   Nothing nothing;
13166   if (!iter_.readMemOrTableCopy(false, &dstMemOrTableIndex, &nothing,
13167                                 &srcMemOrTableIndex, &nothing, &nothing)) {
13168     return false;
13169   }
13170 
13171   if (deadCode_) {
13172     return true;
13173   }
13174 
13175   pushI32(dstMemOrTableIndex);
13176   pushI32(srcMemOrTableIndex);
13177   return emitInstanceCall(lineOrBytecode, SASigTableCopy);
13178 }
13179 
emitDataOrElemDrop(bool isData)13180 bool BaseCompiler::emitDataOrElemDrop(bool isData) {
13181   return emitInstanceCallOp<uint32_t>(
13182       isData ? SASigDataDrop : SASigElemDrop, [&](uint32_t* segIndex) -> bool {
13183         return iter_.readDataOrElemDrop(isData, segIndex);
13184       });
13185 }
13186 
emitMemFill()13187 bool BaseCompiler::emitMemFill() {
13188   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
13189 
13190   Nothing nothing;
13191   if (!iter_.readMemFill(&nothing, &nothing, &nothing)) {
13192     return false;
13193   }
13194 
13195   if (deadCode_) {
13196     return true;
13197   }
13198 
13199   int32_t signedLength;
13200   int32_t signedValue;
13201   if (MacroAssembler::SupportsFastUnalignedAccesses() &&
13202       peek2xConst(&signedLength, &signedValue) && signedLength != 0 &&
13203       uint32_t(signedLength) <= MaxInlineMemoryFillLength) {
13204     return emitMemFillInline();
13205   }
13206   return emitMemFillCall(lineOrBytecode);
13207 }
13208 
emitMemFillCall(uint32_t lineOrBytecode)13209 bool BaseCompiler::emitMemFillCall(uint32_t lineOrBytecode) {
13210   pushHeapBase();
13211   return emitInstanceCall(lineOrBytecode, usesSharedMemory()
13212                                               ? SASigMemFillShared32
13213                                               : SASigMemFill32);
13214 }
13215 
emitMemFillInline()13216 bool BaseCompiler::emitMemFillInline() {
13217   MOZ_ASSERT(MaxInlineMemoryFillLength != 0);
13218 
13219   int32_t signedLength;
13220   int32_t signedValue;
13221   MOZ_ALWAYS_TRUE(popConst(&signedLength));
13222   MOZ_ALWAYS_TRUE(popConst(&signedValue));
13223   uint32_t length = uint32_t(signedLength);
13224   uint32_t value = uint32_t(signedValue);
13225   MOZ_ASSERT(length != 0 && length <= MaxInlineMemoryFillLength);
13226 
13227   RegI32 dest = popI32();
13228 
13229   // Compute the number of copies of each width we will need to do
13230   size_t remainder = length;
13231 #ifdef JS_64BIT
13232   size_t numCopies8 = remainder / sizeof(uint64_t);
13233   remainder %= sizeof(uint64_t);
13234 #endif
13235   size_t numCopies4 = remainder / sizeof(uint32_t);
13236   remainder %= sizeof(uint32_t);
13237   size_t numCopies2 = remainder / sizeof(uint16_t);
13238   remainder %= sizeof(uint16_t);
13239   size_t numCopies1 = remainder;
13240 
13241   MOZ_ASSERT(numCopies2 <= 1 && numCopies1 <= 1);
13242 
13243   // Generate splatted definitions for wider fills as needed
13244 #ifdef JS_64BIT
13245   uint64_t val8 = SplatByteToUInt<uint64_t>(value, 8);
13246 #endif
13247   uint32_t val4 = SplatByteToUInt<uint32_t>(value, 4);
13248   uint32_t val2 = SplatByteToUInt<uint32_t>(value, 2);
13249   uint32_t val1 = value;
13250 
13251   // Store the fill value to the destination from high to low. We will trap
13252   // without writing anything on the first store if any dest byte is
13253   // out-of-bounds.
13254   size_t offset = length;
13255   bool omitBoundsCheck = false;
13256 
13257   if (numCopies1) {
13258     offset -= sizeof(uint8_t);
13259 
13260     RegI32 temp = needI32();
13261     moveI32(dest, temp);
13262     pushI32(temp);
13263     pushI32(val1);
13264 
13265     MemoryAccessDesc access(Scalar::Uint8, 1, offset, bytecodeOffset());
13266     AccessCheck check;
13267     if (!storeCommon(&access, check, ValType::I32)) {
13268       return false;
13269     }
13270 
13271     omitBoundsCheck = true;
13272   }
13273 
13274   if (numCopies2) {
13275     offset -= sizeof(uint16_t);
13276 
13277     RegI32 temp = needI32();
13278     moveI32(dest, temp);
13279     pushI32(temp);
13280     pushI32(val2);
13281 
13282     MemoryAccessDesc access(Scalar::Uint16, 1, offset, bytecodeOffset());
13283     AccessCheck check;
13284     check.omitBoundsCheck = omitBoundsCheck;
13285     if (!storeCommon(&access, check, ValType::I32)) {
13286       return false;
13287     }
13288 
13289     omitBoundsCheck = true;
13290   }
13291 
13292   for (uint32_t i = 0; i < numCopies4; i++) {
13293     offset -= sizeof(uint32_t);
13294 
13295     RegI32 temp = needI32();
13296     moveI32(dest, temp);
13297     pushI32(temp);
13298     pushI32(val4);
13299 
13300     MemoryAccessDesc access(Scalar::Uint32, 1, offset, bytecodeOffset());
13301     AccessCheck check;
13302     check.omitBoundsCheck = omitBoundsCheck;
13303     if (!storeCommon(&access, check, ValType::I32)) {
13304       return false;
13305     }
13306 
13307     omitBoundsCheck = true;
13308   }
13309 
13310 #ifdef JS_64BIT
13311   for (uint32_t i = 0; i < numCopies8; i++) {
13312     offset -= sizeof(uint64_t);
13313 
13314     RegI32 temp = needI32();
13315     moveI32(dest, temp);
13316     pushI32(temp);
13317     pushI64(val8);
13318 
13319     MemoryAccessDesc access(Scalar::Int64, 1, offset, bytecodeOffset());
13320     AccessCheck check;
13321     check.omitBoundsCheck = omitBoundsCheck;
13322     if (!storeCommon(&access, check, ValType::I64)) {
13323       return false;
13324     }
13325 
13326     omitBoundsCheck = true;
13327   }
13328 #endif
13329 
13330   freeI32(dest);
13331   return true;
13332 }
13333 
emitMemInit()13334 bool BaseCompiler::emitMemInit() {
13335   return emitInstanceCallOp<uint32_t>(
13336       SASigMemInit32, [this](uint32_t* segIndex) -> bool {
13337         uint32_t dstTableIndex;
13338         Nothing nothing;
13339         return iter_.readMemOrTableInit(/*isMem*/ true, segIndex,
13340                                         &dstTableIndex, &nothing, &nothing,
13341                                         &nothing);
13342       });
13343 }
13344 
emitTableInit()13345 bool BaseCompiler::emitTableInit() {
13346   return emitInstanceCallOp<uint32_t, uint32_t>(
13347       SASigTableInit,
13348       [this](uint32_t* segIndex, uint32_t* dstTableIndex) -> bool {
13349         Nothing nothing;
13350         return iter_.readMemOrTableInit(/*isMem*/ false, segIndex,
13351                                         dstTableIndex, &nothing, &nothing,
13352                                         &nothing);
13353       });
13354 }
13355 
emitTableFill()13356 [[nodiscard]] bool BaseCompiler::emitTableFill() {
13357   // fill(start:u32, val:ref, len:u32, table:u32) -> void
13358   return emitInstanceCallOp<uint32_t>(
13359       SASigTableFill, [this](uint32_t* tableIndex) -> bool {
13360         Nothing nothing;
13361         return iter_.readTableFill(tableIndex, &nothing, &nothing, &nothing);
13362       });
13363 }
13364 
emitTableGet()13365 [[nodiscard]] bool BaseCompiler::emitTableGet() {
13366   // get(index:u32, table:u32) -> AnyRef
13367   return emitInstanceCallOp<uint32_t>(
13368       SASigTableGet, [this](uint32_t* tableIndex) -> bool {
13369         Nothing nothing;
13370         return iter_.readTableGet(tableIndex, &nothing);
13371       });
13372 }
13373 
emitTableGrow()13374 [[nodiscard]] bool BaseCompiler::emitTableGrow() {
13375   // grow(initValue:anyref, delta:u32, table:u32) -> u32
13376   return emitInstanceCallOp<uint32_t>(
13377       SASigTableGrow, [this](uint32_t* tableIndex) -> bool {
13378         Nothing nothing;
13379         return iter_.readTableGrow(tableIndex, &nothing, &nothing);
13380       });
13381 }
13382 
emitTableSet()13383 [[nodiscard]] bool BaseCompiler::emitTableSet() {
13384   // set(index:u32, value:ref, table:u32) -> void
13385   return emitInstanceCallOp<uint32_t>(
13386       SASigTableSet, [this](uint32_t* tableIndex) -> bool {
13387         Nothing nothing;
13388         return iter_.readTableSet(tableIndex, &nothing, &nothing);
13389       });
13390 }
13391 
emitTableSize()13392 [[nodiscard]] bool BaseCompiler::emitTableSize() {
13393   // size(table:u32) -> u32
13394   return emitInstanceCallOp<uint32_t>(SASigTableSize,
13395                                       [this](uint32_t* tableIndex) -> bool {
13396                                         return iter_.readTableSize(tableIndex);
13397                                       });
13398 }
13399 
emitGcNullCheck(RegRef rp)13400 void BaseCompiler::emitGcNullCheck(RegRef rp) {
13401   Label ok;
13402   masm.branchTestPtr(Assembler::NonZero, rp, rp, &ok);
13403   trap(Trap::NullPointerDereference);
13404   masm.bind(&ok);
13405 }
13406 
emitGcArrayGetData(RegRef rp)13407 RegPtr BaseCompiler::emitGcArrayGetData(RegRef rp) {
13408   RegPtr rdata = needPtr();
13409   // An array is always an outline typed object
13410   masm.loadPtr(Address(rp, OutlineTypedObject::offsetOfData()), rdata);
13411   return rdata;
13412 }
13413 
emitGcArrayGetLength(RegPtr rdata,bool adjustDataPointer)13414 RegI32 BaseCompiler::emitGcArrayGetLength(RegPtr rdata,
13415                                           bool adjustDataPointer) {
13416   STATIC_ASSERT_ARRAYLENGTH_IS_U32;
13417   RegI32 length = needI32();
13418   masm.load32(Address(rdata, OutlineTypedObject::offsetOfArrayLength()),
13419               length);
13420   if (adjustDataPointer) {
13421     masm.addPtr(ImmWord(OutlineTypedObject::offsetOfArrayLength() +
13422                         sizeof(OutlineTypedObject::ArrayLength)),
13423                 rdata);
13424   }
13425   return length;
13426 }
13427 
emitGcArrayBoundsCheck(RegI32 index,RegI32 length)13428 void BaseCompiler::emitGcArrayBoundsCheck(RegI32 index, RegI32 length) {
13429   Label inBounds;
13430   masm.branch32(Assembler::Below, index, length, &inBounds);
13431   masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset());
13432   masm.bind(&inBounds);
13433 }
13434 
13435 template <typename T>
emitGcGet(FieldType type,FieldExtension extension,const T & src)13436 void BaseCompiler::emitGcGet(FieldType type, FieldExtension extension,
13437                              const T& src) {
13438   switch (type.kind()) {
13439     case FieldType::I8: {
13440       MOZ_ASSERT(extension != FieldExtension::None);
13441       RegI32 r = needI32();
13442       if (extension == FieldExtension::Unsigned) {
13443         masm.load8ZeroExtend(src, r);
13444       } else {
13445         masm.load8SignExtend(src, r);
13446       }
13447       pushI32(r);
13448       break;
13449     }
13450     case FieldType::I16: {
13451       MOZ_ASSERT(extension != FieldExtension::None);
13452       RegI32 r = needI32();
13453       if (extension == FieldExtension::Unsigned) {
13454         masm.load16ZeroExtend(src, r);
13455       } else {
13456         masm.load16SignExtend(src, r);
13457       }
13458       pushI32(r);
13459       break;
13460     }
13461     case FieldType::I32: {
13462       MOZ_ASSERT(extension == FieldExtension::None);
13463       RegI32 r = needI32();
13464       masm.load32(src, r);
13465       pushI32(r);
13466       break;
13467     }
13468     case FieldType::I64: {
13469       MOZ_ASSERT(extension == FieldExtension::None);
13470       RegI64 r = needI64();
13471       masm.load64(src, r);
13472       pushI64(r);
13473       break;
13474     }
13475     case FieldType::F32: {
13476       MOZ_ASSERT(extension == FieldExtension::None);
13477       RegF32 r = needF32();
13478       masm.loadFloat32(src, r);
13479       pushF32(r);
13480       break;
13481     }
13482     case FieldType::F64: {
13483       MOZ_ASSERT(extension == FieldExtension::None);
13484       RegF64 r = needF64();
13485       masm.loadDouble(src, r);
13486       pushF64(r);
13487       break;
13488     }
13489 #ifdef ENABLE_WASM_SIMD
13490     case FieldType::V128: {
13491       MOZ_ASSERT(extension == FieldExtension::None);
13492       RegV128 r = needV128();
13493       masm.loadUnalignedSimd128(src, r);
13494       pushV128(r);
13495       break;
13496     }
13497 #endif
13498     case FieldType::Ref: {
13499       MOZ_ASSERT(extension == FieldExtension::None);
13500       RegRef r = needRef();
13501       masm.loadPtr(src, r);
13502       pushRef(r);
13503       break;
13504     }
13505     default: {
13506       MOZ_CRASH("Unexpected field type");
13507     }
13508   }
13509 }
13510 
13511 template <typename T>
emitGcSetScalar(const T & dst,FieldType type,AnyReg value)13512 void BaseCompiler::emitGcSetScalar(const T& dst, FieldType type, AnyReg value) {
13513   switch (type.kind()) {
13514     case FieldType::I8: {
13515       masm.store8(value.i32(), dst);
13516       break;
13517     }
13518     case FieldType::I16: {
13519       masm.store16(value.i32(), dst);
13520       break;
13521     }
13522     case FieldType::I32: {
13523       masm.store32(value.i32(), dst);
13524       break;
13525     }
13526     case FieldType::I64: {
13527       masm.store64(value.i64(), dst);
13528       break;
13529     }
13530     case FieldType::F32: {
13531       masm.storeFloat32(value.f32(), dst);
13532       break;
13533     }
13534     case FieldType::F64: {
13535       masm.storeDouble(value.f64(), dst);
13536       break;
13537     }
13538 #ifdef ENABLE_WASM_SIMD
13539     case FieldType::V128: {
13540       masm.storeUnalignedSimd128(value.v128(), dst);
13541       break;
13542     }
13543 #endif
13544     default: {
13545       MOZ_CRASH("Unexpected field type");
13546     }
13547   }
13548 }
13549 
emitGcStructSet(RegRef object,RegPtr data,const StructField & field,AnyReg value)13550 bool BaseCompiler::emitGcStructSet(RegRef object, RegPtr data,
13551                                    const StructField& field, AnyReg value) {
13552   // Easy path if the field is a scalar
13553   if (!field.type.isReference()) {
13554     emitGcSetScalar(Address(data, field.offset), field.type, value);
13555     freeAny(value);
13556     return true;
13557   }
13558 
13559   // Create temporary for the valueAddr that is not in the prebarrier register
13560   // and can be consumed by the barrier operation
13561   RegPtr valueAddr = RegPtr(PreBarrierReg);
13562   needPtr(valueAddr);
13563   masm.computeEffectiveAddress(Address(data, field.offset), valueAddr);
13564 
13565   // Save state for after barriered write
13566   pushPtr(data);
13567 
13568   // emitBarrieredStore preserves object and value
13569   if (!emitBarrieredStore(Some(object), valueAddr, value.ref())) {
13570     return false;
13571   }
13572   freeRef(value.ref());
13573 
13574   // Restore state
13575   popPtr(data);
13576 
13577   return true;
13578 }
13579 
emitGcArraySet(RegRef object,RegPtr data,RegI32 index,const ArrayType & arrayType,AnyReg value)13580 bool BaseCompiler::emitGcArraySet(RegRef object, RegPtr data, RegI32 index,
13581                                   const ArrayType& arrayType, AnyReg value) {
13582   // Try to use a base index store instruction if the field type fits in a
13583   // shift immediate. If not we shift the index manually and then unshift
13584   // it after the store. We don't use an extra register for this because we
13585   // don't have any to spare on x86.
13586   uint32_t shift = arrayType.elementType_.indexingShift();
13587   Scale scale;
13588   bool shiftedIndex = false;
13589   if (IsShiftInScaleRange(shift)) {
13590     scale = ShiftToScale(shift);
13591   } else {
13592     masm.lshiftPtr(Imm32(shift), index);
13593     scale = TimesOne;
13594     shiftedIndex = true;
13595   }
13596   auto unshiftIndex = mozilla::MakeScopeExit([&] {
13597     if (shiftedIndex) {
13598       masm.rshiftPtr(Imm32(shift), index);
13599     }
13600   });
13601 
13602   // Easy path if the field is a scalar
13603   if (!arrayType.elementType_.isReference()) {
13604     emitGcSetScalar(BaseIndex(data, index, scale, 0), arrayType.elementType_,
13605                     value);
13606     return true;
13607   }
13608 
13609   // Create temporaries for valueAddr that is not in the prebarrier register
13610   // and can be consumed by the barrier operation
13611   RegPtr valueAddr = RegPtr(PreBarrierReg);
13612   needPtr(valueAddr);
13613   masm.computeEffectiveAddress(BaseIndex(data, index, scale, 0), valueAddr);
13614 
13615   // Save state for after barriered write
13616   pushPtr(data);
13617   pushI32(index);
13618 
13619   // emitBarrieredStore preserves object and value
13620   if (!emitBarrieredStore(Some(object), valueAddr, value.ref())) {
13621     return false;
13622   }
13623 
13624   // Restore state
13625   popI32(index);
13626   popPtr(data);
13627 
13628   return true;
13629 }
13630 
emitStructNewWithRtt()13631 bool BaseCompiler::emitStructNewWithRtt() {
13632   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
13633 
13634   uint32_t typeIndex;
13635   Nothing rtt;
13636   NothingVector args{};
13637   if (!iter_.readStructNewWithRtt(&typeIndex, &rtt, &args)) {
13638     return false;
13639   }
13640 
13641   if (deadCode_) {
13642     return true;
13643   }
13644 
13645   const StructType& structType = moduleEnv_.types[typeIndex].structType();
13646 
13647   // Allocate zeroed storage.  The parameter to StructNew is a rtt value that is
13648   // guaranteed to be at the top of the stack by validation.
13649   //
13650   // Traps on OOM.
13651   if (!emitInstanceCall(lineOrBytecode, SASigStructNew)) {
13652     return false;
13653   }
13654 
13655   // Optimization opportunity: Iterate backward to pop arguments off the
13656   // stack.  This will generate more instructions than we want, since we
13657   // really only need to pop the stack once at the end, not for every element,
13658   // but to do better we need a bit more machinery to load elements off the
13659   // stack into registers.
13660 
13661   // Reserve this register early if we will need it so that it is not taken by
13662   // any register used in this function.
13663   needPtr(RegPtr(PreBarrierReg));
13664 
13665   RegRef rp = popRef();
13666   RegPtr rdata = needPtr();
13667 
13668   // Free the barrier reg after we've allocated all registers
13669   freePtr(RegPtr(PreBarrierReg));
13670 
13671   // The struct allocated above is guaranteed to have the exact shape of
13672   // structType, we don't need to branch on whether it's inline or not.
13673   if (InlineTypedObject::canAccommodateSize(structType.size_)) {
13674     masm.computeEffectiveAddress(
13675         Address(rp, InlineTypedObject::offsetOfDataStart()), rdata);
13676   } else {
13677     masm.loadPtr(Address(rp, OutlineTypedObject::offsetOfData()), rdata);
13678   }
13679 
13680   // Optimization opportunity: when the value being stored is a known
13681   // zero/null we need store nothing.  This case may be somewhat common
13682   // because struct.new forces a value to be specified for every field.
13683 
13684   uint32_t fieldIndex = structType.fields_.length();
13685   while (fieldIndex-- > 0) {
13686     const StructField& structField = structType.fields_[fieldIndex];
13687     // Reserve the barrier reg if we might need it for this store
13688     if (structField.type.isReference()) {
13689       needPtr(RegPtr(PreBarrierReg));
13690     }
13691 
13692     AnyReg value = popAny();
13693 
13694     // Free the barrier reg now that we've loaded the value
13695     if (structField.type.isReference()) {
13696       freePtr(RegPtr(PreBarrierReg));
13697     }
13698 
13699     // Consumes value. rp, and rdata are preserved
13700     if (!emitGcStructSet(rp, rdata, structField, value)) {
13701       return false;
13702     }
13703   }
13704 
13705   freePtr(rdata);
13706   pushRef(rp);
13707 
13708   return true;
13709 }
13710 
emitStructNewDefaultWithRtt()13711 bool BaseCompiler::emitStructNewDefaultWithRtt() {
13712   // Allocate zeroed storage.  The parameter to StructNew is a rtt value that is
13713   // guaranteed to be at the top of the stack by validation.
13714   //
13715   // Traps on OOM.
13716 
13717   // (rtt) -> ref; the type index is just dropped on the floor
13718   return emitInstanceCallOp(SASigStructNew, [this]() -> bool {
13719     uint32_t unusedTypeIndex;
13720     Nothing unusedRtt;
13721     return iter_.readStructNewDefaultWithRtt(&unusedTypeIndex, &unusedRtt);
13722   });
13723 }
13724 
emitStructGet(FieldExtension extension)13725 bool BaseCompiler::emitStructGet(FieldExtension extension) {
13726   uint32_t typeIndex;
13727   uint32_t fieldIndex;
13728   Nothing nothing;
13729   if (!iter_.readStructGet(&typeIndex, &fieldIndex, extension, &nothing)) {
13730     return false;
13731   }
13732 
13733   if (deadCode_) {
13734     return true;
13735   }
13736 
13737   const StructType& structType = moduleEnv_.types[typeIndex].structType();
13738 
13739   RegRef rp = popRef();
13740 
13741   // Check for null
13742   emitGcNullCheck(rp);
13743 
13744   // Acquire the data pointer from the object
13745   RegPtr rdata = needPtr();
13746   {
13747     RegPtr scratch = needPtr();
13748     RegPtr clasp = needPtr();
13749     masm.movePtr(SymbolicAddress::InlineTypedObjectClass, clasp);
13750     Label join;
13751     Label isInline;
13752     masm.branchTestObjClass(Assembler::Equal, rp, clasp, scratch, rp,
13753                             &isInline);
13754     freePtr(clasp);
13755     freePtr(scratch);
13756 
13757     masm.loadPtr(Address(rp, OutlineTypedObject::offsetOfData()), rdata);
13758     masm.jump(&join);
13759 
13760     masm.bind(&isInline);
13761     masm.computeEffectiveAddress(
13762         Address(rp, InlineTypedObject::offsetOfDataStart()), rdata);
13763     masm.bind(&join);
13764   }
13765 
13766   // Load the value
13767   FieldType type = structType.fields_[fieldIndex].type;
13768   uint32_t offset = structType.fields_[fieldIndex].offset;
13769   emitGcGet(type, extension, Address(rdata, offset));
13770 
13771   freePtr(rdata);
13772   freeRef(rp);
13773 
13774   return true;
13775 }
13776 
emitStructSet()13777 bool BaseCompiler::emitStructSet() {
13778   uint32_t typeIndex;
13779   uint32_t fieldIndex;
13780   Nothing nothing;
13781   if (!iter_.readStructSet(&typeIndex, &fieldIndex, &nothing, &nothing)) {
13782     return false;
13783   }
13784 
13785   if (deadCode_) {
13786     return true;
13787   }
13788 
13789   const StructType& structType = moduleEnv_.types[typeIndex].structType();
13790   const StructField& structField = structType.fields_[fieldIndex];
13791 
13792   // Reserve this register early if we will need it so that it is not taken by
13793   // any register used in this function.
13794   if (structField.type.isReference()) {
13795     needPtr(RegPtr(PreBarrierReg));
13796   }
13797 
13798   AnyReg value = popAny();
13799   RegRef rp = popRef();
13800   RegPtr rdata = needPtr();
13801 
13802   // Free the barrier reg after we've allocated all registers
13803   if (structField.type.isReference()) {
13804     freePtr(RegPtr(PreBarrierReg));
13805   }
13806 
13807   // Check for null
13808   emitGcNullCheck(rp);
13809 
13810   // Acquire the data pointer from the object.
13811   {
13812     // We don't have a register to spare at this point on x86, so carefully
13813     // borrow the `rdata` as a scratch pointer during the instruction sequence
13814     // that loads `rdata`.
13815     RegPtr scratch = rdata;
13816     RegPtr clasp = needPtr();
13817     masm.movePtr(SymbolicAddress::InlineTypedObjectClass, clasp);
13818     Label join;
13819     Label isInline;
13820     masm.branchTestObjClass(Assembler::Equal, rp, clasp, scratch, rp,
13821                             &isInline);
13822     freePtr(clasp);
13823     masm.loadPtr(Address(rp, OutlineTypedObject::offsetOfData()), rdata);
13824     masm.jump(&join);
13825 
13826     masm.bind(&isInline);
13827     masm.computeEffectiveAddress(
13828         Address(rp, InlineTypedObject::offsetOfDataStart()), rdata);
13829     masm.bind(&join);
13830   }
13831 
13832   // Consumes value. rp, and rdata are preserved
13833   if (!emitGcStructSet(rp, rdata, structField, value)) {
13834     return false;
13835   }
13836 
13837   freePtr(rdata);
13838   freeRef(rp);
13839 
13840   return true;
13841 }
13842 
emitArrayNewWithRtt()13843 bool BaseCompiler::emitArrayNewWithRtt() {
13844   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
13845 
13846   uint32_t typeIndex;
13847   Nothing nothing;
13848   if (!iter_.readArrayNewWithRtt(&typeIndex, &nothing, &nothing, &nothing)) {
13849     return false;
13850   }
13851 
13852   if (deadCode_) {
13853     return true;
13854   }
13855 
13856   const ArrayType& arrayType = moduleEnv_.types[typeIndex].arrayType();
13857 
13858   // Allocate zeroed storage.  The parameter to ArrayNew is a rtt value and
13859   // length that are guaranteed to be at the top of the stack by validation.
13860   //
13861   // Traps on OOM.
13862   if (!emitInstanceCall(lineOrBytecode, SASigArrayNew)) {
13863     return false;
13864   }
13865 
13866   // Reserve this register early if we will need it so that it is not taken by
13867   // any register used in this function.
13868   if (arrayType.elementType_.isReference()) {
13869     needPtr(RegPtr(PreBarrierReg));
13870   }
13871 
13872   RegRef rp = popRef();
13873   AnyReg value = popAny();
13874 
13875   // Acquire the data pointers from the object
13876   RegPtr rdata = emitGcArrayGetData(rp);
13877 
13878   // Acquire the array length and adjust the data pointer to be immediately
13879   // after the array length header
13880   RegI32 length = emitGcArrayGetLength(rdata, true);
13881 
13882   // Free the barrier reg after we've allocated all registers
13883   if (arrayType.elementType_.isReference()) {
13884     freePtr(RegPtr(PreBarrierReg));
13885   }
13886 
13887   // Perform an initialization loop using `length` as the loop variable,
13888   // counting down to zero.
13889   Label done;
13890   Label loop;
13891   // Skip initialization if length = 0
13892   masm.branch32(Assembler::Equal, length, Imm32(0), &done);
13893   masm.bind(&loop);
13894 
13895   // Move to the next element
13896   masm.sub32(Imm32(1), length);
13897 
13898   // Assign value to array[length]. All registers are preserved
13899   if (!emitGcArraySet(rp, rdata, length, arrayType, value)) {
13900     return false;
13901   }
13902 
13903   // Loop back if there are still elements to initialize
13904   masm.branch32(Assembler::GreaterThan, length, Imm32(0), &loop);
13905   masm.bind(&done);
13906 
13907   freeI32(length);
13908   freeAny(value);
13909   freePtr(rdata);
13910   pushRef(rp);
13911 
13912   return true;
13913 }
13914 
emitArrayNewDefaultWithRtt()13915 bool BaseCompiler::emitArrayNewDefaultWithRtt() {
13916   // Allocate zeroed storage. The parameter to ArrayNew is a rtt value that is
13917   // guaranteed to be at the top of the stack by validation.
13918   //
13919   // Traps on OOM.
13920 
13921   // (rtt) -> ref; the type index is dropped on the floor.
13922   return emitInstanceCallOp(SASigArrayNew, [this]() -> bool {
13923     uint32_t unusedTypeIndex;
13924     Nothing nothing;
13925     return iter_.readArrayNewDefaultWithRtt(&unusedTypeIndex, &nothing,
13926                                             &nothing);
13927   });
13928 }
13929 
emitArrayGet(FieldExtension extension)13930 bool BaseCompiler::emitArrayGet(FieldExtension extension) {
13931   uint32_t typeIndex;
13932   Nothing nothing;
13933   if (!iter_.readArrayGet(&typeIndex, extension, &nothing, &nothing)) {
13934     return false;
13935   }
13936 
13937   if (deadCode_) {
13938     return true;
13939   }
13940 
13941   const ArrayType& arrayType = moduleEnv_.types[typeIndex].arrayType();
13942 
13943   RegI32 index = popI32();
13944   RegRef rp = popRef();
13945 
13946   // Check for null
13947   emitGcNullCheck(rp);
13948 
13949   // Acquire the data pointer from the object
13950   RegPtr rdata = emitGcArrayGetData(rp);
13951 
13952   // Acquire the array length and adjust the data pointer to be immediately
13953   // after the array length header
13954   RegI32 length = emitGcArrayGetLength(rdata, true);
13955 
13956   // Bounds check the index
13957   emitGcArrayBoundsCheck(index, length);
13958   freeI32(length);
13959 
13960   // Load the value
13961   uint32_t shift = arrayType.elementType_.indexingShift();
13962   if (IsShiftInScaleRange(shift)) {
13963     emitGcGet(arrayType.elementType_, extension,
13964               BaseIndex(rdata, index, ShiftToScale(shift), 0));
13965   } else {
13966     masm.lshiftPtr(Imm32(shift), index);
13967     emitGcGet(arrayType.elementType_, extension,
13968               BaseIndex(rdata, index, TimesOne, 0));
13969   }
13970 
13971   freePtr(rdata);
13972   freeRef(rp);
13973   freeI32(index);
13974 
13975   return true;
13976 }
13977 
emitArraySet()13978 bool BaseCompiler::emitArraySet() {
13979   uint32_t typeIndex;
13980   Nothing nothing;
13981   if (!iter_.readArraySet(&typeIndex, &nothing, &nothing, &nothing)) {
13982     return false;
13983   }
13984 
13985   if (deadCode_) {
13986     return true;
13987   }
13988 
13989   const ArrayType& arrayType = moduleEnv_.types[typeIndex].arrayType();
13990 
13991   // Reserve this register early if we will need it so that it is not taken by
13992   // any register used in this function.
13993   if (arrayType.elementType_.isReference()) {
13994     needPtr(RegPtr(PreBarrierReg));
13995   }
13996 
13997   AnyReg value = popAny();
13998   RegI32 index = popI32();
13999   RegRef rp = popRef();
14000 
14001   // We run out of registers on x86 with this instruction, so stash `value` on
14002   // the stack until it is needed later.
14003   pushAny(value);
14004 
14005   // Check for null
14006   emitGcNullCheck(rp);
14007 
14008   // Acquire the data pointer from the object
14009   RegPtr rdata = emitGcArrayGetData(rp);
14010 
14011   // Acquire the array length and adjust the data pointer to be immediately
14012   // after the array length header
14013   RegI32 length = emitGcArrayGetLength(rdata, true);
14014 
14015   // Free the barrier reg after we've allocated all registers
14016   if (arrayType.elementType_.isReference()) {
14017     freePtr(RegPtr(PreBarrierReg));
14018   }
14019 
14020   // Bounds check the index
14021   emitGcArrayBoundsCheck(index, length);
14022   freeI32(length);
14023 
14024   // Pull the value out of the stack now that we need it.
14025   popAny(value);
14026 
14027   // All registers are preserved. This isn't strictly necessary, as we'll just
14028   // be freeing them all after this is done. But this is needed for repeated
14029   // assignments used in array.new/new_default.
14030   if (!emitGcArraySet(rp, rdata, index, arrayType, value)) {
14031     return false;
14032   }
14033 
14034   freePtr(rdata);
14035   freeRef(rp);
14036   freeI32(index);
14037   freeAny(value);
14038 
14039   return true;
14040 }
14041 
emitArrayLen()14042 bool BaseCompiler::emitArrayLen() {
14043   uint32_t typeIndex;
14044   Nothing nothing;
14045   if (!iter_.readArrayLen(&typeIndex, &nothing)) {
14046     return false;
14047   }
14048 
14049   if (deadCode_) {
14050     return true;
14051   }
14052 
14053   RegRef rp = popRef();
14054 
14055   // Check for null
14056   emitGcNullCheck(rp);
14057 
14058   // Acquire the data pointer from the object
14059   RegPtr rdata = emitGcArrayGetData(rp);
14060   freeRef(rp);
14061 
14062   // Acquire the length from the array
14063   pushI32(emitGcArrayGetLength(rdata, false));
14064   freePtr(rdata);
14065 
14066   return true;
14067 }
14068 
emitRttCanon()14069 bool BaseCompiler::emitRttCanon() {
14070   ValType rttType;
14071   if (!iter_.readRttCanon(&rttType)) {
14072     return false;
14073   }
14074 
14075   if (deadCode_) {
14076     return true;
14077   }
14078 
14079   const TypeIdDesc& typeId = moduleEnv_.typeIds[rttType.typeIndex()];
14080   RegRef rp = needRef();
14081   fr.loadTlsPtr(WasmTlsReg);
14082   masm.loadWasmGlobalPtr(typeId.globalDataOffset(), rp);
14083   pushRef(rp);
14084   return true;
14085 }
14086 
emitRttSub()14087 bool BaseCompiler::emitRttSub() {
14088   // rttSub builtin has same signature as rtt.sub instruction, stack is
14089   // guaranteed to be in the right condition due to validation.
14090   return emitInstanceCallOp(SASigRttSub, [this]() -> bool {
14091     Nothing nothing;
14092     return iter_.readRttSub(&nothing);
14093   });
14094 }
14095 
emitRefTest()14096 bool BaseCompiler::emitRefTest() {
14097   // refTest builtin has same signature as ref.test instruction, stack is
14098   // guaranteed to be in the right condition due to validation.
14099   return emitInstanceCallOp(SASigRefTest, [this]() -> bool {
14100     Nothing nothing;
14101     uint32_t unusedRttTypeIndex;
14102     uint32_t unusedRttDepth;
14103     return iter_.readRefTest(&nothing, &unusedRttTypeIndex, &unusedRttDepth,
14104                              &nothing);
14105   });
14106 }
14107 
emitRefCast()14108 bool BaseCompiler::emitRefCast() {
14109   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
14110 
14111   Nothing nothing;
14112   uint32_t rttTypeIndex;
14113   uint32_t rttDepth;
14114   if (!iter_.readRefCast(&nothing, &rttTypeIndex, &rttDepth, &nothing)) {
14115     return false;
14116   }
14117 
14118   if (deadCode_) {
14119     return true;
14120   }
14121 
14122   RegRef rttPtr = popRef();
14123   RegRef refPtr = popRef();
14124 
14125   // 1. duplicate and shuffle from [ref, rtt] to [ref, ref, rtt]
14126   RegRef castedPtr = needRef();
14127   moveRef(refPtr, castedPtr);
14128   pushRef(castedPtr);
14129   pushRef(refPtr);
14130   pushRef(rttPtr);
14131 
14132   // 2. ref.test : [ref, rtt] -> [i32]
14133   if (!emitInstanceCall(lineOrBytecode, SASigRefTest)) {
14134     return false;
14135   }
14136 
14137   // 3. trap if result is zero, leaving [ref] as result
14138   RegI32 result = popI32();
14139   Label nonZero;
14140   masm.branchTest32(Assembler::NonZero, result, result, &nonZero);
14141   masm.wasmTrap(Trap::BadCast, bytecodeOffset());
14142   masm.bind(&nonZero);
14143   freeI32(result);
14144 
14145   return true;
14146 }
14147 
emitBrOnCast()14148 bool BaseCompiler::emitBrOnCast() {
14149   MOZ_ASSERT(!hasLatentOp());
14150 
14151   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
14152   uint32_t relativeDepth;
14153   Nothing unused{};
14154   NothingVector unused_values{};
14155   uint32_t rttTypeIndex;
14156   uint32_t rttDepth;
14157   ResultType branchTargetType;
14158   if (!iter_.readBrOnCast(&relativeDepth, &unused, &rttTypeIndex, &rttDepth,
14159                           &branchTargetType, &unused_values)) {
14160     return false;
14161   }
14162 
14163   if (deadCode_) {
14164     return true;
14165   }
14166 
14167   Control& target = controlItem(relativeDepth);
14168   target.bceSafeOnExit &= bceSafe_;
14169 
14170   RegRef rttPtr = popRef();
14171   RegRef refPtr = popRef();
14172 
14173   // 1. duplicate and shuffle from [T*, ref, rtt] to [T*, ref, ref, rtt]
14174   RegRef castedPtr = needRef();
14175   moveRef(refPtr, castedPtr);
14176   pushRef(castedPtr);
14177   pushRef(refPtr);
14178   pushRef(rttPtr);
14179 
14180   // 2. ref.test : [ref, rtt] -> [i32]
14181   if (!emitInstanceCall(lineOrBytecode, SASigRefTest)) {
14182     return false;
14183   }
14184 
14185   // 3. br_if $l : [T*, ref, i32] -> [T*, ref]
14186   BranchState b(&target.label, target.stackHeight, InvertBranch(false),
14187                 branchTargetType);
14188   if (b.hasBlockResults()) {
14189     needResultRegisters(b.resultType);
14190   }
14191   RegI32 condition = popI32();
14192   if (b.hasBlockResults()) {
14193     freeResultRegisters(b.resultType);
14194   }
14195   if (!jumpConditionalWithResults(&b, Assembler::NotEqual, condition,
14196                                   Imm32(0))) {
14197     return false;
14198   }
14199   freeI32(condition);
14200 
14201   return true;
14202 }
14203 
14204 #ifdef ENABLE_WASM_SIMD
14205 
14206 // Emitter trampolines used by abstracted SIMD operations.  Naming here follows
14207 // the SIMD spec pretty closely.
14208 
AndV128(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14209 static void AndV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14210   masm.bitwiseAndSimd128(rs, rsd);
14211 }
14212 
OrV128(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14213 static void OrV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14214   masm.bitwiseOrSimd128(rs, rsd);
14215 }
14216 
XorV128(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14217 static void XorV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14218   masm.bitwiseXorSimd128(rs, rsd);
14219 }
14220 
AddI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14221 static void AddI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14222   masm.addInt8x16(rs, rsd);
14223 }
14224 
AddI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14225 static void AddI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14226   masm.addInt16x8(rs, rsd);
14227 }
14228 
AddI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14229 static void AddI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14230   masm.addInt32x4(rs, rsd);
14231 }
14232 
AddF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14233 static void AddF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14234   masm.addFloat32x4(rs, rsd);
14235 }
14236 
AddI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14237 static void AddI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14238   masm.addInt64x2(rs, rsd);
14239 }
14240 
AddF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14241 static void AddF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14242   masm.addFloat64x2(rs, rsd);
14243 }
14244 
AddSatI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14245 static void AddSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14246   masm.addSatInt8x16(rs, rsd);
14247 }
14248 
AddSatUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14249 static void AddSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14250   masm.unsignedAddSatInt8x16(rs, rsd);
14251 }
14252 
AddSatI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14253 static void AddSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14254   masm.addSatInt16x8(rs, rsd);
14255 }
14256 
AddSatUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14257 static void AddSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14258   masm.unsignedAddSatInt16x8(rs, rsd);
14259 }
14260 
SubI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14261 static void SubI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14262   masm.subInt8x16(rs, rsd);
14263 }
14264 
SubI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14265 static void SubI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14266   masm.subInt16x8(rs, rsd);
14267 }
14268 
SubI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14269 static void SubI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14270   masm.subInt32x4(rs, rsd);
14271 }
14272 
SubF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14273 static void SubF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14274   masm.subFloat32x4(rs, rsd);
14275 }
14276 
SubI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14277 static void SubI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14278   masm.subInt64x2(rs, rsd);
14279 }
14280 
SubF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14281 static void SubF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14282   masm.subFloat64x2(rs, rsd);
14283 }
14284 
SubSatI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14285 static void SubSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14286   masm.subSatInt8x16(rs, rsd);
14287 }
14288 
SubSatUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14289 static void SubSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14290   masm.unsignedSubSatInt8x16(rs, rsd);
14291 }
14292 
SubSatI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14293 static void SubSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14294   masm.subSatInt16x8(rs, rsd);
14295 }
14296 
SubSatUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14297 static void SubSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14298   masm.unsignedSubSatInt16x8(rs, rsd);
14299 }
14300 
MulI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14301 static void MulI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14302   masm.mulInt16x8(rs, rsd);
14303 }
14304 
MulI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14305 static void MulI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14306   masm.mulInt32x4(rs, rsd);
14307 }
14308 
MulF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14309 static void MulF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14310   masm.mulFloat32x4(rs, rsd);
14311 }
14312 
14313 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
MulI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp)14314 static void MulI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14315                      RegV128 temp) {
14316   masm.mulInt64x2(rsd, rs, rsd, temp);
14317 }
14318 #  elif defined(JS_CODEGEN_ARM64)
MulI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14319 static void MulI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14320                      RegV128 temp1, RegV128 temp2) {
14321   masm.mulInt64x2(rsd, rs, rsd, temp1, temp2);
14322 }
14323 #  endif
14324 
MulF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14325 static void MulF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14326   masm.mulFloat64x2(rs, rsd);
14327 }
14328 
DivF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14329 static void DivF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14330   masm.divFloat32x4(rs, rsd);
14331 }
14332 
DivF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14333 static void DivF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14334   masm.divFloat64x2(rs, rsd);
14335 }
14336 
14337 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
MinF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14338 static void MinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14339                      RegV128 temp1, RegV128 temp2) {
14340   masm.minFloat32x4(rs, rsd, temp1, temp2);
14341 }
14342 
MinF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14343 static void MinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14344                      RegV128 temp1, RegV128 temp2) {
14345   masm.minFloat64x2(rs, rsd, temp1, temp2);
14346 }
14347 
MaxF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14348 static void MaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14349                      RegV128 temp1, RegV128 temp2) {
14350   masm.maxFloat32x4(rs, rsd, temp1, temp2);
14351 }
14352 
MaxF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14353 static void MaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd,
14354                      RegV128 temp1, RegV128 temp2) {
14355   masm.maxFloat64x2(rs, rsd, temp1, temp2);
14356 }
14357 
PMinF32x4(MacroAssembler & masm,RegV128 rsd,RegV128 rs,RhsDestOp)14358 static void PMinF32x4(MacroAssembler& masm, RegV128 rsd, RegV128 rs,
14359                       RhsDestOp) {
14360   masm.pseudoMinFloat32x4(rsd, rs);
14361 }
14362 
PMinF64x2(MacroAssembler & masm,RegV128 rsd,RegV128 rs,RhsDestOp)14363 static void PMinF64x2(MacroAssembler& masm, RegV128 rsd, RegV128 rs,
14364                       RhsDestOp) {
14365   masm.pseudoMinFloat64x2(rsd, rs);
14366 }
14367 
PMaxF32x4(MacroAssembler & masm,RegV128 rsd,RegV128 rs,RhsDestOp)14368 static void PMaxF32x4(MacroAssembler& masm, RegV128 rsd, RegV128 rs,
14369                       RhsDestOp) {
14370   masm.pseudoMaxFloat32x4(rsd, rs);
14371 }
14372 
PMaxF64x2(MacroAssembler & masm,RegV128 rsd,RegV128 rs,RhsDestOp)14373 static void PMaxF64x2(MacroAssembler& masm, RegV128 rsd, RegV128 rs,
14374                       RhsDestOp) {
14375   masm.pseudoMaxFloat64x2(rsd, rs);
14376 }
14377 #  elif defined(JS_CODEGEN_ARM64)
MinF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14378 static void MinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14379   masm.minFloat32x4(rs, rsd);
14380 }
14381 
MinF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14382 static void MinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14383   masm.minFloat64x2(rs, rsd);
14384 }
14385 
MaxF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14386 static void MaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14387   masm.maxFloat32x4(rs, rsd);
14388 }
14389 
MaxF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14390 static void MaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14391   masm.maxFloat64x2(rs, rsd);
14392 }
14393 
PMinF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14394 static void PMinF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14395   masm.pseudoMinFloat32x4(rs, rsd);
14396 }
14397 
PMinF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14398 static void PMinF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14399   masm.pseudoMinFloat64x2(rs, rsd);
14400 }
14401 
PMaxF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14402 static void PMaxF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14403   masm.pseudoMaxFloat32x4(rs, rsd);
14404 }
14405 
PMaxF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14406 static void PMaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14407   masm.pseudoMaxFloat64x2(rs, rsd);
14408 }
14409 #  endif
14410 
DotI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14411 static void DotI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14412   masm.widenDotInt16x8(rs, rsd);
14413 }
14414 
ExtMulLowI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14415 static void ExtMulLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14416   masm.extMulLowInt8x16(rs, rsd);
14417 }
14418 
ExtMulHighI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14419 static void ExtMulHighI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14420   masm.extMulHighInt8x16(rs, rsd);
14421 }
14422 
ExtMulLowUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14423 static void ExtMulLowUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14424   masm.unsignedExtMulLowInt8x16(rs, rsd);
14425 }
14426 
ExtMulHighUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14427 static void ExtMulHighUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14428   masm.unsignedExtMulHighInt8x16(rs, rsd);
14429 }
14430 
ExtMulLowI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14431 static void ExtMulLowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14432   masm.extMulLowInt16x8(rs, rsd);
14433 }
14434 
ExtMulHighI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14435 static void ExtMulHighI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14436   masm.extMulHighInt16x8(rs, rsd);
14437 }
14438 
ExtMulLowUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14439 static void ExtMulLowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14440   masm.unsignedExtMulLowInt16x8(rs, rsd);
14441 }
14442 
ExtMulHighUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14443 static void ExtMulHighUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14444   masm.unsignedExtMulHighInt16x8(rs, rsd);
14445 }
14446 
ExtMulLowI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14447 static void ExtMulLowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14448   masm.extMulLowInt32x4(rs, rsd);
14449 }
14450 
ExtMulHighI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14451 static void ExtMulHighI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14452   masm.extMulHighInt32x4(rs, rsd);
14453 }
14454 
ExtMulLowUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14455 static void ExtMulLowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14456   masm.unsignedExtMulLowInt32x4(rs, rsd);
14457 }
14458 
ExtMulHighUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14459 static void ExtMulHighUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14460   masm.unsignedExtMulHighInt32x4(rs, rsd);
14461 }
14462 
Q15MulrSatS(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14463 static void Q15MulrSatS(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14464   masm.q15MulrSatInt16x8(rs, rsd);
14465 }
14466 
CmpI8x16(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14467 static void CmpI8x16(MacroAssembler& masm, Assembler::Condition cond,
14468                      RegV128 rs, RegV128 rsd) {
14469   masm.compareInt8x16(cond, rs, rsd);
14470 }
14471 
CmpI16x8(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14472 static void CmpI16x8(MacroAssembler& masm, Assembler::Condition cond,
14473                      RegV128 rs, RegV128 rsd) {
14474   masm.compareInt16x8(cond, rs, rsd);
14475 }
14476 
CmpI32x4(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14477 static void CmpI32x4(MacroAssembler& masm, Assembler::Condition cond,
14478                      RegV128 rs, RegV128 rsd) {
14479   masm.compareInt32x4(cond, rs, rsd);
14480 }
14481 
14482 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
CmpI64x2ForEquality(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14483 static void CmpI64x2ForEquality(MacroAssembler& masm, Assembler::Condition cond,
14484                                 RegV128 rs, RegV128 rsd) {
14485   masm.compareForEqualityInt64x2(cond, rs, rsd);
14486 }
14487 
CmpI64x2ForOrdering(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd,RegV128 temp1,RegV128 temp2)14488 static void CmpI64x2ForOrdering(MacroAssembler& masm, Assembler::Condition cond,
14489                                 RegV128 rs, RegV128 rsd, RegV128 temp1,
14490                                 RegV128 temp2) {
14491   masm.compareForOrderingInt64x2(cond, rs, rsd, temp1, temp2);
14492 }
14493 #  else
CmpI64x2ForEquality(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14494 static void CmpI64x2ForEquality(MacroAssembler& masm, Assembler::Condition cond,
14495                                 RegV128 rs, RegV128 rsd) {
14496   masm.compareInt64x2(cond, rs, rsd);
14497 }
14498 
CmpI64x2ForOrdering(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14499 static void CmpI64x2ForOrdering(MacroAssembler& masm, Assembler::Condition cond,
14500                                 RegV128 rs, RegV128 rsd) {
14501   masm.compareInt64x2(cond, rs, rsd);
14502 }
14503 #  endif  // JS_CODEGEN_X86 || JS_CODEGEN_X64
14504 
CmpUI8x16(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14505 static void CmpUI8x16(MacroAssembler& masm, Assembler::Condition cond,
14506                       RegV128 rs, RegV128 rsd) {
14507   masm.compareInt8x16(cond, rs, rsd);
14508 }
14509 
CmpUI16x8(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14510 static void CmpUI16x8(MacroAssembler& masm, Assembler::Condition cond,
14511                       RegV128 rs, RegV128 rsd) {
14512   masm.compareInt16x8(cond, rs, rsd);
14513 }
14514 
CmpUI32x4(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14515 static void CmpUI32x4(MacroAssembler& masm, Assembler::Condition cond,
14516                       RegV128 rs, RegV128 rsd) {
14517   masm.compareInt32x4(cond, rs, rsd);
14518 }
14519 
CmpF32x4(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14520 static void CmpF32x4(MacroAssembler& masm, Assembler::Condition cond,
14521                      RegV128 rs, RegV128 rsd) {
14522   masm.compareFloat32x4(cond, rs, rsd);
14523 }
14524 
CmpF64x2(MacroAssembler & masm,Assembler::Condition cond,RegV128 rs,RegV128 rsd)14525 static void CmpF64x2(MacroAssembler& masm, Assembler::Condition cond,
14526                      RegV128 rs, RegV128 rsd) {
14527   masm.compareFloat64x2(cond, rs, rsd);
14528 }
14529 
NegI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14530 static void NegI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14531   masm.negInt8x16(rs, rd);
14532 }
14533 
NegI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14534 static void NegI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14535   masm.negInt16x8(rs, rd);
14536 }
14537 
NegI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14538 static void NegI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14539   masm.negInt32x4(rs, rd);
14540 }
14541 
NegI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14542 static void NegI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14543   masm.negInt64x2(rs, rd);
14544 }
14545 
NegF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14546 static void NegF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14547   masm.negFloat32x4(rs, rd);
14548 }
14549 
NegF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14550 static void NegF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14551   masm.negFloat64x2(rs, rd);
14552 }
14553 
AbsF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14554 static void AbsF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14555   masm.absFloat32x4(rs, rd);
14556 }
14557 
AbsF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14558 static void AbsF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14559   masm.absFloat64x2(rs, rd);
14560 }
14561 
SqrtF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14562 static void SqrtF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14563   masm.sqrtFloat32x4(rs, rd);
14564 }
14565 
SqrtF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14566 static void SqrtF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14567   masm.sqrtFloat64x2(rs, rd);
14568 }
14569 
CeilF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14570 static void CeilF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14571   masm.ceilFloat32x4(rs, rd);
14572 }
14573 
FloorF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14574 static void FloorF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14575   masm.floorFloat32x4(rs, rd);
14576 }
14577 
TruncF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14578 static void TruncF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14579   masm.truncFloat32x4(rs, rd);
14580 }
14581 
NearestF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14582 static void NearestF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14583   masm.nearestFloat32x4(rs, rd);
14584 }
14585 
CeilF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14586 static void CeilF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14587   masm.ceilFloat64x2(rs, rd);
14588 }
14589 
FloorF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14590 static void FloorF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14591   masm.floorFloat64x2(rs, rd);
14592 }
14593 
TruncF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14594 static void TruncF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14595   masm.truncFloat64x2(rs, rd);
14596 }
14597 
NearestF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14598 static void NearestF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14599   masm.nearestFloat64x2(rs, rd);
14600 }
14601 
NotV128(MacroAssembler & masm,RegV128 rs,RegV128 rd)14602 static void NotV128(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14603   masm.bitwiseNotSimd128(rs, rd);
14604 }
14605 
ExtAddPairwiseI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14606 static void ExtAddPairwiseI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14607   masm.extAddPairwiseInt8x16(rs, rsd);
14608 }
14609 
ExtAddPairwiseUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14610 static void ExtAddPairwiseUI8x16(MacroAssembler& masm, RegV128 rs,
14611                                  RegV128 rsd) {
14612   masm.unsignedExtAddPairwiseInt8x16(rs, rsd);
14613 }
14614 
ExtAddPairwiseI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14615 static void ExtAddPairwiseI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14616   masm.extAddPairwiseInt16x8(rs, rsd);
14617 }
14618 
ExtAddPairwiseUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14619 static void ExtAddPairwiseUI16x8(MacroAssembler& masm, RegV128 rs,
14620                                  RegV128 rsd) {
14621   masm.unsignedExtAddPairwiseInt16x8(rs, rsd);
14622 }
14623 
14624 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
ShiftLeftI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp1,RegV128 temp2)14625 static void ShiftLeftI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14626                            RegI32 temp1, RegV128 temp2) {
14627   masm.leftShiftInt8x16(rs, rsd, temp1, temp2);
14628 }
14629 
ShiftLeftI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14630 static void ShiftLeftI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14631                            RegI32 temp) {
14632   masm.leftShiftInt16x8(rs, rsd, temp);
14633 }
14634 
ShiftLeftI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14635 static void ShiftLeftI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14636                            RegI32 temp) {
14637   masm.leftShiftInt32x4(rs, rsd, temp);
14638 }
14639 
ShiftLeftI64x2(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14640 static void ShiftLeftI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14641                            RegI32 temp) {
14642   masm.leftShiftInt64x2(rs, rsd, temp);
14643 }
14644 
ShiftRightI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp1,RegV128 temp2)14645 static void ShiftRightI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14646                             RegI32 temp1, RegV128 temp2) {
14647   masm.rightShiftInt8x16(rs, rsd, temp1, temp2);
14648 }
14649 
ShiftRightUI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp1,RegV128 temp2)14650 static void ShiftRightUI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14651                              RegI32 temp1, RegV128 temp2) {
14652   masm.unsignedRightShiftInt8x16(rs, rsd, temp1, temp2);
14653 }
14654 
ShiftRightI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14655 static void ShiftRightI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14656                             RegI32 temp) {
14657   masm.rightShiftInt16x8(rs, rsd, temp);
14658 }
14659 
ShiftRightUI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14660 static void ShiftRightUI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14661                              RegI32 temp) {
14662   masm.unsignedRightShiftInt16x8(rs, rsd, temp);
14663 }
14664 
ShiftRightI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14665 static void ShiftRightI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14666                             RegI32 temp) {
14667   masm.rightShiftInt32x4(rs, rsd, temp);
14668 }
14669 
ShiftRightUI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14670 static void ShiftRightUI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14671                              RegI32 temp) {
14672   masm.unsignedRightShiftInt32x4(rs, rsd, temp);
14673 }
14674 
ShiftRightUI64x2(MacroAssembler & masm,RegI32 rs,RegV128 rsd,RegI32 temp)14675 static void ShiftRightUI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd,
14676                              RegI32 temp) {
14677   masm.unsignedRightShiftInt64x2(rs, rsd, temp);
14678 }
14679 #  elif defined(JS_CODEGEN_ARM64)
ShiftLeftI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14680 static void ShiftLeftI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14681   masm.leftShiftInt8x16(rsd, rs, rsd);
14682 }
14683 
ShiftLeftI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14684 static void ShiftLeftI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14685   masm.leftShiftInt16x8(rsd, rs, rsd);
14686 }
14687 
ShiftLeftI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14688 static void ShiftLeftI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14689   masm.leftShiftInt32x4(rsd, rs, rsd);
14690 }
14691 
ShiftLeftI64x2(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14692 static void ShiftLeftI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14693   masm.leftShiftInt64x2(rsd, rs, rsd);
14694 }
14695 
ShiftRightI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14696 static void ShiftRightI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14697   masm.rightShiftInt8x16(rsd, rs, rsd);
14698 }
14699 
ShiftRightUI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14700 static void ShiftRightUI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14701   masm.unsignedRightShiftInt8x16(rsd, rs, rsd);
14702 }
14703 
ShiftRightI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14704 static void ShiftRightI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14705   masm.rightShiftInt16x8(rsd, rs, rsd);
14706 }
14707 
ShiftRightUI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14708 static void ShiftRightUI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14709   masm.unsignedRightShiftInt16x8(rsd, rs, rsd);
14710 }
14711 
ShiftRightI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14712 static void ShiftRightI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14713   masm.rightShiftInt32x4(rsd, rs, rsd);
14714 }
14715 
ShiftRightUI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14716 static void ShiftRightUI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14717   masm.unsignedRightShiftInt32x4(rsd, rs, rsd);
14718 }
14719 
ShiftRightI64x2(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14720 static void ShiftRightI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14721   masm.rightShiftInt64x2(rsd, rs, rsd);
14722 }
14723 
ShiftRightUI64x2(MacroAssembler & masm,RegI32 rs,RegV128 rsd)14724 static void ShiftRightUI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd) {
14725   masm.unsignedRightShiftInt64x2(rsd, rs, rsd);
14726 }
14727 #  endif
14728 
AverageUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14729 static void AverageUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14730   masm.unsignedAverageInt8x16(rs, rsd);
14731 }
14732 
AverageUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14733 static void AverageUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14734   masm.unsignedAverageInt16x8(rs, rsd);
14735 }
14736 
MinI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14737 static void MinI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14738   masm.minInt8x16(rs, rsd);
14739 }
14740 
MinUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14741 static void MinUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14742   masm.unsignedMinInt8x16(rs, rsd);
14743 }
14744 
MaxI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14745 static void MaxI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14746   masm.maxInt8x16(rs, rsd);
14747 }
14748 
MaxUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14749 static void MaxUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14750   masm.unsignedMaxInt8x16(rs, rsd);
14751 }
14752 
MinI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14753 static void MinI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14754   masm.minInt16x8(rs, rsd);
14755 }
14756 
MinUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14757 static void MinUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14758   masm.unsignedMinInt16x8(rs, rsd);
14759 }
14760 
MaxI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14761 static void MaxI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14762   masm.maxInt16x8(rs, rsd);
14763 }
14764 
MaxUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14765 static void MaxUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14766   masm.unsignedMaxInt16x8(rs, rsd);
14767 }
14768 
MinI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14769 static void MinI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14770   masm.minInt32x4(rs, rsd);
14771 }
14772 
MinUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14773 static void MinUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14774   masm.unsignedMinInt32x4(rs, rsd);
14775 }
14776 
MaxI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14777 static void MaxI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14778   masm.maxInt32x4(rs, rsd);
14779 }
14780 
MaxUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14781 static void MaxUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14782   masm.unsignedMaxInt32x4(rs, rsd);
14783 }
14784 
NarrowI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14785 static void NarrowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14786   masm.narrowInt16x8(rs, rsd);
14787 }
14788 
NarrowUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14789 static void NarrowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14790   masm.unsignedNarrowInt16x8(rs, rsd);
14791 }
14792 
NarrowI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14793 static void NarrowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14794   masm.narrowInt32x4(rs, rsd);
14795 }
14796 
NarrowUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rsd)14797 static void NarrowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
14798   masm.unsignedNarrowInt32x4(rs, rsd);
14799 }
14800 
WidenLowI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14801 static void WidenLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14802   masm.widenLowInt8x16(rs, rd);
14803 }
14804 
WidenHighI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14805 static void WidenHighI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14806   masm.widenHighInt8x16(rs, rd);
14807 }
14808 
WidenLowUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14809 static void WidenLowUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14810   masm.unsignedWidenLowInt8x16(rs, rd);
14811 }
14812 
WidenHighUI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14813 static void WidenHighUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14814   masm.unsignedWidenHighInt8x16(rs, rd);
14815 }
14816 
WidenLowI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14817 static void WidenLowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14818   masm.widenLowInt16x8(rs, rd);
14819 }
14820 
WidenHighI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14821 static void WidenHighI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14822   masm.widenHighInt16x8(rs, rd);
14823 }
14824 
WidenLowUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14825 static void WidenLowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14826   masm.unsignedWidenLowInt16x8(rs, rd);
14827 }
14828 
WidenHighUI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14829 static void WidenHighUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14830   masm.unsignedWidenHighInt16x8(rs, rd);
14831 }
14832 
WidenLowI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14833 static void WidenLowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14834   masm.widenLowInt32x4(rs, rd);
14835 }
14836 
WidenHighI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14837 static void WidenHighI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14838   masm.widenHighInt32x4(rs, rd);
14839 }
14840 
WidenLowUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14841 static void WidenLowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14842   masm.unsignedWidenLowInt32x4(rs, rd);
14843 }
14844 
WidenHighUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14845 static void WidenHighUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14846   masm.unsignedWidenHighInt32x4(rs, rd);
14847 }
14848 
14849 #  if defined(JS_CODEGEN_ARM64)
PopcntI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14850 static void PopcntI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14851   masm.popcntInt8x16(rs, rd);
14852 }
14853 #  else
PopcntI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd,RegV128 temp)14854 static void PopcntI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd,
14855                         RegV128 temp) {
14856   masm.popcntInt8x16(rs, rd, temp);
14857 }
14858 #  endif  // JS_CODEGEN_ARM64
14859 
AbsI8x16(MacroAssembler & masm,RegV128 rs,RegV128 rd)14860 static void AbsI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14861   masm.absInt8x16(rs, rd);
14862 }
14863 
AbsI16x8(MacroAssembler & masm,RegV128 rs,RegV128 rd)14864 static void AbsI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14865   masm.absInt16x8(rs, rd);
14866 }
14867 
AbsI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)14868 static void AbsI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14869   masm.absInt32x4(rs, rd);
14870 }
14871 
AbsI64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)14872 static void AbsI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
14873   masm.absInt64x2(rs, rd);
14874 }
14875 
ExtractLaneI8x16(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI32 rd)14876 static void ExtractLaneI8x16(MacroAssembler& masm, uint32_t laneIndex,
14877                              RegV128 rs, RegI32 rd) {
14878   masm.extractLaneInt8x16(laneIndex, rs, rd);
14879 }
14880 
ExtractLaneUI8x16(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI32 rd)14881 static void ExtractLaneUI8x16(MacroAssembler& masm, uint32_t laneIndex,
14882                               RegV128 rs, RegI32 rd) {
14883   masm.unsignedExtractLaneInt8x16(laneIndex, rs, rd);
14884 }
14885 
ExtractLaneI16x8(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI32 rd)14886 static void ExtractLaneI16x8(MacroAssembler& masm, uint32_t laneIndex,
14887                              RegV128 rs, RegI32 rd) {
14888   masm.extractLaneInt16x8(laneIndex, rs, rd);
14889 }
14890 
ExtractLaneUI16x8(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI32 rd)14891 static void ExtractLaneUI16x8(MacroAssembler& masm, uint32_t laneIndex,
14892                               RegV128 rs, RegI32 rd) {
14893   masm.unsignedExtractLaneInt16x8(laneIndex, rs, rd);
14894 }
14895 
ExtractLaneI32x4(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI32 rd)14896 static void ExtractLaneI32x4(MacroAssembler& masm, uint32_t laneIndex,
14897                              RegV128 rs, RegI32 rd) {
14898   masm.extractLaneInt32x4(laneIndex, rs, rd);
14899 }
14900 
ExtractLaneI64x2(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegI64 rd)14901 static void ExtractLaneI64x2(MacroAssembler& masm, uint32_t laneIndex,
14902                              RegV128 rs, RegI64 rd) {
14903   masm.extractLaneInt64x2(laneIndex, rs, rd);
14904 }
14905 
ExtractLaneF32x4(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegF32 rd)14906 static void ExtractLaneF32x4(MacroAssembler& masm, uint32_t laneIndex,
14907                              RegV128 rs, RegF32 rd) {
14908   masm.extractLaneFloat32x4(laneIndex, rs, rd);
14909 }
14910 
ExtractLaneF64x2(MacroAssembler & masm,uint32_t laneIndex,RegV128 rs,RegF64 rd)14911 static void ExtractLaneF64x2(MacroAssembler& masm, uint32_t laneIndex,
14912                              RegV128 rs, RegF64 rd) {
14913   masm.extractLaneFloat64x2(laneIndex, rs, rd);
14914 }
14915 
ReplaceLaneI8x16(MacroAssembler & masm,uint32_t laneIndex,RegI32 rs,RegV128 rsd)14916 static void ReplaceLaneI8x16(MacroAssembler& masm, uint32_t laneIndex,
14917                              RegI32 rs, RegV128 rsd) {
14918   masm.replaceLaneInt8x16(laneIndex, rs, rsd);
14919 }
14920 
ReplaceLaneI16x8(MacroAssembler & masm,uint32_t laneIndex,RegI32 rs,RegV128 rsd)14921 static void ReplaceLaneI16x8(MacroAssembler& masm, uint32_t laneIndex,
14922                              RegI32 rs, RegV128 rsd) {
14923   masm.replaceLaneInt16x8(laneIndex, rs, rsd);
14924 }
14925 
ReplaceLaneI32x4(MacroAssembler & masm,uint32_t laneIndex,RegI32 rs,RegV128 rsd)14926 static void ReplaceLaneI32x4(MacroAssembler& masm, uint32_t laneIndex,
14927                              RegI32 rs, RegV128 rsd) {
14928   masm.replaceLaneInt32x4(laneIndex, rs, rsd);
14929 }
14930 
ReplaceLaneI64x2(MacroAssembler & masm,uint32_t laneIndex,RegI64 rs,RegV128 rsd)14931 static void ReplaceLaneI64x2(MacroAssembler& masm, uint32_t laneIndex,
14932                              RegI64 rs, RegV128 rsd) {
14933   masm.replaceLaneInt64x2(laneIndex, rs, rsd);
14934 }
14935 
ReplaceLaneF32x4(MacroAssembler & masm,uint32_t laneIndex,RegF32 rs,RegV128 rsd)14936 static void ReplaceLaneF32x4(MacroAssembler& masm, uint32_t laneIndex,
14937                              RegF32 rs, RegV128 rsd) {
14938   masm.replaceLaneFloat32x4(laneIndex, rs, rsd);
14939 }
14940 
ReplaceLaneF64x2(MacroAssembler & masm,uint32_t laneIndex,RegF64 rs,RegV128 rsd)14941 static void ReplaceLaneF64x2(MacroAssembler& masm, uint32_t laneIndex,
14942                              RegF64 rs, RegV128 rsd) {
14943   masm.replaceLaneFloat64x2(laneIndex, rs, rsd);
14944 }
14945 
SplatI8x16(MacroAssembler & masm,RegI32 rs,RegV128 rd)14946 static void SplatI8x16(MacroAssembler& masm, RegI32 rs, RegV128 rd) {
14947   masm.splatX16(rs, rd);
14948 }
14949 
SplatI16x8(MacroAssembler & masm,RegI32 rs,RegV128 rd)14950 static void SplatI16x8(MacroAssembler& masm, RegI32 rs, RegV128 rd) {
14951   masm.splatX8(rs, rd);
14952 }
14953 
SplatI32x4(MacroAssembler & masm,RegI32 rs,RegV128 rd)14954 static void SplatI32x4(MacroAssembler& masm, RegI32 rs, RegV128 rd) {
14955   masm.splatX4(rs, rd);
14956 }
14957 
SplatI64x2(MacroAssembler & masm,RegI64 rs,RegV128 rd)14958 static void SplatI64x2(MacroAssembler& masm, RegI64 rs, RegV128 rd) {
14959   masm.splatX2(rs, rd);
14960 }
14961 
SplatF32x4(MacroAssembler & masm,RegF32 rs,RegV128 rd)14962 static void SplatF32x4(MacroAssembler& masm, RegF32 rs, RegV128 rd) {
14963   masm.splatX4(rs, rd);
14964 }
14965 
SplatF64x2(MacroAssembler & masm,RegF64 rs,RegV128 rd)14966 static void SplatF64x2(MacroAssembler& masm, RegF64 rs, RegV128 rd) {
14967   masm.splatX2(rs, rd);
14968 }
14969 
14970 // This is the same op independent of lanes: it tests for any nonzero bit.
AnyTrue(MacroAssembler & masm,RegV128 rs,RegI32 rd)14971 static void AnyTrue(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14972   masm.anyTrueSimd128(rs, rd);
14973 }
14974 
AllTrueI8x16(MacroAssembler & masm,RegV128 rs,RegI32 rd)14975 static void AllTrueI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14976   masm.allTrueInt8x16(rs, rd);
14977 }
14978 
AllTrueI16x8(MacroAssembler & masm,RegV128 rs,RegI32 rd)14979 static void AllTrueI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14980   masm.allTrueInt16x8(rs, rd);
14981 }
14982 
AllTrueI32x4(MacroAssembler & masm,RegV128 rs,RegI32 rd)14983 static void AllTrueI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14984   masm.allTrueInt32x4(rs, rd);
14985 }
14986 
AllTrueI64x2(MacroAssembler & masm,RegV128 rs,RegI32 rd)14987 static void AllTrueI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14988   masm.allTrueInt64x2(rs, rd);
14989 }
14990 
14991 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
BitmaskI8x16(MacroAssembler & masm,RegV128 rs,RegI32 rd)14992 static void BitmaskI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14993   masm.bitmaskInt8x16(rs, rd);
14994 }
14995 
BitmaskI16x8(MacroAssembler & masm,RegV128 rs,RegI32 rd)14996 static void BitmaskI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
14997   masm.bitmaskInt16x8(rs, rd);
14998 }
14999 
BitmaskI32x4(MacroAssembler & masm,RegV128 rs,RegI32 rd)15000 static void BitmaskI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
15001   masm.bitmaskInt32x4(rs, rd);
15002 }
15003 
BitmaskI64x2(MacroAssembler & masm,RegV128 rs,RegI32 rd)15004 static void BitmaskI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd) {
15005   masm.bitmaskInt64x2(rs, rd);
15006 }
15007 #  elif defined(JS_CODEGEN_ARM64)
BitmaskI8x16(MacroAssembler & masm,RegV128 rs,RegI32 rd,RegV128 temp)15008 static void BitmaskI8x16(MacroAssembler& masm, RegV128 rs, RegI32 rd,
15009                          RegV128 temp) {
15010   masm.bitmaskInt8x16(rs, rd, temp);
15011 }
15012 
BitmaskI16x8(MacroAssembler & masm,RegV128 rs,RegI32 rd,RegV128 temp)15013 static void BitmaskI16x8(MacroAssembler& masm, RegV128 rs, RegI32 rd,
15014                          RegV128 temp) {
15015   masm.bitmaskInt16x8(rs, rd, temp);
15016 }
15017 
BitmaskI32x4(MacroAssembler & masm,RegV128 rs,RegI32 rd,RegV128 temp)15018 static void BitmaskI32x4(MacroAssembler& masm, RegV128 rs, RegI32 rd,
15019                          RegV128 temp) {
15020   masm.bitmaskInt32x4(rs, rd, temp);
15021 }
15022 
BitmaskI64x2(MacroAssembler & masm,RegV128 rs,RegI32 rd,RegV128 temp)15023 static void BitmaskI64x2(MacroAssembler& masm, RegV128 rs, RegI32 rd,
15024                          RegV128 temp) {
15025   masm.bitmaskInt64x2(rs, rd, temp);
15026 }
15027 #  endif
15028 
Swizzle(MacroAssembler & masm,RegV128 rs,RegV128 rsd)15029 static void Swizzle(MacroAssembler& masm, RegV128 rs, RegV128 rsd) {
15030   masm.swizzleInt8x16(rs, rsd);
15031 }
15032 
ConvertI32x4ToF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)15033 static void ConvertI32x4ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15034   masm.convertInt32x4ToFloat32x4(rs, rd);
15035 }
15036 
ConvertUI32x4ToF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)15037 static void ConvertUI32x4ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15038   masm.unsignedConvertInt32x4ToFloat32x4(rs, rd);
15039 }
15040 
ConvertF32x4ToI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)15041 static void ConvertF32x4ToI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15042   masm.truncSatFloat32x4ToInt32x4(rs, rd);
15043 }
15044 
15045 #  if defined(JS_CODEGEN_ARM64)
ConvertF32x4ToUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)15046 static void ConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15047   masm.unsignedTruncSatFloat32x4ToInt32x4(rs, rd);
15048 }
15049 #  else
ConvertF32x4ToUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd,RegV128 temp)15050 static void ConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd,
15051                                  RegV128 temp) {
15052   masm.unsignedTruncSatFloat32x4ToInt32x4(rs, rd, temp);
15053 }
15054 #  endif  // JS_CODEGEN_ARM64
15055 
ConvertI32x4ToF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)15056 static void ConvertI32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15057   masm.convertInt32x4ToFloat64x2(rs, rd);
15058 }
15059 
ConvertUI32x4ToF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)15060 static void ConvertUI32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15061   masm.unsignedConvertInt32x4ToFloat64x2(rs, rd);
15062 }
15063 
ConvertF64x2ToI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd,RegV128 temp)15064 static void ConvertF64x2ToI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd,
15065                                 RegV128 temp) {
15066   masm.truncSatFloat64x2ToInt32x4(rs, rd, temp);
15067 }
15068 
ConvertF64x2ToUI32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd,RegV128 temp)15069 static void ConvertF64x2ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd,
15070                                  RegV128 temp) {
15071   masm.unsignedTruncSatFloat64x2ToInt32x4(rs, rd, temp);
15072 }
15073 
DemoteF64x2ToF32x4(MacroAssembler & masm,RegV128 rs,RegV128 rd)15074 static void DemoteF64x2ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15075   masm.convertFloat64x2ToFloat32x4(rs, rd);
15076 }
15077 
PromoteF32x4ToF64x2(MacroAssembler & masm,RegV128 rs,RegV128 rd)15078 static void PromoteF32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
15079   masm.convertFloat32x4ToFloat64x2(rs, rd);
15080 }
15081 
15082 // Bitselect: rs1: ifTrue, rs2: ifFalse, rs3: control
15083 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
BitselectV128(BaseCompiler & bc,RegV128 rs1,RegV128 rs2,RegV128 rs3)15084 static RegV128 BitselectV128(BaseCompiler& bc, RegV128 rs1, RegV128 rs2,
15085                              RegV128 rs3) {
15086   // On x86, certain register assignments will result in more compact code: we
15087   // want output=rs1 and tmp=rs3.  Attend to this after we see what other
15088   // platforms want/need.
15089   RegV128 tmp = bc.needV128();  // Distinguished tmp, for now
15090   bc.masm.bitwiseSelectSimd128(rs3, rs1, rs2, rs1, tmp);
15091   bc.freeV128(tmp);
15092   return rs1;
15093 }
15094 #  elif defined(JS_CODEGEN_ARM64)
BitselectV128(BaseCompiler & bc,RegV128 rs1,RegV128 rs2,RegV128 rs3)15095 static RegV128 BitselectV128(BaseCompiler& bc, RegV128 rs1, RegV128 rs2,
15096                              RegV128 rs3) {
15097   bc.masm.bitwiseSelectSimd128(rs1, rs2, rs3);
15098   return rs3;
15099 }
15100 #  endif
15101 
emitVectorAndNot()15102 void BaseCompiler::emitVectorAndNot() {
15103   // We want x & ~y but the available operation is ~x & y, so reverse the
15104   // operands.
15105   RegV128 r, rs;
15106   pop2xV128(&r, &rs);
15107   masm.bitwiseNotAndSimd128(r, rs);
15108   freeV128(r);
15109   pushV128(rs);
15110 }
15111 
emitLoadSplat(Scalar::Type viewType)15112 bool BaseCompiler::emitLoadSplat(Scalar::Type viewType) {
15113   // We can implement loadSplat mostly as load + splat because the push of the
15114   // result onto the value stack in loadCommon normally will not generate any
15115   // code, it will leave the value in a register which we will consume.
15116 
15117   LinearMemoryAddress<Nothing> addr;
15118   if (!iter_.readLoadSplat(Scalar::byteSize(viewType), &addr)) {
15119     return false;
15120   }
15121 
15122   if (deadCode_) {
15123     return true;
15124   }
15125 
15126   // We use uint types when we can on the general assumption that unsigned loads
15127   // might be smaller/faster on some platforms, because no sign extension needs
15128   // to be done after the sub-register load.
15129 
15130   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
15131   switch (viewType) {
15132     case Scalar::Uint8:
15133       if (!loadCommon(&access, AccessCheck(), ValType::I32)) {
15134         return false;
15135       }
15136       emitUnop(SplatI8x16);
15137       break;
15138     case Scalar::Uint16:
15139       if (!loadCommon(&access, AccessCheck(), ValType::I32)) {
15140         return false;
15141       }
15142       emitUnop(SplatI16x8);
15143       break;
15144     case Scalar::Uint32:
15145       if (!loadCommon(&access, AccessCheck(), ValType::I32)) {
15146         return false;
15147       }
15148       emitUnop(SplatI32x4);
15149       break;
15150     case Scalar::Int64:
15151       if (!loadCommon(&access, AccessCheck(), ValType::I64)) {
15152         return false;
15153       }
15154       emitUnop(SplatI64x2);
15155       break;
15156     default:
15157       MOZ_CRASH();
15158   }
15159   return true;
15160 }
15161 
emitLoadZero(Scalar::Type viewType)15162 bool BaseCompiler::emitLoadZero(Scalar::Type viewType) {
15163   // LoadZero has the structure of LoadSplat
15164   LinearMemoryAddress<Nothing> addr;
15165   if (!iter_.readLoadSplat(Scalar::byteSize(viewType), &addr)) {
15166     return false;
15167   }
15168 
15169   if (deadCode_) {
15170     return true;
15171   }
15172 
15173   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
15174   access.setZeroExtendSimd128Load();
15175   return loadCommon(&access, AccessCheck(), ValType::V128);
15176 }
15177 
emitLoadExtend(Scalar::Type viewType)15178 bool BaseCompiler::emitLoadExtend(Scalar::Type viewType) {
15179   LinearMemoryAddress<Nothing> addr;
15180   if (!iter_.readLoadExtend(&addr)) {
15181     return false;
15182   }
15183 
15184   if (deadCode_) {
15185     return true;
15186   }
15187 
15188   MemoryAccessDesc access(Scalar::Int64, addr.align, addr.offset,
15189                           bytecodeOffset());
15190   if (!loadCommon(&access, AccessCheck(), ValType::I64)) {
15191     return false;
15192   }
15193 
15194   RegI64 rs = popI64();
15195   RegV128 rd = needV128();
15196   masm.moveGPR64ToDouble(rs, rd);
15197   switch (viewType) {
15198     case Scalar::Int8:
15199       masm.widenLowInt8x16(rd, rd);
15200       break;
15201     case Scalar::Uint8:
15202       masm.unsignedWidenLowInt8x16(rd, rd);
15203       break;
15204     case Scalar::Int16:
15205       masm.widenLowInt16x8(rd, rd);
15206       break;
15207     case Scalar::Uint16:
15208       masm.unsignedWidenLowInt16x8(rd, rd);
15209       break;
15210     case Scalar::Int32:
15211       masm.widenLowInt32x4(rd, rd);
15212       break;
15213     case Scalar::Uint32:
15214       masm.unsignedWidenLowInt32x4(rd, rd);
15215       break;
15216     default:
15217       MOZ_CRASH();
15218   }
15219   freeI64(rs);
15220   pushV128(rd);
15221 
15222   return true;
15223 }
15224 
emitLoadLane(uint32_t laneSize)15225 bool BaseCompiler::emitLoadLane(uint32_t laneSize) {
15226   Nothing nothing;
15227   LinearMemoryAddress<Nothing> addr;
15228   uint32_t laneIndex;
15229   if (!iter_.readLoadLane(laneSize, &addr, &laneIndex, &nothing)) {
15230     return false;
15231   }
15232 
15233   if (deadCode_) {
15234     return true;
15235   }
15236 
15237   RegV128 rsd = popV128();
15238 
15239   Scalar::Type viewType;
15240   ValType type;
15241   switch (laneSize) {
15242     case 1:
15243       viewType = Scalar::Uint8;
15244       type = ValType::I32;
15245       break;
15246     case 2:
15247       viewType = Scalar::Uint16;
15248       type = ValType::I32;
15249       break;
15250     case 4:
15251       viewType = Scalar::Int32;
15252       type = ValType::I32;
15253       break;
15254     case 8:
15255       viewType = Scalar::Int64;
15256       type = ValType::I64;
15257       break;
15258     default:
15259       MOZ_CRASH("unsupported laneSize");
15260   }
15261 
15262   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
15263   if (!loadCommon(&access, AccessCheck(), type)) {
15264     return false;
15265   }
15266 
15267   if (type == ValType::I32) {
15268     RegI32 rs = popI32();
15269     switch (laneSize) {
15270       case 1:
15271         masm.replaceLaneInt8x16(laneIndex, rs, rsd);
15272         break;
15273       case 2:
15274         masm.replaceLaneInt16x8(laneIndex, rs, rsd);
15275         break;
15276       case 4:
15277         masm.replaceLaneInt32x4(laneIndex, rs, rsd);
15278         break;
15279     }
15280     freeI32(rs);
15281   } else {
15282     MOZ_ASSERT(type == ValType::I64);
15283     RegI64 rs = popI64();
15284     masm.replaceLaneInt64x2(laneIndex, rs, rsd);
15285     freeI64(rs);
15286   }
15287 
15288   pushV128(rsd);
15289 
15290   return true;
15291 }
15292 
emitStoreLane(uint32_t laneSize)15293 bool BaseCompiler::emitStoreLane(uint32_t laneSize) {
15294   Nothing nothing;
15295   LinearMemoryAddress<Nothing> addr;
15296   uint32_t laneIndex;
15297   if (!iter_.readStoreLane(laneSize, &addr, &laneIndex, &nothing)) {
15298     return false;
15299   }
15300 
15301   if (deadCode_) {
15302     return true;
15303   }
15304 
15305   RegV128 rs = popV128();
15306   Scalar::Type viewType;
15307   ValType type;
15308   if (laneSize <= 4) {
15309     RegI32 tmp = needI32();
15310     switch (laneSize) {
15311       case 1:
15312         viewType = Scalar::Uint8;
15313         masm.extractLaneInt8x16(laneIndex, rs, tmp);
15314         break;
15315       case 2:
15316         viewType = Scalar::Uint16;
15317         masm.extractLaneInt16x8(laneIndex, rs, tmp);
15318         break;
15319       case 4:
15320         viewType = Scalar::Int32;
15321         masm.extractLaneInt32x4(laneIndex, rs, tmp);
15322         break;
15323       default:
15324         MOZ_CRASH("unsupported laneSize");
15325     }
15326     pushI32(tmp);
15327     type = ValType::I32;
15328   } else {
15329     MOZ_ASSERT(laneSize == 8);
15330     RegI64 tmp = needI64();
15331     masm.extractLaneInt64x2(laneIndex, rs, tmp);
15332     pushI64(tmp);
15333     type = ValType::I64;
15334     viewType = Scalar::Int64;
15335   }
15336   freeV128(rs);
15337 
15338   MemoryAccessDesc access(viewType, addr.align, addr.offset, bytecodeOffset());
15339   return storeCommon(&access, AccessCheck(), type);
15340 }
15341 
emitBitselect()15342 bool BaseCompiler::emitBitselect() {
15343   Nothing unused_a, unused_b, unused_c;
15344 
15345   if (!iter_.readVectorSelect(&unused_a, &unused_b, &unused_c)) {
15346     return false;
15347   }
15348 
15349   if (deadCode_) {
15350     return true;
15351   }
15352 
15353   RegV128 rs3 = popV128();  // Control
15354   RegV128 rs2 = popV128();  // 'false' vector
15355   RegV128 rs1 = popV128();  // 'true' vector
15356 
15357   RegV128 result = BitselectV128(*this, rs1, rs2, rs3);
15358 
15359   if (rs1 != result) {
15360     freeV128(rs1);
15361   }
15362   if (rs2 != result) {
15363     freeV128(rs2);
15364   }
15365   if (rs3 != result) {
15366     freeV128(rs3);
15367   }
15368   pushV128(result);
15369   return true;
15370 }
15371 
emitVectorShuffle()15372 bool BaseCompiler::emitVectorShuffle() {
15373   Nothing unused_a, unused_b;
15374   V128 shuffleMask;
15375 
15376   if (!iter_.readVectorShuffle(&unused_a, &unused_b, &shuffleMask)) {
15377     return false;
15378   }
15379 
15380   if (deadCode_) {
15381     return true;
15382   }
15383 
15384   RegV128 rd, rs;
15385   pop2xV128(&rd, &rs);
15386 
15387   bool emitShuffle = true;
15388 
15389 #  ifdef ENABLE_WASM_SIMD_WORMHOLE
15390   if (moduleEnv_.simdWormholeEnabled() && IsWormholeTrigger(shuffleMask)) {
15391     emitShuffle = false;
15392     switch (shuffleMask.bytes[15]) {
15393       case 0:
15394         masm.loadConstantSimd128(WormholeSignature(), rd);
15395         break;
15396 #    if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
15397       case 1:
15398         masm.vpmaddubsw(rs, rd, rd);
15399         break;
15400       case 2:
15401         masm.vpmaddwd(Operand(rs), rd, rd);
15402         break;
15403 #    endif
15404       default:
15405         return iter_.fail("Unrecognized wormhole opcode");
15406     }
15407   }
15408 #  endif
15409 
15410   if (emitShuffle) {
15411     masm.shuffleInt8x16(shuffleMask.bytes, rs, rd);
15412   }
15413 
15414   freeV128(rs);
15415   pushV128(rd);
15416 
15417   return true;
15418 }
15419 
15420 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
emitVectorShiftRightI64x2()15421 bool BaseCompiler::emitVectorShiftRightI64x2() {
15422   Nothing unused_a, unused_b;
15423 
15424   if (!iter_.readVectorShift(&unused_a, &unused_b)) {
15425     return false;
15426   }
15427 
15428   if (deadCode_) {
15429     return true;
15430   }
15431 
15432   RegI32 count = popI32RhsForShiftI64();
15433   RegV128 lhsDest = popV128();
15434   RegI64 tmp = needI64();
15435   masm.and32(Imm32(63), count);
15436   masm.extractLaneInt64x2(0, lhsDest, tmp);
15437   masm.rshift64Arithmetic(count, tmp);
15438   masm.replaceLaneInt64x2(0, tmp, lhsDest);
15439   masm.extractLaneInt64x2(1, lhsDest, tmp);
15440   masm.rshift64Arithmetic(count, tmp);
15441   masm.replaceLaneInt64x2(1, tmp, lhsDest);
15442   freeI64(tmp);
15443   freeI32(count);
15444   pushV128(lhsDest);
15445 
15446   return true;
15447 }
15448 #  endif
15449 #endif  // ENABLE_WASM_SIMD
15450 
emitBody()15451 bool BaseCompiler::emitBody() {
15452   MOZ_ASSERT(stackMapGenerator_.framePushedAtEntryToBody.isSome());
15453 
15454   if (!iter_.startFunction(func_.index)) {
15455     return false;
15456   }
15457 
15458   initControl(controlItem(), ResultType::Empty());
15459 
15460   for (;;) {
15461     Nothing unused_a, unused_b;
15462 
15463 #ifdef DEBUG
15464     performRegisterLeakCheck();
15465     assertStackInvariants();
15466 #endif
15467 
15468 #define dispatchBinary0(doEmit, type)             \
15469   iter_.readBinary(type, &unused_a, &unused_b) && \
15470       (deadCode_ || (doEmit(), true))
15471 
15472 #define dispatchBinary1(arg1, type)               \
15473   iter_.readBinary(type, &unused_a, &unused_b) && \
15474       (deadCode_ || (emitBinop(arg1), true))
15475 
15476 #define dispatchBinary2(arg1, arg2, type)         \
15477   iter_.readBinary(type, &unused_a, &unused_b) && \
15478       (deadCode_ || (emitBinop(arg1, arg2), true))
15479 
15480 #define dispatchBinary3(arg1, arg2, arg3, type)   \
15481   iter_.readBinary(type, &unused_a, &unused_b) && \
15482       (deadCode_ || (emitBinop(arg1, arg2, arg3), true))
15483 
15484 #define dispatchUnary0(doEmit, type) \
15485   iter_.readUnary(type, &unused_a) && (deadCode_ || (doEmit(), true))
15486 
15487 #define dispatchUnary1(arg1, type) \
15488   iter_.readUnary(type, &unused_a) && (deadCode_ || (emitUnop(arg1), true))
15489 
15490 #define dispatchUnary2(arg1, arg2, type) \
15491   iter_.readUnary(type, &unused_a) &&    \
15492       (deadCode_ || (emitUnop(arg1, arg2), true))
15493 
15494 #define dispatchComparison0(doEmit, operandType, compareOp)  \
15495   iter_.readComparison(operandType, &unused_a, &unused_b) && \
15496       (deadCode_ || (doEmit(compareOp, operandType), true))
15497 
15498 #define dispatchConversion0(doEmit, inType, outType)  \
15499   iter_.readConversion(inType, outType, &unused_a) && \
15500       (deadCode_ || (doEmit(), true))
15501 
15502 #define dispatchConversion1(arg1, inType, outType)    \
15503   iter_.readConversion(inType, outType, &unused_a) && \
15504       (deadCode_ || (emitUnop(arg1), true))
15505 
15506 #define dispatchConversionOOM(doEmit, inType, outType) \
15507   iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || doEmit())
15508 
15509 #define dispatchCalloutConversionOOM(doEmit, symbol, inType, outType) \
15510   iter_.readConversion(inType, outType, &unused_a) &&                 \
15511       (deadCode_ || doEmit(symbol, inType, outType))
15512 
15513 #define dispatchIntDivCallout(doEmit, symbol, type) \
15514   iter_.readBinary(type, &unused_a, &unused_b) &&   \
15515       (deadCode_ || doEmit(symbol, type))
15516 
15517 #define dispatchVectorBinary(op)                           \
15518   iter_.readBinary(ValType::V128, &unused_a, &unused_b) && \
15519       (deadCode_ || (emitBinop(op), true))
15520 
15521 #define dispatchVectorUnary(op)                \
15522   iter_.readUnary(ValType::V128, &unused_a) && \
15523       (deadCode_ || (emitUnop(op), true))
15524 
15525 #define dispatchVectorComparison(op, compareOp)            \
15526   iter_.readBinary(ValType::V128, &unused_a, &unused_b) && \
15527       (deadCode_ || (emitBinop(compareOp, op), true))
15528 
15529 #define dispatchVectorVariableShift(op)          \
15530   iter_.readVectorShift(&unused_a, &unused_b) && \
15531       (deadCode_ || (emitBinop(op), true))
15532 
15533 #define dispatchExtractLane(op, outType, laneLimit)                   \
15534   iter_.readExtractLane(outType, laneLimit, &laneIndex, &unused_a) && \
15535       (deadCode_ || (emitUnop(laneIndex, op), true))
15536 
15537 #define dispatchReplaceLane(op, inType, laneLimit)                \
15538   iter_.readReplaceLane(inType, laneLimit, &laneIndex, &unused_a, \
15539                         &unused_b) &&                             \
15540       (deadCode_ || (emitBinop(laneIndex, op), true))
15541 
15542 #define dispatchSplat(op, inType)                           \
15543   iter_.readConversion(inType, ValType::V128, &unused_a) && \
15544       (deadCode_ || (emitUnop(op), true))
15545 
15546 #define dispatchVectorReduction(op)                               \
15547   iter_.readConversion(ValType::V128, ValType::I32, &unused_a) && \
15548       (deadCode_ || (emitUnop(op), true))
15549 
15550 #ifdef DEBUG
15551     // Check that the number of ref-typed entries in the operand stack matches
15552     // reality.
15553 #  define CHECK_POINTER_COUNT                                             \
15554     do {                                                                  \
15555       MOZ_ASSERT(countMemRefsOnStk() == stackMapGenerator_.memRefsOnStk); \
15556     } while (0)
15557 #else
15558 #  define CHECK_POINTER_COUNT \
15559     do {                      \
15560     } while (0)
15561 #endif
15562 
15563 #define CHECK(E) \
15564   if (!(E)) return false
15565 #define NEXT()           \
15566   {                      \
15567     CHECK_POINTER_COUNT; \
15568     continue;            \
15569   }
15570 #define CHECK_NEXT(E)     \
15571   if (!(E)) return false; \
15572   {                       \
15573     CHECK_POINTER_COUNT;  \
15574     continue;             \
15575   }
15576 
15577     // Opcodes that push more than MaxPushesPerOpcode (anything with multiple
15578     // results) will perform additional reservation.
15579     CHECK(stk_.reserve(stk_.length() + MaxPushesPerOpcode));
15580 
15581     OpBytes op{};
15582     CHECK(iter_.readOp(&op));
15583 
15584     // When compilerEnv_.debugEnabled(), every operator has a breakpoint site
15585     // except Op::End.
15586     if (compilerEnv_.debugEnabled() && op.b0 != (uint16_t)Op::End) {
15587       // TODO sync only registers that can be clobbered by the exit
15588       // prologue/epilogue or disable these registers for use in
15589       // baseline compiler when compilerEnv_.debugEnabled() is set.
15590       sync();
15591 
15592       insertBreakablePoint(CallSiteDesc::Breakpoint);
15593       if (!createStackMap("debug: per-insn breakpoint")) {
15594         return false;
15595       }
15596     }
15597 
15598     // Going below framePushedAtEntryToBody would imply that we've
15599     // popped off the machine stack, part of the frame created by
15600     // beginFunction().
15601     MOZ_ASSERT(masm.framePushed() >=
15602                stackMapGenerator_.framePushedAtEntryToBody.value());
15603 
15604     // At this point we're definitely not generating code for a function call.
15605     MOZ_ASSERT(
15606         stackMapGenerator_.framePushedExcludingOutboundCallArgs.isNothing());
15607 
15608     switch (op.b0) {
15609       case uint16_t(Op::End):
15610         if (!emitEnd()) {
15611           return false;
15612         }
15613         if (iter_.controlStackEmpty()) {
15614           return true;
15615         }
15616         NEXT();
15617 
15618       // Control opcodes
15619       case uint16_t(Op::Nop):
15620         CHECK_NEXT(iter_.readNop());
15621       case uint16_t(Op::Drop):
15622         CHECK_NEXT(emitDrop());
15623       case uint16_t(Op::Block):
15624         CHECK_NEXT(emitBlock());
15625       case uint16_t(Op::Loop):
15626         CHECK_NEXT(emitLoop());
15627       case uint16_t(Op::If):
15628         CHECK_NEXT(emitIf());
15629       case uint16_t(Op::Else):
15630         CHECK_NEXT(emitElse());
15631 #ifdef ENABLE_WASM_EXCEPTIONS
15632       case uint16_t(Op::Try):
15633         if (!moduleEnv_.exceptionsEnabled()) {
15634           return iter_.unrecognizedOpcode(&op);
15635         }
15636         CHECK_NEXT(emitTry());
15637       case uint16_t(Op::Catch):
15638         if (!moduleEnv_.exceptionsEnabled()) {
15639           return iter_.unrecognizedOpcode(&op);
15640         }
15641         CHECK_NEXT(emitCatch());
15642       case uint16_t(Op::CatchAll):
15643         if (!moduleEnv_.exceptionsEnabled()) {
15644           return iter_.unrecognizedOpcode(&op);
15645         }
15646         CHECK_NEXT(emitCatchAll());
15647       case uint16_t(Op::Delegate):
15648         if (!moduleEnv_.exceptionsEnabled()) {
15649           return iter_.unrecognizedOpcode(&op);
15650         }
15651         CHECK(emitDelegate());
15652         iter_.popDelegate();
15653         NEXT();
15654       case uint16_t(Op::Throw):
15655         if (!moduleEnv_.exceptionsEnabled()) {
15656           return iter_.unrecognizedOpcode(&op);
15657         }
15658         CHECK_NEXT(emitThrow());
15659       case uint16_t(Op::Rethrow):
15660         if (!moduleEnv_.exceptionsEnabled()) {
15661           return iter_.unrecognizedOpcode(&op);
15662         }
15663         CHECK_NEXT(emitRethrow());
15664 #endif
15665       case uint16_t(Op::Br):
15666         CHECK_NEXT(emitBr());
15667       case uint16_t(Op::BrIf):
15668         CHECK_NEXT(emitBrIf());
15669       case uint16_t(Op::BrTable):
15670         CHECK_NEXT(emitBrTable());
15671       case uint16_t(Op::Return):
15672         CHECK_NEXT(emitReturn());
15673       case uint16_t(Op::Unreachable):
15674         CHECK(iter_.readUnreachable());
15675         if (!deadCode_) {
15676           trap(Trap::Unreachable);
15677           deadCode_ = true;
15678         }
15679         NEXT();
15680 
15681       // Calls
15682       case uint16_t(Op::Call):
15683         CHECK_NEXT(emitCall());
15684       case uint16_t(Op::CallIndirect):
15685         CHECK_NEXT(emitCallIndirect());
15686 
15687       // Locals and globals
15688       case uint16_t(Op::GetLocal):
15689         CHECK_NEXT(emitGetLocal());
15690       case uint16_t(Op::SetLocal):
15691         CHECK_NEXT(emitSetLocal());
15692       case uint16_t(Op::TeeLocal):
15693         CHECK_NEXT(emitTeeLocal());
15694       case uint16_t(Op::GetGlobal):
15695         CHECK_NEXT(emitGetGlobal());
15696       case uint16_t(Op::SetGlobal):
15697         CHECK_NEXT(emitSetGlobal());
15698       case uint16_t(Op::TableGet):
15699         CHECK_NEXT(emitTableGet());
15700       case uint16_t(Op::TableSet):
15701         CHECK_NEXT(emitTableSet());
15702 
15703       // Select
15704       case uint16_t(Op::SelectNumeric):
15705         CHECK_NEXT(emitSelect(/*typed*/ false));
15706       case uint16_t(Op::SelectTyped):
15707         CHECK_NEXT(emitSelect(/*typed*/ true));
15708 
15709       // I32
15710       case uint16_t(Op::I32Const): {
15711         int32_t i32;
15712         CHECK(iter_.readI32Const(&i32));
15713         if (!deadCode_) {
15714           pushI32(i32);
15715         }
15716         NEXT();
15717       }
15718       case uint16_t(Op::I32Add):
15719         CHECK_NEXT(dispatchBinary2(AddI32, AddImmI32, ValType::I32));
15720       case uint16_t(Op::I32Sub):
15721         CHECK_NEXT(dispatchBinary2(SubI32, SubImmI32, ValType::I32));
15722       case uint16_t(Op::I32Mul):
15723         CHECK_NEXT(dispatchBinary0(emitMultiplyI32, ValType::I32));
15724       case uint16_t(Op::I32DivS):
15725         CHECK_NEXT(dispatchBinary0(emitQuotientI32, ValType::I32));
15726       case uint16_t(Op::I32DivU):
15727         CHECK_NEXT(dispatchBinary0(emitQuotientU32, ValType::I32));
15728       case uint16_t(Op::I32RemS):
15729         CHECK_NEXT(dispatchBinary0(emitRemainderI32, ValType::I32));
15730       case uint16_t(Op::I32RemU):
15731         CHECK_NEXT(dispatchBinary0(emitRemainderU32, ValType::I32));
15732       case uint16_t(Op::I32Eqz):
15733         CHECK_NEXT(dispatchConversion0(emitEqzI32, ValType::I32, ValType::I32));
15734       case uint16_t(Op::I32TruncSF32):
15735         CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI32<0>, ValType::F32,
15736                                          ValType::I32));
15737       case uint16_t(Op::I32TruncUF32):
15738         CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI32<TRUNC_UNSIGNED>,
15739                                          ValType::F32, ValType::I32));
15740       case uint16_t(Op::I32TruncSF64):
15741         CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI32<0>, ValType::F64,
15742                                          ValType::I32));
15743       case uint16_t(Op::I32TruncUF64):
15744         CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI32<TRUNC_UNSIGNED>,
15745                                          ValType::F64, ValType::I32));
15746       case uint16_t(Op::I32WrapI64):
15747         CHECK_NEXT(
15748             dispatchConversion1(WrapI64ToI32, ValType::I64, ValType::I32));
15749       case uint16_t(Op::I32ReinterpretF32):
15750         CHECK_NEXT(dispatchConversion1(ReinterpretF32AsI32, ValType::F32,
15751                                        ValType::I32));
15752       case uint16_t(Op::I32Clz):
15753         CHECK_NEXT(dispatchUnary1(ClzI32, ValType::I32));
15754       case uint16_t(Op::I32Ctz):
15755         CHECK_NEXT(dispatchUnary1(CtzI32, ValType::I32));
15756       case uint16_t(Op::I32Popcnt):
15757         CHECK_NEXT(dispatchUnary2(PopcntI32, PopcntTemp, ValType::I32));
15758       case uint16_t(Op::I32Or):
15759         CHECK_NEXT(dispatchBinary2(OrI32, OrImmI32, ValType::I32));
15760       case uint16_t(Op::I32And):
15761         CHECK_NEXT(dispatchBinary2(AndI32, AndImmI32, ValType::I32));
15762       case uint16_t(Op::I32Xor):
15763         CHECK_NEXT(dispatchBinary2(XorI32, XorImmI32, ValType::I32));
15764       case uint16_t(Op::I32Shl):
15765         CHECK_NEXT(dispatchBinary3(
15766             ShlI32, ShlImmI32, &BaseCompiler::popI32RhsForShift, ValType::I32));
15767       case uint16_t(Op::I32ShrS):
15768         CHECK_NEXT(dispatchBinary3(
15769             ShrI32, ShrImmI32, &BaseCompiler::popI32RhsForShift, ValType::I32));
15770       case uint16_t(Op::I32ShrU):
15771         CHECK_NEXT(dispatchBinary3(ShrUI32, ShrUImmI32,
15772                                    &BaseCompiler::popI32RhsForShift,
15773                                    ValType::I32));
15774       case uint16_t(Op::I32Load8S):
15775         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int8));
15776       case uint16_t(Op::I32Load8U):
15777         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint8));
15778       case uint16_t(Op::I32Load16S):
15779         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int16));
15780       case uint16_t(Op::I32Load16U):
15781         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint16));
15782       case uint16_t(Op::I32Load):
15783         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int32));
15784       case uint16_t(Op::I32Store8):
15785         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int8));
15786       case uint16_t(Op::I32Store16):
15787         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int16));
15788       case uint16_t(Op::I32Store):
15789         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int32));
15790       case uint16_t(Op::I32Rotr):
15791         CHECK_NEXT(dispatchBinary3(RotrI32, RotrImmI32,
15792                                    &BaseCompiler::popI32RhsForRotate,
15793                                    ValType::I32));
15794       case uint16_t(Op::I32Rotl):
15795         CHECK_NEXT(dispatchBinary3(RotlI32, RotlImmI32,
15796                                    &BaseCompiler::popI32RhsForRotate,
15797                                    ValType::I32));
15798 
15799       // I64
15800       case uint16_t(Op::I64Const): {
15801         int64_t i64;
15802         CHECK(iter_.readI64Const(&i64));
15803         if (!deadCode_) {
15804           pushI64(i64);
15805         }
15806         NEXT();
15807       }
15808       case uint16_t(Op::I64Add):
15809         CHECK_NEXT(dispatchBinary2(AddI64, AddImmI64, ValType::I64));
15810       case uint16_t(Op::I64Sub):
15811         CHECK_NEXT(dispatchBinary2(SubI64, SubImmI64, ValType::I64));
15812       case uint16_t(Op::I64Mul):
15813         CHECK_NEXT(dispatchBinary0(emitMultiplyI64, ValType::I64));
15814       case uint16_t(Op::I64DivS):
15815 #ifdef RABALDR_INT_DIV_I64_CALLOUT
15816         CHECK_NEXT(dispatchIntDivCallout(
15817             emitDivOrModI64BuiltinCall, SymbolicAddress::DivI64, ValType::I64));
15818 #else
15819         CHECK_NEXT(dispatchBinary0(emitQuotientI64, ValType::I64));
15820 #endif
15821       case uint16_t(Op::I64DivU):
15822 #ifdef RABALDR_INT_DIV_I64_CALLOUT
15823         CHECK_NEXT(dispatchIntDivCallout(emitDivOrModI64BuiltinCall,
15824                                          SymbolicAddress::UDivI64,
15825                                          ValType::I64));
15826 #else
15827         CHECK_NEXT(dispatchBinary0(emitQuotientU64, ValType::I64));
15828 #endif
15829       case uint16_t(Op::I64RemS):
15830 #ifdef RABALDR_INT_DIV_I64_CALLOUT
15831         CHECK_NEXT(dispatchIntDivCallout(
15832             emitDivOrModI64BuiltinCall, SymbolicAddress::ModI64, ValType::I64));
15833 #else
15834         CHECK_NEXT(dispatchBinary0(emitRemainderI64, ValType::I64));
15835 #endif
15836       case uint16_t(Op::I64RemU):
15837 #ifdef RABALDR_INT_DIV_I64_CALLOUT
15838         CHECK_NEXT(dispatchIntDivCallout(emitDivOrModI64BuiltinCall,
15839                                          SymbolicAddress::UModI64,
15840                                          ValType::I64));
15841 #else
15842         CHECK_NEXT(dispatchBinary0(emitRemainderU64, ValType::I64));
15843 #endif
15844       case uint16_t(Op::I64TruncSF32):
15845 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
15846         CHECK_NEXT(
15847             dispatchCalloutConversionOOM(emitConvertFloatingToInt64Callout,
15848                                          SymbolicAddress::TruncateDoubleToInt64,
15849                                          ValType::F32, ValType::I64));
15850 #else
15851         CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI64<0>, ValType::F32,
15852                                          ValType::I64));
15853 #endif
15854       case uint16_t(Op::I64TruncUF32):
15855 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
15856         CHECK_NEXT(dispatchCalloutConversionOOM(
15857             emitConvertFloatingToInt64Callout,
15858             SymbolicAddress::TruncateDoubleToUint64, ValType::F32,
15859             ValType::I64));
15860 #else
15861         CHECK_NEXT(dispatchConversionOOM(emitTruncateF32ToI64<TRUNC_UNSIGNED>,
15862                                          ValType::F32, ValType::I64));
15863 #endif
15864       case uint16_t(Op::I64TruncSF64):
15865 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
15866         CHECK_NEXT(
15867             dispatchCalloutConversionOOM(emitConvertFloatingToInt64Callout,
15868                                          SymbolicAddress::TruncateDoubleToInt64,
15869                                          ValType::F64, ValType::I64));
15870 #else
15871         CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI64<0>, ValType::F64,
15872                                          ValType::I64));
15873 #endif
15874       case uint16_t(Op::I64TruncUF64):
15875 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
15876         CHECK_NEXT(dispatchCalloutConversionOOM(
15877             emitConvertFloatingToInt64Callout,
15878             SymbolicAddress::TruncateDoubleToUint64, ValType::F64,
15879             ValType::I64));
15880 #else
15881         CHECK_NEXT(dispatchConversionOOM(emitTruncateF64ToI64<TRUNC_UNSIGNED>,
15882                                          ValType::F64, ValType::I64));
15883 #endif
15884       case uint16_t(Op::I64ExtendSI32):
15885         CHECK_NEXT(dispatchConversion0(emitExtendI32ToI64, ValType::I32,
15886                                        ValType::I64));
15887       case uint16_t(Op::I64ExtendUI32):
15888         CHECK_NEXT(dispatchConversion0(emitExtendU32ToI64, ValType::I32,
15889                                        ValType::I64));
15890       case uint16_t(Op::I64ReinterpretF64):
15891         CHECK_NEXT(dispatchConversion1(ReinterpretF64AsI64, ValType::F64,
15892                                        ValType::I64));
15893       case uint16_t(Op::I64Or):
15894         CHECK_NEXT(dispatchBinary2(OrI64, OrImmI64, ValType::I64));
15895       case uint16_t(Op::I64And):
15896         CHECK_NEXT(dispatchBinary2(AndI64, AndImmI64, ValType::I64));
15897       case uint16_t(Op::I64Xor):
15898         CHECK_NEXT(dispatchBinary2(XorI64, XorImmI64, ValType::I64));
15899       case uint16_t(Op::I64Shl):
15900         CHECK_NEXT(dispatchBinary3(
15901             ShlI64, ShlImmI64, &BaseCompiler::popI64RhsForShift, ValType::I64));
15902       case uint16_t(Op::I64ShrS):
15903         CHECK_NEXT(dispatchBinary3(
15904             ShrI64, ShrImmI64, &BaseCompiler::popI64RhsForShift, ValType::I64));
15905       case uint16_t(Op::I64ShrU):
15906         CHECK_NEXT(dispatchBinary3(ShrUI64, ShrUImmI64,
15907                                    &BaseCompiler::popI64RhsForShift,
15908                                    ValType::I64));
15909       case uint16_t(Op::I64Rotr):
15910         CHECK_NEXT(dispatchBinary0(emitRotrI64, ValType::I64));
15911       case uint16_t(Op::I64Rotl):
15912         CHECK_NEXT(dispatchBinary0(emitRotlI64, ValType::I64));
15913       case uint16_t(Op::I64Clz):
15914         CHECK_NEXT(dispatchUnary1(ClzI64, ValType::I64));
15915       case uint16_t(Op::I64Ctz):
15916         CHECK_NEXT(dispatchUnary1(CtzI64, ValType::I64));
15917       case uint16_t(Op::I64Popcnt):
15918         CHECK_NEXT(dispatchUnary2(PopcntI64, PopcntTemp, ValType::I64));
15919       case uint16_t(Op::I64Eqz):
15920         CHECK_NEXT(dispatchConversion0(emitEqzI64, ValType::I64, ValType::I32));
15921       case uint16_t(Op::I64Load8S):
15922         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int8));
15923       case uint16_t(Op::I64Load16S):
15924         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int16));
15925       case uint16_t(Op::I64Load32S):
15926         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int32));
15927       case uint16_t(Op::I64Load8U):
15928         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint8));
15929       case uint16_t(Op::I64Load16U):
15930         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint16));
15931       case uint16_t(Op::I64Load32U):
15932         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint32));
15933       case uint16_t(Op::I64Load):
15934         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int64));
15935       case uint16_t(Op::I64Store8):
15936         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int8));
15937       case uint16_t(Op::I64Store16):
15938         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int16));
15939       case uint16_t(Op::I64Store32):
15940         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32));
15941       case uint16_t(Op::I64Store):
15942         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int64));
15943 
15944       // F32
15945       case uint16_t(Op::F32Const): {
15946         float f32;
15947         CHECK(iter_.readF32Const(&f32));
15948         if (!deadCode_) {
15949           pushF32(f32);
15950         }
15951         NEXT();
15952       }
15953       case uint16_t(Op::F32Add):
15954         CHECK_NEXT(dispatchBinary1(AddF32, ValType::F32))
15955       case uint16_t(Op::F32Sub):
15956         CHECK_NEXT(dispatchBinary1(SubF32, ValType::F32));
15957       case uint16_t(Op::F32Mul):
15958         CHECK_NEXT(dispatchBinary1(MulF32, ValType::F32));
15959       case uint16_t(Op::F32Div):
15960         CHECK_NEXT(dispatchBinary1(DivF32, ValType::F32));
15961       case uint16_t(Op::F32Min):
15962         CHECK_NEXT(dispatchBinary1(MinF32, ValType::F32));
15963       case uint16_t(Op::F32Max):
15964         CHECK_NEXT(dispatchBinary1(MaxF32, ValType::F32));
15965       case uint16_t(Op::F32Neg):
15966         CHECK_NEXT(dispatchUnary1(NegateF32, ValType::F32));
15967       case uint16_t(Op::F32Abs):
15968         CHECK_NEXT(dispatchUnary1(AbsF32, ValType::F32));
15969       case uint16_t(Op::F32Sqrt):
15970         CHECK_NEXT(dispatchUnary1(SqrtF32, ValType::F32));
15971       case uint16_t(Op::F32Ceil):
15972         CHECK_NEXT(
15973             emitUnaryMathBuiltinCall(SymbolicAddress::CeilF, ValType::F32));
15974       case uint16_t(Op::F32Floor):
15975         CHECK_NEXT(
15976             emitUnaryMathBuiltinCall(SymbolicAddress::FloorF, ValType::F32));
15977       case uint16_t(Op::F32DemoteF64):
15978         CHECK_NEXT(
15979             dispatchConversion1(ConvertF64ToF32, ValType::F64, ValType::F32));
15980       case uint16_t(Op::F32ConvertSI32):
15981         CHECK_NEXT(
15982             dispatchConversion1(ConvertI32ToF32, ValType::I32, ValType::F32));
15983       case uint16_t(Op::F32ConvertUI32):
15984         CHECK_NEXT(
15985             dispatchConversion1(ConvertU32ToF32, ValType::I32, ValType::F32));
15986       case uint16_t(Op::F32ConvertSI64):
15987 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
15988         CHECK_NEXT(dispatchCalloutConversionOOM(
15989             emitConvertInt64ToFloatingCallout, SymbolicAddress::Int64ToFloat32,
15990             ValType::I64, ValType::F32));
15991 #else
15992         CHECK_NEXT(
15993             dispatchConversion1(ConvertI64ToF32, ValType::I64, ValType::F32));
15994 #endif
15995       case uint16_t(Op::F32ConvertUI64):
15996 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
15997         CHECK_NEXT(dispatchCalloutConversionOOM(
15998             emitConvertInt64ToFloatingCallout, SymbolicAddress::Uint64ToFloat32,
15999             ValType::I64, ValType::F32));
16000 #else
16001         CHECK_NEXT(dispatchConversion0(emitConvertU64ToF32, ValType::I64,
16002                                        ValType::F32));
16003 #endif
16004       case uint16_t(Op::F32ReinterpretI32):
16005         CHECK_NEXT(dispatchConversion1(ReinterpretI32AsF32, ValType::I32,
16006                                        ValType::F32));
16007       case uint16_t(Op::F32Load):
16008         CHECK_NEXT(emitLoad(ValType::F32, Scalar::Float32));
16009       case uint16_t(Op::F32Store):
16010         CHECK_NEXT(emitStore(ValType::F32, Scalar::Float32));
16011       case uint16_t(Op::F32CopySign):
16012         CHECK_NEXT(dispatchBinary1(CopysignF32, ValType::F32));
16013       case uint16_t(Op::F32Nearest):
16014         CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntF,
16015                                             ValType::F32));
16016       case uint16_t(Op::F32Trunc):
16017         CHECK_NEXT(
16018             emitUnaryMathBuiltinCall(SymbolicAddress::TruncF, ValType::F32));
16019 
16020       // F64
16021       case uint16_t(Op::F64Const): {
16022         double f64;
16023         CHECK(iter_.readF64Const(&f64));
16024         if (!deadCode_) {
16025           pushF64(f64);
16026         }
16027         NEXT();
16028       }
16029       case uint16_t(Op::F64Add):
16030         CHECK_NEXT(dispatchBinary1(AddF64, ValType::F64))
16031       case uint16_t(Op::F64Sub):
16032         CHECK_NEXT(dispatchBinary1(SubF64, ValType::F64));
16033       case uint16_t(Op::F64Mul):
16034         CHECK_NEXT(dispatchBinary1(MulF64, ValType::F64));
16035       case uint16_t(Op::F64Div):
16036         CHECK_NEXT(dispatchBinary1(DivF64, ValType::F64));
16037       case uint16_t(Op::F64Min):
16038         CHECK_NEXT(dispatchBinary1(MinF64, ValType::F64));
16039       case uint16_t(Op::F64Max):
16040         CHECK_NEXT(dispatchBinary1(MaxF64, ValType::F64));
16041       case uint16_t(Op::F64Neg):
16042         CHECK_NEXT(dispatchUnary1(NegateF64, ValType::F64));
16043       case uint16_t(Op::F64Abs):
16044         CHECK_NEXT(dispatchUnary1(AbsF64, ValType::F64));
16045       case uint16_t(Op::F64Sqrt):
16046         CHECK_NEXT(dispatchUnary1(SqrtF64, ValType::F64));
16047       case uint16_t(Op::F64Ceil):
16048         CHECK_NEXT(
16049             emitUnaryMathBuiltinCall(SymbolicAddress::CeilD, ValType::F64));
16050       case uint16_t(Op::F64Floor):
16051         CHECK_NEXT(
16052             emitUnaryMathBuiltinCall(SymbolicAddress::FloorD, ValType::F64));
16053       case uint16_t(Op::F64PromoteF32):
16054         CHECK_NEXT(
16055             dispatchConversion1(ConvertF32ToF64, ValType::F32, ValType::F64));
16056       case uint16_t(Op::F64ConvertSI32):
16057         CHECK_NEXT(
16058             dispatchConversion1(ConvertI32ToF64, ValType::I32, ValType::F64));
16059       case uint16_t(Op::F64ConvertUI32):
16060         CHECK_NEXT(
16061             dispatchConversion1(ConvertU32ToF64, ValType::I32, ValType::F64));
16062       case uint16_t(Op::F64ConvertSI64):
16063 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
16064         CHECK_NEXT(dispatchCalloutConversionOOM(
16065             emitConvertInt64ToFloatingCallout, SymbolicAddress::Int64ToDouble,
16066             ValType::I64, ValType::F64));
16067 #else
16068         CHECK_NEXT(
16069             dispatchConversion1(ConvertI64ToF64, ValType::I64, ValType::F64));
16070 #endif
16071       case uint16_t(Op::F64ConvertUI64):
16072 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
16073         CHECK_NEXT(dispatchCalloutConversionOOM(
16074             emitConvertInt64ToFloatingCallout, SymbolicAddress::Uint64ToDouble,
16075             ValType::I64, ValType::F64));
16076 #else
16077         CHECK_NEXT(dispatchConversion0(emitConvertU64ToF64, ValType::I64,
16078                                        ValType::F64));
16079 #endif
16080       case uint16_t(Op::F64Load):
16081         CHECK_NEXT(emitLoad(ValType::F64, Scalar::Float64));
16082       case uint16_t(Op::F64Store):
16083         CHECK_NEXT(emitStore(ValType::F64, Scalar::Float64));
16084       case uint16_t(Op::F64ReinterpretI64):
16085         CHECK_NEXT(dispatchConversion1(ReinterpretI64AsF64, ValType::I64,
16086                                        ValType::F64));
16087       case uint16_t(Op::F64CopySign):
16088         CHECK_NEXT(dispatchBinary1(CopysignF64, ValType::F64));
16089       case uint16_t(Op::F64Nearest):
16090         CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntD,
16091                                             ValType::F64));
16092       case uint16_t(Op::F64Trunc):
16093         CHECK_NEXT(
16094             emitUnaryMathBuiltinCall(SymbolicAddress::TruncD, ValType::F64));
16095 
16096       // Comparisons
16097       case uint16_t(Op::I32Eq):
16098         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16099                                        Assembler::Equal));
16100       case uint16_t(Op::I32Ne):
16101         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16102                                        Assembler::NotEqual));
16103       case uint16_t(Op::I32LtS):
16104         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16105                                        Assembler::LessThan));
16106       case uint16_t(Op::I32LeS):
16107         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16108                                        Assembler::LessThanOrEqual));
16109       case uint16_t(Op::I32GtS):
16110         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16111                                        Assembler::GreaterThan));
16112       case uint16_t(Op::I32GeS):
16113         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16114                                        Assembler::GreaterThanOrEqual));
16115       case uint16_t(Op::I32LtU):
16116         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16117                                        Assembler::Below));
16118       case uint16_t(Op::I32LeU):
16119         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16120                                        Assembler::BelowOrEqual));
16121       case uint16_t(Op::I32GtU):
16122         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16123                                        Assembler::Above));
16124       case uint16_t(Op::I32GeU):
16125         CHECK_NEXT(dispatchComparison0(emitCompareI32, ValType::I32,
16126                                        Assembler::AboveOrEqual));
16127       case uint16_t(Op::I64Eq):
16128         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16129                                        Assembler::Equal));
16130       case uint16_t(Op::I64Ne):
16131         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16132                                        Assembler::NotEqual));
16133       case uint16_t(Op::I64LtS):
16134         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16135                                        Assembler::LessThan));
16136       case uint16_t(Op::I64LeS):
16137         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16138                                        Assembler::LessThanOrEqual));
16139       case uint16_t(Op::I64GtS):
16140         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16141                                        Assembler::GreaterThan));
16142       case uint16_t(Op::I64GeS):
16143         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16144                                        Assembler::GreaterThanOrEqual));
16145       case uint16_t(Op::I64LtU):
16146         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16147                                        Assembler::Below));
16148       case uint16_t(Op::I64LeU):
16149         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16150                                        Assembler::BelowOrEqual));
16151       case uint16_t(Op::I64GtU):
16152         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16153                                        Assembler::Above));
16154       case uint16_t(Op::I64GeU):
16155         CHECK_NEXT(dispatchComparison0(emitCompareI64, ValType::I64,
16156                                        Assembler::AboveOrEqual));
16157       case uint16_t(Op::F32Eq):
16158         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16159                                        Assembler::DoubleEqual));
16160       case uint16_t(Op::F32Ne):
16161         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16162                                        Assembler::DoubleNotEqualOrUnordered));
16163       case uint16_t(Op::F32Lt):
16164         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16165                                        Assembler::DoubleLessThan));
16166       case uint16_t(Op::F32Le):
16167         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16168                                        Assembler::DoubleLessThanOrEqual));
16169       case uint16_t(Op::F32Gt):
16170         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16171                                        Assembler::DoubleGreaterThan));
16172       case uint16_t(Op::F32Ge):
16173         CHECK_NEXT(dispatchComparison0(emitCompareF32, ValType::F32,
16174                                        Assembler::DoubleGreaterThanOrEqual));
16175       case uint16_t(Op::F64Eq):
16176         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16177                                        Assembler::DoubleEqual));
16178       case uint16_t(Op::F64Ne):
16179         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16180                                        Assembler::DoubleNotEqualOrUnordered));
16181       case uint16_t(Op::F64Lt):
16182         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16183                                        Assembler::DoubleLessThan));
16184       case uint16_t(Op::F64Le):
16185         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16186                                        Assembler::DoubleLessThanOrEqual));
16187       case uint16_t(Op::F64Gt):
16188         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16189                                        Assembler::DoubleGreaterThan));
16190       case uint16_t(Op::F64Ge):
16191         CHECK_NEXT(dispatchComparison0(emitCompareF64, ValType::F64,
16192                                        Assembler::DoubleGreaterThanOrEqual));
16193 
16194       // Sign extensions
16195       case uint16_t(Op::I32Extend8S):
16196         CHECK_NEXT(
16197             dispatchConversion1(ExtendI32_8, ValType::I32, ValType::I32));
16198       case uint16_t(Op::I32Extend16S):
16199         CHECK_NEXT(
16200             dispatchConversion1(ExtendI32_16, ValType::I32, ValType::I32));
16201       case uint16_t(Op::I64Extend8S):
16202         CHECK_NEXT(
16203             dispatchConversion0(emitExtendI64_8, ValType::I64, ValType::I64));
16204       case uint16_t(Op::I64Extend16S):
16205         CHECK_NEXT(
16206             dispatchConversion0(emitExtendI64_16, ValType::I64, ValType::I64));
16207       case uint16_t(Op::I64Extend32S):
16208         CHECK_NEXT(
16209             dispatchConversion0(emitExtendI64_32, ValType::I64, ValType::I64));
16210 
16211       // Memory Related
16212       case uint16_t(Op::MemoryGrow):
16213         CHECK_NEXT(emitMemoryGrow());
16214       case uint16_t(Op::MemorySize):
16215         CHECK_NEXT(emitMemorySize());
16216 
16217 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
16218       case uint16_t(Op::RefAsNonNull):
16219         if (!moduleEnv_.functionReferencesEnabled()) {
16220           return iter_.unrecognizedOpcode(&op);
16221         }
16222         CHECK_NEXT(emitRefAsNonNull());
16223       case uint16_t(Op::BrOnNull):
16224         if (!moduleEnv_.functionReferencesEnabled()) {
16225           return iter_.unrecognizedOpcode(&op);
16226         }
16227         CHECK_NEXT(emitBrOnNull());
16228 #endif
16229 #ifdef ENABLE_WASM_GC
16230       case uint16_t(Op::RefEq):
16231         if (!moduleEnv_.gcEnabled()) {
16232           return iter_.unrecognizedOpcode(&op);
16233         }
16234         CHECK_NEXT(dispatchComparison0(emitCompareRef, RefType::eq(),
16235                                        Assembler::Equal));
16236 #endif
16237       case uint16_t(Op::RefFunc):
16238         CHECK_NEXT(emitRefFunc());
16239         break;
16240       case uint16_t(Op::RefNull):
16241         CHECK_NEXT(emitRefNull());
16242         break;
16243       case uint16_t(Op::RefIsNull):
16244         CHECK_NEXT(emitRefIsNull());
16245         break;
16246 
16247 #ifdef ENABLE_WASM_GC
16248       // "GC" operations
16249       case uint16_t(Op::GcPrefix): {
16250         if (!moduleEnv_.gcEnabled()) {
16251           return iter_.unrecognizedOpcode(&op);
16252         }
16253         switch (op.b1) {
16254           case uint32_t(GcOp::StructNewWithRtt):
16255             CHECK_NEXT(emitStructNewWithRtt());
16256           case uint32_t(GcOp::StructNewDefaultWithRtt):
16257             CHECK_NEXT(emitStructNewDefaultWithRtt());
16258           case uint32_t(GcOp::StructGet):
16259             CHECK_NEXT(emitStructGet(FieldExtension::None));
16260           case uint32_t(GcOp::StructGetS):
16261             CHECK_NEXT(emitStructGet(FieldExtension::Signed));
16262           case uint32_t(GcOp::StructGetU):
16263             CHECK_NEXT(emitStructGet(FieldExtension::Unsigned));
16264           case uint32_t(GcOp::StructSet):
16265             CHECK_NEXT(emitStructSet());
16266           case uint32_t(GcOp::ArrayNewWithRtt):
16267             CHECK_NEXT(emitArrayNewWithRtt());
16268           case uint32_t(GcOp::ArrayNewDefaultWithRtt):
16269             CHECK_NEXT(emitArrayNewDefaultWithRtt());
16270           case uint32_t(GcOp::ArrayGet):
16271             CHECK_NEXT(emitArrayGet(FieldExtension::None));
16272           case uint32_t(GcOp::ArrayGetS):
16273             CHECK_NEXT(emitArrayGet(FieldExtension::Signed));
16274           case uint32_t(GcOp::ArrayGetU):
16275             CHECK_NEXT(emitArrayGet(FieldExtension::Unsigned));
16276           case uint32_t(GcOp::ArraySet):
16277             CHECK_NEXT(emitArraySet());
16278           case uint32_t(GcOp::ArrayLen):
16279             CHECK_NEXT(emitArrayLen());
16280           case uint32_t(GcOp::RttCanon):
16281             CHECK_NEXT(emitRttCanon());
16282           case uint32_t(GcOp::RttSub):
16283             CHECK_NEXT(emitRttSub());
16284           case uint32_t(GcOp::RefTest):
16285             CHECK_NEXT(emitRefTest());
16286           case uint32_t(GcOp::RefCast):
16287             CHECK_NEXT(emitRefCast());
16288           case uint32_t(GcOp::BrOnCast):
16289             CHECK_NEXT(emitBrOnCast());
16290           default:
16291             break;
16292         }  // switch (op.b1)
16293         return iter_.unrecognizedOpcode(&op);
16294       }
16295 #endif
16296 
16297 #ifdef ENABLE_WASM_SIMD
16298       // SIMD operations
16299       case uint16_t(Op::SimdPrefix): {
16300         uint32_t laneIndex;
16301         if (!moduleEnv_.v128Enabled()) {
16302           return iter_.unrecognizedOpcode(&op);
16303         }
16304         switch (op.b1) {
16305           case uint32_t(SimdOp::I8x16ExtractLaneS):
16306             CHECK_NEXT(dispatchExtractLane(ExtractLaneI8x16, ValType::I32, 16));
16307           case uint32_t(SimdOp::I8x16ExtractLaneU):
16308             CHECK_NEXT(
16309                 dispatchExtractLane(ExtractLaneUI8x16, ValType::I32, 16));
16310           case uint32_t(SimdOp::I16x8ExtractLaneS):
16311             CHECK_NEXT(dispatchExtractLane(ExtractLaneI16x8, ValType::I32, 8));
16312           case uint32_t(SimdOp::I16x8ExtractLaneU):
16313             CHECK_NEXT(dispatchExtractLane(ExtractLaneUI16x8, ValType::I32, 8));
16314           case uint32_t(SimdOp::I32x4ExtractLane):
16315             CHECK_NEXT(dispatchExtractLane(ExtractLaneI32x4, ValType::I32, 4));
16316           case uint32_t(SimdOp::I64x2ExtractLane):
16317             CHECK_NEXT(dispatchExtractLane(ExtractLaneI64x2, ValType::I64, 2));
16318           case uint32_t(SimdOp::F32x4ExtractLane):
16319             CHECK_NEXT(dispatchExtractLane(ExtractLaneF32x4, ValType::F32, 4));
16320           case uint32_t(SimdOp::F64x2ExtractLane):
16321             CHECK_NEXT(dispatchExtractLane(ExtractLaneF64x2, ValType::F64, 2));
16322           case uint32_t(SimdOp::I8x16Splat):
16323             CHECK_NEXT(dispatchSplat(SplatI8x16, ValType::I32));
16324           case uint32_t(SimdOp::I16x8Splat):
16325             CHECK_NEXT(dispatchSplat(SplatI16x8, ValType::I32));
16326           case uint32_t(SimdOp::I32x4Splat):
16327             CHECK_NEXT(dispatchSplat(SplatI32x4, ValType::I32));
16328           case uint32_t(SimdOp::I64x2Splat):
16329             CHECK_NEXT(dispatchSplat(SplatI64x2, ValType::I64));
16330           case uint32_t(SimdOp::F32x4Splat):
16331             CHECK_NEXT(dispatchSplat(SplatF32x4, ValType::F32));
16332           case uint32_t(SimdOp::F64x2Splat):
16333             CHECK_NEXT(dispatchSplat(SplatF64x2, ValType::F64));
16334           case uint32_t(SimdOp::V128AnyTrue):
16335             CHECK_NEXT(dispatchVectorReduction(AnyTrue));
16336           case uint32_t(SimdOp::I8x16AllTrue):
16337             CHECK_NEXT(dispatchVectorReduction(AllTrueI8x16));
16338           case uint32_t(SimdOp::I16x8AllTrue):
16339             CHECK_NEXT(dispatchVectorReduction(AllTrueI16x8));
16340           case uint32_t(SimdOp::I32x4AllTrue):
16341             CHECK_NEXT(dispatchVectorReduction(AllTrueI32x4));
16342           case uint32_t(SimdOp::I64x2AllTrue):
16343             CHECK_NEXT(dispatchVectorReduction(AllTrueI64x2));
16344           case uint32_t(SimdOp::I8x16Bitmask):
16345             CHECK_NEXT(dispatchVectorReduction(BitmaskI8x16));
16346           case uint32_t(SimdOp::I16x8Bitmask):
16347             CHECK_NEXT(dispatchVectorReduction(BitmaskI16x8));
16348           case uint32_t(SimdOp::I32x4Bitmask):
16349             CHECK_NEXT(dispatchVectorReduction(BitmaskI32x4));
16350           case uint32_t(SimdOp::I64x2Bitmask):
16351             CHECK_NEXT(dispatchVectorReduction(BitmaskI64x2));
16352           case uint32_t(SimdOp::I8x16ReplaceLane):
16353             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI8x16, ValType::I32, 16));
16354           case uint32_t(SimdOp::I16x8ReplaceLane):
16355             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI16x8, ValType::I32, 8));
16356           case uint32_t(SimdOp::I32x4ReplaceLane):
16357             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI32x4, ValType::I32, 4));
16358           case uint32_t(SimdOp::I64x2ReplaceLane):
16359             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneI64x2, ValType::I64, 2));
16360           case uint32_t(SimdOp::F32x4ReplaceLane):
16361             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneF32x4, ValType::F32, 4));
16362           case uint32_t(SimdOp::F64x2ReplaceLane):
16363             CHECK_NEXT(dispatchReplaceLane(ReplaceLaneF64x2, ValType::F64, 2));
16364           case uint32_t(SimdOp::I8x16Eq):
16365             CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::Equal));
16366           case uint32_t(SimdOp::I8x16Ne):
16367             CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::NotEqual));
16368           case uint32_t(SimdOp::I8x16LtS):
16369             CHECK_NEXT(dispatchVectorComparison(CmpI8x16, Assembler::LessThan));
16370           case uint32_t(SimdOp::I8x16LtU):
16371             CHECK_NEXT(dispatchVectorComparison(CmpUI8x16, Assembler::Below));
16372           case uint32_t(SimdOp::I8x16GtS):
16373             CHECK_NEXT(
16374                 dispatchVectorComparison(CmpI8x16, Assembler::GreaterThan));
16375           case uint32_t(SimdOp::I8x16GtU):
16376             CHECK_NEXT(dispatchVectorComparison(CmpUI8x16, Assembler::Above));
16377           case uint32_t(SimdOp::I8x16LeS):
16378             CHECK_NEXT(
16379                 dispatchVectorComparison(CmpI8x16, Assembler::LessThanOrEqual));
16380           case uint32_t(SimdOp::I8x16LeU):
16381             CHECK_NEXT(
16382                 dispatchVectorComparison(CmpUI8x16, Assembler::BelowOrEqual));
16383           case uint32_t(SimdOp::I8x16GeS):
16384             CHECK_NEXT(dispatchVectorComparison(CmpI8x16,
16385                                                 Assembler::GreaterThanOrEqual));
16386           case uint32_t(SimdOp::I8x16GeU):
16387             CHECK_NEXT(
16388                 dispatchVectorComparison(CmpUI8x16, Assembler::AboveOrEqual));
16389           case uint32_t(SimdOp::I16x8Eq):
16390             CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::Equal));
16391           case uint32_t(SimdOp::I16x8Ne):
16392             CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::NotEqual));
16393           case uint32_t(SimdOp::I16x8LtS):
16394             CHECK_NEXT(dispatchVectorComparison(CmpI16x8, Assembler::LessThan));
16395           case uint32_t(SimdOp::I16x8LtU):
16396             CHECK_NEXT(dispatchVectorComparison(CmpUI16x8, Assembler::Below));
16397           case uint32_t(SimdOp::I16x8GtS):
16398             CHECK_NEXT(
16399                 dispatchVectorComparison(CmpI16x8, Assembler::GreaterThan));
16400           case uint32_t(SimdOp::I16x8GtU):
16401             CHECK_NEXT(dispatchVectorComparison(CmpUI16x8, Assembler::Above));
16402           case uint32_t(SimdOp::I16x8LeS):
16403             CHECK_NEXT(
16404                 dispatchVectorComparison(CmpI16x8, Assembler::LessThanOrEqual));
16405           case uint32_t(SimdOp::I16x8LeU):
16406             CHECK_NEXT(
16407                 dispatchVectorComparison(CmpUI16x8, Assembler::BelowOrEqual));
16408           case uint32_t(SimdOp::I16x8GeS):
16409             CHECK_NEXT(dispatchVectorComparison(CmpI16x8,
16410                                                 Assembler::GreaterThanOrEqual));
16411           case uint32_t(SimdOp::I16x8GeU):
16412             CHECK_NEXT(
16413                 dispatchVectorComparison(CmpUI16x8, Assembler::AboveOrEqual));
16414           case uint32_t(SimdOp::I32x4Eq):
16415             CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::Equal));
16416           case uint32_t(SimdOp::I32x4Ne):
16417             CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::NotEqual));
16418           case uint32_t(SimdOp::I32x4LtS):
16419             CHECK_NEXT(dispatchVectorComparison(CmpI32x4, Assembler::LessThan));
16420           case uint32_t(SimdOp::I32x4LtU):
16421             CHECK_NEXT(dispatchVectorComparison(CmpUI32x4, Assembler::Below));
16422           case uint32_t(SimdOp::I32x4GtS):
16423             CHECK_NEXT(
16424                 dispatchVectorComparison(CmpI32x4, Assembler::GreaterThan));
16425           case uint32_t(SimdOp::I32x4GtU):
16426             CHECK_NEXT(dispatchVectorComparison(CmpUI32x4, Assembler::Above));
16427           case uint32_t(SimdOp::I32x4LeS):
16428             CHECK_NEXT(
16429                 dispatchVectorComparison(CmpI32x4, Assembler::LessThanOrEqual));
16430           case uint32_t(SimdOp::I32x4LeU):
16431             CHECK_NEXT(
16432                 dispatchVectorComparison(CmpUI32x4, Assembler::BelowOrEqual));
16433           case uint32_t(SimdOp::I32x4GeS):
16434             CHECK_NEXT(dispatchVectorComparison(CmpI32x4,
16435                                                 Assembler::GreaterThanOrEqual));
16436           case uint32_t(SimdOp::I32x4GeU):
16437             CHECK_NEXT(
16438                 dispatchVectorComparison(CmpUI32x4, Assembler::AboveOrEqual));
16439           case uint32_t(SimdOp::I64x2Eq):
16440             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForEquality,
16441                                                 Assembler::Equal));
16442           case uint32_t(SimdOp::I64x2Ne):
16443             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForEquality,
16444                                                 Assembler::NotEqual));
16445           case uint32_t(SimdOp::I64x2LtS):
16446             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering,
16447                                                 Assembler::LessThan));
16448           case uint32_t(SimdOp::I64x2GtS):
16449             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering,
16450                                                 Assembler::GreaterThan));
16451           case uint32_t(SimdOp::I64x2LeS):
16452             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering,
16453                                                 Assembler::LessThanOrEqual));
16454           case uint32_t(SimdOp::I64x2GeS):
16455             CHECK_NEXT(dispatchVectorComparison(CmpI64x2ForOrdering,
16456                                                 Assembler::GreaterThanOrEqual));
16457           case uint32_t(SimdOp::F32x4Eq):
16458             CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::Equal));
16459           case uint32_t(SimdOp::F32x4Ne):
16460             CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::NotEqual));
16461           case uint32_t(SimdOp::F32x4Lt):
16462             CHECK_NEXT(dispatchVectorComparison(CmpF32x4, Assembler::LessThan));
16463           case uint32_t(SimdOp::F32x4Gt):
16464             CHECK_NEXT(
16465                 dispatchVectorComparison(CmpF32x4, Assembler::GreaterThan));
16466           case uint32_t(SimdOp::F32x4Le):
16467             CHECK_NEXT(
16468                 dispatchVectorComparison(CmpF32x4, Assembler::LessThanOrEqual));
16469           case uint32_t(SimdOp::F32x4Ge):
16470             CHECK_NEXT(dispatchVectorComparison(CmpF32x4,
16471                                                 Assembler::GreaterThanOrEqual));
16472           case uint32_t(SimdOp::F64x2Eq):
16473             CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::Equal));
16474           case uint32_t(SimdOp::F64x2Ne):
16475             CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::NotEqual));
16476           case uint32_t(SimdOp::F64x2Lt):
16477             CHECK_NEXT(dispatchVectorComparison(CmpF64x2, Assembler::LessThan));
16478           case uint32_t(SimdOp::F64x2Gt):
16479             CHECK_NEXT(
16480                 dispatchVectorComparison(CmpF64x2, Assembler::GreaterThan));
16481           case uint32_t(SimdOp::F64x2Le):
16482             CHECK_NEXT(
16483                 dispatchVectorComparison(CmpF64x2, Assembler::LessThanOrEqual));
16484           case uint32_t(SimdOp::F64x2Ge):
16485             CHECK_NEXT(dispatchVectorComparison(CmpF64x2,
16486                                                 Assembler::GreaterThanOrEqual));
16487           case uint32_t(SimdOp::V128And):
16488             CHECK_NEXT(dispatchVectorBinary(AndV128));
16489           case uint32_t(SimdOp::V128Or):
16490             CHECK_NEXT(dispatchVectorBinary(OrV128));
16491           case uint32_t(SimdOp::V128Xor):
16492             CHECK_NEXT(dispatchVectorBinary(XorV128));
16493           case uint32_t(SimdOp::V128AndNot):
16494             CHECK_NEXT(dispatchBinary0(emitVectorAndNot, ValType::V128));
16495           case uint32_t(SimdOp::I8x16AvgrU):
16496             CHECK_NEXT(dispatchVectorBinary(AverageUI8x16));
16497           case uint32_t(SimdOp::I16x8AvgrU):
16498             CHECK_NEXT(dispatchVectorBinary(AverageUI16x8));
16499           case uint32_t(SimdOp::I8x16Add):
16500             CHECK_NEXT(dispatchVectorBinary(AddI8x16));
16501           case uint32_t(SimdOp::I8x16AddSaturateS):
16502             CHECK_NEXT(dispatchVectorBinary(AddSatI8x16));
16503           case uint32_t(SimdOp::I8x16AddSaturateU):
16504             CHECK_NEXT(dispatchVectorBinary(AddSatUI8x16));
16505           case uint32_t(SimdOp::I8x16Sub):
16506             CHECK_NEXT(dispatchVectorBinary(SubI8x16));
16507           case uint32_t(SimdOp::I8x16SubSaturateS):
16508             CHECK_NEXT(dispatchVectorBinary(SubSatI8x16));
16509           case uint32_t(SimdOp::I8x16SubSaturateU):
16510             CHECK_NEXT(dispatchVectorBinary(SubSatUI8x16));
16511           case uint32_t(SimdOp::I8x16MinS):
16512             CHECK_NEXT(dispatchVectorBinary(MinI8x16));
16513           case uint32_t(SimdOp::I8x16MinU):
16514             CHECK_NEXT(dispatchVectorBinary(MinUI8x16));
16515           case uint32_t(SimdOp::I8x16MaxS):
16516             CHECK_NEXT(dispatchVectorBinary(MaxI8x16));
16517           case uint32_t(SimdOp::I8x16MaxU):
16518             CHECK_NEXT(dispatchVectorBinary(MaxUI8x16));
16519           case uint32_t(SimdOp::I16x8Add):
16520             CHECK_NEXT(dispatchVectorBinary(AddI16x8));
16521           case uint32_t(SimdOp::I16x8AddSaturateS):
16522             CHECK_NEXT(dispatchVectorBinary(AddSatI16x8));
16523           case uint32_t(SimdOp::I16x8AddSaturateU):
16524             CHECK_NEXT(dispatchVectorBinary(AddSatUI16x8));
16525           case uint32_t(SimdOp::I16x8Sub):
16526             CHECK_NEXT(dispatchVectorBinary(SubI16x8));
16527           case uint32_t(SimdOp::I16x8SubSaturateS):
16528             CHECK_NEXT(dispatchVectorBinary(SubSatI16x8));
16529           case uint32_t(SimdOp::I16x8SubSaturateU):
16530             CHECK_NEXT(dispatchVectorBinary(SubSatUI16x8));
16531           case uint32_t(SimdOp::I16x8Mul):
16532             CHECK_NEXT(dispatchVectorBinary(MulI16x8));
16533           case uint32_t(SimdOp::I16x8MinS):
16534             CHECK_NEXT(dispatchVectorBinary(MinI16x8));
16535           case uint32_t(SimdOp::I16x8MinU):
16536             CHECK_NEXT(dispatchVectorBinary(MinUI16x8));
16537           case uint32_t(SimdOp::I16x8MaxS):
16538             CHECK_NEXT(dispatchVectorBinary(MaxI16x8));
16539           case uint32_t(SimdOp::I16x8MaxU):
16540             CHECK_NEXT(dispatchVectorBinary(MaxUI16x8));
16541           case uint32_t(SimdOp::I32x4Add):
16542             CHECK_NEXT(dispatchVectorBinary(AddI32x4));
16543           case uint32_t(SimdOp::I32x4Sub):
16544             CHECK_NEXT(dispatchVectorBinary(SubI32x4));
16545           case uint32_t(SimdOp::I32x4Mul):
16546             CHECK_NEXT(dispatchVectorBinary(MulI32x4));
16547           case uint32_t(SimdOp::I32x4MinS):
16548             CHECK_NEXT(dispatchVectorBinary(MinI32x4));
16549           case uint32_t(SimdOp::I32x4MinU):
16550             CHECK_NEXT(dispatchVectorBinary(MinUI32x4));
16551           case uint32_t(SimdOp::I32x4MaxS):
16552             CHECK_NEXT(dispatchVectorBinary(MaxI32x4));
16553           case uint32_t(SimdOp::I32x4MaxU):
16554             CHECK_NEXT(dispatchVectorBinary(MaxUI32x4));
16555           case uint32_t(SimdOp::I64x2Add):
16556             CHECK_NEXT(dispatchVectorBinary(AddI64x2));
16557           case uint32_t(SimdOp::I64x2Sub):
16558             CHECK_NEXT(dispatchVectorBinary(SubI64x2));
16559           case uint32_t(SimdOp::I64x2Mul):
16560             CHECK_NEXT(dispatchVectorBinary(MulI64x2));
16561           case uint32_t(SimdOp::F32x4Add):
16562             CHECK_NEXT(dispatchVectorBinary(AddF32x4));
16563           case uint32_t(SimdOp::F32x4Sub):
16564             CHECK_NEXT(dispatchVectorBinary(SubF32x4));
16565           case uint32_t(SimdOp::F32x4Mul):
16566             CHECK_NEXT(dispatchVectorBinary(MulF32x4));
16567           case uint32_t(SimdOp::F32x4Div):
16568             CHECK_NEXT(dispatchVectorBinary(DivF32x4));
16569           case uint32_t(SimdOp::F32x4Min):
16570             CHECK_NEXT(dispatchVectorBinary(MinF32x4));
16571           case uint32_t(SimdOp::F32x4Max):
16572             CHECK_NEXT(dispatchVectorBinary(MaxF32x4));
16573           case uint32_t(SimdOp::F64x2Add):
16574             CHECK_NEXT(dispatchVectorBinary(AddF64x2));
16575           case uint32_t(SimdOp::F64x2Sub):
16576             CHECK_NEXT(dispatchVectorBinary(SubF64x2));
16577           case uint32_t(SimdOp::F64x2Mul):
16578             CHECK_NEXT(dispatchVectorBinary(MulF64x2));
16579           case uint32_t(SimdOp::F64x2Div):
16580             CHECK_NEXT(dispatchVectorBinary(DivF64x2));
16581           case uint32_t(SimdOp::F64x2Min):
16582             CHECK_NEXT(dispatchVectorBinary(MinF64x2));
16583           case uint32_t(SimdOp::F64x2Max):
16584             CHECK_NEXT(dispatchVectorBinary(MaxF64x2));
16585           case uint32_t(SimdOp::I8x16NarrowSI16x8):
16586             CHECK_NEXT(dispatchVectorBinary(NarrowI16x8));
16587           case uint32_t(SimdOp::I8x16NarrowUI16x8):
16588             CHECK_NEXT(dispatchVectorBinary(NarrowUI16x8));
16589           case uint32_t(SimdOp::I16x8NarrowSI32x4):
16590             CHECK_NEXT(dispatchVectorBinary(NarrowI32x4));
16591           case uint32_t(SimdOp::I16x8NarrowUI32x4):
16592             CHECK_NEXT(dispatchVectorBinary(NarrowUI32x4));
16593           case uint32_t(SimdOp::V8x16Swizzle):
16594             CHECK_NEXT(dispatchVectorBinary(Swizzle));
16595           case uint32_t(SimdOp::F32x4PMax):
16596             CHECK_NEXT(dispatchVectorBinary(PMaxF32x4));
16597           case uint32_t(SimdOp::F32x4PMin):
16598             CHECK_NEXT(dispatchVectorBinary(PMinF32x4));
16599           case uint32_t(SimdOp::F64x2PMax):
16600             CHECK_NEXT(dispatchVectorBinary(PMaxF64x2));
16601           case uint32_t(SimdOp::F64x2PMin):
16602             CHECK_NEXT(dispatchVectorBinary(PMinF64x2));
16603           case uint32_t(SimdOp::I32x4DotSI16x8):
16604             CHECK_NEXT(dispatchVectorBinary(DotI16x8));
16605           case uint32_t(SimdOp::I16x8ExtMulLowSI8x16):
16606             CHECK_NEXT(dispatchVectorBinary(ExtMulLowI8x16));
16607           case uint32_t(SimdOp::I16x8ExtMulHighSI8x16):
16608             CHECK_NEXT(dispatchVectorBinary(ExtMulHighI8x16));
16609           case uint32_t(SimdOp::I16x8ExtMulLowUI8x16):
16610             CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI8x16));
16611           case uint32_t(SimdOp::I16x8ExtMulHighUI8x16):
16612             CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI8x16));
16613           case uint32_t(SimdOp::I32x4ExtMulLowSI16x8):
16614             CHECK_NEXT(dispatchVectorBinary(ExtMulLowI16x8));
16615           case uint32_t(SimdOp::I32x4ExtMulHighSI16x8):
16616             CHECK_NEXT(dispatchVectorBinary(ExtMulHighI16x8));
16617           case uint32_t(SimdOp::I32x4ExtMulLowUI16x8):
16618             CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI16x8));
16619           case uint32_t(SimdOp::I32x4ExtMulHighUI16x8):
16620             CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI16x8));
16621           case uint32_t(SimdOp::I64x2ExtMulLowSI32x4):
16622             CHECK_NEXT(dispatchVectorBinary(ExtMulLowI32x4));
16623           case uint32_t(SimdOp::I64x2ExtMulHighSI32x4):
16624             CHECK_NEXT(dispatchVectorBinary(ExtMulHighI32x4));
16625           case uint32_t(SimdOp::I64x2ExtMulLowUI32x4):
16626             CHECK_NEXT(dispatchVectorBinary(ExtMulLowUI32x4));
16627           case uint32_t(SimdOp::I64x2ExtMulHighUI32x4):
16628             CHECK_NEXT(dispatchVectorBinary(ExtMulHighUI32x4));
16629           case uint32_t(SimdOp::I16x8Q15MulrSatS):
16630             CHECK_NEXT(dispatchVectorBinary(Q15MulrSatS));
16631           case uint32_t(SimdOp::I8x16Neg):
16632             CHECK_NEXT(dispatchVectorUnary(NegI8x16));
16633           case uint32_t(SimdOp::I16x8Neg):
16634             CHECK_NEXT(dispatchVectorUnary(NegI16x8));
16635           case uint32_t(SimdOp::I16x8WidenLowSI8x16):
16636             CHECK_NEXT(dispatchVectorUnary(WidenLowI8x16));
16637           case uint32_t(SimdOp::I16x8WidenHighSI8x16):
16638             CHECK_NEXT(dispatchVectorUnary(WidenHighI8x16));
16639           case uint32_t(SimdOp::I16x8WidenLowUI8x16):
16640             CHECK_NEXT(dispatchVectorUnary(WidenLowUI8x16));
16641           case uint32_t(SimdOp::I16x8WidenHighUI8x16):
16642             CHECK_NEXT(dispatchVectorUnary(WidenHighUI8x16));
16643           case uint32_t(SimdOp::I32x4Neg):
16644             CHECK_NEXT(dispatchVectorUnary(NegI32x4));
16645           case uint32_t(SimdOp::I32x4WidenLowSI16x8):
16646             CHECK_NEXT(dispatchVectorUnary(WidenLowI16x8));
16647           case uint32_t(SimdOp::I32x4WidenHighSI16x8):
16648             CHECK_NEXT(dispatchVectorUnary(WidenHighI16x8));
16649           case uint32_t(SimdOp::I32x4WidenLowUI16x8):
16650             CHECK_NEXT(dispatchVectorUnary(WidenLowUI16x8));
16651           case uint32_t(SimdOp::I32x4WidenHighUI16x8):
16652             CHECK_NEXT(dispatchVectorUnary(WidenHighUI16x8));
16653           case uint32_t(SimdOp::I32x4TruncSSatF32x4):
16654             CHECK_NEXT(dispatchVectorUnary(ConvertF32x4ToI32x4));
16655           case uint32_t(SimdOp::I32x4TruncUSatF32x4):
16656             CHECK_NEXT(dispatchVectorUnary(ConvertF32x4ToUI32x4));
16657           case uint32_t(SimdOp::I64x2Neg):
16658             CHECK_NEXT(dispatchVectorUnary(NegI64x2));
16659           case uint32_t(SimdOp::I64x2WidenLowSI32x4):
16660             CHECK_NEXT(dispatchVectorUnary(WidenLowI32x4));
16661           case uint32_t(SimdOp::I64x2WidenHighSI32x4):
16662             CHECK_NEXT(dispatchVectorUnary(WidenHighI32x4));
16663           case uint32_t(SimdOp::I64x2WidenLowUI32x4):
16664             CHECK_NEXT(dispatchVectorUnary(WidenLowUI32x4));
16665           case uint32_t(SimdOp::I64x2WidenHighUI32x4):
16666             CHECK_NEXT(dispatchVectorUnary(WidenHighUI32x4));
16667           case uint32_t(SimdOp::F32x4Abs):
16668             CHECK_NEXT(dispatchVectorUnary(AbsF32x4));
16669           case uint32_t(SimdOp::F32x4Neg):
16670             CHECK_NEXT(dispatchVectorUnary(NegF32x4));
16671           case uint32_t(SimdOp::F32x4Sqrt):
16672             CHECK_NEXT(dispatchVectorUnary(SqrtF32x4));
16673           case uint32_t(SimdOp::F32x4ConvertSI32x4):
16674             CHECK_NEXT(dispatchVectorUnary(ConvertI32x4ToF32x4));
16675           case uint32_t(SimdOp::F32x4ConvertUI32x4):
16676             CHECK_NEXT(dispatchVectorUnary(ConvertUI32x4ToF32x4));
16677           case uint32_t(SimdOp::F32x4DemoteF64x2Zero):
16678             CHECK_NEXT(dispatchVectorUnary(DemoteF64x2ToF32x4));
16679           case uint32_t(SimdOp::F64x2PromoteLowF32x4):
16680             CHECK_NEXT(dispatchVectorUnary(PromoteF32x4ToF64x2));
16681           case uint32_t(SimdOp::F64x2ConvertLowI32x4S):
16682             CHECK_NEXT(dispatchVectorUnary(ConvertI32x4ToF64x2));
16683           case uint32_t(SimdOp::F64x2ConvertLowI32x4U):
16684             CHECK_NEXT(dispatchVectorUnary(ConvertUI32x4ToF64x2));
16685           case uint32_t(SimdOp::I32x4TruncSatF64x2SZero):
16686             CHECK_NEXT(dispatchVectorUnary(ConvertF64x2ToI32x4));
16687           case uint32_t(SimdOp::I32x4TruncSatF64x2UZero):
16688             CHECK_NEXT(dispatchVectorUnary(ConvertF64x2ToUI32x4));
16689           case uint32_t(SimdOp::F64x2Abs):
16690             CHECK_NEXT(dispatchVectorUnary(AbsF64x2));
16691           case uint32_t(SimdOp::F64x2Neg):
16692             CHECK_NEXT(dispatchVectorUnary(NegF64x2));
16693           case uint32_t(SimdOp::F64x2Sqrt):
16694             CHECK_NEXT(dispatchVectorUnary(SqrtF64x2));
16695           case uint32_t(SimdOp::V128Not):
16696             CHECK_NEXT(dispatchVectorUnary(NotV128));
16697           case uint32_t(SimdOp::I8x16Popcnt):
16698             CHECK_NEXT(dispatchVectorUnary(PopcntI8x16));
16699           case uint32_t(SimdOp::I8x16Abs):
16700             CHECK_NEXT(dispatchVectorUnary(AbsI8x16));
16701           case uint32_t(SimdOp::I16x8Abs):
16702             CHECK_NEXT(dispatchVectorUnary(AbsI16x8));
16703           case uint32_t(SimdOp::I32x4Abs):
16704             CHECK_NEXT(dispatchVectorUnary(AbsI32x4));
16705           case uint32_t(SimdOp::I64x2Abs):
16706             CHECK_NEXT(dispatchVectorUnary(AbsI64x2));
16707           case uint32_t(SimdOp::F32x4Ceil):
16708             CHECK_NEXT(dispatchVectorUnary(CeilF32x4));
16709           case uint32_t(SimdOp::F32x4Floor):
16710             CHECK_NEXT(dispatchVectorUnary(FloorF32x4));
16711           case uint32_t(SimdOp::F32x4Trunc):
16712             CHECK_NEXT(dispatchVectorUnary(TruncF32x4));
16713           case uint32_t(SimdOp::F32x4Nearest):
16714             CHECK_NEXT(dispatchVectorUnary(NearestF32x4));
16715           case uint32_t(SimdOp::F64x2Ceil):
16716             CHECK_NEXT(dispatchVectorUnary(CeilF64x2));
16717           case uint32_t(SimdOp::F64x2Floor):
16718             CHECK_NEXT(dispatchVectorUnary(FloorF64x2));
16719           case uint32_t(SimdOp::F64x2Trunc):
16720             CHECK_NEXT(dispatchVectorUnary(TruncF64x2));
16721           case uint32_t(SimdOp::F64x2Nearest):
16722             CHECK_NEXT(dispatchVectorUnary(NearestF64x2));
16723           case uint32_t(SimdOp::I16x8ExtAddPairwiseI8x16S):
16724             CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseI8x16));
16725           case uint32_t(SimdOp::I16x8ExtAddPairwiseI8x16U):
16726             CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseUI8x16));
16727           case uint32_t(SimdOp::I32x4ExtAddPairwiseI16x8S):
16728             CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseI16x8));
16729           case uint32_t(SimdOp::I32x4ExtAddPairwiseI16x8U):
16730             CHECK_NEXT(dispatchVectorUnary(ExtAddPairwiseUI16x8));
16731           case uint32_t(SimdOp::I8x16Shl):
16732             CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI8x16));
16733           case uint32_t(SimdOp::I8x16ShrS):
16734             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI8x16));
16735           case uint32_t(SimdOp::I8x16ShrU):
16736             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI8x16));
16737           case uint32_t(SimdOp::I16x8Shl):
16738             CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI16x8));
16739           case uint32_t(SimdOp::I16x8ShrS):
16740             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI16x8));
16741           case uint32_t(SimdOp::I16x8ShrU):
16742             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI16x8));
16743           case uint32_t(SimdOp::I32x4Shl):
16744             CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI32x4));
16745           case uint32_t(SimdOp::I32x4ShrS):
16746             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI32x4));
16747           case uint32_t(SimdOp::I32x4ShrU):
16748             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI32x4));
16749           case uint32_t(SimdOp::I64x2Shl):
16750             CHECK_NEXT(dispatchVectorVariableShift(ShiftLeftI64x2));
16751           case uint32_t(SimdOp::I64x2ShrS):
16752 #  if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
16753             CHECK_NEXT(emitVectorShiftRightI64x2());
16754 #  else
16755             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightI64x2));
16756 #  endif
16757           case uint32_t(SimdOp::I64x2ShrU):
16758             CHECK_NEXT(dispatchVectorVariableShift(ShiftRightUI64x2));
16759           case uint32_t(SimdOp::V128Bitselect):
16760             CHECK_NEXT(emitBitselect());
16761           case uint32_t(SimdOp::V8x16Shuffle):
16762             CHECK_NEXT(emitVectorShuffle());
16763           case uint32_t(SimdOp::V128Const): {
16764             V128 v128;
16765             CHECK(iter_.readV128Const(&v128));
16766             if (!deadCode_) {
16767               pushV128(v128);
16768             }
16769             NEXT();
16770           }
16771           case uint32_t(SimdOp::V128Load):
16772             CHECK_NEXT(emitLoad(ValType::V128, Scalar::Simd128));
16773           case uint32_t(SimdOp::V8x16LoadSplat):
16774             CHECK_NEXT(emitLoadSplat(Scalar::Uint8));
16775           case uint32_t(SimdOp::V16x8LoadSplat):
16776             CHECK_NEXT(emitLoadSplat(Scalar::Uint16));
16777           case uint32_t(SimdOp::V32x4LoadSplat):
16778             CHECK_NEXT(emitLoadSplat(Scalar::Uint32));
16779           case uint32_t(SimdOp::V64x2LoadSplat):
16780             CHECK_NEXT(emitLoadSplat(Scalar::Int64));
16781           case uint32_t(SimdOp::I16x8LoadS8x8):
16782             CHECK_NEXT(emitLoadExtend(Scalar::Int8));
16783           case uint32_t(SimdOp::I16x8LoadU8x8):
16784             CHECK_NEXT(emitLoadExtend(Scalar::Uint8));
16785           case uint32_t(SimdOp::I32x4LoadS16x4):
16786             CHECK_NEXT(emitLoadExtend(Scalar::Int16));
16787           case uint32_t(SimdOp::I32x4LoadU16x4):
16788             CHECK_NEXT(emitLoadExtend(Scalar::Uint16));
16789           case uint32_t(SimdOp::I64x2LoadS32x2):
16790             CHECK_NEXT(emitLoadExtend(Scalar::Int32));
16791           case uint32_t(SimdOp::I64x2LoadU32x2):
16792             CHECK_NEXT(emitLoadExtend(Scalar::Uint32));
16793           case uint32_t(SimdOp::V128Load32Zero):
16794             CHECK_NEXT(emitLoadZero(Scalar::Float32));
16795           case uint32_t(SimdOp::V128Load64Zero):
16796             CHECK_NEXT(emitLoadZero(Scalar::Float64));
16797           case uint32_t(SimdOp::V128Store):
16798             CHECK_NEXT(emitStore(ValType::V128, Scalar::Simd128));
16799           case uint32_t(SimdOp::V128Load8Lane):
16800             CHECK_NEXT(emitLoadLane(1));
16801           case uint32_t(SimdOp::V128Load16Lane):
16802             CHECK_NEXT(emitLoadLane(2));
16803           case uint32_t(SimdOp::V128Load32Lane):
16804             CHECK_NEXT(emitLoadLane(4));
16805           case uint32_t(SimdOp::V128Load64Lane):
16806             CHECK_NEXT(emitLoadLane(8));
16807           case uint32_t(SimdOp::V128Store8Lane):
16808             CHECK_NEXT(emitStoreLane(1));
16809           case uint32_t(SimdOp::V128Store16Lane):
16810             CHECK_NEXT(emitStoreLane(2));
16811           case uint32_t(SimdOp::V128Store32Lane):
16812             CHECK_NEXT(emitStoreLane(4));
16813           case uint32_t(SimdOp::V128Store64Lane):
16814             CHECK_NEXT(emitStoreLane(8));
16815           default:
16816             break;
16817         }  // switch (op.b1)
16818         return iter_.unrecognizedOpcode(&op);
16819       }
16820 #endif  // ENABLE_WASM_SIMD
16821 
16822       // "Miscellaneous" operations
16823       case uint16_t(Op::MiscPrefix): {
16824         switch (op.b1) {
16825           case uint32_t(MiscOp::I32TruncSSatF32):
16826             CHECK_NEXT(
16827                 dispatchConversionOOM(emitTruncateF32ToI32<TRUNC_SATURATING>,
16828                                       ValType::F32, ValType::I32));
16829           case uint32_t(MiscOp::I32TruncUSatF32):
16830             CHECK_NEXT(dispatchConversionOOM(
16831                 emitTruncateF32ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
16832                 ValType::F32, ValType::I32));
16833           case uint32_t(MiscOp::I32TruncSSatF64):
16834             CHECK_NEXT(
16835                 dispatchConversionOOM(emitTruncateF64ToI32<TRUNC_SATURATING>,
16836                                       ValType::F64, ValType::I32));
16837           case uint32_t(MiscOp::I32TruncUSatF64):
16838             CHECK_NEXT(dispatchConversionOOM(
16839                 emitTruncateF64ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
16840                 ValType::F64, ValType::I32));
16841           case uint32_t(MiscOp::I64TruncSSatF32):
16842 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
16843             CHECK_NEXT(dispatchCalloutConversionOOM(
16844                 emitConvertFloatingToInt64Callout,
16845                 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F32,
16846                 ValType::I64));
16847 #else
16848             CHECK_NEXT(
16849                 dispatchConversionOOM(emitTruncateF32ToI64<TRUNC_SATURATING>,
16850                                       ValType::F32, ValType::I64));
16851 #endif
16852           case uint32_t(MiscOp::I64TruncUSatF32):
16853 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
16854             CHECK_NEXT(dispatchCalloutConversionOOM(
16855                 emitConvertFloatingToInt64Callout,
16856                 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F32,
16857                 ValType::I64));
16858 #else
16859             CHECK_NEXT(dispatchConversionOOM(
16860                 emitTruncateF32ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
16861                 ValType::F32, ValType::I64));
16862 #endif
16863           case uint32_t(MiscOp::I64TruncSSatF64):
16864 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
16865             CHECK_NEXT(dispatchCalloutConversionOOM(
16866                 emitConvertFloatingToInt64Callout,
16867                 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F64,
16868                 ValType::I64));
16869 #else
16870             CHECK_NEXT(
16871                 dispatchConversionOOM(emitTruncateF64ToI64<TRUNC_SATURATING>,
16872                                       ValType::F64, ValType::I64));
16873 #endif
16874           case uint32_t(MiscOp::I64TruncUSatF64):
16875 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
16876             CHECK_NEXT(dispatchCalloutConversionOOM(
16877                 emitConvertFloatingToInt64Callout,
16878                 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F64,
16879                 ValType::I64));
16880 #else
16881             CHECK_NEXT(dispatchConversionOOM(
16882                 emitTruncateF64ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
16883                 ValType::F64, ValType::I64));
16884 #endif
16885           case uint32_t(MiscOp::MemCopy):
16886             CHECK_NEXT(emitMemCopy());
16887           case uint32_t(MiscOp::DataDrop):
16888             CHECK_NEXT(emitDataOrElemDrop(/*isData=*/true));
16889           case uint32_t(MiscOp::MemFill):
16890             CHECK_NEXT(emitMemFill());
16891           case uint32_t(MiscOp::MemInit):
16892             CHECK_NEXT(emitMemInit());
16893           case uint32_t(MiscOp::TableCopy):
16894             CHECK_NEXT(emitTableCopy());
16895           case uint32_t(MiscOp::ElemDrop):
16896             CHECK_NEXT(emitDataOrElemDrop(/*isData=*/false));
16897           case uint32_t(MiscOp::TableInit):
16898             CHECK_NEXT(emitTableInit());
16899           case uint32_t(MiscOp::TableFill):
16900             CHECK_NEXT(emitTableFill());
16901           case uint32_t(MiscOp::TableGrow):
16902             CHECK_NEXT(emitTableGrow());
16903           case uint32_t(MiscOp::TableSize):
16904             CHECK_NEXT(emitTableSize());
16905           default:
16906             break;
16907         }  // switch (op.b1)
16908         return iter_.unrecognizedOpcode(&op);
16909       }
16910 
16911       // Thread operations
16912       case uint16_t(Op::ThreadPrefix): {
16913         // Though thread ops can be used on nonshared memories, we make them
16914         // unavailable if shared memory has been disabled in the prefs, for
16915         // maximum predictability and safety and consistency with JS.
16916         if (moduleEnv_.sharedMemoryEnabled() == Shareable::False) {
16917           return iter_.unrecognizedOpcode(&op);
16918         }
16919         switch (op.b1) {
16920           case uint32_t(ThreadOp::Wake):
16921             CHECK_NEXT(emitWake());
16922 
16923           case uint32_t(ThreadOp::I32Wait):
16924             CHECK_NEXT(emitWait(ValType::I32, 4));
16925           case uint32_t(ThreadOp::I64Wait):
16926             CHECK_NEXT(emitWait(ValType::I64, 8));
16927           case uint32_t(ThreadOp::Fence):
16928             CHECK_NEXT(emitFence());
16929 
16930           case uint32_t(ThreadOp::I32AtomicLoad):
16931             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Int32));
16932           case uint32_t(ThreadOp::I64AtomicLoad):
16933             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Int64));
16934           case uint32_t(ThreadOp::I32AtomicLoad8U):
16935             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint8));
16936           case uint32_t(ThreadOp::I32AtomicLoad16U):
16937             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint16));
16938           case uint32_t(ThreadOp::I64AtomicLoad8U):
16939             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint8));
16940           case uint32_t(ThreadOp::I64AtomicLoad16U):
16941             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint16));
16942           case uint32_t(ThreadOp::I64AtomicLoad32U):
16943             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint32));
16944 
16945           case uint32_t(ThreadOp::I32AtomicStore):
16946             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Int32));
16947           case uint32_t(ThreadOp::I64AtomicStore):
16948             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Int64));
16949           case uint32_t(ThreadOp::I32AtomicStore8U):
16950             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint8));
16951           case uint32_t(ThreadOp::I32AtomicStore16U):
16952             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint16));
16953           case uint32_t(ThreadOp::I64AtomicStore8U):
16954             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint8));
16955           case uint32_t(ThreadOp::I64AtomicStore16U):
16956             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint16));
16957           case uint32_t(ThreadOp::I64AtomicStore32U):
16958             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint32));
16959 
16960           case uint32_t(ThreadOp::I32AtomicAdd):
16961             CHECK_NEXT(
16962                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAddOp));
16963           case uint32_t(ThreadOp::I64AtomicAdd):
16964             CHECK_NEXT(
16965                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAddOp));
16966           case uint32_t(ThreadOp::I32AtomicAdd8U):
16967             CHECK_NEXT(
16968                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAddOp));
16969           case uint32_t(ThreadOp::I32AtomicAdd16U):
16970             CHECK_NEXT(
16971                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAddOp));
16972           case uint32_t(ThreadOp::I64AtomicAdd8U):
16973             CHECK_NEXT(
16974                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAddOp));
16975           case uint32_t(ThreadOp::I64AtomicAdd16U):
16976             CHECK_NEXT(
16977                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAddOp));
16978           case uint32_t(ThreadOp::I64AtomicAdd32U):
16979             CHECK_NEXT(
16980                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAddOp));
16981 
16982           case uint32_t(ThreadOp::I32AtomicSub):
16983             CHECK_NEXT(
16984                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchSubOp));
16985           case uint32_t(ThreadOp::I64AtomicSub):
16986             CHECK_NEXT(
16987                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchSubOp));
16988           case uint32_t(ThreadOp::I32AtomicSub8U):
16989             CHECK_NEXT(
16990                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchSubOp));
16991           case uint32_t(ThreadOp::I32AtomicSub16U):
16992             CHECK_NEXT(
16993                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchSubOp));
16994           case uint32_t(ThreadOp::I64AtomicSub8U):
16995             CHECK_NEXT(
16996                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchSubOp));
16997           case uint32_t(ThreadOp::I64AtomicSub16U):
16998             CHECK_NEXT(
16999                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchSubOp));
17000           case uint32_t(ThreadOp::I64AtomicSub32U):
17001             CHECK_NEXT(
17002                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchSubOp));
17003 
17004           case uint32_t(ThreadOp::I32AtomicAnd):
17005             CHECK_NEXT(
17006                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAndOp));
17007           case uint32_t(ThreadOp::I64AtomicAnd):
17008             CHECK_NEXT(
17009                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAndOp));
17010           case uint32_t(ThreadOp::I32AtomicAnd8U):
17011             CHECK_NEXT(
17012                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAndOp));
17013           case uint32_t(ThreadOp::I32AtomicAnd16U):
17014             CHECK_NEXT(
17015                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAndOp));
17016           case uint32_t(ThreadOp::I64AtomicAnd8U):
17017             CHECK_NEXT(
17018                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAndOp));
17019           case uint32_t(ThreadOp::I64AtomicAnd16U):
17020             CHECK_NEXT(
17021                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAndOp));
17022           case uint32_t(ThreadOp::I64AtomicAnd32U):
17023             CHECK_NEXT(
17024                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAndOp));
17025 
17026           case uint32_t(ThreadOp::I32AtomicOr):
17027             CHECK_NEXT(
17028                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchOrOp));
17029           case uint32_t(ThreadOp::I64AtomicOr):
17030             CHECK_NEXT(
17031                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchOrOp));
17032           case uint32_t(ThreadOp::I32AtomicOr8U):
17033             CHECK_NEXT(
17034                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchOrOp));
17035           case uint32_t(ThreadOp::I32AtomicOr16U):
17036             CHECK_NEXT(
17037                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchOrOp));
17038           case uint32_t(ThreadOp::I64AtomicOr8U):
17039             CHECK_NEXT(
17040                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchOrOp));
17041           case uint32_t(ThreadOp::I64AtomicOr16U):
17042             CHECK_NEXT(
17043                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchOrOp));
17044           case uint32_t(ThreadOp::I64AtomicOr32U):
17045             CHECK_NEXT(
17046                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchOrOp));
17047 
17048           case uint32_t(ThreadOp::I32AtomicXor):
17049             CHECK_NEXT(
17050                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchXorOp));
17051           case uint32_t(ThreadOp::I64AtomicXor):
17052             CHECK_NEXT(
17053                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchXorOp));
17054           case uint32_t(ThreadOp::I32AtomicXor8U):
17055             CHECK_NEXT(
17056                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchXorOp));
17057           case uint32_t(ThreadOp::I32AtomicXor16U):
17058             CHECK_NEXT(
17059                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchXorOp));
17060           case uint32_t(ThreadOp::I64AtomicXor8U):
17061             CHECK_NEXT(
17062                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchXorOp));
17063           case uint32_t(ThreadOp::I64AtomicXor16U):
17064             CHECK_NEXT(
17065                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchXorOp));
17066           case uint32_t(ThreadOp::I64AtomicXor32U):
17067             CHECK_NEXT(
17068                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchXorOp));
17069 
17070           case uint32_t(ThreadOp::I32AtomicXchg):
17071             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Int32));
17072           case uint32_t(ThreadOp::I64AtomicXchg):
17073             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Int64));
17074           case uint32_t(ThreadOp::I32AtomicXchg8U):
17075             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint8));
17076           case uint32_t(ThreadOp::I32AtomicXchg16U):
17077             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint16));
17078           case uint32_t(ThreadOp::I64AtomicXchg8U):
17079             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint8));
17080           case uint32_t(ThreadOp::I64AtomicXchg16U):
17081             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint16));
17082           case uint32_t(ThreadOp::I64AtomicXchg32U):
17083             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint32));
17084 
17085           case uint32_t(ThreadOp::I32AtomicCmpXchg):
17086             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Int32));
17087           case uint32_t(ThreadOp::I64AtomicCmpXchg):
17088             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Int64));
17089           case uint32_t(ThreadOp::I32AtomicCmpXchg8U):
17090             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint8));
17091           case uint32_t(ThreadOp::I32AtomicCmpXchg16U):
17092             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint16));
17093           case uint32_t(ThreadOp::I64AtomicCmpXchg8U):
17094             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint8));
17095           case uint32_t(ThreadOp::I64AtomicCmpXchg16U):
17096             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint16));
17097           case uint32_t(ThreadOp::I64AtomicCmpXchg32U):
17098             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint32));
17099 
17100           default:
17101             return iter_.unrecognizedOpcode(&op);
17102         }
17103         break;
17104       }
17105 
17106       // asm.js and other private operations
17107       case uint16_t(Op::MozPrefix):
17108         return iter_.unrecognizedOpcode(&op);
17109 
17110       default:
17111         return iter_.unrecognizedOpcode(&op);
17112     }
17113 
17114 #undef CHECK
17115 #undef NEXT
17116 #undef CHECK_NEXT
17117 #undef CHECK_POINTER_COUNT
17118 #undef dispatchBinary0
17119 #undef dispatchBinary1
17120 #undef dispatchBinary2
17121 #undef dispatchBinary3
17122 #undef dispatchUnary0
17123 #undef dispatchUnary1
17124 #undef dispatchUnary2
17125 #undef dispatchComparison0
17126 #undef dispatchConversion0
17127 #undef dispatchConversion1
17128 #undef dispatchConversionOOM
17129 #undef dispatchCalloutConversionOOM
17130 #undef dispatchIntDivCallout
17131 #undef dispatchVectorBinary
17132 #undef dispatchVectorUnary
17133 #undef dispatchVectorComparison
17134 #undef dispatchExtractLane
17135 #undef dispatchReplaceLane
17136 #undef dispatchSplat
17137 #undef dispatchVectorReduction
17138 
17139     MOZ_CRASH("unreachable");
17140   }
17141 
17142   MOZ_CRASH("unreachable");
17143 }
17144 
emitFunction()17145 bool BaseCompiler::emitFunction() {
17146   if (!beginFunction()) {
17147     return false;
17148   }
17149 
17150   if (!emitBody()) {
17151     return false;
17152   }
17153 
17154   if (!endFunction()) {
17155     return false;
17156   }
17157 
17158   return true;
17159 }
17160 
BaseCompiler(const ModuleEnvironment & moduleEnv,const CompilerEnvironment & compilerEnv,const FuncCompileInput & func,const ValTypeVector & locals,const MachineState & trapExitLayout,size_t trapExitLayoutNumWords,Decoder & decoder,StkVector & stkSource,TempAllocator * alloc,MacroAssembler * masm,StackMaps * stackMaps)17161 BaseCompiler::BaseCompiler(const ModuleEnvironment& moduleEnv,
17162                            const CompilerEnvironment& compilerEnv,
17163                            const FuncCompileInput& func,
17164                            const ValTypeVector& locals,
17165                            const MachineState& trapExitLayout,
17166                            size_t trapExitLayoutNumWords, Decoder& decoder,
17167                            StkVector& stkSource, TempAllocator* alloc,
17168                            MacroAssembler* masm, StackMaps* stackMaps)
17169     : moduleEnv_(moduleEnv),
17170       compilerEnv_(compilerEnv),
17171       iter_(moduleEnv, decoder),
17172       func_(func),
17173       lastReadCallSite_(0),
17174       alloc_(alloc->fallible()),
17175       locals_(locals),
17176       deadCode_(false),
17177       bceSafe_(0),
17178       latentOp_(LatentOp::None),
17179       latentType_(ValType::I32),
17180       latentIntCmp_(Assembler::Equal),
17181       latentDoubleCmp_(Assembler::DoubleEqual),
17182       masm(*masm),
17183       fr(*masm),
17184       stackMapGenerator_(stackMaps, trapExitLayout, trapExitLayoutNumWords,
17185                          *masm),
17186       stkSource_(stkSource) {
17187   // Our caller, BaselineCompileFunctions, will lend us the vector contents to
17188   // use for the eval stack.  To get hold of those contents, we'll temporarily
17189   // installing an empty one in its place.
17190   MOZ_ASSERT(stk_.empty());
17191   stk_.swap(stkSource_);
17192 
17193   // Assuming that previously processed wasm functions are well formed, the
17194   // eval stack should now be empty.  But empty it anyway; any non-emptyness
17195   // at this point will cause chaos.
17196   stk_.clear();
17197 }
17198 
~BaseCompiler()17199 BaseCompiler::~BaseCompiler() {
17200   stk_.swap(stkSource_);
17201   // We've returned the eval stack vector contents to our caller,
17202   // BaselineCompileFunctions.  We expect the vector we get in return to be
17203   // empty since that's what we swapped for the stack vector in our
17204   // constructor.
17205   MOZ_ASSERT(stk_.empty());
17206 }
17207 
init()17208 bool BaseCompiler::init() {
17209   ra.init(this);
17210 
17211   if (!SigD_.append(ValType::F64)) {
17212     return false;
17213   }
17214   if (!SigF_.append(ValType::F32)) {
17215     return false;
17216   }
17217 
17218   ArgTypeVector args(funcType());
17219   return fr.setupLocals(locals_, args, compilerEnv_.debugEnabled(),
17220                         &localInfo_);
17221 }
17222 
finish()17223 FuncOffsets BaseCompiler::finish() {
17224   MOZ_ASSERT(done(), "all bytes must be consumed");
17225   MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_);
17226 
17227   MOZ_ASSERT(stk_.empty());
17228   MOZ_ASSERT(stackMapGenerator_.memRefsOnStk == 0);
17229 
17230   masm.flushBuffer();
17231 
17232   return offsets_;
17233 }
17234 
17235 }  // namespace wasm
17236 }  // namespace js
17237 
BaselinePlatformSupport()17238 bool js::wasm::BaselinePlatformSupport() {
17239 #if defined(JS_CODEGEN_ARM)
17240   // Simplifying assumption: require SDIV and UDIV.
17241   //
17242   // I have no good data on ARM populations allowing me to say that
17243   // X% of devices in the market implement SDIV and UDIV.  However,
17244   // they are definitely implemented on the Cortex-A7 and Cortex-A15
17245   // and on all ARMv8 systems.
17246   if (!HasIDIV()) {
17247     return false;
17248   }
17249 #endif
17250 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) ||   \
17251     defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
17252     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
17253   return true;
17254 #else
17255   return false;
17256 #endif
17257 }
17258 
BaselineCompileFunctions(const ModuleEnvironment & moduleEnv,const CompilerEnvironment & compilerEnv,LifoAlloc & lifo,const FuncCompileInputVector & inputs,CompiledCode * code,UniqueChars * error)17259 bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& moduleEnv,
17260                                         const CompilerEnvironment& compilerEnv,
17261                                         LifoAlloc& lifo,
17262                                         const FuncCompileInputVector& inputs,
17263                                         CompiledCode* code,
17264                                         UniqueChars* error) {
17265   MOZ_ASSERT(compilerEnv.tier() == Tier::Baseline);
17266   MOZ_ASSERT(moduleEnv.kind == ModuleKind::Wasm);
17267 
17268   // The MacroAssembler will sometimes access the jitContext.
17269 
17270   TempAllocator alloc(&lifo);
17271   JitContext jitContext(&alloc);
17272   MOZ_ASSERT(IsCompilingWasm());
17273   WasmMacroAssembler masm(alloc, moduleEnv);
17274 
17275   // Swap in already-allocated empty vectors to avoid malloc/free.
17276   MOZ_ASSERT(code->empty());
17277   if (!code->swap(masm)) {
17278     return false;
17279   }
17280 
17281   // Create a description of the stack layout created by GenerateTrapExit().
17282   MachineState trapExitLayout;
17283   size_t trapExitLayoutNumWords;
17284   GenerateTrapExitMachineState(&trapExitLayout, &trapExitLayoutNumWords);
17285 
17286   // The compiler's operand stack.  We reuse it across all functions so as to
17287   // avoid malloc/free.  Presize it to 128 elements in the hope of avoiding
17288   // reallocation later.
17289   StkVector stk;
17290   if (!stk.reserve(128)) {
17291     return false;
17292   }
17293 
17294   for (const FuncCompileInput& func : inputs) {
17295     Decoder d(func.begin, func.end, func.lineOrBytecode, error);
17296 
17297     // Build the local types vector.
17298 
17299     ValTypeVector locals;
17300     if (!locals.appendAll(moduleEnv.funcs[func.index].type->args())) {
17301       return false;
17302     }
17303     if (!DecodeLocalEntries(d, moduleEnv.types, moduleEnv.features, &locals)) {
17304       return false;
17305     }
17306 
17307     // One-pass baseline compilation.
17308 
17309     BaseCompiler f(moduleEnv, compilerEnv, func, locals, trapExitLayout,
17310                    trapExitLayoutNumWords, d, stk, &alloc, &masm,
17311                    &code->stackMaps);
17312     if (!f.init()) {
17313       return false;
17314     }
17315     if (!f.emitFunction()) {
17316       return false;
17317     }
17318     if (!code->codeRanges.emplaceBack(func.index, func.lineOrBytecode,
17319                                       f.finish())) {
17320       return false;
17321     }
17322   }
17323 
17324   masm.finish();
17325   if (masm.oom()) {
17326     return false;
17327   }
17328 
17329   return code->swap(masm);
17330 }
17331 
17332 #undef RABALDR_INT_DIV_I64_CALLOUT
17333 #undef RABALDR_I64_TO_FLOAT_CALLOUT
17334 #undef RABALDR_FLOAT_TO_I64_CALLOUT
17335