Faster x64 Syncing. b=598839, r=dvander.

This commit is contained in:
Sean Stangl 2010-10-12 17:39:30 -04:00
parent 39f5c14292
commit d96bb05817
8 changed files with 290 additions and 176 deletions

View File

@ -46,7 +46,7 @@
#elif defined JS_PUNBOX64
# include "PunboxAssembler.h"
#else
# error "Neither JS_NUNBOX32 nor JS_PUNBOX32 is defined."
# error "Neither JS_NUNBOX32 nor JS_PUNBOX64 is defined."
#endif
/* Get a label for assertion purposes. Prevent #ifdef clutter. */

View File

@ -1008,8 +1008,8 @@ mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *tar
JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
/* The lhs/rhs need to be synced in the stub call path. */
frame.syncEntry(stubcc.masm, lhs, lvr);
frame.syncEntry(stubcc.masm, rhs, rvr);
frame.ensureValueSynced(stubcc.masm, lhs, lvr);
frame.ensureValueSynced(stubcc.masm, rhs, rvr);
/* Call the stub, adjusting for the two values just pushed. */
stubcc.call(stub, frame.stackDepth() + script->nfixed + 2);

View File

@ -1653,13 +1653,11 @@ mjit::Compiler::jsop_stricteq(JSOp op)
masm.set32(cond, frame.tempRegForType(test), Imm32(mask), result);
#elif defined JS_CPU_X64
RegisterID maskReg = frame.allocReg();
frame.pinReg(maskReg);
masm.move(ImmTag(known->getKnownTag()), maskReg);
RegisterID r = frame.tempRegForType(test);
masm.setPtr(cond, r, maskReg, result);
frame.unpinReg(maskReg);
frame.freeReg(maskReg);
#endif
frame.popn(2);

View File

@ -195,6 +195,7 @@ class FrameEntry
FrameEntry *copyOf() const {
JS_ASSERT(isCopy());
JS_ASSERT(copy < this);
return copy;
}

View File

@ -126,20 +126,6 @@ FrameState::allocReg(FrameEntry *fe, RematInfo::RematType type)
return reg;
}
inline void
FrameState::emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const
{
emitLoadTypeTag(this->masm, fe, reg);
}
inline void
FrameState::emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const
{
if (fe->isCopy())
fe = fe->copyOf();
masm.loadTypeTag(addressOf(fe), reg);
}
inline void
FrameState::convertInt32ToDouble(Assembler &masm, FrameEntry *fe, FPRegisterID fpreg) const
{
@ -485,33 +471,188 @@ FrameState::shouldAvoidDataRemat(FrameEntry *fe)
}
inline void
FrameState::syncType(const FrameEntry *fe, Address to, Assembler &masm) const
FrameState::ensureFeSynced(const FrameEntry *fe, Assembler &masm) const
{
JS_ASSERT_IF(fe->type.synced(),
fe->isCopied() && addressOf(fe).offset != to.offset);
JS_ASSERT(fe->type.inRegister() || fe->type.isConstant());
Address to = addressOf(fe);
const FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
/* Store a double's type bits, even though !isTypeKnown(). */
if (fe->isConstant())
masm.storeTypeTag(ImmTag(fe->getKnownTag()), to);
else if (fe->isTypeKnown())
masm.storeTypeTag(ImmType(fe->getKnownType()), to);
#if defined JS_PUNBOX64
/* If we can, sync the type and data in one go. */
if (!fe->data.synced() && !fe->type.synced()) {
if (backing->isConstant())
masm.storeValue(backing->getValue(), to);
else if (backing->isTypeKnown())
masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to);
else
masm.storeTypeTag(fe->type.reg(), to);
masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to);
return;
}
#endif
/*
* On x86_64, only one of the following two calls will have output,
* and a load will only occur if necessary.
*/
ensureDataSynced(fe, masm);
ensureTypeSynced(fe, masm);
}
inline void
FrameState::syncData(const FrameEntry *fe, Address to, Assembler &masm) const
FrameState::ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const
{
JS_ASSERT_IF(addressOf(fe).base == to.base &&
addressOf(fe).offset == to.offset,
!fe->data.synced());
JS_ASSERT(fe->data.inRegister() || fe->data.isConstant());
if (fe->type.synced())
return;
if (fe->data.isConstant())
masm.storePayload(ImmPayload(fe->getPayload()), to);
Address to = addressOf(fe);
const FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
#if defined JS_PUNBOX64
/* Attempt to store the entire Value, to prevent a load. */
if (backing->isConstant()) {
masm.storeValue(backing->getValue(), to);
return;
}
if (backing->data.inRegister()) {
RegisterID dreg = backing->data.reg();
if (backing->isTypeKnown())
masm.storeValueFromComponents(ImmType(backing->getKnownType()), dreg, to);
else
masm.storePayload(fe->data.reg(), to);
masm.storeValueFromComponents(backing->type.reg(), dreg, to);
return;
}
#endif
/* Store a double's type bits, even though !isTypeKnown(). */
if (backing->isConstant())
masm.storeTypeTag(ImmTag(backing->getKnownTag()), to);
else if (fe->isTypeKnown())
masm.storeTypeTag(ImmType(backing->getKnownType()), to);
else
masm.storeTypeTag(backing->type.reg(), to);
}
inline void
FrameState::ensureDataSynced(const FrameEntry *fe, Assembler &masm) const
{
if (fe->data.synced())
return;
Address to = addressOf(fe);
const FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
#if defined JS_PUNBOX64
if (backing->isConstant())
masm.storeValue(backing->getValue(), to);
else if (backing->isTypeKnown())
masm.storeValueFromComponents(ImmType(backing->getKnownType()), backing->data.reg(), to);
else if (backing->type.inRegister())
masm.storeValueFromComponents(backing->type.reg(), backing->data.reg(), to);
else
masm.storePayload(backing->data.reg(), to);
#elif defined JS_NUNBOX32
if (backing->isConstant())
masm.storePayload(ImmPayload(backing->getPayload()), to);
else
masm.storePayload(backing->data.reg(), to);
#endif
}
inline void
FrameState::syncFe(FrameEntry *fe)
{
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
bool needTypeReg = !fe->type.synced() && backing->type.inMemory();
bool needDataReg = !fe->data.synced() && backing->data.inMemory();
#if defined JS_NUNBOX32
/* Determine an ordering that won't spill known regs. */
if (needTypeReg && !needDataReg) {
syncData(fe);
syncType(fe);
} else {
syncType(fe);
syncData(fe);
}
#elif defined JS_PUNBOX64
if (JS_UNLIKELY(needTypeReg && needDataReg)) {
/* Memory-to-memory moves can only occur for copies backed by memory. */
JS_ASSERT(backing != fe);
/* Use ValueReg to do a whole-Value mem-to-mem move. */
masm.loadValue(addressOf(backing), Registers::ValueReg);
masm.storeValue(Registers::ValueReg, addressOf(fe));
} else {
/* Store in case unpinning is necessary. */
MaybeRegisterID pairReg;
/* Get a register if necessary, without clobbering its pair. */
if (needTypeReg) {
if (backing->data.inRegister()) {
pairReg = backing->data.reg();
pinReg(backing->data.reg());
}
tempRegForType(backing);
} else if (needDataReg) {
if (backing->type.inRegister()) {
pairReg = backing->type.reg();
pinReg(backing->type.reg());
}
tempRegForData(backing);
}
ensureFeSynced(fe, masm);
if (pairReg.isSet())
unpinReg(pairReg.reg());
}
if (!fe->type.synced())
fe->type.sync();
if (!fe->data.synced())
fe->data.sync();
#endif
}
inline void
FrameState::syncType(FrameEntry *fe)
{
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
if (!fe->type.synced() && backing->type.inMemory())
tempRegForType(backing);
ensureTypeSynced(fe, masm);
if (!fe->type.synced())
fe->type.sync();
}
inline void
FrameState::syncData(FrameEntry *fe)
{
FrameEntry *backing = fe;
if (fe->isCopy())
backing = fe->copyOf();
if (!fe->data.synced() && backing->data.inMemory())
tempRegForData(backing);
ensureDataSynced(fe, masm);
if (!fe->data.synced())
fe->data.sync();
}
inline void
@ -525,7 +666,17 @@ FrameState::forgetType(FrameEntry *fe)
if (!fe->isTypeKnown())
return;
syncType(fe, addressOf(fe), masm);
/*
* Likewise, storeLocal() may have set this FE, with a known type,
* to be a copy of another FE, which has an unknown type.
* Just forget the type, since the backing is used in all cases.
*/
if (fe->isCopy()) {
fe->type.invalidate();
return;
}
ensureTypeSynced(fe, masm);
fe->type.setMemory();
}
@ -852,11 +1003,7 @@ FrameState::loadDouble(FrameEntry *fe, FPRegisterID fpReg, Assembler &masm) cons
return;
}
if (!fe->data.synced())
syncData(fe, addressOf(fe), masm);
if (!fe->type.synced())
syncType(fe, addressOf(fe), masm);
ensureFeSynced(fe, masm);
masm.loadDouble(addressOf(fe), fpReg);
}

View File

@ -121,16 +121,10 @@ FrameState::evictReg(RegisterID reg)
FrameEntry *fe = regstate[reg].fe();
if (regstate[reg].type() == RematInfo::TYPE) {
if (!fe->type.synced()) {
syncType(fe, addressOf(fe), masm);
fe->type.sync();
}
ensureTypeSynced(fe, masm);
fe->type.setMemory();
} else {
if (!fe->data.synced()) {
syncData(fe, addressOf(fe), masm);
fe->data.sync();
}
ensureDataSynced(fe, masm);
fe->data.setMemory();
}
}
@ -441,23 +435,34 @@ FrameState::sync(Assembler &masm, Uses uses) const
return;
/* Sync all registers up-front. */
for (uint32 i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
RegisterID reg = RegisterID(i);
Registers allRegs(Registers::AvailRegs);
while (!allRegs.empty()) {
RegisterID reg = allRegs.takeAnyReg();
FrameEntry *fe = regstate[reg].usedBy();
if (!fe)
continue;
JS_ASSERT(fe->isTracked());
#if defined JS_PUNBOX64
/* Sync entire FE to prevent loads. */
ensureFeSynced(fe, masm);
/* Take the other register in the pair, if one exists. */
if (regstate[reg].type() == RematInfo::DATA && fe->type.inRegister())
allRegs.takeReg(fe->type.reg());
else if (regstate[reg].type() == RematInfo::TYPE && fe->data.inRegister())
allRegs.takeReg(fe->data.reg());
#elif defined JS_NUNBOX32
/* Sync register if unsynced. */
if (regstate[reg].type() == RematInfo::DATA) {
JS_ASSERT(fe->data.reg() == reg);
if (!fe->data.synced())
syncData(fe, addressOf(fe), masm);
ensureDataSynced(fe, masm);
} else {
JS_ASSERT(fe->type.reg() == reg);
if (!fe->type.synced())
syncType(fe, addressOf(fe), masm);
ensureTypeSynced(fe, masm);
}
#endif
}
/*
@ -473,51 +478,37 @@ FrameState::sync(Assembler &masm, Uses uses) const
if (!fe->isTracked())
continue;
Address address = addressOf(fe);
FrameEntry *backing = fe;
if (!fe->isCopy()) {
/*
* If this |fe| has registers, track them as available. They've
* already been synced. Otherwise, see if a constant needs to be
* synced.
*/
if (fe->data.inRegister())
avail.putReg(fe->data.reg());
else if (!fe->data.synced())
syncData(fe, address, masm);
if (fe->type.inRegister())
avail.putReg(fe->type.reg());
else if (!fe->type.synced())
syncType(fe, address, masm);
} else {
FrameEntry *backing = fe->copyOf();
JS_ASSERT(backing != fe);
backing = fe->copyOf();
JS_ASSERT(!backing->isConstant() && !fe->isConstant());
/*
* If the copy is backed by something not in a register, fall back
* to a slower sync algorithm.
*/
if ((!fe->type.synced() && !backing->type.inRegister()) ||
(!fe->data.synced() && !backing->data.inRegister())) {
/* Fall back to a slower sync algorithm if load required. */
if ((!fe->type.synced() && backing->type.inMemory()) ||
(!fe->data.synced() && backing->data.inMemory())) {
syncFancy(masm, avail, fe, bottom);
return;
}
if (!fe->type.synced()) {
/* :TODO: we can do better, the type is learned for all copies. */
if (fe->isTypeKnown()) {
//JS_ASSERT(fe->getTypeTag() == backing->getTypeTag());
masm.storeTypeTag(ImmType(fe->getKnownType()), address);
} else {
masm.storeTypeTag(backing->type.reg(), address);
}
}
if (!fe->data.synced())
masm.storePayload(backing->data.reg(), address);
}
/* If a part still needs syncing, it is either a copy or constant. */
#if defined JS_PUNBOX64
/* All register-backed FEs have been entirely synced up-front. */
if (!fe->type.inRegister() && !fe->data.inRegister())
ensureFeSynced(fe, masm);
#elif defined JS_NUNBOX32
/* All components held in registers have been already synced. */
if (!fe->data.inRegister())
ensureDataSynced(fe, masm);
if (!fe->type.inRegister())
ensureTypeSynced(fe, masm);
#endif
}
}
@ -536,19 +527,35 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
JS_ASSERT(fe->isTracked());
#if defined JS_PUNBOX64
/* Don't use syncFe(), since that may clobber more registers. */
ensureFeSynced(fe, masm);
if (!fe->type.synced())
fe->type.sync();
if (!fe->data.synced())
fe->data.sync();
/* Take the other register in the pair, if one exists. */
if (regstate[reg].type() == RematInfo::DATA) {
JS_ASSERT(fe->data.reg() == reg);
if (!fe->data.synced()) {
syncData(fe, addressOf(fe), masm);
fe->data.sync();
}
if (fe->type.inRegister() && search.hasReg(fe->type.reg()))
search.takeReg(fe->type.reg());
} else {
JS_ASSERT(fe->type.reg() == reg);
if (!fe->type.synced()) {
syncType(fe, addressOf(fe), masm);
fe->type.sync();
if (fe->data.inRegister() && search.hasReg(fe->data.reg()))
search.takeReg(fe->data.reg());
}
#elif defined JS_NUNBOX32
/* Sync this register. */
if (regstate[reg].type() == RematInfo::DATA) {
JS_ASSERT(fe->data.reg() == reg);
syncData(fe);
} else {
JS_ASSERT(fe->type.reg() == reg);
syncType(fe);
}
#endif
}
uint32 maxvisits = tracker.nentries;
@ -563,33 +570,20 @@ FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
if (fe >= spStop)
continue;
Address address = addressOf(fe);
FrameEntry *backing = fe;
syncFe(fe);
if (fe->isCopy())
backing = fe->copyOf();
if (!fe->data.synced()) {
if (backing != fe && backing->data.inMemory())
tempRegForData(backing);
syncData(backing, address, masm);
fe->data.sync();
if (fe->data.inRegister() && kill.hasReg(fe->data.reg())) {
/* Forget registers. */
if (fe->data.inRegister() && kill.hasReg(fe->data.reg()) &&
!regstate[fe->data.reg()].isPinned()) {
forgetReg(fe->data.reg());
fe->data.setMemory();
}
}
if (!fe->type.synced()) {
if (backing != fe && backing->type.inMemory())
tempRegForType(backing);
syncType(backing, address, masm);
fe->type.sync();
if (fe->type.inRegister() && kill.hasReg(fe->type.reg())) {
if (fe->type.inRegister() && kill.hasReg(fe->type.reg()) &&
!regstate[fe->type.reg()].isPinned()) {
forgetReg(fe->type.reg());
fe->type.setMemory();
}
}
}
/*
* Anything still alive at this point is guaranteed to be synced. However,
@ -669,8 +663,7 @@ FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint)
RegisterID reg = fe->data.reg();
if (reg == hint) {
if (freeRegs.empty()) {
if (!fe->data.synced())
syncData(fe, addressOf(fe), masm);
ensureDataSynced(fe, masm);
fe->data.setMemory();
} else {
reg = allocReg();
@ -698,8 +691,7 @@ FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe)
if (fe->data.inRegister()) {
RegisterID reg = fe->data.reg();
if (freeRegs.empty()) {
if (!fe->data.synced())
syncData(fe, addressOf(fe), masm);
ensureDataSynced(fe, masm);
fe->data.setMemory();
regstate[reg].forget();
} else {
@ -731,8 +723,7 @@ FrameState::copyTypeIntoReg(FrameEntry *fe)
if (fe->type.inRegister()) {
RegisterID reg = fe->type.reg();
if (freeRegs.empty()) {
if (!fe->type.synced())
syncType(fe, addressOf(fe), masm);
ensureTypeSynced(fe, masm);
fe->type.setMemory();
regstate[reg].forget();
} else {
@ -784,13 +775,9 @@ FrameState::copyEntryIntoFPReg(Assembler &masm, FrameEntry *fe, FPRegisterID fpr
if (fe->isCopy())
fe = fe->copyOf();
/* The entry must be synced to memory. */
if (!fe->data.synced())
syncData(fe, addressOf(fe), masm);
if (!fe->type.synced())
syncType(fe, addressOf(fe), masm);
ensureFeSynced(fe, masm);
masm.loadDouble(addressOf(fe), fpreg);
return fpreg;
}
@ -810,8 +797,7 @@ FrameState::ownRegForType(FrameEntry *fe)
if (freeRegs.empty()) {
/* For now... just steal the register that already exists. */
if (!backing->type.synced())
syncType(backing, addressOf(backing), masm);
ensureTypeSynced(backing, masm);
reg = backing->type.reg();
backing->type.setMemory();
regstate[reg].forget();
@ -854,8 +840,7 @@ FrameState::ownRegForData(FrameEntry *fe)
if (freeRegs.empty()) {
/* For now... just steal the register that already exists. */
if (!backing->data.synced())
syncData(backing, addressOf(backing), masm);
ensureDataSynced(backing, masm);
reg = backing->data.reg();
backing->data.setMemory();
regstate[reg].forget();
@ -1106,33 +1091,14 @@ FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange)
return;
/* Ensure that the local variable remains synced. */
if (local->isCopy()) {
FrameEntry *backing = local->copyOf();
if (!local->data.synced()) {
if (backing->data.inMemory())
tempRegForData(backing);
syncData(backing, addressOf(local), masm);
}
if (!local->type.synced()) {
if (backing->type.inMemory())
tempRegForType(backing);
syncType(backing, addressOf(local), masm);
}
} else {
if (!local->data.synced()) {
syncData(local, addressOf(local), masm);
local->data.sync();
}
if (!local->type.synced()) {
syncType(local, addressOf(local), masm);
local->type.sync();
}
if (closed)
forgetEntry(local);
}
syncFe(local);
if (closed)
if (closed) {
/* If the FE can have registers, free them before resetting. */
if (!local->isCopy())
forgetEntry(local);
local->resetSynced();
}
}
void
@ -1336,7 +1302,7 @@ FrameState::unpinEntry(const ValueRemat &vr)
}
void
FrameState::syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr)
FrameState::ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr)
{
#if defined JS_PUNBOX64
if (!vr.isDataSynced || !vr.isTypeSynced)

View File

@ -408,13 +408,6 @@ class FrameState
*/
inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const;
/*
* Forcibly loads the type tag for the specified FrameEntry
* into a register already marked as owning the type.
*/
inline void emitLoadTypeTag(FrameEntry *fe, RegisterID reg) const;
inline void emitLoadTypeTag(Assembler &masm, FrameEntry *fe, RegisterID reg) const;
/*
* Convert an integer to a double without applying
* additional Register pressure.
@ -490,7 +483,7 @@ class FrameState
void unpinEntry(const ValueRemat &vr);
/* Syncs fe to memory, given its state as constructed by a call to pinEntry. */
void syncEntry(Assembler &masm, FrameEntry *fe, const ValueRemat &vr);
void ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr);
struct BinaryAlloc {
MaybeRegisterID lhsType;
@ -798,8 +791,17 @@ class FrameState
void evictReg(RegisterID reg);
inline FrameEntry *rawPush();
inline void addToTracker(FrameEntry *fe);
inline void syncType(const FrameEntry *fe, Address to, Assembler &masm) const;
inline void syncData(const FrameEntry *fe, Address to, Assembler &masm) const;
/* Guarantee sync, but do not set any sync flag. */
inline void ensureFeSynced(const FrameEntry *fe, Assembler &masm) const;
inline void ensureTypeSynced(const FrameEntry *fe, Assembler &masm) const;
inline void ensureDataSynced(const FrameEntry *fe, Assembler &masm) const;
/* Guarantee sync, even if register allocation is required, and set sync. */
inline void syncFe(FrameEntry *fe);
inline void syncType(FrameEntry *fe);
inline void syncData(FrameEntry *fe);
inline FrameEntry *getLocal(uint32 slot);
inline void forgetAllRegs(FrameEntry *fe);
inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs);

View File

@ -131,8 +131,8 @@ class Assembler : public BaseAssembler
}
void loadValueAsComponents(const Value &val, RegisterID type, RegisterID payload) {
move(Imm64(val.asRawBits() & 0xFFFF800000000000), type);
move(Imm64(val.asRawBits() & 0x00007FFFFFFFFFFF), payload);
move(Imm64(val.asRawBits() & JSVAL_TAG_MASK), type);
move(Imm64(val.asRawBits() & JSVAL_PAYLOAD_MASK), payload);
}
template <typename T>