//===- ControlFlowUtils.cpp - Control Flow Utilities -----------------------==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Utilities to manipulate the CFG and restore SSA for the new control flow. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/ControlFlowUtils.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Analysis/DomTreeUpdater.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Transforms/Utils/Local.h" #define DEBUG_TYPE "control-flow-hub" using namespace llvm; using BBPredicates = DenseMap; using EdgeDescriptor = ControlFlowHub::BranchDescriptor; // Redirects the terminator of the incoming block to the first guard block in // the hub. Returns the branch condition from `BB` if it exits. // - If only one of Succ0 or Succ1 is not null, the corresponding branch // successor is redirected to the FirstGuardBlock. // - Else both are not null, and branch is replaced with an unconditional // branch to the FirstGuardBlock. static Value *redirectToHub(BasicBlock *BB, BasicBlock *Succ0, BasicBlock *Succ1, BasicBlock *FirstGuardBlock) { assert(isa(BB->getTerminator()) && "Only support branch terminator."); auto *Branch = cast(BB->getTerminator()); auto *Condition = Branch->isConditional() ? Branch->getCondition() : nullptr; assert(Succ0 || Succ1); if (Branch->isUnconditional()) { assert(Succ0 == Branch->getSuccessor(0)); assert(!Succ1); Branch->setSuccessor(0, FirstGuardBlock); } else { assert(!Succ1 || Succ1 == Branch->getSuccessor(1)); if (Succ0 && !Succ1) { Branch->setSuccessor(0, FirstGuardBlock); } else if (Succ1 && !Succ0) { Branch->setSuccessor(1, FirstGuardBlock); } else { Branch->eraseFromParent(); BranchInst::Create(FirstGuardBlock, BB); } } return Condition; } // Setup the branch instructions for guard blocks. // // Each guard block terminates in a conditional branch that transfers // control to the corresponding outgoing block or the next guard // block. The last guard block has two outgoing blocks as successors. static void setupBranchForGuard(ArrayRef GuardBlocks, ArrayRef Outgoing, BBPredicates &GuardPredicates) { assert(Outgoing.size() > 1); assert(GuardBlocks.size() == Outgoing.size() - 1); int I = 0; for (int E = GuardBlocks.size() - 1; I != E; ++I) { BasicBlock *Out = Outgoing[I]; BranchInst::Create(Out, GuardBlocks[I + 1], GuardPredicates[Out], GuardBlocks[I]); } BasicBlock *Out = Outgoing[I]; BranchInst::Create(Out, Outgoing[I + 1], GuardPredicates[Out], GuardBlocks[I]); } // Assign an index to each outgoing block. At the corresponding guard // block, compute the branch condition by comparing this index. static void calcPredicateUsingInteger(ArrayRef Branches, ArrayRef Outgoing, ArrayRef GuardBlocks, BBPredicates &GuardPredicates) { LLVMContext &Context = GuardBlocks.front()->getContext(); BasicBlock *FirstGuardBlock = GuardBlocks.front(); Type *Int32Ty = Type::getInt32Ty(Context); auto *Phi = PHINode::Create(Int32Ty, Branches.size(), "merged.bb.idx", FirstGuardBlock); for (auto [BB, Succ0, Succ1] : Branches) { Value *Condition = redirectToHub(BB, Succ0, Succ1, FirstGuardBlock); Value *IncomingId = nullptr; if (Succ0 && Succ1) { auto Succ0Iter = find(Outgoing, Succ0); auto Succ1Iter = find(Outgoing, Succ1); Value *Id0 = ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), Succ0Iter)); Value *Id1 = ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), Succ1Iter)); IncomingId = SelectInst::Create(Condition, Id0, Id1, "target.bb.idx", BB->getTerminator()->getIterator()); } else { // Get the index of the non-null successor. auto SuccIter = Succ0 ? find(Outgoing, Succ0) : find(Outgoing, Succ1); IncomingId = ConstantInt::get(Int32Ty, std::distance(Outgoing.begin(), SuccIter)); } Phi->addIncoming(IncomingId, BB); } for (int I = 0, E = Outgoing.size() - 1; I != E; ++I) { BasicBlock *Out = Outgoing[I]; LLVM_DEBUG(dbgs() << "Creating integer guard for " << Out->getName() << "\n"); auto *Cmp = ICmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ, Phi, ConstantInt::get(Int32Ty, I), Out->getName() + ".predicate", GuardBlocks[I]); GuardPredicates[Out] = Cmp; } } // Determine the branch condition to be used at each guard block from the // original boolean values. static void calcPredicateUsingBooleans( ArrayRef Branches, ArrayRef Outgoing, SmallVectorImpl &GuardBlocks, BBPredicates &GuardPredicates, SmallVectorImpl &DeletionCandidates) { LLVMContext &Context = GuardBlocks.front()->getContext(); auto *BoolTrue = ConstantInt::getTrue(Context); auto *BoolFalse = ConstantInt::getFalse(Context); BasicBlock *FirstGuardBlock = GuardBlocks.front(); // The predicate for the last outgoing is trivially true, and so we // process only the first N-1 successors. for (int I = 0, E = Outgoing.size() - 1; I != E; ++I) { BasicBlock *Out = Outgoing[I]; LLVM_DEBUG(dbgs() << "Creating boolean guard for " << Out->getName() << "\n"); auto *Phi = PHINode::Create(Type::getInt1Ty(Context), Branches.size(), StringRef("Guard.") + Out->getName(), FirstGuardBlock); GuardPredicates[Out] = Phi; } for (auto [BB, Succ0, Succ1] : Branches) { Value *Condition = redirectToHub(BB, Succ0, Succ1, FirstGuardBlock); // Optimization: Consider an incoming block A with both successors // Succ0 and Succ1 in the set of outgoing blocks. The predicates // for Succ0 and Succ1 complement each other. If Succ0 is visited // first in the loop below, control will branch to Succ0 using the // corresponding predicate. But if that branch is not taken, then // control must reach Succ1, which means that the incoming value of // the predicate from `BB` is true for Succ1. bool OneSuccessorDone = false; for (int I = 0, E = Outgoing.size() - 1; I != E; ++I) { BasicBlock *Out = Outgoing[I]; PHINode *Phi = cast(GuardPredicates[Out]); if (Out != Succ0 && Out != Succ1) { Phi->addIncoming(BoolFalse, BB); } else if (!Succ0 || !Succ1 || OneSuccessorDone) { // Optimization: When only one successor is an outgoing block, // the incoming predicate from `BB` is always true. Phi->addIncoming(BoolTrue, BB); } else { assert(Succ0 && Succ1); if (Out == Succ0) { Phi->addIncoming(Condition, BB); } else { Value *Inverted = invertCondition(Condition); DeletionCandidates.push_back(Condition); Phi->addIncoming(Inverted, BB); } OneSuccessorDone = true; } } } } // Capture the existing control flow as guard predicates, and redirect // control flow from \p Incoming block through the \p GuardBlocks to the // \p Outgoing blocks. // // There is one guard predicate for each outgoing block OutBB. The // predicate represents whether the hub should transfer control flow // to OutBB. These predicates are NOT ORTHOGONAL. The Hub evaluates // them in the same order as the Outgoing set-vector, and control // branches to the first outgoing block whose predicate evaluates to true. // // The last guard block has two outgoing blocks as successors since the // condition for the final outgoing block is trivially true. So we create one // less block (including the first guard block) than the number of outgoing // blocks. static void convertToGuardPredicates( ArrayRef Branches, ArrayRef Outgoing, SmallVectorImpl &GuardBlocks, SmallVectorImpl &DeletionCandidates, const StringRef Prefix, std::optional MaxControlFlowBooleans) { BBPredicates GuardPredicates; Function *F = Outgoing.front()->getParent(); for (int I = 0, E = Outgoing.size() - 1; I != E; ++I) GuardBlocks.push_back( BasicBlock::Create(F->getContext(), Prefix + ".guard", F)); // When we are using an integer to record which target block to jump to, we // are creating less live values, actually we are using one single integer to // store the index of the target block. When we are using booleans to store // the branching information, we need (N-1) boolean values, where N is the // number of outgoing block. if (!MaxControlFlowBooleans || Outgoing.size() <= *MaxControlFlowBooleans) calcPredicateUsingBooleans(Branches, Outgoing, GuardBlocks, GuardPredicates, DeletionCandidates); else calcPredicateUsingInteger(Branches, Outgoing, GuardBlocks, GuardPredicates); setupBranchForGuard(GuardBlocks, Outgoing, GuardPredicates); } // After creating a control flow hub, the operands of PHINodes in an outgoing // block Out no longer match the predecessors of that block. Predecessors of Out // that are incoming blocks to the hub are now replaced by just one edge from // the hub. To match this new control flow, the corresponding values from each // PHINode must now be moved a new PHINode in the first guard block of the hub. // // This operation cannot be performed with SSAUpdater, because it involves one // new use: If the block Out is in the list of Incoming blocks, then the newly // created PHI in the Hub will use itself along that edge from Out to Hub. static void reconnectPhis(BasicBlock *Out, BasicBlock *GuardBlock, ArrayRef Incoming, BasicBlock *FirstGuardBlock) { auto I = Out->begin(); while (I != Out->end() && isa(I)) { auto *Phi = cast(I); auto *NewPhi = PHINode::Create(Phi->getType(), Incoming.size(), Phi->getName() + ".moved", FirstGuardBlock->begin()); bool AllUndef = true; for (auto [BB, Succ0, Succ1] : Incoming) { Value *V = PoisonValue::get(Phi->getType()); if (BB == Out) { V = NewPhi; } else if (Phi->getBasicBlockIndex(BB) != -1) { V = Phi->removeIncomingValue(BB, false); AllUndef &= isa(V); } NewPhi->addIncoming(V, BB); } assert(NewPhi->getNumIncomingValues() == Incoming.size()); Value *NewV = NewPhi; if (AllUndef) { NewPhi->eraseFromParent(); NewV = PoisonValue::get(Phi->getType()); } if (Phi->getNumOperands() == 0) { Phi->replaceAllUsesWith(NewV); I = Phi->eraseFromParent(); continue; } Phi->addIncoming(NewV, GuardBlock); ++I; } } BasicBlock *ControlFlowHub::finalize( DomTreeUpdater *DTU, SmallVectorImpl &GuardBlocks, const StringRef Prefix, std::optional MaxControlFlowBooleans) { #ifndef NDEBUG SmallSet Incoming; #endif SetVector Outgoing; for (auto [BB, Succ0, Succ1] : Branches) { #ifndef NDEBUG assert(Incoming.insert(BB).second && "Duplicate entry for incoming block."); #endif if (Succ0) Outgoing.insert(Succ0); if (Succ1) Outgoing.insert(Succ1); } if (Outgoing.size() < 2) return Outgoing.front(); SmallVector Updates; if (DTU) { for (auto [BB, Succ0, Succ1] : Branches) { if (Succ0) Updates.push_back({DominatorTree::Delete, BB, Succ0}); if (Succ1) Updates.push_back({DominatorTree::Delete, BB, Succ1}); } } SmallVector DeletionCandidates; convertToGuardPredicates(Branches, Outgoing.getArrayRef(), GuardBlocks, DeletionCandidates, Prefix, MaxControlFlowBooleans); BasicBlock *FirstGuardBlock = GuardBlocks.front(); // Update the PHINodes in each outgoing block to match the new control flow. for (int I = 0, E = GuardBlocks.size(); I != E; ++I) reconnectPhis(Outgoing[I], GuardBlocks[I], Branches, FirstGuardBlock); // Process the Nth (last) outgoing block with the (N-1)th (last) guard block. reconnectPhis(Outgoing.back(), GuardBlocks.back(), Branches, FirstGuardBlock); if (DTU) { int NumGuards = GuardBlocks.size(); for (auto [BB, Succ0, Succ1] : Branches) Updates.push_back({DominatorTree::Insert, BB, FirstGuardBlock}); for (int I = 0; I != NumGuards - 1; ++I) { Updates.push_back({DominatorTree::Insert, GuardBlocks[I], Outgoing[I]}); Updates.push_back( {DominatorTree::Insert, GuardBlocks[I], GuardBlocks[I + 1]}); } // The second successor of the last guard block is an outgoing block instead // of having a "next" guard block. Updates.push_back({DominatorTree::Insert, GuardBlocks[NumGuards - 1], Outgoing[NumGuards - 1]}); Updates.push_back({DominatorTree::Insert, GuardBlocks[NumGuards - 1], Outgoing[NumGuards]}); DTU->applyUpdates(Updates); } for (auto I : DeletionCandidates) { if (I->use_empty()) if (auto *Inst = dyn_cast_or_null(I)) Inst->eraseFromParent(); } return FirstGuardBlock; }