From 99088aef58baed2b86f82c7a26ba92a88dc8cfdc Mon Sep 17 00:00:00 2001 From: Jesper Gravgaard Date: Mon, 4 Sep 2017 22:15:18 +0200 Subject: [PATCH] Improved uplift test and zp coalesce test to include live ranges from calling statements in overlap testing. The new live range system is still far from perfect. Next step is getEffectiveAlive(stmt) returning alive vars + alive vars from all calling contexts. --- .../java/dk/camelot64/kickc/Compiler.java | 8 +- src/main/java/dk/camelot64/kickc/TODO.txt | 13 +++ .../kickc/asm/AsmFragmentManager.java | 22 ++++-- .../asm/fragment/aby=coby1_plus_zpby1.asm | 3 + .../dk/camelot64/kickc/passes/Pass2Base.java | 1 - .../Pass4RegisterUpliftCombinations.java | 79 +++++++++++++------ .../kickc/passes/Pass4ZeroPageCoalesce.java | 12 ++- 7 files changed, 105 insertions(+), 33 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_plus_zpby1.asm diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 2468e84d2..f1a927baf 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -135,8 +135,8 @@ public class Compiler { public void pass2OptimizeSSA(Program program) { List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(program)); - //optimizations.add(new Pass2ConstantPropagation(program)); - //optimizations.add(new Pass2ConstantAdditionElimination(program)); + optimizations.add(new Pass2ConstantPropagation(program)); + optimizations.add(new Pass2ConstantAdditionElimination(program)); optimizations.add(new Pass2UnaryNotSimplification(program)); optimizations.add(new Pass2AliasElimination(program)); optimizations.add(new Pass2RedundantPhiElimination(program)); @@ -177,7 +177,7 @@ public class Compiler { program.getLog().append("CALL GRAPH"); program.getLog().append(program.getCallGraph().toString()); - program.getLog().setVerboseLiveRanges(true); + //program.getLog().setVerboseLiveRanges(true); new Pass3LiveRangesAnalysis(program).findLiveRanges(); program.getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND"); @@ -242,7 +242,7 @@ public class Compiler { program.getLog().append(program.getRegisterUpliftProgram().toString((program.getVariableRegisterWeights()))); // Attempt uplifting registers through a lot of combinations - program.getLog().setVerboseUplift(true); + //program.getLog().setVerboseUplift(true); new Pass4RegisterUpliftCombinations(program).performUplift(10_000); //new Pass4RegisterUpliftStatic(program).performUplift(); diff --git a/src/main/java/dk/camelot64/kickc/TODO.txt b/src/main/java/dk/camelot64/kickc/TODO.txt index 98d2c8bd1..df7332bb2 100644 --- a/src/main/java/dk/camelot64/kickc/TODO.txt +++ b/src/main/java/dk/camelot64/kickc/TODO.txt @@ -1,3 +1,16 @@ +TODO's for new Constant Solution +- Live range overlap analysis of register combinations inside methods must also look at registers alive at all calls. +- Examine why temp-vars are used in flipper. +- Examine why flipper is plotted in a wrong position on the screen. +- Implement constants into the symbol table and support them in code. +- Implement new constant consolidation steps. +- Look at optimizing liverange.kc's ASM further - atm. there are to many copy-operations into unnecesary registers. +- In summin.asm the result of the 2nd sum() is clobbered twice during call to sum(). jsr sum, txa, lda #$d, ldx #$9, jsr sum + - Fix by introducing "effective alive vars" which includes alive vars at all calls to containing methods and using that during clobber check. ++ In loopnest.asm x&y are used in both loops - the outer x&y are clobbered by the inner loop. +- In voronoi.asm in render() x is clobbered during call to findcol(). + + Features - Move the main code into a main() function, and disallow code outside functions. The main function per default has no parameters and exits with RTS. - Improve locality of block sequence for if(cond) { stmt1; } else { stmt2; } to if(!cond) goto @else stmt1; jmp @end; @else: stmt2; @end: diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmFragmentManager.java b/src/main/java/dk/camelot64/kickc/asm/AsmFragmentManager.java index 95908c788..6ce3e3746 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmFragmentManager.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmFragmentManager.java @@ -76,15 +76,27 @@ public class AsmFragmentManager { } } if(signature.startsWith("zpby1=")) { - String subSignature = "aby="+signature.substring(6).replace("zpby2", "zpby1"); - CharStream subCharStream = loadOrSynthesizeFragment(subSignature); - if(subCharStream!=null) { - CharStream result = CharStreams.fromString(subCharStream.toString().replace("zpby1", "zpby2")+"\nsta {zpby1}\n"); - return result; + if(signature.contains("{zpby2}")) { + String subSignature = "aby="+signature.substring(6).replace("zpby2", "zpby1"); + CharStream subCharStream = loadOrSynthesizeFragment(subSignature); + if(subCharStream!=null) { + CharStream result = CharStreams.fromString(subCharStream.toString().replace("zpby1", "zpby2")+"\nsta {zpby1}\n"); + return result; + } + } else { + String subSignature = "aby="+signature.substring(6); + CharStream subCharStream = loadOrSynthesizeFragment(subSignature); + if(subCharStream!=null) { + CharStream result = CharStreams.fromString(subCharStream.toString()+"\nsta {zpby1}\n"); + return result; + } } } String sigNew = signature; + sigNew = regexpRewriteSignature(sigNew, "(.*)=(.*)_plus_aby", "$1=aby_plus_$2"); + sigNew = regexpRewriteSignature(sigNew, "(.*)=(.*)_plus_xby", "$1=xby_plus_$2"); + sigNew = regexpRewriteSignature(sigNew, "(.*)=(.*)_plus_yby", "$1=yby_plus_$2"); sigNew = regexpRewriteSignature(sigNew, "(.*)_ge_aby_then_(.*)", "aby_lt_$1_then_$2"); sigNew = regexpRewriteSignature(sigNew, "(.*)_ge_xby_then_(.*)", "xby_lt_$1_then_$2"); sigNew = regexpRewriteSignature(sigNew, "(.*)_ge_yby_then_(.*)", "yby_lt_$1_then_$2"); diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_plus_zpby1.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_plus_zpby1.asm new file mode 100644 index 000000000..159b74b48 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_plus_zpby1.asm @@ -0,0 +1,3 @@ +lda {zpby1} +clc +adc #{coby1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2Base.java b/src/main/java/dk/camelot64/kickc/passes/Pass2Base.java index bb5ee9cc5..fddbf2f62 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2Base.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2Base.java @@ -8,7 +8,6 @@ import dk.camelot64.kickc.icl.ProgramScope; /** Base class for a compiler pass */ public class Pass2Base { - private Program program; public Pass2Base(Program program) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java index b44b957aa..2283110e9 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java @@ -5,9 +5,7 @@ import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.asm.AsmSegment; import dk.camelot64.kickc.icl.*; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /*** Attempt to uplift live range equivalence classes to registers (instead of ZP) in each scope */ public class Pass4RegisterUpliftCombinations extends Pass2Base { @@ -115,7 +113,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { // Apply the uplift combination combination.allocate(program.getScope()); // Check the register allocation for whether a is register being allocated to two variables with overlapping live ranges - if(isAllocationOverlapping(program)) { + if (isAllocationOverlapping(program)) { if (program.getLog().isVerboseUplift()) { StringBuilder msg = new StringBuilder(); msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); @@ -197,27 +195,64 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { * @param program The program * @return true if the register allocation contains an overlapping allocation. false otherwise. */ - private static boolean isAllocationOverlapping(Program program) { - LiveRangeVariables liveRangeVariables = program.getLiveRangeVariables(); - ProgramScope programScope = program.getScope(); + public static boolean isAllocationOverlapping(Program program) { for (ControlFlowBlock block : program.getGraph().getAllBlocks()) { for (Statement statement : block.getStatements()) { - List alive = liveRangeVariables.getAlive(statement); - LinkedHashSet usedRegisters = new LinkedHashSet<>(); - for (VariableRef varRef : alive) { - Variable var = programScope.getVariable(varRef); - Registers.Register allocation = var.getAllocation(); - if(usedRegisters.contains(allocation)) { - if (program.getLog().isVerboseUplift()) { - StringBuilder msg = new StringBuilder(); - msg.append("Overlap register "+allocation+" in "+statement.toString(program)); - program.getLog().append(msg.toString()); - } - return true; - } - usedRegisters.add(allocation); + LinkedHashMap usedRegisters = new LinkedHashMap<>(); + if (isStatementAllocationOverlapping(program, statement, usedRegisters)) { + return true; + } + } + } + return false; + } + + /** + * Determine if a statement has an overlapping register allocation + * + * @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, + LinkedHashMap usedRegisters) { + LiveRangeVariables liveRangeVariables = program.getLiveRangeVariables(); + ProgramScope programScope = program.getScope(); + List alive = liveRangeVariables.getAlive(statement); + for (VariableRef varRef : alive) { + Variable var = programScope.getVariable(varRef); + Registers.Register allocation = var.getAllocation(); + LiveRangeEquivalenceClass allocationClass = usedRegisters.get(allocation); + if (allocationClass != null && !allocationClass.contains(varRef)) { + if (program.getLog().isVerboseUplift()) { + StringBuilder msg = new StringBuilder(); + msg.append("Overlap register " + allocation + " in " + statement.toString(program)); + program.getLog().append(msg.toString()); + } + return true; + } + LiveRangeEquivalenceClass varClass = + program.getLiveRangeEquivalenceClassSet().getEquivalenceClass(varRef); + usedRegisters.put(allocation, varClass); + } + + // If the statement is inside a method -also check against all variables alive at the exit of the calls. + 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; + Collection callers = + program.getCallGraph().getCallers(procedure.getLabel().getRef()); + for (CallGraph.CallBlock.Call caller : callers) { + StatementCall callStatement = + (StatementCall) program.getGraph().getStatementByIndex(caller.getCallStatementIdx()); + if (isStatementAllocationOverlapping(program, callStatement, usedRegisters)) { + return true; } - // TODO: If the statement is inside a method -also check against all variables alive at the calls. } } return false; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java index 80edfd5d2..7b8fba1a5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java @@ -36,6 +36,8 @@ public class Pass4ZeroPageCoalesce extends Pass2Base { getLog().append("Coalescing zero page register [ "+myEquivalenceClass+" ] with [ "+otherEquivalenceClass+" ]" ); myEquivalenceClass.addAll(otherEquivalenceClass); liveRangeEquivalenceClassSet.remove(otherEquivalenceClass); + // Reset the program register allocation + getProgram().getLiveRangeEquivalenceClassSet().storeRegisterAllocation(); return true; } } @@ -53,7 +55,15 @@ public class Pass4ZeroPageCoalesce extends Pass2Base { // Types match if (myEquivalenceClass.getRegister().isZp() && otherEquivalenceClass.getRegister().isZp()) { // Both registers are on Zero Page - if (!myEquivalenceClass.getLiveRange().overlaps(otherEquivalenceClass.getLiveRange())) { + + // Reset the program register allocation to the one specified in the equivalence class set + getProgram().getLiveRangeEquivalenceClassSet().storeRegisterAllocation(); + // Try out the coalesce to test if it works + for (VariableRef var : otherEquivalenceClass.getVariables()) { + Variable variable = getProgram().getScope().getVariable(var); + variable.setAllocation(myEquivalenceClass.getRegister()); + } + if(!Pass4RegisterUpliftCombinations.isAllocationOverlapping(getProgram())) { // Live ranges do not overlap // Perform coalesce! return true;