diff options
Diffstat (limited to 'js/src/jit/BaselineFrameInfo.h')
-rw-r--r-- | js/src/jit/BaselineFrameInfo.h | 435 |
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 */ |