[llvm] [InstCombine] Relax singleUse for GEP(p, add(x, constant)) to 2 GEPs (PR #209551)
Abhay Kanhere via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 14 09:53:30 PDT 2026
https://github.com/AbhayKanhere updated https://github.com/llvm/llvm-project/pull/209551
>From 7d2ce7618d3e010f56ba7842610b46a7e550eea4 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Tue, 14 Jul 2026 09:33:23 -0700
Subject: [PATCH] [InstCombine] Relax singleUse for GEP(p,add(x,constant)) to 2
GEPs
Bound checks for memory safety involve checking index expression before
access.
e.g.
for ( i =0 ;i <n ; i++ ) {
if( i - k > bound)
llvm.trap()
a[ i - k-1] ...
a[ i - k]
}
so invariably, the index expression is no longer singleUse and this prevents
fold of GEP(p,ADD(x,y))) -> GEP( GEP(p,x),y). This later prevents optimizations
such as wideing the load or store
SingleUse check here is not a correctness check.
Here we relax the requirement of singleUse for constant 'y'. This enables
later optimizations such as aggressive instcombine widening with simplified
GEP with constant offsets.
---
.../InstCombine/InstructionCombining.cpp | 6 +-
.../InstCombine/gep-addlike-multiuse.ll | 64 +++++++++++++++++++
2 files changed, 68 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/gep-addlike-multiuse.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 1e24ff8d51057..a149c45e12615 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3652,9 +3652,11 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
};
// Try to replace ADD + GEP with GEP + GEP.
+ // Relax singleUse condition for constant added to GEP without risking
+ // infinite loop.
Value *Idx1, *Idx2;
- if (match(GEP.getOperand(1),
- m_OneUse(m_AddLike(m_Value(Idx1), m_Value(Idx2))))) {
+ if (match(GEP.getOperand(1), m_AddLike(m_Value(Idx1), m_Value(Idx2))) &&
+ (GEP.getOperand(1)->hasOneUse() || match(Idx2, m_ConstantInt()))) {
// %idx = add i64 %idx1, %idx2
// %gep = getelementptr i32, ptr %ptr, i64 %idx
// as:
diff --git a/llvm/test/Transforms/InstCombine/gep-addlike-multiuse.ll b/llvm/test/Transforms/InstCombine/gep-addlike-multiuse.ll
new file mode 100644
index 0000000000000..278d412e427f7
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-addlike-multiuse.ll
@@ -0,0 +1,64 @@
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Bounds-checked array access `a[i + k]`: the index `add i, k` feeds BOTH the
+; bounds-check compare (which dominates the access) AND the element GEP, so it
+; is inherently multi-use. When the addend `k` is a constant we still split it
+; out of the GEP: the inner `gep base, %idx` is shared with sibling accesses
+; (CSE) and the exposed constant offset enables later optimizations
+; (e.g. AggressiveInstCombine load-widening)
+
+define ptr @boundscheck_const_addend(ptr %base, i64 %idx, i64 %n) {
+; CHECK-LABEL: define ptr @boundscheck_const_addend(
+; CHECK-SAME: ptr [[BASE:%.*]], i64 [[IDX:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[OFF:%.*]] = add nsw i64 [[IDX]], 3
+; CHECK-NEXT: [[OOB:%.*]] = icmp ult i64 [[OFF]], [[N]]
+; CHECK-NEXT: br i1 [[OOB]], label %[[OK:.*]], label %[[TRAP:.*]]
+; CHECK: [[OK]]:
+; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[IDX]]
+; CHECK-NEXT: [[P:%.*]] = getelementptr i8, ptr [[TMP1]], i64 3
+; CHECK-NEXT: ret ptr [[P]]
+; CHECK: [[TRAP]]:
+; CHECK-NEXT: call void @llvm.trap()
+; CHECK-NEXT: unreachable
+;
+ %off = add nsw i64 %idx, 3
+ %oob = icmp uge i64 %off, %n
+ br i1 %oob, label %trap, label %ok
+
+ok:
+ %p = getelementptr inbounds i8, ptr %base, i64 %off
+ ret ptr %p
+
+trap:
+ call void @llvm.trap()
+ unreachable
+}
+
+; Negative: a multi-use add with a *variable* addend must NOT split unless profitable
+define ptr @boundscheck_var_addend(ptr %base, i64 %idx, i64 %v, i64 %n) {
+; CHECK-LABEL: define ptr @boundscheck_var_addend(
+; CHECK-SAME: ptr [[BASE:%.*]], i64 [[IDX:%.*]], i64 [[V:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[OFF:%.*]] = add nsw i64 [[IDX]], [[V]]
+; CHECK-NEXT: [[OOB:%.*]] = icmp ult i64 [[OFF]], [[N]]
+; CHECK-NEXT: br i1 [[OOB]], label %[[OK:.*]], label %[[TRAP:.*]]
+; CHECK: [[OK]]:
+; CHECK-NEXT: [[P:%.*]] = getelementptr inbounds i8, ptr [[BASE]], i64 [[OFF]]
+; CHECK-NEXT: ret ptr [[P]]
+; CHECK: [[TRAP]]:
+; CHECK-NEXT: call void @llvm.trap()
+; CHECK-NEXT: unreachable
+;
+ %off = add nsw i64 %idx, %v
+ %oob = icmp uge i64 %off, %n
+ br i1 %oob, label %trap, label %ok
+
+ok:
+ %p = getelementptr inbounds i8, ptr %base, i64 %off
+ ret ptr %p
+
+trap:
+ call void @llvm.trap()
+ unreachable
+}
+
+declare void @llvm.trap()
More information about the llvm-commits
mailing list