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_BaselineFrame_h michael@0: #define jit_BaselineFrame_h michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: #include "jit/IonFrames.h" michael@0: #include "vm/Stack.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: struct BaselineDebugModeOSRInfo; michael@0: michael@0: // The stack looks like this, fp is the frame pointer: michael@0: // michael@0: // fp+y arguments michael@0: // fp+x IonJSFrameLayout (frame header) michael@0: // fp => saved frame pointer michael@0: // fp-x BaselineFrame michael@0: // locals michael@0: // stack values michael@0: michael@0: // Eval frames michael@0: // michael@0: // Like js::InterpreterFrame, every BaselineFrame is either a global frame michael@0: // or a function frame. Both global and function frames can optionally michael@0: // be "eval frames". The callee token for eval function frames is the michael@0: // enclosing function. BaselineFrame::evalScript_ stores the eval script michael@0: // itself. michael@0: class BaselineFrame michael@0: { michael@0: public: michael@0: enum Flags { michael@0: // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL. michael@0: HAS_RVAL = 1 << 0, michael@0: michael@0: // A call object has been pushed on the scope chain. michael@0: HAS_CALL_OBJ = 1 << 2, michael@0: michael@0: // Frame has an arguments object, argsObj_. michael@0: HAS_ARGS_OBJ = 1 << 4, michael@0: michael@0: // See InterpreterFrame::PREV_UP_TO_DATE. michael@0: PREV_UP_TO_DATE = 1 << 5, michael@0: michael@0: // Eval frame, see the "eval frames" comment. michael@0: EVAL = 1 << 6, michael@0: michael@0: // Frame has hookData_ set. michael@0: HAS_HOOK_DATA = 1 << 7, michael@0: michael@0: // Frame has profiler entry pushed. michael@0: HAS_PUSHED_SPS_FRAME = 1 << 8, michael@0: michael@0: // Frame has over-recursed on an early check. michael@0: OVER_RECURSED = 1 << 9, michael@0: michael@0: // Frame has a BaselineRecompileInfo stashed in the scratch value michael@0: // slot. See PatchBaselineFramesForDebugMOde. michael@0: HAS_DEBUG_MODE_OSR_INFO = 1 << 10 michael@0: }; michael@0: michael@0: protected: // Silence Clang warning about unused private fields. michael@0: // We need to split the Value into 2 fields of 32 bits, otherwise the C++ michael@0: // compiler may add some padding between the fields. michael@0: uint32_t loScratchValue_; michael@0: uint32_t hiScratchValue_; michael@0: uint32_t loReturnValue_; // If HAS_RVAL, the frame's return value. michael@0: uint32_t hiReturnValue_; michael@0: uint32_t frameSize_; michael@0: JSObject *scopeChain_; // Scope chain (always initialized). michael@0: JSScript *evalScript_; // If isEvalFrame(), the current eval script. michael@0: ArgumentsObject *argsObj_; // If HAS_ARGS_OBJ, the arguments object. michael@0: void *hookData_; // If HAS_HOOK_DATA, debugger call hook data. michael@0: uint32_t flags_; michael@0: #if JS_BITS_PER_WORD == 32 michael@0: uint32_t padding_; // Pad to 8-byte alignment. michael@0: #endif michael@0: michael@0: public: michael@0: // Distance between the frame pointer and the frame header (return address). michael@0: // This is the old frame pointer saved in the prologue. michael@0: static const uint32_t FramePointerOffset = sizeof(void *); michael@0: michael@0: bool initForOsr(InterpreterFrame *fp, uint32_t numStackValues); michael@0: michael@0: uint32_t frameSize() const { michael@0: return frameSize_; michael@0: } michael@0: void setFrameSize(uint32_t frameSize) { michael@0: frameSize_ = frameSize; michael@0: } michael@0: inline uint32_t *addressOfFrameSize() { michael@0: return &frameSize_; michael@0: } michael@0: JSObject *scopeChain() const { michael@0: return scopeChain_; michael@0: } michael@0: void setScopeChain(JSObject *scopeChain) { michael@0: scopeChain_ = scopeChain; michael@0: } michael@0: inline JSObject **addressOfScopeChain() { michael@0: return &scopeChain_; michael@0: } michael@0: michael@0: inline Value *addressOfScratchValue() { michael@0: return reinterpret_cast(&loScratchValue_); michael@0: } michael@0: michael@0: inline void pushOnScopeChain(ScopeObject &scope); michael@0: inline void popOffScopeChain(); michael@0: michael@0: inline void popWith(JSContext *cx); michael@0: michael@0: CalleeToken calleeToken() const { michael@0: uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken(); michael@0: return *(CalleeToken *)pointer; michael@0: } michael@0: void replaceCalleeToken(CalleeToken token) { michael@0: uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken(); michael@0: *(CalleeToken *)pointer = token; michael@0: } michael@0: JSScript *script() const { michael@0: if (isEvalFrame()) michael@0: return evalScript(); michael@0: return ScriptFromCalleeToken(calleeToken()); michael@0: } michael@0: JSFunction *fun() const { michael@0: return CalleeTokenToFunction(calleeToken()); michael@0: } michael@0: JSFunction *maybeFun() const { michael@0: return isFunctionFrame() ? fun() : nullptr; michael@0: } michael@0: JSFunction *callee() const { michael@0: return CalleeTokenToFunction(calleeToken()); michael@0: } michael@0: Value calleev() const { michael@0: return ObjectValue(*callee()); michael@0: } michael@0: size_t numValueSlots() const { michael@0: size_t size = frameSize(); michael@0: michael@0: JS_ASSERT(size >= BaselineFrame::FramePointerOffset + BaselineFrame::Size()); michael@0: size -= BaselineFrame::FramePointerOffset + BaselineFrame::Size(); michael@0: michael@0: JS_ASSERT((size % sizeof(Value)) == 0); michael@0: return size / sizeof(Value); michael@0: } michael@0: Value *valueSlot(size_t slot) const { michael@0: JS_ASSERT(slot < numValueSlots()); michael@0: return (Value *)this - (slot + 1); michael@0: } michael@0: michael@0: Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { michael@0: JS_ASSERT(i < script()->nfixedvars()); michael@0: JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i)); michael@0: return *valueSlot(i); michael@0: } michael@0: michael@0: Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { michael@0: JS_ASSERT(i < numFormalArgs()); michael@0: JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() && michael@0: !script()->formalIsAliased(i)); michael@0: return argv()[i]; michael@0: } michael@0: michael@0: Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { michael@0: JS_ASSERT(i < numActualArgs()); michael@0: JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); michael@0: JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); michael@0: return argv()[i]; michael@0: } michael@0: michael@0: Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { michael@0: JS_ASSERT(i < script()->nfixed()); michael@0: #ifdef DEBUG michael@0: CheckLocalUnaliased(checkAliasing, script(), i); michael@0: #endif michael@0: return *valueSlot(i); michael@0: } michael@0: michael@0: unsigned numActualArgs() const { michael@0: return *(size_t *)(reinterpret_cast(this) + michael@0: BaselineFrame::Size() + michael@0: offsetOfNumActualArgs()); michael@0: } michael@0: unsigned numFormalArgs() const { michael@0: return script()->functionNonDelazifying()->nargs(); michael@0: } michael@0: Value &thisValue() const { michael@0: return *(Value *)(reinterpret_cast(this) + michael@0: BaselineFrame::Size() + michael@0: offsetOfThis()); michael@0: } michael@0: Value *argv() const { michael@0: return (Value *)(reinterpret_cast(this) + michael@0: BaselineFrame::Size() + michael@0: offsetOfArg(0)); michael@0: } michael@0: michael@0: bool copyRawFrameSlots(AutoValueVector *vec) const; michael@0: michael@0: bool hasReturnValue() const { michael@0: return flags_ & HAS_RVAL; michael@0: } michael@0: MutableHandleValue returnValue() { michael@0: return MutableHandleValue::fromMarkedLocation(reinterpret_cast(&loReturnValue_)); michael@0: } michael@0: void setReturnValue(const Value &v) { michael@0: flags_ |= HAS_RVAL; michael@0: returnValue().set(v); michael@0: } michael@0: inline Value *addressOfReturnValue() { michael@0: return reinterpret_cast(&loReturnValue_); michael@0: } michael@0: michael@0: bool hasCallObj() const { michael@0: return flags_ & HAS_CALL_OBJ; michael@0: } michael@0: michael@0: inline CallObject &callObj() const; michael@0: michael@0: void setFlags(uint32_t flags) { michael@0: flags_ = flags; michael@0: } michael@0: uint32_t *addressOfFlags() { michael@0: return &flags_; michael@0: } michael@0: michael@0: inline bool pushBlock(JSContext *cx, Handle block); michael@0: inline void popBlock(JSContext *cx); michael@0: michael@0: bool strictEvalPrologue(JSContext *cx); michael@0: bool heavyweightFunPrologue(JSContext *cx); michael@0: bool initFunctionScopeObjects(JSContext *cx); michael@0: michael@0: void initArgsObjUnchecked(ArgumentsObject &argsobj) { michael@0: flags_ |= HAS_ARGS_OBJ; michael@0: argsObj_ = &argsobj; michael@0: } michael@0: void initArgsObj(ArgumentsObject &argsobj) { michael@0: JS_ASSERT(script()->needsArgsObj()); michael@0: initArgsObjUnchecked(argsobj); michael@0: } michael@0: bool hasArgsObj() const { michael@0: return flags_ & HAS_ARGS_OBJ; michael@0: } michael@0: ArgumentsObject &argsObj() const { michael@0: JS_ASSERT(hasArgsObj()); michael@0: JS_ASSERT(script()->needsArgsObj()); michael@0: return *argsObj_; michael@0: } michael@0: michael@0: bool prevUpToDate() const { michael@0: return flags_ & PREV_UP_TO_DATE; michael@0: } michael@0: void setPrevUpToDate() { michael@0: flags_ |= PREV_UP_TO_DATE; michael@0: } michael@0: michael@0: JSScript *evalScript() const { michael@0: JS_ASSERT(isEvalFrame()); michael@0: return evalScript_; michael@0: } michael@0: michael@0: bool hasHookData() const { michael@0: return flags_ & HAS_HOOK_DATA; michael@0: } michael@0: michael@0: void *maybeHookData() const { michael@0: return hasHookData() ? hookData_ : nullptr; michael@0: } michael@0: michael@0: void setHookData(void *v) { michael@0: hookData_ = v; michael@0: flags_ |= HAS_HOOK_DATA; michael@0: } michael@0: michael@0: bool hasPushedSPSFrame() const { michael@0: return flags_ & HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: void setPushedSPSFrame() { michael@0: flags_ |= HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: void unsetPushedSPSFrame() { michael@0: flags_ &= ~HAS_PUSHED_SPS_FRAME; michael@0: } michael@0: michael@0: bool overRecursed() const { michael@0: return flags_ & OVER_RECURSED; michael@0: } michael@0: michael@0: void setOverRecursed() { michael@0: flags_ |= OVER_RECURSED; michael@0: } michael@0: michael@0: BaselineDebugModeOSRInfo *debugModeOSRInfo() { michael@0: MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO); michael@0: return *reinterpret_cast(&loScratchValue_); michael@0: } michael@0: michael@0: BaselineDebugModeOSRInfo *getDebugModeOSRInfo() { michael@0: if (flags_ & HAS_DEBUG_MODE_OSR_INFO) michael@0: return debugModeOSRInfo(); michael@0: return nullptr; michael@0: } michael@0: michael@0: void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) { michael@0: flags_ |= HAS_DEBUG_MODE_OSR_INFO; michael@0: *reinterpret_cast(&loScratchValue_) = info; michael@0: } michael@0: michael@0: void deleteDebugModeOSRInfo(); michael@0: michael@0: void trace(JSTracer *trc, JitFrameIterator &frame); michael@0: michael@0: bool isFunctionFrame() const { michael@0: return CalleeTokenIsFunction(calleeToken()); michael@0: } michael@0: bool isGlobalFrame() const { michael@0: return !CalleeTokenIsFunction(calleeToken()); michael@0: } michael@0: bool isEvalFrame() const { michael@0: return flags_ & EVAL; michael@0: } michael@0: bool isStrictEvalFrame() const { michael@0: return isEvalFrame() && script()->strict(); michael@0: } michael@0: bool isNonStrictEvalFrame() const { michael@0: return isEvalFrame() && !script()->strict(); michael@0: } michael@0: bool isDirectEvalFrame() const { michael@0: return isEvalFrame() && script()->staticLevel() > 0; michael@0: } michael@0: bool isNonStrictDirectEvalFrame() const { michael@0: return isNonStrictEvalFrame() && isDirectEvalFrame(); michael@0: } michael@0: bool isNonEvalFunctionFrame() const { michael@0: return isFunctionFrame() && !isEvalFrame(); michael@0: } michael@0: bool isDebuggerFrame() const { michael@0: return false; michael@0: } michael@0: bool isGeneratorFrame() const { michael@0: return false; michael@0: } michael@0: michael@0: IonJSFrameLayout *framePrefix() const { michael@0: uint8_t *fp = (uint8_t *)this + Size() + FramePointerOffset; michael@0: return (IonJSFrameLayout *)fp; michael@0: } michael@0: michael@0: // Methods below are used by the compiler. michael@0: static size_t offsetOfCalleeToken() { michael@0: return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfCalleeToken(); michael@0: } michael@0: static size_t offsetOfThis() { michael@0: return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfThis(); michael@0: } michael@0: static size_t offsetOfArg(size_t index) { michael@0: return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfActualArg(index); michael@0: } michael@0: static size_t offsetOfNumActualArgs() { michael@0: return FramePointerOffset + js::jit::IonJSFrameLayout::offsetOfNumActualArgs(); michael@0: } michael@0: static size_t Size() { michael@0: return sizeof(BaselineFrame); michael@0: } michael@0: michael@0: // The reverseOffsetOf methods below compute the offset relative to the michael@0: // frame's base pointer. Since the stack grows down, these offsets are michael@0: // negative. michael@0: static int reverseOffsetOfFrameSize() { michael@0: return -int(Size()) + offsetof(BaselineFrame, frameSize_); michael@0: } michael@0: static int reverseOffsetOfScratchValue() { michael@0: return -int(Size()) + offsetof(BaselineFrame, loScratchValue_); michael@0: } michael@0: static int reverseOffsetOfScopeChain() { michael@0: return -int(Size()) + offsetof(BaselineFrame, scopeChain_); michael@0: } michael@0: static int reverseOffsetOfArgsObj() { michael@0: return -int(Size()) + offsetof(BaselineFrame, argsObj_); michael@0: } michael@0: static int reverseOffsetOfFlags() { michael@0: return -int(Size()) + offsetof(BaselineFrame, flags_); michael@0: } michael@0: static int reverseOffsetOfEvalScript() { michael@0: return -int(Size()) + offsetof(BaselineFrame, evalScript_); michael@0: } michael@0: static int reverseOffsetOfReturnValue() { michael@0: return -int(Size()) + offsetof(BaselineFrame, loReturnValue_); michael@0: } michael@0: static int reverseOffsetOfLocal(size_t index) { michael@0: return -int(Size()) - (index + 1) * sizeof(Value); michael@0: } michael@0: }; michael@0: michael@0: // Ensure the frame is 8-byte aligned (required on ARM). michael@0: JS_STATIC_ASSERT(((sizeof(BaselineFrame) + BaselineFrame::FramePointerOffset) % 8) == 0); michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif // JS_ION michael@0: michael@0: #endif /* jit_BaselineFrame_h */