Skip to content
10 changes: 10 additions & 0 deletions llvm/include/llvm/Analysis/TargetLibraryInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class TargetLibraryInfoImpl {
#include "llvm/Analysis/TargetLibraryInfo.inc"
bool ShouldExtI32Param, ShouldExtI32Return, ShouldSignExtI32Param, ShouldSignExtI32Return;
unsigned SizeOfInt;
bool IsErrnoFunctionCall;
bool IsErrnoThreadLocal;

enum AvailabilityState {
StandardName = 3, // (memset to all ones)
Expand Down Expand Up @@ -256,6 +258,8 @@ 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;
};

/// Provides information about what library functions are available for
Expand Down Expand Up @@ -601,6 +605,12 @@ 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);
}
};

/// Analysis pass providing the \c TargetLibraryInfo.
Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/Analysis/BasicAliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1870,8 +1870,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 it alias globals where environments define it as a function
// call, nor can a non-thread-local global alias a thread-local errno.
if (!TLI.mayBeErrnoGlobal(GV))
return AliasResult::NoAlias;
}

return AliasResult::MayAlias;
}

Expand Down
39 changes: 36 additions & 3 deletions llvm/lib/Analysis/TargetLibraryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,26 @@ 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();
}

static bool initializeIsErrnoFunctionCall(const Triple &T) {
// Assume errno is implemented as a function call on the following
// environments.
// TODO: Could refine known environments.
return 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)),
IsErrnoThreadLocal(initializeIsErrnoThreadLocal(T)) {
// Default to everything being available.
memset(AvailableArray, -1, sizeof(AvailableArray));

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

bool TargetLibraryInfoImpl::mayBeErrnoGlobal(const GlobalVariable *GV) const {
assert(GV && "Expecting existing GlobalVariable.");
if (IsErrnoFunctionCall)
return false;
if (!GV->isThreadLocal() && IsErrnoThreadLocal)
return false;
return true;
}

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

Expand Down
39 changes: 38 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,43 @@ 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
}

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=arm64-apple-macos -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)
22 changes: 22 additions & 0 deletions llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,28 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
}
}

TEST_F(TargetLibraryInfoTest, IsErrnoGlobal) {
using TLII = TargetLibraryInfoImpl;
parseAssembly(R"(
@global = external global i32
)");
auto *GV = M->getNamedGlobal("global");

// Errno is not a global on the following environments.
EXPECT_FALSE(TLII(Triple("x86_64-unknown-linux-gnu")).mayBeErrnoGlobal(GV));
EXPECT_FALSE(TLII(Triple("x86_64-pc-windows-msvc")).mayBeErrnoGlobal(GV));

// Errno is thread-local on the following OSes.
EXPECT_FALSE(TLII(Triple("arm64-apple-macosx")).mayBeErrnoGlobal(GV));
EXPECT_FALSE(TLII(Triple("x86_64-unknown-freebsd")).mayBeErrnoGlobal(GV));

// Unknown.
EXPECT_TRUE(TLII(Triple("arm-none-eabi")).mayBeErrnoGlobal(GV));
EXPECT_TRUE(TLII(Triple("aarch64-unknown-unknown")).mayBeErrnoGlobal(GV));
GV->setThreadLocalMode(GlobalVariable::GeneralDynamicTLSModel);
EXPECT_TRUE(TLII(Triple("x86_64-pc-linux")).mayBeErrnoGlobal(GV));
}

namespace {

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