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 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jit/x64/MacroAssembler-x64.h"
8 :
9 : #include "jit/Bailouts.h"
10 : #include "jit/BaselineFrame.h"
11 : #include "jit/JitCompartment.h"
12 : #include "jit/JitFrames.h"
13 : #include "jit/MacroAssembler.h"
14 : #include "jit/MoveEmitter.h"
15 :
16 : #include "jit/MacroAssembler-inl.h"
17 :
18 : using namespace js;
19 : using namespace js::jit;
20 :
21 : void
22 0 : MacroAssemblerX64::loadConstantDouble(double d, FloatRegister dest)
23 : {
24 0 : if (maybeInlineDouble(d, dest))
25 0 : return;
26 0 : Double* dbl = getDouble(d);
27 0 : if (!dbl)
28 0 : return;
29 : // The constants will be stored in a pool appended to the text (see
30 : // finish()), so they will always be a fixed distance from the
31 : // instructions which reference them. This allows the instructions to use
32 : // PC-relative addressing. Use "jump" label support code, because we need
33 : // the same PC-relative address patching that jumps use.
34 0 : JmpSrc j = masm.vmovsd_ripr(dest.encoding());
35 0 : propagateOOM(dbl->uses.append(CodeOffset(j.offset())));
36 : }
37 :
38 : void
39 0 : MacroAssemblerX64::loadConstantFloat32(float f, FloatRegister dest)
40 : {
41 0 : if (maybeInlineFloat(f, dest))
42 0 : return;
43 0 : Float* flt = getFloat(f);
44 0 : if (!flt)
45 0 : return;
46 : // See comment in loadConstantDouble
47 0 : JmpSrc j = masm.vmovss_ripr(dest.encoding());
48 0 : propagateOOM(flt->uses.append(CodeOffset(j.offset())));
49 : }
50 :
51 : void
52 0 : MacroAssemblerX64::loadConstantSimd128Int(const SimdConstant& v, FloatRegister dest)
53 : {
54 0 : if (maybeInlineSimd128Int(v, dest))
55 0 : return;
56 0 : SimdData* val = getSimdData(v);
57 0 : if (!val)
58 0 : return;
59 0 : JmpSrc j = masm.vmovdqa_ripr(dest.encoding());
60 0 : propagateOOM(val->uses.append(CodeOffset(j.offset())));
61 : }
62 :
63 : void
64 0 : MacroAssemblerX64::loadConstantSimd128Float(const SimdConstant&v, FloatRegister dest)
65 : {
66 0 : if (maybeInlineSimd128Float(v, dest))
67 0 : return;
68 0 : SimdData* val = getSimdData(v);
69 0 : if (!val)
70 0 : return;
71 0 : JmpSrc j = masm.vmovaps_ripr(dest.encoding());
72 0 : propagateOOM(val->uses.append(CodeOffset(j.offset())));
73 : }
74 :
75 : void
76 0 : MacroAssemblerX64::convertInt64ToDouble(Register64 input, FloatRegister output)
77 : {
78 : // Zero the output register to break dependencies, see convertInt32ToDouble.
79 0 : zeroDouble(output);
80 :
81 0 : vcvtsq2sd(input.reg, output, output);
82 0 : }
83 :
84 : void
85 0 : MacroAssemblerX64::convertInt64ToFloat32(Register64 input, FloatRegister output)
86 : {
87 : // Zero the output register to break dependencies, see convertInt32ToDouble.
88 0 : zeroFloat32(output);
89 :
90 0 : vcvtsq2ss(input.reg, output, output);
91 0 : }
92 :
93 : bool
94 0 : MacroAssemblerX64::convertUInt64ToDoubleNeedsTemp()
95 : {
96 0 : return true;
97 : }
98 :
99 : void
100 0 : MacroAssemblerX64::convertUInt64ToDouble(Register64 input, FloatRegister output, Register temp)
101 : {
102 : // Zero the output register to break dependencies, see convertInt32ToDouble.
103 0 : zeroDouble(output);
104 :
105 : // If the input's sign bit is not set we use vcvtsq2sd directly.
106 : // Else, we divide by 2 and keep the LSB, convert to double, and multiply
107 : // the result by 2.
108 0 : Label done;
109 0 : Label isSigned;
110 :
111 0 : testq(input.reg, input.reg);
112 0 : j(Assembler::Signed, &isSigned);
113 0 : vcvtsq2sd(input.reg, output, output);
114 0 : jump(&done);
115 :
116 0 : bind(&isSigned);
117 :
118 0 : ScratchRegisterScope scratch(asMasm());
119 0 : mov(input.reg, scratch);
120 0 : mov(input.reg, temp);
121 0 : shrq(Imm32(1), scratch);
122 0 : andq(Imm32(1), temp);
123 0 : orq(temp, scratch);
124 :
125 0 : vcvtsq2sd(scratch, output, output);
126 0 : vaddsd(output, output, output);
127 :
128 0 : bind(&done);
129 0 : }
130 :
131 : void
132 0 : MacroAssemblerX64::convertUInt64ToFloat32(Register64 input, FloatRegister output, Register temp)
133 : {
134 : // Zero the output register to break dependencies, see convertInt32ToDouble.
135 0 : zeroFloat32(output);
136 :
137 : // See comment in convertUInt64ToDouble.
138 0 : Label done;
139 0 : Label isSigned;
140 :
141 0 : testq(input.reg, input.reg);
142 0 : j(Assembler::Signed, &isSigned);
143 0 : vcvtsq2ss(input.reg, output, output);
144 0 : jump(&done);
145 :
146 0 : bind(&isSigned);
147 :
148 0 : ScratchRegisterScope scratch(asMasm());
149 0 : mov(input.reg, scratch);
150 0 : mov(input.reg, temp);
151 0 : shrq(Imm32(1), scratch);
152 0 : andq(Imm32(1), temp);
153 0 : orq(temp, scratch);
154 :
155 0 : vcvtsq2ss(scratch, output, output);
156 0 : vaddss(output, output, output);
157 :
158 0 : bind(&done);
159 0 : }
160 :
161 : void
162 0 : MacroAssemblerX64::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, Label* oolEntry,
163 : Label* oolRejoin, FloatRegister tempReg)
164 : {
165 0 : vcvttsd2sq(input, output.reg);
166 0 : cmpq(Imm32(1), output.reg);
167 0 : j(Assembler::Overflow, oolEntry);
168 0 : bind(oolRejoin);
169 0 : }
170 :
171 : void
172 0 : MacroAssemblerX64::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, Label* oolEntry,
173 : Label* oolRejoin, FloatRegister tempReg)
174 : {
175 0 : vcvttss2sq(input, output.reg);
176 0 : cmpq(Imm32(1), output.reg);
177 0 : j(Assembler::Overflow, oolEntry);
178 0 : bind(oolRejoin);
179 0 : }
180 :
181 : void
182 0 : MacroAssemblerX64::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
183 : Label* oolRejoin, FloatRegister tempReg)
184 : {
185 : // If the input < INT64_MAX, vcvttsd2sq will do the right thing, so
186 : // we use it directly. Else, we subtract INT64_MAX, convert to int64,
187 : // and then add INT64_MAX to the result.
188 :
189 0 : Label isLarge;
190 :
191 0 : ScratchDoubleScope scratch(asMasm());
192 0 : loadConstantDouble(double(0x8000000000000000), scratch);
193 0 : asMasm().branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
194 0 : vcvttsd2sq(input, output.reg);
195 0 : testq(output.reg, output.reg);
196 0 : j(Assembler::Signed, oolEntry);
197 0 : jump(oolRejoin);
198 :
199 0 : bind(&isLarge);
200 :
201 0 : moveDouble(input, tempReg);
202 0 : vsubsd(scratch, tempReg, tempReg);
203 0 : vcvttsd2sq(tempReg, output.reg);
204 0 : testq(output.reg, output.reg);
205 0 : j(Assembler::Signed, oolEntry);
206 0 : asMasm().or64(Imm64(0x8000000000000000), output);
207 :
208 0 : bind(oolRejoin);
209 0 : }
210 :
211 : void
212 0 : MacroAssemblerX64::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, Label* oolEntry,
213 : Label* oolRejoin, FloatRegister tempReg)
214 : {
215 : // If the input < INT64_MAX, vcvttss2sq will do the right thing, so
216 : // we use it directly. Else, we subtract INT64_MAX, convert to int64,
217 : // and then add INT64_MAX to the result.
218 :
219 0 : Label isLarge;
220 :
221 0 : ScratchFloat32Scope scratch(asMasm());
222 0 : loadConstantFloat32(float(0x8000000000000000), scratch);
223 0 : asMasm().branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &isLarge);
224 0 : vcvttss2sq(input, output.reg);
225 0 : testq(output.reg, output.reg);
226 0 : j(Assembler::Signed, oolEntry);
227 0 : jump(oolRejoin);
228 :
229 0 : bind(&isLarge);
230 :
231 0 : moveFloat32(input, tempReg);
232 0 : vsubss(scratch, tempReg, tempReg);
233 0 : vcvttss2sq(tempReg, output.reg);
234 0 : testq(output.reg, output.reg);
235 0 : j(Assembler::Signed, oolEntry);
236 0 : asMasm().or64(Imm64(0x8000000000000000), output);
237 :
238 0 : bind(oolRejoin);
239 0 : }
240 :
241 : void
242 0 : MacroAssemblerX64::bindOffsets(const MacroAssemblerX86Shared::UsesVector& uses)
243 : {
244 0 : for (CodeOffset use : uses) {
245 0 : JmpDst dst(currentOffset());
246 0 : JmpSrc src(use.offset());
247 : // Using linkJump here is safe, as explaind in the comment in
248 : // loadConstantDouble.
249 0 : masm.linkJump(src, dst);
250 : }
251 0 : }
252 :
253 : void
254 4499 : MacroAssemblerX64::finish()
255 : {
256 4499 : if (!doubles_.empty())
257 0 : masm.haltingAlign(sizeof(double));
258 4499 : for (const Double& d : doubles_) {
259 0 : bindOffsets(d.uses);
260 0 : masm.doubleConstant(d.value);
261 : }
262 :
263 4499 : if (!floats_.empty())
264 0 : masm.haltingAlign(sizeof(float));
265 4499 : for (const Float& f : floats_) {
266 0 : bindOffsets(f.uses);
267 0 : masm.floatConstant(f.value);
268 : }
269 :
270 : // SIMD memory values must be suitably aligned.
271 4499 : if (!simds_.empty())
272 0 : masm.haltingAlign(SimdMemoryAlignment);
273 4499 : for (const SimdData& v : simds_) {
274 0 : bindOffsets(v.uses);
275 0 : masm.simd128Constant(v.value.bytes());
276 : }
277 :
278 4499 : MacroAssemblerX86Shared::finish();
279 4499 : }
280 :
281 : void
282 394 : MacroAssemblerX64::boxValue(JSValueType type, Register src, Register dest)
283 : {
284 394 : MOZ_ASSERT(src != dest);
285 :
286 394 : JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
287 : #ifdef DEBUG
288 394 : if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
289 456 : Label upper32BitsZeroed;
290 228 : movePtr(ImmWord(UINT32_MAX), dest);
291 228 : asMasm().branchPtr(Assembler::BelowOrEqual, src, dest, &upper32BitsZeroed);
292 228 : breakpoint();
293 228 : bind(&upper32BitsZeroed);
294 : }
295 : #endif
296 394 : mov(ImmShiftedTag(tag), dest);
297 394 : orq(src, dest);
298 394 : }
299 :
300 : void
301 4 : MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
302 : {
303 : // Reserve space for exception information.
304 4 : subq(Imm32(sizeof(ResumeFromException)), rsp);
305 4 : movq(rsp, rax);
306 :
307 : // Call the handler.
308 4 : asMasm().setupUnalignedABICall(rcx);
309 4 : asMasm().passABIArg(rax);
310 4 : asMasm().callWithABI(handler);
311 :
312 8 : Label entryFrame;
313 8 : Label catch_;
314 8 : Label finally;
315 8 : Label return_;
316 8 : Label bailout;
317 :
318 4 : load32(Address(rsp, offsetof(ResumeFromException, kind)), rax);
319 4 : asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
320 4 : asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
321 4 : asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
322 4 : asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
323 4 : asMasm().branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
324 :
325 4 : breakpoint(); // Invalid kind.
326 :
327 : // No exception handler. Load the error value, load the new stack pointer
328 : // and return from the entry frame.
329 4 : bind(&entryFrame);
330 4 : moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
331 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
332 4 : ret();
333 :
334 : // If we found a catch handler, this must be a baseline frame. Restore state
335 : // and jump to the catch block.
336 4 : bind(&catch_);
337 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, target)), rax);
338 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp);
339 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
340 4 : jmp(Operand(rax));
341 :
342 : // If we found a finally block, this must be a baseline frame. Push
343 : // two values expected by JSOP_RETSUB: BooleanValue(true) and the
344 : // exception.
345 4 : bind(&finally);
346 4 : ValueOperand exception = ValueOperand(rcx);
347 4 : loadValue(Address(esp, offsetof(ResumeFromException, exception)), exception);
348 :
349 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, target)), rax);
350 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp);
351 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
352 :
353 4 : pushValue(BooleanValue(true));
354 4 : pushValue(exception);
355 4 : jmp(Operand(rax));
356 :
357 : // Only used in debug mode. Return BaselineFrame->returnValue() to the caller.
358 4 : bind(&return_);
359 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp);
360 4 : loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
361 4 : loadValue(Address(rbp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand);
362 4 : movq(rbp, rsp);
363 4 : pop(rbp);
364 :
365 : // If profiling is enabled, then update the lastProfilingFrame to refer to caller
366 : // frame before returning.
367 : {
368 8 : Label skipProfilingInstrumentation;
369 4 : AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->geckoProfiler().addressOfEnabled());
370 4 : asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation);
371 4 : profilerExitFrame();
372 4 : bind(&skipProfilingInstrumentation);
373 : }
374 :
375 4 : ret();
376 :
377 : // If we are bailing out to baseline to handle an exception, jump to
378 : // the bailout tail stub.
379 4 : bind(&bailout);
380 4 : loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), r9);
381 4 : mov(ImmWord(BAILOUT_RETURN_OK), rax);
382 4 : jmp(Operand(rsp, offsetof(ResumeFromException, target)));
383 4 : }
384 :
385 : void
386 632 : MacroAssemblerX64::profilerEnterFrame(Register framePtr, Register scratch)
387 : {
388 632 : asMasm().loadJSContext(scratch);
389 632 : loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
390 632 : storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
391 632 : storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
392 632 : }
393 :
394 : void
395 631 : MacroAssemblerX64::profilerExitFrame()
396 : {
397 631 : jmp(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
398 631 : }
399 :
400 : MacroAssembler&
401 16092 : MacroAssemblerX64::asMasm()
402 : {
403 16092 : return *static_cast<MacroAssembler*>(this);
404 : }
405 :
406 : const MacroAssembler&
407 0 : MacroAssemblerX64::asMasm() const
408 : {
409 0 : return *static_cast<const MacroAssembler*>(this);
410 : }
411 :
412 : void
413 25400 : MacroAssembler::subFromStackPtr(Imm32 imm32)
414 : {
415 25400 : if (imm32.value) {
416 : // On windows, we cannot skip very far down the stack without touching the
417 : // memory pages in-between. This is a corner-case code for situations where the
418 : // Ion frame data for a piece of code is very large. To handle this special case,
419 : // for frames over 4k in size we allocate memory on the stack incrementally, touching
420 : // it as we go.
421 : //
422 : // When the amount is quite large, which it can be, we emit an actual loop, in order
423 : // to keep the function prologue compact. Compactness is a requirement for eg
424 : // Wasm's CodeRange data structure, which can encode only 8-bit offsets.
425 25064 : uint32_t amountLeft = imm32.value;
426 25064 : uint32_t fullPages = amountLeft / 4096;
427 25064 : if (fullPages <= 8) {
428 25064 : while (amountLeft > 4096) {
429 0 : subq(Imm32(4096), StackPointer);
430 0 : store32(Imm32(0), Address(StackPointer, 0));
431 0 : amountLeft -= 4096;
432 : }
433 25064 : subq(Imm32(amountLeft), StackPointer);
434 : } else {
435 0 : ScratchRegisterScope scratch(*this);
436 0 : Label top;
437 0 : move32(Imm32(fullPages), scratch);
438 0 : bind(&top);
439 0 : subq(Imm32(4096), StackPointer);
440 0 : store32(Imm32(0), Address(StackPointer, 0));
441 0 : subl(Imm32(1), scratch);
442 0 : j(Assembler::NonZero, &top);
443 0 : amountLeft -= fullPages * 4096;
444 0 : if (amountLeft)
445 0 : subq(Imm32(amountLeft), StackPointer);
446 : }
447 : }
448 25400 : }
449 :
450 : //{{{ check_macroassembler_style
451 : // ===============================================================
452 : // ABI function calls.
453 :
454 : void
455 13023 : MacroAssembler::setupUnalignedABICall(Register scratch)
456 : {
457 13023 : MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
458 13023 : setupABICall();
459 13023 : dynamicAlignment_ = true;
460 :
461 13023 : movq(rsp, scratch);
462 13023 : andq(Imm32(~(ABIStackAlignment - 1)), rsp);
463 13023 : push(scratch);
464 13023 : }
465 :
466 : void
467 13023 : MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
468 : {
469 13023 : MOZ_ASSERT(inCall_);
470 13023 : uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
471 :
472 13023 : if (dynamicAlignment_) {
473 : // sizeof(intptr_t) accounts for the saved stack pointer pushed by
474 : // setupUnalignedABICall.
475 13023 : stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
476 : ABIStackAlignment);
477 : } else {
478 0 : uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0;
479 0 : stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
480 : ABIStackAlignment);
481 : }
482 :
483 13023 : *stackAdjust = stackForCall;
484 13023 : reserveStack(stackForCall);
485 :
486 : // Position all arguments.
487 : {
488 13023 : enoughMemory_ &= moveResolver_.resolve();
489 13023 : if (!enoughMemory_)
490 0 : return;
491 :
492 26046 : MoveEmitter emitter(*this);
493 13023 : emitter.emit(moveResolver_);
494 13023 : emitter.finish();
495 : }
496 :
497 13023 : assertStackAlignment(ABIStackAlignment);
498 : }
499 :
500 : void
501 13023 : MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool cleanupArg)
502 : {
503 13023 : freeStack(stackAdjust);
504 13023 : if (dynamicAlignment_)
505 13023 : pop(rsp);
506 :
507 : #ifdef DEBUG
508 13023 : MOZ_ASSERT(inCall_);
509 13023 : inCall_ = false;
510 : #endif
511 13023 : }
512 :
513 : static bool
514 154 : IsIntArgReg(Register reg)
515 : {
516 1066 : for (uint32_t i = 0; i < NumIntArgRegs; i++) {
517 914 : if (IntArgRegs[i] == reg)
518 2 : return true;
519 : }
520 :
521 152 : return false;
522 : }
523 :
524 : void
525 0 : MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
526 : {
527 0 : if (IsIntArgReg(fun)) {
528 : // Callee register may be clobbered for an argument. Move the callee to
529 : // r10, a volatile, non-argument register.
530 0 : propagateOOM(moveResolver_.addMove(MoveOperand(fun), MoveOperand(r10),
531 0 : MoveOp::GENERAL));
532 0 : fun = r10;
533 : }
534 :
535 0 : MOZ_ASSERT(!IsIntArgReg(fun));
536 :
537 : uint32_t stackAdjust;
538 0 : callWithABIPre(&stackAdjust);
539 0 : call(fun);
540 0 : callWithABIPost(stackAdjust, result);
541 0 : }
542 :
543 : void
544 77 : MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
545 : {
546 77 : Address safeFun = fun;
547 77 : if (IsIntArgReg(safeFun.base)) {
548 : // Callee register may be clobbered for an argument. Move the callee to
549 : // r10, a volatile, non-argument register.
550 4 : propagateOOM(moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10),
551 2 : MoveOp::GENERAL));
552 2 : safeFun.base = r10;
553 : }
554 :
555 77 : MOZ_ASSERT(!IsIntArgReg(safeFun.base));
556 :
557 : uint32_t stackAdjust;
558 77 : callWithABIPre(&stackAdjust);
559 77 : call(safeFun);
560 77 : callWithABIPost(stackAdjust, result);
561 77 : }
562 :
563 : // ===============================================================
564 : // Branch functions
565 :
566 : void
567 226 : MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp, Label* label)
568 : {
569 226 : MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
570 :
571 452 : ScratchRegisterScope scratch(*this);
572 226 : MOZ_ASSERT(ptr != temp);
573 226 : MOZ_ASSERT(ptr != scratch);
574 :
575 226 : movePtr(ptr, scratch);
576 226 : orPtr(Imm32(gc::ChunkMask), scratch);
577 452 : branch32(cond, Address(scratch, gc::ChunkLocationOffsetFromLastByte),
578 226 : Imm32(int32_t(gc::ChunkLocation::Nursery)), label);
579 226 : }
580 :
581 : void
582 0 : MacroAssembler::branchValueIsNurseryObject(Condition cond, const Address& address, Register temp,
583 : Label* label)
584 : {
585 0 : branchValueIsNurseryObjectImpl(cond, address, temp, label);
586 0 : }
587 :
588 : void
589 208 : MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp,
590 : Label* label)
591 : {
592 208 : branchValueIsNurseryObjectImpl(cond, value, temp, label);
593 208 : }
594 :
595 : template <typename T>
596 : void
597 208 : MacroAssembler::branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp,
598 : Label* label)
599 : {
600 208 : MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
601 208 : MOZ_ASSERT(temp != InvalidReg);
602 :
603 416 : Label done;
604 208 : branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
605 :
606 208 : extractObject(value, temp);
607 208 : orPtr(Imm32(gc::ChunkMask), temp);
608 208 : branch32(cond, Address(temp, gc::ChunkLocationOffsetFromLastByte),
609 : Imm32(int32_t(gc::ChunkLocation::Nursery)), label);
610 :
611 208 : bind(&done);
612 208 : }
613 :
614 : void
615 89 : MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
616 : const Value& rhs, Label* label)
617 : {
618 89 : MOZ_ASSERT(cond == Equal || cond == NotEqual);
619 178 : ScratchRegisterScope scratch(*this);
620 89 : MOZ_ASSERT(lhs.valueReg() != scratch);
621 89 : moveValue(rhs, scratch);
622 89 : cmpPtr(lhs.valueReg(), scratch);
623 89 : j(cond, label);
624 89 : }
625 :
626 : // ========================================================================
627 : // Memory access primitives.
628 : template <typename T>
629 : void
630 6 : MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
631 : const T& dest, MIRType slotType)
632 : {
633 6 : if (valueType == MIRType::Double) {
634 0 : storeDouble(value.reg().typedReg().fpu(), dest);
635 0 : return;
636 : }
637 :
638 : // For known integers and booleans, we can just store the unboxed value if
639 : // the slot has the same type.
640 6 : if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) {
641 0 : if (value.constant()) {
642 0 : Value val = value.value();
643 0 : if (valueType == MIRType::Int32)
644 0 : store32(Imm32(val.toInt32()), dest);
645 : else
646 0 : store32(Imm32(val.toBoolean() ? 1 : 0), dest);
647 : } else {
648 0 : store32(value.reg().typedReg().gpr(), dest);
649 : }
650 0 : return;
651 : }
652 :
653 6 : if (value.constant())
654 0 : storeValue(value.value(), dest);
655 : else
656 6 : storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest);
657 : }
658 :
659 : template void
660 : MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
661 : const Address& dest, MIRType slotType);
662 : template void
663 : MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
664 : const BaseIndex& dest, MIRType slotType);
665 :
666 : // ========================================================================
667 : // wasm support
668 :
669 : void
670 0 : MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, AnyRegister out)
671 : {
672 0 : memoryBarrier(access.barrierBefore());
673 :
674 0 : size_t loadOffset = size();
675 0 : switch (access.type()) {
676 : case Scalar::Int8:
677 0 : movsbl(srcAddr, out.gpr());
678 0 : break;
679 : case Scalar::Uint8:
680 0 : movzbl(srcAddr, out.gpr());
681 0 : break;
682 : case Scalar::Int16:
683 0 : movswl(srcAddr, out.gpr());
684 0 : break;
685 : case Scalar::Uint16:
686 0 : movzwl(srcAddr, out.gpr());
687 0 : break;
688 : case Scalar::Int32:
689 : case Scalar::Uint32:
690 0 : movl(srcAddr, out.gpr());
691 0 : break;
692 : case Scalar::Float32:
693 0 : loadFloat32(srcAddr, out.fpu());
694 0 : break;
695 : case Scalar::Float64:
696 0 : loadDouble(srcAddr, out.fpu());
697 0 : break;
698 : case Scalar::Float32x4:
699 0 : switch (access.numSimdElems()) {
700 : // In memory-to-register mode, movss zeroes out the high lanes.
701 0 : case 1: loadFloat32(srcAddr, out.fpu()); break;
702 : // See comment above, which also applies to movsd.
703 0 : case 2: loadDouble(srcAddr, out.fpu()); break;
704 0 : case 4: loadUnalignedSimd128Float(srcAddr, out.fpu()); break;
705 0 : default: MOZ_CRASH("unexpected size for partial load");
706 : }
707 0 : break;
708 : case Scalar::Int32x4:
709 0 : switch (access.numSimdElems()) {
710 : // In memory-to-register mode, movd zeroes out the high lanes.
711 0 : case 1: vmovd(srcAddr, out.fpu()); break;
712 : // See comment above, which also applies to movq.
713 0 : case 2: vmovq(srcAddr, out.fpu()); break;
714 0 : case 4: loadUnalignedSimd128Int(srcAddr, out.fpu()); break;
715 0 : default: MOZ_CRASH("unexpected size for partial load");
716 : }
717 0 : break;
718 : case Scalar::Int8x16:
719 0 : MOZ_ASSERT(access.numSimdElems() == 16, "unexpected partial load");
720 0 : loadUnalignedSimd128Int(srcAddr, out.fpu());
721 0 : break;
722 : case Scalar::Int16x8:
723 0 : MOZ_ASSERT(access.numSimdElems() == 8, "unexpected partial load");
724 0 : loadUnalignedSimd128Int(srcAddr, out.fpu());
725 0 : break;
726 : case Scalar::Int64:
727 0 : MOZ_CRASH("int64 loads must use load64");
728 : case Scalar::Uint8Clamped:
729 : case Scalar::MaxTypedArrayViewType:
730 0 : MOZ_CRASH("unexpected array type");
731 : }
732 0 : append(access, loadOffset, framePushed());
733 :
734 0 : memoryBarrier(access.barrierAfter());
735 0 : }
736 :
737 : void
738 0 : MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, Register64 out)
739 : {
740 0 : MOZ_ASSERT(!access.isAtomic());
741 0 : MOZ_ASSERT(!access.isSimd());
742 :
743 0 : size_t loadOffset = size();
744 0 : switch (access.type()) {
745 : case Scalar::Int8:
746 0 : movsbq(srcAddr, out.reg);
747 0 : break;
748 : case Scalar::Uint8:
749 0 : movzbq(srcAddr, out.reg);
750 0 : break;
751 : case Scalar::Int16:
752 0 : movswq(srcAddr, out.reg);
753 0 : break;
754 : case Scalar::Uint16:
755 0 : movzwq(srcAddr, out.reg);
756 0 : break;
757 : case Scalar::Int32:
758 0 : movslq(srcAddr, out.reg);
759 0 : break;
760 : // Int32 to int64 moves zero-extend by default.
761 : case Scalar::Uint32:
762 0 : movl(srcAddr, out.reg);
763 0 : break;
764 : case Scalar::Int64:
765 0 : movq(srcAddr, out.reg);
766 0 : break;
767 : case Scalar::Float32:
768 : case Scalar::Float64:
769 : case Scalar::Float32x4:
770 : case Scalar::Int8x16:
771 : case Scalar::Int16x8:
772 : case Scalar::Int32x4:
773 0 : MOZ_CRASH("non-int64 loads should use load()");
774 : case Scalar::Uint8Clamped:
775 : case Scalar::MaxTypedArrayViewType:
776 0 : MOZ_CRASH("unexpected array type");
777 : }
778 0 : append(access, loadOffset, framePushed());
779 0 : }
780 :
781 : void
782 0 : MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr)
783 : {
784 0 : memoryBarrier(access.barrierBefore());
785 :
786 0 : size_t storeOffset = size();
787 0 : switch (access.type()) {
788 : case Scalar::Int8:
789 : case Scalar::Uint8:
790 0 : movb(value.gpr(), dstAddr);
791 0 : break;
792 : case Scalar::Int16:
793 : case Scalar::Uint16:
794 0 : movw(value.gpr(), dstAddr);
795 0 : break;
796 : case Scalar::Int32:
797 : case Scalar::Uint32:
798 0 : movl(value.gpr(), dstAddr);
799 0 : break;
800 : case Scalar::Int64:
801 0 : movq(value.gpr(), dstAddr);
802 0 : break;
803 : case Scalar::Float32:
804 0 : storeUncanonicalizedFloat32(value.fpu(), dstAddr);
805 0 : break;
806 : case Scalar::Float64:
807 0 : storeUncanonicalizedDouble(value.fpu(), dstAddr);
808 0 : break;
809 : case Scalar::Float32x4:
810 0 : switch (access.numSimdElems()) {
811 : // In memory-to-register mode, movss zeroes out the high lanes.
812 0 : case 1: storeUncanonicalizedFloat32(value.fpu(), dstAddr); break;
813 : // See comment above, which also applies to movsd.
814 0 : case 2: storeUncanonicalizedDouble(value.fpu(), dstAddr); break;
815 0 : case 4: storeUnalignedSimd128Float(value.fpu(), dstAddr); break;
816 0 : default: MOZ_CRASH("unexpected size for partial load");
817 : }
818 0 : break;
819 : case Scalar::Int32x4:
820 0 : switch (access.numSimdElems()) {
821 : // In memory-to-register mode, movd zeroes out the high lanes.
822 0 : case 1: vmovd(value.fpu(), dstAddr); break;
823 : // See comment above, which also applies to movq.
824 0 : case 2: vmovq(value.fpu(), dstAddr); break;
825 0 : case 4: storeUnalignedSimd128Int(value.fpu(), dstAddr); break;
826 0 : default: MOZ_CRASH("unexpected size for partial load");
827 : }
828 0 : break;
829 : case Scalar::Int8x16:
830 0 : MOZ_ASSERT(access.numSimdElems() == 16, "unexpected partial store");
831 0 : storeUnalignedSimd128Int(value.fpu(), dstAddr);
832 0 : break;
833 : case Scalar::Int16x8:
834 0 : MOZ_ASSERT(access.numSimdElems() == 8, "unexpected partial store");
835 0 : storeUnalignedSimd128Int(value.fpu(), dstAddr);
836 0 : break;
837 : case Scalar::Uint8Clamped:
838 : case Scalar::MaxTypedArrayViewType:
839 0 : MOZ_CRASH("unexpected array type");
840 : }
841 0 : append(access, storeOffset, framePushed());
842 :
843 0 : memoryBarrier(access.barrierAfter());
844 0 : }
845 :
846 : void
847 0 : MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
848 : {
849 0 : vcvttsd2sq(input, output);
850 :
851 : // Check that the result is in the uint32_t range.
852 0 : ScratchRegisterScope scratch(*this);
853 0 : move32(Imm32(0xffffffff), scratch);
854 0 : cmpq(scratch, output);
855 0 : j(Assembler::Above, oolEntry);
856 0 : }
857 :
858 : void
859 0 : MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
860 : {
861 0 : vcvttss2sq(input, output);
862 :
863 : // Check that the result is in the uint32_t range.
864 0 : ScratchRegisterScope scratch(*this);
865 0 : move32(Imm32(0xffffffff), scratch);
866 0 : cmpq(scratch, output);
867 0 : j(Assembler::Above, oolEntry);
868 0 : }
869 :
870 : //}}} check_macroassembler_style
|