mask argument does not match the pointer size of the target, the mask is
zero-extended or truncated accordingly.
+.. _int_threadlocal_address:
+
+'``llvm.threadlocal.address``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+ declare ptr @llvm.threadlocal.address(ptr) nounwind readnone willreturn
+
+Arguments:
+""""""""""
+
+The first argument is a pointer, which refers to a thread local global.
+
+Semantics:
+""""""""""
+
+The address of a thread local global is not a constant, since it depends on
+the calling thread. The `llvm.threadlocal.address` intrinsic returns the
+address of the given thread local global in the calling thread.
+
.. _int_vscale:
'``llvm.vscale``' Intrinsic
/// If the pointer isn't i8* it will be converted.
CallInst *CreateInvariantStart(Value *Ptr, ConstantInt *Size = nullptr);
+ /// Create a call to llvm.threadlocal.address intrinsic.
+ CallInst *CreateThreadLocalAddress(Value *Ptr);
+
/// Create a call to Masked Load intrinsic
CallInst *CreateMaskedLoad(Type *Ty, Value *Ptr, Align Alignment, Value *Mask,
Value *PassThru = nullptr, const Twine &Name = "");
def int_ptrmask: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>, llvm_anyint_ty],
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
+// Intrinsic to wrap a thread local variable.
+def int_threadlocal_address : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>],
+ [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
+
def int_experimental_stepvector : DefaultAttrsIntrinsic<[llvm_anyvector_ty],
[], [IntrNoMem]>;
return CreateCall(TheFn, Ops);
}
+CallInst *IRBuilderBase::CreateThreadLocalAddress(Value *Ptr) {
+ assert(isa<GlobalValue>(Ptr) && cast<GlobalValue>(Ptr)->isThreadLocal() &&
+ "threadlocal_address only applies to thread local variables.");
+ return CreateIntrinsic(llvm::Intrinsic::threadlocal_address, {Ptr->getType()},
+ {Ptr});
+}
+
CallInst *
IRBuilderBase::CreateAssumption(Value *Cond,
ArrayRef<OperandBundleDef> OpBundles) {
--- /dev/null
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -stop-after=finalize-isel %s -o - | FileCheck %s
+
+@i = thread_local global i32 0, align 4
+
+define noundef i32 @foo() {
+; CHECK: %0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg :: (load (s64) from got)
+; CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $fs :: (load (s32) from %ir.0)
+; CHECK: %2:gr32 = nsw INC32r %1, implicit-def dead $eflags
+; CHECK: MOV32mr %0, 1, $noreg, 0, $fs, %2 :: (store (s32) into %ir.0)
+; CHECK: $eax = COPY %2
+; CHECK: RET 0, $eax
+entry:
+ %0 = call ptr @llvm.threadlocal.address(ptr @i)
+ %1 = load i32, ptr %0, align 4
+ %inc = add nsw i32 %1, 1
+ store i32 %inc, ptr %0, align 4
+ %2 = call ptr @llvm.threadlocal.address(ptr @i)
+ %3 = load i32, ptr %2, align 4
+ ret i32 %3
+}
+
+@j = thread_local addrspace(1) global i32 addrspace(0)* @i, align 4
+define noundef i32 @bar() {
+; CHECK: %0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @j, $noreg :: (load (s64) from got)
+; CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $fs :: (load (s32) from %ir.0, addrspace 1)
+; CHECK: %2:gr32 = nsw INC32r %1, implicit-def dead $eflags
+; CHECK: MOV32mr %0, 1, $noreg, 0, $fs, %2 :: (store (s32) into %ir.0, addrspace 1)
+; CHECK: $eax = COPY %2
+; CHECK: RET 0, $eax
+entry:
+ %0 = call ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1) @j)
+ %1 = load i32, ptr addrspace(1) %0, align 4
+ %inc = add nsw i32 %1, 1
+ store i32 %inc, ptr addrspace(1) %0, align 4
+ %2 = call ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1) @j)
+ %3 = load i32, ptr addrspace(1) %2, align 4
+ ret i32 %3
+}
+
+declare ptr @llvm.threadlocal.address(ptr) nounwind readnone willreturn
+declare ptr addrspace(1) @llvm.threadlocal.address.p1(ptr addrspace(1)) nounwind readnone willreturn