js/src/jit/x86/Trampoline-x86.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jscompartment.h"
michael@0 8
michael@0 9 #include "assembler/assembler/MacroAssembler.h"
michael@0 10 #include "jit/Bailouts.h"
michael@0 11 #include "jit/BaselineJIT.h"
michael@0 12 #include "jit/IonFrames.h"
michael@0 13 #include "jit/IonLinker.h"
michael@0 14 #include "jit/IonSpewer.h"
michael@0 15 #include "jit/JitCompartment.h"
michael@0 16 #ifdef JS_ION_PERF
michael@0 17 # include "jit/PerfSpewer.h"
michael@0 18 #endif
michael@0 19 #include "jit/VMFunctions.h"
michael@0 20 #include "jit/x86/BaselineHelpers-x86.h"
michael@0 21
michael@0 22 #include "jsscriptinlines.h"
michael@0 23
michael@0 24 #include "jit/ExecutionMode-inl.h"
michael@0 25
michael@0 26 using namespace js;
michael@0 27 using namespace js::jit;
michael@0 28
michael@0 29 // All registers to save and restore. This includes the stack pointer, since we
michael@0 30 // use the ability to reference register values on the stack by index.
michael@0 31 static const RegisterSet AllRegs =
michael@0 32 RegisterSet(GeneralRegisterSet(Registers::AllMask),
michael@0 33 FloatRegisterSet(FloatRegisters::AllMask));
michael@0 34
michael@0 35 enum EnterJitEbpArgumentOffset {
michael@0 36 ARG_JITCODE = 2 * sizeof(void *),
michael@0 37 ARG_ARGC = 3 * sizeof(void *),
michael@0 38 ARG_ARGV = 4 * sizeof(void *),
michael@0 39 ARG_STACKFRAME = 5 * sizeof(void *),
michael@0 40 ARG_CALLEETOKEN = 6 * sizeof(void *),
michael@0 41 ARG_SCOPECHAIN = 7 * sizeof(void *),
michael@0 42 ARG_STACKVALUES = 8 * sizeof(void *),
michael@0 43 ARG_RESULT = 9 * sizeof(void *)
michael@0 44 };
michael@0 45
michael@0 46 /*
michael@0 47 * Generates a trampoline for a C++ function with the EnterJitCode signature,
michael@0 48 * using the standard cdecl calling convention.
michael@0 49 */
michael@0 50 JitCode *
michael@0 51 JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
michael@0 52 {
michael@0 53 MacroAssembler masm(cx);
michael@0 54
michael@0 55 // Save old stack frame pointer, set new stack frame pointer.
michael@0 56 masm.push(ebp);
michael@0 57 masm.movl(esp, ebp);
michael@0 58
michael@0 59 // Save non-volatile registers. These must be saved by the trampoline,
michael@0 60 // rather than the JIT'd code, because they are scanned by the conservative
michael@0 61 // scanner.
michael@0 62 masm.push(ebx);
michael@0 63 masm.push(esi);
michael@0 64 masm.push(edi);
michael@0 65
michael@0 66 // Push the EnterJIT sps mark.
michael@0 67 masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx);
michael@0 68
michael@0 69 // Keep track of the stack which has to be unwound after returning from the
michael@0 70 // compiled function.
michael@0 71 masm.movl(esp, esi);
michael@0 72
michael@0 73 // eax <- 8*argc, eax is now the offset betwen argv and the last
michael@0 74 masm.loadPtr(Address(ebp, ARG_ARGC), eax);
michael@0 75 masm.shll(Imm32(3), eax);
michael@0 76
michael@0 77 // We need to ensure that the stack is aligned on a 12-byte boundary, so
michael@0 78 // inside the JIT function the stack is 16-byte aligned. Our stack right
michael@0 79 // now might not be aligned on some platforms (win32, gcc) so we factor
michael@0 80 // this possibility in, and simulate what the new stack address would be.
michael@0 81 // +argc * 8 for arguments
michael@0 82 // +4 for pushing alignment
michael@0 83 // +4 for pushing the callee token
michael@0 84 // +4 for pushing the return address
michael@0 85 masm.movl(esp, ecx);
michael@0 86 masm.subl(eax, ecx);
michael@0 87 masm.subl(Imm32(4 * 3), ecx);
michael@0 88
michael@0 89 // ecx = ecx & 15, holds alignment.
michael@0 90 masm.andl(Imm32(15), ecx);
michael@0 91 masm.subl(ecx, esp);
michael@0 92
michael@0 93 /***************************************************************
michael@0 94 Loop over argv vector, push arguments onto stack in reverse order
michael@0 95 ***************************************************************/
michael@0 96
michael@0 97 // ebx = argv --argv pointer is in ebp + 16
michael@0 98 masm.loadPtr(Address(ebp, ARG_ARGV), ebx);
michael@0 99
michael@0 100 // eax = argv[8(argc)] --eax now points one value past the last argument
michael@0 101 masm.addl(ebx, eax);
michael@0 102
michael@0 103 // while (eax > ebx) --while still looping through arguments
michael@0 104 {
michael@0 105 Label header, footer;
michael@0 106 masm.bind(&header);
michael@0 107
michael@0 108 masm.cmpl(eax, ebx);
michael@0 109 masm.j(Assembler::BelowOrEqual, &footer);
michael@0 110
michael@0 111 // eax -= 8 --move to previous argument
michael@0 112 masm.subl(Imm32(8), eax);
michael@0 113
michael@0 114 // Push what eax points to on stack, a Value is 2 words
michael@0 115 masm.push(Operand(eax, 4));
michael@0 116 masm.push(Operand(eax, 0));
michael@0 117
michael@0 118 masm.jmp(&header);
michael@0 119 masm.bind(&footer);
michael@0 120 }
michael@0 121
michael@0 122
michael@0 123 // Push the number of actual arguments. |result| is used to store the
michael@0 124 // actual number of arguments without adding an extra argument to the enter
michael@0 125 // JIT.
michael@0 126 masm.mov(Operand(ebp, ARG_RESULT), eax);
michael@0 127 masm.unboxInt32(Address(eax, 0x0), eax);
michael@0 128 masm.push(eax);
michael@0 129
michael@0 130 // Push the callee token.
michael@0 131 masm.push(Operand(ebp, ARG_CALLEETOKEN));
michael@0 132
michael@0 133 // Load the InterpreterFrame address into the OsrFrameReg.
michael@0 134 // This address is also used for setting the constructing bit on all paths.
michael@0 135 masm.loadPtr(Address(ebp, ARG_STACKFRAME), OsrFrameReg);
michael@0 136
michael@0 137 /*****************************************************************
michael@0 138 Push the number of bytes we've pushed so far on the stack and call
michael@0 139 *****************************************************************/
michael@0 140 // Create a frame descriptor.
michael@0 141 masm.subl(esp, esi);
michael@0 142 masm.makeFrameDescriptor(esi, JitFrame_Entry);
michael@0 143 masm.push(esi);
michael@0 144
michael@0 145 CodeLabel returnLabel;
michael@0 146 if (type == EnterJitBaseline) {
michael@0 147 // Handle OSR.
michael@0 148 GeneralRegisterSet regs(GeneralRegisterSet::All());
michael@0 149 regs.take(JSReturnOperand);
michael@0 150 regs.takeUnchecked(OsrFrameReg);
michael@0 151 regs.take(ebp);
michael@0 152 regs.take(ReturnReg);
michael@0 153
michael@0 154 Register scratch = regs.takeAny();
michael@0 155
michael@0 156 Label notOsr;
michael@0 157 masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, &notOsr);
michael@0 158
michael@0 159 Register numStackValues = regs.takeAny();
michael@0 160 masm.loadPtr(Address(ebp, ARG_STACKVALUES), numStackValues);
michael@0 161
michael@0 162 Register jitcode = regs.takeAny();
michael@0 163 masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode);
michael@0 164
michael@0 165 // Push return address, previous frame pointer.
michael@0 166 masm.mov(returnLabel.dest(), scratch);
michael@0 167 masm.push(scratch);
michael@0 168 masm.push(ebp);
michael@0 169
michael@0 170 // Reserve frame.
michael@0 171 Register framePtr = ebp;
michael@0 172 masm.subPtr(Imm32(BaselineFrame::Size()), esp);
michael@0 173 masm.mov(esp, framePtr);
michael@0 174
michael@0 175 #ifdef XP_WIN
michael@0 176 // Can't push large frames blindly on windows. Touch frame memory incrementally.
michael@0 177 masm.mov(numStackValues, scratch);
michael@0 178 masm.shll(Imm32(3), scratch);
michael@0 179 masm.subPtr(scratch, framePtr);
michael@0 180 {
michael@0 181 masm.movePtr(esp, scratch);
michael@0 182 masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
michael@0 183
michael@0 184 Label touchFrameLoop;
michael@0 185 Label touchFrameLoopEnd;
michael@0 186 masm.bind(&touchFrameLoop);
michael@0 187 masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd);
michael@0 188 masm.store32(Imm32(0), Address(scratch, 0));
michael@0 189 masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
michael@0 190 masm.jump(&touchFrameLoop);
michael@0 191 masm.bind(&touchFrameLoopEnd);
michael@0 192 }
michael@0 193 masm.mov(esp, framePtr);
michael@0 194 #endif
michael@0 195
michael@0 196 // Reserve space for locals and stack values.
michael@0 197 masm.mov(numStackValues, scratch);
michael@0 198 masm.shll(Imm32(3), scratch);
michael@0 199 masm.subPtr(scratch, esp);
michael@0 200
michael@0 201 // Enter exit frame.
michael@0 202 masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
michael@0 203 masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
michael@0 204 masm.push(scratch);
michael@0 205 masm.push(Imm32(0)); // Fake return address.
michael@0 206 masm.enterFakeExitFrame();
michael@0 207
michael@0 208 masm.push(framePtr);
michael@0 209 masm.push(jitcode);
michael@0 210
michael@0 211 masm.setupUnalignedABICall(3, scratch);
michael@0 212 masm.passABIArg(framePtr); // BaselineFrame
michael@0 213 masm.passABIArg(OsrFrameReg); // InterpreterFrame
michael@0 214 masm.passABIArg(numStackValues);
michael@0 215 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr));
michael@0 216
michael@0 217 masm.pop(jitcode);
michael@0 218 masm.pop(framePtr);
michael@0 219
michael@0 220 JS_ASSERT(jitcode != ReturnReg);
michael@0 221
michael@0 222 Label error;
michael@0 223 masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), esp);
michael@0 224 masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
michael@0 225 masm.branchIfFalseBool(ReturnReg, &error);
michael@0 226
michael@0 227 masm.jump(jitcode);
michael@0 228
michael@0 229 // OOM: load error value, discard return address and previous frame
michael@0 230 // pointer and return.
michael@0 231 masm.bind(&error);
michael@0 232 masm.mov(framePtr, esp);
michael@0 233 masm.addPtr(Imm32(2 * sizeof(uintptr_t)), esp);
michael@0 234 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
michael@0 235 masm.mov(returnLabel.dest(), scratch);
michael@0 236 masm.jump(scratch);
michael@0 237
michael@0 238 masm.bind(&notOsr);
michael@0 239 masm.loadPtr(Address(ebp, ARG_SCOPECHAIN), R1.scratchReg());
michael@0 240 }
michael@0 241
michael@0 242 /***************************************************************
michael@0 243 Call passed-in code, get return value and fill in the
michael@0 244 passed in return value pointer
michael@0 245 ***************************************************************/
michael@0 246 masm.call(Operand(ebp, ARG_JITCODE));
michael@0 247
michael@0 248 if (type == EnterJitBaseline) {
michael@0 249 // Baseline OSR will return here.
michael@0 250 masm.bind(returnLabel.src());
michael@0 251 if (!masm.addCodeLabel(returnLabel))
michael@0 252 return nullptr;
michael@0 253 }
michael@0 254
michael@0 255 // Pop arguments off the stack.
michael@0 256 // eax <- 8*argc (size of all arguments we pushed on the stack)
michael@0 257 masm.pop(eax);
michael@0 258 masm.shrl(Imm32(FRAMESIZE_SHIFT), eax); // Unmark EntryFrame.
michael@0 259 masm.addl(eax, esp);
michael@0 260
michael@0 261 // |ebp| could have been clobbered by the inner function.
michael@0 262 // Grab the address for the Value result from the argument stack.
michael@0 263 // +24 ... arguments ...
michael@0 264 // +20 <return>
michael@0 265 // +16 ebp <- original %ebp pointing here.
michael@0 266 // +12 ebx
michael@0 267 // +8 esi
michael@0 268 // +4 edi
michael@0 269 // +0 hasSPSFrame
michael@0 270 masm.loadPtr(Address(esp, ARG_RESULT + 4 * sizeof(void *)), eax);
michael@0 271 masm.storeValue(JSReturnOperand, Operand(eax, 0));
michael@0 272
michael@0 273 /**************************************************************
michael@0 274 Return stack and registers to correct state
michael@0 275 **************************************************************/
michael@0 276 // Unwind the sps mark.
michael@0 277 masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx);
michael@0 278
michael@0 279 // Restore non-volatile registers
michael@0 280 masm.pop(edi);
michael@0 281 masm.pop(esi);
michael@0 282 masm.pop(ebx);
michael@0 283
michael@0 284 // Restore old stack frame pointer
michael@0 285 masm.pop(ebp);
michael@0 286 masm.ret();
michael@0 287
michael@0 288 Linker linker(masm);
michael@0 289 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 290
michael@0 291 #ifdef JS_ION_PERF
michael@0 292 writePerfSpewerJitCodeProfile(code, "EnterJIT");
michael@0 293 #endif
michael@0 294
michael@0 295 return code;
michael@0 296 }
michael@0 297
michael@0 298 JitCode *
michael@0 299 JitRuntime::generateInvalidator(JSContext *cx)
michael@0 300 {
michael@0 301 AutoIonContextAlloc aica(cx);
michael@0 302 MacroAssembler masm(cx);
michael@0 303
michael@0 304 // We do the minimum amount of work in assembly and shunt the rest
michael@0 305 // off to InvalidationBailout. Assembly does:
michael@0 306 //
michael@0 307 // - Pop the return address from the invalidation epilogue call.
michael@0 308 // - Push the machine state onto the stack.
michael@0 309 // - Call the InvalidationBailout routine with the stack pointer.
michael@0 310 // - Now that the frame has been bailed out, convert the invalidated
michael@0 311 // frame into an exit frame.
michael@0 312 // - Do the normal check-return-code-and-thunk-to-the-interpreter dance.
michael@0 313
michael@0 314 masm.addl(Imm32(sizeof(uintptr_t)), esp);
michael@0 315
michael@0 316 // Push registers such that we can access them from [base + code].
michael@0 317 masm.PushRegsInMask(AllRegs);
michael@0 318
michael@0 319 masm.movl(esp, eax); // Argument to jit::InvalidationBailout.
michael@0 320
michael@0 321 // Make space for InvalidationBailout's frameSize outparam.
michael@0 322 masm.reserveStack(sizeof(size_t));
michael@0 323 masm.movl(esp, ebx);
michael@0 324
michael@0 325 // Make space for InvalidationBailout's bailoutInfo outparam.
michael@0 326 masm.reserveStack(sizeof(void *));
michael@0 327 masm.movl(esp, ecx);
michael@0 328
michael@0 329 masm.setupUnalignedABICall(3, edx);
michael@0 330 masm.passABIArg(eax);
michael@0 331 masm.passABIArg(ebx);
michael@0 332 masm.passABIArg(ecx);
michael@0 333 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout));
michael@0 334
michael@0 335 masm.pop(ecx); // Get bailoutInfo outparam.
michael@0 336 masm.pop(ebx); // Get the frameSize outparam.
michael@0 337
michael@0 338 // Pop the machine state and the dead frame.
michael@0 339 masm.lea(Operand(esp, ebx, TimesOne, sizeof(InvalidationBailoutStack)), esp);
michael@0 340
michael@0 341 // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
michael@0 342 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
michael@0 343 masm.jmp(bailoutTail);
michael@0 344
michael@0 345 Linker linker(masm);
michael@0 346 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 347 IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw());
michael@0 348
michael@0 349 #ifdef JS_ION_PERF
michael@0 350 writePerfSpewerJitCodeProfile(code, "Invalidator");
michael@0 351 #endif
michael@0 352
michael@0 353 return code;
michael@0 354 }
michael@0 355
michael@0 356 JitCode *
michael@0 357 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
michael@0 358 {
michael@0 359 MacroAssembler masm(cx);
michael@0 360
michael@0 361 // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
michael@0 362 // Including |this|, there are (|nargs| + 1) arguments to copy.
michael@0 363 JS_ASSERT(ArgumentsRectifierReg == esi);
michael@0 364
michael@0 365 // Load the number of |undefined|s to push into %ecx.
michael@0 366 masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax);
michael@0 367 masm.movzwl(Operand(eax, JSFunction::offsetOfNargs()), ecx);
michael@0 368 masm.subl(esi, ecx);
michael@0 369
michael@0 370 // Copy the number of actual arguments.
michael@0 371 masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx);
michael@0 372
michael@0 373 masm.moveValue(UndefinedValue(), ebx, edi);
michael@0 374
michael@0 375 // NOTE: The fact that x86 ArgumentsRectifier saves the FramePointer is relied upon
michael@0 376 // by the baseline bailout code. If this changes, fix that code! See
michael@0 377 // BaselineJIT.cpp/BaselineStackBuilder::calculatePrevFramePtr, and
michael@0 378 // BaselineJIT.cpp/InitFromBailout. Check for the |#if defined(JS_CODEGEN_X86)| portions.
michael@0 379 masm.push(FramePointer);
michael@0 380 masm.movl(esp, FramePointer); // Save %esp.
michael@0 381
michael@0 382 // Push undefined.
michael@0 383 {
michael@0 384 Label undefLoopTop;
michael@0 385 masm.bind(&undefLoopTop);
michael@0 386
michael@0 387 masm.push(ebx); // type(undefined);
michael@0 388 masm.push(edi); // payload(undefined);
michael@0 389 masm.subl(Imm32(1), ecx);
michael@0 390 masm.j(Assembler::NonZero, &undefLoopTop);
michael@0 391 }
michael@0 392
michael@0 393 // Get the topmost argument. We did a push of %ebp earlier, so be sure to
michael@0 394 // account for this in the offset
michael@0 395 BaseIndex b = BaseIndex(FramePointer, esi, TimesEight,
michael@0 396 sizeof(IonRectifierFrameLayout) + sizeof(void*));
michael@0 397 masm.lea(Operand(b), ecx);
michael@0 398
michael@0 399 // Push arguments, |nargs| + 1 times (to include |this|).
michael@0 400 masm.addl(Imm32(1), esi);
michael@0 401 {
michael@0 402 Label copyLoopTop;
michael@0 403
michael@0 404 masm.bind(&copyLoopTop);
michael@0 405 masm.push(Operand(ecx, sizeof(Value)/2));
michael@0 406 masm.push(Operand(ecx, 0x0));
michael@0 407 masm.subl(Imm32(sizeof(Value)), ecx);
michael@0 408 masm.subl(Imm32(1), esi);
michael@0 409 masm.j(Assembler::NonZero, &copyLoopTop);
michael@0 410 }
michael@0 411
michael@0 412 // Construct descriptor, accounting for pushed frame pointer above
michael@0 413 masm.lea(Operand(FramePointer, sizeof(void*)), ebx);
michael@0 414 masm.subl(esp, ebx);
michael@0 415 masm.makeFrameDescriptor(ebx, JitFrame_Rectifier);
michael@0 416
michael@0 417 // Construct IonJSFrameLayout.
michael@0 418 masm.push(edx); // number of actual arguments
michael@0 419 masm.push(eax); // callee token
michael@0 420 masm.push(ebx); // descriptor
michael@0 421
michael@0 422 // Call the target function.
michael@0 423 // Note that this assumes the function is JITted.
michael@0 424 masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax);
michael@0 425 masm.loadBaselineOrIonRaw(eax, eax, mode, nullptr);
michael@0 426 masm.call(eax);
michael@0 427 uint32_t returnOffset = masm.currentOffset();
michael@0 428
michael@0 429 // Remove the rectifier frame.
michael@0 430 masm.pop(ebx); // ebx <- descriptor with FrameType.
michael@0 431 masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
michael@0 432 masm.pop(edi); // Discard calleeToken.
michael@0 433 masm.pop(edi); // Discard number of actual arguments.
michael@0 434
michael@0 435 // Discard pushed arguments, but not the pushed frame pointer.
michael@0 436 BaseIndex unwind = BaseIndex(esp, ebx, TimesOne, -int32_t(sizeof(void*)));
michael@0 437 masm.lea(Operand(unwind), esp);
michael@0 438
michael@0 439 masm.pop(FramePointer);
michael@0 440 masm.ret();
michael@0 441
michael@0 442 Linker linker(masm);
michael@0 443 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 444
michael@0 445 #ifdef JS_ION_PERF
michael@0 446 writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
michael@0 447 #endif
michael@0 448
michael@0 449 CodeOffsetLabel returnLabel(returnOffset);
michael@0 450 returnLabel.fixup(&masm);
michael@0 451 if (returnAddrOut)
michael@0 452 *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
michael@0 453 return code;
michael@0 454 }
michael@0 455
michael@0 456 static void
michael@0 457 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
michael@0 458 {
michael@0 459 // Push registers such that we can access them from [base + code].
michael@0 460 masm.PushRegsInMask(AllRegs);
michael@0 461
michael@0 462 // Push the bailout table number.
michael@0 463 masm.push(Imm32(frameClass));
michael@0 464
michael@0 465 // The current stack pointer is the first argument to jit::Bailout.
michael@0 466 masm.movl(esp, eax);
michael@0 467
michael@0 468 // Make space for Bailout's baioutInfo outparam.
michael@0 469 masm.reserveStack(sizeof(void *));
michael@0 470 masm.movl(esp, ebx);
michael@0 471
michael@0 472 // Call the bailout function. This will correct the size of the bailout.
michael@0 473 masm.setupUnalignedABICall(2, ecx);
michael@0 474 masm.passABIArg(eax);
michael@0 475 masm.passABIArg(ebx);
michael@0 476 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout));
michael@0 477
michael@0 478 masm.pop(ecx); // Get bailoutInfo outparam.
michael@0 479
michael@0 480 // Common size of stuff we've pushed.
michael@0 481 const uint32_t BailoutDataSize = sizeof(void *) + // frameClass
michael@0 482 sizeof(double) * FloatRegisters::Total +
michael@0 483 sizeof(void *) * Registers::Total;
michael@0 484
michael@0 485 // Remove both the bailout frame and the topmost Ion frame's stack.
michael@0 486 if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
michael@0 487 // We want the frameSize. Stack is:
michael@0 488 // ... frame ...
michael@0 489 // snapshotOffset
michael@0 490 // frameSize
michael@0 491 // ... bailoutFrame ...
michael@0 492 masm.addl(Imm32(BailoutDataSize), esp);
michael@0 493 masm.pop(ebx);
michael@0 494 masm.addl(Imm32(sizeof(uint32_t)), esp);
michael@0 495 masm.addl(ebx, esp);
michael@0 496 } else {
michael@0 497 // Stack is:
michael@0 498 // ... frame ...
michael@0 499 // bailoutId
michael@0 500 // ... bailoutFrame ...
michael@0 501 uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
michael@0 502 masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp);
michael@0 503 }
michael@0 504
michael@0 505 // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
michael@0 506 JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
michael@0 507 masm.jmp(bailoutTail);
michael@0 508 }
michael@0 509
michael@0 510 JitCode *
michael@0 511 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
michael@0 512 {
michael@0 513 MacroAssembler masm;
michael@0 514
michael@0 515 Label bailout;
michael@0 516 for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
michael@0 517 masm.call(&bailout);
michael@0 518 masm.bind(&bailout);
michael@0 519
michael@0 520 GenerateBailoutThunk(cx, masm, frameClass);
michael@0 521
michael@0 522 Linker linker(masm);
michael@0 523 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 524
michael@0 525 #ifdef JS_ION_PERF
michael@0 526 writePerfSpewerJitCodeProfile(code, "BailoutHandler");
michael@0 527 #endif
michael@0 528
michael@0 529 return code;
michael@0 530 }
michael@0 531
michael@0 532 JitCode *
michael@0 533 JitRuntime::generateBailoutHandler(JSContext *cx)
michael@0 534 {
michael@0 535 MacroAssembler masm;
michael@0 536
michael@0 537 GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
michael@0 538
michael@0 539 Linker linker(masm);
michael@0 540 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 541
michael@0 542 #ifdef JS_ION_PERF
michael@0 543 writePerfSpewerJitCodeProfile(code, "BailoutHandler");
michael@0 544 #endif
michael@0 545
michael@0 546 return code;
michael@0 547 }
michael@0 548
michael@0 549 JitCode *
michael@0 550 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
michael@0 551 {
michael@0 552 JS_ASSERT(!StackKeptAligned);
michael@0 553 JS_ASSERT(functionWrappers_);
michael@0 554 JS_ASSERT(functionWrappers_->initialized());
michael@0 555 VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
michael@0 556 if (p)
michael@0 557 return p->value();
michael@0 558
michael@0 559 // Generate a separated code for the wrapper.
michael@0 560 MacroAssembler masm;
michael@0 561
michael@0 562 // Avoid conflicts with argument registers while discarding the result after
michael@0 563 // the function call.
michael@0 564 GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask);
michael@0 565
michael@0 566 // Wrapper register set is a superset of Volatile register set.
michael@0 567 JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
michael@0 568
michael@0 569 // The context is the first argument.
michael@0 570 Register cxreg = regs.takeAny();
michael@0 571
michael@0 572 // Stack is:
michael@0 573 // ... frame ...
michael@0 574 // +8 [args]
michael@0 575 // +4 descriptor
michael@0 576 // +0 returnAddress
michael@0 577 //
michael@0 578 // We're aligned to an exit frame, so link it up.
michael@0 579 masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
michael@0 580
michael@0 581 // Save the current stack pointer as the base for copying arguments.
michael@0 582 Register argsBase = InvalidReg;
michael@0 583 if (f.explicitArgs) {
michael@0 584 argsBase = regs.takeAny();
michael@0 585 masm.lea(Operand(esp, IonExitFrameLayout::SizeWithFooter()), argsBase);
michael@0 586 }
michael@0 587
michael@0 588 // Reserve space for the outparameter.
michael@0 589 Register outReg = InvalidReg;
michael@0 590 switch (f.outParam) {
michael@0 591 case Type_Value:
michael@0 592 outReg = regs.takeAny();
michael@0 593 masm.Push(UndefinedValue());
michael@0 594 masm.movl(esp, outReg);
michael@0 595 break;
michael@0 596
michael@0 597 case Type_Handle:
michael@0 598 outReg = regs.takeAny();
michael@0 599 masm.PushEmptyRooted(f.outParamRootType);
michael@0 600 masm.movl(esp, outReg);
michael@0 601 break;
michael@0 602
michael@0 603 case Type_Int32:
michael@0 604 case Type_Pointer:
michael@0 605 case Type_Bool:
michael@0 606 outReg = regs.takeAny();
michael@0 607 masm.reserveStack(sizeof(int32_t));
michael@0 608 masm.movl(esp, outReg);
michael@0 609 break;
michael@0 610
michael@0 611 case Type_Double:
michael@0 612 outReg = regs.takeAny();
michael@0 613 masm.reserveStack(sizeof(double));
michael@0 614 masm.movl(esp, outReg);
michael@0 615 break;
michael@0 616
michael@0 617 default:
michael@0 618 JS_ASSERT(f.outParam == Type_Void);
michael@0 619 break;
michael@0 620 }
michael@0 621
michael@0 622 masm.setupUnalignedABICall(f.argc(), regs.getAny());
michael@0 623 masm.passABIArg(cxreg);
michael@0 624
michael@0 625 size_t argDisp = 0;
michael@0 626
michael@0 627 // Copy arguments.
michael@0 628 for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
michael@0 629 MoveOperand from;
michael@0 630 switch (f.argProperties(explicitArg)) {
michael@0 631 case VMFunction::WordByValue:
michael@0 632 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
michael@0 633 argDisp += sizeof(void *);
michael@0 634 break;
michael@0 635 case VMFunction::DoubleByValue:
michael@0 636 // We don't pass doubles in float registers on x86, so no need
michael@0 637 // to check for argPassedInFloatReg.
michael@0 638 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
michael@0 639 argDisp += sizeof(void *);
michael@0 640 masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
michael@0 641 argDisp += sizeof(void *);
michael@0 642 break;
michael@0 643 case VMFunction::WordByRef:
michael@0 644 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
michael@0 645 MoveOp::GENERAL);
michael@0 646 argDisp += sizeof(void *);
michael@0 647 break;
michael@0 648 case VMFunction::DoubleByRef:
michael@0 649 masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
michael@0 650 MoveOp::GENERAL);
michael@0 651 argDisp += 2 * sizeof(void *);
michael@0 652 break;
michael@0 653 }
michael@0 654 }
michael@0 655
michael@0 656 // Copy the implicit outparam, if any.
michael@0 657 if (outReg != InvalidReg)
michael@0 658 masm.passABIArg(outReg);
michael@0 659
michael@0 660 masm.callWithABI(f.wrapped);
michael@0 661
michael@0 662 // Test for failure.
michael@0 663 switch (f.failType()) {
michael@0 664 case Type_Object:
michael@0 665 masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel(f.executionMode));
michael@0 666 break;
michael@0 667 case Type_Bool:
michael@0 668 masm.testb(eax, eax);
michael@0 669 masm.j(Assembler::Zero, masm.failureLabel(f.executionMode));
michael@0 670 break;
michael@0 671 default:
michael@0 672 MOZ_ASSUME_UNREACHABLE("unknown failure kind");
michael@0 673 }
michael@0 674
michael@0 675 // Load the outparam and free any allocated stack.
michael@0 676 switch (f.outParam) {
michael@0 677 case Type_Handle:
michael@0 678 masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
michael@0 679 break;
michael@0 680
michael@0 681 case Type_Value:
michael@0 682 masm.Pop(JSReturnOperand);
michael@0 683 break;
michael@0 684
michael@0 685 case Type_Int32:
michael@0 686 case Type_Pointer:
michael@0 687 masm.Pop(ReturnReg);
michael@0 688 break;
michael@0 689
michael@0 690 case Type_Bool:
michael@0 691 masm.Pop(ReturnReg);
michael@0 692 masm.movzbl(ReturnReg, ReturnReg);
michael@0 693 break;
michael@0 694
michael@0 695 case Type_Double:
michael@0 696 if (cx->runtime()->jitSupportsFloatingPoint)
michael@0 697 masm.Pop(ReturnFloatReg);
michael@0 698 else
michael@0 699 masm.assumeUnreachable("Unable to pop to float reg, with no FP support.");
michael@0 700 break;
michael@0 701
michael@0 702 default:
michael@0 703 JS_ASSERT(f.outParam == Type_Void);
michael@0 704 break;
michael@0 705 }
michael@0 706 masm.leaveExitFrame();
michael@0 707 masm.retn(Imm32(sizeof(IonExitFrameLayout) +
michael@0 708 f.explicitStackSlots() * sizeof(void *) +
michael@0 709 f.extraValuesToPop * sizeof(Value)));
michael@0 710
michael@0 711 Linker linker(masm);
michael@0 712 JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 713 if (!wrapper)
michael@0 714 return nullptr;
michael@0 715
michael@0 716 #ifdef JS_ION_PERF
michael@0 717 writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
michael@0 718 #endif
michael@0 719
michael@0 720 // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
michael@0 721 // use relookupOrAdd instead of add.
michael@0 722 if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
michael@0 723 return nullptr;
michael@0 724
michael@0 725 return wrapper;
michael@0 726 }
michael@0 727
michael@0 728 JitCode *
michael@0 729 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
michael@0 730 {
michael@0 731 MacroAssembler masm;
michael@0 732
michael@0 733 RegisterSet save;
michael@0 734 if (cx->runtime()->jitSupportsFloatingPoint) {
michael@0 735 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
michael@0 736 FloatRegisterSet(FloatRegisters::VolatileMask));
michael@0 737 } else {
michael@0 738 save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
michael@0 739 FloatRegisterSet());
michael@0 740 }
michael@0 741 masm.PushRegsInMask(save);
michael@0 742
michael@0 743 JS_ASSERT(PreBarrierReg == edx);
michael@0 744 masm.movl(ImmPtr(cx->runtime()), ecx);
michael@0 745
michael@0 746 masm.setupUnalignedABICall(2, eax);
michael@0 747 masm.passABIArg(ecx);
michael@0 748 masm.passABIArg(edx);
michael@0 749
michael@0 750 if (type == MIRType_Value) {
michael@0 751 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon));
michael@0 752 } else {
michael@0 753 JS_ASSERT(type == MIRType_Shape);
michael@0 754 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
michael@0 755 }
michael@0 756
michael@0 757 masm.PopRegsInMask(save);
michael@0 758 masm.ret();
michael@0 759
michael@0 760 Linker linker(masm);
michael@0 761 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 762
michael@0 763 #ifdef JS_ION_PERF
michael@0 764 writePerfSpewerJitCodeProfile(code, "PreBarrier");
michael@0 765 #endif
michael@0 766
michael@0 767 return code;
michael@0 768 }
michael@0 769
michael@0 770 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
michael@0 771 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
michael@0 772
michael@0 773 JitCode *
michael@0 774 JitRuntime::generateDebugTrapHandler(JSContext *cx)
michael@0 775 {
michael@0 776 MacroAssembler masm;
michael@0 777
michael@0 778 Register scratch1 = eax;
michael@0 779 Register scratch2 = ecx;
michael@0 780 Register scratch3 = edx;
michael@0 781
michael@0 782 // Load the return address in scratch1.
michael@0 783 masm.loadPtr(Address(esp, 0), scratch1);
michael@0 784
michael@0 785 // Load BaselineFrame pointer in scratch2.
michael@0 786 masm.mov(ebp, scratch2);
michael@0 787 masm.subPtr(Imm32(BaselineFrame::Size()), scratch2);
michael@0 788
michael@0 789 // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
michael@0 790 // the stub frame has a nullptr ICStub pointer, since this pointer is
michael@0 791 // marked during GC.
michael@0 792 masm.movePtr(ImmPtr(nullptr), BaselineStubReg);
michael@0 793 EmitEnterStubFrame(masm, scratch3);
michael@0 794
michael@0 795 JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
michael@0 796 if (!code)
michael@0 797 return nullptr;
michael@0 798
michael@0 799 masm.push(scratch1);
michael@0 800 masm.push(scratch2);
michael@0 801 EmitCallVM(code, masm);
michael@0 802
michael@0 803 EmitLeaveStubFrame(masm);
michael@0 804
michael@0 805 // If the stub returns |true|, we have to perform a forced return
michael@0 806 // (return from the JS frame). If the stub returns |false|, just return
michael@0 807 // from the trap stub so that execution continues at the current pc.
michael@0 808 Label forcedReturn;
michael@0 809 masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn);
michael@0 810 masm.ret();
michael@0 811
michael@0 812 masm.bind(&forcedReturn);
michael@0 813 masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()),
michael@0 814 JSReturnOperand);
michael@0 815 masm.mov(ebp, esp);
michael@0 816 masm.pop(ebp);
michael@0 817 masm.ret();
michael@0 818
michael@0 819 Linker linker(masm);
michael@0 820 JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 821
michael@0 822 #ifdef JS_ION_PERF
michael@0 823 writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
michael@0 824 #endif
michael@0 825
michael@0 826 return codeDbg;
michael@0 827 }
michael@0 828
michael@0 829 JitCode *
michael@0 830 JitRuntime::generateExceptionTailStub(JSContext *cx)
michael@0 831 {
michael@0 832 MacroAssembler masm;
michael@0 833
michael@0 834 masm.handleFailureWithHandlerTail();
michael@0 835
michael@0 836 Linker linker(masm);
michael@0 837 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 838
michael@0 839 #ifdef JS_ION_PERF
michael@0 840 writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
michael@0 841 #endif
michael@0 842
michael@0 843 return code;
michael@0 844 }
michael@0 845
michael@0 846 JitCode *
michael@0 847 JitRuntime::generateBailoutTailStub(JSContext *cx)
michael@0 848 {
michael@0 849 MacroAssembler masm;
michael@0 850
michael@0 851 masm.generateBailoutTail(edx, ecx);
michael@0 852
michael@0 853 Linker linker(masm);
michael@0 854 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
michael@0 855
michael@0 856 #ifdef JS_ION_PERF
michael@0 857 writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
michael@0 858 #endif
michael@0 859
michael@0 860 return code;
michael@0 861 }

mercurial