Skip to content

Commit 8e6d04d

Browse files
committed
lowering; AddDefaultAndGetRef(out var index)
1 parent b8fc8d6 commit 8e6d04d

2 files changed

Lines changed: 133 additions & 32 deletions

File tree

src/FastExpressionCompiler.LightExpression/FlatExpression.cs

Lines changed: 118 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -101,31 +101,46 @@ public struct ExprNode
101101
/// <summary>Gets the first child idx or an auxiliary payload idx (parameter/label id, closure constant idx).</summary>
102102
public ushort ChildIdx => (ushort)(_child & FirstChildIdxMask);
103103

104+
/// <summary>Gets the raw 32-bit value for inline primitive constants. Only valid when <see cref="Obj"/> == <see cref="InlineValueMarker"/>.</summary>
105+
internal uint InlineValue => _child;
106+
107+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
108+
internal static void Set(ref ExprNode n, ExpressionType nodeType, Type type, object obj = null)
109+
{
110+
n.Type = type;
111+
n.Obj = obj;
112+
n._nodeType = (byte)nodeType;
113+
}
114+
104115
/// <summary>Sets the child-link metadata for the node.</summary>
105116
[MethodImpl(MethodImplOptions.AggressiveInlining)]
106117
public void SetChildrenInfo(ushort childCount, ushort childIdx) => _child = ((uint)childCount << ChildCountShift) | childIdx;
107118

108-
/// <summary>Gets the raw 32-bit value for inline primitive constants. Only valid when <see cref="Obj"/> == <see cref="InlineValueMarker"/>.</summary>
109-
internal uint InlineValue => _child;
119+
/// <summary>Sets the child-link metadata for the node.</summary>
120+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
121+
public static void SetChildrenInfo(ref ExprNode n, ushort childCount, ushort childIdx) =>
122+
n._child = ((uint)childCount << ChildCountShift) | childIdx;
110123

111-
internal ExprNode(ExpressionType nodeType, Type type, object obj, byte flags, ExprNodeKind kind,
124+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
125+
internal static void Set(ref ExprNode n, ExpressionType nodeType, Type type, object obj, byte flags, ExprNodeKind kind,
112126
ushort childIdx = 0, ushort childCount = 0, ushort nextIdx = 0)
113127
{
114-
Type = type;
115-
Obj = obj;
116-
_child = ((uint)childCount << ChildCountShift) | childIdx;
117-
NextIdx = nextIdx;
118-
_nodeType = (byte)nodeType;
119-
FlagsAndKind = (byte)((flags << 4) | ((byte)kind & 0b1111));
128+
n.Type = type;
129+
n.Obj = obj;
130+
n._child = ((uint)childCount << ChildCountShift) | childIdx;
131+
n.NextIdx = nextIdx;
132+
n._nodeType = (byte)nodeType;
133+
n.FlagsAndKind = (byte)((flags << 4) | ((byte)kind & 0b1111));
120134
}
121135

122136
/// <summary>Constructs an inline primitive constant node, <see cref="Obj"/> is set to <see cref="InlineValueMarker"/>.</summary>
123-
internal ExprNode(Type type, uint inlineValue)
137+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
138+
internal static void Set(ref ExprNode n, Type type, uint inlineValue)
124139
{
125-
Type = type;
126-
Obj = InlineValueMarker;
127-
_nodeType = (byte)ExpressionType.Constant;
128-
_child = inlineValue;
140+
n.Type = type;
141+
n.Obj = InlineValueMarker;
142+
n._nodeType = (byte)ExpressionType.Constant;
143+
n._child = inlineValue;
129144
}
130145

131146
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -240,12 +255,31 @@ private void EnsureIndexZeroSentinel()
240255
}
241256

242257
[MethodImpl(MethodImplOptions.AggressiveInlining)]
243-
private ushort AddNode(ExpressionType nodType, Type type, object obj = null, byte flags = 0, ExprNodeKind kind = default,
258+
private ushort LastNodeIdx() => checked((ushort)(Nodes.Count - 1));
259+
260+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
261+
private ushort AddNode(ExpressionType nodeType, Type type, object obj = null)
262+
{
263+
EnsureIndexZeroSentinel();
264+
ExprNode.Set(ref Nodes.AddDefaultAndGetRef(out var idx), nodeType, type, obj);
265+
return (ushort)idx;
266+
}
267+
268+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
269+
private ushort AddNode(ExpressionType nodeType, Type type, object obj, byte flags = 0, ExprNodeKind kind = default,
270+
ushort childIdx = 0, ushort childCount = 0)
271+
{
272+
EnsureIndexZeroSentinel();
273+
ExprNode.Set(ref Nodes.AddDefaultAndGetRef(out var idx), nodeType, type, obj, flags, kind, childIdx, childCount);
274+
return (ushort)idx;
275+
}
276+
277+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
278+
private void AddNode(ref ExprNode n, ExpressionType nodeType, Type type, object obj, byte flags = 0, ExprNodeKind kind = default,
244279
ushort childIdx = 0, ushort childCount = 0)
245280
{
246281
EnsureIndexZeroSentinel();
247-
var node = new ExprNode(nodType, type, obj, flags, kind, childIdx, childCount);
248-
return checked((ushort)Nodes.Add(in node));
282+
ExprNode.Set(ref n, nodeType, type, obj, flags, kind, childIdx, childCount);
249283
}
250284

251285
/// <summary>Adds a parameter node and returns its idx.</summary>
@@ -281,7 +315,9 @@ public ushort Constant(object value, Type type)
281315
if (!In32BitRange(Type.GetTypeCode(Enum.GetUnderlyingType(type))))
282316
return AddNode(ExpressionType.Constant, type, value);
283317
EnsureIndexZeroSentinel();
284-
return checked((ushort)Nodes.Add(new ExprNode(type, unchecked((uint)System.Convert.ToInt64(value)))));
318+
ref var n = ref Nodes.AddDefaultAndGetRef();
319+
ExprNode.Set(ref n, type, unchecked((uint)System.Convert.ToInt64(value)));
320+
return LastNodeIdx();
285321
}
286322

287323
if (type.IsPrimitive)
@@ -290,11 +326,12 @@ public ushort Constant(object value, Type type)
290326
if (!In32BitRange(tc))
291327
return AddNode(ExpressionType.Constant, type, value);
292328
EnsureIndexZeroSentinel();
293-
return checked((ushort)Nodes.Add(new ExprNode(type, ToInlineValue(value, tc))));
329+
ref var n = ref Nodes.AddDefaultAndGetRef();
330+
ExprNode.Set(ref n, type, ToInlineValue(value, tc));
331+
return LastNodeIdx();
294332
}
295333

296-
return AddNode(ExpressionType.Constant, type, ClosureConstantMarker,
297-
childIdx: checked((ushort)ClosureConstants.Add(value)));
334+
return AddNode(ExpressionType.Constant, type, ClosureConstantMarker, childIdx: checked((ushort)ClosureConstants.Add(value)));
298335
}
299336

300337
/// <summary>Adds a constant node using the runtime type of the supplied value.</summary>
@@ -310,7 +347,9 @@ public ushort Constant(object value, Type type)
310347
public ushort ConstantInt(int value)
311348
{
312349
EnsureIndexZeroSentinel();
313-
return checked((ushort)Nodes.Add(new ExprNode(typeof(int), unchecked((uint)value))));
350+
ref var n = ref Nodes.AddDefaultAndGetRef();
351+
ExprNode.Set(ref n, typeof(int), unchecked((uint)value));
352+
return LastNodeIdx();
314353
}
315354

316355
/// <summary>Adds a typed constant node.</summary>
@@ -374,15 +413,18 @@ private void AppendPreparedChild(ushort childIdx, ref ushort firstChildIdx, ref
374413
[MethodImpl(MethodImplOptions.AggressiveInlining)]
375414
private ushort WithOneChild(ExpressionType nodeType, Type type, object obj, byte flags, ExprNodeKind kind, ushort ch0)
376415
{
377-
var ownerIdx = AddNode(nodeType, type, obj, flags, kind);
416+
ref var ownerRef = ref Nodes.AddDefaultAndGetRef(out var idx);
417+
var ownerIdx = (ushort)idx;
418+
AddNode(ref ownerRef, nodeType, type, obj, flags, kind);
419+
378420
ushort first = 0;
379421
ushort count = 0;
380422
if (ch0 != 0)
381423
{
382424
first = MayBeCloneChildForOwner(ch0, ownerIdx);
383425
count = 1;
384426
}
385-
Nodes.GetSurePresentRef(ownerIdx).SetChildrenInfo(count, first);
427+
ExprNode.SetChildrenInfo(ref ownerRef, count, first);
386428
return ownerIdx;
387429
}
388430

@@ -486,7 +528,11 @@ public ushort New(ConstructorInfo ctor, params ushort[] args) =>
486528

487529
/// <summary>Adds an array initialization node.</summary>
488530
[MethodImpl(MethodImplOptions.AggressiveInlining)]
531+
#if NET10_0_OR_GREATER
532+
public ushort NewArrayInit(Type elementType, params ReadOnlySpan<ushort> expressions) =>
533+
#else
489534
public ushort NewArrayInit(Type elementType, params ushort[] expressions) =>
535+
#endif
490536
WithChildren(ExpressionType.NewArrayInit, elementType.MakeArrayType(), null, default, default, expressions);
491537

492538
/// <summary>Adds an array-bounds node.</summary>
@@ -616,7 +662,7 @@ public ushort Block(Type type, ushort[] vars, params ushort[] exprs)
616662
type ??= Nodes[exprs[exprs.Length - 1]].Type;
617663
var blockIdx = WithOneOrMoreChildren(ExpressionType.Block, type, null, default, default, exprsSubNode, vars);
618664
if (vars != null && vars.Length != 0)
619-
BlocksWithVariables.Add(blockIdx);
665+
BlocksWithVariables.Add(blockIdx);
620666
return blockIdx;
621667
}
622668

@@ -629,14 +675,56 @@ public ushort Block(params ushort[] exprs) =>
629675
[MethodImpl(MethodImplOptions.AggressiveInlining)]
630676
#if NET10_0_OR_GREATER
631677
public ushort Lambda(Type delegateType, ushort bodyIdx, params ReadOnlySpan<ushort> pars)
678+
{
632679
#else
633680
public ushort Lambda(Type delegateType, ushort bodyIdx, params ushort[] pars)
634-
#endif
635681
{
636-
var idx = WithOneOrMoreChildren(ExpressionType.Lambda, delegateType, null, default, default, bodyIdx, pars);
637-
LambdaNodes.Add(idx);
638-
CollectLambdaClosureParameterUsages(idx);
639-
return idx;
682+
pars ??= Array.Empty<ushort>();
683+
#endif
684+
EnsureIndexZeroSentinel();
685+
ref var lambdaRef = ref Nodes.AddDefaultAndGetRef(out var idx);
686+
ExprNode.Set(ref lambdaRef, ExpressionType.Lambda, delegateType);
687+
var lambdaIdx = (ushort)idx;
688+
689+
ushort firstIdx = 0;
690+
691+
ref var prevRef = ref Nodes.GetSurePresentRef(bodyIdx);
692+
if (bodyIdx != 0)
693+
{
694+
if (prevRef.NextIdx != 0 || prevRef.NodeType == ExpressionType.Parameter)
695+
{
696+
var bodyCopyRef = prevRef;
697+
prevRef = ref Nodes.AddDefaultAndGetRef(out var bodyCopyIdx);
698+
prevRef = bodyCopyRef;
699+
bodyIdx = (ushort)bodyCopyIdx;
700+
}
701+
prevRef.NextIdx = lambdaIdx; // set next to lambda by default, then it can be overridden by the first parameter if present
702+
firstIdx = bodyIdx;
703+
}
704+
else
705+
{
706+
ref var defaultRef = ref Nodes.AddDefaultAndGetRef(out var defaultIdx);
707+
ExprNode.Set(ref defaultRef, ExpressionType.Default, typeof(void));
708+
firstIdx = (ushort)defaultIdx;
709+
}
710+
711+
var count = (ushort)(pars.Length + 1);
712+
ExprNode.SetChildrenInfo(ref lambdaRef, count, firstIdx);
713+
714+
foreach (var parIdx in pars)
715+
{
716+
Debug.Assert(parIdx != 0, "The parameter should be defined - otherwise what is the matter");
717+
// We do not clone the parameters defined in the Lambda, because this the place where they are defined
718+
prevRef.NextIdx = parIdx;
719+
prevRef = ref Nodes.GetSurePresentRef(parIdx);
720+
Debug.Assert(prevRef.NextIdx == 0, "The parameter should not be linked to any other node - otherwise what is the matter");
721+
}
722+
723+
prevRef.NextIdx = lambdaIdx; // close the child chain with a link back to the lambda node
724+
725+
LambdaNodes.Add(lambdaIdx);
726+
CollectLambdaClosureParameterUsages(lambdaIdx);
727+
return lambdaIdx;
640728
}
641729

642730
/// <summary>Adds a typed lambda node.</summary>

src/FastExpressionCompiler/ImTools.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,20 @@ public ref T AddDefaultAndGetRef()
935935
return ref SmallList.AddDefaultAndGetRef(ref Rest, ref Pool, index - stackCap);
936936
}
937937

938-
/// <summary>Adds the item to the end of the list aka the Stack.Push. Returns the ref to the added item.</summary>
938+
// todo: @wip add to interface
939+
/// <summary>Appends the default item to the end of the list and returns the reference to it and its index</summary>
940+
[UnscopedRef]
941+
[MethodImpl((MethodImplOptions)256)]
942+
public ref T AddDefaultAndGetRef(out int index)
943+
{
944+
index = _count++;
945+
var stackCap = Stack.Capacity;
946+
if (index < stackCap)
947+
return ref Stack.GetSurePresentRef(index);
948+
return ref SmallList.AddDefaultAndGetRef(ref Rest, ref Pool, index - stackCap);
949+
}
950+
951+
/// <summary>Appends the item to the end of the list aka the Stack.Push. Returns the ref to the added item.</summary>
939952
[UnscopedRef]
940953
[MethodImpl((MethodImplOptions)256)]
941954
public ref T AddAndGetRef(in T item)
@@ -945,7 +958,7 @@ public ref T AddAndGetRef(in T item)
945958
return ref r;
946959
}
947960

948-
/// <summary>Adds the item to the end of the list aka the Stack.Push. Returns the index of the added item.</summary>
961+
/// <summary>Appends the item to the end of the list aka the Stack.Push. Returns the index of the added item.</summary>
949962
[MethodImpl((MethodImplOptions)256)]
950963
public int Add(in T item)
951964
{

0 commit comments

Comments
 (0)