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

mercurial