You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a mixed-mode (IJW/C++/CLI) assembly declares a native, file-scope __declspec(thread) global (wrapped in #pragma unmanaged/#pragma managed) and that global is accessed from a public ref class static method, calling into that method from managed code under a Checked CoreCLR build crashes with a JIT assertion:
The assertion fires in CEECodeGenInfo::getHelperFtn (src/coreclr/vm/jitinterface.cpp:11404, in a FEATURE_PORTABLE_ENTRYPOINTS code path), where the JIT's Lowering::LowerDirectCall requests a JIT helper function pointer and the helper table entry is unpopulated.
Repro
Minimal repro (Windows, x64, Checked build):
test.cpp (compiled as an IJW/C++CLI assembly, e.g. via /clr):
#pragma unmanaged
__declspec(thread) int s_tlsFieldData = 51966;
#pragma managed
public ref classTlsTest
{
public:staticintTest()
{
int value = s_tlsFieldData;
s_tlsFieldData = value;
s_tlsFieldData = 100;
return s_tlsFieldData;
}
};
Managed caller (any invocation mechanism — both plain reflection MethodInfo.Invoke and UnsafeAccessor reproduce identically):
Running this against a Checked CoreCLR build crashes with the assertion above. The same code runs successfully against Debug and Release builds. Switching the native storage from __declspec(thread) to a managed [System::ThreadStatic] field on the ref class avoids the crash entirely.
Analysis so far
The crash is independent of the managed→native call mechanism: it reproduces identically whether the native method is invoked via reflection (MethodInfo.Invoke) or via [UnsafeAccessor]/[UnsafeAccessorType].
The crash is tied specifically to __declspec(thread) TLS storage accessed through the IJW-hosted managed entry point; using [System::ThreadStatic] instead does not reproduce the crash.
This was discovered while converting src/tests regression tests that originally used IL .data tls declarations to C++/CLI (see Convert .data tls IL tests to C++/CLI #133533), where a reviewer requested using true native __declspec(thread) TLS (rather than [System::ThreadStatic]) "to actually be testing the right feature." Reintroducing __declspec(thread) reproduced this crash in all four converted tests.
Description
When a mixed-mode (IJW/C++/CLI) assembly declares a native, file-scope
__declspec(thread)global (wrapped in#pragma unmanaged/#pragma managed) and that global is accessed from apublic ref classstatic method, calling into that method from managed code under a Checked CoreCLR build crashes with a JIT assertion:The assertion fires in
CEECodeGenInfo::getHelperFtn(src/coreclr/vm/jitinterface.cpp:11404, in aFEATURE_PORTABLE_ENTRYPOINTScode path), where the JIT'sLowering::LowerDirectCallrequests a JIT helper function pointer and the helper table entry is unpopulated.Repro
Minimal repro (Windows, x64, Checked build):
test.cpp(compiled as an IJW/C++CLI assembly, e.g. via/clr):Managed caller (any invocation mechanism — both plain reflection
MethodInfo.InvokeandUnsafeAccessorreproduce identically):Running this against a Checked CoreCLR build crashes with the assertion above. The same code runs successfully against Debug and Release builds. Switching the native storage from
__declspec(thread)to a managed[System::ThreadStatic]field on theref classavoids the crash entirely.Analysis so far
MethodInfo.Invoke) or via[UnsafeAccessor]/[UnsafeAccessorType].__declspec(thread)TLS storage accessed through the IJW-hosted managed entry point; using[System::ThreadStatic]instead does not reproduce the crash.src/testsregression tests that originally used IL.data tlsdeclarations to C++/CLI (see Convert .data tls IL tests to C++/CLI #133533), where a reviewer requested using true native__declspec(thread)TLS (rather than[System::ThreadStatic]) "to actually be testing the right feature." Reintroducing__declspec(thread)reproduced this crash in all four converted tests.Configuration
/clr(IJW/C++CLI) mixed-mode assemblyNote
This issue was filed with assistance from GitHub Copilot.