Bug 1519880 part 2 - Split Baseline's FrameInfo class in CompilerFrameInfo and InterpreterFrameInfo. r=djvj

InterpreterFrameInfo is just a very simple interface on top of masm.
CompilerFrameInfo maintains the virtual stack based on the script it's
compiling.

Differential Revision: https://phabricator.services.mozilla.com/D16480

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2019-01-17 12:19:06 +00:00
parent 4d6a88776b
commit ed23e847c7
7 changed files with 226 additions and 83 deletions

View File

@ -45,27 +45,30 @@ using mozilla::Maybe;
namespace js {
namespace jit {
BaselineCompilerHandler::BaselineCompilerHandler(TempAllocator& alloc,
BaselineCompilerHandler::BaselineCompilerHandler(MacroAssembler& masm,
TempAllocator& alloc,
JSScript* script)
: alloc_(alloc),
: frame_(script, masm),
alloc_(alloc),
script_(script),
pc_(script->code()),
compileDebugInstrumentation_(script->isDebuggee()) {}
BaselineInterpreterHandler::BaselineInterpreterHandler() {}
BaselineInterpreterHandler::BaselineInterpreterHandler(MacroAssembler& masm)
: frame_(masm) {}
template <typename Handler>
template <typename... HandlerArgs>
BaselineCodeGen<Handler>::BaselineCodeGen(JSContext* cx, TempAllocator& alloc,
JSScript* script,
HandlerArgs&&... args)
: handler(std::forward<HandlerArgs>(args)...),
: handler(masm, std::forward<HandlerArgs>(args)...),
cx(cx),
script(script),
ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
alloc_(alloc),
analysis_(alloc, script),
frame(script, masm),
frame(handler.frame()),
traceLoggerToggleOffsets_(cx),
icEntryIndex_(0),
pushedBeforeCall_(0),
@ -89,6 +92,11 @@ BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc,
#endif
}
BaselineInterpreterGenerator::BaselineInterpreterGenerator(JSContext* cx,
TempAllocator& alloc)
// Note: the nullptr script here is temporary. See bug 1519378.
: BaselineCodeGen(cx, alloc, /* script = */ nullptr) {}
bool BaselineCompilerHandler::init() {
uint32_t len = script_->length();
@ -502,6 +510,39 @@ void BaselineCodeGen<Handler>::prepareVMCall() {
masm.Push(BaselineFrameReg);
}
template <>
void BaselineCompilerCodeGen::storeFrameSizeAndPushDescriptor(
uint32_t frameBaseSize, uint32_t argSize, const Address& frameSizeAddr) {
uint32_t frameVals = frame.nlocals() + frame.stackDepth();
uint32_t frameFullSize = frameBaseSize + (frameVals * sizeof(Value));
masm.store32(Imm32(frameFullSize), frameSizeAddr);
uint32_t descriptor = MakeFrameDescriptor(
frameFullSize + argSize, FrameType::BaselineJS, ExitFrameLayout::Size());
masm.push(Imm32(descriptor));
}
template <>
void BaselineInterpreterCodeGen::storeFrameSizeAndPushDescriptor(
uint32_t frameBaseSize, uint32_t argSize, const Address& frameSizeAddr) {
MOZ_CRASH("NYI: interpreter storeFrameSizeAndPushDescriptor");
}
template <>
void BaselineCompilerCodeGen::computeFullFrameSize(uint32_t frameBaseSize,
Register dest) {
uint32_t frameVals = frame.nlocals() + frame.stackDepth();
uint32_t frameFullSize = frameBaseSize + (frameVals * sizeof(Value));
masm.move32(Imm32(frameFullSize), dest);
}
template <>
void BaselineInterpreterCodeGen::computeFullFrameSize(uint32_t frameBaseSize,
Register dest) {
MOZ_CRASH("NYI: interpreter computeFullFrameSize");
}
template <typename Handler>
bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
CallVMPhase phase) {
@ -533,16 +574,10 @@ bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
Address frameSizeAddress(BaselineFrameReg,
BaselineFrame::reverseOffsetOfFrameSize());
uint32_t frameVals = frame.nlocals() + frame.stackDepth();
uint32_t frameBaseSize =
BaselineFrame::FramePointerOffset + BaselineFrame::Size();
uint32_t frameFullSize = frameBaseSize + (frameVals * sizeof(Value));
if (phase == POST_INITIALIZE) {
masm.store32(Imm32(frameFullSize), frameSizeAddress);
uint32_t descriptor =
MakeFrameDescriptor(frameFullSize + argSize, FrameType::BaselineJS,
ExitFrameLayout::Size());
masm.push(Imm32(descriptor));
storeFrameSizeAndPushDescriptor(frameBaseSize, argSize, frameSizeAddress);
} else {
MOZ_ASSERT(phase == CHECK_OVER_RECURSED);
Label afterWrite;
@ -557,7 +592,7 @@ bool BaselineCodeGen<Handler>::callVM(const VMFunction& fun,
masm.jump(&afterWrite);
masm.bind(&writePostInitialize);
masm.move32(Imm32(frameFullSize), ICTailCallReg);
computeFullFrameSize(frameBaseSize, ICTailCallReg);
masm.bind(&afterWrite);
masm.store32(ICTailCallReg, frameSizeAddress);
@ -3738,7 +3773,7 @@ template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_FINALLY() {
// JSOP_FINALLY has a def count of 2, but these values are already on the
// stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
frame.setStackDepth(frame.stackDepth() + 2);
frame.incStackDepth(2);
// To match the interpreter, emit an interrupt check at the start of the
// finally block.
@ -4116,7 +4151,7 @@ bool BaselineCodeGen<Handler>::emitReturn() {
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_RETURN() {
MOZ_ASSERT(frame.stackDepth() == 1);
frame.assertStackDepth(1);
frame.popValue(JSReturnOperand);
return emitReturn();
@ -4138,7 +4173,7 @@ void BaselineCodeGen<Handler>::emitLoadReturnValue(ValueOperand val) {
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_RETRVAL() {
MOZ_ASSERT(frame.stackDepth() == 0);
frame.assertStackDepth(0);
masm.moveValue(UndefinedValue(), JSReturnOperand);
@ -4715,7 +4750,7 @@ static const VMFunction CreateGeneratorInfo =
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_GENERATOR() {
MOZ_ASSERT(frame.stackDepth() == 0);
frame.assertStackDepth(0);
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@ -4733,7 +4768,7 @@ bool BaselineCodeGen<Handler>::emit_JSOP_GENERATOR() {
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_INITIALYIELD() {
frame.syncStack(0);
MOZ_ASSERT(frame.stackDepth() == 1);
frame.assertStackDepth(1);
Register genObj = R2.scratchReg();
masm.unboxObject(frame.addressOfStackValue(-1), genObj);
@ -4763,7 +4798,7 @@ bool BaselineCodeGen<Handler>::emit_JSOP_INITIALYIELD() {
}
typedef bool (*NormalSuspendFn)(JSContext*, HandleObject, BaselineFrame*,
jsbytecode*, uint32_t);
jsbytecode*);
static const VMFunction NormalSuspendInfo =
FunctionInfo<NormalSuspendFn>(jit::NormalSuspend, "NormalSuspend");
@ -4775,9 +4810,7 @@ bool BaselineCodeGen<Handler>::emit_JSOP_YIELD() {
Register genObj = R2.scratchReg();
masm.unboxObject(R0, genObj);
MOZ_ASSERT(frame.stackDepth() >= 1);
if (frame.stackDepth() == 1) {
if (frame.hasKnownStackDepth(1)) {
// If the expression stack is empty, we can inline the YIELD.
Register temp = R1.scratchReg();
@ -4803,7 +4836,6 @@ bool BaselineCodeGen<Handler>::emit_JSOP_YIELD() {
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
prepareVMCall();
pushArg(Imm32(frame.stackDepth()));
pushBytecodePCArg();
pushArg(R1.scratchReg());
pushArg(genObj);

View File

@ -266,7 +266,7 @@ class BaselineCodeGen {
TempAllocator& alloc_;
BytecodeAnalysis analysis_;
FrameInfo frame;
typename Handler::FrameInfoT& frame;
js::Vector<CodeOffset> traceLoggerToggleOffsets_;
@ -322,6 +322,10 @@ class BaselineCodeGen {
void prepareVMCall();
void storeFrameSizeAndPushDescriptor(uint32_t frameBaseSize, uint32_t argSize,
const Address& frameSizeAddr);
void computeFullFrameSize(uint32_t frameBaseSize, Register dest);
enum CallVMPhase { POST_INITIALIZE, CHECK_OVER_RECURSED };
bool callVM(const VMFunction& fun, CallVMPhase phase = POST_INITIALIZE);
@ -423,6 +427,7 @@ using RetAddrEntryVector = js::Vector<RetAddrEntry, 16, SystemAllocPolicy>;
// Interface used by BaselineCodeGen for BaselineCompiler.
class BaselineCompilerHandler {
CompilerFrameInfo frame_;
TempAllocator& alloc_;
FixedList<Label> labels_;
RetAddrEntryVector retAddrEntries_;
@ -431,10 +436,15 @@ class BaselineCompilerHandler {
bool compileDebugInstrumentation_;
public:
BaselineCompilerHandler(TempAllocator& alloc, JSScript* script);
using FrameInfoT = CompilerFrameInfo;
BaselineCompilerHandler(MacroAssembler& masm, TempAllocator& alloc,
JSScript* script);
MOZ_MUST_USE bool init();
CompilerFrameInfo& frame() { return frame_; }
jsbytecode* pc() const { return pc_; }
jsbytecode* maybePC() const { return pc_; }
@ -563,8 +573,14 @@ class BaselineCompiler final : private BaselineCompilerCodeGen {
// Interface used by BaselineCodeGen for BaselineInterpreterGenerator.
class BaselineInterpreterHandler {
InterpreterFrameInfo frame_;
public:
explicit BaselineInterpreterHandler();
using FrameInfoT = InterpreterFrameInfo;
explicit BaselineInterpreterHandler(MacroAssembler& masm);
InterpreterFrameInfo& frame() { return frame_; }
// Interpreter doesn't know the pc statically.
jsbytecode* maybePC() const { return nullptr; }
@ -583,6 +599,7 @@ using BaselineInterpreterCodeGen = BaselineCodeGen<BaselineInterpreterHandler>;
class BaselineInterpreterGenerator final : private BaselineInterpreterCodeGen {
public:
BaselineInterpreterGenerator(JSContext* cx, TempAllocator& alloc);
};
extern const VMFunction NewArrayCopyOnWriteInfo;

View File

@ -10,7 +10,7 @@
namespace js {
namespace jit {
void FrameInfo::pop(StackAdjustment adjust) {
void CompilerFrameInfo::pop(StackAdjustment adjust) {
spIndex--;
StackValue* popped = &stack[spIndex];
@ -21,7 +21,7 @@ void FrameInfo::pop(StackAdjustment adjust) {
popped->reset();
}
void FrameInfo::popn(uint32_t n, StackAdjustment adjust) {
void CompilerFrameInfo::popn(uint32_t n, StackAdjustment adjust) {
uint32_t poppedStack = 0;
for (uint32_t i = 0; i < n; i++) {
if (peek(-1)->kind() == StackValue::Stack) {

View File

@ -16,7 +16,7 @@
using namespace js;
using namespace js::jit;
bool FrameInfo::init(TempAllocator& alloc) {
bool CompilerFrameInfo::init(TempAllocator& alloc) {
// An extra slot is needed for global scopes because INITGLEXICAL (stack
// depth 1) is compiled as a SETPROP (stack depth 2) on the global lexical
// scope.
@ -30,7 +30,7 @@ bool FrameInfo::init(TempAllocator& alloc) {
return true;
}
void FrameInfo::sync(StackValue* val) {
void CompilerFrameInfo::sync(StackValue* val) {
switch (val->kind()) {
case StackValue::Stack:
break;
@ -60,7 +60,7 @@ void FrameInfo::sync(StackValue* val) {
val->setStack();
}
void FrameInfo::syncStack(uint32_t uses) {
void CompilerFrameInfo::syncStack(uint32_t uses) {
MOZ_ASSERT(uses <= stackDepth());
uint32_t depth = stackDepth() - uses;
@ -71,7 +71,7 @@ void FrameInfo::syncStack(uint32_t uses) {
}
}
uint32_t FrameInfo::numUnsyncedSlots() {
uint32_t CompilerFrameInfo::numUnsyncedSlots() {
// Start at the bottom, find the first value that's not synced.
uint32_t i = 0;
for (; i < stackDepth(); i++) {
@ -82,7 +82,7 @@ uint32_t FrameInfo::numUnsyncedSlots() {
return i;
}
void FrameInfo::popValue(ValueOperand dest) {
void CompilerFrameInfo::popValue(ValueOperand dest) {
StackValue* val = peek(-1);
switch (val->kind()) {
@ -115,7 +115,7 @@ void FrameInfo::popValue(ValueOperand dest) {
pop(DontAdjustStack);
}
void FrameInfo::popRegsAndSync(uint32_t uses) {
void CompilerFrameInfo::popRegsAndSync(uint32_t uses) {
// x86 has only 3 Value registers. Only support 2 regs here for now,
// so that there's always a scratch Value register for reg -> reg
// moves.
@ -146,8 +146,23 @@ void FrameInfo::popRegsAndSync(uint32_t uses) {
}
}
void FrameInfo::storeStackValue(int32_t depth, const Address& dest,
const ValueOperand& scratch) {
void InterpreterFrameInfo::popRegsAndSync(uint32_t uses) {
switch (uses) {
case 1:
popValue(R0);
break;
case 2: {
popValue(R1);
popValue(R0);
break;
}
default:
MOZ_CRASH("Invalid uses");
}
}
void CompilerFrameInfo::storeStackValue(int32_t depth, const Address& dest,
const ValueOperand& scratch) {
const StackValue* source = peek(depth);
switch (source->kind()) {
case StackValue::Constant:
@ -183,7 +198,7 @@ void FrameInfo::storeStackValue(int32_t depth, const Address& dest,
}
#ifdef DEBUG
void FrameInfo::assertValidState(const BytecodeInfo& info) {
void CompilerFrameInfo::assertValidState(const BytecodeInfo& info) {
// Check stack depth.
MOZ_ASSERT(stackDepth() == info.stackDepth);
@ -222,7 +237,7 @@ void FrameInfo::assertValidState(const BytecodeInfo& info) {
}
#endif
PCMappingSlotInfo::SlotLocation FrameInfo::stackValueSlotLocation(
PCMappingSlotInfo::SlotLocation CompilerFrameInfo::stackValueSlotLocation(
int32_t depth) {
const StackValue* stackVal = peek(depth);

View File

@ -22,13 +22,24 @@ namespace jit {
struct BytecodeInfo;
// FrameInfo overview.
// [SMDOC] Baseline 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.
// FrameInfo is used by BaselineCodeGen to track values stored in the frame.
// There are two implementations:
//
// InterpreterFrameInfo
// --------------------
// The InterpreterFrameInfo class is used by the interpreter generator and is
// a very simple interface on top of the MacroAssembler, because the stack is
// always synced.
//
// CompilerFrameInfo
// -----------------
// The CompilerFrameInfo class is more complicated because it maintains a
// virtual stack to optimize some common stack operations. 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:
//
@ -159,16 +170,56 @@ class StackValue {
enum StackAdjustment { AdjustStack, DontAdjustStack };
class FrameInfo {
JSScript* script;
protected:
MacroAssembler& masm;
public:
explicit FrameInfo(MacroAssembler& masm) : masm(masm) {}
Address addressOfLocal(size_t local) const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfLocal(local));
}
Address addressOfArg(size_t arg) const {
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 addressOfEnvironmentChain() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfEnvironmentChain());
}
Address addressOfFlags() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
}
Address addressOfReturnValue() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfReturnValue());
}
Address addressOfArgsObj() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj());
}
Address addressOfScratchValue() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfScratchValue());
}
};
class CompilerFrameInfo : public FrameInfo {
JSScript* script;
FixedList<StackValue> stack;
size_t spIndex;
public:
FrameInfo(JSScript* script, MacroAssembler& masm)
: script(script), masm(masm), stack(), spIndex(0) {}
CompilerFrameInfo(JSScript* script, MacroAssembler& masm)
: FrameInfo(masm), script(script), stack(), spIndex(0) {}
MOZ_MUST_USE bool init(TempAllocator& alloc);
size_t nlocals() const { return script->nfixed(); }
@ -202,6 +253,10 @@ class FrameInfo {
}
}
void assertStackDepth(uint32_t depth) { MOZ_ASSERT(stackDepth() == depth); }
void incStackDepth(int32_t diff) { setStackDepth(stackDepth() + diff); }
bool hasKnownStackDepth(uint32_t depth) { return stackDepth() == depth; }
inline void pop(StackAdjustment adjust = AdjustStack);
inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack);
inline void push(const Value& val) {
@ -237,38 +292,16 @@ class FrameInfo {
StackValue* sv = rawPush();
sv->setStack();
}
inline Address addressOfLocal(size_t local) const {
Address addressOfLocal(size_t local) const {
MOZ_ASSERT(local < nlocals());
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfLocal(local));
return FrameInfo::addressOfLocal(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 addressOfEnvironmentChain() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfEnvironmentChain());
}
Address addressOfFlags() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
}
Address addressOfReturnValue() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfReturnValue());
}
Address addressOfArgsObj() const {
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj());
return FrameInfo::addressOfArg(arg);
}
Address addressOfStackValue(int32_t depth) const {
const StackValue* value = peek(depth);
MOZ_ASSERT(value->kind() == StackValue::Stack);
@ -277,10 +310,6 @@ class FrameInfo {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfLocal(nlocals() + slot));
}
Address addressOfScratchValue() const {
return Address(BaselineFrameReg,
BaselineFrame::reverseOffsetOfScratchValue());
}
void popValue(ValueOperand dest);
@ -289,7 +318,7 @@ class FrameInfo {
uint32_t numUnsyncedSlots();
void popRegsAndSync(uint32_t uses);
inline void assertSyncedStack() const {
void assertSyncedStack() const {
MOZ_ASSERT_IF(stackDepth() > 0, peek(-1)->kind() == StackValue::Stack);
}
@ -310,6 +339,54 @@ class FrameInfo {
#endif
};
class InterpreterFrameInfo : public FrameInfo {
public:
explicit InterpreterFrameInfo(MacroAssembler& masm) : FrameInfo(masm) {}
// These methods are no-ops in the interpreter, because we don't have a
// virtual stack there.
void syncStack(uint32_t uses) {}
void assertSyncedStack() const {}
void assertStackDepth(uint32_t depth) {}
void incStackDepth(int32_t diff) {}
bool hasKnownStackDepth(uint32_t depth) { return false; }
uint32_t numUnsyncedSlots() { return 0; }
bool stackValueHasKnownType(int32_t depth, JSValueType type) const {
return false;
}
Address addressOfStackValue(int depth) const {
MOZ_ASSERT(depth < 0);
return Address(masm.getStackPointer(),
masm.framePushed() + size_t(-(depth + 1)) * sizeof(Value));
}
void popRegsAndSync(uint32_t uses);
void pop() { popn(1); }
void popn(uint32_t n) { masm.addToStackPtr(Imm32(n * sizeof(Value))); }
void popValue(ValueOperand dest) { masm.popValue(dest); }
void push(const ValueOperand& val,
JSValueType knownType = JSVAL_TYPE_UNKNOWN) {
masm.pushValue(val);
}
void push(const Value& val) { masm.pushValue(val); }
void pushThis() { masm.pushValue(addressOfThis()); }
void pushEvalNewTarget() { masm.pushValue(addressOfEvalNewTarget()); }
void pushScratchValue() { masm.pushValue(addressOfScratchValue()); }
void storeStackValue(int32_t depth, const Address& dest,
const ValueOperand& scratch) {
masm.loadValue(addressOfStackValue(depth), scratch);
masm.storeValue(scratch, dest);
}
};
} // namespace jit
} // namespace js

View File

@ -811,9 +811,12 @@ JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame) {
}
bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
jsbytecode* pc, uint32_t stackDepth) {
jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT);
MOZ_ASSERT(frame->numValueSlots() > frame->script()->nfixed());
uint32_t stackDepth = frame->numValueSlots() - frame->script()->nfixed();
// Return value is still on the stack.
MOZ_ASSERT(stackDepth >= 1);

View File

@ -1017,8 +1017,7 @@ void FrameIsDebuggeeCheck(BaselineFrame* frame);
JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame);
MOZ_MUST_USE bool NormalSuspend(JSContext* cx, HandleObject obj,
BaselineFrame* frame, jsbytecode* pc,
uint32_t stackDepth);
BaselineFrame* frame, jsbytecode* pc);
MOZ_MUST_USE bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc);
MOZ_MUST_USE bool InterpretResume(JSContext* cx, HandleObject obj,
HandleValue val, HandlePropertyName kind,