1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-05 21:30:52 +00:00

Refactored call graph analysis in preparation for improved recursion detection.

This commit is contained in:
jespergravgaard 2019-02-03 08:32:38 +01:00
parent b6038ccf0a
commit 347a5d0331
8 changed files with 82 additions and 71 deletions

View File

@ -155,6 +155,10 @@ public class Compiler {
new Pass1FixLValuesLoHi(program).execute();
new Pass1AssertNoLValueIntermediate(program).execute();
new Pass1AddTypePromotions(program).execute();
new PassNStatementIndices(program).step();
new PassNCallGraphAnalysis(program).step();
new Pass1AssertNoRecursion(program).execute();
new Pass1AssertInterrupts(program).execute();
@ -347,7 +351,7 @@ public class Compiler {
new PassNStatementIndices(program).execute();
getLog().append("CALL GRAPH");
new Pass3CallGraphAnalysis(program).findCallGraph();
new PassNCallGraphAnalysis(program).step();
getLog().append(program.getCallGraph().toString());
//getLog().setVerboseLiveRanges(true);
@ -367,7 +371,7 @@ public class Compiler {
new PassNBlockSequencePlanner(program).step();
new Pass3AddNopBeforeCallOns(program).generate();
new PassNStatementIndices(program).execute();
new Pass3CallGraphAnalysis(program).findCallGraph();
new PassNCallGraphAnalysis(program).step();
new PassNStatementInfos(program).execute();
new PassNVariableReferenceInfos(program).execute();
new Pass3SymbolInfos(program).generateSymbolInfos();

View File

@ -1,7 +1,9 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.passes.PassNCallGraphAnalysis;
import java.util.ArrayList;
import java.util.Collection;
@ -10,7 +12,7 @@ import java.util.List;
/**
* The call graph for the entire control flow graph.
* Created by {@link dk.camelot64.kickc.passes.Pass3CallGraphAnalysis}
* Created by {@link PassNCallGraphAnalysis}
*/
public class CallGraph {
@ -26,7 +28,7 @@ public class CallGraph {
* @param scopeLabel The label for the scope.
* @return The call block for the scope
*/
public CallBlock getOrCreateCallBlock(LabelRef scopeLabel) {
public CallBlock getOrCreateCallBlock(ScopeRef scopeLabel) {
CallBlock callBlock = getCallBlock(scopeLabel);
if(callBlock != null) {
return callBlock;
@ -43,7 +45,7 @@ public class CallGraph {
* @param scopeLabel The label for the scope.
* @return The call block for the scope. Null if the call block does not exist (no calls are made from it).
*/
public CallBlock getCallBlock(LabelRef scopeLabel) {
public CallBlock getCallBlock(ScopeRef scopeLabel) {
for(CallBlock callBlock : callBlocks) {
if(callBlock.getScopeLabel().equals(scopeLabel)) {
return callBlock;
@ -52,8 +54,8 @@ public class CallGraph {
return null;
}
public LabelRef getFirstCallBlock() {
return new LabelRef("");
public ScopeRef getFirstCallBlock() {
return ScopeRef.ROOT;
}
/**
@ -63,9 +65,9 @@ public class CallGraph {
* @return The sub call blocks called from the passed block
*/
public Collection<CallBlock> getCalledBlocks(CallBlock block) {
Collection<LabelRef> calledLabels = block.getCalledBlocks();
Collection<ScopeRef> calledLabels = block.getCalledBlocks();
LinkedHashSet<CallBlock> called = new LinkedHashSet<>();
for(LabelRef calledLabel : calledLabels) {
for(ScopeRef calledLabel : calledLabels) {
called.add(getOrCreateCallBlock(calledLabel));
}
return called;
@ -77,8 +79,8 @@ public class CallGraph {
* @param scopeLabel The label of scope (the call block)
* @return The scope labels of call blocks that call the passed block
*/
public Collection<LabelRef> getCallingBlocks(LabelRef scopeLabel) {
ArrayList<LabelRef> callingBlocks = new ArrayList<>();
public Collection<ScopeRef> getCallingBlocks(ScopeRef scopeLabel) {
ArrayList<ScopeRef> callingBlocks = new ArrayList<>();
for(CallBlock callBlock : callBlocks) {
if(callBlock.getCalledBlocks().contains(scopeLabel)) {
callingBlocks.add(callBlock.getScopeLabel());
@ -102,7 +104,7 @@ public class CallGraph {
* @param label The label of the procedure
* @return All calls
*/
public Collection<CallBlock.Call> getCallers(LabelRef label) {
public Collection<CallBlock.Call> getCallers(ScopeRef label) {
Collection<CallBlock.Call> callers = new ArrayList<>();
for(CallBlock callBlock : callBlocks) {
for(CallBlock.Call call : callBlock.getCalls()) {
@ -122,20 +124,20 @@ public class CallGraph {
/**
* The label of the scope. (Program scope has label "" and procedure scopes have their respective labels).
*/
private LabelRef scopeLabel;
private ScopeRef scopeLabel;
private List<Call> calls;
public CallBlock(LabelRef scopeLabel) {
public CallBlock(ScopeRef scopeLabel) {
this.scopeLabel = scopeLabel;
this.calls = new ArrayList<>();
}
public LabelRef getScopeLabel() {
public ScopeRef getScopeLabel() {
return scopeLabel;
}
public void addCall(LabelRef procedureLabel, StatementCall statementCall) {
public void addCall(ProcedureRef procedureLabel, StatementCall statementCall) {
this.calls.add(new Call(procedureLabel, statementCall));
}
@ -144,8 +146,8 @@ public class CallGraph {
*
* @return The scope labels of all call blocks called from this one.
*/
public Collection<LabelRef> getCalledBlocks() {
LinkedHashSet<LabelRef> called = new LinkedHashSet<>();
public Collection<ScopeRef> getCalledBlocks() {
LinkedHashSet<ScopeRef> called = new LinkedHashSet<>();
for(Call call : calls) {
called.add(call.getProcedure());
}
@ -177,7 +179,7 @@ public class CallGraph {
* @param scope The scope label of the block
* @return All calls to the passed scope
*/
public Collection<Call> getCalls(LabelRef scope) {
public Collection<Call> getCalls(ScopeRef scope) {
ArrayList<Call> callsToScope = new ArrayList<>();
for(Call call : calls) {
if(call.getProcedure().equals(scope)) {
@ -200,14 +202,14 @@ public class CallGraph {
/**
* The label of the called procedure.
*/
private LabelRef procedure;
private ProcedureRef procedure;
public Call(LabelRef procedure, StatementCall statementCall) {
Call(ProcedureRef procedure, StatementCall statementCall) {
this.callStatementIdx = statementCall.getIndex();
this.procedure = procedure;
}
public LabelRef getProcedure() {
public ProcedureRef getProcedure() {
return procedure;
}

View File

@ -2,6 +2,8 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.passes.Pass3LiveRangesAnalysis;
import dk.camelot64.kickc.passes.PassNCallGraphAnalysis;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@ -9,7 +11,7 @@ import java.util.List;
/**
* Live ranges for all variables.
* Created by {@link dk.camelot64.kickc.passes.Pass3CallGraphAnalysis}
* Created by {@link Pass3LiveRangesAnalysis}
*/
public class LiveRangeVariables {

View File

@ -229,7 +229,7 @@ public class Pass3LiveRangesAnalysis extends Pass2Base {
ControlFlowBlock block = getProgram().getStatementInfos().getBlock(statement);
if(block.isProcedureEntry(getProgram())) {
// Current is first statement of a call - add the statement preceding the call.
Collection<CallGraph.CallBlock.Call> callers = getProgram().getCallGraph().getCallers(block.getLabel());
Collection<CallGraph.CallBlock.Call> callers = getProgram().getCallGraph().getCallers(block.getScope());
for(CallGraph.CallBlock.Call call : callers) {
Statement callStmt = getProgram().getStatementInfos().getStatement(call.getCallStatementIdx());
Collection<Statement> precedingCallStmt = getPrecedingStatement(callStmt);

View File

@ -72,7 +72,7 @@ public class Pass3LiveRangesEffectiveAnalysis extends Pass2Base {
}
Collection<CallGraph.CallBlock.Call> callers =
getProgram().getCallGraph().getCallers(procedure.getLabel().getRef());
getProgram().getCallGraph().getCallers(procedure.getRef());
for(CallGraph.CallBlock.Call caller : callers) {
// Each caller creates its own call-paths
StatementCall callStatement =

View File

@ -1,9 +1,8 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import java.util.*;
@ -27,25 +26,28 @@ public class Pass3LoopDepthAnalysis extends Pass2Base {
* Uses the call graph and natural loops of the control flow graph.
*/
public void findLoopDepths() {
Deque<LabelRef> todo = new ArrayDeque<>();
Set<LabelRef> done = new LinkedHashSet<>();
Deque<ScopeRef> todo = new ArrayDeque<>();
Set<ScopeRef> done = new LinkedHashSet<>();
List<ControlFlowBlock> entryPointBlocks = getGraph().getEntryPointBlocks(getProgram());
for(ControlFlowBlock entryPointBlock : entryPointBlocks) {
LabelRef label = entryPointBlock.getLabel();
ScopeRef scope;
if(label.getFullName().equals(LabelRef.BEGIN_BLOCK_NAME)) {
label = callGraph.getFirstCallBlock();
scope = callGraph.getFirstCallBlock();
} else {
scope = entryPointBlock.getScope();
}
todo.push(label);
todo.push(scope);
}
while(!todo.isEmpty()) {
LabelRef currentScope = todo.pop();
ScopeRef currentScope = todo.pop();
done.add(currentScope);
CallGraph.CallBlock currentCallBlock = callGraph.getOrCreateCallBlock(currentScope);
// Add called sub blocks for later processing
Collection<LabelRef> subBlocks = currentCallBlock.getCalledBlocks();
for(LabelRef subBlock : subBlocks) {
Collection<ScopeRef> subBlocks = currentCallBlock.getCalledBlocks();
for(ScopeRef subBlock : subBlocks) {
if(!done.contains(subBlock) && !todo.contains(subBlock)) {
todo.add(subBlock);
}
@ -56,10 +58,10 @@ public class Pass3LoopDepthAnalysis extends Pass2Base {
}
}
private int getCallingDepth(LabelRef currentScope) {
private int getCallingDepth(ScopeRef currentScope) {
int callingDepth = 1;
Collection<LabelRef> callingScopes = callGraph.getCallingBlocks(currentScope);
for(LabelRef callingScope : callingScopes) {
Collection<ScopeRef> callingScopes = callGraph.getCallingBlocks(currentScope);
for(ScopeRef callingScope : callingScopes) {
CallGraph.CallBlock callingBlock = callGraph.getCallBlock(callingScope);
Collection<CallGraph.CallBlock.Call> calls = callingBlock.getCalls(currentScope);
for(CallGraph.CallBlock.Call call : calls) {
@ -88,14 +90,14 @@ public class Pass3LoopDepthAnalysis extends Pass2Base {
return callingDepth;
}
private void findLoopDepth(LabelRef currentScope, int initialDepth) {
private void findLoopDepth(ScopeRef currentScope, int initialDepth) {
NaturalLoopSet loopSet = getProgram().getLoopSet();
// Find loops in the current scope block
List<NaturalLoop> currentScopeLoops = new ArrayList<>();
for(NaturalLoop loop : loopSet.getLoops()) {
LabelRef loopHead = loop.getHead();
ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHead);
LabelRef scopeRef = Pass3CallGraphAnalysis.getScopeRef(loopHeadBlock, getProgram());
ScopeRef scopeRef = PassNCallGraphAnalysis.getScopeRef(loopHeadBlock, getProgram());
if(scopeRef.equals(currentScope)) {
// Loop is inside current scope block!
currentScopeLoops.add(loop);
@ -110,9 +112,8 @@ public class Pass3LoopDepthAnalysis extends Pass2Base {
}
// Find loop nesting depths in current scope loops
Deque<NaturalLoop> todo = new ArrayDeque<>();
Set<NaturalLoop> done = new LinkedHashSet<>();
todo.addAll(currentScopeLoops);
Deque<NaturalLoop> todo = new ArrayDeque<>(currentScopeLoops);
while(!todo.isEmpty()) {
NaturalLoop loop = todo.getFirst();
todo.removeFirst();

View File

@ -7,8 +7,8 @@ import dk.camelot64.kickc.asm.AsmSegment;
import dk.camelot64.kickc.model.CallGraph;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import java.util.ArrayList;
import java.util.Collection;
@ -83,10 +83,10 @@ public class Pass4InterruptClobberFix extends Pass2Base {
}
CallGraph callGraph = getProgram().getCallGraph();
CallGraph.CallBlock callBlock = callGraph.getCallBlock(procedure.getLabel().getRef());
CallGraph.CallBlock callBlock = callGraph.getCallBlock(procedure.getRef());
List<CallGraph.CallBlock.Call> calls = callBlock.getCalls();
for(CallGraph.CallBlock.Call call : calls) {
LabelRef calledProcLabel = call.getProcedure();
ScopeRef calledProcLabel = call.getProcedure();
ProcedureRef calledProcRef = new ProcedureRef(calledProcLabel.getFullName());
Procedure calledProc = getProgram().getScope().getProcedure(calledProcRef);
AsmClobber calledClobber = getProcedureClobber(calledProc);

View File

@ -1,54 +1,56 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.CallGraph;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
/** Finds the call graph for the control flow graph - identifies all calls in all scopes and creates a graph from these. */
public class Pass3CallGraphAnalysis extends Pass2Base {
public class PassNCallGraphAnalysis extends Pass2SsaOptimization {
public Pass3CallGraphAnalysis(Program program) {
public PassNCallGraphAnalysis(Program program) {
super(program);
}
/**
* Gets a label reference representing the scope of a block
*
* @param block The block
* @return The label of the scope containing the block. The outermost scope has a label containing an empty string.
*/
public static LabelRef getScopeRef(ControlFlowBlock block, Program program) {
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());
}
return scopeRef;
}
public void findCallGraph() {
@Override
public boolean step() {
CallGraph callGraph = new CallGraph();
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
LabelRef scopeRef = getScopeRef(block, getProgram());
ScopeRef scopeRef = getScopeRef(block, getProgram());
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);
callBlock.addCall(procedure, (StatementCall) statement);
}
}
}
getProgram().setCallGraph(callGraph);
return false;
}
/**
* Gets a reference to the scope of a block
*
* @param block The block
* @return The scope containing the block. The outermost scope has a label containing an empty string.
*/
public static ScopeRef getScopeRef(ControlFlowBlock block, Program program) {
Symbol blockSymbol = program.getScope().getSymbol(block.getLabel());
if(blockSymbol instanceof Procedure) {
return ((Procedure) blockSymbol).getRef();
} else {
Scope blockScope = blockSymbol.getScope();
return blockScope.getRef();
}
}
}