mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-02 08:41:34 +00:00
343 lines
9.8 KiB
C++
343 lines
9.8 KiB
C++
/* -*- 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 {
|
|
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 {
|
|
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;
|
|
}
|
|
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<StackValue> stack;
|
|
size_t spIndex;
|
|
|
|
public:
|
|
FrameInfo(JSScript* script, MacroAssembler& masm)
|
|
: script(script),
|
|
masm(masm),
|
|
stack(),
|
|
spIndex(0)
|
|
{ }
|
|
|
|
bool init(TempAllocator& alloc);
|
|
|
|
size_t nlocals() const {
|
|
return script->nfixed();
|
|
}
|
|
size_t nargs() const {
|
|
return script->functionNonDelazifying()->nargs();
|
|
}
|
|
size_t nvars() const {
|
|
return script->nfixedvars();
|
|
}
|
|
size_t nlexicals() const {
|
|
return script->fixedLexicalEnd() - script->fixedLexicalBegin();
|
|
}
|
|
|
|
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<StackValue*>(&stack[spIndex + index]);
|
|
}
|
|
|
|
inline void pop(StackAdjustment adjust = AdjustStack) {
|
|
spIndex--;
|
|
StackValue* popped = &stack[spIndex];
|
|
|
|
if (adjust == AdjustStack && popped->kind() == StackValue::Stack)
|
|
masm.addToStackPtr(Imm32(sizeof(Value)));
|
|
|
|
// 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.addToStackPtr(Imm32(sizeof(Value) * poppedStack));
|
|
}
|
|
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 addressOfScopeChain() const {
|
|
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScopeChain());
|
|
}
|
|
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 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 */
|