Skip to content
14 changes: 14 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 IsErrnoThreadLocal;

enum AvailabilityState {
StandardName = 3, // (memset to all ones)
Expand Down Expand Up @@ -256,6 +257,9 @@ class TargetLibraryInfoImpl {
/// conventions.
LLVM_ABI static bool isCallingConvCCompatible(CallBase *CI);
LLVM_ABI static bool isCallingConvCCompatible(Function *Callee);

LLVM_ABI bool mayBeErrnoGlobal(const GlobalVariable *GV) const;
LLVM_ABI bool isErrnoThreadLocal() const { return IsErrnoThreadLocal; }
};

/// Provides information about what library functions are available for
Expand Down Expand Up @@ -601,6 +605,16 @@ class TargetLibraryInfo {
bool isKnownVectorFunctionInLibrary(StringRef F) const {
return this->isFunctionVectorizable(F);
}

/// Returns whether the name of the global variable is associated to the
/// representation of the errno storage. Returns true if could not determined.
bool mayBeErrnoGlobal(const GlobalVariable *GV) const {
return Impl->mayBeErrnoGlobal(GV);
}

/// Returns whether the target C library implements errno as a thread-local
/// variable, which is de facto the standard for non-baremetal targets.
bool isErrnoThreadLocal() const { return Impl->isErrnoThreadLocal(); }
};

/// Analysis pass providing the \c TargetLibraryInfo.
Expand Down
18 changes: 17 additions & 1 deletion llvm/lib/Analysis/BasicAliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1870,8 +1870,24 @@ 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 it alias external globals which are known not to represent
// errno.
if (GV->hasExternalLinkage() && !TLI.mayBeErrnoGlobal(GV))
return AliasResult::NoAlias;

// A non-thread-local global cannot alias a thread-local errno.
if (!GV->isThreadLocal() && TLI.isErrnoThreadLocal())
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder whether this logic shouldn't be inside mayBeErrnoGlobal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing. I wonder though if it is still reasonable to keep it separate and have IsErrnoThreadLocal.

return AliasResult::NoAlias;
}
return AliasResult::MayAlias;
}

Expand Down
24 changes: 21 additions & 3 deletions llvm/lib/Analysis/TargetLibraryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,15 @@ static void initialize(TargetLibraryInfoImpl &TLI, const Triple &T,
initializeLibCalls(TLI, T, StandardNames, VecLib);
}

static bool initializeIsErrnoThreadLocal(const Triple &T) {
// Assume errno has thread-local storage for non-baremetal environments.
// TODO: Could refine known OSes.
return T.isOSDarwin() || T.isOSFreeBSD() || T.isOSLinux() || T.isOSWindows();
}

TargetLibraryInfoImpl::TargetLibraryInfoImpl(const Triple &T,
VectorLibrary VecLib) {
VectorLibrary VecLib)
: IsErrnoThreadLocal(initializeIsErrnoThreadLocal(T)) {
// Default to everything being available.
memset(AvailableArray, -1, sizeof(AvailableArray));

Expand All @@ -918,7 +925,7 @@ TargetLibraryInfoImpl::TargetLibraryInfoImpl(const TargetLibraryInfoImpl &TLI)
ShouldExtI32Return(TLI.ShouldExtI32Return),
ShouldSignExtI32Param(TLI.ShouldSignExtI32Param),
ShouldSignExtI32Return(TLI.ShouldSignExtI32Return),
SizeOfInt(TLI.SizeOfInt) {
SizeOfInt(TLI.SizeOfInt), IsErrnoThreadLocal(TLI.IsErrnoThreadLocal) {
memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray));
VectorDescs = TLI.VectorDescs;
ScalarDescs = TLI.ScalarDescs;
Expand All @@ -930,7 +937,7 @@ TargetLibraryInfoImpl::TargetLibraryInfoImpl(TargetLibraryInfoImpl &&TLI)
ShouldExtI32Return(TLI.ShouldExtI32Return),
ShouldSignExtI32Param(TLI.ShouldSignExtI32Param),
ShouldSignExtI32Return(TLI.ShouldSignExtI32Return),
SizeOfInt(TLI.SizeOfInt) {
SizeOfInt(TLI.SizeOfInt), IsErrnoThreadLocal(TLI.IsErrnoThreadLocal) {
std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray),
AvailableArray);
VectorDescs = TLI.VectorDescs;
Expand All @@ -944,6 +951,7 @@ TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(const TargetLibraryInfoI
ShouldSignExtI32Param = TLI.ShouldSignExtI32Param;
ShouldSignExtI32Return = TLI.ShouldSignExtI32Return;
SizeOfInt = TLI.SizeOfInt;
IsErrnoThreadLocal = TLI.IsErrnoThreadLocal;
memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray));
return *this;
}
Expand All @@ -955,6 +963,7 @@ TargetLibraryInfoImpl &TargetLibraryInfoImpl::operator=(TargetLibraryInfoImpl &&
ShouldSignExtI32Param = TLI.ShouldSignExtI32Param;
ShouldSignExtI32Return = TLI.ShouldSignExtI32Return;
SizeOfInt = TLI.SizeOfInt;
IsErrnoThreadLocal = TLI.IsErrnoThreadLocal;
std::move(std::begin(TLI.AvailableArray), std::end(TLI.AvailableArray),
AvailableArray);
return *this;
Expand Down Expand Up @@ -1468,6 +1477,15 @@ unsigned TargetLibraryInfoImpl::getSizeTSize(const Module &M) const {
return M.getDataLayout().getIndexSizeInBits(/*AddressSpace=*/0);
}

bool TargetLibraryInfoImpl::mayBeErrnoGlobal(const GlobalVariable *GV) const {
// TODO: Should consider C++ mangled names for errno.
static constexpr auto ErrnoGlobalNames = {"errno", "__libc_errno"};
assert(GV && "Expecting existing GlobalVariable.");
if (GV->hasName() && !is_contained(ErrnoGlobalNames, GV->getName()))
return false;
return true;
}

TargetLibraryInfoWrapperPass::TargetLibraryInfoWrapperPass()
: ImmutablePass(ID), TLA(TargetLibraryInfoImpl(Triple())) {}

Expand Down
56 changes: 56 additions & 0 deletions llvm/test/Transforms/InstCombine/may-alias-errno.ll
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,62 @@ 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 unless known to be errno,
; can do constant store-to-load forwarding.
define i32 @does_not_alias_errno_external_global_known_by_name(float %f) {
; CHECK-LABEL: define i32 @does_not_alias_errno_external_global_known_by_name(
; 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
}

@errno = external global i32

; errno global variable does alias errno, cannot do constant store-to-load forwarding.
define i32 @does_alias_errno_global(float %f) {
; CHECK-LABEL: define i32 @does_alias_errno_global(
; CHECK-SAME: float [[F:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: store i32 42, ptr @errno, align 4
; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]])
; CHECK-NEXT: [[V:%.*]] = load i32, ptr @errno, align 4
; CHECK-NEXT: ret i32 [[V]]
;
entry:
store i32 42, ptr @errno, align 4
%call = call float @sinf(float %f)
%v = load i32, ptr @errno, 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
23 changes: 23 additions & 0 deletions llvm/test/Transforms/InstCombine/non-tls-does-not-alias-errno.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -S -mtriple=aarch64-linux-gnu -passes=instcombine < %s | FileCheck %s

@errno = external global i32

; non-tls errno global variable does not alias errno when known to be a thread-local variable,
; can do constant store-to-load forwarding.
define i32 @does_not_alias_errno_global_known_tls(float %f) {
; CHECK-LABEL: define i32 @does_not_alias_errno_global_known_tls(
; CHECK-SAME: float [[F:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: store i32 42, ptr @errno, align 4
; CHECK-NEXT: [[CALL:%.*]] = call float @sinf(float [[F]])
; CHECK-NEXT: ret i32 42
;
entry:
store i32 42, ptr @errno, align 4
%call = call float @sinf(float %f)
%v = load i32, ptr @errno, align 4
ret i32 %v
}

declare float @sinf(float) memory(errnomem: write)
16 changes: 16 additions & 0 deletions llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,22 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
}
}

TEST_F(TargetLibraryInfoTest, IsErrnoThreadLocal) {
EXPECT_TRUE(TargetLibraryInfoImpl(Triple("x86_64-unknown-linux-gnu"))
.isErrnoThreadLocal());
EXPECT_TRUE(
TargetLibraryInfoImpl(Triple("arm64-apple-macosx")).isErrnoThreadLocal());
EXPECT_TRUE(TargetLibraryInfoImpl(Triple("x86_64-pc-windows-msvc"))
.isErrnoThreadLocal());
EXPECT_TRUE(TargetLibraryInfoImpl(Triple("x86_64-unknown-freebsd"))
.isErrnoThreadLocal());

EXPECT_FALSE(
TargetLibraryInfoImpl(Triple("arm-none-eabi")).isErrnoThreadLocal());
EXPECT_FALSE(TargetLibraryInfoImpl(Triple("aarch64-unknown-unknown"))
.isErrnoThreadLocal());
}

namespace {

/// Creates TLI for AArch64 and uses it to get the LibFunc names for the given
Expand Down
Loading