Skip to content

Commit 07a1b30

Browse files
committed
GlobalsModRef, ValueTracking: Look through threadlocal.address intrinsic
1 parent 417d510 commit 07a1b30

File tree

5 files changed

+94
-5
lines changed

5 files changed

+94
-5
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,11 @@ inline Value *getArgumentAliasingToReturnedPointer(CallBase *Call,
690690
bool isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(
691691
const CallBase *Call, bool MustPreserveNullness);
692692

693-
/// This method strips off any GEP address adjustments and pointer casts from
694-
/// the specified value, returning the original object being addressed. Note
695-
/// that the returned value has pointer type if the specified value does. If
696-
/// the MaxLookup value is non-zero, it limits the number of instructions to
697-
/// be stripped off.
693+
/// This method strips off any GEP address adjustments, pointer casts
694+
/// or `llvm.threadlocal.address` from the specified value \p V, returning the
695+
/// original object being addressed. Note that the returned value has pointer
696+
/// type if the specified value does. If the \p MaxLookup value is non-zero, it
697+
/// limits the number of instructions to be stripped off.
698698
const Value *getUnderlyingObject(const Value *V, unsigned MaxLookup = 6);
699699
inline Value *getUnderlyingObject(Value *V, unsigned MaxLookup = 6) {
700700
// Force const to avoid infinite recursion.

llvm/lib/Analysis/GlobalsModRef.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,14 @@ bool GlobalsAAResult::AnalyzeUsesOfPointer(Value *V,
344344
if (AnalyzeUsesOfPointer(I, Readers, Writers, OkayStoreDest))
345345
return true;
346346
} else if (auto *Call = dyn_cast<CallBase>(I)) {
347+
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
348+
if (II->getIntrinsicID() == Intrinsic::threadlocal_address &&
349+
V == II->getArgOperand(0)) {
350+
if (AnalyzeUsesOfPointer(II, Readers, Writers))
351+
return true;
352+
continue;
353+
}
354+
}
347355
// Make sure that this is just the function being called, not that it is
348356
// passing into the function.
349357
if (Call->isDataOperand(&U)) {

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6255,6 +6255,10 @@ bool llvm::isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(
62556255
return true;
62566256
case Intrinsic::ptrmask:
62576257
return !MustPreserveNullness;
6258+
case Intrinsic::threadlocal_address:
6259+
// The underlying variable changes with thread ID. The Thread ID may change
6260+
// at coroutine suspend points.
6261+
return !Call->getParent()->getParent()->isPresplitCoroutine();
62586262
default:
62596263
return false;
62606264
}

llvm/test/Analysis/GlobalsModRef/nonescaping-noalias.ll

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,67 @@ entry:
2222
ret i32 %v
2323
}
2424

25+
@g1_tls = internal thread_local global i32 0
26+
27+
define i32 @test1_tls(ptr %param) {
28+
; Ensure that we can fold a store to a load of a global across a store to
29+
; a parameter when the global is non-escaping.
30+
;
31+
; CHECK-LABEL: define i32 @test1_tls(
32+
; CHECK-SAME: ptr [[PARAM:%.*]]) {
33+
; CHECK-NEXT: entry:
34+
; CHECK-NEXT: [[P:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
35+
; CHECK-NEXT: store i32 42, ptr [[P]], align 4
36+
; CHECK-NEXT: store i32 7, ptr [[PARAM]], align 4
37+
; CHECK-NEXT: ret i32 42
38+
;
39+
entry:
40+
%p = call ptr @llvm.threadlocal.address(ptr @g1_tls)
41+
store i32 42, ptr %p
42+
store i32 7, ptr %param
43+
%p2 = call ptr @llvm.threadlocal.address(ptr @g1_tls)
44+
%v = load i32, ptr %p2
45+
ret i32 %v
46+
}
47+
48+
define ptr @test1_tls_noopt(ptr %coro, ptr %param) presplitcoroutine {
49+
; CHECK-LABEL: define ptr @test1_tls_noopt(
50+
; CHECK-SAME: ptr [[CORO:%.*]], ptr [[PARAM:%.*]]) #[[ATTR0:[0-9]+]] {
51+
; CHECK-NEXT: entry:
52+
; CHECK-NEXT: [[P:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
53+
; CHECK-NEXT: store i32 42, ptr [[P]], align 4
54+
; CHECK-NEXT: [[TMP0:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false)
55+
; CHECK-NEXT: switch i8 [[TMP0]], label [[SUSPEND:%.*]] [
56+
; CHECK-NEXT: i8 0, label [[RESUME:%.*]]
57+
; CHECK-NEXT: i8 1, label [[SUSPEND]]
58+
; CHECK-NEXT: ]
59+
; CHECK: resume:
60+
; CHECK-NEXT: [[P2:%.*]] = call ptr @llvm.threadlocal.address.p0(ptr @g1_tls)
61+
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P2]], align 4
62+
; CHECK-NEXT: store i32 [[V]], ptr [[PARAM]], align 4
63+
; CHECK-NEXT: ret ptr [[CORO]]
64+
; CHECK: suspend:
65+
; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.coro.end(ptr [[CORO]], i1 false, token none)
66+
; CHECK-NEXT: ret ptr [[CORO]]
67+
;
68+
entry:
69+
%p = call ptr @llvm.threadlocal.address(ptr @g1_tls)
70+
store i32 42, ptr %p
71+
72+
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
73+
switch i8 %0, label %suspend [i8 0, label %resume
74+
i8 1, label %suspend]
75+
resume:
76+
%p2 = call ptr @llvm.threadlocal.address(ptr @g1_tls)
77+
%v = load i32, ptr %p2
78+
store i32 %v, ptr %param, align 4
79+
ret ptr %coro
80+
81+
suspend:
82+
call i1 @llvm.coro.end(ptr %coro, i1 0)
83+
ret ptr %coro
84+
}
85+
2586
declare ptr @f()
2687

2788
define i32 @test2() {

llvm/unittests/Analysis/ValueTrackingTest.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,19 +1273,35 @@ TEST_F(ValueTrackingTest, getUnderlyingObjectCastsAliases) {
12731273

12741274
TEST_F(ValueTrackingTest, getUnderlyingObjectIntrinsics) {
12751275
parseAssembly(R"IR(
1276+
@tlsvar = thread_local global i32 0
12761277
define void @test(ptr %arg) {
12771278
; intrinsic with Return<> arg attribute
12781279
%A = call ptr @llvm.objc.retain(ptr %arg)
12791280
%A2 = call ptr @llvm.ssa.copy(ptr %arg)
12801281
; special cased intrinsics
12811282
%A3 = call ptr @llvm.launder.invariant.group(ptr %arg)
1283+
%A4 = call ptr @llvm.threadlocal.address(ptr @tlsvar)
12821284
ret void
12831285
}
12841286
)IR");
12851287
Value *arg = F->getArg(0);
12861288
EXPECT_EQ(getUnderlyingObject(A), arg);
12871289
EXPECT_EQ(getUnderlyingObject(A2), arg);
12881290
EXPECT_EQ(getUnderlyingObject(A3), arg);
1291+
Value *tlsvar = M->getNamedGlobal("tlsvar");
1292+
EXPECT_EQ(getUnderlyingObject(A4), tlsvar);
1293+
}
1294+
1295+
TEST_F(ValueTrackingTest, getUnderlyingObjectNoSkipTLS) {
1296+
parseAssembly(R"IR(
1297+
@tlsvar = thread_local global i32 0
1298+
define void @test() presplitcoroutine {
1299+
%A = call ptr @llvm.threadlocal.address(ptr @tlsvar)
1300+
ret void
1301+
}
1302+
)IR");
1303+
// Should not skip `threadlocal.address` when functions can switch thread ids.
1304+
EXPECT_EQ(getUnderlyingObject(A), A);
12891305
}
12901306

12911307
TEST_F(ValueTrackingTest, getUnderlyingObjectPtrInt) {

0 commit comments

Comments
 (0)