ICs for scripted calls (bug 587698, r=dmandelin).
[mozilla-central.git] / js / src / methodjit / MethodJIT.cpp
blob0f93bcbbb0169e5508fbee8d64cc6be8fdd92476
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "MethodJIT.h"
40 #include "Logging.h"
41 #include "assembler/jit/ExecutableAllocator.h"
42 #include "jstracer.h"
43 #include "BaseAssembler.h"
44 #include "MonoIC.h"
45 #include "PolyIC.h"
46 #include "TrampolineCompiler.h"
47 #include "jscntxtinlines.h"
49 using namespace js;
50 using namespace js::mjit;
53 * Explanation of VMFrame activation and various helper thunks below.
55 * JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function
56 * creates a VMFrame on the machine stack and calls into JIT'd code. The JIT'd
57 * code will eventually return to the VMFrame.
59 * - Called from C++ function EnterMethodJIT.
60 * - Parameters: cx, fp, code, stackLimit, safePoint
61 * - Notes: safePoint is used in combination with SafePointTrampoline,
62 * explained further down.
64 * JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a
65 * scripted exception handler is not found, unwinds the VMFrame and returns
66 * to C++.
68 * - To start exception handling, we return from a stub call to the throwpoline.
69 * - On entry to the throwpoline, the normal conditions of the jit-code ABI
70 * are satisfied.
71 * - To do the unwinding and find out where to continue executing, we call
72 * js_InternalThrow.
73 * - js_InternalThrow may return 0, which means the place to continue, if any,
74 * is above this JaegerShot activation, so we just return, in the same way
75 * the trampoline does.
76 * - Otherwise, js_InternalThrow returns a jit-code address to continue execution
77 * at. Because the jit-code ABI conditions are satisfied, we can just jump to
78 * that point.
81 * SafePointTrampoline - Inline script calls link their return addresses through
82 * JSStackFrame::ncode. This includes the return address that unwinds back
83 * to JaegerTrampoline. However, the tracer integration code often wants to
84 * enter a method JIT'd function at an arbitrary safe point. Safe points
85 * do not have the return address linking code that the method prologue has.
86 * SafePointTrampoline is a thunk which correctly links the initial return
87 * address. It is used in JaegerShotAtSafePoint, and passed as the "script
88 * code" parameter. Using the "safePoint" parameter to JaegerTrampoline, it
89 * correctly jumps to the intended point in the method.
91 * - Used by JaegerTrampoline()
93 * InjectJaegerReturn - Implements the tail of InlineReturn. This is needed for
94 * tracer integration, where a "return" opcode might not be a safe-point,
95 * and thus the return path must be injected by hijacking the stub return
96 * address.
98 * - Used by RunTracer()
101 #ifdef JS_METHODJIT_PROFILE_STUBS
102 static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
103 static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
104 #endif
106 extern "C" void JS_FASTCALL
107 PushActiveVMFrame(VMFrame &f)
109 f.previous = JS_METHODJIT_DATA(f.cx).activeFrame;
110 JS_METHODJIT_DATA(f.cx).activeFrame = &f;
113 extern "C" void JS_FASTCALL
114 PopActiveVMFrame(VMFrame &f)
116 JS_ASSERT(JS_METHODJIT_DATA(f.cx).activeFrame);
117 JS_METHODJIT_DATA(f.cx).activeFrame = JS_METHODJIT_DATA(f.cx).activeFrame->previous;
120 extern "C" void JS_FASTCALL
121 SetVMFrameRegs(VMFrame &f)
123 f.oldRegs = f.cx->regs;
124 f.cx->setCurrentRegs(&f.regs);
127 extern "C" void JS_FASTCALL
128 UnsetVMFrameRegs(VMFrame &f)
130 *f.oldRegs = f.regs;
131 f.cx->setCurrentRegs(f.oldRegs);
134 #if defined(__APPLE__) || defined(XP_WIN)
135 # define SYMBOL_STRING(name) "_" #name
136 #else
137 # define SYMBOL_STRING(name) #name
138 #endif
140 JS_STATIC_ASSERT(offsetof(JSFrameRegs, sp) == 0);
142 #if defined(__linux__) && defined(JS_CPU_X64)
143 # define SYMBOL_STRING_RELOC(name) #name "@plt"
144 #else
145 # define SYMBOL_STRING_RELOC(name) SYMBOL_STRING(name)
146 #endif
148 #if defined(XP_MACOSX)
149 # define HIDE_SYMBOL(name) ".private_extern _" #name
150 #elif defined(__linux__)
151 # define HIDE_SYMBOL(name) ".hidden" #name
152 #else
153 # define HIDE_SYMBOL(name)
154 #endif
156 #if defined(__GNUC__)
158 /* If this assert fails, you need to realign VMFrame to 16 bytes. */
159 #ifdef JS_CPU_ARM
160 JS_STATIC_ASSERT(sizeof(VMFrame) % 8 == 0);
161 #else
162 JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0);
163 #endif
165 # if defined(JS_CPU_X64)
168 * *** DANGER ***
169 * If these assertions break, update the constants below.
170 * *** DANGER ***
172 JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58);
173 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
175 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
176 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
178 asm volatile (
179 ".text\n"
180 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
181 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
182 /* Prologue. */
183 "pushq %rbp" "\n"
184 "movq %rsp, %rbp" "\n"
185 /* Save non-volatile registers. */
186 "pushq %r12" "\n"
187 "pushq %r13" "\n"
188 "pushq %r14" "\n"
189 "pushq %r15" "\n"
190 "pushq %rbx" "\n"
192 /* Load mask registers. */
193 "movq $0xFFFF800000000000, %r13" "\n"
194 "movq $0x00007FFFFFFFFFFF, %r14" "\n"
196 /* Build the JIT frame.
197 * rdi = cx
198 * rsi = fp
199 * rcx = inlineCallCount
200 * fp must go into rbx
202 "pushq %rsi" "\n" /* entryFp */
203 "pushq %rcx" "\n" /* inlineCallCount */
204 "pushq %rdi" "\n" /* cx */
205 "pushq %rsi" "\n" /* fp */
206 "movq %rsi, %rbx" "\n"
208 /* Space for the rest of the VMFrame. */
209 "subq $0x28, %rsp" "\n"
212 * This is actually part of the VMFrame, but we need to save |r8| for
213 * SafePointTrampoline.
215 "pushq %r8" "\n"
217 /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
218 "pushq %rdx" "\n"
219 "movq %rsp, %rdi" "\n"
220 "call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
221 "movq %rsp, %rdi" "\n"
222 "call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
225 * Jump into into the JIT'd code.
227 "call *0(%rsp)" "\n"
228 "movq %rsp, %rdi" "\n"
229 "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
230 "movq %rsp, %rdi" "\n"
231 "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
233 "addq $0x58, %rsp" "\n"
234 "popq %rbx" "\n"
235 "popq %r15" "\n"
236 "popq %r14" "\n"
237 "popq %r13" "\n"
238 "popq %r12" "\n"
239 "popq %rbp" "\n"
240 "movq $1, %rax" "\n"
241 "ret" "\n"
244 asm volatile (
245 ".text\n"
246 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
247 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
248 "movq %rsp, %rdi" "\n"
249 "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
250 "testq %rax, %rax" "\n"
251 "je throwpoline_exit" "\n"
252 "jmp *%rax" "\n"
253 "throwpoline_exit:" "\n"
254 "movq %rsp, %rdi" "\n"
255 "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
256 "addq $0x58, %rsp" "\n"
257 "popq %rbx" "\n"
258 "popq %r15" "\n"
259 "popq %r14" "\n"
260 "popq %r13" "\n"
261 "popq %r12" "\n"
262 "popq %rbp" "\n"
263 "xorq %rax,%rax" "\n"
264 "ret" "\n"
267 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 0x60);
268 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
270 asm volatile (
271 ".text\n"
272 ".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
273 SYMBOL_STRING(SafePointTrampoline) ":" "\n"
274 "popq %rax" "\n"
275 "movq %rax, 0x60(%rbx)" "\n"
276 "jmp *8(%rsp)" "\n"
279 asm volatile (
280 ".text\n"
281 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
282 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
283 "movq 0x40(%rbx), %rcx" "\n" /* load Value into typeReg */
284 "movq 0x60(%rbx), %rax" "\n" /* fp->ncode */
286 /* Reimplementation of PunboxAssembler::loadValueAsComponents() */
287 "movq %r14, %rdx" "\n" /* payloadReg = payloadMaskReg */
288 "andq %rcx, %rdx" "\n"
289 "xorq %rdx, %rcx" "\n"
291 "movq 0x38(%rsp), %rbx" "\n" /* f.fp */
292 "jmp *%rax" "\n" /* return. */
295 # elif defined(JS_CPU_X86)
298 * *** DANGER ***
299 * If these assertions break, update the constants below. The throwpoline
300 * should have the offset of savedEBX plus 4, because it needs to clean
301 * up the argument.
302 * *** DANGER ***
304 JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
305 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
307 asm volatile (
308 ".text\n"
309 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
310 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
311 /* Prologue. */
312 "pushl %ebp" "\n"
313 "movl %esp, %ebp" "\n"
314 /* Save non-volatile registers. */
315 "pushl %esi" "\n"
316 "pushl %edi" "\n"
317 "pushl %ebx" "\n"
319 /* Build the JIT frame. Push fields in order,
320 * then align the stack to form esp == VMFrame. */
321 "movl 12(%ebp), %ebx" "\n" /* load fp */
322 "pushl %ebx" "\n" /* entryFp */
323 "pushl 20(%ebp)" "\n" /* stackLimit */
324 "pushl 8(%ebp)" "\n" /* cx */
325 "pushl %ebx" "\n" /* fp */
326 "subl $0x1C, %esp" "\n"
328 /* Jump into the JIT'd code. */
329 "movl %esp, %ecx" "\n"
330 "call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
331 "movl %esp, %ecx" "\n"
332 "call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
334 "call *16(%ebp)" "\n"
335 "movl %esp, %ecx" "\n"
336 "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
337 "movl %esp, %ecx" "\n"
338 "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
340 "addl $0x2C, %esp" "\n"
341 "popl %ebx" "\n"
342 "popl %edi" "\n"
343 "popl %esi" "\n"
344 "popl %ebp" "\n"
345 "movl $1, %eax" "\n"
346 "ret" "\n"
349 asm volatile (
350 ".text\n"
351 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
352 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
353 /* Align the stack to 16 bytes. */
354 "pushl %esp" "\n"
355 "pushl (%esp)" "\n"
356 "pushl (%esp)" "\n"
357 "pushl (%esp)" "\n"
358 "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
359 /* Bump the stack by 0x2c, as in the basic trampoline, but
360 * also one more word to clean up the stack for js_InternalThrow,
361 * and another to balance the alignment above. */
362 "addl $0x10, %esp" "\n"
363 "testl %eax, %eax" "\n"
364 "je throwpoline_exit" "\n"
365 "jmp *%eax" "\n"
366 "throwpoline_exit:" "\n"
367 "movl %esp, %ecx" "\n"
368 "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
369 "addl $0x2c, %esp" "\n"
370 "popl %ebx" "\n"
371 "popl %edi" "\n"
372 "popl %esi" "\n"
373 "popl %ebp" "\n"
374 "xorl %eax, %eax" "\n"
375 "ret" "\n"
378 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 0x3C);
379 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
381 asm volatile (
382 ".text\n"
383 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
384 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
385 "movl 0x28(%ebx), %edx" "\n" /* fp->rval data */
386 "movl 0x2C(%ebx), %ecx" "\n" /* fp->rval type */
387 "movl 0x3C(%ebx), %eax" "\n" /* fp->ncode */
388 "movl 0x1C(%esp), %ebx" "\n" /* f.fp */
389 "pushl %eax" "\n"
390 "ret" "\n"
394 * Take the fifth parameter from JaegerShot() and jump to it. This makes it so
395 * we can jump into arbitrary JIT code, which won't have the frame-fixup prologue.
397 asm volatile (
398 ".text\n"
399 ".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
400 SYMBOL_STRING(SafePointTrampoline) ":" "\n"
401 "popl %eax" "\n"
402 "movl %eax, 0x3C(%ebx)" "\n"
403 "jmp *24(%ebp)" "\n"
406 # elif defined(JS_CPU_ARM)
408 JS_STATIC_ASSERT(sizeof(VMFrame) == 80);
409 JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == (4*19));
410 JS_STATIC_ASSERT(offsetof(VMFrame, entryFp) == (4*10));
411 JS_STATIC_ASSERT(offsetof(VMFrame, stackLimit) == (4*9));
412 JS_STATIC_ASSERT(offsetof(VMFrame, cx) == (4*8));
413 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == (4*7));
414 JS_STATIC_ASSERT(offsetof(VMFrame, oldRegs) == (4*4));
415 JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*3));
416 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 60);
418 JS_STATIC_ASSERT(JSFrameReg == JSC::ARMRegisters::r11);
419 JS_STATIC_ASSERT(JSReturnReg_Data == JSC::ARMRegisters::r1);
420 JS_STATIC_ASSERT(JSReturnReg_Type == JSC::ARMRegisters::r2);
422 asm volatile (
423 ".text\n"
424 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
425 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
426 /* Restore frame regs. */
427 "ldr lr, [r11, #60]" "\n" /* fp->ncode */
428 "ldr r1, [r11, #40]" "\n" /* fp->rval data */
429 "ldr r2, [r11, #44]" "\n" /* fp->rval type */
430 "ldr r11, [sp, #28]" "\n" /* load f.fp */
431 "bx lr" "\n"
434 asm volatile (
435 ".text\n"
436 ".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
437 SYMBOL_STRING(SafePointTrampoline) ":"
439 * On entry to SafePointTrampoline:
440 * r11 = fp
441 * sp[80] = safePoint
443 "ldr ip, [sp, #80]" "\n"
444 /* Save the return address (in JaegerTrampoline) to fp->ncode. */
445 "str lr, [r11, #60]" "\n"
446 /* Jump to 'safePoint' via 'ip' because a load into the PC from an address on
447 * the stack looks like a return, and may upset return stack prediction. */
448 "bx ip" "\n"
451 asm volatile (
452 ".text\n"
453 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
454 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
456 * On entry to JaegerTrampoline:
457 * r0 = cx
458 * r1 = fp
459 * r2 = code
460 * r3 = stackLimit
461 * sp[0] = safePoint
463 * The VMFrame for ARM looks like this:
464 * [ lr ] \
465 * [ r11 ] |
466 * [ r10 ] |
467 * [ r9 ] | Callee-saved registers.
468 * [ r8 ] | VFP registers d8-d15 may be required here too, but
469 * [ r7 ] | unconditionally preserving them might be expensive
470 * [ r6 ] | considering that we might not use them anyway.
471 * [ r5 ] |
472 * [ r4 ] /
473 * [ entryFp ]
474 * [ stkLimit ]
475 * [ cx ]
476 * [ regs.fp ]
477 * [ regs.pc ]
478 * [ regs.sp ]
479 * [ oldRegs ]
480 * [ previous ]
481 * [ args.ptr3 ]
482 * [ args.ptr2 ]
483 * [ args.ptr ]
486 /* Push callee-saved registers. */
487 " push {r4-r11,lr}" "\n"
488 /* Push interesting VMFrame content. */
489 " push {r1}" "\n" /* entryFp */
490 " push {r3}" "\n" /* stackLimit */
491 " push {r0}" "\n" /* cx */
492 " push {r1}" "\n" /* regs.fp */
493 /* Remaining fields are set elsewhere, but we need to leave space for them. */
494 " sub sp, sp, #(4*7)" "\n"
496 /* Preserve 'code' (r2) in an arbitrary callee-saved register. */
497 " mov r4, r2" "\n"
498 /* Preserve 'fp' (r1) in r11 (JSFrameReg) for SafePointTrampoline. */
499 " mov r11, r1" "\n"
501 " mov r0, sp" "\n"
502 " bl " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
503 " mov r0, sp" "\n"
504 " bl " SYMBOL_STRING_RELOC(PushActiveVMFrame)"\n"
506 /* Call the compiled JavaScript function. */
507 " blx r4" "\n"
509 /* Tidy up. */
510 " mov r0, sp" "\n"
511 " bl " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
512 " mov r0, sp" "\n"
513 " bl " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
515 /* Skip past the parameters we pushed (such as cx and the like). */
516 " add sp, sp, #(4*7 + 4*4)" "\n"
518 /* Set a 'true' return value to indicate successful completion. */
519 " mov r0, #1" "\n"
520 " pop {r4-r11,pc}" "\n"
523 asm volatile (
524 ".text\n"
525 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
526 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
527 /* Find the VMFrame pointer for js_InternalThrow. */
528 " mov r0, sp" "\n"
530 /* Call the utility function that sets up the internal throw routine. */
531 " bl " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
533 /* If js_InternalThrow found a scripted handler, jump to it. Otherwise, tidy
534 * up and return. */
535 " cmp r0, #0" "\n"
536 " it ne" "\n"
537 " bxne r0" "\n"
539 /* Tidy up, then return '0' to represent an unhandled exception. */
540 " mov r0, sp" "\n"
541 " bl " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
542 " add sp, sp, #(4*7 + 4*4)" "\n"
543 " mov r0, #0" "\n"
544 " pop {r4-r11,pc}" "\n"
547 asm volatile (
548 ".text\n"
549 ".globl " SYMBOL_STRING(JaegerStubVeneer) "\n"
550 SYMBOL_STRING(JaegerStubVeneer) ":" "\n"
551 /* We enter this function as a veneer between a compiled method and one of the js_ stubs. We
552 * need to store the LR somewhere (so it can be modified in case on an exception) and then
553 * branch to the js_ stub as if nothing had happened.
554 * The arguments are identical to those for js_* except that the target function should be in
555 * 'ip'. */
556 " push {ip,lr}" "\n"
557 " blx ip" "\n"
558 " pop {ip,pc}" "\n"
561 # else
562 # error "Unsupported CPU!"
563 # endif
564 #elif defined(_MSC_VER)
566 #if defined(JS_CPU_X86)
569 * *** DANGER ***
570 * If these assertions break, update the constants below. The throwpoline
571 * should have the offset of savedEBX plus 4, because it needs to clean
572 * up the argument.
573 * *** DANGER ***
575 JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
576 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
578 extern "C" {
580 __declspec(naked) void InjectJaegerReturn()
582 __asm {
583 mov edx, [ebx + 0x28];
584 mov ecx, [ebx + 0x2C];
585 mov eax, [ebx + 0x3C];
586 mov ebx, [esp + 0x1C];
587 push eax;
588 ret;
592 __declspec(naked) void SafePointTrampoline()
594 __asm {
595 pop eax;
596 mov [ebx + 0x3C], eax;
597 jmp [ebp + 24];
601 __declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
602 Value *stackLimit, void *safePoint)
604 __asm {
605 /* Prologue. */
606 push ebp;
607 mov ebp, esp;
608 /* Save non-volatile registers. */
609 push esi;
610 push edi;
611 push ebx;
613 /* Build the JIT frame. Push fields in order,
614 * then align the stack to form esp == VMFrame. */
615 mov ebx, [ebp + 12];
616 push ebx;
617 push [ebp + 20];
618 push [ebp + 8];
619 push ebx;
620 sub esp, 0x1C;
622 /* Jump into into the JIT'd code. */
623 mov ecx, esp;
624 call SetVMFrameRegs;
625 mov ecx, esp;
626 call PushActiveVMFrame;
628 call [ebp + 16];
629 mov ecx, esp;
630 call PopActiveVMFrame;
631 mov ecx, esp;
632 call UnsetVMFrameRegs;
634 add esp, 0x2C;
636 pop ebx;
637 pop edi;
638 pop esi;
639 pop ebp;
640 mov eax, 1;
641 ret;
645 extern "C" void *js_InternalThrow(js::VMFrame &f);
647 __declspec(naked) void *JaegerThrowpoline(js::VMFrame *vmFrame) {
648 __asm {
649 /* Align the stack to 16 bytes. */
650 push esp;
651 push [esp];
652 push [esp];
653 push [esp];
654 call js_InternalThrow;
655 /* Bump the stack by 0x2c, as in the basic trampoline, but
656 * also one more word to clean up the stack for js_InternalThrow,
657 * and another to balance the alignment above. */
658 add esp, 0x10;
659 test eax, eax;
660 je throwpoline_exit;
661 jmp eax;
662 throwpoline_exit:
663 mov ecx, esp;
664 call PopActiveVMFrame;
665 add esp, 0x2c;
666 pop ebx;
667 pop edi;
668 pop esi;
669 pop ebp;
670 xor eax, eax
671 ret;
676 #elif defined(JS_CPU_X64)
679 * *** DANGER ***
680 * If these assertions break, update the constants below.
681 * *** DANGER ***
683 JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58);
684 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
685 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 0x60);
686 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
687 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
689 // Windows x64 uses assembler version since compiler doesn't support
690 // inline assembler
691 #else
692 # error "Unsupported CPU!"
693 #endif
695 #endif /* _MSC_VER */
697 bool
698 ThreadData::Initialize()
700 execPool = new JSC::ExecutableAllocator();
701 if (!execPool)
702 return false;
704 TrampolineCompiler tc(execPool, &trampolines);
705 if (!tc.compile()) {
706 delete execPool;
707 return false;
710 #ifdef JS_METHODJIT_PROFILE_STUBS
711 for (size_t i = 0; i < STUB_CALLS_FOR_OP_COUNT; ++i)
712 StubCallsForOp[i] = 0;
713 #endif
715 activeFrame = NULL;
717 return true;
720 void
721 ThreadData::Finish()
723 TrampolineCompiler::release(&trampolines);
724 delete execPool;
725 #ifdef JS_METHODJIT_PROFILE_STUBS
726 FILE *fp = fopen("/tmp/stub-profiling", "wt");
727 # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \
728 fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]);
729 # include "jsopcode.tbl"
730 # undef OPDEF
731 fclose(fp);
732 #endif
735 extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
736 Value *stackLimit, void *safePoint);
737 extern "C" void SafePointTrampoline();
739 static inline JSBool
740 EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint)
742 JS_ASSERT(cx->regs);
743 JS_CHECK_RECURSION(cx, return JS_FALSE;);
745 #ifdef JS_METHODJIT_SPEW
746 Profiler prof;
747 JSScript *script = fp->getScript();
749 JaegerSpew(JSpew_Prof, "%s jaeger script: %s, line %d\n",
750 safePoint ? "dropping" : "entering",
751 script->filename, script->lineno);
752 prof.start();
753 #endif
755 #ifdef DEBUG
756 JSStackFrame *checkFp = fp;
757 #endif
759 Value *fpAsVp = reinterpret_cast<Value*>(fp);
760 StackSpace &stack = cx->stack();
761 Value *stackLimit = stack.makeStackLimit(fpAsVp);
764 * We ensure that there is always enough space to speculatively create a
765 * stack frame. By passing nslots = 0, we ensure only sizeof(JSStackFrame).
767 if (fpAsVp + VALUES_PER_STACK_FRAME >= stackLimit &&
768 !stack.ensureSpace(cx, fpAsVp, cx->regs->sp, stackLimit, 0)) {
769 js_ReportOutOfScriptQuota(cx);
770 return false;
773 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
774 JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit, safePoint);
776 JS_ASSERT(checkFp == cx->fp());
778 #ifdef JS_METHODJIT_SPEW
779 prof.stop();
780 JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
781 #endif
783 return ok;
786 JSBool
787 mjit::JaegerShot(JSContext *cx)
789 JSScript *script = cx->fp()->getScript();
791 JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
793 #ifdef JS_TRACER
794 if (TRACE_RECORDER(cx))
795 AbortRecording(cx, "attempt to enter method JIT while recording");
796 #endif
798 JS_ASSERT(cx->regs->pc == script->code);
800 return EnterMethodJIT(cx, cx->fp(), script->jit->invoke, NULL);
803 JSBool
804 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
806 #ifdef JS_TRACER
807 JS_ASSERT(!TRACE_RECORDER(cx));
808 #endif
810 void *code = JS_FUNC_TO_DATA_PTR(void *, SafePointTrampoline);
812 return EnterMethodJIT(cx, cx->fp(), code, safePoint);
815 template <typename T>
816 static inline void Destroy(T &t)
818 t.~T();
821 void
822 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
824 if (script->jit) {
825 #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64)
826 memset(script->jit->invoke, 0xcc, script->jit->inlineLength +
827 script->jit->outOfLineLength);
828 #endif
829 script->jit->execPool->release();
830 script->jit->execPool = NULL;
832 // Releasing the execPool takes care of releasing the code.
833 script->ncode = NULL;
835 #if defined JS_POLYIC
836 for (uint32 i = 0; i < script->jit->nPICs; i++) {
837 script->pics[i].releasePools();
838 Destroy(script->pics[i].execPools);
840 #endif
842 #if defined JS_MONOIC
843 for (uint32 i = 0; i < script->jit->nCallICs; i++)
844 script->callICs[i].releasePools();
845 #endif
847 cx->free(script->jit);
849 // The recompiler may call ReleaseScriptCode, in which case it
850 // will get called again when the script is destroyed, so we
851 // must protect against calling ReleaseScriptCode twice.
852 script->jit = NULL;
856 void
857 mjit::SweepCallICs(JSContext *cx)
859 #ifdef JS_MONOIC
860 JSRuntime *rt = cx->runtime;
861 for (size_t i = 0; i < rt->compartments.length(); i++) {
862 JSCompartment *compartment = rt->compartments[i];
863 for (JSScript *script = (JSScript *)compartment->scripts.next;
864 &script->links != &compartment->scripts;
865 script = (JSScript *)script->links.next) {
866 if (script->jit)
867 ic::SweepCallICs(cx, script);
870 #endif
873 #ifdef JS_METHODJIT_PROFILE_STUBS
874 void JS_FASTCALL
875 mjit::ProfileStubCall(VMFrame &f)
877 JSOp op = JSOp(*f.regs.pc);
878 StubCallsForOp[op]++;
880 #endif
882 bool
883 VMFrame::slowEnsureSpace(uint32 nslots)
885 return cx->stack().ensureSpace(cx, reinterpret_cast<Value*>(entryFp), regs.sp,
886 stackLimit, nslots + VALUES_PER_STACK_FRAME);