1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-01-10 12:31:09 +00:00

Implemented graph dominator analysis.

This commit is contained in:
jespergravgaard 2017-07-27 15:23:46 +02:00
parent be212f3b38
commit 709cc57a1a
7 changed files with 245 additions and 27 deletions

View File

@ -16,13 +16,13 @@ public class Compiler {
public static class CompilationResult { public static class CompilationResult {
private AsmProgram asmProgram; private AsmProgram asmProgram;
private ControlFlowGraph graph; private ControlFlowGraph graph;
private ProgramScope symbols; private ProgramScope scope;
private CompileLog log; private CompileLog log;
public CompilationResult(AsmProgram asmProgram, ControlFlowGraph graph, ProgramScope symbols, CompileLog log) { public CompilationResult(AsmProgram asmProgram, ControlFlowGraph graph, ProgramScope scope, CompileLog log) {
this.asmProgram = asmProgram; this.asmProgram = asmProgram;
this.graph = graph; this.graph = graph;
this.symbols = symbols; this.scope = scope;
this.log = log; this.log = log;
} }
@ -34,8 +34,8 @@ public class Compiler {
return graph; return graph;
} }
public ProgramScope getSymbols() { public ProgramScope getScope() {
return symbols; return scope;
} }
public CompileLog getLog() { public CompileLog getLog() {
@ -49,7 +49,7 @@ public class Compiler {
KickCParser.FileContext file = pass0ParseInput(input, log); KickCParser.FileContext file = pass0ParseInput(input, log);
Program program = pass1GenerateSSA(file, log); Program program = pass1GenerateSSA(file, log);
pass2OptimizeSSA(program, log); pass2OptimizeSSA(program, log);
pass3IntervalAnalysis(program, log); pass3LiveRangeAnalysis(program, log);
AsmProgram asmProgram = pass4GenerateAsm(program, log); AsmProgram asmProgram = pass4GenerateAsm(program, log);
pass5OptimizeAsm(asmProgram, log); pass5OptimizeAsm(asmProgram, log);
@ -98,7 +98,7 @@ public class Compiler {
} }
private void pass3IntervalAnalysis(Program program, CompileLog log) { private void pass3LiveRangeAnalysis(Program program, CompileLog log) {
Pass3BlockSequencePlanner pass3BlockSequencePlanner = new Pass3BlockSequencePlanner(program, log); Pass3BlockSequencePlanner pass3BlockSequencePlanner = new Pass3BlockSequencePlanner(program, log);
pass3BlockSequencePlanner.plan(); pass3BlockSequencePlanner.plan();
@ -110,8 +110,8 @@ public class Compiler {
log.append(program.getGraph().toString(program.getScope())); log.append(program.getGraph().toString(program.getScope()));
pass2AssertSSA(program, log); pass2AssertSSA(program, log);
Pass3IdentifyAliveRanges pass3IdentifyAliveRanges = new Pass3IdentifyAliveRanges(program, log); Pass3IdentifyLiveRanges pass3IdentifyLiveRanges = new Pass3IdentifyLiveRanges(program, log);
pass3IdentifyAliveRanges.findLiveRanges(); pass3IdentifyLiveRanges.findLiveRanges();
log.append("CONTROL FLOW GRAPH - LIVE RANGES"); log.append("CONTROL FLOW GRAPH - LIVE RANGES");
log.append(program.getGraph().toString(program.getScope())); log.append(program.getGraph().toString(program.getScope()));
pass2AssertSSA(program, log); pass2AssertSSA(program, log);
@ -121,11 +121,14 @@ public class Compiler {
Pass2CullEmptyBlocks cullEmptyBlocks = new Pass2CullEmptyBlocks(program, log); Pass2CullEmptyBlocks cullEmptyBlocks = new Pass2CullEmptyBlocks(program, log);
cullEmptyBlocks.optimize(); cullEmptyBlocks.optimize();
pass3BlockSequencePlanner.plan(); pass3BlockSequencePlanner.plan();
pass3IdentifyAliveRanges.findLiveRanges(); pass3IdentifyLiveRanges.findLiveRanges();
log.append("CONTROL FLOW GRAPH - PHI MEM COALESCED"); log.append("CONTROL FLOW GRAPH - PHI MEM COALESCED");
log.append(program.getGraph().toString(program.getScope())); log.append(program.getGraph().toString(program.getScope()));
pass2AssertSSA(program, log); pass2AssertSSA(program, log);
Pass3LoopAnalysis pass3LoopAnalysis = new Pass3LoopAnalysis(program, log);
pass3LoopAnalysis.detectLoops();
} }
public void pass2OptimizeSSA(Program program, CompileLog log) { public void pass2OptimizeSSA(Program program, CompileLog log) {

View File

@ -17,13 +17,10 @@ Features
+ Add ++/-- incrementing/decrementing operators. + Add ++/-- incrementing/decrementing operators.
Register Allocation Register Allocation
- Matrix Phi operation (instead of separate statements)
- Safe-Copy based SSA deconstruction - Safe-Copy based SSA deconstruction
- Optimize phi transitions by ensuring that identical phi-transitions with regards to register allocation are collected into a single transition. - Optimize phi transitions by ensuring that identical phi-transitions with regards to register allocation are collected into a single transition.
- Optimize by finding optimal sequence for multiple phi assignments in entry-segments. - Optimize by finding optimal sequence for multiple phi assignments in entry-segments.
- Phi Lifting
- Interval Analysis (variable liveness intervals) - Interval Analysis (variable liveness intervals)
- PhiLifting & PhiMemCoalesce (http://compilers.cs.ucla.edu/fernando/projects/soc/reports/short_tech.pdf)
- ComputeLoopNestDepth(b) - Assign loop nesting levels to blocks. - ComputeLoopNestDepth(b) - Assign loop nesting levels to blocks.
- ComputeRegisterPreference(v), ComputeWeight(v) - ComputeRegisterPreference(v), ComputeWeight(v)
- CalculateClobbering(i) - CalculateClobbering(i)
@ -32,13 +29,16 @@ Register Allocation
- Maybe register preference should also incorporate the types of operations that can be effectively performed with the register? (Maybe based on fragment cost?) - Maybe register preference should also incorporate the types of operations that can be effectively performed with the register? (Maybe based on fragment cost?)
- Implement a register allocation (coloring) algorithm using by liveness intervals, preferences, weights & clobbering information. - Implement a register allocation (coloring) algorithm using by liveness intervals, preferences, weights & clobbering information.
- Optimize register allocation by combining with knowledge of ASM program cost (bytes/cycles) and different ASM fragments with different clobbering. - Optimize register allocation by combining with knowledge of ASM program cost (bytes/cycles) and different ASM fragments with different clobbering.
+ Matrix Phi operation (instead of separate statements)
+ Phi Lifting
+ PhiLifting & PhiMemCoalesce (http://compilers.cs.ucla.edu/fernando/projects/soc/reports/short_tech.pdf)
Process/Code Structure Improvement Process/Code Structure Improvement
- Make each phase return a separate object graph (allowing for keeeping the history in memory & performing rollbacks)
- Refactor Expression Operator Implementation & Evaluation into one class per operator - Refactor Expression Operator Implementation & Evaluation into one class per operator
- Improve error messages to give better context - Improve error messages to give better context
- Offer to compile resulting ASM with KickAssembler - Offer to compile resulting ASM with KickAssembler
+ Implemenent Assertions for the output of different phases (ensuring that the result of the phase is consistent) + Implemenent Assertions for the output of different phases (ensuring that the result of the phase is consistent)
+ Make each phase return a separate object graph (allowing for keeeping the history in memory & performing rollbacks)
Testing Testing
- Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison. - Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison.

View File

@ -66,7 +66,7 @@ public class LiveRange {
private Integer getIndex(Statement statement) { private Integer getIndex(Statement statement) {
Integer index = statement.getIndex(); Integer index = statement.getIndex();
if (index == null) { if (index == null) {
throw new RuntimeException("Statement index not defined! Live Ranges only work after defining statement indexes (Pass3IdentifyAliveRanges.generateStatementIndexes)."); throw new RuntimeException("Statement index not defined! Live Ranges only work after defining statement indexes (Pass3IdentifyLiveRanges.generateStatementIndexes).");
} }
return index; return index;
} }

View File

@ -11,12 +11,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class Pass3IdentifyAliveRanges { public class Pass3IdentifyLiveRanges {
private final Program program; private final Program program;
private final CompileLog log; private final CompileLog log;
public Pass3IdentifyAliveRanges(Program program, CompileLog log) { public Pass3IdentifyLiveRanges(Program program, CompileLog log) {
this.program = program; this.program = program;
this.log = log; this.log = log;
} }

View File

@ -0,0 +1,222 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.icl.ControlFlowBlock;
import dk.camelot64.kickc.icl.LabelRef;
import dk.camelot64.kickc.icl.Program;
import java.util.*;
/**
* Finds loops and nested loops in the control flow graph
*/
public class Pass3LoopAnalysis {
private Program program;
private CompileLog log;
public Pass3LoopAnalysis(Program program, CompileLog log) {
this.program = program;
this.log = log;
}
public Program getProgram() {
return program;
}
public CompileLog getLog() {
return log;
}
public void detectLoops() {
GraphDominators graphDominators = analyseDominators();
}
/**
* Analyse the control flow graph to find dominators for all blocks.
* <p>
* Definition: d dom i if all paths from entry to node i include d
* <p>
* See http://www.cs.colostate.edu/~cs553/ClassNotes/lecture09-control-dominators.ppt.pdf
*
* @return The graph dominators
*/
private GraphDominators analyseDominators() {
GraphDominators graphDominators = new GraphDominators();
// Initialize dominators: Dom[first]={first}, Dom[block]={all}
LabelRef firstBlock = program.getGraph().getFirstBlock().getLabel();
BlockDominators firstDominators = graphDominators.addDominators(firstBlock);
firstDominators.add(firstBlock);
List<LabelRef> allBlocks = new ArrayList<>();
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
allBlocks.add(block.getLabel());
}
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
if (!block.getLabel().equals(firstBlock)) {
BlockDominators blockDominators = graphDominators.addDominators(block.getLabel());
blockDominators.addAll(allBlocks);
}
}
// Iteratively refine dominators until they do not change
// For all nodes:
// Dom[n] = {n} UNION ( INTERSECT Dom[p] for all p that are predecessors of n)
boolean change = false;
do {
change = false;
for (ControlFlowBlock block : program.getGraph().getAllBlocks()) {
if (!block.getLabel().equals(firstBlock)) {
List<ControlFlowBlock> predecessors = program.getGraph().getPredecessors(block);
BlockDominators newDominators = new BlockDominators();
newDominators.addAll(allBlocks);
for (ControlFlowBlock predecessor : predecessors) {
BlockDominators predecessorDominators = graphDominators.getDominators(predecessor.getLabel());
newDominators.intersect(predecessorDominators);
}
newDominators.add(block.getLabel());
BlockDominators currentDominators = graphDominators.getDominators(block.getLabel());
if (!currentDominators.equals(newDominators)) {
change = true;
graphDominators.setDominators(block.getLabel(), newDominators);
}
}
}
} while (change);
return graphDominators;
}
public static class GraphDominators {
/**
* Maps block label to the dominators of the block.
*/
Map<LabelRef, BlockDominators> blockDominators;
public GraphDominators() {
this.blockDominators = new LinkedHashMap<>();
}
/**
* Get the dominators for a specific block.
*
* @param block Label of the block
* @return The block dominators
*/
public BlockDominators getDominators(LabelRef block) {
return blockDominators.get(block);
}
/**
* Creates dominators for a specific block
*
* @param block Label of the block
* @return The newly created block dominators
*/
public BlockDominators addDominators(LabelRef block) {
BlockDominators blockDominators = new BlockDominators();
this.blockDominators.put(block, blockDominators);
return blockDominators;
}
/**
* Set the dominators for a specific block
*
* @param block The block
* @param dominators The new dominators
*/
public void setDominators(LabelRef block, BlockDominators dominators) {
blockDominators.put(block, dominators);
}
}
/**
* The Dominators for a specific block.
*/
public static class BlockDominators {
/**
* Set containing the labels of all blocks that are dominators of the block.
*/
Set<LabelRef> dominators;
public BlockDominators() {
this.dominators = new HashSet<>();
}
/**
* Add a single dominator
*
* @param dominator The dominator to add
*/
public void add(LabelRef dominator) {
dominators.add(dominator);
}
/**
* Adds a bunch of dominators
*
* @param dominators The dominators to add
*/
public void addAll(Collection<LabelRef> dominators) {
for (LabelRef dominator : dominators) {
add(dominator);
}
}
/**
* Modifies this set of dominators to be the intersection between this set and the passed set.
* Effectively removes all labels from this set that is not also present in the passed set.
*
* @param other The dominator set to intersect with
*/
public void intersect(BlockDominators other) {
Iterator<LabelRef> iterator = dominators.iterator();
while (iterator.hasNext()) {
LabelRef dominator = iterator.next();
if (!other.contains(dominator)) {
iterator.remove();
}
}
}
/**
* Determines if the dominator set contains a specific block
*
* @param block The block to look for
* @return true if the dominator set contains the block
*/
private boolean contains(LabelRef block) {
return dominators.contains(block);
}
public Set<LabelRef> getDominators() {
return dominators;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlockDominators that = (BlockDominators) o;
return dominators != null ? dominators.equals(that.dominators) : that.dominators == null;
}
@Override
public int hashCode() {
return dominators != null ? dominators.hashCode() : 0;
}
}
}

View File

@ -29,6 +29,7 @@ public class Pass4ZeroPageAllocationLiveRange {
for (LiveRangeEquivalenceClass liveRangeEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) { for (LiveRangeEquivalenceClass liveRangeEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
log.append(liveRangeEquivalenceClass.toString()); log.append(liveRangeEquivalenceClass.toString());
} }
// Coalesce over copy assignments // Coalesce over copy assignments
EquivalenceClassCopyCoalescer equivalenceClassCopyCoalescer = new EquivalenceClassCopyCoalescer(liveRangeEquivalenceClassSet); EquivalenceClassCopyCoalescer equivalenceClassCopyCoalescer = new EquivalenceClassCopyCoalescer(liveRangeEquivalenceClassSet);
equivalenceClassCopyCoalescer.visitGraph(program.getGraph()); equivalenceClassCopyCoalescer.visitGraph(program.getGraph());

View File

@ -6,15 +6,7 @@ import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CharStreams;
import java.io.*; import java.io.*;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/** /**
* Compile a number of source files and compare the resulting assembler with expected output * Compile a number of source files and compare the resulting assembler with expected output
@ -64,8 +56,8 @@ public class TestCompilationOutput extends TestCase {
Compiler.CompilationResult output = compiler.compile(input); Compiler.CompilationResult output = compiler.compile(input);
boolean success = true; boolean success = true;
success &= helper.testOutput(fileName, ".asm", output.getAsmProgram().toString(false)); success &= helper.testOutput(fileName, ".asm", output.getAsmProgram().toString(false));
success &= helper.testOutput(fileName, ".sym", output.getSymbols().getSymbolTableContents()); success &= helper.testOutput(fileName, ".sym", output.getScope().getSymbolTableContents());
success &= helper.testOutput(fileName, ".cfg", output.getGraph().toString(output.getSymbols())); success &= helper.testOutput(fileName, ".cfg", output.getGraph().toString(output.getScope()));
success &= helper.testOutput(fileName, ".log", output.getLog().toString()); success &= helper.testOutput(fileName, ".log", output.getLog().toString());
if (!success) { if (!success) {
fail("Output does not match reference!"); fail("Output does not match reference!");