diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index b7d1251aeb723..1681079054b8b 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -585,6 +585,7 @@ class AAResults { LLVM_ABI AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AAQueryInfo &AAQI, const Instruction *CtxI = nullptr); + LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M); LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, @@ -744,6 +745,11 @@ class LLVM_ABI AAResults::Concept { const MemoryLocation &LocB, AAQueryInfo &AAQI, const Instruction *CtxI) = 0; + /// Returns an AliasResult indicating whether a specific memory location + /// aliases errno. + virtual AliasResult aliasErrno(const MemoryLocation &Loc, + const Module *M) = 0; + /// @} //===--------------------------------------------------------------------===// /// \name Simple mod/ref information @@ -805,6 +811,10 @@ template class AAResults::Model final : public Concept { return Result.alias(LocA, LocB, AAQI, CtxI); } + AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) override { + return Result.aliasErrno(Loc, M); + } + ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, bool IgnoreLocals) override { return Result.getModRefInfoMask(Loc, AAQI, IgnoreLocals); @@ -860,6 +870,10 @@ class AAResultBase { return AliasResult::MayAlias; } + AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) { + return AliasResult::MayAlias; + } + ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, bool IgnoreLocals) { return ModRefInfo::ModRef; diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h index 6f37a086e323b..31875e59207b3 100644 --- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h @@ -73,6 +73,8 @@ class BasicAAResult : public AAResultBase { const MemoryLocation &LocB, AAQueryInfo &AAQI, const Instruction *CtxI); + LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M); + LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, AAQueryInfo &AAQI); diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h index 77edbe8527aae..38f9fc718824f 100644 --- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h @@ -50,6 +50,7 @@ class TypeBasedAAResult : public AAResultBase { LLVM_ABI AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AAQueryInfo &AAQI, const Instruction *CtxI); + LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M); LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, bool IgnoreLocals); diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index 3ec009ca4adde..f2dc25fa5dbf5 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -148,6 +148,18 @@ AliasResult AAResults::alias(const MemoryLocation &LocA, return Result; } +AliasResult AAResults::aliasErrno(const MemoryLocation &Loc, const Module *M) { + AliasResult Result = AliasResult::MayAlias; + + for (const auto &AA : AAs) { + Result = AA->aliasErrno(Loc, M); + if (Result != AliasResult::MayAlias) + break; + } + + return Result; +} + ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc, bool IgnoreLocals) { SimpleAAQueryInfo AAQIP(*this); diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index de37c391cf254..edc481424a6d0 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -951,7 +951,8 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, return ModRefInfo::NoModRef; ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem); - ModRefInfo OtherMR = ME.getWithoutLoc(IRMemLocation::ArgMem).getModRef(); + ModRefInfo ErrnoMR = ME.getModRef(IRMemLocation::ErrnoMem); + ModRefInfo OtherMR = ME.getModRef(IRMemLocation::Other); // An identified function-local object that does not escape can only be // accessed via call arguments. Reduce OtherMR (which includes accesses to @@ -997,6 +998,15 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, } ModRefInfo Result = ArgMR | OtherMR; + + // Refine accesses to errno memory. + if ((ErrnoMR | Result) != Result) { + if (AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) { + // Exclusion conditions do not hold, this memory location may alias errno. + Result |= ErrnoMR; + } + } + if (!isModAndRefSet(Result)) return Result; @@ -1851,6 +1861,19 @@ AliasResult BasicAAResult::aliasCheckRecursive( return AliasResult::MayAlias; } +AliasResult BasicAAResult::aliasErrno(const MemoryLocation &Loc, + const Module *M) { + // There cannot be any alias with errno if the given memory location is an + // identified function-local object, or the size of the memory access is + // larger than the integer size. + if (Loc.Size.hasValue() && Loc.Size.getValue() * 8 > TLI.getIntSize()) + return AliasResult::NoAlias; + + if (isIdentifiedFunctionLocal(getUnderlyingObject(Loc.Ptr))) + return AliasResult::NoAlias; + return AliasResult::MayAlias; +} + /// Check whether two Values can be considered equivalent. /// /// If the values may come from different cycle iterations, this will also diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp index 7025b8354564a..c7d263a75b33a 100644 --- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -115,6 +115,7 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" @@ -385,6 +386,25 @@ AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA, return AliasResult::NoAlias; } +AliasResult TypeBasedAAResult::aliasErrno(const MemoryLocation &Loc, + const Module *M) { + if (!shouldUseTBAA()) + return AliasResult::MayAlias; + + const auto *N = Loc.AATags.TBAA; + if (!N) + return AliasResult::MayAlias; + + // There cannot be any alias with errno if TBAA proves the given memory + // location does not alias errno. + const auto *ErrnoTBAAMD = M->getNamedMetadata("llvm.errno.tbaa"); + if (!ErrnoTBAAMD || any_of(ErrnoTBAAMD->operands(), [&](const auto *Node) { + return Aliases(N, Node); + })) + return AliasResult::MayAlias; + return AliasResult::NoAlias; +} + ModRefInfo TypeBasedAAResult::getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, bool IgnoreLocals) { diff --git a/llvm/test/Transforms/InstCombine/may-alias-errno.ll b/llvm/test/Transforms/InstCombine/may-alias-errno.ll new file mode 100644 index 0000000000000..aebc8c9e15c04 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/may-alias-errno.ll @@ -0,0 +1,133 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +; sinf clobbering errno, but %p cannot alias errno per C/C++ strict aliasing rules via TBAA. +; Can do constant store-to-load forwarding. +define float @does_not_alias_errno(ptr %p, float %f) { +; CHECK-LABEL: define float @does_not_alias_errno( +; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store float 0.000000e+00, ptr [[P]], align 4, !tbaa [[TBAA4:![0-9]+]] +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: ret float 0.000000e+00 +; +entry: + store float 0.000000e+00, ptr %p, align 4, !tbaa !4 + %call = call float @sinf(float %f) + %0 = load float, ptr %p, align 4, !tbaa !4 + ret float %0 +} + +; sinf clobbering errno, but %p is alloca memory, wich can never aliases errno. +; Can do constant store-to-load forwarding. +define float @does_not_alias_errno_2(float %f) { +; CHECK-LABEL: define float @does_not_alias_errno_2( +; CHECK-SAME: float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[P:%.*]] = alloca float, align 4 +; CHECK-NEXT: call void @escape(ptr nonnull [[P]]) +; CHECK-NEXT: store float 0.000000e+00, ptr [[P]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: ret float 0.000000e+00 +; +entry: + %p = alloca float + call void @escape(ptr %p) + store float 0.0, ptr %p + call float @sinf(float %f) + %v = load float, ptr %p + ret float %v +} + +; sinf clobbering errno, but %p is memory accessed w/ size larger than errno. +; Can do constant store-to-load forwarding. +define double @does_not_alias_errno_3(ptr %p, float %f) { +; CHECK-LABEL: define double @does_not_alias_errno_3( +; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @escape(ptr [[P]]) +; CHECK-NEXT: store double 0.000000e+00, ptr [[P]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: ret double 0.000000e+00 +; +entry: + call void @escape(ptr %p) + store double 0.0, ptr %p + call float @sinf(float %f) + %v = load double, ptr %p + ret double %v +} + +; %p may alias errno, but read_errno does not clobber errno. +; Can do constant store-to-load forwarding. +define float @may_alias_errno_does_not_clobber(ptr %p, ptr byval(i8) %q) { +; CHECK-LABEL: define float @may_alias_errno_does_not_clobber( +; CHECK-SAME: ptr [[P:%.*]], ptr byval(i8) [[Q:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store float 0.000000e+00, ptr [[P]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call float @read_errno(ptr nonnull [[Q]]) +; CHECK-NEXT: ret float 0.000000e+00 +; +entry: + store float 0.000000e+00, ptr %p, align 4 + %call = call float @read_errno(ptr %q) + %0 = load float, ptr %p, align 4 + ret float %0 +} + +; sinf clobbering errno, unknown TBAA info, %p may alias errno. +; Cannot do constant store-to-load forwarding. +define float @may_alias_errno(ptr %p, float %f) { +; CHECK-LABEL: define float @may_alias_errno( +; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store float 0.000000e+00, ptr [[P]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[P]], align 4 +; CHECK-NEXT: ret float [[TMP0]] +; +entry: + store float 0.000000e+00, ptr %p, align 4 + %call = call float @sinf(float %f) + %0 = load float, ptr %p, align 4 + ret float %0 +} + +; sinf clobbering errno, %p, a integer pointer, may alias errno. +; Cannot do constant store-to-load forwarding. +define i32 @may_alias_errno_2(ptr %p, float %f) { +; CHECK-LABEL: define i32 @may_alias_errno_2( +; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store i32 0, ptr [[P]], align 4, !tbaa [[TBAA0:![0-9]+]] +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4, !tbaa [[TBAA0]] +; CHECK-NEXT: ret i32 [[TMP0]] +; +entry: + store i32 0, ptr %p, align 4, !tbaa !0 + %call = call float @sinf(float %f) + %0 = load i32, ptr %p, align 4, !tbaa !0 + ret i32 %0 +} + +declare float @sinf(float) memory(errnomem: write) +declare float @read_errno(ptr) memory(argmem: write, errnomem: read) +declare void @escape(ptr %p) + +!llvm.errno.tbaa = !{!0} + +!0 = !{!1, !1, i64 0} +!1 = !{!"int", !2, i64 0} +!2 = !{!"omnipotent char", !3, i64 0} +!3 = !{!"Simple C/C++ TBAA"} +!4 = !{!5, !5, i64 0} +!5 = !{!"float", !2, i64 0} +;. +; CHECK: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0} +; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]], i64 0} +; CHECK: [[META2]] = !{!"omnipotent char", [[META3:![0-9]+]], i64 0} +; CHECK: [[META3]] = !{!"Simple C/C++ TBAA"} +; CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +; CHECK: [[META5]] = !{!"float", [[META2]], i64 0} +;.