diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
index 52654ef8..a6b79907 100644
--- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
+++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
@@ -49,17 +49,14 @@ 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;
+ 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 KeepWithoutNextMask = ~NextMask;
+ private const uint KeepWithoutTagAndNextMask = ~(NextMask | (0xFFU << TagShift));
private const int FlagsShift = 4;
/// Gets or sets the runtime type of the represented node.
@@ -70,18 +67,21 @@ public struct ExprNode
[FieldOffset(8)]
public object Obj;
[FieldOffset(16)]
- private ulong _data;
+ private uint _data;
+
+ [FieldOffset(20)]
+ private uint _nodeTypeAndKind;
/// Gets the expression kind encoded for this node.
- public ExpressionType NodeType => (ExpressionType)((_data >> NodeTypeShift) & 0xFF);
+ public ExpressionType NodeType => (ExpressionType)((_nodeTypeAndKind >> NodeTypeShift) & 0xFF);
/// Gets the payload classification for this node.
- public ExprNodeKind Kind => (ExprNodeKind)((_data >> TagShift) & KindMask);
+ public ExprNodeKind Kind => (ExprNodeKind)((_nodeTypeAndKind >> TagShift) & KindMask);
- internal byte Flags => (byte)(((byte)(_data >> TagShift)) >> FlagsShift);
+ internal byte Flags => (byte)(((byte)(_nodeTypeAndKind >> TagShift)) >> FlagsShift);
/// Gets the next sibling node index in the intrusive child chain.
- public int NextIdx => (int)((_data >> NextShift) & IndexMask);
+ public int NextIdx => (int)(_nodeTypeAndKind & IndexMask);
/// Gets the number of direct children linked from this node.
public int ChildCount => (int)((_data >> CountShift) & IndexMask);
@@ -89,28 +89,42 @@ 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);
+
internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0, int nextIdx = 0)
{
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)
+ _data = ((uint)(ushort)childCount << CountShift)
| (ushort)childIdx;
+ _nodeTypeAndKind = ((uint)(byte)nodeType << NodeTypeShift)
+ | ((uint)tag << TagShift)
+ | (ushort)nextIdx;
}
[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)(_nodeTypeAndKind >> TagShift);
+ var nextPointsToParentMask = (byte)(ExprTree.NextPointsToParentFlag << FlagsShift);
+ tag = pointsToParent
+ ? (byte)(tag | nextPointsToParentMask)
+ : (byte)(tag & ~nextPointsToParentMask);
+ _nodeTypeAndKind = (_nodeTypeAndKind & KeepWithoutTagAndNextMask)
+ | ((uint)tag << TagShift)
+ | (ushort)nextIdx;
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetChildInfo(int childIdx, int childCount) =>
- _data = (_data & KeepWithoutChildInfoMask)
- | ((ulong)(ushort)childCount << CountShift)
+ _data = ((uint)(ushort)childCount << CountShift)
| (ushort)childIdx;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetValue32(int value) =>
+ _data = unchecked((uint)value);
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Is(ExprNodeKind kind) => Kind == kind;
@@ -120,22 +134,73 @@ 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;
}
+/// Stores owner-side metadata for a lambda node.
+public struct LambdaInfo
+{
+ /// Gets or sets the lambda owner node index.
+ public int LambdaIndex;
+
+ /// Gets or sets the lambda body node index.
+ public int BodyIndex;
+
+ /// Gets or sets the declaration node indexes owned by the lambda.
+ public ChildList ParameterDeclarations;
+
+ internal LambdaInfo(int lambdaIndex, int bodyIndex, in ChildList parameterDeclarations)
+ {
+ LambdaIndex = lambdaIndex;
+ BodyIndex = bodyIndex;
+ ParameterDeclarations = parameterDeclarations;
+ }
+}
+
+/// Stores owner-side metadata for a block node.
+public struct BlockInfo
+{
+ /// Gets or sets the block owner node index.
+ public int BlockIndex;
+
+ /// Gets or sets the child-list node index containing the block body expressions.
+ public int BodyExpressionsIndex;
+
+ /// Gets or sets the declaration node indexes owned by the block.
+ public ChildList VariableDeclarations;
+
+ internal BlockInfo(int blockIndex, int bodyExpressionsIndex, in ChildList variableDeclarations)
+ {
+ BlockIndex = blockIndex;
+ BodyExpressionsIndex = bodyExpressionsIndex;
+ VariableDeclarations = variableDeclarations;
+ }
+}
+
/// 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;
@@ -146,11 +211,38 @@ public struct ExprTree
/// Gets or sets closure constants that are referenced from constant nodes.
public SmallList