[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