|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jscompartment.h" |
|
8 |
|
9 #include "assembler/assembler/MacroAssembler.h" |
|
10 #include "jit/Bailouts.h" |
|
11 #include "jit/BaselineJIT.h" |
|
12 #include "jit/IonFrames.h" |
|
13 #include "jit/IonLinker.h" |
|
14 #include "jit/IonSpewer.h" |
|
15 #include "jit/JitCompartment.h" |
|
16 #ifdef JS_ION_PERF |
|
17 # include "jit/PerfSpewer.h" |
|
18 #endif |
|
19 #include "jit/VMFunctions.h" |
|
20 #include "jit/x86/BaselineHelpers-x86.h" |
|
21 |
|
22 #include "jsscriptinlines.h" |
|
23 |
|
24 #include "jit/ExecutionMode-inl.h" |
|
25 |
|
26 using namespace js; |
|
27 using namespace js::jit; |
|
28 |
|
29 // All registers to save and restore. This includes the stack pointer, since we |
|
30 // use the ability to reference register values on the stack by index. |
|
31 static const RegisterSet AllRegs = |
|
32 RegisterSet(GeneralRegisterSet(Registers::AllMask), |
|
33 FloatRegisterSet(FloatRegisters::AllMask)); |
|
34 |
|
35 enum EnterJitEbpArgumentOffset { |
|
36 ARG_JITCODE = 2 * sizeof(void *), |
|
37 ARG_ARGC = 3 * sizeof(void *), |
|
38 ARG_ARGV = 4 * sizeof(void *), |
|
39 ARG_STACKFRAME = 5 * sizeof(void *), |
|
40 ARG_CALLEETOKEN = 6 * sizeof(void *), |
|
41 ARG_SCOPECHAIN = 7 * sizeof(void *), |
|
42 ARG_STACKVALUES = 8 * sizeof(void *), |
|
43 ARG_RESULT = 9 * sizeof(void *) |
|
44 }; |
|
45 |
|
46 /* |
|
47 * Generates a trampoline for a C++ function with the EnterJitCode signature, |
|
48 * using the standard cdecl calling convention. |
|
49 */ |
|
50 JitCode * |
|
51 JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) |
|
52 { |
|
53 MacroAssembler masm(cx); |
|
54 |
|
55 // Save old stack frame pointer, set new stack frame pointer. |
|
56 masm.push(ebp); |
|
57 masm.movl(esp, ebp); |
|
58 |
|
59 // Save non-volatile registers. These must be saved by the trampoline, |
|
60 // rather than the JIT'd code, because they are scanned by the conservative |
|
61 // scanner. |
|
62 masm.push(ebx); |
|
63 masm.push(esi); |
|
64 masm.push(edi); |
|
65 |
|
66 // Push the EnterJIT sps mark. |
|
67 masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx); |
|
68 |
|
69 // Keep track of the stack which has to be unwound after returning from the |
|
70 // compiled function. |
|
71 masm.movl(esp, esi); |
|
72 |
|
73 // eax <- 8*argc, eax is now the offset betwen argv and the last |
|
74 masm.loadPtr(Address(ebp, ARG_ARGC), eax); |
|
75 masm.shll(Imm32(3), eax); |
|
76 |
|
77 // We need to ensure that the stack is aligned on a 12-byte boundary, so |
|
78 // inside the JIT function the stack is 16-byte aligned. Our stack right |
|
79 // now might not be aligned on some platforms (win32, gcc) so we factor |
|
80 // this possibility in, and simulate what the new stack address would be. |
|
81 // +argc * 8 for arguments |
|
82 // +4 for pushing alignment |
|
83 // +4 for pushing the callee token |
|
84 // +4 for pushing the return address |
|
85 masm.movl(esp, ecx); |
|
86 masm.subl(eax, ecx); |
|
87 masm.subl(Imm32(4 * 3), ecx); |
|
88 |
|
89 // ecx = ecx & 15, holds alignment. |
|
90 masm.andl(Imm32(15), ecx); |
|
91 masm.subl(ecx, esp); |
|
92 |
|
93 /*************************************************************** |
|
94 Loop over argv vector, push arguments onto stack in reverse order |
|
95 ***************************************************************/ |
|
96 |
|
97 // ebx = argv --argv pointer is in ebp + 16 |
|
98 masm.loadPtr(Address(ebp, ARG_ARGV), ebx); |
|
99 |
|
100 // eax = argv[8(argc)] --eax now points one value past the last argument |
|
101 masm.addl(ebx, eax); |
|
102 |
|
103 // while (eax > ebx) --while still looping through arguments |
|
104 { |
|
105 Label header, footer; |
|
106 masm.bind(&header); |
|
107 |
|
108 masm.cmpl(eax, ebx); |
|
109 masm.j(Assembler::BelowOrEqual, &footer); |
|
110 |
|
111 // eax -= 8 --move to previous argument |
|
112 masm.subl(Imm32(8), eax); |
|
113 |
|
114 // Push what eax points to on stack, a Value is 2 words |
|
115 masm.push(Operand(eax, 4)); |
|
116 masm.push(Operand(eax, 0)); |
|
117 |
|
118 masm.jmp(&header); |
|
119 masm.bind(&footer); |
|
120 } |
|
121 |
|
122 |
|
123 // Push the number of actual arguments. |result| is used to store the |
|
124 // actual number of arguments without adding an extra argument to the enter |
|
125 // JIT. |
|
126 masm.mov(Operand(ebp, ARG_RESULT), eax); |
|
127 masm.unboxInt32(Address(eax, 0x0), eax); |
|
128 masm.push(eax); |
|
129 |
|
130 // Push the callee token. |
|
131 masm.push(Operand(ebp, ARG_CALLEETOKEN)); |
|
132 |
|
133 // Load the InterpreterFrame address into the OsrFrameReg. |
|
134 // This address is also used for setting the constructing bit on all paths. |
|
135 masm.loadPtr(Address(ebp, ARG_STACKFRAME), OsrFrameReg); |
|
136 |
|
137 /***************************************************************** |
|
138 Push the number of bytes we've pushed so far on the stack and call |
|
139 *****************************************************************/ |
|
140 // Create a frame descriptor. |
|
141 masm.subl(esp, esi); |
|
142 masm.makeFrameDescriptor(esi, JitFrame_Entry); |
|
143 masm.push(esi); |
|
144 |
|
145 CodeLabel returnLabel; |
|
146 if (type == EnterJitBaseline) { |
|
147 // Handle OSR. |
|
148 GeneralRegisterSet regs(GeneralRegisterSet::All()); |
|
149 regs.take(JSReturnOperand); |
|
150 regs.takeUnchecked(OsrFrameReg); |
|
151 regs.take(ebp); |
|
152 regs.take(ReturnReg); |
|
153 |
|
154 Register scratch = regs.takeAny(); |
|
155 |
|
156 Label notOsr; |
|
157 masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr); |
|
158 |
|
159 Register numStackValues = regs.takeAny(); |
|
160 masm.loadPtr(Address(ebp, ARG_STACKVALUES), numStackValues); |
|
161 |
|
162 Register jitcode = regs.takeAny(); |
|
163 masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode); |
|
164 |
|
165 // Push return address, previous frame pointer. |
|
166 masm.mov(returnLabel.dest(), scratch); |
|
167 masm.push(scratch); |
|
168 masm.push(ebp); |
|
169 |
|
170 // Reserve frame. |
|
171 Register framePtr = ebp; |
|
172 masm.subPtr(Imm32(BaselineFrame::Size()), esp); |
|
173 masm.mov(esp, framePtr); |
|
174 |
|
175 #ifdef XP_WIN |
|
176 // Can't push large frames blindly on windows. Touch frame memory incrementally. |
|
177 masm.mov(numStackValues, scratch); |
|
178 masm.shll(Imm32(3), scratch); |
|
179 masm.subPtr(scratch, framePtr); |
|
180 { |
|
181 masm.movePtr(esp, scratch); |
|
182 masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); |
|
183 |
|
184 Label touchFrameLoop; |
|
185 Label touchFrameLoopEnd; |
|
186 masm.bind(&touchFrameLoop); |
|
187 masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd); |
|
188 masm.store32(Imm32(0), Address(scratch, 0)); |
|
189 masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); |
|
190 masm.jump(&touchFrameLoop); |
|
191 masm.bind(&touchFrameLoopEnd); |
|
192 } |
|
193 masm.mov(esp, framePtr); |
|
194 #endif |
|
195 |
|
196 // Reserve space for locals and stack values. |
|
197 masm.mov(numStackValues, scratch); |
|
198 masm.shll(Imm32(3), scratch); |
|
199 masm.subPtr(scratch, esp); |
|
200 |
|
201 // Enter exit frame. |
|
202 masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); |
|
203 masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); |
|
204 masm.push(scratch); |
|
205 masm.push(Imm32(0)); // Fake return address. |
|
206 masm.enterFakeExitFrame(); |
|
207 |
|
208 masm.push(framePtr); |
|
209 masm.push(jitcode); |
|
210 |
|
211 masm.setupUnalignedABICall(3, scratch); |
|
212 masm.passABIArg(framePtr); // BaselineFrame |
|
213 masm.passABIArg(OsrFrameReg); // InterpreterFrame |
|
214 masm.passABIArg(numStackValues); |
|
215 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr)); |
|
216 |
|
217 masm.pop(jitcode); |
|
218 masm.pop(framePtr); |
|
219 |
|
220 JS_ASSERT(jitcode != ReturnReg); |
|
221 |
|
222 Label error; |
|
223 masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), esp); |
|
224 masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); |
|
225 masm.branchIfFalseBool(ReturnReg, &error); |
|
226 |
|
227 masm.jump(jitcode); |
|
228 |
|
229 // OOM: load error value, discard return address and previous frame |
|
230 // pointer and return. |
|
231 masm.bind(&error); |
|
232 masm.mov(framePtr, esp); |
|
233 masm.addPtr(Imm32(2 * sizeof(uintptr_t)), esp); |
|
234 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); |
|
235 masm.mov(returnLabel.dest(), scratch); |
|
236 masm.jump(scratch); |
|
237 |
|
238 masm.bind(¬Osr); |
|
239 masm.loadPtr(Address(ebp, ARG_SCOPECHAIN), R1.scratchReg()); |
|
240 } |
|
241 |
|
242 /*************************************************************** |
|
243 Call passed-in code, get return value and fill in the |
|
244 passed in return value pointer |
|
245 ***************************************************************/ |
|
246 masm.call(Operand(ebp, ARG_JITCODE)); |
|
247 |
|
248 if (type == EnterJitBaseline) { |
|
249 // Baseline OSR will return here. |
|
250 masm.bind(returnLabel.src()); |
|
251 if (!masm.addCodeLabel(returnLabel)) |
|
252 return nullptr; |
|
253 } |
|
254 |
|
255 // Pop arguments off the stack. |
|
256 // eax <- 8*argc (size of all arguments we pushed on the stack) |
|
257 masm.pop(eax); |
|
258 masm.shrl(Imm32(FRAMESIZE_SHIFT), eax); // Unmark EntryFrame. |
|
259 masm.addl(eax, esp); |
|
260 |
|
261 // |ebp| could have been clobbered by the inner function. |
|
262 // Grab the address for the Value result from the argument stack. |
|
263 // +24 ... arguments ... |
|
264 // +20 <return> |
|
265 // +16 ebp <- original %ebp pointing here. |
|
266 // +12 ebx |
|
267 // +8 esi |
|
268 // +4 edi |
|
269 // +0 hasSPSFrame |
|
270 masm.loadPtr(Address(esp, ARG_RESULT + 4 * sizeof(void *)), eax); |
|
271 masm.storeValue(JSReturnOperand, Operand(eax, 0)); |
|
272 |
|
273 /************************************************************** |
|
274 Return stack and registers to correct state |
|
275 **************************************************************/ |
|
276 // Unwind the sps mark. |
|
277 masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx); |
|
278 |
|
279 // Restore non-volatile registers |
|
280 masm.pop(edi); |
|
281 masm.pop(esi); |
|
282 masm.pop(ebx); |
|
283 |
|
284 // Restore old stack frame pointer |
|
285 masm.pop(ebp); |
|
286 masm.ret(); |
|
287 |
|
288 Linker linker(masm); |
|
289 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
290 |
|
291 #ifdef JS_ION_PERF |
|
292 writePerfSpewerJitCodeProfile(code, "EnterJIT"); |
|
293 #endif |
|
294 |
|
295 return code; |
|
296 } |
|
297 |
|
298 JitCode * |
|
299 JitRuntime::generateInvalidator(JSContext *cx) |
|
300 { |
|
301 AutoIonContextAlloc aica(cx); |
|
302 MacroAssembler masm(cx); |
|
303 |
|
304 // We do the minimum amount of work in assembly and shunt the rest |
|
305 // off to InvalidationBailout. Assembly does: |
|
306 // |
|
307 // - Pop the return address from the invalidation epilogue call. |
|
308 // - Push the machine state onto the stack. |
|
309 // - Call the InvalidationBailout routine with the stack pointer. |
|
310 // - Now that the frame has been bailed out, convert the invalidated |
|
311 // frame into an exit frame. |
|
312 // - Do the normal check-return-code-and-thunk-to-the-interpreter dance. |
|
313 |
|
314 masm.addl(Imm32(sizeof(uintptr_t)), esp); |
|
315 |
|
316 // Push registers such that we can access them from [base + code]. |
|
317 masm.PushRegsInMask(AllRegs); |
|
318 |
|
319 masm.movl(esp, eax); // Argument to jit::InvalidationBailout. |
|
320 |
|
321 // Make space for InvalidationBailout's frameSize outparam. |
|
322 masm.reserveStack(sizeof(size_t)); |
|
323 masm.movl(esp, ebx); |
|
324 |
|
325 // Make space for InvalidationBailout's bailoutInfo outparam. |
|
326 masm.reserveStack(sizeof(void *)); |
|
327 masm.movl(esp, ecx); |
|
328 |
|
329 masm.setupUnalignedABICall(3, edx); |
|
330 masm.passABIArg(eax); |
|
331 masm.passABIArg(ebx); |
|
332 masm.passABIArg(ecx); |
|
333 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); |
|
334 |
|
335 masm.pop(ecx); // Get bailoutInfo outparam. |
|
336 masm.pop(ebx); // Get the frameSize outparam. |
|
337 |
|
338 // Pop the machine state and the dead frame. |
|
339 masm.lea(Operand(esp, ebx, TimesOne, sizeof(InvalidationBailoutStack)), esp); |
|
340 |
|
341 // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx. |
|
342 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); |
|
343 masm.jmp(bailoutTail); |
|
344 |
|
345 Linker linker(masm); |
|
346 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
347 IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); |
|
348 |
|
349 #ifdef JS_ION_PERF |
|
350 writePerfSpewerJitCodeProfile(code, "Invalidator"); |
|
351 #endif |
|
352 |
|
353 return code; |
|
354 } |
|
355 |
|
356 JitCode * |
|
357 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) |
|
358 { |
|
359 MacroAssembler masm(cx); |
|
360 |
|
361 // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. |
|
362 // Including |this|, there are (|nargs| + 1) arguments to copy. |
|
363 JS_ASSERT(ArgumentsRectifierReg == esi); |
|
364 |
|
365 // Load the number of |undefined|s to push into %ecx. |
|
366 masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax); |
|
367 masm.movzwl(Operand(eax, JSFunction::offsetOfNargs()), ecx); |
|
368 masm.subl(esi, ecx); |
|
369 |
|
370 // Copy the number of actual arguments. |
|
371 masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx); |
|
372 |
|
373 masm.moveValue(UndefinedValue(), ebx, edi); |
|
374 |
|
375 // NOTE: The fact that x86 ArgumentsRectifier saves the FramePointer is relied upon |
|
376 // by the baseline bailout code. If this changes, fix that code! See |
|
377 // BaselineJIT.cpp/BaselineStackBuilder::calculatePrevFramePtr, and |
|
378 // BaselineJIT.cpp/InitFromBailout. Check for the |#if defined(JS_CODEGEN_X86)| portions. |
|
379 masm.push(FramePointer); |
|
380 masm.movl(esp, FramePointer); // Save %esp. |
|
381 |
|
382 // Push undefined. |
|
383 { |
|
384 Label undefLoopTop; |
|
385 masm.bind(&undefLoopTop); |
|
386 |
|
387 masm.push(ebx); // type(undefined); |
|
388 masm.push(edi); // payload(undefined); |
|
389 masm.subl(Imm32(1), ecx); |
|
390 masm.j(Assembler::NonZero, &undefLoopTop); |
|
391 } |
|
392 |
|
393 // Get the topmost argument. We did a push of %ebp earlier, so be sure to |
|
394 // account for this in the offset |
|
395 BaseIndex b = BaseIndex(FramePointer, esi, TimesEight, |
|
396 sizeof(IonRectifierFrameLayout) + sizeof(void*)); |
|
397 masm.lea(Operand(b), ecx); |
|
398 |
|
399 // Push arguments, |nargs| + 1 times (to include |this|). |
|
400 masm.addl(Imm32(1), esi); |
|
401 { |
|
402 Label copyLoopTop; |
|
403 |
|
404 masm.bind(©LoopTop); |
|
405 masm.push(Operand(ecx, sizeof(Value)/2)); |
|
406 masm.push(Operand(ecx, 0x0)); |
|
407 masm.subl(Imm32(sizeof(Value)), ecx); |
|
408 masm.subl(Imm32(1), esi); |
|
409 masm.j(Assembler::NonZero, ©LoopTop); |
|
410 } |
|
411 |
|
412 // Construct descriptor, accounting for pushed frame pointer above |
|
413 masm.lea(Operand(FramePointer, sizeof(void*)), ebx); |
|
414 masm.subl(esp, ebx); |
|
415 masm.makeFrameDescriptor(ebx, JitFrame_Rectifier); |
|
416 |
|
417 // Construct IonJSFrameLayout. |
|
418 masm.push(edx); // number of actual arguments |
|
419 masm.push(eax); // callee token |
|
420 masm.push(ebx); // descriptor |
|
421 |
|
422 // Call the target function. |
|
423 // Note that this assumes the function is JITted. |
|
424 masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax); |
|
425 masm.loadBaselineOrIonRaw(eax, eax, mode, nullptr); |
|
426 masm.call(eax); |
|
427 uint32_t returnOffset = masm.currentOffset(); |
|
428 |
|
429 // Remove the rectifier frame. |
|
430 masm.pop(ebx); // ebx <- descriptor with FrameType. |
|
431 masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor. |
|
432 masm.pop(edi); // Discard calleeToken. |
|
433 masm.pop(edi); // Discard number of actual arguments. |
|
434 |
|
435 // Discard pushed arguments, but not the pushed frame pointer. |
|
436 BaseIndex unwind = BaseIndex(esp, ebx, TimesOne, -int32_t(sizeof(void*))); |
|
437 masm.lea(Operand(unwind), esp); |
|
438 |
|
439 masm.pop(FramePointer); |
|
440 masm.ret(); |
|
441 |
|
442 Linker linker(masm); |
|
443 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
444 |
|
445 #ifdef JS_ION_PERF |
|
446 writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier"); |
|
447 #endif |
|
448 |
|
449 CodeOffsetLabel returnLabel(returnOffset); |
|
450 returnLabel.fixup(&masm); |
|
451 if (returnAddrOut) |
|
452 *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); |
|
453 return code; |
|
454 } |
|
455 |
|
456 static void |
|
457 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass) |
|
458 { |
|
459 // Push registers such that we can access them from [base + code]. |
|
460 masm.PushRegsInMask(AllRegs); |
|
461 |
|
462 // Push the bailout table number. |
|
463 masm.push(Imm32(frameClass)); |
|
464 |
|
465 // The current stack pointer is the first argument to jit::Bailout. |
|
466 masm.movl(esp, eax); |
|
467 |
|
468 // Make space for Bailout's baioutInfo outparam. |
|
469 masm.reserveStack(sizeof(void *)); |
|
470 masm.movl(esp, ebx); |
|
471 |
|
472 // Call the bailout function. This will correct the size of the bailout. |
|
473 masm.setupUnalignedABICall(2, ecx); |
|
474 masm.passABIArg(eax); |
|
475 masm.passABIArg(ebx); |
|
476 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); |
|
477 |
|
478 masm.pop(ecx); // Get bailoutInfo outparam. |
|
479 |
|
480 // Common size of stuff we've pushed. |
|
481 const uint32_t BailoutDataSize = sizeof(void *) + // frameClass |
|
482 sizeof(double) * FloatRegisters::Total + |
|
483 sizeof(void *) * Registers::Total; |
|
484 |
|
485 // Remove both the bailout frame and the topmost Ion frame's stack. |
|
486 if (frameClass == NO_FRAME_SIZE_CLASS_ID) { |
|
487 // We want the frameSize. Stack is: |
|
488 // ... frame ... |
|
489 // snapshotOffset |
|
490 // frameSize |
|
491 // ... bailoutFrame ... |
|
492 masm.addl(Imm32(BailoutDataSize), esp); |
|
493 masm.pop(ebx); |
|
494 masm.addl(Imm32(sizeof(uint32_t)), esp); |
|
495 masm.addl(ebx, esp); |
|
496 } else { |
|
497 // Stack is: |
|
498 // ... frame ... |
|
499 // bailoutId |
|
500 // ... bailoutFrame ... |
|
501 uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize(); |
|
502 masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp); |
|
503 } |
|
504 |
|
505 // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx. |
|
506 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); |
|
507 masm.jmp(bailoutTail); |
|
508 } |
|
509 |
|
510 JitCode * |
|
511 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass) |
|
512 { |
|
513 MacroAssembler masm; |
|
514 |
|
515 Label bailout; |
|
516 for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) |
|
517 masm.call(&bailout); |
|
518 masm.bind(&bailout); |
|
519 |
|
520 GenerateBailoutThunk(cx, masm, frameClass); |
|
521 |
|
522 Linker linker(masm); |
|
523 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
524 |
|
525 #ifdef JS_ION_PERF |
|
526 writePerfSpewerJitCodeProfile(code, "BailoutHandler"); |
|
527 #endif |
|
528 |
|
529 return code; |
|
530 } |
|
531 |
|
532 JitCode * |
|
533 JitRuntime::generateBailoutHandler(JSContext *cx) |
|
534 { |
|
535 MacroAssembler masm; |
|
536 |
|
537 GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); |
|
538 |
|
539 Linker linker(masm); |
|
540 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
541 |
|
542 #ifdef JS_ION_PERF |
|
543 writePerfSpewerJitCodeProfile(code, "BailoutHandler"); |
|
544 #endif |
|
545 |
|
546 return code; |
|
547 } |
|
548 |
|
549 JitCode * |
|
550 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) |
|
551 { |
|
552 JS_ASSERT(!StackKeptAligned); |
|
553 JS_ASSERT(functionWrappers_); |
|
554 JS_ASSERT(functionWrappers_->initialized()); |
|
555 VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); |
|
556 if (p) |
|
557 return p->value(); |
|
558 |
|
559 // Generate a separated code for the wrapper. |
|
560 MacroAssembler masm; |
|
561 |
|
562 // Avoid conflicts with argument registers while discarding the result after |
|
563 // the function call. |
|
564 GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask); |
|
565 |
|
566 // Wrapper register set is a superset of Volatile register set. |
|
567 JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0); |
|
568 |
|
569 // The context is the first argument. |
|
570 Register cxreg = regs.takeAny(); |
|
571 |
|
572 // Stack is: |
|
573 // ... frame ... |
|
574 // +8 [args] |
|
575 // +4 descriptor |
|
576 // +0 returnAddress |
|
577 // |
|
578 // We're aligned to an exit frame, so link it up. |
|
579 masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode); |
|
580 |
|
581 // Save the current stack pointer as the base for copying arguments. |
|
582 Register argsBase = InvalidReg; |
|
583 if (f.explicitArgs) { |
|
584 argsBase = regs.takeAny(); |
|
585 masm.lea(Operand(esp, IonExitFrameLayout::SizeWithFooter()), argsBase); |
|
586 } |
|
587 |
|
588 // Reserve space for the outparameter. |
|
589 Register outReg = InvalidReg; |
|
590 switch (f.outParam) { |
|
591 case Type_Value: |
|
592 outReg = regs.takeAny(); |
|
593 masm.Push(UndefinedValue()); |
|
594 masm.movl(esp, outReg); |
|
595 break; |
|
596 |
|
597 case Type_Handle: |
|
598 outReg = regs.takeAny(); |
|
599 masm.PushEmptyRooted(f.outParamRootType); |
|
600 masm.movl(esp, outReg); |
|
601 break; |
|
602 |
|
603 case Type_Int32: |
|
604 case Type_Pointer: |
|
605 case Type_Bool: |
|
606 outReg = regs.takeAny(); |
|
607 masm.reserveStack(sizeof(int32_t)); |
|
608 masm.movl(esp, outReg); |
|
609 break; |
|
610 |
|
611 case Type_Double: |
|
612 outReg = regs.takeAny(); |
|
613 masm.reserveStack(sizeof(double)); |
|
614 masm.movl(esp, outReg); |
|
615 break; |
|
616 |
|
617 default: |
|
618 JS_ASSERT(f.outParam == Type_Void); |
|
619 break; |
|
620 } |
|
621 |
|
622 masm.setupUnalignedABICall(f.argc(), regs.getAny()); |
|
623 masm.passABIArg(cxreg); |
|
624 |
|
625 size_t argDisp = 0; |
|
626 |
|
627 // Copy arguments. |
|
628 for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { |
|
629 MoveOperand from; |
|
630 switch (f.argProperties(explicitArg)) { |
|
631 case VMFunction::WordByValue: |
|
632 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); |
|
633 argDisp += sizeof(void *); |
|
634 break; |
|
635 case VMFunction::DoubleByValue: |
|
636 // We don't pass doubles in float registers on x86, so no need |
|
637 // to check for argPassedInFloatReg. |
|
638 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); |
|
639 argDisp += sizeof(void *); |
|
640 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); |
|
641 argDisp += sizeof(void *); |
|
642 break; |
|
643 case VMFunction::WordByRef: |
|
644 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), |
|
645 MoveOp::GENERAL); |
|
646 argDisp += sizeof(void *); |
|
647 break; |
|
648 case VMFunction::DoubleByRef: |
|
649 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), |
|
650 MoveOp::GENERAL); |
|
651 argDisp += 2 * sizeof(void *); |
|
652 break; |
|
653 } |
|
654 } |
|
655 |
|
656 // Copy the implicit outparam, if any. |
|
657 if (outReg != InvalidReg) |
|
658 masm.passABIArg(outReg); |
|
659 |
|
660 masm.callWithABI(f.wrapped); |
|
661 |
|
662 // Test for failure. |
|
663 switch (f.failType()) { |
|
664 case Type_Object: |
|
665 masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel(f.executionMode)); |
|
666 break; |
|
667 case Type_Bool: |
|
668 masm.testb(eax, eax); |
|
669 masm.j(Assembler::Zero, masm.failureLabel(f.executionMode)); |
|
670 break; |
|
671 default: |
|
672 MOZ_ASSUME_UNREACHABLE("unknown failure kind"); |
|
673 } |
|
674 |
|
675 // Load the outparam and free any allocated stack. |
|
676 switch (f.outParam) { |
|
677 case Type_Handle: |
|
678 masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); |
|
679 break; |
|
680 |
|
681 case Type_Value: |
|
682 masm.Pop(JSReturnOperand); |
|
683 break; |
|
684 |
|
685 case Type_Int32: |
|
686 case Type_Pointer: |
|
687 masm.Pop(ReturnReg); |
|
688 break; |
|
689 |
|
690 case Type_Bool: |
|
691 masm.Pop(ReturnReg); |
|
692 masm.movzbl(ReturnReg, ReturnReg); |
|
693 break; |
|
694 |
|
695 case Type_Double: |
|
696 if (cx->runtime()->jitSupportsFloatingPoint) |
|
697 masm.Pop(ReturnFloatReg); |
|
698 else |
|
699 masm.assumeUnreachable("Unable to pop to float reg, with no FP support."); |
|
700 break; |
|
701 |
|
702 default: |
|
703 JS_ASSERT(f.outParam == Type_Void); |
|
704 break; |
|
705 } |
|
706 masm.leaveExitFrame(); |
|
707 masm.retn(Imm32(sizeof(IonExitFrameLayout) + |
|
708 f.explicitStackSlots() * sizeof(void *) + |
|
709 f.extraValuesToPop * sizeof(Value))); |
|
710 |
|
711 Linker linker(masm); |
|
712 JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
713 if (!wrapper) |
|
714 return nullptr; |
|
715 |
|
716 #ifdef JS_ION_PERF |
|
717 writePerfSpewerJitCodeProfile(wrapper, "VMWrapper"); |
|
718 #endif |
|
719 |
|
720 // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to |
|
721 // use relookupOrAdd instead of add. |
|
722 if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) |
|
723 return nullptr; |
|
724 |
|
725 return wrapper; |
|
726 } |
|
727 |
|
728 JitCode * |
|
729 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type) |
|
730 { |
|
731 MacroAssembler masm; |
|
732 |
|
733 RegisterSet save; |
|
734 if (cx->runtime()->jitSupportsFloatingPoint) { |
|
735 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), |
|
736 FloatRegisterSet(FloatRegisters::VolatileMask)); |
|
737 } else { |
|
738 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), |
|
739 FloatRegisterSet()); |
|
740 } |
|
741 masm.PushRegsInMask(save); |
|
742 |
|
743 JS_ASSERT(PreBarrierReg == edx); |
|
744 masm.movl(ImmPtr(cx->runtime()), ecx); |
|
745 |
|
746 masm.setupUnalignedABICall(2, eax); |
|
747 masm.passABIArg(ecx); |
|
748 masm.passABIArg(edx); |
|
749 |
|
750 if (type == MIRType_Value) { |
|
751 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon)); |
|
752 } else { |
|
753 JS_ASSERT(type == MIRType_Shape); |
|
754 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); |
|
755 } |
|
756 |
|
757 masm.PopRegsInMask(save); |
|
758 masm.ret(); |
|
759 |
|
760 Linker linker(masm); |
|
761 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
762 |
|
763 #ifdef JS_ION_PERF |
|
764 writePerfSpewerJitCodeProfile(code, "PreBarrier"); |
|
765 #endif |
|
766 |
|
767 return code; |
|
768 } |
|
769 |
|
770 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); |
|
771 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); |
|
772 |
|
773 JitCode * |
|
774 JitRuntime::generateDebugTrapHandler(JSContext *cx) |
|
775 { |
|
776 MacroAssembler masm; |
|
777 |
|
778 Register scratch1 = eax; |
|
779 Register scratch2 = ecx; |
|
780 Register scratch3 = edx; |
|
781 |
|
782 // Load the return address in scratch1. |
|
783 masm.loadPtr(Address(esp, 0), scratch1); |
|
784 |
|
785 // Load BaselineFrame pointer in scratch2. |
|
786 masm.mov(ebp, scratch2); |
|
787 masm.subPtr(Imm32(BaselineFrame::Size()), scratch2); |
|
788 |
|
789 // Enter a stub frame and call the HandleDebugTrap VM function. Ensure |
|
790 // the stub frame has a nullptr ICStub pointer, since this pointer is |
|
791 // marked during GC. |
|
792 masm.movePtr(ImmPtr(nullptr), BaselineStubReg); |
|
793 EmitEnterStubFrame(masm, scratch3); |
|
794 |
|
795 JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo); |
|
796 if (!code) |
|
797 return nullptr; |
|
798 |
|
799 masm.push(scratch1); |
|
800 masm.push(scratch2); |
|
801 EmitCallVM(code, masm); |
|
802 |
|
803 EmitLeaveStubFrame(masm); |
|
804 |
|
805 // If the stub returns |true|, we have to perform a forced return |
|
806 // (return from the JS frame). If the stub returns |false|, just return |
|
807 // from the trap stub so that execution continues at the current pc. |
|
808 Label forcedReturn; |
|
809 masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn); |
|
810 masm.ret(); |
|
811 |
|
812 masm.bind(&forcedReturn); |
|
813 masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), |
|
814 JSReturnOperand); |
|
815 masm.mov(ebp, esp); |
|
816 masm.pop(ebp); |
|
817 masm.ret(); |
|
818 |
|
819 Linker linker(masm); |
|
820 JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
821 |
|
822 #ifdef JS_ION_PERF |
|
823 writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler"); |
|
824 #endif |
|
825 |
|
826 return codeDbg; |
|
827 } |
|
828 |
|
829 JitCode * |
|
830 JitRuntime::generateExceptionTailStub(JSContext *cx) |
|
831 { |
|
832 MacroAssembler masm; |
|
833 |
|
834 masm.handleFailureWithHandlerTail(); |
|
835 |
|
836 Linker linker(masm); |
|
837 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
838 |
|
839 #ifdef JS_ION_PERF |
|
840 writePerfSpewerJitCodeProfile(code, "ExceptionTailStub"); |
|
841 #endif |
|
842 |
|
843 return code; |
|
844 } |
|
845 |
|
846 JitCode * |
|
847 JitRuntime::generateBailoutTailStub(JSContext *cx) |
|
848 { |
|
849 MacroAssembler masm; |
|
850 |
|
851 masm.generateBailoutTail(edx, ecx); |
|
852 |
|
853 Linker linker(masm); |
|
854 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
855 |
|
856 #ifdef JS_ION_PERF |
|
857 writePerfSpewerJitCodeProfile(code, "BailoutTailStub"); |
|
858 #endif |
|
859 |
|
860 return code; |
|
861 } |