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/x86-shared/MacroAssembler-x86-shared.h"
8 :
9 : #include "jit/JitFrames.h"
10 : #include "jit/MacroAssembler.h"
11 :
12 : #include "jit/MacroAssembler-inl.h"
13 :
14 : using namespace js;
15 : using namespace js::jit;
16 :
17 : // Note: this function clobbers the input register.
18 : void
19 0 : MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
20 : {
21 0 : ScratchDoubleScope scratch(*this);
22 0 : MOZ_ASSERT(input != scratch);
23 0 : Label positive, done;
24 :
25 : // <= 0 or NaN --> 0
26 0 : zeroDouble(scratch);
27 0 : branchDouble(DoubleGreaterThan, input, scratch, &positive);
28 : {
29 0 : move32(Imm32(0), output);
30 0 : jump(&done);
31 : }
32 :
33 0 : bind(&positive);
34 :
35 : // Add 0.5 and truncate.
36 0 : loadConstantDouble(0.5, scratch);
37 0 : addDouble(scratch, input);
38 :
39 0 : Label outOfRange;
40 :
41 : // Truncate to int32 and ensure the result <= 255. This relies on the
42 : // processor setting output to a value > 255 for doubles outside the int32
43 : // range (for instance 0x80000000).
44 0 : vcvttsd2si(input, output);
45 0 : branch32(Assembler::Above, output, Imm32(255), &outOfRange);
46 : {
47 : // Check if we had a tie.
48 0 : convertInt32ToDouble(output, scratch);
49 0 : branchDouble(DoubleNotEqual, input, scratch, &done);
50 :
51 : // It was a tie. Mask out the ones bit to get an even value.
52 : // See also js_TypedArray_uint8_clamp_double.
53 0 : and32(Imm32(~1), output);
54 0 : jump(&done);
55 : }
56 :
57 : // > 255 --> 255
58 0 : bind(&outOfRange);
59 : {
60 0 : move32(Imm32(255), output);
61 : }
62 :
63 0 : bind(&done);
64 0 : }
65 :
66 : void
67 0 : MacroAssembler::alignFrameForICArguments(AfterICSaveLive& aic)
68 : {
69 : // Exists for MIPS compatibility.
70 0 : }
71 :
72 : void
73 0 : MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive& aic)
74 : {
75 : // Exists for MIPS compatibility.
76 0 : }
77 :
78 : bool
79 0 : MacroAssemblerX86Shared::buildOOLFakeExitFrame(void* fakeReturnAddr)
80 : {
81 0 : uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS,
82 0 : ExitFrameLayout::Size());
83 0 : asMasm().Push(Imm32(descriptor));
84 0 : asMasm().Push(ImmPtr(fakeReturnAddr));
85 0 : return true;
86 : }
87 :
88 : void
89 0 : MacroAssemblerX86Shared::branchNegativeZero(FloatRegister reg,
90 : Register scratch,
91 : Label* label,
92 : bool maybeNonZero)
93 : {
94 : // Determines whether the low double contained in the XMM register reg
95 : // is equal to -0.0.
96 :
97 : #if defined(JS_CODEGEN_X86)
98 : Label nonZero;
99 :
100 : // if not already compared to zero
101 : if (maybeNonZero) {
102 : ScratchDoubleScope scratchDouble(asMasm());
103 :
104 : // Compare to zero. Lets through {0, -0}.
105 : zeroDouble(scratchDouble);
106 :
107 : // If reg is non-zero, jump to nonZero.
108 : asMasm().branchDouble(DoubleNotEqual, reg, scratchDouble, &nonZero);
109 : }
110 : // Input register is either zero or negative zero. Retrieve sign of input.
111 : vmovmskpd(reg, scratch);
112 :
113 : // If reg is 1 or 3, input is negative zero.
114 : // If reg is 0 or 2, input is a normal zero.
115 : asMasm().branchTest32(NonZero, scratch, Imm32(1), label);
116 :
117 : bind(&nonZero);
118 : #elif defined(JS_CODEGEN_X64)
119 0 : vmovq(reg, scratch);
120 0 : cmpq(Imm32(1), scratch);
121 0 : j(Overflow, label);
122 : #endif
123 0 : }
124 :
125 : void
126 0 : MacroAssemblerX86Shared::branchNegativeZeroFloat32(FloatRegister reg,
127 : Register scratch,
128 : Label* label)
129 : {
130 0 : vmovd(reg, scratch);
131 0 : cmp32(scratch, Imm32(1));
132 0 : j(Overflow, label);
133 0 : }
134 :
135 : MacroAssembler&
136 27 : MacroAssemblerX86Shared::asMasm()
137 : {
138 27 : return *static_cast<MacroAssembler*>(this);
139 : }
140 :
141 : const MacroAssembler&
142 0 : MacroAssemblerX86Shared::asMasm() const
143 : {
144 0 : return *static_cast<const MacroAssembler*>(this);
145 : }
146 :
147 : template<typename T>
148 : void
149 0 : MacroAssemblerX86Shared::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
150 : Register oldval, Register newval,
151 : Register temp, AnyRegister output)
152 : {
153 0 : switch (arrayType) {
154 : case Scalar::Int8:
155 0 : compareExchange8SignExtend(mem, oldval, newval, output.gpr());
156 0 : break;
157 : case Scalar::Uint8:
158 0 : compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
159 0 : break;
160 : case Scalar::Int16:
161 0 : compareExchange16SignExtend(mem, oldval, newval, output.gpr());
162 0 : break;
163 : case Scalar::Uint16:
164 0 : compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
165 0 : break;
166 : case Scalar::Int32:
167 0 : compareExchange32(mem, oldval, newval, output.gpr());
168 0 : break;
169 : case Scalar::Uint32:
170 : // At the moment, the code in MCallOptimize.cpp requires the output
171 : // type to be double for uint32 arrays. See bug 1077305.
172 0 : MOZ_ASSERT(output.isFloat());
173 0 : compareExchange32(mem, oldval, newval, temp);
174 0 : asMasm().convertUInt32ToDouble(temp, output.fpu());
175 0 : break;
176 : default:
177 0 : MOZ_CRASH("Invalid typed array type");
178 : }
179 0 : }
180 :
181 : template void
182 : MacroAssemblerX86Shared::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
183 : Register oldval, Register newval, Register temp,
184 : AnyRegister output);
185 : template void
186 : MacroAssemblerX86Shared::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
187 : Register oldval, Register newval, Register temp,
188 : AnyRegister output);
189 :
190 : template<typename T>
191 : void
192 0 : MacroAssemblerX86Shared::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
193 : Register value, Register temp, AnyRegister output)
194 : {
195 0 : switch (arrayType) {
196 : case Scalar::Int8:
197 0 : atomicExchange8SignExtend(mem, value, output.gpr());
198 0 : break;
199 : case Scalar::Uint8:
200 0 : atomicExchange8ZeroExtend(mem, value, output.gpr());
201 0 : break;
202 : case Scalar::Int16:
203 0 : atomicExchange16SignExtend(mem, value, output.gpr());
204 0 : break;
205 : case Scalar::Uint16:
206 0 : atomicExchange16ZeroExtend(mem, value, output.gpr());
207 0 : break;
208 : case Scalar::Int32:
209 0 : atomicExchange32(mem, value, output.gpr());
210 0 : break;
211 : case Scalar::Uint32:
212 : // At the moment, the code in MCallOptimize.cpp requires the output
213 : // type to be double for uint32 arrays. See bug 1077305.
214 0 : MOZ_ASSERT(output.isFloat());
215 0 : atomicExchange32(mem, value, temp);
216 0 : asMasm().convertUInt32ToDouble(temp, output.fpu());
217 0 : break;
218 : default:
219 0 : MOZ_CRASH("Invalid typed array type");
220 : }
221 0 : }
222 :
223 : template void
224 : MacroAssemblerX86Shared::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
225 : Register value, Register temp, AnyRegister output);
226 : template void
227 : MacroAssemblerX86Shared::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
228 : Register value, Register temp, AnyRegister output);
229 :
230 : template<class T, class Map>
231 : T*
232 0 : MacroAssemblerX86Shared::getConstant(const typename T::Pod& value, Map& map,
233 : Vector<T, 0, SystemAllocPolicy>& vec)
234 : {
235 : typedef typename Map::AddPtr AddPtr;
236 0 : if (!map.initialized()) {
237 0 : enoughMemory_ &= map.init();
238 0 : if (!enoughMemory_)
239 0 : return nullptr;
240 : }
241 : size_t index;
242 0 : if (AddPtr p = map.lookupForAdd(value)) {
243 0 : index = p->value();
244 : } else {
245 0 : index = vec.length();
246 0 : enoughMemory_ &= vec.append(T(value));
247 0 : if (!enoughMemory_)
248 0 : return nullptr;
249 0 : enoughMemory_ &= map.add(p, value, index);
250 0 : if (!enoughMemory_)
251 0 : return nullptr;
252 : }
253 0 : return &vec[index];
254 : }
255 :
256 : MacroAssemblerX86Shared::Float*
257 0 : MacroAssemblerX86Shared::getFloat(float f)
258 : {
259 0 : return getConstant<Float, FloatMap>(f, floatMap_, floats_);
260 : }
261 :
262 : MacroAssemblerX86Shared::Double*
263 0 : MacroAssemblerX86Shared::getDouble(double d)
264 : {
265 0 : return getConstant<Double, DoubleMap>(d, doubleMap_, doubles_);
266 : }
267 :
268 : MacroAssemblerX86Shared::SimdData*
269 0 : MacroAssemblerX86Shared::getSimdData(const SimdConstant& v)
270 : {
271 0 : return getConstant<SimdData, SimdMap>(v, simdMap_, simds_);
272 : }
273 :
274 : template<class T, class Map>
275 : static bool
276 0 : MergeConstants(size_t delta, const Vector<T, 0, SystemAllocPolicy>& other,
277 : Map& map, Vector<T, 0, SystemAllocPolicy>& vec)
278 : {
279 : typedef typename Map::AddPtr AddPtr;
280 0 : if (!map.initialized() && !map.init())
281 0 : return false;
282 :
283 0 : for (const T& c : other) {
284 : size_t index;
285 0 : if (AddPtr p = map.lookupForAdd(c.value)) {
286 0 : index = p->value();
287 : } else {
288 0 : index = vec.length();
289 0 : if (!vec.append(T(c.value)) || !map.add(p, c.value, index))
290 0 : return false;
291 : }
292 0 : MacroAssemblerX86Shared::UsesVector& uses = vec[index].uses;
293 0 : for (CodeOffset use : c.uses) {
294 0 : use.offsetBy(delta);
295 0 : if (!uses.append(use))
296 0 : return false;
297 : }
298 : }
299 :
300 0 : return true;
301 : }
302 :
303 : bool
304 0 : MacroAssemblerX86Shared::asmMergeWith(const MacroAssemblerX86Shared& other)
305 : {
306 0 : size_t sizeBefore = masm.size();
307 0 : if (!Assembler::asmMergeWith(other))
308 0 : return false;
309 0 : if (!MergeConstants<Double, DoubleMap>(sizeBefore, other.doubles_, doubleMap_, doubles_))
310 0 : return false;
311 0 : if (!MergeConstants<Float, FloatMap>(sizeBefore, other.floats_, floatMap_, floats_))
312 0 : return false;
313 0 : if (!MergeConstants<SimdData, SimdMap>(sizeBefore, other.simds_, simdMap_, simds_))
314 0 : return false;
315 0 : return true;
316 : }
317 :
318 : void
319 0 : MacroAssemblerX86Shared::minMaxDouble(FloatRegister first, FloatRegister second, bool canBeNaN,
320 : bool isMax)
321 : {
322 0 : Label done, nan, minMaxInst;
323 :
324 : // Do a vucomisd to catch equality and NaNs, which both require special
325 : // handling. If the operands are ordered and inequal, we branch straight to
326 : // the min/max instruction. If we wanted, we could also branch for less-than
327 : // or greater-than here instead of using min/max, however these conditions
328 : // will sometimes be hard on the branch predictor.
329 0 : vucomisd(second, first);
330 0 : j(Assembler::NotEqual, &minMaxInst);
331 0 : if (canBeNaN)
332 0 : j(Assembler::Parity, &nan);
333 :
334 : // Ordered and equal. The operands are bit-identical unless they are zero
335 : // and negative zero. These instructions merge the sign bits in that
336 : // case, and are no-ops otherwise.
337 0 : if (isMax)
338 0 : vandpd(second, first, first);
339 : else
340 0 : vorpd(second, first, first);
341 0 : jump(&done);
342 :
343 : // x86's min/max are not symmetric; if either operand is a NaN, they return
344 : // the read-only operand. We need to return a NaN if either operand is a
345 : // NaN, so we explicitly check for a NaN in the read-write operand.
346 0 : if (canBeNaN) {
347 0 : bind(&nan);
348 0 : vucomisd(first, first);
349 0 : j(Assembler::Parity, &done);
350 : }
351 :
352 : // When the values are inequal, or second is NaN, x86's min and max will
353 : // return the value we need.
354 0 : bind(&minMaxInst);
355 0 : if (isMax)
356 0 : vmaxsd(second, first, first);
357 : else
358 0 : vminsd(second, first, first);
359 :
360 0 : bind(&done);
361 0 : }
362 :
363 : void
364 0 : MacroAssemblerX86Shared::minMaxFloat32(FloatRegister first, FloatRegister second, bool canBeNaN,
365 : bool isMax)
366 : {
367 0 : Label done, nan, minMaxInst;
368 :
369 : // Do a vucomiss to catch equality and NaNs, which both require special
370 : // handling. If the operands are ordered and inequal, we branch straight to
371 : // the min/max instruction. If we wanted, we could also branch for less-than
372 : // or greater-than here instead of using min/max, however these conditions
373 : // will sometimes be hard on the branch predictor.
374 0 : vucomiss(second, first);
375 0 : j(Assembler::NotEqual, &minMaxInst);
376 0 : if (canBeNaN)
377 0 : j(Assembler::Parity, &nan);
378 :
379 : // Ordered and equal. The operands are bit-identical unless they are zero
380 : // and negative zero. These instructions merge the sign bits in that
381 : // case, and are no-ops otherwise.
382 0 : if (isMax)
383 0 : vandps(second, first, first);
384 : else
385 0 : vorps(second, first, first);
386 0 : jump(&done);
387 :
388 : // x86's min/max are not symmetric; if either operand is a NaN, they return
389 : // the read-only operand. We need to return a NaN if either operand is a
390 : // NaN, so we explicitly check for a NaN in the read-write operand.
391 0 : if (canBeNaN) {
392 0 : bind(&nan);
393 0 : vucomiss(first, first);
394 0 : j(Assembler::Parity, &done);
395 : }
396 :
397 : // When the values are inequal, or second is NaN, x86's min and max will
398 : // return the value we need.
399 0 : bind(&minMaxInst);
400 0 : if (isMax)
401 0 : vmaxss(second, first, first);
402 : else
403 0 : vminss(second, first, first);
404 :
405 0 : bind(&done);
406 0 : }
407 :
408 : //{{{ check_macroassembler_style
409 : // ===============================================================
410 : // MacroAssembler high-level usage.
411 :
412 : void
413 0 : MacroAssembler::flush()
414 : {
415 0 : }
416 :
417 : void
418 288 : MacroAssembler::comment(const char* msg)
419 : {
420 288 : masm.comment(msg);
421 288 : }
422 :
423 : // ===============================================================
424 : // Stack manipulation functions.
425 :
426 : void
427 11367 : MacroAssembler::PushRegsInMask(LiveRegisterSet set)
428 : {
429 11367 : FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
430 11367 : unsigned numFpu = fpuSet.size();
431 11367 : int32_t diffF = fpuSet.getPushSizeInBytes();
432 11367 : int32_t diffG = set.gprs().size() * sizeof(intptr_t);
433 :
434 : // On x86, always use push to push the integer registers, as it's fast
435 : // on modern hardware and it's a small instruction.
436 101571 : for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
437 90199 : diffG -= sizeof(intptr_t);
438 90199 : Push(*iter);
439 : }
440 11367 : MOZ_ASSERT(diffG == 0);
441 :
442 11367 : reserveStack(diffF);
443 177039 : for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {
444 165677 : FloatRegister reg = *iter;
445 165677 : diffF -= reg.size();
446 165676 : numFpu -= 1;
447 165676 : Address spillAddress(StackPointer, diffF);
448 165676 : if (reg.isDouble())
449 6 : storeDouble(reg, spillAddress);
450 165663 : else if (reg.isSingle())
451 0 : storeFloat32(reg, spillAddress);
452 165663 : else if (reg.isSimd128())
453 165665 : storeUnalignedSimd128Float(reg, spillAddress);
454 : else
455 0 : MOZ_CRASH("Unknown register type.");
456 : }
457 11367 : MOZ_ASSERT(numFpu == 0);
458 : // x64 padding to keep the stack aligned on uintptr_t. Keep in sync with
459 : // GetPushBytesInSize.
460 11367 : diffF -= diffF % sizeof(uintptr_t);
461 11367 : MOZ_ASSERT(diffF == 0);
462 11367 : }
463 :
464 : void
465 0 : MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register)
466 : {
467 0 : FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
468 0 : unsigned numFpu = fpuSet.size();
469 0 : int32_t diffF = fpuSet.getPushSizeInBytes();
470 0 : int32_t diffG = set.gprs().size() * sizeof(intptr_t);
471 :
472 0 : MOZ_ASSERT(dest.offset >= diffG + diffF);
473 :
474 0 : for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
475 0 : diffG -= sizeof(intptr_t);
476 0 : dest.offset -= sizeof(intptr_t);
477 0 : storePtr(*iter, dest);
478 : }
479 0 : MOZ_ASSERT(diffG == 0);
480 :
481 0 : for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {
482 0 : FloatRegister reg = *iter;
483 0 : diffF -= reg.size();
484 0 : numFpu -= 1;
485 0 : dest.offset -= reg.size();
486 0 : if (reg.isDouble())
487 0 : storeDouble(reg, dest);
488 0 : else if (reg.isSingle())
489 0 : storeFloat32(reg, dest);
490 0 : else if (reg.isSimd128())
491 0 : storeUnalignedSimd128Float(reg, dest);
492 : else
493 0 : MOZ_CRASH("Unknown register type.");
494 : }
495 0 : MOZ_ASSERT(numFpu == 0);
496 : // x64 padding to keep the stack aligned on uintptr_t. Keep in sync with
497 : // GetPushBytesInSize.
498 0 : diffF -= diffF % sizeof(uintptr_t);
499 0 : MOZ_ASSERT(diffF == 0);
500 0 : }
501 :
502 : void
503 11359 : MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
504 : {
505 11359 : FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
506 11359 : unsigned numFpu = fpuSet.size();
507 11359 : int32_t diffG = set.gprs().size() * sizeof(intptr_t);
508 11359 : int32_t diffF = fpuSet.getPushSizeInBytes();
509 11359 : const int32_t reservedG = diffG;
510 11359 : const int32_t reservedF = diffF;
511 :
512 176887 : for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {
513 165549 : FloatRegister reg = *iter;
514 165551 : diffF -= reg.size();
515 165542 : numFpu -= 1;
516 165542 : if (ignore.has(reg))
517 0 : continue;
518 :
519 165541 : Address spillAddress(StackPointer, diffF);
520 165542 : if (reg.isDouble())
521 6 : loadDouble(spillAddress, reg);
522 165535 : else if (reg.isSingle())
523 0 : loadFloat32(spillAddress, reg);
524 165534 : else if (reg.isSimd128())
525 165534 : loadUnalignedSimd128Float(spillAddress, reg);
526 : else
527 0 : MOZ_CRASH("Unknown register type.");
528 : }
529 11359 : freeStack(reservedF);
530 11358 : MOZ_ASSERT(numFpu == 0);
531 : // x64 padding to keep the stack aligned on uintptr_t. Keep in sync with
532 : // GetPushBytesInSize.
533 11358 : diffF -= diffF % sizeof(uintptr_t);
534 11358 : MOZ_ASSERT(diffF == 0);
535 :
536 : // On x86, use pop to pop the integer registers, if we're not going to
537 : // ignore any slots, as it's fast on modern hardware and it's a small
538 : // instruction.
539 11358 : if (ignore.emptyGeneral()) {
540 100501 : for (GeneralRegisterForwardIterator iter(set.gprs()); iter.more(); ++iter) {
541 89279 : diffG -= sizeof(intptr_t);
542 89279 : Pop(*iter);
543 : }
544 : } else {
545 933 : for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
546 796 : diffG -= sizeof(intptr_t);
547 796 : if (!ignore.has(*iter))
548 735 : loadPtr(Address(StackPointer, diffG), *iter);
549 : }
550 137 : freeStack(reservedG);
551 : }
552 11359 : MOZ_ASSERT(diffG == 0);
553 11359 : }
554 :
555 : void
556 385 : MacroAssembler::Push(const Operand op)
557 : {
558 385 : push(op);
559 385 : adjustFrame(sizeof(intptr_t));
560 385 : }
561 :
562 : void
563 99118 : MacroAssembler::Push(Register reg)
564 : {
565 99118 : push(reg);
566 99122 : adjustFrame(sizeof(intptr_t));
567 99122 : }
568 :
569 : void
570 2290 : MacroAssembler::Push(const Imm32 imm)
571 : {
572 2290 : push(imm);
573 2290 : adjustFrame(sizeof(intptr_t));
574 2290 : }
575 :
576 : void
577 1799 : MacroAssembler::Push(const ImmWord imm)
578 : {
579 1799 : push(imm);
580 1799 : adjustFrame(sizeof(intptr_t));
581 1799 : }
582 :
583 : void
584 1799 : MacroAssembler::Push(const ImmPtr imm)
585 : {
586 1799 : Push(ImmWord(uintptr_t(imm.value)));
587 1799 : }
588 :
589 : void
590 514 : MacroAssembler::Push(const ImmGCPtr ptr)
591 : {
592 514 : push(ptr);
593 514 : adjustFrame(sizeof(intptr_t));
594 514 : }
595 :
596 : void
597 0 : MacroAssembler::Push(FloatRegister t)
598 : {
599 0 : push(t);
600 0 : adjustFrame(sizeof(double));
601 0 : }
602 :
603 : void
604 0 : MacroAssembler::PushFlags()
605 : {
606 0 : pushFlags();
607 0 : adjustFrame(sizeof(intptr_t));
608 0 : }
609 :
610 : void
611 665 : MacroAssembler::Pop(const Operand op)
612 : {
613 665 : pop(op);
614 665 : implicitPop(sizeof(intptr_t));
615 665 : }
616 :
617 : void
618 93978 : MacroAssembler::Pop(Register reg)
619 : {
620 93978 : pop(reg);
621 93979 : implicitPop(sizeof(intptr_t));
622 93979 : }
623 :
624 : void
625 0 : MacroAssembler::Pop(FloatRegister reg)
626 : {
627 0 : pop(reg);
628 0 : implicitPop(sizeof(double));
629 0 : }
630 :
631 : void
632 274 : MacroAssembler::Pop(const ValueOperand& val)
633 : {
634 274 : popValue(val);
635 274 : implicitPop(sizeof(Value));
636 274 : }
637 :
638 : void
639 0 : MacroAssembler::PopFlags()
640 : {
641 0 : popFlags();
642 0 : implicitPop(sizeof(intptr_t));
643 0 : }
644 :
645 : void
646 0 : MacroAssembler::PopStackPtr()
647 : {
648 0 : Pop(StackPointer);
649 0 : }
650 :
651 : // ===============================================================
652 : // Simple call functions.
653 :
654 : CodeOffset
655 94 : MacroAssembler::call(Register reg)
656 : {
657 94 : return Assembler::call(reg);
658 : }
659 :
660 : CodeOffset
661 373 : MacroAssembler::call(Label* label)
662 : {
663 373 : return Assembler::call(label);
664 : }
665 :
666 : void
667 19474 : MacroAssembler::call(const Address& addr)
668 : {
669 19474 : Assembler::call(Operand(addr.base, addr.offset));
670 19474 : }
671 :
672 : void
673 0 : MacroAssembler::call(wasm::SymbolicAddress target)
674 : {
675 0 : mov(target, eax);
676 0 : Assembler::call(eax);
677 0 : }
678 :
679 : void
680 0 : MacroAssembler::call(ImmWord target)
681 : {
682 0 : Assembler::call(target);
683 0 : }
684 :
685 : void
686 12946 : MacroAssembler::call(ImmPtr target)
687 : {
688 12946 : Assembler::call(target);
689 12946 : }
690 :
691 : void
692 4069 : MacroAssembler::call(JitCode* target)
693 : {
694 4069 : Assembler::call(target);
695 4069 : }
696 :
697 : CodeOffset
698 0 : MacroAssembler::callWithPatch()
699 : {
700 0 : return Assembler::callWithPatch();
701 : }
702 : void
703 0 : MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
704 : {
705 0 : Assembler::patchCall(callerOffset, calleeOffset);
706 0 : }
707 :
708 : void
709 94 : MacroAssembler::callAndPushReturnAddress(Register reg)
710 : {
711 94 : call(reg);
712 94 : }
713 :
714 : void
715 4 : MacroAssembler::callAndPushReturnAddress(Label* label)
716 : {
717 4 : call(label);
718 4 : }
719 :
720 : // ===============================================================
721 : // Patchable near/far jumps.
722 :
723 : CodeOffset
724 0 : MacroAssembler::farJumpWithPatch()
725 : {
726 0 : return Assembler::farJumpWithPatch();
727 : }
728 :
729 : void
730 0 : MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset)
731 : {
732 0 : Assembler::patchFarJump(farJump, targetOffset);
733 0 : }
734 :
735 : void
736 0 : MacroAssembler::repatchFarJump(uint8_t* code, uint32_t farJumpOffset, uint32_t targetOffset)
737 : {
738 0 : Assembler::repatchFarJump(code, farJumpOffset, targetOffset);
739 0 : }
740 :
741 : CodeOffset
742 0 : MacroAssembler::nopPatchableToNearJump()
743 : {
744 0 : return Assembler::twoByteNop();
745 : }
746 :
747 : void
748 0 : MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target)
749 : {
750 0 : Assembler::patchTwoByteNopToJump(jump, target);
751 0 : }
752 :
753 : void
754 0 : MacroAssembler::patchNearJumpToNop(uint8_t* jump)
755 : {
756 0 : Assembler::patchJumpToTwoByteNop(jump);
757 0 : }
758 :
759 : CodeOffset
760 0 : MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
761 : {
762 0 : CodeOffset offset(currentOffset());
763 0 : masm.nop_five();
764 0 : append(desc, CodeOffset(currentOffset()));
765 0 : MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr));
766 0 : return offset;
767 : }
768 :
769 : void
770 0 : MacroAssembler::patchNopToCall(uint8_t* callsite, uint8_t* target)
771 : {
772 0 : Assembler::patchFiveByteNopToCall(callsite, target);
773 0 : }
774 :
775 : void
776 0 : MacroAssembler::patchCallToNop(uint8_t* callsite)
777 : {
778 0 : Assembler::patchCallToFiveByteNop(callsite);
779 0 : }
780 :
781 : // ===============================================================
782 : // Jit Frames.
783 :
784 : uint32_t
785 9 : MacroAssembler::pushFakeReturnAddress(Register scratch)
786 : {
787 9 : CodeLabel cl;
788 :
789 9 : mov(cl.patchAt(), scratch);
790 9 : Push(scratch);
791 9 : use(cl.target());
792 9 : uint32_t retAddr = currentOffset();
793 :
794 9 : addCodeLabel(cl);
795 9 : return retAddr;
796 : }
797 :
798 : // wasm specific methods, used in both the wasm baseline compiler and ion.
799 :
800 : // RAII class that generates the jumps to traps when it's destructed, to
801 : // prevent some code duplication in the outOfLineWasmTruncateXtoY methods.
802 : struct MOZ_RAII AutoHandleWasmTruncateToIntErrors
803 : {
804 : MacroAssembler& masm;
805 : Label inputIsNaN;
806 : Label fail;
807 : wasm::BytecodeOffset off;
808 :
809 0 : explicit AutoHandleWasmTruncateToIntErrors(MacroAssembler& masm, wasm::BytecodeOffset off)
810 0 : : masm(masm), off(off)
811 0 : { }
812 :
813 0 : ~AutoHandleWasmTruncateToIntErrors() {
814 : // Handle errors.
815 0 : masm.bind(&fail);
816 0 : masm.jump(wasm::TrapDesc(off, wasm::Trap::IntegerOverflow, masm.framePushed()));
817 :
818 0 : masm.bind(&inputIsNaN);
819 0 : masm.jump(wasm::TrapDesc(off, wasm::Trap::InvalidConversionToInteger, masm.framePushed()));
820 0 : }
821 : };
822 :
823 : void
824 0 : MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
825 : {
826 0 : vcvttsd2si(input, output);
827 0 : cmp32(output, Imm32(1));
828 0 : j(Assembler::Overflow, oolEntry);
829 0 : }
830 :
831 : void
832 0 : MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry)
833 : {
834 0 : vcvttss2si(input, output);
835 0 : cmp32(output, Imm32(1));
836 0 : j(Assembler::Overflow, oolEntry);
837 0 : }
838 :
839 : void
840 0 : MacroAssembler::outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned,
841 : wasm::BytecodeOffset off, Label* rejoin)
842 : {
843 0 : AutoHandleWasmTruncateToIntErrors traps(*this, off);
844 :
845 : // Eagerly take care of NaNs.
846 0 : branchDouble(Assembler::DoubleUnordered, input, input, &traps.inputIsNaN);
847 :
848 : // Handle special values (not needed for unsigned values).
849 0 : if (isUnsigned)
850 0 : return;
851 :
852 : // We've used vcvttsd2si. The only valid double values that can
853 : // truncate to INT32_MIN are in ]INT32_MIN - 1; INT32_MIN].
854 0 : loadConstantDouble(double(INT32_MIN) - 1.0, ScratchDoubleReg);
855 0 : branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &traps.fail);
856 :
857 0 : loadConstantDouble(double(INT32_MIN), ScratchDoubleReg);
858 0 : branchDouble(Assembler::DoubleGreaterThan, input, ScratchDoubleReg, &traps.fail);
859 0 : jump(rejoin);
860 : }
861 :
862 : void
863 0 : MacroAssembler::outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned,
864 : wasm::BytecodeOffset off, Label* rejoin)
865 : {
866 0 : AutoHandleWasmTruncateToIntErrors traps(*this, off);
867 :
868 : // Eagerly take care of NaNs.
869 0 : branchFloat(Assembler::DoubleUnordered, input, input, &traps.inputIsNaN);
870 :
871 : // Handle special values (not needed for unsigned values).
872 0 : if (isUnsigned)
873 0 : return;
874 :
875 : // We've used vcvttss2si. Check that the input wasn't
876 : // float(INT32_MIN), which is the only legimitate input that
877 : // would truncate to INT32_MIN.
878 0 : loadConstantFloat32(float(INT32_MIN), ScratchFloat32Reg);
879 0 : branchFloat(Assembler::DoubleNotEqual, input, ScratchFloat32Reg, &traps.fail);
880 0 : jump(rejoin);
881 : }
882 :
883 : void
884 0 : MacroAssembler::outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned,
885 : wasm::BytecodeOffset off, Label* rejoin)
886 : {
887 0 : AutoHandleWasmTruncateToIntErrors traps(*this, off);
888 :
889 : // Eagerly take care of NaNs.
890 0 : branchDouble(Assembler::DoubleUnordered, input, input, &traps.inputIsNaN);
891 :
892 : // Handle special values.
893 0 : if (isUnsigned) {
894 0 : loadConstantDouble(-0.0, ScratchDoubleReg);
895 0 : branchDouble(Assembler::DoubleGreaterThan, input, ScratchDoubleReg, &traps.fail);
896 0 : loadConstantDouble(-1.0, ScratchDoubleReg);
897 0 : branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &traps.fail);
898 0 : jump(rejoin);
899 0 : return;
900 : }
901 :
902 : // We've used vcvtsd2sq. The only legit value whose i64
903 : // truncation is INT64_MIN is double(INT64_MIN): exponent is so
904 : // high that the highest resolution around is much more than 1.
905 0 : loadConstantDouble(double(int64_t(INT64_MIN)), ScratchDoubleReg);
906 0 : branchDouble(Assembler::DoubleNotEqual, input, ScratchDoubleReg, &traps.fail);
907 0 : jump(rejoin);
908 : }
909 :
910 : void
911 0 : MacroAssembler::outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned,
912 : wasm::BytecodeOffset off, Label* rejoin)
913 : {
914 0 : AutoHandleWasmTruncateToIntErrors traps(*this, off);
915 :
916 : // Eagerly take care of NaNs.
917 0 : branchFloat(Assembler::DoubleUnordered, input, input, &traps.inputIsNaN);
918 :
919 : // Handle special values.
920 0 : if (isUnsigned) {
921 0 : loadConstantFloat32(-0.0f, ScratchFloat32Reg);
922 0 : branchFloat(Assembler::DoubleGreaterThan, input, ScratchFloat32Reg, &traps.fail);
923 0 : loadConstantFloat32(-1.0f, ScratchFloat32Reg);
924 0 : branchFloat(Assembler::DoubleLessThanOrEqual, input, ScratchFloat32Reg, &traps.fail);
925 0 : jump(rejoin);
926 0 : return;
927 : }
928 :
929 : // We've used vcvtss2sq. See comment in outOfLineWasmTruncateDoubleToInt64.
930 0 : loadConstantFloat32(float(int64_t(INT64_MIN)), ScratchFloat32Reg);
931 0 : branchFloat(Assembler::DoubleNotEqual, input, ScratchFloat32Reg, &traps.fail);
932 0 : jump(rejoin);
933 : }
934 :
935 : //}}} check_macroassembler_style
|