Bug 1874684 - Part 31: Correctly reject invalid durations in some RoundDuration calls...
[gecko.git] / js / src / jit / BaselineFrameInfo.h
blob5d81a2fce1b33e062a321cf7048162bac0a1f9ab
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"
13 #include <new>
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"
21 namespace js {
22 namespace jit {
24 struct BytecodeInfo;
25 class MacroAssembler;
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
36 // always synced.
38 // CompilerFrameInfo
39 // -----------------
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:
48 // var y = x + 9;
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
56 // ops):
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.
66 class StackValue {
67 public:
68 enum Kind {
69 Constant,
70 Register,
71 Stack,
72 LocalSlot,
73 ArgSlot,
74 ThisSlot,
75 #ifdef DEBUG
76 // In debug builds, assert Kind is initialized.
77 Uninitialized,
78 #endif
81 private:
82 MOZ_INIT_OUTSIDE_CTOR Kind kind_;
84 MOZ_INIT_OUTSIDE_CTOR union Data {
85 JS::Value constant;
86 ValueOperand reg;
87 uint32_t localSlot;
88 uint32_t argSlot;
90 // |constant| has a non-trivial constructor and therefore MUST be
91 // placement-new'd into existence.
92 MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
93 Data() {}
94 MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
95 } data;
97 MOZ_INIT_OUTSIDE_CTOR JSValueType knownType_;
99 public:
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());
110 return knownType_;
112 void reset() {
113 #ifdef DEBUG
114 kind_ = Uninitialized;
115 knownType_ = JSVAL_TYPE_UNKNOWN;
116 #endif
118 Value constant() const {
119 MOZ_ASSERT(kind_ == Constant);
120 return data.constant;
122 ValueOperand reg() const {
123 MOZ_ASSERT(kind_ == Register);
124 return data.reg;
126 uint32_t localSlot() const {
127 MOZ_ASSERT(kind_ == LocalSlot);
128 return data.localSlot;
130 uint32_t argSlot() const {
131 MOZ_ASSERT(kind_ == ArgSlot);
132 return data.argSlot;
135 void setConstant(const Value& v) {
136 kind_ = Constant;
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) {
142 kind_ = Register;
143 new (&data.reg) ValueOperand(val);
144 knownType_ = knownType;
146 void setLocalSlot(uint32_t slot) {
147 kind_ = LocalSlot;
148 new (&data.localSlot) uint32_t(slot);
149 knownType_ = JSVAL_TYPE_UNKNOWN;
151 void setArgSlot(uint32_t slot) {
152 kind_ = ArgSlot;
153 new (&data.argSlot) uint32_t(slot);
154 knownType_ = JSVAL_TYPE_UNKNOWN;
156 void setThis() {
157 kind_ = ThisSlot;
158 knownType_ = JSVAL_TYPE_UNKNOWN;
160 void setStack() {
161 kind_ = Stack;
162 knownType_ = JSVAL_TYPE_UNKNOWN;
166 enum StackAdjustment { AdjustStack, DontAdjustStack };
168 class FrameInfo {
169 protected:
170 MacroAssembler& masm;
172 public:
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());
214 #ifdef DEBUG
215 Address addressOfDebugFrameSize() const {
216 return Address(FramePointer,
217 BaselineFrame::reverseOffsetOfDebugFrameSize());
219 #endif
222 class CompilerFrameInfo : public FrameInfo {
223 friend class BaselinePerfSpewer;
224 JSScript* script;
225 FixedList<StackValue> stack;
226 size_t spIndex;
228 public:
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(); }
236 private:
237 inline StackValue* rawPush() {
238 StackValue* val = &stack[spIndex++];
239 val->reset();
240 return val;
243 inline StackValue* peek(int32_t index) const {
244 MOZ_ASSERT(index < 0);
245 return const_cast<StackValue*>(&stack[spIndex + index]);
248 public:
249 inline size_t stackDepth() const { return spIndex; }
250 inline void setStackDepth(uint32_t newDepth) {
251 if (newDepth <= stackDepth()) {
252 spIndex = newDepth;
253 } else {
254 uint32_t diff = newDepth - stackDepth();
255 for (uint32_t i = 0; i < diff; i++) {
256 StackValue* val = rawPush();
257 val->setStack();
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();
286 sv->setArgSlot(arg);
288 inline void pushThis() {
289 StackValue* sv = rawPush();
290 sv->setThis();
293 inline void pushScratchValue() {
294 masm.pushValue(addressOfScratchValue());
295 StackValue* sv = rawPush();
296 sv->setStack();
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());
347 #ifdef DEBUG
348 // Assert the state is valid before excuting "pc".
349 void assertValidState(const BytecodeInfo& info);
350 #else
351 inline void assertValidState(const BytecodeInfo& info) {}
352 #endif
355 class InterpreterFrameInfo : public FrameInfo {
356 public:
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 {
369 return false;
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);
388 inline void pop();
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) {
404 masm.pushValue(val);
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());
432 } // namespace jit
433 } // namespace js
435 #endif /* jit_BaselineFrameInfo_h */