diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 878be4a6a..1cff9a4a9 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -5,8 +5,12 @@ public class CompileLog { private StringBuilder log; + /** Should register uplift analysis be verbose. */ private boolean verboseUplift; + /** Should live range analysis be verbose. */ + private boolean verboseLiveRanges; + public CompileLog() { this.log = new StringBuilder(); } @@ -29,6 +33,14 @@ public class CompileLog { this.verboseUplift = verboseUplift; } + public boolean isVerboseLiveRanges() { + return verboseLiveRanges; + } + + public void setVerboseLiveRanges(boolean verboseLiveRanges) { + this.verboseLiveRanges = verboseLiveRanges; + } + @Override public String toString() { return log.toString(); diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 8158b047a..2468e84d2 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -135,8 +135,8 @@ public class Compiler { public void pass2OptimizeSSA(Program program) { List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(program)); - optimizations.add(new Pass2ConstantPropagation(program)); - optimizations.add(new Pass2ConstantAdditionElimination(program)); + //optimizations.add(new Pass2ConstantPropagation(program)); + //optimizations.add(new Pass2ConstantAdditionElimination(program)); optimizations.add(new Pass2UnaryNotSimplification(program)); optimizations.add(new Pass2AliasElimination(program)); optimizations.add(new Pass2RedundantPhiElimination(program)); @@ -170,24 +170,34 @@ public class Compiler { program.getLog().append(program.getGraph().toString(program)); pass2AssertSSA(program); + new Pass3AddNopBeforeCallOns(program).generate(); + new Pass3StatementIndices(program).generateStatementIndices(); + + new Pass3CallGraphAnalysis(program).findCallGraph(); + program.getLog().append("CALL GRAPH"); + program.getLog().append(program.getCallGraph().toString()); + + program.getLog().setVerboseLiveRanges(true); + new Pass3LiveRangesAnalysis(program).findLiveRanges(); - program.getLog().append("CONTROL FLOW GRAPH - LIVE RANGES"); + program.getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND"); program.getLog().append(program.getGraph().toString(program)); pass2AssertSSA(program); + //program.getLog().setVerboseLiveRanges(false); + // 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).optimize(); new Pass2CullEmptyBlocks(program).optimize(); new Pass3BlockSequencePlanner(program).plan(); + new Pass3AddNopBeforeCallOns(program).generate(); + new Pass3StatementIndices(program).generateStatementIndices(); + new Pass3CallGraphAnalysis(program).findCallGraph(); new Pass3LiveRangesAnalysis(program).findLiveRanges(); program.getLog().append("CONTROL FLOW GRAPH - PHI MEM COALESCED"); program.getLog().append(program.getGraph().toString(program)); pass2AssertSSA(program); - new Pass3CallGraphAnalysis(program).findCallGraph(); - program.getLog().append("CALL GRAPH"); - program.getLog().append(program.getCallGraph().toString()); - new Pass3DominatorsAnalysis(program).findDominators(); program.getLog().append("DOMINATORS"); program.getLog().append(program.getDominators().toString()); @@ -232,13 +242,13 @@ public class Compiler { program.getLog().append(program.getRegisterUpliftProgram().toString((program.getVariableRegisterWeights()))); // Attempt uplifting registers through a lot of combinations + program.getLog().setVerboseUplift(true); new Pass4RegisterUpliftCombinations(program).performUplift(10_000); - //program.getLog().setVerboseUplift(true); //new Pass4RegisterUpliftStatic(program).performUplift(); // Attempt uplifting registers one at a time to catch remaining potential not realized by combination search - new Pass4RegisterUpliftRemains(program).performUplift(); + new Pass4RegisterUpliftRemains(program).performUplift(10_000); // Final register coalesce and finalization new Pass4ZeroPageCoalesce(program).allocate(); diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java b/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java index c9c2ab661..57a90a5c5 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java @@ -319,11 +319,11 @@ public class AsmFragment { Scope varScope = boundVar.getScope(); String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName(); if (!varScope.getRef().equals(scope) && varScope.getRef().getFullName().length() > 0) { - String param = varScope.getFullName() + "." + asmName.replace('@', 'b').replace(':', '_').replace("#", "_"); + String param = varScope.getFullName() + "." + asmName.replace('@', 'b').replace(':', '_').replace("#", "_").replace("$","_"); //param = ""+((Registers.RegisterZp) register).getZp(); return new AsmParameter(param, true); } else { - String param = asmName.replace('@', 'b').replace(':', '_').replace("#", "_"); + String param = asmName.replace('@', 'b').replace(':', '_').replace("#", "_").replace("$","_"); //param = ""+((Registers.RegisterZp) register).getZp(); return new AsmParameter(param, true); } @@ -355,7 +355,7 @@ public class AsmFragment { return new AsmParameter(param, SymbolTypeBasic.BYTE.equals(boundInt.getType())); } } else if (boundValue instanceof Label) { - String param = ((Label) boundValue).getLocalName().replace('@', 'b').replace(':', '_'); + String param = ((Label) boundValue).getLocalName().replace('@', 'b').replace(':', '_').replace("$","_"); return new AsmParameter(param, false); } else { throw new RuntimeException("Bound Value Type not implemented " + boundValue); diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_star_coby2.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_star_coby2.asm new file mode 100644 index 000000000..6fbd31416 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/aby=coby1_star_coby2.asm @@ -0,0 +1 @@ +lda {coby1}*{coby2} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=coby1_star_coby2.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=coby1_star_coby2.asm new file mode 100644 index 000000000..1ceb6d24c --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=coby1_star_coby2.asm @@ -0,0 +1,2 @@ +lda {coby1}+{coby2} +sta {zpby1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpby2_plus_zpby2.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpby2_plus_zpby2.asm new file mode 100644 index 000000000..996c28e6f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpby2_plus_zpby2.asm @@ -0,0 +1,4 @@ +lda {zpby2} +clc +adc {zpby2} +sta {zpby1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpptrby1_staridx_zpby2.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpptrby1_staridx_zpby2.asm new file mode 100644 index 000000000..8c6efa3fc --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/zpby1=zpptrby1_staridx_zpby2.asm @@ -0,0 +1,3 @@ +ldy {zpby2} +lda ({zpptrby1}),y +sta {zpby1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_aby.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_aby.asm new file mode 100644 index 000000000..f8b1c9ead --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_aby.asm @@ -0,0 +1,6 @@ +clc +adc {zpptrby2} +sta {zpptrby1} +lda #0 +adc {zpptrby2}+1 +sta {zpptrby1}+1 \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_zpby1.asm b/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_zpby1.asm new file mode 100644 index 000000000..0554b9a9e --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/asm/fragment/zpptrby1=zpptrby2_plus_zpby1.asm @@ -0,0 +1,7 @@ +lda {zpby1} +clc +adc {zpptrby2} +sta {zpptrby1} +lda #0 +adc {zpptrby2}+1 +sta {zpptrby1}+1 \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/icl/CallGraph.java b/src/main/java/dk/camelot64/kickc/icl/CallGraph.java index a4f93df4e..4d8aa2666 100644 --- a/src/main/java/dk/camelot64/kickc/icl/CallGraph.java +++ b/src/main/java/dk/camelot64/kickc/icl/CallGraph.java @@ -25,7 +25,9 @@ public class CallGraph { */ public CallBlock getOrCreateCallBlock(LabelRef scopeLabel) { CallBlock callBlock = getCallBlock(scopeLabel); - if (callBlock != null) return callBlock; + if (callBlock != null) { + return callBlock; + } // Not found - create it CallBlock newCallBlock = new CallBlock(scopeLabel); callBlocks.add(newCallBlock); @@ -73,7 +75,7 @@ public class CallGraph { public Collection getCallingBlocks(LabelRef scopeLabel) { ArrayList callingBlocks = new ArrayList<>(); for (CallBlock callBlock : callBlocks) { - if(callBlock.getCalledBlocks().contains(scopeLabel)) { + if (callBlock.getCalledBlocks().contains(scopeLabel)) { callingBlocks.add(callBlock.getScopeLabel()); } } @@ -89,6 +91,23 @@ public class CallGraph { return out.toString(); } + /** + * Get all calls of a specific procedure + * @param label The label of the procedure + * @return All calls + */ + public Collection getCallers(LabelRef label) { + Collection callers = new ArrayList<>(); + for (CallBlock callBlock : callBlocks) { + for (CallBlock.Call call : callBlock.getCalls()) { + if (call.getProcedure().equals(label)) { + callers.add(call); + } + } + } + return callers; + } + /** * A block in the call graph, matching a scope in the program. */ @@ -153,7 +172,7 @@ public class CallGraph { public Collection getCalls(LabelRef scope) { ArrayList callsToScope = new ArrayList<>(); for (Call call : calls) { - if(call.getProcedure().equals(scope)) { + if (call.getProcedure().equals(scope)) { callsToScope.add(call); } } @@ -191,7 +210,7 @@ public class CallGraph { @Override public String toString() { StringBuilder out = new StringBuilder(); - if(callStatementIdx!=null) { + if (callStatementIdx != null) { out.append(callStatementIdx).append(":"); } out.append(procedure); diff --git a/src/main/java/dk/camelot64/kickc/icl/LiveRange.java b/src/main/java/dk/camelot64/kickc/icl/LiveRange.java index c8adf3656..b50f82542 100644 --- a/src/main/java/dk/camelot64/kickc/icl/LiveRange.java +++ b/src/main/java/dk/camelot64/kickc/icl/LiveRange.java @@ -24,7 +24,7 @@ public class LiveRange { private int firstStatementIdx; /** - * The statement index of the las statement where the variable alive when the statement ends. + * The statement index of the last statement where the variable alive when the statement ends. *
  • * If the statement is in the middle of a block: * The variable is always used in the following statement for assignment or calculation - but never after. @@ -75,7 +75,7 @@ public class LiveRange { private Integer getIndex(Statement statement) { Integer index = statement.getIndex(); if (index == null) { - throw new RuntimeException("Statement index not defined! Live Ranges only work after defining statement indexes (Pass3LiveRangesAnalysis.generateStatementIndexes)."); + throw new RuntimeException("Statement index not defined! Live Ranges only work after defining statement indexes (Pass3LiveRangesAnalysis.generateStatementIndices)."); } return index; } diff --git a/src/main/java/dk/camelot64/kickc/icl/RegisterCombination.java b/src/main/java/dk/camelot64/kickc/icl/RegisterCombination.java index 0a1d3aecf..a77cbffeb 100644 --- a/src/main/java/dk/camelot64/kickc/icl/RegisterCombination.java +++ b/src/main/java/dk/camelot64/kickc/icl/RegisterCombination.java @@ -23,6 +23,7 @@ public class RegisterCombination { /** * Allocate the registers of the combination into the programs register allocation + * (does not update the allocation in the equivalence classes). */ public void allocate(ProgramScope scope) { for (LiveRangeEquivalenceClass equivalenceClass : allocation.keySet()) { diff --git a/src/main/java/dk/camelot64/kickc/icl/Registers.java b/src/main/java/dk/camelot64/kickc/icl/Registers.java index 8803bcb31..8eeb4753b 100644 --- a/src/main/java/dk/camelot64/kickc/icl/Registers.java +++ b/src/main/java/dk/camelot64/kickc/icl/Registers.java @@ -54,7 +54,7 @@ public class Registers { @Override public String toString() { - return "zp "+getType().toString()+":"+zp; + return "zp " + getType().toString() + ":" + zp; } @Override @@ -64,8 +64,12 @@ public class Registers { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } RegisterZp that = (RegisterZp) o; @@ -88,14 +92,14 @@ public class Registers { @Override public RegisterType getType() { - return RegisterType.ZP_BYTE; + return RegisterType.ZP_BYTE; } } /** Two zero page addresses used as a register for a single word variable. */ - public static class RegisterZpWord extends RegisterZp { + public static class RegisterZpWord extends RegisterZp { public RegisterZpWord(int zp) { super(zp); @@ -103,13 +107,13 @@ public class Registers { @Override public RegisterType getType() { - return RegisterType.ZP_WORD; + return RegisterType.ZP_WORD; } } /** A zero page address used as a register for a boolean variable. */ - public static class RegisterZpBool extends RegisterZp { + public static class RegisterZpBool extends RegisterZp { public RegisterZpBool(int zp) { super(zp); @@ -117,14 +121,14 @@ public class Registers { @Override public RegisterType getType() { - return RegisterType.ZP_BOOL; + return RegisterType.ZP_BOOL; } } /** A zro page address pair used as a register containing a pointer to a byte. */ - public static class RegisterZpPointerByte extends RegisterZp { + public static class RegisterZpPointerByte extends RegisterZp { public RegisterZpPointerByte(int zp) { super(zp); @@ -137,129 +141,97 @@ public class Registers { } - - - /** The X register. */ - public static class RegisterXByte implements Register { + /** A CPU byte register. */ + public static abstract class RegisterCpuByte implements Register { @Override - public RegisterType getType() { - return RegisterType.REG_X_BYTE; - } + public abstract RegisterType getType(); @Override public boolean isZp() { return false; } + @Override + public abstract String toString(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return getType().hashCode(); + } + + @Override + public String toString(Program program) { + return toString(); + } + + } + + + /** The X register. */ + public static class RegisterXByte extends RegisterCpuByte { + @Override + public RegisterType getType() { + return RegisterType.REG_X_BYTE; + } + @Override public String toString() { return "reg byte x"; } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - return true; - } - - @Override - public String toString(Program program) { - return toString(); - } - } /** The Y register. */ - public static class RegisterYByte implements Register { + public static class RegisterYByte extends RegisterCpuByte { @Override public RegisterType getType() { return RegisterType.REG_Y_BYTE; } - @Override - public boolean isZp() { - return false; - } - @Override public String toString() { return "reg byte y"; } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - return true; - } - - @Override - public String toString(Program program) { - return toString(); - } - } - /** The A register. */ - public static class RegisterAByte implements Register { + /** The Y register. */ + public static class RegisterAByte extends RegisterCpuByte { @Override public RegisterType getType() { return RegisterType.REG_A_BYTE; } - @Override - public boolean isZp() { - return false; - } - @Override public String toString() { return "reg byte a"; } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - return true; - } - - @Override - public String toString(Program program) { - return toString(); - } - } /** The special ALU register. */ - public static class RegisterALUByte implements Register { + public static class RegisterALUByte extends RegisterCpuByte { @Override public RegisterType getType() { return RegisterType.REG_ALU_BYTE; } - @Override - public boolean isZp() { - return false; - } - @Override public String toString() { return "reg byte alu"; } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - return true; - } - - @Override - public String toString(Program program) { - return toString(); - } - } + } diff --git a/src/main/java/dk/camelot64/kickc/icl/StatementPhiBlock.java b/src/main/java/dk/camelot64/kickc/icl/StatementPhiBlock.java index 4ae1467e7..dad465603 100644 --- a/src/main/java/dk/camelot64/kickc/icl/StatementPhiBlock.java +++ b/src/main/java/dk/camelot64/kickc/icl/StatementPhiBlock.java @@ -67,6 +67,12 @@ public class StatementPhiBlock extends StatementBase { StringBuilder s = new StringBuilder(); List variables = new ArrayList<>(phiVariables); Collections.reverse(variables); + if(phiVariables.size()==0) { + s.append(super.idxString()); + s.append("phi()"); + s.append(super.aliveString(program)); + s.append("\n "); + } for (PhiVariable phiVariable : variables) { s.append(super.idxString()); s.append(phiVariable.getVariable().toString(program)); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3AddNopBeforeCallOns.java b/src/main/java/dk/camelot64/kickc/passes/Pass3AddNopBeforeCallOns.java new file mode 100644 index 000000000..c9e606667 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3AddNopBeforeCallOns.java @@ -0,0 +1,39 @@ +package dk.camelot64.kickc.passes; + +/** + * If a method starts with a call to another method (or it is empty) - add an initial NOP operation (empty phi block). + * This ensures that the live range propagation can propagate from method out to caller properly. + */ + +import dk.camelot64.kickc.icl.*; + +import java.util.List; + +public class Pass3AddNopBeforeCallOns extends Pass2Base { + + public Pass3AddNopBeforeCallOns(Program program) { + super(program); + } + + /** + * Create index numbers for all statements in the control flow graph. + */ + public void generate() { + for (ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) { + if (block.isProcedureEntry(getProgram())) { + List statements = block.getStatements(); + if (statements.size() == 0) { + statements.add(0, new StatementPhiBlock()); + getLog().append("Adding NOP phi() at start of " + block.getLabel()); + } else { + Statement firstStmt = statements.get(0); + if (firstStmt instanceof StatementCall) { + statements.add(0, new StatementPhiBlock()); + getLog().append("Adding NOP phi() at start of " + block.getLabel()); + } + } + } + } + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3LiveRangesAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass3LiveRangesAnalysis.java index a9b8d0310..f37594bad 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3LiveRangesAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3LiveRangesAnalysis.java @@ -6,9 +6,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.icl.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; public class Pass3LiveRangesAnalysis extends Pass2Base { @@ -17,497 +15,515 @@ public class Pass3LiveRangesAnalysis extends Pass2Base { } public void findLiveRanges() { - - generateStatementIndexes(); - - LiveRangeVariables liveRanges = initializeLiveRanges(); - getProgram().setLiveRangeVariables(liveRanges); - //getLog().append("CONTROL FLOW GRAPH - LIVE RANGES"); - //getLog().append(getProgram().getGraph().toString(getProgram())); - + LiveRangeVariables liveRanges = new LiveRangeVariables(); boolean propagating; do { - propagating = propagateLiveRanges(liveRanges); + propagating = calculateLiveRanges(liveRanges); getProgram().setLiveRangeVariables(liveRanges); getLog().append("Propagating live ranges..."); - //getLog().append("CONTROL FLOW GRAPH - LIVE RANGES"); - //getLog().append(getProgram().getGraph().toString(getProgram())); + if (getLog().isVerboseLiveRanges()) { + getLog().append("CONTROL FLOW GRAPH - LIVE RANGES IN PROGRESS"); + getLog().append(getProgram().getGraph().toString(getProgram())); + } } while (propagating); - - getProgram().setLiveRangeVariables(liveRanges); } - /** - * Create index numbers for all statements in the control flow graph. + * Runs through all statements propagating variable live ranges. + * Variable live ranges of a statement is defined as all variables that are defined before or in the statement and used after the statement. + * Variable live ranges are propagated through repeatedly executing the following for each statement: + *

    + * alive(stmt) = used(nextstmt) ∪ alive(nextstmt) ∖ defined(nextstmt) + *

    + * where + *

      + *
    • alive(stmt) is the alive variables of the statement
    • + *
    • used(stmt) is the variables used in the statement
    • + *
    • defined(stmt) is the variables defined by the statement
    • + *
    • nextstmt is the statement following the statement stmt
    • + *
    + * + * Special consideration is giving when handling statements at the end/start of blocks, as multiple blocks may jump to the same block resulting in multiple stmt/nextstmt pairs. + * Calls to methods are also given special consideration. + * Variables used inside the method must be propagated bak to all callers while variables not used inside the method must skip the method entirely back to the statement before the call. + * + * @param liveRanges The live ranges to propagate. + * @return true if any live ranges was modified. false if no modification was performed (and the propagation is complete) */ - private void generateStatementIndexes() { - int currentIdx = 0; + private boolean calculateLiveRanges(LiveRangeVariables liveRanges) { + boolean modified = false; for (ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) { - for (Statement statement : block.getStatements()) { - statement.setIndex(currentIdx++); - } - } - } - - /** - * Create initial live ranges for all variables used in the program. - * The initial live ranges include only the statements preceding a usage of a variable. The live ranges will be extended iteratively afterwards. - * - * @return The initial live ranges. - */ - private LiveRangeVariables initializeLiveRanges() { - LiveRangeInitializer liveRangeInitializer = new LiveRangeInitializer(getProgram()); - return liveRangeInitializer.initialize(); - } - - private static class LiveRangeInitializer extends ControlFlowGraphBaseVisitor { - - private final Program program; - private LiveRangeVariables liveRanges; - - /** - * Contains the previous statement through the iteration of each block. null if this statement is the first in the block. - */ - private Statement previousStatement; - /** - * Contains the current block through the iteration. Used to find previous statement(s) when at the start of a block. - */ - private ControlFlowBlock currentBlock; - - public LiveRangeInitializer(Program program) { - this.program = program; - this.liveRanges = new LiveRangeVariables(); - } - - public LiveRangeVariables initialize() { - this.visitGraph(program.getGraph()); - return liveRanges; - } - - @Override - public Void visitBlock(ControlFlowBlock block) { - this.currentBlock = block; - this.previousStatement = null; - return super.visitBlock(block); - } - - @Override - public Void visitStatement(Statement statement) { - super.visitStatement(statement); - this.previousStatement = statement; - return null; - } - - private void addInitialLiveRange(RValue rValue) { - if (rValue == null) { - return; - } else if (rValue instanceof Constant) { - return; - } else if (rValue instanceof PointerDereferenceSimple) { - addInitialLiveRange(((PointerDereferenceSimple) rValue).getPointer()); - } else if (rValue instanceof PointerDereferenceIndexed) { - addInitialLiveRange(((PointerDereferenceIndexed) rValue).getPointer()); - addInitialLiveRange(((PointerDereferenceIndexed) rValue).getIndex()); - } else if (rValue instanceof VariableRef) { - if (previousStatement != null) { - // Inside a block - add live range to previous statement - liveRanges.addAlive((VariableRef) rValue, previousStatement); - } else { - // At start of block without a phi block - add live range at end of all previous blocks - List predecessors = program.getGraph().getPredecessors(currentBlock); - for (ControlFlowBlock predecessor : predecessors) { - List predecessorStatements = predecessor.getStatements(); - Statement predecessorLastStatement = predecessorStatements.get(predecessorStatements.size() - 1); - liveRanges.addAlive((VariableRef) rValue, predecessorLastStatement); - } - } - } else { - throw new RuntimeException("Unhandled RValue type " + rValue); - } - } - - @Override - public Void visitPhiBlock(StatementPhiBlock phi) { - for (StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) { - for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { - if (phiRValue.getrValue() instanceof Constant) { - continue; - } else { - LabelRef predecessorRef = phiRValue.getPredecessor(); - ControlFlowBlock predecessor = program.getGraph().getBlock(predecessorRef); - List predecessorStatements = predecessor.getStatements(); - // Is this block a procedure called from the predecessor? - if (currentBlock.getLabel().equals(predecessor.getCallSuccessor())) { - // Add to last statement before call in predecessor - StatementCall callStatement = (StatementCall) predecessorStatements.get(predecessorStatements.size() - 1); - if (predecessorStatements.size() > 1) { - Statement predecessorLastStatementBeforeCall = predecessorStatements.get(predecessorStatements.size() - 2); - liveRanges.addAlive((VariableRef) phiRValue.getrValue(), predecessorLastStatementBeforeCall); - } - } else { - // Add to last statement of predecessor - Statement predecessorLastStatement = predecessorStatements.get(predecessorStatements.size() - 1); - liveRanges.addAlive((VariableRef) phiRValue.getrValue(), predecessorLastStatement); - } - } - } - } - return null; - } - - @Override - public Void visitCall(StatementCall call) { - if (call.getlValue() instanceof PointerDereferenceIndexed) { - addInitialLiveRange(((PointerDereferenceIndexed) call.getlValue()).getPointer()); - addInitialLiveRange(((PointerDereferenceIndexed) call.getlValue()).getIndex()); - } else if (call.getlValue() instanceof PointerDereferenceSimple) { - addInitialLiveRange(((PointerDereferenceSimple) call.getlValue()).getPointer()); - } - if (call.getParameters() != null) { - for (RValue parameter : call.getParameters()) { - addInitialLiveRange(parameter); - } - } - return null; - } - - @Override - public Void visitReturn(StatementReturn aReturn) { - if (aReturn.getValue() != null) { - addInitialLiveRange(aReturn.getValue()); - } - return null; - } - - @Override - public Void visitAssignment(StatementAssignment assignment) { - if (assignment.getlValue() instanceof PointerDereferenceIndexed) { - addInitialLiveRange(((PointerDereferenceIndexed) assignment.getlValue()).getPointer()); - addInitialLiveRange(((PointerDereferenceIndexed) assignment.getlValue()).getIndex()); - } else if (assignment.getlValue() instanceof PointerDereferenceSimple) { - addInitialLiveRange(((PointerDereferenceSimple) assignment.getlValue()).getPointer()); - } - addInitialLiveRange(assignment.getrValue1()); - addInitialLiveRange(assignment.getrValue2()); - return null; - } - - @Override - public Void visitConditionalJump(StatementConditionalJump conditionalJump) { - addInitialLiveRange(conditionalJump.getrValue1()); - addInitialLiveRange(conditionalJump.getrValue2()); - return null; - } - - } - - /** - * Propagate live ranges to previous statements until reaching the definition of the variable. - * - * @return true if any propagation was done. (and more propagation is necessary to complete the live ranges) - */ - private boolean propagateLiveRanges(LiveRangeVariables liveRanges) { - LiveRangePropagator liveRangePropagator = new LiveRangePropagator(getProgram(), liveRanges); - return liveRangePropagator.propagate(); - } - - private static class LiveRangePropagator extends ControlFlowGraphBaseVisitor { - - /** - * The program. - */ - private Program program; - - /** - * The variable live ranges being propagated. - */ - private LiveRangeVariables liveRanges; - - /** - * Has anything been modified. - */ - private boolean modified; - - /** - * Contains the previous statement through the iteration of each block. null if this statement is the first in the block. - */ - private Statement previousStatement; - - /** - * Contains the current block through the iteration. Used to find previous statement(s) when at the start of a block. - */ - private ControlFlowBlock currentBlock; - - public LiveRangePropagator(Program program, LiveRangeVariables liveRanges) { - this.program = program; - this.liveRanges = liveRanges; - this.modified = false; - } - - public boolean propagate() { - this.visitGraph(program.getGraph()); - return modified; - } - - @Override - public Void visitBlock(ControlFlowBlock block) { - this.currentBlock = block; - this.previousStatement = null; - return super.visitBlock(block); - } - - @Override - public Void visitStatement(Statement statement) { - super.visitStatement(statement); - this.previousStatement = statement; - return null; - } - - /** - * Propagate variable live ranges backward from a statement to a previous statement - * - * @param statement The current statement - * @param previous The previous statement - * @param defined The lValues assigned in the current statement (will not be propagated) - */ - private void propagate(Statement statement, List previous, List defined) { - List alive = getAliveNotDefined(statement, defined); - - // Add all non-defined alive vars to all previous statement - for (VariableRef var : alive) { - for (PreviousStatement prev : previous) { - if(prev.isCaller()) { - // Special handling of propagation back to calls - // Only propagate vars also alive on the call itself - // And propagate them back to the statement prior to the call itself - StatementCall call = (StatementCall) prev.getStatement(); - List callAlive = liveRanges.getAlive(call); - if (callAlive.contains(var)) { - ControlFlowBlock callBlock = program.getGraph().getBlockFromStatementIdx(call.getIndex()); - List callBlockStatements = callBlock.getStatements(); - Statement lastBeforeCall = callBlockStatements.get(callBlockStatements.size() - 2); - modified |= liveRanges.addAlive(var, lastBeforeCall); - program.getLog().append("Propagated " + var + " through call " + call); - } - } else { - modified |= liveRanges.addAlive(var, prev.getStatement()); - } - } - } - ensureDefined(defined); - } - - /** - * Propagate live ranges back from a pricedure to the statements previous to the call og the procedure. - *

    - * Only variables also alive after the call are propagated further backward. - * - * @param procBlock The block starting the procedure - * @param phi The phi statement at the start of the current block - * @param defined The variables defined by the phi statement - */ - private void propagateCall(ControlFlowBlock procBlock, StatementPhiBlock phi, List defined) { - List alive = getAliveNotDefined(phi, defined); - if (alive.size() > 0) { - // Go back through each call - for (ControlFlowBlock predecessor : program.getGraph().getPredecessors(procBlock)) { - // If this block is a procedure called from the predecessor - the last statement is the one before the call - if (procBlock.getLabel().equals(predecessor.getCallSuccessor())) { - List predecessorStatements = predecessor.getStatements(); - StatementCall call = (StatementCall) predecessorStatements.get(predecessorStatements.size() - 1); - if (predecessorStatements.size() > 1) { - Statement lastBeforeCall = predecessorStatements.get(predecessorStatements.size() - 2); - - List callAlive = liveRanges.getAlive(call); - // Add all non-defined alive vars to all previous statement - for (VariableRef var : alive) { - // Only add the variables that are alive on the actual call (and thereby used after the call) - if (callAlive.contains(var)) { - modified |= liveRanges.addAlive(var, lastBeforeCall); - program.getLog().append("Propagated " + var + " through call " + call); + for (Statement stmt : block.getStatements()) { + List aliveNextStmt = liveRanges.getAlive(stmt); + Collection definedNextStmt = getDefined(stmt); + initLiveRange(liveRanges, definedNextStmt); + Collection previousStmts = getPreviousStatements(stmt); + for (PreviousStatement previousStmt : previousStmts) { + if (PreviousStatement.Type.NORMAL.equals(previousStmt.getType())) { + // Add all used variables to the previous statement (taking into account phi from blocks) + modified |= initUsedVars(liveRanges, stmt, previousStmt); + // Add all vars alive in the next statement + for (VariableRef aliveVar : aliveNextStmt) { + if (!definedNextStmt.contains(aliveVar)) { + boolean addAlive = liveRanges.addAlive(aliveVar, previousStmt.getStatement()); + modified |= addAlive; + if (addAlive && getLog().isVerboseLiveRanges()) { + getLog().append("Propagated alive var " + aliveVar + " to " + previousStmt.getStatement()); } } - + } + } else if (PreviousStatement.Type.LAST_IN_METHOD.equals(previousStmt.getType())) { + // Add all vars that are referenced in the method + StatementCall call = (StatementCall) stmt; + ProcedureRef procedure = call.getProcedure(); + Collection procUsed = getReferenced(procedure.getLabelRef()); + // The call statement has no used or defined by itself so only work with the alive vars + for (VariableRef aliveVar : aliveNextStmt) { + // Add all variables to previous that are not used inside the method + if (procUsed.contains(aliveVar)) { + boolean addUsedVar = liveRanges.addAlive(aliveVar, previousStmt.getStatement()); + modified |= addUsedVar; + if (addUsedVar && getLog().isVerboseLiveRanges()) { + getLog().append("Propagated alive var used in method into method " + aliveVar + " to " + previousStmt.getStatement()); + } + } + } + } else if (PreviousStatement.Type.SKIP_METHOD.equals(previousStmt.getType())) { + // Add all vars that the method does not use + StatementCall call = (StatementCall) stmt; + ProcedureRef procedure = call.getProcedure(); + Collection procUsed = getReferenced(procedure.getLabelRef()); + // The call statement has no used or defined by itself so only work with the alive vars + for (VariableRef aliveVar : aliveNextStmt) { + // Add all variables to previous that are not used inside the method + if (!procUsed.contains(aliveVar)) { + boolean addSkipVar = liveRanges.addAlive(aliveVar, previousStmt.getStatement()); + modified |= addSkipVar; + if (addSkipVar && getLog().isVerboseLiveRanges()) { + getLog().append("Propagated alive var unused in method by skipping call " + aliveVar + " to " + previousStmt.getStatement()); + } + } + } + } else if (PreviousStatement.Type.BEFORE_METHOD.equals(previousStmt.getType())) { + // Add all used variables to the previous statement (taking into account phi from blocks) + modified |= initUsedVars(liveRanges, stmt, previousStmt); + // Add all alive variables to previous that are used inside the method + ControlFlowBlock procBlock = getProgram().getGraph().getBlockFromStatementIdx(stmt.getIndex()); + Procedure procedure = (Procedure) getProgram().getScope().getSymbol(procBlock.getLabel()); + Collection procUsed = getUsed(procedure.getRef().getLabelRef()); + // The call statement has no used or defined by itself so only work with the alive vars + for (VariableRef aliveVar : aliveNextStmt) { + // Add all variables to previous that are used inside the method + if (procUsed.contains(aliveVar)) { + if (!definedNextStmt.contains(aliveVar)) { + boolean usedVar = liveRanges.addAlive(aliveVar, previousStmt.getStatement()); + modified |= usedVar; + if (usedVar && getLog().isVerboseLiveRanges()) { + getLog().append("Propagated alive used in method out of method " + aliveVar + " to " + previousStmt.getStatement()); + } + } + } else { + throw new RuntimeException("Never occurs!"); + } } } } } - ensureDefined(defined); } + return modified; + } - - /** - * Get the variables that are alive at a specific statemtn and not defined by the statement itself - * - * @param statement The statement - * @param defined The variables defined by the statement - * @return - */ - private List getAliveNotDefined(Statement statement, List defined) { - List alive = liveRanges.getAlive(statement); - if (defined != null) { - for (LValue lValue : defined) { - if (lValue instanceof VariableRef) { - // Remove the defined variable - alive.remove(lValue); - } - } - } - return alive; - } - - - /** - * If any lValues do not have a live range (they are never used) - create an empty live range for them - */ - private void ensureDefined(List defined) { - if (defined != null) { - for (LValue lValue : defined) { - if (lValue instanceof VariableRef) { - LiveRange lValLiveRange = liveRanges.getLiveRange((VariableRef) lValue); - if (lValLiveRange == null) { - liveRanges.addEmptyAlive((VariableRef) lValue); - program.getLog().append("Adding empty live range for unused variable " + lValue); + /** + * Adds variables used in the next statement to the alive vars of the previous statement + * + * @param liveRanges The live ranges to be updated + * @param stmt The next statement + * @param previousStmt The previous statement + * @return true if any live range modification was made + */ + private boolean initUsedVars( + LiveRangeVariables liveRanges, + Statement stmt, + PreviousStatement previousStmt) { + boolean modified = false; + Collection usedNextStmt = getUsed(stmt); + if (stmt instanceof StatementPhiBlock) { + // If current statement is a phi add the used variables to previous based on the phi entries + StatementPhiBlock phi = (StatementPhiBlock) stmt; + ControlFlowBlock previousBlock = + getProgram().getGraph().getBlockFromStatementIdx(previousStmt.getStatement().getIndex()); + for (StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) { + for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { + if (phiRValue.getPredecessor().equals(previousBlock.getLabel())) { + if (phiRValue.getrValue() instanceof VariableRef) { + VariableRef usedVar = (VariableRef) phiRValue.getrValue(); + boolean addUsed = liveRanges.addAlive(usedVar, previousStmt.getStatement()); + modified |= addUsed; + if (addUsed && getLog().isVerboseLiveRanges()) { + getLog().append("Adding used phi var " + usedVar + " to " + previousStmt.getStatement()); + } } } - + } + } + } else { + // Not a phi block - add used vars to all previous blocks + for (VariableRef usedVar : usedNextStmt) { + boolean addUsed = liveRanges.addAlive(usedVar, previousStmt.getStatement()); + modified |= addUsed; + if (addUsed && getLog().isVerboseLiveRanges()) { + getLog().append("Adding used var " + usedVar + " to " + previousStmt.getStatement()); } } } + return modified; + } - private static class PreviousStatement { - /** The statement */ - private Statement statement; - /** true if the statement is in a block that is a caller of the current block. false if the statement is a direct predecessor statement. */ - private boolean caller; + /** + * Get all variables used or defined inside a block and its successors (including any called method) + * @param labelRef The block to examine + * @return All used variables + */ + private Collection getReferenced(LabelRef labelRef) { + return getReferenced(labelRef, new ArrayList()); + } - public PreviousStatement(Statement statement, boolean caller) { - this.statement = statement; - this.caller = caller; - } + /** + * Get all variables used inside a block and its successors (including any called method) + * @param labelRef The block to examine + * @return All used variables + */ + private Collection getUsed(LabelRef labelRef) { + return getUsed(labelRef, new ArrayList()); + } - public Statement getStatement() { - return statement; - } - - public boolean isCaller() { - return caller; + /** + * Get all variables used inside a block and its successors (including any called method) + * @param labelRef The block to examine + * @param visited The blocks already visited during the search. Used to stop infinite recursion + * @return All used variables + */ + private Collection getUsed(LabelRef labelRef, Collection visited) { + if (labelRef == null) { + return new ArrayList<>(); + } + if (visited.contains(labelRef)) { + return new ArrayList<>(); + } + visited.add(labelRef); + ControlFlowBlock block = getProgram().getGraph().getBlock(labelRef); + if (block == null) { + return new ArrayList<>(); + } + LinkedHashSet used = new LinkedHashSet<>(); + for (Statement statement : block.getStatements()) { + used.addAll(getUsed(statement)); + if (statement instanceof StatementCall) { + ProcedureRef procedure = ((StatementCall) statement).getProcedure(); + used.addAll(getUsed(procedure.getLabelRef(), visited)); } } + used.addAll(getUsed(block.getDefaultSuccessor(), visited)); + used.addAll(getUsed(block.getConditionalSuccessor(), visited)); + return used; + } - - private List getPreviousStatements() { - if (previousStatement != null) { - // Inside a block - return Arrays.asList(new PreviousStatement(previousStatement, false)); - } else { - // At start of block - add last statement of all previous blocks - return getPreviousStatements(this.currentBlock, false); + /** + * Get all variables used or defined inside a block and its successors (including any called method) + * @param labelRef The block to examine + * @param visited The blocks already visited during the search. Used to stop infinite recursion + * @return All used variables + */ + private Collection getReferenced(LabelRef labelRef, Collection visited) { + if (labelRef == null) { + return new ArrayList<>(); + } + if (visited.contains(labelRef)) { + return new ArrayList<>(); + } + visited.add(labelRef); + ControlFlowBlock block = getProgram().getGraph().getBlock(labelRef); + if (block == null) { + return new ArrayList<>(); + } + LinkedHashSet referenced = new LinkedHashSet<>(); + for (Statement statement : block.getStatements()) { + referenced.addAll(getReferenced(statement)); + if (statement instanceof StatementCall) { + ProcedureRef procedure = ((StatementCall) statement).getProcedure(); + referenced.addAll(getReferenced(procedure.getLabelRef(), visited)); } } + referenced.addAll(getReferenced(block.getDefaultSuccessor(), visited)); + referenced.addAll(getReferenced(block.getConditionalSuccessor(), visited)); + return referenced; + } - /** - * Get the statement executed just before entering a specific block - * - * @param block The block to find the previous statement for - * @return The statement executed just before entering the block. - * If there are several predecessor blocks multiple statements are returned. - */ - private ArrayList getPreviousStatements(ControlFlowBlock block, boolean isCaller) { - ArrayList statements = new ArrayList<>(); - List predecessors = program.getGraph().getPredecessors(block); - for (ControlFlowBlock predecessor : predecessors) { - boolean isSubCaller = block.getLabel().equals(predecessor.getCallSuccessor()); - ArrayList lastStatements = getLastStatements(predecessor, isCaller||isSubCaller); - statements.addAll(lastStatements); + /** + * Get the variables defined by a statement + * @param stmt The statement + * @return Variables defined by the statement + */ + private Collection getDefined(Statement stmt) { + if (stmt instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) stmt; + LValue lValue = assignment.getlValue(); + if (lValue instanceof VariableRef) { + return Arrays.asList((VariableRef) lValue); } - return statements; - } - - /** - * Get the last statement executed in a specific block. - * If the block is empty the last statement of the previous block is returned. - * - * @param block The block to examine - * @param isCaller true if this block is a caller of the block we are finding previous statements for - * @return The last statement of the block (or the last statement of previous blocks if the block is empty) - */ - private ArrayList getLastStatements(ControlFlowBlock block, boolean isCaller) { - ArrayList statements = new ArrayList<>(); - List blockStatements = block.getStatements(); - if (blockStatements.size() == 0) { - // Block has no statements - go further back! - statements.addAll(getPreviousStatements(block, isCaller)); - } else { - // Add last statement from block - Statement predecessorLastStatement = blockStatements.get(blockStatements.size() - 1); - statements.add(new PreviousStatement(predecessorLastStatement, isCaller)); - } - return statements; - } - - @Override - public Void visitPhiBlock(StatementPhiBlock phi) { + } else if (stmt instanceof StatementPhiBlock) { List defined = new ArrayList<>(); + StatementPhiBlock phi = (StatementPhiBlock) stmt; for (StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) { defined.add(phiVariable.getVariable()); } - propagate(phi, getPreviousStatements(), defined); - return null; + return defined; } + return new ArrayList<>(); + } - @Override - public Void visitCall(StatementCall call) { - // Do not propagate directly to previous statement - //propagate(call, getPreviousStatements(), null); - // Instead propagate back through all statements of the procedure - ProcedureRef procedure = call.getProcedure(); - LabelRef procedureReturnBlock = procedure.getReturnBlock(); - ControlFlowBlock returnBlock = program.getGraph().getBlock(procedureReturnBlock); - propagate(call, getLastStatements(returnBlock, false), null); - return null; + /** + * Get the variables used, but not defined, in a statement + * @param statement The statement to examine + * @return The used variables (not including defined variables) + */ + private Collection getUsed(Statement statement) { + LinkedHashSet used = new LinkedHashSet<>(); + used.addAll(getReferenced(statement)); + used.removeAll(getDefined(statement)); + return used; + } + + /** + * Get the variables referenced (used or defined) in a statement + * @param statement The statement to examine + * @return The referenced variables + */ + private Collection getReferenced(Statement statement) { + LinkedHashSet referenced = new LinkedHashSet<>(); + if (statement instanceof StatementPhiBlock) { + StatementPhiBlock phiBlock = (StatementPhiBlock) statement; + for (StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) { + referenced.add(phiVariable.getVariable()); + for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { + referenced.addAll(getReferenced(phiRValue.getrValue())); + } + } + } else if (statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + referenced.addAll(getReferenced(assignment.getlValue())); + referenced.addAll(getReferenced(assignment.getrValue1())); + referenced.addAll(getReferenced(assignment.getrValue2())); + } else if (statement instanceof StatementConditionalJump) { + StatementConditionalJump conditionalJump = (StatementConditionalJump) statement; + referenced.addAll(getReferenced(conditionalJump.getrValue1())); + referenced.addAll(getReferenced(conditionalJump.getrValue2())); + } else if (statement instanceof StatementCall) { + StatementCall call = (StatementCall) statement; + referenced.addAll(getReferenced(call.getlValue())); + if (call.getParameters() != null) { + for (RValue param : call.getParameters()) { + referenced.addAll(getReferenced(param)); + } + } + } else if (statement instanceof StatementReturn) { + StatementReturn statementReturn = (StatementReturn) statement; + referenced.addAll(getReferenced(statementReturn.getValue())); + } else { + throw new RuntimeException("Unknown statement type " + statement); } + return referenced; + } - @Override - public Void visitAssignment(StatementAssignment assignment) { - propagate(assignment, getPreviousStatements(), Arrays.asList(assignment.getlValue())); - return null; - } - - @Override - public Void visitConditionalJump(StatementConditionalJump conditionalJump) { - propagate(conditionalJump, getPreviousStatements(), null); - return null; - } - - @Override - public Void visitJump(StatementJump jump) { - propagate(jump, getPreviousStatements(), null); - return null; - } - - @Override - public Void visitReturn(StatementReturn aReturn) { - propagate(aReturn, getPreviousStatements(), null); - return null; - } - - @Override - public Void visitProcedureBegin(StatementProcedureBegin procedureBegin) { - throw new RuntimeException("Statement not supported during live range propagation. Should be eliminated in earlier phase. " + procedureBegin); - } - - @Override - public Void visitProcedureEnd(StatementProcedureEnd procedureEnd) { - throw new RuntimeException("Statement not supported during live range propagation. Should be eliminated in earlier phase. " + procedureEnd); - } - - @Override - public Void visitJumpTarget(StatementLabel jumpTarget) { - throw new RuntimeException("Statement not supported during live range propagation. Should be eliminated in earlier phase. " + jumpTarget); + /** + * Get all variables referenced in an rValue + * @param rValue The rValue + * @return All referenced variables + */ + private Collection getReferenced(RValue rValue) { + if (rValue == null) { + return new ArrayList<>(); + } else if (rValue instanceof Constant) { + return new ArrayList<>(); + } else if (rValue instanceof PointerDereferenceSimple) { + return getReferenced(((PointerDereferenceSimple) rValue).getPointer()); + } else if (rValue instanceof PointerDereferenceIndexed) { + Collection used = new LinkedHashSet<>(); + used.addAll(getReferenced(((PointerDereferenceIndexed) rValue).getPointer())); + used.addAll(getReferenced(((PointerDereferenceIndexed) rValue).getIndex())); + return used; + } else if (rValue instanceof VariableRef) { + return Arrays.asList((VariableRef) rValue); + } else { + throw new RuntimeException("Unhandled RValue type " + rValue); } } + /** A statement just before the current statement. */ + private static class PreviousStatement { + + /** The statement */ + private Statement statement; + + /** The type. */ + private Type type; + + public enum Type { + /** The previous statement is executed immediately before the current statement. + * It may be in the same block as the current statement or a preceding block if the current statement is the first in a block. */ + NORMAL, + /** The previous statement is the last in a method. The current statement is the call to the method. */ + LAST_IN_METHOD, + /** The previous statement is the statement just before a method. The current statement is the first statement inside the method. + * (Used for propagating variables used inside the method back to the calling context)*/ + BEFORE_METHOD, + /** The previous statement is the statement before a call to a method, the current statement is the call + * (used for propagating variables that are not not used by the called method to statements before the call)*/ + SKIP_METHOD + } + + public PreviousStatement(Statement statement, Type type) { + this.statement = statement; + this.type = type; + } + + public Statement getStatement() { + return statement; + } + + public Type getType() { + return type; + } + + } + + /** + * Find the statement(s) executed just before the passed statement. + * There may be multiple previous statements if the current statement is the first in a block. + * @param statement The statement to find previous for + * @return statement(s) executed just before the passed statement + */ + private Collection getPreviousStatements(Statement statement) { + ArrayList previousStatements = new ArrayList<>(); + // Find the statement(s) just before the current statement (disregarding if the current statement is a call - this will be handled later) + Collection precedingStatements = getPrecedingStatement(statement); + if (statement instanceof StatementCall) { + // Add the statement(s) just before the call + for (Statement precedingStatement : precedingStatements) { + previousStatements.add(new PreviousStatement(precedingStatement, PreviousStatement.Type.SKIP_METHOD)); + } + // Add the last statement of the called method + StatementCall call = (StatementCall) statement; + ProcedureRef procedure = call.getProcedure(); + LabelRef procedureReturnBlock = procedure.getReturnBlock(); + ControlFlowBlock returnBlock = getProgram().getGraph().getBlock(procedureReturnBlock); + Collection lastStatements = getLastInBlock(returnBlock); + for (Statement lastStatement : lastStatements) { + previousStatements.add(new PreviousStatement(lastStatement, PreviousStatement.Type.LAST_IN_METHOD)); + } + } else if (precedingStatements.size() > 0) { + // The normal situation where the preceding statements are just normal statements executed before the current statement + for (Statement precedingStatement : precedingStatements) { + previousStatements.add(new PreviousStatement(precedingStatement, PreviousStatement.Type.NORMAL)); + } + } else { + // No preceding statements. Examine if this is the first statement in a call. + ControlFlowBlock block = getProgram().getGraph().getBlockFromStatementIdx(statement.getIndex()); + if (block.isProcedureEntry(getProgram())) { + // Current is first statement of a call - add the statement preceding the call. + Collection callers = getProgram().getCallGraph().getCallers(block.getLabel()); + for (CallGraph.CallBlock.Call call : callers) { + Statement callStmt = getProgram().getGraph().getStatementByIndex(call.getCallStatementIdx()); + Collection precedingCallStmt = getPrecedingStatement(callStmt); + for (Statement precedingCall : precedingCallStmt) { + previousStatements.add(new PreviousStatement(precedingCall, PreviousStatement.Type.BEFORE_METHOD)); + } + } + } + } + return previousStatements; + } + + /** + * Ensures that all defined vars have a non-zero live range + * @param liveRanges The live ranges + * @param defined The variables to init live ranges for + */ + private void initLiveRange(LiveRangeVariables liveRanges, Collection defined) { + if (defined != null) { + for (VariableRef variableRef : defined) { + LiveRange lValLiveRange = liveRanges.getLiveRange(variableRef); + if (lValLiveRange == null) { + liveRanges.addEmptyAlive(variableRef); + getProgram().getLog().append("Adding empty live range for unused variable " + variableRef); + } + } + } + } + + + /** + * Gets all statements preceding a statement directly in the code. + * This ignores calls and just returns the statement preceding the call. + * @param statement The statement to look for a preceding statement for + * @return The preceding statement(s). + * Multiple statements are returned if the current statement is the first in a block with multiple predecessor blocks. + * Zero statements are returned if the current statement is the first statement in the program or the first statement in a method. + */ + private Collection getPrecedingStatement(Statement statement) { + Statement previousStmt = null; + Statement prev = null; + ControlFlowBlock block = getProgram().getGraph().getBlockFromStatementIdx(statement.getIndex()); + List statements = block.getStatements(); + for (Statement stmt : statements) { + if (statement.getIndex().equals(stmt.getIndex())) { + previousStmt = prev; + break; + } + prev = stmt; + } + Collection previous; + if (previousStmt != null) { + previous = Arrays.asList(previousStmt); + } else { + // Current is first in a block - look in predecessor blocks + previous = getLastInPredecessors(block); + } + return previous; + } + + /** + * Find the last statement(s) of a block. Can trace back through predecessor blocks - but not back through calls. + * @param block The block + * @return The last statement(s). May contain multiple statements if the block is empty and has multiple predecessors. + * This method never traces back through calls, so the result may also be empty (if the block is an empty method). + */ + private Collection getLastInBlock(ControlFlowBlock block) { + List statements = block.getStatements(); + if (statements.size() > 0) { + return Arrays.asList(statements.get(statements.size() - 1)); + } else { + // Trace back through direct/conditional predecessors (not calls) + return getLastInPredecessors(block); + } + } + + /** + * Find the last statement(s) in the predecessors of a block. Can trace back through predecessor blocks - but not back through calls. + * @param block The block + * @return The last statement(s). May contain multiple statements if the block is empty and has multiple predecessors. + * This method never traces back through calls, so the result may also be empty (if the block is an empty method). + */ + private Collection getLastInPredecessors(ControlFlowBlock block) { + List predecessors = getProgram().getGraph().getPredecessors(block); + ArrayList last = new ArrayList<>(); + for (ControlFlowBlock predecessor : predecessors) { + if (block.getLabel().equals(predecessor.getDefaultSuccessor()) || block.getLabel().equals(predecessor.getConditionalSuccessor())) { + last.addAll(getLastInBlock(predecessor)); + } + } + return last; + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3StatementIndices.java b/src/main/java/dk/camelot64/kickc/passes/Pass3StatementIndices.java new file mode 100644 index 000000000..44625fdd2 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3StatementIndices.java @@ -0,0 +1,32 @@ +package dk.camelot64.kickc.passes; + +/** + * Identify the alive intervals for all variables. Add the intervals to the ProgramScope. + */ + +import dk.camelot64.kickc.icl.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Pass3StatementIndices extends Pass2Base { + + public Pass3StatementIndices(Program program) { + super(program); + } + + /** + * Create index numbers for all statements in the control flow graph. + */ + public void generateStatementIndices() { + int currentIdx = 0; + for (ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) { + for (Statement statement : block.getStatements()) { + statement.setIndex(currentIdx++); + } + } + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3VariableRegisterWeightAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass3VariableRegisterWeightAnalysis.java index f85a22690..60893f67b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3VariableRegisterWeightAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3VariableRegisterWeightAnalysis.java @@ -87,7 +87,10 @@ public class Pass3VariableRegisterWeightAnalysis extends Pass2Base { int depth = loopSet.getMaxLoopDepth(block); double w = 1.0 + Math.pow(10.0, depth); LiveRange liveRange = liveRangeVariables.getLiveRange(variable); - int s = liveRange.size(); + double s = liveRange.size(); + if(s<0.01) { + s = 0.1; + } variableRegisterWeights.addWeight(variable, w/s); return w/s; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 315a7dc8a..ea27c0cfd 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -91,7 +91,7 @@ public class Pass4CodeGeneration { Registers.RegisterZp registerZp = (Registers.RegisterZp) register; String asmName = scopeVar.getAsmName(); if (asmName != null && !added.contains(asmName)) { - asm.addLabelDecl(asmName, registerZp.getZp()); + asm.addLabelDecl(asmName.replace("#","_").replace("$","_"), registerZp.getZp()); added.add(asmName); } } @@ -254,7 +254,6 @@ public class Pass4CodeGeneration { for (ControlFlowBlock fBlock : transition.getFromBlocks()) { asm.addLabel((toBlock.getLabel().getLocalName() + "_from_" + fBlock.getLabel().getLocalName()).replace('@', 'b').replace(':', '_')); } - List assignments = transition.getAssignments(); for (PhiTransitions.PhiTransition.PhiAssignment assignment : assignments) { genAsmMove(asm, assignment.getVariable(), assignment.getrValue(), assignment.getPhiBlock(), scope); @@ -337,10 +336,14 @@ public class Pass4CodeGeneration { */ private PhiTransition findTransition(ControlFlowBlock fromBlock) { PhiTransition transition = new PhiTransition(fromBlock); - for (PhiTransition candidate : transitions.values()) { - if (candidate.equalAssignments(transition)) { - candidate.addFromBlock(fromBlock); - return candidate; + boolean isCallTransition = toBlock.getLabel().equals(fromBlock.getCallSuccessor()); + if(!isCallTransition) { + // If the transition is not a call - then attempt to join with other equal transition(s) + for (PhiTransition candidate : transitions.values()) { + if (candidate.equalAssignments(transition)) { + candidate.addFromBlock(fromBlock); + return candidate; + } } } return transition; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java index 81e259a60..b44b957aa 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java @@ -9,7 +9,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -/*** Find the variable equivalence classes to attempt to uplift in each scope */ +/*** Attempt to uplift live range equivalence classes to registers (instead of ZP) in each scope */ public class Pass4RegisterUpliftCombinations extends Pass2Base { public Pass4RegisterUpliftCombinations(Program program) { @@ -21,42 +21,13 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { Set unknownFragments = new LinkedHashSet<>(); List registerUpliftScopes = getProgram().getRegisterUpliftProgram().getRegisterUpliftScopes(); for (RegisterUpliftScope upliftScope : registerUpliftScopes) { - int bestScore = Integer.MAX_VALUE; - RegisterCombination bestCombination = null; - RegisterCombinationIterator combinationIterator = upliftScope.getCombinationIterator(getProgram().getRegisterPotentials()); - int countCombinations = 0; - while (combinationIterator.hasNext() && countCombinations < maxCombinations) { - countCombinations++; - if (countCombinations % 10000 == 0) { - getLog().append("Uplift attempts [" + upliftScope.getScopeRef() + "] " + countCombinations + "/" + combinationIterator.getNumIterations() + " (limiting to " + maxCombinations + ")"); - } - RegisterCombination combination = combinationIterator.next(); - if (!generateAsm(combination, getProgram(), unknownFragments, upliftScope)) { - continue; - } - // If no clobber - Find value of the resulting allocation - int combinationScore = getAsmScore(getProgram()); - if (getLog().isVerboseUplift()) { - StringBuilder msg = new StringBuilder(); - msg.append("Uplift attempt [" + upliftScope.getScopeRef() + "] "); - msg.append(combinationScore); - msg.append(" allocation: ").append(combination.toString()); - getLog().append(msg.toString()); - } - if (combinationScore < bestScore) { - bestScore = combinationScore; - bestCombination = combination; - } - } - if (bestCombination != null) { - // Save the best combination in the equivalence class - bestCombination.store(getProgram().getLiveRangeEquivalenceClassSet()); - getLog().append("Uplifting [" + upliftScope.getScopeRef() + "] best " + bestScore + " combination " + bestCombination.toString()); - } - if (combinationIterator.hasNext()) { - getLog().append("Limited combination testing to " + countCombinations + " combinations of " + combinationIterator.getNumIterations() + " possible."); - } + chooseBestUpliftCombination( + combinationIterator, + maxCombinations, + unknownFragments, + upliftScope.getScopeRef(), + getProgram()); } if (unknownFragments.size() > 0) { @@ -65,17 +36,68 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { getLog().append(" " + unknownFragment); } } + } - + /** + * Iterate combinations and choose the combination that provides the best ASM score. + * Stores the best combination directly in the {@link LiveRangeEquivalenceClassSet}. + * + * @param combinationIterator The combination iterator used for supplying different register allocations to test + * @param maxCombinations The maximal number of combinations to test. It the iterator has more combinations he rest is skipped (and a message logged) + * @param unknownFragments Receives any unknown ASM fragments encountered during the combinsation search + * @param scope The scope where the variables are being tested. (Only used for logging) + * @param program The program to test (used for accessing global data structures) + */ + static void chooseBestUpliftCombination( + RegisterCombinationIterator combinationIterator, int maxCombinations, + Set unknownFragments, + ScopeRef scope, + Program program + ) { + int bestScore = Integer.MAX_VALUE; + RegisterCombination bestCombination = null; + int countCombinations = 0; + while (combinationIterator.hasNext() && countCombinations < maxCombinations) { + countCombinations++; + if (countCombinations % 10000 == 0) { + program.getLog().append("Uplift attempts [" + scope + "] " + countCombinations + "/" + combinationIterator.getNumIterations() + " (limiting to " + maxCombinations + ")"); + } + RegisterCombination combination = combinationIterator.next(); + if (!generateCombinationAsm(combination, program, unknownFragments, scope)) { + continue; + } + // If no clobber - Find value of the resulting allocation + int combinationScore = getAsmScore(program); + if (program.getLog().isVerboseUplift()) { + StringBuilder msg = new StringBuilder(); + msg.append("Uplift attempt [" + scope + "] "); + msg.append(combinationScore); + msg.append(" allocation: ").append(combination.toString()); + program.getLog().append(msg.toString()); + } + if (combinationScore < bestScore) { + bestScore = combinationScore; + bestCombination = combination; + } + } + if (bestCombination != null) { + // Save the best combination in the equivalence class + bestCombination.store(program.getLiveRangeEquivalenceClassSet()); + program.getLog().append("Uplifting [" + scope + "] best " + bestScore + " combination " + bestCombination.toString()); + } + if (combinationIterator.hasNext()) { + program.getLog().append("Limited combination testing to " + countCombinations + " combinations of " + combinationIterator.getNumIterations() + " possible."); + } } /** * Attempt generate ASM with a specific register combination. * The generation may result in failure if *

      + *
    • The combination has assigned the same register to variables with overlapping live ranges
    • + *
    • The register combination results in clobbering registers containing values that are still alive
    • *
    • some ASM fragments are missing
    • *
    • the ALU register is used in a non-applicable way
    • - *
    • The register combination results in clobbering registers containing values that are still alive
    • *
    * * @param combination The register allocation combination @@ -83,14 +105,27 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { * @param scope The scope where the combination is tested. (Only used for logging) * @return true if the generation was successful */ - public static boolean generateAsm( - RegisterCombination combination, Program program, + public static boolean generateCombinationAsm( + RegisterCombination combination, + Program program, Set unknownFragments, - RegisterUpliftScope scope) { + ScopeRef scope) { // Reset register allocation to original zero page allocation new Pass4RegistersFinalize(program).allocate(false); // Apply the uplift combination combination.allocate(program.getScope()); + // Check the register allocation for whether a is register being allocated to two variables with overlapping live ranges + if(isAllocationOverlapping(program)) { + if (program.getLog().isVerboseUplift()) { + StringBuilder msg = new StringBuilder(); + msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); + msg.append("overlapping"); + msg.append(" allocation: ").append(combination.toString()); + program.getLog().append(msg.toString()); + } + + return false; + } // Generate ASM try { new Pass4CodeGeneration(program).generate(); @@ -98,7 +133,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { unknownFragments.add(e.getFragmentSignature()); if (program.getLog().isVerboseUplift()) { StringBuilder msg = new StringBuilder(); - msg.append("Uplift attempt [" + (scope==null?"":scope.getScopeRef()) + "] "); + msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); msg.append("missing fragment " + e.getFragmentSignature()); msg.append(" allocation: ").append(combination.toString()); program.getLog().append(msg.toString()); @@ -107,7 +142,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { } catch (AsmFragment.AluNotApplicableException e) { if (program.getLog().isVerboseUplift()) { StringBuilder msg = new StringBuilder(); - msg.append("Uplift attempt [" + (scope==null?"":scope.getScopeRef()) + "] "); + msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); msg.append("alu not applicable"); msg.append(" allocation: ").append(combination.toString()); program.getLog().append(msg.toString()); @@ -118,7 +153,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { if (hasClobberProblem) { if (program.getLog().isVerboseUplift()) { StringBuilder msg = new StringBuilder(); - msg.append("Uplift attempt [" + (scope==null?"":scope.getScopeRef()) + "] "); + msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); msg.append("clobber"); msg.append(" allocation: ").append(combination.toString()); program.getLog().append(msg.toString()); @@ -128,6 +163,14 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { return true; } + /** + * Get the score for the generated ASM program. + * Programs that take less cycles to execute have lower scores. + * In practice the score is calculated by multiplying cycles of ASM instructions with + * an estimate of the invocation count based on the loop depth of the instructions (10^depth). + * @param program The program containing the ASM to check + * @return The score of the ASM + */ public static int getAsmScore(Program program) { int score = 0; AsmProgram asm = program.getAsm(); @@ -148,4 +191,35 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { return score; } + + /** + * Check the register allocation for whether a is register being allocated to two variables with overlapping live ranges + * @param program The program + * @return true if the register allocation contains an overlapping allocation. false otherwise. + */ + private static boolean isAllocationOverlapping(Program program) { + LiveRangeVariables liveRangeVariables = program.getLiveRangeVariables(); + ProgramScope programScope = program.getScope(); + for (ControlFlowBlock block : program.getGraph().getAllBlocks()) { + for (Statement statement : block.getStatements()) { + List alive = liveRangeVariables.getAlive(statement); + LinkedHashSet usedRegisters = new LinkedHashSet<>(); + for (VariableRef varRef : alive) { + Variable var = programScope.getVariable(varRef); + Registers.Register allocation = var.getAllocation(); + if(usedRegisters.contains(allocation)) { + if (program.getLog().isVerboseUplift()) { + StringBuilder msg = new StringBuilder(); + msg.append("Overlap register "+allocation+" in "+statement.toString(program)); + program.getLog().append(msg.toString()); + } + return true; + } + usedRegisters.add(allocation); + } + // TODO: If the statement is inside a method -also check against all variables alive at the calls. + } + } + return false; + } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialAluAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialAluAnalysis.java index 34d2de179..27b7a6821 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialAluAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialAluAnalysis.java @@ -4,6 +4,8 @@ import dk.camelot64.kickc.icl.*; /*** * Find equivalence classes that could be assigned to the special ALU register. + * + * Sets the potential inside the {@link RegisterPotentials} for al variables that has the potential. */ public class Pass4RegisterUpliftPotentialAluAnalysis extends Pass2Base { @@ -39,17 +41,17 @@ public class Pass4RegisterUpliftPotentialAluAnalysis extends Pass2Base { // ALU applicable if the variable is the second lValue and the first lValue is non-null if (assignment.getrValue2().equals(potentialAluVar) && assignment.getrValue1() != null) { // The variable has ALU potential - hasAluPotential(registerPotentials, potentialAluVar); + setHasAluPotential(registerPotentials, potentialAluVar); } } else if (assignment.getOperator()!=null && "+".equals(assignment.getOperator().getOperator())) { // ALU applicable if the variable is one of the two values if (assignment.getrValue2().equals(potentialAluVar) && assignment.getrValue1() != null) { // The variable has ALU potential - hasAluPotential(registerPotentials, potentialAluVar); + setHasAluPotential(registerPotentials, potentialAluVar); } if (assignment.getrValue1().equals(potentialAluVar) && assignment.getrValue2() != null) { // The variable has ALU potential - hasAluPotential(registerPotentials, potentialAluVar); + setHasAluPotential(registerPotentials, potentialAluVar); } } } @@ -85,7 +87,7 @@ public class Pass4RegisterUpliftPotentialAluAnalysis extends Pass2Base { return potentialAluVar; } - private void hasAluPotential(RegisterPotentials registerPotentials, VariableRef ref) { + private void setHasAluPotential(RegisterPotentials registerPotentials, VariableRef ref) { LiveRangeEquivalenceClass potentialAluEquivalenceClass = liveRangeEquivalenceClassSet.getEquivalenceClass(ref); registerPotentials.addPotentialRegister(potentialAluEquivalenceClass, Registers.getRegisterALU()); getLog().append("Equivalence Class "+potentialAluEquivalenceClass+" has ALU potential."); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java index c66d7f2d2..3c91c7d12 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java @@ -5,14 +5,14 @@ import dk.camelot64.kickc.icl.*; import java.util.*; -/*** For eac non-uplifted equivalence class attempt to put it in a register */ +/*** For each non-uplifted equivalence class attempt to put it in a register */ public class Pass4RegisterUpliftRemains extends Pass2Base { public Pass4RegisterUpliftRemains(Program program) { super(program); } - public void performUplift() { + public void performUplift(int maxCombinations) { LiveRangeEquivalenceClassSet equivalenceClassSet = getProgram().getLiveRangeEquivalenceClassSet(); List equivalenceClasses = new ArrayList<>(equivalenceClassSet.getEquivalenceClasses()); @@ -28,60 +28,11 @@ public class Pass4RegisterUpliftRemains extends Pass2Base { for (LiveRangeEquivalenceClass equivalenceClass : equivalenceClasses) { if (equivalenceClass.getRegister().getType().equals(Registers.RegisterType.ZP_BYTE)) { - int bestScore = Integer.MAX_VALUE; - RegisterCombination bestCombination = null; + getLog().append("Attempting to uplift remaining variables in"+equivalenceClass); RegisterCombinationIterator combinationIterator = new RegisterCombinationIterator(Arrays.asList(equivalenceClass), getProgram().getRegisterPotentials()); - - while (combinationIterator.hasNext()) { - RegisterCombination combination = combinationIterator.next(); - // Reset register allocation to original zero page allocation - new Pass4RegistersFinalize(getProgram()).allocate(false); - // Apply the uplift combination - combination.allocate(getProgram().getScope()); - // Generate ASM - try { - new Pass4CodeGeneration(getProgram()).generate(); - } catch (AsmFragment.UnknownFragmentException e) { - unknownFragments.add(e.getFragmentSignature()); - //StringBuilder msg = new StringBuilder(); - //msg.append("Uplift remains attempt [" + equivalenceClass + "] "); - //msg.append("missing fragment " + e.getFragmentSignature()); - //msg.append(" allocation: ").append(combination.toString()); - //getLog().append(msg.toString()); - continue; - } catch (AsmFragment.AluNotApplicableException e) { - //StringBuilder msg = new StringBuilder(); - //msg.append("Uplift remains attempt [" + equivalenceClass + "] "); - //msg.append("alu not applicable"); - //msg.append(" allocation: ").append(combination.toString()); - //getLog().append(msg.toString()); - continue; - } - // If no clobber - Find value of the resulting allocation - boolean hasClobberProblem = new Pass4AssertNoCpuClobber(getProgram()).hasClobberProblem(false); - int combinationScore = Pass4RegisterUpliftCombinations.getAsmScore(getProgram()); - //StringBuilder msg = new StringBuilder(); - //msg.append("Uplift remains attempt [" + equivalenceClass + "] "); - //if (hasClobberProblem) { - // msg.append("clobber"); - //} else { - // msg.append(combinationScore); - //} - //msg.append(" allocation: ").append(combination.toString()); - //getLog().append(msg.toString()); - if (!hasClobberProblem) { - if (combinationScore < bestScore) { - bestScore = combinationScore; - bestCombination = combination; - } - } - } - // Save the best combination in the equivalence class - if(!bestCombination.getRegister(equivalenceClass).isZp()) { - bestCombination.store(equivalenceClassSet); - getLog().append("Uplifting remains [" + equivalenceClass + "] best " + bestScore + " combination " + bestCombination.toString()); - } - + VariableRef variableRef = equivalenceClass.getVariables().get(0); + Scope testedScope = getProgram().getScope().getVariable(variableRef).getScope(); + Pass4RegisterUpliftCombinations.chooseBestUpliftCombination(combinationIterator, maxCombinations, unknownFragments, testedScope.getRef(), getProgram()); } } @@ -92,7 +43,6 @@ public class Pass4RegisterUpliftRemains extends Pass2Base { } } - } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftStatic.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftStatic.java index 22661b84e..1cb08cec0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftStatic.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftStatic.java @@ -14,6 +14,20 @@ public class Pass4RegisterUpliftStatic extends Pass2Base { public void performUplift() { RegisterCombination combination = new RegisterCombination(); + /* + // Combination with a live range overlap issue in liverange.kc + setRegister(combination, "i#0", Registers.getRegisterX()); + setRegister(combination, "i#1", Registers.getRegisterX()); + setRegister(combination, "i#11", Registers.getRegisterX()); + setRegister(combination, "main::a#0", Registers.getRegisterA()); + setRegister(combination, "main::a#1", Registers.getRegisterA()); + setRegister(combination, "main::a#2", Registers.getRegisterA()); + setRegister(combination, "main::$0", Registers.getRegisterA()); + setRegister(combination, "main::$2", Registers.getRegisterA()); + setRegister(combination, "inc::return#0", Registers.getRegisterA()); + */ + + /* setRegister(combination, "cnt#12", Registers.getRegisterX()); setRegister(combination, "cnt2#11", Registers.getRegisterY()); setRegister(combination, "cnt3#11", new Registers.RegisterZpByte(4)); @@ -21,8 +35,9 @@ public class Pass4RegisterUpliftStatic extends Pass2Base { setRegister(combination, "main::$0", Registers.getRegisterA()); setRegister(combination, "main::$1", Registers.getRegisterA()); setRegister(combination, "inccnt::return#0", Registers.getRegisterA()); + */ - boolean success = Pass4RegisterUpliftCombinations.generateAsm( + boolean success = Pass4RegisterUpliftCombinations.generateCombinationAsm( combination, getProgram(), new HashSet(), @@ -38,6 +53,7 @@ public class Pass4RegisterUpliftStatic extends Pass2Base { msg.append(" allocation: ").append(combination.toString()); getLog().append(msg.toString()); } + combination.store(getProgram().getLiveRangeEquivalenceClassSet()); } else { throw new RuntimeException("Static uplift problem."); } @@ -52,5 +68,4 @@ public class Pass4RegisterUpliftStatic extends Pass2Base { combination.setRegister(equivalenceClass, register); } - } diff --git a/src/main/java/dk/camelot64/kickc/test/TestCompilationOutput.java b/src/main/java/dk/camelot64/kickc/test/TestCompilationOutput.java index 0aeddb4fe..5839bc647 100644 --- a/src/main/java/dk/camelot64/kickc/test/TestCompilationOutput.java +++ b/src/main/java/dk/camelot64/kickc/test/TestCompilationOutput.java @@ -24,6 +24,10 @@ public class TestCompilationOutput extends TestCase { helper = new ReferenceHelper("dk/camelot64/kickc/test/ref/"); } + public void testLiveRange() throws IOException, URISyntaxException { + compileAndCompare("liverange"); + } + public void testZpParamMin() throws IOException, URISyntaxException { compileAndCompare("zpparammin"); } diff --git a/src/main/java/dk/camelot64/kickc/test/liverange.kc b/src/main/java/dk/camelot64/kickc/test/liverange.kc new file mode 100644 index 000000000..2ad32aba7 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/test/liverange.kc @@ -0,0 +1,14 @@ +byte i=0; + +main(); + +void main() { + byte a=4; + a=a+inc(); + a=a+inc(); +} + +byte inc() { + i = i+7; + return i; +} \ No newline at end of file