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;