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