From c7dbf63d0e83410d4df3a0c4aa96df4f28e48464 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Jun 2026 00:58:33 +0000 Subject: [PATCH 1/4] Initial plan From abdb252d18ce577434785d4cadb693875a0190c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Jun 2026 01:24:21 +0000 Subject: [PATCH 2/4] Replace Environment.CurrentManagedThreadId FCall with ManagedThreadId.Current on CoreCLR On CoreCLR, `t_currentManagedThreadId` is set up by the type system to point to a special location populated with the thread's managed ID before any managed caller can observe it (the thin-lock implementation already relies on this guarantee). This change makes `Environment.CurrentManagedThreadId` simply delegate to `ManagedThreadId.Current`, turning an FCall into a cheap thread-static read, aligning CoreCLR with NativeAOT/Mono which already do the same. - Environment.CoreCLR.cs: replace InternalCall property with `=> Threading.ManagedThreadId.Current` - ecalllist.h: remove the now-unused `FCFuncElement("get_CurrentManagedThreadId", JIT_GetCurrentManagedThreadId)` `JIT_GetCurrentManagedThreadId` is kept in jithelpers.cpp and jitinterface.h because it is still needed as the native entry point for the JIT helper `CORINFO_HELP_GETCURRENTMANAGEDTHREADID` (used by the JIT when optimizing `Thread.CurrentThread.ManagedThreadId`). Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- .../src/System/Environment.CoreCLR.cs | 6 +----- src/coreclr/vm/ecalllist.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs index 71aff544f92c27..4877b1b13ab430 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -12,11 +12,7 @@ namespace System { public static partial class Environment { - public static extern int CurrentManagedThreadId - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } + public static int CurrentManagedThreadId => Threading.ManagedThreadId.Current; // Terminates this process with the given exit code. [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_Exit")] diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 0bcf6eb37169fc..d0ac7d28c6f747 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -68,7 +68,6 @@ FCFuncStart(gStringFuncs) FCFuncEnd() FCFuncStart(gEnvironmentFuncs) - FCFuncElement("get_CurrentManagedThreadId", JIT_GetCurrentManagedThreadId) FCFuncElement("set_ExitCode", EnvironmentNative::SetExitCode) FCFuncElement("get_ExitCode", EnvironmentNative::GetExitCode) FCFuncEnd() From 3c9cb1125586f10e0098c6629a5b402aaca7765d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Jun 2026 04:03:33 +0000 Subject: [PATCH 3/4] Bind GETCURRENTMANAGEDTHREADID helper to managed Environment.CurrentManagedThreadId; remove native FCall Make the CORINFO_HELP_GETCURRENTMANAGEDTHREADID JIT helper IL-based, binding it to Environment.get_CurrentManagedThreadId (now a thread-static read via ManagedThreadId.Current), following the MEMSET precedent. This lets the helper resolve to the managed property instead of native code, so the dedicated native JIT_GetCurrentManagedThreadId FCall is no longer needed and is removed. - corelib.h: add METHOD__ENVIRONMENT__CURRENT_MANAGED_THREAD_ID - jithelpers.h: GETCURRENTMANAGEDTHREADID -> DYNAMICJITHELPER bound to the method - jithelpers.cpp/jitinterface.h: delete JIT_GetCurrentManagedThreadId - Environment.CoreCLR.cs: keep AggressiveInlining on get_CurrentManagedThreadId Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- .../src/System/Environment.CoreCLR.cs | 6 +++++- src/coreclr/inc/jithelpers.h | 2 +- src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/jithelpers.cpp | 9 --------- src/coreclr/vm/jitinterface.h | 2 -- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs index 4877b1b13ab430..aeba970b274448 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -12,7 +12,11 @@ namespace System { public static partial class Environment { - public static int CurrentManagedThreadId => Threading.ManagedThreadId.Current; + public static int CurrentManagedThreadId + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Threading.ManagedThreadId.Current; + } // Terminates this process with the given exit code. [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_Exit")] diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 281302ccbc742c..7742945b23e7e4 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -224,7 +224,7 @@ JITHELPER(CORINFO_HELP_TAILCALL, NULL, METHOD__NIL) #endif - JITHELPER(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, JIT_GetCurrentManagedThreadId, METHOD__NIL) + DYNAMICJITHELPER(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, NULL, METHOD__ENVIRONMENT__CURRENT_MANAGED_THREAD_ID) JITHELPER(CORINFO_HELP_INIT_PINVOKE_FRAME, JIT_InitPInvokeFrame, METHOD__NIL) diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 2db9241ecbe031..fa6f4000edaa7e 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -278,6 +278,7 @@ DEFINE_CLASS(RUNE, Text, Rune) DEFINE_CLASS(ENUM, System, Enum) DEFINE_CLASS(ENVIRONMENT, System, Environment) +DEFINE_METHOD(ENVIRONMENT, CURRENT_MANAGED_THREAD_ID, get_CurrentManagedThreadId, NoSig) DEFINE_METHOD(ENVIRONMENT, GET_RESOURCE_STRING, GetResourceString, SM_PtrChar_PtrStr_PtrException_RetVoid) DEFINE_METHOD(ENVIRONMENT, INITIALIZE_COMMAND_LINE_ARGS, InitializeCommandLineArgs, SM_PtrChar_Int_PtrPtrChar_PtrArrStr_PtrException_RetVoid) DEFINE_METHOD(ENVIRONMENT, CALL_ENTRY_POINT, CallEntryPoint, SM_IntPtr_PtrArrStr_PtrInt_Bool_PtrException_RetVoid) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index d5a7e922521e0c..42b5bc31a938f9 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1202,15 +1202,6 @@ void JIT_RareDisableHelper() } } -FCIMPL0(INT32, JIT_GetCurrentManagedThreadId) -{ - FCALL_CONTRACT; - - Thread * pThread = GetThread(); - return pThread->GetThreadId(); -} -FCIMPLEND - /*********************************************************************/ /* we don't use HCIMPL macros because we don't want the overhead even in debug mode */ diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index aebb381a0bf37d..d30c9278eb0a25 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -179,8 +179,6 @@ EXTERN_C FCDECL1(void*, JIT_GetGCStaticBase_Helper, MethodTable *pMT); EXTERN_C void DoJITFailFast(); EXTERN_C FCDECL0(void, JIT_FailFast); -FCDECL0(int, JIT_GetCurrentManagedThreadId); - EXTERN_C void ReversePInvokeBadTransition(); #if !defined(FEATURE_USE_ASM_GC_WRITE_BARRIERS) && defined(FEATURE_COUNT_GC_WRITE_BARRIERS) From bc2dae338edce59e0ace955b9317ef245a06c407 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Jun 2026 06:31:04 +0000 Subject: [PATCH 4/4] JIT: fold Thread.CurrentThread.ManagedThreadId to inlinable thread-static read Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- src/coreclr/jit/importercalls.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 8f6b7e101437d1..a0340343c58c44 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4314,7 +4314,21 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, // drop get_CurrentThread() call impPopStack(); call->ReplaceWith(gtNewNothingNode(), this); - retNode = gtNewHelperCallNode(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, TYP_INT); + GenTreeCall* tidCall = gtNewHelperCallNode(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, TYP_INT); + impConvertToUserCallAndMarkForInlining(tidCall); + if (tidCall->IsInlineCandidate()) + { + // The helper was converted into an inlinable user call; spill it to its own + // statement and hand back a GT_RET_EXPR so the inliner can fold the property. + impAppendTree(tidCall, CHECK_SPILL_ALL, impCurStmtDI, false); + GenTreeRetExpr* retExpr = gtNewInlineCandidateReturnExpr(tidCall, TYP_INT); + tidCall->GetSingleInlineCandidateInfo()->retExpr = retExpr; + retNode = retExpr; + } + else + { + retNode = tidCall; + } } } }