1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
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/
11 : *
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.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "MethodJIT.h"
40 : #include "Logging.h"
41 : #include "assembler/jit/ExecutableAllocator.h"
42 : #include "assembler/assembler/RepatchBuffer.h"
43 : #include "js/MemoryMetrics.h"
44 : #include "jsgcmark.h"
45 : #include "BaseAssembler.h"
46 : #include "Compiler.h"
47 : #include "MonoIC.h"
48 : #include "PolyIC.h"
49 : #include "TrampolineCompiler.h"
50 : #include "jscntxtinlines.h"
51 : #include "jscompartment.h"
52 : #include "jsscope.h"
53 :
54 : #include "jsgcinlines.h"
55 : #include "jsinterpinlines.h"
56 :
57 : using namespace js;
58 : using namespace js::mjit;
59 :
60 : #ifdef __GCC_HAVE_DWARF2_CFI_ASM
61 : # define CFI(str) str
62 : #else
63 : # define CFI(str)
64 : #endif
65 :
66 : // Put manually-inserted call frame unwinding information into .debug_frame
67 : // rather than .eh_frame, because we compile with -fno-exceptions which might
68 : // discard the .eh_frame section. (See
69 : // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43232).
70 : CFI(asm(".cfi_sections .debug_frame");)
71 :
72 3299174 : js::mjit::CompilerAllocPolicy::CompilerAllocPolicy(JSContext *cx, Compiler &compiler)
73 : : TempAllocPolicy(cx),
74 3299174 : oomFlag(&compiler.oomInVector)
75 : {
76 3299174 : }
77 : void
78 0 : StackFrame::methodjitStaticAsserts()
79 : {
80 : /* Static assert for x86 trampolines in MethodJIT.cpp. */
81 : #if defined(JS_CPU_X86)
82 : JS_STATIC_ASSERT(offsetof(StackFrame, rval_) == 0x18);
83 : JS_STATIC_ASSERT(offsetof(StackFrame, rval_) + 4 == 0x1C);
84 : JS_STATIC_ASSERT(offsetof(StackFrame, ncode_) == 0x14);
85 : /* ARM uses decimal literals. */
86 : JS_STATIC_ASSERT(offsetof(StackFrame, rval_) == 24);
87 : JS_STATIC_ASSERT(offsetof(StackFrame, rval_) + 4 == 28);
88 : JS_STATIC_ASSERT(offsetof(StackFrame, ncode_) == 20);
89 : #elif defined(JS_CPU_X64)
90 : JS_STATIC_ASSERT(offsetof(StackFrame, rval_) == 0x30);
91 : JS_STATIC_ASSERT(offsetof(StackFrame, ncode_) == 0x28);
92 : #endif
93 0 : }
94 :
95 : /*
96 : * Explanation of VMFrame activation and various helper thunks below.
97 : *
98 : * JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function
99 : * creates a VMFrame on the machine stack and jumps into JIT'd code. The JIT'd
100 : * code will eventually jump back to JaegerTrampolineReturn, clean up the
101 : * VMFrame and return into C++.
102 : *
103 : * - Called from C++ function EnterMethodJIT.
104 : * - Parameters: cx, fp, code, stackLimit
105 : *
106 : * JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a
107 : * scripted exception handler is not found, unwinds the VMFrame and returns
108 : * to C++.
109 : *
110 : * - To start exception handling, we return from a stub call to the throwpoline.
111 : * - On entry to the throwpoline, the normal conditions of the jit-code ABI
112 : * are satisfied.
113 : * - To do the unwinding and find out where to continue executing, we call
114 : * js_InternalThrow.
115 : * - js_InternalThrow may return 0, which means the place to continue, if any,
116 : * is above this JaegerShot activation, so we just return, in the same way
117 : * the trampoline does.
118 : * - Otherwise, js_InternalThrow returns a jit-code address to continue execution
119 : * at. Because the jit-code ABI conditions are satisfied, we can just jump to
120 : * that point.
121 : *
122 : * JaegerInterpoline - After returning from a stub or scripted call made by JIT'd
123 : * code, calls into Interpret and has it finish execution of the JIT'd script.
124 : * If we have to throw away the JIT code for a script for some reason (either
125 : * a new trap is added for debug code, or assumptions made by the JIT code
126 : * have broken and forced its invalidation), the call returns into the
127 : * Interpoline which calls Interpret to finish the JIT frame. The Interpret
128 : * call may eventually recompile the script, in which case it will join into
129 : * that code with a new VMFrame activation and JaegerTrampoline.
130 : *
131 : * - Returned into from stub calls originally made from JIT code.
132 : * - An alternate version, JaegerInterpolineScripted, returns from scripted
133 : * calls originally made from JIT code, and fixes up state to match the
134 : * stub call ABI.
135 : */
136 :
137 : #ifdef JS_METHODJIT_PROFILE_STUBS
138 : static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
139 : static uint32_t StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
140 : #endif
141 :
142 : // Called from JaegerTrampoline only
143 : extern "C" void JS_FASTCALL
144 4229345 : PushActiveVMFrame(VMFrame &f)
145 : {
146 4229345 : f.oldregs = &f.cx->stack.regs();
147 4229345 : f.cx->stack.repointRegs(&f.regs);
148 4229345 : f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f);
149 4229345 : f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
150 4229345 : f.regs.clearInlined();
151 4229345 : }
152 :
153 : // Called from JaegerTrampolineReturn, JaegerThrowpoline, JaegerInterpoline
154 : extern "C" void JS_FASTCALL
155 4229345 : PopActiveVMFrame(VMFrame &f)
156 : {
157 4229345 : f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame();
158 4229345 : f.cx->stack.repointRegs(f.oldregs);
159 4229345 : }
160 :
161 : #if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2)
162 : # define SYMBOL_STRING(name) "_" #name
163 : #else
164 : # define SYMBOL_STRING(name) #name
165 : #endif
166 :
167 : JS_STATIC_ASSERT(offsetof(FrameRegs, sp) == 0);
168 :
169 : #if defined(__linux__) && defined(JS_CPU_X64)
170 : # define SYMBOL_STRING_RELOC(name) #name "@plt"
171 : #else
172 : # define SYMBOL_STRING_RELOC(name) SYMBOL_STRING(name)
173 : #endif
174 :
175 : #if (defined(XP_WIN) || defined(XP_OS2)) && defined(JS_CPU_X86)
176 : # define SYMBOL_STRING_VMFRAME(name) "@" #name "@4"
177 : #else
178 : # define SYMBOL_STRING_VMFRAME(name) SYMBOL_STRING_RELOC(name)
179 : #endif
180 :
181 : #if defined(XP_MACOSX)
182 : # define HIDE_SYMBOL(name) ".private_extern _" #name
183 : #elif defined(__linux__)
184 : # define HIDE_SYMBOL(name) ".hidden" #name
185 : #else
186 : # define HIDE_SYMBOL(name)
187 : #endif
188 :
189 : /*
190 : * Notes about DWARF Call Frame Information (CFI) annotations:
191 : *
192 : * A .cfi directive placed in assembly code describes how to recover the
193 : * caller's registers when control is at or after that directive. That is,
194 : * they describe the states that hold between one instruction and the next,
195 : * not the instructions themselves. Later directives override earlier
196 : * directives.
197 : *
198 : * In DWARF CFI, each stack frame has a Canonical Frame Address (CFA) that
199 : * remains constant throughout the frame's lifetime. Exactly where it is is
200 : * a matter of convention; on the x86 and x86_64, for example, the CFA
201 : * points just after the end of the current stack frame: the address of the
202 : * next word after the return address. The CFI annotations describe 1) how
203 : * to compute the CFA at each point in the function, and 2) given the CFA,
204 : * where the caller's value of each register has been saved. (CFI specifies
205 : * saved registers' locations relative to the CFA, instead of the stack
206 : * pointer, so that when we push or pop the stack, we need only adjust our
207 : * rule for computing the CFA, not the rule for each saved register.)
208 : *
209 : * Quick reference:
210 : *
211 : * .cfi_startproc, .cfi_endproc
212 : * Put these at the beginning and end of the block of code you're
213 : * annotating.
214 : *
215 : * (The following directives apply starting at the point they appear until
216 : * they are overridden or until the .cfi_endproc.)
217 : *
218 : * .cfi_def_cfa REGISTER, OFFSET
219 : * The CFA is the value of REGISTER plus OFFSET.
220 : *
221 : * .cfi_def_cfa_offset OFFSET
222 : * The CFA is the value of the same register as before, but now adding OFFSET.
223 : *
224 : * .cfi_def_cfa_register REGISTER
225 : * The CFA is now the value of REGISTER, adding the same offset as before.
226 : *
227 : * .cfi_offset REGISTER, OFFSET
228 : * The caller's value of REGISTER is saved at OFFSET from the current CFA.
229 : * (This is the directive that actually says something interesting.)
230 : *
231 : * There are other directives that compute the CFA, a saved register's address,
232 : * or a saved register's value, in more complex ways, but the above are the ones
233 : * we use here.
234 : *
235 : * Special rules for JaegerThrowpoline and friends:
236 : *
237 : * In ordinary code, return addresses always point directly after a call
238 : * instruction. When GDB looks up the CFI for a return address it got from the
239 : * stack (as opposed to the current PC), it uses the CFI just before the return
240 : * address --- the CFI associated with the call instruction --- to do the
241 : * unwinding. However, JaegerMonkey uses hacks that edit return addresses to
242 : * point directly at the first instruction of JaegerThrowpoline,
243 : * JaegerInterpoline, and their ilk, so GDB ends up trying to use the CFI
244 : * associated with whatever instruction lies immediately *before* the given
245 : * entry point.
246 : *
247 : * We make sure our CFI covers the code address GDB will actually use, by
248 : * placing a 'nop' *before* the entry point --- it is never executed --- and
249 : * having our CFI apply starting at that nop.
250 : */
251 :
252 : #if defined(__GNUC__) && !defined(_WIN64)
253 :
254 : /* If this assert fails, you need to realign VMFrame to 16 bytes. */
255 : #if defined(JS_CPU_ARM) || defined(JS_CPU_MIPS) || defined(JS_CPU_SPARC)
256 : JS_STATIC_ASSERT(sizeof(VMFrame) % 8 == 0);
257 : #else
258 : JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0);
259 : #endif
260 :
261 : # if defined(JS_CPU_X64)
262 :
263 : /*
264 : * *** DANGER ***
265 : * If these assertions break, update the constants below.
266 : * *** DANGER ***
267 : */
268 : JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x68);
269 : JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0x18);
270 : JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x38);
271 :
272 : JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
273 : JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
274 :
275 : asm (
276 : ".text\n"
277 : ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
278 : SYMBOL_STRING(JaegerTrampoline) ":" "\n"
279 : /* Prologue. */
280 : CFI(".cfi_startproc" "\n")
281 : CFI(".cfi_def_cfa rsp, 8" "\n")
282 : "pushq %rbp" "\n"
283 : CFI(".cfi_def_cfa_offset 16" "\n")
284 : CFI(".cfi_offset rbp, -16" "\n")
285 : "movq %rsp, %rbp" "\n"
286 : CFI(".cfi_def_cfa_register rbp" "\n")
287 : /* Save non-volatile registers. */
288 : "pushq %r12" "\n"
289 : CFI(".cfi_offset r12, -24" "\n")
290 : "pushq %r13" "\n"
291 : CFI(".cfi_offset r13, -32" "\n")
292 : "pushq %r14" "\n"
293 : CFI(".cfi_offset r14, -40" "\n")
294 : "pushq %r15" "\n"
295 : CFI(".cfi_offset r15, -48" "\n")
296 : "pushq %rbx" "\n"
297 : CFI(".cfi_offset rbx, -56" "\n")
298 :
299 : /* Load mask registers. */
300 : "movq $0xFFFF800000000000, %r13" "\n"
301 : "movq $0x00007FFFFFFFFFFF, %r14" "\n"
302 :
303 : /* Build the JIT frame.
304 : * rdi = cx
305 : * rsi = fp
306 : * rcx = inlineCallCount
307 : * fp must go into rbx
308 : */
309 : "pushq $0x0" "\n" /* stubRejoin */
310 : "pushq %rsi" "\n" /* entryncode */
311 : "pushq %rsi" "\n" /* entryfp */
312 : "pushq %rcx" "\n" /* inlineCallCount */
313 : "pushq %rdi" "\n" /* cx */
314 : "pushq %rsi" "\n" /* fp */
315 : "movq %rsi, %rbx" "\n"
316 :
317 : /* Space for the rest of the VMFrame. */
318 : "subq $0x28, %rsp" "\n"
319 :
320 : /* This is actually part of the VMFrame. */
321 : "pushq %r8" "\n"
322 :
323 : /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
324 : "pushq %rdx" "\n"
325 : "movq %rsp, %rdi" "\n"
326 : "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
327 :
328 : /* Jump into the JIT'd code. */
329 : "jmp *0(%rsp)" "\n"
330 : CFI(".cfi_endproc" "\n")
331 : );
332 :
333 : asm (
334 : ".text\n"
335 : /* See "Special rules for JaegerThrowpoline and friends", above. */
336 : CFI(".cfi_startproc" "\n")
337 : CFI(".cfi_def_cfa rbp, 16" "\n")
338 : CFI(".cfi_offset rbp, -16" "\n")
339 : CFI(".cfi_offset r12, -24" "\n")
340 : CFI(".cfi_offset r13, -32" "\n")
341 : CFI(".cfi_offset r14, -40" "\n")
342 : CFI(".cfi_offset r15, -48" "\n")
343 : CFI(".cfi_offset rbx, -56" "\n")
344 : CFI("nop" "\n")
345 : ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
346 : SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
347 : "or %rdi, %rsi" "\n"
348 : "movq %rsi, 0x30(%rbx)" "\n"
349 : "movq %rsp, %rdi" "\n"
350 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
351 :
352 : "addq $0x68, %rsp" "\n"
353 : "popq %rbx" "\n"
354 : "popq %r15" "\n"
355 : "popq %r14" "\n"
356 : "popq %r13" "\n"
357 : "popq %r12" "\n"
358 : "popq %rbp" "\n"
359 : CFI(".cfi_def_cfa rsp, 8" "\n")
360 : "movq $1, %rax" "\n"
361 : "ret" "\n"
362 : CFI(".cfi_endproc" "\n")
363 : );
364 :
365 : asm (
366 : ".text\n"
367 : /* See "Special rules for JaegerThrowpoline and friends", above. */
368 : CFI(".cfi_startproc" "\n")
369 : CFI(".cfi_def_cfa rbp, 16" "\n")
370 : CFI(".cfi_offset rbp, -16" "\n")
371 : CFI(".cfi_offset r12, -24" "\n")
372 : CFI(".cfi_offset r13, -32" "\n")
373 : CFI(".cfi_offset r14, -40" "\n")
374 : CFI(".cfi_offset r15, -48" "\n")
375 : CFI(".cfi_offset rbx, -56" "\n")
376 : CFI("nop" "\n")
377 : ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
378 : SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
379 : "movq %rsp, %rdi" "\n"
380 : "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
381 : "testq %rax, %rax" "\n"
382 : "je throwpoline_exit" "\n"
383 : "jmp *%rax" "\n"
384 : "throwpoline_exit:" "\n"
385 : "movq %rsp, %rdi" "\n"
386 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
387 : "addq $0x68, %rsp" "\n"
388 : "popq %rbx" "\n"
389 : "popq %r15" "\n"
390 : "popq %r14" "\n"
391 : "popq %r13" "\n"
392 : "popq %r12" "\n"
393 : "popq %rbp" "\n"
394 : CFI(".cfi_def_cfa rsp, 8" "\n")
395 : "xorq %rax,%rax" "\n"
396 : "ret" "\n"
397 : CFI(".cfi_endproc" "\n")
398 : );
399 :
400 : asm (
401 : ".text\n"
402 : /* See "Special rules for JaegerThrowpoline and friends", above. */
403 : CFI(".cfi_startproc" "\n")
404 : CFI(".cfi_def_cfa rbp, 16" "\n")
405 : CFI(".cfi_offset rbp, -16" "\n")
406 : CFI(".cfi_offset r12, -24" "\n")
407 : CFI(".cfi_offset r13, -32" "\n")
408 : CFI(".cfi_offset r14, -40" "\n")
409 : CFI(".cfi_offset r15, -48" "\n")
410 : CFI(".cfi_offset rbx, -56" "\n")
411 : CFI("nop" "\n")
412 : ".globl " SYMBOL_STRING(JaegerInterpoline) "\n"
413 : SYMBOL_STRING(JaegerInterpoline) ":" "\n"
414 : "movq %rsp, %rcx" "\n"
415 : "movq %rax, %rdx" "\n"
416 : "call " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n"
417 : "movq 0x38(%rsp), %rbx" "\n" /* Load frame */
418 : "movq 0x30(%rbx), %rsi" "\n" /* Load rval payload */
419 : "and %r14, %rsi" "\n" /* Mask rval payload */
420 : "movq 0x30(%rbx), %rdi" "\n" /* Load rval type */
421 : "and %r13, %rdi" "\n" /* Mask rval type */
422 : "movq 0x18(%rsp), %rcx" "\n" /* Load scratch -> argc */
423 : "testq %rax, %rax" "\n"
424 : "je interpoline_exit" "\n"
425 : "jmp *%rax" "\n"
426 : "interpoline_exit:" "\n"
427 : "movq %rsp, %rdi" "\n"
428 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
429 : "addq $0x68, %rsp" "\n"
430 : "popq %rbx" "\n"
431 : "popq %r15" "\n"
432 : "popq %r14" "\n"
433 : "popq %r13" "\n"
434 : "popq %r12" "\n"
435 : "popq %rbp" "\n"
436 : CFI(".cfi_def_cfa rsp, 8" "\n")
437 : "xorq %rax,%rax" "\n"
438 : "ret" "\n"
439 : CFI(".cfi_endproc" "\n")
440 : );
441 :
442 : asm (
443 : ".text\n"
444 : /* See "Special rules for JaegerThrowpoline and friends", above. */
445 : CFI(".cfi_startproc" "\n")
446 : CFI(".cfi_def_cfa rbp, 16" "\n")
447 : CFI(".cfi_offset rbp, -16" "\n")
448 : CFI(".cfi_offset r12, -24" "\n")
449 : CFI(".cfi_offset r13, -32" "\n")
450 : CFI(".cfi_offset r14, -40" "\n")
451 : CFI(".cfi_offset r15, -48" "\n")
452 : CFI(".cfi_offset rbx, -56" "\n")
453 : CFI("nop" "\n")
454 : ".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n"
455 : SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n"
456 : "movq 0x20(%rbx), %rbx" "\n" /* load prev */
457 : "movq %rbx, 0x38(%rsp)" "\n"
458 : "jmp " SYMBOL_STRING_RELOC(JaegerInterpoline) "\n"
459 : CFI(".cfi_endproc" "\n")
460 : );
461 :
462 : # elif defined(JS_CPU_X86)
463 :
464 : /*
465 : * *** DANGER ***
466 : * If these assertions break, update the constants below. The throwpoline
467 : * should have the offset of savedEBX plus 4, because it needs to clean
468 : * up the argument.
469 : * *** DANGER ***
470 : */
471 : JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x3C);
472 : JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0xC);
473 : JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x1C);
474 :
475 : asm (
476 : ".text\n"
477 : ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
478 : SYMBOL_STRING(JaegerTrampoline) ":" "\n"
479 : /* Prologue. */
480 : CFI(".cfi_startproc" "\n")
481 : CFI(".cfi_def_cfa esp, 4" "\n")
482 : "pushl %ebp" "\n"
483 : CFI(".cfi_def_cfa_offset 8" "\n")
484 : CFI(".cfi_offset ebp, -8" "\n")
485 : "movl %esp, %ebp" "\n"
486 : CFI(".cfi_def_cfa_register ebp" "\n")
487 : /* Save non-volatile registers. */
488 : "pushl %esi" "\n"
489 : CFI(".cfi_offset esi, -12" "\n")
490 : "pushl %edi" "\n"
491 : CFI(".cfi_offset edi, -16" "\n")
492 : "pushl %ebx" "\n"
493 : CFI(".cfi_offset ebx, -20" "\n")
494 :
495 : /* Build the JIT frame. Push fields in order,
496 : * then align the stack to form esp == VMFrame. */
497 : "movl 12(%ebp), %ebx" "\n" /* load fp */
498 : "pushl %ebx" "\n" /* unused1 */
499 : "pushl %ebx" "\n" /* unused0 */
500 : "pushl $0x0" "\n" /* stubRejoin */
501 : "pushl %ebx" "\n" /* entryncode */
502 : "pushl %ebx" "\n" /* entryfp */
503 : "pushl 20(%ebp)" "\n" /* stackLimit */
504 : "pushl 8(%ebp)" "\n" /* cx */
505 : "pushl %ebx" "\n" /* fp */
506 : "subl $0x1C, %esp" "\n"
507 :
508 : /* Jump into the JIT'd code. */
509 : "movl %esp, %ecx" "\n"
510 : "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
511 :
512 : "movl 28(%esp), %ebp" "\n" /* load fp for JIT code */
513 : "jmp *88(%esp)" "\n"
514 : CFI(".cfi_endproc" "\n")
515 : );
516 :
517 : asm (
518 : ".text\n"
519 : /* See "Special rules for JaegerThrowpoline and friends", above. */
520 : CFI(".cfi_startproc" "\n")
521 : CFI(".cfi_def_cfa ebp, 8" "\n")
522 : CFI(".cfi_offset ebp, -8" "\n")
523 : CFI(".cfi_offset esi, -12" "\n")
524 : CFI(".cfi_offset edi, -16" "\n")
525 : CFI(".cfi_offset ebx, -20" "\n")
526 : CFI("nop" "\n")
527 : ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
528 : SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
529 : "movl %esi, 0x18(%ebp)" "\n"
530 : "movl %edi, 0x1C(%ebp)" "\n"
531 : "movl %esp, %ebp" "\n"
532 : "addl $0x48, %ebp" "\n" /* Restore stack at STACK_BASE_DIFFERENCE */
533 : "movl %esp, %ecx" "\n"
534 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
535 :
536 : "addl $0x3C, %esp" "\n"
537 : "popl %ebx" "\n"
538 : "popl %edi" "\n"
539 : "popl %esi" "\n"
540 : "popl %ebp" "\n"
541 : CFI(".cfi_def_cfa esp, 4" "\n")
542 : "movl $1, %eax" "\n"
543 : "ret" "\n"
544 : CFI(".cfi_endproc" "\n")
545 : );
546 :
547 : asm (
548 : ".text\n"
549 : /* See "Special rules for JaegerThrowpoline and friends", above. */
550 : CFI(".cfi_startproc" "\n")
551 : CFI(".cfi_def_cfa ebp, 8" "\n")
552 : CFI(".cfi_offset ebp, -8" "\n")
553 : CFI(".cfi_offset esi, -12" "\n")
554 : CFI(".cfi_offset edi, -16" "\n")
555 : CFI(".cfi_offset ebx, -20" "\n")
556 : CFI("nop" "\n")
557 : ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
558 : SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
559 : /* Align the stack to 16 bytes. */
560 : "pushl %esp" "\n"
561 : "pushl (%esp)" "\n"
562 : "pushl (%esp)" "\n"
563 : "pushl (%esp)" "\n"
564 : "call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
565 : /* Bump the stack by 0x2c, as in the basic trampoline, but
566 : * also one more word to clean up the stack for js_InternalThrow,
567 : * and another to balance the alignment above. */
568 : "addl $0x10, %esp" "\n"
569 : "testl %eax, %eax" "\n"
570 : "je throwpoline_exit" "\n"
571 : "jmp *%eax" "\n"
572 : "throwpoline_exit:" "\n"
573 : "movl %esp, %ecx" "\n"
574 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
575 : "addl $0x3c, %esp" "\n"
576 : "popl %ebx" "\n"
577 : "popl %edi" "\n"
578 : "popl %esi" "\n"
579 : "popl %ebp" "\n"
580 : CFI(".cfi_def_cfa esp, 4" "\n")
581 : "xorl %eax, %eax" "\n"
582 : "ret" "\n"
583 : CFI(".cfi_endproc" "\n")
584 : );
585 :
586 : asm (
587 : ".text\n"
588 : /* See "Special rules for JaegerThrowpoline and friends", above. */
589 : CFI(".cfi_startproc" "\n")
590 : CFI(".cfi_def_cfa ebp, 8" "\n")
591 : CFI(".cfi_offset ebp, -8" "\n")
592 : CFI(".cfi_offset esi, -12" "\n")
593 : CFI(".cfi_offset edi, -16" "\n")
594 : CFI(".cfi_offset ebx, -20" "\n")
595 : CFI("nop" "\n")
596 : ".globl " SYMBOL_STRING(JaegerInterpoline) "\n"
597 : SYMBOL_STRING(JaegerInterpoline) ":" "\n"
598 : /* Align the stack to 16 bytes. */
599 : "pushl %esp" "\n"
600 : "pushl %eax" "\n"
601 : "pushl %edi" "\n"
602 : "pushl %esi" "\n"
603 : "call " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n"
604 : "addl $0x10, %esp" "\n"
605 : "movl 0x1C(%esp), %ebp" "\n" /* Load frame */
606 : "movl 0x18(%ebp), %esi" "\n" /* Load rval payload */
607 : "movl 0x1C(%ebp), %edi" "\n" /* Load rval type */
608 : "movl 0xC(%esp), %ecx" "\n" /* Load scratch -> argc, for any scripted call */
609 : "testl %eax, %eax" "\n"
610 : "je interpoline_exit" "\n"
611 : "jmp *%eax" "\n"
612 : "interpoline_exit:" "\n"
613 : "movl %esp, %ecx" "\n"
614 : "call " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
615 : "addl $0x3c, %esp" "\n"
616 : "popl %ebx" "\n"
617 : "popl %edi" "\n"
618 : "popl %esi" "\n"
619 : "popl %ebp" "\n"
620 : CFI(".cfi_def_cfa esp, 4" "\n")
621 : "xorl %eax, %eax" "\n"
622 : "ret" "\n"
623 : CFI(".cfi_endproc" "\n")
624 : );
625 :
626 : asm (
627 : ".text\n"
628 : /* See "Special rules for JaegerThrowpoline and friends", above. */
629 : CFI(".cfi_startproc" "\n")
630 : CFI(".cfi_def_cfa ebp, 8" "\n")
631 : CFI(".cfi_offset ebp, -8" "\n")
632 : CFI(".cfi_offset esi, -12" "\n")
633 : CFI(".cfi_offset edi, -16" "\n")
634 : CFI(".cfi_offset ebx, -20" "\n")
635 : CFI("nop" "\n")
636 : ".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n"
637 : SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n"
638 : "movl 0x10(%ebp), %ebp" "\n" /* load prev. :XXX: STATIC_ASSERT this */
639 : "movl %ebp, 0x1C(%esp)" "\n"
640 : "jmp " SYMBOL_STRING_RELOC(JaegerInterpoline) "\n"
641 : CFI(".cfi_endproc" "\n")
642 : );
643 :
644 : # elif defined(JS_CPU_ARM)
645 :
646 : JS_STATIC_ASSERT(sizeof(VMFrame) == 88);
647 : JS_STATIC_ASSERT(sizeof(VMFrame)%8 == 0); /* We need 8-byte stack alignment for EABI. */
648 : JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == (4*21));
649 : JS_STATIC_ASSERT(offsetof(VMFrame, entryfp) == (4*10));
650 : JS_STATIC_ASSERT(offsetof(VMFrame, stackLimit) == (4*9));
651 : JS_STATIC_ASSERT(offsetof(VMFrame, cx) == (4*8));
652 : JS_STATIC_ASSERT(VMFrame::offsetOfFp == (4*7));
653 : JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == (4*3));
654 : JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*2));
655 :
656 : JS_STATIC_ASSERT(JSFrameReg == JSC::ARMRegisters::r10);
657 : JS_STATIC_ASSERT(JSReturnReg_Type == JSC::ARMRegisters::r5);
658 : JS_STATIC_ASSERT(JSReturnReg_Data == JSC::ARMRegisters::r4);
659 :
660 : #ifdef MOZ_THUMB2
661 : #define FUNCTION_HEADER_EXTRA \
662 : ".align 2\n" \
663 : ".thumb\n" \
664 : ".thumb_func\n"
665 : #else
666 : #define FUNCTION_HEADER_EXTRA
667 : #endif
668 :
669 : asm (
670 : ".text\n"
671 : FUNCTION_HEADER_EXTRA
672 : ".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
673 : SYMBOL_STRING(JaegerTrampoline) ":" "\n"
674 : /*
675 : * On entry to JaegerTrampoline:
676 : * r0 = cx
677 : * r1 = fp
678 : * r2 = code
679 : * r3 = stackLimit
680 : *
681 : * The VMFrame for ARM looks like this:
682 : * [ lr ] \
683 : * [ r11 ] |
684 : * [ r10 ] |
685 : * [ r9 ] | Callee-saved registers.
686 : * [ r8 ] | VFP registers d8-d15 may be required here too, but
687 : * [ r7 ] | unconditionally preserving them might be expensive
688 : * [ r6 ] | considering that we might not use them anyway.
689 : * [ r5 ] |
690 : * [ r4 ] /
691 : * [ stubRejoin ]
692 : * [ entryncode ]
693 : * [ entryfp ]
694 : * [ stkLimit ]
695 : * [ cx ]
696 : * [ regs.fp ]
697 : * [ regs.inlined ]
698 : * [ regs.pc ]
699 : * [ regs.sp ]
700 : * [ scratch ]
701 : * [ previous ]
702 : * [ args.ptr2 ] [ dynamicArgc ] (union)
703 : * [ args.ptr ] [ lazyArgsObj ] (union)
704 : */
705 :
706 : /* Push callee-saved registers. */
707 : " push {r4-r11,lr}" "\n"
708 : /* Push interesting VMFrame content. */
709 : " mov ip, #0" "\n"
710 : " push {ip}" "\n" /* stubRejoin */
711 : " push {r1}" "\n" /* entryncode */
712 : " push {r1}" "\n" /* entryfp */
713 : " push {r3}" "\n" /* stackLimit */
714 : " push {r0}" "\n" /* cx */
715 : " push {r1}" "\n" /* regs.fp */
716 : /* Remaining fields are set elsewhere, but we need to leave space for them. */
717 : " sub sp, sp, #(4*7)" "\n"
718 :
719 : /* Preserve 'code' (r2) in an arbitrary callee-saved register. */
720 : " mov r4, r2" "\n"
721 : /* Preserve 'fp' (r1) in r10 (JSFrameReg). */
722 : " mov r10, r1" "\n"
723 :
724 : " mov r0, sp" "\n"
725 : " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n"
726 :
727 : /* Call the compiled JavaScript function. */
728 : " bx r4" "\n"
729 : );
730 :
731 : asm (
732 : ".text\n"
733 : FUNCTION_HEADER_EXTRA
734 : ".globl " SYMBOL_STRING(JaegerTrampolineReturn) "\n"
735 : SYMBOL_STRING(JaegerTrampolineReturn) ":" "\n"
736 : " strd r4, r5, [r10, #24]" "\n" /* fp->rval type,data */
737 :
738 : /* Tidy up. */
739 : " mov r0, sp" "\n"
740 : " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
741 :
742 : /* Skip past the parameters we pushed (such as cx and the like). */
743 : " add sp, sp, #(4*7 + 4*6)" "\n"
744 :
745 : /* Set a 'true' return value to indicate successful completion. */
746 : " mov r0, #1" "\n"
747 : " pop {r4-r11,pc}" "\n"
748 : );
749 :
750 : asm (
751 : ".text\n"
752 : FUNCTION_HEADER_EXTRA
753 : ".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
754 : SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
755 : /* Find the VMFrame pointer for js_InternalThrow. */
756 : " mov r0, sp" "\n"
757 :
758 : /* Call the utility function that sets up the internal throw routine. */
759 : " blx " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
760 :
761 : /* If js_InternalThrow found a scripted handler, jump to it. Otherwise, tidy
762 : * up and return. */
763 : " cmp r0, #0" "\n"
764 : " it ne" "\n"
765 : " bxne r0" "\n"
766 :
767 : /* Tidy up, then return '0' to represent an unhandled exception. */
768 : " mov r0, sp" "\n"
769 : " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
770 : " add sp, sp, #(4*7 + 4*6)" "\n"
771 : " mov r0, #0" "\n"
772 : " pop {r4-r11,pc}" "\n"
773 : );
774 :
775 : asm (
776 : ".text\n"
777 : FUNCTION_HEADER_EXTRA
778 : ".globl " SYMBOL_STRING(JaegerInterpolineScripted) "\n"
779 : SYMBOL_STRING(JaegerInterpolineScripted) ":" "\n"
780 : /* The only difference between JaegerInterpoline and JaegerInpolineScripted is that the
781 : * scripted variant has to walk up to the previous StackFrame first. */
782 : " ldr r10, [r10, #(4*4)]" "\n" /* Load f->prev_ */
783 : " str r10, [sp, #(4*7)]" "\n" /* Update f->regs->fp_ */
784 : /* Fall through into JaegerInterpoline. */
785 :
786 : FUNCTION_HEADER_EXTRA
787 : ".globl " SYMBOL_STRING(JaegerInterpoline) "\n"
788 : SYMBOL_STRING(JaegerInterpoline) ":" "\n"
789 : " mov r3, sp" "\n" /* f */
790 : " mov r2, r0" "\n" /* returnReg */
791 : " mov r1, r5" "\n" /* returnType */
792 : " mov r0, r4" "\n" /* returnData */
793 : " blx " SYMBOL_STRING_RELOC(js_InternalInterpret) "\n"
794 : " cmp r0, #0" "\n"
795 : " ldr r10, [sp, #(4*7)]" "\n" /* Load (StackFrame*)f->regs->fp_ */
796 : " ldrd r4, r5, [r10, #(4*6)]" "\n" /* Load rval payload and type. */
797 : " ldr r1, [sp, #(4*3)]" "\n" /* Load scratch. */
798 : " it ne" "\n"
799 : " bxne r0" "\n"
800 : /* Tidy up, then return 0. */
801 : " mov r0, sp" "\n"
802 : " blx " SYMBOL_STRING_VMFRAME(PopActiveVMFrame) "\n"
803 : " add sp, sp, #(4*7 + 4*6)" "\n"
804 : " mov r0, #0" "\n"
805 : " pop {r4-r11,pc}" "\n"
806 : );
807 :
808 : asm (
809 : ".text\n"
810 : FUNCTION_HEADER_EXTRA
811 : ".globl " SYMBOL_STRING(JaegerStubVeneer) "\n"
812 : SYMBOL_STRING(JaegerStubVeneer) ":" "\n"
813 : /* We enter this function as a veneer between a compiled method and one of the js_ stubs. We
814 : * need to store the LR somewhere (so it can be modified in case on an exception) and then
815 : * branch to the js_ stub as if nothing had happened.
816 : * The arguments are identical to those for js_* except that the target function should be in
817 : * 'ip'. */
818 : " push {ip,lr}" "\n"
819 : " blx ip" "\n"
820 : " pop {ip,pc}" "\n"
821 : );
822 :
823 : # elif defined(JS_CPU_SPARC)
824 : # elif defined(JS_CPU_MIPS)
825 : # else
826 : # error "Unsupported CPU!"
827 : # endif
828 : #elif defined(_MSC_VER) && defined(JS_CPU_X86)
829 :
830 : /*
831 : * *** DANGER ***
832 : * If these assertions break, update the constants below. The throwpoline
833 : * should have the offset of savedEBX plus 4, because it needs to clean
834 : * up the argument.
835 : * *** DANGER ***
836 : */
837 : JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x3C);
838 : JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0xC);
839 : JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x1C);
840 :
841 : extern "C" {
842 :
843 : __declspec(naked) JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code,
844 : Value *stackLimit)
845 : {
846 : __asm {
847 : /* Prologue. */
848 : push ebp;
849 : mov ebp, esp;
850 : /* Save non-volatile registers. */
851 : push esi;
852 : push edi;
853 : push ebx;
854 :
855 : /* Build the JIT frame. Push fields in order,
856 : * then align the stack to form esp == VMFrame. */
857 : mov ebx, [ebp + 12];
858 : push ebx;
859 : push ebx;
860 : push 0x0;
861 : push ebx;
862 : push ebx;
863 : push [ebp + 20];
864 : push [ebp + 8];
865 : push ebx;
866 : sub esp, 0x1C;
867 :
868 : /* Jump into into the JIT'd code. */
869 : mov ecx, esp;
870 : call PushActiveVMFrame;
871 :
872 : mov ebp, [esp + 28]; /* load fp for JIT code */
873 : jmp dword ptr [esp + 88];
874 : }
875 : }
876 :
877 : __declspec(naked) void JaegerTrampolineReturn()
878 : {
879 : __asm {
880 : mov [ebp + 0x18], esi;
881 : mov [ebp + 0x1C], edi;
882 : mov ebp, esp;
883 : add ebp, 0x48; /* Restore stack at STACK_BASE_DIFFERENCE */
884 : mov ecx, esp;
885 : call PopActiveVMFrame;
886 :
887 : add esp, 0x3C;
888 :
889 : pop ebx;
890 : pop edi;
891 : pop esi;
892 : pop ebp;
893 : mov eax, 1;
894 : ret;
895 : }
896 : }
897 :
898 : extern "C" void *js_InternalThrow(js::VMFrame &f);
899 :
900 : __declspec(naked) void *JaegerThrowpoline(js::VMFrame *vmFrame) {
901 : __asm {
902 : /* Align the stack to 16 bytes. */
903 : push esp;
904 : push [esp];
905 : push [esp];
906 : push [esp];
907 : call js_InternalThrow;
908 : /* Bump the stack by 0x2c, as in the basic trampoline, but
909 : * also one more word to clean up the stack for js_InternalThrow,
910 : * and another to balance the alignment above. */
911 : add esp, 0x10;
912 : test eax, eax;
913 : je throwpoline_exit;
914 : jmp eax;
915 : throwpoline_exit:
916 : mov ecx, esp;
917 : call PopActiveVMFrame;
918 : add esp, 0x3c;
919 : pop ebx;
920 : pop edi;
921 : pop esi;
922 : pop ebp;
923 : xor eax, eax
924 : ret;
925 : }
926 : }
927 :
928 : extern "C" void *
929 : js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f);
930 :
931 : __declspec(naked) void JaegerInterpoline() {
932 : __asm {
933 : /* Align the stack to 16 bytes. */
934 : push esp;
935 : push eax;
936 : push edi;
937 : push esi;
938 : call js_InternalInterpret;
939 : add esp, 0x10;
940 : mov ebp, [esp + 0x1C]; /* Load frame */
941 : mov esi, [ebp + 0x18]; /* Load rval payload */
942 : mov edi, [ebp + 0x1C]; /* Load rval type */
943 : mov ecx, [esp + 0xC]; /* Load scratch -> argc */
944 : test eax, eax;
945 : je interpoline_exit;
946 : jmp eax;
947 : interpoline_exit:
948 : mov ecx, esp;
949 : call PopActiveVMFrame;
950 : add esp, 0x3c;
951 : pop ebx;
952 : pop edi;
953 : pop esi;
954 : pop ebp;
955 : xor eax, eax
956 : ret;
957 : }
958 : }
959 :
960 : __declspec(naked) void JaegerInterpolineScripted() {
961 : __asm {
962 : mov ebp, [ebp + 0x10]; /* Load prev */
963 : mov [esp + 0x1C], ebp; /* fp -> regs.fp */
964 : jmp JaegerInterpoline;
965 : }
966 : }
967 : }
968 :
969 : // Windows x64 uses assembler version since compiler doesn't support
970 : // inline assembler
971 : #elif defined(_WIN64)
972 :
973 : /*
974 : * *** DANGER ***
975 : * If these assertions break, update the constants below.
976 : * *** DANGER ***
977 : */
978 : JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x68);
979 : JS_STATIC_ASSERT(offsetof(VMFrame, scratch) == 0x18);
980 : JS_STATIC_ASSERT(VMFrame::offsetOfFp == 0x38);
981 : JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
982 : JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
983 :
984 : #endif /* _WIN64 */
985 :
986 11826 : JaegerCompartment::JaegerCompartment()
987 11826 : : orphanedNativeFrames(SystemAllocPolicy()), orphanedNativePools(SystemAllocPolicy())
988 11826 : {}
989 :
990 : bool
991 11826 : JaegerCompartment::Initialize(JSContext *cx)
992 : {
993 : execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>(
994 11826 : cx->runtime->getJitHardening() ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic);
995 11826 : if (!execAlloc_)
996 0 : return false;
997 :
998 11826 : TrampolineCompiler tc(execAlloc_, &trampolines);
999 11826 : if (!tc.compile()) {
1000 0 : js::Foreground::delete_(execAlloc_);
1001 0 : execAlloc_ = NULL;
1002 0 : return false;
1003 : }
1004 :
1005 : #ifdef JS_METHODJIT_PROFILE_STUBS
1006 : for (size_t i = 0; i < STUB_CALLS_FOR_OP_COUNT; ++i)
1007 : StubCallsForOp[i] = 0;
1008 : #endif
1009 :
1010 11826 : activeFrame_ = NULL;
1011 11826 : lastUnfinished_ = (JaegerStatus) 0;
1012 :
1013 11826 : return true;
1014 : }
1015 :
1016 : void
1017 11826 : JaegerCompartment::Finish()
1018 : {
1019 11826 : TrampolineCompiler::release(&trampolines);
1020 11826 : Foreground::delete_(execAlloc_);
1021 : #ifdef JS_METHODJIT_PROFILE_STUBS
1022 : FILE *fp = fopen("/tmp/stub-profiling", "wt");
1023 : # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \
1024 : fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]);
1025 : # include "jsopcode.tbl"
1026 : # undef OPDEF
1027 : fclose(fp);
1028 : #endif
1029 11826 : }
1030 :
1031 : extern "C" JSBool
1032 : JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit);
1033 :
1034 : JaegerStatus
1035 4229345 : mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit, bool partial)
1036 : {
1037 : #ifdef JS_METHODJIT_SPEW
1038 : Profiler prof;
1039 4229345 : JSScript *script = fp->script();
1040 :
1041 : JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n",
1042 4229345 : script->filename, script->lineno);
1043 4229345 : prof.start();
1044 : #endif
1045 :
1046 4229345 : JS_ASSERT(cx->fp() == fp);
1047 :
1048 : JSBool ok;
1049 : {
1050 8458690 : AssertCompartmentUnchanged pcc(cx);
1051 8458690 : JSAutoResolveFlags rf(cx, RESOLVE_INFER);
1052 4229345 : ok = JaegerTrampoline(cx, fp, code, stackLimit);
1053 : }
1054 :
1055 : #ifdef JS_METHODJIT_SPEW
1056 4229345 : prof.stop();
1057 4229345 : JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
1058 : #endif
1059 :
1060 4229345 : JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished();
1061 4229345 : if (status) {
1062 49073 : if (partial) {
1063 : /*
1064 : * Being called from the interpreter, which will resume execution
1065 : * where the JIT left off.
1066 : */
1067 34661 : return status;
1068 : }
1069 :
1070 : /*
1071 : * Call back into the interpreter to finish the initial frame. This may
1072 : * invoke EnterMethodJIT again, but will allow partial execution for
1073 : * that recursive invocation, so we can have at most two VM frames for
1074 : * a range of inline frames.
1075 : */
1076 : InterpMode mode = (status == Jaeger_UnfinishedAtTrap)
1077 : ? JSINTERP_SKIP_TRAP
1078 14412 : : JSINTERP_REJOIN;
1079 14412 : ok = Interpret(cx, fp, mode);
1080 :
1081 14412 : return ok ? Jaeger_Returned : Jaeger_Throwing;
1082 : }
1083 :
1084 : /* The entry frame should have finished. */
1085 4180272 : JS_ASSERT(fp == cx->fp());
1086 :
1087 4180272 : if (ok) {
1088 : /* The trampoline wrote the return value but did not set the HAS_RVAL flag. */
1089 4164695 : fp->markReturnValue();
1090 : }
1091 :
1092 : /* See comment in mjit::Compiler::emitReturn. */
1093 4180272 : if (fp->isFunctionFrame())
1094 4155420 : fp->updateEpilogueFlags();
1095 :
1096 4180272 : return ok ? Jaeger_Returned : Jaeger_Throwing;
1097 : }
1098 :
1099 : static inline JaegerStatus
1100 4229414 : CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, bool partial)
1101 : {
1102 4229414 : JS_CHECK_RECURSION(cx, return Jaeger_Throwing);
1103 :
1104 4229345 : JS_ASSERT(!cx->compartment->activeAnalysis);
1105 4229345 : JS_ASSERT(code);
1106 :
1107 4229345 : Value *stackLimit = cx->stack.space().getStackLimit(cx, REPORT_ERROR);
1108 4229345 : if (!stackLimit)
1109 0 : return Jaeger_ThrowBeforeEnter;
1110 :
1111 4229345 : return EnterMethodJIT(cx, fp, code, stackLimit, partial);
1112 : }
1113 :
1114 : JaegerStatus
1115 4195920 : mjit::JaegerShot(JSContext *cx, bool partial)
1116 : {
1117 4195920 : StackFrame *fp = cx->fp();
1118 4195920 : JSScript *script = fp->script();
1119 4195920 : JITScript *jit = script->getJIT(fp->isConstructing());
1120 :
1121 4195920 : JS_ASSERT(cx->regs().pc == script->code);
1122 :
1123 4195920 : return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry, partial);
1124 : }
1125 :
1126 : JaegerStatus
1127 33494 : js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial)
1128 : {
1129 33494 : return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint, partial);
1130 : }
1131 :
1132 : NativeMapEntry *
1133 1961788 : JITChunk::nmap() const
1134 : {
1135 1961788 : return (NativeMapEntry *)((char*)this + sizeof(*this));
1136 : }
1137 :
1138 : js::mjit::InlineFrame *
1139 1921505 : JITChunk::inlineFrames() const
1140 : {
1141 1921505 : return (js::mjit::InlineFrame *)((char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs);
1142 : }
1143 :
1144 : js::mjit::CallSite *
1145 1903457 : JITChunk::callSites() const
1146 : {
1147 1903457 : return (js::mjit::CallSite *)&inlineFrames()[nInlineFrames];
1148 : }
1149 :
1150 : char *
1151 1793452 : JITChunk::commonSectionLimit() const
1152 : {
1153 1793452 : return (char *)&callSites()[nCallSites];
1154 : }
1155 :
1156 : #ifdef JS_MONOIC
1157 : ic::GetGlobalNameIC *
1158 1793452 : JITChunk::getGlobalNames() const
1159 : {
1160 1793452 : return (ic::GetGlobalNameIC *) commonSectionLimit();
1161 : }
1162 :
1163 : ic::SetGlobalNameIC *
1164 1793452 : JITChunk::setGlobalNames() const
1165 : {
1166 1793452 : return (ic::SetGlobalNameIC *)((char *)getGlobalNames() +
1167 1793452 : sizeof(ic::GetGlobalNameIC) * nGetGlobalNames);
1168 : }
1169 :
1170 : ic::CallICInfo *
1171 1793452 : JITChunk::callICs() const
1172 : {
1173 1793452 : return (ic::CallICInfo *)&setGlobalNames()[nSetGlobalNames];
1174 : }
1175 :
1176 : ic::EqualityICInfo *
1177 386481 : JITChunk::equalityICs() const
1178 : {
1179 386481 : return (ic::EqualityICInfo *)&callICs()[nCallICs];
1180 : }
1181 :
1182 : char *
1183 386481 : JITChunk::monoICSectionsLimit() const
1184 : {
1185 386481 : return (char *)&equalityICs()[nEqualityICs];
1186 : }
1187 : #else // JS_MONOIC
1188 : char *
1189 : JITChunk::monoICSectionsLimit() const
1190 : {
1191 : return commonSectionLimit();
1192 : }
1193 : #endif // JS_MONOIC
1194 :
1195 : #ifdef JS_POLYIC
1196 : ic::GetElementIC *
1197 386481 : JITChunk::getElems() const
1198 : {
1199 386481 : return (ic::GetElementIC *)monoICSectionsLimit();
1200 : }
1201 :
1202 : ic::SetElementIC *
1203 257654 : JITChunk::setElems() const
1204 : {
1205 257654 : return (ic::SetElementIC *)((char *)getElems() + sizeof(ic::GetElementIC) * nGetElems);
1206 : }
1207 :
1208 : ic::PICInfo *
1209 128827 : JITChunk::pics() const
1210 : {
1211 128827 : return (ic::PICInfo *)((char *)setElems() + sizeof(ic::SetElementIC) * nSetElems);
1212 : }
1213 :
1214 : char *
1215 0 : JITChunk::polyICSectionsLimit() const
1216 : {
1217 0 : return (char *)pics() + sizeof(ic::PICInfo) * nPICs;
1218 : }
1219 : #else // JS_POLYIC
1220 : char *
1221 : JITChunk::polyICSectionsLimit() const
1222 : {
1223 : return monoICSectionsLimit();
1224 : }
1225 : #endif // JS_POLYIC
1226 :
1227 : void
1228 5160 : JITScript::patchEdge(const CrossChunkEdge &edge, void *label)
1229 : {
1230 5160 : if (edge.sourceJump1 || edge.sourceJump2) {
1231 3683 : JITChunk *sourceChunk = chunk(script->code + edge.source);
1232 7366 : ic::Repatcher repatch(sourceChunk);
1233 :
1234 : #ifdef JS_CPU_X64
1235 : JS_ASSERT(edge.sourceTrampoline);
1236 :
1237 : static const uint32_t JUMP_LENGTH = 10;
1238 :
1239 : if (edge.sourceJump1) {
1240 : JSC::CodeLocationLabel targetLabel(VerifyRange(edge.sourceJump1, JUMP_LENGTH, label, 0)
1241 : ? label
1242 : : edge.sourceTrampoline);
1243 : repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel);
1244 : }
1245 : if (edge.sourceJump2) {
1246 : JSC::CodeLocationLabel targetLabel(VerifyRange(edge.sourceJump2, JUMP_LENGTH, label, 0)
1247 : ? label
1248 : : edge.sourceTrampoline);
1249 : repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel);
1250 : }
1251 : JSC::CodeLocationDataLabelPtr sourcePatch((char*)edge.sourceTrampoline + JUMP_LENGTH);
1252 : repatch.repatch(sourcePatch, label);
1253 : #else
1254 3683 : JSC::CodeLocationLabel targetLabel(label);
1255 3683 : if (edge.sourceJump1)
1256 3683 : repatch.relink(JSC::CodeLocationJump(edge.sourceJump1), targetLabel);
1257 3683 : if (edge.sourceJump2)
1258 827 : repatch.relink(JSC::CodeLocationJump(edge.sourceJump2), targetLabel);
1259 : #endif
1260 : }
1261 5160 : if (edge.jumpTableEntries) {
1262 8 : for (unsigned i = 0; i < edge.jumpTableEntries->length(); i++)
1263 4 : *(*edge.jumpTableEntries)[i] = label;
1264 : }
1265 5160 : }
1266 :
1267 : template <typename T>
1268 850596 : static inline void Destroy(T &t)
1269 : {
1270 850596 : t.~T();
1271 850596 : }
1272 :
1273 257654 : JITChunk::~JITChunk()
1274 : {
1275 128827 : code.release();
1276 :
1277 128827 : if (pcLengths)
1278 0 : Foreground::free_(pcLengths);
1279 :
1280 : #if defined JS_POLYIC
1281 128827 : ic::GetElementIC *getElems_ = getElems();
1282 128827 : ic::SetElementIC *setElems_ = setElems();
1283 128827 : ic::PICInfo *pics_ = pics();
1284 150522 : for (uint32_t i = 0; i < nGetElems; i++)
1285 21695 : Destroy(getElems_[i]);
1286 134519 : for (uint32_t i = 0; i < nSetElems; i++)
1287 5692 : Destroy(setElems_[i]);
1288 952036 : for (uint32_t i = 0; i < nPICs; i++)
1289 823209 : Destroy(pics_[i]);
1290 : #endif
1291 :
1292 : #if defined JS_MONOIC
1293 265950 : for (JSC::ExecutablePool **pExecPool = execPools.begin();
1294 132975 : pExecPool != execPools.end();
1295 : ++pExecPool)
1296 : {
1297 4148 : (*pExecPool)->release();
1298 : }
1299 :
1300 164348 : for (unsigned i = 0; i < nativeCallStubs.length(); i++) {
1301 35521 : JSC::ExecutablePool *pool = nativeCallStubs[i].pool;
1302 35521 : if (pool)
1303 34658 : pool->release();
1304 : }
1305 :
1306 128827 : ic::CallICInfo *callICs_ = callICs();
1307 276163 : for (uint32_t i = 0; i < nCallICs; i++) {
1308 147336 : callICs_[i].releasePools();
1309 147336 : if (callICs_[i].fastGuardedObject)
1310 6502 : callICs_[i].purgeGuardedObject();
1311 : }
1312 : #endif
1313 128827 : }
1314 :
1315 : void
1316 113898 : JITScript::destroy(JSContext *cx)
1317 : {
1318 230996 : for (unsigned i = 0; i < nchunks; i++)
1319 117098 : destroyChunk(cx, i);
1320 :
1321 113898 : if (shimPool)
1322 234 : shimPool->release();
1323 113898 : }
1324 :
1325 : void
1326 150529 : JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses)
1327 : {
1328 150529 : ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
1329 :
1330 150529 : if (desc.chunk) {
1331 128827 : Probes::discardMJITCode(cx, this, script, desc.chunk->code.m_code.executableAddress());
1332 128827 : cx->delete_(desc.chunk);
1333 128827 : desc.chunk = NULL;
1334 :
1335 128827 : CrossChunkEdge *edges = this->edges();
1336 152748 : for (unsigned i = 0; i < nedges; i++) {
1337 23921 : CrossChunkEdge &edge = edges[i];
1338 23921 : if (edge.source >= desc.begin && edge.source < desc.end) {
1339 1816 : edge.sourceJump1 = edge.sourceJump2 = NULL;
1340 : #ifdef JS_CPU_X64
1341 : edge.sourceTrampoline = NULL;
1342 : #endif
1343 3632 : if (edge.jumpTableEntries) {
1344 10 : cx->delete_(edge.jumpTableEntries);
1345 10 : edge.jumpTableEntries = NULL;
1346 : }
1347 22105 : } else if (edge.target >= desc.begin && edge.target < desc.end) {
1348 1681 : edge.targetLabel = NULL;
1349 1681 : patchEdge(edge, edge.shimLabel);
1350 : }
1351 : }
1352 : }
1353 :
1354 150529 : if (resetUses)
1355 150436 : desc.counter = 0;
1356 :
1357 150529 : if (chunkIndex == 0) {
1358 146648 : if (argsCheckPool) {
1359 575 : argsCheckPool->release();
1360 575 : argsCheckPool = NULL;
1361 : }
1362 :
1363 146648 : invokeEntry = NULL;
1364 146648 : fastEntry = NULL;
1365 146648 : arityCheckEntry = NULL;
1366 146648 : argsCheckEntry = NULL;
1367 :
1368 146648 : if (script->jitNormal == this)
1369 144719 : script->jitArityCheckNormal = NULL;
1370 : else
1371 1929 : script->jitArityCheckCtor = NULL;
1372 :
1373 : // Fixup any ICs still referring to this chunk.
1374 299798 : while (!JS_CLIST_IS_EMPTY(&callers)) {
1375 : JS_STATIC_ASSERT(offsetof(ic::CallICInfo, links) == 0);
1376 6502 : ic::CallICInfo *ic = (ic::CallICInfo *) callers.next;
1377 :
1378 6502 : uint8_t *start = (uint8_t *)ic->funGuard.executableAddress();
1379 13004 : JSC::RepatchBuffer repatch(JSC::JITCode(start - 32, 64));
1380 :
1381 6502 : repatch.repatch(ic->funGuard, NULL);
1382 6502 : repatch.relink(ic->funJump, ic->slowPathStart);
1383 6502 : ic->purgeGuardedObject();
1384 : }
1385 : }
1386 150529 : }
1387 :
1388 : size_t
1389 3818 : JSScript::sizeOfJitScripts(JSMallocSizeOfFun mallocSizeOf)
1390 : {
1391 3818 : size_t n = 0;
1392 3818 : if (jitNormal)
1393 49 : n += jitNormal->sizeOfIncludingThis(mallocSizeOf);
1394 3818 : if (jitCtor)
1395 3 : n += jitCtor->sizeOfIncludingThis(mallocSizeOf);
1396 3818 : return n;
1397 : }
1398 :
1399 : size_t
1400 52 : mjit::JITScript::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
1401 : {
1402 52 : size_t n = mallocSizeOf(this);
1403 104 : for (unsigned i = 0; i < nchunks; i++) {
1404 52 : const ChunkDescriptor &desc = chunkDescriptor(i);
1405 52 : if (desc.chunk)
1406 42 : n += desc.chunk->sizeOfIncludingThis(mallocSizeOf);
1407 : }
1408 52 : return n;
1409 : }
1410 :
1411 : /* Please keep in sync with Compiler::finishThisUp! */
1412 : size_t
1413 128827 : mjit::JITChunk::computedSizeOfIncludingThis()
1414 : {
1415 : return sizeof(JITChunk) +
1416 : sizeof(NativeMapEntry) * nNmapPairs +
1417 : sizeof(InlineFrame) * nInlineFrames +
1418 : sizeof(CallSite) * nCallSites +
1419 : #if defined JS_MONOIC
1420 : sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
1421 : sizeof(ic::SetGlobalNameIC) * nSetGlobalNames +
1422 : sizeof(ic::CallICInfo) * nCallICs +
1423 : sizeof(ic::EqualityICInfo) * nEqualityICs +
1424 : #endif
1425 : #if defined JS_POLYIC
1426 : sizeof(ic::PICInfo) * nPICs +
1427 : sizeof(ic::GetElementIC) * nGetElems +
1428 : sizeof(ic::SetElementIC) * nSetElems +
1429 : #endif
1430 128827 : 0;
1431 : }
1432 :
1433 : /* Please keep in sync with Compiler::finishThisUp! */
1434 : size_t
1435 42 : mjit::JITChunk::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
1436 : {
1437 42 : return mallocSizeOf(this);
1438 : }
1439 :
1440 : void
1441 113898 : mjit::ReleaseScriptCode(JSContext *cx, JSScript *script, bool construct)
1442 : {
1443 : // NB: The recompiler may call ReleaseScriptCode, in which case it
1444 : // will get called again when the script is destroyed, so we
1445 : // must protect against calling ReleaseScriptCode twice.
1446 :
1447 113898 : JITScript **pjit = construct ? &script->jitCtor : &script->jitNormal;
1448 113898 : void **parity = construct ? &script->jitArityCheckCtor : &script->jitArityCheckNormal;
1449 :
1450 113898 : if (*pjit) {
1451 113898 : (*pjit)->destroy(cx);
1452 113898 : cx->free_(*pjit);
1453 113898 : *pjit = NULL;
1454 113898 : *parity = NULL;
1455 : }
1456 113898 : }
1457 :
1458 : #ifdef JS_METHODJIT_PROFILE_STUBS
1459 : void JS_FASTCALL
1460 : mjit::ProfileStubCall(VMFrame &f)
1461 : {
1462 : JSOp op = JSOp(*f.regs.pc);
1463 : StubCallsForOp[op]++;
1464 : }
1465 : #endif
1466 :
1467 : JITChunk *
1468 1393862 : JITScript::findCodeChunk(void *addr)
1469 : {
1470 1404816 : for (unsigned i = 0; i < nchunks; i++) {
1471 1399103 : ChunkDescriptor &desc = chunkDescriptor(i);
1472 1399103 : if (desc.chunk && desc.chunk->isValidCode(addr))
1473 1388149 : return desc.chunk;
1474 : }
1475 5713 : return NULL;
1476 : }
1477 :
1478 : jsbytecode *
1479 1278144 : JITScript::nativeToPC(void *returnAddress, CallSite **pinline)
1480 : {
1481 1278144 : JITChunk *chunk = findCodeChunk(returnAddress);
1482 1278144 : JS_ASSERT(chunk);
1483 :
1484 1278144 : size_t low = 0;
1485 1278144 : size_t high = chunk->nCallICs;
1486 1278144 : js::mjit::ic::CallICInfo *callICs_ = chunk->callICs();
1487 3418677 : while (high > low + 1) {
1488 : /* Could overflow here on a script with 2 billion calls. Oh well. */
1489 862389 : size_t mid = (high + low) / 2;
1490 862389 : void *entry = callICs_[mid].funGuard.executableAddress();
1491 :
1492 : /*
1493 : * Use >= here as the return address of the call is likely to be
1494 : * the start address of the next (possibly IC'ed) operation.
1495 : */
1496 862389 : if (entry >= returnAddress)
1497 435839 : high = mid;
1498 : else
1499 426550 : low = mid;
1500 : }
1501 :
1502 1278144 : js::mjit::ic::CallICInfo &ic = callICs_[low];
1503 1278144 : JS_ASSERT((uint8_t*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress);
1504 :
1505 1278144 : if (ic.call->inlineIndex != UINT32_MAX) {
1506 39 : if (pinline)
1507 39 : *pinline = ic.call;
1508 39 : InlineFrame *frame = &chunk->inlineFrames()[ic.call->inlineIndex];
1509 83 : while (frame && frame->parent)
1510 5 : frame = frame->parent;
1511 39 : return frame->parentpc;
1512 : }
1513 :
1514 1278105 : if (pinline)
1515 1278105 : *pinline = NULL;
1516 1278105 : return script->code + ic.call->pcOffset;
1517 : }
1518 :
1519 : jsbytecode *
1520 12 : mjit::NativeToPC(JITScript *jit, void *ncode, mjit::CallSite **pinline)
1521 : {
1522 12 : return jit->nativeToPC(ncode, pinline);
1523 : }
1524 :
1525 : /* static */ const double mjit::Assembler::oneDouble = 1.0;
|