Skip to content

Commit

Permalink
- Added logical operators (and, or, not)
Browse files Browse the repository at this point in the history
 - Added a lot more parts of CFG reduction and analysis (not yet completed)
  • Loading branch information
martindevans committed Jul 17, 2019
1 parent f57018f commit b0efca6
Show file tree
Hide file tree
Showing 54 changed files with 1,551 additions and 172 deletions.
22 changes: 22 additions & 0 deletions Yolol.Analysis/ControlFlowGraph/AST/ErrorExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Yolol.Execution;
using Yolol.Grammar.AST.Expressions;

namespace Yolol.Analysis.ControlFlowGraph.AST
{
public class ErrorExpression
: BaseExpression
{
public override bool IsConstant => true;
public override bool IsBoolean => false;
public override bool CanRuntimeError => true;
public override Value Evaluate(MachineState state)
{
throw new ExecutionException("Static error");
}

public override string ToString()
{
return "error()";
}
}
}
20 changes: 20 additions & 0 deletions Yolol.Analysis/ControlFlowGraph/AST/ErrorStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Yolol.Execution;
using Yolol.Grammar.AST.Statements;

namespace Yolol.Analysis.ControlFlowGraph.AST
{
public class ErrorStatement
: BaseStatement
{
public override bool CanRuntimeError => true;
public override ExecutionResult Evaluate(MachineState state)
{
throw new ExecutionException("Static error");
}

public override string ToString()
{
return "error()";
}
}
}
7 changes: 4 additions & 3 deletions Yolol.Analysis/ControlFlowGraph/AST/Phi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using JetBrains.Annotations;
using Yolol.Analysis.ControlFlowGraph.Extensions;
using Yolol.Execution;
using Yolol.Grammar;
using Yolol.Grammar.AST.Expressions;

namespace Yolol.Analysis.ControlFlowGraph.AST
Expand All @@ -14,12 +15,12 @@ namespace Yolol.Analysis.ControlFlowGraph.AST
public class Phi
: BaseExpression
{
public string BaseVariable { get; }
public IReadOnlyList<string> AssignedNames { get; }
public VariableName BaseVariable { get; }
public IReadOnlyList<VariableName> AssignedNames { get; }

public ISingleStaticAssignmentTable SSA { get; }

public Phi([NotNull] ISingleStaticAssignmentTable ssa, [NotNull] params string[] assignedNames)
public Phi([NotNull] ISingleStaticAssignmentTable ssa, [NotNull] params VariableName[] assignedNames)
{
if (assignedNames.Length == 0)
throw new ArgumentException("Must specify one or more assigned names");
Expand Down
11 changes: 1 addition & 10 deletions Yolol.Analysis/ControlFlowGraph/ControlFlowGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,4 @@ public override int GetHashCode()
#endregion
}
}
}

/* a = b++ * c * d++
*
w = b++
x = w * c -->
y = d++
z = x * y
a = z
*/
}
30 changes: 30 additions & 0 deletions Yolol.Analysis/ControlFlowGraph/Extensions/AnalysisExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Yolol.Analysis.TreeVisitor.Inspection;
using Yolol.Grammar;
using Yolol.Grammar.AST.Statements;

namespace Yolol.Analysis.ControlFlowGraph.Extensions
{
public static class AnalysisExtensions
{
[NotNull] public static IReadOnlyCollection<VariableName> FindUnreadAssignments([NotNull] this IControlFlowGraph cfg)
{
var assigned = new FindAssignedVariables();
var read = new FindReadVariables();

foreach (var bb in cfg.Vertices)
{
var p = new Program(new[] {new Line(new StatementList(bb.Statements))});
assigned.Visit(p);
read.Visit(p);
}

var result = new HashSet<VariableName>(assigned.Names.Where(n => !n.IsExternal));
result.ExceptWith(read.Names);

return result;
}
}
}
38 changes: 25 additions & 13 deletions Yolol.Analysis/ControlFlowGraph/Extensions/DotFormatExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,39 @@ public static string ToDot([NotNull] this IControlFlowGraph graph)
var root = graph.Vertices.Single(a => a.Type == BasicBlockType.Entry);
sb.AppendLine($" {ID(root.ID)} [label=\"entry\" shape=note rank=min];");

foreach (var edge in graph.Edges)
string EdgeAsString(IEdge edge)
{
// 35 -> 36 [label="eol_fallthrough" color="r g b"];
var a = ID(edge.Start.ID);
var b = ID(edge.End.ID);

string col;
switch (edge.Type)
{
case EdgeType.GotoConstStr:
case EdgeType.RuntimeError:
col = "#ff0000";
break;
default:
col = "#000000";
break;
}
var col = "#000000";
if (edge.Type == EdgeType.GotoConstStr || edge.Type == EdgeType.RuntimeError)
col = "#ff0000";

sb.AppendLine($" {a} -> {b} [label=\"{edge.Type}\" color=\"{col}\"];");
return $" {a} -> {b} [label=\"{edge.Type}\" color=\"{col}\"];";
}

// Group edges into clusters (transfers within one line)
var clusters = from edge in graph.Edges
where edge.Start.LineNumber == edge.End.LineNumber
group edge by edge.Start.LineNumber into grps
select grps;
var others = from edge in graph.Edges
where edge.Start.LineNumber != edge.End.LineNumber
select edge;

foreach (var cluster in clusters)
{
sb.AppendLine($" subgraph cluster_L{cluster.Key} {{");
foreach (var edge in cluster)
sb.AppendLine($" {EdgeAsString(edge)}");
sb.AppendLine(" }");
}

foreach (var edge in others)
sb.AppendLine($"{EdgeAsString(edge)}");

var vs = graph.Vertices.Where(a => a.Type != BasicBlockType.Entry).Select(vertex => {
var style = vertex.Type == BasicBlockType.LineStart ? "style=none" : "style=rounded";
return $" {ID(vertex.ID)} [label=\"{vertex.ToString().Replace("\"", "'")}\" shape=box {style}]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Yolol.Analysis.TreeVisitor;
using Yolol.Grammar.AST.Statements;

namespace Yolol.Analysis.ControlFlowGraph.Extensions
{
Expand All @@ -11,7 +13,7 @@ [NotNull] private static IReadOnlyDictionary<IBasicBlock, IMutableBasicBlock> Cl
[NotNull] this IControlFlowGraph input,
ControlFlowGraph output,
[NotNull] Func<IBasicBlock, bool> keep,
[NotNull] Action<IBasicBlock, IMutableBasicBlock> copy = null
[CanBeNull] Action<IBasicBlock, IMutableBasicBlock> copy = null
)
{
// Clone vertices (without edges)
Expand Down Expand Up @@ -111,6 +113,25 @@ [NotNull] public static IControlFlowGraph Modify([NotNull] this IControlFlowGrap
return cfg;
}

[NotNull] public static IControlFlowGraph Modify([NotNull] this IControlFlowGraph input, [NotNull] Action<IEdge, Action<IBasicBlock, IBasicBlock, EdgeType>> copy)
{
var cfg = new ControlFlowGraph();

// Copy vertices
var replacementVertices = input.CloneVertices(cfg, __ => true);

// Copy edges
foreach (var edge in input.Edges)
{
copy(edge, (s, e, t) => {
var ss = replacementVertices[s];
var ee = replacementVertices[e];
cfg.CreateEdge(ss, ee, t);
});
}

return cfg;
}

/// <summary>
/// Add edges to the graph
Expand All @@ -136,5 +157,42 @@ [NotNull] public static IControlFlowGraph Add([NotNull] this IControlFlowGraph i

return cfg;
}

/// <summary>
/// Apply an AST visitor to each control flow graph vertex
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="input"></param>
/// <param name="factory"></param>
/// <returns></returns>
[NotNull] public static IControlFlowGraph VisitBlocks<T>([NotNull] this IControlFlowGraph input, Func<T> factory)
where T : BaseTreeVisitor
{
return input.Modify((a, b) => {
// Create a program from the statements in this block, visit it
var visited = factory().Visit(new Program(new[] { new Line(new StatementList(a.Statements)) }));
// Extract statements from single line of result programand copy into result block
foreach (var stmt in visited.Lines.Single().Statements.Statements)
b.Add(stmt);
});
}

/// <summary>
/// Apply an AST visitor to each control flow graph vertex
/// </summary>
/// <typeparam name="TVisitor"></typeparam>
/// <typeparam name="TPrep"></typeparam>
/// <param name="input"></param>
/// <param name="factory"></param>
/// <param name="prepare"></param>
/// <returns></returns>
[NotNull] public static IControlFlowGraph VisitBlocks<TVisitor, TPrep>([NotNull] this IControlFlowGraph input, [NotNull] Func<TPrep, TVisitor> factory, [NotNull] Func<IControlFlowGraph, TPrep> prepare)
where TVisitor : BaseTreeVisitor
{
var prep = prepare(input);
return VisitBlocks(input, () => factory(prep));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using JetBrains.Annotations;
using Yolol.Analysis.ControlFlowGraph.AST;
using Yolol.Analysis.Types;
using Yolol.Grammar.AST.Statements;

namespace Yolol.Analysis.ControlFlowGraph.Extensions
{
Expand Down Expand Up @@ -72,20 +71,31 @@ from input in vertex.Incoming
return g;
}

/// <summary>
/// Removed error/continue edges which we know cannot happen due to type info
/// </summary>
/// <param name="graph"></param>
/// <param name="types"></param>
/// <returns></returns>
[NotNull] public static IControlFlowGraph TypeDrivenEdgeTrimming([NotNull] this IControlFlowGraph graph, ITypeAssignments types)
{
return graph.Trim(edge => {
// Find last statement in previous block
var stmt = edge.Start.Statements.LastOrDefault();
var tass = stmt as TypedAssignment;
var err = stmt as ErrorStatement;
// If type is unassigned we can't make a judgement
if (tass?.Type == Execution.Type.Unassigned)
return true;
if (edge.Type == EdgeType.RuntimeError)
{
// If it's an error statement keep it
if (err != null)
return true;
// If there is no statement at all then it can't be an error
if (tass == null)
return false;
Expand All @@ -95,6 +105,10 @@ [NotNull] public static IControlFlowGraph TypeDrivenEdgeTrimming([NotNull] this
}
else
{
// If it's an error statement remove it
if (err != null)
return false;
// If there is no typed assignment we can't judge
if (tass == null)
return true;
Expand All @@ -103,5 +117,37 @@ [NotNull] public static IControlFlowGraph TypeDrivenEdgeTrimming([NotNull] this
}
});
}

/// <summary>
/// Replaces nodes with an error statement and an error edge with an empty node and a continue edge
/// </summary>
/// <param name="cfg"></param>
/// <returns></returns>
[NotNull] public static IControlFlowGraph NormalizeErrors([NotNull] this IControlFlowGraph cfg)
{
var todo = new HashSet<IBasicBlock>();

// Remove the error statements and save blocks to remove edges from
cfg = cfg.Modify((a, b) => {
if (!a.Statements.Any())
return;
var toCopy = a.Statements;
if ((a.Statements.Last() is ErrorStatement) && a.Outgoing.Count() == 1 && a.Outgoing.Single().Type == EdgeType.RuntimeError)
{
toCopy = a.Statements.Take(a.Statements.Count() - 1);
todo.Add(b);
}
foreach (var stmt in toCopy)
b.Add(stmt);
});

// Copy graph, replacing edges as necessary
return cfg.Modify((e, c) => {
c(e.Start, e.End, todo.Contains(e.Start) ? EdgeType.Continue : e.Type);
});
}
}
}
Loading

0 comments on commit b0efca6

Please sign in to comment.