diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index 6eac30f19d785..72db28929c0c3 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -555,17 +555,17 @@ struct BasicAAResult::DecomposedGEP { APInt Offset; // Scaled variable (non-constant) indices. SmallVector VarIndices; - // Are all operations inbounds GEPs or non-indexing operations? - // (std::nullopt iff expression doesn't involve any geps) - std::optional InBounds; + // Nowrap flags common to all GEP operations involved in expression. + GEPNoWrapFlags NWFlags = GEPNoWrapFlags::all(); void dump() const { print(dbgs()); dbgs() << "\n"; } void print(raw_ostream &OS) const { - OS << "(DecomposedGEP Base=" << Base->getName() - << ", Offset=" << Offset + OS << ", inbounds=" << (NWFlags.isInBounds() ? "1" : "0") + << ", nuw=" << (NWFlags.hasNoUnsignedWrap() ? "1" : "0") + << "(DecomposedGEP Base=" << Base->getName() << ", Offset=" << Offset << ", VarIndices=["; for (size_t i = 0; i < VarIndices.size(); i++) { if (i != 0) @@ -644,12 +644,8 @@ 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. + Decomposed.NWFlags &= GEPOp->getNoWrapFlags(); assert(GEPOp->getSourceElementType()->isSized() && "GEP must be sized"); @@ -1112,6 +1108,13 @@ AliasResult BasicAAResult::aliasGEP( if (DecompGEP1.Base == GEP1 && DecompGEP2.Base == V2) return AliasResult::MayAlias; + // Swap GEP1 and GEP2 if GEP2 has more variable indices. + if (DecompGEP1.VarIndices.size() < DecompGEP2.VarIndices.size()) { + std::swap(DecompGEP1, DecompGEP2); + std::swap(V1Size, V2Size); + std::swap(UnderlyingV1, UnderlyingV2); + } + // Subtract the GEP2 pointer from the GEP1 pointer to find out their // symbolic difference. subtractDecomposedGEPs(DecompGEP1, DecompGEP2, AAQI); @@ -1120,20 +1123,19 @@ 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)) return AliasResult::NoAlias; - if (isa(V2)) { - // Symmetric case to above. - if (*DecompGEP2.InBounds && DecompGEP1.VarIndices.empty() && - V1Size.hasValue() && !V1Size.isScalable() && - DecompGEP1.Offset.sle(-V1Size.getValue()) && - isBaseOfObject(DecompGEP1.Base)) - return AliasResult::NoAlias; - } + // Symmetric case to above. + if (DecompGEP2.NWFlags.isInBounds() && DecompGEP1.VarIndices.empty() && + V1Size.hasValue() && !V1Size.isScalable() && + DecompGEP1.Offset.sle(-V1Size.getValue()) && + isBaseOfObject(DecompGEP1.Base)) + return AliasResult::NoAlias; // For GEPs with identical offsets, we can preserve the size and AAInfo // when performing the alias check on the underlying objects. @@ -1239,6 +1241,20 @@ AliasResult BasicAAResult::aliasGEP( } } + // If the difference between pointers is Offset + Indices 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 | + Indices | + // ---------------->|-------------------->| + // |-->V2Size | |-------> V1Size + // LHS RHS + if (!DecompGEP1.VarIndices.empty() && + DecompGEP1.NWFlags.hasNoUnsignedWrap() && V2Size.hasValue() && + !V2Size.isScalable() && DecompGEP1.Offset.uge(V2Size.getValue())) + return AliasResult::NoAlias; + // Bail on analysing scalable LocationSize if (V1Size.isScalable() || V2Size.isScalable()) return AliasResult::MayAlias; @@ -1843,6 +1859,11 @@ bool BasicAAResult::isValueEqualInPotentialCycles(const Value *V, void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP, const DecomposedGEP &SrcGEP, const AAQueryInfo &AAQI) { + // Drop nuw flag from GEP if subtraction of constant offsets overflows in an + // unsigned sense. + if (DestGEP.Offset.ult(SrcGEP.Offset)) + DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap(); + DestGEP.Offset -= SrcGEP.Offset; for (const VariableGEPIndex &Src : SrcGEP.VarIndices) { // Find V in Dest. This is N^2, but pointer indices almost never have more @@ -1865,6 +1886,11 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP, // If we found it, subtract off Scale V's from the entry in Dest. If it // goes to zero, remove the entry. if (Dest.Scale != Src.Scale) { + // Drop nuw flag from GEP if subtraction of V's Scale overflows in an + // unsigned sense. + if (Dest.Scale.ult(Src.Scale)) + DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap(); + Dest.Scale -= Src.Scale; Dest.IsNSW = false; } else { @@ -1879,6 +1905,9 @@ void BasicAAResult::subtractDecomposedGEPs(DecomposedGEP &DestGEP, VariableGEPIndex Entry = {Src.Val, Src.Scale, Src.CxtI, Src.IsNSW, /* IsNegated */ true}; DestGEP.VarIndices.push_back(Entry); + + // Drop nuw flag when we have unconsumed variable indices from SrcGEP. + DestGEP.NWFlags = DestGEP.NWFlags.withoutNoUnsignedWrap(); } } } 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..b80a457f85176 --- /dev/null +++ b/llvm/test/Analysis/BasicAA/gep-nuw-alias.ll @@ -0,0 +1,214 @@ +; RUN: opt < %s -aa-pipeline=basic-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s + +; 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 + %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 + %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 +} + +; CHECK-LABEL: constant_offset_overflow +; +; If subtraction of constant offsets could overflow in an unsigned sense, we +; cannot prove the lower bound between the GEPs and so they may still alias. +; +; CHECK-DAG: MayAlias: i32* %a, i32* %b + +define void @constant_offset_overflow(ptr %p, i64 %i) { + %a = getelementptr i8, ptr %p, i64 -8 + %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: equal_var_idx_noalias +; +; If GEPs have equal variable indices, we can prove NoAlias when the Scale of +; the RHS GEP is greater, as in this scenario the constant lower bound holds. +; +; CHECK-DAG: NoAlias: i32* %a, i32* %b + +define void @equal_var_idx_noalias(ptr %p, i64 %i) { + %a = getelementptr i8, ptr %p, i64 %i + + %add = getelementptr nuw i8, ptr %p, i64 4 + %b = getelementptr nuw i16, ptr %add, i64 %i + + load i32, ptr %a + load i32, ptr %b + + ret void +} + +; CHECK-LABEL: equal_var_idx_alias +; +; If GEPs have equal variable indices, we cannot prove NoAlias when the Scale of +; the RHS GEP is ult Scale of the LHS GEP. +; +; CHECK-DAG: MayAlias: i32* %a, i32* %b + +define void @equal_var_idx_alias(ptr %p, i64 %i) { + %a = getelementptr i32, ptr %p, i64 %i + + %add = getelementptr nuw i8, ptr %p, i64 4 + %b = getelementptr nuw i16, ptr %add, i64 %i + + load i32, ptr %b + load i32, ptr %a + + ret void +} + +; CHECK-LABEL: both_var_idx +; +; If the RHS GEP has unmatched variable indices, we cannot prove a constant +; lower bound between GEPs. +; +; CHECK-DAG: MayAlias: i32* %a, i32* %b + +define void @both_var_idx(ptr %p, i64 %i, i64 %j) { + %a = getelementptr i8, ptr %p, i64 %i + + %add = getelementptr nuw i8, ptr %p, i64 4 + %b = getelementptr nuw i8, ptr %add, i64 %j + + load i32, ptr %a + load i32, ptr %b + + ret void +}