[llvm] [SpeculativeExecution] Drop poison-generating flags when hoisting instructions (PR #225377)

Adrian Kuegel via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 04:51:56 PDT 2026


https://github.com/akuegel created https://github.com/llvm/llvm-project/pull/225377

When SpeculativeExecutionPass::considerHoistingFromTo hoists instructions out of a conditionally executed basic block into a dominating block, any poison-generating flags (such as `disjoint`, `nsw`, `nuw`, `exact`, or `inbounds`) that were inferred under the branch condition may no longer hold in the dominating block.

For example, `InstCombine` can fold `add i32 %x, -2` to `or disjoint i32 %x, -2` inside an `if (%x < 2)` block. If `SpeculativeExecutionPass` hoists the `or disjoint` above `if (%x < 2)` without dropping `disjoint`, subsequent passes such as `NaryReassociate` (via `ScalarEvolution`) or `EarlyCSE` treat the hoisted `or disjoint %x, -2` as equivalent to `add %x, -2` in other blocks where `%x >= 2`, causing a miscompilation.

Call `dropPoisonGeneratingFlags()` on hoisted instructions.

>From 8392a8a9018304a8c1a4f6530ea1da4cb285b798 Mon Sep 17 00:00:00 2001
From: Adrian Kuegel <akuegel at google.com>
Date: Tue, 22 Sep 2026 11:43:21 +0000
Subject: [PATCH] [SpeculativeExecution] Drop poison-generating flags when
 hoisting instructions

When SpeculativeExecutionPass::considerHoistingFromTo hoists instructions
out of a conditionally executed basic block into a dominating block, any
poison-generating flags (such as `disjoint`, `nsw`, `nuw`, `exact`, or
`inbounds`) that were inferred under the branch condition may no longer
hold in the dominating block.

For example, `InstCombine` can fold `add i32 %x, -2` to `or disjoint i32 %x, -2`
inside an `if (%x < 2)` block. If `SpeculativeExecutionPass` hoists the
`or disjoint` above `if (%x < 2)` without dropping `disjoint`, subsequent
passes such as `NaryReassociate` (via `ScalarEvolution`) or `EarlyCSE`
treat the hoisted `or disjoint %x, -2` as equivalent to `add %x, -2` in
other blocks where `%x >= 2`, causing a miscompilation.

Call `dropPoisonGeneratingFlags()` on hoisted instructions.
---
 .../Scalar/SpeculativeExecution.cpp           |  1 +
 .../Transforms/SpeculativeExecution/spec.ll   | 20 +++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp b/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp
index 0a2a3f8a495bf7..2dcabaf7644fa6 100644
--- a/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp
+++ b/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp
@@ -320,6 +320,7 @@ bool SpeculativeExecutionPass::considerHoistingFromTo(
     if (!NotHoisted.count(&*Current)) {
       Current->moveBefore(ToBlock.getTerminator()->getIterator());
       Current->dropLocation();
+      Current->dropPoisonGeneratingFlags();
     }
   }
   return true;
diff --git a/llvm/test/Transforms/SpeculativeExecution/spec.ll b/llvm/test/Transforms/SpeculativeExecution/spec.ll
index 9f409eb059f431..95c6306f04b5a6 100644
--- a/llvm/test/Transforms/SpeculativeExecution/spec.ll
+++ b/llvm/test/Transforms/SpeculativeExecution/spec.ll
@@ -193,3 +193,23 @@ a:
 b:
   ret void
 }
+
+; Drop poison-generating flags when hoisting.
+define void @dropPoisonFlags(i32 %val, ptr %p) {
+; CHECK-LABEL: @dropPoisonFlags(
+; CHECK-NEXT: %or = or i32 %val, -2{{$}}
+; CHECK-NEXT: %add = add i32 %or, 1{{$}}
+; CHECK-NEXT: %gep = getelementptr i8, ptr %p, i32 %add{{$}}
+; CHECK-NEXT: br i1 true
+  br i1 true, label %a, label %b
+; CHECK: a:
+a:
+  %or = or disjoint i32 %val, -2
+  %add = add nuw nsw i32 %or, 1
+  %gep = getelementptr inbounds i8, ptr %p, i32 %add
+  br label %b
+; CHECK: b:
+b:
+  ret void
+}
+



More information about the llvm-commits mailing list