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 : #ifndef jit_x64_Assembler_x64_h
8 : #define jit_x64_Assembler_x64_h
9 :
10 : #include "mozilla/ArrayUtils.h"
11 :
12 : #include "jit/IonCode.h"
13 : #include "jit/JitCompartment.h"
14 : #include "jit/shared/Assembler-shared.h"
15 :
16 : namespace js {
17 : namespace jit {
18 :
19 : static constexpr Register rax { X86Encoding::rax };
20 : static constexpr Register rbx { X86Encoding::rbx };
21 : static constexpr Register rcx { X86Encoding::rcx };
22 : static constexpr Register rdx { X86Encoding::rdx };
23 : static constexpr Register rsi { X86Encoding::rsi };
24 : static constexpr Register rdi { X86Encoding::rdi };
25 : static constexpr Register rbp { X86Encoding::rbp };
26 : static constexpr Register r8 { X86Encoding::r8 };
27 : static constexpr Register r9 { X86Encoding::r9 };
28 : static constexpr Register r10 { X86Encoding::r10 };
29 : static constexpr Register r11 { X86Encoding::r11 };
30 : static constexpr Register r12 { X86Encoding::r12 };
31 : static constexpr Register r13 { X86Encoding::r13 };
32 : static constexpr Register r14 { X86Encoding::r14 };
33 : static constexpr Register r15 { X86Encoding::r15 };
34 : static constexpr Register rsp { X86Encoding::rsp };
35 :
36 : static constexpr FloatRegister xmm0 = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double);
37 : static constexpr FloatRegister xmm1 = FloatRegister(X86Encoding::xmm1, FloatRegisters::Double);
38 : static constexpr FloatRegister xmm2 = FloatRegister(X86Encoding::xmm2, FloatRegisters::Double);
39 : static constexpr FloatRegister xmm3 = FloatRegister(X86Encoding::xmm3, FloatRegisters::Double);
40 : static constexpr FloatRegister xmm4 = FloatRegister(X86Encoding::xmm4, FloatRegisters::Double);
41 : static constexpr FloatRegister xmm5 = FloatRegister(X86Encoding::xmm5, FloatRegisters::Double);
42 : static constexpr FloatRegister xmm6 = FloatRegister(X86Encoding::xmm6, FloatRegisters::Double);
43 : static constexpr FloatRegister xmm7 = FloatRegister(X86Encoding::xmm7, FloatRegisters::Double);
44 : static constexpr FloatRegister xmm8 = FloatRegister(X86Encoding::xmm8, FloatRegisters::Double);
45 : static constexpr FloatRegister xmm9 = FloatRegister(X86Encoding::xmm9, FloatRegisters::Double);
46 : static constexpr FloatRegister xmm10 = FloatRegister(X86Encoding::xmm10, FloatRegisters::Double);
47 : static constexpr FloatRegister xmm11 = FloatRegister(X86Encoding::xmm11, FloatRegisters::Double);
48 : static constexpr FloatRegister xmm12 = FloatRegister(X86Encoding::xmm12, FloatRegisters::Double);
49 : static constexpr FloatRegister xmm13 = FloatRegister(X86Encoding::xmm13, FloatRegisters::Double);
50 : static constexpr FloatRegister xmm14 = FloatRegister(X86Encoding::xmm14, FloatRegisters::Double);
51 : static constexpr FloatRegister xmm15 = FloatRegister(X86Encoding::xmm15, FloatRegisters::Double);
52 :
53 : // X86-common synonyms.
54 : static constexpr Register eax = rax;
55 : static constexpr Register ebx = rbx;
56 : static constexpr Register ecx = rcx;
57 : static constexpr Register edx = rdx;
58 : static constexpr Register esi = rsi;
59 : static constexpr Register edi = rdi;
60 : static constexpr Register ebp = rbp;
61 : static constexpr Register esp = rsp;
62 :
63 : static constexpr Register InvalidReg { X86Encoding::invalid_reg };
64 : static constexpr FloatRegister InvalidFloatReg = FloatRegister();
65 :
66 : static constexpr Register StackPointer = rsp;
67 : static constexpr Register FramePointer = rbp;
68 : static constexpr Register JSReturnReg = rcx;
69 : // Avoid, except for assertions.
70 : static constexpr Register JSReturnReg_Type = JSReturnReg;
71 : static constexpr Register JSReturnReg_Data = JSReturnReg;
72 :
73 : static constexpr Register ScratchReg = r11;
74 :
75 : // Helper class for ScratchRegister usage. Asserts that only one piece
76 : // of code thinks it has exclusive ownership of the scratch register.
77 25577 : struct ScratchRegisterScope : public AutoRegisterScope
78 : {
79 25577 : explicit ScratchRegisterScope(MacroAssembler& masm)
80 25577 : : AutoRegisterScope(masm, ScratchReg)
81 25576 : { }
82 : };
83 :
84 : static constexpr Register ReturnReg = rax;
85 : static constexpr Register HeapReg = r15;
86 : static constexpr Register64 ReturnReg64(rax);
87 : static constexpr FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single);
88 : static constexpr FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double);
89 : static constexpr FloatRegister ReturnSimd128Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Simd128);
90 : static constexpr FloatRegister ScratchFloat32Reg = FloatRegister(X86Encoding::xmm15, FloatRegisters::Single);
91 : static constexpr FloatRegister ScratchDoubleReg = FloatRegister(X86Encoding::xmm15, FloatRegisters::Double);
92 : static constexpr FloatRegister ScratchSimd128Reg = xmm15;
93 :
94 : // Avoid rbp, which is the FramePointer, which is unavailable in some modes.
95 : static constexpr Register ArgumentsRectifierReg = r8;
96 : static constexpr Register CallTempReg0 = rax;
97 : static constexpr Register CallTempReg1 = rdi;
98 : static constexpr Register CallTempReg2 = rbx;
99 : static constexpr Register CallTempReg3 = rcx;
100 : static constexpr Register CallTempReg4 = rsi;
101 : static constexpr Register CallTempReg5 = rdx;
102 :
103 : // Different argument registers for WIN64
104 : #if defined(_WIN64)
105 : static constexpr Register IntArgReg0 = rcx;
106 : static constexpr Register IntArgReg1 = rdx;
107 : static constexpr Register IntArgReg2 = r8;
108 : static constexpr Register IntArgReg3 = r9;
109 : static constexpr uint32_t NumIntArgRegs = 4;
110 : // Use "const" instead of constexpr here to work around a bug
111 : // of VS2015 Update 1. See bug 1229604.
112 : static const Register IntArgRegs[NumIntArgRegs] = { rcx, rdx, r8, r9 };
113 :
114 : static const Register CallTempNonArgRegs[] = { rax, rdi, rbx, rsi };
115 : static const uint32_t NumCallTempNonArgRegs =
116 : mozilla::ArrayLength(CallTempNonArgRegs);
117 :
118 : static constexpr FloatRegister FloatArgReg0 = xmm0;
119 : static constexpr FloatRegister FloatArgReg1 = xmm1;
120 : static constexpr FloatRegister FloatArgReg2 = xmm2;
121 : static constexpr FloatRegister FloatArgReg3 = xmm3;
122 : static const uint32_t NumFloatArgRegs = 4;
123 : static constexpr FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3 };
124 : #else
125 : static constexpr Register IntArgReg0 = rdi;
126 : static constexpr Register IntArgReg1 = rsi;
127 : static constexpr Register IntArgReg2 = rdx;
128 : static constexpr Register IntArgReg3 = rcx;
129 : static constexpr Register IntArgReg4 = r8;
130 : static constexpr Register IntArgReg5 = r9;
131 : static constexpr uint32_t NumIntArgRegs = 6;
132 : static const Register IntArgRegs[NumIntArgRegs] = { rdi, rsi, rdx, rcx, r8, r9 };
133 :
134 : // Use "const" instead of constexpr here to work around a bug
135 : // of VS2015 Update 1. See bug 1229604.
136 : static const Register CallTempNonArgRegs[] = { rax, rbx };
137 : static const uint32_t NumCallTempNonArgRegs =
138 : mozilla::ArrayLength(CallTempNonArgRegs);
139 :
140 : static constexpr FloatRegister FloatArgReg0 = xmm0;
141 : static constexpr FloatRegister FloatArgReg1 = xmm1;
142 : static constexpr FloatRegister FloatArgReg2 = xmm2;
143 : static constexpr FloatRegister FloatArgReg3 = xmm3;
144 : static constexpr FloatRegister FloatArgReg4 = xmm4;
145 : static constexpr FloatRegister FloatArgReg5 = xmm5;
146 : static constexpr FloatRegister FloatArgReg6 = xmm6;
147 : static constexpr FloatRegister FloatArgReg7 = xmm7;
148 : static constexpr uint32_t NumFloatArgRegs = 8;
149 : static constexpr FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
150 : #endif
151 :
152 : // Registers used in the GenerateFFIIonExit Enable Activation block.
153 : static constexpr Register WasmIonExitRegCallee = r10;
154 : static constexpr Register WasmIonExitRegE0 = rax;
155 : static constexpr Register WasmIonExitRegE1 = rdi;
156 :
157 : // Registers used in the GenerateFFIIonExit Disable Activation block.
158 : static constexpr Register WasmIonExitRegReturnData = ecx;
159 : static constexpr Register WasmIonExitRegReturnType = ecx;
160 : static constexpr Register WasmIonExitTlsReg = r14;
161 : static constexpr Register WasmIonExitRegD0 = rax;
162 : static constexpr Register WasmIonExitRegD1 = rdi;
163 : static constexpr Register WasmIonExitRegD2 = rbx;
164 :
165 : // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
166 : static constexpr Register RegExpMatcherRegExpReg = CallTempReg0;
167 : static constexpr Register RegExpMatcherStringReg = CallTempReg1;
168 : static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2;
169 :
170 : // Registerd used in RegExpTester instruction (do not use ReturnReg).
171 : static constexpr Register RegExpTesterRegExpReg = CallTempReg1;
172 : static constexpr Register RegExpTesterStringReg = CallTempReg2;
173 : static constexpr Register RegExpTesterLastIndexReg = CallTempReg3;
174 :
175 : class ABIArgGenerator
176 : {
177 : #if defined(XP_WIN)
178 : unsigned regIndex_;
179 : #else
180 : unsigned intRegIndex_;
181 : unsigned floatRegIndex_;
182 : #endif
183 : uint32_t stackOffset_;
184 : ABIArg current_;
185 :
186 : public:
187 : ABIArgGenerator();
188 : ABIArg next(MIRType argType);
189 0 : ABIArg& current() { return current_; }
190 13023 : uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
191 : };
192 :
193 : // Avoid r11, which is the MacroAssembler's ScratchReg.
194 : static constexpr Register ABINonArgReg0 = rax;
195 : static constexpr Register ABINonArgReg1 = rbx;
196 : static constexpr Register ABINonArgReg2 = r10;
197 :
198 : // Note: these three registers are all guaranteed to be different
199 : static constexpr Register ABINonArgReturnReg0 = r10;
200 : static constexpr Register ABINonArgReturnReg1 = r12;
201 : static constexpr Register ABINonVolatileReg = r13;
202 :
203 : // TLS pointer argument register for WebAssembly functions. This must not alias
204 : // any other register used for passing function arguments or return values.
205 : // Preserved by WebAssembly functions.
206 : static constexpr Register WasmTlsReg = r14;
207 :
208 : // Registers used for asm.js/wasm table calls. These registers must be disjoint
209 : // from the ABI argument registers, WasmTlsReg and each other.
210 : static constexpr Register WasmTableCallScratchReg = ABINonArgReg0;
211 : static constexpr Register WasmTableCallSigReg = ABINonArgReg1;
212 : static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;
213 :
214 : static constexpr Register OsrFrameReg = IntArgReg3;
215 :
216 : static constexpr Register PreBarrierReg = rdx;
217 :
218 : static constexpr uint32_t ABIStackAlignment = 16;
219 : static constexpr uint32_t CodeAlignment = 16;
220 : static constexpr uint32_t JitStackAlignment = 16;
221 :
222 : static constexpr uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value);
223 : static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1,
224 : "Stack alignment should be a non-zero multiple of sizeof(Value)");
225 :
226 : // This boolean indicates whether we support SIMD instructions flavoured for
227 : // this architecture or not. Rather than a method in the LIRGenerator, it is
228 : // here such that it is accessible from the entire codebase. Once full support
229 : // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
230 : static constexpr bool SupportsSimd = true;
231 : static constexpr uint32_t SimdMemoryAlignment = 16;
232 :
233 : static_assert(CodeAlignment % SimdMemoryAlignment == 0,
234 : "Code alignment should be larger than any of the alignments which are used for "
235 : "the constant sections of the code buffer. Thus it should be larger than the "
236 : "alignment for SIMD constants.");
237 :
238 : static_assert(JitStackAlignment % SimdMemoryAlignment == 0,
239 : "Stack alignment should be larger than any of the alignments which are used for "
240 : "spilled values. Thus it should be larger than the alignment for SIMD accesses.");
241 :
242 : static const uint32_t WasmStackAlignment = SimdMemoryAlignment;
243 :
244 : static const Scale ScalePointer = TimesEight;
245 :
246 : } // namespace jit
247 : } // namespace js
248 :
249 : #include "jit/x86-shared/Assembler-x86-shared.h"
250 :
251 : namespace js {
252 : namespace jit {
253 :
254 : // Return operand from a JS -> JS call.
255 : static constexpr ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
256 :
257 4503 : class Assembler : public AssemblerX86Shared
258 : {
259 : // x64 jumps may need extra bits of relocation, because a jump may extend
260 : // beyond the signed 32-bit range. To account for this we add an extended
261 : // jump table at the bottom of the instruction stream, and if a jump
262 : // overflows its range, it will redirect here.
263 : //
264 : // In our relocation table, we store two offsets instead of one: the offset
265 : // to the original jump, and an offset to the extended jump if we will need
266 : // to use it instead. The offsets are stored as:
267 : // [unsigned] Unsigned offset to short jump, from the start of the code.
268 : // [unsigned] Unsigned offset to the extended jump, from the start of
269 : // the jump table, in units of SizeOfJumpTableEntry.
270 : //
271 : // The start of the relocation table contains the offset from the code
272 : // buffer to the start of the extended jump table.
273 : //
274 : // Each entry in this table is a jmp [rip], followed by a ud2 to hint to the
275 : // hardware branch predictor that there is no fallthrough, followed by the
276 : // eight bytes containing an immediate address. This comes out to 16 bytes.
277 : // +1 byte for opcode
278 : // +1 byte for mod r/m
279 : // +4 bytes for rip-relative offset (2)
280 : // +2 bytes for ud2 instruction
281 : // +8 bytes for 64-bit address
282 : //
283 : static const uint32_t SizeOfExtendedJump = 1 + 1 + 4 + 2 + 8;
284 : static const uint32_t SizeOfJumpTableEntry = 16;
285 :
286 : uint32_t extendedJumpTable_;
287 :
288 : static JitCode* CodeFromJump(JitCode* code, uint8_t* jump);
289 :
290 : private:
291 : void writeRelocation(JmpSrc src, Relocation::Kind reloc);
292 : void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc);
293 :
294 : protected:
295 : size_t addPatchableJump(JmpSrc src, Relocation::Kind reloc);
296 :
297 : public:
298 : using AssemblerX86Shared::j;
299 : using AssemblerX86Shared::jmp;
300 : using AssemblerX86Shared::push;
301 : using AssemblerX86Shared::pop;
302 : using AssemblerX86Shared::vmovq;
303 :
304 : static uint8_t* PatchableJumpAddress(JitCode* code, size_t index);
305 : static void PatchJumpEntry(uint8_t* entry, uint8_t* target, ReprotectCode reprotect);
306 :
307 4503 : Assembler()
308 4503 : : extendedJumpTable_(0)
309 : {
310 4503 : }
311 :
312 : static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
313 :
314 : // The buffer is about to be linked, make sure any constant pools or excess
315 : // bookkeeping has been flushed to the instruction stream.
316 : void finish();
317 :
318 : // Copy the assembly code to the given buffer, and perform any pending
319 : // relocations relying on the target address.
320 : void executableCopy(uint8_t* buffer, bool flushICache = true);
321 :
322 : // Actual assembly emitting functions.
323 :
324 514 : void push(const ImmGCPtr ptr) {
325 514 : movq(ptr, ScratchReg);
326 514 : push(ScratchReg);
327 514 : }
328 5419 : void push(const ImmWord ptr) {
329 : // We often end up with ImmWords that actually fit into int32.
330 : // Be aware of the sign extension behavior.
331 5419 : if (ptr.value <= INT32_MAX) {
332 102 : push(Imm32(ptr.value));
333 : } else {
334 5317 : movq(ptr, ScratchReg);
335 5317 : push(ScratchReg);
336 : }
337 5419 : }
338 : void push(ImmPtr imm) {
339 : push(ImmWord(uintptr_t(imm.value)));
340 : }
341 1 : void push(FloatRegister src) {
342 1 : subq(Imm32(sizeof(double)), StackPointer);
343 1 : vmovsd(src, Address(StackPointer, 0));
344 1 : }
345 1087 : CodeOffset pushWithPatch(ImmWord word) {
346 1087 : CodeOffset label = movWithPatch(word, ScratchReg);
347 1087 : push(ScratchReg);
348 1087 : return label;
349 : }
350 :
351 2 : void pop(FloatRegister src) {
352 2 : vmovsd(Address(StackPointer, 0), src);
353 2 : addq(Imm32(sizeof(double)), StackPointer);
354 2 : }
355 :
356 25869 : CodeOffset movWithPatch(ImmWord word, Register dest) {
357 25869 : masm.movq_i64r(word.value, dest.encoding());
358 25869 : return CodeOffset(masm.currentOffset());
359 : }
360 206 : CodeOffset movWithPatch(ImmPtr imm, Register dest) {
361 206 : return movWithPatch(ImmWord(uintptr_t(imm.value)), dest);
362 : }
363 :
364 : // This is for patching during code generation, not after.
365 0 : void patchAddq(CodeOffset offset, int32_t n) {
366 0 : unsigned char* code = masm.data();
367 0 : X86Encoding::SetInt32(code + offset.offset(), n);
368 0 : }
369 :
370 : // Load an ImmWord value into a register. Note that this instruction will
371 : // attempt to optimize its immediate field size. When a full 64-bit
372 : // immediate is needed for a relocation, use movWithPatch.
373 37932 : void movq(ImmWord word, Register dest) {
374 : // Load a 64-bit immediate into a register. If the value falls into
375 : // certain ranges, we can use specialized instructions which have
376 : // smaller encodings.
377 37932 : if (word.value <= UINT32_MAX) {
378 : // movl has a 32-bit unsigned (effectively) immediate field.
379 5777 : masm.movl_i32r((uint32_t)word.value, dest.encoding());
380 32155 : } else if ((intptr_t)word.value >= INT32_MIN && (intptr_t)word.value <= INT32_MAX) {
381 : // movq has a 32-bit signed immediate field.
382 0 : masm.movq_i32r((int32_t)(intptr_t)word.value, dest.encoding());
383 : } else {
384 : // Otherwise use movabs.
385 32155 : masm.movq_i64r(word.value, dest.encoding());
386 : }
387 37931 : }
388 22704 : void movq(ImmPtr imm, Register dest) {
389 22704 : movq(ImmWord(uintptr_t(imm.value)), dest);
390 22704 : }
391 4213 : void movq(ImmGCPtr ptr, Register dest) {
392 4213 : masm.movq_i64r(uintptr_t(ptr.value), dest.encoding());
393 4213 : writeDataRelocation(ptr);
394 4213 : }
395 51753 : void movq(const Operand& src, Register dest) {
396 51753 : switch (src.kind()) {
397 : case Operand::REG:
398 0 : masm.movq_rr(src.reg(), dest.encoding());
399 0 : break;
400 : case Operand::MEM_REG_DISP:
401 51409 : masm.movq_mr(src.disp(), src.base(), dest.encoding());
402 51409 : break;
403 : case Operand::MEM_SCALE:
404 344 : masm.movq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
405 344 : break;
406 : case Operand::MEM_ADDRESS32:
407 0 : masm.movq_mr(src.address(), dest.encoding());
408 0 : break;
409 : default:
410 0 : MOZ_CRASH("unexpected operand kind");
411 : }
412 51753 : }
413 26241 : void movq(Register src, const Operand& dest) {
414 26241 : switch (dest.kind()) {
415 : case Operand::REG:
416 16925 : masm.movq_rr(src.encoding(), dest.reg());
417 16925 : break;
418 : case Operand::MEM_REG_DISP:
419 9252 : masm.movq_rm(src.encoding(), dest.disp(), dest.base());
420 9252 : break;
421 : case Operand::MEM_SCALE:
422 65 : masm.movq_rm(src.encoding(), dest.disp(), dest.base(), dest.index(), dest.scale());
423 65 : break;
424 : case Operand::MEM_ADDRESS32:
425 0 : masm.movq_rm(src.encoding(), dest.address());
426 0 : break;
427 : default:
428 0 : MOZ_CRASH("unexpected operand kind");
429 : }
430 26242 : }
431 1381 : void movq(Imm32 imm32, const Operand& dest) {
432 1381 : switch (dest.kind()) {
433 : case Operand::REG:
434 0 : masm.movl_i32r(imm32.value, dest.reg());
435 0 : break;
436 : case Operand::MEM_REG_DISP:
437 1381 : masm.movq_i32m(imm32.value, dest.disp(), dest.base());
438 1381 : break;
439 : case Operand::MEM_SCALE:
440 0 : masm.movq_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale());
441 0 : break;
442 : case Operand::MEM_ADDRESS32:
443 0 : masm.movq_i32m(imm32.value, dest.address());
444 0 : break;
445 : default:
446 0 : MOZ_CRASH("unexpected operand kind");
447 : }
448 1381 : }
449 52 : void vmovq(Register src, FloatRegister dest) {
450 52 : masm.vmovq_rr(src.encoding(), dest.encoding());
451 52 : }
452 9 : void vmovq(FloatRegister src, Register dest) {
453 9 : masm.vmovq_rr(src.encoding(), dest.encoding());
454 9 : }
455 33037 : void movq(Register src, Register dest) {
456 33037 : masm.movq_rr(src.encoding(), dest.encoding());
457 33037 : }
458 :
459 0 : void cmovzq(const Operand& src, Register dest) {
460 0 : switch (src.kind()) {
461 : case Operand::REG:
462 0 : masm.cmovzq_rr(src.reg(), dest.encoding());
463 0 : break;
464 : case Operand::MEM_REG_DISP:
465 0 : masm.cmovzq_mr(src.disp(), src.base(), dest.encoding());
466 0 : break;
467 : case Operand::MEM_SCALE:
468 0 : masm.cmovzq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
469 0 : break;
470 : default:
471 0 : MOZ_CRASH("unexpected operand kind");
472 : }
473 0 : }
474 :
475 5 : void xchgq(Register src, Register dest) {
476 5 : masm.xchgq_rr(src.encoding(), dest.encoding());
477 5 : }
478 :
479 0 : void movsbq(const Operand& src, Register dest) {
480 0 : switch (src.kind()) {
481 : case Operand::MEM_REG_DISP:
482 0 : masm.movsbq_mr(src.disp(), src.base(), dest.encoding());
483 0 : break;
484 : case Operand::MEM_SCALE:
485 0 : masm.movsbq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
486 0 : break;
487 : default:
488 0 : MOZ_CRASH("unexpected operand kind");
489 : }
490 0 : }
491 :
492 0 : void movzbq(const Operand& src, Register dest) {
493 : // movzbl zero-extends to 64 bits and is one byte smaller, so use that
494 : // instead.
495 0 : movzbl(src, dest);
496 0 : }
497 :
498 0 : void movswq(const Operand& src, Register dest) {
499 0 : switch (src.kind()) {
500 : case Operand::MEM_REG_DISP:
501 0 : masm.movswq_mr(src.disp(), src.base(), dest.encoding());
502 0 : break;
503 : case Operand::MEM_SCALE:
504 0 : masm.movswq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
505 0 : break;
506 : default:
507 0 : MOZ_CRASH("unexpected operand kind");
508 : }
509 0 : }
510 :
511 0 : void movzwq(const Operand& src, Register dest) {
512 : // movzwl zero-extends to 64 bits and is one byte smaller, so use that
513 : // instead.
514 0 : movzwl(src, dest);
515 0 : }
516 :
517 0 : void movslq(Register src, Register dest) {
518 0 : masm.movslq_rr(src.encoding(), dest.encoding());
519 0 : }
520 0 : void movslq(const Operand& src, Register dest) {
521 0 : switch (src.kind()) {
522 : case Operand::REG:
523 0 : masm.movslq_rr(src.reg(), dest.encoding());
524 0 : break;
525 : case Operand::MEM_REG_DISP:
526 0 : masm.movslq_mr(src.disp(), src.base(), dest.encoding());
527 0 : break;
528 : case Operand::MEM_SCALE:
529 0 : masm.movslq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
530 0 : break;
531 : default:
532 0 : MOZ_CRASH("unexpected operand kind");
533 : }
534 0 : }
535 :
536 1452 : void andq(Register src, Register dest) {
537 1452 : masm.andq_rr(src.encoding(), dest.encoding());
538 1452 : }
539 14369 : void andq(Imm32 imm, Register dest) {
540 14369 : masm.andq_ir(imm.value, dest.encoding());
541 14369 : }
542 196 : void andq(const Operand& src, Register dest) {
543 196 : switch (src.kind()) {
544 : case Operand::REG:
545 33 : masm.andq_rr(src.reg(), dest.encoding());
546 33 : break;
547 : case Operand::MEM_REG_DISP:
548 162 : masm.andq_mr(src.disp(), src.base(), dest.encoding());
549 162 : break;
550 : case Operand::MEM_SCALE:
551 1 : masm.andq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
552 1 : break;
553 : case Operand::MEM_ADDRESS32:
554 0 : masm.andq_mr(src.address(), dest.encoding());
555 0 : break;
556 : default:
557 0 : MOZ_CRASH("unexpected operand kind");
558 : }
559 196 : }
560 :
561 35224 : void addq(Imm32 imm, Register dest) {
562 35224 : masm.addq_ir(imm.value, dest.encoding());
563 35223 : }
564 0 : CodeOffset addqWithPatch(Imm32 imm, Register dest) {
565 0 : masm.addq_i32r(imm.value, dest.encoding());
566 0 : return CodeOffset(masm.currentOffset());
567 : }
568 7508 : void addq(Imm32 imm, const Operand& dest) {
569 7508 : switch (dest.kind()) {
570 : case Operand::REG:
571 0 : masm.addq_ir(imm.value, dest.reg());
572 0 : break;
573 : case Operand::MEM_REG_DISP:
574 7508 : masm.addq_im(imm.value, dest.disp(), dest.base());
575 7508 : break;
576 : case Operand::MEM_ADDRESS32:
577 0 : masm.addq_im(imm.value, dest.address());
578 0 : break;
579 : default:
580 0 : MOZ_CRASH("unexpected operand kind");
581 : }
582 7508 : }
583 497 : void addq(Register src, Register dest) {
584 497 : masm.addq_rr(src.encoding(), dest.encoding());
585 497 : }
586 22 : void addq(const Operand& src, Register dest) {
587 22 : switch (src.kind()) {
588 : case Operand::REG:
589 0 : masm.addq_rr(src.reg(), dest.encoding());
590 0 : break;
591 : case Operand::MEM_REG_DISP:
592 22 : masm.addq_mr(src.disp(), src.base(), dest.encoding());
593 22 : break;
594 : case Operand::MEM_ADDRESS32:
595 0 : masm.addq_mr(src.address(), dest.encoding());
596 0 : break;
597 : default:
598 0 : MOZ_CRASH("unexpected operand kind");
599 : }
600 22 : }
601 :
602 28836 : void subq(Imm32 imm, Register dest) {
603 28836 : masm.subq_ir(imm.value, dest.encoding());
604 28836 : }
605 1700 : void subq(Register src, Register dest) {
606 1700 : masm.subq_rr(src.encoding(), dest.encoding());
607 1700 : }
608 91 : void subq(const Operand& src, Register dest) {
609 91 : switch (src.kind()) {
610 : case Operand::REG:
611 0 : masm.subq_rr(src.reg(), dest.encoding());
612 0 : break;
613 : case Operand::MEM_REG_DISP:
614 91 : masm.subq_mr(src.disp(), src.base(), dest.encoding());
615 91 : break;
616 : case Operand::MEM_ADDRESS32:
617 0 : masm.subq_mr(src.address(), dest.encoding());
618 0 : break;
619 : default:
620 0 : MOZ_CRASH("unexpected operand kind");
621 : }
622 91 : }
623 : void subq(Register src, const Operand& dest) {
624 : switch (dest.kind()) {
625 : case Operand::REG:
626 : masm.subq_rr(src.encoding(), dest.reg());
627 : break;
628 : case Operand::MEM_REG_DISP:
629 : masm.subq_rm(src.encoding(), dest.disp(), dest.base());
630 : break;
631 : default:
632 : MOZ_CRASH("unexpected operand kind");
633 : }
634 : }
635 1668 : void shlq(Imm32 imm, Register dest) {
636 1668 : masm.shlq_ir(imm.value, dest.encoding());
637 1668 : }
638 5828 : void shrq(Imm32 imm, Register dest) {
639 5828 : masm.shrq_ir(imm.value, dest.encoding());
640 5828 : }
641 17 : void sarq(Imm32 imm, Register dest) {
642 17 : masm.sarq_ir(imm.value, dest.encoding());
643 17 : }
644 0 : void shlq_cl(Register dest) {
645 0 : masm.shlq_CLr(dest.encoding());
646 0 : }
647 0 : void shrq_cl(Register dest) {
648 0 : masm.shrq_CLr(dest.encoding());
649 0 : }
650 0 : void sarq_cl(Register dest) {
651 0 : masm.sarq_CLr(dest.encoding());
652 0 : }
653 0 : void rolq(Imm32 imm, Register dest) {
654 0 : masm.rolq_ir(imm.value, dest.encoding());
655 0 : }
656 0 : void rolq_cl(Register dest) {
657 0 : masm.rolq_CLr(dest.encoding());
658 0 : }
659 0 : void rorq(Imm32 imm, Register dest) {
660 0 : masm.rorq_ir(imm.value, dest.encoding());
661 0 : }
662 0 : void rorq_cl(Register dest) {
663 0 : masm.rorq_CLr(dest.encoding());
664 0 : }
665 2084 : void orq(Imm32 imm, Register dest) {
666 2084 : masm.orq_ir(imm.value, dest.encoding());
667 2084 : }
668 1306 : void orq(Register src, Register dest) {
669 1306 : masm.orq_rr(src.encoding(), dest.encoding());
670 1306 : }
671 0 : void orq(const Operand& src, Register dest) {
672 0 : switch (src.kind()) {
673 : case Operand::REG:
674 0 : masm.orq_rr(src.reg(), dest.encoding());
675 0 : break;
676 : case Operand::MEM_REG_DISP:
677 0 : masm.orq_mr(src.disp(), src.base(), dest.encoding());
678 0 : break;
679 : case Operand::MEM_ADDRESS32:
680 0 : masm.orq_mr(src.address(), dest.encoding());
681 0 : break;
682 : default:
683 0 : MOZ_CRASH("unexpected operand kind");
684 : }
685 0 : }
686 0 : void xorq(Register src, Register dest) {
687 0 : masm.xorq_rr(src.encoding(), dest.encoding());
688 0 : }
689 473 : void xorq(Imm32 imm, Register dest) {
690 473 : masm.xorq_ir(imm.value, dest.encoding());
691 473 : }
692 0 : void xorq(const Operand& src, Register dest) {
693 0 : switch (src.kind()) {
694 : case Operand::REG:
695 0 : masm.xorq_rr(src.reg(), dest.encoding());
696 0 : break;
697 : case Operand::MEM_REG_DISP:
698 0 : masm.xorq_mr(src.disp(), src.base(), dest.encoding());
699 0 : break;
700 : case Operand::MEM_ADDRESS32:
701 0 : masm.xorq_mr(src.address(), dest.encoding());
702 0 : break;
703 : default:
704 0 : MOZ_CRASH("unexpected operand kind");
705 : }
706 0 : }
707 :
708 0 : void bsrq(const Register& src, const Register& dest) {
709 0 : masm.bsrq_rr(src.encoding(), dest.encoding());
710 0 : }
711 0 : void bsfq(const Register& src, const Register& dest) {
712 0 : masm.bsfq_rr(src.encoding(), dest.encoding());
713 0 : }
714 0 : void popcntq(const Register& src, const Register& dest) {
715 0 : masm.popcntq_rr(src.encoding(), dest.encoding());
716 0 : }
717 :
718 0 : void imulq(Register src, Register dest) {
719 0 : masm.imulq_rr(src.encoding(), dest.encoding());
720 0 : }
721 0 : void imulq(const Operand& src, Register dest) {
722 0 : switch (src.kind()) {
723 : case Operand::REG:
724 0 : masm.imulq_rr(src.reg(), dest.encoding());
725 0 : break;
726 : case Operand::MEM_REG_DISP:
727 0 : masm.imulq_mr(src.disp(), src.base(), dest.encoding());
728 0 : break;
729 : case Operand::MEM_ADDRESS32:
730 0 : MOZ_CRASH("NYI");
731 : break;
732 : default:
733 0 : MOZ_CRASH("unexpected operand kind");
734 : }
735 0 : }
736 :
737 0 : void cqo() {
738 0 : masm.cqo();
739 0 : }
740 0 : void idivq(Register divisor) {
741 0 : masm.idivq_r(divisor.encoding());
742 0 : }
743 0 : void udivq(Register divisor) {
744 0 : masm.divq_r(divisor.encoding());
745 0 : }
746 :
747 : void vcvtsi2sdq(Register src, FloatRegister dest) {
748 : masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
749 : }
750 :
751 0 : void negq(Register reg) {
752 0 : masm.negq_r(reg.encoding());
753 0 : }
754 :
755 11110 : void mov(ImmWord word, Register dest) {
756 : // Use xor for setting registers to zero, as it is specially optimized
757 : // for this purpose on modern hardware. Note that it does clobber FLAGS
758 : // though. Use xorl instead of xorq since they are functionally
759 : // equivalent (32-bit instructions zero-extend their results to 64 bits)
760 : // and xorl has a smaller encoding.
761 11110 : if (word.value == 0)
762 1199 : xorl(dest, dest);
763 : else
764 9911 : movq(word, dest);
765 11110 : }
766 22704 : void mov(ImmPtr imm, Register dest) {
767 22704 : movq(imm, dest);
768 22704 : }
769 0 : void mov(wasm::SymbolicAddress imm, Register dest) {
770 0 : masm.movq_i64r(-1, dest.encoding());
771 0 : append(wasm::SymbolicAccess(CodeOffset(masm.currentOffset()), imm));
772 0 : }
773 : void mov(const Operand& src, Register dest) {
774 : movq(src, dest);
775 : }
776 17128 : void mov(Register src, const Operand& dest) {
777 17128 : movq(src, dest);
778 17128 : }
779 : void mov(Imm32 imm32, const Operand& dest) {
780 : movq(imm32, dest);
781 : }
782 1000 : void mov(Register src, Register dest) {
783 1000 : movq(src, dest);
784 1000 : }
785 17 : void mov(CodeOffset* label, Register dest) {
786 17 : masm.movq_i64r(/* placeholder */ 0, dest.encoding());
787 17 : label->bind(masm.size());
788 17 : }
789 5 : void xchg(Register src, Register dest) {
790 5 : xchgq(src, dest);
791 5 : }
792 3205 : void lea(const Operand& src, Register dest) {
793 3205 : switch (src.kind()) {
794 : case Operand::MEM_REG_DISP:
795 3087 : masm.leaq_mr(src.disp(), src.base(), dest.encoding());
796 3087 : break;
797 : case Operand::MEM_SCALE:
798 118 : masm.leaq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
799 118 : break;
800 : default:
801 0 : MOZ_CRASH("unexepcted operand kind");
802 : }
803 3205 : }
804 :
805 : CodeOffset loadRipRelativeInt32(Register dest) {
806 : return CodeOffset(masm.movl_ripr(dest.encoding()).offset());
807 : }
808 : CodeOffset loadRipRelativeInt64(Register dest) {
809 : return CodeOffset(masm.movq_ripr(dest.encoding()).offset());
810 : }
811 : CodeOffset loadRipRelativeDouble(FloatRegister dest) {
812 : return CodeOffset(masm.vmovsd_ripr(dest.encoding()).offset());
813 : }
814 : CodeOffset loadRipRelativeFloat32(FloatRegister dest) {
815 : return CodeOffset(masm.vmovss_ripr(dest.encoding()).offset());
816 : }
817 : CodeOffset loadRipRelativeInt32x4(FloatRegister dest) {
818 : return CodeOffset(masm.vmovdqa_ripr(dest.encoding()).offset());
819 : }
820 : CodeOffset loadRipRelativeFloat32x4(FloatRegister dest) {
821 : return CodeOffset(masm.vmovaps_ripr(dest.encoding()).offset());
822 : }
823 : CodeOffset storeRipRelativeInt32(Register dest) {
824 : return CodeOffset(masm.movl_rrip(dest.encoding()).offset());
825 : }
826 : CodeOffset storeRipRelativeInt64(Register dest) {
827 : return CodeOffset(masm.movq_rrip(dest.encoding()).offset());
828 : }
829 : CodeOffset storeRipRelativeDouble(FloatRegister dest) {
830 : return CodeOffset(masm.vmovsd_rrip(dest.encoding()).offset());
831 : }
832 : CodeOffset storeRipRelativeFloat32(FloatRegister dest) {
833 : return CodeOffset(masm.vmovss_rrip(dest.encoding()).offset());
834 : }
835 : CodeOffset storeRipRelativeInt32x4(FloatRegister dest) {
836 : return CodeOffset(masm.vmovdqa_rrip(dest.encoding()).offset());
837 : }
838 : CodeOffset storeRipRelativeFloat32x4(FloatRegister dest) {
839 : return CodeOffset(masm.vmovaps_rrip(dest.encoding()).offset());
840 : }
841 : CodeOffset leaRipRelative(Register dest) {
842 : return CodeOffset(masm.leaq_rip(dest.encoding()).offset());
843 : }
844 :
845 413 : void cmpq(Register rhs, Register lhs) {
846 413 : masm.cmpq_rr(rhs.encoding(), lhs.encoding());
847 413 : }
848 1946 : void cmpq(Register rhs, const Operand& lhs) {
849 1946 : switch (lhs.kind()) {
850 : case Operand::REG:
851 117 : masm.cmpq_rr(rhs.encoding(), lhs.reg());
852 117 : break;
853 : case Operand::MEM_REG_DISP:
854 1829 : masm.cmpq_rm(rhs.encoding(), lhs.disp(), lhs.base());
855 1829 : break;
856 : case Operand::MEM_ADDRESS32:
857 0 : masm.cmpq_rm(rhs.encoding(), lhs.address());
858 0 : break;
859 : default:
860 0 : MOZ_CRASH("unexpected operand kind");
861 : }
862 1946 : }
863 0 : void cmpq(Imm32 rhs, Register lhs) {
864 0 : masm.cmpq_ir(rhs.value, lhs.encoding());
865 0 : }
866 2748 : void cmpq(Imm32 rhs, const Operand& lhs) {
867 2748 : switch (lhs.kind()) {
868 : case Operand::REG:
869 1694 : masm.cmpq_ir(rhs.value, lhs.reg());
870 1694 : break;
871 : case Operand::MEM_REG_DISP:
872 1047 : masm.cmpq_im(rhs.value, lhs.disp(), lhs.base());
873 1047 : break;
874 : case Operand::MEM_SCALE:
875 7 : masm.cmpq_im(rhs.value, lhs.disp(), lhs.base(), lhs.index(), lhs.scale());
876 7 : break;
877 : case Operand::MEM_ADDRESS32:
878 0 : masm.cmpq_im(rhs.value, lhs.address());
879 0 : break;
880 : default:
881 0 : MOZ_CRASH("unexpected operand kind");
882 : }
883 2748 : }
884 0 : void cmpq(const Operand& rhs, Register lhs) {
885 0 : switch (rhs.kind()) {
886 : case Operand::REG:
887 0 : masm.cmpq_rr(rhs.reg(), lhs.encoding());
888 0 : break;
889 : case Operand::MEM_REG_DISP:
890 0 : masm.cmpq_mr(rhs.disp(), rhs.base(), lhs.encoding());
891 0 : break;
892 : default:
893 0 : MOZ_CRASH("unexpected operand kind");
894 : }
895 0 : }
896 :
897 13390 : void testq(Imm32 rhs, Register lhs) {
898 13390 : masm.testq_ir(rhs.value, lhs.encoding());
899 13390 : }
900 357 : void testq(Register rhs, Register lhs) {
901 357 : masm.testq_rr(rhs.encoding(), lhs.encoding());
902 357 : }
903 57 : void testq(Imm32 rhs, const Operand& lhs) {
904 57 : switch (lhs.kind()) {
905 : case Operand::REG:
906 0 : masm.testq_ir(rhs.value, lhs.reg());
907 0 : break;
908 : case Operand::MEM_REG_DISP:
909 57 : masm.testq_i32m(rhs.value, lhs.disp(), lhs.base());
910 57 : break;
911 : default:
912 0 : MOZ_CRASH("unexpected operand kind");
913 : break;
914 : }
915 57 : }
916 :
917 2614 : void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
918 2614 : JmpSrc src = masm.jmp();
919 2614 : addPendingJump(src, target, reloc);
920 2614 : }
921 : void j(Condition cond, ImmPtr target,
922 : Relocation::Kind reloc = Relocation::HARDCODED) {
923 : JmpSrc src = masm.jCC(static_cast<X86Encoding::Condition>(cond));
924 : addPendingJump(src, target, reloc);
925 : }
926 :
927 2606 : void jmp(JitCode* target) {
928 2606 : jmp(ImmPtr(target->raw()), Relocation::JITCODE);
929 2606 : }
930 : void j(Condition cond, JitCode* target) {
931 : j(cond, ImmPtr(target->raw()), Relocation::JITCODE);
932 : }
933 4069 : void call(JitCode* target) {
934 4069 : JmpSrc src = masm.call();
935 4069 : addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE);
936 4069 : }
937 0 : void call(ImmWord target) {
938 0 : call(ImmPtr((void*)target.value));
939 0 : }
940 12946 : void call(ImmPtr target) {
941 12946 : JmpSrc src = masm.call();
942 12946 : addPendingJump(src, target, Relocation::HARDCODED);
943 12946 : }
944 :
945 : // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch
946 : // this instruction.
947 0 : CodeOffset toggledCall(JitCode* target, bool enabled) {
948 0 : CodeOffset offset(size());
949 0 : JmpSrc src = enabled ? masm.call() : masm.cmp_eax();
950 0 : addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE);
951 0 : MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr));
952 0 : return offset;
953 : }
954 :
955 0 : static size_t ToggledCallSize(uint8_t* code) {
956 : // Size of a call instruction.
957 0 : return 5;
958 : }
959 :
960 : // Do not mask shared implementations.
961 : using AssemblerX86Shared::call;
962 :
963 0 : void vcvttsd2sq(FloatRegister src, Register dest) {
964 0 : masm.vcvttsd2sq_rr(src.encoding(), dest.encoding());
965 0 : }
966 0 : void vcvttss2sq(FloatRegister src, Register dest) {
967 0 : masm.vcvttss2sq_rr(src.encoding(), dest.encoding());
968 0 : }
969 0 : void vcvtsq2sd(Register src1, FloatRegister src0, FloatRegister dest) {
970 0 : masm.vcvtsq2sd_rr(src1.encoding(), src0.encoding(), dest.encoding());
971 0 : }
972 0 : void vcvtsq2ss(Register src1, FloatRegister src0, FloatRegister dest) {
973 0 : masm.vcvtsq2ss_rr(src1.encoding(), src0.encoding(), dest.encoding());
974 0 : }
975 : };
976 :
977 : static inline void
978 21 : PatchJump(CodeLocationJump jump, CodeLocationLabel label, ReprotectCode reprotect = DontReprotect)
979 : {
980 21 : if (X86Encoding::CanRelinkJump(jump.raw(), label.raw())) {
981 42 : MaybeAutoWritableJitCode awjc(jump.raw() - 8, 8, reprotect);
982 21 : X86Encoding::SetRel32(jump.raw(), label.raw());
983 : } else {
984 : {
985 0 : MaybeAutoWritableJitCode awjc(jump.raw() - 8, 8, reprotect);
986 0 : X86Encoding::SetRel32(jump.raw(), jump.jumpTableEntry());
987 : }
988 0 : Assembler::PatchJumpEntry(jump.jumpTableEntry(), label.raw(), reprotect);
989 : }
990 21 : }
991 :
992 : static inline void
993 0 : PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitZoneGroup::BackedgeTarget target)
994 : {
995 0 : PatchJump(jump_, label);
996 0 : }
997 :
998 : static inline bool
999 36 : GetIntArgReg(uint32_t intArg, uint32_t floatArg, Register* out)
1000 : {
1001 : #if defined(_WIN64)
1002 : uint32_t arg = intArg + floatArg;
1003 : #else
1004 36 : uint32_t arg = intArg;
1005 : #endif
1006 36 : if (arg >= NumIntArgRegs)
1007 0 : return false;
1008 36 : *out = IntArgRegs[arg];
1009 36 : return true;
1010 : }
1011 :
1012 : // Get a register in which we plan to put a quantity that will be used as an
1013 : // integer argument. This differs from GetIntArgReg in that if we have no more
1014 : // actual argument registers to use we will fall back on using whatever
1015 : // CallTempReg* don't overlap the argument registers, and only fail once those
1016 : // run out too.
1017 : static inline bool
1018 36 : GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out)
1019 : {
1020 36 : if (GetIntArgReg(usedIntArgs, usedFloatArgs, out))
1021 36 : return true;
1022 : // Unfortunately, we have to assume things about the point at which
1023 : // GetIntArgReg returns false, because we need to know how many registers it
1024 : // can allocate.
1025 : #if defined(_WIN64)
1026 : uint32_t arg = usedIntArgs + usedFloatArgs;
1027 : #else
1028 0 : uint32_t arg = usedIntArgs;
1029 : #endif
1030 0 : arg -= NumIntArgRegs;
1031 0 : if (arg >= NumCallTempNonArgRegs)
1032 0 : return false;
1033 0 : *out = CallTempNonArgRegs[arg];
1034 0 : return true;
1035 : }
1036 :
1037 : static inline bool
1038 : GetFloatArgReg(uint32_t intArg, uint32_t floatArg, FloatRegister* out)
1039 : {
1040 : #if defined(_WIN64)
1041 : uint32_t arg = intArg + floatArg;
1042 : #else
1043 : uint32_t arg = floatArg;
1044 : #endif
1045 : if (floatArg >= NumFloatArgRegs)
1046 : return false;
1047 : *out = FloatArgRegs[arg];
1048 : return true;
1049 : }
1050 :
1051 : } // namespace jit
1052 : } // namespace js
1053 :
1054 : #endif /* jit_x64_Assembler_x64_h */
|