diff options
Diffstat (limited to 'js/src/jit/InterpreterEntryTrampoline.cpp')
-rw-r--r-- | js/src/jit/InterpreterEntryTrampoline.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/js/src/jit/InterpreterEntryTrampoline.cpp b/js/src/jit/InterpreterEntryTrampoline.cpp new file mode 100644 index 0000000000..a58713ff09 --- /dev/null +++ b/js/src/jit/InterpreterEntryTrampoline.cpp @@ -0,0 +1,269 @@ +/* -*- 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 "jit/InterpreterEntryTrampoline.h" +#include "jit/JitRuntime.h" +#include "jit/Linker.h" +#include "vm/Interpreter.h" + +#include "gc/Marking-inl.h" +#include "jit/MacroAssembler-inl.h" + +using namespace js; +using namespace js::jit; + +void js::ClearInterpreterEntryMap(JSRuntime* runtime) { + if (runtime->hasJitRuntime() && + runtime->jitRuntime()->hasInterpreterEntryMap()) { + runtime->jitRuntime()->getInterpreterEntryMap()->clear(); + } +} + +void EntryTrampolineMap::traceTrampolineCode(JSTracer* trc) { + for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) { + EntryTrampoline& trampoline = e.front().value(); + trampoline.trace(trc); + } +} + +void EntryTrampolineMap::updateScriptsAfterMovingGC(void) { + for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) { + BaseScript* script = e.front().key(); + if (IsForwarded(script)) { + script = Forwarded(script); + e.rekeyFront(script); + } + } +} + +#ifdef JSGC_HASH_TABLE_CHECKS +void EntryTrampoline::checkTrampolineAfterMovingGC() { + JitCode* trampoline = entryTrampoline_; + CheckGCThingAfterMovingGC(trampoline); +} + +void EntryTrampolineMap::checkScriptsAfterMovingGC() { + for (jit::EntryTrampolineMap::Enum r(*this); !r.empty(); r.popFront()) { + BaseScript* script = r.front().key(); + CheckGCThingAfterMovingGC(script); + r.front().value().checkTrampolineAfterMovingGC(); + auto ptr = lookup(script); + MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front()); + } +} +#endif + +void JitRuntime::generateBaselineInterpreterEntryTrampoline( + MacroAssembler& masm) { + AutoCreatedBy acb(masm, + "JitRuntime::generateBaselineInterpreterEntryTrampoline"); + +#ifdef JS_USE_LINK_REGISTER + masm.pushReturnAddress(); +#endif + masm.push(FramePointer); + masm.moveStackPtrTo(FramePointer); + + AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); + Register nargs = regs.takeAny(); + Register callee = regs.takeAny(); + Register scratch = regs.takeAny(); + + // Load callee token and keep it in a register as it will be used often + Address calleeTokenAddr( + FramePointer, BaselineInterpreterEntryFrameLayout::offsetOfCalleeToken()); + masm.loadPtr(calleeTokenAddr, callee); + + // Load argc into nargs. + masm.loadNumActualArgs(FramePointer, nargs); + + Label notFunction; + { + // Check if calleetoken is script or function + masm.branchTestPtr(Assembler::NonZero, callee, Imm32(CalleeTokenScriptBit), + ¬Function); + + // CalleeToken is a function, load |nformals| into scratch + masm.movePtr(callee, scratch); + masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch); + masm.loadFunctionArgCount(scratch, scratch); + + // Take max(nformals, argc). + Label noUnderflow; + masm.branch32(Assembler::AboveOrEqual, nargs, scratch, &noUnderflow); + { masm.movePtr(scratch, nargs); } + masm.bind(&noUnderflow); + + // Add 1 to nargs if constructing. + static_assert( + CalleeToken_FunctionConstructing == 1, + "Ensure that we can use the constructing bit to count the value"); + masm.movePtr(callee, scratch); + masm.and32(Imm32(uint32_t(CalleeToken_FunctionConstructing)), scratch); + masm.addPtr(scratch, nargs); + } + masm.bind(¬Function); + + // Align stack + masm.alignJitStackBasedOnNArgs(nargs, /*countIncludesThis = */ false); + + // Point argPtr to the topmost argument. + static_assert(sizeof(Value) == 8, + "Using TimesEight for scale of sizeof(Value)."); + BaseIndex topPtrAddr(FramePointer, nargs, TimesEight, + sizeof(BaselineInterpreterEntryFrameLayout)); + Register argPtr = nargs; + masm.computeEffectiveAddress(topPtrAddr, argPtr); + + // Load the end address into scratch, which is the callee token. + masm.computeEffectiveAddress(calleeTokenAddr, scratch); + + // Copy |this|+arguments + Label loop; + masm.bind(&loop); + { + masm.pushValue(Address(argPtr, 0)); + masm.subPtr(Imm32(sizeof(Value)), argPtr); + masm.branchPtr(Assembler::Above, argPtr, scratch, &loop); + } + + // Copy callee token + masm.push(callee); + + // Save a new descriptor using BaselineInterpreterEntry frame type. + masm.loadNumActualArgs(FramePointer, scratch); + masm.pushFrameDescriptorForJitCall(FrameType::BaselineInterpreterEntry, + scratch, scratch); + + // Call into baseline interpreter + uint8_t* blinterpAddr = baselineInterpreter().codeRaw(); + masm.assertStackAlignment(JitStackAlignment, 2 * sizeof(uintptr_t)); + masm.call(ImmPtr(blinterpAddr)); + + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); +} + +void JitRuntime::generateInterpreterEntryTrampoline(MacroAssembler& masm) { + AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterEntryTrampoline"); + + // If BLI is disabled, we don't need an offset. + if (IsBaselineInterpreterEnabled()) { + uint32_t offset = startTrampolineCode(masm); + if (!vmInterpreterEntryOffset_) { + vmInterpreterEntryOffset_ = offset; + } + } + +#ifdef JS_CODEGEN_ARM64 + // Use the normal stack pointer for the initial pushes. + masm.SetStackPointer64(sp); + + // Push lr and fp together to maintain 16-byte alignment. + masm.push(lr, FramePointer); + masm.moveStackPtrTo(FramePointer); + + // Save the PSP register (r28), and a scratch (r19). + masm.push(r19, r28); + + // Setup the PSP so we can use callWithABI below. + masm.SetStackPointer64(PseudoStackPointer64); + masm.initPseudoStackPtr(); + + Register arg0 = IntArgReg0; + Register arg1 = IntArgReg1; + Register scratch = r19; +#elif defined(JS_CODEGEN_X86) + masm.push(FramePointer); + masm.moveStackPtrTo(FramePointer); + + AllocatableRegisterSet regs(RegisterSet::Volatile()); + Register arg0 = regs.takeAnyGeneral(); + Register arg1 = regs.takeAnyGeneral(); + Register scratch = regs.takeAnyGeneral(); + + // First two arguments are passed on the stack in 32-bit. + Address cxAddr(FramePointer, 2 * sizeof(void*)); + Address stateAddr(FramePointer, 3 * sizeof(void*)); + masm.loadPtr(cxAddr, arg0); + masm.loadPtr(stateAddr, arg1); +#else + masm.push(FramePointer); + masm.moveStackPtrTo(FramePointer); + + AllocatableRegisterSet regs(RegisterSet::Volatile()); + regs.take(IntArgReg0); + regs.take(IntArgReg1); + Register arg0 = IntArgReg0; + Register arg1 = IntArgReg1; + Register scratch = regs.takeAnyGeneral(); +#endif + + using Fn = bool (*)(JSContext* cx, js::RunState& state); + masm.setupUnalignedABICall(scratch); + masm.passABIArg(arg0); // cx + masm.passABIArg(arg1); // state + masm.callWithABI<Fn, Interpret>( + MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); + +#ifdef JS_CODEGEN_ARM64 + masm.syncStackPtr(); + masm.SetStackPointer64(sp); + + // Restore r28 and r19. + masm.pop(r28, r19); + + // Restore old fp and pop lr for return. + masm.pop(FramePointer, lr); + masm.abiret(); + + // Reset stack pointer. + masm.SetStackPointer64(PseudoStackPointer64); +#else + masm.moveToStackPtr(FramePointer); + masm.pop(FramePointer); + masm.ret(); +#endif +} + +JitCode* JitRuntime::generateEntryTrampolineForScript(JSContext* cx, + JSScript* script) { + if (JitSpewEnabled(JitSpew_Codegen)) { + UniqueChars funName; + if (script->function() && script->function()->displayAtom()) { + funName = AtomToPrintableString(cx, script->function()->displayAtom()); + } + + JitSpew(JitSpew_Codegen, + "# Emitting Interpreter Entry Trampoline for %s (%s:%u:%u)", + funName ? funName.get() : "*", script->filename(), script->lineno(), + script->column()); + } + + TempAllocator temp(&cx->tempLifoAlloc()); + JitContext jctx(cx); + StackMacroAssembler masm(cx, temp); + AutoCreatedBy acb(masm, "JitRuntime::generateEntryTrampolineForScript"); + PerfSpewerRangeRecorder rangeRecorder(masm); + + if (IsBaselineInterpreterEnabled()) { + generateBaselineInterpreterEntryTrampoline(masm); + rangeRecorder.recordOffset("BaselineInterpreter", cx, script); + } + + generateInterpreterEntryTrampoline(masm); + rangeRecorder.recordOffset("Interpreter", cx, script); + + Linker linker(masm); + JitCode* code = linker.newCode(cx, CodeKind::Other); + if (!code) { + return nullptr; + } + rangeRecorder.collectRangesForJitCode(code); + JitSpew(JitSpew_Codegen, "# code = %p", code->raw()); + return code; +} |