summaryrefslogtreecommitdiffstats
path: root/js/src/jit/BaselineFrameInfo.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/BaselineFrameInfo.h')
-rw-r--r--js/src/jit/BaselineFrameInfo.h435
1 files changed, 435 insertions, 0 deletions
diff --git a/js/src/jit/BaselineFrameInfo.h b/js/src/jit/BaselineFrameInfo.h
new file mode 100644
index 0000000000..2e4c994d99
--- /dev/null
+++ b/js/src/jit/BaselineFrameInfo.h
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_BaselineFrameInfo_h
+#define jit_BaselineFrameInfo_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <new>
+
+#include "jit/BaselineFrame.h"
+#include "jit/BaselineJIT.h"
+#include "jit/FixedList.h"
+#include "jit/MacroAssembler.h"
+#include "jit/SharedICRegisters.h"
+
+namespace js {
+namespace jit {
+
+struct BytecodeInfo;
+class MacroAssembler;
+
+// [SMDOC] Baseline FrameInfo overview.
+//
+// FrameInfo is used by BaselineCodeGen to track values stored in the frame.
+// There are two implementations:
+//
+// InterpreterFrameInfo
+// --------------------
+// The InterpreterFrameInfo class is used by the interpreter generator and is
+// a very simple interface on top of the MacroAssembler, because the stack is
+// always synced.
+//
+// CompilerFrameInfo
+// -----------------
+// The CompilerFrameInfo class is more complicated because it maintains a
+// virtual stack to optimize some common stack operations. Locals and arguments
+// are always fully synced. Stack values can either be synced, stored as
+// constant, stored in a Value register or refer to a local slot. Syncing a
+// StackValue ensures it's stored on the stack, e.g. kind == Stack.
+//
+// To see how this works, consider the following statement:
+//
+// var y = x + 9;
+//
+// Here two values are pushed: StackValue(LocalSlot(0)) and
+// StackValue(Int32Value(9)). Only when we reach the ADD op, code is generated
+// to load the operands directly into the right operand registers and sync all
+// other stack values.
+//
+// For stack values, the following invariants hold (and are checked between
+// ops):
+//
+// (1) If a value is synced (kind == Stack), all values below it must also be
+// synced. In other words, values with kind other than Stack can only appear
+// on top of the abstract stack.
+//
+// (2) When we call a stub or IC, all values still on the stack must be synced.
+
+// Represents a value pushed on the stack. Note that StackValue is not used for
+// locals or arguments since these are always fully synced.
+class StackValue {
+ public:
+ enum Kind {
+ Constant,
+ Register,
+ Stack,
+ LocalSlot,
+ ArgSlot,
+ ThisSlot,
+#ifdef DEBUG
+ // In debug builds, assert Kind is initialized.
+ Uninitialized,
+#endif
+ };
+
+ private:
+ MOZ_INIT_OUTSIDE_CTOR Kind kind_;
+
+ MOZ_INIT_OUTSIDE_CTOR union Data {
+ JS::Value constant;
+ ValueOperand reg;
+ uint32_t localSlot;
+ uint32_t argSlot;
+
+ // |constant| has a non-trivial constructor and therefore MUST be
+ // placement-new'd into existence.
+ MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
+ Data() {}
+ MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
+ } data;
+
+ MOZ_INIT_OUTSIDE_CTOR JSValueType knownType_;
+
+ public:
+ StackValue() { reset(); }
+
+ Kind kind() const { return kind_; }
+ bool hasKnownType() const { return knownType_ != JSVAL_TYPE_UNKNOWN; }
+ bool hasKnownType(JSValueType type) const {
+ MOZ_ASSERT(type != JSVAL_TYPE_UNKNOWN);
+ return knownType_ == type;
+ }
+ JSValueType knownType() const {
+ MOZ_ASSERT(hasKnownType());
+ return knownType_;
+ }
+ void reset() {
+#ifdef DEBUG
+ kind_ = Uninitialized;
+ knownType_ = JSVAL_TYPE_UNKNOWN;
+#endif
+ }
+ Value constant() const {
+ MOZ_ASSERT(kind_ == Constant);
+ return data.constant;
+ }
+ ValueOperand reg() const {
+ MOZ_ASSERT(kind_ == Register);
+ return data.reg;
+ }
+ uint32_t localSlot() const {
+ MOZ_ASSERT(kind_ == LocalSlot);
+ return data.localSlot;
+ }
+ uint32_t argSlot() const {
+ MOZ_ASSERT(kind_ == ArgSlot);
+ return data.argSlot;
+ }
+
+ void setConstant(const Value& v) {
+ kind_ = Constant;
+ new (&data.constant) Value(v);
+ knownType_ = v.isDouble() ? JSVAL_TYPE_DOUBLE : v.extractNonDoubleType();
+ }
+ void setRegister(const ValueOperand& val,
+ JSValueType knownType = JSVAL_TYPE_UNKNOWN) {
+ kind_ = Register;
+ new (&data.reg) ValueOperand(val);
+ knownType_ = knownType;
+ }
+ void setLocalSlot(uint32_t slot) {
+ kind_ = LocalSlot;
+ new (&data.localSlot) uint32_t(slot);
+ knownType_ = JSVAL_TYPE_UNKNOWN;
+ }
+ void setArgSlot(uint32_t slot) {
+ kind_ = ArgSlot;
+ new (&data.argSlot) uint32_t(slot);
+ knownType_ = JSVAL_TYPE_UNKNOWN;
+ }
+ void setThis() {
+ kind_ = ThisSlot;
+ knownType_ = JSVAL_TYPE_UNKNOWN;
+ }
+ void setStack() {
+ kind_ = Stack;
+ knownType_ = JSVAL_TYPE_UNKNOWN;
+ }
+};
+
+enum StackAdjustment { AdjustStack, DontAdjustStack };
+
+class FrameInfo {
+ protected:
+ MacroAssembler& masm;
+
+ public:
+ explicit FrameInfo(MacroAssembler& masm) : masm(masm) {}
+
+ Address addressOfLocal(size_t local) const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfLocal(local));
+ }
+ Address addressOfArg(size_t arg) const {
+ return Address(FramePointer, JitFrameLayout::offsetOfActualArg(arg));
+ }
+ Address addressOfThis() const {
+ return Address(FramePointer, JitFrameLayout::offsetOfThis());
+ }
+ Address addressOfCalleeToken() const {
+ return Address(FramePointer, JitFrameLayout::offsetOfCalleeToken());
+ }
+ Address addressOfEnvironmentChain() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfEnvironmentChain());
+ }
+ Address addressOfICScript() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfICScript());
+ }
+ Address addressOfFlags() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfFlags());
+ }
+ Address addressOfReturnValue() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue());
+ }
+ Address addressOfArgsObj() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfArgsObj());
+ }
+ Address addressOfScratchValue() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfScratchValue());
+ }
+ Address addressOfScratchValueLow32() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfScratchValueLow32());
+ }
+ Address addressOfScratchValueHigh32() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfScratchValueHigh32());
+ }
+#ifdef DEBUG
+ Address addressOfDebugFrameSize() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfDebugFrameSize());
+ }
+#endif
+};
+
+class CompilerFrameInfo : public FrameInfo {
+ friend class BaselinePerfSpewer;
+ JSScript* script;
+ FixedList<StackValue> stack;
+ size_t spIndex;
+
+ public:
+ CompilerFrameInfo(JSScript* script, MacroAssembler& masm)
+ : FrameInfo(masm), script(script), stack(), spIndex(0) {}
+ [[nodiscard]] bool init(TempAllocator& alloc);
+
+ size_t nlocals() const { return script->nfixed(); }
+ size_t nargs() const { return script->function()->nargs(); }
+
+ private:
+ inline StackValue* rawPush() {
+ StackValue* val = &stack[spIndex++];
+ val->reset();
+ return val;
+ }
+
+ inline StackValue* peek(int32_t index) const {
+ MOZ_ASSERT(index < 0);
+ return const_cast<StackValue*>(&stack[spIndex + index]);
+ }
+
+ public:
+ inline size_t stackDepth() const { return spIndex; }
+ inline void setStackDepth(uint32_t newDepth) {
+ if (newDepth <= stackDepth()) {
+ spIndex = newDepth;
+ } else {
+ uint32_t diff = newDepth - stackDepth();
+ for (uint32_t i = 0; i < diff; i++) {
+ StackValue* val = rawPush();
+ val->setStack();
+ }
+
+ MOZ_ASSERT(spIndex == newDepth);
+ }
+ }
+
+ void assertStackDepth(uint32_t depth) { MOZ_ASSERT(stackDepth() == depth); }
+ void incStackDepth(int32_t diff) { setStackDepth(stackDepth() + diff); }
+ bool hasKnownStackDepth(uint32_t depth) { return stackDepth() == depth; }
+
+ inline void pop(StackAdjustment adjust = AdjustStack);
+ inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack);
+ inline void push(const Value& val) {
+ StackValue* sv = rawPush();
+ sv->setConstant(val);
+ }
+ inline void push(const ValueOperand& val,
+ JSValueType knownType = JSVAL_TYPE_UNKNOWN) {
+ StackValue* sv = rawPush();
+ sv->setRegister(val, knownType);
+ }
+ inline void pushLocal(uint32_t local) {
+ MOZ_ASSERT(local < nlocals());
+ StackValue* sv = rawPush();
+ sv->setLocalSlot(local);
+ }
+ inline void pushArg(uint32_t arg) {
+ StackValue* sv = rawPush();
+ sv->setArgSlot(arg);
+ }
+ inline void pushThis() {
+ StackValue* sv = rawPush();
+ sv->setThis();
+ }
+
+ inline void pushScratchValue() {
+ masm.pushValue(addressOfScratchValue());
+ StackValue* sv = rawPush();
+ sv->setStack();
+ }
+
+ Address addressOfLocal(size_t local) const {
+ MOZ_ASSERT(local < nlocals());
+ return FrameInfo::addressOfLocal(local);
+ }
+ Address addressOfArg(size_t arg) const {
+ MOZ_ASSERT(arg < nargs());
+ return FrameInfo::addressOfArg(arg);
+ }
+
+ Address addressOfStackValue(int32_t depth) const {
+ const StackValue* value = peek(depth);
+ MOZ_ASSERT(value->kind() == StackValue::Stack);
+ size_t slot = value - &stack[0];
+ MOZ_ASSERT(slot < stackDepth());
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfLocal(nlocals() + slot));
+ }
+
+ void popValue(ValueOperand dest);
+
+ void sync(StackValue* val);
+ void syncStack(uint32_t uses);
+ uint32_t numUnsyncedSlots();
+ void popRegsAndSync(uint32_t uses);
+
+ void assertSyncedStack() const {
+ MOZ_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack);
+ }
+
+ bool stackValueHasKnownType(int32_t depth, JSValueType type) const {
+ return peek(depth)->hasKnownType(type);
+ }
+
+ mozilla::Maybe<Value> knownStackValue(int32_t depth) const {
+ StackValue* val = peek(depth);
+ if (val->kind() == StackValue::Constant) {
+ return mozilla::Some(val->constant());
+ }
+ return mozilla::Nothing();
+ }
+
+ void storeStackValue(int32_t depth, const Address& dest,
+ const ValueOperand& scratch);
+
+ uint32_t frameSize() const {
+ return BaselineFrame::frameSizeForNumValueSlots(nlocals() + stackDepth());
+ }
+
+#ifdef DEBUG
+ // Assert the state is valid before excuting "pc".
+ void assertValidState(const BytecodeInfo& info);
+#else
+ inline void assertValidState(const BytecodeInfo& info) {}
+#endif
+};
+
+class InterpreterFrameInfo : public FrameInfo {
+ public:
+ explicit InterpreterFrameInfo(MacroAssembler& masm) : FrameInfo(masm) {}
+
+ // These methods are no-ops in the interpreter, because we don't have a
+ // virtual stack there.
+ void syncStack(uint32_t uses) {}
+ void assertSyncedStack() const {}
+ void assertStackDepth(uint32_t depth) {}
+ void incStackDepth(int32_t diff) {}
+ bool hasKnownStackDepth(uint32_t depth) { return false; }
+ uint32_t numUnsyncedSlots() { return 0; }
+
+ bool stackValueHasKnownType(int32_t depth, JSValueType type) const {
+ return false;
+ }
+
+ mozilla::Maybe<Value> knownStackValue(int32_t depth) const {
+ return mozilla::Nothing();
+ }
+
+ Address addressOfStackValue(int depth) const {
+ MOZ_ASSERT(depth < 0);
+ return Address(masm.getStackPointer(),
+ masm.framePushed() + size_t(-(depth + 1)) * sizeof(Value));
+ }
+
+ BaseIndex addressOfStackValue(Register index, int32_t offset = 0) const {
+ return BaseIndex(masm.getStackPointer(), index, ValueScale, offset);
+ }
+
+ void popRegsAndSync(uint32_t uses);
+
+ inline void pop();
+
+ inline void popn(uint32_t n);
+
+ void popn(Register reg) {
+ // sp := sp + reg * sizeof(Value)
+ Register spReg = AsRegister(masm.getStackPointer());
+ masm.computeEffectiveAddress(BaseValueIndex(spReg, reg), spReg);
+ // On arm64, SP may be < PSP now (that's OK).
+ // eg testcase: tests/arguments/strict-args-generator-flushstack.js
+ }
+
+ void popValue(ValueOperand dest) { masm.popValue(dest); }
+
+ void push(const ValueOperand& val,
+ JSValueType knownType = JSVAL_TYPE_UNKNOWN) {
+ masm.pushValue(val);
+ }
+ void push(const Value& val) { masm.pushValue(val); }
+
+ void pushThis() { masm.pushValue(addressOfThis()); }
+ void pushScratchValue() { masm.pushValue(addressOfScratchValue()); }
+
+ void storeStackValue(int32_t depth, const Address& dest,
+ const ValueOperand& scratch) {
+ masm.loadValue(addressOfStackValue(depth), scratch);
+ masm.storeValue(scratch, dest);
+ }
+
+ void bumpInterpreterICEntry();
+
+ Address addressOfInterpreterScript() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfInterpreterScript());
+ }
+ Address addressOfInterpreterPC() const {
+ return Address(FramePointer, BaselineFrame::reverseOffsetOfInterpreterPC());
+ }
+ Address addressOfInterpreterICEntry() const {
+ return Address(FramePointer,
+ BaselineFrame::reverseOffsetOfInterpreterICEntry());
+ }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_BaselineFrameInfo_h */