From bc1dd78bd8680a5e003e61764d0b4aee750cb90e Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Thu, 1 Aug 2019 23:00:58 +0200 Subject: [PATCH] Implemented program snapshot and better PASS separation. Preparing for optimizations that require experimentation (snapshot/restore state). --- .../java/dk/camelot64/kickc/Compiler.java | 11 +- .../dk/camelot64/kickc/model/Program.java | 214 +++++++++++------- .../kickc/model/ProgramSnapshot.java | 57 +++++ .../passes/PassNEliminateUnusedVars.java | 6 +- 4 files changed, 197 insertions(+), 91 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 36bfef6a1..e3dfd5f45 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -244,6 +244,8 @@ public class Compiler { getLog().append("SYMBOL TABLE SSA"); getLog().append(program.getScope().toString(program, null)); + program.endPass1(); + return program; } @@ -533,9 +535,6 @@ public class Compiler { } new Pass4RegistersFinalize(program).allocate(true); new Pass4AssertZeropageAllocation(program).check(); - } - - private void pass5GenerateAndOptimizeAsm() { // Final ASM code generation before optimization new Pass4PhiTransitions(program).generate(); @@ -545,6 +544,12 @@ public class Compiler { // Remove unnecessary register savings from interrupts {@link InterruptType#HARDWARE_NOCLOBBER} new Pass4InterruptClobberFix(program).fix(); + program.endPass4(); + + } + + private void pass5GenerateAndOptimizeAsm() { + getLog().append("\nASSEMBLER BEFORE OPTIMIZATION"); getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program)); diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index c6ae55aaf..c8f3f59e0 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -16,67 +16,73 @@ import java.util.Map; /** A KickC Intermediate Compiler Language (ICL) Program */ public class Program { - /** The name of the file being compiled. */ - private String fileName; - /** Paths used for importing files. */ - private List importPaths; - /** Imported files. */ - private List imported; - /** The initial statement sequence generated by the parser. */ - private StatementSequence statementSequence; - /** The main scope. */ - private ProgramScope scope; - /** The control flow graph. */ - private ControlFlowGraph graph; - /** The 6502 ASM program. */ - private AsmProgram asm; - /** Resource files that should be copied to the output folder to be compiled with the generated ASM. */ - private List asmResourceFiles; - /** Comments for the (main) file. */ - private List fileComments; - /** The log containing information about the compilation process. */ private CompileLog log; - /** Variables modified inside procedures. */ - private ProcedureModifiedVars procedureModifiedVars; - /** Information about calls. */ - private CallGraph callGraph; - /** Information about dominators of all blocks */ - private DominatorsGraph dominators; - /** Information about loops. */ - private NaturalLoopSet loopSet; + /** The name of the file being compiled. PASS 0-5 (STATIC) */ + private String fileName; + /** Paths used for importing files. PASS 0 (STATIC) */ + private List importPaths; + /** Imported files. PASS 0 (STATIC) */ + private List imported; - /** Which block is each statement a part of. */ - private StatementInfos statementInfos; - /** Cached information about symbols. Contains a symbol table cache for fast access. */ - private SymbolInfos symbolInfos; - /** The variables referenced by blocks/statements. */ - private VariableReferenceInfos variableReferenceInfos; - /** The live ranges of all variables. */ - private LiveRangeVariables liveRangeVariables; - /** Live range equivalence classes containing variables that do not have overlapping live ranges. */ - private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet; - /** The effective live ranges of all variables. */ - private LiveRangeVariablesEffective liveRangeVariablesEffective; - /** The register weight of all variables describing how much the variable would theoretically gain from being in a register */ - private VariableRegisterWeights variableRegisterWeights; - /** Registers potentially usable as allocation for each live range equivalence class. */ - private RegisterPotentials registerPotentials; - /** Separation of live range equivalence classes into scopes - used for register uplift */ - private RegisterUpliftProgram registerUpliftProgram; - /** Constants identified during pass 1. */ - private Collection earlyIdentifiedConstants; - /** Reserved ZP addresses that the compiler cannot use. */ - private List reservedZps; - /** Absolute start address of the code. Null to start ad 0x080d. */ - private Number programPc; - /** Cached phi transitions into each block. */ - private Map phiTransitions; - /** Struct values unwound to individual variables. */ - private StructUnwinding structUnwinding; - /** The target platform that the program is being build for. */ + /** The target platform that the program is being build for. PASS 0-5 (STATIC) */ private TargetPlatform targetPlatform = TargetPlatform.DEFAULT; + /** Absolute start address of the code. Null to start ad 0x080d. PASS 0-5 (STATIC) */ + private Number programPc; + /** Reserved ZP addresses that the compiler cannot use. PASS 0-5 (STATIC) */ + private List reservedZps; + /** Resource files that should be copied to the output folder to be compiled with the generated ASM. PASS 0-5 (STATIC) */ + private List asmResourceFiles; + /** Comments for the (main) file. PASS 0-4 (STATIC) */ + private List fileComments; + + /** The initial statement sequence generated by the parser. PASS 1 (STATIC) */ + private StatementSequence statementSequence; + /** Constants identified during pass 1. PASS 1 (STATIC) */ + private Collection earlyIdentifiedConstants; + /** Variables modified inside procedures. PASS 1 (STATIC) */ + private ProcedureModifiedVars procedureModifiedVars; + /** Struct values unwound to individual variables. PASS 1 (STATIC) */ + private StructUnwinding structUnwinding; + + /** The main scope. PASS 0-5 (DYNAMIC) */ + private ProgramScope scope; + /** The control flow graph. PASS 1-5 (DYNAMIC) */ + private ControlFlowGraph graph; + /** Live range equivalence classes containing variables that do not have overlapping live ranges. PASS 3-5 (DYNAMIC) */ + private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet; + /** The 6502 ASM program. PASS 4-5 (DYNAMIC) */ + private AsmProgram asm; + + /** A saved program snapshot that can be rolled back. Used to store the (DYNAMIC) state of the program while trying out a potential optimization. PASS 2 (DYNAMIC)*/ + private ProgramSnapshot snapshot; + + /** Cached information about calls. PASS 1-4 (CACHED) */ + private CallGraph callGraph; + /** Cached information about the variables referenced by blocks/statements. PASS 1-4 (CACHED) */ + private VariableReferenceInfos variableReferenceInfos; + /** Information about dominators of all blocks. PASS 2U-4 (CACHED) */ + private DominatorsGraph dominators; + /** Cached information about symbols. Contains a symbol table cache for fast access. PASS 3-4 (CACHED) */ + private SymbolInfos symbolInfos; + /** Cached phi transitions into each block. PASS 4 (CACHED) */ + private Map phiTransitions; + /** The live ranges of all variables. PASS 3-4 (CACHED) */ + private LiveRangeVariables liveRangeVariables; + /** The effective live ranges of all variables. PASS 3-4 (CACHED) */ + private LiveRangeVariablesEffective liveRangeVariablesEffective; + /** Registers potentially usable as allocation for each live range equivalence class. PASS 4 (CACHED) */ + private RegisterPotentials registerPotentials; + /** Separation of live range equivalence classes into scopes - used for register uplift. PASS 4 (CACHED) */ + private RegisterUpliftProgram registerUpliftProgram; + + /** Cached information about which block is each statement a part of. PASS 2U-5 (CACHED) */ + private StatementInfos statementInfos; + /** Information about loops. PASS 2U-5 (CACHED) */ + private NaturalLoopSet loopSet; + /** The register weight of all variables describing how much the variable would theoretically gain from being in a register. PASS 3-5 (CACHED) */ + private VariableRegisterWeights variableRegisterWeights; public Program() { this.scope = new ProgramScope(); @@ -87,6 +93,64 @@ public class Program { this.reservedZps = new ArrayList<>(); } + /** + * Clears all data that is only used in PASS 1 + */ + public void endPass1() { + this.importPaths = null; + this.imported = null; + this.statementSequence = null; + this.earlyIdentifiedConstants = null; + this.procedureModifiedVars = null; + this.structUnwinding = null; + } + + /** + * Clears all data that is only used in PASS 2-4 + */ + public void endPass4() { + this.snapshot = null; + this.fileComments = null; + this.callGraph = null; + this.variableReferenceInfos = null; + this.dominators = null; + this.symbolInfos = null; + this.phiTransitions = null; + this.liveRangeVariables = null; + this.liveRangeVariablesEffective = null; + this.registerPotentials = null; + this.registerUpliftProgram = null; + } + + /** Save a snapshot of the dynamic parts of the program. */ + public void saveSnapshot() { + if(this.snapshot!=null) + throw new InternalError("Snapshot already saved!"); + if(this.liveRangeEquivalenceClassSet!=null) + throw new InternalError("Compiler Program Snapshot does not support liveRangeEquivalenceClassSet!"); + this.snapshot = new ProgramSnapshot(scope, graph); + } + + /** Restore the snapshot of the dynamic parts of the program. Clear all cached data and the snapshot. */ + public void restoreSnapshot() { + this.scope = snapshot.getScope(); + this.graph = snapshot.getGraph(); + this.snapshot = null; + this.callGraph = null; + this.variableReferenceInfos = null; + this.dominators = null; + this.loopSet = null; + this.statementInfos = null; + this.symbolInfos = null; + this.phiTransitions = null; + this.liveRangeVariables = null; + this.liveRangeVariablesEffective = null; + this.variableRegisterWeights = null; + this.registerPotentials = null; + this.registerUpliftProgram = null; + this.asm = null; + } + public TargetPlatform getTargetPlatform() { return targetPlatform; } @@ -279,34 +343,6 @@ public class Program { this.earlyIdentifiedConstants = earlyIdentifiedConstants; } - public CompileLog getLog() { - return log; - } - - public void setLog(CompileLog log) { - this.log = log; - } - - - @Override - public boolean equals(Object o) { - if(this == o) return true; - if(o == null || getClass() != o.getClass()) return false; - - Program program = (Program) o; - - if(scope != null ? !scope.equals(program.scope) : program.scope != null) return false; - if(graph != null ? !graph.equals(program.graph) : program.graph != null) return false; - return asm != null ? asm.equals(program.asm) : program.asm == null; - } - - @Override - public int hashCode() { - int result = scope != null ? scope.hashCode() : 0; - result = 31 * result + (graph != null ? graph.hashCode() : 0); - result = 31 * result + (asm != null ? asm.hashCode() : 0); - return result; - } public void setFileName(String fileName) { this.fileName = fileName; @@ -343,4 +379,14 @@ public class Program { public Number getProgramPc() { return programPc; } + + public CompileLog getLog() { + return log; + } + + public void setLog(CompileLog log) { + this.log = log; + } + + } diff --git a/src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java b/src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java new file mode 100644 index 000000000..ca8015a8c --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java @@ -0,0 +1,57 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.symbols.ProgramScope; + +import java.io.*; + +/** + * A snapshot of all dynamic data in a {@link Program}. + * Used whenever the compiler need to run an experiment where it may need to roll back to a previous state. + */ +public class ProgramSnapshot { + + /** The main scope. PASS 0-5 (DYNAMIC) */ + private ProgramScope scope; + /** The control flow graph. PASS 1-5 (DYNAMIC) */ + private ControlFlowGraph graph; + + public ProgramSnapshot(ProgramScope scope, ControlFlowGraph graph) { + this.scope = snapshot(scope); + this.graph = snapshot(graph); + } + + public ProgramScope getScope() { + return scope; + } + + public ControlFlowGraph getGraph() { + return graph; + } + + /** + * Creates a deep copy of an object (by using serialization) + * @param orig The object tot copy + * @return the copy with zero references to the original + */ + private static T snapshot(T orig) { + if(orig==null) return null; + T obj = null; + try { + // Write the object out to a byte array + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos); + out.writeObject(orig); + out.flush(); + out.close(); + // Make an input stream from the byte array and read a copy of the object back in. + ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(bos.toByteArray())); + obj = (T) in.readObject(); + } + catch(IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + return obj; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java index 64a00777f..6f22a4be9 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java @@ -2,7 +2,6 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.Program; -import dk.camelot64.kickc.model.StructUnwinding; import dk.camelot64.kickc.model.VariableReferenceInfos; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.ConstantVar; @@ -11,6 +10,7 @@ import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.types.SymbolTypeStruct; import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.StructUnwoundPlaceholder; import dk.camelot64.kickc.model.values.VariableRef; import java.util.Collection; @@ -51,9 +51,7 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization { // Not volatile eliminate = true; } else if(variable.isVolatile() && variable.getType() instanceof SymbolTypeStruct) { - // If an unwound volatile struct - eliminate it - StructUnwinding.VariableUnwinding variableUnwinding = getProgram().getStructUnwinding().getVariableUnwinding(variable.getRef()); - if(variableUnwinding != null) { + if(assignment.getOperator()==null && assignment.getrValue2() instanceof StructUnwoundPlaceholder) { eliminate = true; } }