Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,8 @@ struct CORINFO_THREAD_STATIC_BLOCKS_INFO
void* tlsGetAddrFtnPtr; // linux/x64 specific - address of __tls_get_addr() function
void* tlsIndexObject; // linux/x64 specific - address of tls_index object
void* threadVarsSection; // osx x64/arm64 specific - address of __thread_vars section of `t_ThreadStatics`
uint32_t tlsPthreadKey; // osx x64/arm64 specific - pthread TSD key holding the thread local block that
// contains `t_ThreadStatics`. When zero, `threadVarsSection` has to be used instead.
uint32_t offsetOfThreadLocalStoragePointer; // windows specific
uint32_t offsetOfMaxThreadStaticBlocks;
uint32_t offsetOfThreadStaticBlocks;
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* 5a3e8dc8-83bf-47e5-b032-531dbd307dbe */
0x5a3e8dc8,
0x83bf,
0x47e5,
{0xb0, 0x32, 0x53, 0x1d, 0xbd, 0x30, 0x7d, 0xbe}
constexpr GUID JITEEVersionIdentifier = { /* f404b232-7cf0-420c-a34e-013577600b6a */
0xf404b232,
0x7cf0,
0x420c,
{0xa3, 0x4e, 0x01, 0x35, 0x77, 0x60, 0x0b, 0x6a}
};

#endif // JIT_EE_VERSIONING_GUID_H
4 changes: 4 additions & 0 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3168,6 +3168,10 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* lclNode)
}
else if (TargetOS::IsUnix && data->IsIconHandle(GTF_ICON_TLS_HDL))
{
// Apple platforms reach thread locals through the pthread TSD array rather than a
// thread pointer register, and never materialize the TLS handle on its own - see
// the GTF_ICON_TLS_HDL handling in emitInsLoadStoreOp.
noway_assert(!TargetOS::IsApplePlatform);
assert(data->AsIntCon()->IconValue() == 0);
emitAttr attr = emitActualTypeSize(targetType);
// On non-windows, need to load the address from system register.
Expand Down
33 changes: 30 additions & 3 deletions src/coreclr/jit/emitarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ bool emitter::emitInsMayWriteToGCReg(instrDesc* id)
return (ins == INS_autia || ins == INS_autib || ins == INS_pacia || ins == INS_pacib);

case IF_SR_1A: // SR_1A ................ ...........ttttt Rt (dc zva, mrs)
return ins == INS_mrs_tpid0;
return (ins == INS_mrs_tpid0) || (ins == INS_mrs_tpidrro);

// Below SVE instructions write to GPR and hence GC reg
case IF_SVE_CO_3A: // clasta, clastb
Expand Down Expand Up @@ -3849,6 +3849,7 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts o
break;

case INS_mrs_tpid0:
case INS_mrs_tpidrro:
fmt = IF_SR_1A;
break;

Expand Down Expand Up @@ -14949,6 +14950,11 @@ void emitter::emitDispInsHelp(
emitDispReg(id->idReg1(), size, true);
printf("tpidr_el0");
}
else if (ins == INS_mrs_tpidrro)
{
emitDispReg(id->idReg1(), size, true);
printf("tpidrro_el0");
}
else
{
emitDispReg(id->idReg1(), size, false);
Expand Down Expand Up @@ -15151,8 +15157,29 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
}
else if (addr->IsIconHandle(GTF_ICON_TLS_HDL))
{
// On Arm64, TEB is in r18, so load from the r18 as base.
emitIns_R_R_I(ins, attr, dataReg, REG_R18, addr->AsIntCon()->IconValue());
if (TargetOS::IsApplePlatform)
{
// On Apple platforms, thread locals live in a block that is reachable through
// the pthread TSD array of the current thread, and the icon holds the byte
// offset of the owning slot in that array.
//
// mrs dataReg, tpidrro_el0
// and dataReg, dataReg, #~7 ; the low bits hold the CPU number
// ldr dataReg, [dataReg, #offs]
//
// `dataReg` doubles as the scratch register for the first two instructions,
// which is only valid because this is a load.
noway_assert(!emitInsIsStore(ins));
noway_assert(isGeneralRegister(dataReg));
emitIns_R(INS_mrs_tpidrro, EA_8BYTE, dataReg);
emitIns_R_R_I(INS_and, EA_8BYTE, dataReg, dataReg, ~(ssize_t)7);
emitIns_R_R_I(ins, attr, dataReg, dataReg, addr->AsIntCon()->IconValue());
}
else
{
// On Arm64, TEB is in r18, so load from the r18 as base.
emitIns_R_R_I(ins, attr, dataReg, REG_R18, addr->AsIntCon()->IconValue());
}
}
else if (emitIns_valid_imm_for_ldst_offset(offset, emitTypeSize(indir->TypeGet())))
{
Expand Down
94 changes: 66 additions & 28 deletions src/coreclr/jit/helperexpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement*
JITDUMP("threadVarsSection= %p\n", dspPtr(threadStaticBlocksInfo.threadVarsSection));
JITDUMP("offsetOfThreadLocalStoragePointer= %u\n",
dspOffset(threadStaticBlocksInfo.offsetOfThreadLocalStoragePointer));
JITDUMP("tlsPthreadKey= %u\n", threadStaticBlocksInfo.tlsPthreadKey);
JITDUMP("offsetOfMaxThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks));
JITDUMP("offsetOfThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadStaticBlocks));
JITDUMP("offsetOfBaseOfThreadLocalData= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData));
Expand Down Expand Up @@ -936,39 +937,76 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement*
}
else if (TargetOS::IsApplePlatform)
{
// For Apple x64/arm64, we need to get the address of relevant __thread_vars section of
// the thread local variable `t_ThreadStatics`. Address of `tlv_get_address` is stored
// in this entry, which we dereference and invoke it, passing the __thread_vars address
// present in `threadVarsSection`.
//
// Code sequence to access thread local variable on Apple/x64:
//
// mov rdi, threadVarsSection
// call [rdi]
//
// Code sequence to access thread local variable on Apple/arm64:
//
// mov x0, threadVarsSection
// mov x1, [x0]
// blr x1
//
size_t threadVarsSectionVal = (size_t)threadStaticBlocksInfo.threadVarsSection;
GenTree* tls_get_addr_val = gtNewIconHandleNode(threadVarsSectionVal, GTF_ICON_FTN_ADDR);
if (threadStaticBlocksInfo.tlsPthreadKey != 0)
Comment thread
EgorBo marked this conversation as resolved.
{
// The thread local block that contains `t_ThreadStatics` lives in the pthread TSD array
// of the current thread at index `tlsPthreadKey`. This is the inlined fast path of
// dyld's `tlv_get_addr`; the offset of `t_ThreadStatics` within the block is folded
// into `offsetOfBaseOfThreadLocalData` by the VM.
//
// Unlike `tlv_get_addr` this has no null check and no lazy-allocation fallback, which
// relies on the TSD slot being populated on every thread that can run managed code:
// dyld allocates one block per image, so the slot covers all of coreclr's `__thread`
// state (`t_ThreadStatics`, `t_CurrentThreadInfo`, `t_runtime_thread_locals`, ...).
// A thread can only run managed code once it is attached, and attaching goes through
// `SetThread`/`InitializeCurrentThreadsStaticData`, which touch that state from the VM
// (i.e. through the thunk) and therefore instantiate the block. Conversely, if the
// block were released - dyld frees it from a pthread key destructor at thread exit -
// the thread also reads back as unattached, so re-entering managed code has to attach
// it again and re-instantiate the block first. This mirrors what the Windows expansion
// below/above assumes about `TEB->ThreadLocalStoragePointer[tlsIndex]`.
//
// Code sequence to access thread local variable on Apple/x64:
//
// mov xd, gs:[tlsPthreadKey * 8]
//
// Code sequence to access thread local variable on Apple/arm64:
//
// mrs xd, tpidrro_el0
// and xd, xd, #~7 ; the low bits hold the CPU number
// ldr xd, [xd, #tlsPthreadKey * 8]
//
size_t tsdOffset = (size_t)threadStaticBlocksInfo.tlsPthreadKey * TARGET_POINTER_SIZE;
tlsValue = gtNewIconHandleNode(tsdOffset, GTF_ICON_TLS_HDL);
tlsValue = gtNewIndir(TYP_I_IMPL, tlsValue, GTF_IND_NONFAULTING | GTF_IND_INVARIANT);
}
else
{
// The VM was not able to determine the pthread TSD key of the thread local block, so
// fall back to calling the thunk. We need to get the address of the relevant
// __thread_vars section of the thread local variable `t_ThreadStatics`. The address of
// `tlv_get_addr` is stored in this entry, which we dereference and invoke, passing
// the __thread_vars address present in `threadVarsSection`.
//
// Code sequence to access thread local variable on Apple/x64:
//
// mov rdi, threadVarsSection
// call [rdi]
//
// Code sequence to access thread local variable on Apple/arm64:
//
// mov x0, threadVarsSection
// mov x1, [x0]
// blr x1
//
size_t threadVarsSectionVal = (size_t)threadStaticBlocksInfo.threadVarsSection;
GenTree* tls_get_addr_val = gtNewIconHandleNode(threadVarsSectionVal, GTF_ICON_FTN_ADDR);

tls_get_addr_val = gtNewIndir(TYP_I_IMPL, tls_get_addr_val, GTF_IND_NONFAULTING | GTF_IND_INVARIANT);
tls_get_addr_val = gtNewIndir(TYP_I_IMPL, tls_get_addr_val, GTF_IND_NONFAULTING | GTF_IND_INVARIANT);

tlsValue = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL);
GenTreeCall* tlsRefCall = tlsValue->AsCall();
tlsValue = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL);
GenTreeCall* tlsRefCall = tlsValue->AsCall();

// This is a call which takes an argument.
// Populate and set the ABI appropriately.
assert(opts.altJit || threadVarsSectionVal != 0);
GenTree* tlsArg = gtNewIconNode(threadVarsSectionVal, TYP_I_IMPL);
tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg));
// This is a call which takes an argument.
// Populate and set the ABI appropriately.
assert(opts.altJit || threadVarsSectionVal != 0);
GenTree* tlsArg = gtNewIconNode(threadVarsSectionVal, TYP_I_IMPL);
tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg));

fgMorphArgs(tlsRefCall);
fgMorphArgs(tlsRefCall);

tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT);
tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT);
}
}
else if (TargetOS::IsUnix)
{
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/instrsarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,9 @@ INST1(dczva, "dczva", 0, IF_SR_1A, 0xD50B7420)
INST1(mrs_tpid0, "mrs", 0, IF_SR_1A, 0xD53BD040)
// mrs Rt,tpidr_el0 SR_1A 1101010100111011 11010000010ttttt D53B D040 Rt, tpidr_el0

INST1(mrs_tpidrro, "mrs", 0, IF_SR_1A, 0xD53BD060)
// mrs Rt,tpidrro_el0 SR_1A 1101010100111011 11010000011ttttt D53B D060 Rt, tpidrro_el0

INST1(umov, "umov", 0, IF_DV_2B, 0x0E003C00)
// umov Rd,Vn[] DV_2B 0Q001110000iiiii 001111nnnnnddddd 0E00 3C00 Rd,Vn[]

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,7 @@ public unsafe struct CORINFO_THREAD_STATIC_BLOCKS_INFO
public nuint tlsGetAddrFtnPtr;
public nuint tlsIndexObject;
public nuint threadVarsSection;
public uint tlsPthreadKey;
public uint offsetOfThreadLocalStoragePointer;
public uint offsetOfMaxThreadStaticBlocks;
public uint offsetOfThreadStaticBlocks;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ struct Agnostic_GetThreadLocalStaticBlocksInfo
DWORDLONG tlsGetAddrFtnPtr;
DWORDLONG tlsIndexObject;
DWORDLONG threadVarsSection;
DWORD tlsPthreadKey;
DWORD offsetOfThreadLocalStoragePointer;
DWORD offsetOfMaxThreadStaticBlocks;
DWORD offsetOfThreadStaticBlocks;
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3629,6 +3629,7 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC
value.tlsIndexObject = CastPointer(pInfo->tlsIndexObject);
value.threadVarsSection = CastPointer(pInfo->threadVarsSection);
value.offsetOfThreadLocalStoragePointer = pInfo->offsetOfThreadLocalStoragePointer;
value.tlsPthreadKey = pInfo->tlsPthreadKey;
value.offsetOfMaxThreadStaticBlocks = pInfo->offsetOfMaxThreadStaticBlocks;
value.offsetOfThreadStaticBlocks = pInfo->offsetOfThreadStaticBlocks;
value.offsetOfBaseOfThreadLocalData = pInfo->offsetOfBaseOfThreadLocalData;
Expand All @@ -3645,12 +3646,14 @@ void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_
", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64
", threadVarsSection - %016" PRIX64
", offsetOfThreadLocalStoragePointer-%u"
", tlsPthreadKey-%u"
", offsetOfMaxThreadStaticBlocks-%u"
", offsetOfThreadStaticBlocks-%u"
", offsetOfBaseOfThreadLocalData-%u",
key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr,
value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer,
value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfBaseOfThreadLocalData);
value.tlsPthreadKey, value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks,
value.offsetOfBaseOfThreadLocalData);
}

void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo)
Expand All @@ -3665,6 +3668,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC
pInfo->tlsIndexObject = (void*)value.tlsIndexObject;
pInfo->threadVarsSection = (void*)value.threadVarsSection;
pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer;
pInfo->tlsPthreadKey = value.tlsPthreadKey;
pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks;
pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks;
pInfo->offsetOfBaseOfThreadLocalData = value.offsetOfBaseOfThreadLocalData;
Expand Down
Loading
Loading