Skip to content
7 changes: 7 additions & 0 deletions llvm/include/llvm/Analysis/TargetLibraryInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
20 changes: 18 additions & 2 deletions llvm/lib/Analysis/BasicAliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,10 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,

// Refine accesses to errno memory.
if ((ErrnoMR | Result) != Result) {
if (AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) {
// Do not make any assumptions about errno on freestanding environments.
bool IsFreestanding = Call->getFunction()->hasFnAttribute("no-builtins");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check belongs here. I'd expect freestanding to only be relevant for isErrnoFunctionCall.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rework of aliasErrno signatures sort of prevented me from checking it closer to isErrnoFunctionCall, though agree it's best suitable there.

if (IsFreestanding ||
AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) {
// Exclusion conditions do not hold, this memory location may alias errno.
Result |= ErrnoMR;
}
Expand Down Expand Up @@ -1870,8 +1873,21 @@ 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<GlobalVariable>(Object)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need additional logic for GlobalAlias? Or has it been handled by other places?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't believe so - if a GlobalAlias turns out to be a GlobalVariable, getUnderlyingObject() should tell us that.

// 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.
if (TLI.isErrnoFunctionCall())
return AliasResult::NoAlias;
}

return AliasResult::MayAlias;
}

Expand Down
19 changes: 16 additions & 3 deletions llvm/lib/Analysis/TargetLibraryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
57 changes: 56 additions & 1 deletion llvm/test/Transforms/InstCombine/may-alias-errno.ll
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -149,6 +149,61 @@ entry:
ret <vscale x 4 x i32> %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 @internal_g, align 4
; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]])
; CHECK-NEXT: [[V:%.*]] = load i32, ptr @internal_g, align 4
; CHECK-NEXT: ret i32 [[V]]
;
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
}

declare float @sinf(float) memory(errnomem: write)
declare float @read_errno(ptr) memory(argmem: write, errnomem: read)
declare void @escape(ptr %p)
Expand Down
19 changes: 19 additions & 0 deletions llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading