Skip to content

Commit

Permalink
Reduce allocs in AST building. Add ToAst extension method to Architec…
Browse files Browse the repository at this point in the history
…ture.
  • Loading branch information
Washi1337 committed Jan 4, 2024
1 parent 7ed767f commit 9f85b59
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 39 deletions.
15 changes: 15 additions & 0 deletions src/Core/Echo.Ast/AstArchitecture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,19 @@ public int GetWrittenVariables(in Statement<TInstruction> instruction, Span<IVar
return finder.Variables.Count;
}
}

/// <summary>
/// Provides extension methods for AST architectures.
/// </summary>
public static class AstArchitectureExtensions
{
/// <summary>
/// Wraps the provided architecture to an AST architecture.
/// </summary>
/// <param name="self">The architecture.</param>
/// <typeparam name="TInstruction">The type of instructions defined by the architecture.</typeparam>
/// <returns>The lifted architecture.</returns>
public static AstArchitecture<TInstruction> ToAst<TInstruction>(this IArchitecture<TInstruction> self)
=> new(self);
}
}
73 changes: 34 additions & 39 deletions src/Core/Echo.Ast/Construction/AstBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ namespace Echo.Ast.Construction;
public sealed class AstBuilder<TInstruction>
{
private readonly ControlFlowGraph<TInstruction> _original;
private readonly ControlFlowGraph<Statement<TInstruction>> _transformed;
private readonly ControlFlowGraph<Statement<TInstruction>> _lifted;
private readonly IPurityClassifier<TInstruction> _purityClassifier;
private readonly Dictionary<ControlFlowNode<TInstruction>, LiftedNode<TInstruction>> _liftedNodes = new();

private AstBuilder(ControlFlowGraph<TInstruction> original, IPurityClassifier<TInstruction> purityClassifier)
{
_original = original;
_purityClassifier = purityClassifier;
_transformed = new ControlFlowGraph<Statement<TInstruction>>(new AstArchitecture<TInstruction>(original.Architecture));
_lifted = new ControlFlowGraph<Statement<TInstruction>>(original.Architecture.ToAst());
}

/// <summary>
Expand All @@ -36,7 +36,7 @@ public static ControlFlowGraph<Statement<TInstruction>> Lift(
{
var builder = new AstBuilder<TInstruction>(cfg, purityClassifier);
builder.Run();
return builder._transformed;
return builder._lifted;
}

private void Run()
Expand Down Expand Up @@ -66,7 +66,7 @@ private void LiftNodes()
{
var liftedNode = LiftNode(node);
_liftedNodes.Add(node, liftedNode);
_transformed.Nodes.Add(liftedNode.Transformed);
_lifted.Nodes.Add(liftedNode.Transformed);
}
}

Expand Down Expand Up @@ -173,28 +173,25 @@ private void LiftInstruction(

private static Expression<TInstruction> Pop(LiftedNode<TInstruction> node, Stack<Expression<TInstruction>> stack)
{
if (stack.Count == 0)
{
var variable = node.DeclareStackInput();

var expression = variable.ToExpression<TInstruction>();
node.StackInputReferences.Add(variable, expression);

return expression;
}
else
// If there is something on the stack, we can pop it. Otherwise it is a stack-input for this basic block.
if (stack.Count != 0)
return stack.Pop();

var variable = node.DeclareStackInput();
var expression = variable.ToExpression<TInstruction>();
node.StackInputReferences.Add(variable, expression);
return expression;
}

private static void FlushStackAsOutput(LiftedNode<TInstruction> node, Stack<Expression<TInstruction>> stack)
{
FlushStackInternal(node, stack, (n, value) =>
FlushStackInternal(node, stack, static (n, value) =>
{
if (value is VariableExpression<TInstruction> {Variable: SyntheticVariable variable})
{
// If this output expression is already variable expression, we do not need to allocate a new variable
// and can instead just promote the variable to a stack output variable (inlining).
node.StackOutputs.Add(variable);
n.StackOutputs.Add(variable);
}
else
{
Expand All @@ -209,7 +206,7 @@ private static void FlushStackAsOutput(LiftedNode<TInstruction> node, Stack<Expr

private static void FlushStackAndPush(LiftedNode<TInstruction> node, Stack<Expression<TInstruction>> stack)
{
var variables = FlushStackInternal(node, stack, (n, value) =>
var variables = FlushStackInternal(node, stack, static (n, value) =>
{
var intermediate = n.DeclareStackIntermediate();
n.Transformed.Contents.Instructions.Add(Statement.Assignment(intermediate, value));
Expand All @@ -225,7 +222,7 @@ private void FlushStackIfImpure(
Stack<Expression<TInstruction>> stack,
Expression<TInstruction> expression)
{
// Is this expression potentially impure?
// Is this expression pure with 100% certainty?
if (expression.IsPure(_purityClassifier).ToBooleanOrFalse())
return;

Expand Down Expand Up @@ -272,53 +269,51 @@ private void PopulatePhiStatements()

while (agenda.Count > 0)
{
var current = agenda.Dequeue();
var liftedNode = _liftedNodes[current.Node];
var currentState = agenda.Dequeue();
var liftedNode = _liftedNodes[currentState.Node];

// Have we visited this block before?
bool changed = false;
if (!recordedStates.TryGetValue(liftedNode, out var previousState))
{
// We have never visited this block before. Register the new state.
recordedStates[liftedNode] = current;
changed = true;
recordedStates[liftedNode] = currentState;
}
else if (previousState.MergeWith(current, out var newState))
else if (previousState.MergeWith(currentState, out var mergedState))
{
// Merging the states resulted in a change. We have to revisit this path.
current = newState;
recordedStates[liftedNode] = newState;
changed = true;
currentState = mergedState;
recordedStates[liftedNode] = mergedState;
}

// If we did not make any change to the states, we can stop.
if (!changed)
else
{
// We did not change anything to the recorded input states, so there is no need to recompute the PHI
// nodes nor any of its successors.
continue;
}

// Consume stack values, and add them to the phi statements.
for (int i = liftedNode.StackInputs.Count - 1; i >= 0; i--)
{
var input = liftedNode.StackInputs[i];

// Protection against malformed code streams with stack underflow.
if (current.Stack.IsEmpty)
if (currentState.Stack.IsEmpty)
break;

current = current.Pop(out var value);
currentState = currentState.Pop(out var value);
foreach (var source in value.Sources)
{
if (input.Sources.All(x => x.Variable != source))
if (!input.HasSource(source))
input.Sources.Add(source.ToExpression<TInstruction>());
}
}

// Push new values on stack.
foreach (var output in liftedNode.StackOutputs)
current = current.Push(new StackSlot(output));
currentState = currentState.Push(new StackSlot(output));

// Schedule successors.
foreach (var successor in current.Node.GetSuccessors())
agenda.Enqueue(current.MoveTo(successor));
foreach (var successor in currentState.Node.GetSuccessors())
agenda.Enqueue(currentState.MoveTo(successor));
}
}

Expand Down Expand Up @@ -367,9 +362,9 @@ private void AddEdges()

private void TransformRegions()
{
_transformed.EntryPoint = _liftedNodes[_original.EntryPoint].Transformed;
_lifted.EntryPoint = _liftedNodes[_original.EntryPoint].Transformed;
foreach (var region in _original.Regions)
TransformRegion(x => _transformed.Regions.Add((ControlFlowRegion<Statement<TInstruction>>) x), region);
TransformRegion(x => _lifted.Regions.Add((ControlFlowRegion<Statement<TInstruction>>) x), region);
}

private void TransformRegion(
Expand Down
16 changes: 16 additions & 0 deletions src/Core/Echo.Ast/PhiStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public PhiStatement<TInstruction> WithRepresentative(IVariable variable)
return this;
}

/// <summary>
/// Determines whether the provided variable is a source for this PHI node.
/// </summary>
/// <param name="variable">The variable.</param>
/// <returns><c>true</c> if the variable is a valid source, <c>false</c> otherwise.</returns>
public bool HasSource(IVariable variable)
{
for (int i = 0; i < Sources.Count; i++)
{
if (Sources[i].Variable == variable)
return true;
}

return false;
}

/// <summary>
/// Modifies the current <see cref="PhiStatement{TInstruction}"/> to assign values from <paramref name="sources"/>
/// </summary>
Expand Down

0 comments on commit 9f85b59

Please sign in to comment.