diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
index 52654ef8..16fb28f8 100644
--- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
+++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs
@@ -23,6 +23,12 @@ public enum ExprNodeKind : byte
{
/// Represents a regular expression node.
Expression,
+ /// Represents a parameter declaration node.
+ ParameterDeclaration,
+ /// Represents a parameter usage node that points to its declaration node index.
+ ParameterUsage,
+ /// Represents a generic reference to an already-linked node.
+ NodeReference,
/// Represents a switch case payload.
SwitchCase,
/// Represents a catch block payload.
@@ -49,17 +55,9 @@ 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 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 byte KindMask = 0x0F;
+ private const byte NextReservedFlag = 0x4;
+ private const byte NextPointsParentFlag = 0x8;
private const int FlagsShift = 4;
/// Gets or sets the runtime type of the represented node.
@@ -70,66 +68,97 @@ public struct ExprNode
[FieldOffset(8)]
public object Obj;
[FieldOffset(16)]
- private ulong _data;
+ private ushort _childIdx;
+ [FieldOffset(18)]
+ private ushort _childCount;
+ [FieldOffset(20)]
+ private ushort _nextIdx;
+ [FieldOffset(22)]
+ private byte _tag;
+ [FieldOffset(23)]
+ private byte _nodeType;
/// Gets the expression kind encoded for this node.
- public ExpressionType NodeType => (ExpressionType)((_data >> NodeTypeShift) & 0xFF);
+ public ExpressionType NodeType => (ExpressionType)_nodeType;
/// Gets the payload classification for this node.
- public ExprNodeKind Kind => (ExprNodeKind)((_data >> TagShift) & KindMask);
+ public ExprNodeKind Kind => (ExprNodeKind)(_tag & KindMask);
- internal byte Flags => (byte)(((byte)(_data >> TagShift)) >> FlagsShift);
+ internal byte Flags => (byte)(_tag >> FlagsShift);
/// Gets the next sibling node index in the intrusive child chain.
- public int NextIdx => (int)((_data >> NextShift) & IndexMask);
+ public int NextIdx => _nextIdx;
+
+ internal bool IsParentLink => (Flags & NextPointsParentFlag) != 0;
+
+ internal bool HasNextLink => _nextIdx != 0 || (Flags & (NextPointsParentFlag | NextReservedFlag)) != 0;
/// Gets the number of direct children linked from this node.
- public int ChildCount => (int)((_data >> CountShift) & IndexMask);
+ public int ChildCount => _childCount;
/// Gets the first child index or an auxiliary payload index.
- public int ChildIdx => (int)(_data & IndexMask);
+ public int ChildIdx => _childIdx;
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)
- | (ushort)childIdx;
+ _childIdx = (ushort)childIdx;
+ _childCount = (ushort)childCount;
+ _nextIdx = (ushort)nextIdx;
+ _tag = (byte)((flags << FlagsShift) | (byte)kind);
+ _nodeType = (byte)nodeType;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void SetNextIdx(int nextIdx) =>
- _data = (_data & KeepWithoutNextMask) | ((ulong)(ushort)nextIdx << NextShift);
+ internal void SetNextSiblingIdx(int nextIdx)
+ {
+ _nextIdx = (ushort)nextIdx;
+ SetFlags((byte)(Flags & ~(NextPointsParentFlag | NextReservedFlag)));
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void SetChildInfo(int childIdx, int childCount) =>
- _data = (_data & KeepWithoutChildInfoMask)
- | ((ulong)(ushort)childCount << CountShift)
- | (ushort)childIdx;
+ internal void SetParentIdx(int parentIdx)
+ {
+ _nextIdx = (ushort)parentIdx;
+ SetFlags((byte)((Flags | NextPointsParentFlag) & ~NextReservedFlag));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetChildInfo(int childIdx, int childCount)
+ {
+ _childIdx = (ushort)childIdx;
+ _childCount = (ushort)childCount;
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Is(ExprNodeKind kind) => Kind == kind;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool IsExpression() => Kind == ExprNodeKind.Expression;
+ internal bool IsExpression() =>
+ Kind == ExprNodeKind.Expression || Kind == ExprNodeKind.ParameterDeclaration || Kind == ExprNodeKind.ParameterUsage;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool HasFlag(byte flag) => (Flags & flag) != 0;
+ internal byte CopyableFlags => (byte)(Flags & ~(NextPointsParentFlag | NextReservedFlag));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void ReserveAsLinked() => SetFlags((byte)(Flags | NextReservedFlag));
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool ShouldCloneWhenLinked() =>
- Kind == ExprNodeKind.LabelTarget || NodeType == ExpressionType.Parameter || Kind == ExprNodeKind.ObjectReference || ChildCount == 0;
+ private void SetFlags(byte flags)
+ {
+ _tag = (byte)((flags << FlagsShift) | (byte)Kind);
+ }
}
/// 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;
+ private const byte ConstantInClosureFlag = 2;
+ private const int UnboundParameterPosition = ushort.MaxValue;
private const byte BinaryLiftedToNullFlag = 1;
private const byte LoopHasBreakFlag = 1;
private const byte LoopHasContinueFlag = 2;
@@ -143,14 +172,15 @@ public struct ExprTree
/// Gets or sets the flat node storage.
public SmallList, NoArrayPool> Nodes;
- /// Gets or sets closure constants that are referenced from constant nodes.
+ /// Gets or sets non-inlined constants referenced from constant nodes via .
public SmallList