Skip to content

Commit 9701053

Browse files
committed
Introduce @llvm.threadlocal.address intrinsic to access TLS variable
This belongs to a series of patches which try to solve the thread identification problem in coroutines. See https://discourse.llvm.org/t/address-thread-identification-problems-with-coroutine/62015 for a full background. The problem consists of two concrete problems: TLS variable and readnone functions. This patch tries to convert the TLS problem to readnone problem by converting the access of TLS variable to an intrinsic which is marked as readnone. The readnone problem would be addressed in following patches. Reviewed By: nikic, jyknight, nhaehnle, ychen Differential Revision: https://reviews.llvm.org/D125291
1 parent b3452f8 commit 9701053

File tree

7 files changed

+84
-0
lines changed

7 files changed

+84
-0
lines changed

llvm/docs/LangRef.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24545,6 +24545,30 @@ information on the *based on* terminology see
2454524545
mask argument does not match the pointer size of the target, the mask is
2454624546
zero-extended or truncated accordingly.
2454724547

24548+
.. _int_threadlocal_address:
24549+
24550+
'``llvm.threadlocal.address``' Intrinsic
24551+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24552+
24553+
Syntax:
24554+
"""""""
24555+
24556+
::
24557+
24558+
declare ptr @llvm.threadlocal.address(ptr) nounwind readnone willreturn
24559+
24560+
Arguments:
24561+
""""""""""
24562+
24563+
The first argument is a pointer, which refers to a thread local global.
24564+
24565+
Semantics:
24566+
""""""""""
24567+
24568+
The address of a thread local global is not a constant, since it depends on
24569+
the calling thread. The `llvm.threadlocal.address` intrinsic returns the
24570+
address of the given thread local global in the calling thread.
24571+
2454824572
.. _int_vscale:
2454924573

2455024574
'``llvm.vscale``' Intrinsic

llvm/include/llvm/Analysis/TargetTransformInfoImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ class TargetTransformInfoImplBase {
650650
case Intrinsic::coro_align:
651651
case Intrinsic::coro_suspend:
652652
case Intrinsic::coro_subfn_addr:
653+
case Intrinsic::threadlocal_address:
653654
// These intrinsics don't actually represent code after lowering.
654655
return 0;
655656
}

llvm/include/llvm/IR/IRBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ class IRBuilderBase {
753753
/// If the pointer isn't i8* it will be converted.
754754
CallInst *CreateInvariantStart(Value *Ptr, ConstantInt *Size = nullptr);
755755

756+
/// Create a call to llvm.threadlocal.address intrinsic.
757+
CallInst *CreateThreadLocalAddress(Value *Ptr);
758+
756759
/// Create a call to Masked Load intrinsic
757760
CallInst *CreateMaskedLoad(Type *Ty, Value *Ptr, Align Alignment, Value *Mask,
758761
Value *PassThru = nullptr, const Twine &Name = "");

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,10 @@ def int_is_constant : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty],
14041404
def int_ptrmask: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>, llvm_anyint_ty],
14051405
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
14061406

1407+
// Intrinsic to wrap a thread local variable.
1408+
def int_threadlocal_address : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>],
1409+
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
1410+
14071411
def int_experimental_stepvector : DefaultAttrsIntrinsic<[llvm_anyvector_ty],
14081412
[], [IntrNoMem]>;
14091413

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7178,6 +7178,10 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
71787178
DAG.getZExtOrTrunc(Const, sdl, PtrVT)));
71797179
return;
71807180
}
7181+
case Intrinsic::threadlocal_address: {
7182+
setValue(&I, getValue(I.getOperand(0)));
7183+
return;
7184+
}
71817185
case Intrinsic::get_active_lane_mask: {
71827186
EVT CCVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
71837187
SDValue Index = getValue(I.getOperand(0));

llvm/lib/IR/IRBuilder.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,13 @@ CallInst *IRBuilderBase::CreateInvariantStart(Value *Ptr, ConstantInt *Size) {
526526
return CreateCall(TheFn, Ops);
527527
}
528528

529+
CallInst *IRBuilderBase::CreateThreadLocalAddress(Value *Ptr) {
530+
assert(isa<GlobalValue>(Ptr) && cast<GlobalValue>(Ptr)->isThreadLocal() &&
531+
"threadlocal_address only applies to thread local variables.");
532+
return CreateIntrinsic(llvm::Intrinsic::threadlocal_address, {Ptr->getType()},
533+
{Ptr});
534+
}
535+
529536
CallInst *
530537
IRBuilderBase::CreateAssumption(Value *Cond,
531538
ArrayRef<OperandBundleDef> OpBundles) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stop-after=finalize-isel %s -o - | FileCheck %s
2+
3+
@i = thread_local global i32 0, align 4
4+
5+
define noundef i32 @foo() {
6+
; CHECK: %0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg :: (load (s64) from got)
7+
; CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $fs :: (load (s32) from %ir.0)
8+
; CHECK: %2:gr32 = nsw INC32r %1, implicit-def dead $eflags
9+
; CHECK: MOV32mr %0, 1, $noreg, 0, $fs, %2 :: (store (s32) into %ir.0)
10+
; CHECK: $eax = COPY %2
11+
; CHECK: RET 0, $eax
12+
entry:
13+
%0 = call ptr @llvm.threadlocal.address(ptr @i)
14+
%1 = load i32, ptr %0, align 4
15+
%inc = add nsw i32 %1, 1
16+
store i32 %inc, ptr %0, align 4
17+
%2 = call ptr @llvm.threadlocal.address(ptr @i)
18+
%3 = load i32, ptr %2, align 4
19+
ret i32 %3
20+
}
21+
22+
@j = thread_local addrspace(1) global i32 addrspace(0)* @i, align 4
23+
define noundef i32 @bar() {
24+
; CHECK: %0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @j, $noreg :: (load (s64) from got)
25+
; CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $fs :: (load (s32) from %ir.0, addrspace 1)
26+
; CHECK: %2:gr32 = nsw INC32r %1, implicit-def dead $eflags
27+
; CHECK: MOV32mr %0, 1, $noreg, 0, $fs, %2 :: (store (s32) into %ir.0, addrspace 1)
28+
; CHECK: $eax = COPY %2
29+
; CHECK: RET 0, $eax
30+
entry:
31+
%0 = call ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1) @j)
32+
%1 = load i32, ptr addrspace(1) %0, align 4
33+
%inc = add nsw i32 %1, 1
34+
store i32 %inc, ptr addrspace(1) %0, align 4
35+
%2 = call ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1) @j)
36+
%3 = load i32, ptr addrspace(1) %2, align 4
37+
ret i32 %3
38+
}
39+
40+
declare ptr @llvm.threadlocal.address(ptr) nounwind readnone willreturn
41+
declare ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1)) nounwind readnone willreturn

0 commit comments

Comments
 (0)