/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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/Alignment.h" #include "jit/BaselineFrame.h" #include "jit/FixedList.h" #include "jit/MacroAssembler.h" #include "jit/SharedICRegisters.h" namespace js { namespace jit { struct BytecodeInfo; // FrameInfo overview. // // FrameInfo is used by the compiler to track values stored in the frame. This // includes locals, arguments and stack values. 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, EvalNewTargetSlot #ifdef DEBUG // In debug builds, assert Kind is initialized. , Uninitialized #endif }; private: Kind kind_; union { struct { JS::UninitializedValue v; } constant; struct { mozilla::AlignedStorage2 reg; } reg; struct { uint32_t slot; } local; struct { uint32_t slot; } arg; } data; 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; } bool isKnownBoolean() const { return hasKnownType(JSVAL_TYPE_BOOLEAN); } 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.v.asValueRef(); } ValueOperand reg() const { MOZ_ASSERT(kind_ == Register); return *data.reg.reg.addr(); } uint32_t localSlot() const { MOZ_ASSERT(kind_ == LocalSlot); return data.local.slot; } uint32_t argSlot() const { MOZ_ASSERT(kind_ == ArgSlot); return data.arg.slot; } void setConstant(const Value& v) { kind_ = Constant; data.constant.v = v; knownType_ = v.isDouble() ? JSVAL_TYPE_DOUBLE : v.extractNonDoubleType(); } void setRegister(const ValueOperand& val, JSValueType knownType = JSVAL_TYPE_UNKNOWN) { kind_ = Register; *data.reg.reg.addr() = val; knownType_ = knownType; } void setLocalSlot(uint32_t slot) { kind_ = LocalSlot; data.local.slot = slot; knownType_ = JSVAL_TYPE_UNKNOWN; } void setArgSlot(uint32_t slot) { kind_ = ArgSlot; data.arg.slot = slot; knownType_ = JSVAL_TYPE_UNKNOWN; } void setThis() { kind_ = ThisSlot; knownType_ = JSVAL_TYPE_UNKNOWN; } void setEvalNewTarget() { kind_ = EvalNewTargetSlot; knownType_ = JSVAL_TYPE_UNKNOWN; } void setStack() { kind_ = Stack; knownType_ = JSVAL_TYPE_UNKNOWN; } }; enum StackAdjustment { AdjustStack, DontAdjustStack }; class FrameInfo { JSScript* script; MacroAssembler& masm; FixedList stack; size_t spIndex; public: FrameInfo(JSScript* script, MacroAssembler& masm) : script(script), masm(masm), stack(), spIndex(0) { } MOZ_MUST_USE bool init(TempAllocator& alloc); size_t nlocals() const { return script->nfixed(); } size_t nargs() const { return script->functionNonDelazifying()->nargs(); } private: inline StackValue* rawPush() { StackValue* val = &stack[spIndex++]; val->reset(); return val; } 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); } } inline StackValue* peek(int32_t index) const { MOZ_ASSERT(index < 0); return const_cast(&stack[spIndex + index]); } 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 pushEvalNewTarget() { MOZ_ASSERT(script->isForEval()); StackValue* sv = rawPush(); sv->setEvalNewTarget(); } inline void pushScratchValue() { masm.pushValue(addressOfScratchValue()); StackValue* sv = rawPush(); sv->setStack(); } inline Address addressOfLocal(size_t local) const { MOZ_ASSERT(local < nlocals()); return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(local)); } Address addressOfArg(size_t arg) const { MOZ_ASSERT(arg < nargs()); return Address(BaselineFrameReg, BaselineFrame::offsetOfArg(arg)); } Address addressOfThis() const { return Address(BaselineFrameReg, BaselineFrame::offsetOfThis()); } Address addressOfEvalNewTarget() const { return Address(BaselineFrameReg, BaselineFrame::offsetOfEvalNewTarget()); } Address addressOfCalleeToken() const { return Address(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); } Address addressOfEnvironmentChain() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfEnvironmentChain()); } Address addressOfFlags() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()); } Address addressOfReturnValue() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()); } Address addressOfArgsObj() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()); } Address addressOfStackValue(const StackValue* value) const { MOZ_ASSERT(value->kind() == StackValue::Stack); size_t slot = value - &stack[0]; MOZ_ASSERT(slot < stackDepth()); return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(nlocals() + slot)); } Address addressOfScratchValue() const { return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()); } void popValue(ValueOperand dest); void sync(StackValue* val); void syncStack(uint32_t uses); uint32_t numUnsyncedSlots(); void popRegsAndSync(uint32_t uses); inline void assertSyncedStack() const { MOZ_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack); } #ifdef DEBUG // Assert the state is valid before excuting "pc". void assertValidState(const BytecodeInfo& info); #else inline void assertValidState(const BytecodeInfo& info) {} #endif }; } // namespace jit } // namespace js #endif /* jit_BaselineFrameInfo_h */