mirror of https://gitlab.com/camelot/kickc.git
811 lines
31 KiB
Java
811 lines
31 KiB
Java
package dk.camelot64.kickc;
|
|
|
|
import dk.camelot64.kickc.asm.AsmProgram;
|
|
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
|
|
import dk.camelot64.kickc.model.CompileError;
|
|
import dk.camelot64.kickc.model.ProcedureCompilation;
|
|
import dk.camelot64.kickc.model.Program;
|
|
import dk.camelot64.kickc.model.symbols.Procedure;
|
|
import dk.camelot64.kickc.model.values.StringEncoding;
|
|
import dk.camelot64.kickc.parser.CParser;
|
|
import dk.camelot64.kickc.parser.KickCParser;
|
|
import dk.camelot64.kickc.passes.*;
|
|
import dk.camelot64.kickc.preprocessor.CPreprocessor;
|
|
import org.antlr.v4.runtime.Token;
|
|
import org.antlr.v4.runtime.TokenSource;
|
|
|
|
import java.io.File;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Perform KickC compilation and optimizations
|
|
*/
|
|
public class Compiler {
|
|
|
|
private Program program;
|
|
|
|
/** The number of combinations to test when uplifting variables into registers. */
|
|
private int upliftCombinations = 100;
|
|
|
|
/** Enable the zero-page coalesce pass. It takes a lot of time, but limits the zero page usage significantly. */
|
|
private boolean enableZeroPageCoalesce = false;
|
|
|
|
/** Disables coalesce completely, which reduces compile time significantly. */
|
|
private boolean disableCoalesce = false;
|
|
|
|
/** Disable the entire register uplift. This will create significantly less optimized ASM since registers are not utilized. */
|
|
private boolean disableUplift = false;
|
|
|
|
/** Disable the long branch fix pass. */
|
|
private boolean disableLongBranchFix = 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;
|
|
|
|
/** The initial calling convention to use when compiling (from command line parameter). */
|
|
private Procedure.CallingConvention callingConvention;
|
|
|
|
public Compiler() {
|
|
this.program = new Program();
|
|
}
|
|
|
|
public Program getProgram() {
|
|
return program;
|
|
}
|
|
|
|
public void setDisableUplift(boolean disableUplift) {
|
|
this.disableUplift = disableUplift;
|
|
}
|
|
|
|
public void setDisableLongBranchFix(boolean disableLongBranchFix) {
|
|
this.disableLongBranchFix = disableLongBranchFix;
|
|
}
|
|
|
|
public void setWarnFragmentMissing(boolean warnFragmentMissing) {
|
|
program.setWarnFragmentMissing(warnFragmentMissing);
|
|
}
|
|
|
|
void setWarnArrayType(boolean warnArrayType) {
|
|
program.setWarnArrayType(warnArrayType);
|
|
}
|
|
|
|
public void setCallingConvention(Procedure.CallingConvention callingConvention) {
|
|
this.callingConvention = callingConvention;
|
|
}
|
|
|
|
public void setUpliftCombinations(int upliftCombinations) {
|
|
this.upliftCombinations = upliftCombinations;
|
|
}
|
|
|
|
public void enableZeroPageCoalesce() {
|
|
this.enableZeroPageCoalesce = true;
|
|
}
|
|
|
|
public void disableCoalesce() {
|
|
this.disableCoalesce = true;
|
|
}
|
|
|
|
void enableLoopHeadConstant() {
|
|
this.enableLoopHeadConstant = true;
|
|
}
|
|
|
|
void disableLoopHeadConstant() {
|
|
this.enableLoopHeadConstant = false;
|
|
}
|
|
|
|
public void setEnableLiveRangeCallPath(boolean enableLiveRangeCallPath) {
|
|
this.program.setEnableLiveRangeCallPath(enableLiveRangeCallPath);
|
|
}
|
|
|
|
public void setAsmFragmentBaseFolder(Path asmFragmentBaseFolder) {
|
|
program.setAsmFragmentBaseFolder(asmFragmentBaseFolder);
|
|
}
|
|
|
|
public AsmFragmentTemplateMasterSynthesizer getAsmFragmentMasterSynthesizer() {
|
|
return program.getAsmFragmentMasterSynthesizer();
|
|
}
|
|
|
|
public void setLog(CompileLog compileLog) {
|
|
program.setLog(compileLog);
|
|
}
|
|
|
|
public CompileLog getLog() {
|
|
return program.getLog();
|
|
}
|
|
|
|
public void addIncludePath(String path) {
|
|
program.getIncludePaths().add(path);
|
|
}
|
|
|
|
public void addLibraryPath(String path) {
|
|
program.getLibraryPaths().add(path);
|
|
}
|
|
|
|
public void addTargetPlatformPath(String path) {
|
|
program.getTargetPlatformPaths().add(path);
|
|
}
|
|
|
|
public void addAsmLibraryPath(String path) {
|
|
program.getAsmLibraryPaths().add(path);
|
|
}
|
|
|
|
/**
|
|
* Create a CParser and initialize it by adding any command-line defines and loading the files.
|
|
*
|
|
* @param defines The defined to add
|
|
* @param cFiles The files to load
|
|
* @param currentPath The current path (used to search for files)
|
|
* @return The initialized parser
|
|
*/
|
|
private CParser initializeParser(Map<String, String> defines, List<Path> cFiles, Path currentPath) {
|
|
CParser cParser = new CParser(program);
|
|
if(defines != null) {
|
|
for(String macroName : defines.keySet()) {
|
|
cParser.define(macroName, defines.get(macroName));
|
|
}
|
|
}
|
|
for(Path cFile : cFiles) {
|
|
final TokenSource cFileTokens = cParser.loadCFile(cFile.toString(), currentPath, program.getIncludePaths(), false);
|
|
cParser.addSourceLast(cFileTokens);
|
|
}
|
|
return cParser;
|
|
}
|
|
|
|
|
|
public void preprocess(List<Path> cFiles, Map<String, String> defines) {
|
|
Path currentPath = new File(".").toPath();
|
|
CParser cParser = initializeParser(defines, cFiles, currentPath);
|
|
final CPreprocessor preprocessor = cParser.getPreprocessor();
|
|
Token token = preprocessor.nextToken();
|
|
while(token.getType() != Token.EOF) {
|
|
System.out.print(token.getText());
|
|
token = preprocessor.nextToken();
|
|
}
|
|
System.out.println();
|
|
}
|
|
|
|
public void compile(List<Path> cFiles, Map<String, String> defines) {
|
|
if(cFiles.size() == 0)
|
|
throw new CompileError("Error! You must supply at least one file to compile!");
|
|
|
|
Path currentPath = new File(".").toPath();
|
|
CParser cParser = initializeParser(defines, cFiles, currentPath);
|
|
|
|
// Parse the files
|
|
KickCParser.FileContext cFileContext = cParser.getParser().file();
|
|
|
|
if(callingConvention == null) {
|
|
callingConvention = Procedure.CallingConvention.PHI_CALL;
|
|
}
|
|
|
|
// Find encoding
|
|
StringEncoding encoding = program.getTargetPlatform().getEncoding();
|
|
if(encoding==null)
|
|
encoding = StringEncoding.SCREENCODE_MIXED;
|
|
|
|
// Find default interrupt type
|
|
String interruptType = program.getTargetPlatform().getInterruptType();
|
|
|
|
Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program, callingConvention, encoding, interruptType);
|
|
pass0GenerateStatementSequence.generate();
|
|
|
|
pass1GenerateSSA();
|
|
pass2Optimize();
|
|
pass2UnrollLoops();
|
|
pass2InlineConstants();
|
|
pass2FinalizeAllNumbers();
|
|
|
|
//getLog().append("\nCONTROL FLOW GRAPH PASS 2");
|
|
//getLog().append(program.prettyControlFlowGraph());
|
|
|
|
//getLog().append("SYMBOL TABLE PASS 2");
|
|
//getLog().append(program.getScope().toString(program, null));
|
|
|
|
pass3Analysis();
|
|
pass4RegisterAllocation();
|
|
pass5GenerateAndOptimizeAsm();
|
|
}
|
|
|
|
private void pass1GenerateSSA() {
|
|
if(getLog().isVerboseStatementSequence()) {
|
|
getLog().append("\nSTATEMENTS");
|
|
for(Procedure procedure : program.getScope().getAllProcedures(true)) {
|
|
final ProcedureCompilation procedureCompilation = program.getProcedureCompilation(procedure.getRef());
|
|
getLog().append(procedureCompilation.getStatementSequence().toString(program));
|
|
}
|
|
}
|
|
new Pass1AssertJumpLabels(program).execute();
|
|
new Pass1GenerateControlFlowGraph(program).execute();
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("FIRST CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new Pass1ResolveForwardReferences(program).execute();
|
|
new Pass1ByteXIntrinsicRewrite(program).execute();
|
|
new Pass1AssertProcedureDefined(program).execute();
|
|
new Pass1AssertVariableDefined(program).execute();
|
|
new PassNAssertStructMembers(program).execute();
|
|
new Pass1UnwindBlockScopes(program).execute();
|
|
new Pass1Procedures(program).execute();
|
|
new PassNTypeInference(program).execute();
|
|
new PassNFixIntermediateMemoryArea(program).execute();
|
|
new Pass1FixProcedureParamSegment(program).execute();
|
|
new PassNTypeIdSimplification(program).execute();
|
|
new Pass1StructTypeSizeFix(program).execute();
|
|
new Pass1PrintfIntrinsicRewrite(program).execute();
|
|
new Pass1AssertReturn(program).execute();
|
|
new Pass1AssertUsedVars(program).execute();
|
|
new Pass1AssertNoModifyVars(program).execute();
|
|
new Pass1AssertNoLocalAddressArray(program).execute();
|
|
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("SYMBOLS");
|
|
getLog().append(program.getScope().toStringVars(program, false, false));
|
|
}
|
|
|
|
new Pass1FixLValuesLoHi(program).execute();
|
|
new Pass1AssertNoLValueIntermediate(program).execute();
|
|
new PassNAddTypeConversionAssignment(program, true).execute();
|
|
new Pass1AddressOfHandling(program).execute();
|
|
|
|
new Pass1AsmUsesHandling(program).execute();
|
|
|
|
new Pass1AssertProcedureCallParameters(program).execute();
|
|
new Pass1ModifiedVarsAnalysis(program).execute();
|
|
new Pass1CallStackVarPrepare(program).execute();
|
|
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("CONTROL FLOW GRAPH BEFORE SIZEOF FIX");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
|
|
new Pass1PointerSizeofFix(program).execute(); // After this point in the code all pointer math is byte-based
|
|
new PassNSizeOfSimplification(program).execute(); // Needed to eliminate sizeof() referencing pointer value variables
|
|
|
|
new PassNAssertTypeMatch(program).check();
|
|
|
|
new Pass1UnwindStructPrepare(program).execute();
|
|
new Pass1UnwindStructVariables(program).execute();
|
|
new Pass1UnwindStructValues(program).execute();
|
|
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("CONTROL FLOW GRAPH AFTER UNWIND");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
|
|
new PassNDeInlineCastValues(program, true).execute();
|
|
new PassNAddBooleanCasts(program, true).execute();
|
|
new PassNAddTypeConversionAssignment(program, true).execute();
|
|
|
|
new Pass1EarlyConstantIdentification(program).execute();
|
|
new PassNAssertConstantModification(program).execute();
|
|
new PassNAssertTypeDeref(program).check();
|
|
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("CONTROL FLOW GRAPH BEFORE INLINING");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new Pass1ProcedureInline(program).execute();
|
|
new PassNStatementIndices(program).step();
|
|
program.clearCallGraph();
|
|
new Pass1AssertNoRecursion(program).execute();
|
|
new Pass1AssertInterrupts(program).execute();
|
|
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("INITIAL CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
|
|
new Pass1EliminateUncalledProcedures(program).execute();
|
|
new PassNEliminateUnusedVars(program, false).execute();
|
|
new Pass1ExtractInlineStrings(program).execute();
|
|
new PassNCullEmptyBlocks(program, true).execute();
|
|
new PassNRenumberLabels(program, true).execute();
|
|
|
|
new Pass1UnrollConditionVariableSsa(program).step();
|
|
|
|
new Pass1ModifiedVarsAnalysis(program).execute();
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("PROCEDURE MODIFY VARIABLE ANALYSIS");
|
|
getLog().append(program.getProcedureModifiedVars().toString(program));
|
|
}
|
|
|
|
new Pass1CallVoidReturns(program).execute();
|
|
new Pass1CallStackVarConvert(program).execute();
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("PROCEDURE CALLS");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new Pass1CallStack(program).execute();
|
|
new Pass1CallVar(program).execute();
|
|
new Pass1CallPhiParameters(program).execute();
|
|
if(getLog().isVerbosePass1CreateSsa()) {
|
|
getLog().append("PROCEDURE PARAMETERS");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new PassNUnwindLValueLists(program).execute();
|
|
new Pass1GenerateSingleStaticAssignmentForm(program).execute();
|
|
new Pass1CallPhiReturn(program).execute();
|
|
new PassNUnwindLValueLists(program).execute();
|
|
|
|
new PassNStructUnwoundPlaceholderRemoval(program).execute();
|
|
|
|
new PassNAddTypeConversionAssignment(program, true).execute();
|
|
|
|
|
|
getLog().append("\nCONTROL FLOW GRAPH SSA");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
|
|
getLog().append("SYMBOL TABLE SSA");
|
|
getLog().append(program.getScope().toStringVars(program, false, false));
|
|
|
|
program.endPass1();
|
|
|
|
}
|
|
|
|
private void pass2AssertSSA() {
|
|
List<Pass2SsaAssertion> assertions = new ArrayList<>();
|
|
assertions.add(new Pass2AssertNoValueLists(program));
|
|
assertions.add(new PassNAssertTypeDeref(program));
|
|
assertions.add(new PassNAssertTypeMatch(program));
|
|
assertions.add(new Pass2AssertSymbols(program));
|
|
assertions.add(new Pass2AssertBlocks(program));
|
|
assertions.add(new Pass2AssertNoCallParameters(program));
|
|
assertions.add(new Pass2AssertNoCallLvalues(program));
|
|
assertions.add(new Pass2AssertNoReturnValues(program));
|
|
assertions.add(new Pass2AssertNoProcs(program));
|
|
assertions.add(new Pass2AssertNoLabels(program));
|
|
assertions.add(new Pass2AssertSingleAssignment(program));
|
|
assertions.add(new Pass2AssertRValues(program));
|
|
assertions.add(new Pass2AssertPhiPredecessors(program));
|
|
for(Pass2SsaAssertion assertion : assertions) {
|
|
assertion.check();
|
|
}
|
|
}
|
|
|
|
private List<PassStep> getPass2Optimizations() {
|
|
List<PassStep> optimizations = new ArrayList<>();
|
|
optimizations.add(new PassNAddNumberTypeConversions(program));
|
|
optimizations.add(new Pass2InlineCast(program));
|
|
optimizations.add(new PassNCastSimplification(program));
|
|
optimizations.add(new PassNFinalizeNumberTypeConversions(program, false));
|
|
optimizations.add(new PassNTypeInference(program));
|
|
optimizations.add(new PassNAddTypeConversionAssignment(program, false));
|
|
optimizations.add(new PassNTypeIdSimplification(program));
|
|
optimizations.add(new PassNSizeOfSimplification(program));
|
|
optimizations.add(new PassNStatementIndices(program));
|
|
optimizations.add(() -> {
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
return false;
|
|
});
|
|
optimizations.add(new Pass2UnaryNotSimplification(program));
|
|
optimizations.add(new Pass2AliasElimination(program));
|
|
optimizations.add(new Pass2IdenticalPhiElimination(program));
|
|
optimizations.add(new Pass2DuplicateRValueIdentification(program));
|
|
optimizations.add(() -> {
|
|
program.clearStatementIndices();
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
program.clearStatementInfos();
|
|
program.clearDominators();
|
|
return false;
|
|
});
|
|
optimizations.add(new PassNStatementIndices(program));
|
|
optimizations.add(new Pass2ConditionalJumpSimplification(program));
|
|
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
|
|
optimizations.add(new PassNAddBooleanCasts(program, false));
|
|
optimizations.add(new PassNArrayElementAddressOfRewriting(program));
|
|
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
|
|
optimizations.add(new Pass2ConstantRValueConsolidation(program));
|
|
optimizations.add(new Pass2ConstantIntrinsics(program));
|
|
optimizations.add(new Pass2ConstantIdentification(program));
|
|
optimizations.add(new Pass2ConstantValues(program));
|
|
optimizations.add(new Pass2ConstantCallPointerIdentification(program));
|
|
optimizations.add(new Pass2ConstantIfs(program));
|
|
optimizations.add(new Pass2ConstantStringConsolidation(program));
|
|
optimizations.add(new Pass2RangeResolving(program));
|
|
optimizations.add(new Pass2ComparisonOptimization(program));
|
|
optimizations.add(new Pass2InlineDerefIdx(program));
|
|
optimizations.add(new Pass2DeInlineWordDerefIdx(program));
|
|
optimizations.add(new PassNSimplifyConstantZero(program));
|
|
optimizations.add(new PassNSimplifyExpressionWithZero(program));
|
|
optimizations.add(new PassNEliminateUnusedConstructors(program));
|
|
optimizations.add(new Pass2EliminateUnusedBlocks(program)); // Notice sequence is important
|
|
optimizations.add(new PassNEliminateUnusedVars(program, true)); // Notice sequence is important
|
|
optimizations.add(new PassNEliminateEmptyProcedure(program));
|
|
optimizations.add(new PassNEliminateEmptyStart(program));
|
|
if(enableLoopHeadConstant) {
|
|
optimizations.add(new PassNStatementIndices(program));
|
|
optimizations.add(() -> {
|
|
program.clearDominators();
|
|
program.clearLoopSet();
|
|
return false;
|
|
});
|
|
optimizations.add(new Pass2LoopHeadConstantIdentification(program));
|
|
optimizations.add(() -> {
|
|
program.clearStatementIndices();
|
|
return false;
|
|
});
|
|
}
|
|
return optimizations;
|
|
}
|
|
|
|
private void pass2Optimize() {
|
|
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
|
|
List<PassStep> optimizations = getPass2Optimizations();
|
|
pass2Execute(optimizations);
|
|
}
|
|
|
|
private void pass2UnrollLoops() {
|
|
List<PassStep> loopUnrolling = new ArrayList<>();
|
|
loopUnrolling.add(new PassNStatementIndices(program));
|
|
loopUnrolling.add(() -> {
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
return false;
|
|
});
|
|
loopUnrolling.add(() -> {
|
|
program.clearStatementInfos();
|
|
return false;
|
|
});
|
|
loopUnrolling.add(() -> {
|
|
program.clearDominators();
|
|
return false;
|
|
});
|
|
loopUnrolling.add(() -> {
|
|
program.clearLoopSet();
|
|
return false;
|
|
});
|
|
loopUnrolling.add(new Pass2LoopUnroll(program));
|
|
|
|
if(getLog().isVerboseLoopUnroll()) {
|
|
getLog().append("CONTROL FLOW GRAPH BEFORE UNROLLING");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
|
|
boolean unrolled;
|
|
do {
|
|
unrolled = pass2ExecuteOnce(loopUnrolling);
|
|
if(unrolled) {
|
|
if(getLog().isVerboseLoopUnroll()) {
|
|
getLog().append("UNROLLED CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
pass2Optimize();
|
|
new Pass2LoopUnrollAssertComplete(program).step();
|
|
new PassNBlockSequencePlanner(program).step();
|
|
}
|
|
} while(unrolled);
|
|
|
|
}
|
|
|
|
private void pass2InlineConstants() {
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
// Constant inlining optimizations - as the last step to ensure that constant identification has been completed
|
|
List<PassStep> constantOptimizations = new ArrayList<>();
|
|
constantOptimizations.add(new PassNStatementIndices(program));
|
|
constantOptimizations.add(() -> {
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
return false;
|
|
});
|
|
constantOptimizations.add(new Pass2NopCastInlining(program));
|
|
constantOptimizations.add(new Pass2MultiplyToShiftRewriting(program));
|
|
constantOptimizations.add(new Pass2ModuloToAndRewriting(program));
|
|
constantOptimizations.add(new Pass2ConstantInlining(program));
|
|
constantOptimizations.add(new Pass2ConstantAdditionElimination(program));
|
|
constantOptimizations.add(new Pass2ConstantSimplification(program));
|
|
constantOptimizations.addAll(getPass2Optimizations());
|
|
pass2Execute(constantOptimizations);
|
|
|
|
}
|
|
|
|
private void pass2FinalizeAllNumbers() {
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
List<PassStep> constantOptimizations = new ArrayList<>();
|
|
constantOptimizations.add(new PassNFinalizeNumberTypeConversions(program, true));
|
|
constantOptimizations.addAll(getPass2Optimizations());
|
|
pass2Execute(constantOptimizations);
|
|
}
|
|
|
|
/**
|
|
* Execute optimization steps repeatedly until none of them performs an optimization anymore
|
|
*
|
|
* @param optimizations The optimizations to repeat
|
|
*/
|
|
private void pass2Execute(List<PassStep> optimizations) {
|
|
boolean ssaOptimized = true;
|
|
while(ssaOptimized) {
|
|
pass2AssertSSA();
|
|
ssaOptimized = false;
|
|
for(PassStep optimization : optimizations) {
|
|
boolean stepOptimized = true;
|
|
while(stepOptimized) {
|
|
stepOptimized = optimization.step();
|
|
if(stepOptimized) ssaOptimized = true;
|
|
pass2LogOptimization(optimization, stepOptimized);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Repeat a set of optimizations steps once each.
|
|
*
|
|
* @param optimizations The optimizations
|
|
* @return true if any step performed an optimization
|
|
*/
|
|
private boolean pass2ExecuteOnce(List<PassStep> optimizations) {
|
|
boolean ssaOptimized = false;
|
|
for(PassStep optimization : optimizations) {
|
|
pass2AssertSSA();
|
|
boolean stepOptimized = optimization.step();
|
|
if(stepOptimized) ssaOptimized = true;
|
|
pass2LogOptimization(optimization, stepOptimized);
|
|
}
|
|
return ssaOptimized;
|
|
}
|
|
|
|
private void pass2LogOptimization(PassStep optimization, boolean stepOptimized) {
|
|
if(stepOptimized) {
|
|
getLog().append("Successful SSA optimization " + optimization.getClass().getSimpleName() + "");
|
|
if(getLog().isVerboseSSAOptimize()) {
|
|
getLog().append("CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void pass3Analysis() {
|
|
|
|
new Pass1UnwindStructValues(program).execute();
|
|
new PassNStructUnwoundPlaceholderRemoval(program).execute();
|
|
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
new Pass3AssertNoTypeId(program).check();
|
|
new Pass3AssertRValues(program).check();
|
|
new Pass3AssertNoNumbers(program).check();
|
|
new Pass3AssertConstants(program).check();
|
|
new Pass3AssertArrayLengths(program).check();
|
|
new Pass3AssertNoMulDivMod(program).check();
|
|
// Phi lifting ensures that all variables in phi-blocks are in different live range equivalence classes
|
|
new Pass3PhiLifting(program).perform();
|
|
new PassNBlockSequencePlanner(program).step();
|
|
//getLog().append("CONTROL FLOW GRAPH - PHI LIFTED");
|
|
//getLog().append(program.prettyControlFlowGraph());
|
|
pass2AssertSSA();
|
|
new Pass3AddNopBeforeCallOns(program).generate();
|
|
new PassNStatementIndices(program).execute();
|
|
|
|
getLog().append("CALL GRAPH");
|
|
program.clearCallGraph();
|
|
getLog().append(program.getCallGraph().toString());
|
|
|
|
//getLog().setVerboseLiveRanges(true);
|
|
|
|
program.clearStatementInfos();
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
pass2AssertSSA();
|
|
|
|
// Phi mem coalesce removes as many variables introduced by phi lifting as possible - as long as their live ranges do not overlap
|
|
new Pass3PhiMemCoalesce(program).step();
|
|
|
|
if(getLog().isVerboseSSAOptimize()) {
|
|
getLog().append("CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new PassNCullEmptyBlocks(program, false).step();
|
|
if(getLog().isVerboseSSAOptimize()) {
|
|
getLog().append("CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new PassNRenumberLabels(program, false).execute();
|
|
if(getLog().isVerboseSSAOptimize()) {
|
|
getLog().append("CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
}
|
|
new PassNBlockSequencePlanner(program).step();
|
|
new Pass3AddNopBeforeCallOns(program).generate();
|
|
new PassNStatementIndices(program).execute();
|
|
|
|
program.clearCallGraph();
|
|
program.clearStatementIndices();
|
|
program.clearStatementInfos();
|
|
program.clearVariableReferenceInfos();
|
|
program.clearControlFlowBlockSuccessorClosure();
|
|
program.clearLiveRangeVariables();
|
|
program.clearLiveRangeVariablesEffective();
|
|
new PassNStatementIndices(program).execute();
|
|
pass2AssertSSA();
|
|
|
|
// program.getLiveRangeVariablesEffective();
|
|
// getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND");
|
|
// getLog().append(program.prettyControlFlowGraph());
|
|
|
|
program.getLiveRangeVariablesEffective();
|
|
|
|
getLog().append("\nFINAL CONTROL FLOW GRAPH");
|
|
getLog().append(program.prettyControlFlowGraph());
|
|
|
|
}
|
|
|
|
private void pass4RegisterAllocation() {
|
|
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append("DOMINATORS");
|
|
}
|
|
program.clearDominators();
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append(program.getDominators().toString());
|
|
}
|
|
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append("NATURAL LOOPS");
|
|
}
|
|
program.clearLoopSet();
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append(program.getLoopSet().toString());
|
|
}
|
|
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append("NATURAL LOOPS WITH DEPTH");
|
|
}
|
|
new Pass3LoopDepthAnalysis(program).findLoopDepths();
|
|
if(getLog().isVerboseLoopAnalysis()) {
|
|
getLog().append(program.getLoopSet().toString());
|
|
}
|
|
|
|
getLog().append("\nVARIABLE REGISTER WEIGHTS");
|
|
program.getVariableRegisterWeights();
|
|
getLog().append(program.getScope().toStringVars(program, true, false));
|
|
|
|
new Pass4LiveRangeEquivalenceClassesFinalize(program).allocate();
|
|
new Pass4RegistersFinalize(program).allocate(true, false);
|
|
|
|
// Initial Code generation
|
|
new Pass4CodeGeneration(program, false, program.isWarnFragmentMissing()).generate();
|
|
//getLog().append("\nINITIAL ASM");
|
|
//getLog().append("Target platform is " + program.getTargetPlatform().getName() + " / " + program.getTargetCpu().getName().toUpperCase(Locale.ENGLISH));
|
|
//getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
|
new Pass4AssertNoCpuClobber(program).check();
|
|
|
|
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();
|
|
boolean change;
|
|
do {
|
|
change = new Pass4RegisterUpliftPotentialRegisterAnalysis(program).findPotentialRegisters();
|
|
} while(change);
|
|
getLog().append(program.getRegisterPotentials().toString());
|
|
|
|
if(getLog().isVerboseSizeInfo()) {
|
|
getLog().append(program.getSizeInfo());
|
|
}
|
|
|
|
// Find register uplift scopes
|
|
getLog().append("REGISTER UPLIFT SCOPES");
|
|
getLog().append(program.getRegisterUpliftProgram().toString((program.getVariableRegisterWeights())));
|
|
|
|
// Attempt uplifting registers through a lot of combinations
|
|
//getLog().setVerboseUplift(true);
|
|
new Pass4RegisterUpliftCombinations(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 Pass4MemoryCoalesceAssignment(program).coalesce();
|
|
|
|
// Coalesce can be completely disabled for compilation speed reasons during programming and testing.
|
|
if(!disableCoalesce) {
|
|
// Register coalesce on call graph (saving ZP)
|
|
new Pass4MemoryCoalesceCallGraph(program).coalesce();
|
|
|
|
if (enableZeroPageCoalesce) {
|
|
// Register coalesce using exhaustive search (saving even more ZP - but slow)
|
|
new Pass4MemoryCoalesceExhaustive(program).coalesce();
|
|
}
|
|
}
|
|
new Pass4RegistersFinalize(program).allocate(true, true);
|
|
new Pass4AssertZeropageAllocation(program).check();
|
|
|
|
// Final ASM code generation before optimization
|
|
program.clearPhiTransitions();
|
|
new Pass4CodeGeneration(program, false, program.isWarnFragmentMissing()).generate();
|
|
new Pass4AssertNoCpuClobber(program).check();
|
|
|
|
// Remove unnecessary register savings from interrupts {@link InterruptType#HARDWARE_NOCLOBBER}
|
|
new Pass4InterruptClobberFix(program).fix();
|
|
|
|
program.endPass4();
|
|
|
|
}
|
|
|
|
private void pass5GenerateAndOptimizeAsm() {
|
|
|
|
if(getLog().isVerboseAsm()) {
|
|
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
|
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
|
}
|
|
|
|
getLog().append("ASSEMBLER OPTIMIZATIONS");
|
|
List<Pass5AsmOptimization> pass5Optimizations = new ArrayList<>();
|
|
pass5Optimizations.add(new Pass5NextJumpElimination(program));
|
|
pass5Optimizations.add(new Pass5UnnecesaryLoadElimination(program));
|
|
pass5Optimizations.add(new Pass5RedundantLabelElimination(program));
|
|
pass5Optimizations.add(new Pass5UnusedLabelElimination(program));
|
|
pass5Optimizations.add(new Pass5DoubleJumpElimination(program));
|
|
pass5Optimizations.add(new Pass5UnreachableCodeElimination(program));
|
|
pass5Optimizations.add(new Pass5RelabelLongLabels(program));
|
|
pass5Optimizations.add(new Pass5AddMainRts(program));
|
|
boolean asmOptimized = true;
|
|
while(asmOptimized) {
|
|
asmOptimized = false;
|
|
for(Pass5AsmOptimization optimization : pass5Optimizations) {
|
|
boolean stepOptimized = optimization.optimize();
|
|
if(stepOptimized) {
|
|
getLog().append("Succesful ASM optimization " + optimization.getClass().getSimpleName());
|
|
asmOptimized = true;
|
|
if(getLog().isVerboseAsmOptimize()) {
|
|
if(getLog().isVerboseAsm()) {
|
|
getLog().append("ASSEMBLER");
|
|
getLog().append(program.getAsm().toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
new Pass5ReindexAsmLines(program).optimize();
|
|
if(disableLongBranchFix) {
|
|
getLog().append("LONG BRANCH FIX DISABLED.");
|
|
} else {
|
|
new Pass5FixLongBranches(program).optimize();
|
|
}
|
|
|
|
getLog().append("\nFINAL SYMBOL TABLE");
|
|
getLog().append(program.getScope().toStringVars(program, false, false));
|
|
|
|
if(getLog().isVerboseAsm()) {
|
|
getLog().append("\nFINAL ASSEMBLER");
|
|
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
|
|
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
|
|
}
|
|
|
|
getLog().append("\nZERO PAGE TABLE");
|
|
getLog().append(program.getScope().toStringVars(program, true, true));
|
|
|
|
}
|
|
|
|
}
|