summaryrefslogtreecommitdiffstats
path: root/js/src/jit/Trampoline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/Trampoline.cpp')
-rw-r--r--js/src/jit/Trampoline.cpp275
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();
+ }
+}