[llvm] [SimplifyCFG] Simplify switch instruction that has duplicate arms (PR #114262)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 7 23:53:25 PST 2024
================
@@ -7436,6 +7437,176 @@ static bool simplifySwitchOfCmpIntrinsic(SwitchInst *SI, IRBuilderBase &Builder,
return true;
}
+/// Checking whether two CaseHandle.getCaseSuccessor() are equal depends on
+/// the BasicBlock and the incoming values of their successor PHINodes.
+/// PHINode::getIncomingValueForBlock is O(|Preds|), so we'd like to avoid
+/// calling this function on each BasicBlock every time isEqual is called,
+/// especially since the same BasicBlock may be passed as an argument multiple
+/// times. To do this, we can precompute a map of PHINode -> Pred BasicBlock ->
+/// IncomingValue and add it in the Wrapper so isEqual can do O(1) checking
+/// of the incoming values.
+struct CaseHandleWrapper {
+ const SwitchInst::CaseHandle Case;
+ DenseMap<PHINode *, DenseMap<BasicBlock *, Value *>> *PhiPredIVs;
+};
+
+namespace llvm {
+template <> struct DenseMapInfo<const CaseHandleWrapper *> {
+ static const CaseHandleWrapper *getEmptyKey() {
+ return static_cast<CaseHandleWrapper *>(
+ DenseMapInfo<void *>::getEmptyKey());
+ }
+ static const CaseHandleWrapper *getTombstoneKey() {
+ return static_cast<CaseHandleWrapper *>(
+ DenseMapInfo<void *>::getTombstoneKey());
+ }
+ static unsigned getHashValue(const CaseHandleWrapper *CHW) {
+ BasicBlock *Succ = CHW->Case.getCaseSuccessor();
+ BranchInst *BI = cast<BranchInst>(Succ->getTerminator());
+ assert(BI->isUnconditional() &&
+ "Only supporting unconditional branches for now");
+ assert(BI->getNumSuccessors() == 1 &&
+ "Expected unconditional branches to have one successor");
+ assert(Succ->size() == 1 && "Expected just a single branch in the BB");
+
+ // Since we assume the BB is just a single BranchInst with a single
+ // succsessor, we hash as the BB and the incoming Values of its successor
+ // PHIs. Initially, we tried to just use the successor BB as the hash, but
+ // this had poor performance. We find that the extra computation of getting
+ // the incoming PHI values here leads to better performance on overall Set
+ // performance. We also tried to build a map from BB -> Succs.IncomingValues
+ // ahead of time and passing it in CaseHandleWrapper, but this slowed down
+ // the average compile time without having any impact on the worst case
+ // compile time.
+ BasicBlock *BB = BI->getSuccessor(0);
+ SmallVector<Value *> PhiValsForBB;
+ for (PHINode &Phi : BB->phis())
+ PhiValsForBB.emplace_back((*CHW->PhiPredIVs)[&Phi][BB]);
+
+ return hash_combine(
+ BB, hash_combine_range(PhiValsForBB.begin(), PhiValsForBB.end()));
+ }
+ static bool isEqual(const CaseHandleWrapper *LHS,
+ const CaseHandleWrapper *RHS) {
+ auto EKey = DenseMapInfo<CaseHandleWrapper *>::getEmptyKey();
+ auto TKey = DenseMapInfo<CaseHandleWrapper *>::getTombstoneKey();
+ if (LHS == EKey || RHS == EKey || LHS == TKey || RHS == TKey)
+ return LHS == RHS;
+
+ BasicBlock *A = LHS->Case.getCaseSuccessor();
+ BasicBlock *B = RHS->Case.getCaseSuccessor();
+
+ // FIXME: we checked that the size of A and B are both 1 in
+ // simplifyDuplicateSwitchArms to make the Case list smaller to
+ // improve performance. If we decide to support BasicBlocks with more
+ // than just a single instruction, we need to check that A.size() ==
+ // B.size() here, and we need to check more than just the BranchInsts
+ // for equality.
+
+ BranchInst *ABI = cast<BranchInst>(A->getTerminator());
+ BranchInst *BBI = cast<BranchInst>(B->getTerminator());
+ assert(ABI->isUnconditional() && BBI->isUnconditional() &&
+ "Only supporting unconditional branches for now");
+ assert(ABI->getNumSuccessors() == 1 &&
+ "Expected unconditional branches to have one successor");
+ if (ABI->getSuccessor(0) != BBI->getSuccessor(0))
+ return false;
+
+ // Need to check that PHIs in successor have matching values
+ BasicBlock *Succ = ABI->getSuccessor(0);
+ for (PHINode &Phi : Succ->phis()) {
+ auto &PredIVs = (*LHS->PhiPredIVs)[&Phi];
+ if (PredIVs[A] != PredIVs[B])
+ return false;
+ }
+
+ return true;
+ }
+};
+} // namespace llvm
+
+bool SimplifyCFGOpt::simplifyDuplicateSwitchArms(SwitchInst *SI) {
+ // Build Cases. Skip BBs that are not candidates for simplification. Mark
+ // PHINodes which need to be processed into PhiPredIVs. We decide to process
+ // an entire PHI at once after the loop, opposed to calling
+ // getIncomingValueForBlock inside this loop, since each call to
+ // getIncomingValueForBlock is O(|Preds|).
+ SmallPtrSet<PHINode *, 8> Phis;
+ SmallPtrSet<BasicBlock *, 8> Seen;
+ DenseMap<PHINode *, DenseMap<BasicBlock *, Value *>> PhiPredIVs;
+ SmallVector<CaseHandleWrapper> Cases;
+ Cases.reserve(SI->getNumCases());
+ for (auto &Case : SI->cases()) {
+ BasicBlock *BB = Case.getCaseSuccessor();
+
+ // FIXME: Support more than just a single BranchInst. One way we could do
+ // this is by taking a hashing approach of all insts in BB.
+ if (BB->size() != 1)
+ continue;
+
+ Instruction *T = BB->getTerminator();
+ if (!T)
+ continue;
+
+ // FIXME: This case needs some extra care because the terminators other than
+ // SI need to be updated.
+ if (BB->hasNPredecessorsOrMore(2))
+ continue;
+
+ // FIXME: Relax that the terminator is a BranchInst by checking for equality
+ // on other kinds of terminators. We decide to only support unconditional
+ // branches for now for compile time reasons.
+ auto *BI = dyn_cast<BranchInst>(T);
+ if (!BI || BI->isConditional())
+ continue;
+
+ if (Seen.insert(BB).second) {
+ // Keep track of which PHIs we need as keys in PhiPredIVs below.
+ for (BasicBlock *Succ : BI->successors())
+ for (PHINode &Phi : Succ->phis())
+ Phis.insert(&Phi);
+ }
+ Cases.emplace_back(CaseHandleWrapper{Case, &PhiPredIVs});
+ }
+
+ // Precompute a data structure to improve performance of isEqual for
+ // CaseHandleWrapper.
+ PhiPredIVs.reserve(Phis.size());
+ for (PHINode *Phi : Phis) {
+ PhiPredIVs[Phi] =
+ DenseMap<BasicBlock *, Value *>(Phi->getNumIncomingValues());
+ for (auto &IV : Phi->incoming_values())
+ PhiPredIVs[Phi].insert({Phi->getIncomingBlock(IV), IV.get()});
+ }
+
+ // Build a set such that if the CaseHandleWrapper exists in the set and
+ // another CaseHandleWrapper isEqual, then the equivalent CaseHandleWrapper
+ // which is not in the set should be replaced with the one in the set. If the
+ // CaseHandleWrapper is not in the set, then it should be added to the set so
+ // other CaseHandleWrappers can check against it in the same manner. We chose
+ // to use CaseHandleWrapper instead of BasicBlock since we'd need an extra
+ // nested loop since there is no O(1) lookup of BasicBlock -> Cases in
+ // SwichInst.
+ DenseSet<const CaseHandleWrapper *, DenseMapInfo<const CaseHandleWrapper *>>
+ ReplaceWith;
+ ReplaceWith.reserve(Cases.size());
+
+ bool MadeChange = false;
+ for (auto &CHW : Cases) {
+ // CHW is a candidate for simplification. If we find a duplicate BB,
+ // replace it.
+ const auto [It, Inserted] = ReplaceWith.insert(&CHW);
+ if (!Inserted) {
+ CHW.Case.setSuccessor((*It)->Case.getCaseSuccessor());
----------------
dtcxzyw wrote:
The dominator tree should be updated as well.
https://github.com/llvm/llvm-project/pull/114262
More information about the llvm-commits
mailing list