From 133119bf1c949fcccc2e5b48e634ddcb696b1626 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 29 Apr 2026 05:48:54 +0000
Subject: [PATCH 1/5] Initial plan
From 8f5e75b27bc4562d813f0535ccb84bbcbff4ea2d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 29 Apr 2026 06:12:12 +0000
Subject: [PATCH 2/5] Optimize flat expression node linking and constant
storage
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/2a513704-9110-413d-9b3c-70aac362cee3
Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
---
.../FlatExpression.cs | 430 ++++++++++++------
.../LightExpressionTests.cs | 31 +-
2 files changed, 320 insertions(+), 141 deletions(-)
diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
index 52654ef8..a83564b6 100644
--- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
+++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
@@ -49,17 +49,18 @@ public enum ExprNodeKind : byte
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct ExprNode
{
- private const int NodeTypeShift = 56;
- private const int TagShift = 48;
- private const int NextShift = 32;
+ internal const byte ReferenceFlag = 4;
+ internal const byte InlineConstantFlag = 8;
+ private const int NodeTypeShift = 24;
+ private const int TagShift = 16;
private const int CountShift = 16;
- private const ulong IndexMask = 0xFFFF;
- private const ulong KindMask = 0x0F;
- private const ulong NextMask = IndexMask << NextShift;
- private const ulong ChildCountMask = IndexMask << CountShift;
- private const ulong ChildInfoMask = ChildCountMask | IndexMask;
- private const ulong KeepWithoutNextMask = ~NextMask;
- private const ulong KeepWithoutChildInfoMask = ~ChildInfoMask;
+ private const uint IndexMask = 0xFFFF;
+ private const uint KindMask = 0x0F;
+ private const uint NextMask = IndexMask;
+ private const uint ChildCountMask = IndexMask << CountShift;
+ private const uint ChildInfoMask = ChildCountMask | IndexMask;
+ private const uint KeepWithoutNextMask = ~NextMask;
+ private const uint KeepWithoutChildInfoMask = ~ChildInfoMask;
private const int FlagsShift = 4;
/// Gets or sets the runtime type of the represented node.
@@ -70,18 +71,20 @@ public struct ExprNode
[FieldOffset(8)]
public object Obj;
[FieldOffset(16)]
- private ulong _data;
+ private uint _data;
+ [FieldOffset(20)]
+ private uint _meta;
/// Gets the expression kind encoded for this node.
- public ExpressionType NodeType => (ExpressionType)((_data >> NodeTypeShift) & 0xFF);
+ public ExpressionType NodeType => (ExpressionType)((_meta >> NodeTypeShift) & 0xFF);
/// Gets the payload classification for this node.
- public ExprNodeKind Kind => (ExprNodeKind)((_data >> TagShift) & KindMask);
+ public ExprNodeKind Kind => (ExprNodeKind)((_meta >> TagShift) & KindMask);
- internal byte Flags => (byte)(((byte)(_data >> TagShift)) >> FlagsShift);
+ internal byte Flags => (byte)(((byte)(_meta >> TagShift)) >> FlagsShift);
/// Gets the next sibling node index in the intrusive child chain.
- public int NextIdx => (int)((_data >> NextShift) & IndexMask);
+ public int NextIdx => DecodeNext(_meta & IndexMask);
/// Gets the number of direct children linked from this node.
public int ChildCount => (int)((_data >> CountShift) & IndexMask);
@@ -89,28 +92,32 @@ public struct ExprNode
/// Gets the first child index or an auxiliary payload index.
public int ChildIdx => (int)(_data & IndexMask);
- internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0, int nextIdx = 0)
+ internal uint ConstantData => _data;
+
+ internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0, int nextIdx = -1)
{
Type = type;
Obj = obj;
var tag = (byte)((flags << FlagsShift) | (byte)kind);
- _data = ((ulong)(byte)nodeType << NodeTypeShift)
- | ((ulong)tag << TagShift)
- | ((ulong)(ushort)nextIdx << NextShift)
- | ((ulong)(ushort)childCount << CountShift)
- | (ushort)childIdx;
+ _data = ((uint)(ushort)childCount << CountShift) | (ushort)childIdx;
+ _meta = ((uint)(byte)nodeType << NodeTypeShift)
+ | ((uint)tag << TagShift)
+ | EncodeNext(nextIdx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetNextIdx(int nextIdx) =>
- _data = (_data & KeepWithoutNextMask) | ((ulong)(ushort)nextIdx << NextShift);
+ _meta = (_meta & KeepWithoutNextMask) | EncodeNext(nextIdx);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetChildInfo(int childIdx, int childCount) =>
_data = (_data & KeepWithoutChildInfoMask)
- | ((ulong)(ushort)childCount << CountShift)
+ | ((uint)(ushort)childCount << CountShift)
| (ushort)childIdx;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetConstantData(uint data) => _data = data;
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Is(ExprNodeKind kind) => Kind == kind;
@@ -121,8 +128,16 @@ internal void SetChildInfo(int childIdx, int childCount) =>
internal bool HasFlag(byte flag) => (Flags & flag) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool ShouldCloneWhenLinked() =>
- Kind == ExprNodeKind.LabelTarget || NodeType == ExpressionType.Parameter || Kind == ExprNodeKind.ObjectReference || ChildCount == 0;
+ internal bool IsReference() => HasFlag(ReferenceFlag);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool HasNextIdx() => (_meta & IndexMask) != 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint EncodeNext(int nextIdx) => nextIdx >= 0 ? (uint)(ushort)(nextIdx + 1) : 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int DecodeNext(uint encodedNext) => encodedNext == 0 ? -1 : (int)encodedNext - 1;
}
/// Stores an expression tree as a flat node array plus out-of-line closure constants.
@@ -169,7 +184,10 @@ public int Constant(object value) =>
/// Adds a constant node with an explicit constant type.
public int Constant(object value, Type type)
{
- if (ShouldInlineConstant(value, type))
+ if (TryGetInplaceConstantData(value, type, out var data))
+ return AddInlineConstantNode(type, data);
+
+ if (ShouldStoreBoxedConstant(value, type))
return AddRawExpressionNode(type, value, ExpressionType.Constant);
var constantIndex = ClosureConstants.Add(value);
@@ -180,7 +198,7 @@ public int Constant(object value, Type type)
public int ConstantNull(Type type = null) => AddRawExpressionNode(type ?? typeof(object), null, ExpressionType.Constant);
/// Adds an constant node.
- public int ConstantInt(int value) => AddRawExpressionNode(typeof(int), value, ExpressionType.Constant);
+ public int ConstantInt(int value) => AddInlineConstantNode(typeof(int), unchecked((uint)value));
/// Adds a typed constant node.
public int ConstantOf(T value) => Constant(value, typeof(T));
@@ -546,28 +564,28 @@ public SysExpr ToExpression() =>
public LightExpression ToLightExpression() => FastExpressionCompiler.LightExpression.FromSysExpressionConverter.ToLightExpression(ToExpression());
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int child) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, CloneChild(child));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, child);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int child) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(child));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, child);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1, c2);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1, c2, c3);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1, c2, c3, c4);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4, int c5) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4), CloneChild(c5));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1, c2, c3, c4, c5);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4, int c5, int c6) =>
- AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4), CloneChild(c5), CloneChild(c6));
+ AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, c0, c1, c2, c3, c4, c5, c6);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int[] children)
{
@@ -583,21 +601,14 @@ private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeT
case 7: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2], children[3], children[4], children[5], children[6]);
}
- var cloned = CloneChildren(children);
- return AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in cloned);
+ return AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, children);
}
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, in ChildList children)
- {
- var cloned = CloneChildren(children);
- return AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in cloned);
- }
+ => AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in children);
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, in ChildList children)
- {
- var cloned = CloneChildren(children);
- return AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, in cloned);
- }
+ => AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, in children);
private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType) =>
AddLeafNode(type, obj, nodeType, ExprNodeKind.Expression, 0, 0, 0);
@@ -614,29 +625,30 @@ private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType,
private int AddRawLeafExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags = 0, int childIdx = 0, int childCount = 0) =>
AddLeafNode(type, obj, nodeType, ExprNodeKind.Expression, flags, childIdx, childCount);
+ private int AddInlineConstantNode(Type type, uint data)
+ {
+ var nodeIndex = AddRawLeafExpressionNode(type, null, ExpressionType.Constant, ExprNode.InlineConstantFlag);
+ Nodes.GetSurePresentRef(nodeIndex).SetConstantData(data);
+ return nodeIndex;
+ }
+
private int AddRawExpressionNodeWithChildIndex(Type type, object obj, ExpressionType nodeType, int childIdx) =>
AddRawLeafExpressionNode(type, obj, nodeType, childIdx: childIdx);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, int child) =>
- AddNode(type, obj, ExpressionType.Extension, kind, flags, CloneChild(child));
+ AddNode(type, obj, ExpressionType.Extension, kind, flags, child);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, int child) =>
AddFactoryAuxNode(type, obj, kind, 0, child);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, int child0, int child1) =>
- AddNode(type, obj, ExpressionType.Extension, kind, flags, CloneChild(child0), CloneChild(child1));
+ AddNode(type, obj, ExpressionType.Extension, kind, flags, child0, child1);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, int[] children)
- {
- var cloned = CloneChildren(children);
- return AddNode(type, obj, ExpressionType.Extension, kind, 0, in cloned);
- }
+ => AddNode(type, obj, ExpressionType.Extension, kind, 0, children);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, in ChildList children)
- {
- var cloned = CloneChildren(children);
- return AddNode(type, obj, ExpressionType.Extension, kind, flags, in cloned);
- }
+ => AddNode(type, obj, ExpressionType.Extension, kind, flags, in children);
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, in ChildList children) =>
AddFactoryAuxNode(type, obj, kind, 0, in children);
@@ -698,8 +710,7 @@ private int AddExpression(SysExpr expression)
case ExpressionType.Parameter:
{
var parameter = (SysParameterExpression)expression;
- return _tree.AddRawLeafExpressionNode(expression.Type, parameter.Name, expression.NodeType,
- parameter.IsByRef ? ParameterByRefFlag : (byte)0, childIdx: GetId(ref _parameterIds, parameter));
+ return AddParameter(parameter);
}
case ExpressionType.Lambda:
{
@@ -936,7 +947,10 @@ private int AddExpression(SysExpr expression)
private int AddConstant(System.Linq.Expressions.ConstantExpression constant)
{
- if (ShouldInlineConstant(constant.Value, constant.Type))
+ if (TryGetInplaceConstantData(constant.Value, constant.Type, out var data))
+ return _tree.AddInlineConstantNode(constant.Type, data);
+
+ if (ShouldStoreBoxedConstant(constant.Value, constant.Type))
return _tree.AddRawExpressionNode(constant.Type, constant.Value, constant.NodeType);
var constantIndex = _tree.ClosureConstants.Add(constant.Value);
@@ -964,8 +978,15 @@ private int AddCatchBlock(SysCatchBlock catchBlock)
(byte)((catchBlock.Variable != null ? CatchHasVariableFlag : 0) | (catchBlock.Filter != null ? CatchHasFilterFlag : 0)), in children);
}
- private int AddLabelTarget(SysLabelTarget target) =>
- _tree.AddRawLeafAuxNode(target.Type, target.Name, ExprNodeKind.LabelTarget, childIdx: GetId(ref _labelIds, target));
+ private int AddLabelTarget(SysLabelTarget target)
+ {
+ ref var nodeIndex = ref _labelIds.Map.AddOrGetValueRef(target, out var found);
+ if (found)
+ return nodeIndex;
+
+ var id = _tree.Nodes.Count + 1;
+ return nodeIndex = _tree.AddRawLeafAuxNode(target.Type, target.Name, ExprNodeKind.LabelTarget, childIdx: id);
+ }
private int AddMemberBinding(SysMemberBinding binding)
{
@@ -1005,12 +1026,15 @@ private int AddElementInit(SysElementInit init)
return _tree.AddRawAuxNode(init.AddMethod.DeclaringType, init.AddMethod, ExprNodeKind.ElementInit, children);
}
- private static int GetId(ref SmallMap16