Line data Source code
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 status notes:
22 : *
23 : * "FIXME" indicates a known or suspected bug. Always has a bug#.
24 : *
25 : * "TODO" indicates an opportunity for a general improvement, with an additional
26 : * tag to indicate the area of improvement. Usually has a bug#.
27 : *
28 : * Unimplemented functionality:
29 : *
30 : * - Tiered compilation (bug 1277562)
31 : *
32 : * There are lots of machine dependencies here but they are pretty well isolated
33 : * to a segment of the compiler. Many dependencies will eventually be factored
34 : * into the MacroAssembler layer and shared with other code generators.
35 : *
36 : *
37 : * High-value compiler performance improvements:
38 : *
39 : * - (Bug 1316802) The specific-register allocator (the needI32(r), needI64(r)
40 : * etc methods) can avoid syncing the value stack if the specific register is
41 : * in use but there is a free register to shuffle the specific register into.
42 : * (This will also improve the generated code.) The sync happens often enough
43 : * here to show up in profiles, because it is triggered by integer multiply
44 : * and divide.
45 : *
46 : *
47 : * High-value code generation improvements:
48 : *
49 : * - (Bug 1316804) brTable pessimizes by always dispatching to code that pops
50 : * the stack and then jumps to the code for the target case. If no cleanup is
51 : * needed we could just branch conditionally to the target; if the same amount
52 : * of cleanup is needed for all cases then the cleanup can be done before the
53 : * dispatch. Both are highly likely.
54 : *
55 : * - (Bug 1316806) Register management around calls: At the moment we sync the
56 : * value stack unconditionally (this is simple) but there are probably many
57 : * common cases where we could instead save/restore live caller-saves
58 : * registers and perform parallel assignment into argument registers. This
59 : * may be important if we keep some locals in registers.
60 : *
61 : * - (Bug 1316808) Allocate some locals to registers on machines where there are
62 : * enough registers. This is probably hard to do well in a one-pass compiler
63 : * but it might be that just keeping register arguments and the first few
64 : * locals in registers is a viable strategy; another (more general) strategy
65 : * is caching locals in registers in straight-line code. Such caching could
66 : * also track constant values in registers, if that is deemed valuable. A
67 : * combination of techniques may be desirable: parameters and the first few
68 : * locals could be cached on entry to the function but not statically assigned
69 : * to registers throughout.
70 : *
71 : * (On a large corpus of code it should be possible to compute, for every
72 : * signature comprising the types of parameters and locals, and using a static
73 : * weight for loops, a list in priority order of which parameters and locals
74 : * that should be assigned to registers. Or something like that. Wasm makes
75 : * this simple. Static assignments are desirable because they are not flushed
76 : * to memory by the pre-block sync() call.)
77 : */
78 :
79 : #include "wasm/WasmBaselineCompile.h"
80 :
81 : #include "mozilla/MathAlgorithms.h"
82 :
83 : #include "jit/AtomicOp.h"
84 : #include "jit/IonTypes.h"
85 : #include "jit/JitAllocPolicy.h"
86 : #include "jit/Label.h"
87 : #include "jit/MacroAssembler.h"
88 : #include "jit/MIR.h"
89 : #include "jit/Registers.h"
90 : #include "jit/RegisterSets.h"
91 : #if defined(JS_CODEGEN_ARM)
92 : # include "jit/arm/Assembler-arm.h"
93 : #endif
94 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
95 : # include "jit/x86-shared/Architecture-x86-shared.h"
96 : # include "jit/x86-shared/Assembler-x86-shared.h"
97 : #endif
98 :
99 : #include "wasm/WasmBinaryIterator.h"
100 : #include "wasm/WasmGenerator.h"
101 : #include "wasm/WasmSignalHandlers.h"
102 : #include "wasm/WasmValidate.h"
103 :
104 : #include "jit/MacroAssembler-inl.h"
105 :
106 : using mozilla::DebugOnly;
107 : using mozilla::FloatingPoint;
108 : using mozilla::FloorLog2;
109 : using mozilla::IsPowerOfTwo;
110 : using mozilla::SpecificNaN;
111 :
112 : namespace js {
113 : namespace wasm {
114 :
115 : using namespace js::jit;
116 : using JS::GenericNaN;
117 :
118 : typedef bool HandleNaNSpecially;
119 : typedef bool InvertBranch;
120 : typedef bool IsKnownNotZero;
121 : typedef bool IsSigned;
122 : typedef bool IsUnsigned;
123 : typedef bool NeedsBoundsCheck;
124 : typedef bool PopStack;
125 : typedef bool ZeroOnOverflow;
126 :
127 : typedef unsigned ByteSize;
128 : typedef unsigned BitSize;
129 :
130 : // UseABI::Wasm implies that the Tls/Heap/Global registers are nonvolatile,
131 : // except when InterModule::True is also set, when they are volatile.
132 : //
133 : // UseABI::System implies that the Tls/Heap/Global registers are volatile.
134 : // Additionally, the parameter passing mechanism may be slightly different from
135 : // the UseABI::Wasm convention.
136 : //
137 : // When the Tls/Heap/Global registers are not volatile, the baseline compiler
138 : // will restore the Tls register from its save slot before the call, since the
139 : // baseline compiler uses the Tls register for other things.
140 : //
141 : // When those registers are volatile, the baseline compiler will reload them
142 : // after the call (it will restore the Tls register from the save slot and load
143 : // the other two from the Tls data).
144 :
145 : enum class UseABI { Wasm, System };
146 : enum class InterModule { False = false, True = true };
147 :
148 : #ifdef JS_CODEGEN_ARM64
149 : // FIXME: This is not correct, indeed for ARM64 there is no reliable
150 : // StackPointer and we'll need to change the abstractions that use
151 : // SP-relative addressing. There's a guard in emitFunction() below to
152 : // prevent this workaround from having any consequence. This hack
153 : // exists only as a stopgap; there is no ARM64 JIT support yet.
154 : static const Register StackPointer = RealStackPointer;
155 : #endif
156 :
157 : #ifdef JS_CODEGEN_X86
158 : // The selection of EBX here steps gingerly around: the need for EDX
159 : // to be allocatable for multiply/divide; ECX to be allocatable for
160 : // shift/rotate; EAX (= ReturnReg) to be allocatable as the joinreg;
161 : // EBX not being one of the WasmTableCall registers; and needing a
162 : // temp register for load/store that has a single-byte persona.
163 : static const Register ScratchRegX86 = ebx;
164 :
165 : # define INT_DIV_I64_CALLOUT
166 : #endif
167 :
168 : #ifdef JS_CODEGEN_ARM
169 : // We need a temp for funcPtrCall. It can't be any of the
170 : // WasmTableCall registers, an argument register, or a scratch
171 : // register, and probably should not be ReturnReg.
172 : static const Register FuncPtrCallTemp = CallTempReg1;
173 :
174 : // We use our own scratch register, because the macro assembler uses
175 : // the regular scratch register(s) pretty liberally. We could
176 : // work around that in several cases but the mess does not seem
177 : // worth it yet. CallTempReg2 seems safe.
178 : static const Register ScratchRegARM = CallTempReg2;
179 :
180 : # define INT_DIV_I64_CALLOUT
181 : # define I64_TO_FLOAT_CALLOUT
182 : # define FLOAT_TO_I64_CALLOUT
183 : #endif
184 :
185 : template<MIRType t>
186 : struct RegTypeOf {
187 : static_assert(t == MIRType::Float32 || t == MIRType::Double, "Float mask type");
188 : };
189 :
190 : template<> struct RegTypeOf<MIRType::Float32> {
191 : static constexpr RegTypeName value = RegTypeName::Float32;
192 : };
193 : template<> struct RegTypeOf<MIRType::Double> {
194 : static constexpr RegTypeName value = RegTypeName::Float64;
195 : };
196 :
197 0 : BaseLocalIter::BaseLocalIter(const ValTypeVector& locals,
198 : size_t argsLength,
199 0 : bool debugEnabled)
200 : : locals_(locals),
201 : argsLength_(argsLength),
202 : argsRange_(locals.begin(), argsLength),
203 : argsIter_(argsRange_),
204 : index_(0),
205 0 : localSize_(debugEnabled ? DebugFrame::offsetOfFrame() : 0),
206 0 : reservedSize_(localSize_),
207 0 : done_(false)
208 : {
209 0 : MOZ_ASSERT(argsLength <= locals.length());
210 :
211 0 : settle();
212 0 : }
213 :
214 : int32_t
215 0 : BaseLocalIter::pushLocal(size_t nbytes)
216 : {
217 0 : if (nbytes == 8)
218 0 : localSize_ = AlignBytes(localSize_, 8u);
219 0 : else if (nbytes == 16)
220 0 : localSize_ = AlignBytes(localSize_, 16u);
221 0 : localSize_ += nbytes;
222 0 : return localSize_; // Locals grow down so capture base address
223 : }
224 :
225 : void
226 0 : BaseLocalIter::settle()
227 : {
228 0 : if (index_ < argsLength_) {
229 0 : MOZ_ASSERT(!argsIter_.done());
230 0 : mirType_ = argsIter_.mirType();
231 0 : switch (mirType_) {
232 : case MIRType::Int32:
233 0 : if (argsIter_->argInRegister())
234 0 : frameOffset_ = pushLocal(4);
235 : else
236 0 : frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
237 0 : break;
238 : case MIRType::Int64:
239 0 : if (argsIter_->argInRegister())
240 0 : frameOffset_ = pushLocal(8);
241 : else
242 0 : frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
243 0 : break;
244 : case MIRType::Double:
245 0 : if (argsIter_->argInRegister())
246 0 : frameOffset_ = pushLocal(8);
247 : else
248 0 : frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
249 0 : break;
250 : case MIRType::Float32:
251 0 : if (argsIter_->argInRegister())
252 0 : frameOffset_ = pushLocal(4);
253 : else
254 0 : frameOffset_ = -(argsIter_->offsetFromArgBase() + sizeof(Frame));
255 0 : break;
256 : default:
257 0 : MOZ_CRASH("Argument type");
258 : }
259 0 : return;
260 : }
261 :
262 0 : MOZ_ASSERT(argsIter_.done());
263 0 : if (index_ < locals_.length()) {
264 0 : switch (locals_[index_]) {
265 : case ValType::I32:
266 0 : mirType_ = jit::MIRType::Int32;
267 0 : frameOffset_ = pushLocal(4);
268 0 : break;
269 : case ValType::F32:
270 0 : mirType_ = jit::MIRType::Float32;
271 0 : frameOffset_ = pushLocal(4);
272 0 : break;
273 : case ValType::F64:
274 0 : mirType_ = jit::MIRType::Double;
275 0 : frameOffset_ = pushLocal(8);
276 0 : break;
277 : case ValType::I64:
278 0 : mirType_ = jit::MIRType::Int64;
279 0 : frameOffset_ = pushLocal(8);
280 0 : break;
281 : default:
282 0 : MOZ_CRASH("Compiler bug: Unexpected local type");
283 : }
284 0 : return;
285 : }
286 :
287 0 : done_ = true;
288 : }
289 :
290 : void
291 0 : BaseLocalIter::operator++(int)
292 : {
293 0 : MOZ_ASSERT(!done_);
294 0 : index_++;
295 0 : if (!argsIter_.done())
296 0 : argsIter_++;
297 0 : settle();
298 0 : }
299 :
300 0 : class BaseCompiler
301 : {
302 : // We define our own ScratchRegister abstractions, deferring to
303 : // the platform's when possible.
304 :
305 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
306 : typedef ScratchDoubleScope ScratchF64;
307 : #else
308 : class ScratchF64
309 : {
310 : public:
311 : ScratchF64(BaseCompiler& b) {}
312 : operator FloatRegister() const {
313 : MOZ_CRASH("BaseCompiler platform hook - ScratchF64");
314 : }
315 : };
316 : #endif
317 :
318 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
319 : typedef ScratchFloat32Scope ScratchF32;
320 : #else
321 : class ScratchF32
322 : {
323 : public:
324 : ScratchF32(BaseCompiler& b) {}
325 : operator FloatRegister() const {
326 : MOZ_CRASH("BaseCompiler platform hook - ScratchF32");
327 : }
328 : };
329 : #endif
330 :
331 : #if defined(JS_CODEGEN_X64)
332 : typedef ScratchRegisterScope ScratchI32;
333 : #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
334 : class ScratchI32
335 : {
336 : # ifdef DEBUG
337 : BaseCompiler& bc;
338 : public:
339 : explicit ScratchI32(BaseCompiler& bc) : bc(bc) {
340 : MOZ_ASSERT(!bc.scratchRegisterTaken());
341 : bc.setScratchRegisterTaken(true);
342 : }
343 : ~ScratchI32() {
344 : MOZ_ASSERT(bc.scratchRegisterTaken());
345 : bc.setScratchRegisterTaken(false);
346 : }
347 : # else
348 : public:
349 : explicit ScratchI32(BaseCompiler& bc) {}
350 : # endif
351 : operator Register() const {
352 : # ifdef JS_CODEGEN_X86
353 : return ScratchRegX86;
354 : # else
355 : return ScratchRegARM;
356 : # endif
357 : }
358 : };
359 : #else
360 : class ScratchI32
361 : {
362 : public:
363 : ScratchI32(BaseCompiler& bc) {}
364 : operator Register() const {
365 : MOZ_CRASH("BaseCompiler platform hook - ScratchI32");
366 : }
367 : };
368 : #endif
369 :
370 : typedef Vector<NonAssertingLabel, 8, SystemAllocPolicy> LabelVector;
371 :
372 : // The strongly typed register wrappers have saved my bacon a few
373 : // times; though they are largely redundant they stay, for now.
374 :
375 : struct RegI32 : public Register
376 : {
377 0 : RegI32() : Register(Register::Invalid()) {}
378 0 : explicit RegI32(Register reg) : Register(reg) {}
379 : };
380 :
381 : struct RegI64 : public Register64
382 : {
383 0 : RegI64() : Register64(Register64::Invalid()) {}
384 0 : explicit RegI64(Register64 reg) : Register64(reg) {}
385 : };
386 :
387 : struct RegF32 : public FloatRegister
388 : {
389 0 : RegF32() : FloatRegister() {}
390 0 : explicit RegF32(FloatRegister reg) : FloatRegister(reg) {}
391 : };
392 :
393 : struct RegF64 : public FloatRegister
394 : {
395 0 : RegF64() : FloatRegister() {}
396 0 : explicit RegF64(FloatRegister reg) : FloatRegister(reg) {}
397 : };
398 :
399 : struct AnyReg
400 : {
401 0 : AnyReg() { tag = NONE; }
402 0 : explicit AnyReg(RegI32 r) { tag = I32; i32_ = r; }
403 0 : explicit AnyReg(RegI64 r) { tag = I64; i64_ = r; }
404 0 : explicit AnyReg(RegF32 r) { tag = F32; f32_ = r; }
405 0 : explicit AnyReg(RegF64 r) { tag = F64; f64_ = r; }
406 :
407 0 : RegI32 i32() {
408 0 : MOZ_ASSERT(tag == I32);
409 0 : return i32_;
410 : }
411 0 : RegI64 i64() {
412 0 : MOZ_ASSERT(tag == I64);
413 0 : return i64_;
414 : }
415 0 : RegF32 f32() {
416 0 : MOZ_ASSERT(tag == F32);
417 0 : return f32_;
418 : }
419 0 : RegF64 f64() {
420 0 : MOZ_ASSERT(tag == F64);
421 0 : return f64_;
422 : }
423 0 : AnyRegister any() {
424 0 : switch (tag) {
425 0 : case F32: return AnyRegister(f32_);
426 0 : case F64: return AnyRegister(f64_);
427 0 : case I32: return AnyRegister(i32_);
428 : case I64:
429 : #ifdef JS_PUNBOX64
430 0 : return AnyRegister(i64_.reg);
431 : #else
432 : // The compiler is written so that this is never needed: any() is called
433 : // on arbitrary registers for asm.js but asm.js does not have 64-bit ints.
434 : // For wasm, any() is called on arbitrary registers only on 64-bit platforms.
435 : MOZ_CRASH("AnyReg::any() on 32-bit platform");
436 : #endif
437 : case NONE:
438 0 : MOZ_CRASH("AnyReg::any() on NONE");
439 : }
440 : // Work around GCC 5 analysis/warning bug.
441 0 : MOZ_CRASH("AnyReg::any(): impossible case");
442 : }
443 :
444 : union {
445 : RegI32 i32_;
446 : RegI64 i64_;
447 : RegF32 f32_;
448 : RegF64 f64_;
449 : };
450 : enum { NONE, I32, I64, F32, F64 } tag;
451 : };
452 :
453 : struct Local
454 : {
455 0 : Local() : type_(MIRType::None), offs_(UINT32_MAX) {}
456 : Local(MIRType type, uint32_t offs) : type_(type), offs_(offs) {}
457 :
458 0 : void init(MIRType type_, uint32_t offs_) {
459 0 : this->type_ = type_;
460 0 : this->offs_ = offs_;
461 0 : }
462 :
463 : MIRType type_; // Type of the value, or MIRType::None
464 : uint32_t offs_; // Zero-based frame offset of value, or UINT32_MAX
465 :
466 0 : MIRType type() const { MOZ_ASSERT(type_ != MIRType::None); return type_; }
467 0 : uint32_t offs() const { MOZ_ASSERT(offs_ != UINT32_MAX); return offs_; }
468 : };
469 :
470 : // Bit set used for simple bounds check elimination. Capping this at 64
471 : // locals makes sense; even 32 locals would probably be OK in practice.
472 : //
473 : // For more information about BCE, see the block comment above
474 : // popMemoryAccess(), below.
475 :
476 : typedef uint64_t BCESet;
477 :
478 : // Control node, representing labels and stack heights at join points.
479 :
480 0 : struct Control
481 : {
482 0 : Control()
483 0 : : framePushed(UINT32_MAX),
484 : stackSize(UINT32_MAX),
485 : bceSafeOnEntry(0),
486 : bceSafeOnExit(~BCESet(0)),
487 : deadOnArrival(false),
488 0 : deadThenBranch(false)
489 0 : {}
490 :
491 : NonAssertingLabel label; // The "exit" label
492 : NonAssertingLabel otherLabel; // Used for the "else" branch of if-then-else
493 : uint32_t framePushed; // From masm
494 : uint32_t stackSize; // Value stack height
495 : BCESet bceSafeOnEntry; // Bounds check info flowing into the item
496 : BCESet bceSafeOnExit; // Bounds check info flowing out of the item
497 : bool deadOnArrival; // deadCode_ was set on entry to the region
498 : bool deadThenBranch; // deadCode_ was set on exit from "then"
499 : };
500 :
501 0 : struct BaseCompilePolicy
502 : {
503 : // The baseline compiler tracks values on a stack of its own -- it
504 : // needs to scan that stack for spilling -- and thus has no need
505 : // for the values maintained by the iterator.
506 : typedef Nothing Value;
507 :
508 : // The baseline compiler uses the iterator's control stack, attaching
509 : // its own control information.
510 : typedef Control ControlItem;
511 : };
512 :
513 : typedef OpIter<BaseCompilePolicy> BaseOpIter;
514 :
515 : // Volatile registers except ReturnReg.
516 :
517 : static LiveRegisterSet VolatileReturnGPR;
518 :
519 : // The baseline compiler will use OOL code more sparingly than
520 : // Baldr since our code is not high performance and frills like
521 : // code density and branch prediction friendliness will be less
522 : // important.
523 :
524 : class OutOfLineCode : public TempObject
525 : {
526 : private:
527 : NonAssertingLabel entry_;
528 : NonAssertingLabel rejoin_;
529 : uint32_t framePushed_;
530 :
531 : public:
532 0 : OutOfLineCode() : framePushed_(UINT32_MAX) {}
533 :
534 0 : Label* entry() { return &entry_; }
535 0 : Label* rejoin() { return &rejoin_; }
536 :
537 0 : void setFramePushed(uint32_t framePushed) {
538 0 : MOZ_ASSERT(framePushed_ == UINT32_MAX);
539 0 : framePushed_ = framePushed;
540 0 : }
541 :
542 0 : void bind(MacroAssembler& masm) {
543 0 : MOZ_ASSERT(framePushed_ != UINT32_MAX);
544 0 : masm.bind(&entry_);
545 0 : masm.setFramePushed(framePushed_);
546 0 : }
547 :
548 : // The generate() method must be careful about register use
549 : // because it will be invoked when there is a register
550 : // assignment in the BaseCompiler that does not correspond
551 : // to the available registers when the generated OOL code is
552 : // executed. The register allocator *must not* be called.
553 : //
554 : // The best strategy is for the creator of the OOL object to
555 : // allocate all temps that the OOL code will need.
556 : //
557 : // Input, output, and temp registers are embedded in the OOL
558 : // object and are known to the code generator.
559 : //
560 : // Scratch registers are available to use in OOL code.
561 : //
562 : // All other registers must be explicitly saved and restored
563 : // by the OOL code before being used.
564 :
565 : virtual void generate(MacroAssembler& masm) = 0;
566 : };
567 :
568 : enum class LatentOp {
569 : None,
570 : Compare,
571 : Eqz
572 : };
573 :
574 : const ModuleEnvironment& env_;
575 : BaseOpIter iter_;
576 : const FuncBytes& func_;
577 : size_t lastReadCallSite_;
578 : TempAllocator& alloc_;
579 : const ValTypeVector& locals_; // Types of parameters and locals
580 : int32_t localSize_; // Size of local area in bytes (stable after beginFunction)
581 : int32_t varLow_; // Low byte offset of local area for true locals (not parameters)
582 : int32_t varHigh_; // High byte offset + 1 of local area for true locals
583 : int32_t maxFramePushed_; // Max value of masm.framePushed() observed
584 : bool deadCode_; // Flag indicating we should decode & discard the opcode
585 : bool debugEnabled_;
586 : BCESet bceSafe_; // Locals that have been bounds checked and not updated since
587 : ValTypeVector SigI64I64_;
588 : ValTypeVector SigD_;
589 : ValTypeVector SigF_;
590 : MIRTypeVector SigPI_;
591 : MIRTypeVector SigP_;
592 : NonAssertingLabel returnLabel_;
593 : NonAssertingLabel stackOverflowLabel_;
594 : CodeOffset stackAddOffset_;
595 :
596 : LatentOp latentOp_; // Latent operation for branch (seen next)
597 : ValType latentType_; // Operand type, if latentOp_ is true
598 : Assembler::Condition latentIntCmp_; // Comparison operator, if latentOp_ == Compare, int types
599 : Assembler::DoubleCondition latentDoubleCmp_;// Comparison operator, if latentOp_ == Compare, float types
600 :
601 : FuncOffsets offsets_;
602 : MacroAssembler& masm; // No '_' suffix - too tedious...
603 :
604 : AllocatableGeneralRegisterSet availGPR_;
605 : AllocatableFloatRegisterSet availFPU_;
606 : #ifdef DEBUG
607 : bool scratchRegisterTaken_;
608 : AllocatableGeneralRegisterSet allGPR_; // The registers available to the compiler
609 : AllocatableFloatRegisterSet allFPU_; // after removing ScratchReg, HeapReg, etc
610 : #endif
611 :
612 : Vector<Local, 8, SystemAllocPolicy> localInfo_;
613 : Vector<OutOfLineCode*, 8, SystemAllocPolicy> outOfLine_;
614 :
615 : // On specific platforms we sometimes need to use specific registers.
616 :
617 : #ifdef JS_CODEGEN_X64
618 : RegI64 specific_rax;
619 : RegI64 specific_rcx;
620 : RegI64 specific_rdx;
621 : #endif
622 :
623 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
624 : RegI32 specific_eax;
625 : RegI32 specific_ecx;
626 : RegI32 specific_edx;
627 : #endif
628 :
629 : #if defined(JS_CODEGEN_X86)
630 : AllocatableGeneralRegisterSet singleByteRegs_;
631 : #endif
632 : #if defined(JS_NUNBOX32)
633 : RegI64 abiReturnRegI64;
634 : #endif
635 :
636 : // The join registers are used to carry values out of blocks.
637 : // JoinRegI32 and joinRegI64 must overlap: emitBrIf and
638 : // emitBrTable assume that.
639 :
640 : RegI32 joinRegI32;
641 : RegI64 joinRegI64;
642 : RegF32 joinRegF32;
643 : RegF64 joinRegF64;
644 :
645 : // There are more members scattered throughout.
646 :
647 : public:
648 : BaseCompiler(const ModuleEnvironment& env,
649 : Decoder& decoder,
650 : const FuncBytes& func,
651 : const ValTypeVector& locals,
652 : bool debugEnabled,
653 : TempAllocator* alloc,
654 : MacroAssembler* masm);
655 :
656 : MOZ_MUST_USE bool init();
657 :
658 : FuncOffsets finish();
659 :
660 : MOZ_MUST_USE bool emitFunction();
661 :
662 : // Used by some of the ScratchRegister implementations.
663 0 : operator MacroAssembler&() const { return masm; }
664 :
665 : #ifdef DEBUG
666 : bool scratchRegisterTaken() const {
667 : return scratchRegisterTaken_;
668 : }
669 : void setScratchRegisterTaken(bool state) {
670 : scratchRegisterTaken_ = state;
671 : }
672 : #endif
673 :
674 : private:
675 :
676 : ////////////////////////////////////////////////////////////
677 : //
678 : // Out of line code management.
679 :
680 0 : MOZ_MUST_USE OutOfLineCode* addOutOfLineCode(OutOfLineCode* ool) {
681 0 : if (!ool || !outOfLine_.append(ool))
682 0 : return nullptr;
683 0 : ool->setFramePushed(masm.framePushed());
684 0 : return ool;
685 : }
686 :
687 0 : MOZ_MUST_USE bool generateOutOfLineCode() {
688 0 : for (uint32_t i = 0; i < outOfLine_.length(); i++) {
689 0 : OutOfLineCode* ool = outOfLine_[i];
690 0 : ool->bind(masm);
691 0 : ool->generate(masm);
692 : }
693 :
694 0 : return !masm.oom();
695 : }
696 :
697 : ////////////////////////////////////////////////////////////
698 : //
699 : // The stack frame.
700 :
701 : // SP-relative load and store.
702 :
703 0 : int32_t localOffsetToSPOffset(int32_t offset) {
704 0 : return masm.framePushed() - offset;
705 : }
706 :
707 0 : void storeToFrameI32(Register r, int32_t offset) {
708 0 : masm.store32(r, Address(StackPointer, localOffsetToSPOffset(offset)));
709 0 : }
710 :
711 0 : void storeToFrameI64(Register64 r, int32_t offset) {
712 0 : masm.store64(r, Address(StackPointer, localOffsetToSPOffset(offset)));
713 0 : }
714 :
715 : void storeToFramePtr(Register r, int32_t offset) {
716 : masm.storePtr(r, Address(StackPointer, localOffsetToSPOffset(offset)));
717 : }
718 :
719 0 : void storeToFrameF64(FloatRegister r, int32_t offset) {
720 0 : masm.storeDouble(r, Address(StackPointer, localOffsetToSPOffset(offset)));
721 0 : }
722 :
723 0 : void storeToFrameF32(FloatRegister r, int32_t offset) {
724 0 : masm.storeFloat32(r, Address(StackPointer, localOffsetToSPOffset(offset)));
725 0 : }
726 :
727 0 : void loadFromFrameI32(Register r, int32_t offset) {
728 0 : masm.load32(Address(StackPointer, localOffsetToSPOffset(offset)), r);
729 0 : }
730 :
731 0 : void loadFromFrameI64(Register64 r, int32_t offset) {
732 0 : masm.load64(Address(StackPointer, localOffsetToSPOffset(offset)), r);
733 0 : }
734 :
735 : void loadFromFramePtr(Register r, int32_t offset) {
736 : masm.loadPtr(Address(StackPointer, localOffsetToSPOffset(offset)), r);
737 : }
738 :
739 0 : void loadFromFrameF64(FloatRegister r, int32_t offset) {
740 0 : masm.loadDouble(Address(StackPointer, localOffsetToSPOffset(offset)), r);
741 0 : }
742 :
743 0 : void loadFromFrameF32(FloatRegister r, int32_t offset) {
744 0 : masm.loadFloat32(Address(StackPointer, localOffsetToSPOffset(offset)), r);
745 0 : }
746 :
747 0 : int32_t frameOffsetFromSlot(uint32_t slot, MIRType type) {
748 0 : MOZ_ASSERT(localInfo_[slot].type() == type);
749 0 : return localInfo_[slot].offs();
750 : }
751 :
752 : ////////////////////////////////////////////////////////////
753 : //
754 : // Low-level register allocation.
755 :
756 0 : bool isAvailable(Register r) {
757 0 : return availGPR_.has(r);
758 : }
759 :
760 0 : bool hasGPR() {
761 0 : return !availGPR_.empty();
762 : }
763 :
764 0 : void allocGPR(Register r) {
765 0 : MOZ_ASSERT(isAvailable(r));
766 0 : availGPR_.take(r);
767 0 : }
768 :
769 0 : Register allocGPR() {
770 0 : MOZ_ASSERT(hasGPR());
771 0 : return availGPR_.takeAny();
772 : }
773 :
774 0 : void freeGPR(Register r) {
775 0 : availGPR_.add(r);
776 0 : }
777 :
778 0 : bool isAvailable(Register64 r) {
779 : #ifdef JS_PUNBOX64
780 0 : return isAvailable(r.reg);
781 : #else
782 : return isAvailable(r.low) && isAvailable(r.high);
783 : #endif
784 : }
785 :
786 0 : bool hasInt64() {
787 : #ifdef JS_PUNBOX64
788 0 : return !availGPR_.empty();
789 : #else
790 : if (availGPR_.empty())
791 : return false;
792 : Register r = allocGPR();
793 : bool available = !availGPR_.empty();
794 : freeGPR(r);
795 : return available;
796 : #endif
797 : }
798 :
799 0 : void allocInt64(Register64 r) {
800 0 : MOZ_ASSERT(isAvailable(r));
801 : #ifdef JS_PUNBOX64
802 0 : availGPR_.take(r.reg);
803 : #else
804 : availGPR_.take(r.low);
805 : availGPR_.take(r.high);
806 : #endif
807 0 : }
808 :
809 0 : Register64 allocInt64() {
810 0 : MOZ_ASSERT(hasInt64());
811 : #ifdef JS_PUNBOX64
812 0 : return Register64(availGPR_.takeAny());
813 : #else
814 : Register high = availGPR_.takeAny();
815 : Register low = availGPR_.takeAny();
816 : return Register64(high, low);
817 : #endif
818 : }
819 :
820 0 : void freeInt64(Register64 r) {
821 : #ifdef JS_PUNBOX64
822 0 : availGPR_.add(r.reg);
823 : #else
824 : availGPR_.add(r.low);
825 : availGPR_.add(r.high);
826 : #endif
827 0 : }
828 :
829 : // Notes on float register allocation.
830 : //
831 : // The general rule in SpiderMonkey is that float registers can
832 : // alias double registers, but there are predicates to handle
833 : // exceptions to that rule: hasUnaliasedDouble() and
834 : // hasMultiAlias(). The way aliasing actually works is platform
835 : // dependent and exposed through the aliased(n, &r) predicate,
836 : // etc.
837 : //
838 : // - hasUnaliasedDouble(): on ARM VFPv3-D32 there are double
839 : // registers that cannot be treated as float.
840 : // - hasMultiAlias(): on ARM and MIPS a double register aliases
841 : // two float registers.
842 : // - notes in Architecture-arm.h indicate that when we use a
843 : // float register that aliases a double register we only use
844 : // the low float register, never the high float register. I
845 : // think those notes lie, or at least are confusing.
846 : // - notes in Architecture-mips32.h suggest that the MIPS port
847 : // will use both low and high float registers except on the
848 : // Longsoon, which may be the only MIPS that's being tested, so
849 : // who knows what's working.
850 : // - SIMD is not yet implemented on ARM or MIPS so constraints
851 : // may change there.
852 : //
853 : // On some platforms (x86, x64, ARM64) but not all (ARM)
854 : // ScratchFloat32Register is the same as ScratchDoubleRegister.
855 : //
856 : // It's a basic invariant of the AllocatableRegisterSet that it
857 : // deals properly with aliasing of registers: if s0 or s1 are
858 : // allocated then d0 is not allocatable; if s0 and s1 are freed
859 : // individually then d0 becomes allocatable.
860 :
861 : template<MIRType t>
862 0 : bool hasFPU() {
863 0 : return availFPU_.hasAny<RegTypeOf<t>::value>();
864 : }
865 :
866 0 : bool isAvailable(FloatRegister r) {
867 0 : return availFPU_.has(r);
868 : }
869 :
870 0 : void allocFPU(FloatRegister r) {
871 0 : MOZ_ASSERT(isAvailable(r));
872 0 : availFPU_.take(r);
873 0 : }
874 :
875 : template<MIRType t>
876 0 : FloatRegister allocFPU() {
877 0 : return availFPU_.takeAny<RegTypeOf<t>::value>();
878 : }
879 :
880 0 : void freeFPU(FloatRegister r) {
881 0 : availFPU_.add(r);
882 0 : }
883 :
884 : ////////////////////////////////////////////////////////////
885 : //
886 : // Value stack and high-level register allocation.
887 : //
888 : // The value stack facilitates some on-the-fly register allocation
889 : // and immediate-constant use. It tracks constants, latent
890 : // references to locals, register contents, and values on the CPU
891 : // stack.
892 : //
893 : // The stack can be flushed to memory using sync(). This is handy
894 : // to avoid problems with control flow and messy register usage
895 : // patterns.
896 :
897 : struct Stk
898 : {
899 : enum Kind
900 : {
901 : // The Mem opcodes are all clustered at the beginning to
902 : // allow for a quick test within sync().
903 : MemI32, // 32-bit integer stack value ("offs")
904 : MemI64, // 64-bit integer stack value ("offs")
905 : MemF32, // 32-bit floating stack value ("offs")
906 : MemF64, // 64-bit floating stack value ("offs")
907 :
908 : // The Local opcodes follow the Mem opcodes for a similar
909 : // quick test within hasLocal().
910 : LocalI32, // Local int32 var ("slot")
911 : LocalI64, // Local int64 var ("slot")
912 : LocalF32, // Local float32 var ("slot")
913 : LocalF64, // Local double var ("slot")
914 :
915 : RegisterI32, // 32-bit integer register ("i32reg")
916 : RegisterI64, // 64-bit integer register ("i64reg")
917 : RegisterF32, // 32-bit floating register ("f32reg")
918 : RegisterF64, // 64-bit floating register ("f64reg")
919 :
920 : ConstI32, // 32-bit integer constant ("i32val")
921 : ConstI64, // 64-bit integer constant ("i64val")
922 : ConstF32, // 32-bit floating constant ("f32val")
923 : ConstF64, // 64-bit floating constant ("f64val")
924 :
925 : None // Uninitialized or void
926 : };
927 :
928 : Kind kind_;
929 :
930 : static const Kind MemLast = MemF64;
931 : static const Kind LocalLast = LocalF64;
932 :
933 : union {
934 : RegI32 i32reg_;
935 : RegI64 i64reg_;
936 : RegF32 f32reg_;
937 : RegF64 f64reg_;
938 : int32_t i32val_;
939 : int64_t i64val_;
940 : float f32val_;
941 : double f64val_;
942 : uint32_t slot_;
943 : uint32_t offs_;
944 : };
945 :
946 0 : Stk() { kind_ = None; }
947 :
948 0 : Kind kind() const { return kind_; }
949 0 : bool isMem() const { return kind_ <= MemLast; }
950 :
951 0 : RegI32 i32reg() const { MOZ_ASSERT(kind_ == RegisterI32); return i32reg_; }
952 0 : RegI64 i64reg() const { MOZ_ASSERT(kind_ == RegisterI64); return i64reg_; }
953 0 : RegF32 f32reg() const { MOZ_ASSERT(kind_ == RegisterF32); return f32reg_; }
954 0 : RegF64 f64reg() const { MOZ_ASSERT(kind_ == RegisterF64); return f64reg_; }
955 0 : int32_t i32val() const { MOZ_ASSERT(kind_ == ConstI32); return i32val_; }
956 0 : int64_t i64val() const { MOZ_ASSERT(kind_ == ConstI64); return i64val_; }
957 : // For these two, use an out-param instead of simply returning, to
958 : // use the normal stack and not the x87 FP stack (which has effect on
959 : // NaNs with the signaling bit set).
960 0 : void f32val(float* out) const { MOZ_ASSERT(kind_ == ConstF32); *out = f32val_; }
961 0 : void f64val(double* out) const { MOZ_ASSERT(kind_ == ConstF64); *out = f64val_; }
962 0 : uint32_t slot() const { MOZ_ASSERT(kind_ > MemLast && kind_ <= LocalLast); return slot_; }
963 0 : uint32_t offs() const { MOZ_ASSERT(isMem()); return offs_; }
964 :
965 0 : void setI32Reg(RegI32 r) { kind_ = RegisterI32; i32reg_ = r; }
966 0 : void setI64Reg(RegI64 r) { kind_ = RegisterI64; i64reg_ = r; }
967 0 : void setF32Reg(RegF32 r) { kind_ = RegisterF32; f32reg_ = r; }
968 0 : void setF64Reg(RegF64 r) { kind_ = RegisterF64; f64reg_ = r; }
969 0 : void setI32Val(int32_t v) { kind_ = ConstI32; i32val_ = v; }
970 0 : void setI64Val(int64_t v) { kind_ = ConstI64; i64val_ = v; }
971 0 : void setF32Val(float v) { kind_ = ConstF32; f32val_ = v; }
972 0 : void setF64Val(double v) { kind_ = ConstF64; f64val_ = v; }
973 0 : void setSlot(Kind k, uint32_t v) { MOZ_ASSERT(k > MemLast && k <= LocalLast); kind_ = k; slot_ = v; }
974 0 : void setOffs(Kind k, uint32_t v) { MOZ_ASSERT(k <= MemLast); kind_ = k; offs_ = v; }
975 : };
976 :
977 : Vector<Stk, 8, SystemAllocPolicy> stk_;
978 :
979 0 : Stk& push() {
980 0 : stk_.infallibleEmplaceBack(Stk());
981 0 : return stk_.back();
982 : }
983 :
984 : Register64 invalidRegister64() {
985 : return Register64::Invalid();
986 : }
987 :
988 0 : RegI32 invalidI32() {
989 0 : return RegI32(Register::Invalid());
990 : }
991 :
992 : RegI64 invalidI64() {
993 : return RegI64(invalidRegister64());
994 : }
995 :
996 0 : RegF64 invalidF64() {
997 0 : return RegF64(InvalidFloatReg);
998 : }
999 :
1000 0 : RegI32 fromI64(RegI64 r) {
1001 0 : return RegI32(lowPart(r));
1002 : }
1003 :
1004 0 : RegI64 widenI32(RegI32 r) {
1005 0 : MOZ_ASSERT(!isAvailable(r));
1006 : #ifdef JS_PUNBOX64
1007 0 : return RegI64(Register64(r));
1008 : #else
1009 : RegI32 high = needI32();
1010 : return RegI64(Register64(high, r));
1011 : #endif
1012 : }
1013 :
1014 0 : Register lowPart(RegI64 r) {
1015 : #ifdef JS_PUNBOX64
1016 0 : return r.reg;
1017 : #else
1018 : return r.low;
1019 : #endif
1020 : }
1021 :
1022 0 : Register maybeHighPart(RegI64 r) {
1023 : #ifdef JS_PUNBOX64
1024 0 : return Register::Invalid();
1025 : #else
1026 : return r.high;
1027 : #endif
1028 : }
1029 :
1030 0 : void maybeClearHighPart(RegI64 r) {
1031 : #ifdef JS_NUNBOX32
1032 : masm.move32(Imm32(0), r.high);
1033 : #endif
1034 0 : }
1035 :
1036 0 : void freeI32(RegI32 r) {
1037 0 : freeGPR(r);
1038 0 : }
1039 :
1040 0 : void freeI64(RegI64 r) {
1041 0 : freeInt64(r);
1042 0 : }
1043 :
1044 0 : void freeI64Except(RegI64 r, RegI32 except) {
1045 : #ifdef JS_PUNBOX64
1046 0 : MOZ_ASSERT(r.reg == except);
1047 : #else
1048 : MOZ_ASSERT(r.high == except || r.low == except);
1049 : freeI64(r);
1050 : needI32(except);
1051 : #endif
1052 0 : }
1053 :
1054 0 : void freeF64(RegF64 r) {
1055 0 : freeFPU(r);
1056 0 : }
1057 :
1058 0 : void freeF32(RegF32 r) {
1059 0 : freeFPU(r);
1060 0 : }
1061 :
1062 0 : MOZ_MUST_USE RegI32 needI32() {
1063 0 : if (!hasGPR())
1064 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1065 0 : return RegI32(allocGPR());
1066 : }
1067 :
1068 0 : void needI32(RegI32 specific) {
1069 0 : if (!isAvailable(specific))
1070 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1071 0 : allocGPR(specific);
1072 0 : }
1073 :
1074 : // TODO / OPTIMIZE: need2xI32() can be optimized along with needI32()
1075 : // to avoid sync(). (Bug 1316802)
1076 :
1077 0 : void need2xI32(RegI32 r0, RegI32 r1) {
1078 0 : needI32(r0);
1079 0 : needI32(r1);
1080 0 : }
1081 :
1082 0 : MOZ_MUST_USE RegI64 needI64() {
1083 0 : if (!hasInt64())
1084 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1085 0 : return RegI64(allocInt64());
1086 : }
1087 :
1088 0 : void needI64(RegI64 specific) {
1089 0 : if (!isAvailable(specific))
1090 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1091 0 : allocInt64(specific);
1092 0 : }
1093 :
1094 0 : void need2xI64(RegI64 r0, RegI64 r1) {
1095 0 : needI64(r0);
1096 0 : needI64(r1);
1097 0 : }
1098 :
1099 0 : MOZ_MUST_USE RegF32 needF32() {
1100 0 : if (!hasFPU<MIRType::Float32>())
1101 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1102 0 : return RegF32(allocFPU<MIRType::Float32>());
1103 : }
1104 :
1105 0 : void needF32(RegF32 specific) {
1106 0 : if (!isAvailable(specific))
1107 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1108 0 : allocFPU(specific);
1109 0 : }
1110 :
1111 0 : MOZ_MUST_USE RegF64 needF64() {
1112 0 : if (!hasFPU<MIRType::Double>())
1113 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1114 0 : return RegF64(allocFPU<MIRType::Double>());
1115 : }
1116 :
1117 0 : void needF64(RegF64 specific) {
1118 0 : if (!isAvailable(specific))
1119 0 : sync(); // TODO / OPTIMIZE: improve this (Bug 1316802)
1120 0 : allocFPU(specific);
1121 0 : }
1122 :
1123 0 : void moveI32(RegI32 src, RegI32 dest) {
1124 0 : if (src != dest)
1125 0 : masm.move32(src, dest);
1126 0 : }
1127 :
1128 0 : void moveI64(RegI64 src, RegI64 dest) {
1129 0 : if (src != dest)
1130 0 : masm.move64(src, dest);
1131 0 : }
1132 :
1133 0 : void moveF64(RegF64 src, RegF64 dest) {
1134 0 : if (src != dest)
1135 0 : masm.moveDouble(src, dest);
1136 0 : }
1137 :
1138 0 : void moveF32(RegF32 src, RegF32 dest) {
1139 0 : if (src != dest)
1140 0 : masm.moveFloat32(src, dest);
1141 0 : }
1142 :
1143 : void setI64(int64_t v, RegI64 r) {
1144 : masm.move64(Imm64(v), r);
1145 : }
1146 :
1147 0 : void loadConstI32(Register r, Stk& src) {
1148 0 : masm.mov(ImmWord(uint32_t(src.i32val())), r);
1149 0 : }
1150 :
1151 0 : void loadConstI32(Register r, int32_t v) {
1152 0 : masm.mov(ImmWord(uint32_t(v)), r);
1153 0 : }
1154 :
1155 0 : void loadMemI32(Register r, Stk& src) {
1156 0 : loadFromFrameI32(r, src.offs());
1157 0 : }
1158 :
1159 0 : void loadLocalI32(Register r, Stk& src) {
1160 0 : loadFromFrameI32(r, frameOffsetFromSlot(src.slot(), MIRType::Int32));
1161 0 : }
1162 :
1163 0 : void loadRegisterI32(Register r, Stk& src) {
1164 0 : if (src.i32reg() != r)
1165 0 : masm.move32(src.i32reg(), r);
1166 0 : }
1167 :
1168 0 : void loadConstI64(Register64 r, Stk &src) {
1169 0 : masm.move64(Imm64(src.i64val()), r);
1170 0 : }
1171 :
1172 0 : void loadMemI64(Register64 r, Stk& src) {
1173 0 : loadFromFrameI64(r, src.offs());
1174 0 : }
1175 :
1176 0 : void loadLocalI64(Register64 r, Stk& src) {
1177 0 : loadFromFrameI64(r, frameOffsetFromSlot(src.slot(), MIRType::Int64));
1178 0 : }
1179 :
1180 0 : void loadRegisterI64(Register64 r, Stk& src) {
1181 0 : if (src.i64reg() != r)
1182 0 : masm.move64(src.i64reg(), r);
1183 0 : }
1184 :
1185 0 : void loadConstF64(FloatRegister r, Stk &src) {
1186 : double d;
1187 0 : src.f64val(&d);
1188 0 : masm.loadConstantDouble(d, r);
1189 0 : }
1190 :
1191 0 : void loadMemF64(FloatRegister r, Stk& src) {
1192 0 : loadFromFrameF64(r, src.offs());
1193 0 : }
1194 :
1195 0 : void loadLocalF64(FloatRegister r, Stk& src) {
1196 0 : loadFromFrameF64(r, frameOffsetFromSlot(src.slot(), MIRType::Double));
1197 0 : }
1198 :
1199 0 : void loadRegisterF64(FloatRegister r, Stk& src) {
1200 0 : if (src.f64reg() != r)
1201 0 : masm.moveDouble(src.f64reg(), r);
1202 0 : }
1203 :
1204 0 : void loadConstF32(FloatRegister r, Stk &src) {
1205 : float f;
1206 0 : src.f32val(&f);
1207 0 : masm.loadConstantFloat32(f, r);
1208 0 : }
1209 :
1210 0 : void loadMemF32(FloatRegister r, Stk& src) {
1211 0 : loadFromFrameF32(r, src.offs());
1212 0 : }
1213 :
1214 0 : void loadLocalF32(FloatRegister r, Stk& src) {
1215 0 : loadFromFrameF32(r, frameOffsetFromSlot(src.slot(), MIRType::Float32));
1216 0 : }
1217 :
1218 0 : void loadRegisterF32(FloatRegister r, Stk& src) {
1219 0 : if (src.f32reg() != r)
1220 0 : masm.moveFloat32(src.f32reg(), r);
1221 0 : }
1222 :
1223 0 : void loadI32(Register r, Stk& src) {
1224 0 : switch (src.kind()) {
1225 : case Stk::ConstI32:
1226 0 : loadConstI32(r, src);
1227 0 : break;
1228 : case Stk::MemI32:
1229 0 : loadMemI32(r, src);
1230 0 : break;
1231 : case Stk::LocalI32:
1232 0 : loadLocalI32(r, src);
1233 0 : break;
1234 : case Stk::RegisterI32:
1235 0 : loadRegisterI32(r, src);
1236 0 : break;
1237 : case Stk::None:
1238 : default:
1239 0 : MOZ_CRASH("Compiler bug: Expected I32 on stack");
1240 : }
1241 0 : }
1242 :
1243 0 : void loadI64(Register64 r, Stk& src) {
1244 0 : switch (src.kind()) {
1245 : case Stk::ConstI64:
1246 0 : loadConstI64(r, src);
1247 0 : break;
1248 : case Stk::MemI64:
1249 0 : loadMemI64(r, src);
1250 0 : break;
1251 : case Stk::LocalI64:
1252 0 : loadLocalI64(r, src);
1253 0 : break;
1254 : case Stk::RegisterI64:
1255 0 : loadRegisterI64(r, src);
1256 0 : break;
1257 : case Stk::None:
1258 : default:
1259 0 : MOZ_CRASH("Compiler bug: Expected I64 on stack");
1260 : }
1261 0 : }
1262 :
1263 : #ifdef JS_NUNBOX32
1264 : void loadI64Low(Register r, Stk& src) {
1265 : switch (src.kind()) {
1266 : case Stk::ConstI64:
1267 : masm.move32(Imm64(src.i64val()).low(), r);
1268 : break;
1269 : case Stk::MemI64:
1270 : loadFromFrameI32(r, src.offs() - INT64LOW_OFFSET);
1271 : break;
1272 : case Stk::LocalI64:
1273 : loadFromFrameI32(r, frameOffsetFromSlot(src.slot(), MIRType::Int64) - INT64LOW_OFFSET);
1274 : break;
1275 : case Stk::RegisterI64:
1276 : if (src.i64reg().low != r)
1277 : masm.move32(src.i64reg().low, r);
1278 : break;
1279 : case Stk::None:
1280 : default:
1281 : MOZ_CRASH("Compiler bug: Expected I64 on stack");
1282 : }
1283 : }
1284 :
1285 : void loadI64High(Register r, Stk& src) {
1286 : switch (src.kind()) {
1287 : case Stk::ConstI64:
1288 : masm.move32(Imm64(src.i64val()).hi(), r);
1289 : break;
1290 : case Stk::MemI64:
1291 : loadFromFrameI32(r, src.offs() - INT64HIGH_OFFSET);
1292 : break;
1293 : case Stk::LocalI64:
1294 : loadFromFrameI32(r, frameOffsetFromSlot(src.slot(), MIRType::Int64) - INT64HIGH_OFFSET);
1295 : break;
1296 : case Stk::RegisterI64:
1297 : if (src.i64reg().high != r)
1298 : masm.move32(src.i64reg().high, r);
1299 : break;
1300 : case Stk::None:
1301 : default:
1302 : MOZ_CRASH("Compiler bug: Expected I64 on stack");
1303 : }
1304 : }
1305 : #endif
1306 :
1307 0 : void loadF64(FloatRegister r, Stk& src) {
1308 0 : switch (src.kind()) {
1309 : case Stk::ConstF64:
1310 0 : loadConstF64(r, src);
1311 0 : break;
1312 : case Stk::MemF64:
1313 0 : loadMemF64(r, src);
1314 0 : break;
1315 : case Stk::LocalF64:
1316 0 : loadLocalF64(r, src);
1317 0 : break;
1318 : case Stk::RegisterF64:
1319 0 : loadRegisterF64(r, src);
1320 0 : break;
1321 : case Stk::None:
1322 : default:
1323 0 : MOZ_CRASH("Compiler bug: expected F64 on stack");
1324 : }
1325 0 : }
1326 :
1327 0 : void loadF32(FloatRegister r, Stk& src) {
1328 0 : switch (src.kind()) {
1329 : case Stk::ConstF32:
1330 0 : loadConstF32(r, src);
1331 0 : break;
1332 : case Stk::MemF32:
1333 0 : loadMemF32(r, src);
1334 0 : break;
1335 : case Stk::LocalF32:
1336 0 : loadLocalF32(r, src);
1337 0 : break;
1338 : case Stk::RegisterF32:
1339 0 : loadRegisterF32(r, src);
1340 0 : break;
1341 : case Stk::None:
1342 : default:
1343 0 : MOZ_CRASH("Compiler bug: expected F32 on stack");
1344 : }
1345 0 : }
1346 :
1347 : // Flush all local and register value stack elements to memory.
1348 : //
1349 : // TODO / OPTIMIZE: As this is fairly expensive and causes worse
1350 : // code to be emitted subsequently, it is useful to avoid calling
1351 : // it. (Bug 1316802)
1352 : //
1353 : // Some optimization has been done already. Remaining
1354 : // opportunities:
1355 : //
1356 : // - It would be interesting to see if we can specialize it
1357 : // before calls with particularly simple signatures, or where
1358 : // we can do parallel assignment of register arguments, or
1359 : // similar. See notes in emitCall().
1360 : //
1361 : // - Operations that need specific registers: multiply, quotient,
1362 : // remainder, will tend to sync because the registers we need
1363 : // will tend to be allocated. We may be able to avoid that by
1364 : // prioritizing registers differently (takeLast instead of
1365 : // takeFirst) but we may also be able to allocate an unused
1366 : // register on demand to free up one we need, thus avoiding the
1367 : // sync. That type of fix would go into needI32().
1368 :
1369 0 : void sync() {
1370 0 : size_t start = 0;
1371 0 : size_t lim = stk_.length();
1372 :
1373 0 : for (size_t i = lim; i > 0; i--) {
1374 : // Memory opcodes are first in the enum, single check against MemLast is fine.
1375 0 : if (stk_[i - 1].kind() <= Stk::MemLast) {
1376 0 : start = i;
1377 0 : break;
1378 : }
1379 : }
1380 :
1381 0 : for (size_t i = start; i < lim; i++) {
1382 0 : Stk& v = stk_[i];
1383 0 : switch (v.kind()) {
1384 : case Stk::LocalI32: {
1385 0 : ScratchI32 scratch(*this);
1386 0 : loadLocalI32(scratch, v);
1387 0 : masm.Push(scratch);
1388 0 : v.setOffs(Stk::MemI32, masm.framePushed());
1389 0 : break;
1390 : }
1391 : case Stk::RegisterI32: {
1392 0 : masm.Push(v.i32reg());
1393 0 : freeI32(v.i32reg());
1394 0 : v.setOffs(Stk::MemI32, masm.framePushed());
1395 0 : break;
1396 : }
1397 : case Stk::LocalI64: {
1398 0 : ScratchI32 scratch(*this);
1399 : #ifdef JS_PUNBOX64
1400 0 : loadI64(Register64(scratch), v);
1401 0 : masm.Push(scratch);
1402 : #else
1403 : int32_t offset = frameOffsetFromSlot(v.slot(), MIRType::Int64);
1404 : loadFromFrameI32(scratch, offset - INT64HIGH_OFFSET);
1405 : masm.Push(scratch);
1406 : loadFromFrameI32(scratch, offset - INT64LOW_OFFSET);
1407 : masm.Push(scratch);
1408 : #endif
1409 0 : v.setOffs(Stk::MemI64, masm.framePushed());
1410 0 : break;
1411 : }
1412 : case Stk::RegisterI64: {
1413 : #ifdef JS_PUNBOX64
1414 0 : masm.Push(v.i64reg().reg);
1415 0 : freeI64(v.i64reg());
1416 : #else
1417 : masm.Push(v.i64reg().high);
1418 : masm.Push(v.i64reg().low);
1419 : freeI64(v.i64reg());
1420 : #endif
1421 0 : v.setOffs(Stk::MemI64, masm.framePushed());
1422 0 : break;
1423 : }
1424 : case Stk::LocalF64: {
1425 0 : ScratchF64 scratch(*this);
1426 0 : loadF64(scratch, v);
1427 0 : masm.Push(scratch);
1428 0 : v.setOffs(Stk::MemF64, masm.framePushed());
1429 0 : break;
1430 : }
1431 : case Stk::RegisterF64: {
1432 0 : masm.Push(v.f64reg());
1433 0 : freeF64(v.f64reg());
1434 0 : v.setOffs(Stk::MemF64, masm.framePushed());
1435 0 : break;
1436 : }
1437 : case Stk::LocalF32: {
1438 0 : ScratchF32 scratch(*this);
1439 0 : loadF32(scratch, v);
1440 0 : masm.Push(scratch);
1441 0 : v.setOffs(Stk::MemF32, masm.framePushed());
1442 0 : break;
1443 : }
1444 : case Stk::RegisterF32: {
1445 0 : masm.Push(v.f32reg());
1446 0 : freeF32(v.f32reg());
1447 0 : v.setOffs(Stk::MemF32, masm.framePushed());
1448 0 : break;
1449 : }
1450 : default: {
1451 0 : break;
1452 : }
1453 : }
1454 : }
1455 :
1456 0 : maxFramePushed_ = Max(maxFramePushed_, int32_t(masm.framePushed()));
1457 0 : }
1458 :
1459 : // This is an optimization used to avoid calling sync() for
1460 : // setLocal(): if the local does not exist unresolved on the stack
1461 : // then we can skip the sync.
1462 :
1463 0 : bool hasLocal(uint32_t slot) {
1464 0 : for (size_t i = stk_.length(); i > 0; i--) {
1465 : // Memory opcodes are first in the enum, single check against MemLast is fine.
1466 0 : Stk::Kind kind = stk_[i-1].kind();
1467 0 : if (kind <= Stk::MemLast)
1468 0 : return false;
1469 :
1470 : // Local opcodes follow memory opcodes in the enum, single check against
1471 : // LocalLast is sufficient.
1472 0 : if (kind <= Stk::LocalLast && stk_[i-1].slot() == slot)
1473 0 : return true;
1474 : }
1475 0 : return false;
1476 : }
1477 :
1478 0 : void syncLocal(uint32_t slot) {
1479 0 : if (hasLocal(slot))
1480 0 : sync(); // TODO / OPTIMIZE: Improve this? (Bug 1316817)
1481 0 : }
1482 :
1483 : // Push the register r onto the stack.
1484 :
1485 0 : void pushI32(RegI32 r) {
1486 0 : MOZ_ASSERT(!isAvailable(r));
1487 0 : Stk& x = push();
1488 0 : x.setI32Reg(r);
1489 0 : }
1490 :
1491 0 : void pushI64(RegI64 r) {
1492 0 : MOZ_ASSERT(!isAvailable(r));
1493 0 : Stk& x = push();
1494 0 : x.setI64Reg(r);
1495 0 : }
1496 :
1497 0 : void pushF64(RegF64 r) {
1498 0 : MOZ_ASSERT(!isAvailable(r));
1499 0 : Stk& x = push();
1500 0 : x.setF64Reg(r);
1501 0 : }
1502 :
1503 0 : void pushF32(RegF32 r) {
1504 0 : MOZ_ASSERT(!isAvailable(r));
1505 0 : Stk& x = push();
1506 0 : x.setF32Reg(r);
1507 0 : }
1508 :
1509 : // Push the value onto the stack.
1510 :
1511 0 : void pushI32(int32_t v) {
1512 0 : Stk& x = push();
1513 0 : x.setI32Val(v);
1514 0 : }
1515 :
1516 0 : void pushI64(int64_t v) {
1517 0 : Stk& x = push();
1518 0 : x.setI64Val(v);
1519 0 : }
1520 :
1521 0 : void pushF64(double v) {
1522 0 : Stk& x = push();
1523 0 : x.setF64Val(v);
1524 0 : }
1525 :
1526 0 : void pushF32(float v) {
1527 0 : Stk& x = push();
1528 0 : x.setF32Val(v);
1529 0 : }
1530 :
1531 : // Push the local slot onto the stack. The slot will not be read
1532 : // here; it will be read when it is consumed, or when a side
1533 : // effect to the slot forces its value to be saved.
1534 :
1535 0 : void pushLocalI32(uint32_t slot) {
1536 0 : Stk& x = push();
1537 0 : x.setSlot(Stk::LocalI32, slot);
1538 0 : }
1539 :
1540 0 : void pushLocalI64(uint32_t slot) {
1541 0 : Stk& x = push();
1542 0 : x.setSlot(Stk::LocalI64, slot);
1543 0 : }
1544 :
1545 0 : void pushLocalF64(uint32_t slot) {
1546 0 : Stk& x = push();
1547 0 : x.setSlot(Stk::LocalF64, slot);
1548 0 : }
1549 :
1550 0 : void pushLocalF32(uint32_t slot) {
1551 0 : Stk& x = push();
1552 0 : x.setSlot(Stk::LocalF32, slot);
1553 0 : }
1554 :
1555 : // PRIVATE. Call only from other popI32() variants.
1556 : // v must be the stack top.
1557 :
1558 0 : void popI32(Stk& v, RegI32 r) {
1559 0 : switch (v.kind()) {
1560 : case Stk::ConstI32:
1561 0 : loadConstI32(r, v);
1562 0 : break;
1563 : case Stk::LocalI32:
1564 0 : loadLocalI32(r, v);
1565 0 : break;
1566 : case Stk::MemI32:
1567 0 : masm.Pop(r);
1568 0 : break;
1569 : case Stk::RegisterI32:
1570 0 : loadRegisterI32(r, v);
1571 0 : break;
1572 : case Stk::None:
1573 : default:
1574 0 : MOZ_CRASH("Compiler bug: expected int on stack");
1575 : }
1576 0 : }
1577 :
1578 0 : MOZ_MUST_USE RegI32 popI32() {
1579 0 : Stk& v = stk_.back();
1580 0 : RegI32 r;
1581 0 : if (v.kind() == Stk::RegisterI32)
1582 0 : r = v.i32reg();
1583 : else
1584 0 : popI32(v, (r = needI32()));
1585 0 : stk_.popBack();
1586 0 : return r;
1587 : }
1588 :
1589 0 : RegI32 popI32(RegI32 specific) {
1590 0 : Stk& v = stk_.back();
1591 :
1592 0 : if (!(v.kind() == Stk::RegisterI32 && v.i32reg() == specific)) {
1593 0 : needI32(specific);
1594 0 : popI32(v, specific);
1595 0 : if (v.kind() == Stk::RegisterI32)
1596 0 : freeI32(v.i32reg());
1597 : }
1598 :
1599 0 : stk_.popBack();
1600 0 : return specific;
1601 : }
1602 :
1603 : // PRIVATE. Call only from other popI64() variants.
1604 : // v must be the stack top.
1605 :
1606 0 : void popI64(Stk& v, RegI64 r) {
1607 0 : switch (v.kind()) {
1608 : case Stk::ConstI64:
1609 0 : loadConstI64(r, v);
1610 0 : break;
1611 : case Stk::LocalI64:
1612 0 : loadLocalI64(r, v);
1613 0 : break;
1614 : case Stk::MemI64:
1615 : #ifdef JS_PUNBOX64
1616 0 : masm.Pop(r.reg);
1617 : #else
1618 : masm.Pop(r.low);
1619 : masm.Pop(r.high);
1620 : #endif
1621 0 : break;
1622 : case Stk::RegisterI64:
1623 0 : loadRegisterI64(r, v);
1624 0 : break;
1625 : case Stk::None:
1626 : default:
1627 0 : MOZ_CRASH("Compiler bug: expected long on stack");
1628 : }
1629 0 : }
1630 :
1631 0 : MOZ_MUST_USE RegI64 popI64() {
1632 0 : Stk& v = stk_.back();
1633 0 : RegI64 r;
1634 0 : if (v.kind() == Stk::RegisterI64)
1635 0 : r = v.i64reg();
1636 : else
1637 0 : popI64(v, (r = needI64()));
1638 0 : stk_.popBack();
1639 0 : return r;
1640 : }
1641 :
1642 : // Note, the stack top can be in one half of "specific" on 32-bit
1643 : // systems. We can optimize, but for simplicity, if the register
1644 : // does not match exactly, then just force the stack top to memory
1645 : // and then read it back in.
1646 :
1647 0 : RegI64 popI64(RegI64 specific) {
1648 0 : Stk& v = stk_.back();
1649 :
1650 0 : if (!(v.kind() == Stk::RegisterI64 && v.i64reg() == specific)) {
1651 0 : needI64(specific);
1652 0 : popI64(v, specific);
1653 0 : if (v.kind() == Stk::RegisterI64)
1654 0 : freeI64(v.i64reg());
1655 : }
1656 :
1657 0 : stk_.popBack();
1658 0 : return specific;
1659 : }
1660 :
1661 : // PRIVATE. Call only from other popF64() variants.
1662 : // v must be the stack top.
1663 :
1664 0 : void popF64(Stk& v, RegF64 r) {
1665 0 : switch (v.kind()) {
1666 : case Stk::ConstF64:
1667 0 : loadConstF64(r, v);
1668 0 : break;
1669 : case Stk::LocalF64:
1670 0 : loadLocalF64(r, v);
1671 0 : break;
1672 : case Stk::MemF64:
1673 0 : masm.Pop(r);
1674 0 : break;
1675 : case Stk::RegisterF64:
1676 0 : loadRegisterF64(r, v);
1677 0 : break;
1678 : case Stk::None:
1679 : default:
1680 0 : MOZ_CRASH("Compiler bug: expected double on stack");
1681 : }
1682 0 : }
1683 :
1684 0 : MOZ_MUST_USE RegF64 popF64() {
1685 0 : Stk& v = stk_.back();
1686 0 : RegF64 r;
1687 0 : if (v.kind() == Stk::RegisterF64)
1688 0 : r = v.f64reg();
1689 : else
1690 0 : popF64(v, (r = needF64()));
1691 0 : stk_.popBack();
1692 0 : return r;
1693 : }
1694 :
1695 0 : RegF64 popF64(RegF64 specific) {
1696 0 : Stk& v = stk_.back();
1697 :
1698 0 : if (!(v.kind() == Stk::RegisterF64 && v.f64reg() == specific)) {
1699 0 : needF64(specific);
1700 0 : popF64(v, specific);
1701 0 : if (v.kind() == Stk::RegisterF64)
1702 0 : freeF64(v.f64reg());
1703 : }
1704 :
1705 0 : stk_.popBack();
1706 0 : return specific;
1707 : }
1708 :
1709 : // PRIVATE. Call only from other popF32() variants.
1710 : // v must be the stack top.
1711 :
1712 0 : void popF32(Stk& v, RegF32 r) {
1713 0 : switch (v.kind()) {
1714 : case Stk::ConstF32:
1715 0 : loadConstF32(r, v);
1716 0 : break;
1717 : case Stk::LocalF32:
1718 0 : loadLocalF32(r, v);
1719 0 : break;
1720 : case Stk::MemF32:
1721 0 : masm.Pop(r);
1722 0 : break;
1723 : case Stk::RegisterF32:
1724 0 : loadRegisterF32(r, v);
1725 0 : break;
1726 : case Stk::None:
1727 : default:
1728 0 : MOZ_CRASH("Compiler bug: expected float on stack");
1729 : }
1730 0 : }
1731 :
1732 0 : MOZ_MUST_USE RegF32 popF32() {
1733 0 : Stk& v = stk_.back();
1734 0 : RegF32 r;
1735 0 : if (v.kind() == Stk::RegisterF32)
1736 0 : r = v.f32reg();
1737 : else
1738 0 : popF32(v, (r = needF32()));
1739 0 : stk_.popBack();
1740 0 : return r;
1741 : }
1742 :
1743 0 : RegF32 popF32(RegF32 specific) {
1744 0 : Stk& v = stk_.back();
1745 :
1746 0 : if (!(v.kind() == Stk::RegisterF32 && v.f32reg() == specific)) {
1747 0 : needF32(specific);
1748 0 : popF32(v, specific);
1749 0 : if (v.kind() == Stk::RegisterF32)
1750 0 : freeF32(v.f32reg());
1751 : }
1752 :
1753 0 : stk_.popBack();
1754 0 : return specific;
1755 : }
1756 :
1757 0 : MOZ_MUST_USE bool popConstI32(int32_t* c) {
1758 0 : Stk& v = stk_.back();
1759 0 : if (v.kind() != Stk::ConstI32)
1760 0 : return false;
1761 0 : *c = v.i32val();
1762 0 : stk_.popBack();
1763 0 : return true;
1764 : }
1765 :
1766 0 : MOZ_MUST_USE bool popConstI64(int64_t* c) {
1767 0 : Stk& v = stk_.back();
1768 0 : if (v.kind() != Stk::ConstI64)
1769 0 : return false;
1770 0 : *c = v.i64val();
1771 0 : stk_.popBack();
1772 0 : return true;
1773 : }
1774 :
1775 0 : MOZ_MUST_USE bool peekConstI32(int32_t* c) {
1776 0 : Stk& v = stk_.back();
1777 0 : if (v.kind() != Stk::ConstI32)
1778 0 : return false;
1779 0 : *c = v.i32val();
1780 0 : return true;
1781 : }
1782 :
1783 0 : MOZ_MUST_USE bool peekConstI64(int64_t* c) {
1784 0 : Stk& v = stk_.back();
1785 0 : if (v.kind() != Stk::ConstI64)
1786 0 : return false;
1787 0 : *c = v.i64val();
1788 0 : return true;
1789 : }
1790 :
1791 0 : MOZ_MUST_USE bool popConstPositivePowerOfTwoI32(int32_t* c,
1792 : uint_fast8_t* power,
1793 : int32_t cutoff)
1794 : {
1795 0 : Stk& v = stk_.back();
1796 0 : if (v.kind() != Stk::ConstI32)
1797 0 : return false;
1798 0 : *c = v.i32val();
1799 0 : if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint32_t>(*c)))
1800 0 : return false;
1801 0 : *power = FloorLog2(*c);
1802 0 : stk_.popBack();
1803 0 : return true;
1804 : }
1805 :
1806 0 : MOZ_MUST_USE bool popConstPositivePowerOfTwoI64(int64_t* c,
1807 : uint_fast8_t* power,
1808 : int64_t cutoff)
1809 : {
1810 0 : Stk& v = stk_.back();
1811 0 : if (v.kind() != Stk::ConstI64)
1812 0 : return false;
1813 0 : *c = v.i64val();
1814 0 : if (*c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(*c)))
1815 0 : return false;
1816 0 : *power = FloorLog2(*c);
1817 0 : stk_.popBack();
1818 0 : return true;
1819 : }
1820 :
1821 0 : MOZ_MUST_USE bool peekLocalI32(uint32_t* local) {
1822 0 : Stk& v = stk_.back();
1823 0 : if (v.kind() != Stk::LocalI32)
1824 0 : return false;
1825 0 : *local = v.slot();
1826 0 : return true;
1827 : }
1828 :
1829 : // TODO / OPTIMIZE (Bug 1316818): At the moment we use ReturnReg
1830 : // for JoinReg. It is possible other choices would lead to better
1831 : // register allocation, as ReturnReg is often first in the
1832 : // register set and will be heavily wanted by the register
1833 : // allocator that uses takeFirst().
1834 : //
1835 : // Obvious options:
1836 : // - pick a register at the back of the register set
1837 : // - pick a random register per block (different blocks have
1838 : // different join regs)
1839 : //
1840 : // On the other hand, we sync() before every block and only the
1841 : // JoinReg is live out of the block. But on the way out, we
1842 : // currently pop the JoinReg before freeing regs to be discarded,
1843 : // so there is a real risk of some pointless shuffling there. If
1844 : // we instead integrate the popping of the join reg into the
1845 : // popping of the stack we can just use the JoinReg as it will
1846 : // become available in that process.
1847 :
1848 0 : MOZ_MUST_USE AnyReg popJoinRegUnlessVoid(ExprType type) {
1849 0 : switch (type) {
1850 : case ExprType::Void: {
1851 0 : return AnyReg();
1852 : }
1853 : case ExprType::I32: {
1854 0 : DebugOnly<Stk::Kind> k(stk_.back().kind());
1855 0 : MOZ_ASSERT(k == Stk::RegisterI32 || k == Stk::ConstI32 || k == Stk::MemI32 ||
1856 : k == Stk::LocalI32);
1857 0 : return AnyReg(popI32(joinRegI32));
1858 : }
1859 : case ExprType::I64: {
1860 0 : DebugOnly<Stk::Kind> k(stk_.back().kind());
1861 0 : MOZ_ASSERT(k == Stk::RegisterI64 || k == Stk::ConstI64 || k == Stk::MemI64 ||
1862 : k == Stk::LocalI64);
1863 0 : return AnyReg(popI64(joinRegI64));
1864 : }
1865 : case ExprType::F64: {
1866 0 : DebugOnly<Stk::Kind> k(stk_.back().kind());
1867 0 : MOZ_ASSERT(k == Stk::RegisterF64 || k == Stk::ConstF64 || k == Stk::MemF64 ||
1868 : k == Stk::LocalF64);
1869 0 : return AnyReg(popF64(joinRegF64));
1870 : }
1871 : case ExprType::F32: {
1872 0 : DebugOnly<Stk::Kind> k(stk_.back().kind());
1873 0 : MOZ_ASSERT(k == Stk::RegisterF32 || k == Stk::ConstF32 || k == Stk::MemF32 ||
1874 : k == Stk::LocalF32);
1875 0 : return AnyReg(popF32(joinRegF32));
1876 : }
1877 : default: {
1878 0 : MOZ_CRASH("Compiler bug: unexpected expression type");
1879 : }
1880 : }
1881 : }
1882 :
1883 : // If we ever start not sync-ing on entry to Block (but instead try to sync
1884 : // lazily) then this may start asserting because it does not spill the
1885 : // joinreg if the joinreg is already allocated. Note, it *can't* spill the
1886 : // joinreg in the contexts it's being used, so some other solution will need
1887 : // to be found.
1888 :
1889 0 : MOZ_MUST_USE AnyReg captureJoinRegUnlessVoid(ExprType type) {
1890 0 : switch (type) {
1891 : case ExprType::I32:
1892 0 : allocGPR(joinRegI32);
1893 0 : return AnyReg(joinRegI32);
1894 : case ExprType::I64:
1895 0 : allocInt64(joinRegI64);
1896 0 : return AnyReg(joinRegI64);
1897 : case ExprType::F32:
1898 0 : allocFPU(joinRegF32);
1899 0 : return AnyReg(joinRegF32);
1900 : case ExprType::F64:
1901 0 : allocFPU(joinRegF64);
1902 0 : return AnyReg(joinRegF64);
1903 : case ExprType::Void:
1904 0 : return AnyReg();
1905 : default:
1906 0 : MOZ_CRASH("Compiler bug: unexpected type");
1907 : }
1908 : }
1909 :
1910 0 : void pushJoinRegUnlessVoid(AnyReg r) {
1911 0 : switch (r.tag) {
1912 : case AnyReg::NONE:
1913 0 : break;
1914 : case AnyReg::I32:
1915 0 : pushI32(r.i32());
1916 0 : break;
1917 : case AnyReg::I64:
1918 0 : pushI64(r.i64());
1919 0 : break;
1920 : case AnyReg::F64:
1921 0 : pushF64(r.f64());
1922 0 : break;
1923 : case AnyReg::F32:
1924 0 : pushF32(r.f32());
1925 0 : break;
1926 : }
1927 0 : }
1928 :
1929 0 : void freeJoinRegUnlessVoid(AnyReg r) {
1930 0 : switch (r.tag) {
1931 : case AnyReg::NONE:
1932 0 : break;
1933 : case AnyReg::I32:
1934 0 : freeI32(r.i32());
1935 0 : break;
1936 : case AnyReg::I64:
1937 0 : freeI64(r.i64());
1938 0 : break;
1939 : case AnyReg::F64:
1940 0 : freeF64(r.f64());
1941 0 : break;
1942 : case AnyReg::F32:
1943 0 : freeF32(r.f32());
1944 0 : break;
1945 : }
1946 0 : }
1947 :
1948 0 : void maybeReserveJoinRegI(ExprType type) {
1949 0 : if (type == ExprType::I32)
1950 0 : needI32(joinRegI32);
1951 0 : else if (type == ExprType::I64)
1952 0 : needI64(joinRegI64);
1953 0 : }
1954 :
1955 0 : void maybeUnreserveJoinRegI(ExprType type) {
1956 0 : if (type == ExprType::I32)
1957 0 : freeI32(joinRegI32);
1958 0 : else if (type == ExprType::I64)
1959 0 : freeI64(joinRegI64);
1960 0 : }
1961 :
1962 0 : void maybeReserveJoinReg(ExprType type) {
1963 0 : switch (type) {
1964 : case ExprType::I32:
1965 0 : needI32(joinRegI32);
1966 0 : break;
1967 : case ExprType::I64:
1968 0 : needI64(joinRegI64);
1969 0 : break;
1970 : case ExprType::F32:
1971 0 : needF32(joinRegF32);
1972 0 : break;
1973 : case ExprType::F64:
1974 0 : needF64(joinRegF64);
1975 0 : break;
1976 : default:
1977 0 : break;
1978 : }
1979 0 : }
1980 :
1981 0 : void maybeUnreserveJoinReg(ExprType type) {
1982 0 : switch (type) {
1983 : case ExprType::I32:
1984 0 : freeI32(joinRegI32);
1985 0 : break;
1986 : case ExprType::I64:
1987 0 : freeI64(joinRegI64);
1988 0 : break;
1989 : case ExprType::F32:
1990 0 : freeF32(joinRegF32);
1991 0 : break;
1992 : case ExprType::F64:
1993 0 : freeF64(joinRegF64);
1994 0 : break;
1995 : default:
1996 0 : break;
1997 : }
1998 0 : }
1999 :
2000 : // Return the amount of execution stack consumed by the top numval
2001 : // values on the value stack.
2002 :
2003 0 : size_t stackConsumed(size_t numval) {
2004 0 : size_t size = 0;
2005 0 : MOZ_ASSERT(numval <= stk_.length());
2006 0 : for (uint32_t i = stk_.length() - 1; numval > 0; numval--, i--) {
2007 : // The size computations come from the implementation of Push() in
2008 : // MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp,
2009 : // and from VFPRegister::size() in Architecture-arm.h.
2010 : //
2011 : // On ARM unlike on x86 we push a single for float.
2012 :
2013 0 : Stk& v = stk_[i];
2014 0 : switch (v.kind()) {
2015 : case Stk::MemI32:
2016 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
2017 0 : size += sizeof(intptr_t);
2018 : #else
2019 : MOZ_CRASH("BaseCompiler platform hook: stackConsumed I32");
2020 : #endif
2021 0 : break;
2022 : case Stk::MemI64:
2023 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
2024 0 : size += sizeof(int64_t);
2025 : #else
2026 : MOZ_CRASH("BaseCompiler platform hook: stackConsumed I64");
2027 : #endif
2028 0 : break;
2029 : case Stk::MemF64:
2030 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
2031 0 : size += sizeof(double);
2032 : #else
2033 : MOZ_CRASH("BaseCompiler platform hook: stackConsumed F64");
2034 : #endif
2035 0 : break;
2036 : case Stk::MemF32:
2037 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
2038 0 : size += sizeof(double);
2039 : #elif defined(JS_CODEGEN_ARM)
2040 : size += sizeof(float);
2041 : #else
2042 : MOZ_CRASH("BaseCompiler platform hook: stackConsumed F32");
2043 : #endif
2044 0 : break;
2045 : default:
2046 0 : break;
2047 : }
2048 : }
2049 0 : return size;
2050 : }
2051 :
2052 0 : void popValueStackTo(uint32_t stackSize) {
2053 0 : for (uint32_t i = stk_.length(); i > stackSize; i--) {
2054 0 : Stk& v = stk_[i-1];
2055 0 : switch (v.kind()) {
2056 : case Stk::RegisterI32:
2057 0 : freeI32(v.i32reg());
2058 0 : break;
2059 : case Stk::RegisterI64:
2060 0 : freeI64(v.i64reg());
2061 0 : break;
2062 : case Stk::RegisterF64:
2063 0 : freeF64(v.f64reg());
2064 0 : break;
2065 : case Stk::RegisterF32:
2066 0 : freeF32(v.f32reg());
2067 0 : break;
2068 : default:
2069 0 : break;
2070 : }
2071 : }
2072 0 : stk_.shrinkTo(stackSize);
2073 0 : }
2074 :
2075 0 : void popValueStackBy(uint32_t items) {
2076 0 : popValueStackTo(stk_.length() - items);
2077 0 : }
2078 :
2079 : // Before branching to an outer control label, pop the execution
2080 : // stack to the level expected by that region, but do not free the
2081 : // stack as that will happen as compilation leaves the block.
2082 :
2083 0 : void popStackBeforeBranch(uint32_t framePushed) {
2084 0 : uint32_t frameHere = masm.framePushed();
2085 0 : if (frameHere > framePushed)
2086 0 : masm.addPtr(ImmWord(frameHere - framePushed), StackPointer);
2087 0 : }
2088 :
2089 0 : bool willPopStackBeforeBranch(uint32_t framePushed) {
2090 0 : uint32_t frameHere = masm.framePushed();
2091 0 : return frameHere > framePushed;
2092 : }
2093 :
2094 : // Before exiting a nested control region, pop the execution stack
2095 : // to the level expected by the nesting region, and free the
2096 : // stack.
2097 :
2098 0 : void popStackOnBlockExit(uint32_t framePushed) {
2099 0 : uint32_t frameHere = masm.framePushed();
2100 0 : if (frameHere > framePushed) {
2101 0 : if (deadCode_)
2102 0 : masm.adjustStack(frameHere - framePushed);
2103 : else
2104 0 : masm.freeStack(frameHere - framePushed);
2105 : }
2106 0 : }
2107 :
2108 0 : void popStackIfMemory() {
2109 0 : if (peek(0).isMem())
2110 0 : masm.freeStack(stackConsumed(1));
2111 0 : }
2112 :
2113 : // Peek at the stack, for calls.
2114 :
2115 0 : Stk& peek(uint32_t relativeDepth) {
2116 0 : return stk_[stk_.length()-1-relativeDepth];
2117 : }
2118 :
2119 : #ifdef DEBUG
2120 : // Check that we're not leaking registers by comparing the
2121 : // state of the stack + available registers with the set of
2122 : // all available registers.
2123 :
2124 : // Call this before compiling any code.
2125 0 : void setupRegisterLeakCheck() {
2126 0 : allGPR_ = availGPR_;
2127 0 : allFPU_ = availFPU_;
2128 0 : }
2129 :
2130 : // Call this between opcodes.
2131 0 : void performRegisterLeakCheck() {
2132 0 : AllocatableGeneralRegisterSet knownGPR_ = availGPR_;
2133 0 : AllocatableFloatRegisterSet knownFPU_ = availFPU_;
2134 0 : for (size_t i = 0 ; i < stk_.length() ; i++) {
2135 0 : Stk& item = stk_[i];
2136 0 : switch (item.kind_) {
2137 : case Stk::RegisterI32:
2138 0 : knownGPR_.add(item.i32reg());
2139 0 : break;
2140 : case Stk::RegisterI64:
2141 : #ifdef JS_PUNBOX64
2142 0 : knownGPR_.add(item.i64reg().reg);
2143 : #else
2144 : knownGPR_.add(item.i64reg().high);
2145 : knownGPR_.add(item.i64reg().low);
2146 : #endif
2147 0 : break;
2148 : case Stk::RegisterF32:
2149 0 : knownFPU_.add(item.f32reg());
2150 0 : break;
2151 : case Stk::RegisterF64:
2152 0 : knownFPU_.add(item.f64reg());
2153 0 : break;
2154 : default:
2155 0 : break;
2156 : }
2157 : }
2158 0 : MOZ_ASSERT(knownGPR_.bits() == allGPR_.bits());
2159 0 : MOZ_ASSERT(knownFPU_.bits() == allFPU_.bits());
2160 0 : }
2161 : #endif
2162 :
2163 : ////////////////////////////////////////////////////////////
2164 : //
2165 : // Control stack
2166 :
2167 0 : void initControl(Control& item)
2168 : {
2169 : // Make sure the constructor was run properly
2170 0 : MOZ_ASSERT(item.framePushed == UINT32_MAX && item.stackSize == UINT32_MAX);
2171 :
2172 0 : item.framePushed = masm.framePushed();
2173 0 : item.stackSize = stk_.length();
2174 0 : item.deadOnArrival = deadCode_;
2175 0 : item.bceSafeOnEntry = bceSafe_;
2176 0 : }
2177 :
2178 0 : Control& controlItem() {
2179 0 : return iter_.controlItem();
2180 : }
2181 :
2182 0 : Control& controlItem(uint32_t relativeDepth) {
2183 0 : return iter_.controlItem(relativeDepth);
2184 : }
2185 :
2186 0 : Control& controlOutermost() {
2187 0 : return iter_.controlOutermost();
2188 : }
2189 :
2190 : ////////////////////////////////////////////////////////////
2191 : //
2192 : // Labels
2193 :
2194 0 : void insertBreakablePoint(CallSiteDesc::Kind kind) {
2195 : // The debug trap exit requires WasmTlsReg be loaded. However, since we
2196 : // are emitting millions of these breakable points inline, we push this
2197 : // loading of TLS into the FarJumpIsland created by patchCallSites.
2198 0 : masm.nopPatchableToCall(CallSiteDesc(iter_.lastOpcodeOffset(), kind));
2199 0 : }
2200 :
2201 : //////////////////////////////////////////////////////////////////////
2202 : //
2203 : // Function prologue and epilogue.
2204 :
2205 0 : void beginFunction() {
2206 0 : JitSpew(JitSpew_Codegen, "# Emitting wasm baseline code");
2207 :
2208 0 : SigIdDesc sigId = env_.funcSigs[func_.index()]->id;
2209 0 : GenerateFunctionPrologue(masm, localSize_, sigId, &offsets_);
2210 :
2211 0 : MOZ_ASSERT(masm.framePushed() == uint32_t(localSize_));
2212 :
2213 0 : maxFramePushed_ = localSize_;
2214 :
2215 0 : if (debugEnabled_) {
2216 : // Initialize funcIndex and flag fields of DebugFrame.
2217 0 : size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
2218 0 : masm.store32(Imm32(func_.index()),
2219 0 : Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFuncIndex()));
2220 0 : masm.storePtr(ImmWord(0),
2221 0 : Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFlagsWord()));
2222 : }
2223 :
2224 : // We won't know until after we've generated code how big the frame will
2225 : // be (we may need arbitrary spill slots and outgoing param slots) so
2226 : // emit a patchable add that is patched in endFunction().
2227 : //
2228 : // ScratchReg may be used by branchPtr(), so use ABINonArgReg0/1 for
2229 : // temporaries.
2230 :
2231 0 : stackAddOffset_ = masm.add32ToPtrWithPatch(StackPointer, ABINonArgReg0);
2232 0 : masm.wasmEmitStackCheck(ABINonArgReg0, ABINonArgReg1, &stackOverflowLabel_);
2233 :
2234 : // Copy arguments from registers to stack.
2235 :
2236 0 : const ValTypeVector& args = func_.sig().args();
2237 :
2238 0 : for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
2239 0 : Local& l = localInfo_[i.index()];
2240 0 : switch (i.mirType()) {
2241 : case MIRType::Int32:
2242 0 : if (i->argInRegister())
2243 0 : storeToFrameI32(i->gpr(), l.offs());
2244 0 : break;
2245 : case MIRType::Int64:
2246 0 : if (i->argInRegister())
2247 0 : storeToFrameI64(i->gpr64(), l.offs());
2248 0 : break;
2249 : case MIRType::Double:
2250 0 : if (i->argInRegister())
2251 0 : storeToFrameF64(i->fpu(), l.offs());
2252 0 : break;
2253 : case MIRType::Float32:
2254 0 : if (i->argInRegister())
2255 0 : storeToFrameF32(i->fpu(), l.offs());
2256 0 : break;
2257 : default:
2258 0 : MOZ_CRASH("Function argument type");
2259 : }
2260 : }
2261 :
2262 : // Initialize the stack locals to zero.
2263 : //
2264 : // The following are all Bug 1316820:
2265 : //
2266 : // TODO / OPTIMIZE: on x64, at least, scratch will be a 64-bit
2267 : // register and we can move 64 bits at a time.
2268 : //
2269 : // TODO / OPTIMIZE: On SSE2 or better SIMD systems we may be
2270 : // able to store 128 bits at a time. (I suppose on some
2271 : // systems we have 512-bit SIMD for that matter.)
2272 : //
2273 : // TODO / OPTIMIZE: if we have only one initializing store
2274 : // then it's better to store a zero literal, probably.
2275 :
2276 0 : if (varLow_ < varHigh_) {
2277 0 : ScratchI32 scratch(*this);
2278 0 : masm.mov(ImmWord(0), scratch);
2279 0 : for (int32_t i = varLow_ ; i < varHigh_ ; i += 4)
2280 0 : storeToFrameI32(scratch, i + 4);
2281 : }
2282 :
2283 0 : if (debugEnabled_)
2284 0 : insertBreakablePoint(CallSiteDesc::EnterFrame);
2285 0 : }
2286 :
2287 0 : void saveResult() {
2288 0 : MOZ_ASSERT(debugEnabled_);
2289 0 : size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
2290 0 : Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
2291 0 : switch (func_.sig().ret()) {
2292 : case ExprType::Void:
2293 0 : break;
2294 : case ExprType::I32:
2295 0 : masm.store32(RegI32(ReturnReg), resultsAddress);
2296 0 : break;
2297 :
2298 : case ExprType::I64:
2299 0 : masm.store64(RegI64(ReturnReg64), resultsAddress);
2300 0 : break;
2301 : case ExprType::F64:
2302 0 : masm.storeDouble(RegF64(ReturnDoubleReg), resultsAddress);
2303 0 : break;
2304 : case ExprType::F32:
2305 0 : masm.storeFloat32(RegF32(ReturnFloat32Reg), resultsAddress);
2306 0 : break;
2307 : default:
2308 0 : MOZ_CRASH("Function return type");
2309 : }
2310 0 : }
2311 :
2312 0 : void restoreResult() {
2313 0 : MOZ_ASSERT(debugEnabled_);
2314 0 : size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
2315 0 : Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
2316 0 : switch (func_.sig().ret()) {
2317 : case ExprType::Void:
2318 0 : break;
2319 : case ExprType::I32:
2320 0 : masm.load32(resultsAddress, RegI32(ReturnReg));
2321 0 : break;
2322 : case ExprType::I64:
2323 0 : masm.load64(resultsAddress, RegI64(ReturnReg64));
2324 0 : break;
2325 : case ExprType::F64:
2326 0 : masm.loadDouble(resultsAddress, RegF64(ReturnDoubleReg));
2327 0 : break;
2328 : case ExprType::F32:
2329 0 : masm.loadFloat32(resultsAddress, RegF32(ReturnFloat32Reg));
2330 0 : break;
2331 : default:
2332 0 : MOZ_CRASH("Function return type");
2333 : }
2334 0 : }
2335 :
2336 0 : bool endFunction() {
2337 : // Always branch to stackOverflowLabel_ or returnLabel_.
2338 0 : masm.breakpoint();
2339 :
2340 : // Patch the add in the prologue so that it checks against the correct
2341 : // frame size. Flush the constant pool in case it needs to be patched.
2342 0 : MOZ_ASSERT(maxFramePushed_ >= localSize_);
2343 0 : masm.flush();
2344 :
2345 : // Precondition for patching.
2346 0 : if (masm.oom())
2347 0 : return false;
2348 0 : masm.patchAdd32ToPtr(stackAddOffset_, Imm32(-int32_t(maxFramePushed_ - localSize_)));
2349 :
2350 : // Since we just overflowed the stack, to be on the safe side, pop the
2351 : // stack so that, when the trap exit stub executes, it is a safe
2352 : // distance away from the end of the native stack. If debugEnabled_ is
2353 : // set, we pop all locals space except allocated for DebugFrame to
2354 : // maintain the invariant that, when debugEnabled_, all wasm::Frames
2355 : // are valid wasm::DebugFrames which is observable by WasmHandleThrow.
2356 0 : masm.bind(&stackOverflowLabel_);
2357 0 : int32_t debugFrameReserved = debugEnabled_ ? DebugFrame::offsetOfFrame() : 0;
2358 0 : MOZ_ASSERT(localSize_ >= debugFrameReserved);
2359 0 : if (localSize_ > debugFrameReserved)
2360 0 : masm.addToStackPtr(Imm32(localSize_ - debugFrameReserved));
2361 0 : BytecodeOffset prologueTrapOffset(func_.lineOrBytecode());
2362 0 : masm.jump(TrapDesc(prologueTrapOffset, Trap::StackOverflow, debugFrameReserved));
2363 :
2364 0 : masm.bind(&returnLabel_);
2365 :
2366 0 : if (debugEnabled_) {
2367 : // Store and reload the return value from DebugFrame::return so that
2368 : // it can be clobbered, and/or modified by the debug trap.
2369 0 : saveResult();
2370 0 : insertBreakablePoint(CallSiteDesc::Breakpoint);
2371 0 : insertBreakablePoint(CallSiteDesc::LeaveFrame);
2372 0 : restoreResult();
2373 : }
2374 :
2375 0 : GenerateFunctionEpilogue(masm, localSize_, &offsets_);
2376 :
2377 : #if defined(JS_ION_PERF)
2378 : // FIXME - profiling code missing. No bug for this.
2379 :
2380 : // Note the end of the inline code and start of the OOL code.
2381 : //gen->perfSpewer().noteEndInlineCode(masm);
2382 : #endif
2383 :
2384 0 : if (!generateOutOfLineCode())
2385 0 : return false;
2386 :
2387 0 : masm.wasmEmitTrapOutOfLineCode();
2388 :
2389 0 : offsets_.end = masm.currentOffset();
2390 :
2391 : // A frame greater than 256KB is implausible, probably an attack,
2392 : // so fail the compilation.
2393 :
2394 0 : if (maxFramePushed_ > 256 * 1024)
2395 0 : return false;
2396 :
2397 0 : return !masm.oom();
2398 : }
2399 :
2400 : //////////////////////////////////////////////////////////////////////
2401 : //
2402 : // Calls.
2403 :
2404 : struct FunctionCall
2405 : {
2406 0 : explicit FunctionCall(uint32_t lineOrBytecode)
2407 0 : : lineOrBytecode(lineOrBytecode),
2408 : reloadMachineStateAfter(false),
2409 : usesSystemAbi(false),
2410 : #ifdef JS_CODEGEN_ARM
2411 : hardFP(true),
2412 : #endif
2413 : frameAlignAdjustment(0),
2414 0 : stackArgAreaSize(0)
2415 0 : {}
2416 :
2417 : uint32_t lineOrBytecode;
2418 : ABIArgGenerator abi;
2419 : bool reloadMachineStateAfter;
2420 : bool usesSystemAbi;
2421 : #ifdef JS_CODEGEN_ARM
2422 : bool hardFP;
2423 : #endif
2424 : size_t frameAlignAdjustment;
2425 : size_t stackArgAreaSize;
2426 : };
2427 :
2428 0 : void beginCall(FunctionCall& call, UseABI useABI, InterModule interModule)
2429 : {
2430 0 : call.reloadMachineStateAfter = interModule == InterModule::True || useABI == UseABI::System;
2431 0 : call.usesSystemAbi = useABI == UseABI::System;
2432 :
2433 0 : if (call.usesSystemAbi) {
2434 : // Call-outs need to use the appropriate system ABI.
2435 : #if defined(JS_CODEGEN_ARM)
2436 : # if defined(JS_SIMULATOR_ARM)
2437 : call.hardFP = UseHardFpABI();
2438 : # elif defined(JS_CODEGEN_ARM_HARDFP)
2439 : call.hardFP = true;
2440 : # else
2441 : call.hardFP = false;
2442 : # endif
2443 : call.abi.setUseHardFp(call.hardFP);
2444 : #endif
2445 : }
2446 :
2447 0 : call.frameAlignAdjustment = ComputeByteAlignment(masm.framePushed() + sizeof(Frame),
2448 : JitStackAlignment);
2449 0 : }
2450 :
2451 0 : void endCall(FunctionCall& call, size_t stackSpace)
2452 : {
2453 0 : size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
2454 0 : masm.freeStack(stackSpace + adjustment);
2455 :
2456 0 : if (call.reloadMachineStateAfter) {
2457 : // On x86 there are no pinned registers, so don't waste time
2458 : // reloading the Tls.
2459 : #ifndef JS_CODEGEN_X86
2460 0 : masm.loadWasmTlsRegFromFrame();
2461 0 : masm.loadWasmPinnedRegsFromTls();
2462 : #endif
2463 : }
2464 0 : }
2465 :
2466 : // TODO / OPTIMIZE (Bug 1316821): This is expensive; let's roll the iterator
2467 : // walking into the walking done for passArg. See comments in passArg.
2468 :
2469 : // Note, stackArgAreaSize() must process all the arguments to get the
2470 : // alignment right; the signature must therefore be the complete call
2471 : // signature.
2472 :
2473 : template<class T>
2474 0 : size_t stackArgAreaSize(const T& args) {
2475 0 : ABIArgIter<const T> i(args);
2476 0 : while (!i.done())
2477 0 : i++;
2478 0 : return AlignBytes(i.stackBytesConsumedSoFar(), 16u);
2479 : }
2480 :
2481 0 : void startCallArgs(FunctionCall& call, size_t stackArgAreaSize)
2482 : {
2483 0 : call.stackArgAreaSize = stackArgAreaSize;
2484 :
2485 0 : size_t adjustment = call.stackArgAreaSize + call.frameAlignAdjustment;
2486 0 : if (adjustment)
2487 0 : masm.reserveStack(adjustment);
2488 0 : }
2489 :
2490 0 : const ABIArg reservePointerArgument(FunctionCall& call) {
2491 0 : return call.abi.next(MIRType::Pointer);
2492 : }
2493 :
2494 : // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place.
2495 : // (Or it was, until Luke wandered through, but that can be fixed again.)
2496 : // I'm not saying we should manually inline it, but we could hoist the
2497 : // dispatch into the caller and have type-specific implementations of
2498 : // passArg: passArgI32(), etc. Then those might be inlined, at least in PGO
2499 : // builds.
2500 : //
2501 : // The bulk of the work here (60%) is in the next() call, though.
2502 : //
2503 : // Notably, since next() is so expensive, stackArgAreaSize() becomes
2504 : // expensive too.
2505 : //
2506 : // Somehow there could be a trick here where the sequence of
2507 : // argument types (read from the input stream) leads to a cached
2508 : // entry for stackArgAreaSize() and for how to pass arguments...
2509 : //
2510 : // But at least we could reduce the cost of stackArgAreaSize() by
2511 : // first reading the argument types into a (reusable) vector, then
2512 : // we have the outgoing size at low cost, and then we can pass
2513 : // args based on the info we read.
2514 :
2515 0 : void passArg(FunctionCall& call, ValType type, Stk& arg) {
2516 0 : switch (type) {
2517 : case ValType::I32: {
2518 0 : ABIArg argLoc = call.abi.next(MIRType::Int32);
2519 0 : if (argLoc.kind() == ABIArg::Stack) {
2520 0 : ScratchI32 scratch(*this);
2521 0 : loadI32(scratch, arg);
2522 0 : masm.store32(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
2523 : } else {
2524 0 : loadI32(argLoc.gpr(), arg);
2525 : }
2526 0 : break;
2527 : }
2528 : case ValType::I64: {
2529 0 : ABIArg argLoc = call.abi.next(MIRType::Int64);
2530 0 : if (argLoc.kind() == ABIArg::Stack) {
2531 0 : ScratchI32 scratch(*this);
2532 : #if defined(JS_CODEGEN_X64)
2533 0 : loadI64(Register64(scratch), arg);
2534 0 : masm.movq(scratch, Operand(StackPointer, argLoc.offsetFromArgBase()));
2535 : #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
2536 : loadI64Low(scratch, arg);
2537 : masm.store32(scratch, Address(StackPointer, argLoc.offsetFromArgBase() + INT64LOW_OFFSET));
2538 : loadI64High(scratch, arg);
2539 : masm.store32(scratch, Address(StackPointer, argLoc.offsetFromArgBase() + INT64HIGH_OFFSET));
2540 : #else
2541 : MOZ_CRASH("BaseCompiler platform hook: passArg I64");
2542 : #endif
2543 : } else {
2544 0 : loadI64(argLoc.gpr64(), arg);
2545 : }
2546 0 : break;
2547 : }
2548 : case ValType::F64: {
2549 0 : ABIArg argLoc = call.abi.next(MIRType::Double);
2550 0 : switch (argLoc.kind()) {
2551 : case ABIArg::Stack: {
2552 0 : ScratchF64 scratch(*this);
2553 0 : loadF64(scratch, arg);
2554 0 : masm.storeDouble(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
2555 0 : break;
2556 : }
2557 : #if defined(JS_CODEGEN_REGISTER_PAIR)
2558 : case ABIArg::GPR_PAIR: {
2559 : # ifdef JS_CODEGEN_ARM
2560 : ScratchF64 scratch(*this);
2561 : loadF64(scratch, arg);
2562 : masm.ma_vxfer(scratch, argLoc.evenGpr(), argLoc.oddGpr());
2563 : break;
2564 : # else
2565 : MOZ_CRASH("BaseCompiler platform hook: passArg F64 pair");
2566 : # endif
2567 : }
2568 : #endif
2569 : case ABIArg::FPU: {
2570 0 : loadF64(argLoc.fpu(), arg);
2571 0 : break;
2572 : }
2573 : case ABIArg::GPR: {
2574 0 : MOZ_CRASH("Unexpected parameter passing discipline");
2575 : }
2576 : }
2577 0 : break;
2578 : }
2579 : case ValType::F32: {
2580 0 : ABIArg argLoc = call.abi.next(MIRType::Float32);
2581 0 : switch (argLoc.kind()) {
2582 : case ABIArg::Stack: {
2583 0 : ScratchF32 scratch(*this);
2584 0 : loadF32(scratch, arg);
2585 0 : masm.storeFloat32(scratch, Address(StackPointer, argLoc.offsetFromArgBase()));
2586 0 : break;
2587 : }
2588 : case ABIArg::GPR: {
2589 0 : ScratchF32 scratch(*this);
2590 0 : loadF32(scratch, arg);
2591 0 : masm.moveFloat32ToGPR(scratch, argLoc.gpr());
2592 0 : break;
2593 : }
2594 : case ABIArg::FPU: {
2595 0 : loadF32(argLoc.fpu(), arg);
2596 0 : break;
2597 : }
2598 : #if defined(JS_CODEGEN_REGISTER_PAIR)
2599 : case ABIArg::GPR_PAIR: {
2600 : MOZ_CRASH("Unexpected parameter passing discipline");
2601 : }
2602 : #endif
2603 : }
2604 0 : break;
2605 : }
2606 : default:
2607 0 : MOZ_CRASH("Function argument type");
2608 : }
2609 0 : }
2610 :
2611 0 : void callDefinition(uint32_t funcIndex, const FunctionCall& call)
2612 : {
2613 0 : CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Func);
2614 0 : masm.call(desc, funcIndex);
2615 0 : }
2616 :
2617 0 : void callSymbolic(SymbolicAddress callee, const FunctionCall& call) {
2618 0 : CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
2619 0 : masm.call(desc, callee);
2620 0 : }
2621 :
2622 : // Precondition: sync()
2623 :
2624 0 : void callIndirect(uint32_t sigIndex, Stk& indexVal, const FunctionCall& call)
2625 : {
2626 0 : const SigWithId& sig = env_.sigs[sigIndex];
2627 0 : MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
2628 :
2629 0 : MOZ_ASSERT(env_.tables.length() == 1);
2630 0 : const TableDesc& table = env_.tables[0];
2631 :
2632 0 : loadI32(WasmTableCallIndexReg, indexVal);
2633 :
2634 0 : CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
2635 0 : CalleeDesc callee = CalleeDesc::wasmTable(table, sig.id);
2636 0 : masm.wasmCallIndirect(desc, callee, NeedsBoundsCheck(true));
2637 0 : }
2638 :
2639 : // Precondition: sync()
2640 :
2641 0 : void callImport(unsigned globalDataOffset, const FunctionCall& call)
2642 : {
2643 0 : CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
2644 0 : CalleeDesc callee = CalleeDesc::import(globalDataOffset);
2645 0 : masm.wasmCallImport(desc, callee);
2646 0 : }
2647 :
2648 0 : void builtinCall(SymbolicAddress builtin, const FunctionCall& call)
2649 : {
2650 0 : callSymbolic(builtin, call);
2651 0 : }
2652 :
2653 0 : void builtinInstanceMethodCall(SymbolicAddress builtin, const ABIArg& instanceArg,
2654 : const FunctionCall& call)
2655 : {
2656 : // Builtin method calls assume the TLS register has been set.
2657 0 : masm.loadWasmTlsRegFromFrame();
2658 :
2659 0 : CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
2660 0 : masm.wasmCallBuiltinInstanceMethod(desc, instanceArg, builtin);
2661 0 : }
2662 :
2663 : //////////////////////////////////////////////////////////////////////
2664 : //
2665 : // Sundry low-level code generators.
2666 :
2667 0 : void addInterruptCheck()
2668 : {
2669 : // Always use signals for interrupts with Asm.JS/Wasm
2670 0 : MOZ_RELEASE_ASSERT(HaveSignalHandlers());
2671 0 : }
2672 :
2673 0 : void jumpTable(LabelVector& labels, Label* theTable) {
2674 : // Flush constant pools to ensure that the table is never interrupted by
2675 : // constant pool entries.
2676 0 : masm.flush();
2677 :
2678 0 : masm.bind(theTable);
2679 :
2680 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
2681 0 : for (uint32_t i = 0; i < labels.length(); i++) {
2682 0 : CodeLabel cl;
2683 0 : masm.writeCodePointer(cl.patchAt());
2684 0 : cl.target()->bind(labels[i].offset());
2685 0 : masm.addCodeLabel(cl);
2686 : }
2687 : #else
2688 : MOZ_CRASH("BaseCompiler platform hook: jumpTable");
2689 : #endif
2690 0 : }
2691 :
2692 0 : void tableSwitch(Label* theTable, RegI32 switchValue, Label* dispatchCode) {
2693 0 : masm.bind(dispatchCode);
2694 :
2695 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
2696 0 : ScratchI32 scratch(*this);
2697 0 : CodeLabel tableCl;
2698 :
2699 0 : masm.mov(tableCl.patchAt(), scratch);
2700 :
2701 0 : tableCl.target()->bind(theTable->offset());
2702 0 : masm.addCodeLabel(tableCl);
2703 :
2704 0 : masm.jmp(Operand(scratch, switchValue, ScalePointer));
2705 : #elif defined(JS_CODEGEN_ARM)
2706 : // Flush constant pools: offset must reflect the distance from the MOV
2707 : // to the start of the table; as the address of the MOV is given by the
2708 : // label, nothing must come between the bind() and the ma_mov().
2709 : masm.flush();
2710 :
2711 : ScratchI32 scratch(*this);
2712 :
2713 : // Compute the offset from the ma_mov instruction to the jump table.
2714 : Label here;
2715 : masm.bind(&here);
2716 : uint32_t offset = here.offset() - theTable->offset();
2717 :
2718 : // Read PC+8
2719 : masm.ma_mov(pc, scratch);
2720 :
2721 : // ARM scratch register is required by ma_sub.
2722 : ScratchRegisterScope arm_scratch(*this);
2723 :
2724 : // Compute the absolute table base pointer into `scratch`, offset by 8
2725 : // to account for the fact that ma_mov read PC+8.
2726 : masm.ma_sub(Imm32(offset + 8), scratch, arm_scratch);
2727 :
2728 : // Jump indirect via table element.
2729 : masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc, Offset,
2730 : Assembler::Always);
2731 : #else
2732 : MOZ_CRASH("BaseCompiler platform hook: tableSwitch");
2733 : #endif
2734 0 : }
2735 :
2736 0 : RegI32 captureReturnedI32() {
2737 0 : RegI32 rv = RegI32(ReturnReg);
2738 0 : MOZ_ASSERT(isAvailable(rv));
2739 0 : needI32(rv);
2740 0 : return rv;
2741 : }
2742 :
2743 0 : RegI64 captureReturnedI64() {
2744 0 : RegI64 rv = RegI64(ReturnReg64);
2745 0 : MOZ_ASSERT(isAvailable(rv));
2746 0 : needI64(rv);
2747 0 : return rv;
2748 : }
2749 :
2750 0 : RegF32 captureReturnedF32(const FunctionCall& call) {
2751 0 : RegF32 rv = RegF32(ReturnFloat32Reg);
2752 0 : MOZ_ASSERT(isAvailable(rv));
2753 0 : needF32(rv);
2754 : #if defined(JS_CODEGEN_ARM)
2755 : if (call.usesSystemAbi && !call.hardFP)
2756 : masm.ma_vxfer(r0, rv);
2757 : #endif
2758 0 : return rv;
2759 : }
2760 :
2761 0 : RegF64 captureReturnedF64(const FunctionCall& call) {
2762 0 : RegF64 rv = RegF64(ReturnDoubleReg);
2763 0 : MOZ_ASSERT(isAvailable(rv));
2764 0 : needF64(rv);
2765 : #if defined(JS_CODEGEN_ARM)
2766 : if (call.usesSystemAbi && !call.hardFP)
2767 : masm.ma_vxfer(r0, r1, rv);
2768 : #endif
2769 0 : return rv;
2770 : }
2771 :
2772 0 : void returnCleanup(bool popStack) {
2773 0 : if (popStack)
2774 0 : popStackBeforeBranch(controlOutermost().framePushed);
2775 0 : masm.jump(&returnLabel_);
2776 0 : }
2777 :
2778 0 : void pop2xI32ForIntMulDiv(RegI32* r0, RegI32* r1) {
2779 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2780 : // srcDest must be eax, and edx will be clobbered.
2781 0 : need2xI32(specific_eax, specific_edx);
2782 0 : *r1 = popI32();
2783 0 : *r0 = popI32ToSpecific(specific_eax);
2784 0 : freeI32(specific_edx);
2785 : #else
2786 : pop2xI32(r0, r1);
2787 : #endif
2788 0 : }
2789 :
2790 0 : void pop2xI64ForIntDiv(RegI64* r0, RegI64* r1) {
2791 : #ifdef JS_CODEGEN_X64
2792 : // srcDest must be rax, and rdx will be clobbered.
2793 0 : need2xI64(specific_rax, specific_rdx);
2794 0 : *r1 = popI64();
2795 0 : *r0 = popI64ToSpecific(specific_rax);
2796 0 : freeI64(specific_rdx);
2797 : #else
2798 : pop2xI64(r0, r1);
2799 : #endif
2800 0 : }
2801 :
2802 0 : void checkDivideByZeroI32(RegI32 rhs, RegI32 srcDest, Label* done) {
2803 0 : masm.branchTest32(Assembler::Zero, rhs, rhs, trap(Trap::IntegerDivideByZero));
2804 0 : }
2805 :
2806 0 : void checkDivideByZeroI64(RegI64 r) {
2807 0 : ScratchI32 scratch(*this);
2808 0 : masm.branchTest64(Assembler::Zero, r, r, scratch, trap(Trap::IntegerDivideByZero));
2809 0 : }
2810 :
2811 0 : void checkDivideSignedOverflowI32(RegI32 rhs, RegI32 srcDest, Label* done, bool zeroOnOverflow) {
2812 0 : Label notMin;
2813 0 : masm.branch32(Assembler::NotEqual, srcDest, Imm32(INT32_MIN), ¬Min);
2814 0 : if (zeroOnOverflow) {
2815 0 : masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Min);
2816 0 : masm.move32(Imm32(0), srcDest);
2817 0 : masm.jump(done);
2818 : } else {
2819 0 : masm.branch32(Assembler::Equal, rhs, Imm32(-1), trap(Trap::IntegerOverflow));
2820 : }
2821 0 : masm.bind(¬Min);
2822 0 : }
2823 :
2824 0 : void checkDivideSignedOverflowI64(RegI64 rhs, RegI64 srcDest, Label* done, bool zeroOnOverflow) {
2825 0 : Label notmin;
2826 0 : masm.branch64(Assembler::NotEqual, srcDest, Imm64(INT64_MIN), ¬min);
2827 0 : masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬min);
2828 0 : if (zeroOnOverflow) {
2829 0 : masm.xor64(srcDest, srcDest);
2830 0 : masm.jump(done);
2831 : } else {
2832 0 : masm.jump(trap(Trap::IntegerOverflow));
2833 : }
2834 0 : masm.bind(¬min);
2835 0 : }
2836 :
2837 : #ifndef INT_DIV_I64_CALLOUT
2838 0 : void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
2839 : bool isConst, int64_t c)
2840 : {
2841 0 : Label done;
2842 :
2843 0 : if (!isConst || c == 0)
2844 0 : checkDivideByZeroI64(rhs);
2845 :
2846 0 : if (!isUnsigned && (!isConst || c == -1))
2847 0 : checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
2848 :
2849 : # if defined(JS_CODEGEN_X64)
2850 : // The caller must set up the following situation.
2851 0 : MOZ_ASSERT(srcDest.reg == rax);
2852 0 : MOZ_ASSERT(isAvailable(rdx));
2853 0 : if (isUnsigned) {
2854 0 : masm.xorq(rdx, rdx);
2855 0 : masm.udivq(rhs.reg);
2856 : } else {
2857 0 : masm.cqo();
2858 0 : masm.idivq(rhs.reg);
2859 : }
2860 : # else
2861 : MOZ_CRASH("BaseCompiler platform hook: quotientI64");
2862 : # endif
2863 0 : masm.bind(&done);
2864 0 : }
2865 :
2866 0 : void remainderI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
2867 : bool isConst, int64_t c)
2868 : {
2869 0 : Label done;
2870 :
2871 0 : if (!isConst || c == 0)
2872 0 : checkDivideByZeroI64(rhs);
2873 :
2874 0 : if (!isUnsigned && (!isConst || c == -1))
2875 0 : checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
2876 :
2877 : # if defined(JS_CODEGEN_X64)
2878 : // The caller must set up the following situation.
2879 0 : MOZ_ASSERT(srcDest.reg == rax);
2880 0 : MOZ_ASSERT(isAvailable(rdx));
2881 :
2882 0 : if (isUnsigned) {
2883 0 : masm.xorq(rdx, rdx);
2884 0 : masm.udivq(rhs.reg);
2885 : } else {
2886 0 : masm.cqo();
2887 0 : masm.idivq(rhs.reg);
2888 : }
2889 0 : masm.movq(rdx, rax);
2890 : # else
2891 : MOZ_CRASH("BaseCompiler platform hook: remainderI64");
2892 : # endif
2893 0 : masm.bind(&done);
2894 0 : }
2895 : #endif // INT_DIV_I64_CALLOUT
2896 :
2897 0 : void pop2xI32ForShiftOrRotate(RegI32* r0, RegI32* r1) {
2898 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2899 0 : *r1 = popI32(specific_ecx);
2900 0 : *r0 = popI32();
2901 : #else
2902 : pop2xI32(r0, r1);
2903 : #endif
2904 0 : }
2905 :
2906 0 : void pop2xI64ForShiftOrRotate(RegI64* r0, RegI64* r1) {
2907 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2908 0 : needI32(specific_ecx);
2909 0 : *r1 = widenI32(specific_ecx);
2910 0 : *r1 = popI64ToSpecific(*r1);
2911 0 : *r0 = popI64();
2912 : #else
2913 : pop2xI64(r0, r1);
2914 : #endif
2915 0 : }
2916 :
2917 0 : bool rotate64NeedsTemp() const {
2918 : #if defined(JS_CODEGEN_X86)
2919 : return true;
2920 : #else
2921 0 : return false;
2922 : #endif
2923 : }
2924 :
2925 0 : void maskShiftCount32(RegI32 r) {
2926 : #if defined(JS_CODEGEN_ARM)
2927 : masm.and32(Imm32(31), r);
2928 : #endif
2929 0 : }
2930 :
2931 0 : bool popcnt32NeedsTemp() const {
2932 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2933 0 : return !AssemblerX86Shared::HasPOPCNT();
2934 : #elif defined(JS_CODEGEN_ARM)
2935 : return true;
2936 : #else
2937 : MOZ_CRASH("BaseCompiler platform hook: popcnt32NeedsTemp");
2938 : #endif
2939 : }
2940 :
2941 0 : bool popcnt64NeedsTemp() const {
2942 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
2943 0 : return !AssemblerX86Shared::HasPOPCNT();
2944 : #elif defined(JS_CODEGEN_ARM)
2945 : return true;
2946 : #else
2947 : MOZ_CRASH("BaseCompiler platform hook: popcnt64NeedsTemp");
2948 : #endif
2949 : }
2950 :
2951 0 : void reinterpretI64AsF64(RegI64 src, RegF64 dest) {
2952 : #if defined(JS_CODEGEN_X64)
2953 0 : masm.vmovq(src.reg, dest);
2954 : #elif defined(JS_CODEGEN_X86)
2955 : masm.Push(src.high);
2956 : masm.Push(src.low);
2957 : masm.vmovq(Operand(esp, 0), dest);
2958 : masm.freeStack(sizeof(uint64_t));
2959 : #elif defined(JS_CODEGEN_ARM)
2960 : masm.ma_vxfer(src.low, src.high, dest);
2961 : #else
2962 : MOZ_CRASH("BaseCompiler platform hook: reinterpretI64AsF64");
2963 : #endif
2964 0 : }
2965 :
2966 0 : void reinterpretF64AsI64(RegF64 src, RegI64 dest) {
2967 : #if defined(JS_CODEGEN_X64)
2968 0 : masm.vmovq(src, dest.reg);
2969 : #elif defined(JS_CODEGEN_X86)
2970 : masm.reserveStack(sizeof(uint64_t));
2971 : masm.vmovq(src, Operand(esp, 0));
2972 : masm.Pop(dest.low);
2973 : masm.Pop(dest.high);
2974 : #elif defined(JS_CODEGEN_ARM)
2975 : masm.ma_vxfer(src, dest.low, dest.high);
2976 : #else
2977 : MOZ_CRASH("BaseCompiler platform hook: reinterpretF64AsI64");
2978 : #endif
2979 0 : }
2980 :
2981 0 : void wrapI64ToI32(RegI64 src, RegI32 dest) {
2982 : #if defined(JS_CODEGEN_X64)
2983 : // movl clears the high bits if the two registers are the same.
2984 0 : masm.movl(src.reg, dest);
2985 : #elif defined(JS_NUNBOX32)
2986 : if (src.low != dest)
2987 : masm.move32(src.low, dest);
2988 : #else
2989 : MOZ_CRASH("BaseCompiler platform hook: wrapI64ToI32");
2990 : #endif
2991 0 : }
2992 :
2993 0 : RegI64 popI32ForSignExtendI64() {
2994 : #if defined(JS_CODEGEN_X86)
2995 : need2xI32(specific_edx, specific_eax);
2996 : RegI32 r0 = popI32ToSpecific(specific_eax);
2997 : RegI64 x0 = RegI64(Register64(specific_edx, specific_eax));
2998 : (void)r0; // x0 is the widening of r0
2999 : #else
3000 0 : RegI32 r0 = popI32();
3001 0 : RegI64 x0 = widenI32(r0);
3002 : #endif
3003 0 : return x0;
3004 : }
3005 :
3006 0 : void signExtendI32ToI64(RegI32 src, RegI64 dest) {
3007 : #if defined(JS_CODEGEN_X64)
3008 0 : masm.movslq(src, dest.reg);
3009 : #elif defined(JS_CODEGEN_X86)
3010 : MOZ_ASSERT(dest.low == src);
3011 : MOZ_ASSERT(dest.low == eax);
3012 : MOZ_ASSERT(dest.high == edx);
3013 : masm.cdq();
3014 : #elif defined(JS_CODEGEN_ARM)
3015 : masm.ma_mov(src, dest.low);
3016 : masm.ma_asr(Imm32(31), src, dest.high);
3017 : #else
3018 : MOZ_CRASH("BaseCompiler platform hook: signExtendI32ToI64");
3019 : #endif
3020 0 : }
3021 :
3022 0 : void extendU32ToI64(RegI32 src, RegI64 dest) {
3023 : #if defined(JS_CODEGEN_X64)
3024 0 : masm.movl(src, dest.reg);
3025 : #elif defined(JS_NUNBOX32)
3026 : if (src != dest.low)
3027 : masm.move32(src, dest.low);
3028 : masm.move32(Imm32(0), dest.high);
3029 : #else
3030 : MOZ_CRASH("BaseCompiler platform hook: extendU32ToI64");
3031 : #endif
3032 0 : }
3033 :
3034 : class OutOfLineTruncateF32OrF64ToI32 : public OutOfLineCode
3035 : {
3036 : AnyReg src;
3037 : RegI32 dest;
3038 : bool isUnsigned;
3039 : BytecodeOffset off;
3040 :
3041 : public:
3042 0 : OutOfLineTruncateF32OrF64ToI32(AnyReg src, RegI32 dest, bool isUnsigned, BytecodeOffset off)
3043 0 : : src(src),
3044 : dest(dest),
3045 : isUnsigned(isUnsigned),
3046 0 : off(off)
3047 0 : {}
3048 :
3049 0 : virtual void generate(MacroAssembler& masm) {
3050 0 : bool isFloat = src.tag == AnyReg::F32;
3051 0 : FloatRegister fsrc = isFloat ? static_cast<FloatRegister>(src.f32())
3052 0 : : static_cast<FloatRegister>(src.f64());
3053 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3054 0 : if (isFloat)
3055 0 : masm.outOfLineWasmTruncateFloat32ToInt32(fsrc, isUnsigned, off, rejoin());
3056 : else
3057 0 : masm.outOfLineWasmTruncateDoubleToInt32(fsrc, isUnsigned, off, rejoin());
3058 : #elif defined(JS_CODEGEN_ARM)
3059 : masm.outOfLineWasmTruncateToIntCheck(fsrc,
3060 : isFloat ? MIRType::Float32 : MIRType::Double,
3061 : MIRType::Int32, isUnsigned, rejoin(), off);
3062 : #else
3063 : (void)isUnsigned;
3064 : (void)off;
3065 : (void)isFloat;
3066 : (void)fsrc;
3067 : MOZ_CRASH("BaseCompiler platform hook: OutOfLineTruncateF32OrF64ToI32 wasm");
3068 : #endif
3069 0 : }
3070 : };
3071 :
3072 0 : MOZ_MUST_USE bool truncateF32ToI32(RegF32 src, RegI32 dest, bool isUnsigned) {
3073 0 : BytecodeOffset off = bytecodeOffset();
3074 : OutOfLineCode* ool;
3075 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
3076 0 : ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, isUnsigned, off);
3077 0 : ool = addOutOfLineCode(ool);
3078 0 : if (!ool)
3079 0 : return false;
3080 0 : if (isUnsigned)
3081 0 : masm.wasmTruncateFloat32ToUInt32(src, dest, ool->entry());
3082 : else
3083 0 : masm.wasmTruncateFloat32ToInt32(src, dest, ool->entry());
3084 : #else
3085 : (void)off;
3086 : MOZ_CRASH("BaseCompiler platform hook: truncateF32ToI32 wasm");
3087 : #endif
3088 0 : masm.bind(ool->rejoin());
3089 0 : return true;
3090 : }
3091 :
3092 0 : MOZ_MUST_USE bool truncateF64ToI32(RegF64 src, RegI32 dest, bool isUnsigned) {
3093 0 : BytecodeOffset off = bytecodeOffset();
3094 : OutOfLineCode* ool;
3095 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
3096 0 : ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, isUnsigned, off);
3097 0 : ool = addOutOfLineCode(ool);
3098 0 : if (!ool)
3099 0 : return false;
3100 0 : if (isUnsigned)
3101 0 : masm.wasmTruncateDoubleToUInt32(src, dest, ool->entry());
3102 : else
3103 0 : masm.wasmTruncateDoubleToInt32(src, dest, ool->entry());
3104 : #else
3105 : (void)off;
3106 : MOZ_CRASH("BaseCompiler platform hook: truncateF64ToI32 wasm");
3107 : #endif
3108 0 : masm.bind(ool->rejoin());
3109 0 : return true;
3110 : }
3111 :
3112 : // This does not generate a value; if the truncation failed then it traps.
3113 :
3114 : class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode
3115 : {
3116 : AnyReg src;
3117 : bool isUnsigned;
3118 : BytecodeOffset off;
3119 :
3120 : public:
3121 0 : OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src, bool isUnsigned, BytecodeOffset off)
3122 0 : : src(src),
3123 : isUnsigned(isUnsigned),
3124 0 : off(off)
3125 0 : {}
3126 :
3127 0 : virtual void generate(MacroAssembler& masm) {
3128 : #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
3129 0 : if (src.tag == AnyReg::F32)
3130 0 : masm.outOfLineWasmTruncateFloat32ToInt64(src.f32(), isUnsigned, off, rejoin());
3131 0 : else if (src.tag == AnyReg::F64)
3132 0 : masm.outOfLineWasmTruncateDoubleToInt64(src.f64(), isUnsigned, off, rejoin());
3133 : else
3134 0 : MOZ_CRASH("unexpected type");
3135 : #elif defined(JS_CODEGEN_ARM)
3136 : if (src.tag == AnyReg::F32)
3137 : masm.outOfLineWasmTruncateToIntCheck(src.f32(), MIRType::Float32,
3138 : MIRType::Int64, isUnsigned, rejoin(), off);
3139 : else if (src.tag == AnyReg::F64)
3140 : masm.outOfLineWasmTruncateToIntCheck(src.f64(), MIRType::Double, MIRType::Int64,
3141 : isUnsigned, rejoin(), off);
3142 : else
3143 : MOZ_CRASH("unexpected type");
3144 : #else
3145 : (void)src;
3146 : (void)isUnsigned;
3147 : (void)off;
3148 : MOZ_CRASH("BaseCompiler platform hook: OutOfLineTruncateCheckF32OrF64ToI64");
3149 : #endif
3150 0 : }
3151 : };
3152 :
3153 : #ifndef FLOAT_TO_I64_CALLOUT
3154 0 : MOZ_MUST_USE bool truncateF32ToI64(RegF32 src, RegI64 dest, bool isUnsigned, RegF64 temp) {
3155 : # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3156 : OutOfLineCode* ool =
3157 0 : addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(AnyReg(src),
3158 : isUnsigned,
3159 0 : bytecodeOffset()));
3160 0 : if (!ool)
3161 0 : return false;
3162 0 : if (isUnsigned)
3163 0 : masm.wasmTruncateFloat32ToUInt64(src, dest, ool->entry(),
3164 0 : ool->rejoin(), temp);
3165 : else
3166 0 : masm.wasmTruncateFloat32ToInt64(src, dest, ool->entry(),
3167 0 : ool->rejoin(), temp);
3168 : # else
3169 : MOZ_CRASH("BaseCompiler platform hook: truncateF32ToI64");
3170 : # endif
3171 0 : return true;
3172 : }
3173 :
3174 0 : MOZ_MUST_USE bool truncateF64ToI64(RegF64 src, RegI64 dest, bool isUnsigned, RegF64 temp) {
3175 : # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3176 : OutOfLineCode* ool =
3177 0 : addOutOfLineCode(new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(AnyReg(src),
3178 : isUnsigned,
3179 0 : bytecodeOffset()));
3180 0 : if (!ool)
3181 0 : return false;
3182 0 : if (isUnsigned)
3183 0 : masm.wasmTruncateDoubleToUInt64(src, dest, ool->entry(),
3184 0 : ool->rejoin(), temp);
3185 : else
3186 0 : masm.wasmTruncateDoubleToInt64(src, dest, ool->entry(),
3187 0 : ool->rejoin(), temp);
3188 : # else
3189 : MOZ_CRASH("BaseCompiler platform hook: truncateF64ToI64");
3190 : # endif
3191 0 : return true;
3192 : }
3193 : #endif // FLOAT_TO_I64_CALLOUT
3194 :
3195 : #ifndef I64_TO_FLOAT_CALLOUT
3196 0 : bool convertI64ToFloatNeedsTemp(ValType to, bool isUnsigned) const {
3197 : # if defined(JS_CODEGEN_X86)
3198 : return isUnsigned &&
3199 : ((to == ValType::F64 && AssemblerX86Shared::HasSSE3()) ||
3200 : to == ValType::F32);
3201 : # else
3202 0 : return isUnsigned;
3203 : # endif
3204 : }
3205 :
3206 0 : void convertI64ToF32(RegI64 src, bool isUnsigned, RegF32 dest, RegI32 temp) {
3207 : # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3208 0 : if (isUnsigned)
3209 0 : masm.convertUInt64ToFloat32(src, dest, temp);
3210 : else
3211 0 : masm.convertInt64ToFloat32(src, dest);
3212 : # else
3213 : MOZ_CRASH("BaseCompiler platform hook: convertI64ToF32");
3214 : # endif
3215 0 : }
3216 :
3217 0 : void convertI64ToF64(RegI64 src, bool isUnsigned, RegF64 dest, RegI32 temp) {
3218 : # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3219 0 : if (isUnsigned)
3220 0 : masm.convertUInt64ToDouble(src, dest, temp);
3221 : else
3222 0 : masm.convertInt64ToDouble(src, dest);
3223 : # else
3224 : MOZ_CRASH("BaseCompiler platform hook: convertI64ToF64");
3225 : # endif
3226 0 : }
3227 : #endif // I64_TO_FLOAT_CALLOUT
3228 :
3229 0 : void cmp64Set(Assembler::Condition cond, RegI64 lhs, RegI64 rhs, RegI32 dest) {
3230 : #if defined(JS_CODEGEN_X64)
3231 0 : masm.cmpq(rhs.reg, lhs.reg);
3232 0 : masm.emitSet(cond, dest);
3233 : #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
3234 : // TODO / OPTIMIZE (Bug 1316822): This is pretty branchy, we should be
3235 : // able to do better.
3236 : Label done, condTrue;
3237 : masm.branch64(cond, lhs, rhs, &condTrue);
3238 : masm.move32(Imm32(0), dest);
3239 : masm.jump(&done);
3240 : masm.bind(&condTrue);
3241 : masm.move32(Imm32(1), dest);
3242 : masm.bind(&done);
3243 : #else
3244 : MOZ_CRASH("BaseCompiler platform hook: cmp64Set");
3245 : #endif
3246 0 : }
3247 :
3248 0 : void eqz64(RegI64 src, RegI32 dest) {
3249 : #if defined(JS_CODEGEN_X64)
3250 0 : masm.cmpq(Imm32(0), src.reg);
3251 0 : masm.emitSet(Assembler::Equal, dest);
3252 : #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
3253 : masm.or32(src.high, src.low);
3254 : masm.cmp32(src.low, Imm32(0));
3255 : masm.emitSet(Assembler::Equal, dest);
3256 : #else
3257 : MOZ_CRASH("BaseCompiler platform hook: eqz64");
3258 : #endif
3259 0 : }
3260 :
3261 0 : void unreachableTrap()
3262 : {
3263 0 : masm.jump(trap(Trap::Unreachable));
3264 : #ifdef DEBUG
3265 0 : masm.breakpoint();
3266 : #endif
3267 0 : }
3268 :
3269 : //////////////////////////////////////////////////////////////////////
3270 : //
3271 : // Global variable access.
3272 :
3273 0 : uint32_t globalToTlsOffset(uint32_t globalOffset) {
3274 0 : return offsetof(TlsData, globalArea) + globalOffset;
3275 : }
3276 :
3277 0 : void loadGlobalVarI32(unsigned globalDataOffset, RegI32 r)
3278 : {
3279 0 : ScratchI32 tmp(*this);
3280 0 : masm.loadWasmTlsRegFromFrame(tmp);
3281 0 : masm.load32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3282 0 : }
3283 :
3284 0 : void loadGlobalVarI64(unsigned globalDataOffset, RegI64 r)
3285 : {
3286 0 : ScratchI32 tmp(*this);
3287 0 : masm.loadWasmTlsRegFromFrame(tmp);
3288 0 : masm.load64(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3289 0 : }
3290 :
3291 0 : void loadGlobalVarF32(unsigned globalDataOffset, RegF32 r)
3292 : {
3293 0 : ScratchI32 tmp(*this);
3294 0 : masm.loadWasmTlsRegFromFrame(tmp);
3295 0 : masm.loadFloat32(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3296 0 : }
3297 :
3298 0 : void loadGlobalVarF64(unsigned globalDataOffset, RegF64 r)
3299 : {
3300 0 : ScratchI32 tmp(*this);
3301 0 : masm.loadWasmTlsRegFromFrame(tmp);
3302 0 : masm.loadDouble(Address(tmp, globalToTlsOffset(globalDataOffset)), r);
3303 0 : }
3304 :
3305 0 : void storeGlobalVarI32(unsigned globalDataOffset, RegI32 r)
3306 : {
3307 0 : ScratchI32 tmp(*this);
3308 0 : masm.loadWasmTlsRegFromFrame(tmp);
3309 0 : masm.store32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3310 0 : }
3311 :
3312 0 : void storeGlobalVarI64(unsigned globalDataOffset, RegI64 r)
3313 : {
3314 0 : ScratchI32 tmp(*this);
3315 0 : masm.loadWasmTlsRegFromFrame(tmp);
3316 0 : masm.store64(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3317 0 : }
3318 :
3319 0 : void storeGlobalVarF32(unsigned globalDataOffset, RegF32 r)
3320 : {
3321 0 : ScratchI32 tmp(*this);
3322 0 : masm.loadWasmTlsRegFromFrame(tmp);
3323 0 : masm.storeFloat32(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3324 0 : }
3325 :
3326 0 : void storeGlobalVarF64(unsigned globalDataOffset, RegF64 r)
3327 : {
3328 0 : ScratchI32 tmp(*this);
3329 0 : masm.loadWasmTlsRegFromFrame(tmp);
3330 0 : masm.storeDouble(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
3331 0 : }
3332 :
3333 : //////////////////////////////////////////////////////////////////////
3334 : //
3335 : // Heap access.
3336 :
3337 0 : void bceCheckLocal(MemoryAccessDesc* access, uint32_t local, bool* omitBoundsCheck) {
3338 0 : if (local >= sizeof(BCESet)*8)
3339 0 : return;
3340 :
3341 0 : if ((bceSafe_ & (BCESet(1) << local)) && access->offset() < wasm::OffsetGuardLimit)
3342 0 : *omitBoundsCheck = true;
3343 :
3344 : // The local becomes safe even if the offset is beyond the guard limit.
3345 0 : bceSafe_ |= (BCESet(1) << local);
3346 : }
3347 :
3348 0 : void bceLocalIsUpdated(uint32_t local) {
3349 0 : if (local >= sizeof(BCESet)*8)
3350 0 : return;
3351 :
3352 0 : bceSafe_ &= ~(BCESet(1) << local);
3353 : }
3354 :
3355 0 : void checkOffset(MemoryAccessDesc* access, RegI32 ptr) {
3356 0 : if (access->offset() >= OffsetGuardLimit) {
3357 0 : masm.branchAdd32(Assembler::CarrySet, Imm32(access->offset()), ptr,
3358 0 : trap(Trap::OutOfBounds));
3359 0 : access->clearOffset();
3360 : }
3361 0 : }
3362 :
3363 : // This is the temp register passed as the last argument to load()
3364 0 : MOZ_MUST_USE size_t loadTemps(MemoryAccessDesc& access) {
3365 : #if defined(JS_CODEGEN_ARM)
3366 : if (IsUnaligned(access)) {
3367 : switch (access.type()) {
3368 : case Scalar::Float32:
3369 : return 2;
3370 : case Scalar::Float64:
3371 : return 3;
3372 : default:
3373 : return 1;
3374 : }
3375 : }
3376 : return 0;
3377 : #else
3378 0 : return 0;
3379 : #endif
3380 : }
3381 :
3382 0 : MOZ_MUST_USE bool needTlsForAccess(bool omitBoundsCheck) {
3383 : #if defined(JS_CODEGEN_ARM)
3384 : return !omitBoundsCheck;
3385 : #elif defined(JS_CODEGEN_X86)
3386 : return true;
3387 : #else
3388 0 : return false;
3389 : #endif
3390 : }
3391 :
3392 : // ptr and dest may be the same iff dest is I32.
3393 : // This may destroy ptr even if ptr and dest are not the same.
3394 0 : MOZ_MUST_USE bool load(MemoryAccessDesc* access, RegI32 tls, RegI32 ptr, bool omitBoundsCheck,
3395 : AnyReg dest, RegI32 tmp1, RegI32 tmp2, RegI32 tmp3)
3396 : {
3397 0 : checkOffset(access, ptr);
3398 :
3399 : #ifdef WASM_HUGE_MEMORY
3400 : // We have HeapReg and no bounds checking and need load neither
3401 : // memoryBase nor boundsCheckLimit from tls.
3402 0 : MOZ_ASSERT(tls == invalidI32());
3403 : #endif
3404 : #ifdef JS_CODEGEN_ARM
3405 : // We have HeapReg on ARM and don't need to load the memoryBase from tls.
3406 : MOZ_ASSERT_IF(omitBoundsCheck, tls == invalidI32());
3407 : #endif
3408 :
3409 : #ifndef WASM_HUGE_MEMORY
3410 : if (!omitBoundsCheck) {
3411 : masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr,
3412 : Address(tls, offsetof(TlsData, boundsCheckLimit)),
3413 : trap(Trap::OutOfBounds));
3414 : }
3415 : #endif
3416 :
3417 : #if defined(JS_CODEGEN_X64)
3418 0 : Operand srcAddr(HeapReg, ptr, TimesOne, access->offset());
3419 :
3420 0 : if (dest.tag == AnyReg::I64)
3421 0 : masm.wasmLoadI64(*access, srcAddr, dest.i64());
3422 : else
3423 0 : masm.wasmLoad(*access, srcAddr, dest.any());
3424 : #elif defined(JS_CODEGEN_X86)
3425 : masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
3426 : Operand srcAddr(ptr, access->offset());
3427 :
3428 : if (dest.tag == AnyReg::I64) {
3429 : MOZ_ASSERT(dest.i64() == abiReturnRegI64);
3430 : masm.wasmLoadI64(*access, srcAddr, dest.i64());
3431 : } else {
3432 : bool byteRegConflict = access->byteSize() == 1 && !singleByteRegs_.has(dest.i32());
3433 : AnyRegister out = byteRegConflict ? AnyRegister(ScratchRegX86) : dest.any();
3434 :
3435 : masm.wasmLoad(*access, srcAddr, out);
3436 :
3437 : if (byteRegConflict)
3438 : masm.mov(ScratchRegX86, dest.i32());
3439 : }
3440 : #elif defined(JS_CODEGEN_ARM)
3441 : if (IsUnaligned(*access)) {
3442 : switch (dest.tag) {
3443 : case AnyReg::I64:
3444 : masm.wasmUnalignedLoadI64(*access, HeapReg, ptr, ptr, dest.i64(), tmp1);
3445 : break;
3446 : case AnyReg::F32:
3447 : masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f32(), tmp1, tmp2,
3448 : Register::Invalid());
3449 : break;
3450 : case AnyReg::F64:
3451 : masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f64(), tmp1, tmp2, tmp3);
3452 : break;
3453 : default:
3454 : masm.wasmUnalignedLoad(*access, HeapReg, ptr, ptr, dest.i32(), tmp1);
3455 : break;
3456 : }
3457 : } else {
3458 : if (dest.tag == AnyReg::I64)
3459 : masm.wasmLoadI64(*access, HeapReg, ptr, ptr, dest.i64());
3460 : else
3461 : masm.wasmLoad(*access, HeapReg, ptr, ptr, dest.any());
3462 : }
3463 : #else
3464 : MOZ_CRASH("BaseCompiler platform hook: load");
3465 : #endif
3466 :
3467 0 : return true;
3468 : }
3469 :
3470 0 : MOZ_MUST_USE size_t storeTemps(const MemoryAccessDesc& access, ValType srcType) {
3471 : #if defined(JS_CODEGEN_ARM)
3472 : if (IsUnaligned(access) && srcType != ValType::I32)
3473 : return 1;
3474 : #endif
3475 0 : return 0;
3476 : }
3477 :
3478 : // ptr and src must not be the same register.
3479 : // This may destroy ptr and src.
3480 0 : MOZ_MUST_USE bool store(MemoryAccessDesc* access, RegI32 tls, RegI32 ptr, bool omitBoundsCheck,
3481 : AnyReg src, RegI32 tmp)
3482 : {
3483 0 : checkOffset(access, ptr);
3484 :
3485 : #ifdef WASM_HUGE_MEMORY
3486 : // We have HeapReg and no bounds checking and need load neither
3487 : // memoryBase nor boundsCheckLimit from tls.
3488 0 : MOZ_ASSERT(tls == invalidI32());
3489 : #endif
3490 : #ifdef JS_CODEGEN_ARM
3491 : // We have HeapReg on ARM and don't need to load the memoryBase from tls.
3492 : MOZ_ASSERT_IF(omitBoundsCheck, tls == invalidI32());
3493 : #endif
3494 :
3495 : #ifndef WASM_HUGE_MEMORY
3496 : if (!omitBoundsCheck) {
3497 : masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr,
3498 : Address(tls, offsetof(TlsData, boundsCheckLimit)),
3499 : trap(Trap::OutOfBounds));
3500 : }
3501 : #endif
3502 :
3503 : // Emit the store
3504 : #if defined(JS_CODEGEN_X64)
3505 0 : MOZ_ASSERT(tmp == Register::Invalid());
3506 0 : Operand dstAddr(HeapReg, ptr, TimesOne, access->offset());
3507 :
3508 0 : masm.wasmStore(*access, src.any(), dstAddr);
3509 : #elif defined(JS_CODEGEN_X86)
3510 : MOZ_ASSERT(tmp == Register::Invalid());
3511 : masm.addPtr(Address(tls, offsetof(TlsData, memoryBase)), ptr);
3512 : Operand dstAddr(ptr, access->offset());
3513 :
3514 : if (access->type() == Scalar::Int64) {
3515 : masm.wasmStoreI64(*access, src.i64(), dstAddr);
3516 : } else {
3517 : AnyRegister value;
3518 : if (src.tag == AnyReg::I64) {
3519 : if (access->byteSize() == 1 && !singleByteRegs_.has(src.i64().low)) {
3520 : masm.mov(src.i64().low, ScratchRegX86);
3521 : value = AnyRegister(ScratchRegX86);
3522 : } else {
3523 : value = AnyRegister(src.i64().low);
3524 : }
3525 : } else if (access->byteSize() == 1 && !singleByteRegs_.has(src.i32())) {
3526 : masm.mov(src.i32(), ScratchRegX86);
3527 : value = AnyRegister(ScratchRegX86);
3528 : } else {
3529 : value = src.any();
3530 : }
3531 :
3532 : masm.wasmStore(*access, value, dstAddr);
3533 : }
3534 : #elif defined(JS_CODEGEN_ARM)
3535 : if (IsUnaligned(*access)) {
3536 : switch (src.tag) {
3537 : case AnyReg::I64:
3538 : masm.wasmUnalignedStoreI64(*access, src.i64(), HeapReg, ptr, ptr, tmp);
3539 : break;
3540 : case AnyReg::F32:
3541 : masm.wasmUnalignedStoreFP(*access, src.f32(), HeapReg, ptr, ptr, tmp);
3542 : break;
3543 : case AnyReg::F64:
3544 : masm.wasmUnalignedStoreFP(*access, src.f64(), HeapReg, ptr, ptr, tmp);
3545 : break;
3546 : default:
3547 : MOZ_ASSERT(tmp == Register::Invalid());
3548 : masm.wasmUnalignedStore(*access, src.i32(), HeapReg, ptr, ptr);
3549 : break;
3550 : }
3551 : } else {
3552 : MOZ_ASSERT(tmp == Register::Invalid());
3553 : if (access->type() == Scalar::Int64)
3554 : masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr);
3555 : else if (src.tag == AnyReg::I64)
3556 : masm.wasmStore(*access, AnyRegister(src.i64().low), HeapReg, ptr, ptr);
3557 : else
3558 : masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr);
3559 : }
3560 : #else
3561 : MOZ_CRASH("BaseCompiler platform hook: store");
3562 : #endif
3563 :
3564 0 : return true;
3565 : }
3566 :
3567 : MOZ_MUST_USE bool
3568 0 : supportsRoundInstruction(RoundingMode mode)
3569 : {
3570 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3571 0 : return Assembler::HasRoundInstruction(mode);
3572 : #else
3573 : return false;
3574 : #endif
3575 : }
3576 :
3577 : void
3578 0 : roundF32(RoundingMode roundingMode, RegF32 f0)
3579 : {
3580 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3581 0 : masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
3582 : #else
3583 : MOZ_CRASH("NYI");
3584 : #endif
3585 0 : }
3586 :
3587 : void
3588 0 : roundF64(RoundingMode roundingMode, RegF64 f0)
3589 : {
3590 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
3591 0 : masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0);
3592 : #else
3593 : MOZ_CRASH("NYI");
3594 : #endif
3595 0 : }
3596 :
3597 : ////////////////////////////////////////////////////////////
3598 :
3599 : // Generally speaking, ABOVE this point there should be no value
3600 : // stack manipulation (calls to popI32 etc).
3601 :
3602 : // Generally speaking, BELOW this point there should be no
3603 : // platform dependencies. We make an exception for x86 register
3604 : // targeting, which is not too hard to keep clean.
3605 :
3606 : ////////////////////////////////////////////////////////////
3607 : //
3608 : // Sundry wrappers.
3609 :
3610 0 : void pop2xI32(RegI32* r0, RegI32* r1) {
3611 0 : *r1 = popI32();
3612 0 : *r0 = popI32();
3613 0 : }
3614 :
3615 0 : RegI32 popI32ToSpecific(RegI32 specific) {
3616 0 : freeI32(specific);
3617 0 : return popI32(specific);
3618 : }
3619 :
3620 0 : void pop2xI64(RegI64* r0, RegI64* r1) {
3621 0 : *r1 = popI64();
3622 0 : *r0 = popI64();
3623 0 : }
3624 :
3625 0 : RegI64 popI64ToSpecific(RegI64 specific) {
3626 0 : freeI64(specific);
3627 0 : return popI64(specific);
3628 : }
3629 :
3630 0 : void pop2xF32(RegF32* r0, RegF32* r1) {
3631 0 : *r1 = popF32();
3632 0 : *r0 = popF32();
3633 0 : }
3634 :
3635 0 : void pop2xF64(RegF64* r0, RegF64* r1) {
3636 0 : *r1 = popF64();
3637 0 : *r0 = popF64();
3638 0 : }
3639 :
3640 : RegI32 popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck);
3641 :
3642 : ////////////////////////////////////////////////////////////
3643 : //
3644 : // Sundry helpers.
3645 :
3646 0 : uint32_t readCallSiteLineOrBytecode() {
3647 0 : if (!func_.callSiteLineNums().empty())
3648 0 : return func_.callSiteLineNums()[lastReadCallSite_++];
3649 0 : return iter_.lastOpcodeOffset();
3650 : }
3651 :
3652 0 : bool done() const {
3653 0 : return iter_.done();
3654 : }
3655 :
3656 0 : BytecodeOffset bytecodeOffset() const {
3657 0 : return iter_.bytecodeOffset();
3658 : }
3659 :
3660 0 : TrapDesc trap(Trap t) const {
3661 0 : return TrapDesc(bytecodeOffset(), t, masm.framePushed());
3662 : }
3663 :
3664 : ////////////////////////////////////////////////////////////
3665 : //
3666 : // Machinery for optimized conditional branches.
3667 : //
3668 : // To disable this optimization it is enough always to return false from
3669 : // sniffConditionalControl{Cmp,Eqz}.
3670 :
3671 : struct BranchState {
3672 : static const int32_t NoPop = ~0;
3673 :
3674 : union {
3675 : struct {
3676 : RegI32 lhs;
3677 : RegI32 rhs;
3678 : int32_t imm;
3679 : bool rhsImm;
3680 : } i32;
3681 : struct {
3682 : RegI64 lhs;
3683 : RegI64 rhs;
3684 : int64_t imm;
3685 : bool rhsImm;
3686 : } i64;
3687 : struct {
3688 : RegF32 lhs;
3689 : RegF32 rhs;
3690 : } f32;
3691 : struct {
3692 : RegF64 lhs;
3693 : RegF64 rhs;
3694 : } f64;
3695 : };
3696 :
3697 : Label* const label; // The target of the branch, never NULL
3698 : const int32_t framePushed; // Either NoPop, or the value to pop to along the taken edge
3699 : const bool invertBranch; // If true, invert the sense of the branch
3700 : const ExprType resultType; // The result propagated along the edges, or Void
3701 :
3702 0 : explicit BranchState(Label* label, int32_t framePushed = NoPop,
3703 : uint32_t invertBranch = false, ExprType resultType = ExprType::Void)
3704 0 : : label(label),
3705 : framePushed(framePushed),
3706 : invertBranch(invertBranch),
3707 0 : resultType(resultType)
3708 0 : {}
3709 : };
3710 :
3711 0 : void setLatentCompare(Assembler::Condition compareOp, ValType operandType) {
3712 0 : latentOp_ = LatentOp::Compare;
3713 0 : latentType_ = operandType;
3714 0 : latentIntCmp_ = compareOp;
3715 0 : }
3716 :
3717 0 : void setLatentCompare(Assembler::DoubleCondition compareOp, ValType operandType) {
3718 0 : latentOp_ = LatentOp::Compare;
3719 0 : latentType_ = operandType;
3720 0 : latentDoubleCmp_ = compareOp;
3721 0 : }
3722 :
3723 0 : void setLatentEqz(ValType operandType) {
3724 0 : latentOp_ = LatentOp::Eqz;
3725 0 : latentType_ = operandType;
3726 0 : }
3727 :
3728 0 : void resetLatentOp() {
3729 0 : latentOp_ = LatentOp::None;
3730 0 : }
3731 :
3732 0 : void branchTo(Assembler::DoubleCondition c, RegF64 lhs, RegF64 rhs, Label* l) {
3733 0 : masm.branchDouble(c, lhs, rhs, l);
3734 0 : }
3735 :
3736 0 : void branchTo(Assembler::DoubleCondition c, RegF32 lhs, RegF32 rhs, Label* l) {
3737 0 : masm.branchFloat(c, lhs, rhs, l);
3738 0 : }
3739 :
3740 0 : void branchTo(Assembler::Condition c, RegI32 lhs, RegI32 rhs, Label* l) {
3741 0 : masm.branch32(c, lhs, rhs, l);
3742 0 : }
3743 :
3744 0 : void branchTo(Assembler::Condition c, RegI32 lhs, Imm32 rhs, Label* l) {
3745 0 : masm.branch32(c, lhs, rhs, l);
3746 0 : }
3747 :
3748 0 : void branchTo(Assembler::Condition c, RegI64 lhs, RegI64 rhs, Label* l) {
3749 0 : masm.branch64(c, lhs, rhs, l);
3750 0 : }
3751 :
3752 0 : void branchTo(Assembler::Condition c, RegI64 lhs, Imm64 rhs, Label* l) {
3753 0 : masm.branch64(c, lhs, rhs, l);
3754 0 : }
3755 :
3756 : // Emit a conditional branch that optionally and optimally cleans up the CPU
3757 : // stack before we branch.
3758 : //
3759 : // Cond is either Assembler::Condition or Assembler::DoubleCondition.
3760 : //
3761 : // Lhs is Register, Register64, or FloatRegister.
3762 : //
3763 : // Rhs is either the same as Lhs, or an immediate expression compatible with
3764 : // Lhs "when applicable".
3765 :
3766 : template<typename Cond, typename Lhs, typename Rhs>
3767 0 : void jumpConditionalWithJoinReg(BranchState* b, Cond cond, Lhs lhs, Rhs rhs)
3768 : {
3769 0 : AnyReg r = popJoinRegUnlessVoid(b->resultType);
3770 :
3771 0 : if (b->framePushed != BranchState::NoPop && willPopStackBeforeBranch(b->framePushed)) {
3772 0 : Label notTaken;
3773 0 : branchTo(b->invertBranch ? cond : Assembler::InvertCondition(cond), lhs, rhs, ¬Taken);
3774 0 : popStackBeforeBranch(b->framePushed);
3775 0 : masm.jump(b->label);
3776 0 : masm.bind(¬Taken);
3777 : } else {
3778 0 : branchTo(b->invertBranch ? Assembler::InvertCondition(cond) : cond, lhs, rhs, b->label);
3779 : }
3780 :
3781 0 : pushJoinRegUnlessVoid(r);
3782 0 : }
3783 :
3784 : // sniffConditionalControl{Cmp,Eqz} may modify the latentWhatever_ state in
3785 : // the BaseCompiler so that a subsequent conditional branch can be compiled
3786 : // optimally. emitBranchSetup() and emitBranchPerform() will consume that
3787 : // state. If the latter methods are not called because deadCode_ is true
3788 : // then the compiler MUST instead call resetLatentOp() to reset the state.
3789 :
3790 : template<typename Cond> bool sniffConditionalControlCmp(Cond compareOp, ValType operandType);
3791 : bool sniffConditionalControlEqz(ValType operandType);
3792 : void emitBranchSetup(BranchState* b);
3793 : void emitBranchPerform(BranchState* b);
3794 :
3795 : //////////////////////////////////////////////////////////////////////
3796 :
3797 : MOZ_MUST_USE bool emitBody();
3798 : MOZ_MUST_USE bool emitBlock();
3799 : MOZ_MUST_USE bool emitLoop();
3800 : MOZ_MUST_USE bool emitIf();
3801 : MOZ_MUST_USE bool emitElse();
3802 : MOZ_MUST_USE bool emitEnd();
3803 : MOZ_MUST_USE bool emitBr();
3804 : MOZ_MUST_USE bool emitBrIf();
3805 : MOZ_MUST_USE bool emitBrTable();
3806 : MOZ_MUST_USE bool emitDrop();
3807 : MOZ_MUST_USE bool emitReturn();
3808 : MOZ_MUST_USE bool emitCallArgs(const ValTypeVector& args, FunctionCall& baselineCall);
3809 : MOZ_MUST_USE bool emitCall();
3810 : MOZ_MUST_USE bool emitCallIndirect();
3811 : MOZ_MUST_USE bool emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
3812 : MOZ_MUST_USE bool emitGetLocal();
3813 : MOZ_MUST_USE bool emitSetLocal();
3814 : MOZ_MUST_USE bool emitTeeLocal();
3815 : MOZ_MUST_USE bool emitGetGlobal();
3816 : MOZ_MUST_USE bool emitSetGlobal();
3817 : MOZ_MUST_USE RegI32 maybeLoadTlsForAccess(bool omitBoundsCheck);
3818 : MOZ_MUST_USE bool emitLoad(ValType type, Scalar::Type viewType);
3819 : MOZ_MUST_USE bool emitStore(ValType resultType, Scalar::Type viewType);
3820 : MOZ_MUST_USE bool emitSelect();
3821 :
3822 : // Mark these templates as inline to work around a compiler crash in
3823 : // gcc 4.8.5 when compiling for linux64-opt.
3824 :
3825 : template<bool isSetLocal> MOZ_MUST_USE inline bool emitSetOrTeeLocal(uint32_t slot);
3826 :
3827 : void endBlock(ExprType type);
3828 : void endLoop(ExprType type);
3829 : void endIfThen();
3830 : void endIfThenElse(ExprType type);
3831 :
3832 : void doReturn(ExprType returnType, bool popStack);
3833 : void pushReturned(const FunctionCall& call, ExprType type);
3834 :
3835 : void emitCompareI32(Assembler::Condition compareOp, ValType compareType);
3836 : void emitCompareI64(Assembler::Condition compareOp, ValType compareType);
3837 : void emitCompareF32(Assembler::DoubleCondition compareOp, ValType compareType);
3838 : void emitCompareF64(Assembler::DoubleCondition compareOp, ValType compareType);
3839 :
3840 : void emitAddI32();
3841 : void emitAddI64();
3842 : void emitAddF64();
3843 : void emitAddF32();
3844 : void emitSubtractI32();
3845 : void emitSubtractI64();
3846 : void emitSubtractF32();
3847 : void emitSubtractF64();
3848 : void emitMultiplyI32();
3849 : void emitMultiplyI64();
3850 : void emitMultiplyF32();
3851 : void emitMultiplyF64();
3852 : void emitQuotientI32();
3853 : void emitQuotientU32();
3854 : void emitRemainderI32();
3855 : void emitRemainderU32();
3856 : #ifdef INT_DIV_I64_CALLOUT
3857 : void emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType);
3858 : #else
3859 : void emitQuotientI64();
3860 : void emitQuotientU64();
3861 : void emitRemainderI64();
3862 : void emitRemainderU64();
3863 : #endif
3864 : void emitDivideF32();
3865 : void emitDivideF64();
3866 : void emitMinF32();
3867 : void emitMaxF32();
3868 : void emitMinF64();
3869 : void emitMaxF64();
3870 : void emitCopysignF32();
3871 : void emitCopysignF64();
3872 : void emitOrI32();
3873 : void emitOrI64();
3874 : void emitAndI32();
3875 : void emitAndI64();
3876 : void emitXorI32();
3877 : void emitXorI64();
3878 : void emitShlI32();
3879 : void emitShlI64();
3880 : void emitShrI32();
3881 : void emitShrI64();
3882 : void emitShrU32();
3883 : void emitShrU64();
3884 : void emitRotrI32();
3885 : void emitRotrI64();
3886 : void emitRotlI32();
3887 : void emitRotlI64();
3888 : void emitEqzI32();
3889 : void emitEqzI64();
3890 : void emitClzI32();
3891 : void emitClzI64();
3892 : void emitCtzI32();
3893 : void emitCtzI64();
3894 : void emitPopcntI32();
3895 : void emitPopcntI64();
3896 : void emitAbsF32();
3897 : void emitAbsF64();
3898 : void emitNegateF32();
3899 : void emitNegateF64();
3900 : void emitSqrtF32();
3901 : void emitSqrtF64();
3902 : template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF32ToI32();
3903 : template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF64ToI32();
3904 : #ifdef FLOAT_TO_I64_CALLOUT
3905 : MOZ_MUST_USE bool emitConvertFloatingToInt64Callout(SymbolicAddress callee, ValType operandType,
3906 : ValType resultType);
3907 : #else
3908 : template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF32ToI64();
3909 : template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF64ToI64();
3910 : #endif
3911 : void emitWrapI64ToI32();
3912 : void emitExtendI32ToI64();
3913 : void emitExtendU32ToI64();
3914 : void emitReinterpretF32AsI32();
3915 : void emitReinterpretF64AsI64();
3916 : void emitConvertF64ToF32();
3917 : void emitConvertI32ToF32();
3918 : void emitConvertU32ToF32();
3919 : void emitConvertF32ToF64();
3920 : void emitConvertI32ToF64();
3921 : void emitConvertU32ToF64();
3922 : #ifdef I64_TO_FLOAT_CALLOUT
3923 : MOZ_MUST_USE bool emitConvertInt64ToFloatingCallout(SymbolicAddress callee, ValType operandType,
3924 : ValType resultType);
3925 : #else
3926 : void emitConvertI64ToF32();
3927 : void emitConvertU64ToF32();
3928 : void emitConvertI64ToF64();
3929 : void emitConvertU64ToF64();
3930 : #endif
3931 : void emitReinterpretI32AsF32();
3932 : void emitReinterpretI64AsF64();
3933 : void emitRound(RoundingMode roundingMode, ValType operandType);
3934 : MOZ_MUST_USE bool emitGrowMemory();
3935 : MOZ_MUST_USE bool emitCurrentMemory();
3936 : };
3937 :
3938 : void
3939 0 : BaseCompiler::emitAddI32()
3940 : {
3941 : int32_t c;
3942 0 : if (popConstI32(&c)) {
3943 0 : RegI32 r = popI32();
3944 0 : masm.add32(Imm32(c), r);
3945 0 : pushI32(r);
3946 : } else {
3947 0 : RegI32 r0, r1;
3948 0 : pop2xI32(&r0, &r1);
3949 0 : masm.add32(r1, r0);
3950 0 : freeI32(r1);
3951 0 : pushI32(r0);
3952 : }
3953 0 : }
3954 :
3955 : void
3956 0 : BaseCompiler::emitAddI64()
3957 : {
3958 : int64_t c;
3959 0 : if (popConstI64(&c)) {
3960 0 : RegI64 r = popI64();
3961 0 : masm.add64(Imm64(c), r);
3962 0 : pushI64(r);
3963 : } else {
3964 0 : RegI64 r0, r1;
3965 0 : pop2xI64(&r0, &r1);
3966 0 : masm.add64(r1, r0);
3967 0 : freeI64(r1);
3968 0 : pushI64(r0);
3969 : }
3970 0 : }
3971 :
3972 : void
3973 0 : BaseCompiler::emitAddF64()
3974 : {
3975 0 : RegF64 r0, r1;
3976 0 : pop2xF64(&r0, &r1);
3977 0 : masm.addDouble(r1, r0);
3978 0 : freeF64(r1);
3979 0 : pushF64(r0);
3980 0 : }
3981 :
3982 : void
3983 0 : BaseCompiler::emitAddF32()
3984 : {
3985 0 : RegF32 r0, r1;
3986 0 : pop2xF32(&r0, &r1);
3987 0 : masm.addFloat32(r1, r0);
3988 0 : freeF32(r1);
3989 0 : pushF32(r0);
3990 0 : }
3991 :
3992 : void
3993 0 : BaseCompiler::emitSubtractI32()
3994 : {
3995 : int32_t c;
3996 0 : if (popConstI32(&c)) {
3997 0 : RegI32 r = popI32();
3998 0 : masm.sub32(Imm32(c), r);
3999 0 : pushI32(r);
4000 : } else {
4001 0 : RegI32 r0, r1;
4002 0 : pop2xI32(&r0, &r1);
4003 0 : masm.sub32(r1, r0);
4004 0 : freeI32(r1);
4005 0 : pushI32(r0);
4006 : }
4007 0 : }
4008 :
4009 : void
4010 0 : BaseCompiler::emitSubtractI64()
4011 : {
4012 : int64_t c;
4013 0 : if (popConstI64(&c)) {
4014 0 : RegI64 r = popI64();
4015 0 : masm.sub64(Imm64(c), r);
4016 0 : pushI64(r);
4017 : } else {
4018 0 : RegI64 r0, r1;
4019 0 : pop2xI64(&r0, &r1);
4020 0 : masm.sub64(r1, r0);
4021 0 : freeI64(r1);
4022 0 : pushI64(r0);
4023 : }
4024 0 : }
4025 :
4026 : void
4027 0 : BaseCompiler::emitSubtractF32()
4028 : {
4029 0 : RegF32 r0, r1;
4030 0 : pop2xF32(&r0, &r1);
4031 0 : masm.subFloat32(r1, r0);
4032 0 : freeF32(r1);
4033 0 : pushF32(r0);
4034 0 : }
4035 :
4036 : void
4037 0 : BaseCompiler::emitSubtractF64()
4038 : {
4039 0 : RegF64 r0, r1;
4040 0 : pop2xF64(&r0, &r1);
4041 0 : masm.subDouble(r1, r0);
4042 0 : freeF64(r1);
4043 0 : pushF64(r0);
4044 0 : }
4045 :
4046 : void
4047 0 : BaseCompiler::emitMultiplyI32()
4048 : {
4049 0 : RegI32 r0, r1;
4050 0 : pop2xI32ForIntMulDiv(&r0, &r1);
4051 0 : masm.mul32(r1, r0);
4052 0 : freeI32(r1);
4053 0 : pushI32(r0);
4054 0 : }
4055 :
4056 : void
4057 0 : BaseCompiler::emitMultiplyI64()
4058 : {
4059 0 : RegI64 r0, r1;
4060 0 : RegI32 temp;
4061 : #if defined(JS_CODEGEN_X64)
4062 : // srcDest must be rax, and rdx will be clobbered.
4063 0 : need2xI64(specific_rax, specific_rdx);
4064 0 : r1 = popI64();
4065 0 : r0 = popI64ToSpecific(specific_rax);
4066 0 : freeI64(specific_rdx);
4067 : #elif defined(JS_CODEGEN_X86)
4068 : need2xI32(specific_eax, specific_edx);
4069 : r1 = popI64();
4070 : r0 = popI64ToSpecific(RegI64(Register64(specific_edx, specific_eax)));
4071 : temp = needI32();
4072 : #else
4073 : pop2xI64(&r0, &r1);
4074 : temp = needI32();
4075 : #endif
4076 0 : masm.mul64(r1, r0, temp);
4077 0 : if (temp != Register::Invalid())
4078 0 : freeI32(temp);
4079 0 : freeI64(r1);
4080 0 : pushI64(r0);
4081 0 : }
4082 :
4083 : void
4084 0 : BaseCompiler::emitMultiplyF32()
4085 : {
4086 0 : RegF32 r0, r1;
4087 0 : pop2xF32(&r0, &r1);
4088 0 : masm.mulFloat32(r1, r0);
4089 0 : freeF32(r1);
4090 0 : pushF32(r0);
4091 0 : }
4092 :
4093 : void
4094 0 : BaseCompiler::emitMultiplyF64()
4095 : {
4096 0 : RegF64 r0, r1;
4097 0 : pop2xF64(&r0, &r1);
4098 0 : masm.mulDouble(r1, r0);
4099 0 : freeF64(r1);
4100 0 : pushF64(r0);
4101 0 : }
4102 :
4103 : void
4104 0 : BaseCompiler::emitQuotientI32()
4105 : {
4106 : int32_t c;
4107 : uint_fast8_t power;
4108 0 : if (popConstPositivePowerOfTwoI32(&c, &power, 0)) {
4109 0 : if (power != 0) {
4110 0 : RegI32 r = popI32();
4111 0 : Label positive;
4112 0 : masm.branchTest32(Assembler::NotSigned, r, r, &positive);
4113 0 : masm.add32(Imm32(c-1), r);
4114 0 : masm.bind(&positive);
4115 :
4116 0 : masm.rshift32Arithmetic(Imm32(power & 31), r);
4117 0 : pushI32(r);
4118 : }
4119 : } else {
4120 0 : bool isConst = peekConstI32(&c);
4121 0 : RegI32 r0, r1;
4122 0 : pop2xI32ForIntMulDiv(&r0, &r1);
4123 :
4124 0 : Label done;
4125 0 : if (!isConst || c == 0)
4126 0 : checkDivideByZeroI32(r1, r0, &done);
4127 0 : if (!isConst || c == -1)
4128 0 : checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(false));
4129 0 : masm.quotient32(r1, r0, IsUnsigned(false));
4130 0 : masm.bind(&done);
4131 :
4132 0 : freeI32(r1);
4133 0 : pushI32(r0);
4134 : }
4135 0 : }
4136 :
4137 : void
4138 0 : BaseCompiler::emitQuotientU32()
4139 : {
4140 : int32_t c;
4141 : uint_fast8_t power;
4142 0 : if (popConstPositivePowerOfTwoI32(&c, &power, 0)) {
4143 0 : if (power != 0) {
4144 0 : RegI32 r = popI32();
4145 0 : masm.rshift32(Imm32(power & 31), r);
4146 0 : pushI32(r);
4147 : }
4148 : } else {
4149 0 : bool isConst = peekConstI32(&c);
4150 0 : RegI32 r0, r1;
4151 0 : pop2xI32ForIntMulDiv(&r0, &r1);
4152 :
4153 0 : Label done;
4154 0 : if (!isConst || c == 0)
4155 0 : checkDivideByZeroI32(r1, r0, &done);
4156 0 : masm.quotient32(r1, r0, IsUnsigned(true));
4157 0 : masm.bind(&done);
4158 :
4159 0 : freeI32(r1);
4160 0 : pushI32(r0);
4161 : }
4162 0 : }
4163 :
4164 : void
4165 0 : BaseCompiler::emitRemainderI32()
4166 : {
4167 : int32_t c;
4168 : uint_fast8_t power;
4169 0 : if (popConstPositivePowerOfTwoI32(&c, &power, 1)) {
4170 0 : RegI32 r = popI32();
4171 0 : RegI32 temp = needI32();
4172 0 : moveI32(r, temp);
4173 :
4174 0 : Label positive;
4175 0 : masm.branchTest32(Assembler::NotSigned, temp, temp, &positive);
4176 0 : masm.add32(Imm32(c-1), temp);
4177 0 : masm.bind(&positive);
4178 :
4179 0 : masm.rshift32Arithmetic(Imm32(power & 31), temp);
4180 0 : masm.lshift32(Imm32(power & 31), temp);
4181 0 : masm.sub32(temp, r);
4182 0 : freeI32(temp);
4183 :
4184 0 : pushI32(r);
4185 : } else {
4186 0 : bool isConst = peekConstI32(&c);
4187 0 : RegI32 r0, r1;
4188 0 : pop2xI32ForIntMulDiv(&r0, &r1);
4189 :
4190 0 : Label done;
4191 0 : if (!isConst || c == 0)
4192 0 : checkDivideByZeroI32(r1, r0, &done);
4193 0 : if (!isConst || c == -1)
4194 0 : checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(true));
4195 0 : masm.remainder32(r1, r0, IsUnsigned(false));
4196 0 : masm.bind(&done);
4197 :
4198 0 : freeI32(r1);
4199 0 : pushI32(r0);
4200 : }
4201 0 : }
4202 :
4203 : void
4204 0 : BaseCompiler::emitRemainderU32()
4205 : {
4206 : int32_t c;
4207 : uint_fast8_t power;
4208 0 : if (popConstPositivePowerOfTwoI32(&c, &power, 1)) {
4209 0 : RegI32 r = popI32();
4210 0 : masm.and32(Imm32(c-1), r);
4211 0 : pushI32(r);
4212 : } else {
4213 0 : bool isConst = peekConstI32(&c);
4214 0 : RegI32 r0, r1;
4215 0 : pop2xI32ForIntMulDiv(&r0, &r1);
4216 :
4217 0 : Label done;
4218 0 : if (!isConst || c == 0)
4219 0 : checkDivideByZeroI32(r1, r0, &done);
4220 0 : masm.remainder32(r1, r0, IsUnsigned(true));
4221 0 : masm.bind(&done);
4222 :
4223 0 : freeI32(r1);
4224 0 : pushI32(r0);
4225 : }
4226 0 : }
4227 :
4228 : #ifndef INT_DIV_I64_CALLOUT
4229 : void
4230 0 : BaseCompiler::emitQuotientI64()
4231 : {
4232 : # ifdef JS_PUNBOX64
4233 : int64_t c;
4234 : uint_fast8_t power;
4235 0 : if (popConstPositivePowerOfTwoI64(&c, &power, 0)) {
4236 0 : if (power != 0) {
4237 0 : RegI64 r = popI64();
4238 0 : Label positive;
4239 0 : masm.branchTest64(Assembler::NotSigned, r, r, Register::Invalid(),
4240 0 : &positive);
4241 0 : masm.add64(Imm32(c-1), r);
4242 0 : masm.bind(&positive);
4243 :
4244 0 : masm.rshift64Arithmetic(Imm32(power & 63), r);
4245 0 : pushI64(r);
4246 : }
4247 : } else {
4248 0 : bool isConst = peekConstI64(&c);
4249 0 : RegI64 r0, r1;
4250 0 : pop2xI64ForIntDiv(&r0, &r1);
4251 0 : quotientI64(r1, r0, IsUnsigned(false), isConst, c);
4252 0 : freeI64(r1);
4253 0 : pushI64(r0);
4254 : }
4255 : # else
4256 : MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
4257 : # endif
4258 0 : }
4259 :
4260 : void
4261 0 : BaseCompiler::emitQuotientU64()
4262 : {
4263 : # ifdef JS_PUNBOX64
4264 : int64_t c;
4265 : uint_fast8_t power;
4266 0 : if (popConstPositivePowerOfTwoI64(&c, &power, 0)) {
4267 0 : if (power != 0) {
4268 0 : RegI64 r = popI64();
4269 0 : masm.rshift64(Imm32(power & 63), r);
4270 0 : pushI64(r);
4271 : }
4272 : } else {
4273 0 : bool isConst = peekConstI64(&c);
4274 0 : RegI64 r0, r1;
4275 0 : pop2xI64ForIntDiv(&r0, &r1);
4276 0 : quotientI64(r1, r0, IsUnsigned(true), isConst, c);
4277 0 : freeI64(r1);
4278 0 : pushI64(r0);
4279 : }
4280 : # else
4281 : MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
4282 : # endif
4283 0 : }
4284 :
4285 : void
4286 0 : BaseCompiler::emitRemainderI64()
4287 : {
4288 : # ifdef JS_PUNBOX64
4289 : int64_t c;
4290 : uint_fast8_t power;
4291 0 : if (popConstPositivePowerOfTwoI64(&c, &power, 1)) {
4292 0 : RegI64 r = popI64();
4293 0 : RegI64 temp = needI64();
4294 0 : moveI64(r, temp);
4295 :
4296 0 : Label positive;
4297 0 : masm.branchTest64(Assembler::NotSigned, temp, temp,
4298 0 : Register::Invalid(), &positive);
4299 0 : masm.add64(Imm64(c-1), temp);
4300 0 : masm.bind(&positive);
4301 :
4302 0 : masm.rshift64Arithmetic(Imm32(power & 63), temp);
4303 0 : masm.lshift64(Imm32(power & 63), temp);
4304 0 : masm.sub64(temp, r);
4305 0 : freeI64(temp);
4306 :
4307 0 : pushI64(r);
4308 : } else {
4309 0 : bool isConst = peekConstI64(&c);
4310 0 : RegI64 r0, r1;
4311 0 : pop2xI64ForIntDiv(&r0, &r1);
4312 0 : remainderI64(r1, r0, IsUnsigned(false), isConst, c);
4313 0 : freeI64(r1);
4314 0 : pushI64(r0);
4315 : }
4316 : # else
4317 : MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
4318 : # endif
4319 0 : }
4320 :
4321 : void
4322 0 : BaseCompiler::emitRemainderU64()
4323 : {
4324 : # ifdef JS_PUNBOX64
4325 : int64_t c;
4326 : uint_fast8_t power;
4327 0 : if (popConstPositivePowerOfTwoI64(&c, &power, 1)) {
4328 0 : RegI64 r = popI64();
4329 0 : masm.and64(Imm64(c-1), r);
4330 0 : pushI64(r);
4331 : } else {
4332 0 : bool isConst = peekConstI64(&c);
4333 0 : RegI64 r0, r1;
4334 0 : pop2xI64ForIntDiv(&r0, &r1);
4335 0 : remainderI64(r1, r0, IsUnsigned(true), isConst, c);
4336 0 : freeI64(r1);
4337 0 : pushI64(r0);
4338 : }
4339 : # else
4340 : MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
4341 : # endif
4342 0 : }
4343 : #endif // INT_DIV_I64_CALLOUT
4344 :
4345 : void
4346 0 : BaseCompiler::emitDivideF32()
4347 : {
4348 0 : RegF32 r0, r1;
4349 0 : pop2xF32(&r0, &r1);
4350 0 : masm.divFloat32(r1, r0);
4351 0 : freeF32(r1);
4352 0 : pushF32(r0);
4353 0 : }
4354 :
4355 : void
4356 0 : BaseCompiler::emitDivideF64()
4357 : {
4358 0 : RegF64 r0, r1;
4359 0 : pop2xF64(&r0, &r1);
4360 0 : masm.divDouble(r1, r0);
4361 0 : freeF64(r1);
4362 0 : pushF64(r0);
4363 0 : }
4364 :
4365 : void
4366 0 : BaseCompiler::emitMinF32()
4367 : {
4368 0 : RegF32 r0, r1;
4369 0 : pop2xF32(&r0, &r1);
4370 : // Convert signaling NaN to quiet NaNs.
4371 : //
4372 : // TODO / OPTIMIZE (bug 1316824): Don't do this if one of the operands
4373 : // is known to be a constant.
4374 0 : ScratchF32 zero(*this);
4375 0 : masm.loadConstantFloat32(0.f, zero);
4376 0 : masm.subFloat32(zero, r0);
4377 0 : masm.subFloat32(zero, r1);
4378 0 : masm.minFloat32(r1, r0, HandleNaNSpecially(true));
4379 0 : freeF32(r1);
4380 0 : pushF32(r0);
4381 0 : }
4382 :
4383 : void
4384 0 : BaseCompiler::emitMaxF32()
4385 : {
4386 0 : RegF32 r0, r1;
4387 0 : pop2xF32(&r0, &r1);
4388 : // Convert signaling NaN to quiet NaNs.
4389 : //
4390 : // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
4391 0 : ScratchF32 zero(*this);
4392 0 : masm.loadConstantFloat32(0.f, zero);
4393 0 : masm.subFloat32(zero, r0);
4394 0 : masm.subFloat32(zero, r1);
4395 0 : masm.maxFloat32(r1, r0, HandleNaNSpecially(true));
4396 0 : freeF32(r1);
4397 0 : pushF32(r0);
4398 0 : }
4399 :
4400 : void
4401 0 : BaseCompiler::emitMinF64()
4402 : {
4403 0 : RegF64 r0, r1;
4404 0 : pop2xF64(&r0, &r1);
4405 : // Convert signaling NaN to quiet NaNs.
4406 : //
4407 : // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
4408 0 : ScratchF64 zero(*this);
4409 0 : masm.loadConstantDouble(0, zero);
4410 0 : masm.subDouble(zero, r0);
4411 0 : masm.subDouble(zero, r1);
4412 0 : masm.minDouble(r1, r0, HandleNaNSpecially(true));
4413 0 : freeF64(r1);
4414 0 : pushF64(r0);
4415 0 : }
4416 :
4417 : void
4418 0 : BaseCompiler::emitMaxF64()
4419 : {
4420 0 : RegF64 r0, r1;
4421 0 : pop2xF64(&r0, &r1);
4422 : // Convert signaling NaN to quiet NaNs.
4423 : //
4424 : // TODO / OPTIMIZE (bug 1316824): see comment in emitMinF32.
4425 0 : ScratchF64 zero(*this);
4426 0 : masm.loadConstantDouble(0, zero);
4427 0 : masm.subDouble(zero, r0);
4428 0 : masm.subDouble(zero, r1);
4429 0 : masm.maxDouble(r1, r0, HandleNaNSpecially(true));
4430 0 : freeF64(r1);
4431 0 : pushF64(r0);
4432 0 : }
4433 :
4434 : void
4435 0 : BaseCompiler::emitCopysignF32()
4436 : {
4437 0 : RegF32 r0, r1;
4438 0 : pop2xF32(&r0, &r1);
4439 0 : RegI32 i0 = needI32();
4440 0 : RegI32 i1 = needI32();
4441 0 : masm.moveFloat32ToGPR(r0, i0);
4442 0 : masm.moveFloat32ToGPR(r1, i1);
4443 0 : masm.and32(Imm32(INT32_MAX), i0);
4444 0 : masm.and32(Imm32(INT32_MIN), i1);
4445 0 : masm.or32(i1, i0);
4446 0 : masm.moveGPRToFloat32(i0, r0);
4447 0 : freeI32(i0);
4448 0 : freeI32(i1);
4449 0 : freeF32(r1);
4450 0 : pushF32(r0);
4451 0 : }
4452 :
4453 : void
4454 0 : BaseCompiler::emitCopysignF64()
4455 : {
4456 0 : RegF64 r0, r1;
4457 0 : pop2xF64(&r0, &r1);
4458 0 : RegI64 x0 = needI64();
4459 0 : RegI64 x1 = needI64();
4460 0 : reinterpretF64AsI64(r0, x0);
4461 0 : reinterpretF64AsI64(r1, x1);
4462 0 : masm.and64(Imm64(INT64_MAX), x0);
4463 0 : masm.and64(Imm64(INT64_MIN), x1);
4464 0 : masm.or64(x1, x0);
4465 0 : reinterpretI64AsF64(x0, r0);
4466 0 : freeI64(x0);
4467 0 : freeI64(x1);
4468 0 : freeF64(r1);
4469 0 : pushF64(r0);
4470 0 : }
4471 :
4472 : void
4473 0 : BaseCompiler::emitOrI32()
4474 : {
4475 : int32_t c;
4476 0 : if (popConstI32(&c)) {
4477 0 : RegI32 r = popI32();
4478 0 : masm.or32(Imm32(c), r);
4479 0 : pushI32(r);
4480 : } else {
4481 0 : RegI32 r0, r1;
4482 0 : pop2xI32(&r0, &r1);
4483 0 : masm.or32(r1, r0);
4484 0 : freeI32(r1);
4485 0 : pushI32(r0);
4486 : }
4487 0 : }
4488 :
4489 : void
4490 0 : BaseCompiler::emitOrI64()
4491 : {
4492 : int64_t c;
4493 0 : if (popConstI64(&c)) {
4494 0 : RegI64 r = popI64();
4495 0 : masm.or64(Imm64(c), r);
4496 0 : pushI64(r);
4497 : } else {
4498 0 : RegI64 r0, r1;
4499 0 : pop2xI64(&r0, &r1);
4500 0 : masm.or64(r1, r0);
4501 0 : freeI64(r1);
4502 0 : pushI64(r0);
4503 : }
4504 0 : }
4505 :
4506 : void
4507 0 : BaseCompiler::emitAndI32()
4508 : {
4509 : int32_t c;
4510 0 : if (popConstI32(&c)) {
4511 0 : RegI32 r = popI32();
4512 0 : masm.and32(Imm32(c), r);
4513 0 : pushI32(r);
4514 : } else {
4515 0 : RegI32 r0, r1;
4516 0 : pop2xI32(&r0, &r1);
4517 0 : masm.and32(r1, r0);
4518 0 : freeI32(r1);
4519 0 : pushI32(r0);
4520 : }
4521 0 : }
4522 :
4523 : void
4524 0 : BaseCompiler::emitAndI64()
4525 : {
4526 : int64_t c;
4527 0 : if (popConstI64(&c)) {
4528 0 : RegI64 r = popI64();
4529 0 : masm.and64(Imm64(c), r);
4530 0 : pushI64(r);
4531 : } else {
4532 0 : RegI64 r0, r1;
4533 0 : pop2xI64(&r0, &r1);
4534 0 : masm.and64(r1, r0);
4535 0 : freeI64(r1);
4536 0 : pushI64(r0);
4537 : }
4538 0 : }
4539 :
4540 : void
4541 0 : BaseCompiler::emitXorI32()
4542 : {
4543 : int32_t c;
4544 0 : if (popConstI32(&c)) {
4545 0 : RegI32 r = popI32();
4546 0 : masm.xor32(Imm32(c), r);
4547 0 : pushI32(r);
4548 : } else {
4549 0 : RegI32 r0, r1;
4550 0 : pop2xI32(&r0, &r1);
4551 0 : masm.xor32(r1, r0);
4552 0 : freeI32(r1);
4553 0 : pushI32(r0);
4554 : }
4555 0 : }
4556 :
4557 : void
4558 0 : BaseCompiler::emitXorI64()
4559 : {
4560 : int64_t c;
4561 0 : if (popConstI64(&c)) {
4562 0 : RegI64 r = popI64();
4563 0 : masm.xor64(Imm64(c), r);
4564 0 : pushI64(r);
4565 : } else {
4566 0 : RegI64 r0, r1;
4567 0 : pop2xI64(&r0, &r1);
4568 0 : masm.xor64(r1, r0);
4569 0 : freeI64(r1);
4570 0 : pushI64(r0);
4571 : }
4572 0 : }
4573 :
4574 : void
4575 0 : BaseCompiler::emitShlI32()
4576 : {
4577 : int32_t c;
4578 0 : if (popConstI32(&c)) {
4579 0 : RegI32 r = popI32();
4580 0 : masm.lshift32(Imm32(c & 31), r);
4581 0 : pushI32(r);
4582 : } else {
4583 0 : RegI32 r0, r1;
4584 0 : pop2xI32ForShiftOrRotate(&r0, &r1);
4585 0 : maskShiftCount32(r1);
4586 0 : masm.lshift32(r1, r0);
4587 0 : freeI32(r1);
4588 0 : pushI32(r0);
4589 : }
4590 0 : }
4591 :
4592 : void
4593 0 : BaseCompiler::emitShlI64()
4594 : {
4595 : int64_t c;
4596 0 : if (popConstI64(&c)) {
4597 0 : RegI64 r = popI64();
4598 0 : masm.lshift64(Imm32(c & 63), r);
4599 0 : pushI64(r);
4600 : } else {
4601 0 : RegI64 r0, r1;
4602 0 : pop2xI64ForShiftOrRotate(&r0, &r1);
4603 0 : masm.lshift64(lowPart(r1), r0);
4604 0 : freeI64(r1);
4605 0 : pushI64(r0);
4606 : }
4607 0 : }
4608 :
4609 : void
4610 0 : BaseCompiler::emitShrI32()
4611 : {
4612 : int32_t c;
4613 0 : if (popConstI32(&c)) {
4614 0 : RegI32 r = popI32();
4615 0 : masm.rshift32Arithmetic(Imm32(c & 31), r);
4616 0 : pushI32(r);
4617 : } else {
4618 0 : RegI32 r0, r1;
4619 0 : pop2xI32ForShiftOrRotate(&r0, &r1);
4620 0 : maskShiftCount32(r1);
4621 0 : masm.rshift32Arithmetic(r1, r0);
4622 0 : freeI32(r1);
4623 0 : pushI32(r0);
4624 : }
4625 0 : }
4626 :
4627 : void
4628 0 : BaseCompiler::emitShrI64()
4629 : {
4630 : int64_t c;
4631 0 : if (popConstI64(&c)) {
4632 0 : RegI64 r = popI64();
4633 0 : masm.rshift64Arithmetic(Imm32(c & 63), r);
4634 0 : pushI64(r);
4635 : } else {
4636 0 : RegI64 r0, r1;
4637 0 : pop2xI64ForShiftOrRotate(&r0, &r1);
4638 0 : masm.rshift64Arithmetic(lowPart(r1), r0);
4639 0 : freeI64(r1);
4640 0 : pushI64(r0);
4641 : }
4642 0 : }
4643 :
4644 : void
4645 0 : BaseCompiler::emitShrU32()
4646 : {
4647 : int32_t c;
4648 0 : if (popConstI32(&c)) {
4649 0 : RegI32 r = popI32();
4650 0 : masm.rshift32(Imm32(c & 31), r);
4651 0 : pushI32(r);
4652 : } else {
4653 0 : RegI32 r0, r1;
4654 0 : pop2xI32ForShiftOrRotate(&r0, &r1);
4655 0 : maskShiftCount32(r1);
4656 0 : masm.rshift32(r1, r0);
4657 0 : freeI32(r1);
4658 0 : pushI32(r0);
4659 : }
4660 0 : }
4661 :
4662 : void
4663 0 : BaseCompiler::emitShrU64()
4664 : {
4665 : int64_t c;
4666 0 : if (popConstI64(&c)) {
4667 0 : RegI64 r = popI64();
4668 0 : masm.rshift64(Imm32(c & 63), r);
4669 0 : pushI64(r);
4670 : } else {
4671 0 : RegI64 r0, r1;
4672 0 : pop2xI64ForShiftOrRotate(&r0, &r1);
4673 0 : masm.rshift64(lowPart(r1), r0);
4674 0 : freeI64(r1);
4675 0 : pushI64(r0);
4676 : }
4677 0 : }
4678 :
4679 : void
4680 0 : BaseCompiler::emitRotrI32()
4681 : {
4682 : int32_t c;
4683 0 : if (popConstI32(&c)) {
4684 0 : RegI32 r = popI32();
4685 0 : masm.rotateRight(Imm32(c & 31), r, r);
4686 0 : pushI32(r);
4687 : } else {
4688 0 : RegI32 r0, r1;
4689 0 : pop2xI32ForShiftOrRotate(&r0, &r1);
4690 0 : masm.rotateRight(r1, r0, r0);
4691 0 : freeI32(r1);
4692 0 : pushI32(r0);
4693 : }
4694 0 : }
4695 :
4696 : void
4697 0 : BaseCompiler::emitRotrI64()
4698 : {
4699 : int64_t c;
4700 0 : if (popConstI64(&c)) {
4701 0 : RegI64 r = popI64();
4702 0 : RegI32 temp;
4703 0 : if (rotate64NeedsTemp())
4704 0 : temp = needI32();
4705 0 : masm.rotateRight64(Imm32(c & 63), r, r, temp);
4706 0 : if (temp != Register::Invalid())
4707 0 : freeI32(temp);
4708 0 : pushI64(r);
4709 : } else {
4710 0 : RegI64 r0, r1;
4711 0 : pop2xI64ForShiftOrRotate(&r0, &r1);
4712 0 : masm.rotateRight64(lowPart(r1), r0, r0, maybeHighPart(r1));
4713 0 : freeI64(r1);
4714 0 : pushI64(r0);
4715 : }
4716 0 : }
4717 :
4718 : void
4719 0 : BaseCompiler::emitRotlI32()
4720 : {
4721 : int32_t c;
4722 0 : if (popConstI32(&c)) {
4723 0 : RegI32 r = popI32();
4724 0 : masm.rotateLeft(Imm32(c & 31), r, r);
4725 0 : pushI32(r);
4726 : } else {
4727 0 : RegI32 r0, r1;
4728 0 : pop2xI32ForShiftOrRotate(&r0, &r1);
4729 0 : masm.rotateLeft(r1, r0, r0);
4730 0 : freeI32(r1);
4731 0 : pushI32(r0);
4732 : }
4733 0 : }
4734 :
4735 : void
4736 0 : BaseCompiler::emitRotlI64()
4737 : {
4738 : int64_t c;
4739 0 : if (popConstI64(&c)) {
4740 0 : RegI64 r = popI64();
4741 0 : RegI32 temp;
4742 0 : if (rotate64NeedsTemp())
4743 0 : temp = needI32();
4744 0 : masm.rotateLeft64(Imm32(c & 63), r, r, temp);
4745 0 : if (temp != Register::Invalid())
4746 0 : freeI32(temp);
4747 0 : pushI64(r);
4748 : } else {
4749 0 : RegI64 r0, r1;
4750 0 : pop2xI64ForShiftOrRotate(&r0, &r1);
4751 0 : masm.rotateLeft64(lowPart(r1), r0, r0, maybeHighPart(r1));
4752 0 : freeI64(r1);
4753 0 : pushI64(r0);
4754 : }
4755 0 : }
4756 :
4757 : void
4758 0 : BaseCompiler::emitEqzI32()
4759 : {
4760 0 : if (sniffConditionalControlEqz(ValType::I32))
4761 0 : return;
4762 :
4763 0 : RegI32 r0 = popI32();
4764 0 : masm.cmp32Set(Assembler::Equal, r0, Imm32(0), r0);
4765 0 : pushI32(r0);
4766 : }
4767 :
4768 : void
4769 0 : BaseCompiler::emitEqzI64()
4770 : {
4771 0 : if (sniffConditionalControlEqz(ValType::I64))
4772 0 : return;
4773 :
4774 0 : RegI64 r0 = popI64();
4775 0 : RegI32 i0 = fromI64(r0);
4776 0 : eqz64(r0, i0);
4777 0 : freeI64Except(r0, i0);
4778 0 : pushI32(i0);
4779 : }
4780 :
4781 : void
4782 0 : BaseCompiler::emitClzI32()
4783 : {
4784 0 : RegI32 r0 = popI32();
4785 0 : masm.clz32(r0, r0, IsKnownNotZero(false));
4786 0 : pushI32(r0);
4787 0 : }
4788 :
4789 : void
4790 0 : BaseCompiler::emitClzI64()
4791 : {
4792 0 : RegI64 r0 = popI64();
4793 0 : masm.clz64(r0, lowPart(r0));
4794 0 : maybeClearHighPart(r0);
4795 0 : pushI64(r0);
4796 0 : }
4797 :
4798 : void
4799 0 : BaseCompiler::emitCtzI32()
4800 : {
4801 0 : RegI32 r0 = popI32();
4802 0 : masm.ctz32(r0, r0, IsKnownNotZero(false));
4803 0 : pushI32(r0);
4804 0 : }
4805 :
4806 : void
4807 0 : BaseCompiler::emitCtzI64()
4808 : {
4809 0 : RegI64 r0 = popI64();
4810 0 : masm.ctz64(r0, lowPart(r0));
4811 0 : maybeClearHighPart(r0);
4812 0 : pushI64(r0);
4813 0 : }
4814 :
4815 : void
4816 0 : BaseCompiler::emitPopcntI32()
4817 : {
4818 0 : RegI32 r0 = popI32();
4819 0 : if (popcnt32NeedsTemp()) {
4820 0 : RegI32 tmp = needI32();
4821 0 : masm.popcnt32(r0, r0, tmp);
4822 0 : freeI32(tmp);
4823 : } else {
4824 0 : masm.popcnt32(r0, r0, invalidI32());
4825 : }
4826 0 : pushI32(r0);
4827 0 : }
4828 :
4829 : void
4830 0 : BaseCompiler::emitPopcntI64()
4831 : {
4832 0 : RegI64 r0 = popI64();
4833 0 : if (popcnt64NeedsTemp()) {
4834 0 : RegI32 tmp = needI32();
4835 0 : masm.popcnt64(r0, r0, tmp);
4836 0 : freeI32(tmp);
4837 : } else {
4838 0 : masm.popcnt64(r0, r0, invalidI32());
4839 : }
4840 0 : pushI64(r0);
4841 0 : }
4842 :
4843 : void
4844 0 : BaseCompiler::emitAbsF32()
4845 : {
4846 0 : RegF32 r0 = popF32();
4847 0 : masm.absFloat32(r0, r0);
4848 0 : pushF32(r0);
4849 0 : }
4850 :
4851 : void
4852 0 : BaseCompiler::emitAbsF64()
4853 : {
4854 0 : RegF64 r0 = popF64();
4855 0 : masm.absDouble(r0, r0);
4856 0 : pushF64(r0);
4857 0 : }
4858 :
4859 : void
4860 0 : BaseCompiler::emitNegateF32()
4861 : {
4862 0 : RegF32 r0 = popF32();
4863 0 : masm.negateFloat(r0);
4864 0 : pushF32(r0);
4865 0 : }
4866 :
4867 : void
4868 0 : BaseCompiler::emitNegateF64()
4869 : {
4870 0 : RegF64 r0 = popF64();
4871 0 : masm.negateDouble(r0);
4872 0 : pushF64(r0);
4873 0 : }
4874 :
4875 : void
4876 0 : BaseCompiler::emitSqrtF32()
4877 : {
4878 0 : RegF32 r0 = popF32();
4879 0 : masm.sqrtFloat32(r0, r0);
4880 0 : pushF32(r0);
4881 0 : }
4882 :
4883 : void
4884 0 : BaseCompiler::emitSqrtF64()
4885 : {
4886 0 : RegF64 r0 = popF64();
4887 0 : masm.sqrtDouble(r0, r0);
4888 0 : pushF64(r0);
4889 0 : }
4890 :
4891 : template<bool isUnsigned>
4892 : bool
4893 0 : BaseCompiler::emitTruncateF32ToI32()
4894 : {
4895 0 : RegF32 r0 = popF32();
4896 0 : RegI32 i0 = needI32();
4897 0 : if (!truncateF32ToI32(r0, i0, isUnsigned))
4898 0 : return false;
4899 0 : freeF32(r0);
4900 0 : pushI32(i0);
4901 0 : return true;
4902 : }
4903 :
4904 : template<bool isUnsigned>
4905 : bool
4906 0 : BaseCompiler::emitTruncateF64ToI32()
4907 : {
4908 0 : RegF64 r0 = popF64();
4909 0 : RegI32 i0 = needI32();
4910 0 : if (!truncateF64ToI32(r0, i0, isUnsigned))
4911 0 : return false;
4912 0 : freeF64(r0);
4913 0 : pushI32(i0);
4914 0 : return true;
4915 : }
4916 :
4917 : #ifndef FLOAT_TO_I64_CALLOUT
4918 : template<bool isUnsigned>
4919 : bool
4920 0 : BaseCompiler::emitTruncateF32ToI64()
4921 : {
4922 0 : RegF32 r0 = popF32();
4923 0 : RegI64 x0 = needI64();
4924 : if (isUnsigned) {
4925 0 : RegF64 tmp = needF64();
4926 0 : if (!truncateF32ToI64(r0, x0, isUnsigned, tmp))
4927 0 : return false;
4928 0 : freeF64(tmp);
4929 : } else {
4930 0 : if (!truncateF32ToI64(r0, x0, isUnsigned, invalidF64()))
4931 0 : return false;
4932 : }
4933 0 : freeF32(r0);
4934 0 : pushI64(x0);
4935 0 : return true;
4936 : }
4937 :
4938 : template<bool isUnsigned>
4939 : bool
4940 0 : BaseCompiler::emitTruncateF64ToI64()
4941 : {
4942 0 : RegF64 r0 = popF64();
4943 0 : RegI64 x0 = needI64();
4944 : if (isUnsigned) {
4945 0 : RegF64 tmp = needF64();
4946 0 : if (!truncateF64ToI64(r0, x0, isUnsigned, tmp))
4947 0 : return false;
4948 0 : freeF64(tmp);
4949 : } else {
4950 0 : if (!truncateF64ToI64(r0, x0, isUnsigned, invalidF64()))
4951 0 : return false;
4952 : }
4953 0 : freeF64(r0);
4954 0 : pushI64(x0);
4955 0 : return true;
4956 : }
4957 : #endif // FLOAT_TO_I64_CALLOUT
4958 :
4959 : void
4960 0 : BaseCompiler::emitWrapI64ToI32()
4961 : {
4962 0 : RegI64 r0 = popI64();
4963 0 : RegI32 i0 = fromI64(r0);
4964 0 : wrapI64ToI32(r0, i0);
4965 0 : freeI64Except(r0, i0);
4966 0 : pushI32(i0);
4967 0 : }
4968 :
4969 : void
4970 0 : BaseCompiler::emitExtendI32ToI64()
4971 : {
4972 0 : RegI64 x0 = popI32ForSignExtendI64();
4973 0 : RegI32 r0 = RegI32(lowPart(x0));
4974 0 : signExtendI32ToI64(r0, x0);
4975 0 : pushI64(x0);
4976 : // Note: no need to free r0, since it is part of x0
4977 0 : }
4978 :
4979 : void
4980 0 : BaseCompiler::emitExtendU32ToI64()
4981 : {
4982 0 : RegI32 r0 = popI32();
4983 0 : RegI64 x0 = widenI32(r0);
4984 0 : extendU32ToI64(r0, x0);
4985 0 : pushI64(x0);
4986 : // Note: no need to free r0, since it is part of x0
4987 0 : }
4988 :
4989 : void
4990 0 : BaseCompiler::emitReinterpretF32AsI32()
4991 : {
4992 0 : RegF32 r0 = popF32();
4993 0 : RegI32 i0 = needI32();
4994 0 : masm.moveFloat32ToGPR(r0, i0);
4995 0 : freeF32(r0);
4996 0 : pushI32(i0);
4997 0 : }
4998 :
4999 : void
5000 0 : BaseCompiler::emitReinterpretF64AsI64()
5001 : {
5002 0 : RegF64 r0 = popF64();
5003 0 : RegI64 x0 = needI64();
5004 0 : reinterpretF64AsI64(r0, x0);
5005 0 : freeF64(r0);
5006 0 : pushI64(x0);
5007 0 : }
5008 :
5009 : void
5010 0 : BaseCompiler::emitConvertF64ToF32()
5011 : {
5012 0 : RegF64 r0 = popF64();
5013 0 : RegF32 f0 = needF32();
5014 0 : masm.convertDoubleToFloat32(r0, f0);
5015 0 : freeF64(r0);
5016 0 : pushF32(f0);
5017 0 : }
5018 :
5019 : void
5020 0 : BaseCompiler::emitConvertI32ToF32()
5021 : {
5022 0 : RegI32 r0 = popI32();
5023 0 : RegF32 f0 = needF32();
5024 0 : masm.convertInt32ToFloat32(r0, f0);
5025 0 : freeI32(r0);
5026 0 : pushF32(f0);
5027 0 : }
5028 :
5029 : void
5030 0 : BaseCompiler::emitConvertU32ToF32()
5031 : {
5032 0 : RegI32 r0 = popI32();
5033 0 : RegF32 f0 = needF32();
5034 0 : masm.convertUInt32ToFloat32(r0, f0);
5035 0 : freeI32(r0);
5036 0 : pushF32(f0);
5037 0 : }
5038 :
5039 : #ifndef I64_TO_FLOAT_CALLOUT
5040 : void
5041 0 : BaseCompiler::emitConvertI64ToF32()
5042 : {
5043 0 : RegI64 r0 = popI64();
5044 0 : RegF32 f0 = needF32();
5045 0 : convertI64ToF32(r0, IsUnsigned(false), f0, RegI32());
5046 0 : freeI64(r0);
5047 0 : pushF32(f0);
5048 0 : }
5049 :
5050 : void
5051 0 : BaseCompiler::emitConvertU64ToF32()
5052 : {
5053 0 : RegI64 r0 = popI64();
5054 0 : RegF32 f0 = needF32();
5055 0 : RegI32 temp;
5056 0 : if (convertI64ToFloatNeedsTemp(ValType::F32, IsUnsigned(true)))
5057 0 : temp = needI32();
5058 0 : convertI64ToF32(r0, IsUnsigned(true), f0, temp);
5059 0 : if (temp != Register::Invalid())
5060 0 : freeI32(temp);
5061 0 : freeI64(r0);
5062 0 : pushF32(f0);
5063 0 : }
5064 : #endif
5065 :
5066 : void
5067 0 : BaseCompiler::emitConvertF32ToF64()
5068 : {
5069 0 : RegF32 r0 = popF32();
5070 0 : RegF64 d0 = needF64();
5071 0 : masm.convertFloat32ToDouble(r0, d0);
5072 0 : freeF32(r0);
5073 0 : pushF64(d0);
5074 0 : }
5075 :
5076 : void
5077 0 : BaseCompiler::emitConvertI32ToF64()
5078 : {
5079 0 : RegI32 r0 = popI32();
5080 0 : RegF64 d0 = needF64();
5081 0 : masm.convertInt32ToDouble(r0, d0);
5082 0 : freeI32(r0);
5083 0 : pushF64(d0);
5084 0 : }
5085 :
5086 : void
5087 0 : BaseCompiler::emitConvertU32ToF64()
5088 : {
5089 0 : RegI32 r0 = popI32();
5090 0 : RegF64 d0 = needF64();
5091 0 : masm.convertUInt32ToDouble(r0, d0);
5092 0 : freeI32(r0);
5093 0 : pushF64(d0);
5094 0 : }
5095 :
5096 : #ifndef I64_TO_FLOAT_CALLOUT
5097 : void
5098 0 : BaseCompiler::emitConvertI64ToF64()
5099 : {
5100 0 : RegI64 r0 = popI64();
5101 0 : RegF64 d0 = needF64();
5102 0 : convertI64ToF64(r0, IsUnsigned(false), d0, RegI32());
5103 0 : freeI64(r0);
5104 0 : pushF64(d0);
5105 0 : }
5106 :
5107 : void
5108 0 : BaseCompiler::emitConvertU64ToF64()
5109 : {
5110 0 : RegI64 r0 = popI64();
5111 0 : RegF64 d0 = needF64();
5112 0 : RegI32 temp;
5113 0 : if (convertI64ToFloatNeedsTemp(ValType::F64, IsUnsigned(true)))
5114 0 : temp = needI32();
5115 0 : convertI64ToF64(r0, IsUnsigned(true), d0, temp);
5116 0 : if (temp != Register::Invalid())
5117 0 : freeI32(temp);
5118 0 : freeI64(r0);
5119 0 : pushF64(d0);
5120 0 : }
5121 : #endif // I64_TO_FLOAT_CALLOUT
5122 :
5123 : void
5124 0 : BaseCompiler::emitReinterpretI32AsF32()
5125 : {
5126 0 : RegI32 r0 = popI32();
5127 0 : RegF32 f0 = needF32();
5128 0 : masm.moveGPRToFloat32(r0, f0);
5129 0 : freeI32(r0);
5130 0 : pushF32(f0);
5131 0 : }
5132 :
5133 : void
5134 0 : BaseCompiler::emitReinterpretI64AsF64()
5135 : {
5136 0 : RegI64 r0 = popI64();
5137 0 : RegF64 d0 = needF64();
5138 0 : reinterpretI64AsF64(r0, d0);
5139 0 : freeI64(r0);
5140 0 : pushF64(d0);
5141 0 : }
5142 :
5143 : template<typename Cond>
5144 : bool
5145 0 : BaseCompiler::sniffConditionalControlCmp(Cond compareOp, ValType operandType)
5146 : {
5147 0 : MOZ_ASSERT(latentOp_ == LatentOp::None, "Latent comparison state not properly reset");
5148 :
5149 0 : OpBytes op;
5150 0 : iter_.peekOp(&op);
5151 0 : switch (op.b0) {
5152 : case uint16_t(Op::Select):
5153 : #ifdef JS_CODEGEN_X86
5154 : // On x86, with only 5 available registers, a latent i64 binary
5155 : // comparison takes 4 leaving only 1 which is not enough for select.
5156 : if (operandType == ValType::I64)
5157 : return false;
5158 : #endif
5159 : MOZ_FALLTHROUGH;
5160 : case uint16_t(Op::BrIf):
5161 : case uint16_t(Op::If):
5162 0 : setLatentCompare(compareOp, operandType);
5163 0 : return true;
5164 : default:
5165 0 : return false;
5166 : }
5167 : }
5168 :
5169 : bool
5170 0 : BaseCompiler::sniffConditionalControlEqz(ValType operandType)
5171 : {
5172 0 : MOZ_ASSERT(latentOp_ == LatentOp::None, "Latent comparison state not properly reset");
5173 :
5174 0 : OpBytes op;
5175 0 : iter_.peekOp(&op);
5176 0 : switch (op.b0) {
5177 : case uint16_t(Op::BrIf):
5178 : case uint16_t(Op::Select):
5179 : case uint16_t(Op::If):
5180 0 : setLatentEqz(operandType);
5181 0 : return true;
5182 : default:
5183 0 : return false;
5184 : }
5185 : }
5186 :
5187 : void
5188 0 : BaseCompiler::emitBranchSetup(BranchState* b)
5189 : {
5190 0 : maybeReserveJoinReg(b->resultType);
5191 :
5192 : // Set up fields so that emitBranchPerform() need not switch on latentOp_.
5193 0 : switch (latentOp_) {
5194 : case LatentOp::None: {
5195 0 : latentIntCmp_ = Assembler::NotEqual;
5196 0 : latentType_ = ValType::I32;
5197 0 : b->i32.lhs = popI32();
5198 0 : b->i32.rhsImm = true;
5199 0 : b->i32.imm = 0;
5200 0 : break;
5201 : }
5202 : case LatentOp::Compare: {
5203 0 : switch (latentType_) {
5204 : case ValType::I32: {
5205 0 : if (popConstI32(&b->i32.imm)) {
5206 0 : b->i32.lhs = popI32();
5207 0 : b->i32.rhsImm = true;
5208 : } else {
5209 0 : pop2xI32(&b->i32.lhs, &b->i32.rhs);
5210 0 : b->i32.rhsImm = false;
5211 : }
5212 0 : break;
5213 : }
5214 : case ValType::I64: {
5215 0 : pop2xI64(&b->i64.lhs, &b->i64.rhs);
5216 0 : b->i64.rhsImm = false;
5217 0 : break;
5218 : }
5219 : case ValType::F32: {
5220 0 : pop2xF32(&b->f32.lhs, &b->f32.rhs);
5221 0 : break;
5222 : }
5223 : case ValType::F64: {
5224 0 : pop2xF64(&b->f64.lhs, &b->f64.rhs);
5225 0 : break;
5226 : }
5227 : default: {
5228 0 : MOZ_CRASH("Unexpected type for LatentOp::Compare");
5229 : }
5230 : }
5231 0 : break;
5232 : }
5233 : case LatentOp::Eqz: {
5234 0 : switch (latentType_) {
5235 : case ValType::I32: {
5236 0 : latentIntCmp_ = Assembler::Equal;
5237 0 : b->i32.lhs = popI32();
5238 0 : b->i32.rhsImm = true;
5239 0 : b->i32.imm = 0;
5240 0 : break;
5241 : }
5242 : case ValType::I64: {
5243 0 : latentIntCmp_ = Assembler::Equal;
5244 0 : b->i64.lhs = popI64();
5245 0 : b->i64.rhsImm = true;
5246 0 : b->i64.imm = 0;
5247 0 : break;
5248 : }
5249 : default: {
5250 0 : MOZ_CRASH("Unexpected type for LatentOp::Eqz");
5251 : }
5252 : }
5253 0 : break;
5254 : }
5255 : }
5256 :
5257 0 : maybeUnreserveJoinReg(b->resultType);
5258 0 : }
5259 :
5260 : void
5261 0 : BaseCompiler::emitBranchPerform(BranchState* b)
5262 : {
5263 0 : switch (latentType_) {
5264 : case ValType::I32: {
5265 0 : if (b->i32.rhsImm) {
5266 0 : jumpConditionalWithJoinReg(b, latentIntCmp_, b->i32.lhs, Imm32(b->i32.imm));
5267 : } else {
5268 0 : jumpConditionalWithJoinReg(b, latentIntCmp_, b->i32.lhs, b->i32.rhs);
5269 0 : freeI32(b->i32.rhs);
5270 : }
5271 0 : freeI32(b->i32.lhs);
5272 0 : break;
5273 : }
5274 : case ValType::I64: {
5275 0 : if (b->i64.rhsImm) {
5276 0 : jumpConditionalWithJoinReg(b, latentIntCmp_, b->i64.lhs, Imm64(b->i64.imm));
5277 : } else {
5278 0 : jumpConditionalWithJoinReg(b, latentIntCmp_, b->i64.lhs, b->i64.rhs);
5279 0 : freeI64(b->i64.rhs);
5280 : }
5281 0 : freeI64(b->i64.lhs);
5282 0 : break;
5283 : }
5284 : case ValType::F32: {
5285 0 : jumpConditionalWithJoinReg(b, latentDoubleCmp_, b->f32.lhs, b->f32.rhs);
5286 0 : freeF32(b->f32.lhs);
5287 0 : freeF32(b->f32.rhs);
5288 0 : break;
5289 : }
5290 : case ValType::F64: {
5291 0 : jumpConditionalWithJoinReg(b, latentDoubleCmp_, b->f64.lhs, b->f64.rhs);
5292 0 : freeF64(b->f64.lhs);
5293 0 : freeF64(b->f64.rhs);
5294 0 : break;
5295 : }
5296 : default: {
5297 0 : MOZ_CRASH("Unexpected type for LatentOp::Compare");
5298 : }
5299 : }
5300 0 : resetLatentOp();
5301 0 : }
5302 :
5303 : // For blocks and loops and ifs:
5304 : //
5305 : // - Sync the value stack before going into the block in order to simplify exit
5306 : // from the block: all exits from the block can assume that there are no
5307 : // live registers except the one carrying the exit value.
5308 : // - The block can accumulate a number of dead values on the stacks, so when
5309 : // branching out of the block or falling out at the end be sure to
5310 : // pop the appropriate stacks back to where they were on entry, while
5311 : // preserving the exit value.
5312 : // - A continue branch in a loop is much like an exit branch, but the branch
5313 : // value must not be preserved.
5314 : // - The exit value is always in a designated join register (type dependent).
5315 :
5316 : bool
5317 0 : BaseCompiler::emitBlock()
5318 : {
5319 0 : if (!iter_.readBlock())
5320 0 : return false;
5321 :
5322 0 : if (!deadCode_)
5323 0 : sync(); // Simplifies branching out from block
5324 :
5325 0 : initControl(controlItem());
5326 :
5327 0 : return true;
5328 : }
5329 :
5330 : void
5331 0 : BaseCompiler::endBlock(ExprType type)
5332 : {
5333 0 : Control& block = controlItem();
5334 :
5335 : // Save the value.
5336 0 : AnyReg r;
5337 0 : if (!deadCode_) {
5338 0 : r = popJoinRegUnlessVoid(type);
5339 0 : block.bceSafeOnExit &= bceSafe_;
5340 : }
5341 :
5342 : // Leave the block.
5343 0 : popStackOnBlockExit(block.framePushed);
5344 0 : popValueStackTo(block.stackSize);
5345 :
5346 : // Bind after cleanup: branches out will have popped the stack.
5347 0 : if (block.label.used()) {
5348 0 : masm.bind(&block.label);
5349 : // No value was provided by the fallthrough but the branch out will
5350 : // have stored one in joinReg, so capture that.
5351 0 : if (deadCode_)
5352 0 : r = captureJoinRegUnlessVoid(type);
5353 0 : deadCode_ = false;
5354 : }
5355 :
5356 0 : bceSafe_ = block.bceSafeOnExit;
5357 :
5358 : // Retain the value stored in joinReg by all paths, if there are any.
5359 0 : if (!deadCode_)
5360 0 : pushJoinRegUnlessVoid(r);
5361 0 : }
5362 :
5363 : bool
5364 0 : BaseCompiler::emitLoop()
5365 : {
5366 0 : if (!iter_.readLoop())
5367 0 : return false;
5368 :
5369 0 : if (!deadCode_)
5370 0 : sync(); // Simplifies branching out from block
5371 :
5372 0 : initControl(controlItem());
5373 0 : bceSafe_ = 0;
5374 :
5375 0 : if (!deadCode_) {
5376 0 : masm.nopAlign(CodeAlignment);
5377 0 : masm.bind(&controlItem(0).label);
5378 0 : addInterruptCheck();
5379 : }
5380 :
5381 0 : return true;
5382 : }
5383 :
5384 : void
5385 0 : BaseCompiler::endLoop(ExprType type)
5386 : {
5387 0 : Control& block = controlItem();
5388 :
5389 0 : AnyReg r;
5390 0 : if (!deadCode_) {
5391 0 : r = popJoinRegUnlessVoid(type);
5392 : // block.bceSafeOnExit need not be updated because it won't be used for
5393 : // the fallthrough path.
5394 : }
5395 :
5396 0 : popStackOnBlockExit(block.framePushed);
5397 0 : popValueStackTo(block.stackSize);
5398 :
5399 : // bceSafe_ stays the same along the fallthrough path because branches to
5400 : // loops branch to the top.
5401 :
5402 : // Retain the value stored in joinReg by all paths.
5403 0 : if (!deadCode_)
5404 0 : pushJoinRegUnlessVoid(r);
5405 0 : }
5406 :
5407 : // The bodies of the "then" and "else" arms can be arbitrary sequences
5408 : // of expressions, they push control and increment the nesting and can
5409 : // even be targeted by jumps. A branch to the "if" block branches to
5410 : // the exit of the if, ie, it's like "break". Consider:
5411 : //
5412 : // (func (result i32)
5413 : // (if (i32.const 1)
5414 : // (begin (br 1) (unreachable))
5415 : // (begin (unreachable)))
5416 : // (i32.const 1))
5417 : //
5418 : // The branch causes neither of the unreachable expressions to be
5419 : // evaluated.
5420 :
5421 : bool
5422 0 : BaseCompiler::emitIf()
5423 : {
5424 : Nothing unused_cond;
5425 0 : if (!iter_.readIf(&unused_cond))
5426 0 : return false;
5427 :
5428 0 : BranchState b(&controlItem().otherLabel, BranchState::NoPop, InvertBranch(true));
5429 0 : if (!deadCode_) {
5430 0 : emitBranchSetup(&b);
5431 0 : sync();
5432 : } else {
5433 0 : resetLatentOp();
5434 : }
5435 :
5436 0 : initControl(controlItem());
5437 :
5438 0 : if (!deadCode_)
5439 0 : emitBranchPerform(&b);
5440 :
5441 0 : return true;
5442 : }
5443 :
5444 : void
5445 0 : BaseCompiler::endIfThen()
5446 : {
5447 0 : Control& ifThen = controlItem();
5448 :
5449 0 : popStackOnBlockExit(ifThen.framePushed);
5450 0 : popValueStackTo(ifThen.stackSize);
5451 :
5452 0 : if (ifThen.otherLabel.used())
5453 0 : masm.bind(&ifThen.otherLabel);
5454 :
5455 0 : if (ifThen.label.used())
5456 0 : masm.bind(&ifThen.label);
5457 :
5458 0 : if (!deadCode_)
5459 0 : ifThen.bceSafeOnExit &= bceSafe_;
5460 :
5461 0 : deadCode_ = ifThen.deadOnArrival;
5462 :
5463 0 : bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry;
5464 0 : }
5465 :
5466 : bool
5467 0 : BaseCompiler::emitElse()
5468 : {
5469 : ExprType thenType;
5470 : Nothing unused_thenValue;
5471 :
5472 0 : if (!iter_.readElse(&thenType, &unused_thenValue))
5473 0 : return false;
5474 :
5475 0 : Control& ifThenElse = controlItem(0);
5476 :
5477 : // See comment in endIfThenElse, below.
5478 :
5479 : // Exit the "then" branch.
5480 :
5481 0 : ifThenElse.deadThenBranch = deadCode_;
5482 :
5483 0 : AnyReg r;
5484 0 : if (!deadCode_)
5485 0 : r = popJoinRegUnlessVoid(thenType);
5486 :
5487 0 : popStackOnBlockExit(ifThenElse.framePushed);
5488 0 : popValueStackTo(ifThenElse.stackSize);
5489 :
5490 0 : if (!deadCode_)
5491 0 : masm.jump(&ifThenElse.label);
5492 :
5493 0 : if (ifThenElse.otherLabel.used())
5494 0 : masm.bind(&ifThenElse.otherLabel);
5495 :
5496 : // Reset to the "else" branch.
5497 :
5498 0 : if (!deadCode_) {
5499 0 : freeJoinRegUnlessVoid(r);
5500 0 : ifThenElse.bceSafeOnExit &= bceSafe_;
5501 : }
5502 :
5503 0 : deadCode_ = ifThenElse.deadOnArrival;
5504 0 : bceSafe_ = ifThenElse.bceSafeOnEntry;
5505 :
5506 0 : return true;
5507 : }
5508 :
5509 : void
5510 0 : BaseCompiler::endIfThenElse(ExprType type)
5511 : {
5512 0 : Control& ifThenElse = controlItem();
5513 :
5514 : // The expression type is not a reliable guide to what we'll find
5515 : // on the stack, we could have (if E (i32.const 1) (unreachable))
5516 : // in which case the "else" arm is AnyType but the type of the
5517 : // full expression is I32. So restore whatever's there, not what
5518 : // we want to find there. The "then" arm has the same constraint.
5519 :
5520 0 : AnyReg r;
5521 :
5522 0 : if (!deadCode_) {
5523 0 : r = popJoinRegUnlessVoid(type);
5524 0 : ifThenElse.bceSafeOnExit &= bceSafe_;
5525 : }
5526 :
5527 0 : popStackOnBlockExit(ifThenElse.framePushed);
5528 0 : popValueStackTo(ifThenElse.stackSize);
5529 :
5530 0 : if (ifThenElse.label.used())
5531 0 : masm.bind(&ifThenElse.label);
5532 :
5533 0 : bool joinLive = !ifThenElse.deadOnArrival &&
5534 0 : (!ifThenElse.deadThenBranch || !deadCode_ || ifThenElse.label.bound());
5535 :
5536 0 : if (joinLive) {
5537 : // No value was provided by the "then" path but capture the one
5538 : // provided by the "else" path.
5539 0 : if (deadCode_)
5540 0 : r = captureJoinRegUnlessVoid(type);
5541 0 : deadCode_ = false;
5542 : }
5543 :
5544 0 : bceSafe_ = ifThenElse.bceSafeOnExit;
5545 :
5546 0 : if (!deadCode_)
5547 0 : pushJoinRegUnlessVoid(r);
5548 0 : }
5549 :
5550 : bool
5551 0 : BaseCompiler::emitEnd()
5552 : {
5553 : LabelKind kind;
5554 : ExprType type;
5555 : Nothing unused_value;
5556 0 : if (!iter_.readEnd(&kind, &type, &unused_value))
5557 0 : return false;
5558 :
5559 0 : switch (kind) {
5560 0 : case LabelKind::Block: endBlock(type); break;
5561 0 : case LabelKind::Loop: endLoop(type); break;
5562 0 : case LabelKind::Then: endIfThen(); break;
5563 0 : case LabelKind::Else: endIfThenElse(type); break;
5564 : }
5565 :
5566 0 : iter_.popEnd();
5567 :
5568 0 : return true;
5569 : }
5570 :
5571 : bool
5572 0 : BaseCompiler::emitBr()
5573 : {
5574 : uint32_t relativeDepth;
5575 : ExprType type;
5576 : Nothing unused_value;
5577 0 : if (!iter_.readBr(&relativeDepth, &type, &unused_value))
5578 0 : return false;
5579 :
5580 0 : if (deadCode_)
5581 0 : return true;
5582 :
5583 0 : Control& target = controlItem(relativeDepth);
5584 0 : target.bceSafeOnExit &= bceSafe_;
5585 :
5586 : // Save any value in the designated join register, where the
5587 : // normal block exit code will also leave it.
5588 :
5589 0 : AnyReg r = popJoinRegUnlessVoid(type);
5590 :
5591 0 : popStackBeforeBranch(target.framePushed);
5592 0 : masm.jump(&target.label);
5593 :
5594 : // The register holding the join value is free for the remainder
5595 : // of this block.
5596 :
5597 0 : freeJoinRegUnlessVoid(r);
5598 :
5599 0 : deadCode_ = true;
5600 :
5601 0 : return true;
5602 : }
5603 :
5604 : bool
5605 0 : BaseCompiler::emitBrIf()
5606 : {
5607 : uint32_t relativeDepth;
5608 : ExprType type;
5609 : Nothing unused_value, unused_condition;
5610 0 : if (!iter_.readBrIf(&relativeDepth, &type, &unused_value, &unused_condition))
5611 0 : return false;
5612 :
5613 0 : if (deadCode_) {
5614 0 : resetLatentOp();
5615 0 : return true;
5616 : }
5617 :
5618 0 : Control& target = controlItem(relativeDepth);
5619 0 : target.bceSafeOnExit &= bceSafe_;
5620 :
5621 0 : BranchState b(&target.label, target.framePushed, InvertBranch(false), type);
5622 0 : emitBranchSetup(&b);
5623 0 : emitBranchPerform(&b);
5624 :
5625 0 : return true;
5626 : }
5627 :
5628 : bool
5629 0 : BaseCompiler::emitBrTable()
5630 : {
5631 0 : Uint32Vector depths;
5632 : uint32_t defaultDepth;
5633 : ExprType branchValueType;
5634 : Nothing unused_value, unused_index;
5635 0 : if (!iter_.readBrTable(&depths, &defaultDepth, &branchValueType, &unused_value, &unused_index))
5636 0 : return false;
5637 :
5638 0 : if (deadCode_)
5639 0 : return true;
5640 :
5641 : // Don't use joinReg for rc
5642 0 : maybeReserveJoinRegI(branchValueType);
5643 :
5644 : // Table switch value always on top.
5645 0 : RegI32 rc = popI32();
5646 :
5647 0 : maybeUnreserveJoinRegI(branchValueType);
5648 :
5649 0 : AnyReg r = popJoinRegUnlessVoid(branchValueType);
5650 :
5651 0 : Label dispatchCode;
5652 0 : masm.branch32(Assembler::Below, rc, Imm32(depths.length()), &dispatchCode);
5653 :
5654 : // This is the out-of-range stub. rc is dead here but we don't need it.
5655 :
5656 0 : popStackBeforeBranch(controlItem(defaultDepth).framePushed);
5657 0 : controlItem(defaultDepth).bceSafeOnExit &= bceSafe_;
5658 0 : masm.jump(&controlItem(defaultDepth).label);
5659 :
5660 : // Emit stubs. rc is dead in all of these but we don't need it.
5661 : //
5662 : // The labels in the vector are in the TempAllocator and will
5663 : // be freed by and by.
5664 : //
5665 : // TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we
5666 : // can, don't emit an intermediate stub.
5667 :
5668 0 : LabelVector stubs;
5669 0 : if (!stubs.reserve(depths.length()))
5670 0 : return false;
5671 :
5672 0 : for (uint32_t depth : depths) {
5673 0 : stubs.infallibleEmplaceBack(NonAssertingLabel());
5674 0 : masm.bind(&stubs.back());
5675 0 : popStackBeforeBranch(controlItem(depth).framePushed);
5676 0 : controlItem(depth).bceSafeOnExit &= bceSafe_;
5677 0 : masm.jump(&controlItem(depth).label);
5678 : }
5679 :
5680 : // Emit table.
5681 :
5682 0 : Label theTable;
5683 0 : jumpTable(stubs, &theTable);
5684 :
5685 : // Emit indirect jump. rc is live here.
5686 :
5687 0 : tableSwitch(&theTable, rc, &dispatchCode);
5688 :
5689 0 : deadCode_ = true;
5690 :
5691 : // Clean up.
5692 :
5693 0 : freeI32(rc);
5694 0 : freeJoinRegUnlessVoid(r);
5695 :
5696 0 : return true;
5697 : }
5698 :
5699 : bool
5700 0 : BaseCompiler::emitDrop()
5701 : {
5702 0 : if (!iter_.readDrop())
5703 0 : return false;
5704 :
5705 0 : if (deadCode_)
5706 0 : return true;
5707 :
5708 0 : popStackIfMemory();
5709 0 : popValueStackBy(1);
5710 0 : return true;
5711 : }
5712 :
5713 : void
5714 0 : BaseCompiler::doReturn(ExprType type, bool popStack)
5715 : {
5716 0 : switch (type) {
5717 : case ExprType::Void: {
5718 0 : returnCleanup(popStack);
5719 0 : break;
5720 : }
5721 : case ExprType::I32: {
5722 0 : RegI32 rv = popI32(RegI32(ReturnReg));
5723 0 : returnCleanup(popStack);
5724 0 : freeI32(rv);
5725 0 : break;
5726 : }
5727 : case ExprType::I64: {
5728 0 : RegI64 rv = popI64(RegI64(ReturnReg64));
5729 0 : returnCleanup(popStack);
5730 0 : freeI64(rv);
5731 0 : break;
5732 : }
5733 : case ExprType::F64: {
5734 0 : RegF64 rv = popF64(RegF64(ReturnDoubleReg));
5735 0 : returnCleanup(popStack);
5736 0 : freeF64(rv);
5737 0 : break;
5738 : }
5739 : case ExprType::F32: {
5740 0 : RegF32 rv = popF32(RegF32(ReturnFloat32Reg));
5741 0 : returnCleanup(popStack);
5742 0 : freeF32(rv);
5743 0 : break;
5744 : }
5745 : default: {
5746 0 : MOZ_CRASH("Function return type");
5747 : }
5748 : }
5749 0 : }
5750 :
5751 : bool
5752 0 : BaseCompiler::emitReturn()
5753 : {
5754 : Nothing unused_value;
5755 0 : if (!iter_.readReturn(&unused_value))
5756 0 : return false;
5757 :
5758 0 : if (deadCode_)
5759 0 : return true;
5760 :
5761 0 : doReturn(func_.sig().ret(), PopStack(true));
5762 0 : deadCode_ = true;
5763 :
5764 0 : return true;
5765 : }
5766 :
5767 : bool
5768 0 : BaseCompiler::emitCallArgs(const ValTypeVector& argTypes, FunctionCall& baselineCall)
5769 : {
5770 0 : MOZ_ASSERT(!deadCode_);
5771 :
5772 0 : startCallArgs(baselineCall, stackArgAreaSize(argTypes));
5773 :
5774 0 : uint32_t numArgs = argTypes.length();
5775 0 : for (size_t i = 0; i < numArgs; ++i)
5776 0 : passArg(baselineCall, argTypes[i], peek(numArgs - 1 - i));
5777 :
5778 0 : masm.loadWasmTlsRegFromFrame();
5779 0 : return true;
5780 : }
5781 :
5782 : void
5783 0 : BaseCompiler::pushReturned(const FunctionCall& call, ExprType type)
5784 : {
5785 0 : switch (type) {
5786 : case ExprType::Void:
5787 0 : MOZ_CRASH("Compiler bug: attempt to push void return");
5788 : break;
5789 : case ExprType::I32: {
5790 0 : RegI32 rv = captureReturnedI32();
5791 0 : pushI32(rv);
5792 0 : break;
5793 : }
5794 : case ExprType::I64: {
5795 0 : RegI64 rv = captureReturnedI64();
5796 0 : pushI64(rv);
5797 0 : break;
5798 : }
5799 : case ExprType::F32: {
5800 0 : RegF32 rv = captureReturnedF32(call);
5801 0 : pushF32(rv);
5802 0 : break;
5803 : }
5804 : case ExprType::F64: {
5805 0 : RegF64 rv = captureReturnedF64(call);
5806 0 : pushF64(rv);
5807 0 : break;
5808 : }
5809 : default:
5810 0 : MOZ_CRASH("Function return type");
5811 : }
5812 0 : }
5813 :
5814 : // For now, always sync() at the beginning of the call to easily save live
5815 : // values.
5816 : //
5817 : // TODO / OPTIMIZE (Bug 1316806): We may be able to avoid a full sync(), since
5818 : // all we want is to save live registers that won't be saved by the callee or
5819 : // that we need for outgoing args - we don't need to sync the locals. We can
5820 : // just push the necessary registers, it'll be like a lightweight sync.
5821 : //
5822 : // Even some of the pushing may be unnecessary if the registers will be consumed
5823 : // by the call, because then what we want is parallel assignment to the argument
5824 : // registers or onto the stack for outgoing arguments. A sync() is just
5825 : // simpler.
5826 :
5827 : bool
5828 0 : BaseCompiler::emitCall()
5829 : {
5830 0 : uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
5831 :
5832 : uint32_t funcIndex;
5833 0 : BaseOpIter::ValueVector args_;
5834 0 : if (!iter_.readCall(&funcIndex, &args_))
5835 0 : return false;
5836 :
5837 0 : if (deadCode_)
5838 0 : return true;
5839 :
5840 0 : sync();
5841 :
5842 0 : const Sig& sig = *env_.funcSigs[funcIndex];
5843 0 : bool import = env_.funcIsImport(funcIndex);
5844 :
5845 0 : uint32_t numArgs = sig.args().length();
5846 0 : size_t stackSpace = stackConsumed(numArgs);
5847 :
5848 0 : FunctionCall baselineCall(lineOrBytecode);
5849 0 : beginCall(baselineCall, UseABI::Wasm, import ? InterModule::True : InterModule::False);
5850 :
5851 0 : if (!emitCallArgs(sig.args(), baselineCall))
5852 0 : return false;
5853 :
5854 0 : if (import)
5855 0 : callImport(env_.funcImportGlobalDataOffsets[funcIndex], baselineCall);
5856 : else
5857 0 : callDefinition(funcIndex, baselineCall);
5858 :
5859 0 : endCall(baselineCall, stackSpace);
5860 :
5861 0 : popValueStackBy(numArgs);
5862 :
5863 0 : if (!IsVoid(sig.ret()))
5864 0 : pushReturned(baselineCall, sig.ret());
5865 :
5866 0 : return true;
5867 : }
5868 :
5869 : bool
5870 0 : BaseCompiler::emitCallIndirect()
5871 : {
5872 0 : uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
5873 :
5874 : uint32_t sigIndex;
5875 : Nothing callee_;
5876 0 : BaseOpIter::ValueVector args_;
5877 0 : if (!iter_.readCallIndirect(&sigIndex, &callee_, &args_))
5878 0 : return false;
5879 :
5880 0 : if (deadCode_)
5881 0 : return true;
5882 :
5883 0 : sync();
5884 :
5885 0 : const SigWithId& sig = env_.sigs[sigIndex];
5886 :
5887 : // Stack: ... arg1 .. argn callee
5888 :
5889 0 : uint32_t numArgs = sig.args().length();
5890 0 : size_t stackSpace = stackConsumed(numArgs + 1);
5891 :
5892 : // The arguments must be at the stack top for emitCallArgs, so pop the
5893 : // callee if it is on top. Note this only pops the compiler's stack,
5894 : // not the CPU stack.
5895 :
5896 0 : Stk callee = stk_.popCopy();
5897 :
5898 0 : FunctionCall baselineCall(lineOrBytecode);
5899 0 : beginCall(baselineCall, UseABI::Wasm, InterModule::True);
5900 :
5901 0 : if (!emitCallArgs(sig.args(), baselineCall))
5902 0 : return false;
5903 :
5904 0 : callIndirect(sigIndex, callee, baselineCall);
5905 :
5906 0 : endCall(baselineCall, stackSpace);
5907 :
5908 0 : popValueStackBy(numArgs);
5909 :
5910 0 : if (!IsVoid(sig.ret()))
5911 0 : pushReturned(baselineCall, sig.ret());
5912 :
5913 0 : return true;
5914 : }
5915 :
5916 : void
5917 0 : BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType)
5918 : {
5919 0 : if (operandType == ValType::F32) {
5920 0 : RegF32 f0 = popF32();
5921 0 : roundF32(roundingMode, f0);
5922 0 : pushF32(f0);
5923 0 : } else if (operandType == ValType::F64) {
5924 0 : RegF64 f0 = popF64();
5925 0 : roundF64(roundingMode, f0);
5926 0 : pushF64(f0);
5927 : } else {
5928 0 : MOZ_CRASH("unexpected type");
5929 : }
5930 0 : }
5931 :
5932 : bool
5933 0 : BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType)
5934 : {
5935 0 : uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
5936 :
5937 : Nothing operand_;
5938 0 : if (!iter_.readUnary(operandType, &operand_))
5939 0 : return false;
5940 :
5941 0 : if (deadCode_)
5942 0 : return true;
5943 :
5944 : RoundingMode roundingMode;
5945 0 : if (IsRoundingFunction(callee, &roundingMode) && supportsRoundInstruction(roundingMode)) {
5946 0 : emitRound(roundingMode, operandType);
5947 0 : return true;
5948 : }
5949 :
5950 0 : sync();
5951 :
5952 0 : ValTypeVector& signature = operandType == ValType::F32 ? SigF_ : SigD_;
5953 0 : ExprType retType = operandType == ValType::F32 ? ExprType::F32 : ExprType::F64;
5954 0 : uint32_t numArgs = signature.length();
5955 0 : size_t stackSpace = stackConsumed(numArgs);
5956 :
5957 0 : FunctionCall baselineCall(lineOrBytecode);
5958 0 : beginCall(baselineCall, UseABI::System, InterModule::False);
5959 :
5960 0 : if (!emitCallArgs(signature, baselineCall))
5961 0 : return false;
5962 :
5963 0 : builtinCall(callee, baselineCall);
5964 :
5965 0 : endCall(baselineCall, stackSpace);
5966 :
5967 0 : popValueStackBy(numArgs);
5968 :
5969 0 : pushReturned(baselineCall, retType);
5970 :
5971 0 : return true;
5972 : }
5973 :
5974 : #ifdef INT_DIV_I64_CALLOUT
5975 : void
5976 : BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, ValType operandType)
5977 : {
5978 : MOZ_ASSERT(operandType == ValType::I64);
5979 : MOZ_ASSERT(!deadCode_);
5980 :
5981 : sync();
5982 :
5983 : needI64(abiReturnRegI64);
5984 :
5985 : RegI64 rhs = popI64();
5986 : RegI64 srcDest = popI64ToSpecific(abiReturnRegI64);
5987 :
5988 : Label done;
5989 :
5990 : checkDivideByZeroI64(rhs);
5991 :
5992 : if (callee == SymbolicAddress::DivI64)
5993 : checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
5994 : else if (callee == SymbolicAddress::ModI64)
5995 : checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
5996 :
5997 : masm.setupWasmABICall();
5998 : masm.passABIArg(srcDest.high);
5999 : masm.passABIArg(srcDest.low);
6000 : masm.passABIArg(rhs.high);
6001 : masm.passABIArg(rhs.low);
6002 : masm.callWithABI(bytecodeOffset(), callee);
6003 :
6004 : masm.bind(&done);
6005 :
6006 : freeI64(rhs);
6007 : pushI64(srcDest);
6008 : }
6009 : #endif // INT_DIV_I64_CALLOUT
6010 :
6011 : #ifdef I64_TO_FLOAT_CALLOUT
6012 : bool
6013 : BaseCompiler::emitConvertInt64ToFloatingCallout(SymbolicAddress callee, ValType operandType,
6014 : ValType resultType)
6015 : {
6016 : sync();
6017 :
6018 : RegI64 input = popI64();
6019 :
6020 : FunctionCall call(0);
6021 :
6022 : masm.setupWasmABICall();
6023 : # ifdef JS_NUNBOX32
6024 : masm.passABIArg(input.high);
6025 : masm.passABIArg(input.low);
6026 : # else
6027 : MOZ_CRASH("BaseCompiler platform hook: emitConvertInt64ToFloatingCallout");
6028 : # endif
6029 : masm.callWithABI(bytecodeOffset(), callee,
6030 : resultType == ValType::F32 ? MoveOp::FLOAT32 : MoveOp::DOUBLE);
6031 :
6032 : freeI64(input);
6033 :
6034 : if (resultType == ValType::F32)
6035 : pushF32(captureReturnedF32(call));
6036 : else
6037 : pushF64(captureReturnedF64(call));
6038 :
6039 : return true;
6040 : }
6041 : #endif // I64_TO_FLOAT_CALLOUT
6042 :
6043 : #ifdef FLOAT_TO_I64_CALLOUT
6044 : // `Callee` always takes a double, so a float32 input must be converted.
6045 : bool
6046 : BaseCompiler::emitConvertFloatingToInt64Callout(SymbolicAddress callee, ValType operandType,
6047 : ValType resultType)
6048 : {
6049 : RegF64 doubleInput;
6050 : if (operandType == ValType::F32) {
6051 : doubleInput = needF64();
6052 : RegF32 input = popF32();
6053 : masm.convertFloat32ToDouble(input, doubleInput);
6054 : freeF32(input);
6055 : } else {
6056 : doubleInput = popF64();
6057 : }
6058 :
6059 : // We may need the value after the call for the ool check.
6060 : RegF64 otherReg = needF64();
6061 : moveF64(doubleInput, otherReg);
6062 : pushF64(otherReg);
6063 :
6064 : sync();
6065 :
6066 : FunctionCall call(0);
6067 :
6068 : masm.setupWasmABICall();
6069 : masm.passABIArg(doubleInput, MoveOp::DOUBLE);
6070 : masm.callWithABI(bytecodeOffset(), callee);
6071 :
6072 : freeF64(doubleInput);
6073 :
6074 : RegI64 rv = captureReturnedI64();
6075 :
6076 : RegF64 inputVal = popF64();
6077 :
6078 : bool isUnsigned = callee == SymbolicAddress::TruncateDoubleToUint64;
6079 :
6080 : // The OOL check just succeeds or fails, it does not generate a value.
6081 : OutOfLineCode* ool = new (alloc_) OutOfLineTruncateCheckF32OrF64ToI64(AnyReg(inputVal),
6082 : isUnsigned,
6083 : bytecodeOffset());
6084 : ool = addOutOfLineCode(ool);
6085 : if (!ool)
6086 : return false;
6087 :
6088 : masm.branch64(Assembler::Equal, rv, Imm64(0x8000000000000000), ool->entry());
6089 : masm.bind(ool->rejoin());
6090 :
6091 : pushI64(rv);
6092 : freeF64(inputVal);
6093 :
6094 : return true;
6095 : }
6096 : #endif // FLOAT_TO_I64_CALLOUT
6097 :
6098 : bool
6099 0 : BaseCompiler::emitGetLocal()
6100 : {
6101 : uint32_t slot;
6102 0 : if (!iter_.readGetLocal(locals_, &slot))
6103 0 : return false;
6104 :
6105 0 : if (deadCode_)
6106 0 : return true;
6107 :
6108 : // Local loads are pushed unresolved, ie, they may be deferred
6109 : // until needed, until they may be affected by a store, or until a
6110 : // sync. This is intended to reduce register pressure.
6111 :
6112 0 : switch (locals_[slot]) {
6113 : case ValType::I32:
6114 0 : pushLocalI32(slot);
6115 0 : break;
6116 : case ValType::I64:
6117 0 : pushLocalI64(slot);
6118 0 : break;
6119 : case ValType::F64:
6120 0 : pushLocalF64(slot);
6121 0 : break;
6122 : case ValType::F32:
6123 0 : pushLocalF32(slot);
6124 0 : break;
6125 : default:
6126 0 : MOZ_CRASH("Local variable type");
6127 : }
6128 :
6129 0 : return true;
6130 : }
6131 :
6132 : template<bool isSetLocal>
6133 : bool
6134 0 : BaseCompiler::emitSetOrTeeLocal(uint32_t slot)
6135 : {
6136 0 : if (deadCode_)
6137 0 : return true;
6138 :
6139 0 : bceLocalIsUpdated(slot);
6140 0 : switch (locals_[slot]) {
6141 : case ValType::I32: {
6142 0 : RegI32 rv = popI32();
6143 0 : syncLocal(slot);
6144 0 : storeToFrameI32(rv, frameOffsetFromSlot(slot, MIRType::Int32));
6145 : if (isSetLocal)
6146 0 : freeI32(rv);
6147 : else
6148 0 : pushI32(rv);
6149 0 : break;
6150 : }
6151 : case ValType::I64: {
6152 0 : RegI64 rv = popI64();
6153 0 : syncLocal(slot);
6154 0 : storeToFrameI64(rv, frameOffsetFromSlot(slot, MIRType::Int64));
6155 : if (isSetLocal)
6156 0 : freeI64(rv);
6157 : else
6158 0 : pushI64(rv);
6159 0 : break;
6160 : }
6161 : case ValType::F64: {
6162 0 : RegF64 rv = popF64();
6163 0 : syncLocal(slot);
6164 0 : storeToFrameF64(rv, frameOffsetFromSlot(slot, MIRType::Double));
6165 : if (isSetLocal)
6166 0 : freeF64(rv);
6167 : else
6168 0 : pushF64(rv);
6169 0 : break;
6170 : }
6171 : case ValType::F32: {
6172 0 : RegF32 rv = popF32();
6173 0 : syncLocal(slot);
6174 0 : storeToFrameF32(rv, frameOffsetFromSlot(slot, MIRType::Float32));
6175 : if (isSetLocal)
6176 0 : freeF32(rv);
6177 : else
6178 0 : pushF32(rv);
6179 0 : break;
6180 : }
6181 : default:
6182 0 : MOZ_CRASH("Local variable type");
6183 : }
6184 :
6185 0 : return true;
6186 : }
6187 :
6188 : bool
6189 0 : BaseCompiler::emitSetLocal()
6190 : {
6191 : uint32_t slot;
6192 : Nothing unused_value;
6193 0 : if (!iter_.readSetLocal(locals_, &slot, &unused_value))
6194 0 : return false;
6195 0 : return emitSetOrTeeLocal<true>(slot);
6196 : }
6197 :
6198 : bool
6199 0 : BaseCompiler::emitTeeLocal()
6200 : {
6201 : uint32_t slot;
6202 : Nothing unused_value;
6203 0 : if (!iter_.readTeeLocal(locals_, &slot, &unused_value))
6204 0 : return false;
6205 0 : return emitSetOrTeeLocal<false>(slot);
6206 : }
6207 :
6208 : bool
6209 0 : BaseCompiler::emitGetGlobal()
6210 : {
6211 : uint32_t id;
6212 0 : if (!iter_.readGetGlobal(&id))
6213 0 : return false;
6214 :
6215 0 : if (deadCode_)
6216 0 : return true;
6217 :
6218 0 : const GlobalDesc& global = env_.globals[id];
6219 :
6220 0 : if (global.isConstant()) {
6221 0 : Val value = global.constantValue();
6222 0 : switch (value.type()) {
6223 : case ValType::I32:
6224 0 : pushI32(value.i32());
6225 0 : break;
6226 : case ValType::I64:
6227 0 : pushI64(value.i64());
6228 0 : break;
6229 : case ValType::F32:
6230 0 : pushF32(value.f32());
6231 0 : break;
6232 : case ValType::F64:
6233 0 : pushF64(value.f64());
6234 0 : break;
6235 : default:
6236 0 : MOZ_CRASH("Global constant type");
6237 : }
6238 0 : return true;
6239 : }
6240 :
6241 0 : switch (global.type()) {
6242 : case ValType::I32: {
6243 0 : RegI32 rv = needI32();
6244 0 : loadGlobalVarI32(global.offset(), rv);
6245 0 : pushI32(rv);
6246 0 : break;
6247 : }
6248 : case ValType::I64: {
6249 0 : RegI64 rv = needI64();
6250 0 : loadGlobalVarI64(global.offset(), rv);
6251 0 : pushI64(rv);
6252 0 : break;
6253 : }
6254 : case ValType::F32: {
6255 0 : RegF32 rv = needF32();
6256 0 : loadGlobalVarF32(global.offset(), rv);
6257 0 : pushF32(rv);
6258 0 : break;
6259 : }
6260 : case ValType::F64: {
6261 0 : RegF64 rv = needF64();
6262 0 : loadGlobalVarF64(global.offset(), rv);
6263 0 : pushF64(rv);
6264 0 : break;
6265 : }
6266 : default:
6267 0 : MOZ_CRASH("Global variable type");
6268 : break;
6269 : }
6270 0 : return true;
6271 : }
6272 :
6273 : bool
6274 0 : BaseCompiler::emitSetGlobal()
6275 : {
6276 : uint32_t id;
6277 : Nothing unused_value;
6278 0 : if (!iter_.readSetGlobal(&id, &unused_value))
6279 0 : return false;
6280 :
6281 0 : if (deadCode_)
6282 0 : return true;
6283 :
6284 0 : const GlobalDesc& global = env_.globals[id];
6285 :
6286 0 : switch (global.type()) {
6287 : case ValType::I32: {
6288 0 : RegI32 rv = popI32();
6289 0 : storeGlobalVarI32(global.offset(), rv);
6290 0 : freeI32(rv);
6291 0 : break;
6292 : }
6293 : case ValType::I64: {
6294 0 : RegI64 rv = popI64();
6295 0 : storeGlobalVarI64(global.offset(), rv);
6296 0 : freeI64(rv);
6297 0 : break;
6298 : }
6299 : case ValType::F32: {
6300 0 : RegF32 rv = popF32();
6301 0 : storeGlobalVarF32(global.offset(), rv);
6302 0 : freeF32(rv);
6303 0 : break;
6304 : }
6305 : case ValType::F64: {
6306 0 : RegF64 rv = popF64();
6307 0 : storeGlobalVarF64(global.offset(), rv);
6308 0 : freeF64(rv);
6309 0 : break;
6310 : }
6311 : default:
6312 0 : MOZ_CRASH("Global variable type");
6313 : break;
6314 : }
6315 0 : return true;
6316 : }
6317 :
6318 : // Bounds check elimination.
6319 : //
6320 : // We perform BCE on two kinds of address expressions: on constant heap pointers
6321 : // that are known to be in the heap or will be handled by the out-of-bounds trap
6322 : // handler; and on local variables that have been checked in dominating code
6323 : // without being updated since.
6324 : //
6325 : // For an access through a constant heap pointer + an offset we can eliminate
6326 : // the bounds check if the sum of the address and offset is below the sum of the
6327 : // minimum memory length and the offset guard length.
6328 : //
6329 : // For an access through a local variable + an offset we can eliminate the
6330 : // bounds check if the local variable has already been checked and has not been
6331 : // updated since, and the offset is less than the guard limit.
6332 : //
6333 : // To track locals for which we can eliminate checks we use a bit vector
6334 : // bceSafe_ that has a bit set for those locals whose bounds have been checked
6335 : // and which have not subsequently been set. Initially this vector is zero.
6336 : //
6337 : // In straight-line code a bit is set when we perform a bounds check on an
6338 : // access via the local and is reset when the variable is updated.
6339 : //
6340 : // In control flow, the bit vector is manipulated as follows. Each ControlItem
6341 : // has a value bceSafeOnEntry, which is the value of bceSafe_ on entry to the
6342 : // item, and a value bceSafeOnExit, which is initially ~0. On a branch (br,
6343 : // brIf, brTable), we always AND the branch target's bceSafeOnExit with the
6344 : // value of bceSafe_ at the branch point. On exiting an item by falling out of
6345 : // it, provided we're not in dead code, we AND the current value of bceSafe_
6346 : // into the item's bceSafeOnExit. Additional processing depends on the item
6347 : // type:
6348 : //
6349 : // - After a block, set bceSafe_ to the block's bceSafeOnExit.
6350 : //
6351 : // - On loop entry, after pushing the ControlItem, set bceSafe_ to zero; the
6352 : // back edges would otherwise require us to iterate to a fixedpoint.
6353 : //
6354 : // - After a loop, the bceSafe_ is left unchanged, because only fallthrough
6355 : // control flow will reach that point and the bceSafe_ value represents the
6356 : // correct state of the fallthrough path.
6357 : //
6358 : // - Set bceSafe_ to the ControlItem's bceSafeOnEntry at both the 'then' branch
6359 : // and the 'else' branch.
6360 : //
6361 : // - After an if-then-else, set bceSafe_ to the if-then-else's bceSafeOnExit.
6362 : //
6363 : // - After an if-then, set bceSafe_ to the if-then's bceSafeOnExit AND'ed with
6364 : // the if-then's bceSafeOnEntry.
6365 : //
6366 : // Finally, when the debugger allows locals to be mutated we must disable BCE
6367 : // for references via a local, by returning immediately from bceCheckLocal if
6368 : // debugEnabled_ is true.
6369 :
6370 : // TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
6371 : // code by not moving a constant address with a zero offset into a register.
6372 :
6373 : BaseCompiler::RegI32
6374 0 : BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck)
6375 : {
6376 : // Caller must initialize.
6377 0 : MOZ_ASSERT(!*omitBoundsCheck);
6378 :
6379 : int32_t addrTmp;
6380 0 : if (popConstI32(&addrTmp)) {
6381 0 : uint32_t addr = addrTmp;
6382 :
6383 0 : uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
6384 0 : uint64_t limit = uint64_t(env_.minMemoryLength) + uint64_t(wasm::OffsetGuardLimit);
6385 :
6386 0 : *omitBoundsCheck = ea < limit;
6387 :
6388 : // Fold the offset into the pointer if we can, as this is always
6389 : // beneficial.
6390 :
6391 0 : if (ea <= UINT32_MAX) {
6392 0 : addr = uint32_t(ea);
6393 0 : access->clearOffset();
6394 : }
6395 :
6396 0 : RegI32 r = needI32();
6397 0 : loadConstI32(r, int32_t(addr));
6398 0 : return r;
6399 : }
6400 :
6401 : uint32_t local;
6402 0 : if (peekLocalI32(&local))
6403 0 : bceCheckLocal(access, local, omitBoundsCheck);
6404 :
6405 0 : return popI32();
6406 : }
6407 :
6408 : BaseCompiler::RegI32
6409 0 : BaseCompiler::maybeLoadTlsForAccess(bool omitBoundsCheck)
6410 : {
6411 0 : RegI32 tls = invalidI32();
6412 0 : if (needTlsForAccess(omitBoundsCheck)) {
6413 0 : tls = needI32();
6414 0 : masm.loadWasmTlsRegFromFrame(tls);
6415 : }
6416 0 : return tls;
6417 : }
6418 :
6419 : bool
6420 0 : BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
6421 : {
6422 0 : LinearMemoryAddress<Nothing> addr;
6423 0 : if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
6424 0 : return false;
6425 :
6426 0 : if (deadCode_)
6427 0 : return true;
6428 :
6429 0 : bool omitBoundsCheck = false;
6430 0 : MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(bytecodeOffset()));
6431 :
6432 0 : size_t temps = loadTemps(access);
6433 0 : MOZ_ASSERT(temps <= 3);
6434 0 : RegI32 tmp1 = temps >= 1 ? needI32() : invalidI32();
6435 0 : RegI32 tmp2 = temps >= 2 ? needI32() : invalidI32();
6436 0 : RegI32 tmp3 = temps >= 3 ? needI32() : invalidI32();
6437 0 : RegI32 tls = invalidI32();
6438 :
6439 0 : switch (type) {
6440 : case ValType::I32: {
6441 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6442 : #ifdef JS_CODEGEN_ARM
6443 : RegI32 rv = IsUnaligned(access) ? needI32() : rp;
6444 : #else
6445 0 : RegI32 rv = rp;
6446 : #endif
6447 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6448 0 : if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
6449 0 : return false;
6450 0 : pushI32(rv);
6451 0 : if (rp != rv)
6452 0 : freeI32(rp);
6453 0 : break;
6454 : }
6455 : case ValType::I64: {
6456 0 : RegI64 rv;
6457 0 : RegI32 rp;
6458 : #ifdef JS_CODEGEN_X86
6459 : rv = abiReturnRegI64;
6460 : needI64(rv);
6461 : rp = popMemoryAccess(&access, &omitBoundsCheck);
6462 : #else
6463 0 : rp = popMemoryAccess(&access, &omitBoundsCheck);
6464 0 : rv = needI64();
6465 : #endif
6466 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6467 0 : if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
6468 0 : return false;
6469 0 : pushI64(rv);
6470 0 : freeI32(rp);
6471 0 : break;
6472 : }
6473 : case ValType::F32: {
6474 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6475 0 : RegF32 rv = needF32();
6476 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6477 0 : if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
6478 0 : return false;
6479 0 : pushF32(rv);
6480 0 : freeI32(rp);
6481 0 : break;
6482 : }
6483 : case ValType::F64: {
6484 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6485 0 : RegF64 rv = needF64();
6486 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6487 0 : if (!load(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp1, tmp2, tmp3))
6488 0 : return false;
6489 0 : pushF64(rv);
6490 0 : freeI32(rp);
6491 0 : break;
6492 : }
6493 : default:
6494 0 : MOZ_CRASH("load type");
6495 : break;
6496 : }
6497 :
6498 0 : if (tls != invalidI32())
6499 0 : freeI32(tls);
6500 :
6501 0 : MOZ_ASSERT(temps <= 3);
6502 0 : if (temps >= 1)
6503 0 : freeI32(tmp1);
6504 0 : if (temps >= 2)
6505 0 : freeI32(tmp2);
6506 0 : if (temps >= 3)
6507 0 : freeI32(tmp3);
6508 :
6509 0 : return true;
6510 : }
6511 :
6512 : bool
6513 0 : BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
6514 : {
6515 0 : LinearMemoryAddress<Nothing> addr;
6516 : Nothing unused_value;
6517 0 : if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, &unused_value))
6518 0 : return false;
6519 :
6520 0 : if (deadCode_)
6521 0 : return true;
6522 :
6523 0 : bool omitBoundsCheck = false;
6524 0 : MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(bytecodeOffset()));
6525 :
6526 0 : size_t temps = storeTemps(access, resultType);
6527 :
6528 0 : MOZ_ASSERT(temps <= 1);
6529 0 : RegI32 tmp = temps >= 1 ? needI32() : invalidI32();
6530 0 : RegI32 tls = invalidI32();
6531 :
6532 0 : switch (resultType) {
6533 : case ValType::I32: {
6534 0 : RegI32 rv = popI32();
6535 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6536 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6537 0 : if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
6538 0 : return false;
6539 0 : freeI32(rp);
6540 0 : freeI32(rv);
6541 0 : break;
6542 : }
6543 : case ValType::I64: {
6544 0 : RegI64 rv = popI64();
6545 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6546 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6547 0 : if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
6548 0 : return false;
6549 0 : freeI32(rp);
6550 0 : freeI64(rv);
6551 0 : break;
6552 : }
6553 : case ValType::F32: {
6554 0 : RegF32 rv = popF32();
6555 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6556 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6557 0 : if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
6558 0 : return false;
6559 0 : freeI32(rp);
6560 0 : freeF32(rv);
6561 0 : break;
6562 : }
6563 : case ValType::F64: {
6564 0 : RegF64 rv = popF64();
6565 0 : RegI32 rp = popMemoryAccess(&access, &omitBoundsCheck);
6566 0 : tls = maybeLoadTlsForAccess(omitBoundsCheck);
6567 0 : if (!store(&access, tls, rp, omitBoundsCheck, AnyReg(rv), tmp))
6568 0 : return false;
6569 0 : freeI32(rp);
6570 0 : freeF64(rv);
6571 0 : break;
6572 : }
6573 : default:
6574 0 : MOZ_CRASH("store type");
6575 : break;
6576 : }
6577 :
6578 0 : if (tls != invalidI32())
6579 0 : freeI32(tls);
6580 :
6581 0 : MOZ_ASSERT(temps <= 1);
6582 0 : if (temps >= 1)
6583 0 : freeI32(tmp);
6584 :
6585 0 : return true;
6586 : }
6587 :
6588 : bool
6589 0 : BaseCompiler::emitSelect()
6590 : {
6591 : StackType type;
6592 : Nothing unused_trueValue;
6593 : Nothing unused_falseValue;
6594 : Nothing unused_condition;
6595 0 : if (!iter_.readSelect(&type, &unused_trueValue, &unused_falseValue, &unused_condition))
6596 0 : return false;
6597 :
6598 0 : if (deadCode_) {
6599 0 : resetLatentOp();
6600 0 : return true;
6601 : }
6602 :
6603 : // I32 condition on top, then false, then true.
6604 :
6605 0 : Label done;
6606 0 : BranchState b(&done);
6607 0 : emitBranchSetup(&b);
6608 :
6609 0 : switch (NonAnyToValType(type)) {
6610 : case ValType::I32: {
6611 0 : RegI32 r0, r1;
6612 0 : pop2xI32(&r0, &r1);
6613 0 : emitBranchPerform(&b);
6614 0 : moveI32(r1, r0);
6615 0 : masm.bind(&done);
6616 0 : freeI32(r1);
6617 0 : pushI32(r0);
6618 0 : break;
6619 : }
6620 : case ValType::I64: {
6621 : #ifdef JS_CODEGEN_X86
6622 : // There may be as many as four Int64 values in registers at a time: two
6623 : // for the latent branch operands, and two for the true/false values we
6624 : // normally pop before executing the branch. On x86 this is one value
6625 : // too many, so we need to generate more complicated code here, and for
6626 : // simplicity's sake we do so even if the branch operands are not Int64.
6627 : // However, the resulting control flow diamond is complicated since the
6628 : // arms of the diamond will have to stay synchronized with respect to
6629 : // their evaluation stack and regalloc state. To simplify further, we
6630 : // use a double branch and a temporary boolean value for now.
6631 : RegI32 tmp = needI32();
6632 : loadConstI32(tmp, 0);
6633 : emitBranchPerform(&b);
6634 : loadConstI32(tmp, 1);
6635 : masm.bind(&done);
6636 :
6637 : Label trueValue;
6638 : RegI64 r0, r1;
6639 : pop2xI64(&r0, &r1);
6640 : masm.branch32(Assembler::Equal, tmp, Imm32(0), &trueValue);
6641 : moveI64(r1, r0);
6642 : masm.bind(&trueValue);
6643 : freeI32(tmp);
6644 : freeI64(r1);
6645 : pushI64(r0);
6646 : #else
6647 0 : RegI64 r0, r1;
6648 0 : pop2xI64(&r0, &r1);
6649 0 : emitBranchPerform(&b);
6650 0 : moveI64(r1, r0);
6651 0 : masm.bind(&done);
6652 0 : freeI64(r1);
6653 0 : pushI64(r0);
6654 : #endif
6655 0 : break;
6656 : }
6657 : case ValType::F32: {
6658 0 : RegF32 r0, r1;
6659 0 : pop2xF32(&r0, &r1);
6660 0 : emitBranchPerform(&b);
6661 0 : moveF32(r1, r0);
6662 0 : masm.bind(&done);
6663 0 : freeF32(r1);
6664 0 : pushF32(r0);
6665 0 : break;
6666 : }
6667 : case ValType::F64: {
6668 0 : RegF64 r0, r1;
6669 0 : pop2xF64(&r0, &r1);
6670 0 : emitBranchPerform(&b);
6671 0 : moveF64(r1, r0);
6672 0 : masm.bind(&done);
6673 0 : freeF64(r1);
6674 0 : pushF64(r0);
6675 0 : break;
6676 : }
6677 : default: {
6678 0 : MOZ_CRASH("select type");
6679 : }
6680 : }
6681 :
6682 0 : return true;
6683 : }
6684 :
6685 : void
6686 0 : BaseCompiler::emitCompareI32(Assembler::Condition compareOp, ValType compareType)
6687 : {
6688 0 : MOZ_ASSERT(compareType == ValType::I32);
6689 :
6690 0 : if (sniffConditionalControlCmp(compareOp, compareType))
6691 0 : return;
6692 :
6693 : int32_t c;
6694 0 : if (popConstI32(&c)) {
6695 0 : RegI32 r0 = popI32();
6696 0 : masm.cmp32Set(compareOp, r0, Imm32(c), r0);
6697 0 : pushI32(r0);
6698 : } else {
6699 0 : RegI32 r0, r1;
6700 0 : pop2xI32(&r0, &r1);
6701 0 : masm.cmp32Set(compareOp, r0, r1, r0);
6702 0 : freeI32(r1);
6703 0 : pushI32(r0);
6704 : }
6705 : }
6706 :
6707 : void
6708 0 : BaseCompiler::emitCompareI64(Assembler::Condition compareOp, ValType compareType)
6709 : {
6710 0 : MOZ_ASSERT(compareType == ValType::I64);
6711 :
6712 0 : if (sniffConditionalControlCmp(compareOp, compareType))
6713 0 : return;
6714 :
6715 0 : RegI64 r0, r1;
6716 0 : pop2xI64(&r0, &r1);
6717 0 : RegI32 i0(fromI64(r0));
6718 0 : cmp64Set(compareOp, r0, r1, i0);
6719 0 : freeI64(r1);
6720 0 : freeI64Except(r0, i0);
6721 0 : pushI32(i0);
6722 : }
6723 :
6724 : void
6725 0 : BaseCompiler::emitCompareF32(Assembler::DoubleCondition compareOp, ValType compareType)
6726 : {
6727 0 : MOZ_ASSERT(compareType == ValType::F32);
6728 :
6729 0 : if (sniffConditionalControlCmp(compareOp, compareType))
6730 0 : return;
6731 :
6732 0 : Label across;
6733 0 : RegF32 r0, r1;
6734 0 : pop2xF32(&r0, &r1);
6735 0 : RegI32 i0 = needI32();
6736 0 : masm.mov(ImmWord(1), i0);
6737 0 : masm.branchFloat(compareOp, r0, r1, &across);
6738 0 : masm.mov(ImmWord(0), i0);
6739 0 : masm.bind(&across);
6740 0 : freeF32(r0);
6741 0 : freeF32(r1);
6742 0 : pushI32(i0);
6743 : }
6744 :
6745 : void
6746 0 : BaseCompiler::emitCompareF64(Assembler::DoubleCondition compareOp, ValType compareType)
6747 : {
6748 0 : MOZ_ASSERT(compareType == ValType::F64);
6749 :
6750 0 : if (sniffConditionalControlCmp(compareOp, compareType))
6751 0 : return;
6752 :
6753 0 : Label across;
6754 0 : RegF64 r0, r1;
6755 0 : pop2xF64(&r0, &r1);
6756 0 : RegI32 i0 = needI32();
6757 0 : masm.mov(ImmWord(1), i0);
6758 0 : masm.branchDouble(compareOp, r0, r1, &across);
6759 0 : masm.mov(ImmWord(0), i0);
6760 0 : masm.bind(&across);
6761 0 : freeF64(r0);
6762 0 : freeF64(r1);
6763 0 : pushI32(i0);
6764 : }
6765 :
6766 : bool
6767 0 : BaseCompiler::emitGrowMemory()
6768 : {
6769 0 : uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
6770 :
6771 : Nothing arg;
6772 0 : if (!iter_.readGrowMemory(&arg))
6773 0 : return false;
6774 :
6775 0 : if (deadCode_)
6776 0 : return true;
6777 :
6778 0 : sync();
6779 :
6780 0 : uint32_t numArgs = 1;
6781 0 : size_t stackSpace = stackConsumed(numArgs);
6782 :
6783 0 : FunctionCall baselineCall(lineOrBytecode);
6784 0 : beginCall(baselineCall, UseABI::System, InterModule::True);
6785 :
6786 0 : ABIArg instanceArg = reservePointerArgument(baselineCall);
6787 :
6788 0 : startCallArgs(baselineCall, stackArgAreaSize(SigPI_));
6789 0 : passArg(baselineCall, ValType::I32, peek(0));
6790 0 : builtinInstanceMethodCall(SymbolicAddress::GrowMemory, instanceArg, baselineCall);
6791 0 : endCall(baselineCall, stackSpace);
6792 :
6793 0 : popValueStackBy(numArgs);
6794 :
6795 0 : pushReturned(baselineCall, ExprType::I32);
6796 :
6797 0 : return true;
6798 : }
6799 :
6800 : bool
6801 0 : BaseCompiler::emitCurrentMemory()
6802 : {
6803 0 : uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
6804 :
6805 0 : if (!iter_.readCurrentMemory())
6806 0 : return false;
6807 :
6808 0 : if (deadCode_)
6809 0 : return true;
6810 :
6811 0 : sync();
6812 :
6813 0 : FunctionCall baselineCall(lineOrBytecode);
6814 0 : beginCall(baselineCall, UseABI::System, InterModule::False);
6815 :
6816 0 : ABIArg instanceArg = reservePointerArgument(baselineCall);
6817 :
6818 0 : startCallArgs(baselineCall, stackArgAreaSize(SigP_));
6819 0 : builtinInstanceMethodCall(SymbolicAddress::CurrentMemory, instanceArg, baselineCall);
6820 0 : endCall(baselineCall, 0);
6821 :
6822 0 : pushReturned(baselineCall, ExprType::I32);
6823 :
6824 0 : return true;
6825 : }
6826 :
6827 : bool
6828 0 : BaseCompiler::emitBody()
6829 : {
6830 0 : if (!iter_.readFunctionStart(func_.sig().ret()))
6831 0 : return false;
6832 :
6833 0 : initControl(controlItem());
6834 :
6835 0 : uint32_t overhead = 0;
6836 :
6837 : for (;;) {
6838 :
6839 : Nothing unused_a, unused_b;
6840 :
6841 : #ifdef DEBUG
6842 0 : performRegisterLeakCheck();
6843 : #endif
6844 :
6845 : #define emitBinary(doEmit, type) \
6846 : iter_.readBinary(type, &unused_a, &unused_b) && (deadCode_ || (doEmit(), true))
6847 :
6848 : #define emitUnary(doEmit, type) \
6849 : iter_.readUnary(type, &unused_a) && (deadCode_ || (doEmit(), true))
6850 :
6851 : #define emitComparison(doEmit, operandType, compareOp) \
6852 : iter_.readComparison(operandType, &unused_a, &unused_b) && \
6853 : (deadCode_ || (doEmit(compareOp, operandType), true))
6854 :
6855 : #define emitConversion(doEmit, inType, outType) \
6856 : iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || (doEmit(), true))
6857 :
6858 : #define emitConversionOOM(doEmit, inType, outType) \
6859 : iter_.readConversion(inType, outType, &unused_a) && (deadCode_ || doEmit())
6860 :
6861 : #define emitCalloutConversionOOM(doEmit, symbol, inType, outType) \
6862 : iter_.readConversion(inType, outType, &unused_a) && \
6863 : (deadCode_ || doEmit(symbol, inType, outType))
6864 :
6865 : #define emitIntDivCallout(doEmit, symbol, type) \
6866 : iter_.readBinary(type, &unused_a, &unused_b) && (deadCode_ || (doEmit(symbol, type), true))
6867 :
6868 : #define CHECK(E) if (!(E)) return false
6869 : #define NEXT() continue
6870 : #define CHECK_NEXT(E) if (!(E)) return false; continue
6871 :
6872 : // TODO / EVALUATE (bug 1316845): Not obvious that this attempt at
6873 : // reducing overhead is really paying off relative to making the check
6874 : // every iteration.
6875 :
6876 0 : if (overhead == 0) {
6877 : // Check every 50 expressions -- a happy medium between
6878 : // memory usage and checking overhead.
6879 0 : overhead = 50;
6880 :
6881 : // Checking every 50 expressions should be safe, as the
6882 : // baseline JIT does very little allocation per expression.
6883 0 : CHECK(alloc_.ensureBallast());
6884 :
6885 : // The pushiest opcode is LOOP, which pushes two values
6886 : // per instance.
6887 0 : CHECK(stk_.reserve(stk_.length() + overhead * 2));
6888 : }
6889 :
6890 0 : overhead--;
6891 :
6892 0 : OpBytes op;
6893 0 : CHECK(iter_.readOp(&op));
6894 :
6895 : // When debugEnabled_, every operator has breakpoint site but Op::End.
6896 0 : if (debugEnabled_ && op.b0 != (uint16_t)Op::End) {
6897 : // TODO sync only registers that can be clobbered by the exit
6898 : // prologue/epilogue or disable these registers for use in
6899 : // baseline compiler when debugEnabled_ is set.
6900 0 : sync();
6901 :
6902 0 : insertBreakablePoint(CallSiteDesc::Breakpoint);
6903 : }
6904 :
6905 0 : switch (op.b0) {
6906 : case uint16_t(Op::End):
6907 0 : if (!emitEnd())
6908 0 : return false;
6909 :
6910 0 : if (iter_.controlStackEmpty()) {
6911 0 : if (!deadCode_)
6912 0 : doReturn(func_.sig().ret(), PopStack(false));
6913 0 : return iter_.readFunctionEnd(iter_.end());
6914 : }
6915 0 : NEXT();
6916 :
6917 : // Control opcodes
6918 : case uint16_t(Op::Nop):
6919 0 : CHECK_NEXT(iter_.readNop());
6920 : case uint16_t(Op::Drop):
6921 0 : CHECK_NEXT(emitDrop());
6922 : case uint16_t(Op::Block):
6923 0 : CHECK_NEXT(emitBlock());
6924 : case uint16_t(Op::Loop):
6925 0 : CHECK_NEXT(emitLoop());
6926 : case uint16_t(Op::If):
6927 0 : CHECK_NEXT(emitIf());
6928 : case uint16_t(Op::Else):
6929 0 : CHECK_NEXT(emitElse());
6930 : case uint16_t(Op::Br):
6931 0 : CHECK_NEXT(emitBr());
6932 : case uint16_t(Op::BrIf):
6933 0 : CHECK_NEXT(emitBrIf());
6934 : case uint16_t(Op::BrTable):
6935 0 : CHECK_NEXT(emitBrTable());
6936 : case uint16_t(Op::Return):
6937 0 : CHECK_NEXT(emitReturn());
6938 : case uint16_t(Op::Unreachable):
6939 0 : CHECK(iter_.readUnreachable());
6940 0 : if (!deadCode_) {
6941 0 : unreachableTrap();
6942 0 : deadCode_ = true;
6943 : }
6944 0 : NEXT();
6945 :
6946 : // Calls
6947 : case uint16_t(Op::Call):
6948 0 : CHECK_NEXT(emitCall());
6949 : case uint16_t(Op::CallIndirect):
6950 0 : CHECK_NEXT(emitCallIndirect());
6951 :
6952 : // Locals and globals
6953 : case uint16_t(Op::GetLocal):
6954 0 : CHECK_NEXT(emitGetLocal());
6955 : case uint16_t(Op::SetLocal):
6956 0 : CHECK_NEXT(emitSetLocal());
6957 : case uint16_t(Op::TeeLocal):
6958 0 : CHECK_NEXT(emitTeeLocal());
6959 : case uint16_t(Op::GetGlobal):
6960 0 : CHECK_NEXT(emitGetGlobal());
6961 : case uint16_t(Op::SetGlobal):
6962 0 : CHECK_NEXT(emitSetGlobal());
6963 :
6964 : // Select
6965 : case uint16_t(Op::Select):
6966 0 : CHECK_NEXT(emitSelect());
6967 :
6968 : // I32
6969 : case uint16_t(Op::I32Const): {
6970 : int32_t i32;
6971 0 : CHECK(iter_.readI32Const(&i32));
6972 0 : if (!deadCode_)
6973 0 : pushI32(i32);
6974 0 : NEXT();
6975 : }
6976 : case uint16_t(Op::I32Add):
6977 0 : CHECK_NEXT(emitBinary(emitAddI32, ValType::I32));
6978 : case uint16_t(Op::I32Sub):
6979 0 : CHECK_NEXT(emitBinary(emitSubtractI32, ValType::I32));
6980 : case uint16_t(Op::I32Mul):
6981 0 : CHECK_NEXT(emitBinary(emitMultiplyI32, ValType::I32));
6982 : case uint16_t(Op::I32DivS):
6983 0 : CHECK_NEXT(emitBinary(emitQuotientI32, ValType::I32));
6984 : case uint16_t(Op::I32DivU):
6985 0 : CHECK_NEXT(emitBinary(emitQuotientU32, ValType::I32));
6986 : case uint16_t(Op::I32RemS):
6987 0 : CHECK_NEXT(emitBinary(emitRemainderI32, ValType::I32));
6988 : case uint16_t(Op::I32RemU):
6989 0 : CHECK_NEXT(emitBinary(emitRemainderU32, ValType::I32));
6990 : case uint16_t(Op::I32Eqz):
6991 0 : CHECK_NEXT(emitConversion(emitEqzI32, ValType::I32, ValType::I32));
6992 : case uint16_t(Op::I32TruncSF32):
6993 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<false>, ValType::F32, ValType::I32));
6994 : case uint16_t(Op::I32TruncUF32):
6995 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<true>, ValType::F32, ValType::I32));
6996 : case uint16_t(Op::I32TruncSF64):
6997 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<false>, ValType::F64, ValType::I32));
6998 : case uint16_t(Op::I32TruncUF64):
6999 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<true>, ValType::F64, ValType::I32));
7000 : case uint16_t(Op::I32WrapI64):
7001 0 : CHECK_NEXT(emitConversion(emitWrapI64ToI32, ValType::I64, ValType::I32));
7002 : case uint16_t(Op::I32ReinterpretF32):
7003 0 : CHECK_NEXT(emitConversion(emitReinterpretF32AsI32, ValType::F32, ValType::I32));
7004 : case uint16_t(Op::I32Clz):
7005 0 : CHECK_NEXT(emitUnary(emitClzI32, ValType::I32));
7006 : case uint16_t(Op::I32Ctz):
7007 0 : CHECK_NEXT(emitUnary(emitCtzI32, ValType::I32));
7008 : case uint16_t(Op::I32Popcnt):
7009 0 : CHECK_NEXT(emitUnary(emitPopcntI32, ValType::I32));
7010 : case uint16_t(Op::I32Or):
7011 0 : CHECK_NEXT(emitBinary(emitOrI32, ValType::I32));
7012 : case uint16_t(Op::I32And):
7013 0 : CHECK_NEXT(emitBinary(emitAndI32, ValType::I32));
7014 : case uint16_t(Op::I32Xor):
7015 0 : CHECK_NEXT(emitBinary(emitXorI32, ValType::I32));
7016 : case uint16_t(Op::I32Shl):
7017 0 : CHECK_NEXT(emitBinary(emitShlI32, ValType::I32));
7018 : case uint16_t(Op::I32ShrS):
7019 0 : CHECK_NEXT(emitBinary(emitShrI32, ValType::I32));
7020 : case uint16_t(Op::I32ShrU):
7021 0 : CHECK_NEXT(emitBinary(emitShrU32, ValType::I32));
7022 : case uint16_t(Op::I32Load8S):
7023 0 : CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int8));
7024 : case uint16_t(Op::I32Load8U):
7025 0 : CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint8));
7026 : case uint16_t(Op::I32Load16S):
7027 0 : CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int16));
7028 : case uint16_t(Op::I32Load16U):
7029 0 : CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint16));
7030 : case uint16_t(Op::I32Load):
7031 0 : CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int32));
7032 : case uint16_t(Op::I32Store8):
7033 0 : CHECK_NEXT(emitStore(ValType::I32, Scalar::Int8));
7034 : case uint16_t(Op::I32Store16):
7035 0 : CHECK_NEXT(emitStore(ValType::I32, Scalar::Int16));
7036 : case uint16_t(Op::I32Store):
7037 0 : CHECK_NEXT(emitStore(ValType::I32, Scalar::Int32));
7038 : case uint16_t(Op::I32Rotr):
7039 0 : CHECK_NEXT(emitBinary(emitRotrI32, ValType::I32));
7040 : case uint16_t(Op::I32Rotl):
7041 0 : CHECK_NEXT(emitBinary(emitRotlI32, ValType::I32));
7042 :
7043 : // I64
7044 : case uint16_t(Op::I64Const): {
7045 : int64_t i64;
7046 0 : CHECK(iter_.readI64Const(&i64));
7047 0 : if (!deadCode_)
7048 0 : pushI64(i64);
7049 0 : NEXT();
7050 : }
7051 : case uint16_t(Op::I64Add):
7052 0 : CHECK_NEXT(emitBinary(emitAddI64, ValType::I64));
7053 : case uint16_t(Op::I64Sub):
7054 0 : CHECK_NEXT(emitBinary(emitSubtractI64, ValType::I64));
7055 : case uint16_t(Op::I64Mul):
7056 0 : CHECK_NEXT(emitBinary(emitMultiplyI64, ValType::I64));
7057 : case uint16_t(Op::I64DivS):
7058 : #ifdef INT_DIV_I64_CALLOUT
7059 : CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall, SymbolicAddress::DivI64,
7060 : ValType::I64));
7061 : #else
7062 0 : CHECK_NEXT(emitBinary(emitQuotientI64, ValType::I64));
7063 : #endif
7064 : case uint16_t(Op::I64DivU):
7065 : #ifdef INT_DIV_I64_CALLOUT
7066 : CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall, SymbolicAddress::UDivI64,
7067 : ValType::I64));
7068 : #else
7069 0 : CHECK_NEXT(emitBinary(emitQuotientU64, ValType::I64));
7070 : #endif
7071 : case uint16_t(Op::I64RemS):
7072 : #ifdef INT_DIV_I64_CALLOUT
7073 : CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall, SymbolicAddress::ModI64,
7074 : ValType::I64));
7075 : #else
7076 0 : CHECK_NEXT(emitBinary(emitRemainderI64, ValType::I64));
7077 : #endif
7078 : case uint16_t(Op::I64RemU):
7079 : #ifdef INT_DIV_I64_CALLOUT
7080 : CHECK_NEXT(emitIntDivCallout(emitDivOrModI64BuiltinCall, SymbolicAddress::UModI64,
7081 : ValType::I64));
7082 : #else
7083 0 : CHECK_NEXT(emitBinary(emitRemainderU64, ValType::I64));
7084 : #endif
7085 : case uint16_t(Op::I64TruncSF32):
7086 : #ifdef FLOAT_TO_I64_CALLOUT
7087 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
7088 : SymbolicAddress::TruncateDoubleToInt64,
7089 : ValType::F32, ValType::I64));
7090 : #else
7091 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<false>, ValType::F32, ValType::I64));
7092 : #endif
7093 : case uint16_t(Op::I64TruncUF32):
7094 : #ifdef FLOAT_TO_I64_CALLOUT
7095 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
7096 : SymbolicAddress::TruncateDoubleToUint64,
7097 : ValType::F32, ValType::I64));
7098 : #else
7099 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<true>, ValType::F32, ValType::I64));
7100 : #endif
7101 : case uint16_t(Op::I64TruncSF64):
7102 : #ifdef FLOAT_TO_I64_CALLOUT
7103 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
7104 : SymbolicAddress::TruncateDoubleToInt64,
7105 : ValType::F64, ValType::I64));
7106 : #else
7107 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<false>, ValType::F64, ValType::I64));
7108 : #endif
7109 : case uint16_t(Op::I64TruncUF64):
7110 : #ifdef FLOAT_TO_I64_CALLOUT
7111 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
7112 : SymbolicAddress::TruncateDoubleToUint64,
7113 : ValType::F64, ValType::I64));
7114 : #else
7115 0 : CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<true>, ValType::F64, ValType::I64));
7116 : #endif
7117 : case uint16_t(Op::I64ExtendSI32):
7118 0 : CHECK_NEXT(emitConversion(emitExtendI32ToI64, ValType::I32, ValType::I64));
7119 : case uint16_t(Op::I64ExtendUI32):
7120 0 : CHECK_NEXT(emitConversion(emitExtendU32ToI64, ValType::I32, ValType::I64));
7121 : case uint16_t(Op::I64ReinterpretF64):
7122 0 : CHECK_NEXT(emitConversion(emitReinterpretF64AsI64, ValType::F64, ValType::I64));
7123 : case uint16_t(Op::I64Or):
7124 0 : CHECK_NEXT(emitBinary(emitOrI64, ValType::I64));
7125 : case uint16_t(Op::I64And):
7126 0 : CHECK_NEXT(emitBinary(emitAndI64, ValType::I64));
7127 : case uint16_t(Op::I64Xor):
7128 0 : CHECK_NEXT(emitBinary(emitXorI64, ValType::I64));
7129 : case uint16_t(Op::I64Shl):
7130 0 : CHECK_NEXT(emitBinary(emitShlI64, ValType::I64));
7131 : case uint16_t(Op::I64ShrS):
7132 0 : CHECK_NEXT(emitBinary(emitShrI64, ValType::I64));
7133 : case uint16_t(Op::I64ShrU):
7134 0 : CHECK_NEXT(emitBinary(emitShrU64, ValType::I64));
7135 : case uint16_t(Op::I64Rotr):
7136 0 : CHECK_NEXT(emitBinary(emitRotrI64, ValType::I64));
7137 : case uint16_t(Op::I64Rotl):
7138 0 : CHECK_NEXT(emitBinary(emitRotlI64, ValType::I64));
7139 : case uint16_t(Op::I64Clz):
7140 0 : CHECK_NEXT(emitUnary(emitClzI64, ValType::I64));
7141 : case uint16_t(Op::I64Ctz):
7142 0 : CHECK_NEXT(emitUnary(emitCtzI64, ValType::I64));
7143 : case uint16_t(Op::I64Popcnt):
7144 0 : CHECK_NEXT(emitUnary(emitPopcntI64, ValType::I64));
7145 : case uint16_t(Op::I64Eqz):
7146 0 : CHECK_NEXT(emitConversion(emitEqzI64, ValType::I64, ValType::I32));
7147 : case uint16_t(Op::I64Load8S):
7148 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int8));
7149 : case uint16_t(Op::I64Load16S):
7150 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int16));
7151 : case uint16_t(Op::I64Load32S):
7152 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int32));
7153 : case uint16_t(Op::I64Load8U):
7154 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint8));
7155 : case uint16_t(Op::I64Load16U):
7156 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint16));
7157 : case uint16_t(Op::I64Load32U):
7158 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint32));
7159 : case uint16_t(Op::I64Load):
7160 0 : CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int64));
7161 : case uint16_t(Op::I64Store8):
7162 0 : CHECK_NEXT(emitStore(ValType::I64, Scalar::Int8));
7163 : case uint16_t(Op::I64Store16):
7164 0 : CHECK_NEXT(emitStore(ValType::I64, Scalar::Int16));
7165 : case uint16_t(Op::I64Store32):
7166 0 : CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32));
7167 : case uint16_t(Op::I64Store):
7168 0 : CHECK_NEXT(emitStore(ValType::I64, Scalar::Int64));
7169 :
7170 : // F32
7171 : case uint16_t(Op::F32Const): {
7172 : float f32;
7173 0 : CHECK(iter_.readF32Const(&f32));
7174 0 : if (!deadCode_)
7175 0 : pushF32(f32);
7176 0 : NEXT();
7177 : }
7178 : case uint16_t(Op::F32Add):
7179 0 : CHECK_NEXT(emitBinary(emitAddF32, ValType::F32));
7180 : case uint16_t(Op::F32Sub):
7181 0 : CHECK_NEXT(emitBinary(emitSubtractF32, ValType::F32));
7182 : case uint16_t(Op::F32Mul):
7183 0 : CHECK_NEXT(emitBinary(emitMultiplyF32, ValType::F32));
7184 : case uint16_t(Op::F32Div):
7185 0 : CHECK_NEXT(emitBinary(emitDivideF32, ValType::F32));
7186 : case uint16_t(Op::F32Min):
7187 0 : CHECK_NEXT(emitBinary(emitMinF32, ValType::F32));
7188 : case uint16_t(Op::F32Max):
7189 0 : CHECK_NEXT(emitBinary(emitMaxF32, ValType::F32));
7190 : case uint16_t(Op::F32Neg):
7191 0 : CHECK_NEXT(emitUnary(emitNegateF32, ValType::F32));
7192 : case uint16_t(Op::F32Abs):
7193 0 : CHECK_NEXT(emitUnary(emitAbsF32, ValType::F32));
7194 : case uint16_t(Op::F32Sqrt):
7195 0 : CHECK_NEXT(emitUnary(emitSqrtF32, ValType::F32));
7196 : case uint16_t(Op::F32Ceil):
7197 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::CeilF, ValType::F32));
7198 : case uint16_t(Op::F32Floor):
7199 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::FloorF, ValType::F32));
7200 : case uint16_t(Op::F32DemoteF64):
7201 0 : CHECK_NEXT(emitConversion(emitConvertF64ToF32, ValType::F64, ValType::F32));
7202 : case uint16_t(Op::F32ConvertSI32):
7203 0 : CHECK_NEXT(emitConversion(emitConvertI32ToF32, ValType::I32, ValType::F32));
7204 : case uint16_t(Op::F32ConvertUI32):
7205 0 : CHECK_NEXT(emitConversion(emitConvertU32ToF32, ValType::I32, ValType::F32));
7206 : case uint16_t(Op::F32ConvertSI64):
7207 : #ifdef I64_TO_FLOAT_CALLOUT
7208 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
7209 : SymbolicAddress::Int64ToFloat32,
7210 : ValType::I64, ValType::F32));
7211 : #else
7212 0 : CHECK_NEXT(emitConversion(emitConvertI64ToF32, ValType::I64, ValType::F32));
7213 : #endif
7214 : case uint16_t(Op::F32ConvertUI64):
7215 : #ifdef I64_TO_FLOAT_CALLOUT
7216 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
7217 : SymbolicAddress::Uint64ToFloat32,
7218 : ValType::I64, ValType::F32));
7219 : #else
7220 0 : CHECK_NEXT(emitConversion(emitConvertU64ToF32, ValType::I64, ValType::F32));
7221 : #endif
7222 : case uint16_t(Op::F32ReinterpretI32):
7223 0 : CHECK_NEXT(emitConversion(emitReinterpretI32AsF32, ValType::I32, ValType::F32));
7224 : case uint16_t(Op::F32Load):
7225 0 : CHECK_NEXT(emitLoad(ValType::F32, Scalar::Float32));
7226 : case uint16_t(Op::F32Store):
7227 0 : CHECK_NEXT(emitStore(ValType::F32, Scalar::Float32));
7228 : case uint16_t(Op::F32CopySign):
7229 0 : CHECK_NEXT(emitBinary(emitCopysignF32, ValType::F32));
7230 : case uint16_t(Op::F32Nearest):
7231 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntF, ValType::F32));
7232 : case uint16_t(Op::F32Trunc):
7233 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::TruncF, ValType::F32));
7234 :
7235 : // F64
7236 : case uint16_t(Op::F64Const): {
7237 : double f64;
7238 0 : CHECK(iter_.readF64Const(&f64));
7239 0 : if (!deadCode_)
7240 0 : pushF64(f64);
7241 0 : NEXT();
7242 : }
7243 : case uint16_t(Op::F64Add):
7244 0 : CHECK_NEXT(emitBinary(emitAddF64, ValType::F64));
7245 : case uint16_t(Op::F64Sub):
7246 0 : CHECK_NEXT(emitBinary(emitSubtractF64, ValType::F64));
7247 : case uint16_t(Op::F64Mul):
7248 0 : CHECK_NEXT(emitBinary(emitMultiplyF64, ValType::F64));
7249 : case uint16_t(Op::F64Div):
7250 0 : CHECK_NEXT(emitBinary(emitDivideF64, ValType::F64));
7251 : case uint16_t(Op::F64Min):
7252 0 : CHECK_NEXT(emitBinary(emitMinF64, ValType::F64));
7253 : case uint16_t(Op::F64Max):
7254 0 : CHECK_NEXT(emitBinary(emitMaxF64, ValType::F64));
7255 : case uint16_t(Op::F64Neg):
7256 0 : CHECK_NEXT(emitUnary(emitNegateF64, ValType::F64));
7257 : case uint16_t(Op::F64Abs):
7258 0 : CHECK_NEXT(emitUnary(emitAbsF64, ValType::F64));
7259 : case uint16_t(Op::F64Sqrt):
7260 0 : CHECK_NEXT(emitUnary(emitSqrtF64, ValType::F64));
7261 : case uint16_t(Op::F64Ceil):
7262 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::CeilD, ValType::F64));
7263 : case uint16_t(Op::F64Floor):
7264 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::FloorD, ValType::F64));
7265 : case uint16_t(Op::F64PromoteF32):
7266 0 : CHECK_NEXT(emitConversion(emitConvertF32ToF64, ValType::F32, ValType::F64));
7267 : case uint16_t(Op::F64ConvertSI32):
7268 0 : CHECK_NEXT(emitConversion(emitConvertI32ToF64, ValType::I32, ValType::F64));
7269 : case uint16_t(Op::F64ConvertUI32):
7270 0 : CHECK_NEXT(emitConversion(emitConvertU32ToF64, ValType::I32, ValType::F64));
7271 : case uint16_t(Op::F64ConvertSI64):
7272 : #ifdef I64_TO_FLOAT_CALLOUT
7273 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
7274 : SymbolicAddress::Int64ToDouble,
7275 : ValType::I64, ValType::F64));
7276 : #else
7277 0 : CHECK_NEXT(emitConversion(emitConvertI64ToF64, ValType::I64, ValType::F64));
7278 : #endif
7279 : case uint16_t(Op::F64ConvertUI64):
7280 : #ifdef I64_TO_FLOAT_CALLOUT
7281 : CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
7282 : SymbolicAddress::Uint64ToDouble,
7283 : ValType::I64, ValType::F64));
7284 : #else
7285 0 : CHECK_NEXT(emitConversion(emitConvertU64ToF64, ValType::I64, ValType::F64));
7286 : #endif
7287 : case uint16_t(Op::F64Load):
7288 0 : CHECK_NEXT(emitLoad(ValType::F64, Scalar::Float64));
7289 : case uint16_t(Op::F64Store):
7290 0 : CHECK_NEXT(emitStore(ValType::F64, Scalar::Float64));
7291 : case uint16_t(Op::F64ReinterpretI64):
7292 0 : CHECK_NEXT(emitConversion(emitReinterpretI64AsF64, ValType::I64, ValType::F64));
7293 : case uint16_t(Op::F64CopySign):
7294 0 : CHECK_NEXT(emitBinary(emitCopysignF64, ValType::F64));
7295 : case uint16_t(Op::F64Nearest):
7296 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntD, ValType::F64));
7297 : case uint16_t(Op::F64Trunc):
7298 0 : CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::TruncD, ValType::F64));
7299 :
7300 : // Comparisons
7301 : case uint16_t(Op::I32Eq):
7302 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::Equal));
7303 : case uint16_t(Op::I32Ne):
7304 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::NotEqual));
7305 : case uint16_t(Op::I32LtS):
7306 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::LessThan));
7307 : case uint16_t(Op::I32LeS):
7308 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::LessThanOrEqual));
7309 : case uint16_t(Op::I32GtS):
7310 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::GreaterThan));
7311 : case uint16_t(Op::I32GeS):
7312 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::GreaterThanOrEqual));
7313 : case uint16_t(Op::I32LtU):
7314 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::Below));
7315 : case uint16_t(Op::I32LeU):
7316 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::BelowOrEqual));
7317 : case uint16_t(Op::I32GtU):
7318 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::Above));
7319 : case uint16_t(Op::I32GeU):
7320 0 : CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, Assembler::AboveOrEqual));
7321 : case uint16_t(Op::I64Eq):
7322 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::Equal));
7323 : case uint16_t(Op::I64Ne):
7324 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::NotEqual));
7325 : case uint16_t(Op::I64LtS):
7326 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::LessThan));
7327 : case uint16_t(Op::I64LeS):
7328 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::LessThanOrEqual));
7329 : case uint16_t(Op::I64GtS):
7330 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::GreaterThan));
7331 : case uint16_t(Op::I64GeS):
7332 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::GreaterThanOrEqual));
7333 : case uint16_t(Op::I64LtU):
7334 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::Below));
7335 : case uint16_t(Op::I64LeU):
7336 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::BelowOrEqual));
7337 : case uint16_t(Op::I64GtU):
7338 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::Above));
7339 : case uint16_t(Op::I64GeU):
7340 0 : CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, Assembler::AboveOrEqual));
7341 : case uint16_t(Op::F32Eq):
7342 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleEqual));
7343 : case uint16_t(Op::F32Ne):
7344 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleNotEqualOrUnordered));
7345 : case uint16_t(Op::F32Lt):
7346 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleLessThan));
7347 : case uint16_t(Op::F32Le):
7348 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleLessThanOrEqual));
7349 : case uint16_t(Op::F32Gt):
7350 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleGreaterThan));
7351 : case uint16_t(Op::F32Ge):
7352 0 : CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, Assembler::DoubleGreaterThanOrEqual));
7353 : case uint16_t(Op::F64Eq):
7354 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleEqual));
7355 : case uint16_t(Op::F64Ne):
7356 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleNotEqualOrUnordered));
7357 : case uint16_t(Op::F64Lt):
7358 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThan));
7359 : case uint16_t(Op::F64Le):
7360 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThanOrEqual));
7361 : case uint16_t(Op::F64Gt):
7362 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThan));
7363 : case uint16_t(Op::F64Ge):
7364 0 : CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThanOrEqual));
7365 :
7366 : // Memory Related
7367 : case uint16_t(Op::GrowMemory):
7368 0 : CHECK_NEXT(emitGrowMemory());
7369 : case uint16_t(Op::CurrentMemory):
7370 0 : CHECK_NEXT(emitCurrentMemory());
7371 :
7372 : default:
7373 0 : return iter_.unrecognizedOpcode(&op);
7374 : }
7375 :
7376 : #undef CHECK
7377 : #undef NEXT
7378 : #undef CHECK_NEXT
7379 : #undef emitBinary
7380 : #undef emitUnary
7381 : #undef emitComparison
7382 : #undef emitConversion
7383 : #undef emitConversionOOM
7384 : #undef emitCalloutConversionOOM
7385 :
7386 : MOZ_CRASH("unreachable");
7387 0 : }
7388 :
7389 : MOZ_CRASH("unreachable");
7390 : }
7391 :
7392 : bool
7393 0 : BaseCompiler::emitFunction()
7394 : {
7395 0 : beginFunction();
7396 :
7397 0 : if (!emitBody())
7398 0 : return false;
7399 :
7400 0 : if (!endFunction())
7401 0 : return false;
7402 :
7403 0 : return true;
7404 : }
7405 :
7406 0 : BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
7407 : Decoder& decoder,
7408 : const FuncBytes& func,
7409 : const ValTypeVector& locals,
7410 : bool debugEnabled,
7411 : TempAllocator* alloc,
7412 0 : MacroAssembler* masm)
7413 : : env_(env),
7414 : iter_(env, decoder),
7415 : func_(func),
7416 : lastReadCallSite_(0),
7417 : alloc_(*alloc),
7418 : locals_(locals),
7419 : localSize_(0),
7420 : varLow_(0),
7421 : varHigh_(0),
7422 : maxFramePushed_(0),
7423 : deadCode_(false),
7424 : debugEnabled_(debugEnabled),
7425 : bceSafe_(0),
7426 : stackAddOffset_(0),
7427 : latentOp_(LatentOp::None),
7428 : latentType_(ValType::I32),
7429 : latentIntCmp_(Assembler::Equal),
7430 : latentDoubleCmp_(Assembler::DoubleEqual),
7431 : masm(*masm),
7432 0 : availGPR_(GeneralRegisterSet::All()),
7433 0 : availFPU_(FloatRegisterSet::All()),
7434 : #ifdef DEBUG
7435 : scratchRegisterTaken_(false),
7436 : #endif
7437 : #ifdef JS_CODEGEN_X64
7438 0 : specific_rax(RegI64(Register64(rax))),
7439 0 : specific_rcx(RegI64(Register64(rcx))),
7440 0 : specific_rdx(RegI64(Register64(rdx))),
7441 : #endif
7442 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
7443 : specific_eax(RegI32(eax)),
7444 : specific_ecx(RegI32(ecx)),
7445 : specific_edx(RegI32(edx)),
7446 : #endif
7447 : #ifdef JS_CODEGEN_X86
7448 : singleByteRegs_(GeneralRegisterSet(Registers::SingleByteRegs)),
7449 : abiReturnRegI64(RegI64(Register64(edx, eax))),
7450 : #endif
7451 : #ifdef JS_CODEGEN_ARM
7452 : abiReturnRegI64(ReturnReg64),
7453 : #endif
7454 : joinRegI32(RegI32(ReturnReg)),
7455 : joinRegI64(RegI64(ReturnReg64)),
7456 : joinRegF32(RegF32(ReturnFloat32Reg)),
7457 0 : joinRegF64(RegF64(ReturnDoubleReg))
7458 : {
7459 : // jit/RegisterAllocator.h: RegisterAllocator::RegisterAllocator()
7460 :
7461 : #if defined(JS_CODEGEN_X64)
7462 0 : availGPR_.take(HeapReg);
7463 : #elif defined(JS_CODEGEN_ARM)
7464 : availGPR_.take(HeapReg);
7465 : availGPR_.take(ScratchRegARM);
7466 : #elif defined(JS_CODEGEN_ARM64)
7467 : availGPR_.take(HeapReg);
7468 : availGPR_.take(HeapLenReg);
7469 : #elif defined(JS_CODEGEN_X86)
7470 : availGPR_.take(ScratchRegX86);
7471 : #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
7472 : availGPR_.take(HeapReg);
7473 : #endif
7474 0 : availGPR_.take(FramePointer);
7475 :
7476 : #ifdef DEBUG
7477 0 : setupRegisterLeakCheck();
7478 : #endif
7479 0 : }
7480 :
7481 : bool
7482 0 : BaseCompiler::init()
7483 : {
7484 0 : if (!SigD_.append(ValType::F64))
7485 0 : return false;
7486 0 : if (!SigF_.append(ValType::F32))
7487 0 : return false;
7488 0 : if (!SigP_.append(MIRType::Pointer))
7489 0 : return false;
7490 0 : if (!SigPI_.append(MIRType::Pointer) || !SigPI_.append(MIRType::Int32))
7491 0 : return false;
7492 0 : if (!SigI64I64_.append(ValType::I64) || !SigI64I64_.append(ValType::I64))
7493 0 : return false;
7494 :
7495 0 : const ValTypeVector& args = func_.sig().args();
7496 :
7497 0 : if (!localInfo_.resize(locals_.length()))
7498 0 : return false;
7499 :
7500 0 : BaseLocalIter i(locals_, args.length(), debugEnabled_);
7501 0 : varLow_ = i.reservedSize();
7502 0 : for (; !i.done() && i.index() < args.length(); i++) {
7503 0 : MOZ_ASSERT(i.isArg());
7504 0 : Local& l = localInfo_[i.index()];
7505 0 : l.init(i.mirType(), i.frameOffset());
7506 0 : varLow_ = i.currentLocalSize();
7507 : }
7508 :
7509 0 : varHigh_ = varLow_;
7510 0 : for (; !i.done() ; i++) {
7511 0 : MOZ_ASSERT(!i.isArg());
7512 0 : Local& l = localInfo_[i.index()];
7513 0 : l.init(i.mirType(), i.frameOffset());
7514 0 : varHigh_ = i.currentLocalSize();
7515 : }
7516 :
7517 0 : localSize_ = AlignBytes(varHigh_, 16u);
7518 :
7519 0 : addInterruptCheck();
7520 :
7521 0 : return true;
7522 : }
7523 :
7524 : FuncOffsets
7525 0 : BaseCompiler::finish()
7526 : {
7527 0 : MOZ_ASSERT(done(), "all bytes must be consumed");
7528 0 : MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
7529 :
7530 0 : masm.flushBuffer();
7531 :
7532 0 : return offsets_;
7533 : }
7534 :
7535 : } // wasm
7536 : } // js
7537 :
7538 : bool
7539 0 : js::wasm::BaselineCanCompile()
7540 : {
7541 : // On all platforms we require signals for Wasm.
7542 : // If we made it this far we must have signals.
7543 0 : MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
7544 :
7545 : #if defined(JS_CODEGEN_ARM)
7546 : // Simplifying assumption: require SDIV and UDIV.
7547 : //
7548 : // I have no good data on ARM populations allowing me to say that
7549 : // X% of devices in the market implement SDIV and UDIV. However,
7550 : // they are definitely implemented on the Cortex-A7 and Cortex-A15
7551 : // and on all ARMv8 systems.
7552 : if (!HasIDIV())
7553 : return false;
7554 : #endif
7555 :
7556 : #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
7557 0 : return true;
7558 : #else
7559 : return false;
7560 : #endif
7561 : }
7562 :
7563 : bool
7564 0 : js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars *error)
7565 : {
7566 0 : MOZ_ASSERT(task->tier() == Tier::Baseline);
7567 0 : MOZ_ASSERT(task->env().kind == ModuleKind::Wasm);
7568 :
7569 0 : const FuncBytes& func = unit->func();
7570 :
7571 0 : Decoder d(func.bytes().begin(), func.bytes().end(), func.lineOrBytecode(), error);
7572 :
7573 : // Build the local types vector.
7574 :
7575 0 : ValTypeVector locals;
7576 0 : if (!locals.appendAll(func.sig().args()))
7577 0 : return false;
7578 0 : if (!DecodeLocalEntries(d, task->env().kind, &locals))
7579 0 : return false;
7580 :
7581 : // The MacroAssembler will sometimes access the jitContext.
7582 :
7583 0 : JitContext jitContext(&task->alloc());
7584 :
7585 : // One-pass baseline compilation.
7586 :
7587 0 : BaseCompiler f(task->env(), d, func, locals, task->debugEnabled(), &task->alloc(), &task->masm());
7588 0 : if (!f.init())
7589 0 : return false;
7590 :
7591 0 : if (!f.emitFunction())
7592 0 : return false;
7593 :
7594 0 : unit->finish(f.finish());
7595 0 : return true;
7596 : }
7597 :
7598 : #undef INT_DIV_I64_CALLOUT
7599 : #undef I64_TO_FLOAT_CALLOUT
7600 : #undef FLOAT_TO_I64_CALLOUT
|