1
0
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:
jespergravgaard 2019-05-31 18:20:03 +02:00
parent b6ee63ea9f
commit 8925a57955
7 changed files with 112 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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