[llvm] [JumpThreading] Preserve assume instructions to maintain constraint information (PR #176600)
Bu Le via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 1 06:07:56 PDT 2026
https://github.com/lebu26 updated https://github.com/llvm/llvm-project/pull/176600
>From ac5a14b23349c074205f015bade3172f9ef78cc2 Mon Sep 17 00:00:00 2001
From: Bu Le <cityubule at gmail.com>
Date: Sun, 18 Jan 2026 06:13:29 +0800
Subject: [PATCH] [JumpThreading] Preserve assume instructions to maintain
constraint information
When jump threading optimizes a conditional branch in a basic block, it may
replace the condition variable with a constant. However, if the basic block
contains an assume() intrinsic that constrains this variable, we should not
replace the condition in the assume instruction.
This is important because downstream basic blocks that are dominated by the
current block may need to analyze the original constraint on the variable for
their own optimizations. Replacing the condition with assume(true) would break
the data flow for such analyses.
Add test case: assume-edge-dom1.ll
This test verifies that JumpThreading does not modify assume instructions when
the condition variable is optimized away in other branches.
---
llvm/lib/Transforms/Scalar/JumpThreading.cpp | 7 ++
.../JumpThreading/assume-edge-dom1.ll | 71 +++++++++++++++++++
2 files changed, 78 insertions(+)
create mode 100644 llvm/test/Transforms/JumpThreading/assume-edge-dom1.ll
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 415136b612ac2..292b82c8f87e3 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -413,6 +413,13 @@ static bool replaceFoldableUses(Instruction *Cond, Value *ToVal,
// of BB, where we know Cond is ToVal.
if (!isGuaranteedToTransferExecutionToSuccessor(&I))
break;
+ // Do not replace assume instruction's condition because other basic blocks
+ // dominated by this one may need to analyze the original constraint on the
+ // condition variable.
+ if (auto *II = llvm::dyn_cast<llvm::IntrinsicInst>(&I)) {
+ if (II->getIntrinsicID() == llvm::Intrinsic::assume)
+ break;
+ }
Changed |= I.replaceUsesOfWith(Cond, ToVal);
}
if (Cond->use_empty() && !Cond->mayHaveSideEffects()) {
diff --git a/llvm/test/Transforms/JumpThreading/assume-edge-dom1.ll b/llvm/test/Transforms/JumpThreading/assume-edge-dom1.ll
new file mode 100644
index 0000000000000..1064567698907
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/assume-edge-dom1.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt %s -passes=jump-threading -S | FileCheck %s
+;
+; Test: Preserve assume information in dominated basic blocks
+;
+; This test ensures that when a basic block contains an assume() that constrains
+; a variable (e.g., assume(x == 0)), the JumpThreading pass does not replace it
+; with assume(true). If it did, dominated basic blocks that depend on analyzing
+; the variable's constraints would lose the ability to find the original definition
+; and could miss optimization opportunities or produce incorrect code.
+;
+; The key scenario:
+; 1. assume_bb contains: call void @llvm.assume(i1 %check_val)
+; 2. check_bb (dominated by assume_bb) branches on the same %check_val
+; 3. JumpThreading should NOT rewrite the assume to assume(true) in assume_bb
+; because it would break the data flow for analyses in other dominated blocks
+
+declare void @side_effect(i32)
+
+; Function Attrs: inaccessiblememonly nocallback nofree nosync nounwind willreturn
+declare void @llvm.assume(i1 noundef) #0
+
+define i64 @test_preserve_assume_info(i32 %unused, i1 %outer_cond) {
+; CHECK-LABEL: define i64 @test_preserve_assume_info(
+; CHECK-SAME: i32 [[UNUSED:%.*]], i1 [[OUTER_COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CHECK_VAL:%.*]] = icmp eq i32 [[UNUSED]], 0
+; CHECK-NEXT: br i1 [[OUTER_COND]], label %[[EXIT:.*]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: call void @llvm.assume(i1 [[CHECK_VAL]])
+; CHECK-NEXT: ret i64 0
+;
+entry:
+ %check_val = icmp eq i32 %unused, 0
+ br i1 %outer_cond, label %merge, label %cond_true
+
+cond_true:
+ br label %merge
+
+merge:
+ br label %assume_bb
+
+assume_bb:
+ ; This assume constrains %check_val. JumpThreading should NOT replace it with
+ ; assume(true) because dominated blocks need to analyze %check_val's definition.
+ call void @llvm.assume(i1 %check_val)
+ br i1 %check_val, label %merge1, label %use_bb1
+
+use_bb1:
+ call void @side_effect(i32 99998)
+ br label %merge1
+
+merge1:
+ br label %check_bb
+
+check_bb:
+ ; This block is dominated by assume_bb. It needs the original constraint on
+ ; %check_val to be preserved in assume_bb for correct analysis.
+ br i1 %check_val, label %exit, label %use_bb2
+
+use_bb2:
+ ; This call should be removed by JumpThreading because %check_val is false here.
+ ; This only works correctly if the assume in assume_bb is preserved properly.
+ call void @side_effect(i32 99999)
+ br label %exit
+
+exit:
+ ret i64 0
+}
+
+attributes #0 = { inaccessiblememonly nocallback nofree nosync nounwind willreturn }
More information about the llvm-commits
mailing list