[llvm] [CodeGenPrepare] Drop nuw on gep unmerging if the new index is negative (PR #193488)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 10:54:25 PDT 2026


https://github.com/MuellerMP updated https://github.com/llvm/llvm-project/pull/193488

>From 1c28d0fbe8631546fd6869802c2eebc2e87d6632 Mon Sep 17 00:00:00 2001
From: MuellerMP <mirkomueller97 at live.de>
Date: Wed, 22 Apr 2026 14:31:54 +0200
Subject: [PATCH] [CodeGenPrepare] Drop nuw on gep unmerging if the new index
 is negative

Fixes #193487.

Drop nuw if unmerging would result in gep with a negative index.
---
 llvm/lib/CodeGen/CodeGenPrepare.cpp           |  9 +++++--
 .../X86/indirect-br-gep-unmerge-nuw.ll        | 26 +++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/indirect-br-gep-unmerge-nuw.ll

diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 2c232c6158749..0a469e82c8a64 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -8814,14 +8814,19 @@ static bool tryUnmergingGEPsAcrossIndirectBr(GetElementPtrInst *GEPI,
   for (GetElementPtrInst *UGEPI : UGEPIs) {
     UGEPI->setOperand(0, GEPI);
     ConstantInt *UGEPIIdx = cast<ConstantInt>(UGEPI->getOperand(1));
-    Constant *NewUGEPIIdx = ConstantInt::get(
-        GEPIIdx->getType(), UGEPIIdx->getValue() - GEPIIdx->getValue());
+    auto NewIdx = UGEPIIdx->getValue() - GEPIIdx->getValue();
+    Constant *NewUGEPIIdx = ConstantInt::get(GEPIIdx->getType(), NewIdx);
     UGEPI->setOperand(1, NewUGEPIIdx);
     // If GEPI is not inbounds but UGEPI is inbounds, change UGEPI to not
     // inbounds to avoid UB.
     if (!GEPI->isInBounds()) {
       UGEPI->setIsInBounds(false);
     }
+
+    // If UGEPI now has a negative index, drop the nuw flag.
+    auto Flags = UGEPI->getNoWrapFlags();
+    if (NewIdx.isNegative() && Flags.hasNoUnsignedWrap())
+      UGEPI->setNoWrapFlags(Flags.withoutNoUnsignedWrap());
   }
   // After unmerging, verify that GEPIOp is actually only used in SrcBlock (not
   // alive on IndirectBr edges).
diff --git a/llvm/test/CodeGen/X86/indirect-br-gep-unmerge-nuw.ll b/llvm/test/CodeGen/X86/indirect-br-gep-unmerge-nuw.ll
new file mode 100644
index 0000000000000..7c47dec8d029f
--- /dev/null
+++ b/llvm/test/CodeGen/X86/indirect-br-gep-unmerge-nuw.ll
@@ -0,0 +1,26 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes="require<profile-summary>,function(codegenprepare)" %s -o - | FileCheck %s
+target triple = "x86_64-unknown-linux-gnu"
+
+define ptr @test(ptr %memory, ptr %blockaddress) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[MEMORY:%.*]], ptr [[BLOCKADDRESS:%.*]]) {
+; CHECK-NEXT:  [[BB4:.*:]]
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[MEMORY]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr i8, ptr [[TMP2]], i64 8
+; CHECK-NEXT:    indirectbr ptr [[BLOCKADDRESS]], [label %[[EXIT:.*]]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr i8, ptr [[TMP3]], i64 -1
+; CHECK-NEXT:    [[TMP6:%.*]] = load i8, ptr [[TMP3]], align 1
+; CHECK-NEXT:    ret ptr [[TMP5]]
+;
+entry:
+  %pointer = load ptr, ptr %memory, align 8
+  %initalGEP = getelementptr i8, ptr %pointer, i64 8
+  indirectbr ptr %blockaddress, [label %exit]
+
+exit:
+  %unmergedGEP = getelementptr nuw i8, ptr %pointer, i64 7
+  %useOfFirstGEP = load i8, ptr %initalGEP, align 1
+  ret ptr %unmergedGEP
+}



More information about the llvm-commits mailing list