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 #ifndef jit_BaselineFrame_h
8 #define jit_BaselineFrame_h
12 #include "jit/CalleeToken.h"
13 #include "jit/JitFrames.h"
14 #include "jit/ScriptFromCalleeToken.h"
24 // The stack looks like this, fp is the frame pointer:
27 // fp => JitFrameLayout (frame header)
34 enum Flags
: uint32_t {
35 // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL.
38 // The frame is running in the Baseline interpreter instead of JIT.
39 RUNNING_IN_INTERPRETER
= 1 << 1,
41 // An initial environment has been pushed on the environment chain for
42 // function frames that need a CallObject or eval frames that need a
43 // VarEnvironmentObject.
44 HAS_INITIAL_ENV
= 1 << 2,
46 // Frame has an arguments object, argsObj_.
47 HAS_ARGS_OBJ
= 1 << 4,
49 // See InterpreterFrame::PREV_UP_TO_DATE.
50 PREV_UP_TO_DATE
= 1 << 5,
52 // Frame has execution observed by a Debugger.
54 // See comment above 'isDebuggee' in vm/Realm.h for explanation
55 // of invariants of debuggee compartments, scripts, and frames.
59 protected: // Silence Clang warning about unused private fields.
60 // The fields below are only valid if RUNNING_IN_INTERPRETER.
61 JSScript
* interpreterScript_
;
62 jsbytecode
* interpreterPC_
;
63 ICEntry
* interpreterICEntry_
;
65 JSObject
* envChain_
; // Environment chain (always initialized).
66 ICScript
* icScript_
; // IC script (initialized if Warp is enabled).
67 ArgumentsObject
* argsObj_
; // If HAS_ARGS_OBJ, the arguments object.
69 // We need to split the Value into 2 fields of 32 bits, otherwise the C++
70 // compiler may add some padding between the fields.
71 uint32_t loScratchValue_
;
72 uint32_t hiScratchValue_
;
75 // Size of the frame. Stored in DEBUG builds when calling into C++. This is
76 // BaselineFrame::Size() + the size of the local and expression stack Values.
78 // We don't store this in release builds because it's redundant with the frame
79 // size computed from the frame pointers. In debug builds it's still useful
81 uint32_t debugFrameSize_
;
85 uint32_t loReturnValue_
; // If HAS_RVAL, the frame's return value.
86 uint32_t hiReturnValue_
;
89 [[nodiscard
]] bool initForOsr(InterpreterFrame
* fp
, uint32_t numStackValues
);
92 uint32_t debugFrameSize() const { return debugFrameSize_
; }
93 void setDebugFrameSize(uint32_t frameSize
) { debugFrameSize_
= frameSize
; }
96 JSObject
* environmentChain() const { return envChain_
; }
97 void setEnvironmentChain(JSObject
* envChain
) { envChain_
= envChain
; }
99 template <typename SpecificEnvironment
>
100 inline void pushOnEnvironmentChain(SpecificEnvironment
& env
);
101 template <typename SpecificEnvironment
>
102 inline void popOffEnvironmentChain();
103 inline void replaceInnermostEnvironment(EnvironmentObject
& env
);
105 CalleeToken
calleeToken() const { return framePrefix()->calleeToken(); }
106 void replaceCalleeToken(CalleeToken token
) {
107 framePrefix()->replaceCalleeToken(token
);
109 bool isConstructing() const {
110 return CalleeTokenIsConstructing(calleeToken());
112 JSScript
* script() const {
113 return MaybeForwardedScriptFromCalleeToken(calleeToken());
115 JSFunction
* callee() const { return CalleeTokenToFunction(calleeToken()); }
116 Value
calleev() const { return ObjectValue(*callee()); }
118 size_t numValueSlots(size_t frameSize
) const {
119 MOZ_ASSERT(frameSize
== debugFrameSize());
121 MOZ_ASSERT(frameSize
>= BaselineFrame::Size());
122 frameSize
-= BaselineFrame::Size();
124 MOZ_ASSERT((frameSize
% sizeof(Value
)) == 0);
125 return frameSize
/ sizeof(Value
);
128 Value
newTarget() const {
129 MOZ_ASSERT(isFunctionFrame());
130 MOZ_ASSERT(!callee()->isArrow());
132 if (isConstructing()) {
133 unsigned pushedArgs
= std::max(numFormalArgs(), numActualArgs());
134 return argv()[pushedArgs
];
136 return UndefinedValue();
140 size_t debugNumValueSlots() const { return numValueSlots(debugFrameSize()); }
143 Value
* valueSlot(size_t slot
) const {
144 #ifndef ENABLE_PORTABLE_BASELINE_INTERP
145 // Assert that we're within the frame, but only if the "debug
146 // frame size" has been set. Ordinarily if we are in C++ code
147 // looking upward at a baseline frame, it will be, because it is
148 // set for the *previous* frame when we push an exit frame and
149 // call back into C++ from generated baseline code. However, the
150 // portable baseline interpreter uses accessors on BaselineFrame
151 // directly within the active frame and so the "debug frame size"
152 // hasn't been set (and it would be expensive to constantly update
153 // it). Because this is only used for assertions, and is not
154 // needed for correctness, we can disable this check below when
156 MOZ_ASSERT(slot
< debugNumValueSlots());
158 return (Value
*)this - (slot
+ 1);
161 static size_t frameSizeForNumValueSlots(size_t numValueSlots
) {
162 return BaselineFrame::Size() + numValueSlots
* sizeof(Value
);
165 Value
& unaliasedFormal(
166 unsigned i
, MaybeCheckAliasing checkAliasing
= CHECK_ALIASING
) const {
167 MOZ_ASSERT(i
< numFormalArgs());
168 MOZ_ASSERT_IF(checkAliasing
, !script()->argsObjAliasesFormals() &&
169 !script()->formalIsAliased(i
));
173 Value
& unaliasedActual(
174 unsigned i
, MaybeCheckAliasing checkAliasing
= CHECK_ALIASING
) const {
175 MOZ_ASSERT(i
< numActualArgs());
176 MOZ_ASSERT_IF(checkAliasing
, !script()->argsObjAliasesFormals());
177 MOZ_ASSERT_IF(checkAliasing
&& i
< numFormalArgs(),
178 !script()->formalIsAliased(i
));
182 Value
& unaliasedLocal(uint32_t i
) const {
183 MOZ_ASSERT(i
< script()->nfixed());
184 return *valueSlot(i
);
187 unsigned numActualArgs() const { return framePrefix()->numActualArgs(); }
188 unsigned numFormalArgs() const { return script()->function()->nargs(); }
189 Value
& thisArgument() const {
190 MOZ_ASSERT(isFunctionFrame());
191 return framePrefix()->thisv();
193 Value
* argv() const { return framePrefix()->actualArgs(); }
195 [[nodiscard
]] bool saveGeneratorSlots(JSContext
* cx
, unsigned nslots
,
196 ArrayObject
* dest
) const;
199 void prepareForBaselineInterpreterToJitOSR() {
200 // Clearing the RUNNING_IN_INTERPRETER flag is sufficient, but we also null
201 // out the interpreter fields to ensure we don't use stale values.
202 flags_
&= ~RUNNING_IN_INTERPRETER
;
203 interpreterScript_
= nullptr;
204 interpreterPC_
= nullptr;
208 bool uninlineIsProfilerSamplingEnabled(JSContext
* cx
);
211 // Switch a JIT frame on the stack to Interpreter mode. The caller is
212 // responsible for patching the return address into this frame to a location
213 // in the interpreter code. Also assert profiler sampling has been suppressed
214 // so the sampler thread doesn't see an inconsistent state while we are
216 void switchFromJitToInterpreter(JSContext
* cx
, jsbytecode
* pc
) {
217 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx
));
218 MOZ_ASSERT(!runningInInterpreter());
219 flags_
|= RUNNING_IN_INTERPRETER
;
220 setInterpreterFields(pc
);
222 void switchFromJitToInterpreterAtPrologue(JSContext
* cx
) {
223 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx
));
224 MOZ_ASSERT(!runningInInterpreter());
225 flags_
|= RUNNING_IN_INTERPRETER
;
226 setInterpreterFieldsForPrologue(script());
229 // Like switchFromJitToInterpreter, but set the interpreterICEntry_ field to
230 // nullptr. Initializing this field requires a binary search on the
231 // JitScript's ICEntry list but the exception handler never returns to this
232 // pc anyway so we can avoid the overhead.
233 void switchFromJitToInterpreterForExceptionHandler(JSContext
* cx
,
235 MOZ_ASSERT(!uninlineIsProfilerSamplingEnabled(cx
));
236 MOZ_ASSERT(!runningInInterpreter());
237 flags_
|= RUNNING_IN_INTERPRETER
;
238 interpreterScript_
= script();
240 interpreterICEntry_
= nullptr;
243 bool runningInInterpreter() const { return flags_
& RUNNING_IN_INTERPRETER
; }
245 JSScript
* interpreterScript() const {
246 MOZ_ASSERT(runningInInterpreter());
247 return interpreterScript_
;
250 jsbytecode
* interpreterPC() const {
251 MOZ_ASSERT(runningInInterpreter());
252 return interpreterPC_
;
254 jsbytecode
*& interpreterPC() {
255 MOZ_ASSERT(runningInInterpreter());
256 return interpreterPC_
;
259 ICEntry
* interpreterICEntry() const {
260 MOZ_ASSERT(runningInInterpreter());
261 return interpreterICEntry_
;
263 ICEntry
*& interpreterICEntry() {
264 MOZ_ASSERT(runningInInterpreter());
265 return interpreterICEntry_
;
268 void setInterpreterFields(JSScript
* script
, jsbytecode
* pc
);
270 void setInterpreterFields(jsbytecode
* pc
) {
271 setInterpreterFields(script(), pc
);
274 // Initialize interpreter fields for resuming in the prologue (before the
275 // argument type check ICs).
276 void setInterpreterFieldsForPrologue(JSScript
* script
);
278 ICScript
* icScript() const { return icScript_
; }
279 void setICScript(ICScript
* icScript
) { icScript_
= icScript
; }
281 // The script that owns the current ICScript.
282 JSScript
* outerScript() const;
284 bool hasReturnValue() const { return flags_
& HAS_RVAL
; }
285 MutableHandleValue
returnValue() {
286 if (!hasReturnValue()) {
287 addressOfReturnValue()->setUndefined();
289 return MutableHandleValue::fromMarkedLocation(addressOfReturnValue());
291 void setReturnValue(const Value
& v
) {
292 returnValue().set(v
);
295 inline Value
* addressOfReturnValue() {
296 return reinterpret_cast<Value
*>(&loReturnValue_
);
299 bool hasInitialEnvironment() const { return flags_
& HAS_INITIAL_ENV
; }
301 inline CallObject
& callObj() const;
303 void setFlag(uint32_t flag
) { flags_
|= flag
; }
304 void setFlags(uint32_t flags
) { flags_
= flags
; }
306 [[nodiscard
]] inline bool pushLexicalEnvironment(JSContext
* cx
,
307 Handle
<LexicalScope
*> scope
);
308 template <bool IsDebuggee
>
309 [[nodiscard
]] inline bool freshenLexicalEnvironment(
310 JSContext
* cx
, const jsbytecode
* pc
= nullptr);
311 template <bool IsDebuggee
>
312 [[nodiscard
]] inline bool recreateLexicalEnvironment(
313 JSContext
* cx
, const jsbytecode
* pc
= nullptr);
315 [[nodiscard
]] bool initFunctionEnvironmentObjects(JSContext
* cx
);
316 [[nodiscard
]] bool pushClassBodyEnvironment(JSContext
* cx
,
317 Handle
<ClassBodyScope
*> scope
);
318 [[nodiscard
]] bool pushVarEnvironment(JSContext
* cx
, Handle
<Scope
*> scope
);
320 void initArgsObjUnchecked(ArgumentsObject
& argsobj
) {
321 flags_
|= HAS_ARGS_OBJ
;
324 void initArgsObj(ArgumentsObject
& argsobj
) {
325 MOZ_ASSERT(script()->needsArgsObj());
326 initArgsObjUnchecked(argsobj
);
328 bool hasArgsObj() const { return flags_
& HAS_ARGS_OBJ
; }
329 ArgumentsObject
& argsObj() const {
330 MOZ_ASSERT(hasArgsObj());
331 MOZ_ASSERT(script()->needsArgsObj());
335 bool prevUpToDate() const { return flags_
& PREV_UP_TO_DATE
; }
336 void setPrevUpToDate() { flags_
|= PREV_UP_TO_DATE
; }
337 void unsetPrevUpToDate() { flags_
&= ~PREV_UP_TO_DATE
; }
339 bool isDebuggee() const { return flags_
& DEBUGGEE
; }
340 void setIsDebuggee() { flags_
|= DEBUGGEE
; }
341 inline void unsetIsDebuggee();
343 void trace(JSTracer
* trc
, const JSJitFrameIter
& frame
);
345 bool isGlobalFrame() const { return script()->isGlobalCode(); }
346 bool isModuleFrame() const { return script()->isModule(); }
347 bool isEvalFrame() const { return script()->isForEval(); }
348 bool isFunctionFrame() const {
349 return CalleeTokenIsFunction(calleeToken()) && !isModuleFrame();
351 bool isDebuggerEvalFrame() const { return false; }
353 JitFrameLayout
* framePrefix() const {
354 uint8_t* fp
= (uint8_t*)this + Size();
355 return (JitFrameLayout
*)fp
;
358 static size_t Size() { return sizeof(BaselineFrame
); }
360 // The reverseOffsetOf methods below compute the offset relative to the
361 // frame's base pointer. Since the stack grows down, these offsets are
365 static int reverseOffsetOfDebugFrameSize() {
366 return -int(Size()) + offsetof(BaselineFrame
, debugFrameSize_
);
370 // The scratch value slot can either be used as a Value slot or as two
371 // separate 32-bit integer slots.
372 static int reverseOffsetOfScratchValueLow32() {
373 return -int(Size()) + offsetof(BaselineFrame
, loScratchValue_
);
375 static int reverseOffsetOfScratchValueHigh32() {
376 return -int(Size()) + offsetof(BaselineFrame
, hiScratchValue_
);
378 static int reverseOffsetOfScratchValue() {
379 return reverseOffsetOfScratchValueLow32();
382 static int reverseOffsetOfEnvironmentChain() {
383 return -int(Size()) + offsetof(BaselineFrame
, envChain_
);
385 static int reverseOffsetOfArgsObj() {
386 return -int(Size()) + offsetof(BaselineFrame
, argsObj_
);
388 static int reverseOffsetOfFlags() {
389 return -int(Size()) + offsetof(BaselineFrame
, flags_
);
391 static int reverseOffsetOfReturnValue() {
392 return -int(Size()) + offsetof(BaselineFrame
, loReturnValue_
);
394 static int reverseOffsetOfInterpreterScript() {
395 return -int(Size()) + offsetof(BaselineFrame
, interpreterScript_
);
397 static int reverseOffsetOfInterpreterPC() {
398 return -int(Size()) + offsetof(BaselineFrame
, interpreterPC_
);
400 static int reverseOffsetOfInterpreterICEntry() {
401 return -int(Size()) + offsetof(BaselineFrame
, interpreterICEntry_
);
403 static int reverseOffsetOfICScript() {
404 return -int(Size()) + offsetof(BaselineFrame
, icScript_
);
406 static int reverseOffsetOfLocal(size_t index
) {
407 return -int(Size()) - (index
+ 1) * sizeof(Value
);
411 // Ensure the frame is 8-byte aligned (required on ARM).
412 static_assert((sizeof(BaselineFrame
) % 8) == 0, "frame must be 8-byte aligned");
417 #endif /* jit_BaselineFrame_h */