void FrameState::pushCopyOf(uint32 index) { FrameEntry *backing = entryFor(index); FrameEntry *fe = rawPush(); fe->resetUnsynced(); if (backing->isConstant()) { fe->setConstant(Jsvalify(backing->getValue())); } else { if (backing->isTypeKnown()) fe->setType(backing->getKnownType()); else fe->type.invalidate(); fe->isNumber = backing->isNumber; fe->data.invalidate(); if (backing->isCopy()) { backing = backing->copyOf(); fe->setCopyOf(backing); } else { fe->setCopyOf(backing); backing->setCopied(); } /* Maintain tracker ordering guarantees for copies. */ JS_ASSERT(backing->isCopied()); if (fe->trackerIndex() < backing->trackerIndex()) swapInTracker(fe, backing); } }
void FrameState::assertValidRegisterState() const { Registers checkedFreeRegs; FrameEntry *tos = tosFe(); for (uint32 i = 0; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; if (fe >= tos) continue; JS_ASSERT(i == fe->trackerIndex()); JS_ASSERT_IF(fe->isCopy(), fe->trackerIndex() > fe->copyOf()->trackerIndex()); JS_ASSERT_IF(fe->isCopy(), !fe->type.inRegister() && !fe->data.inRegister()); JS_ASSERT_IF(fe->isCopy(), fe->copyOf() < tos); JS_ASSERT_IF(fe->isCopy(), fe->copyOf()->isCopied()); if (fe->isCopy()) continue; if (fe->type.inRegister()) { checkedFreeRegs.takeReg(fe->type.reg()); JS_ASSERT(regstate[fe->type.reg()].fe == fe); } if (fe->data.inRegister()) { checkedFreeRegs.takeReg(fe->data.reg()); JS_ASSERT(regstate[fe->data.reg()].fe == fe); } } JS_ASSERT(checkedFreeRegs == freeRegs); }
void FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange) { FrameEntry *localFe = getLocal(n); bool cacheable = !eval && !escaping[n]; if (!popGuaranteed && !cacheable) { JS_ASSERT_IF(base[localIndex(n)] && (!eval || n < script->nfixed), entries[localIndex(n)].type.inMemory() && entries[localIndex(n)].data.inMemory()); Address local(JSFrameReg, sizeof(JSStackFrame) + n * sizeof(Value)); storeTo(peek(-1), local, false); forgetAllRegs(getLocal(n)); localFe->resetSynced(); return; } bool wasSynced = localFe->type.synced(); /* Detect something like (x = x) which is a no-op. */ FrameEntry *top = peek(-1); if (top->isCopy() && top->copyOf() == localFe) { JS_ASSERT(localFe->isCopied()); return; } /* Completely invalidate the local variable. */ if (localFe->isCopied()) { uncopy(localFe); if (!localFe->isCopied()) forgetAllRegs(localFe); } else { forgetAllRegs(localFe); } localFe->resetUnsynced(); /* Constants are easy to propagate. */ if (top->isConstant()) { localFe->setCopyOf(NULL); localFe->setNotCopied(); localFe->setConstant(Jsvalify(top->getValue())); return; } /* * When dealing with copies, there are two important invariants: * * 1) The backing store precedes all copies in the tracker. * 2) The backing store of a local is never a stack slot, UNLESS the local * variable itself is a stack slot (blocks) that precedes the stack * slot. * * If the top is a copy, and the second condition holds true, the local * can be rewritten as a copy of the original backing slot. If the first * condition does not hold, force it to hold by swapping in-place. */ FrameEntry *backing = top; if (top->isCopy()) { backing = top->copyOf(); JS_ASSERT(backing->trackerIndex() < top->trackerIndex()); uint32 backingIndex = indexOfFe(backing); uint32 tol = uint32(spBase - base); if (backingIndex < tol || backingIndex < localIndex(n)) { /* local.idx < backing.idx means local cannot be a copy yet */ if (localFe->trackerIndex() < backing->trackerIndex()) swapInTracker(backing, localFe); localFe->setNotCopied(); localFe->setCopyOf(backing); if (backing->isTypeKnown()) localFe->setType(backing->getKnownType()); else localFe->type.invalidate(); localFe->data.invalidate(); localFe->isNumber = backing->isNumber; return; } /* * If control flow lands here, then there was a bytecode sequence like * * ENTERBLOCK 2 * GETLOCAL 1 * SETLOCAL 0 * * The problem is slot N can't be backed by M if M could be popped * before N. We want a guarantee that when we pop M, even if it was * copied, it has no outstanding copies. * * Because of |let| expressions, it's kind of hard to really know * whether a region on the stack will be popped all at once. Bleh! * * This should be rare except in browser code (and maybe even then), * but even so there's a quick workaround. We take all copies of the * backing fe, and redirect them to be copies of the destination. */ FrameEntry *tos = tosFe(); for (uint32 i = backing->trackerIndex() + 1; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; if (fe >= tos) continue; if (fe->isCopy() && fe->copyOf() == backing) fe->setCopyOf(localFe); } } backing->setNotCopied(); /* * This is valid from the top->isCopy() path because we're guaranteed a * consistent ordering - all copies of |backing| are tracked after * |backing|. Transitively, only one swap is needed. */ if (backing->trackerIndex() < localFe->trackerIndex()) swapInTracker(backing, localFe); /* * Move the backing store down - we spill registers here, but we could be * smarter and re-use the type reg. */ RegisterID reg = tempRegForData(backing); localFe->data.setRegister(reg); moveOwnership(reg, localFe); if (typeChange) { if (backing->isTypeKnown()) { localFe->setType(backing->getKnownType()); } else { RegisterID reg = tempRegForType(backing); localFe->type.setRegister(reg); moveOwnership(reg, localFe); } } else { if (!wasSynced) masm.storeTypeTag(ImmType(backing->getKnownType()), addressOf(localFe)); localFe->type.setMemory(); } if (!backing->isTypeKnown()) backing->type.invalidate(); backing->data.invalidate(); backing->setCopyOf(localFe); backing->isNumber = localFe->isNumber; localFe->setCopied(); if (!cacheable) { /* TODO: x64 optimization */ if (!localFe->type.synced()) syncType(localFe, addressOf(localFe), masm); if (!localFe->data.synced()) syncData(localFe, addressOf(localFe), masm); forgetAllRegs(localFe); localFe->type.setMemory(); localFe->data.setMemory(); } JS_ASSERT(top->copyOf() == localFe); }
FrameEntry * FrameState::uncopy(FrameEntry *original) { JS_ASSERT(original->isCopied()); /* * Copies have two critical invariants: * 1) The backing store precedes all copies in the tracker. * 2) The backing store of a copy cannot be popped from the stack * while the copy is still live. * * Maintaining this invariant iteratively is kind of hard, so we choose * the "lowest" copy in the frame up-front. * * For example, if the stack is: * [A, B, C, D] * And the tracker has: * [A, D, C, B] * * If B, C, and D are copies of A - we will walk the tracker to the end * and select D, not B (see bug 583684). */ uint32 firstCopy = InvalidIndex; FrameEntry *tos = tosFe(); FrameEntry *bestFe = NULL; uint32 ncopies = 0; for (uint32 i = 0; i < tracker.nentries; i++) { FrameEntry *fe = tracker[i]; if (fe >= tos) continue; if (fe->isCopy() && fe->copyOf() == original) { if (firstCopy == InvalidIndex) { firstCopy = i; bestFe = fe; } else if (fe < bestFe) { bestFe = fe; } ncopies++; } } if (!ncopies) { JS_ASSERT(firstCopy == InvalidIndex); JS_ASSERT(!bestFe); original->copied = false; return NULL; } JS_ASSERT(firstCopy != InvalidIndex); JS_ASSERT(bestFe); /* Mark all extra copies as copies of the new backing index. */ bestFe->setCopyOf(NULL); if (ncopies > 1) { bestFe->setCopied(); for (uint32 i = firstCopy; i < tracker.nentries; i++) { FrameEntry *other = tracker[i]; if (other >= tos || other == bestFe) continue; /* The original must be tracked before copies. */ JS_ASSERT(other != original); if (!other->isCopy() || other->copyOf() != original) continue; other->setCopyOf(bestFe); /* * This is safe even though we're mutating during iteration. There * are two cases. The first is that both indexes are <= i, and :. * will never be observed. The other case is we're placing the * other FE such that it will be observed later. Luckily, copyOf() * will return != original, so nothing will happen. */ if (other->trackerIndex() < bestFe->trackerIndex()) swapInTracker(bestFe, other); } } else { bestFe->setNotCopied(); } FrameEntry *fe = bestFe; /* * Switch the new backing store to the old backing store. During * this process we also necessarily make sure the copy can be * synced. */ if (!original->isTypeKnown()) { /* * If the copy is unsynced, and the original is in memory, * give the original a register. We do this below too; it's * okay if it's spilled. */ if (original->type.inMemory() && !fe->type.synced()) tempRegForType(original); fe->type.inherit(original->type); if (fe->type.inRegister()) moveOwnership(fe->type.reg(), fe); } else { JS_ASSERT(fe->isTypeKnown()); JS_ASSERT(fe->getKnownType() == original->getKnownType()); } if (original->data.inMemory() && !fe->data.synced()) tempRegForData(original); fe->data.inherit(original->data); if (fe->data.inRegister()) moveOwnership(fe->data.reg(), fe); return fe; }