void WebAssemblyExceptionInfo::discoverAndMapException( WebAssemblyException *WE, const MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF) { unsigned NumBlocks = 0; unsigned NumSubExceptions = 0; // Map blocks that belong to a catchpad / cleanuppad MachineBasicBlock *EHPad = WE->getEHPad(); // We group catch & catch-all terminate pads together within an exception if (WebAssembly::isCatchTerminatePad(*EHPad)) { assert(EHPad->succ_size() == 1 && "Catch terminate pad has more than one successors"); changeExceptionFor(EHPad, WE); changeExceptionFor(*(EHPad->succ_begin()), WE); return; } SmallVector<MachineBasicBlock *, 8> WL; WL.push_back(EHPad); while (!WL.empty()) { MachineBasicBlock *MBB = WL.pop_back_val(); // Find its outermost discovered exception. If this is a discovered block, // check if it is already discovered to be a subexception of this exception. WebAssemblyException *SubE = getOutermostException(MBB); if (SubE) { if (SubE != WE) { // Discover a subexception of this exception. SubE->setParentException(WE); ++NumSubExceptions; NumBlocks += SubE->getBlocksVector().capacity(); // All blocks that belong to this subexception have been already // discovered. Skip all of them. Add the subexception's landing pad's // dominance frontier to the worklist. for (auto &Frontier : MDF.find(SubE->getEHPad())->second) if (MDT.dominates(EHPad, Frontier)) WL.push_back(Frontier); } continue; } // This is an undiscovered block. Map it to the current exception. changeExceptionFor(MBB, WE); ++NumBlocks; // Add successors dominated by the current BB to the worklist. for (auto *Succ : MBB->successors()) if (MDT.dominates(EHPad, Succ)) WL.push_back(Succ); } WE->getSubExceptions().reserve(NumSubExceptions); WE->reserveBlocks(NumBlocks); }
/// Test whether OneUse, a use of Reg, dominates all of Reg's other uses. static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse, const MachineBasicBlock &MBB, const MachineRegisterInfo &MRI, const MachineDominatorTree &MDT, LiveIntervals &LIS, WebAssemblyFunctionInfo &MFI) { const LiveInterval &LI = LIS.getInterval(Reg); const MachineInstr *OneUseInst = OneUse.getParent(); VNInfo *OneUseVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(*OneUseInst)); for (const MachineOperand &Use : MRI.use_nodbg_operands(Reg)) { if (&Use == &OneUse) continue; const MachineInstr *UseInst = Use.getParent(); VNInfo *UseVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(*UseInst)); if (UseVNI != OneUseVNI) continue; const MachineInstr *OneUseInst = OneUse.getParent(); if (UseInst == OneUseInst) { // Another use in the same instruction. We need to ensure that the one // selected use happens "before" it. if (&OneUse > &Use) return false; } else { // Test that the use is dominated by the one selected use. while (!MDT.dominates(OneUseInst, UseInst)) { // Actually, dominating is over-conservative. Test that the use would // happen after the one selected use in the stack evaluation order. // // This is needed as a consequence of using implicit get_locals for // uses and implicit set_locals for defs. if (UseInst->getDesc().getNumDefs() == 0) return false; const MachineOperand &MO = UseInst->getOperand(0); if (!MO.isReg()) return false; unsigned DefReg = MO.getReg(); if (!TargetRegisterInfo::isVirtualRegister(DefReg) || !MFI.isVRegStackified(DefReg)) return false; assert(MRI.hasOneUse(DefReg)); const MachineOperand &NewUse = *MRI.use_begin(DefReg); const MachineInstr *NewUseInst = NewUse.getParent(); if (NewUseInst == OneUseInst) { if (&OneUse > &NewUse) return false; break; } UseInst = NewUseInst; } } } return true; }
// Checks if there is potential path From instruction To instruction. // If CutOff is specified and it sits in between of that path we ignore // a higher portion of the path and report it is not reachable. static bool isReachable(const MachineInstr *From, const MachineInstr *To, const MachineBasicBlock *CutOff, MachineDominatorTree &MDT) { // If either From block dominates To block or instructions are in the same // block and From is higher. if (MDT.dominates(From, To)) return true; const MachineBasicBlock *MBBFrom = From->getParent(); const MachineBasicBlock *MBBTo = To->getParent(); if (MBBFrom == MBBTo) return false; // Instructions are in different blocks, do predecessor search. // We should almost never get here since we do not usually produce M0 stores // other than -1. return searchPredecessors(MBBTo, CutOff, [MBBFrom] (const MachineBasicBlock *MBB) { return MBB == MBBFrom; }); }
/// Sort the blocks, taking special care to make sure that loops are not /// interrupted by blocks not dominated by their header. /// TODO: There are many opportunities for improving the heuristics here. /// Explore them. static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI, const MachineDominatorTree &MDT) { // Prepare for a topological sort: Record the number of predecessors each // block has, ignoring loop backedges. MF.RenumberBlocks(); SmallVector<unsigned, 16> NumPredsLeft(MF.getNumBlockIDs(), 0); for (MachineBasicBlock &MBB : MF) { unsigned N = MBB.pred_size(); if (MachineLoop *L = MLI.getLoopFor(&MBB)) if (L->getHeader() == &MBB) for (const MachineBasicBlock *Pred : MBB.predecessors()) if (L->contains(Pred)) --N; NumPredsLeft[MBB.getNumber()] = N; } // Topological sort the CFG, with additional constraints: // - Between a loop header and the last block in the loop, there can be // no blocks not dominated by the loop header. // - It's desirable to preserve the original block order when possible. // We use two ready lists; Preferred and Ready. Preferred has recently // processed sucessors, to help preserve block sequences from the original // order. Ready has the remaining ready blocks. PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>, CompareBlockNumbers> Preferred; PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>, CompareBlockNumbersBackwards> Ready; SmallVector<Entry, 4> Loops; for (MachineBasicBlock *MBB = &MF.front();;) { const MachineLoop *L = MLI.getLoopFor(MBB); if (L) { // If MBB is a loop header, add it to the active loop list. We can't put // any blocks that it doesn't dominate until we see the end of the loop. if (L->getHeader() == MBB) Loops.push_back(Entry(L)); // For each active loop the block is in, decrement the count. If MBB is // the last block in an active loop, take it off the list and pick up any // blocks deferred because the header didn't dominate them. for (Entry &E : Loops) if (E.Loop->contains(MBB) && --E.NumBlocksLeft == 0) for (auto DeferredBlock : E.Deferred) Ready.push(DeferredBlock); while (!Loops.empty() && Loops.back().NumBlocksLeft == 0) Loops.pop_back(); } // The main topological sort logic. for (MachineBasicBlock *Succ : MBB->successors()) { // Ignore backedges. if (MachineLoop *SuccL = MLI.getLoopFor(Succ)) if (SuccL->getHeader() == Succ && SuccL->contains(MBB)) continue; // Decrement the predecessor count. If it's now zero, it's ready. if (--NumPredsLeft[Succ->getNumber()] == 0) Preferred.push(Succ); } // Determine the block to follow MBB. First try to find a preferred block, // to preserve the original block order when possible. MachineBasicBlock *Next = nullptr; while (!Preferred.empty()) { Next = Preferred.top(); Preferred.pop(); // If X isn't dominated by the top active loop header, defer it until that // loop is done. if (!Loops.empty() && !MDT.dominates(Loops.back().Loop->getHeader(), Next)) { Loops.back().Deferred.push_back(Next); Next = nullptr; continue; } // If Next was originally ordered before MBB, and it isn't because it was // loop-rotated above the header, it's not preferred. if (Next->getNumber() < MBB->getNumber() && (!L || !L->contains(Next) || L->getHeader()->getNumber() < Next->getNumber())) { Ready.push(Next); Next = nullptr; continue; } break; } // If we didn't find a suitable block in the Preferred list, check the // general Ready list. if (!Next) { // If there are no more blocks to process, we're done. if (Ready.empty()) { MaybeUpdateTerminator(MBB); break; } for (;;) { Next = Ready.top(); Ready.pop(); // If Next isn't dominated by the top active loop header, defer it until // that loop is done. if (!Loops.empty() && !MDT.dominates(Loops.back().Loop->getHeader(), Next)) { Loops.back().Deferred.push_back(Next); continue; } break; } } // Move the next block into place and iterate. Next->moveAfter(MBB); MaybeUpdateTerminator(MBB); MBB = Next; } assert(Loops.empty() && "Active loop list not finished"); MF.RenumberBlocks(); #ifndef NDEBUG SmallSetVector<MachineLoop *, 8> OnStack; // Insert a sentinel representing the degenerate loop that starts at the // function entry block and includes the entire function as a "loop" that // executes once. OnStack.insert(nullptr); for (auto &MBB : MF) { assert(MBB.getNumber() >= 0 && "Renumbered blocks should be non-negative."); MachineLoop *Loop = MLI.getLoopFor(&MBB); if (Loop && &MBB == Loop->getHeader()) { // Loop header. The loop predecessor should be sorted above, and the other // predecessors should be backedges below. for (auto Pred : MBB.predecessors()) assert( (Pred->getNumber() < MBB.getNumber() || Loop->contains(Pred)) && "Loop header predecessors must be loop predecessors or backedges"); assert(OnStack.insert(Loop) && "Loops should be declared at most once."); } else { // Not a loop header. All predecessors should be sorted above. for (auto Pred : MBB.predecessors()) assert(Pred->getNumber() < MBB.getNumber() && "Non-loop-header predecessors should be topologically sorted"); assert(OnStack.count(MLI.getLoopFor(&MBB)) && "Blocks must be nested in their loops"); } while (OnStack.size() > 1 && &MBB == LoopBottom(OnStack.back())) OnStack.pop_back(); } assert(OnStack.pop_back_val() == nullptr && "The function entry block shouldn't actually be a loop header"); assert(OnStack.empty() && "Control flow stack pushes and pops should be balanced."); #endif }
// Hoist and merge identical SGPR initializations into a common predecessor. // This is intended to combine M0 initializations, but can work with any // SGPR. A VGPR cannot be processed since we cannot guarantee vector // executioon. static bool hoistAndMergeSGPRInits(unsigned Reg, const MachineRegisterInfo &MRI, MachineDominatorTree &MDT) { // List of inits by immediate value. typedef std::map<unsigned, std::list<MachineInstr*>> InitListMap; InitListMap Inits; // List of clobbering instructions. SmallVector<MachineInstr*, 8> Clobbers; bool Changed = false; for (auto &MI : MRI.def_instructions(Reg)) { MachineOperand *Imm = nullptr; for (auto &MO: MI.operands()) { if ((MO.isReg() && ((MO.isDef() && MO.getReg() != Reg) || !MO.isDef())) || (!MO.isImm() && !MO.isReg()) || (MO.isImm() && Imm)) { Imm = nullptr; break; } else if (MO.isImm()) Imm = &MO; } if (Imm) Inits[Imm->getImm()].push_front(&MI); else Clobbers.push_back(&MI); } for (auto &Init : Inits) { auto &Defs = Init.second; for (auto I1 = Defs.begin(), E = Defs.end(); I1 != E; ) { MachineInstr *MI1 = *I1; for (auto I2 = std::next(I1); I2 != E; ) { MachineInstr *MI2 = *I2; // Check any possible interference auto intereferes = [&](MachineBasicBlock::iterator From, MachineBasicBlock::iterator To) -> bool { assert(MDT.dominates(&*To, &*From)); auto interferes = [&MDT, From, To](MachineInstr* &Clobber) -> bool { const MachineBasicBlock *MBBFrom = From->getParent(); const MachineBasicBlock *MBBTo = To->getParent(); bool MayClobberFrom = isReachable(Clobber, &*From, MBBTo, MDT); bool MayClobberTo = isReachable(Clobber, &*To, MBBTo, MDT); if (!MayClobberFrom && !MayClobberTo) return false; if ((MayClobberFrom && !MayClobberTo) || (!MayClobberFrom && MayClobberTo)) return true; // Both can clobber, this is not an interference only if both are // dominated by Clobber and belong to the same block or if Clobber // properly dominates To, given that To >> From, so it dominates // both and located in a common dominator. return !((MBBFrom == MBBTo && MDT.dominates(Clobber, &*From) && MDT.dominates(Clobber, &*To)) || MDT.properlyDominates(Clobber->getParent(), MBBTo)); }; return (any_of(Clobbers, interferes)) || (any_of(Inits, [&](InitListMap::value_type &C) { return C.first != Init.first && any_of(C.second, interferes); })); }; if (MDT.dominates(MI1, MI2)) { if (!intereferes(MI2, MI1)) { DEBUG(dbgs() << "Erasing from BB#" << MI2->getParent()->getNumber() << " " << *MI2); MI2->eraseFromParent(); Defs.erase(I2++); Changed = true; continue; } } else if (MDT.dominates(MI2, MI1)) { if (!intereferes(MI1, MI2)) { DEBUG(dbgs() << "Erasing from BB#" << MI1->getParent()->getNumber() << " " << *MI1); MI1->eraseFromParent(); Defs.erase(I1++); Changed = true; break; } } else { auto *MBB = MDT.findNearestCommonDominator(MI1->getParent(), MI2->getParent()); if (!MBB) { ++I2; continue; } MachineBasicBlock::iterator I = MBB->getFirstNonPHI(); if (!intereferes(MI1, I) && !intereferes(MI2, I)) { DEBUG(dbgs() << "Erasing from BB#" << MI1->getParent()->getNumber() << " " << *MI1 << "and moving from BB#" << MI2->getParent()->getNumber() << " to BB#" << I->getParent()->getNumber() << " " << *MI2); I->getParent()->splice(I, MI2->getParent(), MI2); MI1->eraseFromParent(); Defs.erase(I1++); Changed = true; break; } } ++I2; } ++I1; } } if (Changed) MRI.clearKillFlags(Reg); return Changed; }
// Replace uses of FromReg with ToReg if they are dominated by MI. static bool ReplaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI, unsigned FromReg, unsigned ToReg, const MachineRegisterInfo &MRI, MachineDominatorTree &MDT, LiveIntervals &LIS) { bool Changed = false; LiveInterval *FromLI = &LIS.getInterval(FromReg); LiveInterval *ToLI = &LIS.getInterval(ToReg); SlotIndex FromIdx = LIS.getInstructionIndex(MI).getRegSlot(); VNInfo *FromVNI = FromLI->getVNInfoAt(FromIdx); SmallVector<SlotIndex, 4> Indices; for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end(); I != E;) { MachineOperand &O = *I++; MachineInstr *Where = O.getParent(); // Check that MI dominates the instruction in the normal way. if (&MI == Where || !MDT.dominates(&MI, Where)) continue; // If this use gets a different value, skip it. SlotIndex WhereIdx = LIS.getInstructionIndex(*Where); VNInfo *WhereVNI = FromLI->getVNInfoAt(WhereIdx); if (WhereVNI && WhereVNI != FromVNI) continue; // Make sure ToReg isn't clobbered before it gets there. VNInfo *ToVNI = ToLI->getVNInfoAt(WhereIdx); if (ToVNI && ToVNI != FromVNI) continue; Changed = true; LLVM_DEBUG(dbgs() << "Setting operand " << O << " in " << *Where << " from " << MI << "\n"); O.setReg(ToReg); // If the store's def was previously dead, it is no longer. if (!O.isUndef()) { MI.getOperand(0).setIsDead(false); Indices.push_back(WhereIdx.getRegSlot()); } } if (Changed) { // Extend ToReg's liveness. LIS.extendToIndices(*ToLI, Indices); // Shrink FromReg's liveness. LIS.shrinkToUses(FromLI); // If we replaced all dominated uses, FromReg is now killed at MI. if (!FromLI->liveAt(FromIdx.getDeadSlot())) MI.addRegisterKilled(FromReg, MBB.getParent() ->getSubtarget<WebAssemblySubtarget>() .getRegisterInfo()); } return Changed; }