blob: e7c0b0223a0ab86aab31e490f3fac7a32089a08d [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_BaselineFrameInfo_h
#define jit_BaselineFrameInfo_h
#ifdef JS_ION
#include "jscntxt.h"
#include "jscompartment.h"
#include "BaselineJIT.h"
#include "BaselineFrame.h"
#include "BaselineRegisters.h"
#include "BytecodeAnalysis.h"
#include "IonMacroAssembler.h"
#include "FixedList.h"
namespace js {
namespace jit {
// 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
#ifdef DEBUG
// In debug builds, assert Kind is initialized.
, Uninitialized
#endif
};
private:
Kind kind_;
union {
struct {
Value v;
} constant;
struct {
mozilla::AlignedStorage2<ValueOperand> 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 {
JS_ASSERT(type != JSVAL_TYPE_UNKNOWN);
return knownType_ == type;
}
bool isKnownBoolean() const {
return hasKnownType(JSVAL_TYPE_BOOLEAN);
}
JSValueType knownType() const {
JS_ASSERT(hasKnownType());
return knownType_;
}
void reset() {
#ifdef DEBUG
kind_ = Uninitialized;
knownType_ = JSVAL_TYPE_UNKNOWN;
#endif
}
Value constant() const {
JS_ASSERT(kind_ == Constant);
return data.constant.v;
}
ValueOperand reg() const {
JS_ASSERT(kind_ == Register);
return *data.reg.reg.addr();
}
uint32_t localSlot() const {
JS_ASSERT(kind_ == LocalSlot);
return data.local.slot;
}
uint32_t argSlot() const {
JS_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 setStack() {
kind_ = Stack;
knownType_ = JSVAL_TYPE_UNKNOWN;
}
};
enum StackAdjustment { AdjustStack, DontAdjustStack };
class BaselineCompilerShared;
class FrameInfo
{
RootedScript script;
MacroAssembler &masm;
FixedList<StackValue> stack;
size_t spIndex;
public:
FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
: script(cx, script),
masm(masm),
stack(),
spIndex(0)
{ }
bool init();
uint32_t nlocals() const {
return script->nfixed;
}
uint32_t nargs() const {
return script->function()->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();
}
JS_ASSERT(spIndex == newDepth);
}
}
inline StackValue *peek(int32_t index) const {
JS_ASSERT(index < 0);
return const_cast<StackValue *>(&stack[spIndex + index]);
}
inline void pop(StackAdjustment adjust = AdjustStack) {
spIndex--;
StackValue *popped = &stack[spIndex];
if (adjust == AdjustStack && popped->kind() == StackValue::Stack)
masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
// Assert when anything uses this value.
popped->reset();
}
inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack) {
uint32_t poppedStack = 0;
for (uint32_t i = 0; i < n; i++) {
if (peek(-1)->kind() == StackValue::Stack)
poppedStack++;
pop(DontAdjustStack);
}
if (adjust == AdjustStack && poppedStack > 0)
masm.addPtr(Imm32(sizeof(Value) * poppedStack), BaselineStackReg);
}
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) {
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();
}
inline Address addressOfLocal(size_t local) const {
#ifdef DEBUG
if (local >= nlocals()) {
// GETLOCAL and SETLOCAL can be used to access stack values. This is
// fine, as long as they are synced.
size_t slot = local - nlocals();
JS_ASSERT(slot < stackDepth());
JS_ASSERT(stack[slot].kind() == StackValue::Stack);
}
#endif
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfLocal(local));
}
Address addressOfArg(size_t arg) const {
JS_ASSERT(arg < nargs());
return Address(BaselineFrameReg, BaselineFrame::offsetOfArg(arg));
}
Address addressOfThis() const {
return Address(BaselineFrameReg, BaselineFrame::offsetOfThis());
}
Address addressOfCallee() const {
return Address(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken());
}
Address addressOfScopeChain() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScopeChain());
}
Address addressOfBlockChain() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfBlockChain());
}
Address addressOfFlags() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
}
Address addressOfEvalScript() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfEvalScript());
}
Address addressOfReturnValue() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue());
}
Address addressOfStackValue(const StackValue *value) const {
JS_ASSERT(value->kind() == StackValue::Stack);
size_t slot = value - &stack[0];
JS_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 {
JS_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 // JS_ION
#endif /* jit_BaselineFrameInfo_h */