mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-21 22:29:07 +00:00
Implemented program snapshot and better PASS separation. Preparing for optimizations that require experimentation (snapshot/restore state).
This commit is contained in:
parent
764e0c1069
commit
bc1dd78bd8
@ -244,6 +244,8 @@ public class Compiler {
|
|||||||
getLog().append("SYMBOL TABLE SSA");
|
getLog().append("SYMBOL TABLE SSA");
|
||||||
getLog().append(program.getScope().toString(program, null));
|
getLog().append(program.getScope().toString(program, null));
|
||||||
|
|
||||||
|
program.endPass1();
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,9 +535,6 @@ public class Compiler {
|
|||||||
}
|
}
|
||||||
new Pass4RegistersFinalize(program).allocate(true);
|
new Pass4RegistersFinalize(program).allocate(true);
|
||||||
new Pass4AssertZeropageAllocation(program).check();
|
new Pass4AssertZeropageAllocation(program).check();
|
||||||
}
|
|
||||||
|
|
||||||
private void pass5GenerateAndOptimizeAsm() {
|
|
||||||
|
|
||||||
// Final ASM code generation before optimization
|
// Final ASM code generation before optimization
|
||||||
new Pass4PhiTransitions(program).generate();
|
new Pass4PhiTransitions(program).generate();
|
||||||
@ -545,6 +544,12 @@ public class Compiler {
|
|||||||
// Remove unnecessary register savings from interrupts {@link InterruptType#HARDWARE_NOCLOBBER}
|
// Remove unnecessary register savings from interrupts {@link InterruptType#HARDWARE_NOCLOBBER}
|
||||||
new Pass4InterruptClobberFix(program).fix();
|
new Pass4InterruptClobberFix(program).fix();
|
||||||
|
|
||||||
|
program.endPass4();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pass5GenerateAndOptimizeAsm() {
|
||||||
|
|
||||||
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
||||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
||||||
|
|
||||||
|
@ -16,67 +16,73 @@ import java.util.Map;
|
|||||||
/** A KickC Intermediate Compiler Language (ICL) Program */
|
/** A KickC Intermediate Compiler Language (ICL) Program */
|
||||||
public class 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. */
|
/** The log containing information about the compilation process. */
|
||||||
private CompileLog log;
|
private CompileLog log;
|
||||||
|
|
||||||
/** Variables modified inside procedures. */
|
/** The name of the file being compiled. PASS 0-5 (STATIC) */
|
||||||
private ProcedureModifiedVars procedureModifiedVars;
|
private String fileName;
|
||||||
/** Information about calls. */
|
/** Paths used for importing files. PASS 0 (STATIC) */
|
||||||
private CallGraph callGraph;
|
private List<String> importPaths;
|
||||||
/** Information about dominators of all blocks */
|
/** Imported files. PASS 0 (STATIC) */
|
||||||
private DominatorsGraph dominators;
|
private List<String> imported;
|
||||||
/** Information about loops. */
|
|
||||||
private NaturalLoopSet loopSet;
|
|
||||||
|
|
||||||
/** Which block is each statement a part of. */
|
/** The target platform that the program is being build for. PASS 0-5 (STATIC) */
|
||||||
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. */
|
|
||||||
private TargetPlatform targetPlatform = TargetPlatform.DEFAULT;
|
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() {
|
public Program() {
|
||||||
this.scope = new ProgramScope();
|
this.scope = new ProgramScope();
|
||||||
@ -87,6 +93,64 @@ public class Program {
|
|||||||
this.reservedZps = new ArrayList<>();
|
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() {
|
public TargetPlatform getTargetPlatform() {
|
||||||
return targetPlatform;
|
return targetPlatform;
|
||||||
}
|
}
|
||||||
@ -279,34 +343,6 @@ public class Program {
|
|||||||
this.earlyIdentifiedConstants = earlyIdentifiedConstants;
|
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) {
|
public void setFileName(String fileName) {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
@ -343,4 +379,14 @@ public class Program {
|
|||||||
public Number getProgramPc() {
|
public Number getProgramPc() {
|
||||||
return programPc;
|
return programPc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompileLog getLog() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLog(CompileLog log) {
|
||||||
|
this.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
57
src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java
Normal file
57
src/main/java/dk/camelot64/kickc/model/ProgramSnapshot.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,6 @@ package dk.camelot64.kickc.passes;
|
|||||||
|
|
||||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
import dk.camelot64.kickc.model.Program;
|
import dk.camelot64.kickc.model.Program;
|
||||||
import dk.camelot64.kickc.model.StructUnwinding;
|
|
||||||
import dk.camelot64.kickc.model.VariableReferenceInfos;
|
import dk.camelot64.kickc.model.VariableReferenceInfos;
|
||||||
import dk.camelot64.kickc.model.statements.*;
|
import dk.camelot64.kickc.model.statements.*;
|
||||||
import dk.camelot64.kickc.model.symbols.ConstantVar;
|
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.symbols.Variable;
|
||||||
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
||||||
import dk.camelot64.kickc.model.values.LValue;
|
import dk.camelot64.kickc.model.values.LValue;
|
||||||
|
import dk.camelot64.kickc.model.values.StructUnwoundPlaceholder;
|
||||||
import dk.camelot64.kickc.model.values.VariableRef;
|
import dk.camelot64.kickc.model.values.VariableRef;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -51,9 +51,7 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||||||
// Not volatile
|
// Not volatile
|
||||||
eliminate = true;
|
eliminate = true;
|
||||||
} else if(variable.isVolatile() && variable.getType() instanceof SymbolTypeStruct) {
|
} else if(variable.isVolatile() && variable.getType() instanceof SymbolTypeStruct) {
|
||||||
// If an unwound volatile struct - eliminate it
|
if(assignment.getOperator()==null && assignment.getrValue2() instanceof StructUnwoundPlaceholder) {
|
||||||
StructUnwinding.VariableUnwinding variableUnwinding = getProgram().getStructUnwinding().getVariableUnwinding(variable.getRef());
|
|
||||||
if(variableUnwinding != null) {
|
|
||||||
eliminate = true;
|
eliminate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user