mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-20 02:32:36 +00:00
Cached phi transition calculations yilding significantly faster compilation of complex programs.
This commit is contained in:
parent
b6ee63ea9f
commit
8925a57955
@ -456,6 +456,7 @@ public class Compiler {
|
||||
new Pass4RegistersFinalize(program).allocate(true);
|
||||
|
||||
// Initial Code generation
|
||||
new Pass4PhiTransitions(program).generate();
|
||||
new Pass4CodeGeneration(program, false).generate();
|
||||
new Pass4AssertNoCpuClobber(program).check();
|
||||
getLog().append("\nINITIAL ASM");
|
||||
@ -499,6 +500,7 @@ public class Compiler {
|
||||
private void pass5GenerateAndOptimizeAsm() {
|
||||
|
||||
// Final ASM code generation before optimization
|
||||
new Pass4PhiTransitions(program).generate();
|
||||
new Pass4CodeGeneration(program, false).generate();
|
||||
new Pass4AssertNoCpuClobber(program).check();
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package dk.camelot64.kickc.model;
|
||||
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -34,12 +34,7 @@ public class PhiTransitions {
|
||||
if(toBlock.hasPhiBlock()) {
|
||||
this.phiBlock = toBlock.getPhiBlock();
|
||||
List<ControlFlowBlock> predecessors = new ArrayList<>(program.getGraph().getPredecessors(toBlock));
|
||||
Collections.sort(predecessors, new Comparator<ControlFlowBlock>() {
|
||||
@Override
|
||||
public int compare(ControlFlowBlock o1, ControlFlowBlock o2) {
|
||||
return o1.getLabel().getFullName().compareTo(o2.getLabel().getFullName());
|
||||
}
|
||||
});
|
||||
predecessors.sort(Comparator.comparing(o -> o.getLabel().getFullName()));
|
||||
for(ControlFlowBlock predecessor : predecessors) {
|
||||
PhiTransition transition = findTransition(predecessor);
|
||||
transitions.put(predecessor, transition);
|
||||
@ -55,7 +50,7 @@ public class PhiTransitions {
|
||||
* @return The transition into the passed block
|
||||
*/
|
||||
private PhiTransition findTransition(ControlFlowBlock fromBlock) {
|
||||
PhiTransition transition = new PhiTransition(fromBlock);
|
||||
PhiTransition transition = new PhiTransition(fromBlock, toBlock, phiBlock, program);
|
||||
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)
|
||||
@ -80,8 +75,7 @@ public class PhiTransitions {
|
||||
* @return The transition from the from-block into the to-block
|
||||
*/
|
||||
public PhiTransition getTransition(ControlFlowBlock fromBlock) {
|
||||
PhiTransition transition = transitions.get(fromBlock);
|
||||
return transition;
|
||||
return transitions.get(fromBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,20 +98,31 @@ public class PhiTransitions {
|
||||
* The transition contains the assignments necessary to enter the to-block from specific from-block(s).
|
||||
* The transition may be shared between multiple from-blocks, if the assignments are identical.
|
||||
*/
|
||||
public class PhiTransition {
|
||||
public static class PhiTransition {
|
||||
|
||||
/** The program used for accessing information. */
|
||||
private Program program;
|
||||
|
||||
/** The block we are entering into. */
|
||||
private ControlFlowBlock toBlock;
|
||||
|
||||
/** The phi statement of the to-block. */
|
||||
private StatementPhiBlock phiBlock;
|
||||
|
||||
/** The block we are entering from. */
|
||||
private List<ControlFlowBlock> fromBlocks;
|
||||
|
||||
/** The assignments when transitioning between the two blocks. */
|
||||
private List<PhiTransition.PhiAssignment> assignments;
|
||||
|
||||
private boolean generated;
|
||||
|
||||
private int nextIdx;
|
||||
|
||||
public PhiTransition(ControlFlowBlock fromBlock) {
|
||||
PhiTransition(ControlFlowBlock fromBlock, ControlFlowBlock toBlock, StatementPhiBlock phiBlock, Program program) {
|
||||
this.program = program;
|
||||
this.toBlock = toBlock;
|
||||
this.phiBlock = phiBlock;
|
||||
this.fromBlocks = new ArrayList<>();
|
||||
this.fromBlocks.add(fromBlock);
|
||||
this.generated = false;
|
||||
this.nextIdx = 0;
|
||||
initAssignments(fromBlock);
|
||||
}
|
||||
@ -129,7 +134,7 @@ public class PhiTransitions {
|
||||
Collections.reverse(phiVariables);
|
||||
for(StatementPhiBlock.PhiVariable phiVariable : phiVariables) {
|
||||
List<StatementPhiBlock.PhiRValue> phiRValues = new ArrayList<>(phiVariable.getValues());
|
||||
Collections.sort(phiRValues, Comparator.comparing(o -> o.getPredecessor().getFullName()));
|
||||
phiRValues.sort(Comparator.comparing(o -> o.getPredecessor().getFullName()));
|
||||
for(StatementPhiBlock.PhiRValue phiRValue : phiRValues) {
|
||||
if(phiRValue.getPredecessor().equals(fromBlock.getLabel())) {
|
||||
this.assignments.add(new PhiTransition.PhiAssignment(phiVariable, phiRValue, nextIdx++));
|
||||
@ -163,15 +168,7 @@ public class PhiTransitions {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
public boolean isGenerated() {
|
||||
return generated;
|
||||
}
|
||||
|
||||
public void setGenerated(boolean generated) {
|
||||
this.generated = generated;
|
||||
}
|
||||
|
||||
public void addFromBlock(ControlFlowBlock fromBlock) {
|
||||
void addFromBlock(ControlFlowBlock fromBlock) {
|
||||
fromBlocks.add(fromBlock);
|
||||
}
|
||||
|
||||
@ -181,7 +178,7 @@ public class PhiTransitions {
|
||||
* @param other The other transition to examine
|
||||
* @return true if the assignments are identical
|
||||
*/
|
||||
public boolean equalAssignments(PhiTransition other) {
|
||||
boolean equalAssignments(PhiTransition other) {
|
||||
List<PhiTransition.PhiAssignment> otherAssignments = other.getAssignments();
|
||||
if(assignments.size() != otherAssignments.size()) {
|
||||
return false;
|
||||
@ -227,7 +224,7 @@ public class PhiTransitions {
|
||||
/** The index of the assignment within the transition. */
|
||||
private int idx;
|
||||
|
||||
public PhiAssignment(StatementPhiBlock.PhiVariable phiVariable, StatementPhiBlock.PhiRValue phiRValue, int idx) {
|
||||
PhiAssignment(StatementPhiBlock.PhiVariable phiVariable, StatementPhiBlock.PhiRValue phiRValue, int idx) {
|
||||
this.phiVariable = phiVariable;
|
||||
this.phiRValue = phiRValue;
|
||||
this.idx = idx;
|
||||
@ -248,8 +245,22 @@ public class PhiTransitions {
|
||||
public int getAssignmentIdx() {
|
||||
return idx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
PhiTransition that = (PhiTransition) o;
|
||||
return Objects.equals(toBlock, that.toBlock) &&
|
||||
Objects.equals(fromBlocks, that.fromBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(toBlock, fromBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,12 +4,14 @@ import dk.camelot64.kickc.CompileLog;
|
||||
import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.model.statements.StatementInfos;
|
||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** A KickC Intermediate Compiler Language (ICL) Program */
|
||||
public class Program {
|
||||
@ -67,9 +69,10 @@ public class Program {
|
||||
private Collection<VariableRef> earlyIdentifiedConstants;
|
||||
/** Reserved ZP addresses that the compiler cannot use. */
|
||||
private List<Number> reservedZps;
|
||||
|
||||
/** Absolute start address of the code. Null to start ad 0x080d. */
|
||||
private Number programPc;
|
||||
/** Cached phi transisitons into each block. */
|
||||
Map<LabelRef, PhiTransitions> phiTransitions;
|
||||
|
||||
public Program() {
|
||||
this.scope = new ProgramScope();
|
||||
@ -80,6 +83,14 @@ public class Program {
|
||||
this.reservedZps = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Map<LabelRef, PhiTransitions> getPhiTransitions() {
|
||||
return phiTransitions;
|
||||
}
|
||||
|
||||
public void setPhiTransitions(Map<LabelRef, PhiTransitions> phiTransitions) {
|
||||
this.phiTransitions = phiTransitions;
|
||||
}
|
||||
|
||||
public List<Comment> getFileComments() {
|
||||
return fileComments;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.asm.AsmSegment;
|
||||
import dk.camelot64.kickc.model.*;
|
||||
import dk.camelot64.kickc.model.statements.StatementSource;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
@ -13,6 +14,7 @@ import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/*** Ensures that no statement clobbers a CPU register used by an alive variable - and that assigning statements clobber the CPU registers they assign to */
|
||||
public class Pass4AssertNoCpuClobber extends Pass2Base {
|
||||
@ -56,6 +58,7 @@ public class Pass4AssertNoCpuClobber extends Pass2Base {
|
||||
* @return true if there is a clobber problem in the program
|
||||
*/
|
||||
public boolean hasClobberProblem(boolean verbose) {
|
||||
|
||||
AsmProgram asm = getProgram().getAsm();
|
||||
boolean clobberProblem = false;
|
||||
for(AsmSegment asmSegment : asm.getSegments()) {
|
||||
@ -71,13 +74,13 @@ public class Pass4AssertNoCpuClobber extends Pass2Base {
|
||||
|
||||
// Find alive variables
|
||||
List<VariableRef> aliveVars = new ArrayList<>(getProgram().getLiveRangeVariablesEffective().getAliveEffective(statement));
|
||||
|
||||
// If the segment is an assignment in a phi transition, examine the later phi transition assignments and update alive variables alive and variables assigned
|
||||
if(asmSegment.getPhiTransitionId() != null && asmSegment.getPhiTransitionAssignmentIdx() != null) {
|
||||
String phiTransitionId = asmSegment.getPhiTransitionId();
|
||||
int transitionAssignmentIdx = asmSegment.getPhiTransitionAssignmentIdx();
|
||||
ControlFlowBlock statementBlock = getProgram().getStatementInfos().getBlock(statementIdx);
|
||||
PhiTransitions phiTransitions = new PhiTransitions(getProgram(), statementBlock);
|
||||
Map<LabelRef, PhiTransitions> programPhiTransitions = getProgram().getPhiTransitions();
|
||||
PhiTransitions phiTransitions = programPhiTransitions.get(statementBlock.getLabel());
|
||||
PhiTransitions.PhiTransition phiTransition = phiTransitions.getTransition(phiTransitionId);
|
||||
for(PhiTransitions.PhiTransition.PhiAssignment phiAssignment : phiTransition.getAssignments()) {
|
||||
if(phiAssignment.getAssignmentIdx() > transitionAssignmentIdx) {
|
||||
|
@ -27,7 +27,24 @@ public class Pass4CodeGeneration {
|
||||
* Used to ensure that duplicate transitions are only code generated once.
|
||||
* Maps to-blocks to the transition information for the block
|
||||
*/
|
||||
private Map<ControlFlowBlock, PhiTransitions> blockTransitions = new LinkedHashMap<>();
|
||||
private Map<PhiTransitions.PhiTransition, Boolean> transitionsGenerated = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Determines if a phi-transition has already been code-generated
|
||||
* @param transition The transition to examine
|
||||
* @return true if it has already been generated
|
||||
*/
|
||||
private boolean transitionIsGenerated(PhiTransitions.PhiTransition transition) {
|
||||
return Boolean.TRUE.equals(transitionsGenerated.get(transition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a Phi transition as generated
|
||||
* @param transition The transition
|
||||
*/
|
||||
private void transitionSetGenerated(PhiTransitions.PhiTransition transition) {
|
||||
transitionsGenerated.put(transition, Boolean.TRUE);
|
||||
}
|
||||
|
||||
public Pass4CodeGeneration(Program program, boolean verboseAliveInfo) {
|
||||
this.program = program;
|
||||
@ -109,7 +126,7 @@ public class Pass4CodeGeneration {
|
||||
if(defaultSuccessor != null) {
|
||||
if(defaultSuccessor.hasPhiBlock()) {
|
||||
PhiTransitions.PhiTransition transition = getTransitions(defaultSuccessor).getTransition(block);
|
||||
if(!transition.isGenerated()) {
|
||||
if(!transitionIsGenerated(transition)) {
|
||||
genBlockPhiTransition(asm, block, defaultSuccessor, defaultSuccessor.getScope());
|
||||
String label = defaultSuccessor.getLabel().getLocalName().replace('@', 'b').replace(':', '_');
|
||||
asm.addInstruction("JMP", AsmAddressingMode.ABS, label, false);
|
||||
@ -562,7 +579,7 @@ public class Pass4CodeGeneration {
|
||||
ControlFlowBlock callSuccessor = getGraph().getCallSuccessor(block);
|
||||
if(callSuccessor != null && callSuccessor.hasPhiBlock()) {
|
||||
PhiTransitions.PhiTransition transition = getTransitions(callSuccessor).getTransition(block);
|
||||
if(transition.isGenerated()) {
|
||||
if(transitionIsGenerated(transition)) {
|
||||
throw new RuntimeException("Error! JSR transition already generated. Must modify PhiTransitions code to ensure this does not happen.");
|
||||
}
|
||||
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
|
||||
@ -780,7 +797,7 @@ public class Pass4CodeGeneration {
|
||||
PhiTransitions transitions = getTransitions(toBlock);
|
||||
for(ControlFlowBlock fromBlock : transitions.getFromBlocks()) {
|
||||
PhiTransitions.PhiTransition transition = transitions.getTransition(fromBlock);
|
||||
if(!transition.isGenerated() && toBlock.getLabel().equals(fromBlock.getConditionalSuccessor())) {
|
||||
if(!transitionIsGenerated(transition) && toBlock.getLabel().equals(fromBlock.getConditionalSuccessor())) {
|
||||
genBlockPhiTransition(asm, fromBlock, toBlock, toBlock.getScope());
|
||||
asm.addInstruction("JMP", AsmAddressingMode.ABS, toBlock.getLabel().getLocalName().replace('@', 'b').replace(':', '_'), false);
|
||||
}
|
||||
@ -802,7 +819,7 @@ public class Pass4CodeGeneration {
|
||||
private void genBlockPhiTransition(AsmProgram asm, ControlFlowBlock fromBlock, ControlFlowBlock toBlock, ScopeRef scope) {
|
||||
PhiTransitions transitions = getTransitions(toBlock);
|
||||
PhiTransitions.PhiTransition transition = transitions.getTransition(fromBlock);
|
||||
if(!transition.isGenerated()) {
|
||||
if(!transitionIsGenerated(transition)) {
|
||||
Statement toFirstStatement = toBlock.getStatements().get(0);
|
||||
String segmentSrc = "[" + toFirstStatement.getIndex() + "] phi from ";
|
||||
for(ControlFlowBlock fBlock : transition.getFromBlocks()) {
|
||||
@ -830,7 +847,7 @@ public class Pass4CodeGeneration {
|
||||
generateAsm(asm, asmFragmentInstanceSpecFactory);
|
||||
}
|
||||
}
|
||||
transition.setGenerated(true);
|
||||
transitionSetGenerated(transition);
|
||||
} else {
|
||||
program.getLog().append("Already generated transition from " + fromBlock.getLabel() + " to " + toBlock.getLabel() + " - not generating it again!");
|
||||
}
|
||||
@ -843,12 +860,7 @@ public class Pass4CodeGeneration {
|
||||
* @return The transitions into the block
|
||||
*/
|
||||
private PhiTransitions getTransitions(ControlFlowBlock toBlock) {
|
||||
PhiTransitions transitions = this.blockTransitions.get(toBlock);
|
||||
if(transitions == null) {
|
||||
transitions = new PhiTransitions(program, toBlock);
|
||||
this.blockTransitions.put(toBlock, transitions);
|
||||
}
|
||||
return transitions;
|
||||
return program.getPhiTransitions().get(toBlock.getLabel());
|
||||
}
|
||||
|
||||
private Registers.Register getRegister(RValue rValue) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.PhiTransitions;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Generate phi-transitions for all phi-block transitions
|
||||
*/
|
||||
public class Pass4PhiTransitions extends Pass2Base {
|
||||
|
||||
public Pass4PhiTransitions(Program program) {
|
||||
super(program);
|
||||
}
|
||||
|
||||
public void generate() {
|
||||
LinkedHashMap<LabelRef, PhiTransitions> phiTransitions = new LinkedHashMap<>();
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
PhiTransitions blockTransitions = new PhiTransitions(getProgram(), block);
|
||||
phiTransitions.put(block.getLabel(), blockTransitions);
|
||||
}
|
||||
getProgram().setPhiTransitions(phiTransitions);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -110,6 +110,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base {
|
||||
}
|
||||
// Generate ASM
|
||||
try {
|
||||
new Pass4PhiTransitions(program).generate();
|
||||
new Pass4CodeGeneration(program, false).generate();
|
||||
} catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
|
||||
unknownFragments.add(e.getFragmentSignature());
|
||||
|
Loading…
Reference in New Issue
Block a user