diff options
Diffstat (limited to 'js/src/jit/Trampoline.cpp')
-rw-r--r-- | js/src/jit/Trampoline.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/js/src/jit/Trampoline.cpp b/js/src/jit/Trampoline.cpp new file mode 100644 index 0000000000..85661784a7 --- /dev/null +++ b/js/src/jit/Trampoline.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <initializer_list> + +#include "jit/JitFrames.h" +#include "jit/JitRuntime.h" +#include "jit/MacroAssembler.h" +#include "vm/JitActivation.h" +#include "vm/JSContext.h" + +#include "jit/MacroAssembler-inl.h" + +using namespace js; +using namespace js::jit; + +void JitRuntime::generateExceptionTailStub(MacroAssembler& masm, + Label* profilerExitTail, + Label* bailoutTail) { + AutoCreatedBy acb(masm, "JitRuntime::generateExceptionTailStub"); + + exceptionTailOffset_ = startTrampolineCode(masm); + + masm.bind(masm.failureLabel()); + masm.handleFailureWithHandlerTail(profilerExitTail, bailoutTail); +} + +void JitRuntime::generateProfilerExitFrameTailStub(MacroAssembler& masm, + Label* profilerExitTail) { + AutoCreatedBy acb(masm, "JitRuntime::generateProfilerExitFrameTailStub"); + + profilerExitFrameTailOffset_ = startTrampolineCode(masm); + masm.bind(profilerExitTail); + + static constexpr size_t CallerFPOffset = + CommonFrameLayout::offsetOfCallerFramePtr(); + + // Assert the caller frame's type is one of the types we expect. + auto emitAssertPrevFrameType = [&masm]( + Register framePtr, Register scratch, + std::initializer_list<FrameType> types) { +#ifdef DEBUG + masm.loadPtr(Address(framePtr, CommonFrameLayout::offsetOfDescriptor()), + scratch); + masm.and32(Imm32(FRAMETYPE_MASK), scratch); + + Label checkOk; + for (FrameType type : types) { + masm.branch32(Assembler::Equal, scratch, Imm32(type), &checkOk); + } + masm.assumeUnreachable("Unexpected previous frame"); + masm.bind(&checkOk); +#else + (void)masm; +#endif + }; + + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); + regs.take(JSReturnOperand); + Register scratch = regs.takeAny(); + + // The code generated below expects that the current frame pointer points + // to an Ion or Baseline frame, at the state it would be immediately before + // the frame epilogue and ret(). Thus, after this stub's business is done, it + // restores the frame pointer and stack pointer, then executes a ret() and + // returns directly to the caller frame, on behalf of the callee script that + // jumped to this code. + // + // Thus the expected state is: + // + // [JitFrameLayout] <-- FramePointer + // [frame contents] <-- StackPointer + // + // The generated jitcode is responsible for overwriting the + // jitActivation->lastProfilingFrame field with a pointer to the previous + // Ion or Baseline jit-frame that was pushed before this one. It is also + // responsible for overwriting jitActivation->lastProfilingCallSite with + // the return address into that frame. + // + // So this jitcode is responsible for "walking up" the jit stack, finding + // the previous Ion or Baseline JS frame, and storing its address and the + // return address into the appropriate fields on the current jitActivation. + // + // There are a fixed number of different path types that can lead to the + // current frame, which is either a Baseline or Ion frame: + // + // <Baseline-Or-Ion> + // ^ + // | + // ^--- Ion (or Baseline JSOp::Resume) + // | + // ^--- Baseline Stub <---- Baseline + // | + // ^--- IonICCall <---- Ion + // | + // ^--- Arguments Rectifier + // | ^ + // | | + // | ^--- Ion + // | | + // | ^--- Baseline Stub <---- Baseline + // | | + // | ^--- Entry Frame (CppToJSJit or WasmToJSJit) + // | + // ^--- Entry Frame (CppToJSJit or WasmToJSJit) + // | + // ^--- Entry Frame (BaselineInterpreter) + // + // NOTE: Keep this in sync with JSJitProfilingFrameIterator::moveToNextFrame! + + Register actReg = regs.takeAny(); + masm.loadJSContext(actReg); + masm.loadPtr(Address(actReg, offsetof(JSContext, profilingActivation_)), + actReg); + + Address lastProfilingFrame(actReg, + JitActivation::offsetOfLastProfilingFrame()); + Address lastProfilingCallSite(actReg, + JitActivation::offsetOfLastProfilingCallSite()); + +#ifdef DEBUG + // Ensure that frame we are exiting is current lastProfilingFrame + { + masm.loadPtr(lastProfilingFrame, scratch); + Label checkOk; + masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), &checkOk); + masm.branchPtr(Assembler::Equal, FramePointer, scratch, &checkOk); + masm.assumeUnreachable( + "Mismatch between stored lastProfilingFrame and current frame " + "pointer."); + masm.bind(&checkOk); + } +#endif + + // Move FP into a scratch register and use that scratch register below, to + // allow unwrapping rectifier frames without clobbering FP. + Register fpScratch = regs.takeAny(); + masm.mov(FramePointer, fpScratch); + + Label again; + masm.bind(&again); + + // Load the frame descriptor into |scratch|, figure out what to do depending + // on its type. + masm.loadPtr(Address(fpScratch, JitFrameLayout::offsetOfDescriptor()), + scratch); + masm.and32(Imm32(FRAMETYPE_MASK), scratch); + + // Handling of each case is dependent on FrameDescriptor.type + Label handle_BaselineOrIonJS; + Label handle_BaselineStub; + Label handle_Rectifier; + Label handle_BaselineInterpreterEntry; + Label handle_IonICCall; + Label handle_Entry; + + // We check for IonJS and BaselineStub first because these are the most common + // types. Calls from Baseline are usually from a BaselineStub frame. + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::IonJS), + &handle_BaselineOrIonJS); + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::BaselineStub), + &handle_BaselineStub); + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::Rectifier), + &handle_Rectifier); + if (JitOptions.emitInterpreterEntryTrampoline) { + masm.branch32(Assembler::Equal, scratch, + Imm32(FrameType::BaselineInterpreterEntry), + &handle_BaselineInterpreterEntry); + } + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::CppToJSJit), + &handle_Entry); + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::BaselineJS), + &handle_BaselineOrIonJS); + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::IonICCall), + &handle_IonICCall); + masm.branch32(Assembler::Equal, scratch, Imm32(FrameType::WasmToJSJit), + &handle_Entry); + + masm.assumeUnreachable( + "Invalid caller frame type when returning from a JIT frame."); + + masm.bind(&handle_BaselineOrIonJS); + { + // Returning directly to a Baseline or Ion frame. + + // lastProfilingCallSite := ReturnAddress + masm.loadPtr(Address(fpScratch, JitFrameLayout::offsetOfReturnAddress()), + scratch); + masm.storePtr(scratch, lastProfilingCallSite); + + // lastProfilingFrame := CallerFrame + masm.loadPtr(Address(fpScratch, CallerFPOffset), scratch); + masm.storePtr(scratch, lastProfilingFrame); + + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); + } + + // Shared implementation for BaselineStub and IonICCall frames. + auto emitHandleStubFrame = [&](FrameType expectedPrevType) { + // Load pointer to stub frame and assert type of its caller frame. + masm.loadPtr(Address(fpScratch, CallerFPOffset), fpScratch); + emitAssertPrevFrameType(fpScratch, scratch, {expectedPrevType}); + + // lastProfilingCallSite := StubFrame.ReturnAddress + masm.loadPtr(Address(fpScratch, CommonFrameLayout::offsetOfReturnAddress()), + scratch); + masm.storePtr(scratch, lastProfilingCallSite); + + // lastProfilingFrame := StubFrame.CallerFrame + masm.loadPtr(Address(fpScratch, CallerFPOffset), scratch); + masm.storePtr(scratch, lastProfilingFrame); + + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); + }; + + masm.bind(&handle_BaselineStub); + { + // BaselineJS => BaselineStub frame. + emitHandleStubFrame(FrameType::BaselineJS); + } + + masm.bind(&handle_IonICCall); + { + // IonJS => IonICCall frame. + emitHandleStubFrame(FrameType::IonJS); + } + + masm.bind(&handle_Rectifier); + { + // There can be multiple previous frame types so just "unwrap" the arguments + // rectifier frame and try again. + masm.loadPtr(Address(fpScratch, CallerFPOffset), fpScratch); + emitAssertPrevFrameType(fpScratch, scratch, + {FrameType::IonJS, FrameType::BaselineStub, + FrameType::CppToJSJit, FrameType::WasmToJSJit}); + masm.jump(&again); + } + + if (JitOptions.emitInterpreterEntryTrampoline) { + masm.bind(&handle_BaselineInterpreterEntry); + { + // Unwrap the baseline interpreter entry frame and try again. + masm.loadPtr(Address(fpScratch, CallerFPOffset), fpScratch); + emitAssertPrevFrameType( + fpScratch, scratch, + {FrameType::IonJS, FrameType::BaselineJS, FrameType::BaselineStub, + FrameType::CppToJSJit, FrameType::WasmToJSJit, FrameType::IonICCall, + FrameType::Rectifier}); + masm.jump(&again); + } + } + + masm.bind(&handle_Entry); + { + // FrameType::CppToJSJit / FrameType::WasmToJSJit + // + // A fast-path wasm->jit transition frame is an entry frame from the point + // of view of the JIT. + // Store null into both fields. + masm.movePtr(ImmPtr(nullptr), scratch); + masm.storePtr(scratch, lastProfilingCallSite); + masm.storePtr(scratch, lastProfilingFrame); + + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); + } +} |