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:
parent
b6038ccf0a
commit
347a5d0331
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user