From d501534561ec09089f5877414740fa2521cf59ba Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 28 Jul 2017 00:38:17 +0200 Subject: [PATCH] Implemented call graph analysis. --- src/dk/camelot64/kickc/Compiler.java | 5 + src/dk/camelot64/kickc/icl/CallGraph.java | 96 +++++++++++++++++++ .../camelot64/kickc/icl/ControlFlowGraph.java | 9 ++ .../kickc/passes/Pass3CallGraphAnalysis.java | 49 ++++++++++ src/dk/camelot64/kickc/test/ref/bresenham.log | 2 + .../camelot64/kickc/test/ref/flipper-rex2.log | 4 + src/dk/camelot64/kickc/test/ref/loopmin.log | 2 + src/dk/camelot64/kickc/test/ref/loopsplit.log | 2 + src/dk/camelot64/kickc/test/ref/minus.log | 2 + src/dk/camelot64/kickc/test/ref/summin.log | 3 + 10 files changed, 174 insertions(+) create mode 100644 src/dk/camelot64/kickc/icl/CallGraph.java create mode 100644 src/dk/camelot64/kickc/passes/Pass3CallGraphAnalysis.java diff --git a/src/dk/camelot64/kickc/Compiler.java b/src/dk/camelot64/kickc/Compiler.java index a56b1bc81..a47d63d27 100644 --- a/src/dk/camelot64/kickc/Compiler.java +++ b/src/dk/camelot64/kickc/Compiler.java @@ -126,6 +126,11 @@ public class Compiler { log.append(program.getGraph().toString(program.getScope())); pass2AssertSSA(program, log); + Pass3CallGraphAnalysis pass3CallGraphAnalysis = new Pass3CallGraphAnalysis(program, log); + pass3CallGraphAnalysis.findCallGraph(); + log.append("CALL GRAPH"); + log.append(program.getGraph().getCallGraph().toString()); + Pass3DominatorsAnalysis pass3DominatorsAnalysis = new Pass3DominatorsAnalysis(program, log); pass3DominatorsAnalysis.findDominators(); log.append("DOMINATORS"); diff --git a/src/dk/camelot64/kickc/icl/CallGraph.java b/src/dk/camelot64/kickc/icl/CallGraph.java new file mode 100644 index 000000000..b5b3cbad8 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/CallGraph.java @@ -0,0 +1,96 @@ +package dk.camelot64.kickc.icl; + +import java.util.ArrayList; +import java.util.List; + +/** The call graph for the entire control flow graph, */ +public class CallGraph { + + private List callBlocks; + + + public CallGraph() { + this.callBlocks = new ArrayList<>(); + } + + /** + * Get the call block for a specific scope. Create it if it does not already exist. + * @param scopeLabel The label for the scope. + * @return The call block for the scope + */ + public CallBlock getOrCreateCallBlock(LabelRef scopeLabel) { + for (CallBlock callBlock : callBlocks) { + if(callBlock.getScopeLabel().equals(scopeLabel)) { + return callBlock; + } + } + // Not found - create it + CallBlock newCallBlock = new CallBlock(scopeLabel); + callBlocks.add(newCallBlock); + return newCallBlock; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + for (CallBlock callBlock : callBlocks) { + out.append(callBlock.toString()).append("\n"); + } + return out.toString(); + } + + /** A block in the call graph, matching a scope in the program. */ + public static class CallBlock { + + /** The label of the scope. (Program scope has label "" and procedure scopes have their respective labels). */ + private LabelRef scopeLabel; + + private List calls; + + public CallBlock(LabelRef scopeLabel) { + this.scopeLabel = scopeLabel; + this.calls = new ArrayList<>(); + } + + public LabelRef getScopeLabel() { + return scopeLabel; + } + + public void addCall(LabelRef procedureLabel, StatementCall statementCall) { + this.calls.add(new Call(procedureLabel, statementCall)); + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("Calls in [").append(scopeLabel.toString()).append("] to "); + for (Call call : calls) { + out.append(call.toString()).append(" "); + } + return out.toString(); + } + + /** A single call in the call block. */ + public static class Call { + + /** Statement index of the call statement, */ + private Integer callStatementIdx; + + /** The label of the called procedure. */ + private LabelRef procedure; + + public Call(LabelRef procedure, StatementCall statementCall) { + this.callStatementIdx = statementCall.getIndex(); + this.procedure = procedure; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append(callStatementIdx).append(":").append(procedure); + return out.toString(); + } + } + + } +} diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java index f3f659d25..d5f6169a8 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java @@ -15,6 +15,7 @@ public class ControlFlowGraph { private List sequence; private DominatorsGraph dominators; private NaturalLoopSet loopSet; + private CallGraph callGraph; public ControlFlowGraph(Map blocks, LabelRef firstBlockRef) { this.blocks = blocks; @@ -193,4 +194,12 @@ public class ControlFlowGraph { return loopSet; } + public CallGraph getCallGraph() { + return callGraph; + } + + public void setCallGraph(CallGraph callGraph) { + this.callGraph = callGraph; + } + } diff --git a/src/dk/camelot64/kickc/passes/Pass3CallGraphAnalysis.java b/src/dk/camelot64/kickc/passes/Pass3CallGraphAnalysis.java new file mode 100644 index 000000000..7df53d823 --- /dev/null +++ b/src/dk/camelot64/kickc/passes/Pass3CallGraphAnalysis.java @@ -0,0 +1,49 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.CompileLog; +import dk.camelot64.kickc.icl.*; + +/** Finds the call graph for the control flow graph - identifies all calls in all scopes and creates a graph from these. */ +public class Pass3CallGraphAnalysis { + + private Program program; + private CompileLog log; + + public Pass3CallGraphAnalysis(Program program, CompileLog log) { + this.program = program; + this.log = log; + } + + public Program getProgram() { + return program; + } + + public CompileLog getLog() { + return log; + } + + public void findCallGraph() { + CallGraph callGraph = new CallGraph(); + + for (ControlFlowBlock block : program.getGraph().getAllBlocks()) { + Symbol blockSymbol = program.getScope().getSymbol(block.getLabel()); + LabelRef scopeRef; + if(blockSymbol instanceof Procedure) { + scopeRef = ((Procedure) blockSymbol).getRef().getLabelRef(); + } else { + Scope blockScope = blockSymbol.getScope(); + scopeRef = new LabelRef(blockScope.getFullName()); + } + for (Statement statement : block.getStatements()) { + if(statement instanceof StatementCall) { + ProcedureRef procedure = ((StatementCall) statement).getProcedure(); + LabelRef procedureLabel = procedure.getLabelRef(); + CallGraph.CallBlock callBlock = callGraph.getOrCreateCallBlock(scopeRef); + callBlock.addCall(procedureLabel, (StatementCall) statement); + } + } + } + program.getGraph().setCallGraph(callGraph); + } + +} diff --git a/src/dk/camelot64/kickc/test/ref/bresenham.log b/src/dk/camelot64/kickc/test/ref/bresenham.log index 7c2db8aff..a177a49a0 100644 --- a/src/dk/camelot64/kickc/test/ref/bresenham.log +++ b/src/dk/camelot64/kickc/test/ref/bresenham.log @@ -985,6 +985,8 @@ CONTROL FLOW GRAPH - PHI MEM COALESCED [10] (byte) e#2 ← (byte) e#1 - (byte) 39 [ x#1 cursor#2 e#2 y#1 ] to:@3 +CALL GRAPH + DOMINATORS @BEGIN dominated by @BEGIN @1 dominated by @1 @BEGIN diff --git a/src/dk/camelot64/kickc/test/ref/flipper-rex2.log b/src/dk/camelot64/kickc/test/ref/flipper-rex2.log index 164f9eb81..8d299c8b9 100644 --- a/src/dk/camelot64/kickc/test/ref/flipper-rex2.log +++ b/src/dk/camelot64/kickc/test/ref/flipper-rex2.log @@ -3733,6 +3733,10 @@ prepare::@return: from prepare::@1 [45] return [ ] to:@RETURN +CALL GRAPH +Calls in [] to 0:main +Calls in [main] to 1:prepare 9:flip 10:plot + DOMINATORS @BEGIN dominated by @BEGIN @END dominated by @BEGIN @END diff --git a/src/dk/camelot64/kickc/test/ref/loopmin.log b/src/dk/camelot64/kickc/test/ref/loopmin.log index 11057691d..4695dbf99 100644 --- a/src/dk/camelot64/kickc/test/ref/loopmin.log +++ b/src/dk/camelot64/kickc/test/ref/loopmin.log @@ -341,6 +341,8 @@ CONTROL FLOW GRAPH - PHI MEM COALESCED [5] (byte) s#1 ← (byte) s#2 + (byte) i#2 [ i#2 s#1 ] to:@3 +CALL GRAPH + DOMINATORS @BEGIN dominated by @BEGIN @1 dominated by @1 @BEGIN diff --git a/src/dk/camelot64/kickc/test/ref/loopsplit.log b/src/dk/camelot64/kickc/test/ref/loopsplit.log index 94e0fc122..261947725 100644 --- a/src/dk/camelot64/kickc/test/ref/loopsplit.log +++ b/src/dk/camelot64/kickc/test/ref/loopsplit.log @@ -361,6 +361,8 @@ CONTROL FLOW GRAPH - PHI MEM COALESCED [5] (byte) s#1 ← ++ (byte) s#3 [ i#1 s#1 ] to:@1 +CALL GRAPH + DOMINATORS @BEGIN dominated by @BEGIN @1 dominated by @1 @BEGIN diff --git a/src/dk/camelot64/kickc/test/ref/minus.log b/src/dk/camelot64/kickc/test/ref/minus.log index 322fdb4c0..afdc4e1aa 100644 --- a/src/dk/camelot64/kickc/test/ref/minus.log +++ b/src/dk/camelot64/kickc/test/ref/minus.log @@ -263,6 +263,8 @@ CONTROL FLOW GRAPH - PHI MEM COALESCED to:@END @END: from @1 +CALL GRAPH + DOMINATORS @BEGIN dominated by @BEGIN @1 dominated by @1 @BEGIN diff --git a/src/dk/camelot64/kickc/test/ref/summin.log b/src/dk/camelot64/kickc/test/ref/summin.log index e7cec26ab..20b54caa0 100644 --- a/src/dk/camelot64/kickc/test/ref/summin.log +++ b/src/dk/camelot64/kickc/test/ref/summin.log @@ -298,6 +298,9 @@ sum::@return: from sum [4] return (byte) s1#0 [ ] to:@RETURN +CALL GRAPH +Calls in [] to 0:sum 1:sum + DOMINATORS @BEGIN dominated by @BEGIN @2 dominated by @BEGIN @2