[llvm] [BasicAA] Use nuw attribute of GEPs (PR #98608)
Hari Limaye via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 15 01:36:01 PDT 2024
https://github.com/hazzlim updated https://github.com/llvm/llvm-project/pull/98608
>From b9e90dcd7f87f031a5c1ef1d670859f306e1a865 Mon Sep 17 00:00:00 2001
From: Hari Limaye <hari.limaye at arm.com>
Date: Wed, 26 Jun 2024 15:09:35 +0000
Subject: [PATCH 1/2] [BasicAA] Use nuw attribute of GEPs
Use the nuw attribute of GEPs to prove that pointers do not alias, in
cases matching the following:
+ + +
| BaseOffset | +<nuw> Indices |
---------------->|-------------------->|
|-->VLeftSize | |------->VRightSize
LHS RHS
If the difference between pointers is Offset +<nuw> Indices (the variable
indices all come from nuw GEPs) then we know that the addition does not
wrap the pointer index type (add nuw) and the constant Offset is a lower
bound on the distance between the pointers. We can then prove NoAlias via
Offset u>= VLeftSize.
---
llvm/lib/Analysis/BasicAliasAnalysis.cpp | 45 +++++--
llvm/test/Analysis/BasicAA/gep-nuw-alias.ll | 142 ++++++++++++++++++++
2 files changed, 177 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/Analysis/BasicAA/gep-nuw-alias.ll
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 161a3034e4829..13e26fdc2ed29 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -548,9 +548,9 @@ struct BasicAAResult::DecomposedGEP {
APInt Offset;
// Scaled variable (non-constant) indices.
SmallVector<VariableGEPIndex, 4> VarIndices;
- // Are all operations inbounds GEPs or non-indexing operations?
+ // Nowrap flags common to all GEP operations involved in expression.
// (std::nullopt iff expression doesn't involve any geps)
- std::optional<bool> InBounds;
+ std::optional<GEPNoWrapFlags> NWFlags;
void dump() const {
print(dbgs());
@@ -637,12 +637,11 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
return Decomposed;
}
- // Track whether we've seen at least one in bounds gep, and if so, whether
- // all geps parsed were in bounds.
- if (Decomposed.InBounds == std::nullopt)
- Decomposed.InBounds = GEPOp->isInBounds();
- else if (!GEPOp->isInBounds())
- Decomposed.InBounds = false;
+ // Track the common nowrap flags for all GEPs we see.
+ if (Decomposed.NWFlags == std::nullopt)
+ Decomposed.NWFlags = GEPOp->getNoWrapFlags();
+ else
+ *Decomposed.NWFlags &= GEPOp->getNoWrapFlags();
assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized");
@@ -1111,7 +1110,7 @@ AliasResult BasicAAResult::aliasGEP(
// for the two to alias, then we can assume noalias.
// TODO: Remove !isScalable() once BasicAA fully support scalable location
// size
- if (*DecompGEP1.InBounds && DecompGEP1.VarIndices.empty() &&
+ if (DecompGEP1.NWFlags->isInBounds() && DecompGEP1.VarIndices.empty() &&
V2Size.hasValue() && !V2Size.isScalable() &&
DecompGEP1.Offset.sge(V2Size.getValue()) &&
isBaseOfObject(DecompGEP2.Base))
@@ -1119,7 +1118,7 @@ AliasResult BasicAAResult::aliasGEP(
if (isa<GEPOperator>(V2)) {
// Symmetric case to above.
- if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() &&
+ if (DecompGEP2.NWFlags->isInBounds() && DecompGEP1.VarIndices.empty() &&
V1Size.hasValue() && !V1Size.isScalable() &&
DecompGEP1.Offset.sle(-V1Size.getValue()) &&
isBaseOfObject(DecompGEP1.Base))
@@ -1233,6 +1232,32 @@ AliasResult BasicAAResult::aliasGEP(
}
}
+ // If the difference between pointers is Offset +<nuw> Indices (the variable
+ // indices all come from nuw GEPs) then we know that the addition does not
+ // wrap the pointer index type (add nuw) and the constant Offset is a lower
+ // bound on the distance between the pointers. We can then prove NoAlias via
+ // Offset u>= VLeftSize.
+ // + + +
+ // | BaseOffset | +<nuw> Indices |
+ // ---------------->|-------------------->|
+ // |-->VLeftSize | |-------> VRightSize
+ // LHS RHS
+ if (!DecompGEP1.VarIndices.empty() &&
+ llvm::all_of(DecompGEP1.VarIndices, [&](const VariableGEPIndex &V) {
+ return V.IsNegated == DecompGEP1.VarIndices.front().IsNegated;
+ })) {
+ APInt Off = DecompGEP1.Offset;
+ bool Swapped = Off.isNegative();
+ LocationSize VLeftSize = Swapped ? V1Size : V2Size;
+ DecomposedGEP &DecompRight = Swapped ? DecompGEP2 : DecompGEP1;
+
+ bool IndicesFromRight = DecompGEP1.VarIndices.front().IsNegated == Swapped;
+ if (IndicesFromRight && DecompRight.NWFlags->hasNoUnsignedWrap())
+ if (!VLeftSize.isScalable() && VLeftSize.hasValue() &&
+ Off.abs().uge(VLeftSize.getValue()))
+ return AliasResult::NoAlias;
+ }
+
// Bail on analysing scalable LocationSize
if (V1Size.isScalable() || V2Size.isScalable())
return AliasResult::MayAlias;
diff --git a/llvm/test/Analysis/BasicAA/gep-nuw-alias.ll b/llvm/test/Analysis/BasicAA/gep-nuw-alias.ll
new file mode 100644
index 0000000000000..79899d60640d6
--- /dev/null
+++ b/llvm/test/Analysis/BasicAA/gep-nuw-alias.ll
@@ -0,0 +1,142 @@
+; RUN: opt < %s -aa-pipeline=basic-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+; CHECK-LABEL: test_no_lower_bound
+;
+; CHECK-DAG: MayAlias: i32* %a, i32* %b
+define void @test_no_lower_bound(ptr %p, i64 %i) {
+ %a = getelementptr i8, ptr %p, i64 4
+ %b = getelementptr nuw i8, ptr %p, i64 %i
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+; CHECK-LABEL: test_lower_bound_lt_size
+;
+; CHECK-DAG: MayAlias: i32* %a, i32* %b
+define void @test_lower_bound_lt_size(ptr %p, i64 %i) {
+ %a = getelementptr i8, ptr %p
+ %add = getelementptr nuw i8, ptr %p, i64 2
+ %b = getelementptr nuw i8, ptr %add, i64 %i
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+; CHECK-LABEL: test_lower_bound_ge_size
+;
+; CHECK-DAG: NoAlias: i32* %a, i32* %b
+define void @test_lower_bound_ge_size(ptr %p, i64 %i) {
+ %a = getelementptr i8, ptr %p
+ %add = getelementptr nuw i8, ptr %p, i64 4
+ %b = getelementptr nuw i8, ptr %add, i64 %i
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+; CHECK-LABEL: test_not_all_nuw
+;
+; If part of the addressing is done with non-nuw GEPs, we can't use properties
+; implied by the last GEP with the whole offset. In this case, the calculation
+; of %add (%p + 4) could wrap the pointer index type, such that %add +<nuw> %i
+; could still alias with %p.
+;
+; CHECK-DAG: MayAlias: i32* %a, i32* %b
+define void @test_not_all_nuw(ptr %p, i64 %i) {
+ %a = getelementptr i8, ptr %p
+ %add = getelementptr i8, ptr %p, i64 4
+ %b = getelementptr nuw i8, ptr %add, i64 %i
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+; CHECK-LABEL: test_multi_step_not_all_nuw
+;
+; CHECK-DAG: MayAlias: i32* %a, i32* %b
+define void @test_multi_step_not_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
+ %a = getelementptr i8, ptr %p
+ %add = getelementptr i8, ptr %p, i64 4
+ %step1 = getelementptr i8, ptr %add, i64 %i
+ %step2 = getelementptr i8, ptr %step1, i64 %j
+ %b = getelementptr nuw i8, ptr %step2, i64 %k
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+; CHECK-LABEL: test_multi_step_all_nuw
+;
+; CHECK-DAG: NoAlias: i32* %a, i32* %b
+define void @test_multi_step_all_nuw(ptr %p, i64 %i, i64 %j, i64 %k) {
+ %a = getelementptr i8, ptr %p
+ %add = getelementptr nuw i8, ptr %p, i64 4
+ %step1 = getelementptr nuw i8, ptr %add, i64 %i
+ %step2 = getelementptr nuw i8, ptr %step1, i64 %j
+ %b = getelementptr nuw i8, ptr %step2, i64 %k
+
+ load i32, ptr %a
+ load i32, ptr %b
+
+ ret void
+}
+
+%struct = type { i64, [2 x i32], i64 }
+
+; CHECK-LABEL: test_struct_no_nuw
+;
+; The array access may alias with the struct elements before and after, because
+; we cannot prove that (%arr + %i) does not alias with the base pointer %p.
+;
+; CHECK-DAG: MayAlias: i32* %arrayidx, i64* %st
+; CHECK-DAG: NoAlias: i64* %after, i64* %st
+; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
+
+define void @test_struct_no_nuw(ptr %st, i64 %i) {
+ %arr = getelementptr i8, ptr %st, i64 8
+ %arrayidx = getelementptr [2 x i32], ptr %arr, i64 0, i64 %i
+ %after = getelementptr i8, ptr %st, i64 16
+
+ load i64, ptr %st
+ load i32, ptr %arrayidx
+ load i64, ptr %after
+
+ ret void
+}
+
+; CHECK-LABEL: test_struct_nuw
+;
+; We can prove that the array access does not alias with struct element before,
+; because we can prove that (%arr +<nuw> %i) does not wrap the pointer index
+; type (add nuw). The array access may still alias with the struct element
+; after, as the add nuw property does not preclude this.
+;
+; CHECK-DAG: NoAlias: i32* %arrayidx, i64* %st
+; CHECK-DAG: NoAlias: i64* %after, i64* %st
+; CHECK-DAG: MayAlias: i64* %after, i32* %arrayidx
+
+define void @test_struct_nuw(ptr %st, i64 %i) {
+ %arr = getelementptr nuw i8, ptr %st, i64 8
+ %arrayidx = getelementptr nuw [2 x i32], ptr %arr, i64 0, i64 %i
+ %after = getelementptr nuw i8, ptr %st, i64 16
+
+ load i64, ptr %st
+ load i32, ptr %arrayidx
+ load i64, ptr %after
+
+ ret void
+}
+
>From 8bbb95a4e185194821d72c6f6b567de81b626049 Mon Sep 17 00:00:00 2001
From: Hari Limaye <hari.limaye at arm.com>
Date: Mon, 15 Jul 2024 08:24:59 +0000
Subject: [PATCH 2/2] Reorder checks (Addressing review comment)
---
llvm/lib/Analysis/BasicAliasAnalysis.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 13e26fdc2ed29..233994b91e7b0 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -1253,7 +1253,7 @@ AliasResult BasicAAResult::aliasGEP(
bool IndicesFromRight = DecompGEP1.VarIndices.front().IsNegated == Swapped;
if (IndicesFromRight && DecompRight.NWFlags->hasNoUnsignedWrap())
- if (!VLeftSize.isScalable() && VLeftSize.hasValue() &&
+ if (VLeftSize.hasValue() && !VLeftSize.isScalable() &&
Off.abs().uge(VLeftSize.getValue()))
return AliasResult::NoAlias;
}
More information about the llvm-commits
mailing list