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

Implemented program snapshot and better PASS separation. Preparing for optimizations that require experimentation (snapshot/restore state).

This commit is contained in:
jespergravgaard 2019-08-01 23:00:58 +02:00
parent 764e0c1069
commit bc1dd78bd8
4 changed files with 197 additions and 91 deletions

View File

@ -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));

View File

@ -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<String> importPaths;
/** Imported files. */
private List<String> 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<Path> asmResourceFiles;
/** Comments for the (main) file. */
private List<Comment> 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<String> importPaths;
/** Imported files. PASS 0 (STATIC) */
private List<String> 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<VariableRef> earlyIdentifiedConstants;
/** Reserved ZP addresses that the compiler cannot use. */
private List<Number> reservedZps;
/** Absolute start address of the code. Null to start ad 0x080d. */
private Number programPc;
/** Cached phi transitions into each block. */
private Map<LabelRef, PhiTransitions> 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<Number> reservedZps;
/** Resource files that should be copied to the output folder to be compiled with the generated ASM. PASS 0-5 (STATIC) */
private List<Path> asmResourceFiles;
/** Comments for the (main) file. PASS 0-4 (STATIC) */
private List<Comment> 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<VariableRef> 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<LabelRef, PhiTransitions> 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;
}
}

View File

@ -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 extends Object> 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;
}
}

View File

@ -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;
}
}