197 lines
5.1 KiB
C++
197 lines
5.1 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/. */
|
|
|
|
#include "jit/BaselineFrameInfo.h"
|
|
|
|
#ifdef DEBUG
|
|
# include "jit/BytecodeAnalysis.h"
|
|
#endif
|
|
|
|
#include "jit/BaselineFrameInfo-inl.h"
|
|
#include "jit/MacroAssembler-inl.h"
|
|
|
|
using namespace js;
|
|
using namespace js::jit;
|
|
|
|
bool
|
|
FrameInfo::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.
|
|
size_t extra = script->isGlobalCode() ? 1 : 0;
|
|
size_t nstack = Max(script->nslots() - script->nfixed(), size_t(MinJITStackSize)) + extra;
|
|
if (!stack.init(alloc, nstack))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
FrameInfo::sync(StackValue* val)
|
|
{
|
|
switch (val->kind()) {
|
|
case StackValue::Stack:
|
|
break;
|
|
case StackValue::LocalSlot:
|
|
masm.pushValue(addressOfLocal(val->localSlot()));
|
|
break;
|
|
case StackValue::ArgSlot:
|
|
masm.pushValue(addressOfArg(val->argSlot()));
|
|
break;
|
|
case StackValue::ThisSlot:
|
|
masm.pushValue(addressOfThis());
|
|
break;
|
|
case StackValue::EvalNewTargetSlot:
|
|
MOZ_ASSERT(script->isForEval());
|
|
masm.pushValue(addressOfEvalNewTarget());
|
|
break;
|
|
case StackValue::Register:
|
|
masm.pushValue(val->reg());
|
|
break;
|
|
case StackValue::Constant:
|
|
masm.pushValue(val->constant());
|
|
break;
|
|
default:
|
|
MOZ_CRASH("Invalid kind");
|
|
}
|
|
|
|
val->setStack();
|
|
}
|
|
|
|
void
|
|
FrameInfo::syncStack(uint32_t uses)
|
|
{
|
|
MOZ_ASSERT(uses <= stackDepth());
|
|
|
|
uint32_t depth = stackDepth() - uses;
|
|
|
|
for (uint32_t i = 0; i < depth; i++) {
|
|
StackValue* current = &stack[i];
|
|
sync(current);
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
FrameInfo::numUnsyncedSlots()
|
|
{
|
|
// Start at the bottom, find the first value that's not synced.
|
|
uint32_t i = 0;
|
|
for (; i < stackDepth(); i++) {
|
|
if (peek(-int32_t(i + 1))->kind() == StackValue::Stack)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
void
|
|
FrameInfo::popValue(ValueOperand dest)
|
|
{
|
|
StackValue* val = peek(-1);
|
|
|
|
switch (val->kind()) {
|
|
case StackValue::Constant:
|
|
masm.moveValue(val->constant(), dest);
|
|
break;
|
|
case StackValue::LocalSlot:
|
|
masm.loadValue(addressOfLocal(val->localSlot()), dest);
|
|
break;
|
|
case StackValue::ArgSlot:
|
|
masm.loadValue(addressOfArg(val->argSlot()), dest);
|
|
break;
|
|
case StackValue::ThisSlot:
|
|
masm.loadValue(addressOfThis(), dest);
|
|
break;
|
|
case StackValue::EvalNewTargetSlot:
|
|
masm.loadValue(addressOfEvalNewTarget(), dest);
|
|
break;
|
|
case StackValue::Stack:
|
|
masm.popValue(dest);
|
|
break;
|
|
case StackValue::Register:
|
|
masm.moveValue(val->reg(), dest);
|
|
break;
|
|
default:
|
|
MOZ_CRASH("Invalid kind");
|
|
}
|
|
|
|
// masm.popValue already adjusted the stack pointer, don't do it twice.
|
|
pop(DontAdjustStack);
|
|
}
|
|
|
|
void
|
|
FrameInfo::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.
|
|
MOZ_ASSERT(uses > 0);
|
|
MOZ_ASSERT(uses <= 2);
|
|
MOZ_ASSERT(uses <= stackDepth());
|
|
|
|
syncStack(uses);
|
|
|
|
switch (uses) {
|
|
case 1:
|
|
popValue(R0);
|
|
break;
|
|
case 2: {
|
|
// If the second value is in R1, move it to R2 so that it's not
|
|
// clobbered by the first popValue.
|
|
StackValue* val = peek(-2);
|
|
if (val->kind() == StackValue::Register && val->reg() == R1) {
|
|
masm.moveValue(R1, R2);
|
|
val->setRegister(R2);
|
|
}
|
|
popValue(R1);
|
|
popValue(R0);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH("Invalid uses");
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
FrameInfo::assertValidState(const BytecodeInfo& info)
|
|
{
|
|
// Check stack depth.
|
|
MOZ_ASSERT(stackDepth() == info.stackDepth);
|
|
|
|
// Start at the bottom, find the first value that's not synced.
|
|
uint32_t i = 0;
|
|
for (; i < stackDepth(); i++) {
|
|
if (stack[i].kind() != StackValue::Stack)
|
|
break;
|
|
}
|
|
|
|
// Assert all values on top of it are also not synced.
|
|
for (; i < stackDepth(); i++)
|
|
MOZ_ASSERT(stack[i].kind() != StackValue::Stack);
|
|
|
|
// Assert every Value register is used by at most one StackValue.
|
|
// R2 is used as scratch register by the compiler and FrameInfo,
|
|
// so it shouldn't be used for StackValues.
|
|
bool usedR0 = false, usedR1 = false;
|
|
|
|
for (i = 0; i < stackDepth(); i++) {
|
|
if (stack[i].kind() == StackValue::Register) {
|
|
ValueOperand reg = stack[i].reg();
|
|
if (reg == R0) {
|
|
MOZ_ASSERT(!usedR0);
|
|
usedR0 = true;
|
|
} else if (reg == R1) {
|
|
MOZ_ASSERT(!usedR1);
|
|
usedR1 = true;
|
|
} else {
|
|
MOZ_CRASH("Invalid register");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|