diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 07b1ce0ec..9c9490d13 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -32,6 +32,11 @@ public class CompileLog { */ private boolean verboseSSAOptimize = false; + /** + * Should Memory usage be verbose + */ + private boolean verboseMemoryUsage = false; + /** * Should loop unrolling be verbose. */ @@ -96,6 +101,19 @@ public class CompileLog { return log; } + public CompileLog verboseMemoryUsage() { + setVerboseMemoryUsage(true); + return this; + } + + public boolean isVerboseMemoryUsage() { + return verboseMemoryUsage; + } + + public void setVerboseMemoryUsage(boolean verboseMemoryUsage) { + this.verboseMemoryUsage = verboseMemoryUsage; + } + public CompileLog verboseSsaSourceCode() { setVerboseSsaSourceCode(true); return this; diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index e8189ba87..eb0ddcdb0 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -31,6 +31,9 @@ public class Compiler { /** Enable the zero-page coalesce pass. It takes a lot of time, but limits the zero page usage significantly. */ private boolean enableZeroPageCoalasce = false; + /** Disable the entire register uplift. This will create significantly less optimized ASM since registers are not utilized. */ + private boolean disableUplift = false; + /** Enable loop head constant optimization. It identified whenever a while()/for() has a constant condition on the first iteration and rewrites it. * Currently the optimization is flaky and results in NPE's and wrong values in some programs. */ private boolean enableLoopHeadConstant = false; @@ -42,6 +45,10 @@ public class Compiler { this.program = new Program(); } + public void setDisableUplift(boolean disableUplift) { + this.disableUplift = disableUplift; + } + public void setWarnFragmentMissing(boolean warnFragmentMissing) { program.setWarnFragmentMissing(warnFragmentMissing); } @@ -125,9 +132,15 @@ public class Compiler { pass1GenerateSSA(); pass2Optimize(); - pass2UnrollLoops(); pass2InlineConstants(); + + //getLog().append("\nCONTROL FLOW GRAPH PASS 2"); + //getLog().append(program.getGraph().toString(program)); + + //getLog().append("SYMBOL TABLE PASS 2"); + //getLog().append(program.getScope().toString(program, null)); + pass3Analysis(); pass4RegisterAllocation(); pass5GenerateAndOptimizeAsm(); @@ -307,6 +320,11 @@ public class Compiler { } private void pass2Optimize() { + + if(getLog().isVerboseMemoryUsage()) { + getLog().append(program.getSizeInfo()); + } + List optimizations = getPass2Optimizations(); pass2Execute(optimizations); } @@ -354,6 +372,11 @@ public class Compiler { } private void pass2InlineConstants() { + + if(getLog().isVerboseMemoryUsage()) { + getLog().append(program.getSizeInfo()); + } + // Constant inlining optimizations - as the last step to ensure that constant identification has been completed List constantOptimizations = new ArrayList<>(); constantOptimizations.add(new PassNStatementIndices(program)); @@ -421,6 +444,11 @@ public class Compiler { } private void pass3Analysis() { + + if(getLog().isVerboseMemoryUsage()) { + getLog().append(program.getSizeInfo()); + } + new Pass3AssertNoTypeId(program).check(); new Pass3AssertRValues(program).check(); new Pass3AssertNoNumbers(program).check(); @@ -471,6 +499,10 @@ public class Compiler { private void pass4RegisterAllocation() { + if(getLog().isVerboseMemoryUsage()) { + getLog().append(program.getSizeInfo()); + } + if(getLog().isVerboseLoopAnalysis()) { getLog().append("DOMINATORS"); } @@ -509,30 +541,38 @@ public class Compiler { getLog().append("Target platform is " + program.getTargetPlatform().getName() + " / " +program.getTargetCpu().getName().toUpperCase(Locale.ENGLISH)); getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program)); - // Find potential registers for each live range equivalence class - based on clobbering of fragments - getLog().append("REGISTER UPLIFT POTENTIAL REGISTERS"); - new Pass4RegisterUpliftPotentialInitialize(program).initPotentialRegisters(); - new Pass4RegisterUpliftPotentialAluAnalysis(program).findPotentialAlu(); - boolean change; - do { - change = new Pass4RegisterUpliftPotentialRegisterAnalysis(program).findPotentialRegisters(); - } while(change); - getLog().append(program.getRegisterPotentials().toString()); + if(disableUplift) { + getLog().append("REGISTER UPLIFT DISABLED!"); + } else { + // Find potential registers for each live range equivalence class - based on clobbering of fragments + getLog().append("REGISTER UPLIFT POTENTIAL REGISTERS"); + new Pass4RegisterUpliftPotentialInitialize(program).initPotentialRegisters(); + new Pass4RegisterUpliftPotentialAluAnalysis(program).findPotentialAlu(); + boolean change; + do { + change = new Pass4RegisterUpliftPotentialRegisterAnalysis(program).findPotentialRegisters(); + } while(change); + getLog().append(program.getRegisterPotentials().toString()); - // Find register uplift scopes - getLog().append("REGISTER UPLIFT SCOPES"); - getLog().append(program.getRegisterUpliftProgram().toString((program.getVariableRegisterWeights()))); + if(getLog().isVerboseMemoryUsage()) { + getLog().append(program.getSizeInfo()); + } - // Attempt uplifting registers through a lot of combinations - //getLog().setVerboseUplift(true); - new Pass4RegisterUpliftCombinations(program).performUplift(upliftCombinations); + // Find register uplift scopes + getLog().append("REGISTER UPLIFT SCOPES"); + getLog().append(program.getRegisterUpliftProgram().toString((program.getVariableRegisterWeights()))); - //getLog().setVerboseUplift(true); - //new Pass4RegisterUpliftStatic(program).performUplift(); - //getLog().setVerboseUplift(false); + // Attempt uplifting registers through a lot of combinations + //getLog().setVerboseUplift(true); + new Pass4RegisterUpliftCombinations(program).performUplift(upliftCombinations); - // Attempt uplifting registers one at a time to catch remaining potential not realized by combination search - new Pass4RegisterUpliftRemains(program).performUplift(upliftCombinations); + //getLog().setVerboseUplift(true); + //new Pass4RegisterUpliftStatic(program).performUplift(); + //getLog().setVerboseUplift(false); + + // Attempt uplifting registers one at a time to catch remaining potential not realized by combination search + new Pass4RegisterUpliftRemains(program).performUplift(upliftCombinations); + } // Register coalesce on assignment (saving bytes & cycles) new Pass4ZeroPageCoalesceAssignment(program).coalesce(); diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index 42c304205..cd00a421d 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -63,6 +63,9 @@ public class KickC implements Callable { @CommandLine.Option(names = {"-Ouplift"}, description = "Optimization Option. Number of combinations to test when uplifting variables to registers in a scope. By default 100 combinations are tested.") private Integer optimizeUpliftCombinations = null; + @CommandLine.Option(names = {"-Onouplift"}, description = "Optimization Option. Disable the register uplift allocation phase. This will be much faster but produce significantly slower ASM.") + private boolean optimizeNoUplift = false; + @CommandLine.Option(names = {"-Ocoalesce"}, description = "Optimization Option. Enables zero-page coalesce pass which limits zero-page usage significantly, but takes a lot of compile time.") private boolean optimizeZeroPageCoalesce = false; @@ -87,6 +90,9 @@ public class KickC implements Callable { @CommandLine.Option(names = {"-voptimize"}, description = "Verbosity Option. Control Flow Graph Optimization.") private boolean verboseSSAOptimize = false; + @CommandLine.Option(names = {"-vmemory"}, description = "Verbosity Option. Compiler Data Structure Sizes.") + private boolean verboseMemory = false; + @CommandLine.Option(names = {"-vnonoptimize"}, description = "Verbosity Option. Choices not to optimize.") private boolean verboseNonOptimization = false; @@ -236,6 +242,10 @@ public class KickC implements Callable { asmFileName = fileBaseName + ".asm"; } + if(optimizeNoUplift) { + compiler.setDisableUplift(true); + } + if(optimizeUpliftCombinations != null) { compiler.setUpliftCombinations(optimizeUpliftCombinations); } @@ -370,6 +380,10 @@ public class KickC implements Callable { compiler.getLog().setVerboseSSAOptimize(true); compiler.getLog().setSysOut(true); } + if(verboseMemory) { + compiler.getLog().setVerboseMemoryUsage(true); + compiler.getLog().setSysOut(true); + } if(verboseNonOptimization) { compiler.getLog().setVerboseNonOptimization(true); compiler.getLog().setSysOut(true); diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java b/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java index 4b39dc6a9..c1e5bf580 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmProgram.java @@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.values.ScopeRef; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * A 6502 assembler program @@ -265,6 +266,19 @@ public class AsmProgram { return null; } + /** + * Get information about the size of the program + * @return Size information + */ + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + sizeInfo.append("SIZE ASM chunks "+ getChunks().size()).append("\n"); + AtomicInteger numLines = new AtomicInteger(); + getChunks().stream().forEach(asmChunk -> numLines.addAndGet(asmChunk.getLines().size())); + sizeInfo.append("SIZE ASM lines "+ numLines).append("\n"); + return sizeInfo.toString(); + } + public static class AsmPrintState { // Output comments with information about the file/line number in the source file private boolean sourceFileInfo; diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java index 78cbca39a..e4ace5612 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java @@ -271,4 +271,19 @@ public class ControlFlowGraph implements Serializable { } return scopeBlocks; } + + /** + * Get information about the size of the program + * @return Size information + */ + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + sizeInfo.append("SIZE blocks "+ getAllBlocks().size()).append("\n"); + int numStmt = getAllBlocks().stream().mapToInt(block -> block.getStatements().size()).sum(); + sizeInfo.append("SIZE statements "+ numStmt).append("\n"); + int numPhiVars = getAllBlocks().stream().mapToInt(value -> value.getStatements().stream().mapToInt(value1 -> (value1 instanceof StatementPhiBlock)?((StatementPhiBlock) value1).getPhiVariables().size():0).sum()).sum(); + sizeInfo.append("SIZE phi variables "+ numPhiVars).append("\n"); + return sizeInfo.toString(); + } + } diff --git a/src/main/java/dk/camelot64/kickc/model/LiveRangeEquivalenceClassSet.java b/src/main/java/dk/camelot64/kickc/model/LiveRangeEquivalenceClassSet.java index e549d5294..b1353d503 100644 --- a/src/main/java/dk/camelot64/kickc/model/LiveRangeEquivalenceClassSet.java +++ b/src/main/java/dk/camelot64/kickc/model/LiveRangeEquivalenceClassSet.java @@ -113,4 +113,13 @@ public class LiveRangeEquivalenceClassSet { } } + /** + * Get information about the size of the program + * @return Size information + */ + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + sizeInfo.append("SIZE live range equivalence classes "+ getEquivalenceClasses().size()).append("\n"); + return sizeInfo.toString(); + } } diff --git a/src/main/java/dk/camelot64/kickc/model/LiveRangeVariablesEffective.java b/src/main/java/dk/camelot64/kickc/model/LiveRangeVariablesEffective.java index e3e955723..c29961df8 100644 --- a/src/main/java/dk/camelot64/kickc/model/LiveRangeVariablesEffective.java +++ b/src/main/java/dk/camelot64/kickc/model/LiveRangeVariablesEffective.java @@ -1,16 +1,17 @@ package dk.camelot64.kickc.model; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.symbols.Procedure; +import dk.camelot64.kickc.model.symbols.Scope; 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.model.values.VariableRef; -import dk.camelot64.kickc.model.statements.Statement; -import dk.camelot64.kickc.model.symbols.Procedure; -import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.passes.Pass2AliasElimination; import dk.camelot64.kickc.passes.calcs.PassNCalcLiveRangesEffective; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** * Effective variable live ranges for all statements. @@ -62,7 +63,7 @@ public class LiveRangeVariablesEffective { */ public Collection getAliveEffective(Statement statement) { Collection effectiveAliveTotal = statementAliveEffective.get(statement.getIndex()); - if(effectiveAliveTotal==null) { + if(effectiveAliveTotal == null) { effectiveAliveTotal = new LinkedHashSet<>(); AliveCombinations aliveCombinations = getAliveCombinations(statement); for(CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) { @@ -77,7 +78,7 @@ public class LiveRangeVariablesEffective { /** Cached alive combinations. */ Map statementAliveCombinations = new LinkedHashMap<>(); - /** Special procedure reference used to represent the ROOT scope during live range analysis.*/ + /** Special procedure reference used to represent the ROOT scope during live range analysis. */ static final ProcedureRef ROOT_PROCEDURE = new ProcedureRef(""); /** @@ -93,7 +94,7 @@ public class LiveRangeVariablesEffective { */ public AliveCombinations getAliveCombinations(Statement statement) { AliveCombinations stmtCombinations = this.statementAliveCombinations.get(statement.getIndex()); - if(stmtCombinations==null) { + if(stmtCombinations == null) { Collection aliveAtStmt = statementLiveVariables.get(statement.getIndex()); CallPaths callPaths; Collection referencedInProcedure; @@ -146,6 +147,50 @@ public class LiveRangeVariablesEffective { return stmtCombinations; } + + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + if(this.procedureCallPaths != null) { + AtomicInteger numCallPaths = new AtomicInteger(); + this.procedureCallPaths.values().forEach(callPaths -> numCallPaths.addAndGet(callPaths.getCallPaths().size())); + sizeInfo.append("SIZE procedureCallPaths ").append(numCallPaths.get()).append("\n"); + } + if(this.statementAliveEffective != null) { + sizeInfo.append("SIZE statementAliveEffective ").append(statementAliveEffective.size()).append(" statements "); + int sub = 0; + for(Collection variableRefs : statementAliveEffective.values()) { + sub += variableRefs.size(); + } + sizeInfo.append(" ").append(sub).append(" varrefs").append("\n"); + } + if(this.statementAliveCombinations != null) { + sizeInfo.append("SIZE statementAliveCombinations ").append(statementAliveCombinations.size()).append(" statements "); + int subVarRef = 0; + int subAlias = 0; + int subCallPath = 0; + int subEffectiveVarRef = 0; + for(AliveCombinations aliveCombinations : statementAliveCombinations.values()) { + subVarRef += aliveCombinations.aliveAtStmt.size(); + subVarRef += aliveCombinations.referencedInProcedure.size(); + if(aliveCombinations.callAliases!=null) + subAlias += aliveCombinations.callAliases.size(); + if(aliveCombinations.callPaths.callPaths!=null) + subCallPath += aliveCombinations.callPaths.callPaths.size(); + if(aliveCombinations.effectiveAliveAtStmt!=null) + for(Collection varRefs : aliveCombinations.effectiveAliveAtStmt.values()) { + subEffectiveVarRef += varRefs.size(); + } + } + sizeInfo.append(" ").append(subVarRef).append(" varrefs "); + sizeInfo.append(" ").append(subAlias).append(" aliases "); + sizeInfo.append(" ").append(subCallPath).append(" callpaths "); + sizeInfo.append(" ").append(subEffectiveVarRef).append(" eff-varrefs "); + sizeInfo.append("\n"); + } + return sizeInfo.toString(); + } + + /** * All call-paths leading into a specific procedure. */ diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index 0abdf2d0d..df37c3f96 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -498,4 +498,24 @@ public class Program { public String getLinkScriptBody() { return linkScriptBody; } + + /** + * Get information about the size of the program + * @return Size information + */ + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + sizeInfo.append(getScope().getSizeInfo()); + sizeInfo.append(getGraph().getSizeInfo()); + if(variableReferenceInfos!=null) + sizeInfo.append(variableReferenceInfos.getSizeInfo()); + if(getLiveRangeEquivalenceClassSet()!=null) + sizeInfo.append(getLiveRangeEquivalenceClassSet().getSizeInfo()); + if(liveRangeVariablesEffective!=null) + sizeInfo.append(liveRangeVariablesEffective.getSizeInfo()); + if(getAsm()!=null) + sizeInfo.append(getAsm().getSizeInfo()); + return sizeInfo.toString(); + } + } diff --git a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java index 6ea6cea5c..ea2ed4d00 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java @@ -135,7 +135,7 @@ public class VariableReferenceInfos { public String getSizeInfo() { StringBuilder sizeInfo = new StringBuilder(); if(blockSuccessorClosure != null) { - sizeInfo.append("blockSuccessorClosure ").append(blockSuccessorClosure.size()).append(" labels "); + sizeInfo.append("SIZE blockSuccessorClosure ").append(blockSuccessorClosure.size()).append(" labels "); int sub = 0; for(Collection labelRefs : blockSuccessorClosure.values()) { sub += labelRefs.size(); @@ -143,7 +143,7 @@ public class VariableReferenceInfos { sizeInfo.append(" ").append(sub).append(" labels").append("\n"); } { - sizeInfo.append("symbolVarReferences ").append(symbolVarReferences.size()).append(" SymbolVariableRefs "); + sizeInfo.append("SIZE symbolVarReferences ").append(symbolVarReferences.size()).append(" SymbolVariableRefs "); int sub = 0; for(Collection value : symbolVarReferences.values()) { sub += value.size(); @@ -151,7 +151,7 @@ public class VariableReferenceInfos { sizeInfo.append(" ").append(sub).append(" ReferenceToSymbolVars").append("\n"); } { - sizeInfo.append("statementVarReferences ").append(statementVarReferences.size()).append(" statements "); + sizeInfo.append("SIZE statementVarReferences ").append(statementVarReferences.size()).append(" statements "); int sub = 0; for(Collection value : statementVarReferences.values()) { sub += value.size(); @@ -159,7 +159,7 @@ public class VariableReferenceInfos { sizeInfo.append(" ").append(sub).append(" ReferenceToSymbolVars").append("\n"); } { - sizeInfo.append("blockVarReferences ").append(blockVarReferences.size()).append(" blocks "); + sizeInfo.append("SIZE blockVarReferences ").append(blockVarReferences.size()).append(" blocks "); int sub = 0; for(Collection value : blockVarReferences.values()) { sub += value.size(); diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/ProgramScope.java b/src/main/java/dk/camelot64/kickc/model/symbols/ProgramScope.java index e2b664a95..3008f5c4e 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/ProgramScope.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/ProgramScope.java @@ -46,4 +46,16 @@ public class ProgramScope extends Scope { return typeDefsScope; } + /** + * Get information about the size of the program + * @return Size information + */ + public String getSizeInfo() { + StringBuilder sizeInfo = new StringBuilder(); + sizeInfo.append("SIZE procedures "+ getAllProcedures(true).size()).append("\n"); + sizeInfo.append("SIZE scopes "+ getAllScopes(true).size()).append("\n"); + sizeInfo.append("SIZE variables "+ getAllVariables(true).size()).append("\n"); + sizeInfo.append("SIZE constants "+ getAllConstants(true).size()).append("\n"); + return sizeInfo.toString(); + } } diff --git a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java index eac666bb4..2ad137958 100644 --- a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java @@ -87,11 +87,7 @@ public class PassNCalcVariableReferenceInfos extends PassNCalcBase