diff options
Diffstat (limited to 'llvm/lib/Transforms/Utils')
| -rw-r--r-- | llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 73 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/ControlFlowUtils.cpp | 5 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/FixIrreducible.cpp | 126 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp | 19 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Utils/UnifyLoopExits.cpp | 77 | 
5 files changed, 238 insertions, 62 deletions
| diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index 9829d4d..11db0ec 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -674,6 +674,79 @@ BasicBlock *llvm::SplitEdge(BasicBlock *BB, BasicBlock *Succ, DominatorTree *DT,    return SplitBlock(BB, BB->getTerminator(), DT, LI, MSSAU, BBName);  } +/// Helper function to update the cycle or loop information after inserting a +/// new block between a callbr instruction and one of its target blocks.  Adds +/// the new block to the innermost cycle or loop that the callbr instruction and +/// the original target block share. +/// \p LCI            cycle or loop information to update +/// \p CallBrBlock    block containing the callbr instruction +/// \p CallBrTarget   new target block of the callbr instruction +/// \p Succ           original target block of the callbr instruction +template <typename TI, typename T> +static bool updateCycleLoopInfo(TI *LCI, BasicBlock *CallBrBlock, +                                BasicBlock *CallBrTarget, BasicBlock *Succ) { +  static_assert(std::is_same_v<TI, CycleInfo> || std::is_same_v<TI, LoopInfo>, +                "type must be CycleInfo or LoopInfo"); +  if (!LCI) +    return false; + +  T *LC; +  if constexpr (std::is_same_v<TI, CycleInfo>) +    LC = LCI->getSmallestCommonCycle(CallBrBlock, Succ); +  else +    LC = LCI->getSmallestCommonLoop(CallBrBlock, Succ); +  if (!LC) +    return false; + +  if constexpr (std::is_same_v<TI, CycleInfo>) +    LCI->addBlockToCycle(CallBrTarget, LC); +  else +    LC->addBasicBlockToLoop(CallBrTarget, *LCI); + +  return true; +} + +BasicBlock *llvm::SplitCallBrEdge(BasicBlock *CallBrBlock, BasicBlock *Succ, +                                  unsigned SuccIdx, DomTreeUpdater *DTU, +                                  CycleInfo *CI, LoopInfo *LI, +                                  bool *UpdatedLI) { +  CallBrInst *CallBr = dyn_cast<CallBrInst>(CallBrBlock->getTerminator()); +  assert(CallBr && "expected callbr terminator"); +  assert(SuccIdx < CallBr->getNumSuccessors() && +         Succ == CallBr->getSuccessor(SuccIdx) && "invalid successor index"); + +  // Create a new block between callbr and the specified successor. +  // splitBlockBefore cannot be re-used here since it cannot split if the split +  // point is a PHI node (because BasicBlock::splitBasicBlockBefore cannot +  // handle that). But we don't need to rewire every part of a potential PHI +  // node. We only care about the edge between CallBrBlock and the original +  // successor. +  BasicBlock *CallBrTarget = +      BasicBlock::Create(CallBrBlock->getContext(), +                         CallBrBlock->getName() + ".target." + Succ->getName(), +                         CallBrBlock->getParent()); +  // Rewire control flow from the new target block to the original successor. +  Succ->replacePhiUsesWith(CallBrBlock, CallBrTarget); +  // Rewire control flow from callbr to the new target block. +  CallBr->setSuccessor(SuccIdx, CallBrTarget); +  // Jump from the new target block to the original successor. +  BranchInst::Create(Succ, CallBrTarget); + +  bool Updated = +      updateCycleLoopInfo<LoopInfo, Loop>(LI, CallBrBlock, CallBrTarget, Succ); +  if (UpdatedLI) +    *UpdatedLI = Updated; +  updateCycleLoopInfo<CycleInfo, Cycle>(CI, CallBrBlock, CallBrTarget, Succ); +  if (DTU) { +    DTU->applyUpdates({{DominatorTree::Insert, CallBrBlock, CallBrTarget}}); +    if (DTU->getDomTree().dominates(CallBrBlock, Succ)) +      DTU->applyUpdates({{DominatorTree::Delete, CallBrBlock, Succ}, +                         {DominatorTree::Insert, CallBrTarget, Succ}}); +  } + +  return CallBrTarget; +} +  void llvm::setUnwindEdgeTo(Instruction *TI, BasicBlock *Succ) {    if (auto *II = dyn_cast<InvokeInst>(TI))      II->setUnwindDest(Succ); diff --git a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp index 0046a00..287a177 100644 --- a/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp +++ b/llvm/lib/Transforms/Utils/ControlFlowUtils.cpp @@ -13,6 +13,7 @@  #include "llvm/Transforms/Utils/ControlFlowUtils.h"  #include "llvm/ADT/SetVector.h"  #include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/LoopInfo.h"  #include "llvm/IR/Constants.h"  #include "llvm/IR/Instructions.h"  #include "llvm/IR/ValueHandle.h" @@ -281,7 +282,9 @@ std::pair<BasicBlock *, bool> ControlFlowHub::finalize(    for (auto [BB, Succ0, Succ1] : Branches) {  #ifndef NDEBUG -    assert(Incoming.insert(BB).second && "Duplicate entry for incoming block."); +    assert( +        (Incoming.insert(BB).second || isa<CallBrInst>(BB->getTerminator())) && +        "Duplicate entry for incoming block.");  #endif      if (Succ0)        Outgoing.insert(Succ0); diff --git a/llvm/lib/Transforms/Utils/FixIrreducible.cpp b/llvm/lib/Transforms/Utils/FixIrreducible.cpp index 45e1d12..804af22 100644 --- a/llvm/lib/Transforms/Utils/FixIrreducible.cpp +++ b/llvm/lib/Transforms/Utils/FixIrreducible.cpp @@ -79,6 +79,53 @@  // Limitation: The pass cannot handle switch statements and indirect  //             branches. Both must be lowered to plain branches first.  // +// CallBr support: CallBr is handled as a more general branch instruction which +// can have multiple successors. The pass redirects the edges to intermediate +// target blocks that unconditionally branch to the original callbr target +// blocks. This allows the control flow hub to know to which of the original +// target blocks to jump to. +// Example input CFG: +//                        Entry (callbr) +//                       /     \ +//                      v       v +//                      H ----> B +//                      ^      /| +//                       `----' | +//                              v +//                             Exit +// +// becomes: +//                        Entry (callbr) +//                       /     \ +//                      v       v +//                 target.H   target.B +//                      |       | +//                      v       v +//                      H ----> B +//                      ^      /| +//                       `----' | +//                              v +//                             Exit +// +// Note +// OUTPUT CFG: Converted to a natural loop with a new header N. +// +//                        Entry (callbr) +//                       /     \ +//                      v       v +//                 target.H   target.B +//                      \       / +//                       \     / +//                        v   v +//                          N <---. +//                         / \     \ +//                        /   \     | +//                       v     v    / +//                       H --> B --' +//                             | +//                             v +//                            Exit +//  //===----------------------------------------------------------------------===//  #include "llvm/Transforms/Utils/FixIrreducible.h" @@ -231,6 +278,7 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,      return false;    LLVM_DEBUG(dbgs() << "Processing cycle:\n" << CI.print(&C) << "\n";); +  DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);    ControlFlowHub CHub;    SetVector<BasicBlock *> Predecessors; @@ -242,18 +290,32 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,    }    for (BasicBlock *P : Predecessors) { -    auto *Branch = cast<BranchInst>(P->getTerminator()); -    // Exactly one of the two successors is the header. -    BasicBlock *Succ0 = Branch->getSuccessor(0) == Header ? Header : nullptr; -    BasicBlock *Succ1 = Succ0 ? nullptr : Header; -    if (!Succ0) -      assert(Branch->getSuccessor(1) == Header); -    assert(Succ0 || Succ1); -    CHub.addBranch(P, Succ0, Succ1); - -    LLVM_DEBUG(dbgs() << "Added internal branch: " << P->getName() << " -> " -                      << (Succ0 ? Succ0->getName() : "") << " " -                      << (Succ1 ? Succ1->getName() : "") << "\n"); +    if (BranchInst *Branch = dyn_cast<BranchInst>(P->getTerminator())) { +      // Exactly one of the two successors is the header. +      BasicBlock *Succ0 = Branch->getSuccessor(0) == Header ? Header : nullptr; +      BasicBlock *Succ1 = Succ0 ? nullptr : Header; +      assert(Succ0 || Branch->getSuccessor(1) == Header); +      assert(Succ0 || Succ1); +      CHub.addBranch(P, Succ0, Succ1); + +      LLVM_DEBUG(dbgs() << "Added internal branch: " << printBasicBlock(P) +                        << " -> " << printBasicBlock(Succ0) +                        << (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1) +                        << '\n'); +    } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) { +      for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) { +        BasicBlock *Succ = CallBr->getSuccessor(I); +        if (Succ != Header) +          continue; +        BasicBlock *NewSucc = SplitCallBrEdge(P, Succ, I, &DTU, &CI, LI); +        CHub.addBranch(NewSucc, Succ); +        LLVM_DEBUG(dbgs() << "Added internal branch: " +                          << printBasicBlock(NewSucc) << " -> " +                          << printBasicBlock(Succ) << '\n'); +      } +    } else { +      llvm_unreachable("unsupported block terminator"); +    }    }    // Redirect external incoming edges. This includes the edges on the header. @@ -266,17 +328,32 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,    }    for (BasicBlock *P : Predecessors) { -    auto *Branch = cast<BranchInst>(P->getTerminator()); -    BasicBlock *Succ0 = Branch->getSuccessor(0); -    Succ0 = C.contains(Succ0) ? Succ0 : nullptr; -    BasicBlock *Succ1 = -        Branch->isUnconditional() ? nullptr : Branch->getSuccessor(1); -    Succ1 = Succ1 && C.contains(Succ1) ? Succ1 : nullptr; -    CHub.addBranch(P, Succ0, Succ1); - -    LLVM_DEBUG(dbgs() << "Added external branch: " << P->getName() << " -> " -                      << (Succ0 ? Succ0->getName() : "") << " " -                      << (Succ1 ? Succ1->getName() : "") << "\n"); +    if (BranchInst *Branch = dyn_cast<BranchInst>(P->getTerminator()); Branch) { +      BasicBlock *Succ0 = Branch->getSuccessor(0); +      Succ0 = C.contains(Succ0) ? Succ0 : nullptr; +      BasicBlock *Succ1 = +          Branch->isUnconditional() ? nullptr : Branch->getSuccessor(1); +      Succ1 = Succ1 && C.contains(Succ1) ? Succ1 : nullptr; +      CHub.addBranch(P, Succ0, Succ1); + +      LLVM_DEBUG(dbgs() << "Added external branch: " << printBasicBlock(P) +                        << " -> " << printBasicBlock(Succ0) +                        << (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1) +                        << '\n'); +    } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(P->getTerminator())) { +      for (unsigned I = 0; I < CallBr->getNumSuccessors(); ++I) { +        BasicBlock *Succ = CallBr->getSuccessor(I); +        if (!C.contains(Succ)) +          continue; +        BasicBlock *NewSucc = SplitCallBrEdge(P, Succ, I, &DTU, &CI, LI); +        CHub.addBranch(NewSucc, Succ); +        LLVM_DEBUG(dbgs() << "Added external branch: " +                          << printBasicBlock(NewSucc) << " -> " +                          << printBasicBlock(Succ) << '\n'); +      } +    } else { +      llvm_unreachable("unsupported block terminator"); +    }    }    // Redirect all the backedges through a "hub" consisting of a series @@ -292,7 +369,6 @@ static bool fixIrreducible(Cycle &C, CycleInfo &CI, DominatorTree &DT,    SetVector<BasicBlock *> Entries;    Entries.insert(C.entry_rbegin(), C.entry_rend()); -  DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);    CHub.finalize(&DTU, GuardBlocks, "irr");  #if defined(EXPENSIVE_CHECKS)    assert(DT.verify(DominatorTree::VerificationLevel::Full)); @@ -325,8 +401,6 @@ static bool FixIrreducibleImpl(Function &F, CycleInfo &CI, DominatorTree &DT,    LLVM_DEBUG(dbgs() << "===== Fix irreducible control-flow in function: "                      << F.getName() << "\n"); -  assert(hasOnlySimpleTerminator(F) && "Unsupported block terminator."); -    bool Changed = false;    for (Cycle *TopCycle : CI.toplevel_cycles()) {      for (Cycle *C : depth_first(TopCycle)) { diff --git a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp index 6312831..7a2b8da 100644 --- a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp @@ -460,25 +460,10 @@ CloneLoopBlocks(Loop *L, Value *NewIter, const bool UseEpilogRemainder,    Loop *NewLoop = NewLoops[L];    assert(NewLoop && "L should have been cloned"); -  MDNode *LoopID = NewLoop->getLoopID(); - -  // Only add loop metadata if the loop is not going to be completely -  // unrolled. -  if (UnrollRemainder) -    return NewLoop; - -  std::optional<MDNode *> NewLoopID = makeFollowupLoopID( -      LoopID, {LLVMLoopUnrollFollowupAll, LLVMLoopUnrollFollowupRemainder}); -  if (NewLoopID) { -    NewLoop->setLoopID(*NewLoopID); - -    // Do not setLoopAlreadyUnrolled if loop attributes have been defined -    // explicitly. -    return NewLoop; -  }    // Add unroll disable metadata to disable future unrolling for this loop. -  NewLoop->setLoopAlreadyUnrolled(); +  if (!UnrollRemainder) +    NewLoop->setLoopAlreadyUnrolled();    return NewLoop;  } diff --git a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp index 9f338db..94c5c170 100644 --- a/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp +++ b/llvm/lib/Transforms/Utils/UnifyLoopExits.cpp @@ -12,7 +12,11 @@  //  // Limitation: This assumes that all terminators in the CFG are direct branches  //             (the "br" instruction). The presence of any other control flow -//             such as indirectbr, switch or callbr will cause an assert. +//             such as indirectbr or switch will cause an assert. +//             The callbr terminator is supported by creating intermediate +//             target blocks that unconditionally branch to the original target +//             blocks. These intermediate target blocks can then be redirected +//             through the ControlFlowHub as usual.  //  //===----------------------------------------------------------------------===// @@ -150,25 +154,55 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {    SmallVector<BasicBlock *, 8> ExitingBlocks;    L->getExitingBlocks(ExitingBlocks); +  DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); +  SmallVector<BasicBlock *, 8> CallBrTargetBlocksToFix;    // Redirect exiting edges through a control flow hub.    ControlFlowHub CHub; -  for (auto *BB : ExitingBlocks) { -    auto *Branch = cast<BranchInst>(BB->getTerminator()); -    BasicBlock *Succ0 = Branch->getSuccessor(0); -    Succ0 = L->contains(Succ0) ? nullptr : Succ0; - -    BasicBlock *Succ1 = -        Branch->isUnconditional() ? nullptr : Branch->getSuccessor(1); -    Succ1 = L->contains(Succ1) ? nullptr : Succ1; -    CHub.addBranch(BB, Succ0, Succ1); - -    LLVM_DEBUG(dbgs() << "Added exiting branch: " << BB->getName() << " -> {" -                      << (Succ0 ? Succ0->getName() : "<none>") << ", " -                      << (Succ1 ? Succ1->getName() : "<none>") << "}\n"); + +  for (unsigned I = 0; I < ExitingBlocks.size(); ++I) { +    BasicBlock *BB = ExitingBlocks[I]; +    if (BranchInst *Branch = dyn_cast<BranchInst>(BB->getTerminator())) { +      BasicBlock *Succ0 = Branch->getSuccessor(0); +      Succ0 = L->contains(Succ0) ? nullptr : Succ0; + +      BasicBlock *Succ1 = +          Branch->isUnconditional() ? nullptr : Branch->getSuccessor(1); +      Succ1 = L->contains(Succ1) ? nullptr : Succ1; +      CHub.addBranch(BB, Succ0, Succ1); + +      LLVM_DEBUG(dbgs() << "Added extiting branch: " << printBasicBlock(BB) +                        << " -> " << printBasicBlock(Succ0) +                        << (Succ0 && Succ1 ? " " : "") << printBasicBlock(Succ1) +                        << '\n'); +    } else if (CallBrInst *CallBr = dyn_cast<CallBrInst>(BB->getTerminator())) { +      for (unsigned J = 0; J < CallBr->getNumSuccessors(); ++J) { +        BasicBlock *Succ = CallBr->getSuccessor(J); +        if (L->contains(Succ)) +          continue; +        bool UpdatedLI = false; +        BasicBlock *NewSucc = +            SplitCallBrEdge(BB, Succ, J, &DTU, nullptr, &LI, &UpdatedLI); +        // Even if CallBr and Succ do not have a common parent loop, we need to +        // add the new target block to the parent loop of the current loop. +        if (!UpdatedLI) +          CallBrTargetBlocksToFix.push_back(NewSucc); +        // ExitingBlocks is later used to restore SSA, so we need to make sure +        // that the blocks used for phi nodes in the guard blocks match the +        // predecessors of the guard blocks, which, in the case of callbr, are +        // the new intermediate target blocks instead of the callbr blocks +        // themselves. +        ExitingBlocks[I] = NewSucc; +        CHub.addBranch(NewSucc, Succ); +        LLVM_DEBUG(dbgs() << "Added exiting branch: " +                          << printBasicBlock(NewSucc) << " -> " +                          << printBasicBlock(Succ) << '\n'); +      } +    } else { +      llvm_unreachable("unsupported block terminator"); +    }    }    SmallVector<BasicBlock *, 8> GuardBlocks; -  DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);    BasicBlock *LoopExitBlock;    bool ChangedCFG;    std::tie(LoopExitBlock, ChangedCFG) = CHub.finalize( @@ -187,10 +221,19 @@ static bool unifyLoopExits(DominatorTree &DT, LoopInfo &LI, Loop *L) {    // The guard blocks were created outside the loop, so they need to become    // members of the parent loop. -  if (auto ParentLoop = L->getParentLoop()) { +  // Same goes for the callbr target blocks.  Although we try to add them to the +  // smallest common parent loop of the callbr block and the corresponding +  // original target block, there might not have been such a loop, in which case +  // the newly created callbr target blocks are not part of any loop. For nested +  // loops, this might result in them leading to a loop with multiple entry +  // points. +  if (auto *ParentLoop = L->getParentLoop()) {      for (auto *G : GuardBlocks) {        ParentLoop->addBasicBlockToLoop(G, LI);      } +    for (auto *C : CallBrTargetBlocksToFix) { +      ParentLoop->addBasicBlockToLoop(C, LI); +    }      ParentLoop->verifyLoop();    } @@ -218,8 +261,6 @@ bool UnifyLoopExitsLegacyPass::runOnFunction(Function &F) {    auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();    auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree(); -  assert(hasOnlySimpleTerminator(F) && "Unsupported block terminator."); -    return runImpl(LI, DT);  } | 
