aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <npopov@redhat.com>2024-09-13 10:38:34 +0200
committerNikita Popov <npopov@redhat.com>2024-09-13 11:15:22 +0200
commit1c298c927498983dee29037ed1d3e71b72ca0082 (patch)
tree22039f87c04937ce18d89a8f6cf8556409b9de4d
parenta0f88901a4e6a6618c3ec02108103d0415e28834 (diff)
downloadllvm-1c298c927498983dee29037ed1d3e71b72ca0082.zip
llvm-1c298c927498983dee29037ed1d3e71b72ca0082.tar.gz
llvm-1c298c927498983dee29037ed1d3e71b72ca0082.tar.bz2
[InstCombine] Preserve nuw flags when merging geps
These transforms all perform a variant of (gep (gep p, x), y) to (gep p, (x + y)). We can preserve both inbounds and nuw during such transforms (https://alive2.llvm.org/ce/z/Stu4cN), but not nusw, which would require proving that the new add is nsw. For the constant offset case, I've conservatively retained the logic that checks for negative intermediate offsets, though I'm not sure it's still reachable nowadays.
-rw-r--r--clang/test/CodeGen/attr-counted-by.c32
-rw-r--r--clang/test/OpenMP/bug57757.cpp4
-rw-r--r--llvm/lib/Transforms/InstCombine/InstructionCombining.cpp23
-rw-r--r--llvm/test/Transforms/InstCombine/getelementptr.ll102
4 files changed, 137 insertions, 24 deletions
diff --git a/clang/test/CodeGen/attr-counted-by.c b/clang/test/CodeGen/attr-counted-by.c
index a06e815..2e2d9cd 100644
--- a/clang/test/CodeGen/attr-counted-by.c
+++ b/clang/test/CodeGen/attr-counted-by.c
@@ -686,7 +686,7 @@ size_t test6_bdos(struct anon_struct *p) {
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB11:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]]
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
// SANITIZE-WITH-ATTR: cont7:
-// SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITH-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA8:![0-9]+]]
// SANITIZE-WITH-ATTR-NEXT: ret void
@@ -694,7 +694,7 @@ size_t test6_bdos(struct anon_struct *p) {
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test7(
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
-// NO-SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// NO-SANITIZE-WITH-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITH-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6:![0-9]+]]
@@ -703,7 +703,7 @@ size_t test6_bdos(struct anon_struct *p) {
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test7(
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6:![0-9]+]]
@@ -712,7 +712,7 @@ size_t test6_bdos(struct anon_struct *p) {
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test7(
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6:![0-9]+]]
@@ -778,7 +778,7 @@ size_t test7_bdos(struct union_of_fams *p) {
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test8(
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -787,7 +787,7 @@ size_t test7_bdos(struct union_of_fams *p) {
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test8(
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 9
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[INTS:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 9
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[INTS]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -840,7 +840,7 @@ size_t test8_bdos(struct union_of_fams *p) {
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB14:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]]
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
// SANITIZE-WITH-ATTR: cont7:
-// SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITH-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA8]]
// SANITIZE-WITH-ATTR-NEXT: ret void
@@ -848,7 +848,7 @@ size_t test8_bdos(struct union_of_fams *p) {
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test9(
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
-// NO-SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// NO-SANITIZE-WITH-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITH-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -857,7 +857,7 @@ size_t test8_bdos(struct union_of_fams *p) {
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test9(
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -866,7 +866,7 @@ size_t test8_bdos(struct union_of_fams *p) {
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test9(
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -936,7 +936,7 @@ size_t test9_bdos(struct union_of_fams *p) {
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test10(
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -945,7 +945,7 @@ size_t test9_bdos(struct union_of_fams *p) {
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test10(
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 12
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[BYTES]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i8 -1, ptr [[ARRAYIDX]], align 1, !tbaa [[TBAA6]]
@@ -1583,7 +1583,7 @@ struct test26_foo {
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB30:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR10]], !nosanitize [[META2]]
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
// SANITIZE-WITH-ATTR: cont5:
-// SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 8
+// SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 8
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARR]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA4]]
// SANITIZE-WITH-ATTR-NEXT: ret i32 [[TMP2]]
@@ -1591,7 +1591,7 @@ struct test26_foo {
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i32 @test26(
// NO-SANITIZE-WITH-ATTR-SAME: i32 noundef [[C:%.*]], ptr nocapture noundef readonly [[FOO:%.*]]) local_unnamed_addr #[[ATTR2]] {
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
-// NO-SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 8
+// NO-SANITIZE-WITH-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 8
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[C]] to i64
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARR]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
@@ -1600,7 +1600,7 @@ struct test26_foo {
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i32 @test26(
// SANITIZE-WITHOUT-ATTR-SAME: i32 noundef [[C:%.*]], ptr noundef [[FOO:%.*]]) local_unnamed_addr #[[ATTR0]] {
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// SANITIZE-WITHOUT-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 8
+// SANITIZE-WITHOUT-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 8
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[C]] to i64
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARR]], i64 0, i64 [[IDXPROM]]
// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
@@ -1609,7 +1609,7 @@ struct test26_foo {
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i32 @test26(
// NO-SANITIZE-WITHOUT-ATTR-SAME: i32 noundef [[C:%.*]], ptr nocapture noundef readonly [[FOO:%.*]]) local_unnamed_addr #[[ATTR6]] {
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
-// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds i8, ptr [[FOO]], i64 8
+// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[FOO]], i64 8
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[C]] to i64
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARR]], i64 0, i64 [[IDXPROM]]
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
diff --git a/clang/test/OpenMP/bug57757.cpp b/clang/test/OpenMP/bug57757.cpp
index bb78ddd..240b22a 100644
--- a/clang/test/OpenMP/bug57757.cpp
+++ b/clang/test/OpenMP/bug57757.cpp
@@ -43,8 +43,8 @@ void foo() {
// CHECK-NEXT: br label [[DOTOMP_OUTLINED__EXIT]]
// CHECK: .untied.next..i:
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP1]], i64 40
-// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 52
-// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 48
+// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP1]], i64 52
+// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP1]], i64 48
// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP5]], align 8, !tbaa [[TBAA19:![0-9]+]], !noalias [[META13]]
// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[TMP7]], align 4, !tbaa [[TBAA16]], !noalias [[META13]]
// CHECK-NEXT: [[TMP10:%.*]] = load float, ptr [[TMP6]], align 4, !tbaa [[TBAA20:![0-9]+]], !noalias [[META13]]
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 0f9d296..2bdedfe 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2327,8 +2327,16 @@ Instruction *InstCombinerImpl::narrowMathIfNoOverflow(BinaryOperator &BO) {
return CastInst::Create(CastOpc, NarrowBO, BO.getType());
}
-static bool isMergedGEPInBounds(GEPOperator &GEP1, GEPOperator &GEP2) {
- return GEP1.isInBounds() && GEP2.isInBounds();
+/// Determine nowrap flags for (gep (gep p, x), y) to (gep p, (x + y))
+/// transform.
+static GEPNoWrapFlags getMergedGEPNoWrapFlags(GEPOperator &GEP1,
+ GEPOperator &GEP2) {
+ GEPNoWrapFlags NW = GEP1.getNoWrapFlags() & GEP2.getNoWrapFlags();
+ // Without inbounds, we could only preserve nusw if we know that x + y does
+ // not wrap.
+ if (!NW.isInBounds())
+ NW = NW.withoutNoUnsignedSignedWrap();
+ return NW;
}
/// Thread a GEP operation with constant indices through the constant true/false
@@ -2448,7 +2456,7 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
if (!Offset.isZero() || (!IsFirstType && !ConstIndices[0].isZero()))
return nullptr;
- bool IsInBounds = isMergedGEPInBounds(*Src, *cast<GEPOperator>(&GEP));
+ GEPNoWrapFlags NW = getMergedGEPNoWrapFlags(*Src, *cast<GEPOperator>(&GEP));
SmallVector<Value *> Indices;
append_range(Indices, drop_end(Src->indices(),
Src->getNumIndices() - NumVarIndices));
@@ -2458,12 +2466,15 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
// by first performing a larger negative offset, and then a smaller
// positive one. The large negative offset might go out of bounds. Only
// preserve inbounds if all signs are the same.
- IsInBounds &= Idx.isNonNegative() == ConstIndices[0].isNonNegative();
+ if (Idx.isNonNegative() != ConstIndices[0].isNonNegative())
+ NW = NW.withoutNoUnsignedSignedWrap();
+ if (!Idx.isNonNegative())
+ NW = NW.withoutNoUnsignedWrap();
}
return replaceInstUsesWith(
GEP, Builder.CreateGEP(Src->getSourceElementType(), Src->getOperand(0),
- Indices, "", IsInBounds));
+ Indices, "", NW));
}
if (Src->getResultElementType() != GEP.getSourceElementType())
@@ -2513,7 +2524,7 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP,
return replaceInstUsesWith(
GEP, Builder.CreateGEP(
Src->getSourceElementType(), Src->getOperand(0), Indices, "",
- isMergedGEPInBounds(*Src, *cast<GEPOperator>(&GEP))));
+ getMergedGEPNoWrapFlags(*Src, *cast<GEPOperator>(&GEP))));
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/getelementptr.ll b/llvm/test/Transforms/InstCombine/getelementptr.ll
index 8607e58f..6a5f77e 100644
--- a/llvm/test/Transforms/InstCombine/getelementptr.ll
+++ b/llvm/test/Transforms/InstCombine/getelementptr.ll
@@ -1917,5 +1917,107 @@ define ptr @gep_merge_not_nuw(ptr %p, i64 %idx) {
ret ptr %gep
}
+define ptr @gep_merge_nuw(ptr %p, i64 %x, i64 %y) {
+; CHECK-LABEL: @gep_merge_nuw(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %sub = sub i64 %y, %x
+ %gep1 = getelementptr nuw i8, ptr %p, i64 %x
+ %gep = getelementptr nuw i8, ptr %gep1, i64 %sub
+ ret ptr %gep
+}
+
+define ptr @gep_merge_nuw_only_one1(ptr %p, i64 %x, i64 %y) {
+; CHECK-LABEL: @gep_merge_nuw_only_one1(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %sub = sub i64 %y, %x
+ %gep1 = getelementptr i8, ptr %p, i64 %x
+ %gep = getelementptr nuw i8, ptr %gep1, i64 %sub
+ ret ptr %gep
+}
+
+define ptr @gep_merge_nuw_only_one2(ptr %p, i64 %x, i64 %y) {
+; CHECK-LABEL: @gep_merge_nuw_only_one2(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %sub = sub i64 %y, %x
+ %gep1 = getelementptr nuw i8, ptr %p, i64 %x
+ %gep = getelementptr i8, ptr %gep1, i64 %sub
+ ret ptr %gep
+}
+
+; Cannot preserve nusw, unless we know that the addition x + (y-x)
+; does not overflow.
+define ptr @gep_merge_nusw(ptr %p, i64 %x, i64 %y) {
+; CHECK-LABEL: @gep_merge_nusw(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 [[Y:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %sub = sub i64 %y, %x
+ %gep1 = getelementptr nusw i8, ptr %p, i64 %x
+ %gep = getelementptr nusw i8, ptr %gep1, i64 %sub
+ ret ptr %gep
+}
+
+define ptr @gep_merge_nuw_add_zero(ptr %p, i64 %idx, i64 %idx2) {
+; CHECK-LABEL: @gep_merge_nuw_add_zero(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw [2 x i32], ptr [[P:%.*]], i64 [[IDX:%.*]], i64 [[IDX2:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %gep1 = getelementptr nuw [2 x i32], ptr %p, i64 %idx
+ %gep = getelementptr nuw [2 x i32], ptr %gep1, i64 0, i64 %idx2
+ ret ptr %gep
+}
+
+; Cannot preserve nusw, even if the merge only involves an add with a zero
+; index. This is because the whole offset calculation is required to be nsw
+; after the merge.
+define ptr @gep_merge_nusw_add_zero(ptr %p, i64 %idx, i64 %idx2) {
+; CHECK-LABEL: @gep_merge_nusw_add_zero(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr [2 x i32], ptr [[P:%.*]], i64 [[IDX:%.*]], i64 [[IDX2:%.*]]
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %gep1 = getelementptr nusw [2 x i32], ptr %p, i64 %idx
+ %gep = getelementptr nusw [2 x i32], ptr %gep1, i64 0, i64 %idx2
+ ret ptr %gep
+}
+
+define ptr @gep_merge_nuw_const(ptr %p, i64 %idx, i64 %idx2) {
+; CHECK-LABEL: @gep_merge_nuw_const(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw [2 x i32], ptr [[P:%.*]], i64 [[IDX:%.*]], i64 1
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %gep1 = getelementptr nuw [2 x i32], ptr %p, i64 %idx
+ %gep = getelementptr nuw i8, ptr %gep1, i64 4
+ ret ptr %gep
+}
+
+define ptr @gep_merge_nuw_const_neg(ptr %p, i64 %idx, i64 %idx2) {
+; CHECK-LABEL: @gep_merge_nuw_const_neg(
+; CHECK-NEXT: [[GEP1:%.*]] = getelementptr nuw [2 x i32], ptr [[P:%.*]], i64 [[IDX:%.*]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[GEP1]], i64 -4
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %gep1 = getelementptr nuw [2 x i32], ptr %p, i64 %idx
+ %gep = getelementptr nuw i8, ptr %gep1, i64 -4
+ ret ptr %gep
+}
+
+; Cannot preserve nusw, because we don't know that the new offset calculation
+; does not overflow.
+define ptr @gep_merge_nusw_const(ptr %p, i64 %idx, i64 %idx2) {
+; CHECK-LABEL: @gep_merge_nusw_const(
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr [2 x i32], ptr [[P:%.*]], i64 [[IDX:%.*]], i64 1
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+ %gep1 = getelementptr nusw [2 x i32], ptr %p, i64 %idx
+ %gep = getelementptr nusw i8, ptr %gep1, i64 4
+ ret ptr %gep
+}
+
!0 = !{!"branch_weights", i32 2, i32 10}