[llvm] [SeparateConstOffsetFromGEP] Fix incorrect inbounds flag in case of non-negative index but negative offset (PR #190192)

Sergey Kachkov via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 09:47:12 PDT 2026


https://github.com/skachkov-sc updated https://github.com/llvm/llvm-project/pull/190192

>From 500db441681f997a736d98ee87ab392a1c6db433 Mon Sep 17 00:00:00 2001
From: Sergey Kachkov <sergey.kachkov at syntacore.com>
Date: Wed, 1 Apr 2026 19:01:00 +0300
Subject: [PATCH 1/3] [NFC][SeparateConstOffsetFromGEP] Add pre-commit test

---
 .../RISCV/split-gep.ll                        | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
index deaffc88117dd..8cdd453f25903 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
@@ -289,3 +289,24 @@ entry:
   store i32 %i, ptr %gep8
   ret i32 undef
 }
+
+define i64 @test_inbounds(ptr %arr, i64 %x) {
+; CHECK-LABEL: @test_inbounds(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[MIN:%.*]] = tail call i64 @llvm.umin.i64(i64 [[X:%.*]], i64 4)
+; CHECK-NEXT:    [[XOR:%.*]] = xor i64 [[MIN]], -1
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[XOR]], 1
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [6 x i64], ptr [[ARR:%.*]], i64 0, i64 [[SHR]]
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 40
+; CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[GEP2]], align 8
+; CHECK-NEXT:    ret i64 [[RES]]
+;
+entry:
+  %min = tail call i64 @llvm.umin.i64(i64 %x, i64 4)
+  %xor = xor i64 %min, -1
+  %shr = lshr i64 %xor, 1
+  %sub = add nsw i64 %shr, -9223372036854775803
+  %gep = getelementptr inbounds nuw [6 x i64], ptr %arr, i64 0, i64 %sub
+  %res = load i64, ptr %gep, align 8
+  ret i64 %res
+}

>From 312beb0a7e844f90eb4c61372c797fb4dd3864e9 Mon Sep 17 00:00:00 2001
From: Sergey Kachkov <sergey.kachkov at syntacore.com>
Date: Wed, 1 Apr 2026 19:23:30 +0300
Subject: [PATCH 2/3] [SeparateConstOffsetFromGEP] Fix incorrect inbounds flag
 in case of non-negative index but negative offset

---
 .../lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp | 9 +++++++--
 .../SeparateConstOffsetFromGEP/RISCV/split-gep.ll        | 4 ++--
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
index 5a38f11314b13..84ad792f44a7b 100644
--- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
+++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
@@ -1178,8 +1178,13 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
         Idx = NewIdx;
         AllNUWPreserved &= PreservesNUW;
       }
-      AllOffsetsNonNegative =
-          AllOffsetsNonNegative && isKnownNonNegative(Idx, *DL);
+      // Ensure that index offset in bytes (Idx * SequentialElementStride) is
+      // non-negative. If Idx value has at least log2(Stride) + 1 zeros, it
+      // guarantees that (Idx * Stride) will have zeroed sign bit.
+      bool OffsetNonNegative =
+          computeKnownBits(Idx, *DL).countMinLeadingZeros() >=
+          Log2_64_Ceil(GTI.getSequentialElementStride(*DL)) + 1;
+      AllOffsetsNonNegative = AllOffsetsNonNegative && OffsetNonNegative;
     }
   }
   if (ExtractBase) {
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
index 8cdd453f25903..f696f89e2f45c 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll
@@ -296,8 +296,8 @@ define i64 @test_inbounds(ptr %arr, i64 %x) {
 ; CHECK-NEXT:    [[MIN:%.*]] = tail call i64 @llvm.umin.i64(i64 [[X:%.*]], i64 4)
 ; CHECK-NEXT:    [[XOR:%.*]] = xor i64 [[MIN]], -1
 ; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[XOR]], 1
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [6 x i64], ptr [[ARR:%.*]], i64 0, i64 [[SHR]]
-; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 40
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr [6 x i64], ptr [[ARR:%.*]], i64 0, i64 [[SHR]]
+; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 40
 ; CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[GEP2]], align 8
 ; CHECK-NEXT:    ret i64 [[RES]]
 ;

>From 2d9f5845d9c688dd4bd7956381e71b6e5a0ccf3d Mon Sep 17 00:00:00 2001
From: Sergey Kachkov <sergey.kachkov at syntacore.com>
Date: Thu, 2 Apr 2026 19:46:43 +0300
Subject: [PATCH 3/3] Update AMDGPU tests

---
 .../AMDGPU/preserve-inbounds.ll                           | 4 ++--
 .../AMDGPU/reorder-gep-inbounds.ll                        | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/preserve-inbounds.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/preserve-inbounds.ll
index 0d21a99153ea0..000d8a55d74eb 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/preserve-inbounds.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/preserve-inbounds.ll
@@ -41,8 +41,8 @@ define ptr @sign_bit_clear(ptr %p, i64 %i) {
 ; CHECK-LABEL: @sign_bit_clear(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[IDX:%.*]] = and i64 [[I:%.*]], 9223372036854775807
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[IDX]]
-; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 4
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 [[IDX]]
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 4
 ; CHECK-NEXT:    ret ptr [[ARRAYIDX]]
 ;
 entry:
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep-inbounds.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep-inbounds.ll
index f7c019bba5d1f..7fddf97097fc6 100644
--- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep-inbounds.ll
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep-inbounds.ll
@@ -85,8 +85,8 @@ define void @inboundsNonNegative_i8i16(ptr %in.ptr, i64 %in.idx1) {
 ; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[IDXPROM:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i16, ptr [[IN_PTR]], i64 [[IDXPROM]]
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 1024
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr i16, ptr [[IN_PTR]], i64 [[IDXPROM]]
+; CHECK-NEXT:    [[IDX11:%.*]] = getelementptr i8, ptr [[TMP0]], i64 1024
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -262,8 +262,8 @@ define void @addrspace1(ptr addrspace(1) %in.ptr, i64 %in.idx1) {
 ; CHECK-SAME: ptr addrspace(1) [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[IN_IDX1_NNEG:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i128, ptr addrspace(1) [[IN_PTR]], i64 [[IN_IDX1_NNEG]]
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[TMP0]], i64 1024
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr i128, ptr addrspace(1) [[IN_PTR]], i64 [[IN_IDX1_NNEG]]
+; CHECK-NEXT:    [[IDX11:%.*]] = getelementptr i8, ptr addrspace(1) [[TMP0]], i64 1024
 ; CHECK-NEXT:    ret void
 ;
 entry:



More information about the llvm-commits mailing list