From 7c753a202ccf3442fc16f837bdd3f1b97192878f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Thu, 11 Jun 2026 19:22:11 +0200 Subject: [PATCH 1/6] Optimize NativeAOT delegate field layout --- .../src/System/Delegate.cs | 4 ++-- .../tools/Common/JitInterface/CorInfoImpl.cs | 4 ++-- .../Compiler/TypePreinit.cs | 20 +++++++++---------- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 532e84cad3138a..a046efc1b1a4e0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -43,10 +43,10 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al // New Delegate Implementation - private object _firstParameter; private object _helperObject; - private nint _extraFunctionPointerOrData; + private object _firstParameter; private IntPtr _functionPointer; + private nint _extraFunctionPointerOrData; // _helperObject may point to an array of delegates if this is a multicast delegate. We use this wrapper to distinguish between // our own array of delegates and user provided Wrapper[]. As a added benefit, this wrapper also eliminates array co-variance diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 5638e885771ee5..133c9a9eaa1389 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3451,8 +3451,8 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame; - pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate::_firstParameter - pEEInfoOut.offsetOfDelegateFirstTarget = OffsetOfDelegateFirstTarget; + pEEInfoOut.offsetOfDelegateInstance = OffsetOfDelegateInstance; + pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._functionPointer / _methodPtr pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs index 83218e7cf139cf..7529da04eeafba 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs @@ -3295,34 +3295,34 @@ public void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, No { Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeOpenStaticThunk"u8); - // _firstParameter - builder.EmitPointerReloc(thisNode); - // _helperObject builder.EmitZeroPointer(); - // _extraFunctionPointerOrData - builder.EmitPointerReloc(creationInfo.GetTargetNode(factory)); + // _firstParameter + builder.EmitPointerReloc(thisNode); // _functionPointer Debug.Assert(creationInfo.Thunk != null); builder.EmitPointerReloc(creationInfo.Thunk); + + // _extraFunctionPointerOrData + builder.EmitPointerReloc(creationInfo.GetTargetNode(factory)); } else { Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeClosedInstance"u8); - // _firstParameter - _firstParameter.WriteFieldData(ref builder, factory); - // _helperObject builder.EmitZeroPointer(); - // _extraFunctionPointerOrData - builder.EmitZeroPointer(); + // _firstParameter + _firstParameter.WriteFieldData(ref builder, factory); // _functionPointer builder.EmitPointerReloc(creationInfo.GetTargetNode(factory)); + + // _extraFunctionPointerOrData + builder.EmitZeroPointer(); } } 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 8df87b8ac5cde4..e9c226bf9d1f9a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -477,7 +477,7 @@ unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORECLR_ABI; - private uint OffsetOfDelegateFirstTarget => (uint)(3 * PointerSize); // Delegate._methodPtr + private uint OffsetOfDelegateInstance => (uint)PointerSize; // Delegate._target private readonly ReadyToRunCodegenCompilation _compilation; private MethodWithGCInfo _methodCodeNode; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 07450bd48f0d82..b2bd75ee872bfc 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -29,7 +29,7 @@ internal unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_NATIVEAOT_ABI; - private uint OffsetOfDelegateFirstTarget => (uint)(4 * PointerSize); // Delegate._functionPointer + private uint OffsetOfDelegateInstance => 2 * (uint)PointerSize; // Delegate._firstParameter private int SizeOfReversePInvokeTransitionFrame => 2 * PointerSize; private RyuJitCompilation _compilation; From 00f0d31e364e506a3e99ca55ce50960542ad3fcd Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 11 Jun 2026 21:10:25 -0700 Subject: [PATCH 2/6] Apply suggestion from @jkotas --- .../nativeaot/System.Private.CoreLib/src/System/Delegate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index a046efc1b1a4e0..4673fb4e8cf0e5 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -44,7 +44,7 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al // New Delegate Implementation private object _helperObject; - private object _firstParameter; + private object _firstParameter; // Keep _firstParameter and _functionPointer next to each other for optimal delegate invoke performance private IntPtr _functionPointer; private nint _extraFunctionPointerOrData; From 1f5208b999241f4b646e53dc243d683b903e5399 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 11 Jun 2026 21:10:50 -0700 Subject: [PATCH 3/6] Apply suggestion from @jkotas --- .../nativeaot/System.Private.CoreLib/src/System/Delegate.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 4673fb4e8cf0e5..9968de32d01574 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -41,8 +41,6 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al throw new PlatformNotSupportedException(); } - // New Delegate Implementation - private object _helperObject; private object _firstParameter; // Keep _firstParameter and _functionPointer next to each other for optimal delegate invoke performance private IntPtr _functionPointer; From 0f7493280ea4d05ee7839d5a62aef15ae485e991 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 11 Jun 2026 21:36:37 -0700 Subject: [PATCH 4/6] Apply suggestion from @jkotas --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 133c9a9eaa1389..f36f5701b54275 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3451,8 +3451,13 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame; - pEEInfoOut.offsetOfDelegateInstance = OffsetOfDelegateInstance; - pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._functionPointer / _methodPtr +#if READYTORUN + pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate._target + pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._methodPtr +#else + pEEInfoOut.offsetOfDelegateInstance = 2 * (uint)pointerSize; // Delegate._firstParameter + pEEInfoOut.offsetOfDelegateFirstTarget = 3 * (uint)pointerSize; // Delegate._functionPointer +#endif pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame; From 6658b51e2aa107e9f5c538a00deb75818bedefbd Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 11 Jun 2026 21:37:02 -0700 Subject: [PATCH 5/6] Apply suggestion from @jkotas --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 -- 1 file changed, 2 deletions(-) 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 e9c226bf9d1f9a..6537dd111808a6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -477,8 +477,6 @@ unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORECLR_ABI; - private uint OffsetOfDelegateInstance => (uint)PointerSize; // Delegate._target - private readonly ReadyToRunCodegenCompilation _compilation; private MethodWithGCInfo _methodCodeNode; private MethodColdCodeNode _methodColdCodeNode; From 1dc0e8c78a59f72a0420113fba075db5955d1456 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 11 Jun 2026 21:37:24 -0700 Subject: [PATCH 6/6] Apply suggestion from @jkotas --- .../aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index b2bd75ee872bfc..185f3863a3eb8f 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -29,7 +29,6 @@ internal unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_NATIVEAOT_ABI; - private uint OffsetOfDelegateInstance => 2 * (uint)PointerSize; // Delegate._firstParameter private int SizeOfReversePInvokeTransitionFrame => 2 * PointerSize; private RyuJitCompilation _compilation;