Fix crash in AtSafePoint (bug 589398, r=dmandelin).
[mozilla-central.git] / js / src / methodjit / MethodJIT.cpp
blobe82193df99e6b5b1a18878688431d8cbb6e673ad
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"
48 #include "jscompartment.h"
49 #include "jsscope.h"
51 #include "jsgcinlines.h"
53 using namespace js;
54 using namespace js::mjit;
57 void
58 JSStackFrame::methodjitStaticAsserts()
60 /* Static assert for x86 trampolines in MethodJIT.cpp. */
61 #if defined(JS_CPU_X86)
62 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 0x18);
63 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) + 4 == 0x1C);
64 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x14);
65 /* ARM uses decimal literals. */
66 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 24);
67 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) + 4 == 28);
68 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 20);
69 #elif defined(JS_CPU_X64)
70 JS_STATIC_ASSERT(offsetof(JSStackFrame, rval_) == 0x30);
71 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode_) == 0x28);
72 #endif
76 * Explanation of VMFrame activation and various helper thunks below.
78 * JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function
79 * creates a VMFrame on the machine stack and jumps into JIT'd code. The JIT'd
80 * code will eventually jump back to the VMFrame.
82 * - Called from C++ function EnterMethodJIT.
83 * - Parameters: cx, fp, code, stackLimit
85 * JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a
86 * scripted exception handler is not found, unwinds the VMFrame and returns
87 * to C++.
89 * - To start exception handling, we return from a stub call to the throwpoline.
90 * - On entry to the throwpoline, the normal conditions of the jit-code ABI
91 * are satisfied.
92 * - To do the unwinding and find out where to continue executing, we call
93 * js_InternalThrow.
94 * - js_InternalThrow may return 0, which means the place to continue, if any,
95 * is above this JaegerShot activation, so we just return, in the same way
96 * the trampoline does.
97 * - Otherwise, js_InternalThrow returns a jit-code address to continue execution
98 * at. Because the jit-code ABI conditions are satisfied, we can just jump to
99 * that point.
101 * InjectJaegerReturn - Implements the tail of InlineReturn. This is needed for
102 * tracer integration, where a "return" opcode might not be a safe-point,
103 * and thus the return path must be injected by hijacking the stub return
104 * address.
106 * - Used by RunTracer()
109 #ifdef JS_METHODJIT_PROFILE_STUBS
110 static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
111 static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
112 #endif
114 extern "C" void JaegerTrampolineReturn();
116 extern "C" void JS_FASTCALL
117 PushActiveVMFrame(VMFrame &f)
119 f.previous = JS_METHODJIT_DATA(f.cx).activeFrame;
120 JS_METHODJIT_DATA(f.cx).activeFrame = &f;
122 f.regs.fp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
125 extern "C" void JS_FASTCALL
126 PopActiveVMFrame(VMFrame &f)
128 JS_ASSERT(JS_METHODJIT_DATA(f.cx).activeFrame);
129 JS_METHODJIT_DATA(f.cx).activeFrame = JS_METHODJIT_DATA(f.cx).activeFrame->previous;
132 extern "C" void JS_FASTCALL
133 SetVMFrameRegs(VMFrame &f)
135 f.cx->setCurrentRegs(&f.regs);
138 #if defined(__APPLE__) || defined(XP_WIN)
139 # define SYMBOL_STRING(name) "_" #name
140 #else
141 # define SYMBOL_STRING(name) #name
142 #endif
144 JS_STATIC_ASSERT(offsetof(JSFrameRegs, sp) == 0);
146 #if defined(__linux__) && defined(JS_CPU_X64)
147 # define SYMBOL_STRING_RELOC(name) #name "@plt"
148 #else
149 # define SYMBOL_STRING_RELOC(name) SYMBOL_STRING(name)
150 #endif
152 #if defined(XP_WIN) && defined(JS_CPU_X86)
153 # define SYMBOL_STRING_VMFRAME(name) "@" #name "@4"
154 #else
155 # define SYMBOL_STRING_VMFRAME(name) SYMBOL_STRING_RELOC(name)
156 #endif
158 #if defined(XP_MACOSX)
159 # define HIDE_SYMBOL(name) ".private_extern _" #name
160 #elif defined(__linux__)
161 # define HIDE_SYMBOL(name) ".hidden" #name
162 #else
163 # define HIDE_SYMBOL(name)
164 #endif
166 #if defined(__GNUC__)
168 /* If this assert fails, you need to realign VMFrame to 16 bytes. */
169 #ifdef JS_CPU_ARM
170 JS_STATIC_ASSERT(sizeof(VMFrame) % 8 == 0);
171 #else
172 JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0);
173 #endif
175 # if defined(JS_CPU_X64)
178 * *** DANGER ***
179 * If these assertions break, update the constants below.
180 * *** DANGER ***
182 JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58);
183 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
185 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
186 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
188 asm volatile (
189 ".text\n"
190 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
191 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
192 /* Prologue. */
193 "pushq %rbp" "\n"
194 "movq %rsp, %rbp" "\n"
195 /* Save non-volatile registers. */
196 "pushq %r12" "\n"
197 "pushq %r13" "\n"
198 "pushq %r14" "\n"
199 "pushq %r15" "\n"
200 "pushq %rbx" "\n"
202 /* Load mask registers. */
203 "movq $0xFFFF800000000000, %r13" "\n"
204 "movq $0x00007FFFFFFFFFFF, %r14" "\n"
206 /* Build the JIT frame.
207 * rdi = cx
208 * rsi = fp
209 * rcx = inlineCallCount
210 * fp must go into rbx
212 "pushq %rsi" "\n" /* entryFp */
213 "pushq %rcx" "\n" /* inlineCallCount */
214 "pushq %rdi" "\n" /* cx */
215 "pushq %rsi" "\n" /* fp */
216 "movq %rsi, %rbx" "\n"
218 /* Space for the rest of the VMFrame. */
219 "subq $0x28, %rsp" "\n"
221 /* This is actually part of the VMFrame. */
222 "pushq %r8" "\n"
224 /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
225 "pushq %rdx" "\n"
226 "movq %rsp, %rdi" "\n"
227 "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n"
228 "movq %rsp, %rdi" "\n"
229 "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
231 /* Jump into the JIT'd code. */
232 "jmp *0(%rsp)" "\n"
235 asm volatile (
236 ".text\n"
237 ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
238 SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
239 "or %rdx, %rcx" "\n"
240 "movq %rcx, 0x30(%rbx)" "\n"
241 "movq %rsp, %rdi" "\n"
242 "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
244 "addq $0x58, %rsp" "\n"
245 "popq %rbx" "\n"
246 "popq %r15" "\n"
247 "popq %r14" "\n"
248 "popq %r13" "\n"
249 "popq %r12" "\n"
250 "popq %rbp" "\n"
251 "movq $1, %rax" "\n"
252 "ret" "\n"
255 asm volatile (
256 ".text\n"
257 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
258 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
259 "movq %rsp, %rdi" "\n"
260 "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
261 "testq %rax, %rax" "\n"
262 "je throwpoline_exit" "\n"
263 "jmp *%rax" "\n"
264 "throwpoline_exit:" "\n"
265 "movq %rsp, %rdi" "\n"
266 "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
267 "addq $0x58, %rsp" "\n"
268 "popq %rbx" "\n"
269 "popq %r15" "\n"
270 "popq %r14" "\n"
271 "popq %r13" "\n"
272 "popq %r12" "\n"
273 "popq %rbp" "\n"
274 "xorq %rax,%rax" "\n"
275 "ret" "\n"
278 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
280 asm volatile (
281 ".text\n"
282 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
283 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
284 "movq 0x30(%rbx), %rcx" "\n" /* load fp->rval_ into typeReg */
285 "movq 0x28(%rbx), %rax" "\n" /* fp->ncode_ */
287 /* Reimplementation of PunboxAssembler::loadValueAsComponents() */
288 "movq %r14, %rdx" "\n" /* payloadReg = payloadMaskReg */
289 "andq %rcx, %rdx" "\n"
290 "xorq %rdx, %rcx" "\n"
292 "movq 0x38(%rsp), %rbx" "\n" /* f.fp */
293 "jmp *%rax" "\n" /* return. */
296 # elif defined(JS_CPU_X86)
299 * *** DANGER ***
300 * If these assertions break, update the constants below. The throwpoline
301 * should have the offset of savedEBX plus 4, because it needs to clean
302 * up the argument.
303 * *** DANGER ***
305 JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
306 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
308 asm volatile (
309 ".text\n"
310 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
311 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
312 /* Prologue. */
313 "pushl %ebp" "\n"
314 "movl %esp, %ebp" "\n"
315 /* Save non-volatile registers. */
316 "pushl %esi" "\n"
317 "pushl %edi" "\n"
318 "pushl %ebx" "\n"
320 /* Build the JIT frame. Push fields in order,
321 * then align the stack to form esp == VMFrame. */
322 "movl 12(%ebp), %ebx" "\n" /* load fp */
323 "pushl %ebx" "\n" /* entryFp */
324 "pushl 20(%ebp)" "\n" /* stackLimit */
325 "pushl 8(%ebp)" "\n" /* cx */
326 "pushl %ebx" "\n" /* fp */
327 "subl $0x1C, %esp" "\n"
329 /* Jump into the JIT'd code. */
330 "movl %esp, %ecx" "\n"
331 "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n"
332 "movl %esp, %ecx" "\n"
333 "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
335 "jmp *16(%ebp)" "\n"
338 asm volatile (
339 ".text\n"
340 ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
341 SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
342 "movl %edx, 0x18(%ebx)" "\n"
343 "movl %ecx, 0x1C(%ebx)" "\n"
344 "movl %esp, %ecx" "\n"
345 "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
347 "addl $0x2C, %esp" "\n"
348 "popl %ebx" "\n"
349 "popl %edi" "\n"
350 "popl %esi" "\n"
351 "popl %ebp" "\n"
352 "movl $1, %eax" "\n"
353 "ret" "\n"
356 asm volatile (
357 ".text\n"
358 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
359 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
360 /* Align the stack to 16 bytes. */
361 "pushl %esp" "\n"
362 "pushl (%esp)" "\n"
363 "pushl (%esp)" "\n"
364 "pushl (%esp)" "\n"
365 "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
366 /* Bump the stack by 0x2c, as in the basic trampoline, but
367 * also one more word to clean up the stack for js_InternalThrow,
368 * and another to balance the alignment above. */
369 "addl $0x10, %esp" "\n"
370 "testl %eax, %eax" "\n"
371 "je throwpoline_exit" "\n"
372 "jmp *%eax" "\n"
373 "throwpoline_exit:" "\n"
374 "movl %esp, %ecx" "\n"
375 "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
376 "addl $0x2c, %esp" "\n"
377 "popl %ebx" "\n"
378 "popl %edi" "\n"
379 "popl %esi" "\n"
380 "popl %ebp" "\n"
381 "xorl %eax, %eax" "\n"
382 "ret" "\n"
385 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
387 asm volatile (
388 ".text\n"
389 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
390 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
391 "movl 0x18(%ebx), %edx" "\n" /* fp->rval_ data */
392 "movl 0x1C(%ebx), %ecx" "\n" /* fp->rval_ type */
393 "movl 0x14(%ebx), %eax" "\n" /* fp->ncode_ */
394 "movl 0x1C(%esp), %ebx" "\n" /* f.fp */
395 "jmp *%eax" "\n"
398 # elif defined(JS_CPU_ARM)
400 JS_STATIC_ASSERT(sizeof(VMFrame) == 80);
401 JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == (4*19));
402 JS_STATIC_ASSERT(offsetof(VMFrame, entryFp) == (4*10));
403 JS_STATIC_ASSERT(offsetof(VMFrame, stackLimit) == (4*9));
404 JS_STATIC_ASSERT(offsetof(VMFrame, cx) == (4*8));
405 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == (4*7));
406 JS_STATIC_ASSERT(offsetof(VMFrame, unused) == (4*4));
407 JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*3));
409 JS_STATIC_ASSERT(JSFrameReg == JSC::ARMRegisters::r11);
410 JS_STATIC_ASSERT(JSReturnReg_Data == JSC::ARMRegisters::r1);
411 JS_STATIC_ASSERT(JSReturnReg_Type == JSC::ARMRegisters::r2);
413 #ifdef MOZ_THUMB2
414 #define FUNCTION_HEADER_EXTRA \
415 ".align 2\n" \
416 ".thumb\n" \
417 ".thumb_func\n"
418 #else
419 #define FUNCTION_HEADER_EXTRA
420 #endif
422 asm volatile (
423 ".text\n"
424 FUNCTION_HEADER_EXTRA
425 ".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
426 SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
427 /* Restore frame regs. */
428 "ldr lr, [r11, #20]" "\n" /* fp->ncode */
429 "ldr r1, [r11, #24]" "\n" /* fp->rval data */
430 "ldr r2, [r11, #28]" "\n" /* fp->rval type */
431 "ldr r11, [sp, #28]" "\n" /* load f.fp */
432 "bx lr" "\n"
435 asm volatile (
436 ".text\n"
437 FUNCTION_HEADER_EXTRA
438 ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
439 SYMBOL_STRING(JaegerTrampoline) ":" "\n"
441 * On entry to JaegerTrampoline:
442 * r0 = cx
443 * r1 = fp
444 * r2 = code
445 * r3 = stackLimit
447 * The VMFrame for ARM looks like this:
448 * [ lr ] \
449 * [ r11 ] |
450 * [ r10 ] |
451 * [ r9 ] | Callee-saved registers.
452 * [ r8 ] | VFP registers d8-d15 may be required here too, but
453 * [ r7 ] | unconditionally preserving them might be expensive
454 * [ r6 ] | considering that we might not use them anyway.
455 * [ r5 ] |
456 * [ r4 ] /
457 * [ entryFp ]
458 * [ stkLimit ]
459 * [ cx ]
460 * [ regs.fp ]
461 * [ regs.pc ]
462 * [ regs.sp ]
463 * [ unused ]
464 * [ previous ]
465 * [ args.ptr3 ]
466 * [ args.ptr2 ]
467 * [ args.ptr ]
470 /* Push callee-saved registers. */
471 " push {r4-r11,lr}" "\n"
472 /* Push interesting VMFrame content. */
473 " push {r1}" "\n" /* entryFp */
474 " push {r3}" "\n" /* stackLimit */
475 " push {r0}" "\n" /* cx */
476 " push {r1}" "\n" /* regs.fp */
477 /* Remaining fields are set elsewhere, but we need to leave space for them. */
478 " sub sp, sp, #(4*7)" "\n"
480 /* Preserve 'code' (r2) in an arbitrary callee-saved register. */
481 " mov r4, r2" "\n"
482 /* Preserve 'fp' (r1) in r11 (JSFrameReg). */
483 " mov r11, r1" "\n"
485 " mov r0, sp" "\n"
486 " blx " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n"
487 " mov r0, sp" "\n"
488 " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n"
490 /* Call the compiled JavaScript function. */
491 " bx r4" "\n"
494 asm volatile (
495 ".text\n"
496 FUNCTION_HEADER_EXTRA
497 ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
498 SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
499 " str r1, [r11, #24]" "\n" /* fp->rval data */
500 " str r2, [r11, #28]" "\n" /* fp->rval type */
502 /* Tidy up. */
503 " mov r0, sp" "\n"
504 " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
506 /* Skip past the parameters we pushed (such as cx and the like). */
507 " add sp, sp, #(4*7 + 4*4)" "\n"
509 /* Set a 'true' return value to indicate successful completion. */
510 " mov r0, #1" "\n"
511 " pop {r4-r11,pc}" "\n"
514 asm volatile (
515 ".text\n"
516 FUNCTION_HEADER_EXTRA
517 ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
518 SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
519 /* Find the VMFrame pointer for js_InternalThrow. */
520 " mov r0, sp" "\n"
522 /* Call the utility function that sets up the internal throw routine. */
523 " blx " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
525 /* If js_InternalThrow found a scripted handler, jump to it. Otherwise, tidy
526 * up and return. */
527 " cmp r0, #0" "\n"
528 " it ne" "\n"
529 " bxne r0" "\n"
531 /* Tidy up, then return '0' to represent an unhandled exception. */
532 " mov r0, sp" "\n"
533 " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
534 " add sp, sp, #(4*7 + 4*4)" "\n"
535 " mov r0, #0" "\n"
536 " pop {r4-r11,pc}" "\n"
539 asm volatile (
540 ".text\n"
541 FUNCTION_HEADER_EXTRA
542 ".globl " SYMBOL_STRING(JaegerStubVeneer) "\n"
543 SYMBOL_STRING(JaegerStubVeneer) ":" "\n"
544 /* We enter this function as a veneer between a compiled method and one of the js_ stubs. We
545 * need to store the LR somewhere (so it can be modified in case on an exception) and then
546 * branch to the js_ stub as if nothing had happened.
547 * The arguments are identical to those for js_* except that the target function should be in
548 * 'ip'. */
549 " push {ip,lr}" "\n"
550 " blx ip" "\n"
551 " pop {ip,pc}" "\n"
554 # else
555 # error "Unsupported CPU!"
556 # endif
557 #elif defined(_MSC_VER)
559 #if defined(JS_CPU_X86)
562 * *** DANGER ***
563 * If these assertions break, update the constants below. The throwpoline
564 * should have the offset of savedEBX plus 4, because it needs to clean
565 * up the argument.
566 * *** DANGER ***
568 JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
569 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x1C);
571 extern "C" {
573 __declspec(naked) void InjectJaegerReturn()
575 __asm {
576 mov edx, [ebx + 0x18];
577 mov ecx, [ebx + 0x1C];
578 mov eax, [ebx + 0x14];
579 mov ebx, [esp + 0x1C];
580 jmp eax;
584 __declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
585 Value *stackLimit)
587 __asm {
588 /* Prologue. */
589 push ebp;
590 mov ebp, esp;
591 /* Save non-volatile registers. */
592 push esi;
593 push edi;
594 push ebx;
596 /* Build the JIT frame. Push fields in order,
597 * then align the stack to form esp == VMFrame. */
598 mov ebx, [ebp + 12];
599 push ebx;
600 push [ebp + 20];
601 push [ebp + 8];
602 push ebx;
603 sub esp, 0x1C;
605 /* Jump into into the JIT'd code. */
606 mov ecx, esp;
607 call SetVMFrameRegs;
608 mov ecx, esp;
609 call PushActiveVMFrame;
611 jmp dword ptr [ebp + 16];
615 __declspec(naked) void JaegerTrampolineReturn()
617 __asm {
618 mov [ebx + 0x18], edx;
619 mov [ebx + 0x1C], ecx;
620 mov ecx, esp;
621 call PopActiveVMFrame;
623 add esp, 0x2C;
625 pop ebx;
626 pop edi;
627 pop esi;
628 pop ebp;
629 mov eax, 1;
630 ret;
634 extern "C" void *js_InternalThrow(js::VMFrame &f);
636 __declspec(naked) void *JaegerThrowpoline(js::VMFrame *vmFrame) {
637 __asm {
638 /* Align the stack to 16 bytes. */
639 push esp;
640 push [esp];
641 push [esp];
642 push [esp];
643 call js_InternalThrow;
644 /* Bump the stack by 0x2c, as in the basic trampoline, but
645 * also one more word to clean up the stack for js_InternalThrow,
646 * and another to balance the alignment above. */
647 add esp, 0x10;
648 test eax, eax;
649 je throwpoline_exit;
650 jmp eax;
651 throwpoline_exit:
652 mov ecx, esp;
653 call PopActiveVMFrame;
654 add esp, 0x2c;
655 pop ebx;
656 pop edi;
657 pop esi;
658 pop ebp;
659 xor eax, eax
660 ret;
665 #elif defined(JS_CPU_X64)
668 * *** DANGER ***
669 * If these assertions break, update the constants below.
670 * *** DANGER ***
672 JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x58);
673 JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
674 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
675 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
677 // Windows x64 uses assembler version since compiler doesn't support
678 // inline assembler
679 #else
680 # error "Unsupported CPU!"
681 #endif
683 #endif /* _MSC_VER */
685 bool
686 ThreadData::Initialize()
688 execAlloc = new JSC::ExecutableAllocator();
689 if (!execAlloc)
690 return false;
692 TrampolineCompiler tc(execAlloc, &trampolines);
693 if (!tc.compile()) {
694 delete execAlloc;
695 return false;
698 #ifdef JS_METHODJIT_PROFILE_STUBS
699 for (size_t i = 0; i < STUB_CALLS_FOR_OP_COUNT; ++i)
700 StubCallsForOp[i] = 0;
701 #endif
703 activeFrame = NULL;
705 return true;
708 void
709 ThreadData::Finish()
711 TrampolineCompiler::release(&trampolines);
712 delete execAlloc;
713 #ifdef JS_METHODJIT_PROFILE_STUBS
714 FILE *fp = fopen("/tmp/stub-profiling", "wt");
715 # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \
716 fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]);
717 # include "jsopcode.tbl"
718 # undef OPDEF
719 fclose(fp);
720 #endif
723 extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
724 Value *stackLimit);
726 static inline JSBool
727 EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
729 JS_ASSERT(cx->regs);
730 JS_CHECK_RECURSION(cx, return JS_FALSE;);
732 #ifdef JS_METHODJIT_SPEW
733 Profiler prof;
734 JSScript *script = fp->script();
736 JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n",
737 script->filename, script->lineno);
738 prof.start();
739 #endif
741 Value *stackLimit = cx->stack().getStackLimit(cx);
742 if (!stackLimit)
743 return false;
745 JSFrameRegs *oldRegs = cx->regs;
747 JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
748 JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit);
750 cx->setCurrentRegs(oldRegs);
751 JS_ASSERT(fp == cx->fp());
753 /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */
754 fp->markReturnValue();
756 #ifdef JS_METHODJIT_SPEW
757 prof.stop();
758 JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
759 #endif
761 return ok;
764 JSBool
765 mjit::JaegerShot(JSContext *cx)
767 JSStackFrame *fp = cx->fp();
768 JSScript *script = fp->script();
769 JITScript *jit = script->getJIT(fp->isConstructing());
771 #ifdef JS_TRACER
772 if (TRACE_RECORDER(cx))
773 AbortRecording(cx, "attempt to enter method JIT while recording");
774 #endif
776 JS_ASSERT(cx->regs->pc == script->code);
778 return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
781 JSBool
782 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
784 #ifdef JS_TRACER
785 JS_ASSERT(!TRACE_RECORDER(cx));
786 #endif
788 return EnterMethodJIT(cx, cx->fp(), safePoint);
791 template <typename T>
792 static inline void Destroy(T &t)
794 t.~T();
797 void
798 mjit::JITScript::release()
800 #if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64)
801 void *addr = code.m_code.executableAddress();
802 memset(addr, 0xcc, code.m_size);
803 #endif
805 code.m_executablePool->release();
807 #if defined JS_POLYIC
808 for (uint32 i = 0; i < nPICs; i++) {
809 pics[i].releasePools();
810 Destroy(pics[i].execPools);
812 #endif
814 #if defined JS_MONOIC
815 for (uint32 i = 0; i < nCallICs; i++)
816 callICs[i].releasePools();
817 #endif
820 void
821 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
823 // NB: The recompiler may call ReleaseScriptCode, in which case it
824 // will get called again when the script is destroyed, so we
825 // must protect against calling ReleaseScriptCode twice.
827 if (script->jitNormal) {
828 script->jitNormal->release();
829 script->jitArityCheckNormal = NULL;
830 cx->free(script->jitNormal);
831 script->jitNormal = NULL;
832 script->nmapNormal = NULL;
835 if (script->jitCtor) {
836 script->jitCtor->release();
837 script->jitArityCheckCtor = NULL;
838 cx->free(script->jitCtor);
839 script->jitCtor = NULL;
840 script->nmapCtor = NULL;
844 #ifdef JS_METHODJIT_PROFILE_STUBS
845 void JS_FASTCALL
846 mjit::ProfileStubCall(VMFrame &f)
848 JSOp op = JSOp(*f.regs.pc);
849 StubCallsForOp[op]++;
851 #endif