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

Working on a new live range analysis and the ability to handle constants symbolically. TODOs remaining: Live range overlap analysis of register combinations inside methods must also look at registers alive at all calls. Examine why temp-vars are used in flipper. Examine why flipper is plotted in a wrong position on the screen. Implement constants into the symbol table and support them in code. Implement new constant consolidation steps.

This commit is contained in:
Jesper Gravgaard 2017-09-04 00:49:00 +02:00
parent 61d16b3030
commit eb6cf1188c
25 changed files with 860 additions and 665 deletions

View File

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

View File

@ -135,8 +135,8 @@ public class Compiler {
public void pass2OptimizeSSA(Program program) {
List<Pass2SsaOptimization> 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();

View File

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

View File

@ -0,0 +1 @@
lda {coby1}*{coby2}

View File

@ -0,0 +1,2 @@
lda {coby1}+{coby2}
sta {zpby1}

View File

@ -0,0 +1,4 @@
lda {zpby2}
clc
adc {zpby2}
sta {zpby1}

View File

@ -0,0 +1,3 @@
ldy {zpby2}
lda ({zpptrby1}),y
sta {zpby1}

View File

@ -0,0 +1,6 @@
clc
adc {zpptrby2}
sta {zpptrby1}
lda #0
adc {zpptrby2}+1
sta {zpptrby1}+1

View File

@ -0,0 +1,7 @@
lda {zpby1}
clc
adc {zpptrby2}
sta {zpptrby1}
lda #0
adc {zpptrby2}+1
sta {zpptrby1}+1

View File

@ -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<LabelRef> getCallingBlocks(LabelRef scopeLabel) {
ArrayList<LabelRef> 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<CallBlock.Call> getCallers(LabelRef label) {
Collection<CallBlock.Call> 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<Call> getCalls(LabelRef scope) {
ArrayList<Call> 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);

View File

@ -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.
* <ul><li>
* 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;
}

View File

@ -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()) {

View File

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

View File

@ -67,6 +67,12 @@ public class StatementPhiBlock extends StatementBase {
StringBuilder s = new StringBuilder();
List<PhiVariable> 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));

View File

@ -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<Statement> 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());
}
}
}
}
}
}

View File

@ -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:
* <p>
* <code>alive(stmt) = used(nextstmt) &cup; alive(nextstmt) &#x2216; defined(nextstmt)</code>
* <p>
* where
* <ul>
* <li>alive(stmt) is the alive variables of the statement</li>
* <li>used(stmt) is the variables used in the statement</li>
* <li>defined(stmt) is the variables defined by the statement</li>
* <li>nextstmt is the statement following the statement stmt</li>
* </ul>
*
* 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<Void> {
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<ControlFlowBlock> predecessors = program.getGraph().getPredecessors(currentBlock);
for (ControlFlowBlock predecessor : predecessors) {
List<Statement> 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<Statement> 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<Void> {
/**
* 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<PreviousStatement> previous, List<? extends LValue> defined) {
List<VariableRef> 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<VariableRef> callAlive = liveRanges.getAlive(call);
if (callAlive.contains(var)) {
ControlFlowBlock callBlock = program.getGraph().getBlockFromStatementIdx(call.getIndex());
List<Statement> 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.
* <p>
* 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<VariableRef> defined) {
List<VariableRef> 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<Statement> predecessorStatements = predecessor.getStatements();
StatementCall call = (StatementCall) predecessorStatements.get(predecessorStatements.size() - 1);
if (predecessorStatements.size() > 1) {
Statement lastBeforeCall = predecessorStatements.get(predecessorStatements.size() - 2);
List<VariableRef> 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<VariableRef> aliveNextStmt = liveRanges.getAlive(stmt);
Collection<VariableRef> definedNextStmt = getDefined(stmt);
initLiveRange(liveRanges, definedNextStmt);
Collection<PreviousStatement> 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<VariableRef> 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<VariableRef> 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<VariableRef> 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<VariableRef> getAliveNotDefined(Statement statement, List<? extends LValue> defined) {
List<VariableRef> 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<? extends LValue> 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<VariableRef> 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<VariableRef> getReferenced(LabelRef labelRef) {
return getReferenced(labelRef, new ArrayList<LabelRef>());
}
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<VariableRef> getUsed(LabelRef labelRef) {
return getUsed(labelRef, new ArrayList<LabelRef>());
}
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<VariableRef> getUsed(LabelRef labelRef, Collection<LabelRef> 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<VariableRef> 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<PreviousStatement> 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<VariableRef> getReferenced(LabelRef labelRef, Collection<LabelRef> 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<VariableRef> 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<PreviousStatement> getPreviousStatements(ControlFlowBlock block, boolean isCaller) {
ArrayList<PreviousStatement> statements = new ArrayList<>();
List<ControlFlowBlock> predecessors = program.getGraph().getPredecessors(block);
for (ControlFlowBlock predecessor : predecessors) {
boolean isSubCaller = block.getLabel().equals(predecessor.getCallSuccessor());
ArrayList<PreviousStatement> 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<VariableRef> 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<PreviousStatement> getLastStatements(ControlFlowBlock block, boolean isCaller) {
ArrayList<PreviousStatement> statements = new ArrayList<>();
List<Statement> 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<VariableRef> 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<VariableRef> getUsed(Statement statement) {
LinkedHashSet<VariableRef> 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<VariableRef> getReferenced(Statement statement) {
LinkedHashSet<VariableRef> 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<VariableRef> 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<VariableRef> 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<PreviousStatement> getPreviousStatements(Statement statement) {
ArrayList<PreviousStatement> 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<Statement> 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<Statement> 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<CallGraph.CallBlock.Call> callers = getProgram().getCallGraph().getCallers(block.getLabel());
for (CallGraph.CallBlock.Call call : callers) {
Statement callStmt = getProgram().getGraph().getStatementByIndex(call.getCallStatementIdx());
Collection<Statement> 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<VariableRef> 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<Statement> getPrecedingStatement(Statement statement) {
Statement previousStmt = null;
Statement prev = null;
ControlFlowBlock block = getProgram().getGraph().getBlockFromStatementIdx(statement.getIndex());
List<Statement> statements = block.getStatements();
for (Statement stmt : statements) {
if (statement.getIndex().equals(stmt.getIndex())) {
previousStmt = prev;
break;
}
prev = stmt;
}
Collection<Statement> 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<Statement> getLastInBlock(ControlFlowBlock block) {
List<Statement> 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<Statement> getLastInPredecessors(ControlFlowBlock block) {
List<ControlFlowBlock> predecessors = getProgram().getGraph().getPredecessors(block);
ArrayList<Statement> last = new ArrayList<>();
for (ControlFlowBlock predecessor : predecessors) {
if (block.getLabel().equals(predecessor.getDefaultSuccessor()) || block.getLabel().equals(predecessor.getConditionalSuccessor())) {
last.addAll(getLastInBlock(predecessor));
}
}
return last;
}
}

View File

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

View File

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

View File

@ -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<PhiTransitions.PhiTransition.PhiAssignment> 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;

View File

@ -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<String> unknownFragments = new LinkedHashSet<>();
List<RegisterUpliftScope> 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<String> 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
* <ul>
* <li>The combination has assigned the same register to variables with overlapping live ranges</li>
* <li>The register combination results in clobbering registers containing values that are still alive</li>
* <li>some ASM fragments are missing </li>
* <li>the ALU register is used in a non-applicable way</li>
* <li>The register combination results in clobbering registers containing values that are still alive</li>
* </ul>
*
* @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<String> 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<VariableRef> alive = liveRangeVariables.getAlive(statement);
LinkedHashSet<Registers.Register> 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;
}
}

View File

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

View File

@ -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<LiveRangeEquivalenceClass> 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 {
}
}
}
}

View File

@ -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<String>(),
@ -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);
}
}

View File

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

View File

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