|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jscompartment.h" |
|
8 |
|
9 #include "assembler/assembler/MacroAssembler.h" |
|
10 #include "jit/arm/BaselineHelpers-arm.h" |
|
11 #include "jit/Bailouts.h" |
|
12 #include "jit/IonFrames.h" |
|
13 #include "jit/IonLinker.h" |
|
14 #include "jit/IonSpewer.h" |
|
15 #include "jit/JitCompartment.h" |
|
16 #ifdef JS_ION_PERF |
|
17 # include "jit/PerfSpewer.h" |
|
18 #endif |
|
19 #include "jit/VMFunctions.h" |
|
20 |
|
21 #include "jit/ExecutionMode-inl.h" |
|
22 |
|
23 using namespace js; |
|
24 using namespace js::jit; |
|
25 |
|
26 static const FloatRegisterSet NonVolatileFloatRegs = |
|
27 FloatRegisterSet((1 << FloatRegisters::d8) | |
|
28 (1 << FloatRegisters::d9) | |
|
29 (1 << FloatRegisters::d10) | |
|
30 (1 << FloatRegisters::d11) | |
|
31 (1 << FloatRegisters::d12) | |
|
32 (1 << FloatRegisters::d13) | |
|
33 (1 << FloatRegisters::d14) | |
|
34 (1 << FloatRegisters::d15)); |
|
35 |
|
36 static void |
|
37 GenerateReturn(MacroAssembler &masm, int returnCode, SPSProfiler *prof) |
|
38 { |
|
39 // Restore non-volatile floating point registers |
|
40 masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA); |
|
41 |
|
42 // Unwind the sps mark. |
|
43 masm.spsUnmarkJit(prof, r8); |
|
44 |
|
45 // Set up return value |
|
46 masm.ma_mov(Imm32(returnCode), r0); |
|
47 |
|
48 // Pop and return |
|
49 masm.startDataTransferM(IsLoad, sp, IA, WriteBack); |
|
50 masm.transferReg(r4); |
|
51 masm.transferReg(r5); |
|
52 masm.transferReg(r6); |
|
53 masm.transferReg(r7); |
|
54 masm.transferReg(r8); |
|
55 masm.transferReg(r9); |
|
56 masm.transferReg(r10); |
|
57 masm.transferReg(r11); |
|
58 // r12 isn't saved, so it shouldn't be restored. |
|
59 masm.transferReg(pc); |
|
60 masm.finishDataTransfer(); |
|
61 masm.dumpPool(); |
|
62 } |
|
63 |
|
64 struct EnterJITStack |
|
65 { |
|
66 double d8; |
|
67 double d9; |
|
68 double d10; |
|
69 double d11; |
|
70 double d12; |
|
71 double d13; |
|
72 double d14; |
|
73 double d15; |
|
74 |
|
75 size_t hasSPSMark; |
|
76 |
|
77 // non-volatile registers. |
|
78 void *r4; |
|
79 void *r5; |
|
80 void *r6; |
|
81 void *r7; |
|
82 void *r8; |
|
83 void *r9; |
|
84 void *r10; |
|
85 void *r11; |
|
86 // The abi does not expect r12 (ip) to be preserved |
|
87 void *lr; |
|
88 |
|
89 // Arguments. |
|
90 // code == r0 |
|
91 // argc == r1 |
|
92 // argv == r2 |
|
93 // frame == r3 |
|
94 CalleeToken token; |
|
95 JSObject *scopeChain; |
|
96 size_t numStackValues; |
|
97 Value *vp; |
|
98 }; |
|
99 |
|
100 /* |
|
101 * This method generates a trampoline for a c++ function with the following |
|
102 * signature: |
|
103 * void enter(void *code, int argc, Value *argv, InterpreterFrame *fp, CalleeToken |
|
104 * calleeToken, JSObject *scopeChain, Value *vp) |
|
105 * ...using standard EABI calling convention |
|
106 */ |
|
107 JitCode * |
|
108 JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) |
|
109 { |
|
110 const Address slot_token(sp, offsetof(EnterJITStack, token)); |
|
111 const Address slot_vp(sp, offsetof(EnterJITStack, vp)); |
|
112 |
|
113 JS_ASSERT(OsrFrameReg == r3); |
|
114 |
|
115 MacroAssembler masm(cx); |
|
116 Assembler *aasm = &masm; |
|
117 |
|
118 // Save non-volatile registers. These must be saved by the trampoline, |
|
119 // rather than the JIT'd code, because they are scanned by the conservative |
|
120 // scanner. |
|
121 masm.startDataTransferM(IsStore, sp, DB, WriteBack); |
|
122 masm.transferReg(r4); // [sp,0] |
|
123 masm.transferReg(r5); // [sp,4] |
|
124 masm.transferReg(r6); // [sp,8] |
|
125 masm.transferReg(r7); // [sp,12] |
|
126 masm.transferReg(r8); // [sp,16] |
|
127 masm.transferReg(r9); // [sp,20] |
|
128 masm.transferReg(r10); // [sp,24] |
|
129 masm.transferReg(r11); // [sp,28] |
|
130 // The abi does not expect r12 (ip) to be preserved |
|
131 masm.transferReg(lr); // [sp,32] |
|
132 // The 5th argument is located at [sp, 36] |
|
133 masm.finishDataTransfer(); |
|
134 |
|
135 // Push the EnterJIT sps mark. "Frame pointer" = start of saved core regs. |
|
136 masm.movePtr(sp, r8); |
|
137 masm.spsMarkJit(&cx->runtime()->spsProfiler, r8, r9); |
|
138 |
|
139 // Push the float registers. |
|
140 masm.transferMultipleByRuns(NonVolatileFloatRegs, IsStore, sp, DB); |
|
141 |
|
142 // Save stack pointer into r8 |
|
143 masm.movePtr(sp, r8); |
|
144 |
|
145 // Load calleeToken into r9. |
|
146 masm.loadPtr(slot_token, r9); |
|
147 |
|
148 // Save stack pointer. |
|
149 if (type == EnterJitBaseline) |
|
150 masm.movePtr(sp, r11); |
|
151 |
|
152 // Load the number of actual arguments into r10. |
|
153 masm.loadPtr(slot_vp, r10); |
|
154 masm.unboxInt32(Address(r10, 0), r10); |
|
155 |
|
156 // Subtract off the size of the arguments from the stack pointer, store elsewhere |
|
157 aasm->as_sub(r4, sp, O2RegImmShift(r1, LSL, 3)); //r4 = sp - argc*8 |
|
158 // Get the final position of the stack pointer into the stack pointer |
|
159 aasm->as_sub(sp, r4, Imm8(16)); // sp' = sp - argc*8 - 16 |
|
160 // Get a copy of the number of args to use as a decrement counter, also |
|
161 // Set the zero condition code |
|
162 aasm->as_mov(r5, O2Reg(r1), SetCond); |
|
163 |
|
164 // Loop over arguments, copying them from an unknown buffer onto the Ion |
|
165 // stack so they can be accessed from JIT'ed code. |
|
166 { |
|
167 Label header, footer; |
|
168 // If there aren't any arguments, don't do anything |
|
169 aasm->as_b(&footer, Assembler::Zero); |
|
170 // Get the top of the loop |
|
171 masm.bind(&header); |
|
172 aasm->as_sub(r5, r5, Imm8(1), SetCond); |
|
173 // We could be more awesome, and unroll this, using a loadm |
|
174 // (particularly since the offset is effectively 0) |
|
175 // but that seems more error prone, and complex. |
|
176 // BIG FAT WARNING: this loads both r6 and r7. |
|
177 aasm->as_extdtr(IsLoad, 64, true, PostIndex, r6, EDtrAddr(r2, EDtrOffImm(8))); |
|
178 aasm->as_extdtr(IsStore, 64, true, PostIndex, r6, EDtrAddr(r4, EDtrOffImm(8))); |
|
179 aasm->as_b(&header, Assembler::NonZero); |
|
180 masm.bind(&footer); |
|
181 } |
|
182 |
|
183 masm.ma_sub(r8, sp, r8); |
|
184 masm.makeFrameDescriptor(r8, JitFrame_Entry); |
|
185 |
|
186 masm.startDataTransferM(IsStore, sp, IB, NoWriteBack); |
|
187 // [sp] = return address (written later) |
|
188 masm.transferReg(r8); // [sp',4] = descriptor, argc*8+20 |
|
189 masm.transferReg(r9); // [sp',8] = callee token |
|
190 masm.transferReg(r10); // [sp',12] = actual arguments |
|
191 masm.finishDataTransfer(); |
|
192 |
|
193 Label returnLabel; |
|
194 if (type == EnterJitBaseline) { |
|
195 // Handle OSR. |
|
196 GeneralRegisterSet regs(GeneralRegisterSet::All()); |
|
197 regs.take(JSReturnOperand); |
|
198 regs.takeUnchecked(OsrFrameReg); |
|
199 regs.take(r11); |
|
200 regs.take(ReturnReg); |
|
201 |
|
202 const Address slot_numStackValues(r11, offsetof(EnterJITStack, numStackValues)); |
|
203 |
|
204 Label notOsr; |
|
205 masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr); |
|
206 |
|
207 Register scratch = regs.takeAny(); |
|
208 |
|
209 Register numStackValues = regs.takeAny(); |
|
210 masm.load32(slot_numStackValues, numStackValues); |
|
211 |
|
212 // Write return address. On ARM, CodeLabel is only used for tableswitch, |
|
213 // so we can't use it here to get the return address. Instead, we use |
|
214 // pc + a fixed offset to a jump to returnLabel. The pc register holds |
|
215 // pc + 8, so we add the size of 2 instructions to skip the instructions |
|
216 // emitted by storePtr and jump(&skipJump). |
|
217 { |
|
218 AutoForbidPools afp(&masm); |
|
219 Label skipJump; |
|
220 masm.mov(pc, scratch); |
|
221 masm.addPtr(Imm32(2 * sizeof(uint32_t)), scratch); |
|
222 masm.storePtr(scratch, Address(sp, 0)); |
|
223 masm.jump(&skipJump); |
|
224 masm.jump(&returnLabel); |
|
225 masm.bind(&skipJump); |
|
226 } |
|
227 |
|
228 // Push previous frame pointer. |
|
229 masm.push(r11); |
|
230 |
|
231 // Reserve frame. |
|
232 Register framePtr = r11; |
|
233 masm.subPtr(Imm32(BaselineFrame::Size()), sp); |
|
234 masm.mov(sp, framePtr); |
|
235 |
|
236 #ifdef XP_WIN |
|
237 // Can't push large frames blindly on windows. Touch frame memory incrementally. |
|
238 masm.ma_lsl(Imm32(3), numStackValues, scratch); |
|
239 masm.subPtr(scratch, framePtr); |
|
240 { |
|
241 masm.ma_sub(sp, Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); |
|
242 |
|
243 Label touchFrameLoop; |
|
244 Label touchFrameLoopEnd; |
|
245 masm.bind(&touchFrameLoop); |
|
246 masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd); |
|
247 masm.store32(Imm32(0), Address(scratch, 0)); |
|
248 masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); |
|
249 masm.jump(&touchFrameLoop); |
|
250 masm.bind(&touchFrameLoopEnd); |
|
251 } |
|
252 masm.mov(sp, framePtr); |
|
253 #endif |
|
254 |
|
255 // Reserve space for locals and stack values. |
|
256 masm.ma_lsl(Imm32(3), numStackValues, scratch); |
|
257 masm.ma_sub(sp, scratch, sp); |
|
258 |
|
259 // Enter exit frame. |
|
260 masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); |
|
261 masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); |
|
262 masm.push(scratch); |
|
263 masm.push(Imm32(0)); // Fake return address. |
|
264 masm.enterFakeExitFrame(); |
|
265 |
|
266 masm.push(framePtr); // BaselineFrame |
|
267 masm.push(r0); // jitcode |
|
268 |
|
269 masm.setupUnalignedABICall(3, scratch); |
|
270 masm.passABIArg(r11); // BaselineFrame |
|
271 masm.passABIArg(OsrFrameReg); // InterpreterFrame |
|
272 masm.passABIArg(numStackValues); |
|
273 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr)); |
|
274 |
|
275 Register jitcode = regs.takeAny(); |
|
276 masm.pop(jitcode); |
|
277 masm.pop(framePtr); |
|
278 |
|
279 JS_ASSERT(jitcode != ReturnReg); |
|
280 |
|
281 Label error; |
|
282 masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), sp); |
|
283 masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); |
|
284 masm.branchIfFalseBool(ReturnReg, &error); |
|
285 |
|
286 masm.jump(jitcode); |
|
287 |
|
288 // OOM: load error value, discard return address and previous frame |
|
289 // pointer and return. |
|
290 masm.bind(&error); |
|
291 masm.mov(framePtr, sp); |
|
292 masm.addPtr(Imm32(2 * sizeof(uintptr_t)), sp); |
|
293 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); |
|
294 masm.jump(&returnLabel); |
|
295 |
|
296 masm.bind(¬Osr); |
|
297 // Load the scope chain in R1. |
|
298 JS_ASSERT(R1.scratchReg() != r0); |
|
299 masm.loadPtr(Address(r11, offsetof(EnterJITStack, scopeChain)), R1.scratchReg()); |
|
300 } |
|
301 |
|
302 // Call the function. |
|
303 masm.ma_callIonNoPush(r0); |
|
304 |
|
305 if (type == EnterJitBaseline) { |
|
306 // Baseline OSR will return here. |
|
307 masm.bind(&returnLabel); |
|
308 } |
|
309 |
|
310 // The top of the stack now points to the address of the field following |
|
311 // the return address because the return address is popped for the |
|
312 // return, so we need to remove the size of the return address field. |
|
313 aasm->as_sub(sp, sp, Imm8(4)); |
|
314 |
|
315 // Load off of the stack the size of our local stack |
|
316 masm.loadPtr(Address(sp, IonJSFrameLayout::offsetOfDescriptor()), r5); |
|
317 aasm->as_add(sp, sp, lsr(r5, FRAMESIZE_SHIFT)); |
|
318 |
|
319 // Store the returned value into the slot_vp |
|
320 masm.loadPtr(slot_vp, r5); |
|
321 masm.storeValue(JSReturnOperand, Address(r5, 0)); |
|
322 |
|
323 // :TODO: Optimize storeValue with: |
|
324 // We're using a load-double here. In order for that to work, |
|
325 // the data needs to be stored in two consecutive registers, |
|
326 // make sure this is the case |
|
327 // ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1); |
|
328 // aasm->as_extdtr(IsStore, 64, true, Offset, |
|
329 // JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0))); |
|
330 |
|
331 // Restore non-volatile registers and return. |
|
332 GenerateReturn(masm, true, &cx->runtime()->spsProfiler); |
|
333 |
|
334 Linker linker(masm); |
|
335 AutoFlushICache afc("EnterJIT"); |
|
336 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
337 |
|
338 #ifdef JS_ION_PERF |
|
339 writePerfSpewerJitCodeProfile(code, "EnterJIT"); |
|
340 #endif |
|
341 |
|
342 return code; |
|
343 } |
|
344 |
|
345 JitCode * |
|
346 JitRuntime::generateInvalidator(JSContext *cx) |
|
347 { |
|
348 // See large comment in x86's JitRuntime::generateInvalidator. |
|
349 MacroAssembler masm(cx); |
|
350 //masm.as_bkpt(); |
|
351 // At this point, one of two things has happened. |
|
352 // 1) Execution has just returned from C code, which left the stack aligned |
|
353 // 2) Execution has just returned from Ion code, which left the stack unaligned. |
|
354 // The old return address should not matter, but we still want the |
|
355 // stack to be aligned, and there is no good reason to automatically align it with |
|
356 // a call to setupUnalignedABICall. |
|
357 masm.ma_and(Imm32(~7), sp, sp); |
|
358 masm.startDataTransferM(IsStore, sp, DB, WriteBack); |
|
359 // We don't have to push everything, but this is likely easier. |
|
360 // setting regs_ |
|
361 for (uint32_t i = 0; i < Registers::Total; i++) |
|
362 masm.transferReg(Register::FromCode(i)); |
|
363 masm.finishDataTransfer(); |
|
364 |
|
365 masm.startFloatTransferM(IsStore, sp, DB, WriteBack); |
|
366 for (uint32_t i = 0; i < FloatRegisters::Total; i++) |
|
367 masm.transferFloatReg(FloatRegister::FromCode(i)); |
|
368 masm.finishFloatTransfer(); |
|
369 |
|
370 masm.ma_mov(sp, r0); |
|
371 const int sizeOfRetval = sizeof(size_t)*2; |
|
372 masm.reserveStack(sizeOfRetval); |
|
373 masm.mov(sp, r1); |
|
374 const int sizeOfBailoutInfo = sizeof(void *)*2; |
|
375 masm.reserveStack(sizeOfBailoutInfo); |
|
376 masm.mov(sp, r2); |
|
377 masm.setupAlignedABICall(3); |
|
378 masm.passABIArg(r0); |
|
379 masm.passABIArg(r1); |
|
380 masm.passABIArg(r2); |
|
381 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); |
|
382 |
|
383 masm.ma_ldr(Address(sp, 0), r2); |
|
384 masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1); |
|
385 // Remove the return address, the IonScript, the register state |
|
386 // (InvaliationBailoutStack) and the space that was allocated for the return value |
|
387 masm.ma_add(sp, Imm32(sizeof(InvalidationBailoutStack) + sizeOfRetval + sizeOfBailoutInfo), sp); |
|
388 // remove the space that this frame was using before the bailout |
|
389 // (computed by InvalidationBailout) |
|
390 masm.ma_add(sp, r1, sp); |
|
391 |
|
392 // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. |
|
393 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); |
|
394 masm.branch(bailoutTail); |
|
395 |
|
396 Linker linker(masm); |
|
397 AutoFlushICache afc("Invalidator"); |
|
398 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
399 IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); |
|
400 |
|
401 #ifdef JS_ION_PERF |
|
402 writePerfSpewerJitCodeProfile(code, "Invalidator"); |
|
403 #endif |
|
404 |
|
405 return code; |
|
406 } |
|
407 |
|
408 JitCode * |
|
409 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) |
|
410 { |
|
411 MacroAssembler masm(cx); |
|
412 // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. |
|
413 // Including |this|, there are (|nargs| + 1) arguments to copy. |
|
414 JS_ASSERT(ArgumentsRectifierReg == r8); |
|
415 |
|
416 // Copy number of actual arguments into r0 |
|
417 masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfNumActualArgs())), r0); |
|
418 |
|
419 // Load the number of |undefined|s to push into r6. |
|
420 masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfCalleeToken())), r1); |
|
421 masm.ma_ldrh(EDtrAddr(r1, EDtrOffImm(JSFunction::offsetOfNargs())), r6); |
|
422 |
|
423 masm.ma_sub(r6, r8, r2); |
|
424 |
|
425 masm.moveValue(UndefinedValue(), r5, r4); |
|
426 |
|
427 masm.ma_mov(sp, r3); // Save %sp. |
|
428 masm.ma_mov(sp, r7); // Save %sp again. |
|
429 |
|
430 // Push undefined. |
|
431 { |
|
432 Label undefLoopTop; |
|
433 masm.bind(&undefLoopTop); |
|
434 masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); |
|
435 masm.ma_sub(r2, Imm32(1), r2, SetCond); |
|
436 |
|
437 masm.ma_b(&undefLoopTop, Assembler::NonZero); |
|
438 } |
|
439 |
|
440 // Get the topmost argument. |
|
441 |
|
442 masm.ma_alu(r3, lsl(r8, 3), r3, op_add); // r3 <- r3 + nargs * 8 |
|
443 masm.ma_add(r3, Imm32(sizeof(IonRectifierFrameLayout)), r3); |
|
444 |
|
445 // Push arguments, |nargs| + 1 times (to include |this|). |
|
446 { |
|
447 Label copyLoopTop; |
|
448 masm.bind(©LoopTop); |
|
449 masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex); |
|
450 masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); |
|
451 |
|
452 masm.ma_sub(r8, Imm32(1), r8, SetCond); |
|
453 masm.ma_b(©LoopTop, Assembler::NotSigned); |
|
454 } |
|
455 |
|
456 // translate the framesize from values into bytes |
|
457 masm.ma_add(r6, Imm32(1), r6); |
|
458 masm.ma_lsl(Imm32(3), r6, r6); |
|
459 |
|
460 // Construct sizeDescriptor. |
|
461 masm.makeFrameDescriptor(r6, JitFrame_Rectifier); |
|
462 |
|
463 // Construct IonJSFrameLayout. |
|
464 masm.ma_push(r0); // actual arguments. |
|
465 masm.ma_push(r1); // callee token |
|
466 masm.ma_push(r6); // frame descriptor. |
|
467 |
|
468 // Call the target function. |
|
469 // Note that this code assumes the function is JITted. |
|
470 masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3); |
|
471 masm.loadBaselineOrIonRaw(r3, r3, mode, nullptr); |
|
472 masm.ma_callIonHalfPush(r3); |
|
473 |
|
474 uint32_t returnOffset = masm.currentOffset(); |
|
475 |
|
476 // arg1 |
|
477 // ... |
|
478 // argN |
|
479 // num actual args |
|
480 // callee token |
|
481 // sizeDescriptor <- sp now |
|
482 // return address |
|
483 |
|
484 // Remove the rectifier frame. |
|
485 masm.ma_dtr(IsLoad, sp, Imm32(12), r4, PostIndex); |
|
486 |
|
487 // arg1 |
|
488 // ... |
|
489 // argN <- sp now; r4 <- frame descriptor |
|
490 // num actual args |
|
491 // callee token |
|
492 // sizeDescriptor |
|
493 // return address |
|
494 |
|
495 // Discard pushed arguments. |
|
496 masm.ma_alu(sp, lsr(r4, FRAMESIZE_SHIFT), sp, op_add); |
|
497 |
|
498 masm.ret(); |
|
499 Linker linker(masm); |
|
500 AutoFlushICache afc("ArgumentsRectifier"); |
|
501 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
502 |
|
503 CodeOffsetLabel returnLabel(returnOffset); |
|
504 returnLabel.fixup(&masm); |
|
505 if (returnAddrOut) |
|
506 *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); |
|
507 |
|
508 #ifdef JS_ION_PERF |
|
509 writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier"); |
|
510 #endif |
|
511 |
|
512 return code; |
|
513 } |
|
514 |
|
515 static void |
|
516 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass) |
|
517 { |
|
518 // the stack should look like: |
|
519 // [IonFrame] |
|
520 // bailoutFrame.registersnapshot |
|
521 // bailoutFrame.fpsnapshot |
|
522 // bailoutFrame.snapshotOffset |
|
523 // bailoutFrame.frameSize |
|
524 |
|
525 // STEP 1a: save our register sets to the stack so Bailout() can |
|
526 // read everything. |
|
527 // sp % 8 == 0 |
|
528 masm.startDataTransferM(IsStore, sp, DB, WriteBack); |
|
529 // We don't have to push everything, but this is likely easier. |
|
530 // setting regs_ |
|
531 for (uint32_t i = 0; i < Registers::Total; i++) |
|
532 masm.transferReg(Register::FromCode(i)); |
|
533 masm.finishDataTransfer(); |
|
534 |
|
535 masm.startFloatTransferM(IsStore, sp, DB, WriteBack); |
|
536 for (uint32_t i = 0; i < FloatRegisters::Total; i++) |
|
537 masm.transferFloatReg(FloatRegister::FromCode(i)); |
|
538 masm.finishFloatTransfer(); |
|
539 |
|
540 // STEP 1b: Push both the "return address" of the function call (the |
|
541 // address of the instruction after the call that we used to get |
|
542 // here) as well as the callee token onto the stack. The return |
|
543 // address is currently in r14. We will proceed by loading the |
|
544 // callee token into a sacrificial register <= r14, then pushing |
|
545 // both onto the stack |
|
546 |
|
547 // now place the frameClass onto the stack, via a register |
|
548 masm.ma_mov(Imm32(frameClass), r4); |
|
549 // And onto the stack. Since the stack is full, we need to put this |
|
550 // one past the end of the current stack. Sadly, the ABI says that we need |
|
551 // to always point to the lowest place that has been written. the OS is |
|
552 // free to do whatever it wants below sp. |
|
553 masm.startDataTransferM(IsStore, sp, DB, WriteBack); |
|
554 // set frameClassId_ |
|
555 masm.transferReg(r4); |
|
556 // Set tableOffset_; higher registers are stored at higher locations on |
|
557 // the stack. |
|
558 masm.transferReg(lr); |
|
559 masm.finishDataTransfer(); |
|
560 |
|
561 // SP % 8 == 4 |
|
562 // STEP 1c: Call the bailout function, giving a pointer to the |
|
563 // structure we just blitted onto the stack |
|
564 masm.ma_mov(sp, r0); |
|
565 const int sizeOfBailoutInfo = sizeof(void *)*2; |
|
566 masm.reserveStack(sizeOfBailoutInfo); |
|
567 masm.mov(sp, r1); |
|
568 masm.setupAlignedABICall(2); |
|
569 |
|
570 // Decrement sp by another 4, so we keep alignment |
|
571 // Not Anymore! pushing both the snapshotoffset as well as the |
|
572 // masm.as_sub(sp, sp, Imm8(4)); |
|
573 |
|
574 // Set the old (4-byte aligned) value of the sp as the first argument |
|
575 masm.passABIArg(r0); |
|
576 masm.passABIArg(r1); |
|
577 |
|
578 // Sp % 8 == 0 |
|
579 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); |
|
580 masm.ma_ldr(Address(sp, 0), r2); |
|
581 masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp); |
|
582 // Common size of a bailout frame. |
|
583 uint32_t bailoutFrameSize = sizeof(void *) + // frameClass |
|
584 sizeof(double) * FloatRegisters::Total + |
|
585 sizeof(void *) * Registers::Total; |
|
586 |
|
587 if (frameClass == NO_FRAME_SIZE_CLASS_ID) { |
|
588 // Make sure the bailout frame size fits into the offset for a load |
|
589 masm.as_dtr(IsLoad, 32, Offset, |
|
590 r4, DTRAddr(sp, DtrOffImm(4))); |
|
591 // used to be: offsetof(BailoutStack, frameSize_) |
|
592 // this structure is no longer available to us :( |
|
593 // We add 12 to the bailoutFrameSize because: |
|
594 // sizeof(uint32_t) for the tableOffset that was pushed onto the stack |
|
595 // sizeof(uintptr_t) for the snapshotOffset; |
|
596 // alignment to round the uintptr_t up to a multiple of 8 bytes. |
|
597 masm.ma_add(sp, Imm32(bailoutFrameSize+12), sp); |
|
598 masm.as_add(sp, sp, O2Reg(r4)); |
|
599 } else { |
|
600 uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize(); |
|
601 masm.ma_add(Imm32(frameSize // the frame that was added when we entered the most recent function |
|
602 + sizeof(void*) // the size of the "return address" that was dumped on the stack |
|
603 + bailoutFrameSize) // everything else that was pushed on the stack |
|
604 , sp); |
|
605 } |
|
606 |
|
607 // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. |
|
608 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); |
|
609 masm.branch(bailoutTail); |
|
610 } |
|
611 |
|
612 JitCode * |
|
613 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass) |
|
614 { |
|
615 MacroAssembler masm(cx); |
|
616 |
|
617 Label bailout; |
|
618 for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) |
|
619 masm.ma_bl(&bailout); |
|
620 masm.bind(&bailout); |
|
621 |
|
622 GenerateBailoutThunk(cx, masm, frameClass); |
|
623 |
|
624 Linker linker(masm); |
|
625 AutoFlushICache afc("BailoutTable"); |
|
626 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
627 |
|
628 #ifdef JS_ION_PERF |
|
629 writePerfSpewerJitCodeProfile(code, "BailoutTable"); |
|
630 #endif |
|
631 |
|
632 return code; |
|
633 } |
|
634 |
|
635 JitCode * |
|
636 JitRuntime::generateBailoutHandler(JSContext *cx) |
|
637 { |
|
638 MacroAssembler masm(cx); |
|
639 GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); |
|
640 |
|
641 Linker linker(masm); |
|
642 AutoFlushICache afc("BailoutHandler"); |
|
643 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
644 |
|
645 #ifdef JS_ION_PERF |
|
646 writePerfSpewerJitCodeProfile(code, "BailoutHandler"); |
|
647 #endif |
|
648 |
|
649 return code; |
|
650 } |
|
651 |
|
652 JitCode * |
|
653 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) |
|
654 { |
|
655 JS_ASSERT(functionWrappers_); |
|
656 JS_ASSERT(functionWrappers_->initialized()); |
|
657 VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); |
|
658 if (p) |
|
659 return p->value(); |
|
660 |
|
661 // Generate a separated code for the wrapper. |
|
662 MacroAssembler masm(cx); |
|
663 GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask); |
|
664 |
|
665 // Wrapper register set is a superset of Volatile register set. |
|
666 JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0); |
|
667 |
|
668 // The context is the first argument; r0 is the first argument register. |
|
669 Register cxreg = r0; |
|
670 regs.take(cxreg); |
|
671 |
|
672 // Stack is: |
|
673 // ... frame ... |
|
674 // +8 [args] + argPadding |
|
675 // +0 ExitFrame |
|
676 // |
|
677 // We're aligned to an exit frame, so link it up. |
|
678 masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode); |
|
679 |
|
680 // Save the base of the argument set stored on the stack. |
|
681 Register argsBase = InvalidReg; |
|
682 if (f.explicitArgs) { |
|
683 argsBase = r5; |
|
684 regs.take(argsBase); |
|
685 masm.ma_add(sp, Imm32(IonExitFrameLayout::SizeWithFooter()), argsBase); |
|
686 } |
|
687 |
|
688 // Reserve space for the outparameter. |
|
689 Register outReg = InvalidReg; |
|
690 switch (f.outParam) { |
|
691 case Type_Value: |
|
692 outReg = r4; |
|
693 regs.take(outReg); |
|
694 masm.reserveStack(sizeof(Value)); |
|
695 masm.ma_mov(sp, outReg); |
|
696 break; |
|
697 |
|
698 case Type_Handle: |
|
699 outReg = r4; |
|
700 regs.take(outReg); |
|
701 masm.PushEmptyRooted(f.outParamRootType); |
|
702 masm.ma_mov(sp, outReg); |
|
703 break; |
|
704 |
|
705 case Type_Int32: |
|
706 case Type_Pointer: |
|
707 case Type_Bool: |
|
708 outReg = r4; |
|
709 regs.take(outReg); |
|
710 masm.reserveStack(sizeof(int32_t)); |
|
711 masm.ma_mov(sp, outReg); |
|
712 break; |
|
713 |
|
714 case Type_Double: |
|
715 outReg = r4; |
|
716 regs.take(outReg); |
|
717 masm.reserveStack(sizeof(double)); |
|
718 masm.ma_mov(sp, outReg); |
|
719 break; |
|
720 |
|
721 default: |
|
722 JS_ASSERT(f.outParam == Type_Void); |
|
723 break; |
|
724 } |
|
725 |
|
726 masm.setupUnalignedABICall(f.argc(), regs.getAny()); |
|
727 masm.passABIArg(cxreg); |
|
728 |
|
729 size_t argDisp = 0; |
|
730 |
|
731 // Copy any arguments. |
|
732 for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { |
|
733 MoveOperand from; |
|
734 switch (f.argProperties(explicitArg)) { |
|
735 case VMFunction::WordByValue: |
|
736 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); |
|
737 argDisp += sizeof(void *); |
|
738 break; |
|
739 case VMFunction::DoubleByValue: |
|
740 // Values should be passed by reference, not by value, so we |
|
741 // assert that the argument is a double-precision float. |
|
742 JS_ASSERT(f.argPassedInFloatReg(explicitArg)); |
|
743 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); |
|
744 argDisp += sizeof(double); |
|
745 break; |
|
746 case VMFunction::WordByRef: |
|
747 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL); |
|
748 argDisp += sizeof(void *); |
|
749 break; |
|
750 case VMFunction::DoubleByRef: |
|
751 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL); |
|
752 argDisp += 2 * sizeof(void *); |
|
753 break; |
|
754 } |
|
755 } |
|
756 |
|
757 // Copy the implicit outparam, if any. |
|
758 if (outReg != InvalidReg) |
|
759 masm.passABIArg(outReg); |
|
760 |
|
761 masm.callWithABI(f.wrapped); |
|
762 |
|
763 // Test for failure. |
|
764 switch (f.failType()) { |
|
765 case Type_Object: |
|
766 masm.branchTestPtr(Assembler::Zero, r0, r0, masm.failureLabel(f.executionMode)); |
|
767 break; |
|
768 case Type_Bool: |
|
769 masm.branchIfFalseBool(r0, masm.failureLabel(f.executionMode)); |
|
770 break; |
|
771 default: |
|
772 MOZ_ASSUME_UNREACHABLE("unknown failure kind"); |
|
773 } |
|
774 |
|
775 // Load the outparam and free any allocated stack. |
|
776 switch (f.outParam) { |
|
777 case Type_Handle: |
|
778 masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); |
|
779 break; |
|
780 |
|
781 case Type_Value: |
|
782 masm.loadValue(Address(sp, 0), JSReturnOperand); |
|
783 masm.freeStack(sizeof(Value)); |
|
784 break; |
|
785 |
|
786 case Type_Int32: |
|
787 case Type_Pointer: |
|
788 masm.load32(Address(sp, 0), ReturnReg); |
|
789 masm.freeStack(sizeof(int32_t)); |
|
790 break; |
|
791 |
|
792 case Type_Bool: |
|
793 masm.load8ZeroExtend(Address(sp, 0), ReturnReg); |
|
794 masm.freeStack(sizeof(int32_t)); |
|
795 break; |
|
796 |
|
797 case Type_Double: |
|
798 if (cx->runtime()->jitSupportsFloatingPoint) |
|
799 masm.loadDouble(Address(sp, 0), ReturnFloatReg); |
|
800 else |
|
801 masm.assumeUnreachable("Unable to load into float reg, with no FP support."); |
|
802 masm.freeStack(sizeof(double)); |
|
803 break; |
|
804 |
|
805 default: |
|
806 JS_ASSERT(f.outParam == Type_Void); |
|
807 break; |
|
808 } |
|
809 masm.leaveExitFrame(); |
|
810 masm.retn(Imm32(sizeof(IonExitFrameLayout) + |
|
811 f.explicitStackSlots() * sizeof(void *) + |
|
812 f.extraValuesToPop * sizeof(Value))); |
|
813 |
|
814 Linker linker(masm); |
|
815 AutoFlushICache afc("VMWrapper"); |
|
816 JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
817 if (!wrapper) |
|
818 return nullptr; |
|
819 |
|
820 // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to |
|
821 // use relookupOrAdd instead of add. |
|
822 if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) |
|
823 return nullptr; |
|
824 |
|
825 #ifdef JS_ION_PERF |
|
826 writePerfSpewerJitCodeProfile(wrapper, "VMWrapper"); |
|
827 #endif |
|
828 |
|
829 return wrapper; |
|
830 } |
|
831 |
|
832 JitCode * |
|
833 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type) |
|
834 { |
|
835 MacroAssembler masm(cx); |
|
836 |
|
837 RegisterSet save; |
|
838 if (cx->runtime()->jitSupportsFloatingPoint) { |
|
839 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), |
|
840 FloatRegisterSet(FloatRegisters::VolatileMask)); |
|
841 } else { |
|
842 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), |
|
843 FloatRegisterSet()); |
|
844 } |
|
845 masm.PushRegsInMask(save); |
|
846 |
|
847 JS_ASSERT(PreBarrierReg == r1); |
|
848 masm.movePtr(ImmPtr(cx->runtime()), r0); |
|
849 |
|
850 masm.setupUnalignedABICall(2, r2); |
|
851 masm.passABIArg(r0); |
|
852 masm.passABIArg(r1); |
|
853 if (type == MIRType_Value) { |
|
854 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon)); |
|
855 } else { |
|
856 JS_ASSERT(type == MIRType_Shape); |
|
857 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); |
|
858 } |
|
859 |
|
860 masm.PopRegsInMask(save); |
|
861 masm.ret(); |
|
862 |
|
863 Linker linker(masm); |
|
864 AutoFlushICache afc("PreBarrier"); |
|
865 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
866 |
|
867 #ifdef JS_ION_PERF |
|
868 writePerfSpewerJitCodeProfile(code, "PreBarrier"); |
|
869 #endif |
|
870 |
|
871 return code; |
|
872 } |
|
873 |
|
874 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); |
|
875 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap); |
|
876 |
|
877 JitCode * |
|
878 JitRuntime::generateDebugTrapHandler(JSContext *cx) |
|
879 { |
|
880 MacroAssembler masm; |
|
881 |
|
882 Register scratch1 = r0; |
|
883 Register scratch2 = r1; |
|
884 |
|
885 // Load BaselineFrame pointer in scratch1. |
|
886 masm.mov(r11, scratch1); |
|
887 masm.subPtr(Imm32(BaselineFrame::Size()), scratch1); |
|
888 |
|
889 // Enter a stub frame and call the HandleDebugTrap VM function. Ensure |
|
890 // the stub frame has a nullptr ICStub pointer, since this pointer is |
|
891 // marked during GC. |
|
892 masm.movePtr(ImmPtr(nullptr), BaselineStubReg); |
|
893 EmitEnterStubFrame(masm, scratch2); |
|
894 |
|
895 JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo); |
|
896 if (!code) |
|
897 return nullptr; |
|
898 |
|
899 masm.push(lr); |
|
900 masm.push(scratch1); |
|
901 EmitCallVM(code, masm); |
|
902 |
|
903 EmitLeaveStubFrame(masm); |
|
904 |
|
905 // If the stub returns |true|, we have to perform a forced return |
|
906 // (return from the JS frame). If the stub returns |false|, just return |
|
907 // from the trap stub so that execution continues at the current pc. |
|
908 Label forcedReturn; |
|
909 masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn); |
|
910 masm.mov(lr, pc); |
|
911 |
|
912 masm.bind(&forcedReturn); |
|
913 masm.loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), |
|
914 JSReturnOperand); |
|
915 masm.mov(r11, sp); |
|
916 masm.pop(r11); |
|
917 masm.ret(); |
|
918 |
|
919 Linker linker(masm); |
|
920 AutoFlushICache afc("DebugTrapHandler"); |
|
921 JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
922 |
|
923 #ifdef JS_ION_PERF |
|
924 writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler"); |
|
925 #endif |
|
926 |
|
927 return codeDbg; |
|
928 } |
|
929 |
|
930 JitCode * |
|
931 JitRuntime::generateExceptionTailStub(JSContext *cx) |
|
932 { |
|
933 MacroAssembler masm; |
|
934 |
|
935 masm.handleFailureWithHandlerTail(); |
|
936 |
|
937 Linker linker(masm); |
|
938 AutoFlushICache afc("ExceptionTailStub"); |
|
939 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
940 |
|
941 #ifdef JS_ION_PERF |
|
942 writePerfSpewerJitCodeProfile(code, "ExceptionTailStub"); |
|
943 #endif |
|
944 |
|
945 return code; |
|
946 } |
|
947 |
|
948 JitCode * |
|
949 JitRuntime::generateBailoutTailStub(JSContext *cx) |
|
950 { |
|
951 MacroAssembler masm; |
|
952 |
|
953 masm.generateBailoutTail(r1, r2); |
|
954 |
|
955 Linker linker(masm); |
|
956 AutoFlushICache afc("BailoutTailStub"); |
|
957 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE); |
|
958 |
|
959 #ifdef JS_ION_PERF |
|
960 writePerfSpewerJitCodeProfile(code, "BailoutTailStub"); |
|
961 #endif |
|
962 |
|
963 return code; |
|
964 } |