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/. */
7 #include "jit/InterpreterEntryTrampoline.h"
8 #include "jit/JitRuntime.h"
9 #include "jit/Linker.h"
10 #include "vm/Interpreter.h"
12 #include "gc/Marking-inl.h"
13 #include "jit/MacroAssembler-inl.h"
16 using namespace js::jit
;
18 void js::ClearInterpreterEntryMap(JSRuntime
* runtime
) {
19 if (runtime
->hasJitRuntime() &&
20 runtime
->jitRuntime()->hasInterpreterEntryMap()) {
21 runtime
->jitRuntime()->getInterpreterEntryMap()->clear();
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
);
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
);
42 #ifdef JSGC_HASH_TABLE_CHECKS
43 void EntryTrampoline::checkTrampolineAfterMovingGC() {
44 JitCode
* trampoline
= entryTrampoline_
;
45 CheckGCThingAfterMovingGC(trampoline
);
48 void EntryTrampolineMap::checkScriptsAfterMovingGC() {
49 for (jit::EntryTrampolineMap::Enum
r(*this); !r
.empty(); r
.popFront()) {
50 BaseScript
* script
= r
.front().key();
51 CheckGCThingAfterMovingGC(script
);
52 r
.front().value().checkTrampolineAfterMovingGC();
53 auto ptr
= lookup(script
);
54 MOZ_RELEASE_ASSERT(ptr
.found() && &*ptr
== &r
.front());
59 void JitRuntime::generateBaselineInterpreterEntryTrampoline(
60 MacroAssembler
& masm
) {
61 AutoCreatedBy
acb(masm
,
62 "JitRuntime::generateBaselineInterpreterEntryTrampoline");
64 #ifdef JS_USE_LINK_REGISTER
65 masm
.pushReturnAddress();
67 masm
.push(FramePointer
);
68 masm
.moveStackPtrTo(FramePointer
);
70 AllocatableGeneralRegisterSet
regs(GeneralRegisterSet::All());
71 Register nargs
= regs
.takeAny();
72 Register callee
= regs
.takeAny();
73 Register scratch
= regs
.takeAny();
75 // Load callee token and keep it in a register as it will be used often
76 Address
calleeTokenAddr(
77 FramePointer
, BaselineInterpreterEntryFrameLayout::offsetOfCalleeToken());
78 masm
.loadPtr(calleeTokenAddr
, callee
);
80 // Load argc into nargs.
81 masm
.loadNumActualArgs(FramePointer
, nargs
);
85 // Check if calleetoken is script or function
86 masm
.branchTestPtr(Assembler::NonZero
, callee
, Imm32(CalleeTokenScriptBit
),
89 // CalleeToken is a function, load |nformals| into scratch
90 masm
.movePtr(callee
, scratch
);
91 masm
.andPtr(Imm32(uint32_t(CalleeTokenMask
)), scratch
);
92 masm
.loadFunctionArgCount(scratch
, scratch
);
94 // Take max(nformals, argc).
96 masm
.branch32(Assembler::AboveOrEqual
, nargs
, scratch
, &noUnderflow
);
97 { masm
.movePtr(scratch
, nargs
); }
98 masm
.bind(&noUnderflow
);
100 // Add 1 to nargs if constructing.
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
);
108 masm
.bind(¬Function
);
111 masm
.alignJitStackBasedOnNArgs(nargs
, /*countIncludesThis = */ false);
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
);
121 // Load the end address into scratch, which is the callee token.
122 masm
.computeEffectiveAddress(calleeTokenAddr
, scratch
);
124 // Copy |this|+arguments
128 masm
.pushValue(Address(argPtr
, 0));
129 masm
.subPtr(Imm32(sizeof(Value
)), argPtr
);
130 masm
.branchPtr(Assembler::Above
, argPtr
, scratch
, &loop
);
136 // Save a new descriptor using BaselineInterpreterEntry frame type.
137 masm
.loadNumActualArgs(FramePointer
, scratch
);
138 masm
.pushFrameDescriptorForJitCall(FrameType::BaselineInterpreterEntry
,
141 // Call into baseline interpreter
142 uint8_t* blinterpAddr
= baselineInterpreter().codeRaw();
143 masm
.assertStackAlignment(JitStackAlignment
, 2 * sizeof(uintptr_t));
144 masm
.call(ImmPtr(blinterpAddr
));
146 masm
.moveToStackPtr(FramePointer
);
147 masm
.pop(FramePointer
);
151 void JitRuntime::generateInterpreterEntryTrampoline(MacroAssembler
& masm
) {
152 AutoCreatedBy
acb(masm
, "JitRuntime::generateInterpreterEntryTrampoline");
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
;
162 #ifdef JS_CODEGEN_ARM64
163 // Use the normal stack pointer for the initial pushes.
164 masm
.SetStackPointer64(sp
);
166 // Push lr and fp together to maintain 16-byte alignment.
167 masm
.push(lr
, FramePointer
);
168 masm
.moveStackPtrTo(FramePointer
);
170 // Save the PSP register (r28), and a scratch (r19).
173 // Setup the PSP so we can use callWithABI below.
174 masm
.SetStackPointer64(PseudoStackPointer64
);
175 masm
.initPseudoStackPtr();
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
);
184 AllocatableRegisterSet
regs(RegisterSet::Volatile());
185 Register arg0
= regs
.takeAnyGeneral();
186 Register arg1
= regs
.takeAnyGeneral();
187 Register scratch
= regs
.takeAnyGeneral();
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
);
195 masm
.push(FramePointer
);
196 masm
.moveStackPtrTo(FramePointer
);
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();
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
);
213 #ifdef JS_CODEGEN_ARM64
215 masm
.SetStackPointer64(sp
);
217 // Restore r28 and r19.
220 // Restore old fp and pop lr for return.
221 masm
.pop(FramePointer
, lr
);
224 // Reset stack pointer.
225 masm
.SetStackPointer64(PseudoStackPointer64
);
227 masm
.moveToStackPtr(FramePointer
);
228 masm
.pop(FramePointer
);
233 JitCode
* JitRuntime::generateEntryTrampolineForScript(JSContext
* cx
,
235 if (JitSpewEnabled(JitSpew_Codegen
)) {
237 if (script
->function() && script
->function()->fullDisplayAtom()) {
239 AtomToPrintableString(cx
, script
->function()->fullDisplayAtom());
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());
248 TempAllocator
temp(&cx
->tempLifoAlloc());
250 StackMacroAssembler
masm(cx
, temp
);
251 AutoCreatedBy
acb(masm
, "JitRuntime::generateEntryTrampolineForScript");
252 PerfSpewerRangeRecorder
rangeRecorder(masm
);
254 if (IsBaselineInterpreterEnabled()) {
255 generateBaselineInterpreterEntryTrampoline(masm
);
256 rangeRecorder
.recordOffset("BaselineInterpreter", cx
, script
);
259 generateInterpreterEntryTrampoline(masm
);
260 rangeRecorder
.recordOffset("Interpreter", cx
, script
);
263 JitCode
* code
= linker
.newCode(cx
, CodeKind::Other
);
267 rangeRecorder
.collectRangesForJitCode(code
);
268 JitSpew(JitSpew_Codegen
, "# code = %p", code
->raw());