|
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 ¤t() { 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 */ |