diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index 878b7e7a1fb3b..a27fcda9fc53f 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -585,7 +585,8 @@ 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 AliasResult aliasErrno(const MemoryLocation &Loc, + const CallBase *Call); LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, @@ -748,7 +749,7 @@ class LLVM_ABI AAResults::Concept { /// Returns an AliasResult indicating whether a specific memory location /// aliases errno. virtual AliasResult aliasErrno(const MemoryLocation &Loc, - const Module *M) = 0; + const CallBase *Call) = 0; /// @} //===--------------------------------------------------------------------===// @@ -811,8 +812,9 @@ 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); + AliasResult aliasErrno(const MemoryLocation &Loc, + const CallBase *Call) override { + return Result.aliasErrno(Loc, Call); } ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI, @@ -870,7 +872,7 @@ class AAResultBase { return AliasResult::MayAlias; } - AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) { + AliasResult aliasErrno(const MemoryLocation &Loc, const CallBase *Call) { return AliasResult::MayAlias; } diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h index 31875e59207b3..911646ec2b11b 100644 --- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h @@ -73,7 +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 AliasResult aliasErrno(const MemoryLocation &Loc, + const CallBase *Call); LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h index 0f98af69f12c6..f96c4f7950204 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -89,6 +89,7 @@ class TargetLibraryInfoImpl { #include "llvm/Analysis/TargetLibraryInfo.inc" bool ShouldExtI32Param, ShouldExtI32Return, ShouldSignExtI32Param, ShouldSignExtI32Return; unsigned SizeOfInt; + bool IsErrnoFunctionCall; enum AvailabilityState { StandardName = 3, // (memset to all ones) @@ -256,6 +257,8 @@ class TargetLibraryInfoImpl { /// conventions. LLVM_ABI static bool isCallingConvCCompatible(CallBase *CI); LLVM_ABI static bool isCallingConvCCompatible(Function *Callee); + + bool isErrnoFunctionCall() const { return IsErrnoFunctionCall; } }; /// Provides information about what library functions are available for @@ -601,6 +604,10 @@ class TargetLibraryInfo { bool isKnownVectorFunctionInLibrary(StringRef F) const { return this->isFunctionVectorizable(F); } + + /// Returns whether `errno` is defined as a function call on known + /// environments. + bool isErrnoFunctionCall() const { return Impl->isErrnoFunctionCall(); } }; /// Analysis pass providing the \c TargetLibraryInfo. diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h index 38f9fc718824f..360fff2f4677c 100644 --- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h @@ -50,7 +50,8 @@ 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 AliasResult aliasErrno(const MemoryLocation &Loc, + const CallBase *Call); 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 26a560252d9aa..eed708f43b44b 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -148,11 +148,12 @@ AliasResult AAResults::alias(const MemoryLocation &LocA, return Result; } -AliasResult AAResults::aliasErrno(const MemoryLocation &Loc, const Module *M) { +AliasResult AAResults::aliasErrno(const MemoryLocation &Loc, + const CallBase *Call) { AliasResult Result = AliasResult::MayAlias; for (const auto &AA : AAs) { - Result = AA->aliasErrno(Loc, M); + Result = AA->aliasErrno(Loc, Call); if (Result != AliasResult::MayAlias) break; } diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index f812809e5e0b5..3ea14d87b6aa7 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -1001,7 +1001,7 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, // Refine accesses to errno memory. if ((ErrnoMR | Result) != Result) { - if (AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) { + if (AAQI.AAR.aliasErrno(Loc, Call) != AliasResult::NoAlias) { // Exclusion conditions do not hold, this memory location may alias errno. Result |= ErrnoMR; } @@ -1862,7 +1862,7 @@ AliasResult BasicAAResult::aliasCheckRecursive( } AliasResult BasicAAResult::aliasErrno(const MemoryLocation &Loc, - const Module *M) { + const CallBase *Call) { // 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. @@ -1870,8 +1870,23 @@ AliasResult BasicAAResult::aliasErrno(const MemoryLocation &Loc, Loc.Size.getValue().getKnownMinValue() * 8 > TLI.getIntSize()) return AliasResult::NoAlias; - if (isIdentifiedFunctionLocal(getUnderlyingObject(Loc.Ptr))) + const Value *Object = getUnderlyingObject(Loc.Ptr); + if (isIdentifiedFunctionLocal(Object)) return AliasResult::NoAlias; + + if (auto *GV = dyn_cast(Object)) { + // Errno cannot alias internal/private globals. + if (GV->hasLocalLinkage()) + return AliasResult::NoAlias; + + // Neither can errno alias globals where environments define it as a + // function call, unless targeting freestanding environments, for which we + // do not make any assumptions. + bool IsFreestanding = Call->getFunction()->hasFnAttribute("no-builtins"); + if (!IsFreestanding && TLI.isErrnoFunctionCall()) + return AliasResult::NoAlias; + } + return AliasResult::MayAlias; } diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp index 51b1f5874bcb6..d31861972eccb 100644 --- a/llvm/lib/Analysis/TargetLibraryInfo.cpp +++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp @@ -905,8 +905,19 @@ static void initialize(TargetLibraryInfoImpl &TLI, const Triple &T, initializeLibCalls(TLI, T, StandardNames, VecLib); } +static bool initializeIsErrnoFunctionCall(const Triple &T) { + // Assume errno is implemented as a function call on the following + // known environments. + // TODO: Could refine them. + return T.isOSDarwin() || T.isAndroid() || T.isGNUEnvironment() || + T.isMusl() || T.getEnvironment() == Triple::LLVM || + T.getEnvironment() == Triple::Mlibc || + T.getEnvironment() == Triple::MSVC; +} + TargetLibraryInfoImpl::TargetLibraryInfoImpl(const Triple &T, - VectorLibrary VecLib) { + VectorLibrary VecLib) + : IsErrnoFunctionCall(initializeIsErrnoFunctionCall(T)) { // Default to everything being available. memset(AvailableArray, -1, sizeof(AvailableArray)); @@ -918,7 +929,7 @@ TargetLibraryInfoImpl::TargetLibraryInfoImpl(const TargetLibraryInfoImpl &TLI) ShouldExtI32Return(TLI.ShouldExtI32Return), ShouldSignExtI32Param(TLI.ShouldSignExtI32Param), ShouldSignExtI32Return(TLI.ShouldSignExtI32Return), - SizeOfInt(TLI.SizeOfInt) { + SizeOfInt(TLI.SizeOfInt), IsErrnoFunctionCall(TLI.IsErrnoFunctionCall) { memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray)); VectorDescs = TLI.VectorDescs; ScalarDescs = TLI.ScalarDescs; @@ -930,7 +941,7 @@ TargetLibraryInfoImpl::TargetLibraryInfoImpl(TargetLibraryInfoImpl &&TLI) ShouldExtI32Return(TLI.ShouldExtI32Return), ShouldSignExtI32Param(TLI.ShouldSignExtI32Param), ShouldSignExtI32Return(TLI.ShouldSignExtI32Return), - SizeOfInt(TLI.SizeOfInt) { + SizeOfInt(TLI.SizeOfInt), IsErrnoFunctionCall(TLI.IsErrnoFunctionCall) { std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray), AvailableArray); VectorDescs = TLI.VectorDescs; @@ -944,6 +955,7 @@ TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(const TargetLibraryInfoI ShouldSignExtI32Param = TLI.ShouldSignExtI32Param; ShouldSignExtI32Return = TLI.ShouldSignExtI32Return; SizeOfInt = TLI.SizeOfInt; + IsErrnoFunctionCall = TLI.IsErrnoFunctionCall; memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray)); return *this; } @@ -955,6 +967,7 @@ TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(TargetLibraryInfoImpl && ShouldSignExtI32Param = TLI.ShouldSignExtI32Param; ShouldSignExtI32Return = TLI.ShouldSignExtI32Return; SizeOfInt = TLI.SizeOfInt; + IsErrnoFunctionCall = TLI.IsErrnoFunctionCall; std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray), AvailableArray); return *this; diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp index c7d263a75b33a..e84cfbcda5ff7 100644 --- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -387,7 +387,7 @@ AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA, } AliasResult TypeBasedAAResult::aliasErrno(const MemoryLocation &Loc, - const Module *M) { + const CallBase *Call) { if (!shouldUseTBAA()) return AliasResult::MayAlias; @@ -397,7 +397,8 @@ AliasResult TypeBasedAAResult::aliasErrno(const MemoryLocation &Loc, // 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"); + const auto *ErrnoTBAAMD = + Call->getModule()->getNamedMetadata("llvm.errno.tbaa"); if (!ErrnoTBAAMD || any_of(ErrnoTBAAMD->operands(), [&](const auto *Node) { return Aliases(N, Node); })) diff --git a/llvm/test/Transforms/InstCombine/may-alias-errno.ll b/llvm/test/Transforms/InstCombine/may-alias-errno.ll index 40fab8024b362..a7cc23c867fb2 100644 --- a/llvm/test/Transforms/InstCombine/may-alias-errno.ll +++ b/llvm/test/Transforms/InstCombine/may-alias-errno.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; RUN: opt -S -passes=instcombine < %s | FileCheck %s +; RUN: opt -S -passes=instcombine -mtriple=aarch64-linux-gnu < %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. @@ -149,6 +149,61 @@ entry: ret %v } +@internal_g = internal global i32 0 + +; errno cannot alias an internal global variable, can do constant store-to-load forwarding. +define i32 @does_not_alias_errno_internal_global(float %f) { +; CHECK-LABEL: define i32 @does_not_alias_errno_internal_global( +; CHECK-SAME: float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store i32 42, ptr @internal_g, align 4 +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: ret i32 42 +; +entry: + store i32 42, ptr @internal_g, align 4 + %call = call float @sinf(float %f) + %v = load i32, ptr @internal_g, align 4 + ret i32 %v +} + +@external_g = external global i32 + +; errno cannot alias an external global variable in GNU environment, +; can do constant store-to-load forwarding. +define i32 @does_not_alias_errno_external_known_environment(float %f) { +; CHECK-LABEL: define i32 @does_not_alias_errno_external_known_environment( +; CHECK-SAME: float [[F:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store i32 42, ptr @external_g, align 4 +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: ret i32 42 +; +entry: + store i32 42, ptr @external_g, align 4 + %call = call float @sinf(float %f) + %v = load i32, ptr @external_g, align 4 + ret i32 %v +} + +; Do not make any assumptions when may be targeting freestanding +; environments (which implies nobuiltin). +define i32 @may_alias_errno_nobuiltin(float %f) "no-builtins" { +; CHECK-LABEL: define i32 @may_alias_errno_nobuiltin( +; CHECK-SAME: float [[F:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: store i32 42, ptr @external_g, align 4 +; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]]) +; CHECK-NEXT: [[V:%.*]] = load i32, ptr @external_g, align 4 +; CHECK-NEXT: ret i32 [[V]] +; +entry: + store i32 42, ptr @external_g, align 4 + %call = call float @sinf(float %f) + %v = load i32, ptr @external_g, align 4 + ret i32 %v +} + declare float @sinf(float) memory(errnomem: write) declare float @read_errno(ptr) memory(argmem: write, errnomem: read) declare void @escape(ptr %p) diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp index 3029f10b1bb29..18463d862350c 100644 --- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp +++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp @@ -703,6 +703,25 @@ TEST_F(TargetLibraryInfoTest, ValidProto) { } } +TEST_F(TargetLibraryInfoTest, IsErrnoGlobal) { + using TLII = TargetLibraryInfoImpl; + + // Errno is defined as a function call on the following environments. + EXPECT_TRUE(TLII(Triple("arm64-apple-macosx")).isErrnoFunctionCall()); + EXPECT_TRUE(TLII(Triple("arm--linux-androideabi")).isErrnoFunctionCall()); + EXPECT_TRUE( + TLII(Triple("armv7-unknown-freebsd-gnueabihf")).isErrnoFunctionCall()); + EXPECT_TRUE(TLII(Triple("riscv32-unknown-linux-musl")).isErrnoFunctionCall()); + EXPECT_TRUE(TLII(Triple("x86_64-pc-windows-msvc")).isErrnoFunctionCall()); + EXPECT_TRUE(TLII(Triple("x86_64-unknown-linux-gnu")).isErrnoFunctionCall()); + + // Unknown. + EXPECT_FALSE(TLII(Triple("aarch64-unknown-unknown")).isErrnoFunctionCall()); + EXPECT_FALSE(TLII(Triple("arm-none-eabi")).isErrnoFunctionCall()); + EXPECT_FALSE(TLII(Triple("powerpc-none-none")).isErrnoFunctionCall()); + EXPECT_FALSE(TLII(Triple("x86_64-pc-linux")).isErrnoFunctionCall()); +} + namespace { /// Creates TLI for AArch64 and uses it to get the LibFunc names for the given