InterpreterEntryTrampoline.cpp (8554B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/InterpreterEntryTrampoline.h" 8 #include "jit/JitRuntime.h" 9 #include "jit/Linker.h" 10 #include "vm/Interpreter.h" 11 12 #include "gc/Marking-inl.h" 13 #include "jit/MacroAssembler-inl.h" 14 15 using namespace js; 16 using namespace js::jit; 17 18 void js::ClearInterpreterEntryMap(JSRuntime* runtime) { 19 if (runtime->hasJitRuntime() && 20 runtime->jitRuntime()->hasInterpreterEntryMap()) { 21 runtime->jitRuntime()->getInterpreterEntryMap()->clear(); 22 } 23 } 24 25 void EntryTrampolineMap::traceTrampolineCode(JSTracer* trc) { 26 for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) { 27 EntryTrampoline& trampoline = e.front().value(); 28 trampoline.trace(trc); 29 } 30 } 31 32 void EntryTrampolineMap::updateScriptsAfterMovingGC(void) { 33 for (jit::EntryTrampolineMap::Enum e(*this); !e.empty(); e.popFront()) { 34 BaseScript* script = e.front().key(); 35 if (IsForwarded(script)) { 36 script = Forwarded(script); 37 e.rekeyFront(script); 38 } 39 } 40 } 41 42 #ifdef JSGC_HASH_TABLE_CHECKS 43 void EntryTrampoline::checkTrampolineAfterMovingGC() const { 44 JitCode* trampoline = entryTrampoline_; 45 CheckGCThingAfterMovingGC(trampoline); 46 } 47 48 void EntryTrampolineMap::checkScriptsAfterMovingGC() { 49 gc::CheckTableAfterMovingGC(*this, [](const auto& entry) { 50 BaseScript* script = entry.key(); 51 CheckGCThingAfterMovingGC(script); 52 entry.value().checkTrampolineAfterMovingGC(); 53 return script; 54 }); 55 } 56 #endif 57 58 void JitRuntime::generateBaselineInterpreterEntryTrampoline( 59 MacroAssembler& masm) { 60 AutoCreatedBy acb(masm, 61 "JitRuntime::generateBaselineInterpreterEntryTrampoline"); 62 63 #ifdef JS_USE_LINK_REGISTER 64 masm.pushReturnAddress(); 65 #endif 66 masm.push(FramePointer); 67 masm.moveStackPtrTo(FramePointer); 68 69 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); 70 Register nargs = regs.takeAny(); 71 Register callee = regs.takeAny(); 72 Register scratch = regs.takeAny(); 73 74 // Load callee token and keep it in a register as it will be used often 75 Address calleeTokenAddr( 76 FramePointer, BaselineInterpreterEntryFrameLayout::offsetOfCalleeToken()); 77 masm.loadPtr(calleeTokenAddr, callee); 78 79 // Load argc into nargs. 80 masm.loadNumActualArgs(FramePointer, nargs); 81 82 Label notFunction; 83 { 84 // Check if calleetoken is script or function 85 masm.branchTestPtr(Assembler::NonZero, callee, Imm32(CalleeTokenScriptBit), 86 ¬Function); 87 88 // CalleeToken is a function, load |nformals| into scratch 89 masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), callee, scratch); 90 masm.loadFunctionArgCount(scratch, scratch); 91 92 // Take max(nformals, argc). 93 Label noUnderflow; 94 masm.branch32(Assembler::AboveOrEqual, nargs, scratch, &noUnderflow); 95 { 96 masm.movePtr(scratch, nargs); 97 } 98 masm.bind(&noUnderflow); 99 100 // Add 1 to nargs if constructing. 101 static_assert( 102 CalleeToken_FunctionConstructing == 1, 103 "Ensure that we can use the constructing bit to count the value"); 104 masm.movePtr(callee, scratch); 105 masm.and32(Imm32(uint32_t(CalleeToken_FunctionConstructing)), scratch); 106 masm.addPtr(scratch, nargs); 107 } 108 masm.bind(¬Function); 109 110 // Align stack 111 masm.alignJitStackBasedOnNArgs(nargs, /*countIncludesThis = */ false); 112 113 // Point argPtr to the topmost argument. 114 static_assert(sizeof(Value) == 8, 115 "Using TimesEight for scale of sizeof(Value)."); 116 BaseIndex topPtrAddr(FramePointer, nargs, TimesEight, 117 sizeof(BaselineInterpreterEntryFrameLayout)); 118 Register argPtr = nargs; 119 masm.computeEffectiveAddress(topPtrAddr, argPtr); 120 121 // Load the end address into scratch, which is the callee token. 122 masm.computeEffectiveAddress(calleeTokenAddr, scratch); 123 124 // Copy |this|+arguments 125 Label loop; 126 masm.bind(&loop); 127 { 128 masm.pushValue(Address(argPtr, 0)); 129 masm.subPtr(Imm32(sizeof(Value)), argPtr); 130 masm.branchPtr(Assembler::Above, argPtr, scratch, &loop); 131 } 132 133 // Copy callee token 134 masm.push(callee); 135 136 // Save a new descriptor using BaselineInterpreterEntry frame type. 137 masm.loadNumActualArgs(FramePointer, scratch); 138 masm.pushFrameDescriptorForJitCall(FrameType::BaselineInterpreterEntry, 139 scratch, scratch); 140 141 // Call into baseline interpreter 142 uint8_t* blinterpAddr = baselineInterpreter().codeRaw(); 143 masm.assertStackAlignment(JitStackAlignment, 2 * sizeof(uintptr_t)); 144 masm.call(ImmPtr(blinterpAddr)); 145 146 masm.moveToStackPtr(FramePointer); 147 masm.pop(FramePointer); 148 masm.ret(); 149 } 150 151 void JitRuntime::generateInterpreterEntryTrampoline(MacroAssembler& masm) { 152 AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterEntryTrampoline"); 153 154 // If BLI is disabled, we don't need an offset. 155 if (IsBaselineInterpreterEnabled()) { 156 uint32_t offset = startTrampolineCode(masm); 157 if (!vmInterpreterEntryOffset_) { 158 vmInterpreterEntryOffset_ = offset; 159 } 160 } 161 162 #ifdef JS_CODEGEN_ARM64 163 // Use the normal stack pointer for the initial pushes. 164 masm.SetStackPointer64(sp); 165 166 // Push lr and fp together to maintain 16-byte alignment. 167 masm.push(lr, FramePointer); 168 masm.moveStackPtrTo(FramePointer); 169 170 // Save the PSP register (r28), and a scratch (r19). 171 masm.push(r19, r28); 172 173 // Setup the PSP so we can use callWithABI below. 174 masm.SetStackPointer64(PseudoStackPointer64); 175 masm.initPseudoStackPtr(); 176 177 Register arg0 = IntArgReg0; 178 Register arg1 = IntArgReg1; 179 Register scratch = r19; 180 #elif defined(JS_CODEGEN_X86) 181 masm.push(FramePointer); 182 masm.moveStackPtrTo(FramePointer); 183 184 AllocatableRegisterSet regs(RegisterSet::Volatile()); 185 Register arg0 = regs.takeAnyGeneral(); 186 Register arg1 = regs.takeAnyGeneral(); 187 Register scratch = regs.takeAnyGeneral(); 188 189 // First two arguments are passed on the stack in 32-bit. 190 Address cxAddr(FramePointer, 2 * sizeof(void*)); 191 Address stateAddr(FramePointer, 3 * sizeof(void*)); 192 masm.loadPtr(cxAddr, arg0); 193 masm.loadPtr(stateAddr, arg1); 194 #else 195 masm.push(FramePointer); 196 masm.moveStackPtrTo(FramePointer); 197 198 AllocatableRegisterSet regs(RegisterSet::Volatile()); 199 regs.take(IntArgReg0); 200 regs.take(IntArgReg1); 201 Register arg0 = IntArgReg0; 202 Register arg1 = IntArgReg1; 203 Register scratch = regs.takeAnyGeneral(); 204 #endif 205 206 using Fn = bool (*)(JSContext* cx, js::RunState& state); 207 masm.setupUnalignedABICall(scratch); 208 masm.passABIArg(arg0); // cx 209 masm.passABIArg(arg1); // state 210 masm.callWithABI<Fn, Interpret>( 211 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); 212 213 #ifdef JS_CODEGEN_ARM64 214 masm.syncStackPtr(); 215 masm.SetStackPointer64(sp); 216 217 // Restore r28 and r19. 218 masm.pop(r28, r19); 219 220 // Restore old fp and pop lr for return. 221 masm.pop(FramePointer, lr); 222 masm.abiret(); 223 224 // Reset stack pointer. 225 masm.SetStackPointer64(PseudoStackPointer64); 226 #else 227 masm.moveToStackPtr(FramePointer); 228 masm.pop(FramePointer); 229 masm.ret(); 230 #endif 231 } 232 233 JitCode* JitRuntime::generateEntryTrampolineForScript(JSContext* cx, 234 JSScript* script) { 235 if (JitSpewEnabled(JitSpew_Codegen)) { 236 UniqueChars funName; 237 if (script->function() && script->function()->fullDisplayAtom()) { 238 funName = 239 AtomToPrintableString(cx, script->function()->fullDisplayAtom()); 240 } 241 242 JitSpew(JitSpew_Codegen, 243 "# Emitting Interpreter Entry Trampoline for %s (%s:%u:%u)", 244 funName ? funName.get() : "*", script->filename(), script->lineno(), 245 script->column().oneOriginValue()); 246 } 247 248 TempAllocator temp(&cx->tempLifoAlloc()); 249 JitContext jctx(cx); 250 StackMacroAssembler masm(cx, temp); 251 AutoCreatedBy acb(masm, "JitRuntime::generateEntryTrampolineForScript"); 252 PerfSpewerRangeRecorder rangeRecorder(masm); 253 254 if (IsBaselineInterpreterEnabled()) { 255 generateBaselineInterpreterEntryTrampoline(masm); 256 rangeRecorder.recordOffset("BaselineInterpreter", cx, script); 257 } 258 259 generateInterpreterEntryTrampoline(masm); 260 rangeRecorder.recordOffset("Interpreter", cx, script); 261 262 Linker linker(masm); 263 JitCode* code = linker.newCode(cx, CodeKind::Other); 264 if (!code) { 265 return nullptr; 266 } 267 rangeRecorder.collectRangesForJitCode(code); 268 JitSpew(JitSpew_Codegen, "# code = %p", code->raw()); 269 return code; 270 }