[llvm] [JumpThreading] Clone noalias scopes in duplicateCondBranchOnPHIIntoPred (PR #200550)
via llvm-commits
llvm-commits at lists.llvm.org
Sat May 30 01:05:43 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Justin Lebar (jlebar)
<details>
<summary>Changes</summary>
duplicateCondBranchOnPHIIntoPred clones a block into a predecessor with
its own loop. Unlike cloneInstructions (used by threadEdge), it never
cloned the duplicated noalias scope declarations. The duplicated code
therefore shared the original's !alias.scope/!noalias MDNodes, letting
AA incorrectly treat two accesses on different paths as non-aliasing.
Fix by cloning the scopes.
This bug was found by a large run of Opus 4.7 looking for bugs in LLVM.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/200550.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/JumpThreading.cpp (+11)
- (added) llvm/test/Transforms/JumpThreading/duplicate-cond-br-noalias-scope.ll (+49)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 415136b612ac2..3e449f1cf2e04 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -2689,11 +2689,22 @@ bool JumpThreadingPass::duplicateCondBranchOnPHIIntoPred(
BasicBlock::iterator BI = BB->begin();
for (; PHINode *PN = dyn_cast<PHINode>(BI); ++BI)
ValueMapping[PN] = PN->getIncomingValueForBlock(PredBB);
+
+ // Clone noalias scope declarations in the duplicated instructions. Otherwise
+ // the duplicate would share the original block's scopes, and alias analysis
+ // could conclude two accesses on different paths do not alias when they may.
+ SmallVector<MDNode *> NoAliasScopes;
+ DenseMap<MDNode *, MDNode *> ClonedScopes;
+ LLVMContext &Context = PredBB->getContext();
+ identifyNoAliasScopesToClone(BI, BB->end(), NoAliasScopes);
+ cloneNoAliasScopes(NoAliasScopes, ClonedScopes, "thread", Context);
+
// Clone the non-phi instructions of BB into PredBB, keeping track of the
// mapping and using it to remap operands in the cloned instructions.
for (; BI != BB->end(); ++BI) {
Instruction *New = BI->clone();
New->insertInto(PredBB, OldPredBranch->getIterator());
+ adaptNoAliasScopes(New, ClonedScopes, Context);
// Remap operands to patch up intra-block references.
for (unsigned i = 0, e = New->getNumOperands(); i != e; ++i)
diff --git a/llvm/test/Transforms/JumpThreading/duplicate-cond-br-noalias-scope.ll b/llvm/test/Transforms/JumpThreading/duplicate-cond-br-noalias-scope.ll
new file mode 100644
index 0000000000000..f279110ddc192
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/duplicate-cond-br-noalias-scope.ll
@@ -0,0 +1,49 @@
+; RUN: opt -S -passes=jump-threading < %s | FileCheck %s
+
+; duplicateCondBranchOnPHIIntoPred duplicates a block whose conditional branch
+; is on a PHI of non-constant values into a predecessor. It must clone the
+; block's noalias scope declarations (as cloneInstructions does for threadEdge),
+; otherwise the duplicate shares the original's scope MDNodes and alias analysis
+; can wrongly conclude two accesses on different paths do not alias.
+
+declare i1 @opaque()
+declare void @llvm.experimental.noalias.scope.decl(metadata)
+
+define void @dup_clones_noalias_scope(ptr %p) {
+; CHECK-LABEL: @dup_clones_noalias_scope(
+; The duplicated decl/store reference a freshly cloned "scope1:thread" scope...
+; CHECK: call void @llvm.experimental.noalias.scope.decl(metadata [[DUP:![0-9]+]])
+; CHECK-NEXT: store i8 0, ptr %p, align 1, !noalias [[DUP]]
+; ...distinct from the original block, which keeps the unmodified scope.
+; CHECK: call void @llvm.experimental.noalias.scope.decl(metadata [[ORIG:![0-9]+]])
+; CHECK-NEXT: store i8 0, ptr %p, align 1, !noalias [[ORIG]]
+; CHECK-DAG: [[DUP]] = !{[[DUPSCOPE:![0-9]+]]}
+; CHECK-DAG: [[DUPSCOPE]] = distinct !{[[DUPSCOPE]], {{![0-9]+}}, !"scope1:thread"}
+entry:
+ %c = call i1 @opaque()
+ br i1 %c, label %pred, label %other
+
+pred:
+ %pv = call i1 @opaque()
+ br label %bb
+
+other:
+ %ov = call i1 @opaque()
+ br label %bb
+
+bb:
+ %x = phi i1 [ %pv, %pred ], [ %ov, %other ]
+ call void @llvm.experimental.noalias.scope.decl(metadata !0)
+ store i8 0, ptr %p, !noalias !0
+ br i1 %x, label %tb, label %fb
+
+tb:
+ ret void
+
+fb:
+ ret void
+}
+
+!0 = !{!1}
+!1 = distinct !{!1, !2, !"scope1"}
+!2 = distinct !{!2, !"domain"}
``````````
</details>
https://github.com/llvm/llvm-project/pull/200550
More information about the llvm-commits
mailing list