Skip to content

Commit

Permalink
Optimize memory block sizes allocated for virtual stack frames
Browse files Browse the repository at this point in the history
Add stack depth analysis to compute a more precise upper bound for stack depth for a given stack frame.
  • Loading branch information
Viir committed Jan 6, 2025
1 parent 5182ae2 commit ba36482
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 26 deletions.
44 changes: 39 additions & 5 deletions implement/pine/Pine/PineVM/PineVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,40 @@ public record StackFrameInstructions(
.DefaultIfEmpty(-1)
.Max();

public int MaxStackUsage { init; get; } = ComputeMaxStackUsage(Instructions);

public static int ComputeMaxStackUsage(
IReadOnlyList<StackInstruction> Instructions)
{
int currentDepth = 0;
int maxDepth = 0;

for (int i = 0; i < Instructions.Count; i++)
{
var instr = Instructions[i];

var (popCount, pushCount) =
StackInstruction.GetPopCountAndPushCount(instr);

currentDepth = currentDepth - popCount + pushCount;

if (currentDepth < 0)
{
throw new InvalidOperationException(
"Instruction sequence pops more items than available.");
}

maxDepth =
maxDepth < currentDepth
?
currentDepth
:
maxDepth;
}

return maxDepth;
}

public virtual bool Equals(StackFrameInstructions? other)
{
if (other is not { } notNull)
Expand Down Expand Up @@ -299,7 +333,7 @@ StackFrame StackFrameFromExpression(
expression,
instructions,
EnvironmentValue: environment,
StackValues: new PineValue[instructions.Instructions.Count],
StackValues: new PineValue[instructions.MaxStackUsage],
LocalsValues: new PineValue[instructions.MaxLocalIndex + 1],
BeginInstructionCount: beginInstructionCount,
BeginParseAndEvalCount: beginParseAndEvalCount,
Expand Down Expand Up @@ -1516,7 +1550,7 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame)
continue;
}

case StackInstructionKind.Concat_List:
case StackInstructionKind.Concat_Generic:
{
var listValue = currentFrame.PopTopmostFromStack();

Expand Down Expand Up @@ -1797,7 +1831,7 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame)
continue;
}

case StackInstructionKind.Int_Is_Sorted_Asc_List:
case StackInstructionKind.Int_Is_Sorted_Asc_Generic:
{
var listValue = currentFrame.PopTopmostFromStack();

Expand Down Expand Up @@ -1981,7 +2015,7 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame)
continue;
}

case StackInstructionKind.Int_Add_List:
case StackInstructionKind.Int_Add_Generic:
{
var listValue = currentFrame.PopTopmostFromStack();

Expand All @@ -1992,7 +2026,7 @@ static ExecutionErrorReport buildErrorReport(StackFrame stackFrame)
continue;
}

case StackInstructionKind.Int_Mul_List:
case StackInstructionKind.Int_Mul_Generic:
{
var listValue = currentFrame.PopTopmostFromStack();

Expand Down
20 changes: 10 additions & 10 deletions implement/pine/PineVM/PineIRCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ public static NodeCompilationResult CompileKernelApplication_Equal(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Equal_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Equal_Generic));
}

public static NodeCompilationResult CompileKernelApplication_Head(
Expand Down Expand Up @@ -743,7 +743,7 @@ public static NodeCompilationResult CompileKernelApplication_Concat(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Concat_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Concat_Generic));
}
}

Expand Down Expand Up @@ -956,7 +956,7 @@ public static NodeCompilationResult CompileKernelApplication_Int_Add(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Add_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Add_Generic));
}
}

Expand Down Expand Up @@ -1045,7 +1045,7 @@ public static NodeCompilationResult CompileKernelApplication_Int_Mul(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Mul_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Mul_Generic));
}
}

Expand Down Expand Up @@ -1091,7 +1091,7 @@ public static NodeCompilationResult CompileKernelApplication_Int_Is_Sorted_Asc(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Is_Sorted_Asc_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Int_Is_Sorted_Asc_Generic));
}

public static NodeCompilationResult CompileKernelApplication_Bit_And(
Expand Down Expand Up @@ -1142,7 +1142,7 @@ public static NodeCompilationResult CompileKernelApplication_Bit_And(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_And_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_And_Generic));
}
}

Expand Down Expand Up @@ -1194,7 +1194,7 @@ public static NodeCompilationResult CompileKernelApplication_Bit_Or(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Or_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Or_Generic));
}
}

Expand Down Expand Up @@ -1246,7 +1246,7 @@ public static NodeCompilationResult CompileKernelApplication_Bit_Xor(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Xor_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Xor_Generic));
}
}

Expand Down Expand Up @@ -1291,7 +1291,7 @@ public static NodeCompilationResult CompileKernelApplication_Bit_Shift_Left(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Left_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Left_Generic));
}

public static NodeCompilationResult CompileKernelApplication_Bit_Shift_Right(
Expand Down Expand Up @@ -1322,7 +1322,7 @@ public static NodeCompilationResult CompileKernelApplication_Bit_Shift_Right(
input,
copyToLocal,
prior)
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Right_List));
.AppendInstruction(new StackInstruction(StackInstructionKind.Bit_Shift_Right_Generic));
}

public static Expression? TryParseExprAsIntNegation(Expression expression)
Expand Down
119 changes: 109 additions & 10 deletions implement/pine/PineVM/StackInstruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public enum StackInstructionKind

/// <summary>
/// Copy the top value from the stack into the local at index <see cref="StackInstruction.LocalIndex"/>.
/// The value is not popped from the stack.
/// </summary>
Local_Set,

Expand All @@ -47,7 +48,7 @@ public enum StackInstructionKind
/// <summary>
/// Concatenates the items in the list from the top value on the stack.
/// </summary>
Concat_List,
Concat_Generic,

/// <summary>
/// Concatenates the top value from the stack and the second value from the stack.
Expand Down Expand Up @@ -112,7 +113,7 @@ public enum StackInstructionKind
/// <summary>
/// Check if all items in the list from the top value on the stack are equal.
/// </summary>
Equal_List,
Equal_Generic,

/// <summary>
/// Negate the top value on the stack, works for both integers and booleans.
Expand All @@ -123,12 +124,16 @@ public enum StackInstructionKind
/// Corresponding to the kernel function 'int_is_sorted_asc' of the Pine language,
/// where we cannot proof a more specific representation is possible at compile time.
/// </summary>
Int_Is_Sorted_Asc_List,
Int_Is_Sorted_Asc_Generic,

Int_Less_Than_Binary,

Int_Less_Than_Or_Equal_Binary,

Int_Less_Than_Const,

Int_Less_Than_Or_Equal_Const,

/// <summary>
/// Jump to the offset from <see cref="StackInstruction.JumpOffset"/> if the top value on the stack is true.
/// </summary>
Expand Down Expand Up @@ -163,7 +168,7 @@ public enum StackInstructionKind
/// <summary>
/// Add all items in the list from the top value from the stack.
/// </summary>
Int_Add_List,
Int_Add_Generic,

/// <summary>
/// Subtract the top value from the second value on the stack.
Expand All @@ -183,17 +188,21 @@ public enum StackInstructionKind
/// <summary>
/// Multiply all items in the list from the top value from the stack.
/// </summary>
Int_Mul_List,
Int_Mul_Generic,

Bit_And_List,
Bit_And_Generic,

Bit_And_Binary,

Bit_Or_List,
Bit_And_Const,

Bit_Or_Generic,

Bit_Or_Binary,

Bit_Xor_List,
Bit_Or_Const,

Bit_Xor_Generic,

Bit_Xor_Binary,

Expand All @@ -203,13 +212,13 @@ public enum StackInstructionKind

Bit_Shift_Left_Const,

Bit_Shift_Left_List,
Bit_Shift_Left_Generic,

Bit_Shift_Right_Var,

Bit_Shift_Right_Const,

Bit_Shift_Right_List,
Bit_Shift_Right_Generic,
}

/// <summary>
Expand Down Expand Up @@ -258,6 +267,96 @@ public static StackInstruction Jump_Unconditional(int offset) =>
/// </summary>
public static StackInstruction Jump_If_True(int offset) =>
new(StackInstructionKind.Jump_If_True_Const, JumpOffset: offset);

public static (int popCount, int pushCount) GetPopCountAndPushCount(StackInstruction instruction) =>
instruction.Kind switch
{
StackInstructionKind.Push_Literal => (0, 1),
StackInstructionKind.Push_Environment => (0, 1),

StackInstructionKind.Local_Set => (0, 0),
StackInstructionKind.Local_Get => (0, 1),

StackInstructionKind.Pop => (1, 0),

StackInstructionKind.Length => (1, 1),

StackInstructionKind.Concat_Generic => (1, 1),
StackInstructionKind.Concat_Binary => (2, 1),

StackInstructionKind.Slice_Skip_Var_Take_Var => (3, 1),
StackInstructionKind.Slice_Skip_Var_Take_Const => (2, 1),

StackInstructionKind.Skip_Generic => (1, 1),
StackInstructionKind.Skip_Binary => (2, 1),

StackInstructionKind.Take_Generic => (1, 1),
StackInstructionKind.Take_Binary => (2, 1),

StackInstructionKind.Skip_Head_Var => (2, 1),
StackInstructionKind.Skip_Head_Const => (1, 1),

StackInstructionKind.Head_Generic => (1, 1),

StackInstructionKind.Reverse => (1, 1),

StackInstructionKind.BuildList =>
(instruction.TakeCount
?? throw new System.Exception(
"Missing TakeCount for BuildList instruction"),
1),

StackInstructionKind.Equal_Binary_Var => (2, 1),
StackInstructionKind.Not_Equal_Binary_Var => (2, 1),
StackInstructionKind.Equal_Binary_Const => (1, 1),
StackInstructionKind.Not_Equal_Binary_Const => (1, 1),
StackInstructionKind.Equal_Generic => (1, 1),
StackInstructionKind.Negate => (1, 1),

StackInstructionKind.Int_Is_Sorted_Asc_Generic => (1, 1),
StackInstructionKind.Int_Less_Than_Binary => (2, 1),
StackInstructionKind.Int_Less_Than_Or_Equal_Binary => (2, 1),
StackInstructionKind.Int_Less_Than_Const => (1, 1),
StackInstructionKind.Int_Less_Than_Or_Equal_Const => (1, 1),

StackInstructionKind.Jump_If_True_Const => (1, 0),
StackInstructionKind.Jump_Const => (0, 0),
StackInstructionKind.Return => (1, 0),
StackInstructionKind.Parse_And_Eval => (2, 1),

StackInstructionKind.Int_Add_Binary => (2, 1),
StackInstructionKind.Int_Add_Const => (1, 1),
StackInstructionKind.Int_Add_Generic => (1, 1),
StackInstructionKind.Int_Sub_Binary => (2, 1),
StackInstructionKind.Int_Mul_Binary => (2, 1),
StackInstructionKind.Int_Mul_Const => (1, 1),
StackInstructionKind.Int_Mul_Generic => (1, 1),

StackInstructionKind.Bit_And_Generic => (1, 1),
StackInstructionKind.Bit_And_Binary => (2, 1),
StackInstructionKind.Bit_And_Const => (1, 1),

StackInstructionKind.Bit_Or_Generic => (1, 1),
StackInstructionKind.Bit_Or_Binary => (2, 1),
StackInstructionKind.Bit_Or_Const => (1, 1),

StackInstructionKind.Bit_Xor_Generic => (1, 1),
StackInstructionKind.Bit_Xor_Binary => (2, 1),

StackInstructionKind.Bit_Not => (1, 1),

StackInstructionKind.Bit_Shift_Left_Var => (2, 1),
StackInstructionKind.Bit_Shift_Left_Const => (1, 1),
StackInstructionKind.Bit_Shift_Left_Generic => (1, 1),

StackInstructionKind.Bit_Shift_Right_Var => (2, 1),
StackInstructionKind.Bit_Shift_Right_Const => (1, 1),
StackInstructionKind.Bit_Shift_Right_Generic => (1, 1),

var otherKind =>
throw new System.NotImplementedException(
"Unknown StackInstructionKind: " + otherKind)
};
}


2 changes: 1 addition & 1 deletion implement/test-elm-time/PineVMTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ public void Compile_stack_frame_instructions()

new StackInstruction(StackInstructionKind.Skip_Binary),

new StackInstruction(StackInstructionKind.Concat_List),
new StackInstruction(StackInstructionKind.Concat_Generic),

new StackInstruction(StackInstructionKind.Local_Set, LocalIndex: 0),

Expand Down

0 comments on commit ba36482

Please sign in to comment.