[llvm] [Transforms] Add DeadBranchElimination pass (PR #211176)
Yoni Peleg via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 06:58:41 PDT 2026
================
@@ -0,0 +1,408 @@
+//===- DeadBranchElimination.cpp ------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass eliminates conditional branches that are unreachable but that
+// cannot be proven unreachable directly, because the branch body modifies the
+// very values its condition depends on. The motivating example:
+//
+// int a = 0, b = 0, limit = 100;
+// while (a < limit) {
+// if (b == limit) // Unreachable: a == b always, and a < limit here.
+// limit += 1; // ...but it modifies limit (circular dependency).
+// a++; b++;
+// }
+//
+// Proving the branch dead requires knowing limit is loop-invariant, which
+// requires knowing the branch is dead.
+//
+// The algorithm is an optimistic fixed point over the two bodies (true side,
+// false side) of every conditional branch. Each body starts as Unknown
+// ("assumed dead") and can only be promoted to ProvenReachable:
+//
+// 1. Materialize the assumption set in place (no cloning): PHI slots fed
+// by assumed-dead edges or assumed-unreachable regions are temporarily
+// overwritten with the surviving values (see AssumedDeadEdges), so the
+// assumed-dead bodies' effects vanish from the analysis.
+// 2. Run ScalarEvolution on the function in this state.
+// 3. For each Unknown body, check whether the analysis proves its branch
+// edge is never taken (context-sensitive SCEV proof, or the branch
+// sits in an assumed-dead region). If the edge cannot be proven dead,
+// mark it ProvenReachable; its body is restored for the next
+// iteration.
+// 4. Undo the PHI rewrites exactly and repeat until no status changes.
+// Statuses move in one direction only, so this terminates.
+//
+// At convergence the remaining Unknown set is self-consistent: assuming
+// those bodies never run, the analysis proves they indeed never run
+// (consider the first time one would run -- up to that point the rewritten
+// values match the real execution, and the analysis proves the edge is not
+// taken). Those branches are then folded.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar/DeadBranchElimination.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassInstrumentation.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/Utils/Local.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "dead-branch-elim"
+
+STATISTIC(NumBranchesFolded, "Number of provably dead branch edges folded");
+
+static cl::opt<unsigned> MaxRefineIterations(
+ "dbe-max-iterations", cl::init(8), cl::Hidden,
+ cl::desc("Maximum fixed-point iterations per function before giving up"));
+
+namespace {
+
+enum class Status { Unknown, ProvenReachable };
+
+/// One side (body) of a conditional branch.
+struct BranchBody {
+ BasicBlock *BranchBB; ///< Block whose terminator is the conditional branch.
+ unsigned SuccIdx; ///< Which side (0 = true body, 1 = false body).
+ Status St = Status::Unknown;
+};
+
+/// The fixed point is only worthwhile for the pattern this pass targets: a
+/// branch inside a loop whose condition SCEV can reason about. Everything
+/// else is seeded ProvenReachable so that functions without such branches
+/// are never analyzed at all. Straight-line provably-dead branches are left
+/// to SCCP/SimplifyCFG.
+SmallVector<BranchBody> collectBranchBodies(Function &F, LoopInfo &LI) {
+ SmallVector<BranchBody> Bodies;
----------------
yonipeleg33 wrote:
SmallVector is [documented](https://llvm.org/doxygen/classllvm_1_1SmallVector.html) to at least attempt to keep the vector size at around 64 bytes. With `BranchBody` taking say 16 bytes, this will immediately heap-allocate on each iteration for any function with >4 branches, approximately.
I'm unsure SmallVector should be used at all here, or at least consider explicitly defining the amount of elements to something sane e.g. `SmallVector<BranchBody, 32>`.
https://github.com/llvm/llvm-project/pull/211176
More information about the llvm-commits
mailing list