/* -*- 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( 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; }