From 2be6c601ee1aa819937e7e51dd98a67676047c59 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 19:59:13 +0000
Subject: [PATCH 01/14] Initial plan
From 5c9dea95edb0d3f8ca7d2f8174f6c84196a651e1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 20:05:57 +0000
Subject: [PATCH 02/14] Add CORINFO_HELP_READYTORUN_THREADLOCALBASE helper for
DirectOnThreadLocalData
This change adds a new JIT helper to handle thread static access for DirectOnThreadLocalData in ReadyToRun images. The new helper returns the address of the thread local data structure, which is used to access the pNativeThread field. This eliminates the need for the BypassReadyToRun attribute on GetThreadStaticsBase, allowing it to be included in R2R images and improving performance on platforms like iOS that rely on composite-r2r with interpreter fallback.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
.../src/System/Threading/Thread.CoreCLR.cs | 7 -------
src/coreclr/inc/corinfo.h | 1 +
src/coreclr/inc/jiteeversionguid.h | 10 +++++-----
src/coreclr/inc/jithelpers.h | 1 +
src/coreclr/vm/jithelpers.cpp | 10 ++++++++++
src/coreclr/vm/prestub.cpp | 6 ++++++
6 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
index a48ff67a654fa8..338af221779cbf 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
@@ -486,13 +486,6 @@ private static class DirectOnThreadLocalData
/// Get the ThreadStaticBase used for this threads TLS data. This ends up being a pointer to the pNativeThread field on the ThreadLocalData,
/// which is at a well known offset from the start of the ThreadLocalData
///
- ///
- ///
- /// We use BypassReadyToRunAttribute to ensure that this method is not compiled using ReadyToRun. This avoids an issue where we might
- /// fail to use the JIT_GetNonGCThreadStaticBaseOptimized2 JIT helpers to access the field, which would result in a stack overflow, as accessing
- /// this field would recursively call this method.
- ///
- [System.Runtime.BypassReadyToRunAttribute]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[DebuggerHidden]
[DebuggerStepThrough]
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index f6922867abcfee..5e4b73b07bf58d 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -526,6 +526,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE,
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR,
CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE,
+ CORINFO_HELP_READYTORUN_THREADLOCALBASE,
CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE,
CORINFO_HELP_READYTORUN_DELEGATE_CTOR,
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index 75e98a796a949c..5b17e9c7ca5119 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -37,11 +37,11 @@
#include
-constexpr GUID JITEEVersionIdentifier = { /* d98b5b0d-dd75-4e4d-b950-0d1cafd01dea */
- 0xd98b5b0d,
- 0xdd75,
- 0x4e4d,
- {0xb9, 0x50, 0x0d, 0x1c, 0xaf, 0xd0, 0x1d, 0xea}
+constexpr GUID JITEEVersionIdentifier = { /* 9068c4e3-6a0a-4347-a754-9ff2d63a1207 */
+ 0x9068c4e3,
+ 0x6a0a,
+ 0x4347,
+ {0xa7, 0x54, 0x9f, 0xf2, 0xd6, 0x3a, 0x12, 0x07}
};
#endif // JIT_EE_VERSIONING_GUID_H
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index d042769830249a..571c147899c7ae 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -257,6 +257,7 @@
JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE, NULL,METHOD__NIL)
+ JITHELPER(CORINFO_HELP_READYTORUN_THREADLOCALBASE, JIT_GetThreadLocalBase, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_GENERIC_HANDLE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, NULL, METHOD__NIL)
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 4a63050a9493b5..ae55fd0afc7d35 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -371,6 +371,16 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex)
}
HCIMPLEND
+// *** This helper corresponds CORINFO_HELP_READYTORUN_THREADLOCALBASE.
+// Returns the address of the pThread field (t_ThreadStatics) for DirectOnThreadLocalData
+HCIMPL0(void*, JIT_GetThreadLocalBase)
+{
+ FCALL_CONTRACT;
+
+ return (void*)&t_ThreadStatics;
+}
+HCIMPLEND
+
#include
//========================================================================
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index a3637e14a10af5..3f0bb5800e4bbd 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -3003,6 +3003,12 @@ static PCODE getHelperForStaticBase(Module * pModule, ReadyToRunFixupKind kind,
bool noCtor = pMT->IsClassInitedOrPreinited();
bool threadStatic = (kind == READYTORUN_FIXUP_ThreadStaticBaseNonGC || kind == READYTORUN_FIXUP_ThreadStaticBaseGC);
+ // Special case for DirectOnThreadLocalData: return helper that gets the address of the thread local data
+ if (threadStatic && !GCStatic && pMT == CoreLibBinder::GetExistingClass(CLASS__DIRECTONTHREADLOCALDATA))
+ {
+ return CEEJitInfo::getHelperFtnStatic(CORINFO_HELP_READYTORUN_THREADLOCALBASE);
+ }
+
CorInfoHelpFunc helper;
if (threadStatic)
From 47c8bc49c842b3381a8090efa36d4601d0c94ce5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 20:31:14 +0000
Subject: [PATCH 03/14] Add ReadyToRun compiler support for
CORINFO_HELP_READYTORUN_THREADLOCALBASE
Update the CrossGen2 ReadyToRun compiler to handle the new CORINFO_HELP_READYTORUN_THREADLOCALBASE helper. This helper is used to get the address of the thread local data structure for DirectOnThreadLocalData.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
.../tools/Common/JitInterface/CorInfoHelpFunc.cs | 1 +
.../JitInterface/CorInfoImpl.ReadyToRun.cs | 12 ++++++++++++
2 files changed, 13 insertions(+)
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 87c743f1680b76..7563e1b14dd1e1 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -217,6 +217,7 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE,
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR,
CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE,
+ CORINFO_HELP_READYTORUN_THREADLOCALBASE,
CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE,
CORINFO_HELP_READYTORUN_DELEGATE_CTOR,
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index 88dc5ac89085ba..256368653280d4 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -907,6 +907,13 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref
pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type));
}
break;
+ case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADLOCALBASE:
+ {
+ // This helper returns the address of the thread local data structure directly
+ // It doesn't take a type parameter like the other thread static helpers
+ pLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol("JIT_GetThreadLocalBase"));
+ }
+ break;
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE:
{
Debug.Assert(pGenericLookupKind.needsRuntimeLookup);
@@ -1268,6 +1275,11 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
id = ReadyToRunHelper.GetCurrentManagedThreadId;
break;
+ case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADLOCALBASE:
+ // This helper returns the address of the thread local data structure directly
+ // It doesn't use the ReadyToRunHelper mechanism, but directly calls JIT_GetThreadLocalBase
+ return _compilation.NodeFactory.ExternSymbol("JIT_GetThreadLocalBase");
+
case CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER:
id = ReadyToRunHelper.ReversePInvokeEnter;
break;
From a9506d74039a72e0f446654e72c158bf0a156ec9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 20:33:33 +0000
Subject: [PATCH 04/14] Add GetThreadLocalBase ReadyToRun helper and update
enums
Add the new GetThreadLocalBase helper to the ReadyToRunHelper enum and map it to the CORINFO_HELP_READYTORUN_THREADLOCALBASE helper in the ReadyToRun constants and helper mapping tables.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/inc/readytorun.h | 3 +++
src/coreclr/inc/readytorunhelpers.h | 1 +
.../Common/Internal/Runtime/ReadyToRunConstants.cs | 3 +++
.../JitInterface/CorInfoImpl.ReadyToRun.cs | 11 ++---------
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h
index 7057132b460fac..4c4cbad59bef5d 100644
--- a/src/coreclr/inc/readytorun.h
+++ b/src/coreclr/inc/readytorun.h
@@ -465,6 +465,9 @@ enum ReadyToRunHelper
READYTORUN_HELPER_StackProbe = 0x111,
READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112,
+
+ // Helper to get the address of the thread local data structure
+ READYTORUN_HELPER_GetThreadLocalBase = 0x113,
};
#include "readytoruninstructionset.h"
diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h
index c6e1a09ca5eb87..2ac870901cee95 100644
--- a/src/coreclr/inc/readytorunhelpers.h
+++ b/src/coreclr/inc/readytorunhelpers.h
@@ -126,6 +126,7 @@ HELPER(READYTORUN_HELPER_StackProbe, CORINFO_HELP_STACK_PROBE,
#endif
HELPER(READYTORUN_HELPER_GetCurrentManagedThreadId, CORINFO_HELP_GETCURRENTMANAGEDTHREADID, )
+HELPER(READYTORUN_HELPER_GetThreadLocalBase, CORINFO_HELP_READYTORUN_THREADLOCALBASE, )
#undef HELPER
#undef OPTIMIZEFORSPEED
diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
index 0f607102a34400..c3063abd857783 100644
--- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
+++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
@@ -352,6 +352,9 @@ public enum ReadyToRunHelper
GetCurrentManagedThreadId = 0x112,
+ // Helper to get the address of the thread local data structure
+ GetThreadLocalBase = 0x113,
+
// **********************************************************************************************
//
// These are not actually part of the R2R file format. We have them here because it's convenient.
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index 256368653280d4..a5411a7a8afa8c 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -907,13 +907,6 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref
pLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(helperId, type));
}
break;
- case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADLOCALBASE:
- {
- // This helper returns the address of the thread local data structure directly
- // It doesn't take a type parameter like the other thread static helpers
- pLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol("JIT_GetThreadLocalBase"));
- }
- break;
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_HANDLE:
{
Debug.Assert(pGenericLookupKind.needsRuntimeLookup);
@@ -1277,8 +1270,8 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADLOCALBASE:
// This helper returns the address of the thread local data structure directly
- // It doesn't use the ReadyToRunHelper mechanism, but directly calls JIT_GetThreadLocalBase
- return _compilation.NodeFactory.ExternSymbol("JIT_GetThreadLocalBase");
+ id = ReadyToRunHelper.GetThreadLocalBase;
+ break;
case CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER:
id = ReadyToRunHelper.ReversePInvokeEnter;
From e5a6c50345e43385b9c785ea9e5b15048f390db0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 20:39:29 +0000
Subject: [PATCH 05/14] Fix comment in JIT_GetThreadLocalBase helper
Corrected the comment to accurately state that the helper returns the address of the thread local data structure, not just the pThread field.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/vm/jithelpers.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index ae55fd0afc7d35..1845c6f81a6020 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -372,7 +372,7 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex)
HCIMPLEND
// *** This helper corresponds CORINFO_HELP_READYTORUN_THREADLOCALBASE.
-// Returns the address of the pThread field (t_ThreadStatics) for DirectOnThreadLocalData
+// Returns the address of the thread local data structure (t_ThreadStatics)
HCIMPL0(void*, JIT_GetThreadLocalBase)
{
FCALL_CONTRACT;
From 638868415ee3831136d1fe05eb20bb2596283b0f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 20:55:26 +0000
Subject: [PATCH 06/14] Address review feedback: Use normal JIT helper instead
of ReadyToRun helper
- Changed CORINFO_HELP_READYTORUN_THREADLOCALBASE to CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE as a normal JIT helper
- Renamed JIT_GetThreadLocalBase to JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase
- Fixed implementation to return address of pThread field instead of entire ThreadLocalData structure
- Reverted all ReadyToRun constant changes (readytorun.h, readytorunhelpers.h, ReadyToRunConstants.cs)
- Removed ReadyToRun compiler awareness of this helper
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/inc/corinfo.h | 2 +-
src/coreclr/inc/jithelpers.h | 1 -
src/coreclr/inc/readytorun.h | 3 ---
src/coreclr/inc/readytorunhelpers.h | 1 -
.../tools/Common/Internal/Runtime/ReadyToRunConstants.cs | 3 ---
src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs | 1 -
.../JitInterface/CorInfoImpl.ReadyToRun.cs | 5 -----
src/coreclr/vm/jithelpers.cpp | 8 ++++----
src/coreclr/vm/prestub.cpp | 4 ++--
9 files changed, 7 insertions(+), 21 deletions(-)
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 5e4b73b07bf58d..07c169161a1028 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -479,6 +479,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2,
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2_NOJITOPT,
+ CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE,
/* Debugger */
@@ -526,7 +527,6 @@ enum CorInfoHelpFunc
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE,
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR,
CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE,
- CORINFO_HELP_READYTORUN_THREADLOCALBASE,
CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE,
CORINFO_HELP_READYTORUN_DELEGATE_CTOR,
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 571c147899c7ae..d042769830249a 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -257,7 +257,6 @@
JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE, NULL,METHOD__NIL)
- JITHELPER(CORINFO_HELP_READYTORUN_THREADLOCALBASE, JIT_GetThreadLocalBase, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_GENERIC_HANDLE, NULL, METHOD__NIL)
JITHELPER(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, NULL, METHOD__NIL)
diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h
index 4c4cbad59bef5d..7057132b460fac 100644
--- a/src/coreclr/inc/readytorun.h
+++ b/src/coreclr/inc/readytorun.h
@@ -465,9 +465,6 @@ enum ReadyToRunHelper
READYTORUN_HELPER_StackProbe = 0x111,
READYTORUN_HELPER_GetCurrentManagedThreadId = 0x112,
-
- // Helper to get the address of the thread local data structure
- READYTORUN_HELPER_GetThreadLocalBase = 0x113,
};
#include "readytoruninstructionset.h"
diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h
index 2ac870901cee95..c6e1a09ca5eb87 100644
--- a/src/coreclr/inc/readytorunhelpers.h
+++ b/src/coreclr/inc/readytorunhelpers.h
@@ -126,7 +126,6 @@ HELPER(READYTORUN_HELPER_StackProbe, CORINFO_HELP_STACK_PROBE,
#endif
HELPER(READYTORUN_HELPER_GetCurrentManagedThreadId, CORINFO_HELP_GETCURRENTMANAGEDTHREADID, )
-HELPER(READYTORUN_HELPER_GetThreadLocalBase, CORINFO_HELP_READYTORUN_THREADLOCALBASE, )
#undef HELPER
#undef OPTIMIZEFORSPEED
diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
index c3063abd857783..0f607102a34400 100644
--- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
+++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
@@ -352,9 +352,6 @@ public enum ReadyToRunHelper
GetCurrentManagedThreadId = 0x112,
- // Helper to get the address of the thread local data structure
- GetThreadLocalBase = 0x113,
-
// **********************************************************************************************
//
// These are not actually part of the R2R file format. We have them here because it's convenient.
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 7563e1b14dd1e1..87c743f1680b76 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -217,7 +217,6 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE,
CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR,
CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE,
- CORINFO_HELP_READYTORUN_THREADLOCALBASE,
CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE,
CORINFO_HELP_READYTORUN_DELEGATE_CTOR,
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index a5411a7a8afa8c..88dc5ac89085ba 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -1268,11 +1268,6 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
id = ReadyToRunHelper.GetCurrentManagedThreadId;
break;
- case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADLOCALBASE:
- // This helper returns the address of the thread local data structure directly
- id = ReadyToRunHelper.GetThreadLocalBase;
- break;
-
case CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER:
id = ReadyToRunHelper.ReversePInvokeEnter;
break;
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 1845c6f81a6020..80b4f1e0879d4d 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -371,13 +371,13 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex)
}
HCIMPLEND
-// *** This helper corresponds CORINFO_HELP_READYTORUN_THREADLOCALBASE.
-// Returns the address of the thread local data structure (t_ThreadStatics)
-HCIMPL0(void*, JIT_GetThreadLocalBase)
+// *** This helper corresponds CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE.
+// Returns the address of the pThread field in the ThreadLocalData structure for DirectOnThreadLocalData
+HCIMPL0(void*, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase)
{
FCALL_CONTRACT;
- return (void*)&t_ThreadStatics;
+ return (void*)&(((ThreadLocalData*)&t_ThreadStatics)->pThread);
}
HCIMPLEND
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 3f0bb5800e4bbd..9b4b886981a82c 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -3003,10 +3003,10 @@ static PCODE getHelperForStaticBase(Module * pModule, ReadyToRunFixupKind kind,
bool noCtor = pMT->IsClassInitedOrPreinited();
bool threadStatic = (kind == READYTORUN_FIXUP_ThreadStaticBaseNonGC || kind == READYTORUN_FIXUP_ThreadStaticBaseGC);
- // Special case for DirectOnThreadLocalData: return helper that gets the address of the thread local data
+ // Special case for DirectOnThreadLocalData: return helper that gets the address of the pThread field
if (threadStatic && !GCStatic && pMT == CoreLibBinder::GetExistingClass(CLASS__DIRECTONTHREADLOCALDATA))
{
- return CEEJitInfo::getHelperFtnStatic(CORINFO_HELP_READYTORUN_THREADLOCALBASE);
+ return CEEJitInfo::getHelperFtnStatic(CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE);
}
CorInfoHelpFunc helper;
From f699f05c9ef01922003fe34da4950d476c4dcc2b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 21:00:40 +0000
Subject: [PATCH 07/14] Fix missing helper definition in jithelpers.h
Added the CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE helper definition that was missing from jithelpers.h, causing enum mismatch errors.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/inc/jithelpers.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index d042769830249a..72946a3115e1f3 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -209,6 +209,7 @@
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, NULL, METHOD__STATICSHELPERS__GET_OPTIMIZED_NONGC_THREADSTATIC)
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2, JIT_GetNonGCThreadStaticBaseOptimized2, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2_NOJITOPT, JIT_GetNonGCThreadStaticBaseOptimized2, METHOD__NIL)
+ DYNAMICJITHELPER(CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase, METHOD__NIL)
// Debugger
JITHELPER(CORINFO_HELP_DBG_IS_JUST_MY_CODE, JIT_DbgIsJustMyCode,METHOD__NIL)
From 5964bd58f37108ac98fd4057219f2f1e93869afa Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 21:07:08 +0000
Subject: [PATCH 08/14] Change helper from DYNAMICJITHELPER to JITHELPER
JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase should be a JITHELPER, not a DYNAMICJITHELPER.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/inc/jithelpers.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 72946a3115e1f3..201a3c0139bdfd 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -209,7 +209,7 @@
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, NULL, METHOD__STATICSHELPERS__GET_OPTIMIZED_NONGC_THREADSTATIC)
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2, JIT_GetNonGCThreadStaticBaseOptimized2, METHOD__NIL)
DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2_NOJITOPT, JIT_GetNonGCThreadStaticBaseOptimized2, METHOD__NIL)
- DYNAMICJITHELPER(CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase, METHOD__NIL)
+ JITHELPER(CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase, METHOD__NIL)
// Debugger
JITHELPER(CORINFO_HELP_DBG_IS_JUST_MY_CODE, JIT_DbgIsJustMyCode,METHOD__NIL)
From c6772e4b6f96844f8a91174ce9d57a21710f9732 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 21:09:30 +0000
Subject: [PATCH 09/14] Add helper to RequiresRuntimeJitException list in
ReadyToRun compiler
The CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE helper should not be used during R2R compilation and should throw RequiresRuntimeJitException.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
.../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index 88dc5ac89085ba..26843eb2c267bc 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -1291,6 +1291,8 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
case CorInfoHelpFunc.CORINFO_HELP_THROW_NOT_IMPLEMENTED:
// For x86 tailcall where helper is required we need runtime JIT.
case CorInfoHelpFunc.CORINFO_HELP_TAILCALL:
+ // DirectOnThreadLocalData helper should not be used in R2R compilation
+ case CorInfoHelpFunc.CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE:
throw new RequiresRuntimeJitException(ftnNum.ToString());
default:
From e691c501f55f8260c65e60718a1547859a21726c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 21:11:02 +0000
Subject: [PATCH 10/14] Add helper to CorInfoHelpFunc.cs enum
Added CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE to the CorInfoHelpFunc enum in the tools directory.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 87c743f1680b76..e02b217395b1c4 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -171,6 +171,7 @@ which is the right helper to use to allocate an object of a given type. */
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2,
CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2_NOJITOPT,
+ CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE,
/* Debugger */
CORINFO_HELP_DBG_IS_JUST_MY_CODE, // Check if this is "JustMyCode" and needs to be stepped through.
From 8cb501090511b0cd439bb3ddeb59614f651d58f7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 21:17:14 +0000
Subject: [PATCH 11/14] Improve comment for
JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase
Made the comment more precise to clarify that this helper returns the address of the pThread field which serves as the storage location for DirectOnThreadLocalData.pNativeThread thread static field.
Co-authored-by: davidwrighton <10779849+davidwrighton@users.noreply.github.com>
---
src/coreclr/vm/jithelpers.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 80b4f1e0879d4d..51d4b4b8c5c25a 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -372,7 +372,8 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex)
HCIMPLEND
// *** This helper corresponds CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE.
-// Returns the address of the pThread field in the ThreadLocalData structure for DirectOnThreadLocalData
+// Returns the address of the pThread field within thread-local storage, which serves as the storage
+// location for the DirectOnThreadLocalData.pNativeThread thread static field.
HCIMPL0(void*, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase)
{
FCALL_CONTRACT;
From af30be80c52bbbb6df6f14305a88b87c4b89cd26 Mon Sep 17 00:00:00 2001
From: David Wrighton
Date: Fri, 30 Jan 2026 13:30:43 -0800
Subject: [PATCH 12/14] Adjust base address by CORINFO_Array__data to match the
behavior expected
---
src/coreclr/vm/jithelpers.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 80b4f1e0879d4d..c894c26639770f 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -373,11 +373,13 @@ HCIMPLEND
// *** This helper corresponds CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE.
// Returns the address of the pThread field in the ThreadLocalData structure for DirectOnThreadLocalData
+// adjusted by OFFSETOF__CORINFO_Array__data, since all thread static bases are returned as relative to
+// the start of an array object.
HCIMPL0(void*, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase)
{
FCALL_CONTRACT;
- return (void*)&(((ThreadLocalData*)&t_ThreadStatics)->pThread);
+ return (void*)((uint8_t*)&(((ThreadLocalData*)&t_ThreadStatics)->pThread) - OFFSETOF__CORINFO_Array__data);
}
HCIMPLEND
From dd91a8f47daa38fb671dd92f34358aa17f46df9a Mon Sep 17 00:00:00 2001
From: David Wrighton
Date: Sat, 31 Jan 2026 02:40:09 +0000
Subject: [PATCH 13/14] Update src/coreclr/vm/jithelpers.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
src/coreclr/vm/jithelpers.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 313da93c7c745d..7680dc6406b126 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -373,7 +373,7 @@ HCIMPLEND
// *** This helper corresponds CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE.
// Returns the address of the pThread field within thread-local storage, which serves as the storage
-// location for the DirectOnThreadLocalData.pNativeThread thread static field. Adjust the value by adjusted by
+// location for the DirectOnThreadLocalData.pNativeThread thread static field. Adjust the value by
// OFFSETOF__CORINFO_Array__data, since all thread static bases are returned as relative to the start of an
// array object.
HCIMPL0(void*, JIT_GetDirectOnThreadLocalDataNonGCThreadStaticBase)
From 16cd619c35086670bbd4dc547c414332abed87c9 Mon Sep 17 00:00:00 2001
From: David Wrighton
Date: Sat, 31 Jan 2026 02:40:34 +0000
Subject: [PATCH 14/14] Update
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index 26843eb2c267bc..b75f78f3506431 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -1291,7 +1291,7 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
case CorInfoHelpFunc.CORINFO_HELP_THROW_NOT_IMPLEMENTED:
// For x86 tailcall where helper is required we need runtime JIT.
case CorInfoHelpFunc.CORINFO_HELP_TAILCALL:
- // DirectOnThreadLocalData helper should not be used in R2R compilation
+ // DirectOnThreadLocalData helper is used at runtime during R2R fixup resolution, not during R2R compilation
case CorInfoHelpFunc.CORINFO_HELP_GETDIRECTONTHREADLOCALDATA_NONGCTHREADSTATIC_BASE:
throw new RequiresRuntimeJitException(ftnNum.ToString());