Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "jscompartment.h"
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"
22 #include "jsscriptinlines.h"
24 #include "jit/ExecutionMode-inl.h"
26 using namespace js;
27 using namespace js::jit;
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));
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 };
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);
55 // Save old stack frame pointer, set new stack frame pointer.
56 masm.push(ebp);
57 masm.movl(esp, ebp);
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);
66 // Push the EnterJIT sps mark.
67 masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx);
69 // Keep track of the stack which has to be unwound after returning from the
70 // compiled function.
71 masm.movl(esp, esi);
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);
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);
89 // ecx = ecx & 15, holds alignment.
90 masm.andl(Imm32(15), ecx);
91 masm.subl(ecx, esp);
93 /***************************************************************
94 Loop over argv vector, push arguments onto stack in reverse order
95 ***************************************************************/
97 // ebx = argv --argv pointer is in ebp + 16
98 masm.loadPtr(Address(ebp, ARG_ARGV), ebx);
100 // eax = argv[8(argc)] --eax now points one value past the last argument
101 masm.addl(ebx, eax);
103 // while (eax > ebx) --while still looping through arguments
104 {
105 Label header, footer;
106 masm.bind(&header);
108 masm.cmpl(eax, ebx);
109 masm.j(Assembler::BelowOrEqual, &footer);
111 // eax -= 8 --move to previous argument
112 masm.subl(Imm32(8), eax);
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));
118 masm.jmp(&header);
119 masm.bind(&footer);
120 }
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);
130 // Push the callee token.
131 masm.push(Operand(ebp, ARG_CALLEETOKEN));
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);
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);
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);
154 Register scratch = regs.takeAny();
156 Label notOsr;
157 masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr);
159 Register numStackValues = regs.takeAny();
160 masm.loadPtr(Address(ebp, ARG_STACKVALUES), numStackValues);
162 Register jitcode = regs.takeAny();
163 masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode);
165 // Push return address, previous frame pointer.
166 masm.mov(returnLabel.dest(), scratch);
167 masm.push(scratch);
168 masm.push(ebp);
170 // Reserve frame.
171 Register framePtr = ebp;
172 masm.subPtr(Imm32(BaselineFrame::Size()), esp);
173 masm.mov(esp, framePtr);
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);
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
196 // Reserve space for locals and stack values.
197 masm.mov(numStackValues, scratch);
198 masm.shll(Imm32(3), scratch);
199 masm.subPtr(scratch, esp);
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();
208 masm.push(framePtr);
209 masm.push(jitcode);
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));
217 masm.pop(jitcode);
218 masm.pop(framePtr);
220 JS_ASSERT(jitcode != ReturnReg);
222 Label error;
223 masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), esp);
224 masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
225 masm.branchIfFalseBool(ReturnReg, &error);
227 masm.jump(jitcode);
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);
238 masm.bind(¬Osr);
239 masm.loadPtr(Address(ebp, ARG_SCOPECHAIN), R1.scratchReg());
240 }
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));
248 if (type == EnterJitBaseline) {
249 // Baseline OSR will return here.
250 masm.bind(returnLabel.src());
251 if (!masm.addCodeLabel(returnLabel))
252 return nullptr;
253 }
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);
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));
273 /**************************************************************
274 Return stack and registers to correct state
275 **************************************************************/
276 // Unwind the sps mark.
277 masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx);
279 // Restore non-volatile registers
280 masm.pop(edi);
281 masm.pop(esi);
282 masm.pop(ebx);
284 // Restore old stack frame pointer
285 masm.pop(ebp);
286 masm.ret();
288 Linker linker(masm);
289 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
291 #ifdef JS_ION_PERF
292 writePerfSpewerJitCodeProfile(code, "EnterJIT");
293 #endif
295 return code;
296 }
298 JitCode *
299 JitRuntime::generateInvalidator(JSContext *cx)
300 {
301 AutoIonContextAlloc aica(cx);
302 MacroAssembler masm(cx);
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.
314 masm.addl(Imm32(sizeof(uintptr_t)), esp);
316 // Push registers such that we can access them from [base + code].
317 masm.PushRegsInMask(AllRegs);
319 masm.movl(esp, eax); // Argument to jit::InvalidationBailout.
321 // Make space for InvalidationBailout's frameSize outparam.
322 masm.reserveStack(sizeof(size_t));
323 masm.movl(esp, ebx);
325 // Make space for InvalidationBailout's bailoutInfo outparam.
326 masm.reserveStack(sizeof(void *));
327 masm.movl(esp, ecx);
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));
335 masm.pop(ecx); // Get bailoutInfo outparam.
336 masm.pop(ebx); // Get the frameSize outparam.
338 // Pop the machine state and the dead frame.
339 masm.lea(Operand(esp, ebx, TimesOne, sizeof(InvalidationBailoutStack)), esp);
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);
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());
349 #ifdef JS_ION_PERF
350 writePerfSpewerJitCodeProfile(code, "Invalidator");
351 #endif
353 return code;
354 }
356 JitCode *
357 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
358 {
359 MacroAssembler masm(cx);
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);
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);
370 // Copy the number of actual arguments.
371 masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx);
373 masm.moveValue(UndefinedValue(), ebx, edi);
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.
382 // Push undefined.
383 {
384 Label undefLoopTop;
385 masm.bind(&undefLoopTop);
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 }
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);
399 // Push arguments, |nargs| + 1 times (to include |this|).
400 masm.addl(Imm32(1), esi);
401 {
402 Label copyLoopTop;
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 }
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);
417 // Construct IonJSFrameLayout.
418 masm.push(edx); // number of actual arguments
419 masm.push(eax); // callee token
420 masm.push(ebx); // descriptor
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();
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.
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);
439 masm.pop(FramePointer);
440 masm.ret();
442 Linker linker(masm);
443 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
445 #ifdef JS_ION_PERF
446 writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
447 #endif
449 CodeOffsetLabel returnLabel(returnOffset);
450 returnLabel.fixup(&masm);
451 if (returnAddrOut)
452 *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
453 return code;
454 }
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);
462 // Push the bailout table number.
463 masm.push(Imm32(frameClass));
465 // The current stack pointer is the first argument to jit::Bailout.
466 masm.movl(esp, eax);
468 // Make space for Bailout's baioutInfo outparam.
469 masm.reserveStack(sizeof(void *));
470 masm.movl(esp, ebx);
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));
478 masm.pop(ecx); // Get bailoutInfo outparam.
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;
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 }
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 }
510 JitCode *
511 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
512 {
513 MacroAssembler masm;
515 Label bailout;
516 for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
517 masm.call(&bailout);
518 masm.bind(&bailout);
520 GenerateBailoutThunk(cx, masm, frameClass);
522 Linker linker(masm);
523 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
525 #ifdef JS_ION_PERF
526 writePerfSpewerJitCodeProfile(code, "BailoutHandler");
527 #endif
529 return code;
530 }
532 JitCode *
533 JitRuntime::generateBailoutHandler(JSContext *cx)
534 {
535 MacroAssembler masm;
537 GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
539 Linker linker(masm);
540 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
542 #ifdef JS_ION_PERF
543 writePerfSpewerJitCodeProfile(code, "BailoutHandler");
544 #endif
546 return code;
547 }
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();
559 // Generate a separated code for the wrapper.
560 MacroAssembler masm;
562 // Avoid conflicts with argument registers while discarding the result after
563 // the function call.
564 GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask);
566 // Wrapper register set is a superset of Volatile register set.
567 JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
569 // The context is the first argument.
570 Register cxreg = regs.takeAny();
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);
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 }
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;
597 case Type_Handle:
598 outReg = regs.takeAny();
599 masm.PushEmptyRooted(f.outParamRootType);
600 masm.movl(esp, outReg);
601 break;
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;
611 case Type_Double:
612 outReg = regs.takeAny();
613 masm.reserveStack(sizeof(double));
614 masm.movl(esp, outReg);
615 break;
617 default:
618 JS_ASSERT(f.outParam == Type_Void);
619 break;
620 }
622 masm.setupUnalignedABICall(f.argc(), regs.getAny());
623 masm.passABIArg(cxreg);
625 size_t argDisp = 0;
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 }
656 // Copy the implicit outparam, if any.
657 if (outReg != InvalidReg)
658 masm.passABIArg(outReg);
660 masm.callWithABI(f.wrapped);
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 }
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;
681 case Type_Value:
682 masm.Pop(JSReturnOperand);
683 break;
685 case Type_Int32:
686 case Type_Pointer:
687 masm.Pop(ReturnReg);
688 break;
690 case Type_Bool:
691 masm.Pop(ReturnReg);
692 masm.movzbl(ReturnReg, ReturnReg);
693 break;
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;
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)));
711 Linker linker(masm);
712 JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
713 if (!wrapper)
714 return nullptr;
716 #ifdef JS_ION_PERF
717 writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
718 #endif
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;
725 return wrapper;
726 }
728 JitCode *
729 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
730 {
731 MacroAssembler masm;
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);
743 JS_ASSERT(PreBarrierReg == edx);
744 masm.movl(ImmPtr(cx->runtime()), ecx);
746 masm.setupUnalignedABICall(2, eax);
747 masm.passABIArg(ecx);
748 masm.passABIArg(edx);
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 }
757 masm.PopRegsInMask(save);
758 masm.ret();
760 Linker linker(masm);
761 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
763 #ifdef JS_ION_PERF
764 writePerfSpewerJitCodeProfile(code, "PreBarrier");
765 #endif
767 return code;
768 }
770 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
771 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
773 JitCode *
774 JitRuntime::generateDebugTrapHandler(JSContext *cx)
775 {
776 MacroAssembler masm;
778 Register scratch1 = eax;
779 Register scratch2 = ecx;
780 Register scratch3 = edx;
782 // Load the return address in scratch1.
783 masm.loadPtr(Address(esp, 0), scratch1);
785 // Load BaselineFrame pointer in scratch2.
786 masm.mov(ebp, scratch2);
787 masm.subPtr(Imm32(BaselineFrame::Size()), scratch2);
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);
795 JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
796 if (!code)
797 return nullptr;
799 masm.push(scratch1);
800 masm.push(scratch2);
801 EmitCallVM(code, masm);
803 EmitLeaveStubFrame(masm);
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();
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();
819 Linker linker(masm);
820 JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
822 #ifdef JS_ION_PERF
823 writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
824 #endif
826 return codeDbg;
827 }
829 JitCode *
830 JitRuntime::generateExceptionTailStub(JSContext *cx)
831 {
832 MacroAssembler masm;
834 masm.handleFailureWithHandlerTail();
836 Linker linker(masm);
837 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
839 #ifdef JS_ION_PERF
840 writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
841 #endif
843 return code;
844 }
846 JitCode *
847 JitRuntime::generateBailoutTailStub(JSContext *cx)
848 {
849 MacroAssembler masm;
851 masm.generateBailoutTail(edx, ecx);
853 Linker linker(masm);
854 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
856 #ifdef JS_ION_PERF
857 writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
858 #endif
860 return code;
861 }