[llvm] [SeparateConstOffsetFromGEP] Fix incorrect inbounds flag in case of non-negative index but negative offset (PR #190192)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 2 08:19:46 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: Sergey Kachkov (skachkov-sc)
<details>
<summary>Changes</summary>
Fixes #<!-- -->190187
Currently, SeparateConstOffsetFromGEP preserves inbounds attribute if new sequence of GEPs from the same base pointer has non-negative offsets in each GEP (this was mentioned in https://github.com/llvm/llvm-project/pull/159515). This statement seems correct for me (if the sequence consists from 2 GEPs), but current implementation has a flaw: it checks that constant byte offset and GEP indices are non-negative. However, in some corner cases we can have a situation when the index is non-negative, but its offset (in bytes) is negative, so we can't preserve inbounds attribute. In the example, GEP index after transformation can have values `0x7ffffffffffffffd`/`0x7ffffffffffffffe`/`0x7fffffffffffffff`; they are all non-negative (sign bit is zero), however, after multiplication on sizeof(i64) they become negative and inbounds can't be preserved anymore.
The proposed fix is to check that Idx * ElementStride is non-negative (instead of checking Idx only).
---
Full diff: https://github.com/llvm/llvm-project/pull/190192.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp (+7-2)
- (modified) llvm/test/Transforms/SeparateConstOffsetFromGEP/RISCV/split-gep.ll (+21)
``````````diff
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 deaffc88117dd..f696f89e2f45c 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 [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]]
+;
+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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/190192
More information about the llvm-commits
mailing list