From 46f3ca6bd7866baa905d9f5d5f9ec232c5428810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20R=C3=A4tzel?= Date: Sat, 4 Jan 2025 20:31:07 +0000 Subject: [PATCH] Tune IR compiler and IR for runtime efficiency + Reorder emitted IR instructions to avoid superfluous instruction pairs to store and load from local: Change the compilation around conditionals to avoid pulling the condition forward in front of other outer subexpressions. + Change the instruction to store to a local to align with the more frequent case: Do not pop that value from the stack since we now use it again immediately anyway. --- .../pine/Pine/PineVM/ImperativeTreeNode.cs | 45 - implement/pine/Pine/PineVM/PineVM.cs | 469 +-------- implement/pine/PineVM/PineIRCompiler.cs | 932 +++++++++++------- implement/pine/PineVM/StackInstruction.cs | 2 +- implement/test-elm-time/PineVMTests.cs | 186 ++-- 5 files changed, 743 insertions(+), 891 deletions(-) delete mode 100644 implement/pine/Pine/PineVM/ImperativeTreeNode.cs diff --git a/implement/pine/Pine/PineVM/ImperativeTreeNode.cs b/implement/pine/Pine/PineVM/ImperativeTreeNode.cs deleted file mode 100644 index 1e570b6b..00000000 --- a/implement/pine/Pine/PineVM/ImperativeTreeNode.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using Pine.Core; - -namespace Pine.PineVM; - -public abstract record ImperativeNode -{ - public record LeafNode(Expression Expression) - : ImperativeNode; - - public record ConditionalNode( - Expression.Conditional Origin, - ImperativeNode Condition, - ImperativeNode FalseBranch, - ImperativeNode TrueBranch, - ImperativeNode Continuation) - : ImperativeNode; - - public static IEnumerable EnumerateSelfAndDescendants( - ImperativeNode rootNode, - bool skipBranches) - { - var stack = new Stack([rootNode]); - - while (stack.Count > 0) - { - var node = stack.Pop(); - - yield return node; - - if (node is ConditionalNode conditional) - { - stack.Push(conditional.Condition); - - if (!skipBranches) - { - stack.Push(conditional.FalseBranch); - stack.Push(conditional.TrueBranch); - } - - stack.Push(conditional.Continuation); - } - } - } -} diff --git a/implement/pine/Pine/PineVM/PineVM.cs b/implement/pine/Pine/PineVM/PineVM.cs index 06833127..f16be248 100644 --- a/implement/pine/Pine/PineVM/PineVM.cs +++ b/implement/pine/Pine/PineVM/PineVM.cs @@ -82,6 +82,12 @@ public record StackFrameInstructions( IReadOnlyList Instructions, EnvConstraintId? TrackEnvConstraint = null) { + public int MaxLocalIndex { init; get; } = + Instructions + .Select(i => i.Kind is StackInstructionKind.Local_Set ? i.LocalIndex ?? 0 : 0) + .DefaultIfEmpty(-1) + .Max(); + public virtual bool Equals(StackFrameInstructions? other) { if (other is not { } notNull) @@ -180,6 +186,14 @@ public PineValue PopTopmostFromStack() --StackPointer; return StackValues.Span[StackPointer]; } + + public PineValue PeekTopmostFromStack() + { + if (StackPointer <= 0) + throw new InvalidOperationException("PeekTopmostFromStack called with empty stack"); + + return StackValues.Span[StackPointer - 1]; + } } /* @@ -286,7 +300,7 @@ StackFrame StackFrameFromExpression( instructions, EnvironmentValue: environment, StackValues: new PineValue[instructions.Instructions.Count], - LocalsValues: new PineValue[instructions.Instructions.Count], + LocalsValues: new PineValue[instructions.MaxLocalIndex + 1], BeginInstructionCount: beginInstructionCount, BeginParseAndEvalCount: beginParseAndEvalCount, BeginStackFrameCount: beginStackFrameCount, @@ -777,411 +791,12 @@ public static Expression SubstituteSubexpressionsForEnvironmentConstraint( public static IReadOnlyList InstructionsFromExpressionTransitive( Expression rootExpression) { - var node = - NodeFromExpressionTransitive( - rootExpression, - conditionalsToSkip: []); - return - InstructionsFromNode( - node, - instructionIndex: 0, - reusableExpressionResultOffset: _ => null, - makeReusable: _ => false).instructions; - } - - public static ImperativeNode NodeFromExpressionTransitive( - Expression rootExpression, - ImmutableHashSet conditionalsToSkip) - { - var conditionalToSplit = - ListComponentsOrderedForCompilation(rootExpression, skipDescendants: null) - .OfType() - .Where(c => !conditionalsToSkip.Contains(c)) - .FirstOrDefault(); - - if (conditionalToSplit is not null) - { - var conditionNode = - NodeFromExpressionTransitive( - conditionalToSplit.Condition, - conditionalsToSkip: []); - - var falseNode = - NodeFromExpressionTransitive( - conditionalToSplit.FalseBranch, - conditionalsToSkip: []); - - var trueNode = - NodeFromExpressionTransitive( - conditionalToSplit.TrueBranch, - conditionalsToSkip: []); - - var continuationNode = - NodeFromExpressionTransitive( - rootExpression, - conditionalsToSkip: conditionalsToSkip.Add(conditionalToSplit)); - - return - new ImperativeNode.ConditionalNode( - Origin: conditionalToSplit, - Condition: conditionNode, - FalseBranch: falseNode, - TrueBranch: trueNode, - Continuation: continuationNode); - } - - return new ImperativeNode.LeafNode(rootExpression); - } - - public static (IReadOnlyList instructions, IReadOnlyDictionary localsSet) - InstructionsFromNode( - ImperativeNode imperativeNode, - int instructionIndex, - Func reusableExpressionResultOffset, - Func makeReusable) - { - if (imperativeNode is ImperativeNode.LeafNode leaf) - { - var allSubexpressions = new HashSet(); - - var subexpressionsToReuse = new HashSet(); - - foreach ( - var subexpression in - Expression.EnumerateSelfAndDescendants( - leaf.Expression, - skipDescendants: - subexpression => - { - if (reusableExpressionResultOffset(subexpression).HasValue) - { - return true; - } - - if (!ExpressionLargeEnoughForCSE(subexpression)) - { - return true; - } - - if (makeReusable(subexpression)) - { - subexpressionsToReuse.Add(subexpression); - } - - if (allSubexpressions.Contains(subexpression)) - { - subexpressionsToReuse.Add(subexpression); - - return true; - } - - allSubexpressions.Add(subexpression); - - return false; - })) - { - } - - var localInstructionIndexFromExpr = new Dictionary(); - - int? reusableLocalIndex(Expression expr) - { - { - if (reusableExpressionResultOffset.Invoke(expr) is { } reusableIndex) - { - return reusableIndex; - } - } - - { - if (localInstructionIndexFromExpr.TryGetValue(expr, out var earlierIndex)) - { - return earlierIndex; - } - } - - return null; - } - - var subexpressionsToReuseInstructions = new List(); - - var subexpressionsToReuseOrdered = - subexpressionsToReuse - .OrderBy(se => se.SubexpressionCount) - .ToImmutableArray(); - - foreach (var subexpression in subexpressionsToReuseOrdered) - { - var subexpressionInstructions = - PineIRCompiler.CompileExpressionTransitive( - subexpression, - localIndexFromExpr: reusableLocalIndex); - - var localIndex = - instructionIndex + - subexpressionsToReuseInstructions.Count + - subexpressionInstructions.Count; - - subexpressionsToReuseInstructions.AddRange( - [ - ..subexpressionInstructions, - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: localIndex) - ]); - - localInstructionIndexFromExpr[subexpression] = localIndex; - } - - var rootInstructions = - PineIRCompiler.CompileExpressionTransitive( - leaf.Expression, - reusableLocalIndex) - .ToImmutableArray(); - - IReadOnlyList instructionsOptimized = - [ - ..subexpressionsToReuseInstructions, - ..rootInstructions - ]; - - return (instructionsOptimized, localInstructionIndexFromExpr); - } - - if (imperativeNode is ImperativeNode.ConditionalNode conditional) - { - var unconditionalNodesUnderCondition = - ImperativeNode.EnumerateSelfAndDescendants( - conditional.Condition, - skipBranches: true) - .ToImmutableArray(); - - var unconditionalExprUnderCondition = new HashSet(); - - - /* - * TODO: Change unconditionalExprUnderCondition - * General 'unconditional' is either in condition or in continuation. - * */ - - foreach (var nodeUnderCondition in unconditionalNodesUnderCondition) - { - if (nodeUnderCondition is not ImperativeNode.LeafNode leafNode) - continue; - - foreach (var expression in - Expression.EnumerateSelfAndDescendants( - leafNode.Expression, - skipDescendants: - expr => reusableExpressionResultOffset(expr).HasValue)) - { - unconditionalExprUnderCondition.Add(expression); - } - } - - var otherNodes = - new[] { conditional.FalseBranch, conditional.TrueBranch, conditional.Continuation } - .SelectMany(otherRoot => - ImperativeNode.EnumerateSelfAndDescendants( - otherRoot, - skipBranches: false)) - .ToImmutableArray(); - - var candidatesForCSE = new HashSet(); - - foreach (var otherNode in otherNodes) - { - if (otherNode is not ImperativeNode.LeafNode otherLeafNode) - continue; - - foreach (var otherExpr in Expression.EnumerateSelfAndDescendants( - otherLeafNode.Expression, - skipDescendants: - descendant => - candidatesForCSE.Contains(descendant) || - descendant == conditional.Origin)) - { - if (!ExpressionLargeEnoughForCSE(otherExpr)) - continue; - - if (reusableExpressionResultOffset(otherExpr).HasValue) - continue; - - if (unconditionalExprUnderCondition.Contains(otherExpr)) - { - candidatesForCSE.Add(otherExpr); - } - } - } - - var conditionResult = - InstructionsFromNode( - conditional.Condition, - instructionIndex: instructionIndex, - reusableExpressionResultOffset, - makeReusable: candidatesForCSE.Contains); - - var conditionInstructions = conditionResult.instructions; - - var instructionsBeforeBranchFalseCount = - conditionInstructions.Count + 1; - - int? reusableResultOffsetForBranch(Expression expression) - { - if (reusableExpressionResultOffset(expression) is { } earlierOffset) - { - return earlierOffset; - } - - if (conditionResult.localsSet.TryGetValue(expression, out var localIndex)) - { - return localIndex; - } - - return null; - } - - IReadOnlyList falseBranchInstructions = - [.. InstructionsFromNode( - conditional.FalseBranch, - instructionIndex: instructionIndex + instructionsBeforeBranchFalseCount, - reusableResultOffsetForBranch, - makeReusable: _ => false) - .instructions]; - - IReadOnlyList trueBranchInstructions = - [.. InstructionsFromNode( - conditional.TrueBranch, - instructionIndex: instructionIndex + instructionsBeforeBranchFalseCount + falseBranchInstructions.Count + 1, - reusableExpressionResultOffset: - reusableResultOffsetForBranch, - makeReusable: _ => false) - .instructions]; - - IReadOnlyList falseBranchInstructionsAndJump = - [.. falseBranchInstructions, - StackInstruction.Jump_Unconditional(trueBranchInstructions.Count + 1) - ]; - - var branchInstruction = - StackInstruction.Jump_If_True( - offset: falseBranchInstructionsAndJump.Count); - - var conditionalResultLocalIndex = - instructionIndex + instructionsBeforeBranchFalseCount + falseBranchInstructions.Count; - - IReadOnlyList instructionsBeforeContinuation = - [..conditionInstructions, - branchInstruction, - ..falseBranchInstructionsAndJump, - ..trueBranchInstructions, - new StackInstruction( - StackInstructionKind.Local_Set, - LocalIndex: conditionalResultLocalIndex) - ]; - - int? reusableResultOffsetForContinuation(Expression expression) - { - if (expression == conditional.Origin) - { - return conditionalResultLocalIndex; - } - - return reusableResultOffsetForBranch(expression); - } - - IReadOnlyList continuationInstructions = - [ - .. InstructionsFromNode( - conditional.Continuation, - instructionIndex: instructionIndex + instructionsBeforeContinuation.Count + 1, - reusableResultOffsetForContinuation, - makeReusable: _ => false) - .instructions - ]; - - IReadOnlyList mergedInstructions = - [..instructionsBeforeContinuation, - ..continuationInstructions - ]; - - return (mergedInstructions, conditionResult.localsSet); - } - - throw new NotImplementedException( - "Unexpected node type: " + imperativeNode.GetType().FullName); - } - - static IReadOnlyList ExpressionsToSeparateSkippingConditional( - Expression rootExpression, - Func expressionAlreadyCovered, - Func shouldSeparate) - { - var allExpressions = - ListComponentsOrderedForCompilation( + PineIRCompiler.CompileExpressionTransitive( rootExpression, - skipDescendants: expressionAlreadyCovered) - .ToImmutableArray(); - - var allExpressionsCount = new Dictionary(); - - foreach (var expression in allExpressions) - { - if (allExpressionsCount.TryGetValue(expression, out var count)) - { - allExpressionsCount[expression] = count + 1; - } - else - { - allExpressionsCount[expression] = 1; - } - } - - var allExpressionsExceptUnderDuplicate = - ListComponentsOrderedForCompilation( - rootExpression, - skipDescendants: node => 1 < allExpressionsCount[node] || expressionAlreadyCovered(node)) - .ToImmutableArray(); - - var allExpressionsExceptUnderDuplicateCount = new Dictionary(); - - foreach (var expression in allExpressionsExceptUnderDuplicate) - { - if (allExpressionsExceptUnderDuplicateCount.TryGetValue(expression, out var count)) - { - allExpressionsExceptUnderDuplicateCount[expression] = count + 1; - } - else - { - allExpressionsExceptUnderDuplicateCount[expression] = 1; - } - } - - var separatedInstructions = - allExpressions - .SelectMany(expression => - { - if (expressionAlreadyCovered(expression)) - return []; - - if (shouldSeparate(expression)) - return [expression]; - - if (expression is Expression.ParseAndEval parseAndEval) - { - return (IReadOnlyList)[expression]; - } - - if (ExpressionLargeEnoughForCSE(expression) && - allExpressionsExceptUnderDuplicateCount.TryGetValue(expression, out var exprInstCount) && 1 < exprInstCount) - { - return [expression]; - } - - return []; - }) - .ToImmutableArray(); - - return separatedInstructions; + copyToLocal: ImmutableHashSet.Empty, + localIndexFromExpr: + ImmutableDictionary.Empty).Instructions; } public static IEnumerable ListComponentsOrderedForCompilation( @@ -1248,28 +863,6 @@ public static IEnumerable ListComponentsOrderedForCompilation( } } - static bool ExpressionLargeEnoughForCSE(Expression expression) - { - if (expression is Expression.KernelApplication) - return true; - - if (expression is Expression.List list) - { - for (int i = 0; i < list.items.Count; ++i) - { - if (ExpressionLargeEnoughForCSE(list.items[i])) - return true; - } - } - - if (expression is Expression.StringTag stringTag) - { - return ExpressionLargeEnoughForCSE(stringTag.Tagged); - } - - return 10 < expression.SubexpressionCount; - } - public static Expression? TryFuseStep(Expression expression) { if (TryMapToKernelApplications_Skip_ListHead_Path_Expression(expression) is { } fused) @@ -1966,7 +1559,7 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame) case StackInstructionKind.Local_Set: { - var fromStack = currentFrame.PopTopmostFromStack(); + var fromStack = currentFrame.PeekTopmostFromStack(); currentFrame.LocalSet( currentInstruction.LocalIndex @@ -2315,6 +1908,28 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame) continue; } + case StackInstructionKind.Int_Add_List: + { + var listValue = currentFrame.PopTopmostFromStack(); + + var sumValue = KernelFunction.int_add(listValue); + + currentFrame.PushInstructionResult(sumValue); + + continue; + } + + case StackInstructionKind.Int_Mul_List: + { + var listValue = currentFrame.PopTopmostFromStack(); + + var productValue = KernelFunction.int_mul(listValue); + + currentFrame.PushInstructionResult(productValue); + + continue; + } + default: throw new NotImplementedException( "Unexpected instruction kind: " + instructionKind); diff --git a/implement/pine/PineVM/PineIRCompiler.cs b/implement/pine/PineVM/PineIRCompiler.cs index c3674ed0..0ad6a387 100644 --- a/implement/pine/PineVM/PineIRCompiler.cs +++ b/implement/pine/PineVM/PineIRCompiler.cs @@ -1,42 +1,168 @@ using Pine.Core; -using System.Collections.Generic; using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; namespace Pine.PineVM; public class PineIRCompiler { + public record NodeCompilationResult( + ImmutableList Instructions, + ImmutableDictionary LocalsSet) + { + public NodeCompilationResult ContinueWithExpression( + Expression expression, + IReadOnlySet copyToLocal) + { + var exprResult = CompileExpressionTransitive(expression, copyToLocal, LocalsSet); + + return new NodeCompilationResult( + Instructions.AddRange(exprResult.Instructions), + LocalsSet.AddRange(exprResult.LocalsSet)); + } + + public NodeCompilationResult AppendInstruction(StackInstruction instruction) => + AppendInstructions([instruction]); + + public NodeCompilationResult AppendInstructions(IEnumerable instructions) => + this + with + { + Instructions = Instructions.AddRange(instructions) + }; + } /// - /// Recursively compile an expression into a flat list of PineIROp instructions. + /// Recursively compile an expression into a flat list of instructions. /// - public static IReadOnlyList CompileExpressionTransitive( + public static NodeCompilationResult CompileExpressionTransitive( + Expression expression, + IReadOnlySet copyToLocal, + ImmutableDictionary localIndexFromExpr) + { + return + CompileExpressionTransitive( + expression, + copyToLocal: copyToLocal, + new NodeCompilationResult( + Instructions: [], + localIndexFromExpr)); + } + + /// + /// Recursively compile an expression into a flat list of instructions. + /// + public static NodeCompilationResult CompileExpressionTransitive( + Expression expression, + IReadOnlySet copyToLocal, + NodeCompilationResult prior) + { + var allSubexpressions = new HashSet(); + + var subexpressionsToReuse = new HashSet(); + + foreach ( + var subexpression in + Expression.EnumerateSelfAndDescendants( + expression, + skipDescendants: + subexpression => + { + if (prior.LocalsSet.ContainsKey(subexpression)) + { + return true; + } + + if (copyToLocal.Contains(subexpression)) + { + return false; + } + + if (!ExpressionLargeEnoughForCSE(subexpression)) + { + return true; + } + + if (allSubexpressions.Contains(subexpression)) + { + subexpressionsToReuse.Add(subexpression); + + return true; + } + + allSubexpressions.Add(subexpression); + + return false; + })) + { + } + + var copyToLocalNew = + copyToLocal.ToImmutableHashSet() + .Union(subexpressionsToReuse); + + var lessCSE = + CompileExpressionTransitiveLessCSE( + expression, + copyToLocalNew, + prior); + + if (!prior.LocalsSet.ContainsKey(expression) && copyToLocal.Contains(expression)) + { + var newLocalIndex = + lessCSE.LocalsSet.IsEmpty + ? + 0 + : + lessCSE.LocalsSet.Values.Max() + 1; + + return + lessCSE + .AppendInstruction( + new StackInstruction( + Kind: StackInstructionKind.Local_Set, + LocalIndex: newLocalIndex)) + with + { + LocalsSet = lessCSE.LocalsSet.Add(expression, newLocalIndex) + }; + } + + return lessCSE; + } + + public static NodeCompilationResult CompileExpressionTransitiveLessCSE( Expression expr, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { - if (localIndexFromExpr(expr) is { } localIndex) + if (prior.LocalsSet.TryGetValue(expr, out var localIndex)) { return - [new StackInstruction( - Kind: StackInstructionKind.Local_Get, - LocalIndex: localIndex) - ]; + prior + .AppendInstruction( + new StackInstruction( + Kind: StackInstructionKind.Local_Get, + LocalIndex: localIndex)); } switch (expr) { case Expression.Literal literalExpr: return - [new StackInstruction( - Kind: StackInstructionKind.Push_Literal, - Literal: literalExpr.Value) - ]; + prior + .AppendInstruction( + new StackInstruction( + Kind: StackInstructionKind.Push_Literal, + Literal: literalExpr.Value)); case Expression.Environment: return - [ - StackInstruction.PushEnvironment - ]; + prior + .AppendInstruction( + new StackInstruction(StackInstructionKind.Push_Environment)); case Expression.List listExpr: { @@ -47,45 +173,47 @@ [new StackInstruction( * we don't use a non-consuming instruction but use local_get instead to copy it) * */ - var itemsInstructions = new List(); + var lastItemResult = prior; for (var i = 0; i < listExpr.items.Count; ++i) { var itemExpr = listExpr.items[i]; - var itemInstructions = + lastItemResult = CompileExpressionTransitive( itemExpr, - localIndexFromExpr); - - itemsInstructions.AddRange(itemInstructions); + copyToLocal, + lastItemResult); } return - [ - ..itemsInstructions, - new StackInstruction( - StackInstructionKind.BuildList, - TakeCount: listExpr.items.Count) - ]; + lastItemResult + .AppendInstruction( + new StackInstruction( + StackInstructionKind.BuildList, + TakeCount: listExpr.items.Count)); } - case Expression.Conditional: - - throw new InvalidOperationException( - "Conditional should be filtered out earlier"); + case Expression.Conditional conditional: + return + CompileConditional( + conditional, + copyToLocal, + prior); case Expression.ParseAndEval pae: return CompileParseAndEval( pae, - localIndexFromExpr); + copyToLocal, + prior); case Expression.KernelApplication kernelApp: return CompileKernelApplication( kernelApp, - localIndexFromExpr); + copyToLocal, + prior); default: throw new NotImplementedException( @@ -93,125 +221,186 @@ [new StackInstruction( } } - public static IReadOnlyList CompileParseAndEval( - Expression.ParseAndEval parseAndEvalExpr, - Func localIndexFromExpr) + public static NodeCompilationResult CompileConditional( + Expression.Conditional conditional, + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { - var environmentOps = + var afterCondition = CompileExpressionTransitive( - parseAndEvalExpr.Environment, - localIndexFromExpr); + conditional.Condition, + copyToLocal, + prior); - var encodedOps = + var falseBranchInstructions = CompileExpressionTransitive( + conditional.FalseBranch, + copyToLocal, + new NodeCompilationResult( + Instructions: [], + LocalsSet: afterCondition.LocalsSet)) + .Instructions; + + var trueBranchInstructions = + CompileExpressionTransitive( + conditional.TrueBranch, + copyToLocal, + new NodeCompilationResult( + Instructions: [], + LocalsSet: afterCondition.LocalsSet)) + .Instructions; + + IReadOnlyList falseBranchInstructionsAndJump = + [.. falseBranchInstructions, + StackInstruction.Jump_Unconditional(trueBranchInstructions.Count + 1) + ]; + + var branchInstruction = + StackInstruction.Jump_If_True( + offset: falseBranchInstructionsAndJump.Count); + + var afterConditionAndJump = + afterCondition + .AppendInstruction(branchInstruction); + + return + afterConditionAndJump + .AppendInstructions(falseBranchInstructionsAndJump) + .AppendInstructions(trueBranchInstructions); + } + + public static NodeCompilationResult CompileParseAndEval( + Expression.ParseAndEval parseAndEvalExpr, + IReadOnlySet copyToLocal, + NodeCompilationResult prior) + { + var afterEnvironment = + prior.ContinueWithExpression( + parseAndEvalExpr.Environment, + copyToLocal); + + var afterExpr = + afterEnvironment.ContinueWithExpression( parseAndEvalExpr.Encoded, - localIndexFromExpr); + copyToLocal); return - [ - .. environmentOps, - .. encodedOps, - new StackInstruction(StackInstructionKind.Parse_And_Eval) - ]; + afterExpr + .AppendInstruction( + new StackInstruction(StackInstructionKind.Parse_And_Eval)); } - public static IReadOnlyList CompileKernelApplication( + public static NodeCompilationResult CompileKernelApplication( Expression.KernelApplication kernelApplication, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { return kernelApplication.Function switch { nameof(KernelFunction.length) => - [ - .. CompileExpressionTransitive( - kernelApplication.Input, - localIndexFromExpr), - new StackInstruction(StackInstructionKind.Length) - ], + CompileExpressionTransitive( + kernelApplication.Input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Length)), nameof(KernelFunction.negate) => - [ - .. CompileExpressionTransitive( - kernelApplication.Input, - localIndexFromExpr), - new StackInstruction(StackInstructionKind.Negate) - ], + CompileExpressionTransitive( + kernelApplication.Input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Negate)), nameof(KernelFunction.equal) => CompileKernelApplication_Equal( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.head) => CompileKernelApplication_Head( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.skip) => CompileKernelApplication_Skip( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.take) => CompileKernelApplication_Take( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.concat) => CompileKernelApplication_Concat( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.reverse) => CompileKernelApplication_Reverse( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.int_add) => CompileKernelApplication_Int_Add( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.int_mul) => CompileKernelApplication_Int_Mul( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.int_is_sorted_asc) => CompileKernelApplication_Int_Is_Sorted_Asc( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_and) => CompileKernelApplication_Bit_And( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_or) => CompileKernelApplication_Bit_Or( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_xor) => CompileKernelApplication_Bit_Xor( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_not) => CompileKernelApplication_Bit_Not( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_shift_left) => CompileKernelApplication_Bit_Shift_Left( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), nameof(KernelFunction.bit_shift_right) => CompileKernelApplication_Bit_Shift_Right( kernelApplication.Input, - localIndexFromExpr), + copyToLocal, + prior), _ => throw new NotImplementedException( @@ -219,152 +408,140 @@ .. CompileExpressionTransitive( }; } - public static IReadOnlyList CompileKernelApplication_Equal( + public static NodeCompilationResult CompileKernelApplication_Equal( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 2) { - var leftOps = + var afterLeft = CompileExpressionTransitive( listExpr.items[0], - localIndexFromExpr); + copyToLocal, + prior); - var rightOps = + var afterRight = CompileExpressionTransitive( listExpr.items[1], - localIndexFromExpr); + copyToLocal, + afterLeft); return - [ - .. rightOps, - .. leftOps, - new StackInstruction(StackInstructionKind.Equal_Binary) - ]; + afterRight + .AppendInstruction(new StackInstruction(StackInstructionKind.Equal_Binary)); } } return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Equal_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Equal_List)); } - public static IReadOnlyList CompileKernelApplication_Head( + public static NodeCompilationResult CompileKernelApplication_Head( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.KernelApplication innerKernelApp && innerKernelApp.Function is "skip") { if (innerKernelApp.Input is Expression.List skipList && skipList.items.Count is 2) { - var sourceOps = + var afterSource = CompileExpressionTransitive( skipList.items[1], - localIndexFromExpr); + copyToLocal, + prior); if (skipList.items[0] is Expression.Literal literalExpr) { if (PineValueAsInteger.SignedIntegerFromValueRelaxed(literalExpr.Value).IsOkOrNullable() is { } skipCount) { return - [ - .. sourceOps, - new StackInstruction(StackInstructionKind.Skip_Head_Const, SkipCount: (int)skipCount) - ]; + afterSource + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Skip_Head_Const, + SkipCount: (int)skipCount)); } } - var skipCountOps = + var afterSkipCount = CompileExpressionTransitive( skipList.items[0], - localIndexFromExpr); + copyToLocal, + afterSource); return - [ - .. sourceOps, - .. skipCountOps, - new StackInstruction(StackInstructionKind.Skip_Head_Var) - ]; + afterSkipCount + .AppendInstruction(new StackInstruction(StackInstructionKind.Skip_Head_Var)); } } - var inputOps = + return CompileExpressionTransitive( input, - localIndexFromExpr); - - return - [.. inputOps, - new StackInstruction(StackInstructionKind.Head_Generic) - ]; + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Head_Generic)); } - public static IReadOnlyList CompileKernelApplication_Skip( - Expression input, - Func localIndexFromExpr) + public static NodeCompilationResult CompileKernelApplication_Skip( + Expression input, + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr && listExpr.items.Count is 2) { - var skipCountOps = + var afterSource = CompileExpressionTransitive( - listExpr.items[0], - localIndexFromExpr); + listExpr.items[1], + copyToLocal, + prior); - var sourceOps = + var afterSkipCount = CompileExpressionTransitive( - listExpr.items[1], - localIndexFromExpr); + listExpr.items[0], + copyToLocal, + afterSource); return - [ - .. sourceOps, - .. skipCountOps, - new StackInstruction(StackInstructionKind.Skip_Binary) - ]; + afterSkipCount + .AppendInstruction(new StackInstruction(StackInstructionKind.Skip_Binary)); } - var inputOps = + return CompileExpressionTransitive( input, - localIndexFromExpr); - - return - [.. inputOps, - new StackInstruction(StackInstructionKind.Skip_Generic) - ]; + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Skip_Generic)); } - public static IReadOnlyList CompileKernelApplication_Take( + public static NodeCompilationResult CompileKernelApplication_Take( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr && listExpr.items.Count is 2) { var takeCountValueExpr = listExpr.items[0]; - var takeCountOps = - CompileExpressionTransitive( - takeCountValueExpr, - localIndexFromExpr); - if (listExpr.items[1] is Expression.KernelApplication sourceKernelApp && sourceKernelApp.Function is "skip") { if (sourceKernelApp.Input is Expression.List skipList && skipList.items.Count is 2) { - var skipCountOps = - CompileExpressionTransitive( - skipList.items[0], - localIndexFromExpr); - - var sourceOps = + var afterSource = CompileExpressionTransitive( skipList.items[1], - localIndexFromExpr); + copyToLocal, + prior); /* * Earlier reduction pass should already have reduced the contents in take to @@ -374,61 +551,78 @@ public static IReadOnlyList CompileKernelApplication_Take( { if (KernelFunction.SignedIntegerFromValueRelaxed(takeCountLiteral.Value) is { } takeCount) { + var afterSkipCount = + CompileExpressionTransitive( + skipList.items[0], + copyToLocal, + afterSource); + return - [ - .. sourceOps, - .. skipCountOps, - new StackInstruction( - StackInstructionKind.Slice_Skip_Var_Take_Const, - TakeCount: (int)takeCount) - ]; + afterSkipCount + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Slice_Skip_Var_Take_Const, + TakeCount: (int)takeCount)); } } - return - [ - .. sourceOps, - .. skipCountOps, - .. takeCountOps, - new StackInstruction(StackInstructionKind.Slice_Skip_Var_Take_Var) - ]; + { + var afterSkipCount = + CompileExpressionTransitive( + skipList.items[0], + copyToLocal, + afterSource); + + var afterTakeCount = + CompileExpressionTransitive( + takeCountValueExpr, + copyToLocal, + afterSkipCount); + + return + afterTakeCount + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Slice_Skip_Var_Take_Var)); + } } } { - var sourceOps = + var afterSource = CompileExpressionTransitive( listExpr.items[1], - localIndexFromExpr); + copyToLocal, + prior); + + var afterTakeCount = + CompileExpressionTransitive( + takeCountValueExpr, + copyToLocal, + afterSource); return - [ - .. sourceOps, - .. takeCountOps, - new StackInstruction(StackInstructionKind.Take_Binary) - ]; + afterTakeCount + .AppendInstruction(new StackInstruction(StackInstructionKind.Take_Binary)); } } - var inputOps = + return CompileExpressionTransitive( input, - localIndexFromExpr); - - return - [ - .. inputOps, - new StackInstruction(StackInstructionKind.Take_Generic) - ]; + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Take_Generic)); } - public static IReadOnlyList CompileKernelApplication_Concat( + public static NodeCompilationResult CompileKernelApplication_Concat( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { - var concatOps = new List(); + var concatOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { @@ -437,13 +631,16 @@ public static IReadOnlyList CompileKernelApplication_Concat( var itemOps = CompileExpressionTransitive( item, - localIndexFromExpr); + copyToLocal, + concatOps); - concatOps.AddRange(itemOps); + concatOps = itemOps; if (0 < i) { - concatOps.Add(new StackInstruction(StackInstructionKind.Concat_Binary)); + concatOps = + concatOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Concat_Binary)); } } @@ -452,73 +649,82 @@ public static IReadOnlyList CompileKernelApplication_Concat( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Concat_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Concat_List)); } } - public static IReadOnlyList CompileKernelApplication_Reverse( + public static NodeCompilationResult CompileKernelApplication_Reverse( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { - return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Reverse) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Reverse)); } - public static IReadOnlyList CompileKernelApplication_Int_Add( + public static NodeCompilationResult CompileKernelApplication_Int_Add( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0) { return - [ - new StackInstruction( - StackInstructionKind.Push_Literal, - Literal: PineValueAsInteger.ValueFromSignedInteger(0)) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(0))); } - var addOps = new List(); + var addOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { - var item = listExpr.items[i]; + var itemExpr = listExpr.items[i]; - if (0 < i && TryParseExprAsIntNegation(item) is { } negatedItem) + if (0 < i && TryParseExprAsIntNegation(itemExpr) is { } negatedItemExpr) { var itemOps = CompileExpressionTransitive( - negatedItem, - localIndexFromExpr); + negatedItemExpr, + copyToLocal, + addOps); - addOps.AddRange(itemOps); + addOps = itemOps; if (0 < i) { - addOps.Add(new StackInstruction(StackInstructionKind.Int_Sub_Binary)); + addOps = + addOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Int_Sub_Binary)); } } else { var itemOps = CompileExpressionTransitive( - item, - localIndexFromExpr); + itemExpr, + copyToLocal, + addOps); - addOps.AddRange(itemOps); + addOps = itemOps; if (0 < i) { - addOps.Add(new StackInstruction(StackInstructionKind.Int_Add_Binary)); + addOps = + addOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Int_Add_Binary)); } } } @@ -528,43 +734,50 @@ public static IReadOnlyList CompileKernelApplication_Int_Add( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Int_Add_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Int_Add_List)); } } - public static IReadOnlyList CompileKernelApplication_Int_Mul( + public static NodeCompilationResult CompileKernelApplication_Int_Mul( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0) { return - [ - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(1)) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(1))); } - var mulOps = new List(); + var mulOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { - var item = listExpr.items[i]; + var itemExpr = listExpr.items[i]; var itemOps = CompileExpressionTransitive( - item, - localIndexFromExpr); + itemExpr, + copyToLocal, + mulOps); - mulOps.AddRange(itemOps); + mulOps = itemOps; if (0 < i) { - mulOps.Add(new StackInstruction(StackInstructionKind.Int_Mul_Binary)); + mulOps = + mulOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Int_Mul_Binary)); } } @@ -573,85 +786,95 @@ public static IReadOnlyList CompileKernelApplication_Int_Mul( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Int_Mul_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Int_Mul_List)); } } - public static IReadOnlyList CompileKernelApplication_Int_Is_Sorted_Asc( + public static NodeCompilationResult CompileKernelApplication_Int_Is_Sorted_Asc( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0 || listExpr.items.Count is 1) { return - [ - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineVMValues.TrueValue) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineVMValues.TrueValue)); } if (listExpr.items.Count is 2) { - var leftOps = + var afterLeft = CompileExpressionTransitive( listExpr.items[0], - localIndexFromExpr); + copyToLocal, + prior); - var rightOps = + var afterRight = CompileExpressionTransitive( listExpr.items[1], - localIndexFromExpr); + copyToLocal, + afterLeft); return - [ - .. leftOps, - .. rightOps, - new StackInstruction(StackInstructionKind.Int_Less_Than_Or_Equal_Binary) - ]; + afterRight + .AppendInstruction(new StackInstruction(StackInstructionKind.Int_Less_Than_Or_Equal_Binary)); } } return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Int_Is_Sorted_Asc_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Int_Is_Sorted_Asc_List)); } - public static IReadOnlyList CompileKernelApplication_Bit_And( + public static NodeCompilationResult CompileKernelApplication_Bit_And( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0) { return - [ - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyBlob) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyBlob)); } - var andOps = new List(); + var andOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { - var item = listExpr.items[i]; + var itemExpr = listExpr.items[i]; var itemOps = CompileExpressionTransitive( - item, - localIndexFromExpr); + itemExpr, + copyToLocal, + andOps); - andOps.AddRange(itemOps); + andOps = itemOps; if (0 < i) { - andOps.Add(new StackInstruction(StackInstructionKind.Bit_And_Binary)); + andOps = + andOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Bit_And_Binary)); } } @@ -660,43 +883,50 @@ public static IReadOnlyList CompileKernelApplication_Bit_And( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_And_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_And_List)); } } - public static IReadOnlyList CompileKernelApplication_Bit_Or( + public static NodeCompilationResult CompileKernelApplication_Bit_Or( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0) { return - [ - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyBlob) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyBlob)); } - var orOps = new List(); + var orOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { - var item = listExpr.items[i]; + var itemExpr = listExpr.items[i]; var itemOps = CompileExpressionTransitive( - item, - localIndexFromExpr); + itemExpr, + copyToLocal, + orOps); - orOps.AddRange(itemOps); + orOps = itemOps; if (0 < i) { - orOps.Add(new StackInstruction(StackInstructionKind.Bit_Or_Binary)); + orOps = + orOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Bit_Or_Binary)); } } @@ -705,43 +935,50 @@ public static IReadOnlyList CompileKernelApplication_Bit_Or( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_Or_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Or_List)); } } - public static IReadOnlyList CompileKernelApplication_Bit_Xor( + public static NodeCompilationResult CompileKernelApplication_Bit_Xor( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr) { if (listExpr.items.Count is 0) { return - [ - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyBlob) - ]; + prior + .AppendInstruction( + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyBlob)); } - var xorOps = new List(); + var xorOps = prior; for (var i = 0; i < listExpr.items.Count; ++i) { - var item = listExpr.items[i]; + var itemExpr = listExpr.items[i]; var itemOps = CompileExpressionTransitive( - item, - localIndexFromExpr); + itemExpr, + copyToLocal, + xorOps); - xorOps.AddRange(itemOps); + xorOps = itemOps; if (0 < i) { - xorOps.Add(new StackInstruction(StackInstructionKind.Bit_Xor_Binary)); + xorOps = + xorOps.AppendInstruction( + new StackInstruction(StackInstructionKind.Bit_Xor_Binary)); } } @@ -750,84 +987,87 @@ public static IReadOnlyList CompileKernelApplication_Bit_Xor( else { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_Xor_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Xor_List)); } } - public static IReadOnlyList CompileKernelApplication_Bit_Not( + public static NodeCompilationResult CompileKernelApplication_Bit_Not( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_Not) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Not)); } - public static IReadOnlyList CompileKernelApplication_Bit_Shift_Left( + public static NodeCompilationResult CompileKernelApplication_Bit_Shift_Left( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr && listExpr.items.Count is 2) { - var shiftCountOps = - CompileExpressionTransitive( - listExpr.items[0], - localIndexFromExpr); - var sourceOps = CompileExpressionTransitive( listExpr.items[1], - localIndexFromExpr); + copyToLocal, + prior); + + var shiftCountOps = + CompileExpressionTransitive( + listExpr.items[0], + copyToLocal, + sourceOps); return - [ - .. sourceOps, - .. shiftCountOps, - new StackInstruction(StackInstructionKind.Bit_Shift_Left_Var) - ]; + shiftCountOps + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Left_Var)); } - return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_Shift_Left_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Left_List)); } - public static IReadOnlyList CompileKernelApplication_Bit_Shift_Right( + public static NodeCompilationResult CompileKernelApplication_Bit_Shift_Right( Expression input, - Func localIndexFromExpr) + IReadOnlySet copyToLocal, + NodeCompilationResult prior) { if (input is Expression.List listExpr && listExpr.items.Count is 2) { - var shiftCountOps = - CompileExpressionTransitive( - listExpr.items[0], - localIndexFromExpr); - var sourceOps = CompileExpressionTransitive( listExpr.items[1], - localIndexFromExpr); + copyToLocal, + prior); + + var shiftCountOps = + CompileExpressionTransitive( + listExpr.items[0], + copyToLocal, + sourceOps); return - [ - .. sourceOps, - .. shiftCountOps, - new StackInstruction(StackInstructionKind.Bit_Shift_Right_Var) - ]; + shiftCountOps + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Right_Var)); } - return - [ - .. CompileExpressionTransitive(input, localIndexFromExpr), - new StackInstruction(StackInstructionKind.Bit_Shift_Right_List) - ]; + CompileExpressionTransitive( + input, + copyToLocal, + prior) + .AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Right_List)); } public static Expression? TryParseExprAsIntNegation(Expression expression) @@ -862,4 +1102,26 @@ .. CompileExpressionTransitive(input, localIndexFromExpr), return null; } + + public static bool ExpressionLargeEnoughForCSE(Expression expression) + { + if (expression is Expression.KernelApplication) + return true; + + if (expression is Expression.List list) + { + for (int i = 0; i < list.items.Count; ++i) + { + if (ExpressionLargeEnoughForCSE(list.items[i])) + return true; + } + } + + if (expression is Expression.StringTag stringTag) + { + return ExpressionLargeEnoughForCSE(stringTag.Tagged); + } + + return 10 < expression.SubexpressionCount; + } } diff --git a/implement/pine/PineVM/StackInstruction.cs b/implement/pine/PineVM/StackInstruction.cs index 0d9dd23c..24a55bde 100644 --- a/implement/pine/PineVM/StackInstruction.cs +++ b/implement/pine/PineVM/StackInstruction.cs @@ -24,7 +24,7 @@ public enum StackInstructionKind Push_Environment, /// - /// Pop the top value from the stack and store it in the local at index . + /// Copy the top value from the stack into the local at index . /// Local_Set, diff --git a/implement/test-elm-time/PineVMTests.cs b/implement/test-elm-time/PineVMTests.cs index 7161c955..0ed90a40 100644 --- a/implement/test-elm-time/PineVMTests.cs +++ b/implement/test-elm-time/PineVMTests.cs @@ -315,21 +315,25 @@ public void Compile_stack_frame_instructions() expected = new PineVM.StackFrameInstructions( [ + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyList), + StackInstruction.PushEnvironment, - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(1)), + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(1)), new StackInstruction(StackInstructionKind.Skip_Binary), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 3), - - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyList), + new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 0), - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), - - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyBlob), + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyBlob), - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), + new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 0), new StackInstruction(StackInstructionKind.BuildList, TakeCount: 4), @@ -385,6 +389,10 @@ public void Compile_stack_frame_instructions() expected = new PineVM.StackFrameInstructions( [ + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyList), + StackInstruction.PushEnvironment, new StackInstruction( @@ -395,15 +403,13 @@ public void Compile_stack_frame_instructions() new StackInstruction(StackInstructionKind.Concat_List), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 4), - - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyList), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 4), + new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 0), - new StackInstruction(StackInstructionKind.Push_Literal, Literal: PineValue.EmptyBlob), + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValue.EmptyBlob), - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 4), + new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 0), new StackInstruction(StackInstructionKind.BuildList, TakeCount: 4), @@ -514,6 +520,56 @@ public void Compile_stack_frame_instructions() ]) }, + new + { + expression = + (Expression)Expression.ListInstance( + [ + new Expression.Literal(PineValueAsInteger.ValueFromSignedInteger(17)), + Expression.ConditionalInstance( + condition: + Expression.EnvironmentInstance, + trueBranch: + new Expression.Literal(PineValueAsInteger.ValueFromSignedInteger(21)), + falseBranch: + new Expression.Literal(PineValueAsInteger.ValueFromSignedInteger(23)) + ), + new Expression.Literal(PineValueAsInteger.ValueFromSignedInteger(27)), + ]), + + expected = + new PineVM.StackFrameInstructions( + [ + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(17)), + + new StackInstruction(StackInstructionKind.Push_Environment), + + StackInstruction.Jump_If_True(offset : 2), + + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(23)), + + StackInstruction.Jump_Unconditional(offset: 2), + + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(21)), + + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(27)), + + new StackInstruction( + StackInstructionKind.BuildList, + TakeCount: 3), + + StackInstruction.Return, + ]) + }, + new { expression = @@ -545,10 +601,6 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(13)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 3), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), - StackInstruction.Return ]) }, @@ -604,10 +656,6 @@ public void Compile_stack_frame_instructions() new StackInstruction(StackInstructionKind.Parse_And_Eval), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 3), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), - StackInstruction.Return ]) }, @@ -646,6 +694,10 @@ public void Compile_stack_frame_instructions() expected = new PineVM.StackFrameInstructions( [ + new StackInstruction( + StackInstructionKind.Push_Literal, + Literal: PineValueAsInteger.ValueFromSignedInteger(47)), + new StackInstruction( StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(11)), @@ -670,14 +722,6 @@ public void Compile_stack_frame_instructions() new StackInstruction(StackInstructionKind.Parse_And_Eval), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 3), - - new StackInstruction( - StackInstructionKind.Push_Literal, - Literal: PineValueAsInteger.ValueFromSignedInteger(47)), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), - new StackInstruction(StackInstructionKind.BuildList, TakeCount: 2), StackInstruction.Return @@ -711,7 +755,7 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(11)), - StackInstruction.Jump_If_True(offset : 10), + StackInstruction.Jump_If_True(offset : 8), new StackInstruction( StackInstructionKind.Push_Literal, @@ -731,20 +775,12 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(21)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 7), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 7), - StackInstruction.Jump_Unconditional(2), new StackInstruction( StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(23)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 11), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 11), - StackInstruction.Return ]) }, @@ -803,8 +839,6 @@ public void Compile_stack_frame_instructions() new StackInstruction(StackInstructionKind.Parse_And_Eval), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 3), - new StackInstruction( StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(13)), @@ -825,12 +859,6 @@ public void Compile_stack_frame_instructions() new StackInstruction(StackInstructionKind.Parse_And_Eval), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 12), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 3), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 12), - new StackInstruction(StackInstructionKind.BuildList, TakeCount: 2), StackInstruction.Return @@ -862,7 +890,7 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(11)), - StackInstruction.Jump_If_True(offset : 8), + StackInstruction.Jump_If_True(offset : 6), new StackInstruction( StackInstructionKind.Push_Literal, @@ -880,20 +908,12 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(23)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 5), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 5), - StackInstruction.Jump_Unconditional(2), new StackInstruction( StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(13)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 9), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 9), - StackInstruction.Return ]) }, @@ -1067,19 +1087,17 @@ public void Compile_stack_frame_instructions() new StackInstruction( StackInstructionKind.Local_Set, - LocalIndex: 5), + LocalIndex: 0), - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 5), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 5), + new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 0), new StackInstruction( StackInstructionKind.BuildList, TakeCount: 2), - StackInstruction.Jump_If_True(offset: 8), + StackInstruction.Jump_If_True(offset: 6), - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 5), + new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 0), StackInstruction.Jump_If_True(offset: 2), @@ -1093,20 +1111,12 @@ public void Compile_stack_frame_instructions() StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(23)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 13), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 13), - StackInstruction.Jump_Unconditional(offset: 2), new StackInstruction( StackInstructionKind.Push_Literal, Literal: PineValueAsInteger.ValueFromSignedInteger(13)), - new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 17), - - new StackInstruction(StackInstructionKind.Local_Get, LocalIndex: 17), - StackInstruction.Return ]) }, @@ -1237,8 +1247,10 @@ public void Compile_stack_frame_instructions() var parseCache = new PineVMParseCache(); - foreach (var testCase in testCases) + for (var testCaseIndex = 0; testCaseIndex < testCases.Length; ++testCaseIndex) { + var testCase = testCases[testCaseIndex]; + var compiled = PineVM.CompileExpression( testCase.expression, @@ -1247,20 +1259,28 @@ public void Compile_stack_frame_instructions() disableReduction: true, skipInlining: (_, _) => false); - Assert.AreEqual( - testCase.expected.Instructions.Count, - compiled.Generic.Instructions.Count, - "Instructions count"); - - for (var i = 0; i < testCase.expected.Instructions.Count; i++) + try { Assert.AreEqual( - testCase.expected.Instructions[i], - compiled.Generic.Instructions[i], - $"Instruction at index {i} of " + compiled.Generic.Instructions.Count); + testCase.expected.Instructions.Count, + compiled.Generic.Instructions.Count, + "Instructions count"); + + for (var instructionIndex = 0; instructionIndex < testCase.expected.Instructions.Count; instructionIndex++) + { + Assert.AreEqual( + testCase.expected.Instructions[instructionIndex], + compiled.Generic.Instructions[instructionIndex], + $"Instruction at index {instructionIndex} of " + compiled.Generic.Instructions.Count); + } + + Assert.AreEqual(testCase.expected, compiled.Generic); + } + catch (System.Exception e) + { + throw new System.Exception( + $"Failed for test case {testCaseIndex} with expression {testCase.expression}", e); } - - Assert.AreEqual(testCase.expected, compiled.Generic); } } } \ No newline at end of file