1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Implemented call-path based effective alive analysis with call-path specific aliases.

This commit is contained in:
jespergravgaard 2017-11-07 09:32:16 +01:00
parent b20327590f
commit 0026676080
14 changed files with 2456 additions and 69 deletions

View File

@ -209,6 +209,8 @@ public class Compiler {
new Pass3StatementIndices(program).generateStatementIndices();
new Pass3CallGraphAnalysis(program).findCallGraph();
new Pass3LiveRangesAnalysis(program).findLiveRanges();
program.getLog().append("CONTROL FLOW GRAPH - BEFORE EFFECTIVE LIVE RANGES");
program.getLog().append(program.getGraph().toString(program));
new Pass3LiveRangesEffectiveAnalysis(program).findLiveRangesEffective();
program.getLog().append("CONTROL FLOW GRAPH - PHI MEM COALESCED");
program.getLog().append(program.getGraph().toString(program));
@ -262,9 +264,9 @@ public class Compiler {
//program.getLog().setVerboseUplift(true);
new Pass4RegisterUpliftCombinations(program).performUplift(10_000);
program.getLog().setVerboseUplift(true);
new Pass4RegisterUpliftStatic(program).performUplift();
program.getLog().setVerboseUplift(false);
//program.getLog().setVerboseUplift(true);
//new Pass4RegisterUpliftStatic(program).performUplift();
//program.getLog().setVerboseUplift(false);
// Attempt uplifting registers one at a time to catch remaining potential not realized by combination search
new Pass4RegisterUpliftRemains(program).performUplift(10_000);

View File

@ -0,0 +1 @@
jmp {la1}

View File

@ -1,31 +1,139 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.passes.Pass2AliasElimination;
import java.util.*;
/** Effective variable live ranges for all statements. (Including variables alive in calling methods)
* Created by {@link dk.camelot64.kickc.passes.Pass3CallGraphAnalysis}
/**
* Effective variable live ranges for all statements.
* (Including variables alive in calling methods).
* Created by {@link dk.camelot64.kickc.passes.Pass3LiveRangesEffectiveAnalysis}
*/
public class LiveRangeVariablesEffective {
/** Effectively alive variables by statement index. */
Map<Integer, AliveCombinations> effectiveLiveCombinations;
/**
* The program.
*/
private Program program;
public LiveRangeVariablesEffective(Map<Integer, AliveCombinations> effectiveLiveCombinations) {
this.effectiveLiveCombinations = effectiveLiveCombinations;
/**
* Call-paths for all procedures.
*/
private Map<ProcedureRef, CallPaths> procedureCallPaths;
/**
* Normal variable live ranges.
*/
private LiveRangeVariables liveRangeVariables;
/**
* Information about which procedures reference which variables.
*/
private VariableReferenceInfo referenceInfo;
public LiveRangeVariablesEffective(Program program, Map<ProcedureRef, CallPaths> procedureCallPaths, LiveRangeVariables liveRangeVariables, VariableReferenceInfo referenceInfo) {
this.program = program;
this.procedureCallPaths = procedureCallPaths;
this.liveRangeVariables = liveRangeVariables;
this.referenceInfo = referenceInfo;
}
/**
* All call-paths leading into a specific procedure.
*/
public static class CallPaths {
/**
* The procedure
*/
private ProcedureRef procedure;
/**
* All call-paths leading into the procedure from the main() procedure.
*/
private Collection<CallPath> callPaths;
public CallPaths(ProcedureRef procedure) {
this.procedure = procedure;
this.callPaths = new ArrayList<>();
}
public ProcedureRef getProcedure() {
return procedure;
}
public Collection<CallPath> getCallPaths() {
return callPaths;
}
public void add(CallPath callPath) {
this.callPaths.add(callPath);
}
}
/**
* All variables alive in a specific procedure at a specific call-path.
* The call-path is th path from the main()-procedure to the procedure in question.
*/
public static class CallPath {
/**
* The path from main() to the procedure. First element is the call to main(), last element is the call to the procedure.
*/
private List<CallGraph.CallBlock.Call> path;
/**
* Alive variables on the call-path. Based on alive vars at each call in the path.
*/
private Collection<VariableRef> alive;
/**
* Alias variables on the call-path. All global aliases plus any variables alias-assigned in a phi-block on the path.
*/
private Pass2AliasElimination.Aliases aliases;
public CallPath(List<CallGraph.CallBlock.Call> path, Collection<VariableRef> alive, Pass2AliasElimination.Aliases aliases) {
this.path = path;
this.alive = alive;
this.aliases = aliases;
}
/**
* The path from main() to the procedure. First element is the call to main(), last element is the call to the procedure.
* @return Tha call path
*/
public List<CallGraph.CallBlock.Call> getPath() {
return path;
}
/**
* Alive variables on the call-path. Based on alive vars at each call in the path.
* @return The alive variables
*/
public Collection<VariableRef> getAlive() {
return alive;
}
/**
* Alias variables on the call-path. All global aliases plus any variables alias-assigned in a phi-block on the path.
* @return The aliases
*/
public Pass2AliasElimination.Aliases getAliases() {
return aliases;
}
}
/**
* Get all variables potentially alive at a statement.
* If the statement is inside a method this also includes all variables alive at the exit of any call.
* </p>
*
* @param statement The statement to examine
* @return All variables potentially alive at the statement
*/
public Collection<VariableRef> getAliveEffective(Statement statement) {
Set<VariableRef> effectiveAliveTotal = new LinkedHashSet<>();
AliveCombinations aliveCombinations = effectiveLiveCombinations.get(statement.getIndex());
for (AliveCombination aliveCombination : aliveCombinations.getCombinations()) {
effectiveAliveTotal.addAll(aliveCombination.getAlive());
AliveCombinations aliveCombinations = getAliveCombinations(statement);
for (CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) {
Collection<VariableRef> alive = aliveCombinations.getEffectiveAliveAtStmt(callPath);
effectiveAliveTotal.addAll(alive);
}
return effectiveAliveTotal;
}
@ -36,46 +144,80 @@ public class LiveRangeVariablesEffective {
* (recursively up til the main()-method.
* Each combination includes all variables alive at the exit of any surrounding call.
* </p>
*
* @param statement The statement to examine
* @return All combinations of variables alive at the statement
*/
public AliveCombinations getAliveCombinations(Statement statement) {
return effectiveLiveCombinations.get(statement.getIndex());
List<VariableRef> aliveAtStmt = liveRangeVariables.getAlive(statement);
CallPaths callPaths;
Collection<VariableRef> referencedInProcedure;
ControlFlowBlock block = program.getGraph().getBlockFromStatementIdx(statement.getIndex());
ScopeRef scopeRef = block.getScope();
Scope scope = program.getScope().getScope(scopeRef);
if (scope instanceof Procedure) {
Procedure procedure = (Procedure) scope;
callPaths = procedureCallPaths.get(procedure.getRef());
referencedInProcedure = referenceInfo.getReferenced(procedure.getRef().getLabelRef());
} else {
callPaths = new CallPaths(Procedure.ROOT);
referencedInProcedure = new ArrayList<>();
}
return new AliveCombinations(callPaths, referencedInProcedure, aliveAtStmt);
}
/** Combinations of variables effectively alive at a specific statement.
/**
* Combinations of variables effectively alive at a specific statement.
* If the statement is inside a method the combinations are the live variables inside the method combined with each calling statements alive vars.
* As each caller might also be inside a methos there may be a large amount of combinations.
*/
public static class AliveCombinations {
private Collection<AliveCombination> combinations;
/**
* All call-paths to the procedure containing the statement.
*/
private CallPaths callPaths;
/**
* All variables referenced in the procedure containing the statement.
*/
private Collection<VariableRef> referencedInProcedure;
/**
* Variables alive at the statement inside the procedure.
*/
private Collection<VariableRef> aliveAtStmt;
public AliveCombinations(Collection<Collection<VariableRef>> aliveCombinations) {
ArrayList<AliveCombination> combinations = new ArrayList<>();
for (Collection<VariableRef> aliveCombination : aliveCombinations) {
combinations.add(new AliveCombination(aliveCombination));
}
this.combinations = combinations;
public AliveCombinations(CallPaths callPaths, Collection<VariableRef> referencedInProcedure, Collection<VariableRef> aliveAtStmt) {
this.callPaths = callPaths;
this.referencedInProcedure = referencedInProcedure;
this.aliveAtStmt = aliveAtStmt;
}
public Collection<AliveCombination> getCombinations() {
return combinations;
public CallPaths getCallPaths() {
return callPaths;
}
}
/** One single combinations of variables effectively alive at a specific statement. */
public static class AliveCombination {
private Collection<VariableRef> alive;
public AliveCombination(Collection<VariableRef> alive) {
this.alive = alive;
public Collection<VariableRef> getReferencedInProcedure() {
return referencedInProcedure;
}
public Collection<VariableRef> getAlive() {
return alive;
public Collection<VariableRef> getAliveAtStmt() {
return aliveAtStmt;
}
/**
* Get all variables effective alive at the statement for a specific call path.
* @param callPath The call path (returned from getCallPaths)
* @return All variables effectively alive at the statement on the call-path
*/
public Collection<VariableRef> getEffectiveAliveAtStmt(CallPath callPath) {
LinkedHashSet<VariableRef> effectiveAlive = new LinkedHashSet<>();
// Add alive at call
effectiveAlive.addAll(callPath.getAlive());
// Clear out any variables referenced in the method
effectiveAlive.removeAll(referencedInProcedure);
// Add alive at statement
effectiveAlive.addAll(aliveAtStmt);
return effectiveAlive;
}
}

View File

@ -14,6 +14,8 @@ public class Procedure extends Scope {
private final SymbolType returnType;
private List<String> parameterNames;
public static final ProcedureRef ROOT = new ProcedureRef("");
public Procedure(String name, SymbolType returnType, Scope parentScope) {
super(name, parentScope);
this.returnType = returnType;

View File

@ -55,8 +55,8 @@ public abstract class StatementBase implements Statement {
if(liveRangeVariablesEffective!=null) {
LiveRangeVariablesEffective.AliveCombinations aliveCombinations = liveRangeVariablesEffective.getAliveCombinations(this);
alive.append(" ( ");
for (LiveRangeVariablesEffective.AliveCombination aliveCombination : aliveCombinations.getCombinations()) {
alive.append(getAliveString(aliveCombination.getAlive()));
for (LiveRangeVariablesEffective.CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) {
alive.append(getAliveString(aliveCombinations.getEffectiveAliveAtStmt(callPath)));
alive.append(" ");
}
alive.append(")");

View File

@ -19,7 +19,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
*/
@Override
public boolean optimize() {
final Aliases aliases = findAliases();
final Aliases aliases = findAliases(getProgram(), false);
removeAliasAssignments(aliases);
replaceVariables(aliases.getReplacements());
for (AliasSet aliasSet : aliases.getAliasSets()) {
@ -88,6 +88,14 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
this.aliases = new ArrayList<>();
}
public Aliases(Aliases aliases) {
this.aliases = new ArrayList<>();
for (AliasSet aliasSet : aliases.getAliasSets()) {
AliasSet copySet = new AliasSet(aliasSet);
this.aliases.add(copySet);
}
}
public List<VariableRef> getSymbolsToRemove() {
ArrayList<VariableRef> eliminates = new ArrayList<>();
for (AliasSet alias : aliases) {
@ -159,6 +167,10 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
this.vars = new ArrayList<>();
}
public AliasSet(AliasSet aliasSet) {
this.vars = new ArrayList<>(aliasSet.getVars());
}
public void add(VariableRef variable) {
vars.add(variable);
}
@ -226,16 +238,16 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
}
private Aliases findAliases() {
Aliases candidates = findAliasesCandidates(false, getProgram());
cleanupCandidates(candidates);
public static Aliases findAliases(Program program, boolean allowCrossScope) {
Aliases candidates = findAliasesCandidates(allowCrossScope, program);
cleanupCandidates(candidates, program);
return candidates;
}
// Remove all candidates that are used after assignment in phi blocks
private void cleanupCandidates(Aliases candidates) {
private static void cleanupCandidates(Aliases candidates, Program program) {
for (final AliasSet aliasSet : candidates.aliases) {
for (ControlFlowBlock block : getGraph().getAllBlocks()) {
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
if(block.hasPhiBlock()) {
StatementPhiBlock phi = block.getPhiBlock();
boolean lMatch = false;
@ -244,7 +256,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
RValue rValue = phiRValue.getrValue();
if (aliasSet.contains(rValue)) {
getLog().append("Alias candidate removed " + rValue.toString(getProgram()));
program.getLog().append("Alias candidate removed " + rValue.toString(program));
aliasSet.remove(rValue);
break;
}
@ -265,7 +277,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
*
* @return Map from Variable to the Constant value
*/
public static Aliases findAliasesCandidates(final boolean allowCrossScope, final Program program) {
private static Aliases findAliasesCandidates(final boolean allowCrossScope, final Program program) {
final Aliases aliases = new Aliases();
final ControlFlowGraphBaseVisitor<Void> visitor = new ControlFlowGraphBaseVisitor<Void>() {
@Override

View File

@ -9,29 +9,36 @@ import java.util.*;
*/
public class Pass3LiveRangesEffectiveAnalysis extends Pass2Base {
/**
* Call-paths for all procedures.
*/
private Map<ProcedureRef, LiveRangeVariablesEffective.CallPaths> procedureCallPaths;
/**
* Normal variable live ranges.
*/
private LiveRangeVariables liveRangeVariables;
/**
* Information about which procedures reference which variables.
*/
private VariableReferenceInfo referenceInfo;
public Pass3LiveRangesEffectiveAnalysis(Program program) {
super(program);
}
public void findLiveRangesEffective() {
LiveRangeVariablesEffective aliveEffective = findAliveEffective(getProgram());
this.liveRangeVariables = getProgram().getLiveRangeVariables();
this.referenceInfo = new VariableReferenceInfo(getProgram());
this.procedureCallPaths = new LinkedHashMap<>();
populateProcedureCallPaths();
LiveRangeVariablesEffective aliveEffective = new LiveRangeVariablesEffective(getProgram(), procedureCallPaths, liveRangeVariables, referenceInfo);
getProgram().setLiveRangeVariablesEffective(aliveEffective);
//getLog().append("Calculated effective variable live ranges");
}
private LiveRangeVariablesEffective findAliveEffective(Program program) {
LiveRangeVariables liveRangeVariables = program.getLiveRangeVariables();
Map<Integer, LiveRangeVariablesEffective.AliveCombinations> effectiveLiveVariablesCombinations = new HashMap<>();
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
for (Statement statement : block.getStatements()) {
Collection<Collection<VariableRef>> statementAliveCombinations = findAliveEffective(liveRangeVariables, statement);
LiveRangeVariablesEffective.AliveCombinations aliveCombinations = new LiveRangeVariablesEffective.AliveCombinations(statementAliveCombinations);
effectiveLiveVariablesCombinations.put(statement.getIndex(), aliveCombinations);
}
}
return new LiveRangeVariablesEffective(effectiveLiveVariablesCombinations);
}
/**
* Get all variables alive at a statement.
* If the statement is inside a method this also includes all variables alive at the exit of the calls.
@ -54,7 +61,6 @@ public class Pass3LiveRangesEffectiveAnalysis extends Pass2Base {
Procedure procedure = (Procedure) scope;
Collection<CallGraph.CallBlock.Call> callers =
getProgram().getCallGraph().getCallers(procedure.getLabel().getRef());
VariableReferenceInfo referenceInfo = new VariableReferenceInfo(getProgram());
Collection<VariableRef> referencedInProcedure = referenceInfo.getReferenced(procedure.getRef().getLabelRef());
for (CallGraph.CallBlock.Call caller : callers) {
// Each caller creates its own combinations
@ -81,4 +87,76 @@ public class Pass3LiveRangesEffectiveAnalysis extends Pass2Base {
return combinations;
}
private void populateProcedureCallPaths() {
this.procedureCallPaths = new LinkedHashMap<>();
Collection<Procedure> procedures = getProgram().getScope().getAllProcedures(true);
for (Procedure procedure : procedures) {
populateProcedureCallPaths(procedure);
}
}
private void populateProcedureCallPaths(Procedure procedure) {
ProcedureRef procedureRef = procedure.getRef();
LiveRangeVariablesEffective.CallPaths callPaths = procedureCallPaths.get(procedureRef);
if (callPaths == null) {
callPaths = new LiveRangeVariablesEffective.CallPaths(procedureRef);
Collection<CallGraph.CallBlock.Call> callers =
getProgram().getCallGraph().getCallers(procedure.getLabel().getRef());
ControlFlowBlock procedureBlock = getProgram().getGraph().getBlock(procedure.getLabel().getRef());
StatementPhiBlock procedurePhiBlock = null;
if(procedureBlock.hasPhiBlock()) {
procedurePhiBlock = procedureBlock.getPhiBlock();
}
for (CallGraph.CallBlock.Call caller : callers) {
// Each caller creates its own call-paths
StatementCall callStatement =
(StatementCall) getProgram().getGraph().getStatementByIndex(caller.getCallStatementIdx());
ControlFlowBlock callBlock = getProgram().getGraph().getBlockFromStatementIdx(callStatement.getIndex());
ScopeRef callScopeRef = callBlock.getScope();
Scope callScope = getProgram().getScope().getScope(callScopeRef);
if (callScope instanceof Procedure) {
// Found calling procedure!
Procedure callerProcedure = (Procedure) callScope;
// Make sure we have populated the call-paths of the calling procedure
populateProcedureCallPaths(callerProcedure);
// Find variables referenced in caller procedure
Collection<VariableRef> referencedInCaller = referenceInfo.getReferenced(callerProcedure.getRef().getLabelRef());
// For each caller path - create a new call-path
LiveRangeVariablesEffective.CallPaths callerPaths = procedureCallPaths.get(callerProcedure.getRef());
for (LiveRangeVariablesEffective.CallPath callerPath : callerPaths.getCallPaths()) {
ArrayList<CallGraph.CallBlock.Call> path = new ArrayList<>(callerPath.getPath());
path.add(caller);
Collection<VariableRef> alive = new LinkedHashSet<>();
alive.addAll(callerPath.getAlive());
alive.removeAll(referencedInCaller);
alive.addAll(liveRangeVariables.getAlive(callStatement));
Pass2AliasElimination.Aliases aliases = new Pass2AliasElimination.Aliases(callerPath.getAliases());
if(procedurePhiBlock!=null) {
for (StatementPhiBlock.PhiVariable phiVariable : procedurePhiBlock.getPhiVariables()) {
RValue phiRvalue = phiVariable.getrValue(callBlock.getLabel());
if (phiRvalue instanceof VariableRef) {
aliases.add(phiVariable.getVariable(), (VariableRef) phiRvalue);
}
}
}
LiveRangeVariablesEffective.CallPath callPath = new LiveRangeVariablesEffective.CallPath(path, alive, aliases);
callPaths.add(callPath);
}
} else {
// main() call outside procedure scope - create initial call-path.
ArrayList<CallGraph.CallBlock.Call> rootPath = new ArrayList<>();
rootPath.add(caller);
ArrayList<VariableRef> rootAlive = new ArrayList<>();
// Initialize with global cross-scope aliases
Pass2AliasElimination.Aliases rootAliases = Pass2AliasElimination.findAliases(getProgram(), true);
LiveRangeVariablesEffective.CallPath rootCallPath = new LiveRangeVariablesEffective.CallPath(rootPath, rootAlive, rootAliases);
callPaths.add(rootCallPath);
}
}
procedureCallPaths.put(procedureRef, callPaths);
}
}
}

View File

@ -198,10 +198,9 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base {
* @return true if the register allocation contains an overlapping allocation. false otherwise.
*/
public static boolean isAllocationOverlapping(Program program) {
Pass2AliasElimination.Aliases aliases = Pass2AliasElimination.findAliasesCandidates(true, program);
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
for (Statement statement : block.getStatements()) {
if (isStatementAllocationOverlapping(program, statement, aliases)) {
if (isStatementAllocationOverlapping(program, statement)) {
return true;
}
}
@ -214,15 +213,15 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base {
*
* @param program The program
* @param statement The statement to check
* @param usedRegisters The used registers. Will be extended with all registers used in the statement.
* @return true if there is an overlapping register allocation
*/
private static boolean isStatementAllocationOverlapping(Program program, Statement statement, Pass2AliasElimination.Aliases aliases) {
private static boolean isStatementAllocationOverlapping(Program program, Statement statement) {
ProgramScope programScope = program.getScope();
LiveRangeVariablesEffective.AliveCombinations aliveCombinations = program.getLiveRangeVariablesEffective().getAliveCombinations(statement);
for (LiveRangeVariablesEffective.AliveCombination aliveCombination : aliveCombinations.getCombinations()) {
for (LiveRangeVariablesEffective.CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) {
LinkedHashMap<Registers.Register, LiveRangeEquivalenceClass> usedRegisters = new LinkedHashMap<>();
Collection<VariableRef> alive = aliveCombination.getAlive();
Collection<VariableRef> alive = aliveCombinations.getEffectiveAliveAtStmt(callPath);
Pass2AliasElimination.Aliases callPathAliases = callPath.getAliases();
for (VariableRef varRef : alive) {
Variable var = programScope.getVariable(varRef);
Registers.Register allocation = var.getAllocation();
@ -230,7 +229,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base {
if (allocationClass != null && !allocationClass.contains(varRef)) {
// Examine if the var is an alias of a var in the allocation class
boolean overlap = true;
Pass2AliasElimination.AliasSet aliasSet = aliases.findAliasSet(varRef);
Pass2AliasElimination.AliasSet aliasSet = callPathAliases.findAliasSet(varRef);
if(aliasSet!=null) {
for (VariableRef aliasVar : aliasSet.getVars()) {
if(allocationClass.contains(aliasVar)) {

View File

@ -28,9 +28,6 @@ public class TestErrors extends TestCase {
compileAndCompare("inline-asm-param");
}
public void testOverlapAllocation() throws IOException, URISyntaxException {
compileAndCompare("overlap-allocation");
}
public void testIncD020() throws IOException, URISyntaxException {
compileAndCompare("incd020");

View File

@ -24,6 +24,10 @@ public class TestPrograms extends TestCase {
helper = new ReferenceHelper("dk/camelot64/kickc/test/ref/");
}
public void testOverlapAllocation() throws IOException, URISyntaxException {
compileAndCompare("overlap-allocation");
}
public void testBitmapBresenham() throws IOException, URISyntaxException {
compileAndCompare("bitmap-bresenham");
}

View File

@ -0,0 +1,31 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.const SCREEN = $400
jsr main
main: {
ldx #0
b1:
jsr plot
inx
cpx #$b
bne b1
ldx #0
b2:
jsr plot
inx
cpx #$b
bne b2
ldx #0
b3:
jsr plot
inx
cpx #$b
bne b3
rts
}
plot: {
lda #'*'
sta SCREEN,x
rts
}

View File

@ -0,0 +1,46 @@
@begin: scope:[] from
to:@2
@2: scope:[] from @begin
[0] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @2
main: scope:[main] from @2
[1] phi() [ ] ( [ ] )
to:main::@1
main::@1: scope:[main] from main main::@7
[2] (byte) main::i#2 ← phi( main/(byte) 0 main::@7/(byte) main::i#1 ) [ main::i#2 ] ( [ main::i#2 ] )
[3] (byte) plot::x#0 ← (byte) main::i#2 [ main::i#2 plot::x#0 ] ( [ main::i#2 plot::x#0 ] )
[4] call plot param-assignment [ main::i#2 ] ( [ main::i#2 ] )
to:main::@7
main::@7: scope:[main] from main::@1
[5] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( [ main::i#1 ] )
[6] if((byte) main::i#1!=(byte) 11) goto main::@1 [ main::i#1 ] ( [ main::i#1 ] )
to:main::@2
main::@2: scope:[main] from main::@7 main::@8
[7] (byte) main::j#2 ← phi( main::@7/(byte) 0 main::@8/(byte) main::j#1 ) [ main::j#2 ] ( [ main::j#2 ] )
[8] (byte) plot::x#1 ← (byte) main::j#2 [ main::j#2 plot::x#1 ] ( [ main::j#2 plot::x#1 ] )
[9] call plot param-assignment [ main::j#2 ] ( [ main::j#2 ] )
to:main::@8
main::@8: scope:[main] from main::@2
[10] (byte) main::j#1 ← ++ (byte) main::j#2 [ main::j#1 ] ( [ main::j#1 ] )
[11] if((byte) main::j#1!=(byte) 11) goto main::@2 [ main::j#1 ] ( [ main::j#1 ] )
to:main::@3
main::@3: scope:[main] from main::@8 main::@9
[12] (byte) main::k#2 ← phi( main::@8/(byte) 0 main::@9/(byte) main::k#1 ) [ main::k#2 ] ( [ main::k#2 ] )
[13] (byte) plot::x#2 ← (byte) main::k#2 [ main::k#2 plot::x#2 ] ( [ main::k#2 plot::x#2 ] )
[14] call plot param-assignment [ main::k#2 ] ( [ main::k#2 ] )
to:main::@9
main::@9: scope:[main] from main::@3
[15] (byte) main::k#1 ← ++ (byte) main::k#2 [ main::k#1 ] ( [ main::k#1 ] )
[16] if((byte) main::k#1!=(byte) 11) goto main::@3 [ main::k#1 ] ( [ main::k#1 ] )
to:main::@return
main::@return: scope:[main] from main::@9
[17] return [ ] ( [ ] )
to:@return
plot: scope:[plot] from main::@1 main::@2 main::@3
[18] (byte) plot::x#3 ← phi( main::@1/(byte) plot::x#0 main::@2/(byte) plot::x#1 main::@3/(byte) plot::x#2 ) [ plot::x#3 ] ( [ main::i#2 plot::x#3 ] [ main::j#2 plot::x#3 ] [ main::k#2 plot::x#3 ] )
[19] *((const byte*) SCREEN#0 + (byte) plot::x#3) ← (byte) '*' [ ] ( [ main::i#2 ] [ main::j#2 ] [ main::k#2 ] )
to:plot::@return
plot::@return: scope:[plot] from plot
[20] return [ ] ( [ main::i#2 ] [ main::j#2 ] [ main::k#2 ] )
to:@return

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
(label) @2
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (word) 1024
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@7
(label) main::@8
(label) main::@9
(label) main::@return
(byte) main::i
(byte) main::i#1 reg byte x 16.5
(byte) main::i#2 reg byte x 11.0
(byte) main::j
(byte) main::j#1 reg byte x 16.5
(byte) main::j#2 reg byte x 11.0
(byte) main::k
(byte) main::k#1 reg byte x 16.5
(byte) main::k#2 reg byte x 11.0
(void()) plot((byte) plot::x)
(label) plot::@return
(byte) plot::x
(byte) plot::x#0 reg byte x 22.0
(byte) plot::x#1 reg byte x 22.0
(byte) plot::x#2 reg byte x 22.0
(byte) plot::x#3 reg byte x 35.0
reg byte x [ main::i#2 main::i#1 ]
reg byte x [ main::j#2 main::j#1 ]
reg byte x [ main::k#2 main::k#1 ]
reg byte x [ plot::x#3 plot::x#0 plot::x#1 plot::x#2 ]