[llvm-branch-commits] [llvm] release/23.x: [CodeGenPrepare] Drop poison-generating flags on extensions in optimizeExtUses (#234134) (PR #234530)

Douglas Yung via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 22 23:54:06 PDT 2026


https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/234530

>From 395ba2c4d274e0d81bea69a49aba372e3743d113 Mon Sep 17 00:00:00 2001
From: dyslexicsteak <dyslexicsteak at gmail.com>
Date: Thu, 22 Oct 2026 16:20:12 +0800
Subject: [PATCH] [CodeGenPrepare] Drop poison-generating flags on extensions
 in optimizeExtUses (#234134)

`optimizeExtUses` rewrites uses of an extension's source in other blocks
to read `trunc` of the extension, which is only correct where the
extension isn't `poison`. A `zext nneg` can be valid only where it was
originally used, like behind a check that its source isn't negative. If
that check is one of the rewritten uses, it ends up reading `poison`,
and on `x86_64-pc-windows-msvc`, InstSimplify in WinEHPrepare's cleanup
folds it to true and causes a miscompile.

Drop the extension's poison-generating flags once a use has been
rewritten, like #72103 and #90382 did for similar cases in
`CodeGenPrepare`.

Fixes #234098.

(cherry picked from commit 93fccabb4ebc82695475eaaa279ea5fbb2265450)
---
 llvm/lib/CodeGen/CodeGenPrepare.cpp           |  6 ++
 .../X86/optimize-ext-uses-nneg.ll             | 73 +++++++++++++++++++
 2 files changed, 79 insertions(+)
 create mode 100644 llvm/test/Transforms/CodeGenPrepare/X86/optimize-ext-uses-nneg.ll

diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 9d3f18c8fce638..149de8fcb748c6 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -7386,6 +7386,12 @@ bool CodeGenPrepare::optimizeExtUses(Instruction *I) {
     MadeChange = true;
   }
 
+  // The uses rewritten above read trunc(I), which equals Src only where I isn't
+  // poison. A poison-generating flag such as zext nneg can only hold under a
+  // guard, such as a sign check of Src, that itself is one of those uses.
+  if (MadeChange)
+    I->dropPoisonGeneratingFlags();
+
   return MadeChange;
 }
 
diff --git a/llvm/test/Transforms/CodeGenPrepare/X86/optimize-ext-uses-nneg.ll b/llvm/test/Transforms/CodeGenPrepare/X86/optimize-ext-uses-nneg.ll
new file mode 100644
index 00000000000000..3c6014004c1b40
--- /dev/null
+++ b/llvm/test/Transforms/CodeGenPrepare/X86/optimize-ext-uses-nneg.ll
@@ -0,0 +1,73 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='require<profile-summary>,function(codegenprepare)' -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+; optimizeExtUses rewrites uses of %id outside the extension's block to read
+; trunc(%z). That's only correct where %z isn't poison, and here zext nneg only
+; holds behind the sign check, which is itself one of the rewritten uses. Make
+; sure nneg is dropped.
+
+define i64 @drop_nneg_same_block(ptr %p, i1 %c) {
+; CHECK-LABEL: define i64 @drop_nneg_same_block(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ID:%.*]] = load i32, ptr [[P]], align 4
+; CHECK-NEXT:    [[Z:%.*]] = zext i32 [[ID]] to i64
+; CHECK-NEXT:    br i1 [[C]], label %[[TEST:.*]], label %[[EXIT:.*]]
+; CHECK:       [[TEST]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i64 [[Z]] to i32
+; CHECK-NEXT:    [[NONNEG:%.*]] = icmp sgt i32 [[TMP0]], -1
+; CHECK-NEXT:    br i1 [[NONNEG]], label %[[DIRECT:.*]], label %[[EXIT]]
+; CHECK:       [[DIRECT]]:
+; CHECK-NEXT:    ret i64 [[Z]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 -1
+;
+entry:
+  %id = load i32, ptr %p, align 4
+  %z = zext nneg i32 %id to i64
+  br i1 %c, label %test, label %exit
+
+test:
+  %nonneg = icmp sgt i32 %id, -1
+  br i1 %nonneg, label %direct, label %exit
+
+direct:
+  ret i64 %z
+
+exit:
+  ret i64 -1
+}
+
+; Same, but %z starts out in the guarded block, where nneg holds, and
+; optimizeExt first moves it next to the load to form an extending load.
+define i64 @drop_nneg_after_move_to_load(ptr %p, i1 %c) {
+; CHECK-LABEL: define i64 @drop_nneg_after_move_to_load(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ID:%.*]] = load i32, ptr [[P]], align 4
+; CHECK-NEXT:    [[Z:%.*]] = zext i32 [[ID]] to i64
+; CHECK-NEXT:    br i1 [[C]], label %[[TEST:.*]], label %[[EXIT:.*]]
+; CHECK:       [[DIRECT:.*]]:
+; CHECK-NEXT:    ret i64 [[Z]]
+; CHECK:       [[TEST]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i64 [[Z]] to i32
+; CHECK-NEXT:    [[NONNEG:%.*]] = icmp sgt i32 [[TMP0]], -1
+; CHECK-NEXT:    br i1 [[NONNEG]], label %[[DIRECT]], label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 -1
+;
+entry:
+  %id = load i32, ptr %p, align 4
+  br i1 %c, label %test, label %exit
+
+direct:
+  %z = zext nneg i32 %id to i64
+  ret i64 %z
+
+test:
+  %nonneg = icmp sgt i32 %id, -1
+  br i1 %nonneg, label %direct, label %exit
+
+exit:
+  ret i64 -1
+}



More information about the llvm-branch-commits mailing list