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), ¬Min);
3402 if (zeroOnOverflow) {
3403 masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Min);
3404 moveImm32(0, srcDest);
3405 masm.jump(done);
3406 } else {
3407 masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Min);
3408 trap(Trap::IntegerOverflow);
3409 }
3410 masm.bind(¬Min);
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), ¬min);
3417 masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬min);
3418 if (zeroOnOverflow) {
3419 masm.xor64(srcDest, srcDest);
3420 masm.jump(done);
3421 } else {
3422 trap(Trap::IntegerOverflow);
3423 }
3424 masm.bind(¬min);
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, ¬Taken);
5056 fr.popStackBeforeBranch(b->stackHeight);
5057 masm.jump(b->label);
5058 masm.bind(¬Taken);
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, ¬hing, ¬hing)) 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, ¬hing)) 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