js/src/jit/x64/Assembler-x64.h

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b0e9b3a1532f
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/shared/Assembler-shared.h"
14
15 namespace js {
16 namespace jit {
17
18 static MOZ_CONSTEXPR_VAR Register rax = { JSC::X86Registers::eax };
19 static MOZ_CONSTEXPR_VAR Register rbx = { JSC::X86Registers::ebx };
20 static MOZ_CONSTEXPR_VAR Register rcx = { JSC::X86Registers::ecx };
21 static MOZ_CONSTEXPR_VAR Register rdx = { JSC::X86Registers::edx };
22 static MOZ_CONSTEXPR_VAR Register rsi = { JSC::X86Registers::esi };
23 static MOZ_CONSTEXPR_VAR Register rdi = { JSC::X86Registers::edi };
24 static MOZ_CONSTEXPR_VAR Register rbp = { JSC::X86Registers::ebp };
25 static MOZ_CONSTEXPR_VAR Register r8 = { JSC::X86Registers::r8 };
26 static MOZ_CONSTEXPR_VAR Register r9 = { JSC::X86Registers::r9 };
27 static MOZ_CONSTEXPR_VAR Register r10 = { JSC::X86Registers::r10 };
28 static MOZ_CONSTEXPR_VAR Register r11 = { JSC::X86Registers::r11 };
29 static MOZ_CONSTEXPR_VAR Register r12 = { JSC::X86Registers::r12 };
30 static MOZ_CONSTEXPR_VAR Register r13 = { JSC::X86Registers::r13 };
31 static MOZ_CONSTEXPR_VAR Register r14 = { JSC::X86Registers::r14 };
32 static MOZ_CONSTEXPR_VAR Register r15 = { JSC::X86Registers::r15 };
33 static MOZ_CONSTEXPR_VAR Register rsp = { JSC::X86Registers::esp };
34
35 static MOZ_CONSTEXPR_VAR FloatRegister xmm0 = { JSC::X86Registers::xmm0 };
36 static MOZ_CONSTEXPR_VAR FloatRegister xmm1 = { JSC::X86Registers::xmm1 };
37 static MOZ_CONSTEXPR_VAR FloatRegister xmm2 = { JSC::X86Registers::xmm2 };
38 static MOZ_CONSTEXPR_VAR FloatRegister xmm3 = { JSC::X86Registers::xmm3 };
39 static MOZ_CONSTEXPR_VAR FloatRegister xmm4 = { JSC::X86Registers::xmm4 };
40 static MOZ_CONSTEXPR_VAR FloatRegister xmm5 = { JSC::X86Registers::xmm5 };
41 static MOZ_CONSTEXPR_VAR FloatRegister xmm6 = { JSC::X86Registers::xmm6 };
42 static MOZ_CONSTEXPR_VAR FloatRegister xmm7 = { JSC::X86Registers::xmm7 };
43 static MOZ_CONSTEXPR_VAR FloatRegister xmm8 = { JSC::X86Registers::xmm8 };
44 static MOZ_CONSTEXPR_VAR FloatRegister xmm9 = { JSC::X86Registers::xmm9 };
45 static MOZ_CONSTEXPR_VAR FloatRegister xmm10 = { JSC::X86Registers::xmm10 };
46 static MOZ_CONSTEXPR_VAR FloatRegister xmm11 = { JSC::X86Registers::xmm11 };
47 static MOZ_CONSTEXPR_VAR FloatRegister xmm12 = { JSC::X86Registers::xmm12 };
48 static MOZ_CONSTEXPR_VAR FloatRegister xmm13 = { JSC::X86Registers::xmm13 };
49 static MOZ_CONSTEXPR_VAR FloatRegister xmm14 = { JSC::X86Registers::xmm14 };
50 static MOZ_CONSTEXPR_VAR FloatRegister xmm15 = { JSC::X86Registers::xmm15 };
51
52 // X86-common synonyms.
53 static MOZ_CONSTEXPR_VAR Register eax = rax;
54 static MOZ_CONSTEXPR_VAR Register ebx = rbx;
55 static MOZ_CONSTEXPR_VAR Register ecx = rcx;
56 static MOZ_CONSTEXPR_VAR Register edx = rdx;
57 static MOZ_CONSTEXPR_VAR Register esi = rsi;
58 static MOZ_CONSTEXPR_VAR Register edi = rdi;
59 static MOZ_CONSTEXPR_VAR Register ebp = rbp;
60 static MOZ_CONSTEXPR_VAR Register esp = rsp;
61
62 static MOZ_CONSTEXPR_VAR Register InvalidReg = { JSC::X86Registers::invalid_reg };
63 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { JSC::X86Registers::invalid_xmm };
64
65 static MOZ_CONSTEXPR_VAR Register StackPointer = rsp;
66 static MOZ_CONSTEXPR_VAR Register FramePointer = rbp;
67 static MOZ_CONSTEXPR_VAR Register JSReturnReg = rcx;
68 // Avoid, except for assertions.
69 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = JSReturnReg;
70 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = JSReturnReg;
71
72 static MOZ_CONSTEXPR_VAR Register ReturnReg = rax;
73 static MOZ_CONSTEXPR_VAR Register ScratchReg = r11;
74 static MOZ_CONSTEXPR_VAR Register HeapReg = r15;
75 static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = xmm0;
76 static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = xmm15;
77
78 // Avoid rbp, which is the FramePointer, which is unavailable in some modes.
79 static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = r8;
80 static MOZ_CONSTEXPR_VAR Register CallTempReg0 = rax;
81 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = rdi;
82 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = rbx;
83 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = rcx;
84 static MOZ_CONSTEXPR_VAR Register CallTempReg4 = rsi;
85 static MOZ_CONSTEXPR_VAR Register CallTempReg5 = rdx;
86
87 // Different argument registers for WIN64
88 #if defined(_WIN64)
89 static MOZ_CONSTEXPR_VAR Register IntArgReg0 = rcx;
90 static MOZ_CONSTEXPR_VAR Register IntArgReg1 = rdx;
91 static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r8;
92 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r9;
93 static MOZ_CONSTEXPR_VAR uint32_t NumIntArgRegs = 4;
94 static MOZ_CONSTEXPR_VAR Register IntArgRegs[NumIntArgRegs] = { rcx, rdx, r8, r9 };
95
96 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { rax, rdi, rbx, rsi };
97 static const uint32_t NumCallTempNonArgRegs =
98 mozilla::ArrayLength(CallTempNonArgRegs);
99
100 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg0 = xmm0;
101 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg1 = xmm1;
102 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg2 = xmm2;
103 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg3 = xmm3;
104 static const uint32_t NumFloatArgRegs = 4;
105 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3 };
106 #else
107 static MOZ_CONSTEXPR_VAR Register IntArgReg0 = rdi;
108 static MOZ_CONSTEXPR_VAR Register IntArgReg1 = rsi;
109 static MOZ_CONSTEXPR_VAR Register IntArgReg2 = rdx;
110 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = rcx;
111 static MOZ_CONSTEXPR_VAR Register IntArgReg4 = r8;
112 static MOZ_CONSTEXPR_VAR Register IntArgReg5 = r9;
113 static MOZ_CONSTEXPR_VAR uint32_t NumIntArgRegs = 6;
114 static MOZ_CONSTEXPR_VAR Register IntArgRegs[NumIntArgRegs] = { rdi, rsi, rdx, rcx, r8, r9 };
115
116 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { rax, rbx };
117 static const uint32_t NumCallTempNonArgRegs =
118 mozilla::ArrayLength(CallTempNonArgRegs);
119
120 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg0 = xmm0;
121 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg1 = xmm1;
122 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg2 = xmm2;
123 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg3 = xmm3;
124 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg4 = xmm4;
125 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg5 = xmm5;
126 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg6 = xmm6;
127 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgReg7 = xmm7;
128 static MOZ_CONSTEXPR_VAR uint32_t NumFloatArgRegs = 8;
129 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
130 #endif
131
132 // The convention used by the ForkJoinGetSlice stub. None of these can be rax
133 // or rdx, which the stub also needs for cmpxchg and div, respectively.
134 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_cx = rdi;
135 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_temp0 = rbx;
136 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_temp1 = rcx;
137 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_output = rsi;
138
139 // Registers used in the GenerateFFIIonExit Enable Activation block.
140 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r10;
141 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = rax;
142 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = rdi;
143 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = rbx;
144 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = rsi;
145
146 // Registers used in the GenerateFFIIonExit Disable Activation block.
147 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = ecx;
148 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
149 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
150 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
151 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
152
153 class ABIArgGenerator
154 {
155 #if defined(XP_WIN)
156 unsigned regIndex_;
157 #else
158 unsigned intRegIndex_;
159 unsigned floatRegIndex_;
160 #endif
161 uint32_t stackOffset_;
162 ABIArg current_;
163
164 public:
165 ABIArgGenerator();
166 ABIArg next(MIRType argType);
167 ABIArg &current() { return current_; }
168 uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
169
170 // Note: these registers are all guaranteed to be different
171 static const Register NonArgReturnVolatileReg0;
172 static const Register NonArgReturnVolatileReg1;
173 static const Register NonVolatileReg;
174 };
175
176 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = IntArgReg3;
177
178 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = rdx;
179
180 // GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
181 // jitted code.
182 static const uint32_t StackAlignment = 16;
183 static const bool StackKeptAligned = false;
184 static const uint32_t CodeAlignment = 8;
185 static const uint32_t NativeFrameSize = sizeof(void*);
186 static const uint32_t AlignmentAtPrologue = sizeof(void*);
187 static const uint32_t AlignmentMidPrologue = AlignmentAtPrologue;
188
189 static const Scale ScalePointer = TimesEight;
190
191 } // namespace jit
192 } // namespace js
193
194 #include "jit/shared/Assembler-x86-shared.h"
195
196 namespace js {
197 namespace jit {
198
199 // Return operand from a JS -> JS call.
200 static MOZ_CONSTEXPR_VAR ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
201
202 class Assembler : public AssemblerX86Shared
203 {
204 // x64 jumps may need extra bits of relocation, because a jump may extend
205 // beyond the signed 32-bit range. To account for this we add an extended
206 // jump table at the bottom of the instruction stream, and if a jump
207 // overflows its range, it will redirect here.
208 //
209 // In our relocation table, we store two offsets instead of one: the offset
210 // to the original jump, and an offset to the extended jump if we will need
211 // to use it instead. The offsets are stored as:
212 // [unsigned] Unsigned offset to short jump, from the start of the code.
213 // [unsigned] Unsigned offset to the extended jump, from the start of
214 // the jump table, in units of SizeOfJumpTableEntry.
215 //
216 // The start of the relocation table contains the offset from the code
217 // buffer to the start of the extended jump table.
218 //
219 // Each entry in this table is a jmp [rip], followed by a ud2 to hint to the
220 // hardware branch predictor that there is no fallthrough, followed by the
221 // eight bytes containing an immediate address. This comes out to 16 bytes.
222 // +1 byte for opcode
223 // +1 byte for mod r/m
224 // +4 bytes for rip-relative offset (2)
225 // +2 bytes for ud2 instruction
226 // +8 bytes for 64-bit address
227 //
228 static const uint32_t SizeOfExtendedJump = 1 + 1 + 4 + 2 + 8;
229 static const uint32_t SizeOfJumpTableEntry = 16;
230
231 uint32_t extendedJumpTable_;
232
233 static JitCode *CodeFromJump(JitCode *code, uint8_t *jump);
234
235 private:
236 void writeRelocation(JmpSrc src, Relocation::Kind reloc);
237 void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc);
238
239 protected:
240 size_t addPatchableJump(JmpSrc src, Relocation::Kind reloc);
241
242 public:
243 using AssemblerX86Shared::j;
244 using AssemblerX86Shared::jmp;
245 using AssemblerX86Shared::push;
246 using AssemblerX86Shared::pop;
247
248 static uint8_t *PatchableJumpAddress(JitCode *code, size_t index);
249 static void PatchJumpEntry(uint8_t *entry, uint8_t *target);
250
251 Assembler()
252 : extendedJumpTable_(0)
253 {
254 }
255
256 static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
257
258 // The buffer is about to be linked, make sure any constant pools or excess
259 // bookkeeping has been flushed to the instruction stream.
260 void finish();
261
262 // Copy the assembly code to the given buffer, and perform any pending
263 // relocations relying on the target address.
264 void executableCopy(uint8_t *buffer);
265
266 // Actual assembly emitting functions.
267
268 void push(const ImmGCPtr ptr) {
269 movq(ptr, ScratchReg);
270 push(ScratchReg);
271 }
272 void push(const ImmWord ptr) {
273 // We often end up with ImmWords that actually fit into int32.
274 // Be aware of the sign extension behavior.
275 if (ptr.value <= INT32_MAX) {
276 push(Imm32(ptr.value));
277 } else {
278 movq(ptr, ScratchReg);
279 push(ScratchReg);
280 }
281 }
282 void push(const ImmPtr &imm) {
283 push(ImmWord(uintptr_t(imm.value)));
284 }
285 void push(const FloatRegister &src) {
286 subq(Imm32(sizeof(double)), StackPointer);
287 movsd(src, Address(StackPointer, 0));
288 }
289 CodeOffsetLabel pushWithPatch(const ImmWord &word) {
290 CodeOffsetLabel label = movWithPatch(word, ScratchReg);
291 push(ScratchReg);
292 return label;
293 }
294
295 void pop(const FloatRegister &src) {
296 movsd(Address(StackPointer, 0), src);
297 addq(Imm32(sizeof(double)), StackPointer);
298 }
299
300 CodeOffsetLabel movWithPatch(const ImmWord &word, const Register &dest) {
301 masm.movq_i64r(word.value, dest.code());
302 return masm.currentOffset();
303 }
304 CodeOffsetLabel movWithPatch(const ImmPtr &imm, const Register &dest) {
305 return movWithPatch(ImmWord(uintptr_t(imm.value)), dest);
306 }
307
308 // Load an ImmWord value into a register. Note that this instruction will
309 // attempt to optimize its immediate field size. When a full 64-bit
310 // immediate is needed for a relocation, use movWithPatch.
311 void movq(ImmWord word, const Register &dest) {
312 // Load a 64-bit immediate into a register. If the value falls into
313 // certain ranges, we can use specialized instructions which have
314 // smaller encodings.
315 if (word.value <= UINT32_MAX) {
316 // movl has a 32-bit unsigned (effectively) immediate field.
317 masm.movl_i32r((uint32_t)word.value, dest.code());
318 } else if ((intptr_t)word.value >= INT32_MIN && (intptr_t)word.value <= INT32_MAX) {
319 // movq has a 32-bit signed immediate field.
320 masm.movq_i32r((int32_t)(intptr_t)word.value, dest.code());
321 } else {
322 // Otherwise use movabs.
323 masm.movq_i64r(word.value, dest.code());
324 }
325 }
326 void movq(ImmPtr imm, const Register &dest) {
327 movq(ImmWord(uintptr_t(imm.value)), dest);
328 }
329 void movq(ImmGCPtr ptr, const Register &dest) {
330 masm.movq_i64r(ptr.value, dest.code());
331 writeDataRelocation(ptr);
332 }
333 void movq(const Operand &src, const Register &dest) {
334 switch (src.kind()) {
335 case Operand::REG:
336 masm.movq_rr(src.reg(), dest.code());
337 break;
338 case Operand::MEM_REG_DISP:
339 masm.movq_mr(src.disp(), src.base(), dest.code());
340 break;
341 case Operand::MEM_SCALE:
342 masm.movq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
343 break;
344 case Operand::MEM_ADDRESS32:
345 masm.movq_mr(src.address(), dest.code());
346 break;
347 default:
348 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
349 }
350 }
351 void movq(const Register &src, const Operand &dest) {
352 switch (dest.kind()) {
353 case Operand::REG:
354 masm.movq_rr(src.code(), dest.reg());
355 break;
356 case Operand::MEM_REG_DISP:
357 masm.movq_rm(src.code(), dest.disp(), dest.base());
358 break;
359 case Operand::MEM_SCALE:
360 masm.movq_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale());
361 break;
362 case Operand::MEM_ADDRESS32:
363 masm.movq_rm(src.code(), dest.address());
364 break;
365 default:
366 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
367 }
368 }
369 void movq(Imm32 imm32, const Operand &dest) {
370 switch (dest.kind()) {
371 case Operand::REG:
372 masm.movl_i32r(imm32.value, dest.reg());
373 break;
374 case Operand::MEM_REG_DISP:
375 masm.movq_i32m(imm32.value, dest.disp(), dest.base());
376 break;
377 case Operand::MEM_SCALE:
378 masm.movq_i32m(imm32.value, dest.disp(), dest.base(), dest.index(), dest.scale());
379 break;
380 case Operand::MEM_ADDRESS32:
381 masm.movq_i32m(imm32.value, dest.address());
382 break;
383 default:
384 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
385 }
386 }
387 void movq(const Register &src, const FloatRegister &dest) {
388 masm.movq_rr(src.code(), dest.code());
389 }
390 void movq(const FloatRegister &src, const Register &dest) {
391 masm.movq_rr(src.code(), dest.code());
392 }
393 void movq(const Register &src, const Register &dest) {
394 masm.movq_rr(src.code(), dest.code());
395 }
396
397 void xchgq(const Register &src, const Register &dest) {
398 masm.xchgq_rr(src.code(), dest.code());
399 }
400
401 void andq(const Register &src, const Register &dest) {
402 masm.andq_rr(src.code(), dest.code());
403 }
404 void andq(Imm32 imm, const Register &dest) {
405 masm.andq_ir(imm.value, dest.code());
406 }
407
408 void addq(Imm32 imm, const Register &dest) {
409 masm.addq_ir(imm.value, dest.code());
410 }
411 void addq(Imm32 imm, const Operand &dest) {
412 switch (dest.kind()) {
413 case Operand::REG:
414 masm.addq_ir(imm.value, dest.reg());
415 break;
416 case Operand::MEM_REG_DISP:
417 masm.addq_im(imm.value, dest.disp(), dest.base());
418 break;
419 case Operand::MEM_ADDRESS32:
420 masm.addq_im(imm.value, dest.address());
421 break;
422 default:
423 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
424 }
425 }
426 void addq(const Register &src, const Register &dest) {
427 masm.addq_rr(src.code(), dest.code());
428 }
429 void addq(const Operand &src, const Register &dest) {
430 switch (src.kind()) {
431 case Operand::REG:
432 masm.addq_rr(src.reg(), dest.code());
433 break;
434 case Operand::MEM_REG_DISP:
435 masm.addq_mr(src.disp(), src.base(), dest.code());
436 break;
437 case Operand::MEM_ADDRESS32:
438 masm.addq_mr(src.address(), dest.code());
439 break;
440 default:
441 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
442 }
443 }
444
445 void subq(Imm32 imm, const Register &dest) {
446 masm.subq_ir(imm.value, dest.code());
447 }
448 void subq(const Register &src, const Register &dest) {
449 masm.subq_rr(src.code(), dest.code());
450 }
451 void subq(const Operand &src, const Register &dest) {
452 switch (src.kind()) {
453 case Operand::REG:
454 masm.subq_rr(src.reg(), dest.code());
455 break;
456 case Operand::MEM_REG_DISP:
457 masm.subq_mr(src.disp(), src.base(), dest.code());
458 break;
459 case Operand::MEM_ADDRESS32:
460 masm.subq_mr(src.address(), dest.code());
461 break;
462 default:
463 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
464 }
465 }
466 void subq(const Register &src, const Operand &dest) {
467 switch (dest.kind()) {
468 case Operand::REG:
469 masm.subq_rr(src.code(), dest.reg());
470 break;
471 case Operand::MEM_REG_DISP:
472 masm.subq_rm(src.code(), dest.disp(), dest.base());
473 break;
474 default:
475 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
476 }
477 }
478 void shlq(Imm32 imm, const Register &dest) {
479 masm.shlq_i8r(imm.value, dest.code());
480 }
481 void shrq(Imm32 imm, const Register &dest) {
482 masm.shrq_i8r(imm.value, dest.code());
483 }
484 void sarq(Imm32 imm, const Register &dest) {
485 masm.sarq_i8r(imm.value, dest.code());
486 }
487 void orq(Imm32 imm, const Register &dest) {
488 masm.orq_ir(imm.value, dest.code());
489 }
490 void orq(const Register &src, const Register &dest) {
491 masm.orq_rr(src.code(), dest.code());
492 }
493 void orq(const Operand &src, const Register &dest) {
494 switch (src.kind()) {
495 case Operand::REG:
496 masm.orq_rr(src.reg(), dest.code());
497 break;
498 case Operand::MEM_REG_DISP:
499 masm.orq_mr(src.disp(), src.base(), dest.code());
500 break;
501 case Operand::MEM_ADDRESS32:
502 masm.orq_mr(src.address(), dest.code());
503 break;
504 default:
505 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
506 }
507 }
508 void xorq(const Register &src, const Register &dest) {
509 masm.xorq_rr(src.code(), dest.code());
510 }
511 void xorq(Imm32 imm, const Register &dest) {
512 masm.xorq_ir(imm.value, dest.code());
513 }
514
515 void mov(ImmWord word, const Register &dest) {
516 // Use xor for setting registers to zero, as it is specially optimized
517 // for this purpose on modern hardware. Note that it does clobber FLAGS
518 // though. Use xorl instead of xorq since they are functionally
519 // equivalent (32-bit instructions zero-extend their results to 64 bits)
520 // and xorl has a smaller encoding.
521 if (word.value == 0)
522 xorl(dest, dest);
523 else
524 movq(word, dest);
525 }
526 void mov(ImmPtr imm, const Register &dest) {
527 movq(imm, dest);
528 }
529 void mov(AsmJSImmPtr imm, const Register &dest) {
530 masm.movq_i64r(-1, dest.code());
531 enoughMemory_ &= append(AsmJSAbsoluteLink(masm.currentOffset(), imm.kind()));
532 }
533 void mov(const Operand &src, const Register &dest) {
534 movq(src, dest);
535 }
536 void mov(const Register &src, const Operand &dest) {
537 movq(src, dest);
538 }
539 void mov(const Imm32 &imm32, const Operand &dest) {
540 movq(imm32, dest);
541 }
542 void mov(const Register &src, const Register &dest) {
543 movq(src, dest);
544 }
545 void mov(AbsoluteLabel *label, const Register &dest) {
546 JS_ASSERT(!label->bound());
547 // Thread the patch list through the unpatched address word in the
548 // instruction stream.
549 masm.movq_i64r(label->prev(), dest.code());
550 label->setPrev(masm.size());
551 }
552 void xchg(const Register &src, const Register &dest) {
553 xchgq(src, dest);
554 }
555 void lea(const Operand &src, const Register &dest) {
556 switch (src.kind()) {
557 case Operand::MEM_REG_DISP:
558 masm.leaq_mr(src.disp(), src.base(), dest.code());
559 break;
560 case Operand::MEM_SCALE:
561 masm.leaq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
562 break;
563 default:
564 MOZ_ASSUME_UNREACHABLE("unexepcted operand kind");
565 }
566 }
567
568 CodeOffsetLabel loadRipRelativeInt32(const Register &dest) {
569 return CodeOffsetLabel(masm.movl_ripr(dest.code()).offset());
570 }
571 CodeOffsetLabel loadRipRelativeInt64(const Register &dest) {
572 return CodeOffsetLabel(masm.movq_ripr(dest.code()).offset());
573 }
574 CodeOffsetLabel loadRipRelativeDouble(const FloatRegister &dest) {
575 return CodeOffsetLabel(masm.movsd_ripr(dest.code()).offset());
576 }
577 CodeOffsetLabel storeRipRelativeInt32(const Register &dest) {
578 return CodeOffsetLabel(masm.movl_rrip(dest.code()).offset());
579 }
580 CodeOffsetLabel storeRipRelativeDouble(const FloatRegister &dest) {
581 return CodeOffsetLabel(masm.movsd_rrip(dest.code()).offset());
582 }
583 CodeOffsetLabel leaRipRelative(const Register &dest) {
584 return CodeOffsetLabel(masm.leaq_rip(dest.code()).offset());
585 }
586
587 // The below cmpq methods switch the lhs and rhs when it invokes the
588 // macroassembler to conform with intel standard. When calling this
589 // function put the left operand on the left as you would expect.
590 void cmpq(const Operand &lhs, const Register &rhs) {
591 switch (lhs.kind()) {
592 case Operand::REG:
593 masm.cmpq_rr(rhs.code(), lhs.reg());
594 break;
595 case Operand::MEM_REG_DISP:
596 masm.cmpq_rm(rhs.code(), lhs.disp(), lhs.base());
597 break;
598 case Operand::MEM_ADDRESS32:
599 masm.cmpq_rm(rhs.code(), lhs.address());
600 break;
601 default:
602 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
603 }
604 }
605 void cmpq(const Operand &lhs, Imm32 rhs) {
606 switch (lhs.kind()) {
607 case Operand::REG:
608 masm.cmpq_ir(rhs.value, lhs.reg());
609 break;
610 case Operand::MEM_REG_DISP:
611 masm.cmpq_im(rhs.value, lhs.disp(), lhs.base());
612 break;
613 case Operand::MEM_ADDRESS32:
614 masm.cmpq_im(rhs.value, lhs.address());
615 break;
616 default:
617 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
618 }
619 }
620 void cmpq(const Register &lhs, const Operand &rhs) {
621 switch (rhs.kind()) {
622 case Operand::REG:
623 masm.cmpq_rr(rhs.reg(), lhs.code());
624 break;
625 case Operand::MEM_REG_DISP:
626 masm.cmpq_mr(rhs.disp(), rhs.base(), lhs.code());
627 break;
628 default:
629 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
630 }
631 }
632 void cmpq(const Register &lhs, const Register &rhs) {
633 masm.cmpq_rr(rhs.code(), lhs.code());
634 }
635 void cmpq(const Register &lhs, Imm32 rhs) {
636 masm.cmpq_ir(rhs.value, lhs.code());
637 }
638
639 void testq(const Register &lhs, Imm32 rhs) {
640 masm.testq_i32r(rhs.value, lhs.code());
641 }
642 void testq(const Register &lhs, const Register &rhs) {
643 masm.testq_rr(rhs.code(), lhs.code());
644 }
645 void testq(const Operand &lhs, Imm32 rhs) {
646 switch (lhs.kind()) {
647 case Operand::REG:
648 masm.testq_i32r(rhs.value, lhs.reg());
649 break;
650 case Operand::MEM_REG_DISP:
651 masm.testq_i32m(rhs.value, lhs.disp(), lhs.base());
652 break;
653 default:
654 MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
655 break;
656 }
657 }
658
659 void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
660 JmpSrc src = masm.jmp();
661 addPendingJump(src, target, reloc);
662 }
663 void j(Condition cond, ImmPtr target,
664 Relocation::Kind reloc = Relocation::HARDCODED) {
665 JmpSrc src = masm.jCC(static_cast<JSC::X86Assembler::Condition>(cond));
666 addPendingJump(src, target, reloc);
667 }
668
669 void jmp(JitCode *target) {
670 jmp(ImmPtr(target->raw()), Relocation::JITCODE);
671 }
672 void j(Condition cond, JitCode *target) {
673 j(cond, ImmPtr(target->raw()), Relocation::JITCODE);
674 }
675 void call(JitCode *target) {
676 JmpSrc src = masm.call();
677 addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE);
678 }
679
680 // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch
681 // this instruction.
682 CodeOffsetLabel toggledCall(JitCode *target, bool enabled) {
683 CodeOffsetLabel offset(size());
684 JmpSrc src = enabled ? masm.call() : masm.cmp_eax();
685 addPendingJump(src, ImmPtr(target->raw()), Relocation::JITCODE);
686 JS_ASSERT(size() - offset.offset() == ToggledCallSize());
687 return offset;
688 }
689
690 static size_t ToggledCallSize() {
691 // Size of a call instruction.
692 return 5;
693 }
694
695 // Do not mask shared implementations.
696 using AssemblerX86Shared::call;
697
698 void cvttsd2sq(const FloatRegister &src, const Register &dest) {
699 masm.cvttsd2sq_rr(src.code(), dest.code());
700 }
701 void cvttss2sq(const FloatRegister &src, const Register &dest) {
702 masm.cvttss2sq_rr(src.code(), dest.code());
703 }
704 void cvtsq2sd(const Register &src, const FloatRegister &dest) {
705 masm.cvtsq2sd_rr(src.code(), dest.code());
706 }
707 void cvtsq2ss(const Register &src, const FloatRegister &dest) {
708 masm.cvtsq2ss_rr(src.code(), dest.code());
709 }
710 };
711
712 static inline void
713 PatchJump(CodeLocationJump jump, CodeLocationLabel label)
714 {
715 if (JSC::X86Assembler::canRelinkJump(jump.raw(), label.raw())) {
716 JSC::X86Assembler::setRel32(jump.raw(), label.raw());
717 } else {
718 JSC::X86Assembler::setRel32(jump.raw(), jump.jumpTableEntry());
719 Assembler::PatchJumpEntry(jump.jumpTableEntry(), label.raw());
720 }
721 }
722
723 static inline bool
724 GetIntArgReg(uint32_t intArg, uint32_t floatArg, Register *out)
725 {
726 #if defined(_WIN64)
727 uint32_t arg = intArg + floatArg;
728 #else
729 uint32_t arg = intArg;
730 #endif
731 if (arg >= NumIntArgRegs)
732 return false;
733 *out = IntArgRegs[arg];
734 return true;
735 }
736
737 // Get a register in which we plan to put a quantity that will be used as an
738 // integer argument. This differs from GetIntArgReg in that if we have no more
739 // actual argument registers to use we will fall back on using whatever
740 // CallTempReg* don't overlap the argument registers, and only fail once those
741 // run out too.
742 static inline bool
743 GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
744 {
745 if (GetIntArgReg(usedIntArgs, usedFloatArgs, out))
746 return true;
747 // Unfortunately, we have to assume things about the point at which
748 // GetIntArgReg returns false, because we need to know how many registers it
749 // can allocate.
750 #if defined(_WIN64)
751 uint32_t arg = usedIntArgs + usedFloatArgs;
752 #else
753 uint32_t arg = usedIntArgs;
754 #endif
755 arg -= NumIntArgRegs;
756 if (arg >= NumCallTempNonArgRegs)
757 return false;
758 *out = CallTempNonArgRegs[arg];
759 return true;
760 }
761
762 static inline bool
763 GetFloatArgReg(uint32_t intArg, uint32_t floatArg, FloatRegister *out)
764 {
765 #if defined(_WIN64)
766 uint32_t arg = intArg + floatArg;
767 #else
768 uint32_t arg = floatArg;
769 #endif
770 if (floatArg >= NumFloatArgRegs)
771 return false;
772 *out = FloatArgRegs[arg];
773 return true;
774 }
775
776 } // namespace jit
777 } // namespace js
778
779 #endif /* jit_x64_Assembler_x64_h */

mercurial