From 7c9504753020ce9cf44e3bb32e3b2d724e176276 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 13 Apr 2026 22:04:02 +0000
Subject: [PATCH 1/4] Initial plan
From f86f658b288af8c74e4f94f24ec45cb2a582b50f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 13 Apr 2026 22:31:47 +0000
Subject: [PATCH 2/4] Add IL emit hack: SUPPORTS_IL_EMIT_HACK, UseILEmitHack
flag, hack emit helpers, NET10 benchmark class
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/dc0b3120-928b-481e-ac2d-183f3609f945
Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
---
.../FastExpressionCompiler.cs | 323 +++++++++++++++++-
.../EmitHacks.cs | 60 +++-
.../FastExpressionCompiler.Benchmarks.csproj | 4 +-
.../EmitHacksTest.cs | 48 ++-
4 files changed, 427 insertions(+), 8 deletions(-)
diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs
index 21af4134..ed7bd885 100644
--- a/src/FastExpressionCompiler/FastExpressionCompiler.cs
+++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs
@@ -41,6 +41,13 @@ THE SOFTWARE.
#define SUPPORTS_IL_GENERATOR_REUSE
#endif
+// Enables direct IL stream writes for token-emitting opcodes (Call, Ldfld, Newobj, etc.)
+// using UnsafeAccessorType introduced in .NET 10.
+// Set ILGeneratorTools.UseILEmitHack = false to fall back to ILGenerator.Emit() for debugging.
+#if NET10_0_OR_GREATER
+#define SUPPORTS_IL_EMIT_HACK
+#endif
+
#if LIGHT_EXPRESSION
#define SUPPORTS_ARGUMENT_PROVIDER
#endif
@@ -63,6 +70,7 @@ namespace FastExpressionCompiler
using static FastExpressionCompiler.ImTools.SmallMap;
#endif
using System;
+ using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -8616,6 +8624,16 @@ public static class ILGeneratorTools
public static bool DisableILGeneratorPooling;
/// Configuration option to disable the ILGenerator Emit debug output
public static bool DisableDemit;
+#if SUPPORTS_IL_EMIT_HACK
+ ///
+ /// When true, uses direct IL stream writes for token-emitting opcodes
+ /// (Call, Callvirt, Newobj, Ldfld, Stfld, etc.) bypassing ILGenerator.Emit() overhead.
+ /// Set to false to fall back to standard ILGenerator.Emit() (e.g. for benchmarking or debugging).
+ /// Starts as false and is enabled by DynamicMethodHacks static initializer once the required
+ /// internals (m_scope, etc.) have been resolved successfully.
+ ///
+ public static bool UseILEmitHack;
+#endif
#if DEMIT
[MethodImpl((MethodImplOptions)256)]
@@ -8773,13 +8791,31 @@ public static void Demit(this ILGenerator il, string value, OpCode opcode, [Call
public static void Demit(this ILGenerator il, OpCode opcode, Type value) => il.Emit(opcode, value);
[MethodImpl((MethodImplOptions)256)]
- public static void Demit(this ILGenerator il, OpCode opcode, FieldInfo value) => il.Emit(opcode, value);
+ public static void Demit(this ILGenerator il, OpCode opcode, FieldInfo value)
+ {
+#if SUPPORTS_IL_EMIT_HACK
+ if (UseILEmitHack) { DynamicMethodHacks.HackEmitField(il, opcode, value); return; }
+#endif
+ il.Emit(opcode, value);
+ }
[MethodImpl((MethodImplOptions)256)]
- public static void Demit(this ILGenerator il, OpCode opcode, MethodInfo value) => il.Emit(opcode, value);
+ public static void Demit(this ILGenerator il, OpCode opcode, MethodInfo value)
+ {
+#if SUPPORTS_IL_EMIT_HACK
+ if (UseILEmitHack) { DynamicMethodHacks.HackEmitMethod(il, opcode, value); return; }
+#endif
+ il.Emit(opcode, value);
+ }
[MethodImpl((MethodImplOptions)256)]
- public static void Demit(this ILGenerator il, OpCode opcode, ConstructorInfo value) => il.Emit(opcode, value);
+ public static void Demit(this ILGenerator il, OpCode opcode, ConstructorInfo value)
+ {
+#if SUPPORTS_IL_EMIT_HACK
+ if (UseILEmitHack) { DynamicMethodHacks.HackEmitCtor(il, opcode, value); return; }
+#endif
+ il.Emit(opcode, value);
+ }
[MethodImpl((MethodImplOptions)256)]
public static void Demit(this ILGenerator il, OpCode opcode, Label value) => il.Emit(opcode, value);
@@ -8813,6 +8849,91 @@ public static void Demit(this ILGenerator il, string value, OpCode opcode, [Call
[MethodImpl((MethodImplOptions)256)]
public static void Demit(this ILGenerator il, string value, OpCode opcode) => il.Emit(opcode, value);
+
+#if SUPPORTS_IL_EMIT_HACK
+ // ─────────────────────────────────────────────────────────────────────────────
+ // Specialized emit helpers for when the call site already knows method metadata.
+ // These skip the IsStatic / ReturnType / GetParameters checks inside Demit(),
+ // reducing overhead further when emitting many related calls in sequence.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ /// Emits call to a static method that returns void.
+ [MethodImpl((MethodImplOptions)256)]
+ public static void DemitCallStaticVoid(this ILGenerator il, MethodInfo meth, int paramCount)
+ {
+ if (UseILEmitHack)
+ {
+ var dt = meth.DeclaringType;
+ if ((dt == null || (!dt.IsGenericType && !dt.IsArray)) && il.GetType() == DynamicMethodHacks.DynamicILGeneratorType)
+ { DynamicMethodHacks.HackEmitMethodToken(il, OpCodes.Call, meth.MethodHandle, -paramCount); return; }
+ }
+ il.Emit(OpCodes.Call, meth);
+ }
+
+ /// Emits call to a static method that returns a value (pushes +1).
+ [MethodImpl((MethodImplOptions)256)]
+ public static void DemitCallStatic(this ILGenerator il, MethodInfo meth, int paramCount)
+ {
+ if (UseILEmitHack)
+ {
+ var dt = meth.DeclaringType;
+ if ((dt == null || (!dt.IsGenericType && !dt.IsArray)) && il.GetType() == DynamicMethodHacks.DynamicILGeneratorType)
+ { DynamicMethodHacks.HackEmitMethodToken(il, OpCodes.Call, meth.MethodHandle, 1 - paramCount); return; }
+ }
+ il.Emit(OpCodes.Call, meth);
+ }
+
+ /// Emits call/callvirt to an instance method that returns void.
+ [MethodImpl((MethodImplOptions)256)]
+ public static void DemitCallInstanceVoid(this ILGenerator il, OpCode opcode, MethodInfo meth, int paramCount)
+ {
+ if (UseILEmitHack)
+ {
+ var dt = meth.DeclaringType;
+ if ((dt == null || (!dt.IsGenericType && !dt.IsArray)) && il.GetType() == DynamicMethodHacks.DynamicILGeneratorType)
+ { DynamicMethodHacks.HackEmitMethodToken(il, opcode, meth.MethodHandle, -paramCount - 1); return; }
+ }
+ il.Emit(opcode, meth);
+ }
+
+ /// Emits call/callvirt to an instance method that returns a value (net stack: -paramCount).
+ [MethodImpl((MethodImplOptions)256)]
+ public static void DemitCallInstance(this ILGenerator il, OpCode opcode, MethodInfo meth, int paramCount)
+ {
+ if (UseILEmitHack)
+ {
+ var dt = meth.DeclaringType;
+ if ((dt == null || (!dt.IsGenericType && !dt.IsArray)) && il.GetType() == DynamicMethodHacks.DynamicILGeneratorType)
+ { DynamicMethodHacks.HackEmitMethodToken(il, opcode, meth.MethodHandle, -paramCount); return; }
+ }
+ il.Emit(opcode, meth);
+ }
+
+ /// Emits newobj for a constructor (net stack: 1 - paramCount).
+ [MethodImpl((MethodImplOptions)256)]
+ public static void DemitNewobj(this ILGenerator il, ConstructorInfo ctor, int paramCount)
+ {
+ if (UseILEmitHack)
+ {
+ var dt = ctor.DeclaringType;
+ if ((dt == null || (!dt.IsGenericType && !dt.IsArray)) && il.GetType() == DynamicMethodHacks.DynamicILGeneratorType)
+ { DynamicMethodHacks.HackEmitMethodToken(il, OpCodes.Newobj, ctor.MethodHandle, 1 - paramCount); return; }
+ }
+ il.Emit(OpCodes.Newobj, ctor);
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // ILEmitContext: caches refs to m_length and m_ILStream for a sequence of emits,
+ // eliminating the per-call UAT field accesses when emitting multiple instructions.
+ //
+ // Usage (NET10+ only, ref struct so stack-only):
+ // var ctx = new DynamicMethodHacks.ILEmitContext(il);
+ // ctx.EmitBatch(OpCodes.Ldarg_0, /* stackChange */ 1,
+ // OpCodes.Ldfld, fieldHandle, /* stackChange */ 0);
+ //
+ // todo: @perf wire up call sites once the batch helpers cover enough patterns
+ // ─────────────────────────────────────────────────────────────────────────────
+#endif
#endif
}
@@ -8880,6 +9001,12 @@ public static ILGenerator RentPooledOrNewILGenerator(DynamicMethod dynMethod, Ty
internal static FieldInfo ILGeneratorField;
internal static Type DynamicILGeneratorType;
+#if SUPPORTS_IL_EMIT_HACK
+ // m_scope is on DynamicILGenerator (non-public type). Since its return type DynamicScope is also
+ // non-public, UnsafeAccessorType cannot express this field access — reflection is still used here.
+ // m_tokens on the returned scope is then accessed via UnsafeAccessorType (GetMTokens below).
+ internal static FieldInfo _mScopeField;
+#endif
static DynamicMethodHacks()
{
@@ -8893,6 +9020,9 @@ static DynamicMethodHacks()
return; // nothing to do here
DynamicILGeneratorType = ILGeneratorField.FieldType;
+#if SUPPORTS_IL_EMIT_HACK
+ _mScopeField = DynamicILGeneratorType.GetField("m_scope", instanceNonPublic);
+#endif
// Avoid demit polluting the output of the the initialization phase
var prevDemitValue = ILGeneratorTools.DisableDemit;
@@ -9354,6 +9484,12 @@ public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
// Restore the demit
ILGeneratorTools.DisableDemit = prevDemitValue;
+#if SUPPORTS_IL_EMIT_HACK
+ // Enable the IL emit hack now that initialization is complete.
+ // Only activate if _mScopeField was successfully resolved (required for GetScopeTokens).
+ if (_mScopeField != null)
+ ILGeneratorTools.UseILEmitHack = true;
+#endif
// ## 3 TBD
//
@@ -9511,6 +9647,187 @@ internal static extern void UpdateStackSize(
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_tokens")]
internal static extern ref System.Collections.Generic.List