// Sink IncRefs consumed off trace. // When <trace> is an exit trace, <toSink> contains all live IncRefs in the // main trace that are consumed off trace. void sinkIncRefs(Trace* trace, IRFactory* irFactory, IRInstruction::List& toSink) { IRInstruction::List& instList = trace->getInstructionList(); IRInstruction::Iterator it; std::map<SSATmp*, SSATmp*> sunkTmps; if (!trace->isMain()) { // Sink REFCOUNT_CONSUMED_OFF_TRACE IncRefs before the first non-label // instruction, and create a mapping between the original tmps to the sunk // tmps so that we can later replace the original ones with the sunk ones. for (IRInstruction::ReverseIterator j = toSink.rbegin(); j != toSink.rend(); ++j) { // prependInstruction inserts an instruction to the beginning. Therefore, // we iterate through toSink in the reversed order. IRInstruction* sunkInst = irFactory->incRef((*j)->getSrc(0)); sunkInst->setId(LIVE); trace->prependInstruction(sunkInst); ASSERT((*j)->getDst()); ASSERT(!sunkTmps.count((*j)->getDst())); sunkTmps[(*j)->getDst()] = irFactory->getSSATmp(sunkInst); } } // An exit trace may be entered from multiple exit points. We keep track of // which exit traces we already pushed sunk IncRefs to, so that we won't push // them multiple times. std::set<Trace*> pushedTo; for (it = instList.begin(); it != instList.end(); ++it) { IRInstruction* inst = *it; if (trace->isMain()) { if (inst->getOpcode() == IncRef) { // Must be REFCOUNT_CONSUMED or REFCOUNT_CONSUMED_OFF_TRACE; // otherwise, it should be already removed in optimizeRefCount. ASSERT(inst->getId() == REFCOUNT_CONSUMED || inst->getId() == REFCOUNT_CONSUMED_OFF_TRACE); if (inst->getId() == REFCOUNT_CONSUMED_OFF_TRACE) { inst->setOpcode(Mov); // Mark them as dead so that they'll be removed later. inst->setId(DEAD); // Put all REFCOUNT_CONSUMED_OFF_TRACE IncRefs to the sinking list. toSink.push_back(inst); } } if (inst->getOpcode() == DecRefNZ) { IRInstruction* srcInst = inst->getSrc(0)->getInstruction(); if (srcInst->getId() == DEAD) { inst->setId(DEAD); // This may take O(I) time where I is the number of IncRefs // in the main trace. toSink.remove(srcInst); } } if (LabelInstruction* label = inst->getLabel()) { Trace* exitTrace = label->getTrace(); if (!pushedTo.count(exitTrace)) { pushedTo.insert(exitTrace); sinkIncRefs(exitTrace, irFactory, toSink); } } } else { // Replace the original tmps with the sunk tmps. for (uint32 i = 0; i < inst->getNumSrcs(); ++i) { SSATmp* src = inst->getSrc(i); if (SSATmp* sunkTmp = sunkTmps[src]) { inst->setSrc(i, sunkTmp); } } } } // Do copyProp at last, because we need to keep REFCOUNT_CONSUMED_OFF_TRACE // Movs as the prototypes for sunk instructions. for (it = instList.begin(); it != instList.end(); ++it) { Simplifier::copyProp(*it); } }