1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 /* WebAssembly baseline compiler ("RabaldrMonkey")
20  *
21  * General assumptions for 32-bit vs 64-bit code:
22  *
23  * - A 32-bit register can be extended in-place to a 64-bit register on 64-bit
24  *   systems.
25  *
26  * - Code that knows that Register64 has a '.reg' member on 64-bit systems and
27  *   '.high' and '.low' members on 32-bit systems, or knows the implications
28  *   thereof, is #ifdef JS_PUNBOX64.  All other code is #if(n)?def JS_64BIT.
29  *
30  *
31  * Coding standards:
32  *
33  * - In "small" code generating functions (eg emitMultiplyF64, emitQuotientI32,
34  *   and surrounding functions; most functions fall into this class) where the
35  *   meaning is obvious:
36  *
37  *   - if there is a single source + destination register, it is called 'r'
38  *   - if there is one source and a different destination, they are called 'rs'
39  *     and 'rd'
40  *   - if there is one source + destination register and another source register
41  *     they are called 'r' and 'rs'
42  *   - if there are two source registers and a destination register they are
43  *     called 'rs0', 'rs1', and 'rd'.
44  *
45  * - Generic temp registers are named /temp[0-9]?/ not /tmp[0-9]?/.
46  *
47  * - Registers can be named non-generically for their function ('rp' for the
48  *   'pointer' register and 'rv' for the 'value' register are typical) and those
49  *   names may or may not have an 'r' prefix.
50  *
51  * - "Larger" code generating functions make their own rules.
52  *
53  *
54  * General status notes:
55  *
56  * "FIXME" indicates a known or suspected bug.  Always has a bug#.
57  *
58  * "TODO" indicates an opportunity for a general improvement, with an additional
59  * tag to indicate the area of improvement.  Usually has a bug#.
60  *
61  * There are lots of machine dependencies here but they are pretty well isolated
62  * to a segment of the compiler.  Many dependencies will eventually be factored
63  * into the MacroAssembler layer and shared with other code generators.
64  *
65  *
66  * High-value compiler performance improvements:
67  *
68  * - (Bug 1316802) The specific-register allocator (the needI32(r), needI64(r)
69  *   etc methods) can avoid syncing the value stack if the specific register is
70  *   in use but there is a free register to shuffle the specific register into.
71  *   (This will also improve the generated code.)  The sync happens often enough
72  *   here to show up in profiles, because it is triggered by integer multiply
73  *   and divide.
74  *
75  *
76  * High-value code generation improvements:
77  *
78  * - (Bug 1316804) brTable pessimizes by always dispatching to code that pops
79  *   the stack and then jumps to the code for the target case.  If no cleanup is
80  *   needed we could just branch conditionally to the target; if the same amount
81  *   of cleanup is needed for all cases then the cleanup can be done before the
82  *   dispatch.  Both are highly likely.
83  *
84  * - (Bug 1316806) Register management around calls: At the moment we sync the
85  *   value stack unconditionally (this is simple) but there are probably many
86  *   common cases where we could instead save/restore live caller-saves
87  *   registers and perform parallel assignment into argument registers.  This
88  *   may be important if we keep some locals in registers.
89  *
90  * - (Bug 1316808) Allocate some locals to registers on machines where there are
91  *   enough registers.  This is probably hard to do well in a one-pass compiler
92  *   but it might be that just keeping register arguments and the first few
93  *   locals in registers is a viable strategy; another (more general) strategy
94  *   is caching locals in registers in straight-line code.  Such caching could
95  *   also track constant values in registers, if that is deemed valuable.  A
96  *   combination of techniques may be desirable: parameters and the first few
97  *   locals could be cached on entry to the function but not statically assigned
98  *   to registers throughout.
99  *
100  *   (On a large corpus of code it should be possible to compute, for every
101  *   signature comprising the types of parameters and locals, and using a static
102  *   weight for loops, a list in priority order of which parameters and locals
103  *   that should be assigned to registers.  Or something like that.  Wasm makes
104  *   this simple.  Static assignments are desirable because they are not flushed
105  *   to memory by the pre-block sync() call.)
106  */
107 
108 #include "wasm/WasmBaselineCompile.h"
109 
110 #include "mozilla/MathAlgorithms.h"
111 #include "mozilla/Maybe.h"
112 
113 #include "jit/AtomicOp.h"
114 #include "jit/IonTypes.h"
115 #include "jit/JitAllocPolicy.h"
116 #include "jit/Label.h"
117 #include "jit/MacroAssembler.h"
118 #include "jit/MIR.h"
119 #include "jit/RegisterAllocator.h"
120 #include "jit/Registers.h"
121 #include "jit/RegisterSets.h"
122 #if defined(JS_CODEGEN_ARM)
123 #include "jit/arm/Assembler-arm.h"
124 #endif
125 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
126 #include "jit/x86-shared/Architecture-x86-shared.h"
127 #include "jit/x86-shared/Assembler-x86-shared.h"
128 #endif
129 #if defined(JS_CODEGEN_MIPS32)
130 #include "jit/mips-shared/Assembler-mips-shared.h"
131 #include "jit/mips32/Assembler-mips32.h"
132 #endif
133 #if defined(JS_CODEGEN_MIPS64)
134 #include "jit/mips-shared/Assembler-mips-shared.h"
135 #include "jit/mips64/Assembler-mips64.h"
136 #endif
137 
138 #include "wasm/WasmBinaryIterator.h"
139 #include "wasm/WasmGenerator.h"
140 #include "wasm/WasmSignalHandlers.h"
141 #include "wasm/WasmValidate.h"
142 
143 #include "jit/MacroAssembler-inl.h"
144 
145 using mozilla::DebugOnly;
146 using mozilla::FloorLog2;
147 using mozilla::IsPowerOfTwo;
148 using mozilla::Maybe;
149 
150 namespace js {
151 namespace wasm {
152 
153 using namespace js::jit;
154 
155 typedef bool HandleNaNSpecially;
156 typedef bool InvertBranch;
157 typedef bool IsKnownNotZero;
158 typedef bool IsSigned;
159 typedef bool IsUnsigned;
160 typedef bool NeedsBoundsCheck;
161 typedef bool PopStack;
162 typedef bool WantResult;
163 typedef bool ZeroOnOverflow;
164 
165 typedef unsigned ByteSize;
166 typedef unsigned BitSize;
167 
168 // UseABI::Wasm implies that the Tls/Heap/Global registers are nonvolatile,
169 // except when InterModule::True is also set, when they are volatile.
170 //
171 // UseABI::System implies that the Tls/Heap/Global registers are volatile.
172 // Additionally, the parameter passing mechanism may be slightly different from
173 // the UseABI::Wasm convention.
174 //
175 // When the Tls/Heap/Global registers are not volatile, the baseline compiler
176 // will restore the Tls register from its save slot before the call, since the
177 // baseline compiler uses the Tls register for other things.
178 //
179 // When those registers are volatile, the baseline compiler will reload them
180 // after the call (it will restore the Tls register from the save slot and load
181 // the other two from the Tls data).
182 
183 enum class UseABI { Wasm, System };
184 enum class InterModule { False = false, True = true };
185 
186 #if defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_NONE)
187 #define RABALDR_SCRATCH_I32
188 #define RABALDR_SCRATCH_F32
189 #define RABALDR_SCRATCH_F64
190 
191 static const Register RabaldrScratchI32 = Register::Invalid();
192 static const FloatRegister RabaldrScratchF32 = InvalidFloatReg;
193 static const FloatRegister RabaldrScratchF64 = InvalidFloatReg;
194 #endif
195 
196 #ifdef JS_CODEGEN_X86
197 // The selection of EBX here steps gingerly around: the need for EDX
198 // to be allocatable for multiply/divide; ECX to be allocatable for
199 // shift/rotate; EAX (= ReturnReg) to be allocatable as the joinreg;
200 // EBX not being one of the WasmTableCall registers; and needing a
201 // temp register for load/store that has a single-byte persona.
202 //
203 // The compiler assumes that RabaldrScratchI32 has a single-byte
204 // persona.  Code for 8-byte atomic operations assumes that
205 // RabaldrScratchI32 is in fact ebx.
206 
207 #define RABALDR_SCRATCH_I32
208 static const Register RabaldrScratchI32 = ebx;
209 
210 #define RABALDR_INT_DIV_I64_CALLOUT
211 #endif
212 
213 #ifdef JS_CODEGEN_ARM
214 // We use our own scratch register, because the macro assembler uses
215 // the regular scratch register(s) pretty liberally.  We could
216 // work around that in several cases but the mess does not seem
217 // worth it yet.  CallTempReg2 seems safe.
218 
219 #define RABALDR_SCRATCH_I32
220 static const Register RabaldrScratchI32 = CallTempReg2;
221 
222 #define RABALDR_INT_DIV_I64_CALLOUT
223 #define RABALDR_I64_TO_FLOAT_CALLOUT
224 #define RABALDR_FLOAT_TO_I64_CALLOUT
225 #endif
226 
227 #ifdef JS_CODEGEN_MIPS32
228 #define RABALDR_SCRATCH_I32
229 static const Register RabaldrScratchI32 = CallTempReg2;
230 
231 #define RABALDR_INT_DIV_I64_CALLOUT
232 #define RABALDR_I64_TO_FLOAT_CALLOUT
233 #define RABALDR_FLOAT_TO_I64_CALLOUT
234 #endif
235 
236 #ifdef JS_CODEGEN_MIPS64
237 #define RABALDR_SCRATCH_I32
238 static const Register RabaldrScratchI32 = CallTempReg2;
239 #endif
240 
241 template <MIRType t>
242 struct RegTypeOf {
243   static_assert(t == MIRType::Float32 || t == MIRType::Double,
244                 "Float mask type");
245 };
246 
247 template <>
248 struct RegTypeOf<MIRType::Float32> {
249   static constexpr RegTypeName value = RegTypeName::Float32;
250 };
251 template <>
252 struct RegTypeOf<MIRType::Double> {
253   static constexpr RegTypeName value = RegTypeName::Float64;
254 };
255 
256 // The strongly typed register wrappers are especially useful to distinguish
257 // float registers from double registers, but they also clearly distinguish
258 // 32-bit registers from 64-bit register pairs on 32-bit systems.
259 
260 struct RegI32 : public Register {
RegI32js::wasm::RegI32261   RegI32() : Register(Register::Invalid()) {}
RegI32js::wasm::RegI32262   explicit RegI32(Register reg) : Register(reg) {}
isValidjs::wasm::RegI32263   bool isValid() const { return *this != Invalid(); }
isInvalidjs::wasm::RegI32264   bool isInvalid() const { return !isValid(); }
Invalidjs::wasm::RegI32265   static RegI32 Invalid() { return RegI32(Register::Invalid()); }
266 };
267 
268 struct RegI64 : public Register64 {
RegI64js::wasm::RegI64269   RegI64() : Register64(Register64::Invalid()) {}
RegI64js::wasm::RegI64270   explicit RegI64(Register64 reg) : Register64(reg) {}
isValidjs::wasm::RegI64271   bool isValid() const { return *this != Invalid(); }
isInvalidjs::wasm::RegI64272   bool isInvalid() const { return !isValid(); }
Invalidjs::wasm::RegI64273   static RegI64 Invalid() { return RegI64(Register64::Invalid()); }
274 };
275 
276 struct RegF32 : public FloatRegister {
RegF32js::wasm::RegF32277   RegF32() : FloatRegister() {}
RegF32js::wasm::RegF32278   explicit RegF32(FloatRegister reg) : FloatRegister(reg) {}
isValidjs::wasm::RegF32279   bool isValid() const { return *this != Invalid(); }
isInvalidjs::wasm::RegF32280   bool isInvalid() const { return !isValid(); }
Invalidjs::wasm::RegF32281   static RegF32 Invalid() { return RegF32(InvalidFloatReg); }
282 };
283 
284 struct RegF64 : public FloatRegister {
RegF64js::wasm::RegF64285   RegF64() : FloatRegister() {}
RegF64js::wasm::RegF64286   explicit RegF64(FloatRegister reg) : FloatRegister(reg) {}
isValidjs::wasm::RegF64287   bool isValid() const { return *this != Invalid(); }
isInvalidjs::wasm::RegF64288   bool isInvalid() const { return !isValid(); }
Invalidjs::wasm::RegF64289   static RegF64 Invalid() { return RegF64(InvalidFloatReg); }
290 };
291 
292 struct AnyReg {
AnyRegjs::wasm::AnyReg293   explicit AnyReg(RegI32 r) {
294     tag = I32;
295     i32_ = r;
296   }
AnyRegjs::wasm::AnyReg297   explicit AnyReg(RegI64 r) {
298     tag = I64;
299     i64_ = r;
300   }
AnyRegjs::wasm::AnyReg301   explicit AnyReg(RegF32 r) {
302     tag = F32;
303     f32_ = r;
304   }
AnyRegjs::wasm::AnyReg305   explicit AnyReg(RegF64 r) {
306     tag = F64;
307     f64_ = r;
308   }
309 
i32js::wasm::AnyReg310   RegI32 i32() const {
311     MOZ_ASSERT(tag == I32);
312     return i32_;
313   }
i64js::wasm::AnyReg314   RegI64 i64() const {
315     MOZ_ASSERT(tag == I64);
316     return i64_;
317   }
f32js::wasm::AnyReg318   RegF32 f32() const {
319     MOZ_ASSERT(tag == F32);
320     return f32_;
321   }
f64js::wasm::AnyReg322   RegF64 f64() const {
323     MOZ_ASSERT(tag == F64);
324     return f64_;
325   }
anyjs::wasm::AnyReg326   AnyRegister any() const {
327     switch (tag) {
328       case F32:
329         return AnyRegister(f32_);
330       case F64:
331         return AnyRegister(f64_);
332       case I32:
333         return AnyRegister(i32_);
334       case I64:
335 #ifdef JS_PUNBOX64
336         return AnyRegister(i64_.reg);
337 #else
338         // The compiler is written so that this is never needed: any() is
339         // called on arbitrary registers for asm.js but asm.js does not have
340         // 64-bit ints.  For wasm, any() is called on arbitrary registers
341         // only on 64-bit platforms.
342         MOZ_CRASH("AnyReg::any() on 32-bit platform");
343 #endif
344       default:
345         MOZ_CRASH();
346     }
347     // Work around GCC 5 analysis/warning bug.
348     MOZ_CRASH("AnyReg::any(): impossible case");
349   }
350 
351   union {
352     RegI32 i32_;
353     RegI64 i64_;
354     RegF32 f32_;
355     RegF64 f64_;
356   };
357   enum { I32, I64, F32, F64 } tag;
358 };
359 
360 // Platform-specific registers.
361 //
362 // All platforms must define struct SpecificRegs.  All 32-bit platforms must
363 // have an abiReturnRegI64 member in that struct.
364 
365 #if defined(JS_CODEGEN_X64)
366 struct SpecificRegs {
367   RegI32 eax, ecx, edx, edi, esi;
368   RegI64 rax, rcx, rdx;
369 
SpecificRegsjs::wasm::SpecificRegs370   SpecificRegs()
371       : eax(RegI32(js::jit::eax)),
372         ecx(RegI32(js::jit::ecx)),
373         edx(RegI32(js::jit::edx)),
374         edi(RegI32(js::jit::edi)),
375         esi(RegI32(js::jit::esi)),
376         rax(RegI64(Register64(js::jit::rax))),
377         rcx(RegI64(Register64(js::jit::rcx))),
378         rdx(RegI64(Register64(js::jit::rdx))) {}
379 };
380 #elif defined(JS_CODEGEN_X86)
381 struct SpecificRegs {
382   RegI32 eax, ecx, edx, edi, esi;
383   RegI64 ecx_ebx, edx_eax, abiReturnRegI64;
384 
SpecificRegsjs::wasm::SpecificRegs385   SpecificRegs()
386       : eax(RegI32(js::jit::eax)),
387         ecx(RegI32(js::jit::ecx)),
388         edx(RegI32(js::jit::edx)),
389         edi(RegI32(js::jit::edi)),
390         esi(RegI32(js::jit::esi)),
391         ecx_ebx(RegI64(Register64(js::jit::ecx, js::jit::ebx))),
392         edx_eax(RegI64(Register64(js::jit::edx, js::jit::eax))),
393         abiReturnRegI64(edx_eax) {}
394 };
395 #elif defined(JS_CODEGEN_ARM)
396 struct SpecificRegs {
397   RegI64 abiReturnRegI64;
398 
SpecificRegsjs::wasm::SpecificRegs399   SpecificRegs() : abiReturnRegI64(ReturnReg64) {}
400 };
401 #elif defined(JS_CODEGEN_MIPS32)
402 struct SpecificRegs {
403   RegI64 abiReturnRegI64;
404 
SpecificRegsjs::wasm::SpecificRegs405   SpecificRegs() : abiReturnRegI64(ReturnReg64) {}
406 };
407 #elif defined(JS_CODEGEN_MIPS64)
408 struct SpecificRegs {};
409 #else
410 struct SpecificRegs {
411 #ifndef JS_64BIT
412   RegI64 abiReturnRegI64;
413 #endif
414 
SpecificRegsjs::wasm::SpecificRegs415   SpecificRegs() { MOZ_CRASH("BaseCompiler porting interface: SpecificRegs"); }
416 };
417 #endif
418 
419 class BaseCompilerInterface {
420  public:
421   // Spill all spillable registers.
422   //
423   // TODO / OPTIMIZE (Bug 1316802): It's possible to do better here by
424   // spilling only enough registers to satisfy current needs.
425   virtual void sync() = 0;
426 };
427 
428 // Register allocator.
429 
430 class BaseRegAlloc {
431   // Notes on float register allocation.
432   //
433   // The general rule in SpiderMonkey is that float registers can alias double
434   // registers, but there are predicates to handle exceptions to that rule:
435   // hasUnaliasedDouble() and hasMultiAlias().  The way aliasing actually
436   // works is platform dependent and exposed through the aliased(n, &r)
437   // predicate, etc.
438   //
439   //  - hasUnaliasedDouble(): on ARM VFPv3-D32 there are double registers that
440   //    cannot be treated as float.
441   //  - hasMultiAlias(): on ARM and MIPS a double register aliases two float
442   //    registers.
443   //
444   // On some platforms (x86, x64, ARM64) but not all (ARM)
445   // ScratchFloat32Register is the same as ScratchDoubleRegister.
446   //
447   // It's a basic invariant of the AllocatableRegisterSet that it deals
448   // properly with aliasing of registers: if s0 or s1 are allocated then d0 is
449   // not allocatable; if s0 and s1 are freed individually then d0 becomes
450   // allocatable.
451 
452   BaseCompilerInterface& bc;
453   AllocatableGeneralRegisterSet availGPR;
454   AllocatableFloatRegisterSet availFPU;
455 #ifdef DEBUG
456   AllocatableGeneralRegisterSet
457       allGPR;  // The registers available to the compiler
458   AllocatableFloatRegisterSet
459       allFPU;  //   after removing ScratchReg, HeapReg, etc
460   uint32_t scratchTaken;
461 #endif
462 #ifdef JS_CODEGEN_X86
463   AllocatableGeneralRegisterSet singleByteRegs;
464 #endif
465 
hasGPR()466   bool hasGPR() { return !availGPR.empty(); }
467 
hasGPR64()468   bool hasGPR64() {
469 #ifdef JS_PUNBOX64
470     return !availGPR.empty();
471 #else
472     if (availGPR.empty()) return false;
473     Register r = allocGPR();
474     bool available = !availGPR.empty();
475     freeGPR(r);
476     return available;
477 #endif
478   }
479 
480   template <MIRType t>
hasFPU()481   bool hasFPU() {
482     return availFPU.hasAny<RegTypeOf<t>::value>();
483   }
484 
isAvailableGPR(Register r)485   bool isAvailableGPR(Register r) { return availGPR.has(r); }
486 
isAvailableFPU(FloatRegister r)487   bool isAvailableFPU(FloatRegister r) { return availFPU.has(r); }
488 
allocGPR(Register r)489   void allocGPR(Register r) {
490     MOZ_ASSERT(isAvailableGPR(r));
491     availGPR.take(r);
492   }
493 
allocGPR()494   Register allocGPR() {
495     MOZ_ASSERT(hasGPR());
496     return availGPR.takeAny();
497   }
498 
allocInt64(Register64 r)499   void allocInt64(Register64 r) {
500 #ifdef JS_PUNBOX64
501     allocGPR(r.reg);
502 #else
503     allocGPR(r.low);
504     allocGPR(r.high);
505 #endif
506   }
507 
allocInt64()508   Register64 allocInt64() {
509     MOZ_ASSERT(hasGPR64());
510 #ifdef JS_PUNBOX64
511     return Register64(availGPR.takeAny());
512 #else
513     Register high = availGPR.takeAny();
514     Register low = availGPR.takeAny();
515     return Register64(high, low);
516 #endif
517   }
518 
519 #ifdef JS_CODEGEN_ARM
520   // r12 is normally the ScratchRegister and r13 is always the stack pointer,
521   // so the highest possible pair has r10 as the even-numbered register.
522 
523   static const uint32_t pairLimit = 10;
524 
hasGPRPair()525   bool hasGPRPair() {
526     for (uint32_t i = 0; i <= pairLimit; i += 2) {
527       if (isAvailableGPR(Register::FromCode(i)) &&
528           isAvailableGPR(Register::FromCode(i + 1)))
529         return true;
530     }
531     return false;
532   }
533 
allocGPRPair(Register * low,Register * high)534   void allocGPRPair(Register* low, Register* high) {
535     MOZ_ASSERT(hasGPRPair());
536     for (uint32_t i = 0; i <= pairLimit; i += 2) {
537       if (isAvailableGPR(Register::FromCode(i)) &&
538           isAvailableGPR(Register::FromCode(i + 1))) {
539         *low = Register::FromCode(i);
540         *high = Register::FromCode(i + 1);
541         allocGPR(*low);
542         allocGPR(*high);
543         return;
544       }
545     }
546     MOZ_CRASH("No pair");
547   }
548 #endif
549 
allocFPU(FloatRegister r)550   void allocFPU(FloatRegister r) {
551     MOZ_ASSERT(isAvailableFPU(r));
552     availFPU.take(r);
553   }
554 
555   template <MIRType t>
allocFPU()556   FloatRegister allocFPU() {
557     return availFPU.takeAny<RegTypeOf<t>::value>();
558   }
559 
freeGPR(Register r)560   void freeGPR(Register r) { availGPR.add(r); }
561 
freeInt64(Register64 r)562   void freeInt64(Register64 r) {
563 #ifdef JS_PUNBOX64
564     freeGPR(r.reg);
565 #else
566     freeGPR(r.low);
567     freeGPR(r.high);
568 #endif
569   }
570 
freeFPU(FloatRegister r)571   void freeFPU(FloatRegister r) { availFPU.add(r); }
572 
573  public:
BaseRegAlloc(BaseCompilerInterface & bc)574   explicit BaseRegAlloc(BaseCompilerInterface& bc)
575       : bc(bc),
576         availGPR(GeneralRegisterSet::All()),
577         availFPU(FloatRegisterSet::All())
578 #ifdef DEBUG
579         ,
580         scratchTaken(0)
581 #endif
582 #ifdef JS_CODEGEN_X86
583         ,
584         singleByteRegs(GeneralRegisterSet(Registers::SingleByteRegs))
585 #endif
586   {
587     RegisterAllocator::takeWasmRegisters(availGPR);
588 
589     // Allocate any private scratch registers.  For now we assume none of
590     // these registers alias.
591 #if defined(RABALDR_SCRATCH_I32)
592     if (RabaldrScratchI32 != RegI32::Invalid())
593       availGPR.take(RabaldrScratchI32);
594 #endif
595 #if defined(RABALDR_SCRATCH_F32)
596     if (RabaldrScratchF32 != RegF32::Invalid())
597       availFPU.take(RabaldrScratchF32);
598 #endif
599 #if defined(RABALDR_SCRATCH_F64)
600     if (RabaldrScratchF64 != RegF64::Invalid())
601       availFPU.take(RabaldrScratchF64);
602 #endif
603 
604 #ifdef DEBUG
605     allGPR = availGPR;
606     allFPU = availFPU;
607 #endif
608   }
609 
610   enum class ScratchKind { I32 = 1, F32 = 2, F64 = 4 };
611 
612 #ifdef DEBUG
isScratchRegisterTaken(ScratchKind s) const613   bool isScratchRegisterTaken(ScratchKind s) const {
614     return (scratchTaken & uint32_t(s)) != 0;
615   }
616 
setScratchRegisterTaken(ScratchKind s,bool state)617   void setScratchRegisterTaken(ScratchKind s, bool state) {
618     if (state)
619       scratchTaken |= uint32_t(s);
620     else
621       scratchTaken &= ~uint32_t(s);
622   }
623 #endif
624 
625 #ifdef JS_CODEGEN_X86
isSingleByteI32(Register r)626   bool isSingleByteI32(Register r) { return singleByteRegs.has(r); }
627 #endif
628 
isAvailableI32(RegI32 r)629   bool isAvailableI32(RegI32 r) { return isAvailableGPR(r); }
630 
isAvailableI64(RegI64 r)631   bool isAvailableI64(RegI64 r) {
632 #ifdef JS_PUNBOX64
633     return isAvailableGPR(r.reg);
634 #else
635     return isAvailableGPR(r.low) && isAvailableGPR(r.high);
636 #endif
637   }
638 
isAvailableF32(RegF32 r)639   bool isAvailableF32(RegF32 r) { return isAvailableFPU(r); }
640 
isAvailableF64(RegF64 r)641   bool isAvailableF64(RegF64 r) { return isAvailableFPU(r); }
642 
643   // TODO / OPTIMIZE (Bug 1316802): Do not sync everything on allocation
644   // failure, only as much as we need.
645 
needI32()646   MOZ_MUST_USE RegI32 needI32() {
647     if (!hasGPR()) bc.sync();
648     return RegI32(allocGPR());
649   }
650 
needI32(RegI32 specific)651   void needI32(RegI32 specific) {
652     if (!isAvailableI32(specific)) bc.sync();
653     allocGPR(specific);
654   }
655 
needI64()656   MOZ_MUST_USE RegI64 needI64() {
657     if (!hasGPR64()) bc.sync();
658     return RegI64(allocInt64());
659   }
660 
needI64(RegI64 specific)661   void needI64(RegI64 specific) {
662     if (!isAvailableI64(specific)) bc.sync();
663     allocInt64(specific);
664   }
665 
needF32()666   MOZ_MUST_USE RegF32 needF32() {
667     if (!hasFPU<MIRType::Float32>()) bc.sync();
668     return RegF32(allocFPU<MIRType::Float32>());
669   }
670 
needF32(RegF32 specific)671   void needF32(RegF32 specific) {
672     if (!isAvailableF32(specific)) bc.sync();
673     allocFPU(specific);
674   }
675 
needF64()676   MOZ_MUST_USE RegF64 needF64() {
677     if (!hasFPU<MIRType::Double>()) bc.sync();
678     return RegF64(allocFPU<MIRType::Double>());
679   }
680 
needF64(RegF64 specific)681   void needF64(RegF64 specific) {
682     if (!isAvailableF64(specific)) bc.sync();
683     allocFPU(specific);
684   }
685 
freeI32(RegI32 r)686   void freeI32(RegI32 r) { freeGPR(r); }
687 
freeI64(RegI64 r)688   void freeI64(RegI64 r) { freeInt64(r); }
689 
freeF64(RegF64 r)690   void freeF64(RegF64 r) { freeFPU(r); }
691 
freeF32(RegF32 r)692   void freeF32(RegF32 r) { freeFPU(r); }
693 
694 #ifdef JS_CODEGEN_ARM
needI64Pair()695   MOZ_MUST_USE RegI64 needI64Pair() {
696     if (!hasGPRPair()) bc.sync();
697     Register low, high;
698     allocGPRPair(&low, &high);
699     return RegI64(Register64(high, low));
700   }
701 #endif
702 
703 #ifdef DEBUG
704   friend class LeakCheck;
705 
706   class MOZ_RAII LeakCheck {
707    private:
708     const BaseRegAlloc& ra;
709     AllocatableGeneralRegisterSet knownGPR;
710     AllocatableFloatRegisterSet knownFPU;
711 
712    public:
LeakCheck(const BaseRegAlloc & ra)713     explicit LeakCheck(const BaseRegAlloc& ra) : ra(ra) {
714       knownGPR = ra.availGPR;
715       knownFPU = ra.availFPU;
716     }
717 
~LeakCheck()718     ~LeakCheck() {
719       MOZ_ASSERT(knownGPR.bits() == ra.allGPR.bits());
720       MOZ_ASSERT(knownFPU.bits() == ra.allFPU.bits());
721     }
722 
addKnownI32(RegI32 r)723     void addKnownI32(RegI32 r) { knownGPR.add(r); }
724 
addKnownI64(RegI64 r)725     void addKnownI64(RegI64 r) {
726 #ifdef JS_PUNBOX64
727       knownGPR.add(r.reg);
728 #else
729       knownGPR.add(r.high);
730       knownGPR.add(r.low);
731 #endif
732     }
733 
addKnownF32(RegF32 r)734     void addKnownF32(RegF32 r) { knownFPU.add(r); }
735 
addKnownF64(RegF64 r)736     void addKnownF64(RegF64 r) { knownFPU.add(r); }
737   };
738 #endif
739 };
740 
741 // Scratch register abstractions.
742 //
743 // We define our own scratch registers when the platform doesn't provide what we
744 // need.  A notable use case is that we will need a private scratch register
745 // when the platform masm uses its scratch register very frequently (eg, ARM).
746 
747 class BaseScratchRegister {
748 #ifdef DEBUG
749   BaseRegAlloc& ra;
750   BaseRegAlloc::ScratchKind s;
751 
752  public:
BaseScratchRegister(BaseRegAlloc & ra,BaseRegAlloc::ScratchKind s)753   explicit BaseScratchRegister(BaseRegAlloc& ra, BaseRegAlloc::ScratchKind s)
754       : ra(ra), s(s) {
755     MOZ_ASSERT(!ra.isScratchRegisterTaken(s));
756     ra.setScratchRegisterTaken(s, true);
757   }
~BaseScratchRegister()758   ~BaseScratchRegister() {
759     MOZ_ASSERT(ra.isScratchRegisterTaken(s));
760     ra.setScratchRegisterTaken(s, false);
761   }
762 #else
763  public:
764   explicit BaseScratchRegister(BaseRegAlloc& ra, BaseRegAlloc::ScratchKind s) {}
765 #endif
766 };
767 
768 #ifdef RABALDR_SCRATCH_F64
769 class ScratchF64 : public BaseScratchRegister {
770  public:
ScratchF64(BaseRegAlloc & ra)771   explicit ScratchF64(BaseRegAlloc& ra)
772       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F64) {}
operator RegF64() const773   operator RegF64() const { return RegF64(RabaldrScratchF64); }
774 };
775 #else
776 class ScratchF64 : public ScratchDoubleScope {
777  public:
ScratchF64(MacroAssembler & m)778   explicit ScratchF64(MacroAssembler& m) : ScratchDoubleScope(m) {}
operator RegF64() const779   operator RegF64() const { return RegF64(FloatRegister(*this)); }
780 };
781 #endif
782 
783 #ifdef RABALDR_SCRATCH_F32
784 class ScratchF32 : public BaseScratchRegister {
785  public:
ScratchF32(BaseRegAlloc & ra)786   explicit ScratchF32(BaseRegAlloc& ra)
787       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F32) {}
operator RegF32() const788   operator RegF32() const { return RegF32(RabaldrScratchF32); }
789 };
790 #else
791 class ScratchF32 : public ScratchFloat32Scope {
792  public:
ScratchF32(MacroAssembler & m)793   explicit ScratchF32(MacroAssembler& m) : ScratchFloat32Scope(m) {}
operator RegF32() const794   operator RegF32() const { return RegF32(FloatRegister(*this)); }
795 };
796 #endif
797 
798 #ifdef RABALDR_SCRATCH_I32
799 class ScratchI32 : public BaseScratchRegister {
800  public:
ScratchI32(BaseRegAlloc & ra)801   explicit ScratchI32(BaseRegAlloc& ra)
802       : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::I32) {}
operator RegI32() const803   operator RegI32() const { return RegI32(RabaldrScratchI32); }
804 };
805 #else
806 class ScratchI32 : public ScratchRegisterScope {
807  public:
ScratchI32(MacroAssembler & m)808   explicit ScratchI32(MacroAssembler& m) : ScratchRegisterScope(m) {}
operator RegI32() const809   operator RegI32() const { return RegI32(Register(*this)); }
810 };
811 #endif
812 
813 #if defined(JS_CODEGEN_X86)
814 // ScratchEBX is a mnemonic device: For some atomic ops we really need EBX,
815 // no other register will do.  And we would normally have to allocate that
816 // register using ScratchI32 since normally the scratch register is EBX.
817 // But the whole point of ScratchI32 is to hide that relationship.  By using
818 // the ScratchEBX alias, we document that at that point we require the
819 // scratch register to be EBX.
820 using ScratchEBX = ScratchI32;
821 
822 // ScratchI8 is a mnemonic device: For some ops we need a register with a
823 // byte subregister.
824 using ScratchI8 = ScratchI32;
825 #endif
826 
827 // The stack frame.
828 //
829 // The frame has four parts ("below" means at lower addresses):
830 //
831 //  - the Header, comprising the Frame and DebugFrame elements;
832 //  - the Local area, allocated below the header with various forms of
833 //    alignment;
834 //  - the Stack area, comprising the temporary storage the compiler uses
835 //    for register spilling, allocated below the Local area;
836 //  - the Arguments area, comprising memory allocated for outgoing calls,
837 //    allocated below the stack area.
838 //
839 // The header is addressed off the stack pointer.  masm.framePushed() is always
840 // correct, and masm.getStackPointer() + masm.framePushed() always addresses the
841 // Frame, with the DebugFrame optionally below it.
842 //
843 // The local area is laid out by BaseLocalIter and is allocated and deallocated
844 // by standard prologue and epilogue functions that manipulate the stack
845 // pointer, but it is accessed via BaseStackFrame.
846 //
847 // The stack area is maintained by and accessed via BaseStackFrame.  On some
848 // systems, the stack memory may be allocated in chunks because the SP needs a
849 // specific alignment.
850 //
851 // The arguments area is allocated and deallocated via BaseStackFrame (see
852 // comments later) but is accessed directly off the stack pointer.
853 
854 // BaseLocalIter iterates over a vector of types of locals and provides offsets
855 // from the Frame address for those locals, and associated data.
856 //
857 // The implementation of BaseLocalIter is the property of the BaseStackFrame.
858 // But it is also exposed for eg the debugger to use.
859 
BaseLocalIter(const ValTypeVector & locals,size_t argsLength,bool debugEnabled)860 BaseLocalIter::BaseLocalIter(const ValTypeVector& locals, size_t argsLength,
861                              bool debugEnabled)
862     : locals_(locals),
863       argsLength_(argsLength),
864       argsRange_(locals.begin(), argsLength),
865       argsIter_(argsRange_),
866       index_(0),
867       localSize_(debugEnabled ? DebugFrame::offsetOfFrame() : 0),
868       reservedSize_(localSize_),
869       done_(false) {
870   MOZ_ASSERT(argsLength <= locals.length());
871 
872   settle();
873 }
874 
pushLocal(size_t nbytes)875 int32_t BaseLocalIter::pushLocal(size_t nbytes) {
876   MOZ_ASSERT(nbytes % 4 == 0 && nbytes <= 16);
877   localSize_ = AlignBytes(localSize_, nbytes) + nbytes;
878   return localSize_;  // Locals grow down so capture base address
879 }
880 
settle()881 void BaseLocalIter::settle() {
882   if (index_ < argsLength_) {
883     MOZ_ASSERT(!argsIter_.done());
884     mirType_ = argsIter_.mirType();
885     switch (mirType_) {
886       case MIRType::Int32:
887       case MIRType::Int64:
888       case MIRType::Double:
889       case MIRType::Float32:
890         if (argsIter_->argInRegister())
891           frameOffset_ = pushLocal(MIRTypeToSize(mirType_));
892         else
893           frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
894         break;
895       default:
896         MOZ_CRASH("Argument type");
897     }
898     return;
899   }
900 
901   MOZ_ASSERT(argsIter_.done());
902   if (index_ < locals_.length()) {
903     switch (locals_[index_]) {
904       case ValType::I32:
905       case ValType::I64:
906       case ValType::F32:
907       case ValType::F64:
908         mirType_ = ToMIRType(locals_[index_]);
909         frameOffset_ = pushLocal(MIRTypeToSize(mirType_));
910         break;
911       default:
912         MOZ_CRASH("Compiler bug: Unexpected local type");
913     }
914     return;
915   }
916 
917   done_ = true;
918 }
919 
operator ++(int)920 void BaseLocalIter::operator++(int) {
921   MOZ_ASSERT(!done_);
922   index_++;
923   if (!argsIter_.done()) argsIter_++;
924   settle();
925 }
926 
927 // Abstraction of the baseline compiler's stack frame (except for the Frame /
928 // DebugFrame parts).  See comments above for more.
929 
930 class BaseStackFrame {
931   MacroAssembler& masm;
932 
933   // Size of local area in bytes (stable after beginFunction).
934   uint32_t localSize_;
935 
936   // Low byte offset of local area for true locals (not parameters).
937   uint32_t varLow_;
938 
939   // High byte offset + 1 of local area for true locals.
940   uint32_t varHigh_;
941 
942   // The largest stack height, not necessarily zero-based.  Read this for its
943   // true value only when code generation is finished.
944   uint32_t maxStackHeight_;
945 
946   // Patch point where we check for stack overflow.
947   CodeOffset stackAddOffset_;
948 
949   // The stack pointer, cached for brevity.
950   RegisterOrSP sp_;
951 
952  public:
BaseStackFrame(MacroAssembler & masm)953   explicit BaseStackFrame(MacroAssembler& masm)
954       : masm(masm),
955         localSize_(UINT32_MAX),
956         varLow_(UINT32_MAX),
957         varHigh_(UINT32_MAX),
958         maxStackHeight_(0),
959         stackAddOffset_(0),
960         sp_(masm.getStackPointer()) {}
961 
962   //////////////////////////////////////////////////////////////////////
963   //
964   // The local area.
965 
966   // Locals - the static part of the frame.
967 
968   struct Local {
969     // Type of the value.
970     const MIRType type;
971 
972     // Byte offset from Frame "into" the locals, ie positive for true locals
973     // and negative for incoming args that read directly from the arg area.
974     // It assumes the stack is growing down and that locals are on the stack
975     // at lower addresses than Frame, and is the offset from Frame of the
976     // lowest-addressed byte of the local.
977     const int32_t offs;
978 
Localjs::wasm::BaseStackFrame::Local979     Local(MIRType type, int32_t offs) : type(type), offs(offs) {}
980   };
981 
982   typedef Vector<Local, 8, SystemAllocPolicy> LocalVector;
983 
984  private:
985   // Offset off of sp_ for `local`.
localOffset(const Local & local)986   int32_t localOffset(const Local& local) { return localOffset(local.offs); }
987 
988   // Offset off of sp_ for a local with offset `offset` from Frame.
localOffset(int32_t offset)989   int32_t localOffset(int32_t offset) { return masm.framePushed() - offset; }
990 
991  public:
endFunctionPrologue()992   void endFunctionPrologue() {
993     MOZ_ASSERT(masm.framePushed() == localSize_);
994     MOZ_ASSERT(localSize_ != UINT32_MAX);
995     MOZ_ASSERT(localSize_ % WasmStackAlignment == 0);
996     maxStackHeight_ = localSize_;
997   }
998 
999   // Initialize `localInfo` based on the types of `locals` and `args`.
setupLocals(const ValTypeVector & locals,const ValTypeVector & args,bool debugEnabled,LocalVector * localInfo)1000   bool setupLocals(const ValTypeVector& locals, const ValTypeVector& args,
1001                    bool debugEnabled, LocalVector* localInfo) {
1002     MOZ_ASSERT(maxStackHeight_ != UINT32_MAX);
1003 
1004     if (!localInfo->reserve(locals.length())) return false;
1005 
1006     DebugOnly<uint32_t> index = 0;
1007     BaseLocalIter i(locals, args.length(), debugEnabled);
1008     varLow_ = i.reservedSize();
1009     for (; !i.done() && i.index() < args.length(); i++) {
1010       MOZ_ASSERT(i.isArg());
1011       MOZ_ASSERT(i.index() == index);
1012       localInfo->infallibleEmplaceBack(i.mirType(), i.frameOffset());
1013       varLow_ = i.currentLocalSize();
1014       index++;
1015     }
1016 
1017     varHigh_ = varLow_;
1018     for (; !i.done(); i++) {
1019       MOZ_ASSERT(!i.isArg());
1020       MOZ_ASSERT(i.index() == index);
1021       localInfo->infallibleEmplaceBack(i.mirType(), i.frameOffset());
1022       varHigh_ = i.currentLocalSize();
1023       index++;
1024     }
1025 
1026     localSize_ = AlignBytes(varHigh_, WasmStackAlignment);
1027 
1028     return true;
1029   }
1030 
1031   // The fixed amount of memory, in bytes, allocated on the stack below the
1032   // Frame for purposes such as locals and other fixed values.  Includes all
1033   // necessary alignment.
1034 
initialSize() const1035   uint32_t initialSize() const {
1036     MOZ_ASSERT(localSize_ != UINT32_MAX);
1037 
1038     return localSize_;
1039   }
1040 
1041   void zeroLocals(BaseRegAlloc& ra);
1042 
loadLocalI32(const Local & src,RegI32 dest)1043   void loadLocalI32(const Local& src, RegI32 dest) {
1044     masm.load32(Address(sp_, localOffset(src)), dest);
1045   }
1046 
1047 #ifndef JS_PUNBOX64
loadLocalI64Low(const Local & src,RegI32 dest)1048   void loadLocalI64Low(const Local& src, RegI32 dest) {
1049     masm.load32(Address(sp_, localOffset(src) + INT64LOW_OFFSET), dest);
1050   }
1051 
loadLocalI64High(const Local & src,RegI32 dest)1052   void loadLocalI64High(const Local& src, RegI32 dest) {
1053     masm.load32(Address(sp_, localOffset(src) + INT64HIGH_OFFSET), dest);
1054   }
1055 #endif
1056 
loadLocalI64(const Local & src,RegI64 dest)1057   void loadLocalI64(const Local& src, RegI64 dest) {
1058     masm.load64(Address(sp_, localOffset(src)), dest);
1059   }
1060 
loadLocalPtr(const Local & src,Register dest)1061   void loadLocalPtr(const Local& src, Register dest) {
1062     masm.loadPtr(Address(sp_, localOffset(src)), dest);
1063   }
1064 
loadLocalF64(const Local & src,RegF64 dest)1065   void loadLocalF64(const Local& src, RegF64 dest) {
1066     masm.loadDouble(Address(sp_, localOffset(src)), dest);
1067   }
1068 
loadLocalF32(const Local & src,RegF32 dest)1069   void loadLocalF32(const Local& src, RegF32 dest) {
1070     masm.loadFloat32(Address(sp_, localOffset(src)), dest);
1071   }
1072 
storeLocalI32(RegI32 src,const Local & dest)1073   void storeLocalI32(RegI32 src, const Local& dest) {
1074     masm.store32(src, Address(sp_, localOffset(dest)));
1075   }
1076 
storeLocalI64(RegI64 src,const Local & dest)1077   void storeLocalI64(RegI64 src, const Local& dest) {
1078     masm.store64(src, Address(sp_, localOffset(dest)));
1079   }
1080 
storeLocalPtr(Register src,const Local & dest)1081   void storeLocalPtr(Register src, const Local& dest) {
1082     masm.storePtr(src, Address(sp_, localOffset(dest)));
1083   }
1084 
storeLocalF64(RegF64 src,const Local & dest)1085   void storeLocalF64(RegF64 src, const Local& dest) {
1086     masm.storeDouble(src, Address(sp_, localOffset(dest)));
1087   }
1088 
storeLocalF32(RegF32 src,const Local & dest)1089   void storeLocalF32(RegF32 src, const Local& dest) {
1090     masm.storeFloat32(src, Address(sp_, localOffset(dest)));
1091   }
1092 
1093   //////////////////////////////////////////////////////////////////////
1094   //
1095   // The stack area - the dynamic part of the frame.
1096 
1097  private:
1098   // Offset off of sp_ for the slot at stack area location `offset`
stackOffset(int32_t offset)1099   int32_t stackOffset(int32_t offset) { return masm.framePushed() - offset; }
1100 
1101  public:
1102   // Sizes of items in the stack area.
1103   //
1104   // The size values come from the implementations of Push() in
1105   // MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp, and from
1106   // VFPRegister::size() in Architecture-arm.h.
1107   //
1108   // On ARM unlike on x86 we push a single for float.
1109 
1110   static const size_t StackSizeOfPtr = sizeof(intptr_t);
1111   static const size_t StackSizeOfInt64 = sizeof(int64_t);
1112 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
1113   static const size_t StackSizeOfFloat = sizeof(float);
1114 #else
1115   static const size_t StackSizeOfFloat = sizeof(double);
1116 #endif
1117   static const size_t StackSizeOfDouble = sizeof(double);
1118 
1119   // We won't know until after we've generated code how big the frame will be
1120   // (we may need arbitrary spill slots and outgoing param slots) so emit a
1121   // patchable add that is patched in endFunction().
1122   //
1123   // Note the platform scratch register may be used by branchPtr(), so
1124   // generally tmp must be something else.
1125 
allocStack(Register tmp,BytecodeOffset trapOffset)1126   void allocStack(Register tmp, BytecodeOffset trapOffset) {
1127     stackAddOffset_ = masm.sub32FromStackPtrWithPatch(tmp);
1128     Label ok;
1129     masm.branchPtr(Assembler::Below,
1130                    Address(WasmTlsReg, offsetof(wasm::TlsData, stackLimit)),
1131                    tmp, &ok);
1132     masm.wasmTrap(Trap::StackOverflow, trapOffset);
1133     masm.bind(&ok);
1134   }
1135 
patchAllocStack()1136   void patchAllocStack() {
1137     masm.patchSub32FromStackPtr(stackAddOffset_,
1138                                 Imm32(int32_t(maxStackHeight_ - localSize_)));
1139   }
1140 
1141   // Very large frames are implausible, probably an attack.
checkStackHeight()1142   bool checkStackHeight() {
1143     // 512KiB should be enough, considering how Rabaldr uses the stack and
1144     // what the standard limits are:
1145     //
1146     // - 1,000 parameters
1147     // - 50,000 locals
1148     // - 10,000 values on the eval stack (not an official limit)
1149     //
1150     // At sizeof(int64) bytes per slot this works out to about 480KiB.
1151     return maxStackHeight_ <= 512 * 1024;
1152   }
1153 
1154   // The current height of the stack area, not necessarily zero-based.
stackHeight() const1155   uint32_t stackHeight() const { return masm.framePushed(); }
1156 
1157   // Set the frame height.  This must only be called with a value returned
1158   // from stackHeight().
setStackHeight(uint32_t amount)1159   void setStackHeight(uint32_t amount) { masm.setFramePushed(amount); }
1160 
pushPtr(Register r)1161   uint32_t pushPtr(Register r) {
1162     DebugOnly<uint32_t> stackBefore = stackHeight();
1163     masm.Push(r);
1164     maxStackHeight_ = Max(maxStackHeight_, stackHeight());
1165     MOZ_ASSERT(stackBefore + StackSizeOfPtr == stackHeight());
1166     return stackHeight();
1167   }
1168 
pushFloat32(FloatRegister r)1169   uint32_t pushFloat32(FloatRegister r) {
1170     DebugOnly<uint32_t> stackBefore = stackHeight();
1171     masm.Push(r);
1172     maxStackHeight_ = Max(maxStackHeight_, stackHeight());
1173     MOZ_ASSERT(stackBefore + StackSizeOfFloat == stackHeight());
1174     return stackHeight();
1175   }
1176 
pushDouble(FloatRegister r)1177   uint32_t pushDouble(FloatRegister r) {
1178     DebugOnly<uint32_t> stackBefore = stackHeight();
1179     masm.Push(r);
1180     maxStackHeight_ = Max(maxStackHeight_, stackHeight());
1181     MOZ_ASSERT(stackBefore + StackSizeOfDouble == stackHeight());
1182     return stackHeight();
1183   }
1184 
popPtr(Register r)1185   void popPtr(Register r) {
1186     DebugOnly<uint32_t> stackBefore = stackHeight();
1187     masm.Pop(r);
1188     MOZ_ASSERT(stackBefore - StackSizeOfPtr == stackHeight());
1189   }
1190 
popFloat32(FloatRegister r)1191   void popFloat32(FloatRegister r) {
1192     DebugOnly<uint32_t> stackBefore = stackHeight();
1193     masm.Pop(r);
1194     MOZ_ASSERT(stackBefore - StackSizeOfFloat == stackHeight());
1195   }
1196 
popDouble(FloatRegister r)1197   void popDouble(FloatRegister r) {
1198     DebugOnly<uint32_t> stackBefore = stackHeight();
1199     masm.Pop(r);
1200     MOZ_ASSERT(stackBefore - StackSizeOfDouble == stackHeight());
1201   }
1202 
popBytes(size_t bytes)1203   void popBytes(size_t bytes) {
1204     if (bytes > 0) masm.freeStack(bytes);
1205   }
1206 
1207   // Before branching to an outer control label, pop the execution stack to
1208   // the level expected by that region, but do not update masm.framePushed()
1209   // as that will happen as compilation leaves the block.
1210 
popStackBeforeBranch(uint32_t destStackHeight)1211   void popStackBeforeBranch(uint32_t destStackHeight) {
1212     uint32_t stackHere = stackHeight();
1213     if (stackHere > destStackHeight)
1214       masm.addToStackPtr(Imm32(stackHere - destStackHeight));
1215   }
1216 
willPopStackBeforeBranch(uint32_t destStackHeight)1217   bool willPopStackBeforeBranch(uint32_t destStackHeight) {
1218     uint32_t stackHere = stackHeight();
1219     return stackHere > destStackHeight;
1220   }
1221 
1222   // Before exiting a nested control region, pop the execution stack
1223   // to the level expected by the nesting region, and free the
1224   // stack.
1225 
popStackOnBlockExit(uint32_t destStackHeight,bool deadCode)1226   void popStackOnBlockExit(uint32_t destStackHeight, bool deadCode) {
1227     uint32_t stackHere = stackHeight();
1228     if (stackHere > destStackHeight) {
1229       if (deadCode)
1230         masm.setFramePushed(destStackHeight);
1231       else
1232         masm.freeStack(stackHere - destStackHeight);
1233     }
1234   }
1235 
loadStackI32(int32_t offset,RegI32 dest)1236   void loadStackI32(int32_t offset, RegI32 dest) {
1237     masm.load32(Address(sp_, stackOffset(offset)), dest);
1238   }
1239 
loadStackI64(int32_t offset,RegI64 dest)1240   void loadStackI64(int32_t offset, RegI64 dest) {
1241     masm.load64(Address(sp_, stackOffset(offset)), dest);
1242   }
1243 
1244 #ifndef JS_PUNBOX64
loadStackI64Low(int32_t offset,RegI32 dest)1245   void loadStackI64Low(int32_t offset, RegI32 dest) {
1246     masm.load32(Address(sp_, stackOffset(offset - INT64LOW_OFFSET)), dest);
1247   }
1248 
loadStackI64High(int32_t offset,RegI32 dest)1249   void loadStackI64High(int32_t offset, RegI32 dest) {
1250     masm.load32(Address(sp_, stackOffset(offset - INT64HIGH_OFFSET)), dest);
1251   }
1252 #endif
1253 
1254   // Disambiguation: this loads a "Ptr" value from the stack, it does not load
1255   // the "StackPtr".
1256 
loadStackPtr(int32_t offset,Register dest)1257   void loadStackPtr(int32_t offset, Register dest) {
1258     masm.loadPtr(Address(sp_, stackOffset(offset)), dest);
1259   }
1260 
loadStackF64(int32_t offset,RegF64 dest)1261   void loadStackF64(int32_t offset, RegF64 dest) {
1262     masm.loadDouble(Address(sp_, stackOffset(offset)), dest);
1263   }
1264 
loadStackF32(int32_t offset,RegF32 dest)1265   void loadStackF32(int32_t offset, RegF32 dest) {
1266     masm.loadFloat32(Address(sp_, stackOffset(offset)), dest);
1267   }
1268 
1269   //////////////////////////////////////////////////////////////////////
1270   //
1271   // The argument area - for outgoing calls.
1272   //
1273   // We abstract these operations as an optimization: we can merge the freeing
1274   // of the argument area and dropping values off the stack after a call.  But
1275   // they always amount to manipulating the real stack pointer by some amount.
1276 
1277   // This is always equivalent to a masm.reserveStack() call.
allocArgArea(size_t size)1278   void allocArgArea(size_t size) {
1279     if (size) masm.reserveStack(size);
1280   }
1281 
1282   // This is always equivalent to a sequence of masm.freeStack() calls.
freeArgAreaAndPopBytes(size_t argSize,size_t dropSize)1283   void freeArgAreaAndPopBytes(size_t argSize, size_t dropSize) {
1284     if (argSize + dropSize) masm.freeStack(argSize + dropSize);
1285   }
1286 };
1287 
zeroLocals(BaseRegAlloc & ra)1288 void BaseStackFrame::zeroLocals(BaseRegAlloc& ra) {
1289   MOZ_ASSERT(varLow_ != UINT32_MAX);
1290 
1291   if (varLow_ == varHigh_) return;
1292 
1293   static const uint32_t wordSize = sizeof(void*);
1294 
1295   // The adjustments to 'low' by the size of the item being stored compensates
1296   // for the fact that locals offsets are the offsets from Frame to the bytes
1297   // directly "above" the locals in the locals area.  See comment at Local.
1298 
1299   // On 64-bit systems we may have 32-bit alignment for the local area as it
1300   // may be preceded by parameters and prologue/debug data.
1301 
1302   uint32_t low = varLow_;
1303   if (low % wordSize) {
1304     masm.store32(Imm32(0), Address(sp_, localOffset(low + 4)));
1305     low += 4;
1306   }
1307   MOZ_ASSERT(low % wordSize == 0);
1308 
1309   const uint32_t high = AlignBytes(varHigh_, wordSize);
1310   MOZ_ASSERT(high <= localSize_, "localSize_ should be aligned at least that");
1311 
1312   // An UNROLL_LIMIT of 16 is chosen so that we only need an 8-bit signed
1313   // immediate to represent the offset in the store instructions in the loop
1314   // on x64.
1315 
1316   const uint32_t UNROLL_LIMIT = 16;
1317   const uint32_t initWords = (high - low) / wordSize;
1318   const uint32_t tailWords = initWords % UNROLL_LIMIT;
1319   const uint32_t loopHigh = high - (tailWords * wordSize);
1320 
1321   // With only one word to initialize, just store an immediate zero.
1322 
1323   if (initWords == 1) {
1324     masm.storePtr(ImmWord(0), Address(sp_, localOffset(low + wordSize)));
1325     return;
1326   }
1327 
1328   // For other cases, it's best to have a zero in a register.
1329   //
1330   // One can do more here with SIMD registers (store 16 bytes at a time) or
1331   // with instructions like STRD on ARM (store 8 bytes at a time), but that's
1332   // for another day.
1333 
1334   RegI32 zero = ra.needI32();
1335   masm.mov(ImmWord(0), zero);
1336 
1337   // For the general case we want to have a loop body of UNROLL_LIMIT stores
1338   // and then a tail of less than UNROLL_LIMIT stores.  When initWords is less
1339   // than 2*UNROLL_LIMIT the loop trip count is at most 1 and there is no
1340   // benefit to having the pointer calculations and the compare-and-branch.
1341   // So we completely unroll when we have initWords < 2 * UNROLL_LIMIT.  (In
1342   // this case we'll end up using 32-bit offsets on x64 for up to half of the
1343   // stores, though.)
1344 
1345   // Fully-unrolled case.
1346 
1347   if (initWords < 2 * UNROLL_LIMIT) {
1348     for (uint32_t i = low; i < high; i += wordSize)
1349       masm.storePtr(zero, Address(sp_, localOffset(i + wordSize)));
1350     ra.freeI32(zero);
1351     return;
1352   }
1353 
1354   // Unrolled loop with a tail. Stores will use negative offsets. That's OK
1355   // for x86 and ARM, at least.
1356 
1357   // Compute pointer to the highest-addressed slot on the frame.
1358   RegI32 p = ra.needI32();
1359   masm.computeEffectiveAddress(Address(sp_, localOffset(low + wordSize)), p);
1360 
1361   // Compute pointer to the lowest-addressed slot on the frame that will be
1362   // initialized by the loop body.
1363   RegI32 lim = ra.needI32();
1364   masm.computeEffectiveAddress(Address(sp_, localOffset(loopHigh + wordSize)),
1365                                lim);
1366 
1367   // The loop body.  Eventually we'll have p == lim and exit the loop.
1368   Label again;
1369   masm.bind(&again);
1370   for (uint32_t i = 0; i < UNROLL_LIMIT; ++i)
1371     masm.storePtr(zero, Address(p, -(wordSize * i)));
1372   masm.subPtr(Imm32(UNROLL_LIMIT * wordSize), p);
1373   masm.branchPtr(Assembler::LessThan, lim, p, &again);
1374 
1375   // The tail.
1376   for (uint32_t i = 0; i < tailWords; ++i)
1377     masm.storePtr(zero, Address(p, -(wordSize * i)));
1378 
1379   ra.freeI32(p);
1380   ra.freeI32(lim);
1381   ra.freeI32(zero);
1382 }
1383 
1384 // The baseline compiler proper.
1385 
1386 class BaseCompiler final : public BaseCompilerInterface {
1387   typedef BaseStackFrame::Local Local;
1388   typedef Vector<NonAssertingLabel, 8, SystemAllocPolicy> LabelVector;
1389   typedef Vector<MIRType, 8, SystemAllocPolicy> MIRTypeVector;
1390 
1391   // Bit set used for simple bounds check elimination.  Capping this at 64
1392   // locals makes sense; even 32 locals would probably be OK in practice.
1393   //
1394   // For more information about BCE, see the block comment above
1395   // popMemoryAccess(), below.
1396 
1397   typedef uint64_t BCESet;
1398 
1399   // Control node, representing labels and stack heights at join points.
1400 
1401   struct Control {
Controljs::wasm::BaseCompiler::Control1402     Control()
1403         : stackHeight(UINT32_MAX),
1404           stackSize(UINT32_MAX),
1405           bceSafeOnEntry(0),
1406           bceSafeOnExit(~BCESet(0)),
1407           deadOnArrival(false),
1408           deadThenBranch(false) {}
1409 
1410     NonAssertingLabel label;       // The "exit" label
1411     NonAssertingLabel otherLabel;  // Used for the "else" branch of if-then-else
1412     uint32_t stackHeight;          // From BaseStackFrame
1413     uint32_t stackSize;            // Value stack height
1414     BCESet bceSafeOnEntry;         // Bounds check info flowing into the item
1415     BCESet bceSafeOnExit;          // Bounds check info flowing out of the item
1416     bool deadOnArrival;            // deadCode_ was set on entry to the region
1417     bool deadThenBranch;           // deadCode_ was set on exit from "then"
1418   };
1419 
1420   struct BaseCompilePolicy {
1421     // The baseline compiler tracks values on a stack of its own -- it
1422     // needs to scan that stack for spilling -- and thus has no need
1423     // for the values maintained by the iterator.
1424     typedef Nothing Value;
1425 
1426     // The baseline compiler uses the iterator's control stack, attaching
1427     // its own control information.
1428     typedef Control ControlItem;
1429   };
1430 
1431   typedef OpIter<BaseCompilePolicy> BaseOpIter;
1432 
1433   // The baseline compiler will use OOL code more sparingly than
1434   // Baldr since our code is not high performance and frills like
1435   // code density and branch prediction friendliness will be less
1436   // important.
1437 
1438   class OutOfLineCode : public TempObject {
1439    private:
1440     NonAssertingLabel entry_;
1441     NonAssertingLabel rejoin_;
1442     uint32_t stackHeight_;
1443 
1444    public:
OutOfLineCode()1445     OutOfLineCode() : stackHeight_(UINT32_MAX) {}
1446 
entry()1447     Label* entry() { return &entry_; }
rejoin()1448     Label* rejoin() { return &rejoin_; }
1449 
setStackHeight(uint32_t stackHeight)1450     void setStackHeight(uint32_t stackHeight) {
1451       MOZ_ASSERT(stackHeight_ == UINT32_MAX);
1452       stackHeight_ = stackHeight;
1453     }
1454 
bind(BaseStackFrame * fr,MacroAssembler * masm)1455     void bind(BaseStackFrame* fr, MacroAssembler* masm) {
1456       MOZ_ASSERT(stackHeight_ != UINT32_MAX);
1457       masm->bind(&entry_);
1458       fr->setStackHeight(stackHeight_);
1459     }
1460 
1461     // The generate() method must be careful about register use
1462     // because it will be invoked when there is a register
1463     // assignment in the BaseCompiler that does not correspond
1464     // to the available registers when the generated OOL code is
1465     // executed.  The register allocator *must not* be called.
1466     //
1467     // The best strategy is for the creator of the OOL object to
1468     // allocate all temps that the OOL code will need.
1469     //
1470     // Input, output, and temp registers are embedded in the OOL
1471     // object and are known to the code generator.
1472     //
1473     // Scratch registers are available to use in OOL code.
1474     //
1475     // All other registers must be explicitly saved and restored
1476     // by the OOL code before being used.
1477 
1478     virtual void generate(MacroAssembler* masm) = 0;
1479   };
1480 
1481   enum class LatentOp { None, Compare, Eqz };
1482 
1483   struct AccessCheck {
AccessCheckjs::wasm::BaseCompiler::AccessCheck1484     AccessCheck()
1485         : omitBoundsCheck(false),
1486           omitAlignmentCheck(false),
1487           onlyPointerAlignment(false) {}
1488 
1489     // If `omitAlignmentCheck` is true then we need check neither the
1490     // pointer nor the offset.  Otherwise, if `onlyPointerAlignment` is true
1491     // then we need check only the pointer.  Otherwise, check the sum of
1492     // pointer and offset.
1493 
1494     bool omitBoundsCheck;
1495     bool omitAlignmentCheck;
1496     bool onlyPointerAlignment;
1497   };
1498 
1499   const ModuleEnvironment& env_;
1500   BaseOpIter iter_;
1501   const FuncCompileInput& func_;
1502   size_t lastReadCallSite_;
1503   TempAllocator& alloc_;
1504   const ValTypeVector& locals_;  // Types of parameters and locals
1505   bool deadCode_;  // Flag indicating we should decode & discard the opcode
1506   bool debugEnabled_;
1507   BCESet
1508       bceSafe_;  // Locals that have been bounds checked and not updated since
1509   ValTypeVector SigD_;
1510   ValTypeVector SigF_;
1511   MIRTypeVector SigP_;
1512   MIRTypeVector SigPI_;
1513   MIRTypeVector SigPII_;
1514   MIRTypeVector SigPIIL_;
1515   MIRTypeVector SigPILL_;
1516   NonAssertingLabel returnLabel_;
1517   CompileMode mode_;
1518 
1519   LatentOp latentOp_;   // Latent operation for branch (seen next)
1520   ValType latentType_;  // Operand type, if latentOp_ is true
1521   Assembler::Condition
1522       latentIntCmp_;  // Comparison operator, if latentOp_ == Compare, int types
1523   Assembler::DoubleCondition latentDoubleCmp_;  // Comparison operator, if
1524                                                 // latentOp_ == Compare, float
1525                                                 // types
1526 
1527   FuncOffsets offsets_;
1528   MacroAssembler& masm;  // No '_' suffix - too tedious...
1529   BaseRegAlloc ra;       // Ditto
1530   BaseStackFrame fr;
1531 
1532   BaseStackFrame::LocalVector localInfo_;
1533   Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
1534 
1535   // On specific platforms we sometimes need to use specific registers.
1536 
1537   SpecificRegs specific;
1538 
1539   // The join registers are used to carry values out of blocks.
1540   // JoinRegI32 and joinRegI64 must overlap: emitBrIf and
1541   // emitBrTable assume that.
1542 
1543   RegI32 joinRegI32;
1544   RegI64 joinRegI64;
1545   RegF32 joinRegF32;
1546   RegF64 joinRegF64;
1547 
1548   // There are more members scattered throughout.
1549 
1550  public:
1551   BaseCompiler(const ModuleEnvironment& env, Decoder& decoder,
1552                const FuncCompileInput& input, const ValTypeVector& locals,
1553                bool debugEnabled, TempAllocator* alloc, MacroAssembler* masm,
1554                CompileMode mode);
1555 
1556   MOZ_MUST_USE bool init();
1557 
1558   FuncOffsets finish();
1559 
1560   MOZ_MUST_USE bool emitFunction();
1561   void emitInitStackLocals();
1562 
sig() const1563   const SigWithId& sig() const { return *env_.funcSigs[func_.index]; }
1564 
1565   // Used by some of the ScratchRegister implementations.
operator MacroAssembler&() const1566   operator MacroAssembler&() const { return masm; }
operator BaseRegAlloc&()1567   operator BaseRegAlloc&() { return ra; }
1568 
1569  private:
1570   ////////////////////////////////////////////////////////////
1571   //
1572   // Out of line code management.
1573 
addOutOfLineCode(OutOfLineCode * ool)1574   MOZ_MUST_USE OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool) {
1575     if (!ool || !outOfLine_.append(ool)) return nullptr;
1576     ool->setStackHeight(fr.stackHeight());
1577     return ool;
1578   }
1579 
generateOutOfLineCode()1580   MOZ_MUST_USE bool generateOutOfLineCode() {
1581     for (uint32_t i = 0; i < outOfLine_.length(); i++) {
1582       OutOfLineCode* ool = outOfLine_[i];
1583       ool->bind(&fr, &masm);
1584       ool->generate(&masm);
1585     }
1586 
1587     return !masm.oom();
1588   }
1589 
1590   // Utility.
1591 
localFromSlot(uint32_t slot,MIRType type)1592   const Local& localFromSlot(uint32_t slot, MIRType type) {
1593     MOZ_ASSERT(localInfo_[slot].type == type);
1594     return localInfo_[slot];
1595   }
1596 
1597   ////////////////////////////////////////////////////////////
1598   //
1599   // High-level register management.
1600 
isAvailableI32(RegI32 r)1601   bool isAvailableI32(RegI32 r) { return ra.isAvailableI32(r); }
isAvailableI64(RegI64 r)1602   bool isAvailableI64(RegI64 r) { return ra.isAvailableI64(r); }
isAvailableF32(RegF32 r)1603   bool isAvailableF32(RegF32 r) { return ra.isAvailableF32(r); }
isAvailableF64(RegF64 r)1604   bool isAvailableF64(RegF64 r) { return ra.isAvailableF64(r); }
1605 
needI32()1606   MOZ_MUST_USE RegI32 needI32() { return ra.needI32(); }
needI64()1607   MOZ_MUST_USE RegI64 needI64() { return ra.needI64(); }
needF32()1608   MOZ_MUST_USE RegF32 needF32() { return ra.needF32(); }
needF64()1609   MOZ_MUST_USE RegF64 needF64() { return ra.needF64(); }
1610 
needI32(RegI32 specific)1611   void needI32(RegI32 specific) { ra.needI32(specific); }
needI64(RegI64 specific)1612   void needI64(RegI64 specific) { ra.needI64(specific); }
needF32(RegF32 specific)1613   void needF32(RegF32 specific) { ra.needF32(specific); }
needF64(RegF64 specific)1614   void needF64(RegF64 specific) { ra.needF64(specific); }
1615 
1616 #if defined(JS_CODEGEN_ARM)
needI64Pair()1617   MOZ_MUST_USE RegI64 needI64Pair() { return ra.needI64Pair(); }
1618 #endif
1619 
freeI32(RegI32 r)1620   void freeI32(RegI32 r) { ra.freeI32(r); }
freeI64(RegI64 r)1621   void freeI64(RegI64 r) { ra.freeI64(r); }
freeF32(RegF32 r)1622   void freeF32(RegF32 r) { ra.freeF32(r); }
freeF64(RegF64 r)1623   void freeF64(RegF64 r) { ra.freeF64(r); }
1624 
freeI64Except(RegI64 r,RegI32 except)1625   void freeI64Except(RegI64 r, RegI32 except) {
1626 #ifdef JS_PUNBOX64
1627     MOZ_ASSERT(r.reg == except);
1628 #else
1629     MOZ_ASSERT(r.high == except || r.low == except);
1630     freeI64(r);
1631     needI32(except);
1632 #endif
1633   }
1634 
maybeFreeI32(RegI32 r)1635   void maybeFreeI32(RegI32 r) {
1636     if (r.isValid()) freeI32(r);
1637   }
1638 
maybeFreeI64(RegI64 r)1639   void maybeFreeI64(RegI64 r) {
1640     if (r.isValid()) freeI64(r);
1641   }
1642 
maybeFreeF64(RegF64 r)1643   void maybeFreeF64(RegF64 r) {
1644     if (r.isValid()) freeF64(r);
1645   }
1646 
needI32NoSync(RegI32 r)1647   void needI32NoSync(RegI32 r) {
1648     MOZ_ASSERT(isAvailableI32(r));
1649     needI32(r);
1650   }
1651 
1652   // TODO / OPTIMIZE: need2xI32() can be optimized along with needI32()
1653   // to avoid sync(). (Bug 1316802)
1654 
need2xI32(RegI32 r0,RegI32 r1)1655   void need2xI32(RegI32 r0, RegI32 r1) {
1656     needI32(r0);
1657     needI32(r1);
1658   }
1659 
need2xI64(RegI64 r0,RegI64 r1)1660   void need2xI64(RegI64 r0, RegI64 r1) {
1661     needI64(r0);
1662     needI64(r1);
1663   }
1664 
fromI64(RegI64 r)1665   RegI32 fromI64(RegI64 r) { return RegI32(lowPart(r)); }
1666 
1667 #ifdef JS_PUNBOX64
fromI32(RegI32 r)1668   RegI64 fromI32(RegI32 r) { return RegI64(Register64(r)); }
1669 #endif
1670 
widenI32(RegI32 r)1671   RegI64 widenI32(RegI32 r) {
1672     MOZ_ASSERT(!isAvailableI32(r));
1673 #ifdef JS_PUNBOX64
1674     return fromI32(r);
1675 #else
1676     RegI32 high = needI32();
1677     return RegI64(Register64(high, r));
1678 #endif
1679   }
1680 
narrowI64(RegI64 r)1681   RegI32 narrowI64(RegI64 r) {
1682 #ifdef JS_PUNBOX64
1683     return RegI32(r.reg);
1684 #else
1685     freeI32(RegI32(r.high));
1686     return RegI32(r.low);
1687 #endif
1688   }
1689 
lowPart(RegI64 r)1690   RegI32 lowPart(RegI64 r) {
1691 #ifdef JS_PUNBOX64
1692     return RegI32(r.reg);
1693 #else
1694     return RegI32(r.low);
1695 #endif
1696   }
1697 
maybeHighPart(RegI64 r)1698   RegI32 maybeHighPart(RegI64 r) {
1699 #ifdef JS_PUNBOX64
1700     return RegI32::Invalid();
1701 #else
1702     return RegI32(r.high);
1703 #endif
1704   }
1705 
maybeClearHighPart(RegI64 r)1706   void maybeClearHighPart(RegI64 r) {
1707 #if !defined(JS_PUNBOX64)
1708     moveImm32(0, RegI32(r.high));
1709 #endif
1710   }
1711 
moveI32(RegI32 src,RegI32 dest)1712   void moveI32(RegI32 src, RegI32 dest) {
1713     if (src != dest) masm.move32(src, dest);
1714   }
1715 
moveI64(RegI64 src,RegI64 dest)1716   void moveI64(RegI64 src, RegI64 dest) {
1717     if (src != dest) masm.move64(src, dest);
1718   }
1719 
moveF64(RegF64 src,RegF64 dest)1720   void moveF64(RegF64 src, RegF64 dest) {
1721     if (src != dest) masm.moveDouble(src, dest);
1722   }
1723 
moveF32(RegF32 src,RegF32 dest)1724   void moveF32(RegF32 src, RegF32 dest) {
1725     if (src != dest) masm.moveFloat32(src, dest);
1726   }
1727 
maybeReserveJoinRegI(ExprType type)1728   void maybeReserveJoinRegI(ExprType type) {
1729     if (type == ExprType::I32)
1730       needI32(joinRegI32);
1731     else if (type == ExprType::I64)
1732       needI64(joinRegI64);
1733   }
1734 
maybeUnreserveJoinRegI(ExprType type)1735   void maybeUnreserveJoinRegI(ExprType type) {
1736     if (type == ExprType::I32)
1737       freeI32(joinRegI32);
1738     else if (type == ExprType::I64)
1739       freeI64(joinRegI64);
1740   }
1741 
maybeReserveJoinReg(ExprType type)1742   void maybeReserveJoinReg(ExprType type) {
1743     switch (type) {
1744       case ExprType::I32:
1745         needI32(joinRegI32);
1746         break;
1747       case ExprType::I64:
1748         needI64(joinRegI64);
1749         break;
1750       case ExprType::F32:
1751         needF32(joinRegF32);
1752         break;
1753       case ExprType::F64:
1754         needF64(joinRegF64);
1755         break;
1756       default:
1757         break;
1758     }
1759   }
1760 
maybeUnreserveJoinReg(ExprType type)1761   void maybeUnreserveJoinReg(ExprType type) {
1762     switch (type) {
1763       case ExprType::I32:
1764         freeI32(joinRegI32);
1765         break;
1766       case ExprType::I64:
1767         freeI64(joinRegI64);
1768         break;
1769       case ExprType::F32:
1770         freeF32(joinRegF32);
1771         break;
1772       case ExprType::F64:
1773         freeF64(joinRegF64);
1774         break;
1775       default:
1776         break;
1777     }
1778   }
1779 
1780   ////////////////////////////////////////////////////////////
1781   //
1782   // Value stack and spilling.
1783   //
1784   // The value stack facilitates some on-the-fly register allocation
1785   // and immediate-constant use.  It tracks constants, latent
1786   // references to locals, register contents, and values on the CPU
1787   // stack.
1788   //
1789   // The stack can be flushed to memory using sync().  This is handy
1790   // to avoid problems with control flow and messy register usage
1791   // patterns.
1792 
1793   struct Stk {
1794     enum Kind {
1795       // The Mem opcodes are all clustered at the beginning to
1796       // allow for a quick test within sync().
1797       MemI32,  // 32-bit integer stack value ("offs")
1798       MemI64,  // 64-bit integer stack value ("offs")
1799       MemF32,  // 32-bit floating stack value ("offs")
1800       MemF64,  // 64-bit floating stack value ("offs")
1801 
1802       // The Local opcodes follow the Mem opcodes for a similar
1803       // quick test within hasLocal().
1804       LocalI32,  // Local int32 var ("slot")
1805       LocalI64,  // Local int64 var ("slot")
1806       LocalF32,  // Local float32 var ("slot")
1807       LocalF64,  // Local double var ("slot")
1808 
1809       RegisterI32,  // 32-bit integer register ("i32reg")
1810       RegisterI64,  // 64-bit integer register ("i64reg")
1811       RegisterF32,  // 32-bit floating register ("f32reg")
1812       RegisterF64,  // 64-bit floating register ("f64reg")
1813 
1814       ConstI32,  // 32-bit integer constant ("i32val")
1815       ConstI64,  // 64-bit integer constant ("i64val")
1816       ConstF32,  // 32-bit floating constant ("f32val")
1817       ConstF64,  // 64-bit floating constant ("f64val")
1818 
1819       None  // Uninitialized or void
1820     };
1821 
1822     Kind kind_;
1823 
1824     static const Kind MemLast = MemF64;
1825     static const Kind LocalLast = LocalF64;
1826 
1827     union {
1828       RegI32 i32reg_;
1829       RegI64 i64reg_;
1830       RegF32 f32reg_;
1831       RegF64 f64reg_;
1832       int32_t i32val_;
1833       int64_t i64val_;
1834       float f32val_;
1835       double f64val_;
1836       uint32_t slot_;
1837       uint32_t offs_;
1838     };
1839 
Stkjs::wasm::BaseCompiler::Stk1840     Stk() { kind_ = None; }
1841 
kindjs::wasm::BaseCompiler::Stk1842     Kind kind() const { return kind_; }
isMemjs::wasm::BaseCompiler::Stk1843     bool isMem() const { return kind_ <= MemLast; }
1844 
i32regjs::wasm::BaseCompiler::Stk1845     RegI32 i32reg() const {
1846       MOZ_ASSERT(kind_ == RegisterI32);
1847       return i32reg_;
1848     }
i64regjs::wasm::BaseCompiler::Stk1849     RegI64 i64reg() const {
1850       MOZ_ASSERT(kind_ == RegisterI64);
1851       return i64reg_;
1852     }
f32regjs::wasm::BaseCompiler::Stk1853     RegF32 f32reg() const {
1854       MOZ_ASSERT(kind_ == RegisterF32);
1855       return f32reg_;
1856     }
f64regjs::wasm::BaseCompiler::Stk1857     RegF64 f64reg() const {
1858       MOZ_ASSERT(kind_ == RegisterF64);
1859       return f64reg_;
1860     }
i32valjs::wasm::BaseCompiler::Stk1861     int32_t i32val() const {
1862       MOZ_ASSERT(kind_ == ConstI32);
1863       return i32val_;
1864     }
i64valjs::wasm::BaseCompiler::Stk1865     int64_t i64val() const {
1866       MOZ_ASSERT(kind_ == ConstI64);
1867       return i64val_;
1868     }
1869     // For these two, use an out-param instead of simply returning, to
1870     // use the normal stack and not the x87 FP stack (which has effect on
1871     // NaNs with the signaling bit set).
f32valjs::wasm::BaseCompiler::Stk1872     void f32val(float* out) const {
1873       MOZ_ASSERT(kind_ == ConstF32);
1874       *out = f32val_;
1875     }
f64valjs::wasm::BaseCompiler::Stk1876     void f64val(double* out) const {
1877       MOZ_ASSERT(kind_ == ConstF64);
1878       *out = f64val_;
1879     }
slotjs::wasm::BaseCompiler::Stk1880     uint32_t slot() const {
1881       MOZ_ASSERT(kind_ > MemLast && kind_ <= LocalLast);
1882       return slot_;
1883     }
offsjs::wasm::BaseCompiler::Stk1884     uint32_t offs() const {
1885       MOZ_ASSERT(isMem());
1886       return offs_;
1887     }
1888 
setI32Regjs::wasm::BaseCompiler::Stk1889     void setI32Reg(RegI32 r) {
1890       kind_ = RegisterI32;
1891       i32reg_ = r;
1892     }
setI64Regjs::wasm::BaseCompiler::Stk1893     void setI64Reg(RegI64 r) {
1894       kind_ = RegisterI64;
1895       i64reg_ = r;
1896     }
setF32Regjs::wasm::BaseCompiler::Stk1897     void setF32Reg(RegF32 r) {
1898       kind_ = RegisterF32;
1899       f32reg_ = r;
1900     }
setF64Regjs::wasm::BaseCompiler::Stk1901     void setF64Reg(RegF64 r) {
1902       kind_ = RegisterF64;
1903       f64reg_ = r;
1904     }
setI32Valjs::wasm::BaseCompiler::Stk1905     void setI32Val(int32_t v) {
1906       kind_ = ConstI32;
1907       i32val_ = v;
1908     }
setI64Valjs::wasm::BaseCompiler::Stk1909     void setI64Val(int64_t v) {
1910       kind_ = ConstI64;
1911       i64val_ = v;
1912     }
setF32Valjs::wasm::BaseCompiler::Stk1913     void setF32Val(float v) {
1914       kind_ = ConstF32;
1915       f32val_ = v;
1916     }
setF64Valjs::wasm::BaseCompiler::Stk1917     void setF64Val(double v) {
1918       kind_ = ConstF64;
1919       f64val_ = v;
1920     }
setSlotjs::wasm::BaseCompiler::Stk1921     void setSlot(Kind k, uint32_t v) {
1922       MOZ_ASSERT(k > MemLast && k <= LocalLast);
1923       kind_ = k;
1924       slot_ = v;
1925     }
setOffsjs::wasm::BaseCompiler::Stk1926     void setOffs(Kind k, uint32_t v) {
1927       MOZ_ASSERT(k <= MemLast);
1928       kind_ = k;
1929       offs_ = v;
1930     }
1931   };
1932 
1933   Vector<Stk, 8, SystemAllocPolicy> stk_;
1934 
push()1935   Stk& push() {
1936     stk_.infallibleEmplaceBack(Stk());
1937     return stk_.back();
1938   }
1939 
loadConstI32(Stk & src,RegI32 dest)1940   void loadConstI32(Stk& src, RegI32 dest) { moveImm32(src.i32val(), dest); }
1941 
loadMemI32(Stk & src,RegI32 dest)1942   void loadMemI32(Stk& src, RegI32 dest) { fr.loadStackI32(src.offs(), dest); }
1943 
loadLocalI32(Stk & src,RegI32 dest)1944   void loadLocalI32(Stk& src, RegI32 dest) {
1945     fr.loadLocalI32(localFromSlot(src.slot(), MIRType::Int32), dest);
1946   }
1947 
loadRegisterI32(Stk & src,RegI32 dest)1948   void loadRegisterI32(Stk& src, RegI32 dest) { moveI32(src.i32reg(), dest); }
1949 
loadConstI64(Stk & src,RegI64 dest)1950   void loadConstI64(Stk& src, RegI64 dest) { moveImm64(src.i64val(), dest); }
1951 
loadMemI64(Stk & src,RegI64 dest)1952   void loadMemI64(Stk& src, RegI64 dest) { fr.loadStackI64(src.offs(), dest); }
1953 
loadLocalI64(Stk & src,RegI64 dest)1954   void loadLocalI64(Stk& src, RegI64 dest) {
1955     fr.loadLocalI64(localFromSlot(src.slot(), MIRType::Int64), dest);
1956   }
1957 
loadRegisterI64(Stk & src,RegI64 dest)1958   void loadRegisterI64(Stk& src, RegI64 dest) { moveI64(src.i64reg(), dest); }
1959 
loadConstF64(Stk & src,RegF64 dest)1960   void loadConstF64(Stk& src, RegF64 dest) {
1961     double d;
1962     src.f64val(&d);
1963     masm.loadConstantDouble(d, dest);
1964   }
1965 
loadMemF64(Stk & src,RegF64 dest)1966   void loadMemF64(Stk& src, RegF64 dest) { fr.loadStackF64(src.offs(), dest); }
1967 
loadLocalF64(Stk & src,RegF64 dest)1968   void loadLocalF64(Stk& src, RegF64 dest) {
1969     fr.loadLocalF64(localFromSlot(src.slot(), MIRType::Double), dest);
1970   }
1971 
loadRegisterF64(Stk & src,RegF64 dest)1972   void loadRegisterF64(Stk& src, RegF64 dest) { moveF64(src.f64reg(), dest); }
1973 
loadConstF32(Stk & src,RegF32 dest)1974   void loadConstF32(Stk& src, RegF32 dest) {
1975     float f;
1976     src.f32val(&f);
1977     masm.loadConstantFloat32(f, dest);
1978   }
1979 
loadMemF32(Stk & src,RegF32 dest)1980   void loadMemF32(Stk& src, RegF32 dest) { fr.loadStackF32(src.offs(), dest); }
1981 
loadLocalF32(Stk & src,RegF32 dest)1982   void loadLocalF32(Stk& src, RegF32 dest) {
1983     fr.loadLocalF32(localFromSlot(src.slot(), MIRType::Float32), dest);
1984   }
1985 
loadRegisterF32(Stk & src,RegF32 dest)1986   void loadRegisterF32(Stk& src, RegF32 dest) { moveF32(src.f32reg(), dest); }
1987 
loadI32(Stk & src,RegI32 dest)1988   void loadI32(Stk& src, RegI32 dest) {
1989     switch (src.kind()) {
1990       case Stk::ConstI32:
1991         loadConstI32(src, dest);
1992         break;
1993       case Stk::MemI32:
1994         loadMemI32(src, dest);
1995         break;
1996       case Stk::LocalI32:
1997         loadLocalI32(src, dest);
1998         break;
1999       case Stk::RegisterI32:
2000         loadRegisterI32(src, dest);
2001         break;
2002       case Stk::None:
2003       default:
2004         MOZ_CRASH("Compiler bug: Expected I32 on stack");
2005     }
2006   }
2007 
loadI64(Stk & src,RegI64 dest)2008   void loadI64(Stk& src, RegI64 dest) {
2009     switch (src.kind()) {
2010       case Stk::ConstI64:
2011         loadConstI64(src, dest);
2012         break;
2013       case Stk::MemI64:
2014         loadMemI64(src, dest);
2015         break;
2016       case Stk::LocalI64:
2017         loadLocalI64(src, dest);
2018         break;
2019       case Stk::RegisterI64:
2020         loadRegisterI64(src, dest);
2021         break;
2022       case Stk::None:
2023       default:
2024         MOZ_CRASH("Compiler bug: Expected I64 on stack");
2025     }
2026   }
2027 
2028 #if !defined(JS_PUNBOX64)
loadI64Low(Stk & src,RegI32 dest)2029   void loadI64Low(Stk& src, RegI32 dest) {
2030     switch (src.kind()) {
2031       case Stk::ConstI64:
2032         moveImm32(int32_t(src.i64val()), dest);
2033         break;
2034       case Stk::MemI64:
2035         fr.loadStackI64Low(src.offs(), dest);
2036         break;
2037       case Stk::LocalI64:
2038         fr.loadLocalI64Low(localFromSlot(src.slot(), MIRType::Int64), dest);
2039         break;
2040       case Stk::RegisterI64:
2041         moveI32(RegI32(src.i64reg().low), dest);
2042         break;
2043       case Stk::None:
2044       default:
2045         MOZ_CRASH("Compiler bug: Expected I64 on stack");
2046     }
2047   }
2048 
loadI64High(Stk & src,RegI32 dest)2049   void loadI64High(Stk& src, RegI32 dest) {
2050     switch (src.kind()) {
2051       case Stk::ConstI64:
2052         moveImm32(int32_t(src.i64val() >> 32), dest);
2053         break;
2054       case Stk::MemI64:
2055         fr.loadStackI64High(src.offs(), dest);
2056         break;
2057       case Stk::LocalI64:
2058         fr.loadLocalI64High(localFromSlot(src.slot(), MIRType::Int64), dest);
2059         break;
2060       case Stk::RegisterI64:
2061         moveI32(RegI32(src.i64reg().high), dest);
2062         break;
2063       case Stk::None:
2064       default:
2065         MOZ_CRASH("Compiler bug: Expected I64 on stack");
2066     }
2067   }
2068 #endif
2069 
loadF64(Stk & src,RegF64 dest)2070   void loadF64(Stk& src, RegF64 dest) {
2071     switch (src.kind()) {
2072       case Stk::ConstF64:
2073         loadConstF64(src, dest);
2074         break;
2075       case Stk::MemF64:
2076         loadMemF64(src, dest);
2077         break;
2078       case Stk::LocalF64:
2079         loadLocalF64(src, dest);
2080         break;
2081       case Stk::RegisterF64:
2082         loadRegisterF64(src, dest);
2083         break;
2084       case Stk::None:
2085       default:
2086         MOZ_CRASH("Compiler bug: expected F64 on stack");
2087     }
2088   }
2089 
loadF32(Stk & src,RegF32 dest)2090   void loadF32(Stk& src, RegF32 dest) {
2091     switch (src.kind()) {
2092       case Stk::ConstF32:
2093         loadConstF32(src, dest);
2094         break;
2095       case Stk::MemF32:
2096         loadMemF32(src, dest);
2097         break;
2098       case Stk::LocalF32:
2099         loadLocalF32(src, dest);
2100         break;
2101       case Stk::RegisterF32:
2102         loadRegisterF32(src, dest);
2103         break;
2104       case Stk::None:
2105       default:
2106         MOZ_CRASH("Compiler bug: expected F32 on stack");
2107     }
2108   }
2109 
2110   // Flush all local and register value stack elements to memory.
2111   //
2112   // TODO / OPTIMIZE: As this is fairly expensive and causes worse
2113   // code to be emitted subsequently, it is useful to avoid calling
2114   // it.  (Bug 1316802)
2115   //
2116   // Some optimization has been done already.  Remaining
2117   // opportunities:
2118   //
2119   //  - It would be interesting to see if we can specialize it
2120   //    before calls with particularly simple signatures, or where
2121   //    we can do parallel assignment of register arguments, or
2122   //    similar.  See notes in emitCall().
2123   //
2124   //  - Operations that need specific registers: multiply, quotient,
2125   //    remainder, will tend to sync because the registers we need
2126   //    will tend to be allocated.  We may be able to avoid that by
2127   //    prioritizing registers differently (takeLast instead of
2128   //    takeFirst) but we may also be able to allocate an unused
2129   //    register on demand to free up one we need, thus avoiding the
2130   //    sync.  That type of fix would go into needI32().
2131 
sync()2132   void sync() final {
2133     size_t start = 0;
2134     size_t lim = stk_.length();
2135 
2136     for (size_t i = lim; i > 0; i--) {
2137       // Memory opcodes are first in the enum, single check against MemLast is
2138       // fine.
2139       if (stk_[i - 1].kind() <= Stk::MemLast) {
2140         start = i;
2141         break;
2142       }
2143     }
2144 
2145     for (size_t i = start; i < lim; i++) {
2146       Stk& v = stk_[i];
2147       switch (v.kind()) {
2148         case Stk::LocalI32: {
2149           ScratchI32 scratch(*this);
2150           loadLocalI32(v, scratch);
2151           uint32_t offs = fr.pushPtr(scratch);
2152           v.setOffs(Stk::MemI32, offs);
2153           break;
2154         }
2155         case Stk::RegisterI32: {
2156           uint32_t offs = fr.pushPtr(v.i32reg());
2157           freeI32(v.i32reg());
2158           v.setOffs(Stk::MemI32, offs);
2159           break;
2160         }
2161         case Stk::LocalI64: {
2162           ScratchI32 scratch(*this);
2163 #ifdef JS_PUNBOX64
2164           loadI64(v, fromI32(scratch));
2165           uint32_t offs = fr.pushPtr(scratch);
2166 #else
2167           fr.loadLocalI64High(localFromSlot(v.slot(), MIRType::Int64), scratch);
2168           fr.pushPtr(scratch);
2169           fr.loadLocalI64Low(localFromSlot(v.slot(), MIRType::Int64), scratch);
2170           uint32_t offs = fr.pushPtr(scratch);
2171 #endif
2172           v.setOffs(Stk::MemI64, offs);
2173           break;
2174         }
2175         case Stk::RegisterI64: {
2176 #ifdef JS_PUNBOX64
2177           uint32_t offs = fr.pushPtr(v.i64reg().reg);
2178           freeI64(v.i64reg());
2179 #else
2180           fr.pushPtr(v.i64reg().high);
2181           uint32_t offs = fr.pushPtr(v.i64reg().low);
2182           freeI64(v.i64reg());
2183 #endif
2184           v.setOffs(Stk::MemI64, offs);
2185           break;
2186         }
2187         case Stk::LocalF64: {
2188           ScratchF64 scratch(*this);
2189           loadF64(v, scratch);
2190           uint32_t offs = fr.pushDouble(scratch);
2191           v.setOffs(Stk::MemF64, offs);
2192           break;
2193         }
2194         case Stk::RegisterF64: {
2195           uint32_t offs = fr.pushDouble(v.f64reg());
2196           freeF64(v.f64reg());
2197           v.setOffs(Stk::MemF64, offs);
2198           break;
2199         }
2200         case Stk::LocalF32: {
2201           ScratchF32 scratch(*this);
2202           loadF32(v, scratch);
2203           uint32_t offs = fr.pushFloat32(scratch);
2204           v.setOffs(Stk::MemF32, offs);
2205           break;
2206         }
2207         case Stk::RegisterF32: {
2208           uint32_t offs = fr.pushFloat32(v.f32reg());
2209           freeF32(v.f32reg());
2210           v.setOffs(Stk::MemF32, offs);
2211           break;
2212         }
2213         default: { break; }
2214       }
2215     }
2216   }
2217 
2218   // This is an optimization used to avoid calling sync() for
2219   // setLocal(): if the local does not exist unresolved on the stack
2220   // then we can skip the sync.
2221 
hasLocal(uint32_t slot)2222   bool hasLocal(uint32_t slot) {
2223     for (size_t i = stk_.length(); i > 0; i--) {
2224       // Memory opcodes are first in the enum, single check against MemLast is
2225       // fine.
2226       Stk::Kind kind = stk_[i - 1].kind();
2227       if (kind <= Stk::MemLast) return false;
2228 
2229       // Local opcodes follow memory opcodes in the enum, single check against
2230       // LocalLast is sufficient.
2231       if (kind <= Stk::LocalLast && stk_[i - 1].slot() == slot) return true;
2232     }
2233     return false;
2234   }
2235 
syncLocal(uint32_t slot)2236   void syncLocal(uint32_t slot) {
2237     if (hasLocal(slot))
2238       sync();  // TODO / OPTIMIZE: Improve this?  (Bug 1316817)
2239   }
2240 
2241   // Push the register r onto the stack.
2242 
pushI32(RegI32 r)2243   void pushI32(RegI32 r) {
2244     MOZ_ASSERT(!isAvailableI32(r));
2245     Stk& x = push();
2246     x.setI32Reg(r);
2247   }
2248 
pushI64(RegI64 r)2249   void pushI64(RegI64 r) {
2250     MOZ_ASSERT(!isAvailableI64(r));
2251     Stk& x = push();
2252     x.setI64Reg(r);
2253   }
2254 
pushF64(RegF64 r)2255   void pushF64(RegF64 r) {
2256     MOZ_ASSERT(!isAvailableF64(r));
2257     Stk& x = push();
2258     x.setF64Reg(r);
2259   }
2260 
pushF32(RegF32 r)2261   void pushF32(RegF32 r) {
2262     MOZ_ASSERT(!isAvailableF32(r));
2263     Stk& x = push();
2264     x.setF32Reg(r);
2265   }
2266 
2267   // Push the value onto the stack.
2268 
pushI32(int32_t v)2269   void pushI32(int32_t v) {
2270     Stk& x = push();
2271     x.setI32Val(v);
2272   }
2273 
pushI64(int64_t v)2274   void pushI64(int64_t v) {
2275     Stk& x = push();
2276     x.setI64Val(v);
2277   }
2278 
pushF64(double v)2279   void pushF64(double v) {
2280     Stk& x = push();
2281     x.setF64Val(v);
2282   }
2283 
pushF32(float v)2284   void pushF32(float v) {
2285     Stk& x = push();
2286     x.setF32Val(v);
2287   }
2288 
2289   // Push the local slot onto the stack.  The slot will not be read
2290   // here; it will be read when it is consumed, or when a side
2291   // effect to the slot forces its value to be saved.
2292 
pushLocalI32(uint32_t slot)2293   void pushLocalI32(uint32_t slot) {
2294     Stk& x = push();
2295     x.setSlot(Stk::LocalI32, slot);
2296   }
2297 
pushLocalI64(uint32_t slot)2298   void pushLocalI64(uint32_t slot) {
2299     Stk& x = push();
2300     x.setSlot(Stk::LocalI64, slot);
2301   }
2302 
pushLocalF64(uint32_t slot)2303   void pushLocalF64(uint32_t slot) {
2304     Stk& x = push();
2305     x.setSlot(Stk::LocalF64, slot);
2306   }
2307 
pushLocalF32(uint32_t slot)2308   void pushLocalF32(uint32_t slot) {
2309     Stk& x = push();
2310     x.setSlot(Stk::LocalF32, slot);
2311   }
2312 
2313   // Call only from other popI32() variants.
2314   // v must be the stack top.  May pop the CPU stack.
2315 
popI32(Stk & v,RegI32 dest)2316   void popI32(Stk& v, RegI32 dest) {
2317     MOZ_ASSERT(&v == &stk_.back());
2318     switch (v.kind()) {
2319       case Stk::ConstI32:
2320         loadConstI32(v, dest);
2321         break;
2322       case Stk::LocalI32:
2323         loadLocalI32(v, dest);
2324         break;
2325       case Stk::MemI32:
2326         fr.popPtr(dest);
2327         break;
2328       case Stk::RegisterI32:
2329         loadRegisterI32(v, dest);
2330         break;
2331       case Stk::None:
2332       default:
2333         MOZ_CRASH("Compiler bug: expected int on stack");
2334     }
2335   }
2336 
popI32()2337   MOZ_MUST_USE RegI32 popI32() {
2338     Stk& v = stk_.back();
2339     RegI32 r;
2340     if (v.kind() == Stk::RegisterI32)
2341       r = v.i32reg();
2342     else
2343       popI32(v, (r = needI32()));
2344     stk_.popBack();
2345     return r;
2346   }
2347 
popI32(RegI32 specific)2348   RegI32 popI32(RegI32 specific) {
2349     Stk& v = stk_.back();
2350 
2351     if (!(v.kind() == Stk::RegisterI32 && v.i32reg() == specific)) {
2352       needI32(specific);
2353       popI32(v, specific);
2354       if (v.kind() == Stk::RegisterI32) freeI32(v.i32reg());
2355     }
2356 
2357     stk_.popBack();
2358     return specific;
2359   }
2360 
2361   // Call only from other popI64() variants.
2362   // v must be the stack top.  May pop the CPU stack.
2363 
popI64(Stk & v,RegI64 dest)2364   void popI64(Stk& v, RegI64 dest) {
2365     MOZ_ASSERT(&v == &stk_.back());
2366     switch (v.kind()) {
2367       case Stk::ConstI64:
2368         loadConstI64(v, dest);
2369         break;
2370       case Stk::LocalI64:
2371         loadLocalI64(v, dest);
2372         break;
2373       case Stk::MemI64:
2374 #ifdef JS_PUNBOX64
2375         fr.popPtr(dest.reg);
2376 #else
2377         fr.popPtr(dest.low);
2378         fr.popPtr(dest.high);
2379 #endif
2380         break;
2381       case Stk::RegisterI64:
2382         loadRegisterI64(v, dest);
2383         break;
2384       case Stk::None:
2385       default:
2386         MOZ_CRASH("Compiler bug: expected long on stack");
2387     }
2388   }
2389 
popI64()2390   MOZ_MUST_USE RegI64 popI64() {
2391     Stk& v = stk_.back();
2392     RegI64 r;
2393     if (v.kind() == Stk::RegisterI64)
2394       r = v.i64reg();
2395     else
2396       popI64(v, (r = needI64()));
2397     stk_.popBack();
2398     return r;
2399   }
2400 
2401   // Note, the stack top can be in one half of "specific" on 32-bit
2402   // systems.  We can optimize, but for simplicity, if the register
2403   // does not match exactly, then just force the stack top to memory
2404   // and then read it back in.
2405 
popI64(RegI64 specific)2406   RegI64 popI64(RegI64 specific) {
2407     Stk& v = stk_.back();
2408 
2409     if (!(v.kind() == Stk::RegisterI64 && v.i64reg() == specific)) {
2410       needI64(specific);
2411       popI64(v, specific);
2412       if (v.kind() == Stk::RegisterI64) freeI64(v.i64reg());
2413     }
2414 
2415     stk_.popBack();
2416     return specific;
2417   }
2418 
2419   // Call only from other popF64() variants.
2420   // v must be the stack top.  May pop the CPU stack.
2421 
popF64(Stk & v,RegF64 dest)2422   void popF64(Stk& v, RegF64 dest) {
2423     MOZ_ASSERT(&v == &stk_.back());
2424     switch (v.kind()) {
2425       case Stk::ConstF64:
2426         loadConstF64(v, dest);
2427         break;
2428       case Stk::LocalF64:
2429         loadLocalF64(v, dest);
2430         break;
2431       case Stk::MemF64:
2432         fr.popDouble(dest);
2433         break;
2434       case Stk::RegisterF64:
2435         loadRegisterF64(v, dest);
2436         break;
2437       case Stk::None:
2438       default:
2439         MOZ_CRASH("Compiler bug: expected double on stack");
2440     }
2441   }
2442 
popF64()2443   MOZ_MUST_USE RegF64 popF64() {
2444     Stk& v = stk_.back();
2445     RegF64 r;
2446     if (v.kind() == Stk::RegisterF64)
2447       r = v.f64reg();
2448     else
2449       popF64(v, (r = needF64()));
2450     stk_.popBack();
2451     return r;
2452   }
2453 
popF64(RegF64 specific)2454   RegF64 popF64(RegF64 specific) {
2455     Stk& v = stk_.back();
2456 
2457     if (!(v.kind() == Stk::RegisterF64 && v.f64reg() == specific)) {
2458       needF64(specific);
2459       popF64(v, specific);
2460       if (v.kind() == Stk::RegisterF64) freeF64(v.f64reg());
2461     }
2462 
2463     stk_.popBack();
2464     return specific;
2465   }
2466 
2467   // Call only from other popF32() variants.
2468   // v must be the stack top.  May pop the CPU stack.
2469 
popF32(Stk & v,RegF32 dest)2470   void popF32(Stk& v, RegF32 dest) {
2471     MOZ_ASSERT(&v == &stk_.back());
2472     switch (v.kind()) {
2473       case Stk::ConstF32:
2474         loadConstF32(v, dest);
2475         break;
2476       case Stk::LocalF32:
2477         loadLocalF32(v, dest);
2478         break;
2479       case Stk::MemF32:
2480         fr.popFloat32(dest);
2481         break;
2482       case Stk::RegisterF32:
2483         loadRegisterF32(v, dest);
2484         break;
2485       case Stk::None:
2486       default:
2487         MOZ_CRASH("Compiler bug: expected float on stack");
2488     }
2489   }
2490 
popF32()2491   MOZ_MUST_USE RegF32 popF32() {
2492     Stk& v = stk_.back();
2493     RegF32 r;
2494     if (v.kind() == Stk::RegisterF32)
2495       r = v.f32reg();
2496     else
2497       popF32(v, (r = needF32()));
2498     stk_.popBack();
2499     return r;
2500   }
2501 
popF32(RegF32 specific)2502   RegF32 popF32(RegF32 specific) {
2503     Stk& v = stk_.back();
2504 
2505     if (!(v.kind() == Stk::RegisterF32 && v.f32reg() == specific)) {
2506       needF32(specific);
2507       popF32(v, specific);
2508       if (v.kind() == Stk::RegisterF32) freeF32(v.f32reg());
2509     }
2510 
2511     stk_.popBack();
2512     return specific;
2513   }
2514 
popConstI32(int32_t * c)2515   MOZ_MUST_USE bool popConstI32(int32_t* c) {
2516     Stk& v = stk_.back();
2517     if (v.kind() != Stk::ConstI32) return false;
2518     *c = v.i32val();
2519     stk_.popBack();
2520     return true;
2521   }
2522 
popConstI64(int64_t * c)2523   MOZ_MUST_USE bool popConstI64(int64_t* c) {
2524     Stk& v = stk_.back();
2525     if (v.kind() != Stk::ConstI64) return false;
2526     *c = v.i64val();
2527     stk_.popBack();
2528     return true;
2529   }
2530 
peekConstI32(int32_t * c)2531   MOZ_MUST_USE bool peekConstI32(int32_t* c) {
2532     Stk& v = stk_.back();
2533     if (v.kind() != Stk::ConstI32) return false;
2534     *c = v.i32val();
2535     return true;
2536   }
2537 
peekConstI64(int64_t * c)2538   MOZ_MUST_USE bool peekConstI64(int64_t* c) {
2539     Stk& v = stk_.back();
2540     if (v.kind() != Stk::ConstI64) return false;
2541     *c = v.i64val();
2542     return true;
2543   }
2544 
popConstPositivePowerOfTwoI32(int32_t * c,uint_fast8_t * power,int32_t cutoff)2545   MOZ_MUST_USE bool popConstPositivePowerOfTwoI32(int32_t* c,
2546                                                   uint_fast8_t* power,
2547                                                   int32_t cutoff) {
2548     Stk& v = stk_.back();
2549     if (v.kind() != Stk::ConstI32) return false;
2550     *c = v.i32val();
2551     if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint32_t>(*c))) return false;
2552     *power = FloorLog2(*c);
2553     stk_.popBack();
2554     return true;
2555   }
2556 
popConstPositivePowerOfTwoI64(int64_t * c,uint_fast8_t * power,int64_t cutoff)2557   MOZ_MUST_USE bool popConstPositivePowerOfTwoI64(int64_t* c,
2558                                                   uint_fast8_t* power,
2559                                                   int64_t cutoff) {
2560     Stk& v = stk_.back();
2561     if (v.kind() != Stk::ConstI64) return false;
2562     *c = v.i64val();
2563     if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(*c))) return false;
2564     *power = FloorLog2(*c);
2565     stk_.popBack();
2566     return true;
2567   }
2568 
peekLocalI32(uint32_t * local)2569   MOZ_MUST_USE bool peekLocalI32(uint32_t* local) {
2570     Stk& v = stk_.back();
2571     if (v.kind() != Stk::LocalI32) return false;
2572     *local = v.slot();
2573     return true;
2574   }
2575 
2576   // TODO / OPTIMIZE (Bug 1316818): At the moment we use ReturnReg
2577   // for JoinReg.  It is possible other choices would lead to better
2578   // register allocation, as ReturnReg is often first in the
2579   // register set and will be heavily wanted by the register
2580   // allocator that uses takeFirst().
2581   //
2582   // Obvious options:
2583   //  - pick a register at the back of the register set
2584   //  - pick a random register per block (different blocks have
2585   //    different join regs)
2586   //
2587   // On the other hand, we sync() before every block and only the
2588   // JoinReg is live out of the block.  But on the way out, we
2589   // currently pop the JoinReg before freeing regs to be discarded,
2590   // so there is a real risk of some pointless shuffling there.  If
2591   // we instead integrate the popping of the join reg into the
2592   // popping of the stack we can just use the JoinReg as it will
2593   // become available in that process.
2594 
popJoinRegUnlessVoid(ExprType type)2595   MOZ_MUST_USE Maybe<AnyReg> popJoinRegUnlessVoid(ExprType type) {
2596     switch (type) {
2597       case ExprType::Void: {
2598         return Nothing();
2599       }
2600       case ExprType::I32: {
2601         DebugOnly<Stk::Kind> k(stk_.back().kind());
2602         MOZ_ASSERT(k == Stk::RegisterI32 || k == Stk::ConstI32 ||
2603                    k == Stk::MemI32 || k == Stk::LocalI32);
2604         return Some(AnyReg(popI32(joinRegI32)));
2605       }
2606       case ExprType::I64: {
2607         DebugOnly<Stk::Kind> k(stk_.back().kind());
2608         MOZ_ASSERT(k == Stk::RegisterI64 || k == Stk::ConstI64 ||
2609                    k == Stk::MemI64 || k == Stk::LocalI64);
2610         return Some(AnyReg(popI64(joinRegI64)));
2611       }
2612       case ExprType::F64: {
2613         DebugOnly<Stk::Kind> k(stk_.back().kind());
2614         MOZ_ASSERT(k == Stk::RegisterF64 || k == Stk::ConstF64 ||
2615                    k == Stk::MemF64 || k == Stk::LocalF64);
2616         return Some(AnyReg(popF64(joinRegF64)));
2617       }
2618       case ExprType::F32: {
2619         DebugOnly<Stk::Kind> k(stk_.back().kind());
2620         MOZ_ASSERT(k == Stk::RegisterF32 || k == Stk::ConstF32 ||
2621                    k == Stk::MemF32 || k == Stk::LocalF32);
2622         return Some(AnyReg(popF32(joinRegF32)));
2623       }
2624       default: { MOZ_CRASH("Compiler bug: unexpected expression type"); }
2625     }
2626   }
2627 
2628   // If we ever start not sync-ing on entry to Block (but instead try to sync
2629   // lazily) then this may start asserting because it does not spill the
2630   // joinreg if the joinreg is already allocated.  Note, it *can't* spill the
2631   // joinreg in the contexts it's being used, so some other solution will need
2632   // to be found.
2633 
captureJoinRegUnlessVoid(ExprType type)2634   MOZ_MUST_USE Maybe<AnyReg> captureJoinRegUnlessVoid(ExprType type) {
2635     switch (type) {
2636       case ExprType::I32:
2637         MOZ_ASSERT(isAvailableI32(joinRegI32));
2638         needI32(joinRegI32);
2639         return Some(AnyReg(joinRegI32));
2640       case ExprType::I64:
2641         MOZ_ASSERT(isAvailableI64(joinRegI64));
2642         needI64(joinRegI64);
2643         return Some(AnyReg(joinRegI64));
2644       case ExprType::F32:
2645         MOZ_ASSERT(isAvailableF32(joinRegF32));
2646         needF32(joinRegF32);
2647         return Some(AnyReg(joinRegF32));
2648       case ExprType::F64:
2649         MOZ_ASSERT(isAvailableF64(joinRegF64));
2650         needF64(joinRegF64);
2651         return Some(AnyReg(joinRegF64));
2652       case ExprType::Void:
2653         return Nothing();
2654       default:
2655         MOZ_CRASH("Compiler bug: unexpected type");
2656     }
2657   }
2658 
pushJoinRegUnlessVoid(const Maybe<AnyReg> & r)2659   void pushJoinRegUnlessVoid(const Maybe<AnyReg>& r) {
2660     if (!r) return;
2661     switch (r->tag) {
2662       case AnyReg::I32:
2663         pushI32(r->i32());
2664         break;
2665       case AnyReg::I64:
2666         pushI64(r->i64());
2667         break;
2668       case AnyReg::F64:
2669         pushF64(r->f64());
2670         break;
2671       case AnyReg::F32:
2672         pushF32(r->f32());
2673         break;
2674     }
2675   }
2676 
freeJoinRegUnlessVoid(const Maybe<AnyReg> & r)2677   void freeJoinRegUnlessVoid(const Maybe<AnyReg>& r) {
2678     if (!r) return;
2679     switch (r->tag) {
2680       case AnyReg::I32:
2681         freeI32(r->i32());
2682         break;
2683       case AnyReg::I64:
2684         freeI64(r->i64());
2685         break;
2686       case AnyReg::F64:
2687         freeF64(r->f64());
2688         break;
2689       case AnyReg::F32:
2690         freeF32(r->f32());
2691         break;
2692     }
2693   }
2694 
2695   // Return the amount of execution stack consumed by the top numval
2696   // values on the value stack.
2697 
stackConsumed(size_t numval)2698   size_t stackConsumed(size_t numval) {
2699     size_t size = 0;
2700     MOZ_ASSERT(numval <= stk_.length());
2701     for (uint32_t i = stk_.length() - 1; numval > 0; numval--, i--) {
2702       Stk& v = stk_[i];
2703       switch (v.kind()) {
2704         case Stk::MemI32:
2705           size += BaseStackFrame::StackSizeOfPtr;
2706           break;
2707         case Stk::MemI64:
2708           size += BaseStackFrame::StackSizeOfInt64;
2709           break;
2710         case Stk::MemF64:
2711           size += BaseStackFrame::StackSizeOfDouble;
2712           break;
2713         case Stk::MemF32:
2714           size += BaseStackFrame::StackSizeOfFloat;
2715           break;
2716         default:
2717           break;
2718       }
2719     }
2720     return size;
2721   }
2722 
popValueStackTo(uint32_t stackSize)2723   void popValueStackTo(uint32_t stackSize) {
2724     for (uint32_t i = stk_.length(); i > stackSize; i--) {
2725       Stk& v = stk_[i - 1];
2726       switch (v.kind()) {
2727         case Stk::RegisterI32:
2728           freeI32(v.i32reg());
2729           break;
2730         case Stk::RegisterI64:
2731           freeI64(v.i64reg());
2732           break;
2733         case Stk::RegisterF64:
2734           freeF64(v.f64reg());
2735           break;
2736         case Stk::RegisterF32:
2737           freeF32(v.f32reg());
2738           break;
2739         default:
2740           break;
2741       }
2742     }
2743     stk_.shrinkTo(stackSize);
2744   }
2745 
popValueStackBy(uint32_t items)2746   void popValueStackBy(uint32_t items) {
2747     popValueStackTo(stk_.length() - items);
2748   }
2749 
dropValue()2750   void dropValue() {
2751     if (peek(0).isMem()) fr.popBytes(stackConsumed(1));
2752     popValueStackBy(1);
2753   }
2754 
2755   // Peek at the stack, for calls.
2756 
peek(uint32_t relativeDepth)2757   Stk& peek(uint32_t relativeDepth) {
2758     return stk_[stk_.length() - 1 - relativeDepth];
2759   }
2760 
2761 #ifdef DEBUG
2762   // Check that we're not leaking registers by comparing the
2763   // state of the stack + available registers with the set of
2764   // all available registers.
2765 
2766   // Call this between opcodes.
performRegisterLeakCheck()2767   void performRegisterLeakCheck() {
2768     BaseRegAlloc::LeakCheck check(ra);
2769     for (size_t i = 0; i < stk_.length(); i++) {
2770       Stk& item = stk_[i];
2771       switch (item.kind_) {
2772         case Stk::RegisterI32:
2773           check.addKnownI32(item.i32reg());
2774           break;
2775         case Stk::RegisterI64:
2776           check.addKnownI64(item.i64reg());
2777           break;
2778         case Stk::RegisterF32:
2779           check.addKnownF32(item.f32reg());
2780           break;
2781         case Stk::RegisterF64:
2782           check.addKnownF64(item.f64reg());
2783           break;
2784         default:
2785           break;
2786       }
2787     }
2788   }
2789 #endif
2790 
2791   ////////////////////////////////////////////////////////////
2792   //
2793   // Control stack
2794 
initControl(Control & item)2795   void initControl(Control& item) {
2796     // Make sure the constructor was run properly
2797     MOZ_ASSERT(item.stackHeight == UINT32_MAX && item.stackSize == UINT32_MAX);
2798 
2799     item.stackHeight = fr.stackHeight();
2800     item.stackSize = stk_.length();
2801     item.deadOnArrival = deadCode_;
2802     item.bceSafeOnEntry = bceSafe_;
2803   }
2804 
controlItem()2805   Control& controlItem() { return iter_.controlItem(); }
2806 
controlItem(uint32_t relativeDepth)2807   Control& controlItem(uint32_t relativeDepth) {
2808     return iter_.controlItem(relativeDepth);
2809   }
2810 
controlOutermost()2811   Control& controlOutermost() { return iter_.controlOutermost(); }
2812 
2813   ////////////////////////////////////////////////////////////
2814   //
2815   // Labels
2816 
insertBreakablePoint(CallSiteDesc::Kind kind)2817   void insertBreakablePoint(CallSiteDesc::Kind kind) {
2818     // The debug trap exit requires WasmTlsReg be loaded. However, since we
2819     // are emitting millions of these breakable points inline, we push this
2820     // loading of TLS into the FarJumpIsland created by linkCallSites.
2821     masm.nopPatchableToCall(CallSiteDesc(iter_.lastOpcodeOffset(), kind));
2822   }
2823 
2824   //////////////////////////////////////////////////////////////////////
2825   //
2826   // Function prologue and epilogue.
2827 
beginFunction()2828   void beginFunction() {
2829     JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
2830 
2831     // We are unconditionally checking for overflow in fr.allocStack(), so
2832     // pass IsLeaf = true to avoid a second check in the prologue.
2833     IsLeaf isLeaf = true;
2834     SigIdDesc sigId = env_.funcSigs[func_.index]->id;
2835     BytecodeOffset trapOffset(func_.lineOrBytecode);
2836     GenerateFunctionPrologue(
2837         masm, fr.initialSize(), isLeaf, sigId, trapOffset, &offsets_,
2838         mode_ == CompileMode::Tier1 ? Some(func_.index) : Nothing());
2839 
2840     fr.endFunctionPrologue();
2841 
2842     if (debugEnabled_) {
2843       // Initialize funcIndex and flag fields of DebugFrame.
2844       size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
2845       masm.store32(Imm32(func_.index),
2846                    Address(masm.getStackPointer(),
2847                            debugFrame + DebugFrame::offsetOfFuncIndex()));
2848       masm.storePtr(ImmWord(0),
2849                     Address(masm.getStackPointer(),
2850                             debugFrame + DebugFrame::offsetOfFlagsWord()));
2851     }
2852 
2853     fr.allocStack(ABINonArgReg0, trapOffset);
2854 
2855     // Copy arguments from registers to stack.
2856 
2857     const ValTypeVector& args = sig().args();
2858 
2859     for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
2860       Local& l = localInfo_[i.index()];
2861       switch (i.mirType()) {
2862         case MIRType::Int32:
2863           if (i->argInRegister()) fr.storeLocalI32(RegI32(i->gpr()), l);
2864           break;
2865         case MIRType::Int64:
2866           if (i->argInRegister()) fr.storeLocalI64(RegI64(i->gpr64()), l);
2867           break;
2868         case MIRType::Double:
2869           if (i->argInRegister()) fr.storeLocalF64(RegF64(i->fpu()), l);
2870           break;
2871         case MIRType::Float32:
2872           if (i->argInRegister()) fr.storeLocalF32(RegF32(i->fpu()), l);
2873           break;
2874         default:
2875           MOZ_CRASH("Function argument type");
2876       }
2877     }
2878 
2879     fr.zeroLocals(ra);
2880 
2881     if (debugEnabled_) insertBreakablePoint(CallSiteDesc::EnterFrame);
2882   }
2883 
saveResult()2884   void saveResult() {
2885     MOZ_ASSERT(debugEnabled_);
2886     size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
2887     Address resultsAddress(masm.getStackPointer(),
2888                            debugFrameOffset + DebugFrame::offsetOfResults());
2889     switch (sig().ret()) {
2890       case ExprType::Void:
2891         break;
2892       case ExprType::I32:
2893         masm.store32(RegI32(ReturnReg), resultsAddress);
2894         break;
2895       case ExprType::I64:
2896         masm.store64(RegI64(ReturnReg64), resultsAddress);
2897         break;
2898       case ExprType::F64:
2899         masm.storeDouble(RegF64(ReturnDoubleReg), resultsAddress);
2900         break;
2901       case ExprType::F32:
2902         masm.storeFloat32(RegF32(ReturnFloat32Reg), resultsAddress);
2903         break;
2904       default:
2905         MOZ_CRASH("Function return type");
2906     }
2907   }
2908 
restoreResult()2909   void restoreResult() {
2910     MOZ_ASSERT(debugEnabled_);
2911     size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
2912     Address resultsAddress(masm.getStackPointer(),
2913                            debugFrameOffset + DebugFrame::offsetOfResults());
2914     switch (sig().ret()) {
2915       case ExprType::Void:
2916         break;
2917       case ExprType::I32:
2918         masm.load32(resultsAddress, RegI32(ReturnReg));
2919         break;
2920       case ExprType::I64:
2921         masm.load64(resultsAddress, RegI64(ReturnReg64));
2922         break;
2923       case ExprType::F64:
2924         masm.loadDouble(resultsAddress, RegF64(ReturnDoubleReg));
2925         break;
2926       case ExprType::F32:
2927         masm.loadFloat32(resultsAddress, RegF32(ReturnFloat32Reg));
2928         break;
2929       default:
2930         MOZ_CRASH("Function return type");
2931     }
2932   }
2933 
endFunction()2934   bool endFunction() {
2935     // Always branch to returnLabel_.
2936     masm.breakpoint();
2937 
2938     // Patch the add in the prologue so that it checks against the correct
2939     // frame size. Flush the constant pool in case it needs to be patched.
2940     masm.flush();
2941 
2942     // Precondition for patching.
2943     if (masm.oom()) return false;
2944 
2945     fr.patchAllocStack();
2946 
2947     masm.bind(&returnLabel_);
2948 
2949     if (debugEnabled_) {
2950       // Store and reload the return value from DebugFrame::return so that
2951       // it can be clobbered, and/or modified by the debug trap.
2952       saveResult();
2953       insertBreakablePoint(CallSiteDesc::Breakpoint);
2954       insertBreakablePoint(CallSiteDesc::LeaveFrame);
2955       restoreResult();
2956     }
2957 
2958     GenerateFunctionEpilogue(masm, fr.initialSize(), &offsets_);
2959 
2960 #if defined(JS_ION_PERF)
2961     // FIXME - profiling code missing.  No bug for this.
2962 
2963     // Note the end of the inline code and start of the OOL code.
2964     // gen->perfSpewer().noteEndInlineCode(masm);
2965 #endif
2966 
2967     if (!generateOutOfLineCode()) return false;
2968 
2969     masm.wasmEmitOldTrapOutOfLineCode();
2970 
2971     offsets_.end = masm.currentOffset();
2972 
2973     if (!fr.checkStackHeight()) return false;
2974 
2975     return !masm.oom();
2976   }
2977 
2978   //////////////////////////////////////////////////////////////////////
2979   //
2980   // Calls.
2981 
2982   struct FunctionCall {
FunctionCalljs::wasm::BaseCompiler::FunctionCall2983     explicit FunctionCall(uint32_t lineOrBytecode)
2984         : lineOrBytecode(lineOrBytecode),
2985           reloadMachineStateAfter(false),
2986           usesSystemAbi(false),
2987 #ifdef JS_CODEGEN_ARM
2988           hardFP(true),
2989 #endif
2990           frameAlignAdjustment(0),
2991           stackArgAreaSize(0) {
2992     }
2993 
2994     uint32_t lineOrBytecode;
2995     ABIArgGenerator abi;
2996     bool reloadMachineStateAfter;
2997     bool usesSystemAbi;
2998 #ifdef JS_CODEGEN_ARM
2999     bool hardFP;
3000 #endif
3001     size_t frameAlignAdjustment;
3002     size_t stackArgAreaSize;
3003   };
3004 
beginCall(FunctionCall & call,UseABI useABI,InterModule interModule)3005   void beginCall(FunctionCall& call, UseABI useABI, InterModule interModule) {
3006     call.reloadMachineStateAfter =
3007         interModule == InterModule::True || useABI == UseABI::System;
3008     call.usesSystemAbi = useABI == UseABI::System;
3009 
3010     if (call.usesSystemAbi) {
3011     // Call-outs need to use the appropriate system ABI.
3012 #if defined(JS_CODEGEN_ARM)
3013 #if defined(JS_SIMULATOR_ARM)
3014       call.hardFP = UseHardFpABI();
3015 #elif defined(JS_CODEGEN_ARM_HARDFP)
3016       call.hardFP = true;
3017 #else
3018       call.hardFP = false;
3019 #endif
3020       call.abi.setUseHardFp(call.hardFP);
3021 #elif defined(JS_CODEGEN_MIPS32)
3022       call.abi.enforceO32ABI();
3023 #endif
3024     }
3025 
3026     // Use masm.framePushed() because the value we want here does not depend
3027     // on the height of the frame's stack area, but the actual size of the
3028     // allocated frame.
3029     call.frameAlignAdjustment = ComputeByteAlignment(
3030         masm.framePushed() + sizeof(Frame), JitStackAlignment);
3031   }
3032 
endCall(FunctionCall & call,size_t stackSpace)3033   void endCall(FunctionCall& call, size_t stackSpace) {
3034     size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
3035     fr.freeArgAreaAndPopBytes(adjustment, stackSpace);
3036 
3037     if (call.reloadMachineStateAfter) {
3038     // On x86 there are no pinned registers, so don't waste time
3039     // reloading the Tls.
3040 #ifndef JS_CODEGEN_X86
3041       masm.loadWasmTlsRegFromFrame();
3042       masm.loadWasmPinnedRegsFromTls();
3043 #endif
3044     }
3045   }
3046 
3047   // TODO / OPTIMIZE (Bug 1316821): This is expensive; let's roll the iterator
3048   // walking into the walking done for passArg.  See comments in passArg.
3049 
3050   // Note, stackArgAreaSize() must process all the arguments to get the
3051   // alignment right; the signature must therefore be the complete call
3052   // signature.
3053 
3054   template <class T>
stackArgAreaSize(const T & args)3055   size_t stackArgAreaSize(const T& args) {
3056     ABIArgIter<const T> i(args);
3057     while (!i.done()) i++;
3058     return AlignBytes(i.stackBytesConsumedSoFar(), 16u);
3059   }
3060 
startCallArgs(FunctionCall & call,size_t stackArgAreaSize)3061   void startCallArgs(FunctionCall& call, size_t stackArgAreaSize) {
3062     call.stackArgAreaSize = stackArgAreaSize;
3063 
3064     size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
3065     fr.allocArgArea(adjustment);
3066   }
3067 
reservePointerArgument(FunctionCall & call)3068   const ABIArg reservePointerArgument(FunctionCall& call) {
3069     return call.abi.next(MIRType::Pointer);
3070   }
3071 
3072   // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place.
3073   // (Or it was, until Luke wandered through, but that can be fixed again.)
3074   // I'm not saying we should manually inline it, but we could hoist the
3075   // dispatch into the caller and have type-specific implementations of
3076   // passArg: passArgI32(), etc.  Then those might be inlined, at least in PGO
3077   // builds.
3078   //
3079   // The bulk of the work here (60%) is in the next() call, though.
3080   //
3081   // Notably, since next() is so expensive, stackArgAreaSize() becomes
3082   // expensive too.
3083   //
3084   // Somehow there could be a trick here where the sequence of
3085   // argument types (read from the input stream) leads to a cached
3086   // entry for stackArgAreaSize() and for how to pass arguments...
3087   //
3088   // But at least we could reduce the cost of stackArgAreaSize() by
3089   // first reading the argument types into a (reusable) vector, then
3090   // we have the outgoing size at low cost, and then we can pass
3091   // args based on the info we read.
3092 
passArg(FunctionCall & call,ValType type,Stk & arg)3093   void passArg(FunctionCall& call, ValType type, Stk& arg) {
3094     switch (type) {
3095       case ValType::I32: {
3096         ABIArg argLoc = call.abi.next(MIRType::Int32);
3097         if (argLoc.kind() == ABIArg::Stack) {
3098           ScratchI32 scratch(*this);
3099           loadI32(arg, scratch);
3100           masm.store32(scratch, Address(masm.getStackPointer(),
3101                                         argLoc.offsetFromArgBase()));
3102         } else {
3103           loadI32(arg, RegI32(argLoc.gpr()));
3104         }
3105         break;
3106       }
3107       case ValType::I64: {
3108         ABIArg argLoc = call.abi.next(MIRType::Int64);
3109         if (argLoc.kind() == ABIArg::Stack) {
3110           ScratchI32 scratch(*this);
3111 #ifdef JS_PUNBOX64
3112           loadI64(arg, fromI32(scratch));
3113           masm.storePtr(scratch, Address(masm.getStackPointer(),
3114                                          argLoc.offsetFromArgBase()));
3115 #else
3116           loadI64Low(arg, scratch);
3117           masm.store32(scratch, LowWord(Address(masm.getStackPointer(),
3118                                                 argLoc.offsetFromArgBase())));
3119           loadI64High(arg, scratch);
3120           masm.store32(scratch, HighWord(Address(masm.getStackPointer(),
3121                                                  argLoc.offsetFromArgBase())));
3122 #endif
3123         } else {
3124           loadI64(arg, RegI64(argLoc.gpr64()));
3125         }
3126         break;
3127       }
3128       case ValType::F64: {
3129         ABIArg argLoc = call.abi.next(MIRType::Double);
3130         switch (argLoc.kind()) {
3131           case ABIArg::Stack: {
3132             ScratchF64 scratch(*this);
3133             loadF64(arg, scratch);
3134             masm.storeDouble(scratch, Address(masm.getStackPointer(),
3135                                               argLoc.offsetFromArgBase()));
3136             break;
3137           }
3138 #if defined(JS_CODEGEN_REGISTER_PAIR)
3139           case ABIArg::GPR_PAIR: {
3140 #if defined(JS_CODEGEN_ARM)
3141             ScratchF64 scratch(*this);
3142             loadF64(arg, scratch);
3143             masm.ma_vxfer(scratch, argLoc.evenGpr(), argLoc.oddGpr());
3144             break;
3145 #elif defined(JS_CODEGEN_MIPS32)
3146             ScratchF64 scratch(*this);
3147             loadF64(arg, scratch);
3148             MOZ_ASSERT(MOZ_LITTLE_ENDIAN);
3149             masm.moveFromDoubleLo(scratch, argLoc.evenGpr());
3150             masm.moveFromDoubleHi(scratch, argLoc.oddGpr());
3151             break;
3152 #else
3153             MOZ_CRASH("BaseCompiler platform hook: passArg F64 pair");
3154 #endif
3155           }
3156 #endif
3157           case ABIArg::FPU: {
3158             loadF64(arg, RegF64(argLoc.fpu()));
3159             break;
3160           }
3161           case ABIArg::GPR: {
3162             MOZ_CRASH("Unexpected parameter passing discipline");
3163           }
3164           case ABIArg::Uninitialized:
3165             MOZ_CRASH("Uninitialized ABIArg kind");
3166         }
3167         break;
3168       }
3169       case ValType::F32: {
3170         ABIArg argLoc = call.abi.next(MIRType::Float32);
3171         switch (argLoc.kind()) {
3172           case ABIArg::Stack: {
3173             ScratchF32 scratch(*this);
3174             loadF32(arg, scratch);
3175             masm.storeFloat32(scratch, Address(masm.getStackPointer(),
3176                                                argLoc.offsetFromArgBase()));
3177             break;
3178           }
3179           case ABIArg::GPR: {
3180             ScratchF32 scratch(*this);
3181             loadF32(arg, scratch);
3182             masm.moveFloat32ToGPR(scratch, argLoc.gpr());
3183             break;
3184           }
3185           case ABIArg::FPU: {
3186             loadF32(arg, RegF32(argLoc.fpu()));
3187             break;
3188           }
3189 #if defined(JS_CODEGEN_REGISTER_PAIR)
3190           case ABIArg::GPR_PAIR: {
3191             MOZ_CRASH("Unexpected parameter passing discipline");
3192           }
3193 #endif
3194           case ABIArg::Uninitialized:
3195             MOZ_CRASH("Uninitialized ABIArg kind");
3196         }
3197         break;
3198       }
3199       default:
3200         MOZ_CRASH("Function argument type");
3201     }
3202   }
3203 
callDefinition(uint32_t funcIndex,const FunctionCall & call)3204   void callDefinition(uint32_t funcIndex, const FunctionCall& call) {
3205     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Func);
3206     masm.call(desc, funcIndex);
3207   }
3208 
callSymbolic(SymbolicAddress callee,const FunctionCall & call)3209   void callSymbolic(SymbolicAddress callee, const FunctionCall& call) {
3210     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
3211     masm.call(desc, callee);
3212   }
3213 
3214   // Precondition: sync()
3215 
callIndirect(uint32_t sigIndex,Stk & indexVal,const FunctionCall & call)3216   void callIndirect(uint32_t sigIndex, Stk& indexVal,
3217                     const FunctionCall& call) {
3218     const SigWithId& sig = env_.sigs[sigIndex];
3219     MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
3220 
3221     MOZ_ASSERT(env_.tables.length() == 1);
3222     const TableDesc& table = env_.tables[0];
3223 
3224     loadI32(indexVal, RegI32(WasmTableCallIndexReg));
3225 
3226     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
3227     CalleeDesc callee = CalleeDesc::wasmTable(table, sig.id);
3228     masm.wasmCallIndirect(desc, callee, NeedsBoundsCheck(true));
3229   }
3230 
3231   // Precondition: sync()
3232 
callImport(unsigned globalDataOffset,const FunctionCall & call)3233   void callImport(unsigned globalDataOffset, const FunctionCall& call) {
3234     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
3235     CalleeDesc callee = CalleeDesc::import(globalDataOffset);
3236     masm.wasmCallImport(desc, callee);
3237   }
3238 
builtinCall(SymbolicAddress builtin,const FunctionCall & call)3239   void builtinCall(SymbolicAddress builtin, const FunctionCall& call) {
3240     callSymbolic(builtin, call);
3241   }
3242 
builtinInstanceMethodCall(SymbolicAddress builtin,const ABIArg & instanceArg,const FunctionCall & call)3243   void builtinInstanceMethodCall(SymbolicAddress builtin,
3244                                  const ABIArg& instanceArg,
3245                                  const FunctionCall& call) {
3246     // Builtin method calls assume the TLS register has been set.
3247     masm.loadWasmTlsRegFromFrame();
3248 
3249     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
3250     masm.wasmCallBuiltinInstanceMethod(desc, instanceArg, builtin);
3251   }
3252 
3253   //////////////////////////////////////////////////////////////////////
3254   //
3255   // Sundry low-level code generators.
3256 
3257   // The compiler depends on moveImm32() clearing the high bits of a 64-bit
3258   // register on 64-bit systems except MIPS64 where high bits are sign extended
3259   // from lower bits.
3260 
moveImm32(int32_t v,RegI32 dest)3261   void moveImm32(int32_t v, RegI32 dest) { masm.move32(Imm32(v), dest); }
3262 
moveImm64(int64_t v,RegI64 dest)3263   void moveImm64(int64_t v, RegI64 dest) { masm.move64(Imm64(v), dest); }
3264 
moveImmF32(float f,RegF32 dest)3265   void moveImmF32(float f, RegF32 dest) { masm.loadConstantFloat32(f, dest); }
3266 
moveImmF64(double d,RegF64 dest)3267   void moveImmF64(double d, RegF64 dest) { masm.loadConstantDouble(d, dest); }
3268 
addInterruptCheck()3269   void addInterruptCheck() {
3270     // Always use signals for interrupts with Asm.JS/Wasm
3271     MOZ_RELEASE_ASSERT(HaveSignalHandlers());
3272   }
3273 
jumpTable(const LabelVector & labels,Label * theTable)3274   void jumpTable(const LabelVector& labels, Label* theTable) {
3275     // Flush constant pools to ensure that the table is never interrupted by
3276     // constant pool entries.
3277     masm.flush();
3278 
3279     masm.bind(theTable);
3280 
3281     for (uint32_t i = 0; i < labels.length(); i++) {
3282       CodeLabel cl;
3283       masm.writeCodePointer(&cl);
3284       cl.target()->bind(labels[i].offset());
3285       masm.addCodeLabel(cl);
3286     }
3287   }
3288 
tableSwitch(Label * theTable,RegI32 switchValue,Label * dispatchCode)3289   void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode) {
3290     masm.bind(dispatchCode);
3291 
3292 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3293     ScratchI32 scratch(*this);
3294     CodeLabel tableCl;
3295 
3296     masm.mov(&tableCl, scratch);
3297 
3298     tableCl.target()->bind(theTable->offset());
3299     masm.addCodeLabel(tableCl);
3300 
3301     masm.jmp(Operand(scratch, switchValue, ScalePointer));
3302 #elif defined(JS_CODEGEN_ARM)
3303     // Flush constant pools: offset must reflect the distance from the MOV
3304     // to the start of the table; as the address of the MOV is given by the
3305     // label, nothing must come between the bind() and the ma_mov().
3306     masm.flush();
3307 
3308     ScratchI32 scratch(*this);
3309 
3310     // Compute the offset from the ma_mov instruction to the jump table.
3311     Label here;
3312     masm.bind(&here);
3313     uint32_t offset = here.offset() - theTable->offset();
3314 
3315     // Read PC+8
3316     masm.ma_mov(pc, scratch);
3317 
3318     // ARM scratch register is required by ma_sub.
3319     ScratchRegisterScope arm_scratch(*this);
3320 
3321     // Compute the absolute table base pointer into `scratch`, offset by 8
3322     // to account for the fact that ma_mov read PC+8.
3323     masm.ma_sub(Imm32(offset + 8), scratch, arm_scratch);
3324 
3325     // Jump indirect via table element.
3326     masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc,
3327                 Offset, Assembler::Always);
3328 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
3329     ScratchI32 scratch(*this);
3330     CodeLabel tableCl;
3331 
3332     masm.ma_li(scratch, &tableCl);
3333 
3334     tableCl.target()->bind(theTable->offset());
3335     masm.addCodeLabel(tableCl);
3336 
3337     masm.branchToComputedAddress(BaseIndex(scratch, switchValue, ScalePointer));
3338 #else
3339     MOZ_CRASH("BaseCompiler platform hook: tableSwitch");
3340 #endif
3341   }
3342 
captureReturnedI32()3343   RegI32 captureReturnedI32() {
3344     RegI32 r = RegI32(ReturnReg);
3345     MOZ_ASSERT(isAvailableI32(r));
3346     needI32(r);
3347     return r;
3348   }
3349 
captureReturnedI64()3350   RegI64 captureReturnedI64() {
3351     RegI64 r = RegI64(ReturnReg64);
3352     MOZ_ASSERT(isAvailableI64(r));
3353     needI64(r);
3354     return r;
3355   }
3356 
captureReturnedF32(const FunctionCall & call)3357   RegF32 captureReturnedF32(const FunctionCall& call) {
3358     RegF32 r = RegF32(ReturnFloat32Reg);
3359     MOZ_ASSERT(isAvailableF32(r));
3360     needF32(r);
3361 #if defined(JS_CODEGEN_ARM)
3362     if (call.usesSystemAbi && !call.hardFP) masm.ma_vxfer(ReturnReg, r);
3363 #endif
3364     return r;
3365   }
3366 
captureReturnedF64(const FunctionCall & call)3367   RegF64 captureReturnedF64(const FunctionCall& call) {
3368     RegF64 r = RegF64(ReturnDoubleReg);
3369     MOZ_ASSERT(isAvailableF64(r));
3370     needF64(r);
3371 #if defined(JS_CODEGEN_ARM)
3372     if (call.usesSystemAbi && !call.hardFP)
3373       masm.ma_vxfer(ReturnReg64.low, ReturnReg64.high, r);
3374 #endif
3375     return r;
3376   }
3377 
returnCleanup(bool popStack)3378   void returnCleanup(bool popStack) {
3379     if (popStack) fr.popStackBeforeBranch(controlOutermost().stackHeight);
3380     masm.jump(&returnLabel_);
3381   }
3382 
checkDivideByZeroI32(RegI32 rhs,RegI32 srcDest,Label * done)3383   void checkDivideByZeroI32(RegI32 rhs, RegI32 srcDest, Label* done) {
3384     Label nonZero;
3385     masm.branchTest32(Assembler::NonZero, rhs, rhs, &nonZero);
3386     trap(Trap::IntegerDivideByZero);
3387     masm.bind(&nonZero);
3388   }
3389 
checkDivideByZeroI64(RegI64 r)3390   void checkDivideByZeroI64(RegI64 r) {
3391     Label nonZero;
3392     ScratchI32 scratch(*this);
3393     masm.branchTest64(Assembler::NonZero, r, r, scratch, &nonZero);
3394     trap(Trap::IntegerDivideByZero);
3395     masm.bind(&nonZero);
3396   }
3397 
checkDivideSignedOverflowI32(RegI32 rhs,RegI32 srcDest,Label * done,bool zeroOnOverflow)3398   void checkDivideSignedOverflowI32(RegI32 rhs, RegI32 srcDest, Label* done,
3399                                     bool zeroOnOverflow) {
3400     Label notMin;
3401     masm.branch32(Assembler::NotEqual, srcDest, Imm32(INT32_MIN), &notMin);
3402     if (zeroOnOverflow) {
3403       masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notMin);
3404       moveImm32(0, srcDest);
3405       masm.jump(done);
3406     } else {
3407       masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notMin);
3408       trap(Trap::IntegerOverflow);
3409     }
3410     masm.bind(&notMin);
3411   }
3412 
checkDivideSignedOverflowI64(RegI64 rhs,RegI64 srcDest,Label * done,bool zeroOnOverflow)3413   void checkDivideSignedOverflowI64(RegI64 rhs, RegI64 srcDest, Label* done,
3414                                     bool zeroOnOverflow) {
3415     Label notmin;
3416     masm.branch64(Assembler::NotEqual, srcDest, Imm64(INT64_MIN), &notmin);
3417     masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), &notmin);
3418     if (zeroOnOverflow) {
3419       masm.xor64(srcDest, srcDest);
3420       masm.jump(done);
3421     } else {
3422       trap(Trap::IntegerOverflow);
3423     }
3424     masm.bind(&notmin);
3425   }
3426 
3427 #ifndef RABALDR_INT_DIV_I64_CALLOUT
quotientI64(RegI64 rhs,RegI64 srcDest,RegI64 reserved,IsUnsigned isUnsigned,bool isConst,int64_t c)3428   void quotientI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved,
3429                    IsUnsigned isUnsigned, bool isConst, int64_t c) {
3430     Label done;
3431 
3432     if (!isConst || c == 0) checkDivideByZeroI64(rhs);
3433 
3434     if (!isUnsigned && (!isConst || c == -1))
3435       checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
3436 
3437 #if defined(JS_CODEGEN_X64)
3438     // The caller must set up the following situation.
3439     MOZ_ASSERT(srcDest.reg == rax);
3440     MOZ_ASSERT(reserved == specific.rdx);
3441     if (isUnsigned) {
3442       masm.xorq(rdx, rdx);
3443       masm.udivq(rhs.reg);
3444     } else {
3445       masm.cqo();
3446       masm.idivq(rhs.reg);
3447     }
3448 #elif defined(JS_CODEGEN_MIPS64)
3449     if (isUnsigned)
3450       masm.as_ddivu(srcDest.reg, rhs.reg);
3451     else
3452       masm.as_ddiv(srcDest.reg, rhs.reg);
3453     masm.as_mflo(srcDest.reg);
3454 #else
3455     MOZ_CRASH("BaseCompiler platform hook: quotientI64");
3456 #endif
3457     masm.bind(&done);
3458   }
3459 
remainderI64(RegI64 rhs,RegI64 srcDest,RegI64 reserved,IsUnsigned isUnsigned,bool isConst,int64_t c)3460   void remainderI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved,
3461                     IsUnsigned isUnsigned, bool isConst, int64_t c) {
3462     Label done;
3463 
3464     if (!isConst || c == 0) checkDivideByZeroI64(rhs);
3465 
3466     if (!isUnsigned && (!isConst || c == -1))
3467       checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
3468 
3469 #if defined(JS_CODEGEN_X64)
3470     // The caller must set up the following situation.
3471     MOZ_ASSERT(srcDest.reg == rax);
3472     MOZ_ASSERT(reserved == specific.rdx);
3473 
3474     if (isUnsigned) {
3475       masm.xorq(rdx, rdx);
3476       masm.udivq(rhs.reg);
3477     } else {
3478       masm.cqo();
3479       masm.idivq(rhs.reg);
3480     }
3481     masm.movq(rdx, rax);
3482 #elif defined(JS_CODEGEN_MIPS64)
3483     if (isUnsigned)
3484       masm.as_ddivu(srcDest.reg, rhs.reg);
3485     else
3486       masm.as_ddiv(srcDest.reg, rhs.reg);
3487     masm.as_mfhi(srcDest.reg);
3488 #else
3489     MOZ_CRASH("BaseCompiler platform hook: remainderI64");
3490 #endif
3491     masm.bind(&done);
3492   }
3493 #endif  // RABALDR_INT_DIV_I64_CALLOUT
3494 
needRotate64Temp()3495   RegI32 needRotate64Temp() {
3496 #if defined(JS_CODEGEN_X86)
3497     return needI32();
3498 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \
3499     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
3500     return RegI32::Invalid();
3501 #else
3502     MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp");
3503 #endif
3504   }
3505 
maskShiftCount32(RegI32 r)3506   void maskShiftCount32(RegI32 r) {
3507 #if defined(JS_CODEGEN_ARM)
3508     masm.and32(Imm32(31), r);
3509 #endif
3510   }
3511 
needPopcnt32Temp()3512   RegI32 needPopcnt32Temp() {
3513 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3514     return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : needI32();
3515 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
3516     defined(JS_CODEGEN_MIPS64)
3517     return needI32();
3518 #else
3519     MOZ_CRASH("BaseCompiler platform hook: needPopcnt32Temp");
3520 #endif
3521   }
3522 
needPopcnt64Temp()3523   RegI32 needPopcnt64Temp() {
3524 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3525     return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : needI32();
3526 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
3527     defined(JS_CODEGEN_MIPS64)
3528     return needI32();
3529 #else
3530     MOZ_CRASH("BaseCompiler platform hook: needPopcnt64Temp");
3531 #endif
3532   }
3533 
3534   class OutOfLineTruncateCheckF32OrF64ToI32 : public OutOfLineCode {
3535     AnyReg src;
3536     RegI32 dest;
3537     TruncFlags flags;
3538     BytecodeOffset off;
3539 
3540    public:
OutOfLineTruncateCheckF32OrF64ToI32(AnyReg src,RegI32 dest,TruncFlags flags,BytecodeOffset off)3541     OutOfLineTruncateCheckF32OrF64ToI32(AnyReg src, RegI32 dest,
3542                                         TruncFlags flags, BytecodeOffset off)
3543         : src(src), dest(dest), flags(flags), off(off) {}
3544 
generate(MacroAssembler * masm)3545     virtual void generate(MacroAssembler* masm) override {
3546       if (src.tag == AnyReg::F32)
3547         masm->oolWasmTruncateCheckF32ToI32(src.f32(), dest, flags, off,
3548                                            rejoin());
3549       else if (src.tag == AnyReg::F64)
3550         masm->oolWasmTruncateCheckF64ToI32(src.f64(), dest, flags, off,
3551                                            rejoin());
3552       else
3553         MOZ_CRASH("unexpected type");
3554     }
3555   };
3556 
truncateF32ToI32(RegF32 src,RegI32 dest,TruncFlags flags)3557   MOZ_MUST_USE bool truncateF32ToI32(RegF32 src, RegI32 dest,
3558                                      TruncFlags flags) {
3559     BytecodeOffset off = bytecodeOffset();
3560     OutOfLineCode* ool =
3561         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32(
3562             AnyReg(src), dest, flags, off));
3563     if (!ool) return false;
3564     bool isSaturating = flags & TRUNC_SATURATING;
3565     if (flags & TRUNC_UNSIGNED)
3566       masm.wasmTruncateFloat32ToUInt32(src, dest, isSaturating, ool->entry());
3567     else
3568       masm.wasmTruncateFloat32ToInt32(src, dest, isSaturating, ool->entry());
3569     masm.bind(ool->rejoin());
3570     return true;
3571   }
3572 
truncateF64ToI32(RegF64 src,RegI32 dest,TruncFlags flags)3573   MOZ_MUST_USE bool truncateF64ToI32(RegF64 src, RegI32 dest,
3574                                      TruncFlags flags) {
3575     BytecodeOffset off = bytecodeOffset();
3576     OutOfLineCode* ool =
3577         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI32(
3578             AnyReg(src), dest, flags, off));
3579     if (!ool) return false;
3580     bool isSaturating = flags & TRUNC_SATURATING;
3581     if (flags & TRUNC_UNSIGNED)
3582       masm.wasmTruncateDoubleToUInt32(src, dest, isSaturating, ool->entry());
3583     else
3584       masm.wasmTruncateDoubleToInt32(src, dest, isSaturating, ool->entry());
3585     masm.bind(ool->rejoin());
3586     return true;
3587   }
3588 
3589   class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode {
3590     AnyReg src;
3591     RegI64 dest;
3592     TruncFlags flags;
3593     BytecodeOffset off;
3594 
3595    public:
OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src,RegI64 dest,TruncFlags flags,BytecodeOffset off)3596     OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src, RegI64 dest,
3597                                         TruncFlags flags, BytecodeOffset off)
3598         : src(src), dest(dest), flags(flags), off(off) {}
3599 
generate(MacroAssembler * masm)3600     virtual void generate(MacroAssembler* masm) override {
3601       if (src.tag == AnyReg::F32)
3602         masm->oolWasmTruncateCheckF32ToI64(src.f32(), dest, flags, off,
3603                                            rejoin());
3604       else if (src.tag == AnyReg::F64)
3605         masm->oolWasmTruncateCheckF64ToI64(src.f64(), dest, flags, off,
3606                                            rejoin());
3607       else
3608         MOZ_CRASH("unexpected type");
3609     }
3610   };
3611 
3612 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT
needTempForFloatingToI64(TruncFlags flags)3613   MOZ_MUST_USE RegF64 needTempForFloatingToI64(TruncFlags flags) {
3614 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3615     if (flags & TRUNC_UNSIGNED) return needF64();
3616 #endif
3617     return RegF64::Invalid();
3618   }
3619 
truncateF32ToI64(RegF32 src,RegI64 dest,TruncFlags flags,RegF64 temp)3620   MOZ_MUST_USE bool truncateF32ToI64(RegF32 src, RegI64 dest, TruncFlags flags,
3621                                      RegF64 temp) {
3622     OutOfLineCode* ool =
3623         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
3624             AnyReg(src), dest, flags, bytecodeOffset()));
3625     if (!ool) return false;
3626     bool isSaturating = flags & TRUNC_SATURATING;
3627     if (flags & TRUNC_UNSIGNED) {
3628       masm.wasmTruncateFloat32ToUInt64(src, dest, isSaturating, ool->entry(),
3629                                        ool->rejoin(), temp);
3630     } else {
3631       masm.wasmTruncateFloat32ToInt64(src, dest, isSaturating, ool->entry(),
3632                                       ool->rejoin(), temp);
3633     }
3634     return true;
3635   }
3636 
truncateF64ToI64(RegF64 src,RegI64 dest,TruncFlags flags,RegF64 temp)3637   MOZ_MUST_USE bool truncateF64ToI64(RegF64 src, RegI64 dest, TruncFlags flags,
3638                                      RegF64 temp) {
3639     OutOfLineCode* ool =
3640         addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
3641             AnyReg(src), dest, flags, bytecodeOffset()));
3642     if (!ool) return false;
3643     bool isSaturating = flags & TRUNC_SATURATING;
3644     if (flags & TRUNC_UNSIGNED) {
3645       masm.wasmTruncateDoubleToUInt64(src, dest, isSaturating, ool->entry(),
3646                                       ool->rejoin(), temp);
3647     } else {
3648       masm.wasmTruncateDoubleToInt64(src, dest, isSaturating, ool->entry(),
3649                                      ool->rejoin(), temp);
3650     }
3651     return true;
3652   }
3653 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
3654 
3655 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
needConvertI64ToFloatTemp(ValType to,bool isUnsigned)3656   RegI32 needConvertI64ToFloatTemp(ValType to, bool isUnsigned) {
3657     bool needs = false;
3658     if (to == ValType::F64) {
3659       needs = isUnsigned && masm.convertUInt64ToDoubleNeedsTemp();
3660     } else {
3661 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3662       needs = true;
3663 #endif
3664     }
3665     return needs ? needI32() : RegI32::Invalid();
3666   }
3667 
convertI64ToF32(RegI64 src,bool isUnsigned,RegF32 dest,RegI32 temp)3668   void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp) {
3669     if (isUnsigned)
3670       masm.convertUInt64ToFloat32(src, dest, temp);
3671     else
3672       masm.convertInt64ToFloat32(src, dest);
3673   }
3674 
convertI64ToF64(RegI64 src,bool isUnsigned,RegF64 dest,RegI32 temp)3675   void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp) {
3676     if (isUnsigned)
3677       masm.convertUInt64ToDouble(src, dest, temp);
3678     else
3679       masm.convertInt64ToDouble(src, dest);
3680   }
3681 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
3682 
cmp64Set(Assembler::Condition cond,RegI64 lhs,RegI64 rhs,RegI32 dest)3683   void cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs,
3684                 RegI32 dest) {
3685 #if defined(JS_PUNBOX64)
3686     masm.cmpPtrSet(cond, lhs.reg, rhs.reg, dest);
3687 #elif defined(JS_CODEGEN_MIPS32)
3688     masm.cmp64Set(cond, lhs, rhs, dest);
3689 #else
3690     // TODO / OPTIMIZE (Bug 1316822): This is pretty branchy, we should be
3691     // able to do better.
3692     Label done, condTrue;
3693     masm.branch64(cond, lhs, rhs, &condTrue);
3694     moveImm32(0, dest);
3695     masm.jump(&done);
3696     masm.bind(&condTrue);
3697     moveImm32(1, dest);
3698     masm.bind(&done);
3699 #endif
3700   }
3701 
eqz64(RegI64 src,RegI32 dest)3702   void eqz64(RegI64 src, RegI32 dest) {
3703 #ifdef JS_PUNBOX64
3704     masm.cmpPtrSet(Assembler::Equal, src.reg, ImmWord(0), dest);
3705 #else
3706     masm.or32(src.high, src.low);
3707     masm.cmp32Set(Assembler::Equal, src.low, Imm32(0), dest);
3708 #endif
3709   }
3710 
supportsRoundInstruction(RoundingMode mode)3711   MOZ_MUST_USE bool supportsRoundInstruction(RoundingMode mode) {
3712 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3713     return Assembler::HasRoundInstruction(mode);
3714 #else
3715     return false;
3716 #endif
3717   }
3718 
roundF32(RoundingMode roundingMode,RegF32 f0)3719   void roundF32(RoundingMode roundingMode, RegF32 f0) {
3720 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3721     masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
3722 #else
3723     MOZ_CRASH("NYI");
3724 #endif
3725   }
3726 
roundF64(RoundingMode roundingMode,RegF64 f0)3727   void roundF64(RoundingMode roundingMode, RegF64 f0) {
3728 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3729     masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
3730 #else
3731     MOZ_CRASH("NYI");
3732 #endif
3733   }
3734 
3735   //////////////////////////////////////////////////////////////////////
3736   //
3737   // Global variable access.
3738 
globalToTlsOffset(uint32_t globalOffset)3739   uint32_t globalToTlsOffset(uint32_t globalOffset) {
3740     return offsetof(TlsData, globalArea) + globalOffset;
3741   }
3742 
loadGlobalVarI32(unsigned globalDataOffset,RegI32 r)3743   void loadGlobalVarI32(unsigned globalDataOffset, RegI32 r) {
3744     ScratchI32 tmp(*this);
3745     masm.loadWasmTlsRegFromFrame(tmp);
3746     masm.load32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3747   }
3748 
loadGlobalVarI64(unsigned globalDataOffset,RegI64 r)3749   void loadGlobalVarI64(unsigned globalDataOffset, RegI64 r) {
3750     ScratchI32 tmp(*this);
3751     masm.loadWasmTlsRegFromFrame(tmp);
3752     masm.load64(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3753   }
3754 
loadGlobalVarF32(unsigned globalDataOffset,RegF32 r)3755   void loadGlobalVarF32(unsigned globalDataOffset, RegF32 r) {
3756     ScratchI32 tmp(*this);
3757     masm.loadWasmTlsRegFromFrame(tmp);
3758     masm.loadFloat32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3759   }
3760 
loadGlobalVarF64(unsigned globalDataOffset,RegF64 r)3761   void loadGlobalVarF64(unsigned globalDataOffset, RegF64 r) {
3762     ScratchI32 tmp(*this);
3763     masm.loadWasmTlsRegFromFrame(tmp);
3764     masm.loadDouble(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3765   }
3766 
storeGlobalVarI32(unsigned globalDataOffset,RegI32 r)3767   void storeGlobalVarI32(unsigned globalDataOffset, RegI32 r) {
3768     ScratchI32 tmp(*this);
3769     masm.loadWasmTlsRegFromFrame(tmp);
3770     masm.store32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3771   }
3772 
storeGlobalVarI64(unsigned globalDataOffset,RegI64 r)3773   void storeGlobalVarI64(unsigned globalDataOffset, RegI64 r) {
3774     ScratchI32 tmp(*this);
3775     masm.loadWasmTlsRegFromFrame(tmp);
3776     masm.store64(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3777   }
3778 
storeGlobalVarF32(unsigned globalDataOffset,RegF32 r)3779   void storeGlobalVarF32(unsigned globalDataOffset, RegF32 r) {
3780     ScratchI32 tmp(*this);
3781     masm.loadWasmTlsRegFromFrame(tmp);
3782     masm.storeFloat32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3783   }
3784 
storeGlobalVarF64(unsigned globalDataOffset,RegF64 r)3785   void storeGlobalVarF64(unsigned globalDataOffset, RegF64 r) {
3786     ScratchI32 tmp(*this);
3787     masm.loadWasmTlsRegFromFrame(tmp);
3788     masm.storeDouble(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3789   }
3790 
3791   //////////////////////////////////////////////////////////////////////
3792   //
3793   // Heap access.
3794 
bceCheckLocal(MemoryAccessDesc * access,AccessCheck * check,uint32_t local)3795   void bceCheckLocal(MemoryAccessDesc* access, AccessCheck* check,
3796                      uint32_t local) {
3797     if (local >= sizeof(BCESet) * 8) return;
3798 
3799     if ((bceSafe_ & (BCESet(1) << local)) &&
3800         access->offset() < wasm::OffsetGuardLimit)
3801       check->omitBoundsCheck = true;
3802 
3803     // The local becomes safe even if the offset is beyond the guard limit.
3804     bceSafe_ |= (BCESet(1) << local);
3805   }
3806 
bceLocalIsUpdated(uint32_t local)3807   void bceLocalIsUpdated(uint32_t local) {
3808     if (local >= sizeof(BCESet) * 8) return;
3809 
3810     bceSafe_ &= ~(BCESet(1) << local);
3811   }
3812 
prepareMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)3813   void prepareMemoryAccess(MemoryAccessDesc* access, AccessCheck* check,
3814                            RegI32 tls, RegI32 ptr) {
3815     // Fold offset if necessary for further computations.
3816 
3817     if (access->offset() >= OffsetGuardLimit ||
3818         (access->isAtomic() && !check->omitAlignmentCheck &&
3819          !check->onlyPointerAlignment)) {
3820       masm.branchAdd32(Assembler::CarrySet, Imm32(access->offset()), ptr,
3821                        oldTrap(Trap::OutOfBounds));
3822       access->clearOffset();
3823       check->onlyPointerAlignment = true;
3824     }
3825 
3826     // Alignment check if required.
3827 
3828     if (access->isAtomic() && !check->omitAlignmentCheck) {
3829       MOZ_ASSERT(check->onlyPointerAlignment);
3830       // We only care about the low pointer bits here.
3831       masm.branchTest32(Assembler::NonZero, ptr, Imm32(access->byteSize() - 1),
3832                         oldTrap(Trap::UnalignedAccess));
3833     }
3834 
3835       // Ensure no tls if we don't need it.
3836 
3837 #ifdef WASM_HUGE_MEMORY
3838     // We have HeapReg and no bounds checking and need load neither
3839     // memoryBase nor boundsCheckLimit from tls.
3840     MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
3841 #endif
3842 #ifdef JS_CODEGEN_ARM
3843     // We have HeapReg on ARM and don't need to load the memoryBase from tls.
3844     MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
3845 #endif
3846 
3847     // Bounds check if required.
3848 
3849 #ifndef WASM_HUGE_MEMORY
3850     if (!check->omitBoundsCheck) {
3851       masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr,
3852                            Address(tls, offsetof(TlsData, boundsCheckLimit)),
3853                            oldTrap(Trap::OutOfBounds));
3854     }
3855 #endif
3856   }
3857 
3858 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \
3859     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)3860   BaseIndex prepareAtomicMemoryAccess(MemoryAccessDesc* access,
3861                                       AccessCheck* check, RegI32 tls,
3862                                       RegI32 ptr) {
3863     MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid());
3864     prepareMemoryAccess(access, check, tls, ptr);
3865     return BaseIndex(HeapReg, ptr, TimesOne, access->offset());
3866   }
3867 #elif defined(JS_CODEGEN_X86)
3868   // Some consumers depend on the address not retaining tls, as tls may be the
3869   // scratch register.
3870 
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)3871   Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
3872                                     AccessCheck* check, RegI32 tls,
3873                                     RegI32 ptr) {
3874     MOZ_ASSERT(needTlsForAccess(*check) == tls.isValid());
3875     prepareMemoryAccess(access, check, tls, ptr);
3876     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
3877     return Address(ptr, access->offset());
3878   }
3879 #else
prepareAtomicMemoryAccess(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr)3880   Address prepareAtomicMemoryAccess(MemoryAccessDesc* access,
3881                                     AccessCheck* check, RegI32 tls,
3882                                     RegI32 ptr) {
3883     MOZ_CRASH("BaseCompiler platform hook: prepareAtomicMemoryAccess");
3884   }
3885 #endif
3886 
needLoadTemps(const MemoryAccessDesc & access,RegI32 * temp1,RegI32 * temp2,RegI32 * temp3)3887   void needLoadTemps(const MemoryAccessDesc& access, RegI32* temp1,
3888                      RegI32* temp2, RegI32* temp3) {
3889 #if defined(JS_CODEGEN_ARM)
3890     if (IsUnaligned(access)) {
3891       switch (access.type()) {
3892         case Scalar::Float64:
3893           *temp3 = needI32();
3894           MOZ_FALLTHROUGH;
3895         case Scalar::Float32:
3896           *temp2 = needI32();
3897           MOZ_FALLTHROUGH;
3898         default:
3899           *temp1 = needI32();
3900           break;
3901       }
3902     }
3903 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
3904     *temp1 = needI32();
3905 #endif
3906   }
3907 
needTlsForAccess(const AccessCheck & check)3908   MOZ_MUST_USE bool needTlsForAccess(const AccessCheck& check) {
3909 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
3910     defined(JS_CODEGEN_MIPS64)
3911     return !check.omitBoundsCheck;
3912 #elif defined(JS_CODEGEN_X86)
3913     return true;
3914 #else
3915     return false;
3916 #endif
3917   }
3918 
3919   // ptr and dest may be the same iff dest is I32.
3920   // 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)3921   MOZ_MUST_USE bool load(MemoryAccessDesc* access, AccessCheck* check,
3922                          RegI32 tls, RegI32 ptr, AnyReg dest, RegI32 temp1,
3923                          RegI32 temp2, RegI32 temp3) {
3924     prepareMemoryAccess(access, check, tls, ptr);
3925 
3926 #if defined(JS_CODEGEN_X64)
3927     Operand srcAddr(HeapReg, ptr, TimesOne, access->offset());
3928 
3929     if (dest.tag == AnyReg::I64)
3930       masm.wasmLoadI64(*access, srcAddr, dest.i64());
3931     else
3932       masm.wasmLoad(*access, srcAddr, dest.any());
3933 #elif defined(JS_CODEGEN_X86)
3934     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
3935     Operand srcAddr(ptr, access->offset());
3936 
3937     if (dest.tag == AnyReg::I64) {
3938       MOZ_ASSERT(dest.i64() == specific.abiReturnRegI64);
3939       masm.wasmLoadI64(*access, srcAddr, dest.i64());
3940     } else {
3941       ScratchI8 scratch(*this);
3942       bool byteRegConflict =
3943           access->byteSize() == 1 && !ra.isSingleByteI32(dest.i32());
3944       AnyRegister out = byteRegConflict ? AnyRegister(scratch) : dest.any();
3945 
3946       masm.wasmLoad(*access, srcAddr, out);
3947 
3948       if (byteRegConflict) masm.mov(scratch, dest.i32());
3949     }
3950 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
3951     defined(JS_CODEGEN_MIPS64)
3952     if (IsUnaligned(*access)) {
3953       switch (dest.tag) {
3954         case AnyReg::I64:
3955           masm.wasmUnalignedLoadI64(*access, HeapReg, ptr, ptr, dest.i64(),
3956                                     temp1);
3957           break;
3958         case AnyReg::F32:
3959           masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f32(),
3960                                    temp1, temp2, RegI32::Invalid());
3961           break;
3962         case AnyReg::F64:
3963           masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f64(),
3964                                    temp1, temp2, temp3);
3965           break;
3966         default:
3967           masm.wasmUnalignedLoad(*access, HeapReg, ptr, ptr, dest.i32(), temp1);
3968           break;
3969       }
3970     } else {
3971       if (dest.tag == AnyReg::I64)
3972         masm.wasmLoadI64(*access, HeapReg, ptr, ptr, dest.i64());
3973       else
3974         masm.wasmLoad(*access, HeapReg, ptr, ptr, dest.any());
3975     }
3976 #else
3977     MOZ_CRASH("BaseCompiler platform hook: load");
3978 #endif
3979 
3980     return true;
3981   }
3982 
needStoreTemp(const MemoryAccessDesc & access,ValType srcType)3983   RegI32 needStoreTemp(const MemoryAccessDesc& access, ValType srcType) {
3984 #if defined(JS_CODEGEN_ARM)
3985     if (IsUnaligned(access) && srcType != ValType::I32) return needI32();
3986 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
3987     return needI32();
3988 #endif
3989     return RegI32::Invalid();
3990   }
3991 
3992   // ptr and src must not be the same register.
3993   // This may destroy ptr and src.
store(MemoryAccessDesc * access,AccessCheck * check,RegI32 tls,RegI32 ptr,AnyReg src,RegI32 temp)3994   MOZ_MUST_USE bool store(MemoryAccessDesc* access, AccessCheck* check,
3995                           RegI32 tls, RegI32 ptr, AnyReg src, RegI32 temp) {
3996     prepareMemoryAccess(access, check, tls, ptr);
3997 
3998     // Emit the store
3999 #if defined(JS_CODEGEN_X64)
4000     MOZ_ASSERT(temp.isInvalid());
4001     Operand dstAddr(HeapReg, ptr, TimesOne, access->offset());
4002 
4003     masm.wasmStore(*access, src.any(), dstAddr);
4004 #elif defined(JS_CODEGEN_X86)
4005     MOZ_ASSERT(temp.isInvalid());
4006     masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
4007     Operand dstAddr(ptr, access->offset());
4008 
4009     if (access->type() == Scalar::Int64) {
4010       masm.wasmStoreI64(*access, src.i64(), dstAddr);
4011     } else {
4012       AnyRegister value;
4013       ScratchI8 scratch(*this);
4014       if (src.tag == AnyReg::I64) {
4015         if (access->byteSize() == 1 && !ra.isSingleByteI32(src.i64().low)) {
4016           masm.mov(src.i64().low, scratch);
4017           value = AnyRegister(scratch);
4018         } else {
4019           value = AnyRegister(src.i64().low);
4020         }
4021       } else if (access->byteSize() == 1 && !ra.isSingleByteI32(src.i32())) {
4022         masm.mov(src.i32(), scratch);
4023         value = AnyRegister(scratch);
4024       } else {
4025         value = src.any();
4026       }
4027 
4028       masm.wasmStore(*access, value, dstAddr);
4029     }
4030 #elif defined(JS_CODEGEN_ARM)
4031     if (IsUnaligned(*access)) {
4032       switch (src.tag) {
4033         case AnyReg::I64:
4034           masm.wasmUnalignedStoreI64(*access, src.i64(), HeapReg, ptr, ptr,
4035                                      temp);
4036           break;
4037         case AnyReg::F32:
4038           masm.wasmUnalignedStoreFP(*access, src.f32(), HeapReg, ptr, ptr,
4039                                     temp);
4040           break;
4041         case AnyReg::F64:
4042           masm.wasmUnalignedStoreFP(*access, src.f64(), HeapReg, ptr, ptr,
4043                                     temp);
4044           break;
4045         default:
4046           MOZ_ASSERT(temp.isInvalid());
4047           masm.wasmUnalignedStore(*access, src.i32(), HeapReg, ptr, ptr, temp);
4048           break;
4049       }
4050     } else {
4051       MOZ_ASSERT(temp.isInvalid());
4052       if (access->type() == Scalar::Int64)
4053         masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr);
4054       else if (src.tag == AnyReg::I64)
4055         masm.wasmStore(*access, AnyRegister(src.i64().low), HeapReg, ptr, ptr);
4056       else
4057         masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr);
4058     }
4059 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4060     if (IsUnaligned(*access)) {
4061       switch (src.tag) {
4062         case AnyReg::I64:
4063           masm.wasmUnalignedStoreI64(*access, src.i64(), HeapReg, ptr, ptr,
4064                                      temp);
4065           break;
4066         case AnyReg::F32:
4067           masm.wasmUnalignedStoreFP(*access, src.f32(), HeapReg, ptr, ptr,
4068                                     temp);
4069           break;
4070         case AnyReg::F64:
4071           masm.wasmUnalignedStoreFP(*access, src.f64(), HeapReg, ptr, ptr,
4072                                     temp);
4073           break;
4074         default:
4075           masm.wasmUnalignedStore(*access, src.i32(), HeapReg, ptr, ptr, temp);
4076           break;
4077       }
4078     } else {
4079       if (src.tag == AnyReg::I64)
4080         masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr);
4081       else
4082         masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr);
4083     }
4084 #else
4085     MOZ_CRASH("BaseCompiler platform hook: store");
4086 #endif
4087 
4088     return true;
4089   }
4090 
4091   template <size_t Count>
4092   struct Atomic32Temps : mozilla::Array<RegI32, Count> {
4093     // Allocate all temp registers if 'allocate' is not specified.
allocatejs::wasm::BaseCompiler::Atomic32Temps4094     void allocate(BaseCompiler* bc, size_t allocate = Count) {
4095       MOZ_ASSERT(Count != 0);
4096       for (size_t i = 0; i < allocate; ++i) this->operator[](i) = bc->needI32();
4097     }
maybeFreejs::wasm::BaseCompiler::Atomic32Temps4098     void maybeFree(BaseCompiler* bc) {
4099       for (size_t i = 0; i < Count; ++i) bc->maybeFreeI32(this->operator[](i));
4100     }
4101   };
4102 
4103 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4104   typedef Atomic32Temps<3> AtomicRMW32Temps;
4105 #else
4106   typedef Atomic32Temps<1> AtomicRMW32Temps;
4107 #endif
4108 
4109   template <typename T>
atomicRMW32(T srcAddr,Scalar::Type viewType,AtomicOp op,RegI32 rv,RegI32 rd,const AtomicRMW32Temps & temps)4110   void atomicRMW32(T srcAddr, Scalar::Type viewType, AtomicOp op, RegI32 rv,
4111                    RegI32 rd, const AtomicRMW32Temps& temps) {
4112     Synchronization sync = Synchronization::Full();
4113     switch (viewType) {
4114       case Scalar::Uint8:
4115 #ifdef JS_CODEGEN_X86
4116       {
4117         RegI32 temp = temps[0];
4118         // The temp, if used, must be a byte register.
4119         MOZ_ASSERT(temp.isInvalid());
4120         ScratchI8 scratch(*this);
4121         if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) temp = scratch;
4122         masm.atomicFetchOp(viewType, sync, op, rv, srcAddr, temp, rd);
4123         break;
4124       }
4125 #endif
4126       case Scalar::Uint16:
4127       case Scalar::Int32:
4128       case Scalar::Uint32:
4129 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4130         masm.atomicFetchOp(viewType, sync, op, rv, srcAddr, temps[0], temps[1],
4131                            temps[2], rd);
4132 #else
4133         masm.atomicFetchOp(viewType, sync, op, rv, srcAddr, temps[0], rd);
4134 #endif
4135         break;
4136       default: { MOZ_CRASH("Bad type for atomic operation"); }
4137     }
4138   }
4139 
4140   // On x86, V is Address.  On other platforms, it is Register64.
4141   // T is BaseIndex or Address.
4142   template <typename T, typename V>
atomicRMW64(const T & srcAddr,AtomicOp op,V value,Register64 temp,Register64 rd)4143   void atomicRMW64(const T& srcAddr, AtomicOp op, V value, Register64 temp,
4144                    Register64 rd) {
4145     masm.atomicFetchOp64(Synchronization::Full(), op, value, srcAddr, temp, rd);
4146   }
4147 
4148 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4149   typedef Atomic32Temps<3> AtomicCmpXchg32Temps;
4150 #else
4151   typedef Atomic32Temps<0> AtomicCmpXchg32Temps;
4152 #endif
4153 
4154   template <typename T>
atomicCmpXchg32(T srcAddr,Scalar::Type viewType,RegI32 rexpect,RegI32 rnew,RegI32 rd,const AtomicCmpXchg32Temps & temps)4155   void atomicCmpXchg32(T srcAddr, Scalar::Type viewType, RegI32 rexpect,
4156                        RegI32 rnew, RegI32 rd,
4157                        const AtomicCmpXchg32Temps& temps) {
4158     Synchronization sync = Synchronization::Full();
4159     switch (viewType) {
4160       case Scalar::Uint8:
4161 #if defined(JS_CODEGEN_X86)
4162       {
4163         ScratchI8 scratch(*this);
4164         MOZ_ASSERT(rd == specific.eax);
4165         if (!ra.isSingleByteI32(rnew)) {
4166           // The replacement value must have a byte persona.
4167           masm.movl(rnew, scratch);
4168           rnew = scratch;
4169         }
4170         masm.compareExchange(viewType, sync, srcAddr, rexpect, rnew, rd);
4171         break;
4172       }
4173 #endif
4174       case Scalar::Uint16:
4175       case Scalar::Int32:
4176       case Scalar::Uint32:
4177 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4178         masm.compareExchange(viewType, sync, srcAddr, rexpect, rnew, temps[0],
4179                              temps[1], temps[2], rd);
4180 #else
4181         masm.compareExchange(viewType, sync, srcAddr, rexpect, rnew, rd);
4182 #endif
4183         break;
4184       default:
4185         MOZ_CRASH("Bad type for atomic operation");
4186     }
4187   }
4188 
4189 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4190   typedef Atomic32Temps<3> AtomicXchg32Temps;
4191 #else
4192   typedef Atomic32Temps<0> AtomicXchg32Temps;
4193 #endif
4194 
4195   template <typename T>
atomicXchg32(T srcAddr,Scalar::Type viewType,RegI32 rv,RegI32 rd,const AtomicXchg32Temps & temps)4196   void atomicXchg32(T srcAddr, Scalar::Type viewType, RegI32 rv, RegI32 rd,
4197                     const AtomicXchg32Temps& temps) {
4198     Synchronization sync = Synchronization::Full();
4199     switch (viewType) {
4200       case Scalar::Uint8:
4201 #if defined(JS_CODEGEN_X86)
4202       {
4203         if (!ra.isSingleByteI32(rd)) {
4204           ScratchI8 scratch(*this);
4205           // The output register must have a byte persona.
4206           masm.atomicExchange(viewType, sync, srcAddr, rv, scratch);
4207           masm.movl(scratch, rd);
4208         } else {
4209           masm.atomicExchange(viewType, sync, srcAddr, rv, rd);
4210         }
4211         break;
4212       }
4213 #endif
4214       case Scalar::Uint16:
4215       case Scalar::Int32:
4216       case Scalar::Uint32:
4217 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4218         masm.atomicExchange(viewType, sync, srcAddr, rv, temps[0], temps[1],
4219                             temps[2], rd);
4220 #else
4221         masm.atomicExchange(viewType, sync, srcAddr, rv, rd);
4222 #endif
4223         break;
4224       default:
4225         MOZ_CRASH("Bad type for atomic operation");
4226     }
4227   }
4228 
4229   ////////////////////////////////////////////////////////////
4230   //
4231   // Generally speaking, ABOVE this point there should be no
4232   // value stack manipulation (calls to popI32 etc).
4233   //
4234   ////////////////////////////////////////////////////////////
4235 
4236   ////////////////////////////////////////////////////////////
4237   //
4238   // Platform-specific popping and register targeting.
4239   //
4240   // These fall into two groups, popping methods for simple needs, and RAII
4241   // wrappers for more complex behavior.
4242 
4243   // The simple popping methods pop values into targeted registers; the caller
4244   // can free registers using standard functions.  These are always called
4245   // popXForY where X says something about types and Y something about the
4246   // operation being targeted.
4247 
pop2xI32ForMulDivI32(RegI32 * r0,RegI32 * r1,RegI32 * reserved)4248   void pop2xI32ForMulDivI32(RegI32* r0, RegI32* r1, RegI32* reserved) {
4249 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
4250     // r0 must be eax, and edx will be clobbered.
4251     need2xI32(specific.eax, specific.edx);
4252     *r1 = popI32();
4253     *r0 = popI32ToSpecific(specific.eax);
4254     *reserved = specific.edx;
4255 #else
4256     pop2xI32(r0, r1);
4257 #endif
4258   }
4259 
pop2xI64ForMulI64(RegI64 * r0,RegI64 * r1,RegI32 * temp,RegI64 * reserved)4260   void pop2xI64ForMulI64(RegI64* r0, RegI64* r1, RegI32* temp,
4261                          RegI64* reserved) {
4262 #if defined(JS_CODEGEN_X64)
4263     // r0 must be rax, and rdx will be clobbered.
4264     need2xI64(specific.rax, specific.rdx);
4265     *r1 = popI64();
4266     *r0 = popI64ToSpecific(specific.rax);
4267     *reserved = specific.rdx;
4268 #elif defined(JS_CODEGEN_X86)
4269     // As for x64, though edx is part of r0.
4270     need2xI32(specific.eax, specific.edx);
4271     *r1 = popI64();
4272     *r0 = popI64ToSpecific(specific.edx_eax);
4273     *temp = needI32();
4274 #elif defined(JS_CODEGEN_MIPS64)
4275     pop2xI64(r0, r1);
4276 #else
4277     pop2xI64(r0, r1);
4278     *temp = needI32();
4279 #endif
4280   }
4281 
pop2xI64ForDivI64(RegI64 * r0,RegI64 * r1,RegI64 * reserved)4282   void pop2xI64ForDivI64(RegI64* r0, RegI64* r1, RegI64* reserved) {
4283 #ifdef JS_CODEGEN_X64
4284     // r0 must be rax, and rdx will be clobbered.
4285     need2xI64(specific.rax, specific.rdx);
4286     *r1 = popI64();
4287     *r0 = popI64ToSpecific(specific.rax);
4288     *reserved = specific.rdx;
4289 #else
4290     pop2xI64(r0, r1);
4291 #endif
4292   }
4293 
pop2xI32ForShiftOrRotate(RegI32 * r0,RegI32 * r1)4294   void pop2xI32ForShiftOrRotate(RegI32* r0, RegI32* r1) {
4295 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
4296     // r1 must be ecx for a variable shift.
4297     *r1 = popI32(specific.ecx);
4298     *r0 = popI32();
4299 #else
4300     pop2xI32(r0, r1);
4301 #endif
4302   }
4303 
pop2xI64ForShiftOrRotate(RegI64 * r0,RegI64 * r1)4304   void pop2xI64ForShiftOrRotate(RegI64* r0, RegI64* r1) {
4305 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
4306     // r1 must be ecx for a variable shift.
4307     needI32(specific.ecx);
4308     *r1 = popI64ToSpecific(widenI32(specific.ecx));
4309     *r0 = popI64();
4310 #else
4311     pop2xI64(r0, r1);
4312 #endif
4313   }
4314 
popI32ForSignExtendI64(RegI64 * r0)4315   void popI32ForSignExtendI64(RegI64* r0) {
4316 #if defined(JS_CODEGEN_X86)
4317     // r0 must be edx:eax for cdq
4318     need2xI32(specific.edx, specific.eax);
4319     *r0 = specific.edx_eax;
4320     popI32ToSpecific(specific.eax);
4321 #else
4322     *r0 = widenI32(popI32());
4323 #endif
4324   }
4325 
popI64ForSignExtendI64(RegI64 * r0)4326   void popI64ForSignExtendI64(RegI64* r0) {
4327 #if defined(JS_CODEGEN_X86)
4328     // r0 must be edx:eax for cdq
4329     need2xI32(specific.edx, specific.eax);
4330     // Low on top, high underneath
4331     *r0 = popI64ToSpecific(specific.edx_eax);
4332 #else
4333     *r0 = popI64();
4334 #endif
4335   }
4336 
4337   // The RAII wrappers are used because we sometimes have to free partial
4338   // registers, as when part of a register is the scratch register that has
4339   // been temporarily used, or not free a register at all, as when the
4340   // register is the same as the destination register (but only on some
4341   // platforms, not on all).  These are called PopX{32,64}Regs where X is the
4342   // operation being targeted.
4343 
4344   // Utility struct that holds the BaseCompiler and the destination, and frees
4345   // the destination if it has not been extracted.
4346 
4347   template <typename T>
4348   class PopBase {
4349     T rd_;
4350 
maybeFree(RegI32 r)4351     void maybeFree(RegI32 r) { bc->maybeFreeI32(r); }
maybeFree(RegI64 r)4352     void maybeFree(RegI64 r) { bc->maybeFreeI64(r); }
4353 
4354    protected:
4355     BaseCompiler* const bc;
4356 
setRd(T r)4357     void setRd(T r) {
4358       MOZ_ASSERT(rd_.isInvalid());
4359       rd_ = r;
4360     }
getRd() const4361     T getRd() const {
4362       MOZ_ASSERT(rd_.isValid());
4363       return rd_;
4364     }
4365 
4366    public:
PopBase(BaseCompiler * bc)4367     explicit PopBase(BaseCompiler* bc) : bc(bc) {}
~PopBase()4368     ~PopBase() { maybeFree(rd_); }
4369 
4370     // Take and clear the Rd - use this when pushing Rd.
takeRd()4371     T takeRd() {
4372       MOZ_ASSERT(rd_.isValid());
4373       T r = rd_;
4374       rd_ = T::Invalid();
4375       return r;
4376     }
4377   };
4378 
4379   friend class PopAtomicCmpXchg32Regs;
4380   class PopAtomicCmpXchg32Regs : public PopBase<RegI32> {
4381     using Base = PopBase<RegI32>;
4382     RegI32 rexpect, rnew;
4383     AtomicCmpXchg32Temps temps;
4384 
4385    public:
4386 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicCmpXchg32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType)4387     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
4388                                     Scalar::Type viewType)
4389         : Base(bc) {
4390       // For cmpxchg, the expected value and the result are both in eax.
4391       bc->needI32(bc->specific.eax);
4392       if (type == ValType::I64) {
4393         rnew = bc->popI64ToI32();
4394         rexpect = bc->popI64ToSpecificI32(bc->specific.eax);
4395       } else {
4396         rnew = bc->popI32();
4397         rexpect = bc->popI32ToSpecific(bc->specific.eax);
4398       }
4399       setRd(rexpect);
4400     }
~PopAtomicCmpXchg32Regs()4401     ~PopAtomicCmpXchg32Regs() { bc->freeI32(rnew); }
4402 #elif defined(JS_CODEGEN_ARM)
4403     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
4404                                     Scalar::Type viewType)
4405         : Base(bc) {
4406       if (type == ValType::I64) {
4407         rnew = bc->popI64ToI32();
4408         rexpect = bc->popI64ToI32();
4409       } else {
4410         rnew = bc->popI32();
4411         rexpect = bc->popI32();
4412       }
4413       setRd(bc->needI32());
4414     }
4415     ~PopAtomicCmpXchg32Regs() {
4416       bc->freeI32(rnew);
4417       bc->freeI32(rexpect);
4418     }
4419 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4420     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
4421                                     Scalar::Type viewType)
4422         : Base(bc) {
4423       if (type == ValType::I64) {
4424         rnew = bc->popI64ToI32();
4425         rexpect = bc->popI64ToI32();
4426       } else {
4427         rnew = bc->popI32();
4428         rexpect = bc->popI32();
4429       }
4430       if (Scalar::byteSize(viewType) < 4) temps.allocate(bc);
4431       setRd(bc->needI32());
4432     }
4433     ~PopAtomicCmpXchg32Regs() {
4434       bc->freeI32(rnew);
4435       bc->freeI32(rexpect);
4436       temps.maybeFree(bc);
4437     }
4438 #else
4439     explicit PopAtomicCmpXchg32Regs(BaseCompiler* bc, ValType type,
4440                                     Scalar::Type viewType)
4441         : Base(bc) {
4442       MOZ_CRASH("BaseCompiler porting interface: PopAtomicCmpXchg32Regs");
4443     }
4444 #endif
4445 
4446     template <typename T>
atomicCmpXchg32(T srcAddr,Scalar::Type viewType)4447     void atomicCmpXchg32(T srcAddr, Scalar::Type viewType) {
4448       bc->atomicCmpXchg32(srcAddr, viewType, rexpect, rnew, getRd(), temps);
4449     }
4450   };
4451 
4452   friend class PopAtomicCmpXchg64Regs;
4453   class PopAtomicCmpXchg64Regs : public PopBase<RegI64> {
4454     using Base = PopBase<RegI64>;
4455     RegI64 rexpect, rnew;
4456 
4457    public:
4458 #ifdef JS_CODEGEN_X64
PopAtomicCmpXchg64Regs(BaseCompiler * bc)4459     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
4460       // For cmpxchg, the expected value and the result are both in rax.
4461       bc->needI64(bc->specific.rax);
4462       rnew = bc->popI64();
4463       rexpect = bc->popI64ToSpecific(bc->specific.rax);
4464       setRd(rexpect);
4465     }
~PopAtomicCmpXchg64Regs()4466     ~PopAtomicCmpXchg64Regs() { bc->freeI64(rnew); }
4467 #elif defined(JS_CODEGEN_X86)
4468     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
4469       // For cmpxchg8b, the expected value and the result are both in
4470       // edx:eax, and the replacement value is in ecx:ebx.  But we can't
4471       // allocate ebx here, so instead we allocate a temp to hold the low
4472       // word of 'new'.
4473       bc->needI64(bc->specific.edx_eax);
4474       bc->needI32(bc->specific.ecx);
4475 
4476       rnew = bc->popI64ToSpecific(
4477           RegI64(Register64(bc->specific.ecx, bc->needI32())));
4478       rexpect = bc->popI64ToSpecific(bc->specific.edx_eax);
4479       setRd(rexpect);
4480     }
4481     ~PopAtomicCmpXchg64Regs() { bc->freeI64(rnew); }
4482 #elif defined(JS_CODEGEN_ARM)
4483     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
4484       // The replacement value and the result must both be odd/even pairs.
4485       rnew = bc->popI64Pair();
4486       rexpect = bc->popI64();
4487       setRd(bc->needI64Pair());
4488     }
4489     ~PopAtomicCmpXchg64Regs() {
4490       bc->freeI64(rexpect);
4491       bc->freeI64(rnew);
4492     }
4493 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4494     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
4495       rnew = bc->popI64();
4496       rexpect = bc->popI64();
4497       setRd(bc->needI64());
4498     }
4499     ~PopAtomicCmpXchg64Regs() {
4500       bc->freeI64(rexpect);
4501       bc->freeI64(rnew);
4502     }
4503 #else
4504     explicit PopAtomicCmpXchg64Regs(BaseCompiler* bc) : Base(bc) {
4505       MOZ_CRASH("BaseCompiler porting interface: PopAtomicCmpXchg64Regs");
4506     }
4507 #endif
4508 
4509 #ifdef JS_CODEGEN_X86
4510     template <typename T>
atomicCmpXchg64(T srcAddr,RegI32 ebx)4511     void atomicCmpXchg64(T srcAddr, RegI32 ebx) {
4512       MOZ_ASSERT(ebx == js::jit::ebx);
4513       bc->masm.move32(rnew.low, ebx);
4514       bc->masm.compareExchange64(Synchronization::Full(), srcAddr, rexpect,
4515                                  bc->specific.ecx_ebx, getRd());
4516     }
4517 #else
4518     template <typename T>
atomicCmpXchg64(T srcAddr)4519     void atomicCmpXchg64(T srcAddr) {
4520       bc->masm.compareExchange64(Synchronization::Full(), srcAddr, rexpect,
4521                                  rnew, getRd());
4522     }
4523 #endif
4524   };
4525 
4526 #ifndef JS_64BIT
4527   class PopAtomicLoad64Regs : public PopBase<RegI64> {
4528     using Base = PopBase<RegI64>;
4529 
4530    public:
4531 #if defined(JS_CODEGEN_X86)
PopAtomicLoad64Regs(BaseCompiler * bc)4532     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
4533       // The result is in edx:eax, and we need ecx:ebx as a temp.  But we
4534       // can't reserve ebx yet, so we'll accept it as an argument to the
4535       // operation (below).
4536       bc->needI32(bc->specific.ecx);
4537       bc->needI64(bc->specific.edx_eax);
4538       setRd(bc->specific.edx_eax);
4539     }
~PopAtomicLoad64Regs()4540     ~PopAtomicLoad64Regs() { bc->freeI32(bc->specific.ecx); }
4541 #elif defined(JS_CODEGEN_ARM)
4542     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
4543       setRd(bc->needI64Pair());
4544     }
4545 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4546     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
4547       setRd(bc->needI64());
4548     }
4549 #else
4550     explicit PopAtomicLoad64Regs(BaseCompiler* bc) : Base(bc) {
4551       MOZ_CRASH("BaseCompiler porting interface: PopAtomicLoad64Regs");
4552     }
4553 #endif
4554 
4555 #ifdef JS_CODEGEN_X86
4556     template <typename T>
atomicLoad64(T srcAddr,RegI32 ebx)4557     void atomicLoad64(T srcAddr, RegI32 ebx) {
4558       MOZ_ASSERT(ebx == js::jit::ebx);
4559       bc->masm.atomicLoad64(Synchronization::Full(), srcAddr,
4560                             bc->specific.ecx_ebx, getRd());
4561     }
4562 #else
4563     template <typename T>
atomicLoad64(T srcAddr)4564     void atomicLoad64(T srcAddr) {
4565       bc->masm.atomicLoad64(Synchronization::Full(), srcAddr, RegI64::Invalid(),
4566                             getRd());
4567     }
4568 #endif
4569   };
4570 #endif  // JS_64BIT
4571 
4572   friend class PopAtomicRMW32Regs;
4573   class PopAtomicRMW32Regs : public PopBase<RegI32> {
4574     using Base = PopBase<RegI32>;
4575     RegI32 rv;
4576     AtomicRMW32Temps temps;
4577 
4578    public:
4579 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicRMW32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType,AtomicOp op)4580     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
4581                                 Scalar::Type viewType, AtomicOp op)
4582         : Base(bc) {
4583       bc->needI32(bc->specific.eax);
4584       if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) {
4585         // We use xadd, so source and destination are the same.  Using
4586         // eax here is overconstraining, but for byte operations on x86
4587         // we do need something with a byte register.
4588         if (type == ValType::I64)
4589           rv = bc->popI64ToSpecificI32(bc->specific.eax);
4590         else
4591           rv = bc->popI32ToSpecific(bc->specific.eax);
4592         setRd(rv);
4593       } else {
4594         // We use a cmpxchg loop.  The output must be eax; the input
4595         // must be in a separate register since it may be used several
4596         // times.
4597         if (type == ValType::I64)
4598           rv = bc->popI64ToI32();
4599         else
4600           rv = bc->popI32();
4601         setRd(bc->specific.eax);
4602 #if defined(JS_CODEGEN_X86)
4603         // Single-byte is a special case handled very locally with
4604         // ScratchReg, see atomicRMW32 above.
4605         if (Scalar::byteSize(viewType) > 1) temps.allocate(bc);
4606 #else
4607         temps.allocate(bc);
4608 #endif
4609       }
4610     }
~PopAtomicRMW32Regs()4611     ~PopAtomicRMW32Regs() {
4612       if (rv != bc->specific.eax) bc->freeI32(rv);
4613       temps.maybeFree(bc);
4614     }
4615 #elif defined(JS_CODEGEN_ARM)
4616     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
4617                                 Scalar::Type viewType, AtomicOp op)
4618         : Base(bc) {
4619       rv = type == ValType::I64 ? bc->popI64ToI32() : bc->popI32();
4620       temps.allocate(bc);
4621       setRd(bc->needI32());
4622     }
4623     ~PopAtomicRMW32Regs() {
4624       bc->freeI32(rv);
4625       temps.maybeFree(bc);
4626     }
4627 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4628     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
4629                                 Scalar::Type viewType, AtomicOp op)
4630         : Base(bc) {
4631       rv = type == ValType::I64 ? bc->popI64ToI32() : bc->popI32();
4632       if (Scalar::byteSize(viewType) < 4) temps.allocate(bc);
4633 
4634       setRd(bc->needI32());
4635     }
4636     ~PopAtomicRMW32Regs() {
4637       bc->freeI32(rv);
4638       temps.maybeFree(bc);
4639     }
4640 #else
4641     explicit PopAtomicRMW32Regs(BaseCompiler* bc, ValType type,
4642                                 Scalar::Type viewType, AtomicOp op)
4643         : Base(bc) {
4644       MOZ_CRASH("BaseCompiler porting interface: PopAtomicRMW32Regs");
4645     }
4646 #endif
4647 
4648     template <typename T>
atomicRMW32(T srcAddr,Scalar::Type viewType,AtomicOp op)4649     void atomicRMW32(T srcAddr, Scalar::Type viewType, AtomicOp op) {
4650       bc->atomicRMW32(srcAddr, viewType, op, rv, getRd(), temps);
4651     }
4652   };
4653 
4654   friend class PopAtomicRMW64Regs;
4655   class PopAtomicRMW64Regs : public PopBase<RegI64> {
4656     using Base = PopBase<RegI64>;
4657 #if defined(JS_CODEGEN_X64)
4658     AtomicOp op;
4659 #endif
4660     RegI64 rv, temp;
4661 
4662    public:
4663 #if defined(JS_CODEGEN_X64)
PopAtomicRMW64Regs(BaseCompiler * bc,AtomicOp op)4664     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp op)
4665         : Base(bc), op(op) {
4666       if (op == AtomicFetchAddOp || op == AtomicFetchSubOp) {
4667         // We use xaddq, so input and output must be the same register.
4668         rv = bc->popI64();
4669         setRd(rv);
4670       } else {
4671         // We use a cmpxchgq loop, so the output must be rax.
4672         bc->needI64(bc->specific.rax);
4673         rv = bc->popI64();
4674         temp = bc->needI64();
4675         setRd(bc->specific.rax);
4676       }
4677     }
~PopAtomicRMW64Regs()4678     ~PopAtomicRMW64Regs() {
4679       bc->maybeFreeI64(temp);
4680       if (op != AtomicFetchAddOp && op != AtomicFetchSubOp) bc->freeI64(rv);
4681     }
4682 #elif defined(JS_CODEGEN_X86)
4683     // We'll use cmpxchg8b, so rv must be in ecx:ebx, and rd must be
4684     // edx:eax.  But we can't reserve ebx here because we need it later, so
4685     // use a separate temp and set up ebx when we perform the operation.
4686     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
4687       bc->needI32(bc->specific.ecx);
4688       bc->needI64(bc->specific.edx_eax);
4689 
4690       temp = RegI64(Register64(bc->specific.ecx, bc->needI32()));
4691       bc->popI64ToSpecific(temp);
4692 
4693       setRd(bc->specific.edx_eax);
4694     }
4695     ~PopAtomicRMW64Regs() { bc->freeI64(temp); }
4696     RegI32 valueHigh() const { return RegI32(temp.high); }
4697     RegI32 valueLow() const { return RegI32(temp.low); }
4698 #elif defined(JS_CODEGEN_ARM)
4699     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
4700       // We use a ldrex/strexd loop so the temp and the output must be
4701       // odd/even pairs.
4702       rv = bc->popI64();
4703       temp = bc->needI64Pair();
4704       setRd(bc->needI64Pair());
4705     }
4706     ~PopAtomicRMW64Regs() {
4707       bc->freeI64(rv);
4708       bc->freeI64(temp);
4709     }
4710 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4711     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
4712       rv = bc->popI64();
4713       temp = bc->needI64();
4714       setRd(bc->needI64());
4715     }
4716     ~PopAtomicRMW64Regs() {
4717       bc->freeI64(rv);
4718       bc->freeI64(temp);
4719     }
4720 #else
4721     explicit PopAtomicRMW64Regs(BaseCompiler* bc, AtomicOp) : Base(bc) {
4722       MOZ_CRASH("BaseCompiler porting interface: PopAtomicRMW64Regs");
4723     }
4724 #endif
4725 
4726 #ifdef JS_CODEGEN_X86
4727     template <typename T, typename V>
atomicRMW64(T srcAddr,AtomicOp op,const V & value,RegI32 ebx)4728     void atomicRMW64(T srcAddr, AtomicOp op, const V& value, RegI32 ebx) {
4729       MOZ_ASSERT(ebx == js::jit::ebx);
4730       bc->atomicRMW64(srcAddr, op, value, bc->specific.ecx_ebx, getRd());
4731     }
4732 #else
4733     template <typename T>
atomicRMW64(T srcAddr,AtomicOp op)4734     void atomicRMW64(T srcAddr, AtomicOp op) {
4735       bc->atomicRMW64(srcAddr, op, rv, temp, getRd());
4736     }
4737 #endif
4738   };
4739 
4740   friend class PopAtomicXchg32Regs;
4741   class PopAtomicXchg32Regs : public PopBase<RegI32> {
4742     using Base = PopBase<RegI32>;
4743     RegI32 rv;
4744     AtomicXchg32Temps temps;
4745 
4746    public:
4747 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
PopAtomicXchg32Regs(BaseCompiler * bc,ValType type,Scalar::Type viewType)4748     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
4749                                  Scalar::Type viewType)
4750         : Base(bc) {
4751       // The xchg instruction reuses rv as rd.
4752       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
4753       setRd(rv);
4754     }
4755 #elif defined(JS_CODEGEN_ARM)
4756     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
4757                                  Scalar::Type viewType)
4758         : Base(bc) {
4759       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
4760       setRd(bc->needI32());
4761     }
4762     ~PopAtomicXchg32Regs() { bc->freeI32(rv); }
4763 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4764     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
4765                                  Scalar::Type viewType)
4766         : Base(bc) {
4767       rv = (type == ValType::I64) ? bc->popI64ToI32() : bc->popI32();
4768       if (Scalar::byteSize(viewType) < 4) temps.allocate(bc);
4769       setRd(bc->needI32());
4770     }
4771     ~PopAtomicXchg32Regs() {
4772       temps.maybeFree(bc);
4773       bc->freeI32(rv);
4774     }
4775 #else
4776     explicit PopAtomicXchg32Regs(BaseCompiler* bc, ValType type,
4777                                  Scalar::Type viewType)
4778         : Base(bc) {
4779       MOZ_CRASH("BaseCompiler porting interface: PopAtomicXchg32Regs");
4780     }
4781 #endif
4782 
4783     template <typename T>
atomicXchg32(T srcAddr,Scalar::Type viewType)4784     void atomicXchg32(T srcAddr, Scalar::Type viewType) {
4785       bc->atomicXchg32(srcAddr, viewType, rv, getRd(), temps);
4786     }
4787   };
4788 
4789   friend class PopAtomicXchg64Regs;
4790   class PopAtomicXchg64Regs : public PopBase<RegI64> {
4791     using Base = PopBase<RegI64>;
4792     RegI64 rv;
4793 
4794    public:
4795 #ifdef JS_CODEGEN_X64
PopAtomicXchg64Regs(BaseCompiler * bc)4796     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
4797       rv = bc->popI64();
4798       setRd(rv);
4799     }
4800 #elif defined(JS_CODEGEN_X86)
4801     // We'll use cmpxchg8b, so rv must be in ecx:ebx, and rd must be
4802     // edx:eax.  But we can't reserve ebx here because we need it later, so
4803     // use a separate temp and set up ebx when we perform the operation.
4804     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
4805       bc->needI32(bc->specific.ecx);
4806       bc->needI64(bc->specific.edx_eax);
4807 
4808       rv = RegI64(Register64(bc->specific.ecx, bc->needI32()));
4809       bc->popI64ToSpecific(rv);
4810 
4811       setRd(bc->specific.edx_eax);
4812     }
4813     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
4814 #elif defined(JS_CODEGEN_ARM)
4815     // Both rv and rd must be odd/even pairs.
4816     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
4817       rv = bc->popI64ToSpecific(bc->needI64Pair());
4818       setRd(bc->needI64Pair());
4819     }
4820     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
4821 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
4822     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
4823       rv = bc->popI64ToSpecific(bc->needI64());
4824       setRd(bc->needI64());
4825     }
4826     ~PopAtomicXchg64Regs() { bc->freeI64(rv); }
4827 #else
4828     explicit PopAtomicXchg64Regs(BaseCompiler* bc) : Base(bc) {
4829       MOZ_CRASH("BaseCompiler porting interface: xchg64");
4830     }
4831 #endif
4832 
4833 #ifdef JS_CODEGEN_X86
4834     template <typename T>
atomicXchg64(T srcAddr,RegI32 ebx) const4835     void atomicXchg64(T srcAddr, RegI32 ebx) const {
4836       MOZ_ASSERT(ebx == js::jit::ebx);
4837       bc->masm.move32(rv.low, ebx);
4838       bc->masm.atomicExchange64(Synchronization::Full(), srcAddr,
4839                                 bc->specific.ecx_ebx, getRd());
4840     }
4841 #else
4842     template <typename T>
atomicXchg64(T srcAddr) const4843     void atomicXchg64(T srcAddr) const {
4844       bc->masm.atomicExchange64(Synchronization::Full(), srcAddr, rv, getRd());
4845     }
4846 #endif
4847   };
4848 
4849   ////////////////////////////////////////////////////////////
4850   //
4851   // Generally speaking, BELOW this point there should be no
4852   // platform dependencies.  We make very occasional exceptions
4853   // when it doesn't become messy and further abstraction is
4854   // not desirable.
4855   //
4856   ////////////////////////////////////////////////////////////
4857 
4858   ////////////////////////////////////////////////////////////
4859   //
4860   // Sundry wrappers.
4861 
pop2xI32(RegI32 * r0,RegI32 * r1)4862   void pop2xI32(RegI32* r0, RegI32* r1) {
4863     *r1 = popI32();
4864     *r0 = popI32();
4865   }
4866 
popI32ToSpecific(RegI32 specific)4867   RegI32 popI32ToSpecific(RegI32 specific) {
4868     freeI32(specific);
4869     return popI32(specific);
4870   }
4871 
pop2xI64(RegI64 * r0,RegI64 * r1)4872   void pop2xI64(RegI64* r0, RegI64* r1) {
4873     *r1 = popI64();
4874     *r0 = popI64();
4875   }
4876 
popI64ToSpecific(RegI64 specific)4877   RegI64 popI64ToSpecific(RegI64 specific) {
4878     freeI64(specific);
4879     return popI64(specific);
4880   }
4881 
4882 #ifdef JS_CODEGEN_ARM
popI64Pair()4883   RegI64 popI64Pair() {
4884     RegI64 r = needI64Pair();
4885     popI64ToSpecific(r);
4886     return r;
4887   }
4888 #endif
4889 
pop2xF32(RegF32 * r0,RegF32 * r1)4890   void pop2xF32(RegF32* r0, RegF32* r1) {
4891     *r1 = popF32();
4892     *r0 = popF32();
4893   }
4894 
pop2xF64(RegF64 * r0,RegF64 * r1)4895   void pop2xF64(RegF64* r0, RegF64* r1) {
4896     *r1 = popF64();
4897     *r0 = popF64();
4898   }
4899 
popI64ToI32()4900   RegI32 popI64ToI32() {
4901     RegI64 r = popI64();
4902     return narrowI64(r);
4903   }
4904 
popI64ToSpecificI32(RegI32 specific)4905   RegI32 popI64ToSpecificI32(RegI32 specific) {
4906     RegI64 rd = widenI32(specific);
4907     popI64ToSpecific(rd);
4908     return narrowI64(rd);
4909   }
4910 
pushU32AsI64(RegI32 rs)4911   void pushU32AsI64(RegI32 rs) {
4912     RegI64 rd = widenI32(rs);
4913     masm.move32To64ZeroExtend(rs, rd);
4914     pushI64(rd);
4915   }
4916 
4917   RegI32 popMemoryAccess(MemoryAccessDesc* access, AccessCheck* check);
4918 
4919   ////////////////////////////////////////////////////////////
4920   //
4921   // Sundry helpers.
4922 
readCallSiteLineOrBytecode()4923   uint32_t readCallSiteLineOrBytecode() {
4924     if (!func_.callSiteLineNums.empty())
4925       return func_.callSiteLineNums[lastReadCallSite_++];
4926     return iter_.lastOpcodeOffset();
4927   }
4928 
done() const4929   bool done() const { return iter_.done(); }
4930 
bytecodeOffset() const4931   BytecodeOffset bytecodeOffset() const { return iter_.bytecodeOffset(); }
4932 
trap(Trap t) const4933   void trap(Trap t) const { masm.wasmTrap(t, bytecodeOffset()); }
4934 
oldTrap(Trap t) const4935   OldTrapDesc oldTrap(Trap t) const {
4936     // Use masm.framePushed() because the value needed by the trap machinery
4937     // is the size of the frame overall, not the height of the stack area of
4938     // the frame.
4939     return OldTrapDesc(bytecodeOffset(), t, masm.framePushed());
4940   }
4941 
4942   ////////////////////////////////////////////////////////////
4943   //
4944   // Machinery for optimized conditional branches.
4945   //
4946   // To disable this optimization it is enough always to return false from
4947   // sniffConditionalControl{Cmp,Eqz}.
4948 
4949   struct BranchState {
4950     static const int32_t NoPop = ~0;
4951 
4952     union {
4953       struct {
4954         RegI32 lhs;
4955         RegI32 rhs;
4956         int32_t imm;
4957         bool rhsImm;
4958       } i32;
4959       struct {
4960         RegI64 lhs;
4961         RegI64 rhs;
4962         int64_t imm;
4963         bool rhsImm;
4964       } i64;
4965       struct {
4966         RegF32 lhs;
4967         RegF32 rhs;
4968       } f32;
4969       struct {
4970         RegF64 lhs;
4971         RegF64 rhs;
4972       } f64;
4973     };
4974 
4975     Label* const label;         // The target of the branch, never NULL
4976     const int32_t stackHeight;  // Either NoPop, or the value to pop to along
4977                                 // the taken edge
4978     const bool invertBranch;    // If true, invert the sense of the branch
4979     const ExprType
4980         resultType;  // The result propagated along the edges, or Void
4981 
BranchStatejs::wasm::BaseCompiler::BranchState4982     explicit BranchState(Label* label, int32_t stackHeight = NoPop,
4983                          uint32_t invertBranch = false,
4984                          ExprType resultType = ExprType::Void)
4985         : label(label),
4986           stackHeight(stackHeight),
4987           invertBranch(invertBranch),
4988           resultType(resultType) {}
4989   };
4990 
setLatentCompare(Assembler::Condition compareOp,ValType operandType)4991   void setLatentCompare(Assembler::Condition compareOp, ValType operandType) {
4992     latentOp_ = LatentOp::Compare;
4993     latentType_ = operandType;
4994     latentIntCmp_ = compareOp;
4995   }
4996 
setLatentCompare(Assembler::DoubleCondition compareOp,ValType operandType)4997   void setLatentCompare(Assembler::DoubleCondition compareOp,
4998                         ValType operandType) {
4999     latentOp_ = LatentOp::Compare;
5000     latentType_ = operandType;
5001     latentDoubleCmp_ = compareOp;
5002   }
5003 
setLatentEqz(ValType operandType)5004   void setLatentEqz(ValType operandType) {
5005     latentOp_ = LatentOp::Eqz;
5006     latentType_ = operandType;
5007   }
5008 
resetLatentOp()5009   void resetLatentOp() { latentOp_ = LatentOp::None; }
5010 
branchTo(Assembler::DoubleCondition c,RegF64 lhs,RegF64 rhs,Label * l)5011   void branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs,
5012                 Label* l) {
5013     masm.branchDouble(c, lhs, rhs, l);
5014   }
5015 
branchTo(Assembler::DoubleCondition c,RegF32 lhs,RegF32 rhs,Label * l)5016   void branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs,
5017                 Label* l) {
5018     masm.branchFloat(c, lhs, rhs, l);
5019   }
5020 
branchTo(Assembler::Condition c,RegI32 lhs,RegI32 rhs,Label * l)5021   void branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs, Label* l) {
5022     masm.branch32(c, lhs, rhs, l);
5023   }
5024 
branchTo(Assembler::Condition c,RegI32 lhs,Imm32 rhs,Label * l)5025   void branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l) {
5026     masm.branch32(c, lhs, rhs, l);
5027   }
5028 
branchTo(Assembler::Condition c,RegI64 lhs,RegI64 rhs,Label * l)5029   void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, Label* l) {
5030     masm.branch64(c, lhs, rhs, l);
5031   }
5032 
branchTo(Assembler::Condition c,RegI64 lhs,Imm64 rhs,Label * l)5033   void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l) {
5034     masm.branch64(c, lhs, rhs, l);
5035   }
5036 
5037   // Emit a conditional branch that optionally and optimally cleans up the CPU
5038   // stack before we branch.
5039   //
5040   // Cond is either Assembler::Condition or Assembler::DoubleCondition.
5041   //
5042   // Lhs is RegI32, RegI64, or RegF32, or RegF64.
5043   //
5044   // Rhs is either the same as Lhs, or an immediate expression compatible with
5045   // Lhs "when applicable".
5046 
5047   template <typename Cond, typename Lhs, typename Rhs>
jumpConditionalWithJoinReg(BranchState * b,Cond cond,Lhs lhs,Rhs rhs)5048   void jumpConditionalWithJoinReg(BranchState* b, Cond cond, Lhs lhs, Rhs rhs) {
5049     Maybe<AnyReg> r = popJoinRegUnlessVoid(b->resultType);
5050 
5051     if (b->stackHeight != BranchState::NoPop &&
5052         fr.willPopStackBeforeBranch(b->stackHeight)) {
5053       Label notTaken;
5054       branchTo(b->invertBranch ? cond : Assembler::InvertCondition(cond), lhs,
5055                rhs, &notTaken);
5056       fr.popStackBeforeBranch(b->stackHeight);
5057       masm.jump(b->label);
5058       masm.bind(&notTaken);
5059     } else {
5060       branchTo(b->invertBranch ? Assembler::InvertCondition(cond) : cond, lhs,
5061                rhs, b->label);
5062     }
5063 
5064     pushJoinRegUnlessVoid(r);
5065   }
5066 
5067   // sniffConditionalControl{Cmp,Eqz} may modify the latentWhatever_ state in
5068   // the BaseCompiler so that a subsequent conditional branch can be compiled
5069   // optimally.  emitBranchSetup() and emitBranchPerform() will consume that
5070   // state.  If the latter methods are not called because deadCode_ is true
5071   // then the compiler MUST instead call resetLatentOp() to reset the state.
5072 
5073   template <typename Cond>
5074   bool sniffConditionalControlCmp(Cond compareOp, ValType operandType);
5075   bool sniffConditionalControlEqz(ValType operandType);
5076   void emitBranchSetup(BranchState* b);
5077   void emitBranchPerform(BranchState* b);
5078 
5079   //////////////////////////////////////////////////////////////////////
5080 
5081   MOZ_MUST_USE bool emitBody();
5082   MOZ_MUST_USE bool emitBlock();
5083   MOZ_MUST_USE bool emitLoop();
5084   MOZ_MUST_USE bool emitIf();
5085   MOZ_MUST_USE bool emitElse();
5086   MOZ_MUST_USE bool emitEnd();
5087   MOZ_MUST_USE bool emitBr();
5088   MOZ_MUST_USE bool emitBrIf();
5089   MOZ_MUST_USE bool emitBrTable();
5090   MOZ_MUST_USE bool emitDrop();
5091   MOZ_MUST_USE bool emitReturn();
5092   MOZ_MUST_USE bool emitCallArgs(const ValTypeVector& args,
5093                                  FunctionCall& baselineCall);
5094   MOZ_MUST_USE bool emitCall();
5095   MOZ_MUST_USE bool emitCallIndirect();
5096   MOZ_MUST_USE bool emitUnaryMathBuiltinCall(SymbolicAddress callee,
5097                                              ValType operandType);
5098   MOZ_MUST_USE bool emitGetLocal();
5099   MOZ_MUST_USE bool emitSetLocal();
5100   MOZ_MUST_USE bool emitTeeLocal();
5101   MOZ_MUST_USE bool emitGetGlobal();
5102   MOZ_MUST_USE bool emitSetGlobal();
5103   MOZ_MUST_USE RegI32 maybeLoadTlsForAccess(const AccessCheck& check);
5104   MOZ_MUST_USE RegI32 maybeLoadTlsForAccess(const AccessCheck& check,
5105                                             RegI32 specific);
5106   MOZ_MUST_USE bool emitLoad(ValType type, Scalar::Type viewType);
5107   MOZ_MUST_USE bool loadCommon(MemoryAccessDesc* access, ValType type);
5108   MOZ_MUST_USE bool emitStore(ValType resultType, Scalar::Type viewType);
5109   MOZ_MUST_USE bool storeCommon(MemoryAccessDesc* access, ValType resultType);
5110   MOZ_MUST_USE bool emitSelect();
5111 
5112   // Mark these templates as inline to work around a compiler crash in
5113   // gcc 4.8.5 when compiling for linux64-opt.
5114 
5115   template <bool isSetLocal>
5116   MOZ_MUST_USE inline bool emitSetOrTeeLocal(uint32_t slot);
5117 
5118   void endBlock(ExprType type);
5119   void endLoop(ExprType type);
5120   void endIfThen();
5121   void endIfThenElse(ExprType type);
5122 
5123   void doReturn(ExprType returnType, bool popStack);
5124   void pushReturned(const FunctionCall& call, ExprType type);
5125 
5126   void emitCompareI32(Assembler::Condition compareOp, ValType compareType);
5127   void emitCompareI64(Assembler::Condition compareOp, ValType compareType);
5128   void emitCompareF32(Assembler::DoubleCondition compareOp,
5129                       ValType compareType);
5130   void emitCompareF64(Assembler::DoubleCondition compareOp,
5131                       ValType compareType);
5132 
5133   void emitAddI32();
5134   void emitAddI64();
5135   void emitAddF64();
5136   void emitAddF32();
5137   void emitSubtractI32();
5138   void emitSubtractI64();
5139   void emitSubtractF32();
5140   void emitSubtractF64();
5141   void emitMultiplyI32();
5142   void emitMultiplyI64();
5143   void emitMultiplyF32();
5144   void emitMultiplyF64();
5145   void emitQuotientI32();
5146   void emitQuotientU32();
5147   void emitRemainderI32();
5148   void emitRemainderU32();
5149 #ifdef RABALDR_INT_DIV_I64_CALLOUT
5150   void emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType);
5151 #else
5152   void emitQuotientI64();
5153   void emitQuotientU64();
5154   void emitRemainderI64();
5155   void emitRemainderU64();
5156 #endif
5157   void emitDivideF32();
5158   void emitDivideF64();
5159   void emitMinF32();
5160   void emitMaxF32();
5161   void emitMinF64();
5162   void emitMaxF64();
5163   void emitCopysignF32();
5164   void emitCopysignF64();
5165   void emitOrI32();
5166   void emitOrI64();
5167   void emitAndI32();
5168   void emitAndI64();
5169   void emitXorI32();
5170   void emitXorI64();
5171   void emitShlI32();
5172   void emitShlI64();
5173   void emitShrI32();
5174   void emitShrI64();
5175   void emitShrU32();
5176   void emitShrU64();
5177   void emitRotrI32();
5178   void emitRotrI64();
5179   void emitRotlI32();
5180   void emitRotlI64();
5181   void emitEqzI32();
5182   void emitEqzI64();
5183   void emitClzI32();
5184   void emitClzI64();
5185   void emitCtzI32();
5186   void emitCtzI64();
5187   void emitPopcntI32();
5188   void emitPopcntI64();
5189   void emitAbsF32();
5190   void emitAbsF64();
5191   void emitNegateF32();
5192   void emitNegateF64();
5193   void emitSqrtF32();
5194   void emitSqrtF64();
5195   template <TruncFlags flags>
5196   MOZ_MUST_USE bool emitTruncateF32ToI32();
5197   template <TruncFlags flags>
5198   MOZ_MUST_USE bool emitTruncateF64ToI32();
5199 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
5200   MOZ_MUST_USE bool emitConvertFloatingToInt64Callout(SymbolicAddress callee,
5201                                                       ValType operandType,
5202                                                       ValType resultType);
5203 #else
5204   template <TruncFlags flags>
5205   MOZ_MUST_USE bool emitTruncateF32ToI64();
5206   template <TruncFlags flags>
5207   MOZ_MUST_USE bool emitTruncateF64ToI64();
5208 #endif
5209   void emitWrapI64ToI32();
5210   void emitExtendI32_8();
5211   void emitExtendI32_16();
5212   void emitExtendI64_8();
5213   void emitExtendI64_16();
5214   void emitExtendI64_32();
5215   void emitExtendI32ToI64();
5216   void emitExtendU32ToI64();
5217   void emitReinterpretF32AsI32();
5218   void emitReinterpretF64AsI64();
5219   void emitConvertF64ToF32();
5220   void emitConvertI32ToF32();
5221   void emitConvertU32ToF32();
5222   void emitConvertF32ToF64();
5223   void emitConvertI32ToF64();
5224   void emitConvertU32ToF64();
5225 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
5226   MOZ_MUST_USE bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee,
5227                                                       ValType operandType,
5228                                                       ValType resultType);
5229 #else
5230   void emitConvertI64ToF32();
5231   void emitConvertU64ToF32();
5232   void emitConvertI64ToF64();
5233   void emitConvertU64ToF64();
5234 #endif
5235   void emitReinterpretI32AsF32();
5236   void emitReinterpretI64AsF64();
5237   void emitRound(RoundingMode roundingMode, ValType operandType);
5238   void emitInstanceCall(uint32_t lineOrBytecode, const MIRTypeVector& sig,
5239                         ExprType retType, SymbolicAddress builtin);
5240   MOZ_MUST_USE bool emitGrowMemory();
5241   MOZ_MUST_USE bool emitCurrentMemory();
5242 
5243   MOZ_MUST_USE bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
5244   MOZ_MUST_USE bool emitAtomicLoad(ValType type, Scalar::Type viewType);
5245   MOZ_MUST_USE bool emitAtomicRMW(ValType type, Scalar::Type viewType,
5246                                   AtomicOp op);
5247   MOZ_MUST_USE bool emitAtomicStore(ValType type, Scalar::Type viewType);
5248   MOZ_MUST_USE bool emitWait(ValType type, uint32_t byteSize);
5249   MOZ_MUST_USE bool emitWake();
5250   MOZ_MUST_USE bool emitAtomicXchg(ValType type, Scalar::Type viewType);
5251   void emitAtomicXchg64(MemoryAccessDesc* access, ValType type,
5252                         WantResult wantResult);
5253 };
5254 
emitAddI32()5255 void BaseCompiler::emitAddI32() {
5256   int32_t c;
5257   if (popConstI32(&c)) {
5258     RegI32 r = popI32();
5259     masm.add32(Imm32(c), r);
5260     pushI32(r);
5261   } else {
5262     RegI32 r, rs;
5263     pop2xI32(&r, &rs);
5264     masm.add32(rs, r);
5265     freeI32(rs);
5266     pushI32(r);
5267   }
5268 }
5269 
emitAddI64()5270 void BaseCompiler::emitAddI64() {
5271   int64_t c;
5272   if (popConstI64(&c)) {
5273     RegI64 r = popI64();
5274     masm.add64(Imm64(c), r);
5275     pushI64(r);
5276   } else {
5277     RegI64 r, rs;
5278     pop2xI64(&r, &rs);
5279     masm.add64(rs, r);
5280     freeI64(rs);
5281     pushI64(r);
5282   }
5283 }
5284 
emitAddF64()5285 void BaseCompiler::emitAddF64() {
5286   RegF64 r, rs;
5287   pop2xF64(&r, &rs);
5288   masm.addDouble(rs, r);
5289   freeF64(rs);
5290   pushF64(r);
5291 }
5292 
emitAddF32()5293 void BaseCompiler::emitAddF32() {
5294   RegF32 r, rs;
5295   pop2xF32(&r, &rs);
5296   masm.addFloat32(rs, r);
5297   freeF32(rs);
5298   pushF32(r);
5299 }
5300 
emitSubtractI32()5301 void BaseCompiler::emitSubtractI32() {
5302   int32_t c;
5303   if (popConstI32(&c)) {
5304     RegI32 r = popI32();
5305     masm.sub32(Imm32(c), r);
5306     pushI32(r);
5307   } else {
5308     RegI32 r, rs;
5309     pop2xI32(&r, &rs);
5310     masm.sub32(rs, r);
5311     freeI32(rs);
5312     pushI32(r);
5313   }
5314 }
5315 
emitSubtractI64()5316 void BaseCompiler::emitSubtractI64() {
5317   int64_t c;
5318   if (popConstI64(&c)) {
5319     RegI64 r = popI64();
5320     masm.sub64(Imm64(c), r);
5321     pushI64(r);
5322   } else {
5323     RegI64 r, rs;
5324     pop2xI64(&r, &rs);
5325     masm.sub64(rs, r);
5326     freeI64(rs);
5327     pushI64(r);
5328   }
5329 }
5330 
emitSubtractF32()5331 void BaseCompiler::emitSubtractF32() {
5332   RegF32 r, rs;
5333   pop2xF32(&r, &rs);
5334   masm.subFloat32(rs, r);
5335   freeF32(rs);
5336   pushF32(r);
5337 }
5338 
emitSubtractF64()5339 void BaseCompiler::emitSubtractF64() {
5340   RegF64 r, rs;
5341   pop2xF64(&r, &rs);
5342   masm.subDouble(rs, r);
5343   freeF64(rs);
5344   pushF64(r);
5345 }
5346 
emitMultiplyI32()5347 void BaseCompiler::emitMultiplyI32() {
5348   RegI32 r, rs, reserved;
5349   pop2xI32ForMulDivI32(&r, &rs, &reserved);
5350   masm.mul32(rs, r);
5351   maybeFreeI32(reserved);
5352   freeI32(rs);
5353   pushI32(r);
5354 }
5355 
emitMultiplyI64()5356 void BaseCompiler::emitMultiplyI64() {
5357   RegI64 r, rs, reserved;
5358   RegI32 temp;
5359   pop2xI64ForMulI64(&r, &rs, &temp, &reserved);
5360   masm.mul64(rs, r, temp);
5361   maybeFreeI64(reserved);
5362   maybeFreeI32(temp);
5363   freeI64(rs);
5364   pushI64(r);
5365 }
5366 
emitMultiplyF32()5367 void BaseCompiler::emitMultiplyF32() {
5368   RegF32 r, rs;
5369   pop2xF32(&r, &rs);
5370   masm.mulFloat32(rs, r);
5371   freeF32(rs);
5372   pushF32(r);
5373 }
5374 
emitMultiplyF64()5375 void BaseCompiler::emitMultiplyF64() {
5376   RegF64 r, rs;
5377   pop2xF64(&r, &rs);
5378   masm.mulDouble(rs, r);
5379   freeF64(rs);
5380   pushF64(r);
5381 }
5382 
emitQuotientI32()5383 void BaseCompiler::emitQuotientI32() {
5384   int32_t c;
5385   uint_fast8_t power;
5386   if (popConstPositivePowerOfTwoI32(&c, &power, 0)) {
5387     if (power != 0) {
5388       RegI32 r = popI32();
5389       Label positive;
5390       masm.branchTest32(Assembler::NotSigned, r, r, &positive);
5391       masm.add32(Imm32(c - 1), r);
5392       masm.bind(&positive);
5393 
5394       masm.rshift32Arithmetic(Imm32(power & 31), r);
5395       pushI32(r);
5396     }
5397   } else {
5398     bool isConst = peekConstI32(&c);
5399     RegI32 r, rs, reserved;
5400     pop2xI32ForMulDivI32(&r, &rs, &reserved);
5401 
5402     Label done;
5403     if (!isConst || c == 0) checkDivideByZeroI32(rs, r, &done);
5404     if (!isConst || c == -1)
5405       checkDivideSignedOverflowI32(rs, r, &done, ZeroOnOverflow(false));
5406     masm.quotient32(rs, r, IsUnsigned(false));
5407     masm.bind(&done);
5408 
5409     maybeFreeI32(reserved);
5410     freeI32(rs);
5411     pushI32(r);
5412   }
5413 }
5414 
emitQuotientU32()5415 void BaseCompiler::emitQuotientU32() {
5416   int32_t c;
5417   uint_fast8_t power;
5418   if (popConstPositivePowerOfTwoI32(&c, &power, 0)) {
5419     if (power != 0) {
5420       RegI32 r = popI32();
5421       masm.rshift32(Imm32(power & 31), r);
5422       pushI32(r);
5423     }
5424   } else {
5425     bool isConst = peekConstI32(&c);
5426     RegI32 r, rs, reserved;
5427     pop2xI32ForMulDivI32(&r, &rs, &reserved);
5428 
5429     Label done;
5430     if (!isConst || c == 0) checkDivideByZeroI32(rs, r, &done);
5431     masm.quotient32(rs, r, IsUnsigned(true));
5432     masm.bind(&done);
5433 
5434     maybeFreeI32(reserved);
5435     freeI32(rs);
5436     pushI32(r);
5437   }
5438 }
5439 
emitRemainderI32()5440 void BaseCompiler::emitRemainderI32() {
5441   int32_t c;
5442   uint_fast8_t power;
5443   if (popConstPositivePowerOfTwoI32(&c, &power, 1)) {
5444     RegI32 r = popI32();
5445     RegI32 temp = needI32();
5446     moveI32(r, temp);
5447 
5448     Label positive;
5449     masm.branchTest32(Assembler::NotSigned, temp, temp, &positive);
5450     masm.add32(Imm32(c - 1), temp);
5451     masm.bind(&positive);
5452 
5453     masm.rshift32Arithmetic(Imm32(power & 31), temp);
5454     masm.lshift32(Imm32(power & 31), temp);
5455     masm.sub32(temp, r);
5456     freeI32(temp);
5457 
5458     pushI32(r);
5459   } else {
5460     bool isConst = peekConstI32(&c);
5461     RegI32 r, rs, reserved;
5462     pop2xI32ForMulDivI32(&r, &rs, &reserved);
5463 
5464     Label done;
5465     if (!isConst || c == 0) checkDivideByZeroI32(rs, r, &done);
5466     if (!isConst || c == -1)
5467       checkDivideSignedOverflowI32(rs, r, &done, ZeroOnOverflow(true));
5468     masm.remainder32(rs, r, IsUnsigned(false));
5469     masm.bind(&done);
5470 
5471     maybeFreeI32(reserved);
5472     freeI32(rs);
5473     pushI32(r);
5474   }
5475 }
5476 
emitRemainderU32()5477 void BaseCompiler::emitRemainderU32() {
5478   int32_t c;
5479   uint_fast8_t power;
5480   if (popConstPositivePowerOfTwoI32(&c, &power, 1)) {
5481     RegI32 r = popI32();
5482     masm.and32(Imm32(c - 1), r);
5483     pushI32(r);
5484   } else {
5485     bool isConst = peekConstI32(&c);
5486     RegI32 r, rs, reserved;
5487     pop2xI32ForMulDivI32(&r, &rs, &reserved);
5488 
5489     Label done;
5490     if (!isConst || c == 0) checkDivideByZeroI32(rs, r, &done);
5491     masm.remainder32(rs, r, IsUnsigned(true));
5492     masm.bind(&done);
5493 
5494     maybeFreeI32(reserved);
5495     freeI32(rs);
5496     pushI32(r);
5497   }
5498 }
5499 
5500 #ifndef RABALDR_INT_DIV_I64_CALLOUT
emitQuotientI64()5501 void BaseCompiler::emitQuotientI64() {
5502 #ifdef JS_64BIT
5503   int64_t c;
5504   uint_fast8_t power;
5505   if (popConstPositivePowerOfTwoI64(&c, &power, 0)) {
5506     if (power != 0) {
5507       RegI64 r = popI64();
5508       Label positive;
5509       masm.branchTest64(Assembler::NotSigned, r, r, RegI32::Invalid(),
5510                         &positive);
5511       masm.add64(Imm64(c - 1), r);
5512       masm.bind(&positive);
5513 
5514       masm.rshift64Arithmetic(Imm32(power & 63), r);
5515       pushI64(r);
5516     }
5517   } else {
5518     bool isConst = peekConstI64(&c);
5519     RegI64 r, rs, reserved;
5520     pop2xI64ForDivI64(&r, &rs, &reserved);
5521     quotientI64(rs, r, reserved, IsUnsigned(false), isConst, c);
5522     maybeFreeI64(reserved);
5523     freeI64(rs);
5524     pushI64(r);
5525   }
5526 #else
5527   MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
5528 #endif
5529 }
5530 
emitQuotientU64()5531 void BaseCompiler::emitQuotientU64() {
5532 #ifdef JS_64BIT
5533   int64_t c;
5534   uint_fast8_t power;
5535   if (popConstPositivePowerOfTwoI64(&c, &power, 0)) {
5536     if (power != 0) {
5537       RegI64 r = popI64();
5538       masm.rshift64(Imm32(power & 63), r);
5539       pushI64(r);
5540     }
5541   } else {
5542     bool isConst = peekConstI64(&c);
5543     RegI64 r, rs, reserved;
5544     pop2xI64ForDivI64(&r, &rs, &reserved);
5545     quotientI64(rs, r, reserved, IsUnsigned(true), isConst, c);
5546     maybeFreeI64(reserved);
5547     freeI64(rs);
5548     pushI64(r);
5549   }
5550 #else
5551   MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
5552 #endif
5553 }
5554 
emitRemainderI64()5555 void BaseCompiler::emitRemainderI64() {
5556 #ifdef JS_64BIT
5557   int64_t c;
5558   uint_fast8_t power;
5559   if (popConstPositivePowerOfTwoI64(&c, &power, 1)) {
5560     RegI64 r = popI64();
5561     RegI64 temp = needI64();
5562     moveI64(r, temp);
5563 
5564     Label positive;
5565     masm.branchTest64(Assembler::NotSigned, temp, temp, RegI32::Invalid(),
5566                       &positive);
5567     masm.add64(Imm64(c - 1), temp);
5568     masm.bind(&positive);
5569 
5570     masm.rshift64Arithmetic(Imm32(power & 63), temp);
5571     masm.lshift64(Imm32(power & 63), temp);
5572     masm.sub64(temp, r);
5573     freeI64(temp);
5574 
5575     pushI64(r);
5576   } else {
5577     bool isConst = peekConstI64(&c);
5578     RegI64 r, rs, reserved;
5579     pop2xI64ForDivI64(&r, &rs, &reserved);
5580     remainderI64(rs, r, reserved, IsUnsigned(false), isConst, c);
5581     maybeFreeI64(reserved);
5582     freeI64(rs);
5583     pushI64(r);
5584   }
5585 #else
5586   MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
5587 #endif
5588 }
5589 
emitRemainderU64()5590 void BaseCompiler::emitRemainderU64() {
5591 #ifdef JS_64BIT
5592   int64_t c;
5593   uint_fast8_t power;
5594   if (popConstPositivePowerOfTwoI64(&c, &power, 1)) {
5595     RegI64 r = popI64();
5596     masm.and64(Imm64(c - 1), r);
5597     pushI64(r);
5598   } else {
5599     bool isConst = peekConstI64(&c);
5600     RegI64 r, rs, reserved;
5601     pop2xI64ForDivI64(&r, &rs, &reserved);
5602     remainderI64(rs, r, reserved, IsUnsigned(true), isConst, c);
5603     maybeFreeI64(reserved);
5604     freeI64(rs);
5605     pushI64(r);
5606   }
5607 #else
5608   MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
5609 #endif
5610 }
5611 #endif  // RABALDR_INT_DIV_I64_CALLOUT
5612 
emitDivideF32()5613 void BaseCompiler::emitDivideF32() {
5614   RegF32 r, rs;
5615   pop2xF32(&r, &rs);
5616   masm.divFloat32(rs, r);
5617   freeF32(rs);
5618   pushF32(r);
5619 }
5620 
emitDivideF64()5621 void BaseCompiler::emitDivideF64() {
5622   RegF64 r, rs;
5623   pop2xF64(&r, &rs);
5624   masm.divDouble(rs, r);
5625   freeF64(rs);
5626   pushF64(r);
5627 }
5628 
emitMinF32()5629 void BaseCompiler::emitMinF32() {
5630   RegF32 r, rs;
5631   pop2xF32(&r, &rs);
5632   // Convert signaling NaN to quiet NaNs.
5633   //
5634   // TODO / OPTIMIZE (bug 1316824): Don't do this if one of the operands
5635   // is known to be a constant.
5636   ScratchF32 zero(*this);
5637   moveImmF32(0.f, zero);
5638   masm.subFloat32(zero, r);
5639   masm.subFloat32(zero, rs);
5640   masm.minFloat32(rs, r, HandleNaNSpecially(true));
5641   freeF32(rs);
5642   pushF32(r);
5643 }
5644 
emitMaxF32()5645 void BaseCompiler::emitMaxF32() {
5646   RegF32 r, rs;
5647   pop2xF32(&r, &rs);
5648   // Convert signaling NaN to quiet NaNs.
5649   //
5650   // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
5651   ScratchF32 zero(*this);
5652   moveImmF32(0.f, zero);
5653   masm.subFloat32(zero, r);
5654   masm.subFloat32(zero, rs);
5655   masm.maxFloat32(rs, r, HandleNaNSpecially(true));
5656   freeF32(rs);
5657   pushF32(r);
5658 }
5659 
emitMinF64()5660 void BaseCompiler::emitMinF64() {
5661   RegF64 r, rs;
5662   pop2xF64(&r, &rs);
5663   // Convert signaling NaN to quiet NaNs.
5664   //
5665   // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
5666   ScratchF64 zero(*this);
5667   moveImmF64(0, zero);
5668   masm.subDouble(zero, r);
5669   masm.subDouble(zero, rs);
5670   masm.minDouble(rs, r, HandleNaNSpecially(true));
5671   freeF64(rs);
5672   pushF64(r);
5673 }
5674 
emitMaxF64()5675 void BaseCompiler::emitMaxF64() {
5676   RegF64 r, rs;
5677   pop2xF64(&r, &rs);
5678   // Convert signaling NaN to quiet NaNs.
5679   //
5680   // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
5681   ScratchF64 zero(*this);
5682   moveImmF64(0, zero);
5683   masm.subDouble(zero, r);
5684   masm.subDouble(zero, rs);
5685   masm.maxDouble(rs, r, HandleNaNSpecially(true));
5686   freeF64(rs);
5687   pushF64(r);
5688 }
5689 
emitCopysignF32()5690 void BaseCompiler::emitCopysignF32() {
5691   RegF32 r, rs;
5692   pop2xF32(&r, &rs);
5693   RegI32 temp0 = needI32();
5694   RegI32 temp1 = needI32();
5695   masm.moveFloat32ToGPR(r, temp0);
5696   masm.moveFloat32ToGPR(rs, temp1);
5697   masm.and32(Imm32(INT32_MAX), temp0);
5698   masm.and32(Imm32(INT32_MIN), temp1);
5699   masm.or32(temp1, temp0);
5700   masm.moveGPRToFloat32(temp0, r);
5701   freeI32(temp0);
5702   freeI32(temp1);
5703   freeF32(rs);
5704   pushF32(r);
5705 }
5706 
emitCopysignF64()5707 void BaseCompiler::emitCopysignF64() {
5708   RegF64 r, rs;
5709   pop2xF64(&r, &rs);
5710   RegI64 temp0 = needI64();
5711   RegI64 temp1 = needI64();
5712   masm.moveDoubleToGPR64(r, temp0);
5713   masm.moveDoubleToGPR64(rs, temp1);
5714   masm.and64(Imm64(INT64_MAX), temp0);
5715   masm.and64(Imm64(INT64_MIN), temp1);
5716   masm.or64(temp1, temp0);
5717   masm.moveGPR64ToDouble(temp0, r);
5718   freeI64(temp0);
5719   freeI64(temp1);
5720   freeF64(rs);
5721   pushF64(r);
5722 }
5723 
emitOrI32()5724 void BaseCompiler::emitOrI32() {
5725   int32_t c;
5726   if (popConstI32(&c)) {
5727     RegI32 r = popI32();
5728     masm.or32(Imm32(c), r);
5729     pushI32(r);
5730   } else {
5731     RegI32 r, rs;
5732     pop2xI32(&r, &rs);
5733     masm.or32(rs, r);
5734     freeI32(rs);
5735     pushI32(r);
5736   }
5737 }
5738 
emitOrI64()5739 void BaseCompiler::emitOrI64() {
5740   int64_t c;
5741   if (popConstI64(&c)) {
5742     RegI64 r = popI64();
5743     masm.or64(Imm64(c), r);
5744     pushI64(r);
5745   } else {
5746     RegI64 r, rs;
5747     pop2xI64(&r, &rs);
5748     masm.or64(rs, r);
5749     freeI64(rs);
5750     pushI64(r);
5751   }
5752 }
5753 
emitAndI32()5754 void BaseCompiler::emitAndI32() {
5755   int32_t c;
5756   if (popConstI32(&c)) {
5757     RegI32 r = popI32();
5758     masm.and32(Imm32(c), r);
5759     pushI32(r);
5760   } else {
5761     RegI32 r, rs;
5762     pop2xI32(&r, &rs);
5763     masm.and32(rs, r);
5764     freeI32(rs);
5765     pushI32(r);
5766   }
5767 }
5768 
emitAndI64()5769 void BaseCompiler::emitAndI64() {
5770   int64_t c;
5771   if (popConstI64(&c)) {
5772     RegI64 r = popI64();
5773     masm.and64(Imm64(c), r);
5774     pushI64(r);
5775   } else {
5776     RegI64 r, rs;
5777     pop2xI64(&r, &rs);
5778     masm.and64(rs, r);
5779     freeI64(rs);
5780     pushI64(r);
5781   }
5782 }
5783 
emitXorI32()5784 void BaseCompiler::emitXorI32() {
5785   int32_t c;
5786   if (popConstI32(&c)) {
5787     RegI32 r = popI32();
5788     masm.xor32(Imm32(c), r);
5789     pushI32(r);
5790   } else {
5791     RegI32 r, rs;
5792     pop2xI32(&r, &rs);
5793     masm.xor32(rs, r);
5794     freeI32(rs);
5795     pushI32(r);
5796   }
5797 }
5798 
emitXorI64()5799 void BaseCompiler::emitXorI64() {
5800   int64_t c;
5801   if (popConstI64(&c)) {
5802     RegI64 r = popI64();
5803     masm.xor64(Imm64(c), r);
5804     pushI64(r);
5805   } else {
5806     RegI64 r, rs;
5807     pop2xI64(&r, &rs);
5808     masm.xor64(rs, r);
5809     freeI64(rs);
5810     pushI64(r);
5811   }
5812 }
5813 
emitShlI32()5814 void BaseCompiler::emitShlI32() {
5815   int32_t c;
5816   if (popConstI32(&c)) {
5817     RegI32 r = popI32();
5818     masm.lshift32(Imm32(c & 31), r);
5819     pushI32(r);
5820   } else {
5821     RegI32 r, rs;
5822     pop2xI32ForShiftOrRotate(&r, &rs);
5823     maskShiftCount32(rs);
5824     masm.lshift32(rs, r);
5825     freeI32(rs);
5826     pushI32(r);
5827   }
5828 }
5829 
emitShlI64()5830 void BaseCompiler::emitShlI64() {
5831   int64_t c;
5832   if (popConstI64(&c)) {
5833     RegI64 r = popI64();
5834     masm.lshift64(Imm32(c & 63), r);
5835     pushI64(r);
5836   } else {
5837     RegI64 r, rs;
5838     pop2xI64ForShiftOrRotate(&r, &rs);
5839     masm.lshift64(lowPart(rs), r);
5840     freeI64(rs);
5841     pushI64(r);
5842   }
5843 }
5844 
emitShrI32()5845 void BaseCompiler::emitShrI32() {
5846   int32_t c;
5847   if (popConstI32(&c)) {
5848     RegI32 r = popI32();
5849     masm.rshift32Arithmetic(Imm32(c & 31), r);
5850     pushI32(r);
5851   } else {
5852     RegI32 r, rs;
5853     pop2xI32ForShiftOrRotate(&r, &rs);
5854     maskShiftCount32(rs);
5855     masm.rshift32Arithmetic(rs, r);
5856     freeI32(rs);
5857     pushI32(r);
5858   }
5859 }
5860 
emitShrI64()5861 void BaseCompiler::emitShrI64() {
5862   int64_t c;
5863   if (popConstI64(&c)) {
5864     RegI64 r = popI64();
5865     masm.rshift64Arithmetic(Imm32(c & 63), r);
5866     pushI64(r);
5867   } else {
5868     RegI64 r, rs;
5869     pop2xI64ForShiftOrRotate(&r, &rs);
5870     masm.rshift64Arithmetic(lowPart(rs), r);
5871     freeI64(rs);
5872     pushI64(r);
5873   }
5874 }
5875 
emitShrU32()5876 void BaseCompiler::emitShrU32() {
5877   int32_t c;
5878   if (popConstI32(&c)) {
5879     RegI32 r = popI32();
5880     masm.rshift32(Imm32(c & 31), r);
5881     pushI32(r);
5882   } else {
5883     RegI32 r, rs;
5884     pop2xI32ForShiftOrRotate(&r, &rs);
5885     maskShiftCount32(rs);
5886     masm.rshift32(rs, r);
5887     freeI32(rs);
5888     pushI32(r);
5889   }
5890 }
5891 
emitShrU64()5892 void BaseCompiler::emitShrU64() {
5893   int64_t c;
5894   if (popConstI64(&c)) {
5895     RegI64 r = popI64();
5896     masm.rshift64(Imm32(c & 63), r);
5897     pushI64(r);
5898   } else {
5899     RegI64 r, rs;
5900     pop2xI64ForShiftOrRotate(&r, &rs);
5901     masm.rshift64(lowPart(rs), r);
5902     freeI64(rs);
5903     pushI64(r);
5904   }
5905 }
5906 
emitRotrI32()5907 void BaseCompiler::emitRotrI32() {
5908   int32_t c;
5909   if (popConstI32(&c)) {
5910     RegI32 r = popI32();
5911     masm.rotateRight(Imm32(c & 31), r, r);
5912     pushI32(r);
5913   } else {
5914     RegI32 r, rs;
5915     pop2xI32ForShiftOrRotate(&r, &rs);
5916     masm.rotateRight(rs, r, r);
5917     freeI32(rs);
5918     pushI32(r);
5919   }
5920 }
5921 
emitRotrI64()5922 void BaseCompiler::emitRotrI64() {
5923   int64_t c;
5924   if (popConstI64(&c)) {
5925     RegI64 r = popI64();
5926     RegI32 temp = needRotate64Temp();
5927     masm.rotateRight64(Imm32(c & 63), r, r, temp);
5928     maybeFreeI32(temp);
5929     pushI64(r);
5930   } else {
5931     RegI64 r, rs;
5932     pop2xI64ForShiftOrRotate(&r, &rs);
5933     masm.rotateRight64(lowPart(rs), r, r, maybeHighPart(rs));
5934     freeI64(rs);
5935     pushI64(r);
5936   }
5937 }
5938 
emitRotlI32()5939 void BaseCompiler::emitRotlI32() {
5940   int32_t c;
5941   if (popConstI32(&c)) {
5942     RegI32 r = popI32();
5943     masm.rotateLeft(Imm32(c & 31), r, r);
5944     pushI32(r);
5945   } else {
5946     RegI32 r, rs;
5947     pop2xI32ForShiftOrRotate(&r, &rs);
5948     masm.rotateLeft(rs, r, r);
5949     freeI32(rs);
5950     pushI32(r);
5951   }
5952 }
5953 
emitRotlI64()5954 void BaseCompiler::emitRotlI64() {
5955   int64_t c;
5956   if (popConstI64(&c)) {
5957     RegI64 r = popI64();
5958     RegI32 temp = needRotate64Temp();
5959     masm.rotateLeft64(Imm32(c & 63), r, r, temp);
5960     maybeFreeI32(temp);
5961     pushI64(r);
5962   } else {
5963     RegI64 r, rs;
5964     pop2xI64ForShiftOrRotate(&r, &rs);
5965     masm.rotateLeft64(lowPart(rs), r, r, maybeHighPart(rs));
5966     freeI64(rs);
5967     pushI64(r);
5968   }
5969 }
5970 
emitEqzI32()5971 void BaseCompiler::emitEqzI32() {
5972   if (sniffConditionalControlEqz(ValType::I32)) return;
5973 
5974   RegI32 r = popI32();
5975   masm.cmp32Set(Assembler::Equal, r, Imm32(0), r);
5976   pushI32(r);
5977 }
5978 
emitEqzI64()5979 void BaseCompiler::emitEqzI64() {
5980   if (sniffConditionalControlEqz(ValType::I64)) return;
5981 
5982   RegI64 rs = popI64();
5983   RegI32 rd = fromI64(rs);
5984   eqz64(rs, rd);
5985   freeI64Except(rs, rd);
5986   pushI32(rd);
5987 }
5988 
emitClzI32()5989 void BaseCompiler::emitClzI32() {
5990   RegI32 r = popI32();
5991   masm.clz32(r, r, IsKnownNotZero(false));
5992   pushI32(r);
5993 }
5994 
emitClzI64()5995 void BaseCompiler::emitClzI64() {
5996   RegI64 r = popI64();
5997   masm.clz64(r, lowPart(r));
5998   maybeClearHighPart(r);
5999   pushI64(r);
6000 }
6001 
emitCtzI32()6002 void BaseCompiler::emitCtzI32() {
6003   RegI32 r = popI32();
6004   masm.ctz32(r, r, IsKnownNotZero(false));
6005   pushI32(r);
6006 }
6007 
emitCtzI64()6008 void BaseCompiler::emitCtzI64() {
6009   RegI64 r = popI64();
6010   masm.ctz64(r, lowPart(r));
6011   maybeClearHighPart(r);
6012   pushI64(r);
6013 }
6014 
emitPopcntI32()6015 void BaseCompiler::emitPopcntI32() {
6016   RegI32 r = popI32();
6017   RegI32 temp = needPopcnt32Temp();
6018   masm.popcnt32(r, r, temp);
6019   maybeFreeI32(temp);
6020   pushI32(r);
6021 }
6022 
emitPopcntI64()6023 void BaseCompiler::emitPopcntI64() {
6024   RegI64 r = popI64();
6025   RegI32 temp = needPopcnt64Temp();
6026   masm.popcnt64(r, r, temp);
6027   maybeFreeI32(temp);
6028   pushI64(r);
6029 }
6030 
emitAbsF32()6031 void BaseCompiler::emitAbsF32() {
6032   RegF32 r = popF32();
6033   masm.absFloat32(r, r);
6034   pushF32(r);
6035 }
6036 
emitAbsF64()6037 void BaseCompiler::emitAbsF64() {
6038   RegF64 r = popF64();
6039   masm.absDouble(r, r);
6040   pushF64(r);
6041 }
6042 
emitNegateF32()6043 void BaseCompiler::emitNegateF32() {
6044   RegF32 r = popF32();
6045   masm.negateFloat(r);
6046   pushF32(r);
6047 }
6048 
emitNegateF64()6049 void BaseCompiler::emitNegateF64() {
6050   RegF64 r = popF64();
6051   masm.negateDouble(r);
6052   pushF64(r);
6053 }
6054 
emitSqrtF32()6055 void BaseCompiler::emitSqrtF32() {
6056   RegF32 r = popF32();
6057   masm.sqrtFloat32(r, r);
6058   pushF32(r);
6059 }
6060 
emitSqrtF64()6061 void BaseCompiler::emitSqrtF64() {
6062   RegF64 r = popF64();
6063   masm.sqrtDouble(r, r);
6064   pushF64(r);
6065 }
6066 
6067 template <TruncFlags flags>
emitTruncateF32ToI32()6068 bool BaseCompiler::emitTruncateF32ToI32() {
6069   RegF32 rs = popF32();
6070   RegI32 rd = needI32();
6071   if (!truncateF32ToI32(rs, rd, flags)) return false;
6072   freeF32(rs);
6073   pushI32(rd);
6074   return true;
6075 }
6076 
6077 template <TruncFlags flags>
emitTruncateF64ToI32()6078 bool BaseCompiler::emitTruncateF64ToI32() {
6079   RegF64 rs = popF64();
6080   RegI32 rd = needI32();
6081   if (!truncateF64ToI32(rs, rd, flags)) return false;
6082   freeF64(rs);
6083   pushI32(rd);
6084   return true;
6085 }
6086 
6087 #ifndef RABALDR_FLOAT_TO_I64_CALLOUT
6088 template <TruncFlags flags>
emitTruncateF32ToI64()6089 bool BaseCompiler::emitTruncateF32ToI64() {
6090   RegF32 rs = popF32();
6091   RegI64 rd = needI64();
6092   RegF64 temp = needTempForFloatingToI64(flags);
6093   if (!truncateF32ToI64(rs, rd, flags, temp)) return false;
6094   maybeFreeF64(temp);
6095   freeF32(rs);
6096   pushI64(rd);
6097   return true;
6098 }
6099 
6100 template <TruncFlags flags>
emitTruncateF64ToI64()6101 bool BaseCompiler::emitTruncateF64ToI64() {
6102   RegF64 rs = popF64();
6103   RegI64 rd = needI64();
6104   RegF64 temp = needTempForFloatingToI64(flags);
6105   if (!truncateF64ToI64(rs, rd, flags, temp)) return false;
6106   maybeFreeF64(temp);
6107   freeF64(rs);
6108   pushI64(rd);
6109   return true;
6110 }
6111 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
6112 
emitWrapI64ToI32()6113 void BaseCompiler::emitWrapI64ToI32() {
6114   RegI64 rs = popI64();
6115   RegI32 rd = fromI64(rs);
6116   masm.move64To32(rs, rd);
6117   freeI64Except(rs, rd);
6118   pushI32(rd);
6119 }
6120 
emitExtendI32_8()6121 void BaseCompiler::emitExtendI32_8() {
6122   RegI32 r = popI32();
6123 #ifdef JS_CODEGEN_X86
6124   if (!ra.isSingleByteI32(r)) {
6125     ScratchI8 scratch(*this);
6126     moveI32(r, scratch);
6127     masm.move8SignExtend(scratch, r);
6128     pushI32(r);
6129     return;
6130   }
6131 #endif
6132   masm.move8SignExtend(r, r);
6133   pushI32(r);
6134 }
6135 
emitExtendI32_16()6136 void BaseCompiler::emitExtendI32_16() {
6137   RegI32 r = popI32();
6138   masm.move16SignExtend(r, r);
6139   pushI32(r);
6140 }
6141 
emitExtendI64_8()6142 void BaseCompiler::emitExtendI64_8() {
6143   RegI64 r;
6144   popI64ForSignExtendI64(&r);
6145   masm.move8To64SignExtend(lowPart(r), r);
6146   pushI64(r);
6147 }
6148 
emitExtendI64_16()6149 void BaseCompiler::emitExtendI64_16() {
6150   RegI64 r;
6151   popI64ForSignExtendI64(&r);
6152   masm.move16To64SignExtend(lowPart(r), r);
6153   pushI64(r);
6154 }
6155 
emitExtendI64_32()6156 void BaseCompiler::emitExtendI64_32() {
6157   RegI64 r;
6158   popI64ForSignExtendI64(&r);
6159   masm.move32To64SignExtend(lowPart(r), r);
6160   pushI64(r);
6161 }
6162 
emitExtendI32ToI64()6163 void BaseCompiler::emitExtendI32ToI64() {
6164   RegI64 r;
6165   popI32ForSignExtendI64(&r);
6166   masm.move32To64SignExtend(lowPart(r), r);
6167   pushI64(r);
6168 }
6169 
emitExtendU32ToI64()6170 void BaseCompiler::emitExtendU32ToI64() {
6171   RegI32 rs = popI32();
6172   RegI64 rd = widenI32(rs);
6173   masm.move32To64ZeroExtend(rs, rd);
6174   pushI64(rd);
6175 }
6176 
emitReinterpretF32AsI32()6177 void BaseCompiler::emitReinterpretF32AsI32() {
6178   RegF32 rs = popF32();
6179   RegI32 rd = needI32();
6180   masm.moveFloat32ToGPR(rs, rd);
6181   freeF32(rs);
6182   pushI32(rd);
6183 }
6184 
emitReinterpretF64AsI64()6185 void BaseCompiler::emitReinterpretF64AsI64() {
6186   RegF64 rs = popF64();
6187   RegI64 rd = needI64();
6188   masm.moveDoubleToGPR64(rs, rd);
6189   freeF64(rs);
6190   pushI64(rd);
6191 }
6192 
emitConvertF64ToF32()6193 void BaseCompiler::emitConvertF64ToF32() {
6194   RegF64 rs = popF64();
6195   RegF32 rd = needF32();
6196   masm.convertDoubleToFloat32(rs, rd);
6197   freeF64(rs);
6198   pushF32(rd);
6199 }
6200 
emitConvertI32ToF32()6201 void BaseCompiler::emitConvertI32ToF32() {
6202   RegI32 rs = popI32();
6203   RegF32 rd = needF32();
6204   masm.convertInt32ToFloat32(rs, rd);
6205   freeI32(rs);
6206   pushF32(rd);
6207 }
6208 
emitConvertU32ToF32()6209 void BaseCompiler::emitConvertU32ToF32() {
6210   RegI32 rs = popI32();
6211   RegF32 rd = needF32();
6212   masm.convertUInt32ToFloat32(rs, rd);
6213   freeI32(rs);
6214   pushF32(rd);
6215 }
6216 
6217 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
emitConvertI64ToF32()6218 void BaseCompiler::emitConvertI64ToF32() {
6219   RegI64 rs = popI64();
6220   RegF32 rd = needF32();
6221   convertI64ToF32(rs, IsUnsigned(false), rd, RegI32());
6222   freeI64(rs);
6223   pushF32(rd);
6224 }
6225 
emitConvertU64ToF32()6226 void BaseCompiler::emitConvertU64ToF32() {
6227   RegI64 rs = popI64();
6228   RegF32 rd = needF32();
6229   RegI32 temp = needConvertI64ToFloatTemp(ValType::F32, IsUnsigned(true));
6230   convertI64ToF32(rs, IsUnsigned(true), rd, temp);
6231   maybeFreeI32(temp);
6232   freeI64(rs);
6233   pushF32(rd);
6234 }
6235 #endif
6236 
emitConvertF32ToF64()6237 void BaseCompiler::emitConvertF32ToF64() {
6238   RegF32 rs = popF32();
6239   RegF64 rd = needF64();
6240   masm.convertFloat32ToDouble(rs, rd);
6241   freeF32(rs);
6242   pushF64(rd);
6243 }
6244 
emitConvertI32ToF64()6245 void BaseCompiler::emitConvertI32ToF64() {
6246   RegI32 rs = popI32();
6247   RegF64 rd = needF64();
6248   masm.convertInt32ToDouble(rs, rd);
6249   freeI32(rs);
6250   pushF64(rd);
6251 }
6252 
emitConvertU32ToF64()6253 void BaseCompiler::emitConvertU32ToF64() {
6254   RegI32 rs = popI32();
6255   RegF64 rd = needF64();
6256   masm.convertUInt32ToDouble(rs, rd);
6257   freeI32(rs);
6258   pushF64(rd);
6259 }
6260 
6261 #ifndef RABALDR_I64_TO_FLOAT_CALLOUT
emitConvertI64ToF64()6262 void BaseCompiler::emitConvertI64ToF64() {
6263   RegI64 rs = popI64();
6264   RegF64 rd = needF64();
6265   convertI64ToF64(rs, IsUnsigned(false), rd, RegI32());
6266   freeI64(rs);
6267   pushF64(rd);
6268 }
6269 
emitConvertU64ToF64()6270 void BaseCompiler::emitConvertU64ToF64() {
6271   RegI64 rs = popI64();
6272   RegF64 rd = needF64();
6273   RegI32 temp = needConvertI64ToFloatTemp(ValType::F64, IsUnsigned(true));
6274   convertI64ToF64(rs, IsUnsigned(true), rd, temp);
6275   maybeFreeI32(temp);
6276   freeI64(rs);
6277   pushF64(rd);
6278 }
6279 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
6280 
emitReinterpretI32AsF32()6281 void BaseCompiler::emitReinterpretI32AsF32() {
6282   RegI32 rs = popI32();
6283   RegF32 rd = needF32();
6284   masm.moveGPRToFloat32(rs, rd);
6285   freeI32(rs);
6286   pushF32(rd);
6287 }
6288 
emitReinterpretI64AsF64()6289 void BaseCompiler::emitReinterpretI64AsF64() {
6290   RegI64 rs = popI64();
6291   RegF64 rd = needF64();
6292   masm.moveGPR64ToDouble(rs, rd);
6293   freeI64(rs);
6294   pushF64(rd);
6295 }
6296 
6297 template <typename Cond>
sniffConditionalControlCmp(Cond compareOp,ValType operandType)6298 bool BaseCompiler::sniffConditionalControlCmp(Cond compareOp,
6299                                               ValType operandType) {
6300   MOZ_ASSERT(latentOp_ == LatentOp::None,
6301              "Latent comparison state not properly reset");
6302 
6303 #ifdef JS_CODEGEN_X86
6304   // On x86, latent i64 binary comparisons use too many registers: the
6305   // reserved join register and the lhs and rhs operands require six, but we
6306   // only have five.
6307   if (operandType == ValType::I64) return false;
6308 #endif
6309 
6310   OpBytes op;
6311   iter_.peekOp(&op);
6312   switch (op.b0) {
6313     case uint16_t(Op::Select):
6314       MOZ_FALLTHROUGH;
6315     case uint16_t(Op::BrIf):
6316     case uint16_t(Op::If):
6317       setLatentCompare(compareOp, operandType);
6318       return true;
6319     default:
6320       return false;
6321   }
6322 }
6323 
sniffConditionalControlEqz(ValType operandType)6324 bool BaseCompiler::sniffConditionalControlEqz(ValType operandType) {
6325   MOZ_ASSERT(latentOp_ == LatentOp::None,
6326              "Latent comparison state not properly reset");
6327 
6328   OpBytes op;
6329   iter_.peekOp(&op);
6330   switch (op.b0) {
6331     case uint16_t(Op::BrIf):
6332     case uint16_t(Op::Select):
6333     case uint16_t(Op::If):
6334       setLatentEqz(operandType);
6335       return true;
6336     default:
6337       return false;
6338   }
6339 }
6340 
emitBranchSetup(BranchState * b)6341 void BaseCompiler::emitBranchSetup(BranchState* b) {
6342   maybeReserveJoinReg(b->resultType);
6343 
6344   // Set up fields so that emitBranchPerform() need not switch on latentOp_.
6345   switch (latentOp_) {
6346     case LatentOp::None: {
6347       latentIntCmp_ = Assembler::NotEqual;
6348       latentType_ = ValType::I32;
6349       b->i32.lhs = popI32();
6350       b->i32.rhsImm = true;
6351       b->i32.imm = 0;
6352       break;
6353     }
6354     case LatentOp::Compare: {
6355       switch (latentType_) {
6356         case ValType::I32: {
6357           if (popConstI32(&b->i32.imm)) {
6358             b->i32.lhs = popI32();
6359             b->i32.rhsImm = true;
6360           } else {
6361             pop2xI32(&b->i32.lhs, &b->i32.rhs);
6362             b->i32.rhsImm = false;
6363           }
6364           break;
6365         }
6366         case ValType::I64: {
6367           pop2xI64(&b->i64.lhs, &b->i64.rhs);
6368           b->i64.rhsImm = false;
6369           break;
6370         }
6371         case ValType::F32: {
6372           pop2xF32(&b->f32.lhs, &b->f32.rhs);
6373           break;
6374         }
6375         case ValType::F64: {
6376           pop2xF64(&b->f64.lhs, &b->f64.rhs);
6377           break;
6378         }
6379         default: { MOZ_CRASH("Unexpected type for LatentOp::Compare"); }
6380       }
6381       break;
6382     }
6383     case LatentOp::Eqz: {
6384       switch (latentType_) {
6385         case ValType::I32: {
6386           latentIntCmp_ = Assembler::Equal;
6387           b->i32.lhs = popI32();
6388           b->i32.rhsImm = true;
6389           b->i32.imm = 0;
6390           break;
6391         }
6392         case ValType::I64: {
6393           latentIntCmp_ = Assembler::Equal;
6394           b->i64.lhs = popI64();
6395           b->i64.rhsImm = true;
6396           b->i64.imm = 0;
6397           break;
6398         }
6399         default: { MOZ_CRASH("Unexpected type for LatentOp::Eqz"); }
6400       }
6401       break;
6402     }
6403   }
6404 
6405   maybeUnreserveJoinReg(b->resultType);
6406 }
6407 
emitBranchPerform(BranchState * b)6408 void BaseCompiler::emitBranchPerform(BranchState* b) {
6409   switch (latentType_) {
6410     case ValType::I32: {
6411       if (b->i32.rhsImm) {
6412         jumpConditionalWithJoinReg(b, latentIntCmp_, b->i32.lhs,
6413                                    Imm32(b->i32.imm));
6414       } else {
6415         jumpConditionalWithJoinReg(b, latentIntCmp_, b->i32.lhs, b->i32.rhs);
6416         freeI32(b->i32.rhs);
6417       }
6418       freeI32(b->i32.lhs);
6419       break;
6420     }
6421     case ValType::I64: {
6422       if (b->i64.rhsImm) {
6423         jumpConditionalWithJoinReg(b, latentIntCmp_, b->i64.lhs,
6424                                    Imm64(b->i64.imm));
6425       } else {
6426         jumpConditionalWithJoinReg(b, latentIntCmp_, b->i64.lhs, b->i64.rhs);
6427         freeI64(b->i64.rhs);
6428       }
6429       freeI64(b->i64.lhs);
6430       break;
6431     }
6432     case ValType::F32: {
6433       jumpConditionalWithJoinReg(b, latentDoubleCmp_, b->f32.lhs, b->f32.rhs);
6434       freeF32(b->f32.lhs);
6435       freeF32(b->f32.rhs);
6436       break;
6437     }
6438     case ValType::F64: {
6439       jumpConditionalWithJoinReg(b, latentDoubleCmp_, b->f64.lhs, b->f64.rhs);
6440       freeF64(b->f64.lhs);
6441       freeF64(b->f64.rhs);
6442       break;
6443     }
6444     default: { MOZ_CRASH("Unexpected type for LatentOp::Compare"); }
6445   }
6446   resetLatentOp();
6447 }
6448 
6449 // For blocks and loops and ifs:
6450 //
6451 //  - Sync the value stack before going into the block in order to simplify exit
6452 //    from the block: all exits from the block can assume that there are no
6453 //    live registers except the one carrying the exit value.
6454 //  - The block can accumulate a number of dead values on the stacks, so when
6455 //    branching out of the block or falling out at the end be sure to
6456 //    pop the appropriate stacks back to where they were on entry, while
6457 //    preserving the exit value.
6458 //  - A continue branch in a loop is much like an exit branch, but the branch
6459 //    value must not be preserved.
6460 //  - The exit value is always in a designated join register (type dependent).
6461 
emitBlock()6462 bool BaseCompiler::emitBlock() {
6463   if (!iter_.readBlock()) return false;
6464 
6465   if (!deadCode_) sync();  // Simplifies branching out from block
6466 
6467   initControl(controlItem());
6468 
6469   return true;
6470 }
6471 
endBlock(ExprType type)6472 void BaseCompiler::endBlock(ExprType type) {
6473   Control& block = controlItem();
6474 
6475   // Save the value.
6476   Maybe<AnyReg> r;
6477   if (!deadCode_) {
6478     r = popJoinRegUnlessVoid(type);
6479     block.bceSafeOnExit &= bceSafe_;
6480   }
6481 
6482   // Leave the block.
6483   fr.popStackOnBlockExit(block.stackHeight, deadCode_);
6484   popValueStackTo(block.stackSize);
6485 
6486   // Bind after cleanup: branches out will have popped the stack.
6487   if (block.label.used()) {
6488     masm.bind(&block.label);
6489     // No value was provided by the fallthrough but the branch out will
6490     // have stored one in joinReg, so capture that.
6491     if (deadCode_) r = captureJoinRegUnlessVoid(type);
6492     deadCode_ = false;
6493   }
6494 
6495   bceSafe_ = block.bceSafeOnExit;
6496 
6497   // Retain the value stored in joinReg by all paths, if there are any.
6498   if (!deadCode_) pushJoinRegUnlessVoid(r);
6499 }
6500 
emitLoop()6501 bool BaseCompiler::emitLoop() {
6502   if (!iter_.readLoop()) return false;
6503 
6504   if (!deadCode_) sync();  // Simplifies branching out from block
6505 
6506   initControl(controlItem());
6507   bceSafe_ = 0;
6508 
6509   if (!deadCode_) {
6510     masm.nopAlign(CodeAlignment);
6511     masm.bind(&controlItem(0).label);
6512     addInterruptCheck();
6513   }
6514 
6515   return true;
6516 }
6517 
endLoop(ExprType type)6518 void BaseCompiler::endLoop(ExprType type) {
6519   Control& block = controlItem();
6520 
6521   Maybe<AnyReg> r;
6522   if (!deadCode_) {
6523     r = popJoinRegUnlessVoid(type);
6524     // block.bceSafeOnExit need not be updated because it won't be used for
6525     // the fallthrough path.
6526   }
6527 
6528   fr.popStackOnBlockExit(block.stackHeight, deadCode_);
6529   popValueStackTo(block.stackSize);
6530 
6531   // bceSafe_ stays the same along the fallthrough path because branches to
6532   // loops branch to the top.
6533 
6534   // Retain the value stored in joinReg by all paths.
6535   if (!deadCode_) pushJoinRegUnlessVoid(r);
6536 }
6537 
6538 // The bodies of the "then" and "else" arms can be arbitrary sequences
6539 // of expressions, they push control and increment the nesting and can
6540 // even be targeted by jumps.  A branch to the "if" block branches to
6541 // the exit of the if, ie, it's like "break".  Consider:
6542 //
6543 //      (func (result i32)
6544 //       (if (i32.const 1)
6545 //           (begin (br 1) (unreachable))
6546 //           (begin (unreachable)))
6547 //       (i32.const 1))
6548 //
6549 // The branch causes neither of the unreachable expressions to be
6550 // evaluated.
6551 
emitIf()6552 bool BaseCompiler::emitIf() {
6553   Nothing unused_cond;
6554   if (!iter_.readIf(&unused_cond)) return false;
6555 
6556   BranchState b(&controlItem().otherLabel, BranchState::NoPop,
6557                 InvertBranch(true));
6558   if (!deadCode_) {
6559     emitBranchSetup(&b);
6560     sync();
6561   } else {
6562     resetLatentOp();
6563   }
6564 
6565   initControl(controlItem());
6566 
6567   if (!deadCode_) emitBranchPerform(&b);
6568 
6569   return true;
6570 }
6571 
endIfThen()6572 void BaseCompiler::endIfThen() {
6573   Control& ifThen = controlItem();
6574 
6575   fr.popStackOnBlockExit(ifThen.stackHeight, deadCode_);
6576   popValueStackTo(ifThen.stackSize);
6577 
6578   if (ifThen.otherLabel.used()) masm.bind(&ifThen.otherLabel);
6579 
6580   if (ifThen.label.used()) masm.bind(&ifThen.label);
6581 
6582   if (!deadCode_) ifThen.bceSafeOnExit &= bceSafe_;
6583 
6584   deadCode_ = ifThen.deadOnArrival;
6585 
6586   bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry;
6587 }
6588 
emitElse()6589 bool BaseCompiler::emitElse() {
6590   ExprType thenType;
6591   Nothing unused_thenValue;
6592 
6593   if (!iter_.readElse(&thenType, &unused_thenValue)) return false;
6594 
6595   Control& ifThenElse = controlItem(0);
6596 
6597   // See comment in endIfThenElse, below.
6598 
6599   // Exit the "then" branch.
6600 
6601   ifThenElse.deadThenBranch = deadCode_;
6602 
6603   Maybe<AnyReg> r;
6604   if (!deadCode_) r = popJoinRegUnlessVoid(thenType);
6605 
6606   fr.popStackOnBlockExit(ifThenElse.stackHeight, deadCode_);
6607   popValueStackTo(ifThenElse.stackSize);
6608 
6609   if (!deadCode_) masm.jump(&ifThenElse.label);
6610 
6611   if (ifThenElse.otherLabel.used()) masm.bind(&ifThenElse.otherLabel);
6612 
6613   // Reset to the "else" branch.
6614 
6615   if (!deadCode_) {
6616     freeJoinRegUnlessVoid(r);
6617     ifThenElse.bceSafeOnExit &= bceSafe_;
6618   }
6619 
6620   deadCode_ = ifThenElse.deadOnArrival;
6621   bceSafe_ = ifThenElse.bceSafeOnEntry;
6622 
6623   return true;
6624 }
6625 
endIfThenElse(ExprType type)6626 void BaseCompiler::endIfThenElse(ExprType type) {
6627   Control& ifThenElse = controlItem();
6628 
6629   // The expression type is not a reliable guide to what we'll find
6630   // on the stack, we could have (if E (i32.const 1) (unreachable))
6631   // in which case the "else" arm is AnyType but the type of the
6632   // full expression is I32.  So restore whatever's there, not what
6633   // we want to find there.  The "then" arm has the same constraint.
6634 
6635   Maybe<AnyReg> r;
6636   if (!deadCode_) {
6637     r = popJoinRegUnlessVoid(type);
6638     ifThenElse.bceSafeOnExit &= bceSafe_;
6639   }
6640 
6641   fr.popStackOnBlockExit(ifThenElse.stackHeight, deadCode_);
6642   popValueStackTo(ifThenElse.stackSize);
6643 
6644   if (ifThenElse.label.used()) masm.bind(&ifThenElse.label);
6645 
6646   bool joinLive =
6647       !ifThenElse.deadOnArrival &&
6648       (!ifThenElse.deadThenBranch || !deadCode_ || ifThenElse.label.bound());
6649 
6650   if (joinLive) {
6651     // No value was provided by the "then" path but capture the one
6652     // provided by the "else" path.
6653     if (deadCode_) r = captureJoinRegUnlessVoid(type);
6654     deadCode_ = false;
6655   }
6656 
6657   bceSafe_ = ifThenElse.bceSafeOnExit;
6658 
6659   if (!deadCode_) pushJoinRegUnlessVoid(r);
6660 }
6661 
emitEnd()6662 bool BaseCompiler::emitEnd() {
6663   LabelKind kind;
6664   ExprType type;
6665   Nothing unused_value;
6666   if (!iter_.readEnd(&kind, &type, &unused_value)) return false;
6667 
6668   switch (kind) {
6669     case LabelKind::Block:
6670       endBlock(type);
6671       break;
6672     case LabelKind::Loop:
6673       endLoop(type);
6674       break;
6675     case LabelKind::Then:
6676       endIfThen();
6677       break;
6678     case LabelKind::Else:
6679       endIfThenElse(type);
6680       break;
6681   }
6682 
6683   iter_.popEnd();
6684 
6685   return true;
6686 }
6687 
emitBr()6688 bool BaseCompiler::emitBr() {
6689   uint32_t relativeDepth;
6690   ExprType type;
6691   Nothing unused_value;
6692   if (!iter_.readBr(&relativeDepth, &type, &unused_value)) return false;
6693 
6694   if (deadCode_) return true;
6695 
6696   Control& target = controlItem(relativeDepth);
6697   target.bceSafeOnExit &= bceSafe_;
6698 
6699   // Save any value in the designated join register, where the
6700   // normal block exit code will also leave it.
6701 
6702   Maybe<AnyReg> r = popJoinRegUnlessVoid(type);
6703 
6704   fr.popStackBeforeBranch(target.stackHeight);
6705   masm.jump(&target.label);
6706 
6707   // The register holding the join value is free for the remainder
6708   // of this block.
6709 
6710   freeJoinRegUnlessVoid(r);
6711 
6712   deadCode_ = true;
6713 
6714   return true;
6715 }
6716 
emitBrIf()6717 bool BaseCompiler::emitBrIf() {
6718   uint32_t relativeDepth;
6719   ExprType type;
6720   Nothing unused_value, unused_condition;
6721   if (!iter_.readBrIf(&relativeDepth, &type, &unused_value, &unused_condition))
6722     return false;
6723 
6724   if (deadCode_) {
6725     resetLatentOp();
6726     return true;
6727   }
6728 
6729   Control& target = controlItem(relativeDepth);
6730   target.bceSafeOnExit &= bceSafe_;
6731 
6732   BranchState b(&target.label, target.stackHeight, InvertBranch(false), type);
6733   emitBranchSetup(&b);
6734   emitBranchPerform(&b);
6735 
6736   return true;
6737 }
6738 
emitBrTable()6739 bool BaseCompiler::emitBrTable() {
6740   Uint32Vector depths;
6741   uint32_t defaultDepth;
6742   ExprType branchValueType;
6743   Nothing unused_value, unused_index;
6744   if (!iter_.readBrTable(&depths, &defaultDepth, &branchValueType,
6745                          &unused_value, &unused_index))
6746     return false;
6747 
6748   if (deadCode_) return true;
6749 
6750   // Don't use joinReg for rc
6751   maybeReserveJoinRegI(branchValueType);
6752 
6753   // Table switch value always on top.
6754   RegI32 rc = popI32();
6755 
6756   maybeUnreserveJoinRegI(branchValueType);
6757 
6758   Maybe<AnyReg> r = popJoinRegUnlessVoid(branchValueType);
6759 
6760   Label dispatchCode;
6761   masm.branch32(Assembler::Below, rc, Imm32(depths.length()), &dispatchCode);
6762 
6763   // This is the out-of-range stub.  rc is dead here but we don't need it.
6764 
6765   fr.popStackBeforeBranch(controlItem(defaultDepth).stackHeight);
6766   controlItem(defaultDepth).bceSafeOnExit &= bceSafe_;
6767   masm.jump(&controlItem(defaultDepth).label);
6768 
6769   // Emit stubs.  rc is dead in all of these but we don't need it.
6770   //
6771   // The labels in the vector are in the TempAllocator and will
6772   // be freed by and by.
6773   //
6774   // TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we
6775   // can, don't emit an intermediate stub.
6776 
6777   LabelVector stubs;
6778   if (!stubs.reserve(depths.length())) return false;
6779 
6780   for (uint32_t depth : depths) {
6781     stubs.infallibleEmplaceBack(NonAssertingLabel());
6782     masm.bind(&stubs.back());
6783     fr.popStackBeforeBranch(controlItem(depth).stackHeight);
6784     controlItem(depth).bceSafeOnExit &= bceSafe_;
6785     masm.jump(&controlItem(depth).label);
6786   }
6787 
6788   // Emit table.
6789 
6790   Label theTable;
6791   jumpTable(stubs, &theTable);
6792 
6793   // Emit indirect jump.  rc is live here.
6794 
6795   tableSwitch(&theTable, rc, &dispatchCode);
6796 
6797   deadCode_ = true;
6798 
6799   // Clean up.
6800 
6801   freeI32(rc);
6802   freeJoinRegUnlessVoid(r);
6803 
6804   return true;
6805 }
6806 
emitDrop()6807 bool BaseCompiler::emitDrop() {
6808   if (!iter_.readDrop()) return false;
6809 
6810   if (deadCode_) return true;
6811 
6812   dropValue();
6813   return true;
6814 }
6815 
doReturn(ExprType type,bool popStack)6816 void BaseCompiler::doReturn(ExprType type, bool popStack) {
6817   switch (type) {
6818     case ExprType::Void: {
6819       returnCleanup(popStack);
6820       break;
6821     }
6822     case ExprType::I32: {
6823       RegI32 rv = popI32(RegI32(ReturnReg));
6824       returnCleanup(popStack);
6825       freeI32(rv);
6826       break;
6827     }
6828     case ExprType::I64: {
6829       RegI64 rv = popI64(RegI64(ReturnReg64));
6830       returnCleanup(popStack);
6831       freeI64(rv);
6832       break;
6833     }
6834     case ExprType::F64: {
6835       RegF64 rv = popF64(RegF64(ReturnDoubleReg));
6836       returnCleanup(popStack);
6837       freeF64(rv);
6838       break;
6839     }
6840     case ExprType::F32: {
6841       RegF32 rv = popF32(RegF32(ReturnFloat32Reg));
6842       returnCleanup(popStack);
6843       freeF32(rv);
6844       break;
6845     }
6846     default: { MOZ_CRASH("Function return type"); }
6847   }
6848 }
6849 
emitReturn()6850 bool BaseCompiler::emitReturn() {
6851   Nothing unused_value;
6852   if (!iter_.readReturn(&unused_value)) return false;
6853 
6854   if (deadCode_) return true;
6855 
6856   doReturn(sig().ret(), PopStack(true));
6857   deadCode_ = true;
6858 
6859   return true;
6860 }
6861 
emitCallArgs(const ValTypeVector & argTypes,FunctionCall & baselineCall)6862 bool BaseCompiler::emitCallArgs(const ValTypeVector& argTypes,
6863                                 FunctionCall& baselineCall) {
6864   MOZ_ASSERT(!deadCode_);
6865 
6866   startCallArgs(baselineCall, stackArgAreaSize(argTypes));
6867 
6868   uint32_t numArgs = argTypes.length();
6869   for (size_t i = 0; i < numArgs; ++i)
6870     passArg(baselineCall, argTypes[i], peek(numArgs - 1 - i));
6871 
6872   masm.loadWasmTlsRegFromFrame();
6873   return true;
6874 }
6875 
pushReturned(const FunctionCall & call,ExprType type)6876 void BaseCompiler::pushReturned(const FunctionCall& call, ExprType type) {
6877   switch (type) {
6878     case ExprType::Void:
6879       MOZ_CRASH("Compiler bug: attempt to push void return");
6880       break;
6881     case ExprType::I32: {
6882       RegI32 rv = captureReturnedI32();
6883       pushI32(rv);
6884       break;
6885     }
6886     case ExprType::I64: {
6887       RegI64 rv = captureReturnedI64();
6888       pushI64(rv);
6889       break;
6890     }
6891     case ExprType::F32: {
6892       RegF32 rv = captureReturnedF32(call);
6893       pushF32(rv);
6894       break;
6895     }
6896     case ExprType::F64: {
6897       RegF64 rv = captureReturnedF64(call);
6898       pushF64(rv);
6899       break;
6900     }
6901     default:
6902       MOZ_CRASH("Function return type");
6903   }
6904 }
6905 
6906 // For now, always sync() at the beginning of the call to easily save live
6907 // values.
6908 //
6909 // TODO / OPTIMIZE (Bug 1316806): We may be able to avoid a full sync(), since
6910 // all we want is to save live registers that won't be saved by the callee or
6911 // that we need for outgoing args - we don't need to sync the locals.  We can
6912 // just push the necessary registers, it'll be like a lightweight sync.
6913 //
6914 // Even some of the pushing may be unnecessary if the registers will be consumed
6915 // by the call, because then what we want is parallel assignment to the argument
6916 // registers or onto the stack for outgoing arguments.  A sync() is just
6917 // simpler.
6918 
emitCall()6919 bool BaseCompiler::emitCall() {
6920   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
6921 
6922   uint32_t funcIndex;
6923   BaseOpIter::ValueVector args_;
6924   if (!iter_.readCall(&funcIndex, &args_)) return false;
6925 
6926   if (deadCode_) return true;
6927 
6928   sync();
6929 
6930   const Sig& sig = *env_.funcSigs[funcIndex];
6931   bool import = env_.funcIsImport(funcIndex);
6932 
6933   uint32_t numArgs = sig.args().length();
6934   size_t stackSpace = stackConsumed(numArgs);
6935 
6936   FunctionCall baselineCall(lineOrBytecode);
6937   beginCall(baselineCall, UseABI::Wasm,
6938             import ? InterModule::True : InterModule::False);
6939 
6940   if (!emitCallArgs(sig.args(), baselineCall)) return false;
6941 
6942   if (import)
6943     callImport(env_.funcImportGlobalDataOffsets[funcIndex], baselineCall);
6944   else
6945     callDefinition(funcIndex, baselineCall);
6946 
6947   endCall(baselineCall, stackSpace);
6948 
6949   popValueStackBy(numArgs);
6950 
6951   if (!IsVoid(sig.ret())) pushReturned(baselineCall, sig.ret());
6952 
6953   return true;
6954 }
6955 
emitCallIndirect()6956 bool BaseCompiler::emitCallIndirect() {
6957   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
6958 
6959   uint32_t sigIndex;
6960   Nothing callee_;
6961   BaseOpIter::ValueVector args_;
6962   if (!iter_.readCallIndirect(&sigIndex, &callee_, &args_)) return false;
6963 
6964   if (deadCode_) return true;
6965 
6966   sync();
6967 
6968   const SigWithId& sig = env_.sigs[sigIndex];
6969 
6970   // Stack: ... arg1 .. argn callee
6971 
6972   uint32_t numArgs = sig.args().length();
6973   size_t stackSpace = stackConsumed(numArgs + 1);
6974 
6975   // The arguments must be at the stack top for emitCallArgs, so pop the
6976   // callee if it is on top.  Note this only pops the compiler's stack,
6977   // not the CPU stack.
6978 
6979   Stk callee = stk_.popCopy();
6980 
6981   FunctionCall baselineCall(lineOrBytecode);
6982   beginCall(baselineCall, UseABI::Wasm, InterModule::True);
6983 
6984   if (!emitCallArgs(sig.args(), baselineCall)) return false;
6985 
6986   callIndirect(sigIndex, callee, baselineCall);
6987 
6988   endCall(baselineCall, stackSpace);
6989 
6990   popValueStackBy(numArgs);
6991 
6992   if (!IsVoid(sig.ret())) pushReturned(baselineCall, sig.ret());
6993 
6994   return true;
6995 }
6996 
emitRound(RoundingMode roundingMode,ValType operandType)6997 void BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType) {
6998   if (operandType == ValType::F32) {
6999     RegF32 f0 = popF32();
7000     roundF32(roundingMode, f0);
7001     pushF32(f0);
7002   } else if (operandType == ValType::F64) {
7003     RegF64 f0 = popF64();
7004     roundF64(roundingMode, f0);
7005     pushF64(f0);
7006   } else {
7007     MOZ_CRASH("unexpected type");
7008   }
7009 }
7010 
emitUnaryMathBuiltinCall(SymbolicAddress callee,ValType operandType)7011 bool BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee,
7012                                             ValType operandType) {
7013   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
7014 
7015   Nothing operand_;
7016   if (!iter_.readUnary(operandType, &operand_)) return false;
7017 
7018   if (deadCode_) return true;
7019 
7020   RoundingMode roundingMode;
7021   if (IsRoundingFunction(callee, &roundingMode) &&
7022       supportsRoundInstruction(roundingMode)) {
7023     emitRound(roundingMode, operandType);
7024     return true;
7025   }
7026 
7027   sync();
7028 
7029   ValTypeVector& signature = operandType == ValType::F32 ? SigF_ : SigD_;
7030   ExprType retType =
7031       operandType == ValType::F32 ? ExprType::F32 : ExprType::F64;
7032   uint32_t numArgs = signature.length();
7033   size_t stackSpace = stackConsumed(numArgs);
7034 
7035   FunctionCall baselineCall(lineOrBytecode);
7036   beginCall(baselineCall, UseABI::System, InterModule::False);
7037 
7038   if (!emitCallArgs(signature, baselineCall)) return false;
7039 
7040   builtinCall(callee, baselineCall);
7041 
7042   endCall(baselineCall, stackSpace);
7043 
7044   popValueStackBy(numArgs);
7045 
7046   pushReturned(baselineCall, retType);
7047 
7048   return true;
7049 }
7050 
7051 #ifdef RABALDR_INT_DIV_I64_CALLOUT
emitDivOrModI64BuiltinCall(SymbolicAddress callee,ValType operandType)7052 void BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee,
7053                                               ValType operandType) {
7054   MOZ_ASSERT(operandType == ValType::I64);
7055   MOZ_ASSERT(!deadCode_);
7056 
7057   sync();
7058 
7059   needI64(specific.abiReturnRegI64);
7060 
7061   RegI64 rhs = popI64();
7062   RegI64 srcDest = popI64ToSpecific(specific.abiReturnRegI64);
7063 
7064   Label done;
7065 
7066   checkDivideByZeroI64(rhs);
7067 
7068   if (callee == SymbolicAddress::DivI64)
7069     checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
7070   else if (callee == SymbolicAddress::ModI64)
7071     checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
7072 
7073   masm.setupWasmABICall();
7074   masm.passABIArg(srcDest.high);
7075   masm.passABIArg(srcDest.low);
7076   masm.passABIArg(rhs.high);
7077   masm.passABIArg(rhs.low);
7078   masm.callWithABI(bytecodeOffset(), callee);
7079 
7080   masm.bind(&done);
7081 
7082   freeI64(rhs);
7083   pushI64(srcDest);
7084 }
7085 #endif  // RABALDR_INT_DIV_I64_CALLOUT
7086 
7087 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
emitConvertInt64ToFloatingCallout(SymbolicAddress callee,ValType operandType,ValType resultType)7088 bool BaseCompiler::emitConvertInt64ToFloatingCallout(SymbolicAddress callee,
7089                                                      ValType operandType,
7090                                                      ValType resultType) {
7091   sync();
7092 
7093   RegI64 input = popI64();
7094 
7095   FunctionCall call(0);
7096 
7097   masm.setupWasmABICall();
7098 #ifdef JS_PUNBOX64
7099   MOZ_CRASH("BaseCompiler platform hook: emitConvertInt64ToFloatingCallout");
7100 #else
7101   masm.passABIArg(input.high);
7102   masm.passABIArg(input.low);
7103 #endif
7104   masm.callWithABI(
7105       bytecodeOffset(), callee,
7106       resultType == ValType::F32 ? MoveOp::FLOAT32 : MoveOp::DOUBLE);
7107 
7108   freeI64(input);
7109 
7110   if (resultType == ValType::F32)
7111     pushF32(captureReturnedF32(call));
7112   else
7113     pushF64(captureReturnedF64(call));
7114 
7115   return true;
7116 }
7117 #endif  // RABALDR_I64_TO_FLOAT_CALLOUT
7118 
7119 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
7120 // `Callee` always takes a double, so a float32 input must be converted.
emitConvertFloatingToInt64Callout(SymbolicAddress callee,ValType operandType,ValType resultType)7121 bool BaseCompiler::emitConvertFloatingToInt64Callout(SymbolicAddress callee,
7122                                                      ValType operandType,
7123                                                      ValType resultType) {
7124   RegF64 doubleInput;
7125   if (operandType == ValType::F32) {
7126     doubleInput = needF64();
7127     RegF32 input = popF32();
7128     masm.convertFloat32ToDouble(input, doubleInput);
7129     freeF32(input);
7130   } else {
7131     doubleInput = popF64();
7132   }
7133 
7134   // We may need the value after the call for the ool check.
7135   RegF64 otherReg = needF64();
7136   moveF64(doubleInput, otherReg);
7137   pushF64(otherReg);
7138 
7139   sync();
7140 
7141   FunctionCall call(0);
7142 
7143   masm.setupWasmABICall();
7144   masm.passABIArg(doubleInput, MoveOp::DOUBLE);
7145   masm.callWithABI(bytecodeOffset(), callee);
7146 
7147   freeF64(doubleInput);
7148 
7149   RegI64 rv = captureReturnedI64();
7150 
7151   RegF64 inputVal = popF64();
7152 
7153   TruncFlags flags = 0;
7154   if (callee == SymbolicAddress::TruncateDoubleToUint64)
7155     flags |= TRUNC_UNSIGNED;
7156   if (callee == SymbolicAddress::SaturatingTruncateDoubleToInt64 ||
7157       callee == SymbolicAddress::SaturatingTruncateDoubleToUint64) {
7158     flags |= TRUNC_SATURATING;
7159   }
7160 
7161   // If we're saturating, the callout will always produce the final result
7162   // value. Otherwise, the callout value will return 0x8000000000000000
7163   // and we need to produce traps.
7164   OutOfLineCode* ool = nullptr;
7165   if (!(flags & TRUNC_SATURATING)) {
7166     // The OOL check just succeeds or fails, it does not generate a value.
7167     ool = addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(
7168         AnyReg(inputVal), rv, flags, bytecodeOffset()));
7169     if (!ool) return false;
7170 
7171     masm.branch64(Assembler::Equal, rv, Imm64(0x8000000000000000),
7172                   ool->entry());
7173     masm.bind(ool->rejoin());
7174   }
7175 
7176   pushI64(rv);
7177   freeF64(inputVal);
7178 
7179   return true;
7180 }
7181 #endif  // RABALDR_FLOAT_TO_I64_CALLOUT
7182 
emitGetLocal()7183 bool BaseCompiler::emitGetLocal() {
7184   uint32_t slot;
7185   if (!iter_.readGetLocal(locals_, &slot)) return false;
7186 
7187   if (deadCode_) return true;
7188 
7189   // Local loads are pushed unresolved, ie, they may be deferred
7190   // until needed, until they may be affected by a store, or until a
7191   // sync.  This is intended to reduce register pressure.
7192 
7193   switch (locals_[slot]) {
7194     case ValType::I32:
7195       pushLocalI32(slot);
7196       break;
7197     case ValType::I64:
7198       pushLocalI64(slot);
7199       break;
7200     case ValType::F64:
7201       pushLocalF64(slot);
7202       break;
7203     case ValType::F32:
7204       pushLocalF32(slot);
7205       break;
7206     default:
7207       MOZ_CRASH("Local variable type");
7208   }
7209 
7210   return true;
7211 }
7212 
7213 template <bool isSetLocal>
emitSetOrTeeLocal(uint32_t slot)7214 bool BaseCompiler::emitSetOrTeeLocal(uint32_t slot) {
7215   if (deadCode_) return true;
7216 
7217   bceLocalIsUpdated(slot);
7218   switch (locals_[slot]) {
7219     case ValType::I32: {
7220       RegI32 rv = popI32();
7221       syncLocal(slot);
7222       fr.storeLocalI32(rv, localFromSlot(slot, MIRType::Int32));
7223       if (isSetLocal)
7224         freeI32(rv);
7225       else
7226         pushI32(rv);
7227       break;
7228     }
7229     case ValType::I64: {
7230       RegI64 rv = popI64();
7231       syncLocal(slot);
7232       fr.storeLocalI64(rv, localFromSlot(slot, MIRType::Int64));
7233       if (isSetLocal)
7234         freeI64(rv);
7235       else
7236         pushI64(rv);
7237       break;
7238     }
7239     case ValType::F64: {
7240       RegF64 rv = popF64();
7241       syncLocal(slot);
7242       fr.storeLocalF64(rv, localFromSlot(slot, MIRType::Double));
7243       if (isSetLocal)
7244         freeF64(rv);
7245       else
7246         pushF64(rv);
7247       break;
7248     }
7249     case ValType::F32: {
7250       RegF32 rv = popF32();
7251       syncLocal(slot);
7252       fr.storeLocalF32(rv, localFromSlot(slot, MIRType::Float32));
7253       if (isSetLocal)
7254         freeF32(rv);
7255       else
7256         pushF32(rv);
7257       break;
7258     }
7259     default:
7260       MOZ_CRASH("Local variable type");
7261   }
7262 
7263   return true;
7264 }
7265 
emitSetLocal()7266 bool BaseCompiler::emitSetLocal() {
7267   uint32_t slot;
7268   Nothing unused_value;
7269   if (!iter_.readSetLocal(locals_, &slot, &unused_value)) return false;
7270   return emitSetOrTeeLocal<true>(slot);
7271 }
7272 
emitTeeLocal()7273 bool BaseCompiler::emitTeeLocal() {
7274   uint32_t slot;
7275   Nothing unused_value;
7276   if (!iter_.readTeeLocal(locals_, &slot, &unused_value)) return false;
7277   return emitSetOrTeeLocal<false>(slot);
7278 }
7279 
emitGetGlobal()7280 bool BaseCompiler::emitGetGlobal() {
7281   uint32_t id;
7282   if (!iter_.readGetGlobal(&id)) return false;
7283 
7284   if (deadCode_) return true;
7285 
7286   const GlobalDesc& global = env_.globals[id];
7287 
7288   if (global.isConstant()) {
7289     Val value = global.constantValue();
7290     switch (value.type()) {
7291       case ValType::I32:
7292         pushI32(value.i32());
7293         break;
7294       case ValType::I64:
7295         pushI64(value.i64());
7296         break;
7297       case ValType::F32:
7298         pushF32(value.f32());
7299         break;
7300       case ValType::F64:
7301         pushF64(value.f64());
7302         break;
7303       default:
7304         MOZ_CRASH("Global constant type");
7305     }
7306     return true;
7307   }
7308 
7309   switch (global.type()) {
7310     case ValType::I32: {
7311       RegI32 rv = needI32();
7312       loadGlobalVarI32(global.offset(), rv);
7313       pushI32(rv);
7314       break;
7315     }
7316     case ValType::I64: {
7317       RegI64 rv = needI64();
7318       loadGlobalVarI64(global.offset(), rv);
7319       pushI64(rv);
7320       break;
7321     }
7322     case ValType::F32: {
7323       RegF32 rv = needF32();
7324       loadGlobalVarF32(global.offset(), rv);
7325       pushF32(rv);
7326       break;
7327     }
7328     case ValType::F64: {
7329       RegF64 rv = needF64();
7330       loadGlobalVarF64(global.offset(), rv);
7331       pushF64(rv);
7332       break;
7333     }
7334     default:
7335       MOZ_CRASH("Global variable type");
7336       break;
7337   }
7338   return true;
7339 }
7340 
emitSetGlobal()7341 bool BaseCompiler::emitSetGlobal() {
7342   uint32_t id;
7343   Nothing unused_value;
7344   if (!iter_.readSetGlobal(&id, &unused_value)) return false;
7345 
7346   if (deadCode_) return true;
7347 
7348   const GlobalDesc& global = env_.globals[id];
7349 
7350   switch (global.type()) {
7351     case ValType::I32: {
7352       RegI32 rv = popI32();
7353       storeGlobalVarI32(global.offset(), rv);
7354       freeI32(rv);
7355       break;
7356     }
7357     case ValType::I64: {
7358       RegI64 rv = popI64();
7359       storeGlobalVarI64(global.offset(), rv);
7360       freeI64(rv);
7361       break;
7362     }
7363     case ValType::F32: {
7364       RegF32 rv = popF32();
7365       storeGlobalVarF32(global.offset(), rv);
7366       freeF32(rv);
7367       break;
7368     }
7369     case ValType::F64: {
7370       RegF64 rv = popF64();
7371       storeGlobalVarF64(global.offset(), rv);
7372       freeF64(rv);
7373       break;
7374     }
7375     default:
7376       MOZ_CRASH("Global variable type");
7377       break;
7378   }
7379   return true;
7380 }
7381 
7382 // Bounds check elimination.
7383 //
7384 // We perform BCE on two kinds of address expressions: on constant heap pointers
7385 // that are known to be in the heap or will be handled by the out-of-bounds trap
7386 // handler; and on local variables that have been checked in dominating code
7387 // without being updated since.
7388 //
7389 // For an access through a constant heap pointer + an offset we can eliminate
7390 // the bounds check if the sum of the address and offset is below the sum of the
7391 // minimum memory length and the offset guard length.
7392 //
7393 // For an access through a local variable + an offset we can eliminate the
7394 // bounds check if the local variable has already been checked and has not been
7395 // updated since, and the offset is less than the guard limit.
7396 //
7397 // To track locals for which we can eliminate checks we use a bit vector
7398 // bceSafe_ that has a bit set for those locals whose bounds have been checked
7399 // and which have not subsequently been set.  Initially this vector is zero.
7400 //
7401 // In straight-line code a bit is set when we perform a bounds check on an
7402 // access via the local and is reset when the variable is updated.
7403 //
7404 // In control flow, the bit vector is manipulated as follows.  Each ControlItem
7405 // has a value bceSafeOnEntry, which is the value of bceSafe_ on entry to the
7406 // item, and a value bceSafeOnExit, which is initially ~0.  On a branch (br,
7407 // brIf, brTable), we always AND the branch target's bceSafeOnExit with the
7408 // value of bceSafe_ at the branch point.  On exiting an item by falling out of
7409 // it, provided we're not in dead code, we AND the current value of bceSafe_
7410 // into the item's bceSafeOnExit.  Additional processing depends on the item
7411 // type:
7412 //
7413 //  - After a block, set bceSafe_ to the block's bceSafeOnExit.
7414 //
7415 //  - On loop entry, after pushing the ControlItem, set bceSafe_ to zero; the
7416 //    back edges would otherwise require us to iterate to a fixedpoint.
7417 //
7418 //  - After a loop, the bceSafe_ is left unchanged, because only fallthrough
7419 //    control flow will reach that point and the bceSafe_ value represents the
7420 //    correct state of the fallthrough path.
7421 //
7422 //  - Set bceSafe_ to the ControlItem's bceSafeOnEntry at both the 'then' branch
7423 //    and the 'else' branch.
7424 //
7425 //  - After an if-then-else, set bceSafe_ to the if-then-else's bceSafeOnExit.
7426 //
7427 //  - After an if-then, set bceSafe_ to the if-then's bceSafeOnExit AND'ed with
7428 //    the if-then's bceSafeOnEntry.
7429 //
7430 // Finally, when the debugger allows locals to be mutated we must disable BCE
7431 // for references via a local, by returning immediately from bceCheckLocal if
7432 // debugEnabled_ is true.
7433 //
7434 //
7435 // Alignment check elimination.
7436 //
7437 // Alignment checks for atomic operations can be omitted if the pointer is a
7438 // constant and the pointer + offset is aligned.  Alignment checking that can't
7439 // be omitted can still be simplified by checking only the pointer if the offset
7440 // is aligned.
7441 //
7442 // (In addition, alignment checking of the pointer can be omitted if the pointer
7443 // has been checked in dominating code, but we don't do that yet.)
7444 
7445 // TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
7446 // code by not moving a constant address with a zero offset into a register.
7447 
popMemoryAccess(MemoryAccessDesc * access,AccessCheck * check)7448 RegI32 BaseCompiler::popMemoryAccess(MemoryAccessDesc* access,
7449                                      AccessCheck* check) {
7450   check->onlyPointerAlignment =
7451       (access->offset() & (access->byteSize() - 1)) == 0;
7452 
7453   int32_t addrTemp;
7454   if (popConstI32(&addrTemp)) {
7455     uint32_t addr = addrTemp;
7456 
7457     uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
7458     uint64_t limit =
7459         uint64_t(env_.minMemoryLength) + uint64_t(wasm::OffsetGuardLimit);
7460 
7461     check->omitBoundsCheck = ea < limit;
7462     check->omitAlignmentCheck = (ea & (access->byteSize() - 1)) == 0;
7463 
7464     // Fold the offset into the pointer if we can, as this is always
7465     // beneficial.
7466 
7467     if (ea <= UINT32_MAX) {
7468       addr = uint32_t(ea);
7469       access->clearOffset();
7470     }
7471 
7472     RegI32 r = needI32();
7473     moveImm32(int32_t(addr), r);
7474     return r;
7475   }
7476 
7477   uint32_t local;
7478   if (peekLocalI32(&local)) bceCheckLocal(access, check, local);
7479 
7480   return popI32();
7481 }
7482 
maybeLoadTlsForAccess(const AccessCheck & check)7483 RegI32 BaseCompiler::maybeLoadTlsForAccess(const AccessCheck& check) {
7484   RegI32 tls;
7485   if (needTlsForAccess(check)) {
7486     tls = needI32();
7487     masm.loadWasmTlsRegFromFrame(tls);
7488   }
7489   return tls;
7490 }
7491 
maybeLoadTlsForAccess(const AccessCheck & check,RegI32 specific)7492 RegI32 BaseCompiler::maybeLoadTlsForAccess(const AccessCheck& check,
7493                                            RegI32 specific) {
7494   if (needTlsForAccess(check)) {
7495     masm.loadWasmTlsRegFromFrame(specific);
7496     return specific;
7497   }
7498   return RegI32::Invalid();
7499 }
7500 
loadCommon(MemoryAccessDesc * access,ValType type)7501 bool BaseCompiler::loadCommon(MemoryAccessDesc* access, ValType type) {
7502   AccessCheck check;
7503 
7504   RegI32 tls, temp1, temp2, temp3;
7505   needLoadTemps(*access, &temp1, &temp2, &temp3);
7506 
7507   switch (type) {
7508     case ValType::I32: {
7509       RegI32 rp = popMemoryAccess(access, &check);
7510 #ifdef JS_CODEGEN_ARM
7511       RegI32 rv = IsUnaligned(*access) ? needI32() : rp;
7512 #else
7513       RegI32 rv = rp;
7514 #endif
7515       tls = maybeLoadTlsForAccess(check);
7516       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3))
7517         return false;
7518       pushI32(rv);
7519       if (rp != rv) freeI32(rp);
7520       break;
7521     }
7522     case ValType::I64: {
7523       RegI64 rv;
7524       RegI32 rp;
7525 #ifdef JS_CODEGEN_X86
7526       rv = specific.abiReturnRegI64;
7527       needI64(rv);
7528       rp = popMemoryAccess(access, &check);
7529 #else
7530       rp = popMemoryAccess(access, &check);
7531       rv = needI64();
7532 #endif
7533       tls = maybeLoadTlsForAccess(check);
7534       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3))
7535         return false;
7536       pushI64(rv);
7537       freeI32(rp);
7538       break;
7539     }
7540     case ValType::F32: {
7541       RegI32 rp = popMemoryAccess(access, &check);
7542       RegF32 rv = needF32();
7543       tls = maybeLoadTlsForAccess(check);
7544       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3))
7545         return false;
7546       pushF32(rv);
7547       freeI32(rp);
7548       break;
7549     }
7550     case ValType::F64: {
7551       RegI32 rp = popMemoryAccess(access, &check);
7552       RegF64 rv = needF64();
7553       tls = maybeLoadTlsForAccess(check);
7554       if (!load(access, &check, tls, rp, AnyReg(rv), temp1, temp2, temp3))
7555         return false;
7556       pushF64(rv);
7557       freeI32(rp);
7558       break;
7559     }
7560     default:
7561       MOZ_CRASH("load type");
7562       break;
7563   }
7564 
7565   maybeFreeI32(tls);
7566   maybeFreeI32(temp1);
7567   maybeFreeI32(temp2);
7568   maybeFreeI32(temp3);
7569 
7570   return true;
7571 }
7572 
emitLoad(ValType type,Scalar::Type viewType)7573 bool BaseCompiler::emitLoad(ValType type, Scalar::Type viewType) {
7574   LinearMemoryAddress<Nothing> addr;
7575   if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr)) return false;
7576 
7577   if (deadCode_) return true;
7578 
7579   MemoryAccessDesc access(viewType, addr.align, addr.offset,
7580                           Some(bytecodeOffset()));
7581   return loadCommon(&access, type);
7582 }
7583 
storeCommon(MemoryAccessDesc * access,ValType resultType)7584 bool BaseCompiler::storeCommon(MemoryAccessDesc* access, ValType resultType) {
7585   AccessCheck check;
7586 
7587   RegI32 tls;
7588   RegI32 temp = needStoreTemp(*access, resultType);
7589 
7590   switch (resultType) {
7591     case ValType::I32: {
7592       RegI32 rv = popI32();
7593       RegI32 rp = popMemoryAccess(access, &check);
7594       tls = maybeLoadTlsForAccess(check);
7595       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) return false;
7596       freeI32(rp);
7597       freeI32(rv);
7598       break;
7599     }
7600     case ValType::I64: {
7601       RegI64 rv = popI64();
7602       RegI32 rp = popMemoryAccess(access, &check);
7603       tls = maybeLoadTlsForAccess(check);
7604       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) return false;
7605       freeI32(rp);
7606       freeI64(rv);
7607       break;
7608     }
7609     case ValType::F32: {
7610       RegF32 rv = popF32();
7611       RegI32 rp = popMemoryAccess(access, &check);
7612       tls = maybeLoadTlsForAccess(check);
7613       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) return false;
7614       freeI32(rp);
7615       freeF32(rv);
7616       break;
7617     }
7618     case ValType::F64: {
7619       RegF64 rv = popF64();
7620       RegI32 rp = popMemoryAccess(access, &check);
7621       tls = maybeLoadTlsForAccess(check);
7622       if (!store(access, &check, tls, rp, AnyReg(rv), temp)) return false;
7623       freeI32(rp);
7624       freeF64(rv);
7625       break;
7626     }
7627     default:
7628       MOZ_CRASH("store type");
7629       break;
7630   }
7631 
7632   maybeFreeI32(tls);
7633   maybeFreeI32(temp);
7634 
7635   return true;
7636 }
7637 
emitStore(ValType resultType,Scalar::Type viewType)7638 bool BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType) {
7639   LinearMemoryAddress<Nothing> addr;
7640   Nothing unused_value;
7641   if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr,
7642                        &unused_value))
7643     return false;
7644 
7645   if (deadCode_) return true;
7646 
7647   MemoryAccessDesc access(viewType, addr.align, addr.offset,
7648                           Some(bytecodeOffset()));
7649   return storeCommon(&access, resultType);
7650 }
7651 
emitSelect()7652 bool BaseCompiler::emitSelect() {
7653   StackType type;
7654   Nothing unused_trueValue;
7655   Nothing unused_falseValue;
7656   Nothing unused_condition;
7657   if (!iter_.readSelect(&type, &unused_trueValue, &unused_falseValue,
7658                         &unused_condition))
7659     return false;
7660 
7661   if (deadCode_) {
7662     resetLatentOp();
7663     return true;
7664   }
7665 
7666   // I32 condition on top, then false, then true.
7667 
7668   Label done;
7669   BranchState b(&done);
7670   emitBranchSetup(&b);
7671 
7672   switch (NonAnyToValType(type)) {
7673     case ValType::I32: {
7674       RegI32 r, rs;
7675       pop2xI32(&r, &rs);
7676       emitBranchPerform(&b);
7677       moveI32(rs, r);
7678       masm.bind(&done);
7679       freeI32(rs);
7680       pushI32(r);
7681       break;
7682     }
7683     case ValType::I64: {
7684 #ifdef JS_CODEGEN_X86
7685       // There may be as many as four Int64 values in registers at a time: two
7686       // for the latent branch operands, and two for the true/false values we
7687       // normally pop before executing the branch.  On x86 this is one value
7688       // too many, so we need to generate more complicated code here, and for
7689       // simplicity's sake we do so even if the branch operands are not Int64.
7690       // However, the resulting control flow diamond is complicated since the
7691       // arms of the diamond will have to stay synchronized with respect to
7692       // their evaluation stack and regalloc state.  To simplify further, we
7693       // use a double branch and a temporary boolean value for now.
7694       RegI32 temp = needI32();
7695       moveImm32(0, temp);
7696       emitBranchPerform(&b);
7697       moveImm32(1, temp);
7698       masm.bind(&done);
7699 
7700       Label trueValue;
7701       RegI64 r, rs;
7702       pop2xI64(&r, &rs);
7703       masm.branch32(Assembler::Equal, temp, Imm32(0), &trueValue);
7704       moveI64(rs, r);
7705       masm.bind(&trueValue);
7706       freeI32(temp);
7707       freeI64(rs);
7708       pushI64(r);
7709 #else
7710       RegI64 r, rs;
7711       pop2xI64(&r, &rs);
7712       emitBranchPerform(&b);
7713       moveI64(rs, r);
7714       masm.bind(&done);
7715       freeI64(rs);
7716       pushI64(r);
7717 #endif
7718       break;
7719     }
7720     case ValType::F32: {
7721       RegF32 r, rs;
7722       pop2xF32(&r, &rs);
7723       emitBranchPerform(&b);
7724       moveF32(rs, r);
7725       masm.bind(&done);
7726       freeF32(rs);
7727       pushF32(r);
7728       break;
7729     }
7730     case ValType::F64: {
7731       RegF64 r, rs;
7732       pop2xF64(&r, &rs);
7733       emitBranchPerform(&b);
7734       moveF64(rs, r);
7735       masm.bind(&done);
7736       freeF64(rs);
7737       pushF64(r);
7738       break;
7739     }
7740     default: { MOZ_CRASH("select type"); }
7741   }
7742 
7743   return true;
7744 }
7745 
emitCompareI32(Assembler::Condition compareOp,ValType compareType)7746 void BaseCompiler::emitCompareI32(Assembler::Condition compareOp,
7747                                   ValType compareType) {
7748   MOZ_ASSERT(compareType == ValType::I32);
7749 
7750   if (sniffConditionalControlCmp(compareOp, compareType)) return;
7751 
7752   int32_t c;
7753   if (popConstI32(&c)) {
7754     RegI32 r = popI32();
7755     masm.cmp32Set(compareOp, r, Imm32(c), r);
7756     pushI32(r);
7757   } else {
7758     RegI32 r, rs;
7759     pop2xI32(&r, &rs);
7760     masm.cmp32Set(compareOp, r, rs, r);
7761     freeI32(rs);
7762     pushI32(r);
7763   }
7764 }
7765 
emitCompareI64(Assembler::Condition compareOp,ValType compareType)7766 void BaseCompiler::emitCompareI64(Assembler::Condition compareOp,
7767                                   ValType compareType) {
7768   MOZ_ASSERT(compareType == ValType::I64);
7769 
7770   if (sniffConditionalControlCmp(compareOp, compareType)) return;
7771 
7772   RegI64 rs0, rs1;
7773   pop2xI64(&rs0, &rs1);
7774   RegI32 rd(fromI64(rs0));
7775   cmp64Set(compareOp, rs0, rs1, rd);
7776   freeI64(rs1);
7777   freeI64Except(rs0, rd);
7778   pushI32(rd);
7779 }
7780 
emitCompareF32(Assembler::DoubleCondition compareOp,ValType compareType)7781 void BaseCompiler::emitCompareF32(Assembler::DoubleCondition compareOp,
7782                                   ValType compareType) {
7783   MOZ_ASSERT(compareType == ValType::F32);
7784 
7785   if (sniffConditionalControlCmp(compareOp, compareType)) return;
7786 
7787   Label across;
7788   RegF32 rs0, rs1;
7789   pop2xF32(&rs0, &rs1);
7790   RegI32 rd = needI32();
7791   moveImm32(1, rd);
7792   masm.branchFloat(compareOp, rs0, rs1, &across);
7793   moveImm32(0, rd);
7794   masm.bind(&across);
7795   freeF32(rs0);
7796   freeF32(rs1);
7797   pushI32(rd);
7798 }
7799 
emitCompareF64(Assembler::DoubleCondition compareOp,ValType compareType)7800 void BaseCompiler::emitCompareF64(Assembler::DoubleCondition compareOp,
7801                                   ValType compareType) {
7802   MOZ_ASSERT(compareType == ValType::F64);
7803 
7804   if (sniffConditionalControlCmp(compareOp, compareType)) return;
7805 
7806   Label across;
7807   RegF64 rs0, rs1;
7808   pop2xF64(&rs0, &rs1);
7809   RegI32 rd = needI32();
7810   moveImm32(1, rd);
7811   masm.branchDouble(compareOp, rs0, rs1, &across);
7812   moveImm32(0, rd);
7813   masm.bind(&across);
7814   freeF64(rs0);
7815   freeF64(rs1);
7816   pushI32(rd);
7817 }
7818 
emitInstanceCall(uint32_t lineOrBytecode,const MIRTypeVector & sig,ExprType retType,SymbolicAddress builtin)7819 void BaseCompiler::emitInstanceCall(uint32_t lineOrBytecode,
7820                                     const MIRTypeVector& sig, ExprType retType,
7821                                     SymbolicAddress builtin) {
7822   MOZ_ASSERT(sig[0] == MIRType::Pointer);
7823 
7824   sync();
7825 
7826   uint32_t numArgs = sig.length() - 1 /* instance */;
7827   size_t stackSpace = stackConsumed(numArgs);
7828 
7829   FunctionCall baselineCall(lineOrBytecode);
7830   beginCall(baselineCall, UseABI::System, InterModule::True);
7831 
7832   ABIArg instanceArg = reservePointerArgument(baselineCall);
7833 
7834   startCallArgs(baselineCall, stackArgAreaSize(sig));
7835   for (uint32_t i = 1; i < sig.length(); i++) {
7836     ValType t;
7837     switch (sig[i]) {
7838       case MIRType::Int32:
7839         t = ValType::I32;
7840         break;
7841       case MIRType::Int64:
7842         t = ValType::I64;
7843         break;
7844       default:
7845         MOZ_CRASH("Unexpected type");
7846     }
7847     passArg(baselineCall, t, peek(numArgs - i));
7848   }
7849   builtinInstanceMethodCall(builtin, instanceArg, baselineCall);
7850   endCall(baselineCall, stackSpace);
7851 
7852   popValueStackBy(numArgs);
7853 
7854   pushReturned(baselineCall, retType);
7855 }
7856 
emitGrowMemory()7857 bool BaseCompiler::emitGrowMemory() {
7858   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
7859 
7860   Nothing arg;
7861   if (!iter_.readGrowMemory(&arg)) return false;
7862 
7863   if (deadCode_) return true;
7864 
7865   emitInstanceCall(lineOrBytecode, SigPI_, ExprType::I32,
7866                    SymbolicAddress::GrowMemory);
7867   return true;
7868 }
7869 
emitCurrentMemory()7870 bool BaseCompiler::emitCurrentMemory() {
7871   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
7872 
7873   if (!iter_.readCurrentMemory()) return false;
7874 
7875   if (deadCode_) return true;
7876 
7877   emitInstanceCall(lineOrBytecode, SigP_, ExprType::I32,
7878                    SymbolicAddress::CurrentMemory);
7879   return true;
7880 }
7881 
emitAtomicCmpXchg(ValType type,Scalar::Type viewType)7882 bool BaseCompiler::emitAtomicCmpXchg(ValType type, Scalar::Type viewType) {
7883   LinearMemoryAddress<Nothing> addr;
7884   Nothing unused;
7885 
7886   if (!iter_.readAtomicCmpXchg(&addr, type, Scalar::byteSize(viewType), &unused,
7887                                &unused))
7888     return false;
7889 
7890   if (deadCode_) return true;
7891 
7892   MemoryAccessDesc access(viewType, addr.align, addr.offset,
7893                           Some(bytecodeOffset()),
7894                           /*numSimdExprs=*/0, Synchronization::Full());
7895 
7896   if (Scalar::byteSize(viewType) <= 4) {
7897     PopAtomicCmpXchg32Regs regs(this, type, viewType);
7898 
7899     AccessCheck check;
7900     RegI32 rp = popMemoryAccess(&access, &check);
7901     RegI32 tls = maybeLoadTlsForAccess(check);
7902 
7903     regs.atomicCmpXchg32(prepareAtomicMemoryAccess(&access, &check, tls, rp),
7904                          viewType);
7905 
7906     maybeFreeI32(tls);
7907     freeI32(rp);
7908 
7909     if (type == ValType::I64)
7910       pushU32AsI64(regs.takeRd());
7911     else
7912       pushI32(regs.takeRd());
7913 
7914     return true;
7915   }
7916 
7917   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
7918 
7919   PopAtomicCmpXchg64Regs regs(this);
7920 
7921   AccessCheck check;
7922   RegI32 rp = popMemoryAccess(&access, &check);
7923 
7924 #ifdef JS_CODEGEN_X86
7925   ScratchEBX ebx(*this);
7926   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
7927   regs.atomicCmpXchg64(prepareAtomicMemoryAccess(&access, &check, tls, rp),
7928                        ebx);
7929 #else
7930   RegI32 tls = maybeLoadTlsForAccess(check);
7931   regs.atomicCmpXchg64(prepareAtomicMemoryAccess(&access, &check, tls, rp));
7932   maybeFreeI32(tls);
7933 #endif
7934 
7935   freeI32(rp);
7936 
7937   pushI64(regs.takeRd());
7938   return true;
7939 }
7940 
emitAtomicLoad(ValType type,Scalar::Type viewType)7941 bool BaseCompiler::emitAtomicLoad(ValType type, Scalar::Type viewType) {
7942   LinearMemoryAddress<Nothing> addr;
7943   if (!iter_.readAtomicLoad(&addr, type, Scalar::byteSize(viewType)))
7944     return false;
7945 
7946   if (deadCode_) return true;
7947 
7948   MemoryAccessDesc access(viewType, addr.align, addr.offset,
7949                           Some(bytecodeOffset()),
7950                           /*numSimdElems=*/0, Synchronization::Load());
7951 
7952   if (Scalar::byteSize(viewType) <= sizeof(void*))
7953     return loadCommon(&access, type);
7954 
7955   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
7956 
7957 #if defined(JS_64BIT)
7958   MOZ_CRASH("Should not happen");
7959 #else
7960   PopAtomicLoad64Regs regs(this);
7961 
7962   AccessCheck check;
7963   RegI32 rp = popMemoryAccess(&access, &check);
7964 
7965 #ifdef JS_CODEGEN_X86
7966   ScratchEBX ebx(*this);
7967   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
7968   regs.atomicLoad64(prepareAtomicMemoryAccess(&access, &check, tls, rp), ebx);
7969 #else
7970   RegI32 tls = maybeLoadTlsForAccess(check);
7971   regs.atomicLoad64(prepareAtomicMemoryAccess(&access, &check, tls, rp));
7972   maybeFreeI32(tls);
7973 #endif
7974 
7975   freeI32(rp);
7976 
7977   pushI64(regs.takeRd());
7978   return true;
7979 #endif  // JS_64BIT
7980 }
7981 
emitAtomicRMW(ValType type,Scalar::Type viewType,AtomicOp op)7982 bool BaseCompiler::emitAtomicRMW(ValType type, Scalar::Type viewType,
7983                                  AtomicOp op) {
7984   LinearMemoryAddress<Nothing> addr;
7985   Nothing unused_value;
7986   if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType),
7987                            &unused_value))
7988     return false;
7989 
7990   if (deadCode_) return true;
7991 
7992   MemoryAccessDesc access(viewType, addr.align, addr.offset,
7993                           Some(bytecodeOffset()),
7994                           /*numSimdElems=*/0, Synchronization::Full());
7995 
7996   if (Scalar::byteSize(viewType) <= 4) {
7997     PopAtomicRMW32Regs regs(this, type, viewType, op);
7998 
7999     AccessCheck check;
8000     RegI32 rp = popMemoryAccess(&access, &check);
8001     RegI32 tls = maybeLoadTlsForAccess(check);
8002 
8003     regs.atomicRMW32(prepareAtomicMemoryAccess(&access, &check, tls, rp),
8004                      viewType, op);
8005 
8006     maybeFreeI32(tls);
8007     freeI32(rp);
8008 
8009     if (type == ValType::I64)
8010       pushU32AsI64(regs.takeRd());
8011     else
8012       pushI32(regs.takeRd());
8013     return true;
8014   }
8015 
8016   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
8017 
8018   PopAtomicRMW64Regs regs(this, op);
8019 
8020   AccessCheck check;
8021   RegI32 rp = popMemoryAccess(&access, &check);
8022 
8023 #ifdef JS_CODEGEN_X86
8024   ScratchEBX ebx(*this);
8025   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
8026 
8027   fr.pushPtr(regs.valueHigh());
8028   fr.pushPtr(regs.valueLow());
8029   Address value(esp, 0);
8030 
8031   regs.atomicRMW64(prepareAtomicMemoryAccess(&access, &check, tls, rp), op,
8032                    value, ebx);
8033 
8034   fr.popBytes(8);
8035 #else
8036   RegI32 tls = maybeLoadTlsForAccess(check);
8037   regs.atomicRMW64(prepareAtomicMemoryAccess(&access, &check, tls, rp), op);
8038   maybeFreeI32(tls);
8039 #endif
8040 
8041   freeI32(rp);
8042 
8043   pushI64(regs.takeRd());
8044   return true;
8045 }
8046 
emitAtomicStore(ValType type,Scalar::Type viewType)8047 bool BaseCompiler::emitAtomicStore(ValType type, Scalar::Type viewType) {
8048   LinearMemoryAddress<Nothing> addr;
8049   Nothing unused_value;
8050   if (!iter_.readAtomicStore(&addr, type, Scalar::byteSize(viewType),
8051                              &unused_value))
8052     return false;
8053 
8054   if (deadCode_) return true;
8055 
8056   MemoryAccessDesc access(viewType, addr.align, addr.offset,
8057                           Some(bytecodeOffset()),
8058                           /*numSimdElems=*/0, Synchronization::Store());
8059 
8060   if (Scalar::byteSize(viewType) <= sizeof(void*))
8061     return storeCommon(&access, type);
8062 
8063   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
8064 
8065 #ifdef JS_64BIT
8066   MOZ_CRASH("Should not happen");
8067 #else
8068   emitAtomicXchg64(&access, type, WantResult(false));
8069   return true;
8070 #endif
8071 }
8072 
emitAtomicXchg(ValType type,Scalar::Type viewType)8073 bool BaseCompiler::emitAtomicXchg(ValType type, Scalar::Type viewType) {
8074   LinearMemoryAddress<Nothing> addr;
8075   Nothing unused_value;
8076   if (!iter_.readAtomicRMW(&addr, type, Scalar::byteSize(viewType),
8077                            &unused_value))
8078     return false;
8079 
8080   if (deadCode_) return true;
8081 
8082   AccessCheck check;
8083   MemoryAccessDesc access(viewType, addr.align, addr.offset,
8084                           Some(bytecodeOffset()),
8085                           /*numSimdElems=*/0, Synchronization::Full());
8086 
8087   if (Scalar::byteSize(viewType) <= 4) {
8088     PopAtomicXchg32Regs regs(this, type, viewType);
8089     RegI32 rp = popMemoryAccess(&access, &check);
8090     RegI32 tls = maybeLoadTlsForAccess(check);
8091 
8092     regs.atomicXchg32(prepareAtomicMemoryAccess(&access, &check, tls, rp),
8093                       viewType);
8094 
8095     maybeFreeI32(tls);
8096     freeI32(rp);
8097 
8098     if (type == ValType::I64)
8099       pushU32AsI64(regs.takeRd());
8100     else
8101       pushI32(regs.takeRd());
8102     return true;
8103   }
8104 
8105   MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
8106 
8107   emitAtomicXchg64(&access, type, WantResult(true));
8108   return true;
8109 }
8110 
emitAtomicXchg64(MemoryAccessDesc * access,ValType type,WantResult wantResult)8111 void BaseCompiler::emitAtomicXchg64(MemoryAccessDesc* access, ValType type,
8112                                     WantResult wantResult) {
8113   PopAtomicXchg64Regs regs(this);
8114 
8115   AccessCheck check;
8116   RegI32 rp = popMemoryAccess(access, &check);
8117 
8118 #ifdef JS_CODEGEN_X86
8119   ScratchEBX ebx(*this);
8120   RegI32 tls = maybeLoadTlsForAccess(check, ebx);
8121   regs.atomicXchg64(prepareAtomicMemoryAccess(access, &check, tls, rp), ebx);
8122 #else
8123   RegI32 tls = maybeLoadTlsForAccess(check);
8124   regs.atomicXchg64(prepareAtomicMemoryAccess(access, &check, tls, rp));
8125   maybeFreeI32(tls);
8126 #endif
8127 
8128   freeI32(rp);
8129 
8130   if (wantResult) pushI64(regs.takeRd());
8131 }
8132 
emitWait(ValType type,uint32_t byteSize)8133 bool BaseCompiler::emitWait(ValType type, uint32_t byteSize) {
8134   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
8135 
8136   Nothing nothing;
8137   LinearMemoryAddress<Nothing> addr;
8138   if (!iter_.readWait(&addr, type, byteSize, &nothing, &nothing)) return false;
8139 
8140   if (deadCode_) return true;
8141 
8142   switch (type) {
8143     case ValType::I32:
8144       emitInstanceCall(lineOrBytecode, SigPIIL_, ExprType::I32,
8145                        SymbolicAddress::WaitI32);
8146       break;
8147     case ValType::I64:
8148       emitInstanceCall(lineOrBytecode, SigPILL_, ExprType::I32,
8149                        SymbolicAddress::WaitI64);
8150       break;
8151     default:
8152       MOZ_CRASH();
8153   }
8154 
8155   Label ok;
8156   masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
8157   trap(Trap::ThrowReported);
8158   masm.bind(&ok);
8159 
8160   return true;
8161 }
8162 
emitWake()8163 bool BaseCompiler::emitWake() {
8164   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
8165 
8166   Nothing nothing;
8167   LinearMemoryAddress<Nothing> addr;
8168   if (!iter_.readWake(&addr, &nothing)) return false;
8169 
8170   if (deadCode_) return true;
8171 
8172   emitInstanceCall(lineOrBytecode, SigPII_, ExprType::I32,
8173                    SymbolicAddress::Wake);
8174 
8175   Label ok;
8176   masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
8177   trap(Trap::ThrowReported);
8178   masm.bind(&ok);
8179 
8180   return true;
8181 }
8182 
emitBody()8183 bool BaseCompiler::emitBody() {
8184   if (!iter_.readFunctionStart(sig().ret())) return false;
8185 
8186   initControl(controlItem());
8187 
8188   uint32_t overhead = 0;
8189 
8190   for (;;) {
8191     Nothing unused_a, unused_b;
8192 
8193 #ifdef DEBUG
8194     performRegisterLeakCheck();
8195 #endif
8196 
8197 #define emitBinary(doEmit, type)                  \
8198   iter_.readBinary(type, &unused_a, &unused_b) && \
8199       (deadCode_ || (doEmit(), true))
8200 
8201 #define emitUnary(doEmit, type) \
8202   iter_.readUnary(type, &unused_a) && (deadCode_ || (doEmit(), true))
8203 
8204 #define emitComparison(doEmit, operandType, compareOp)       \
8205   iter_.readComparison(operandType, &unused_a, &unused_b) && \
8206       (deadCode_ || (doEmit(compareOp, operandType), true))
8207 
8208 #define emitConversion(doEmit, inType, outType)       \
8209   iter_.readConversion(inType, outType, &unused_a) && \
8210       (deadCode_ || (doEmit(), true))
8211 
8212 #define emitConversionOOM(doEmit, inType, outType) \
8213   iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || doEmit())
8214 
8215 #define emitCalloutConversionOOM(doEmit, symbol, inType, outType) \
8216   iter_.readConversion(inType, outType, &unused_a) &&             \
8217       (deadCode_ || doEmit(symbol, inType, outType))
8218 
8219 #define emitIntDivCallout(doEmit, symbol, type)   \
8220   iter_.readBinary(type, &unused_a, &unused_b) && \
8221       (deadCode_ || (doEmit(symbol, type), true))
8222 
8223 #define CHECK(E) \
8224   if (!(E)) return false
8225 #define NEXT() continue
8226 #define CHECK_NEXT(E)     \
8227   if (!(E)) return false; \
8228   continue
8229 
8230     // TODO / EVALUATE (bug 1316845): Not obvious that this attempt at
8231     // reducing overhead is really paying off relative to making the check
8232     // every iteration.
8233 
8234     if (overhead == 0) {
8235       // Check every 50 expressions -- a happy medium between
8236       // memory usage and checking overhead.
8237       overhead = 50;
8238 
8239       // Checking every 50 expressions should be safe, as the
8240       // baseline JIT does very little allocation per expression.
8241       CHECK(alloc_.ensureBallast());
8242 
8243       // The pushiest opcode is LOOP, which pushes two values
8244       // per instance.
8245       CHECK(stk_.reserve(stk_.length() + overhead * 2));
8246     }
8247 
8248     overhead--;
8249 
8250     OpBytes op;
8251     CHECK(iter_.readOp(&op));
8252 
8253     // When debugEnabled_, every operator has breakpoint site but Op::End.
8254     if (debugEnabled_ && op.b0 != (uint16_t)Op::End) {
8255       // TODO sync only registers that can be clobbered by the exit
8256       // prologue/epilogue or disable these registers for use in
8257       // baseline compiler when debugEnabled_ is set.
8258       sync();
8259 
8260       insertBreakablePoint(CallSiteDesc::Breakpoint);
8261     }
8262 
8263     switch (op.b0) {
8264       case uint16_t(Op::End):
8265         if (!emitEnd()) return false;
8266 
8267         if (iter_.controlStackEmpty()) {
8268           if (!deadCode_) doReturn(sig().ret(), PopStack(false));
8269           return iter_.readFunctionEnd(iter_.end());
8270         }
8271         NEXT();
8272 
8273       // Control opcodes
8274       case uint16_t(Op::Nop):
8275         CHECK_NEXT(iter_.readNop());
8276       case uint16_t(Op::Drop):
8277         CHECK_NEXT(emitDrop());
8278       case uint16_t(Op::Block):
8279         CHECK_NEXT(emitBlock());
8280       case uint16_t(Op::Loop):
8281         CHECK_NEXT(emitLoop());
8282       case uint16_t(Op::If):
8283         CHECK_NEXT(emitIf());
8284       case uint16_t(Op::Else):
8285         CHECK_NEXT(emitElse());
8286       case uint16_t(Op::Br):
8287         CHECK_NEXT(emitBr());
8288       case uint16_t(Op::BrIf):
8289         CHECK_NEXT(emitBrIf());
8290       case uint16_t(Op::BrTable):
8291         CHECK_NEXT(emitBrTable());
8292       case uint16_t(Op::Return):
8293         CHECK_NEXT(emitReturn());
8294       case uint16_t(Op::Unreachable):
8295         CHECK(iter_.readUnreachable());
8296         if (!deadCode_) {
8297           trap(Trap::Unreachable);
8298           deadCode_ = true;
8299         }
8300         NEXT();
8301 
8302       // Calls
8303       case uint16_t(Op::Call):
8304         CHECK_NEXT(emitCall());
8305       case uint16_t(Op::CallIndirect):
8306         CHECK_NEXT(emitCallIndirect());
8307 
8308       // Locals and globals
8309       case uint16_t(Op::GetLocal):
8310         CHECK_NEXT(emitGetLocal());
8311       case uint16_t(Op::SetLocal):
8312         CHECK_NEXT(emitSetLocal());
8313       case uint16_t(Op::TeeLocal):
8314         CHECK_NEXT(emitTeeLocal());
8315       case uint16_t(Op::GetGlobal):
8316         CHECK_NEXT(emitGetGlobal());
8317       case uint16_t(Op::SetGlobal):
8318         CHECK_NEXT(emitSetGlobal());
8319 
8320       // Select
8321       case uint16_t(Op::Select):
8322         CHECK_NEXT(emitSelect());
8323 
8324       // I32
8325       case uint16_t(Op::I32Const): {
8326         int32_t i32;
8327         CHECK(iter_.readI32Const(&i32));
8328         if (!deadCode_) pushI32(i32);
8329         NEXT();
8330       }
8331       case uint16_t(Op::I32Add):
8332         CHECK_NEXT(emitBinary(emitAddI32, ValType::I32));
8333       case uint16_t(Op::I32Sub):
8334         CHECK_NEXT(emitBinary(emitSubtractI32, ValType::I32));
8335       case uint16_t(Op::I32Mul):
8336         CHECK_NEXT(emitBinary(emitMultiplyI32, ValType::I32));
8337       case uint16_t(Op::I32DivS):
8338         CHECK_NEXT(emitBinary(emitQuotientI32, ValType::I32));
8339       case uint16_t(Op::I32DivU):
8340         CHECK_NEXT(emitBinary(emitQuotientU32, ValType::I32));
8341       case uint16_t(Op::I32RemS):
8342         CHECK_NEXT(emitBinary(emitRemainderI32, ValType::I32));
8343       case uint16_t(Op::I32RemU):
8344         CHECK_NEXT(emitBinary(emitRemainderU32, ValType::I32));
8345       case uint16_t(Op::I32Eqz):
8346         CHECK_NEXT(emitConversion(emitEqzI32, ValType::I32, ValType::I32));
8347       case uint16_t(Op::I32TruncSF32):
8348         CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<0>, ValType::F32,
8349                                      ValType::I32));
8350       case uint16_t(Op::I32TruncUF32):
8351         CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<TRUNC_UNSIGNED>,
8352                                      ValType::F32, ValType::I32));
8353       case uint16_t(Op::I32TruncSF64):
8354         CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<0>, ValType::F64,
8355                                      ValType::I32));
8356       case uint16_t(Op::I32TruncUF64):
8357         CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<TRUNC_UNSIGNED>,
8358                                      ValType::F64, ValType::I32));
8359       case uint16_t(Op::I32WrapI64):
8360         CHECK_NEXT(
8361             emitConversion(emitWrapI64ToI32, ValType::I64, ValType::I32));
8362       case uint16_t(Op::I32ReinterpretF32):
8363         CHECK_NEXT(emitConversion(emitReinterpretF32AsI32, ValType::F32,
8364                                   ValType::I32));
8365       case uint16_t(Op::I32Clz):
8366         CHECK_NEXT(emitUnary(emitClzI32, ValType::I32));
8367       case uint16_t(Op::I32Ctz):
8368         CHECK_NEXT(emitUnary(emitCtzI32, ValType::I32));
8369       case uint16_t(Op::I32Popcnt):
8370         CHECK_NEXT(emitUnary(emitPopcntI32, ValType::I32));
8371       case uint16_t(Op::I32Or):
8372         CHECK_NEXT(emitBinary(emitOrI32, ValType::I32));
8373       case uint16_t(Op::I32And):
8374         CHECK_NEXT(emitBinary(emitAndI32, ValType::I32));
8375       case uint16_t(Op::I32Xor):
8376         CHECK_NEXT(emitBinary(emitXorI32, ValType::I32));
8377       case uint16_t(Op::I32Shl):
8378         CHECK_NEXT(emitBinary(emitShlI32, ValType::I32));
8379       case uint16_t(Op::I32ShrS):
8380         CHECK_NEXT(emitBinary(emitShrI32, ValType::I32));
8381       case uint16_t(Op::I32ShrU):
8382         CHECK_NEXT(emitBinary(emitShrU32, ValType::I32));
8383       case uint16_t(Op::I32Load8S):
8384         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int8));
8385       case uint16_t(Op::I32Load8U):
8386         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint8));
8387       case uint16_t(Op::I32Load16S):
8388         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int16));
8389       case uint16_t(Op::I32Load16U):
8390         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint16));
8391       case uint16_t(Op::I32Load):
8392         CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int32));
8393       case uint16_t(Op::I32Store8):
8394         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int8));
8395       case uint16_t(Op::I32Store16):
8396         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int16));
8397       case uint16_t(Op::I32Store):
8398         CHECK_NEXT(emitStore(ValType::I32, Scalar::Int32));
8399       case uint16_t(Op::I32Rotr):
8400         CHECK_NEXT(emitBinary(emitRotrI32, ValType::I32));
8401       case uint16_t(Op::I32Rotl):
8402         CHECK_NEXT(emitBinary(emitRotlI32, ValType::I32));
8403 
8404       // I64
8405       case uint16_t(Op::I64Const): {
8406         int64_t i64;
8407         CHECK(iter_.readI64Const(&i64));
8408         if (!deadCode_) pushI64(i64);
8409         NEXT();
8410       }
8411       case uint16_t(Op::I64Add):
8412         CHECK_NEXT(emitBinary(emitAddI64, ValType::I64));
8413       case uint16_t(Op::I64Sub):
8414         CHECK_NEXT(emitBinary(emitSubtractI64, ValType::I64));
8415       case uint16_t(Op::I64Mul):
8416         CHECK_NEXT(emitBinary(emitMultiplyI64, ValType::I64));
8417       case uint16_t(Op::I64DivS):
8418 #ifdef RABALDR_INT_DIV_I64_CALLOUT
8419         CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall,
8420                                      SymbolicAddress::DivI64, ValType::I64));
8421 #else
8422         CHECK_NEXT(emitBinary(emitQuotientI64, ValType::I64));
8423 #endif
8424       case uint16_t(Op::I64DivU):
8425 #ifdef RABALDR_INT_DIV_I64_CALLOUT
8426         CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall,
8427                                      SymbolicAddress::UDivI64, ValType::I64));
8428 #else
8429         CHECK_NEXT(emitBinary(emitQuotientU64, ValType::I64));
8430 #endif
8431       case uint16_t(Op::I64RemS):
8432 #ifdef RABALDR_INT_DIV_I64_CALLOUT
8433         CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall,
8434                                      SymbolicAddress::ModI64, ValType::I64));
8435 #else
8436         CHECK_NEXT(emitBinary(emitRemainderI64, ValType::I64));
8437 #endif
8438       case uint16_t(Op::I64RemU):
8439 #ifdef RABALDR_INT_DIV_I64_CALLOUT
8440         CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall,
8441                                      SymbolicAddress::UModI64, ValType::I64));
8442 #else
8443         CHECK_NEXT(emitBinary(emitRemainderU64, ValType::I64));
8444 #endif
8445       case uint16_t(Op::I64TruncSF32):
8446 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8447         CHECK_NEXT(
8448             emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
8449                                      SymbolicAddress::TruncateDoubleToInt64,
8450                                      ValType::F32, ValType::I64));
8451 #else
8452         CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<0>, ValType::F32,
8453                                      ValType::I64));
8454 #endif
8455       case uint16_t(Op::I64TruncUF32):
8456 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8457         CHECK_NEXT(
8458             emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
8459                                      SymbolicAddress::TruncateDoubleToUint64,
8460                                      ValType::F32, ValType::I64));
8461 #else
8462         CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<TRUNC_UNSIGNED>,
8463                                      ValType::F32, ValType::I64));
8464 #endif
8465       case uint16_t(Op::I64TruncSF64):
8466 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8467         CHECK_NEXT(
8468             emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
8469                                      SymbolicAddress::TruncateDoubleToInt64,
8470                                      ValType::F64, ValType::I64));
8471 #else
8472         CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<0>, ValType::F64,
8473                                      ValType::I64));
8474 #endif
8475       case uint16_t(Op::I64TruncUF64):
8476 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8477         CHECK_NEXT(
8478             emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
8479                                      SymbolicAddress::TruncateDoubleToUint64,
8480                                      ValType::F64, ValType::I64));
8481 #else
8482         CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<TRUNC_UNSIGNED>,
8483                                      ValType::F64, ValType::I64));
8484 #endif
8485       case uint16_t(Op::I64ExtendSI32):
8486         CHECK_NEXT(
8487             emitConversion(emitExtendI32ToI64, ValType::I32, ValType::I64));
8488       case uint16_t(Op::I64ExtendUI32):
8489         CHECK_NEXT(
8490             emitConversion(emitExtendU32ToI64, ValType::I32, ValType::I64));
8491       case uint16_t(Op::I64ReinterpretF64):
8492         CHECK_NEXT(emitConversion(emitReinterpretF64AsI64, ValType::F64,
8493                                   ValType::I64));
8494       case uint16_t(Op::I64Or):
8495         CHECK_NEXT(emitBinary(emitOrI64, ValType::I64));
8496       case uint16_t(Op::I64And):
8497         CHECK_NEXT(emitBinary(emitAndI64, ValType::I64));
8498       case uint16_t(Op::I64Xor):
8499         CHECK_NEXT(emitBinary(emitXorI64, ValType::I64));
8500       case uint16_t(Op::I64Shl):
8501         CHECK_NEXT(emitBinary(emitShlI64, ValType::I64));
8502       case uint16_t(Op::I64ShrS):
8503         CHECK_NEXT(emitBinary(emitShrI64, ValType::I64));
8504       case uint16_t(Op::I64ShrU):
8505         CHECK_NEXT(emitBinary(emitShrU64, ValType::I64));
8506       case uint16_t(Op::I64Rotr):
8507         CHECK_NEXT(emitBinary(emitRotrI64, ValType::I64));
8508       case uint16_t(Op::I64Rotl):
8509         CHECK_NEXT(emitBinary(emitRotlI64, ValType::I64));
8510       case uint16_t(Op::I64Clz):
8511         CHECK_NEXT(emitUnary(emitClzI64, ValType::I64));
8512       case uint16_t(Op::I64Ctz):
8513         CHECK_NEXT(emitUnary(emitCtzI64, ValType::I64));
8514       case uint16_t(Op::I64Popcnt):
8515         CHECK_NEXT(emitUnary(emitPopcntI64, ValType::I64));
8516       case uint16_t(Op::I64Eqz):
8517         CHECK_NEXT(emitConversion(emitEqzI64, ValType::I64, ValType::I32));
8518       case uint16_t(Op::I64Load8S):
8519         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int8));
8520       case uint16_t(Op::I64Load16S):
8521         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int16));
8522       case uint16_t(Op::I64Load32S):
8523         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int32));
8524       case uint16_t(Op::I64Load8U):
8525         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint8));
8526       case uint16_t(Op::I64Load16U):
8527         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint16));
8528       case uint16_t(Op::I64Load32U):
8529         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint32));
8530       case uint16_t(Op::I64Load):
8531         CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int64));
8532       case uint16_t(Op::I64Store8):
8533         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int8));
8534       case uint16_t(Op::I64Store16):
8535         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int16));
8536       case uint16_t(Op::I64Store32):
8537         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32));
8538       case uint16_t(Op::I64Store):
8539         CHECK_NEXT(emitStore(ValType::I64, Scalar::Int64));
8540 
8541       // F32
8542       case uint16_t(Op::F32Const): {
8543         float f32;
8544         CHECK(iter_.readF32Const(&f32));
8545         if (!deadCode_) pushF32(f32);
8546         NEXT();
8547       }
8548       case uint16_t(Op::F32Add):
8549         CHECK_NEXT(emitBinary(emitAddF32, ValType::F32));
8550       case uint16_t(Op::F32Sub):
8551         CHECK_NEXT(emitBinary(emitSubtractF32, ValType::F32));
8552       case uint16_t(Op::F32Mul):
8553         CHECK_NEXT(emitBinary(emitMultiplyF32, ValType::F32));
8554       case uint16_t(Op::F32Div):
8555         CHECK_NEXT(emitBinary(emitDivideF32, ValType::F32));
8556       case uint16_t(Op::F32Min):
8557         CHECK_NEXT(emitBinary(emitMinF32, ValType::F32));
8558       case uint16_t(Op::F32Max):
8559         CHECK_NEXT(emitBinary(emitMaxF32, ValType::F32));
8560       case uint16_t(Op::F32Neg):
8561         CHECK_NEXT(emitUnary(emitNegateF32, ValType::F32));
8562       case uint16_t(Op::F32Abs):
8563         CHECK_NEXT(emitUnary(emitAbsF32, ValType::F32));
8564       case uint16_t(Op::F32Sqrt):
8565         CHECK_NEXT(emitUnary(emitSqrtF32, ValType::F32));
8566       case uint16_t(Op::F32Ceil):
8567         CHECK_NEXT(
8568             emitUnaryMathBuiltinCall(SymbolicAddress::CeilF, ValType::F32));
8569       case uint16_t(Op::F32Floor):
8570         CHECK_NEXT(
8571             emitUnaryMathBuiltinCall(SymbolicAddress::FloorF, ValType::F32));
8572       case uint16_t(Op::F32DemoteF64):
8573         CHECK_NEXT(
8574             emitConversion(emitConvertF64ToF32, ValType::F64, ValType::F32));
8575       case uint16_t(Op::F32ConvertSI32):
8576         CHECK_NEXT(
8577             emitConversion(emitConvertI32ToF32, ValType::I32, ValType::F32));
8578       case uint16_t(Op::F32ConvertUI32):
8579         CHECK_NEXT(
8580             emitConversion(emitConvertU32ToF32, ValType::I32, ValType::F32));
8581       case uint16_t(Op::F32ConvertSI64):
8582 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
8583         CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
8584                                             SymbolicAddress::Int64ToFloat32,
8585                                             ValType::I64, ValType::F32));
8586 #else
8587         CHECK_NEXT(
8588             emitConversion(emitConvertI64ToF32, ValType::I64, ValType::F32));
8589 #endif
8590       case uint16_t(Op::F32ConvertUI64):
8591 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
8592         CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
8593                                             SymbolicAddress::Uint64ToFloat32,
8594                                             ValType::I64, ValType::F32));
8595 #else
8596         CHECK_NEXT(
8597             emitConversion(emitConvertU64ToF32, ValType::I64, ValType::F32));
8598 #endif
8599       case uint16_t(Op::F32ReinterpretI32):
8600         CHECK_NEXT(emitConversion(emitReinterpretI32AsF32, ValType::I32,
8601                                   ValType::F32));
8602       case uint16_t(Op::F32Load):
8603         CHECK_NEXT(emitLoad(ValType::F32, Scalar::Float32));
8604       case uint16_t(Op::F32Store):
8605         CHECK_NEXT(emitStore(ValType::F32, Scalar::Float32));
8606       case uint16_t(Op::F32CopySign):
8607         CHECK_NEXT(emitBinary(emitCopysignF32, ValType::F32));
8608       case uint16_t(Op::F32Nearest):
8609         CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntF,
8610                                             ValType::F32));
8611       case uint16_t(Op::F32Trunc):
8612         CHECK_NEXT(
8613             emitUnaryMathBuiltinCall(SymbolicAddress::TruncF, ValType::F32));
8614 
8615       // F64
8616       case uint16_t(Op::F64Const): {
8617         double f64;
8618         CHECK(iter_.readF64Const(&f64));
8619         if (!deadCode_) pushF64(f64);
8620         NEXT();
8621       }
8622       case uint16_t(Op::F64Add):
8623         CHECK_NEXT(emitBinary(emitAddF64, ValType::F64));
8624       case uint16_t(Op::F64Sub):
8625         CHECK_NEXT(emitBinary(emitSubtractF64, ValType::F64));
8626       case uint16_t(Op::F64Mul):
8627         CHECK_NEXT(emitBinary(emitMultiplyF64, ValType::F64));
8628       case uint16_t(Op::F64Div):
8629         CHECK_NEXT(emitBinary(emitDivideF64, ValType::F64));
8630       case uint16_t(Op::F64Min):
8631         CHECK_NEXT(emitBinary(emitMinF64, ValType::F64));
8632       case uint16_t(Op::F64Max):
8633         CHECK_NEXT(emitBinary(emitMaxF64, ValType::F64));
8634       case uint16_t(Op::F64Neg):
8635         CHECK_NEXT(emitUnary(emitNegateF64, ValType::F64));
8636       case uint16_t(Op::F64Abs):
8637         CHECK_NEXT(emitUnary(emitAbsF64, ValType::F64));
8638       case uint16_t(Op::F64Sqrt):
8639         CHECK_NEXT(emitUnary(emitSqrtF64, ValType::F64));
8640       case uint16_t(Op::F64Ceil):
8641         CHECK_NEXT(
8642             emitUnaryMathBuiltinCall(SymbolicAddress::CeilD, ValType::F64));
8643       case uint16_t(Op::F64Floor):
8644         CHECK_NEXT(
8645             emitUnaryMathBuiltinCall(SymbolicAddress::FloorD, ValType::F64));
8646       case uint16_t(Op::F64PromoteF32):
8647         CHECK_NEXT(
8648             emitConversion(emitConvertF32ToF64, ValType::F32, ValType::F64));
8649       case uint16_t(Op::F64ConvertSI32):
8650         CHECK_NEXT(
8651             emitConversion(emitConvertI32ToF64, ValType::I32, ValType::F64));
8652       case uint16_t(Op::F64ConvertUI32):
8653         CHECK_NEXT(
8654             emitConversion(emitConvertU32ToF64, ValType::I32, ValType::F64));
8655       case uint16_t(Op::F64ConvertSI64):
8656 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
8657         CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
8658                                             SymbolicAddress::Int64ToDouble,
8659                                             ValType::I64, ValType::F64));
8660 #else
8661         CHECK_NEXT(
8662             emitConversion(emitConvertI64ToF64, ValType::I64, ValType::F64));
8663 #endif
8664       case uint16_t(Op::F64ConvertUI64):
8665 #ifdef RABALDR_I64_TO_FLOAT_CALLOUT
8666         CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
8667                                             SymbolicAddress::Uint64ToDouble,
8668                                             ValType::I64, ValType::F64));
8669 #else
8670         CHECK_NEXT(
8671             emitConversion(emitConvertU64ToF64, ValType::I64, ValType::F64));
8672 #endif
8673       case uint16_t(Op::F64Load):
8674         CHECK_NEXT(emitLoad(ValType::F64, Scalar::Float64));
8675       case uint16_t(Op::F64Store):
8676         CHECK_NEXT(emitStore(ValType::F64, Scalar::Float64));
8677       case uint16_t(Op::F64ReinterpretI64):
8678         CHECK_NEXT(emitConversion(emitReinterpretI64AsF64, ValType::I64,
8679                                   ValType::F64));
8680       case uint16_t(Op::F64CopySign):
8681         CHECK_NEXT(emitBinary(emitCopysignF64, ValType::F64));
8682       case uint16_t(Op::F64Nearest):
8683         CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntD,
8684                                             ValType::F64));
8685       case uint16_t(Op::F64Trunc):
8686         CHECK_NEXT(
8687             emitUnaryMathBuiltinCall(SymbolicAddress::TruncD, ValType::F64));
8688 
8689       // Comparisons
8690       case uint16_t(Op::I32Eq):
8691         CHECK_NEXT(
8692             emitComparison(emitCompareI32, ValType::I32, Assembler::Equal));
8693       case uint16_t(Op::I32Ne):
8694         CHECK_NEXT(
8695             emitComparison(emitCompareI32, ValType::I32, Assembler::NotEqual));
8696       case uint16_t(Op::I32LtS):
8697         CHECK_NEXT(
8698             emitComparison(emitCompareI32, ValType::I32, Assembler::LessThan));
8699       case uint16_t(Op::I32LeS):
8700         CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32,
8701                                   Assembler::LessThanOrEqual));
8702       case uint16_t(Op::I32GtS):
8703         CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32,
8704                                   Assembler::GreaterThan));
8705       case uint16_t(Op::I32GeS):
8706         CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32,
8707                                   Assembler::GreaterThanOrEqual));
8708       case uint16_t(Op::I32LtU):
8709         CHECK_NEXT(
8710             emitComparison(emitCompareI32, ValType::I32, Assembler::Below));
8711       case uint16_t(Op::I32LeU):
8712         CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32,
8713                                   Assembler::BelowOrEqual));
8714       case uint16_t(Op::I32GtU):
8715         CHECK_NEXT(
8716             emitComparison(emitCompareI32, ValType::I32, Assembler::Above));
8717       case uint16_t(Op::I32GeU):
8718         CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32,
8719                                   Assembler::AboveOrEqual));
8720       case uint16_t(Op::I64Eq):
8721         CHECK_NEXT(
8722             emitComparison(emitCompareI64, ValType::I64, Assembler::Equal));
8723       case uint16_t(Op::I64Ne):
8724         CHECK_NEXT(
8725             emitComparison(emitCompareI64, ValType::I64, Assembler::NotEqual));
8726       case uint16_t(Op::I64LtS):
8727         CHECK_NEXT(
8728             emitComparison(emitCompareI64, ValType::I64, Assembler::LessThan));
8729       case uint16_t(Op::I64LeS):
8730         CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64,
8731                                   Assembler::LessThanOrEqual));
8732       case uint16_t(Op::I64GtS):
8733         CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64,
8734                                   Assembler::GreaterThan));
8735       case uint16_t(Op::I64GeS):
8736         CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64,
8737                                   Assembler::GreaterThanOrEqual));
8738       case uint16_t(Op::I64LtU):
8739         CHECK_NEXT(
8740             emitComparison(emitCompareI64, ValType::I64, Assembler::Below));
8741       case uint16_t(Op::I64LeU):
8742         CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64,
8743                                   Assembler::BelowOrEqual));
8744       case uint16_t(Op::I64GtU):
8745         CHECK_NEXT(
8746             emitComparison(emitCompareI64, ValType::I64, Assembler::Above));
8747       case uint16_t(Op::I64GeU):
8748         CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64,
8749                                   Assembler::AboveOrEqual));
8750       case uint16_t(Op::F32Eq):
8751         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8752                                   Assembler::DoubleEqual));
8753       case uint16_t(Op::F32Ne):
8754         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8755                                   Assembler::DoubleNotEqualOrUnordered));
8756       case uint16_t(Op::F32Lt):
8757         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8758                                   Assembler::DoubleLessThan));
8759       case uint16_t(Op::F32Le):
8760         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8761                                   Assembler::DoubleLessThanOrEqual));
8762       case uint16_t(Op::F32Gt):
8763         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8764                                   Assembler::DoubleGreaterThan));
8765       case uint16_t(Op::F32Ge):
8766         CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32,
8767                                   Assembler::DoubleGreaterThanOrEqual));
8768       case uint16_t(Op::F64Eq):
8769         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8770                                   Assembler::DoubleEqual));
8771       case uint16_t(Op::F64Ne):
8772         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8773                                   Assembler::DoubleNotEqualOrUnordered));
8774       case uint16_t(Op::F64Lt):
8775         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8776                                   Assembler::DoubleLessThan));
8777       case uint16_t(Op::F64Le):
8778         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8779                                   Assembler::DoubleLessThanOrEqual));
8780       case uint16_t(Op::F64Gt):
8781         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8782                                   Assembler::DoubleGreaterThan));
8783       case uint16_t(Op::F64Ge):
8784         CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64,
8785                                   Assembler::DoubleGreaterThanOrEqual));
8786 
8787         // Sign extensions
8788 #ifdef ENABLE_WASM_SIGNEXTEND_OPS
8789       case uint16_t(Op::I32Extend8S):
8790         CHECK_NEXT(emitConversion(emitExtendI32_8, ValType::I32, ValType::I32));
8791       case uint16_t(Op::I32Extend16S):
8792         CHECK_NEXT(
8793             emitConversion(emitExtendI32_16, ValType::I32, ValType::I32));
8794       case uint16_t(Op::I64Extend8S):
8795         CHECK_NEXT(emitConversion(emitExtendI64_8, ValType::I64, ValType::I64));
8796       case uint16_t(Op::I64Extend16S):
8797         CHECK_NEXT(
8798             emitConversion(emitExtendI64_16, ValType::I64, ValType::I64));
8799       case uint16_t(Op::I64Extend32S):
8800         CHECK_NEXT(
8801             emitConversion(emitExtendI64_32, ValType::I64, ValType::I64));
8802 #endif
8803 
8804       // Memory Related
8805       case uint16_t(Op::GrowMemory):
8806         CHECK_NEXT(emitGrowMemory());
8807       case uint16_t(Op::CurrentMemory):
8808         CHECK_NEXT(emitCurrentMemory());
8809 
8810       // Numeric operations
8811       case uint16_t(Op::NumericPrefix): {
8812 #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
8813         switch (op.b1) {
8814           case uint16_t(NumericOp::I32TruncSSatF32):
8815             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<TRUNC_SATURATING>,
8816                                          ValType::F32, ValType::I32));
8817           case uint16_t(NumericOp::I32TruncUSatF32):
8818             CHECK_NEXT(emitConversionOOM(
8819                 emitTruncateF32ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
8820                 ValType::F32, ValType::I32));
8821           case uint16_t(NumericOp::I32TruncSSatF64):
8822             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<TRUNC_SATURATING>,
8823                                          ValType::F64, ValType::I32));
8824           case uint16_t(NumericOp::I32TruncUSatF64):
8825             CHECK_NEXT(emitConversionOOM(
8826                 emitTruncateF64ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
8827                 ValType::F64, ValType::I32));
8828           case uint16_t(NumericOp::I64TruncSSatF32):
8829 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8830             CHECK_NEXT(emitCalloutConversionOOM(
8831                 emitConvertFloatingToInt64Callout,
8832                 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F32,
8833                 ValType::I64));
8834 #else
8835             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<TRUNC_SATURATING>,
8836                                          ValType::F32, ValType::I64));
8837 #endif
8838           case uint16_t(NumericOp::I64TruncUSatF32):
8839 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8840             CHECK_NEXT(emitCalloutConversionOOM(
8841                 emitConvertFloatingToInt64Callout,
8842                 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F32,
8843                 ValType::I64));
8844 #else
8845             CHECK_NEXT(emitConversionOOM(
8846                 emitTruncateF32ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
8847                 ValType::F32, ValType::I64));
8848 #endif
8849           case uint16_t(NumericOp::I64TruncSSatF64):
8850 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8851             CHECK_NEXT(emitCalloutConversionOOM(
8852                 emitConvertFloatingToInt64Callout,
8853                 SymbolicAddress::SaturatingTruncateDoubleToInt64, ValType::F64,
8854                 ValType::I64));
8855 #else
8856             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<TRUNC_SATURATING>,
8857                                          ValType::F64, ValType::I64));
8858 #endif
8859           case uint16_t(NumericOp::I64TruncUSatF64):
8860 #ifdef RABALDR_FLOAT_TO_I64_CALLOUT
8861             CHECK_NEXT(emitCalloutConversionOOM(
8862                 emitConvertFloatingToInt64Callout,
8863                 SymbolicAddress::SaturatingTruncateDoubleToUint64, ValType::F64,
8864                 ValType::I64));
8865 #else
8866             CHECK_NEXT(emitConversionOOM(
8867                 emitTruncateF64ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
8868                 ValType::F64, ValType::I64));
8869 #endif
8870           default:
8871             return iter_.unrecognizedOpcode(&op);
8872         }
8873         break;
8874 #else
8875         return iter_.unrecognizedOpcode(&op);
8876 #endif
8877       }
8878 
8879       // Thread operations
8880       case uint16_t(Op::ThreadPrefix): {
8881 #ifdef ENABLE_WASM_THREAD_OPS
8882         switch (op.b1) {
8883           case uint16_t(ThreadOp::Wake):
8884             CHECK_NEXT(emitWake());
8885 
8886           case uint16_t(ThreadOp::I32Wait):
8887             CHECK_NEXT(emitWait(ValType::I32, 4));
8888           case uint16_t(ThreadOp::I64Wait):
8889             CHECK_NEXT(emitWait(ValType::I64, 8));
8890 
8891           case uint16_t(ThreadOp::I32AtomicLoad):
8892             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Int32));
8893           case uint16_t(ThreadOp::I64AtomicLoad):
8894             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Int64));
8895           case uint16_t(ThreadOp::I32AtomicLoad8U):
8896             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint8));
8897           case uint16_t(ThreadOp::I32AtomicLoad16U):
8898             CHECK_NEXT(emitAtomicLoad(ValType::I32, Scalar::Uint16));
8899           case uint16_t(ThreadOp::I64AtomicLoad8U):
8900             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint8));
8901           case uint16_t(ThreadOp::I64AtomicLoad16U):
8902             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint16));
8903           case uint16_t(ThreadOp::I64AtomicLoad32U):
8904             CHECK_NEXT(emitAtomicLoad(ValType::I64, Scalar::Uint32));
8905 
8906           case uint16_t(ThreadOp::I32AtomicStore):
8907             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Int32));
8908           case uint16_t(ThreadOp::I64AtomicStore):
8909             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Int64));
8910           case uint16_t(ThreadOp::I32AtomicStore8U):
8911             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint8));
8912           case uint16_t(ThreadOp::I32AtomicStore16U):
8913             CHECK_NEXT(emitAtomicStore(ValType::I32, Scalar::Uint16));
8914           case uint16_t(ThreadOp::I64AtomicStore8U):
8915             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint8));
8916           case uint16_t(ThreadOp::I64AtomicStore16U):
8917             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint16));
8918           case uint16_t(ThreadOp::I64AtomicStore32U):
8919             CHECK_NEXT(emitAtomicStore(ValType::I64, Scalar::Uint32));
8920 
8921           case uint16_t(ThreadOp::I32AtomicAdd):
8922             CHECK_NEXT(
8923                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAddOp));
8924           case uint16_t(ThreadOp::I64AtomicAdd):
8925             CHECK_NEXT(
8926                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAddOp));
8927           case uint16_t(ThreadOp::I32AtomicAdd8U):
8928             CHECK_NEXT(
8929                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAddOp));
8930           case uint16_t(ThreadOp::I32AtomicAdd16U):
8931             CHECK_NEXT(
8932                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAddOp));
8933           case uint16_t(ThreadOp::I64AtomicAdd8U):
8934             CHECK_NEXT(
8935                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAddOp));
8936           case uint16_t(ThreadOp::I64AtomicAdd16U):
8937             CHECK_NEXT(
8938                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAddOp));
8939           case uint16_t(ThreadOp::I64AtomicAdd32U):
8940             CHECK_NEXT(
8941                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAddOp));
8942 
8943           case uint16_t(ThreadOp::I32AtomicSub):
8944             CHECK_NEXT(
8945                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchSubOp));
8946           case uint16_t(ThreadOp::I64AtomicSub):
8947             CHECK_NEXT(
8948                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchSubOp));
8949           case uint16_t(ThreadOp::I32AtomicSub8U):
8950             CHECK_NEXT(
8951                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchSubOp));
8952           case uint16_t(ThreadOp::I32AtomicSub16U):
8953             CHECK_NEXT(
8954                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchSubOp));
8955           case uint16_t(ThreadOp::I64AtomicSub8U):
8956             CHECK_NEXT(
8957                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchSubOp));
8958           case uint16_t(ThreadOp::I64AtomicSub16U):
8959             CHECK_NEXT(
8960                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchSubOp));
8961           case uint16_t(ThreadOp::I64AtomicSub32U):
8962             CHECK_NEXT(
8963                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchSubOp));
8964 
8965           case uint16_t(ThreadOp::I32AtomicAnd):
8966             CHECK_NEXT(
8967                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchAndOp));
8968           case uint16_t(ThreadOp::I64AtomicAnd):
8969             CHECK_NEXT(
8970                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchAndOp));
8971           case uint16_t(ThreadOp::I32AtomicAnd8U):
8972             CHECK_NEXT(
8973                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchAndOp));
8974           case uint16_t(ThreadOp::I32AtomicAnd16U):
8975             CHECK_NEXT(
8976                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchAndOp));
8977           case uint16_t(ThreadOp::I64AtomicAnd8U):
8978             CHECK_NEXT(
8979                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchAndOp));
8980           case uint16_t(ThreadOp::I64AtomicAnd16U):
8981             CHECK_NEXT(
8982                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchAndOp));
8983           case uint16_t(ThreadOp::I64AtomicAnd32U):
8984             CHECK_NEXT(
8985                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchAndOp));
8986 
8987           case uint16_t(ThreadOp::I32AtomicOr):
8988             CHECK_NEXT(
8989                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchOrOp));
8990           case uint16_t(ThreadOp::I64AtomicOr):
8991             CHECK_NEXT(
8992                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchOrOp));
8993           case uint16_t(ThreadOp::I32AtomicOr8U):
8994             CHECK_NEXT(
8995                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchOrOp));
8996           case uint16_t(ThreadOp::I32AtomicOr16U):
8997             CHECK_NEXT(
8998                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchOrOp));
8999           case uint16_t(ThreadOp::I64AtomicOr8U):
9000             CHECK_NEXT(
9001                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchOrOp));
9002           case uint16_t(ThreadOp::I64AtomicOr16U):
9003             CHECK_NEXT(
9004                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchOrOp));
9005           case uint16_t(ThreadOp::I64AtomicOr32U):
9006             CHECK_NEXT(
9007                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchOrOp));
9008 
9009           case uint16_t(ThreadOp::I32AtomicXor):
9010             CHECK_NEXT(
9011                 emitAtomicRMW(ValType::I32, Scalar::Int32, AtomicFetchXorOp));
9012           case uint16_t(ThreadOp::I64AtomicXor):
9013             CHECK_NEXT(
9014                 emitAtomicRMW(ValType::I64, Scalar::Int64, AtomicFetchXorOp));
9015           case uint16_t(ThreadOp::I32AtomicXor8U):
9016             CHECK_NEXT(
9017                 emitAtomicRMW(ValType::I32, Scalar::Uint8, AtomicFetchXorOp));
9018           case uint16_t(ThreadOp::I32AtomicXor16U):
9019             CHECK_NEXT(
9020                 emitAtomicRMW(ValType::I32, Scalar::Uint16, AtomicFetchXorOp));
9021           case uint16_t(ThreadOp::I64AtomicXor8U):
9022             CHECK_NEXT(
9023                 emitAtomicRMW(ValType::I64, Scalar::Uint8, AtomicFetchXorOp));
9024           case uint16_t(ThreadOp::I64AtomicXor16U):
9025             CHECK_NEXT(
9026                 emitAtomicRMW(ValType::I64, Scalar::Uint16, AtomicFetchXorOp));
9027           case uint16_t(ThreadOp::I64AtomicXor32U):
9028             CHECK_NEXT(
9029                 emitAtomicRMW(ValType::I64, Scalar::Uint32, AtomicFetchXorOp));
9030 
9031           case uint16_t(ThreadOp::I32AtomicXchg):
9032             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Int32));
9033           case uint16_t(ThreadOp::I64AtomicXchg):
9034             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Int64));
9035           case uint16_t(ThreadOp::I32AtomicXchg8U):
9036             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint8));
9037           case uint16_t(ThreadOp::I32AtomicXchg16U):
9038             CHECK_NEXT(emitAtomicXchg(ValType::I32, Scalar::Uint16));
9039           case uint16_t(ThreadOp::I64AtomicXchg8U):
9040             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint8));
9041           case uint16_t(ThreadOp::I64AtomicXchg16U):
9042             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint16));
9043           case uint16_t(ThreadOp::I64AtomicXchg32U):
9044             CHECK_NEXT(emitAtomicXchg(ValType::I64, Scalar::Uint32));
9045 
9046           case uint16_t(ThreadOp::I32AtomicCmpXchg):
9047             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Int32));
9048           case uint16_t(ThreadOp::I64AtomicCmpXchg):
9049             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Int64));
9050           case uint16_t(ThreadOp::I32AtomicCmpXchg8U):
9051             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint8));
9052           case uint16_t(ThreadOp::I32AtomicCmpXchg16U):
9053             CHECK_NEXT(emitAtomicCmpXchg(ValType::I32, Scalar::Uint16));
9054           case uint16_t(ThreadOp::I64AtomicCmpXchg8U):
9055             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint8));
9056           case uint16_t(ThreadOp::I64AtomicCmpXchg16U):
9057             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint16));
9058           case uint16_t(ThreadOp::I64AtomicCmpXchg32U):
9059             CHECK_NEXT(emitAtomicCmpXchg(ValType::I64, Scalar::Uint32));
9060 
9061           default:
9062             return iter_.unrecognizedOpcode(&op);
9063         }
9064 #else
9065         return iter_.unrecognizedOpcode(&op);
9066 #endif  // ENABLE_WASM_THREAD_OPS
9067         break;
9068       }
9069 
9070       // asm.js operations
9071       case uint16_t(Op::MozPrefix):
9072         return iter_.unrecognizedOpcode(&op);
9073 
9074       default:
9075         return iter_.unrecognizedOpcode(&op);
9076     }
9077 
9078 #undef CHECK
9079 #undef NEXT
9080 #undef CHECK_NEXT
9081 #undef emitBinary
9082 #undef emitUnary
9083 #undef emitComparison
9084 #undef emitConversion
9085 #undef emitConversionOOM
9086 #undef emitCalloutConversionOOM
9087 
9088     MOZ_CRASH("unreachable");
9089   }
9090 
9091   MOZ_CRASH("unreachable");
9092 }
9093 
emitFunction()9094 bool BaseCompiler::emitFunction() {
9095   beginFunction();
9096 
9097   if (!emitBody()) return false;
9098 
9099   if (!endFunction()) return false;
9100 
9101   return true;
9102 }
9103 
BaseCompiler(const ModuleEnvironment & env,Decoder & decoder,const FuncCompileInput & func,const ValTypeVector & locals,bool debugEnabled,TempAllocator * alloc,MacroAssembler * masm,CompileMode mode)9104 BaseCompiler::BaseCompiler(const ModuleEnvironment& env, Decoder& decoder,
9105                            const FuncCompileInput& func,
9106                            const ValTypeVector& locals, bool debugEnabled,
9107                            TempAllocator* alloc, MacroAssembler* masm,
9108                            CompileMode mode)
9109     : env_(env),
9110       iter_(env, decoder),
9111       func_(func),
9112       lastReadCallSite_(0),
9113       alloc_(*alloc),
9114       locals_(locals),
9115       deadCode_(false),
9116       debugEnabled_(debugEnabled),
9117       bceSafe_(0),
9118       mode_(mode),
9119       latentOp_(LatentOp::None),
9120       latentType_(ValType::I32),
9121       latentIntCmp_(Assembler::Equal),
9122       latentDoubleCmp_(Assembler::DoubleEqual),
9123       masm(*masm),
9124       ra(*this),
9125       fr(*masm),
9126       joinRegI32(RegI32(ReturnReg)),
9127       joinRegI64(RegI64(ReturnReg64)),
9128       joinRegF32(RegF32(ReturnFloat32Reg)),
9129       joinRegF64(RegF64(ReturnDoubleReg)) {}
9130 
init()9131 bool BaseCompiler::init() {
9132   if (!SigD_.append(ValType::F64)) return false;
9133   if (!SigF_.append(ValType::F32)) return false;
9134   if (!SigP_.append(MIRType::Pointer)) return false;
9135   if (!SigPI_.append(MIRType::Pointer) || !SigPI_.append(MIRType::Int32))
9136     return false;
9137   if (!SigPII_.append(MIRType::Pointer) || !SigPII_.append(MIRType::Int32) ||
9138       !SigPII_.append(MIRType::Int32)) {
9139     return false;
9140   }
9141   if (!SigPIIL_.append(MIRType::Pointer) || !SigPIIL_.append(MIRType::Int32) ||
9142       !SigPIIL_.append(MIRType::Int32) || !SigPIIL_.append(MIRType::Int64)) {
9143     return false;
9144   }
9145   if (!SigPILL_.append(MIRType::Pointer) || !SigPILL_.append(MIRType::Int32) ||
9146       !SigPILL_.append(MIRType::Int64) || !SigPILL_.append(MIRType::Int64)) {
9147     return false;
9148   }
9149 
9150   if (!fr.setupLocals(locals_, sig().args(), debugEnabled_, &localInfo_))
9151     return false;
9152 
9153   addInterruptCheck();
9154 
9155   return true;
9156 }
9157 
finish()9158 FuncOffsets BaseCompiler::finish() {
9159   MOZ_ASSERT(done(), "all bytes must be consumed");
9160   MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_);
9161 
9162   masm.flushBuffer();
9163 
9164   return offsets_;
9165 }
9166 
9167 }  // namespace wasm
9168 }  // namespace js
9169 
BaselineCanCompile()9170 bool js::wasm::BaselineCanCompile() {
9171   // On all platforms we require signals for Wasm.
9172   // If we made it this far we must have signals.
9173   MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
9174 
9175 #if defined(JS_CODEGEN_ARM)
9176   // Simplifying assumption: require SDIV and UDIV.
9177   //
9178   // I have no good data on ARM populations allowing me to say that
9179   // X% of devices in the market implement SDIV and UDIV.  However,
9180   // they are definitely implemented on the Cortex-A7 and Cortex-A15
9181   // and on all ARMv8 systems.
9182   if (!HasIDIV()) return false;
9183 #endif
9184 
9185 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) ||    \
9186     defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
9187     defined(JS_CODEGEN_MIPS64)
9188   return true;
9189 #else
9190   return false;
9191 #endif
9192 }
9193 
BaselineCompileFunctions(const ModuleEnvironment & env,LifoAlloc & lifo,const FuncCompileInputVector & inputs,CompiledCode * code,UniqueChars * error)9194 bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& env,
9195                                         LifoAlloc& lifo,
9196                                         const FuncCompileInputVector& inputs,
9197                                         CompiledCode* code,
9198                                         UniqueChars* error) {
9199   MOZ_ASSERT(env.tier == Tier::Baseline);
9200   MOZ_ASSERT(env.kind == ModuleKind::Wasm);
9201 
9202   // The MacroAssembler will sometimes access the jitContext.
9203 
9204   TempAllocator alloc(&lifo);
9205   JitContext jitContext(&alloc);
9206   MOZ_ASSERT(IsCompilingWasm());
9207   MacroAssembler masm(MacroAssembler::WasmToken(), alloc);
9208 
9209   // Swap in already-allocated empty vectors to avoid malloc/free.
9210   MOZ_ASSERT(code->empty());
9211   if (!code->swap(masm)) return false;
9212 
9213   for (const FuncCompileInput& func : inputs) {
9214     Decoder d(func.begin, func.end, func.lineOrBytecode, error);
9215 
9216     // Build the local types vector.
9217 
9218     ValTypeVector locals;
9219     if (!locals.appendAll(env.funcSigs[func.index]->args())) return false;
9220     if (!DecodeLocalEntries(d, env.kind, &locals)) return false;
9221 
9222     // One-pass baseline compilation.
9223 
9224     BaseCompiler f(env, d, func, locals, env.debugEnabled(), &alloc, &masm,
9225                    env.mode);
9226     if (!f.init()) return false;
9227 
9228     if (!f.emitFunction()) return false;
9229 
9230     if (!code->codeRanges.emplaceBack(func.index, func.lineOrBytecode,
9231                                       f.finish()))
9232       return false;
9233   }
9234 
9235   masm.finish();
9236   if (masm.oom()) return false;
9237 
9238   return code->swap(masm);
9239 }
9240 
9241 #undef RABALDR_INT_DIV_I64_CALLOUT
9242 #undef RABALDR_I64_TO_FLOAT_CALLOUT
9243 #undef RABALDR_FLOAT_TO_I64_CALLOUT
9244