From 6cd2c03e052ed93eed85781ba4bbd27b83207d22 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Apr 2026 20:54:01 +0000
Subject: [PATCH 1/8] Initial plan
From c64ab98e6f6c3573ee417adba9a78f93925e7d41 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Apr 2026 21:10:33 +0000
Subject: [PATCH 2/8] Implement flat expression parameter and constant layout
updates
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/7d16be33-857d-4217-8cbc-c6e95d110f84
Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
---
.../FlatExpression.cs | 410 ++++++++++++++----
.../LightExpressionTests.cs | 45 +-
2 files changed, 377 insertions(+), 78 deletions(-)
diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
index 52654ef8..7083c296 100644
--- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
+++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
@@ -60,6 +60,7 @@ public struct ExprNode
private const ulong ChildInfoMask = ChildCountMask | IndexMask;
private const ulong KeepWithoutNextMask = ~NextMask;
private const ulong KeepWithoutChildInfoMask = ~ChildInfoMask;
+ private const ulong KeepWithoutTagAndNextMask = ~(NextMask | (0xFFUL << TagShift));
private const int FlagsShift = 4;
/// Gets or sets the runtime type of the represented node.
@@ -89,6 +90,8 @@ public struct ExprNode
/// Gets the first child index or an auxiliary payload index.
public int ChildIdx => (int)(_data & IndexMask);
+ internal int Value32 => unchecked((int)(_data & 0xFFFFFFFF));
+
internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0, int nextIdx = 0)
{
Type = type;
@@ -102,8 +105,17 @@ internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind k
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void SetNextIdx(int nextIdx) =>
- _data = (_data & KeepWithoutNextMask) | ((ulong)(ushort)nextIdx << NextShift);
+ internal void SetNextIdx(int nextIdx, bool pointsToParent = false)
+ {
+ var tag = (byte)(_data >> TagShift);
+ var nextPointsToParentMask = (byte)(ExprTree.NextPointsToParentFlag << FlagsShift);
+ tag = pointsToParent
+ ? (byte)(tag | nextPointsToParentMask)
+ : (byte)(tag & ~nextPointsToParentMask);
+ _data = (_data & KeepWithoutTagAndNextMask)
+ | ((ulong)tag << TagShift)
+ | ((ulong)(ushort)nextIdx << NextShift);
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetChildInfo(int childIdx, int childCount) =>
@@ -111,6 +123,11 @@ internal void SetChildInfo(int childIdx, int childCount) =>
| ((ulong)(ushort)childCount << CountShift)
| (ushort)childIdx;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetValue32(int value) =>
+ _data = (_data & KeepWithoutChildInfoMask)
+ | (uint)value;
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Is(ExprNodeKind kind) => Kind == kind;
@@ -120,6 +137,13 @@ internal void SetChildInfo(int childIdx, int childCount) =>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool HasFlag(byte flag) => (Flags & flag) != 0;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool NextPointsToParent() => HasFlag(ExprTree.NextPointsToParentFlag);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool IsParameterDeclaration() =>
+ NodeType == ExpressionType.Parameter && HasFlag(ExprTree.ParameterDeclarationFlag);
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool ShouldCloneWhenLinked() =>
Kind == ExprNodeKind.LabelTarget || NodeType == ExpressionType.Parameter || Kind == ExprNodeKind.ObjectReference || ChildCount == 0;
@@ -128,14 +152,17 @@ internal bool ShouldCloneWhenLinked() =>
/// Stores an expression tree as a flat node array plus out-of-line closure constants.
public struct ExprTree
{
- private static readonly object ClosureConstantMarker = new();
- private const byte ParameterByRefFlag = 1;
+ internal const byte ParameterByRefFlag = 1;
+ internal const byte ParameterDeclarationFlag = 2;
+ private const byte ConstantInlineValue32Flag = 1;
private const byte BinaryLiftedToNullFlag = 1;
private const byte LoopHasBreakFlag = 1;
private const byte LoopHasContinueFlag = 2;
private const byte CatchHasVariableFlag = 1;
private const byte CatchHasFilterFlag = 2;
private const byte TryFaultFlag = 1;
+ internal const byte NextPointsToParentFlag = 8;
+ private const ushort UnboundParameterScopeIndex = ushort.MaxValue;
/// Gets or sets the root node index.
public int RootIndex;
@@ -149,8 +176,10 @@ public struct ExprTree
/// Adds a parameter node and returns its index.
public int Parameter(Type type, string name = null)
{
- var id = Nodes.Count + 1;
- return AddRawLeafExpressionNode(type, name, ExpressionType.Parameter, type.IsByRef ? ParameterByRefFlag : (byte)0, childIdx: id);
+ var parameterType = type.IsByRef ? type.GetElementType() ?? type : type;
+ return AddRawLeafExpressionNode(parameterType, name, ExpressionType.Parameter,
+ (byte)((type.IsByRef ? ParameterByRefFlag : 0) | ParameterDeclarationFlag),
+ childIdx: UnboundParameterScopeIndex);
}
/// Adds a typed parameter node and returns its index.
@@ -169,18 +198,17 @@ 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))
- return AddRawExpressionNode(type, value, ExpressionType.Constant);
+ if (TryGetInlineConstantValue32(value, type, out var value32))
+ return AddRawInlineConstantNode(type, value32);
- var constantIndex = ClosureConstants.Add(value);
- return AddRawExpressionNodeWithChildIndex(type, ClosureConstantMarker, ExpressionType.Constant, constantIndex);
+ return AddRawExpressionNode(type, value, ExpressionType.Constant);
}
/// Adds a null constant node.
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) => AddRawInlineConstantNode(typeof(int), value);
/// Adds a typed constant node.
public int ConstantOf(T value) => Constant(value, typeof(T));
@@ -314,15 +342,18 @@ public int Block(Type type, IEnumerable variables, params int[] expressions
{
ChildList variableChildren = default;
foreach (var variable in variables)
- variableChildren.Add(variable);
+ variableChildren.Add(RequireParameterDeclarationIndex(variable));
if (variableChildren.Count != 0)
children.Add(AddChildListNode(in variableChildren));
}
ChildList bodyChildren = default;
for (var i = 0; i < expressions.Length; ++i)
- bodyChildren.Add(expressions[i]);
+ bodyChildren.Add(CloneChild(expressions[i]));
children.Add(AddChildListNode(in bodyChildren));
- return AddFactoryExpressionNode(type ?? Nodes[expressions[expressions.Length - 1]].Type, null, ExpressionType.Block, in children);
+ var blockIndex = AddNode(type ?? Nodes.GetSurePresentRef(expressions[expressions.Length - 1]).Type, null, ExpressionType.Block, ExprNodeKind.Expression, 0, in children);
+ if (variables != null && children.Count == 2)
+ BindParameterDeclarations(blockIndex, GetChildren(children[0]));
+ return blockIndex;
}
/// Adds a typed lambda node.
@@ -330,10 +361,25 @@ public int Lambda(int body, params int[] parameters) where TDelegate
Lambda(typeof(TDelegate), body, parameters);
/// Adds a lambda node.
- public int Lambda(Type delegateType, int body, params int[] parameters) =>
- parameters == null || parameters.Length == 0
- ? AddFactoryExpressionNode(delegateType, null, ExpressionType.Lambda, 0, body)
- : AddFactoryExpressionNode(delegateType, null, ExpressionType.Lambda, PrependToChildList(body, parameters));
+ public int Lambda(Type delegateType, int body, params int[] parameters)
+ {
+ if (parameters == null || parameters.Length == 0)
+ return AddFactoryExpressionNode(delegateType, null, ExpressionType.Lambda, 0, body);
+
+ ChildList children = default;
+ children.Add(CloneChild(body));
+ ChildList declarations = default;
+ for (var i = 0; i < parameters.Length; ++i)
+ {
+ var declarationIndex = RequireParameterDeclarationIndex(parameters[i]);
+ declarations.Add(declarationIndex);
+ children.Add(declarationIndex);
+ }
+
+ var lambdaIndex = AddNode(delegateType, null, ExpressionType.Lambda, ExprNodeKind.Expression, 0, in children);
+ BindParameterDeclarations(lambdaIndex, declarations);
+ return lambdaIndex;
+ }
/// Adds a member-assignment binding node.
public int Bind(System.Reflection.MemberInfo member, int expression) =>
@@ -614,6 +660,10 @@ 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 AddRawInlineConstantNode(Type type, int value32) =>
+ AddRawLeafExpressionNode(type, null, ExpressionType.Constant, ConstantInlineValue32Flag,
+ childIdx: (ushort)value32, childCount: (ushort)(value32 >> 16));
+
private int AddRawExpressionNodeWithChildIndex(Type type, object obj, ExpressionType nodeType, int childIdx) =>
AddRawLeafExpressionNode(type, obj, nodeType, childIdx: childIdx);
@@ -677,7 +727,7 @@ private static ChildList PrependToChildList(int first, int[] rest)
/// Builds the flat representation while preserving parameter and label identity with stack-friendly maps.
private struct Builder
{
- private SmallMap16