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_BaselineFrameInfo_h
8 #define jit_BaselineFrameInfo_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
15 #include "jit/BaselineFrame.h"
16 #include "jit/BaselineJIT.h"
17 #include "jit/FixedList.h"
18 #include "jit/MacroAssembler.h"
19 #include "jit/SharedICRegisters.h"
27 // [SMDOC] Baseline FrameInfo overview.
29 // FrameInfo is used by BaselineCodeGen to track values stored in the frame.
30 // There are two implementations:
32 // InterpreterFrameInfo
33 // --------------------
34 // The InterpreterFrameInfo class is used by the interpreter generator and is
35 // a very simple interface on top of the MacroAssembler, because the stack is
40 // The CompilerFrameInfo class is more complicated because it maintains a
41 // virtual stack to optimize some common stack operations. Locals and arguments
42 // are always fully synced. Stack values can either be synced, stored as
43 // constant, stored in a Value register or refer to a local slot. Syncing a
44 // StackValue ensures it's stored on the stack, e.g. kind == Stack.
46 // To see how this works, consider the following statement:
50 // Here two values are pushed: StackValue(LocalSlot(0)) and
51 // StackValue(Int32Value(9)). Only when we reach the ADD op, code is generated
52 // to load the operands directly into the right operand registers and sync all
53 // other stack values.
55 // For stack values, the following invariants hold (and are checked between
58 // (1) If a value is synced (kind == Stack), all values below it must also be
59 // synced. In other words, values with kind other than Stack can only appear
60 // on top of the abstract stack.
62 // (2) When we call a stub or IC, all values still on the stack must be synced.
64 // Represents a value pushed on the stack. Note that StackValue is not used for
65 // locals or arguments since these are always fully synced.
76 // In debug builds, assert Kind is initialized.
82 MOZ_INIT_OUTSIDE_CTOR Kind kind_
;
84 MOZ_INIT_OUTSIDE_CTOR
union Data
{
90 // |constant| has a non-trivial constructor and therefore MUST be
91 // placement-new'd into existence.
92 MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
94 MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
97 MOZ_INIT_OUTSIDE_CTOR JSValueType knownType_
;
100 StackValue() { reset(); }
102 Kind
kind() const { return kind_
; }
103 bool hasKnownType() const { return knownType_
!= JSVAL_TYPE_UNKNOWN
; }
104 bool hasKnownType(JSValueType type
) const {
105 MOZ_ASSERT(type
!= JSVAL_TYPE_UNKNOWN
);
106 return knownType_
== type
;
108 JSValueType
knownType() const {
109 MOZ_ASSERT(hasKnownType());
114 kind_
= Uninitialized
;
115 knownType_
= JSVAL_TYPE_UNKNOWN
;
118 Value
constant() const {
119 MOZ_ASSERT(kind_
== Constant
);
120 return data
.constant
;
122 ValueOperand
reg() const {
123 MOZ_ASSERT(kind_
== Register
);
126 uint32_t localSlot() const {
127 MOZ_ASSERT(kind_
== LocalSlot
);
128 return data
.localSlot
;
130 uint32_t argSlot() const {
131 MOZ_ASSERT(kind_
== ArgSlot
);
135 void setConstant(const Value
& v
) {
137 new (&data
.constant
) Value(v
);
138 knownType_
= v
.isDouble() ? JSVAL_TYPE_DOUBLE
: v
.extractNonDoubleType();
140 void setRegister(const ValueOperand
& val
,
141 JSValueType knownType
= JSVAL_TYPE_UNKNOWN
) {
143 new (&data
.reg
) ValueOperand(val
);
144 knownType_
= knownType
;
146 void setLocalSlot(uint32_t slot
) {
148 new (&data
.localSlot
) uint32_t(slot
);
149 knownType_
= JSVAL_TYPE_UNKNOWN
;
151 void setArgSlot(uint32_t slot
) {
153 new (&data
.argSlot
) uint32_t(slot
);
154 knownType_
= JSVAL_TYPE_UNKNOWN
;
158 knownType_
= JSVAL_TYPE_UNKNOWN
;
162 knownType_
= JSVAL_TYPE_UNKNOWN
;
166 enum StackAdjustment
{ AdjustStack
, DontAdjustStack
};
170 MacroAssembler
& masm
;
173 explicit FrameInfo(MacroAssembler
& masm
) : masm(masm
) {}
175 Address
addressOfLocal(size_t local
) const {
176 return Address(FramePointer
, BaselineFrame::reverseOffsetOfLocal(local
));
178 Address
addressOfArg(size_t arg
) const {
179 return Address(FramePointer
, JitFrameLayout::offsetOfActualArg(arg
));
181 Address
addressOfThis() const {
182 return Address(FramePointer
, JitFrameLayout::offsetOfThis());
184 Address
addressOfCalleeToken() const {
185 return Address(FramePointer
, JitFrameLayout::offsetOfCalleeToken());
187 Address
addressOfEnvironmentChain() const {
188 return Address(FramePointer
,
189 BaselineFrame::reverseOffsetOfEnvironmentChain());
191 Address
addressOfICScript() const {
192 return Address(FramePointer
, BaselineFrame::reverseOffsetOfICScript());
194 Address
addressOfFlags() const {
195 return Address(FramePointer
, BaselineFrame::reverseOffsetOfFlags());
197 Address
addressOfReturnValue() const {
198 return Address(FramePointer
, BaselineFrame::reverseOffsetOfReturnValue());
200 Address
addressOfArgsObj() const {
201 return Address(FramePointer
, BaselineFrame::reverseOffsetOfArgsObj());
203 Address
addressOfScratchValue() const {
204 return Address(FramePointer
, BaselineFrame::reverseOffsetOfScratchValue());
206 Address
addressOfScratchValueLow32() const {
207 return Address(FramePointer
,
208 BaselineFrame::reverseOffsetOfScratchValueLow32());
210 Address
addressOfScratchValueHigh32() const {
211 return Address(FramePointer
,
212 BaselineFrame::reverseOffsetOfScratchValueHigh32());
215 Address
addressOfDebugFrameSize() const {
216 return Address(FramePointer
,
217 BaselineFrame::reverseOffsetOfDebugFrameSize());
222 class CompilerFrameInfo
: public FrameInfo
{
223 friend class BaselinePerfSpewer
;
225 FixedList
<StackValue
> stack
;
229 CompilerFrameInfo(JSScript
* script
, MacroAssembler
& masm
)
230 : FrameInfo(masm
), script(script
), spIndex(0) {}
231 [[nodiscard
]] bool init(TempAllocator
& alloc
);
233 size_t nlocals() const { return script
->nfixed(); }
234 size_t nargs() const { return script
->function()->nargs(); }
237 inline StackValue
* rawPush() {
238 StackValue
* val
= &stack
[spIndex
++];
243 inline StackValue
* peek(int32_t index
) const {
244 MOZ_ASSERT(index
< 0);
245 return const_cast<StackValue
*>(&stack
[spIndex
+ index
]);
249 inline size_t stackDepth() const { return spIndex
; }
250 inline void setStackDepth(uint32_t newDepth
) {
251 if (newDepth
<= stackDepth()) {
254 uint32_t diff
= newDepth
- stackDepth();
255 for (uint32_t i
= 0; i
< diff
; i
++) {
256 StackValue
* val
= rawPush();
260 MOZ_ASSERT(spIndex
== newDepth
);
264 void assertStackDepth(uint32_t depth
) { MOZ_ASSERT(stackDepth() == depth
); }
265 void incStackDepth(int32_t diff
) { setStackDepth(stackDepth() + diff
); }
266 bool hasKnownStackDepth(uint32_t depth
) { return stackDepth() == depth
; }
268 inline void pop(StackAdjustment adjust
= AdjustStack
);
269 inline void popn(uint32_t n
, StackAdjustment adjust
= AdjustStack
);
270 inline void push(const Value
& val
) {
271 StackValue
* sv
= rawPush();
272 sv
->setConstant(val
);
274 inline void push(const ValueOperand
& val
,
275 JSValueType knownType
= JSVAL_TYPE_UNKNOWN
) {
276 StackValue
* sv
= rawPush();
277 sv
->setRegister(val
, knownType
);
279 inline void pushLocal(uint32_t local
) {
280 MOZ_ASSERT(local
< nlocals());
281 StackValue
* sv
= rawPush();
282 sv
->setLocalSlot(local
);
284 inline void pushArg(uint32_t arg
) {
285 StackValue
* sv
= rawPush();
288 inline void pushThis() {
289 StackValue
* sv
= rawPush();
293 inline void pushScratchValue() {
294 masm
.pushValue(addressOfScratchValue());
295 StackValue
* sv
= rawPush();
299 Address
addressOfLocal(size_t local
) const {
300 MOZ_ASSERT(local
< nlocals());
301 return FrameInfo::addressOfLocal(local
);
303 Address
addressOfArg(size_t arg
) const {
304 MOZ_ASSERT(arg
< nargs());
305 return FrameInfo::addressOfArg(arg
);
308 Address
addressOfStackValue(int32_t depth
) const {
309 const StackValue
* value
= peek(depth
);
310 MOZ_ASSERT(value
->kind() == StackValue::Stack
);
311 size_t slot
= value
- &stack
[0];
312 MOZ_ASSERT(slot
< stackDepth());
313 return Address(FramePointer
,
314 BaselineFrame::reverseOffsetOfLocal(nlocals() + slot
));
317 void popValue(ValueOperand dest
);
319 void sync(StackValue
* val
);
320 void syncStack(uint32_t uses
);
321 uint32_t numUnsyncedSlots();
322 void popRegsAndSync(uint32_t uses
);
324 void assertSyncedStack() const {
325 MOZ_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack
);
328 bool stackValueHasKnownType(int32_t depth
, JSValueType type
) const {
329 return peek(depth
)->hasKnownType(type
);
332 mozilla::Maybe
<Value
> knownStackValue(int32_t depth
) const {
333 StackValue
* val
= peek(depth
);
334 if (val
->kind() == StackValue::Constant
) {
335 return mozilla::Some(val
->constant());
337 return mozilla::Nothing();
340 void storeStackValue(int32_t depth
, const Address
& dest
,
341 const ValueOperand
& scratch
);
343 uint32_t frameSize() const {
344 return BaselineFrame::frameSizeForNumValueSlots(nlocals() + stackDepth());
348 // Assert the state is valid before excuting "pc".
349 void assertValidState(const BytecodeInfo
& info
);
351 inline void assertValidState(const BytecodeInfo
& info
) {}
355 class InterpreterFrameInfo
: public FrameInfo
{
357 explicit InterpreterFrameInfo(MacroAssembler
& masm
) : FrameInfo(masm
) {}
359 // These methods are no-ops in the interpreter, because we don't have a
360 // virtual stack there.
361 void syncStack(uint32_t uses
) {}
362 void assertSyncedStack() const {}
363 void assertStackDepth(uint32_t depth
) {}
364 void incStackDepth(int32_t diff
) {}
365 bool hasKnownStackDepth(uint32_t depth
) { return false; }
366 uint32_t numUnsyncedSlots() { return 0; }
368 bool stackValueHasKnownType(int32_t depth
, JSValueType type
) const {
372 mozilla::Maybe
<Value
> knownStackValue(int32_t depth
) const {
373 return mozilla::Nothing();
376 Address
addressOfStackValue(int depth
) const {
377 MOZ_ASSERT(depth
< 0);
378 return Address(masm
.getStackPointer(),
379 masm
.framePushed() + size_t(-(depth
+ 1)) * sizeof(Value
));
382 BaseIndex
addressOfStackValue(Register index
, int32_t offset
= 0) const {
383 return BaseIndex(masm
.getStackPointer(), index
, ValueScale
, offset
);
386 void popRegsAndSync(uint32_t uses
);
390 inline void popn(uint32_t n
);
392 void popn(Register reg
) {
393 // sp := sp + reg * sizeof(Value)
394 Register spReg
= AsRegister(masm
.getStackPointer());
395 masm
.computeEffectiveAddress(BaseValueIndex(spReg
, reg
), spReg
);
396 // On arm64, SP may be < PSP now (that's OK).
397 // eg testcase: tests/arguments/strict-args-generator-flushstack.js
400 void popValue(ValueOperand dest
) { masm
.popValue(dest
); }
402 void push(const ValueOperand
& val
,
403 JSValueType knownType
= JSVAL_TYPE_UNKNOWN
) {
406 void push(const Value
& val
) { masm
.pushValue(val
); }
408 void pushThis() { masm
.pushValue(addressOfThis()); }
409 void pushScratchValue() { masm
.pushValue(addressOfScratchValue()); }
411 void storeStackValue(int32_t depth
, const Address
& dest
,
412 const ValueOperand
& scratch
) {
413 masm
.loadValue(addressOfStackValue(depth
), scratch
);
414 masm
.storeValue(scratch
, dest
);
417 void bumpInterpreterICEntry();
419 Address
addressOfInterpreterScript() const {
420 return Address(FramePointer
,
421 BaselineFrame::reverseOffsetOfInterpreterScript());
423 Address
addressOfInterpreterPC() const {
424 return Address(FramePointer
, BaselineFrame::reverseOffsetOfInterpreterPC());
426 Address
addressOfInterpreterICEntry() const {
427 return Address(FramePointer
,
428 BaselineFrame::reverseOffsetOfInterpreterICEntry());
435 #endif /* jit_BaselineFrameInfo_h */