michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_BaselineFrameInfo_h michael@0: #define jit_BaselineFrameInfo_h michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/BaselineRegisters.h" michael@0: #include "jit/FixedList.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: struct BytecodeInfo; michael@0: michael@0: // FrameInfo overview. michael@0: // michael@0: // FrameInfo is used by the compiler to track values stored in the frame. This michael@0: // includes locals, arguments and stack values. Locals and arguments are always michael@0: // fully synced. Stack values can either be synced, stored as constant, stored in michael@0: // a Value register or refer to a local slot. Syncing a StackValue ensures it's michael@0: // stored on the stack, e.g. kind == Stack. michael@0: // michael@0: // To see how this works, consider the following statement: michael@0: // michael@0: // var y = x + 9; michael@0: // michael@0: // Here two values are pushed: StackValue(LocalSlot(0)) and StackValue(Int32Value(9)). michael@0: // Only when we reach the ADD op, code is generated to load the operands directly michael@0: // into the right operand registers and sync all other stack values. michael@0: // michael@0: // For stack values, the following invariants hold (and are checked between ops): michael@0: // michael@0: // (1) If a value is synced (kind == Stack), all values below it must also be synced. michael@0: // In other words, values with kind other than Stack can only appear on top of the michael@0: // abstract stack. michael@0: // michael@0: // (2) When we call a stub or IC, all values still on the stack must be synced. michael@0: michael@0: // Represents a value pushed on the stack. Note that StackValue is not used for michael@0: // locals or arguments since these are always fully synced. michael@0: class StackValue michael@0: { michael@0: public: michael@0: enum Kind { michael@0: Constant, michael@0: Register, michael@0: Stack, michael@0: LocalSlot, michael@0: ArgSlot, michael@0: ThisSlot michael@0: #ifdef DEBUG michael@0: // In debug builds, assert Kind is initialized. michael@0: , Uninitialized michael@0: #endif michael@0: }; michael@0: michael@0: private: michael@0: Kind kind_; michael@0: michael@0: union { michael@0: struct { michael@0: Value v; michael@0: } constant; michael@0: struct { michael@0: mozilla::AlignedStorage2 reg; michael@0: } reg; michael@0: struct { michael@0: uint32_t slot; michael@0: } local; michael@0: struct { michael@0: uint32_t slot; michael@0: } arg; michael@0: } data; michael@0: michael@0: JSValueType knownType_; michael@0: michael@0: public: michael@0: StackValue() { michael@0: reset(); michael@0: } michael@0: michael@0: Kind kind() const { michael@0: return kind_; michael@0: } michael@0: bool hasKnownType() const { michael@0: return knownType_ != JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: bool hasKnownType(JSValueType type) const { michael@0: JS_ASSERT(type != JSVAL_TYPE_UNKNOWN); michael@0: return knownType_ == type; michael@0: } michael@0: bool isKnownBoolean() const { michael@0: return hasKnownType(JSVAL_TYPE_BOOLEAN); michael@0: } michael@0: JSValueType knownType() const { michael@0: JS_ASSERT(hasKnownType()); michael@0: return knownType_; michael@0: } michael@0: void reset() { michael@0: #ifdef DEBUG michael@0: kind_ = Uninitialized; michael@0: knownType_ = JSVAL_TYPE_UNKNOWN; michael@0: #endif michael@0: } michael@0: Value constant() const { michael@0: JS_ASSERT(kind_ == Constant); michael@0: return data.constant.v; michael@0: } michael@0: ValueOperand reg() const { michael@0: JS_ASSERT(kind_ == Register); michael@0: return *data.reg.reg.addr(); michael@0: } michael@0: uint32_t localSlot() const { michael@0: JS_ASSERT(kind_ == LocalSlot); michael@0: return data.local.slot; michael@0: } michael@0: uint32_t argSlot() const { michael@0: JS_ASSERT(kind_ == ArgSlot); michael@0: return data.arg.slot; michael@0: } michael@0: michael@0: void setConstant(const Value &v) { michael@0: kind_ = Constant; michael@0: data.constant.v = v; michael@0: knownType_ = v.isDouble() ? JSVAL_TYPE_DOUBLE : v.extractNonDoubleType(); michael@0: } michael@0: void setRegister(const ValueOperand &val, JSValueType knownType = JSVAL_TYPE_UNKNOWN) { michael@0: kind_ = Register; michael@0: *data.reg.reg.addr() = val; michael@0: knownType_ = knownType; michael@0: } michael@0: void setLocalSlot(uint32_t slot) { michael@0: kind_ = LocalSlot; michael@0: data.local.slot = slot; michael@0: knownType_ = JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: void setArgSlot(uint32_t slot) { michael@0: kind_ = ArgSlot; michael@0: data.arg.slot = slot; michael@0: knownType_ = JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: void setThis() { michael@0: kind_ = ThisSlot; michael@0: knownType_ = JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: void setStack() { michael@0: kind_ = Stack; michael@0: knownType_ = JSVAL_TYPE_UNKNOWN; michael@0: } michael@0: }; michael@0: michael@0: enum StackAdjustment { AdjustStack, DontAdjustStack }; michael@0: michael@0: class FrameInfo michael@0: { michael@0: JSScript *script; michael@0: MacroAssembler &masm; michael@0: michael@0: FixedList stack; michael@0: size_t spIndex; michael@0: michael@0: public: michael@0: FrameInfo(JSScript *script, MacroAssembler &masm) michael@0: : script(script), michael@0: masm(masm), michael@0: stack(), michael@0: spIndex(0) michael@0: { } michael@0: michael@0: bool init(TempAllocator &alloc); michael@0: michael@0: uint32_t nlocals() const { michael@0: return script->nfixed(); michael@0: } michael@0: uint32_t nargs() const { michael@0: return script->functionNonDelazifying()->nargs(); michael@0: } michael@0: michael@0: private: michael@0: inline StackValue *rawPush() { michael@0: StackValue *val = &stack[spIndex++]; michael@0: val->reset(); michael@0: return val; michael@0: } michael@0: michael@0: public: michael@0: inline size_t stackDepth() const { michael@0: return spIndex; michael@0: } michael@0: inline void setStackDepth(uint32_t newDepth) { michael@0: if (newDepth <= stackDepth()) { michael@0: spIndex = newDepth; michael@0: } else { michael@0: uint32_t diff = newDepth - stackDepth(); michael@0: for (uint32_t i = 0; i < diff; i++) { michael@0: StackValue *val = rawPush(); michael@0: val->setStack(); michael@0: } michael@0: michael@0: JS_ASSERT(spIndex == newDepth); michael@0: } michael@0: } michael@0: inline StackValue *peek(int32_t index) const { michael@0: JS_ASSERT(index < 0); michael@0: return const_cast(&stack[spIndex + index]); michael@0: } michael@0: michael@0: inline void pop(StackAdjustment adjust = AdjustStack) { michael@0: spIndex--; michael@0: StackValue *popped = &stack[spIndex]; michael@0: michael@0: if (adjust == AdjustStack && popped->kind() == StackValue::Stack) michael@0: masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg); michael@0: michael@0: // Assert when anything uses this value. michael@0: popped->reset(); michael@0: } michael@0: inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack) { michael@0: uint32_t poppedStack = 0; michael@0: for (uint32_t i = 0; i < n; i++) { michael@0: if (peek(-1)->kind() == StackValue::Stack) michael@0: poppedStack++; michael@0: pop(DontAdjustStack); michael@0: } michael@0: if (adjust == AdjustStack && poppedStack > 0) michael@0: masm.addPtr(Imm32(sizeof(Value) * poppedStack), BaselineStackReg); michael@0: } michael@0: inline void push(const Value &val) { michael@0: StackValue *sv = rawPush(); michael@0: sv->setConstant(val); michael@0: } michael@0: inline void push(const ValueOperand &val, JSValueType knownType=JSVAL_TYPE_UNKNOWN) { michael@0: StackValue *sv = rawPush(); michael@0: sv->setRegister(val, knownType); michael@0: } michael@0: inline void pushLocal(uint32_t local) { michael@0: JS_ASSERT(local < nlocals()); michael@0: StackValue *sv = rawPush(); michael@0: sv->setLocalSlot(local); michael@0: } michael@0: inline void pushArg(uint32_t arg) { michael@0: StackValue *sv = rawPush(); michael@0: sv->setArgSlot(arg); michael@0: } michael@0: inline void pushThis() { michael@0: StackValue *sv = rawPush(); michael@0: sv->setThis(); michael@0: } michael@0: inline void pushScratchValue() { michael@0: masm.pushValue(addressOfScratchValue()); michael@0: StackValue *sv = rawPush(); michael@0: sv->setStack(); michael@0: } michael@0: inline Address addressOfLocal(size_t local) const { michael@0: JS_ASSERT(local < nlocals()); michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(local)); michael@0: } michael@0: Address addressOfArg(size_t arg) const { michael@0: JS_ASSERT(arg < nargs()); michael@0: return Address(BaselineFrameReg, BaselineFrame::offsetOfArg(arg)); michael@0: } michael@0: Address addressOfThis() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::offsetOfThis()); michael@0: } michael@0: Address addressOfCallee() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); michael@0: } michael@0: Address addressOfScopeChain() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScopeChain()); michael@0: } michael@0: Address addressOfFlags() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()); michael@0: } michael@0: Address addressOfEvalScript() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfEvalScript()); michael@0: } michael@0: Address addressOfReturnValue() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()); michael@0: } michael@0: Address addressOfStackValue(const StackValue *value) const { michael@0: JS_ASSERT(value->kind() == StackValue::Stack); michael@0: size_t slot = value - &stack[0]; michael@0: JS_ASSERT(slot < stackDepth()); michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(nlocals() + slot)); michael@0: } michael@0: Address addressOfScratchValue() const { michael@0: return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()); michael@0: } michael@0: michael@0: void popValue(ValueOperand dest); michael@0: michael@0: void sync(StackValue *val); michael@0: void syncStack(uint32_t uses); michael@0: uint32_t numUnsyncedSlots(); michael@0: void popRegsAndSync(uint32_t uses); michael@0: michael@0: inline void assertSyncedStack() const { michael@0: JS_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // Assert the state is valid before excuting "pc". michael@0: void assertValidState(const BytecodeInfo &info); michael@0: #else michael@0: inline void assertValidState(const BytecodeInfo &info) {} michael@0: #endif michael@0: }; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif // JS_ION michael@0: michael@0: #endif /* jit_BaselineFrameInfo_h */