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:
parent
be212f3b38
commit
709cc57a1a
@ -16,13 +16,13 @@ public class Compiler {
|
||||
public static class CompilationResult {
|
||||
private AsmProgram asmProgram;
|
||||
private ControlFlowGraph graph;
|
||||
private ProgramScope symbols;
|
||||
private ProgramScope scope;
|
||||
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.graph = graph;
|
||||
this.symbols = symbols;
|
||||
this.scope = scope;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ public class Compiler {
|
||||
return graph;
|
||||
}
|
||||
|
||||
public ProgramScope getSymbols() {
|
||||
return symbols;
|
||||
public ProgramScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public CompileLog getLog() {
|
||||
@ -49,7 +49,7 @@ public class Compiler {
|
||||
KickCParser.FileContext file = pass0ParseInput(input, log);
|
||||
Program program = pass1GenerateSSA(file, log);
|
||||
pass2OptimizeSSA(program, log);
|
||||
pass3IntervalAnalysis(program, log);
|
||||
pass3LiveRangeAnalysis(program, log);
|
||||
AsmProgram asmProgram = pass4GenerateAsm(program, 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.plan();
|
||||
@ -110,8 +110,8 @@ public class Compiler {
|
||||
log.append(program.getGraph().toString(program.getScope()));
|
||||
pass2AssertSSA(program, log);
|
||||
|
||||
Pass3IdentifyAliveRanges pass3IdentifyAliveRanges = new Pass3IdentifyAliveRanges(program, log);
|
||||
pass3IdentifyAliveRanges.findLiveRanges();
|
||||
Pass3IdentifyLiveRanges pass3IdentifyLiveRanges = new Pass3IdentifyLiveRanges(program, log);
|
||||
pass3IdentifyLiveRanges.findLiveRanges();
|
||||
log.append("CONTROL FLOW GRAPH - LIVE RANGES");
|
||||
log.append(program.getGraph().toString(program.getScope()));
|
||||
pass2AssertSSA(program, log);
|
||||
@ -121,11 +121,14 @@ public class Compiler {
|
||||
Pass2CullEmptyBlocks cullEmptyBlocks = new Pass2CullEmptyBlocks(program, log);
|
||||
cullEmptyBlocks.optimize();
|
||||
pass3BlockSequencePlanner.plan();
|
||||
pass3IdentifyAliveRanges.findLiveRanges();
|
||||
pass3IdentifyLiveRanges.findLiveRanges();
|
||||
log.append("CONTROL FLOW GRAPH - PHI MEM COALESCED");
|
||||
log.append(program.getGraph().toString(program.getScope()));
|
||||
pass2AssertSSA(program, log);
|
||||
|
||||
Pass3LoopAnalysis pass3LoopAnalysis = new Pass3LoopAnalysis(program, log);
|
||||
pass3LoopAnalysis.detectLoops();
|
||||
|
||||
}
|
||||
|
||||
public void pass2OptimizeSSA(Program program, CompileLog log) {
|
||||
|
@ -17,13 +17,10 @@ Features
|
||||
+ Add ++/-- incrementing/decrementing operators.
|
||||
|
||||
Register Allocation
|
||||
- Matrix Phi operation (instead of separate statements)
|
||||
- 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 by finding optimal sequence for multiple phi assignments in entry-segments.
|
||||
- Phi Lifting
|
||||
- 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.
|
||||
- ComputeRegisterPreference(v), ComputeWeight(v)
|
||||
- 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?)
|
||||
- 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.
|
||||
+ 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
|
||||
- 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
|
||||
- Improve error messages to give better context
|
||||
- Offer to compile resulting ASM with KickAssembler
|
||||
+ 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
|
||||
- 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.
|
||||
|
@ -66,7 +66,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 (Pass3IdentifyAliveRanges.generateStatementIndexes).");
|
||||
throw new RuntimeException("Statement index not defined! Live Ranges only work after defining statement indexes (Pass3IdentifyLiveRanges.generateStatementIndexes).");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
@ -11,12 +11,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Pass3IdentifyAliveRanges {
|
||||
public class Pass3IdentifyLiveRanges {
|
||||
|
||||
private final Program program;
|
||||
private final CompileLog log;
|
||||
|
||||
public Pass3IdentifyAliveRanges(Program program, CompileLog log) {
|
||||
public Pass3IdentifyLiveRanges(Program program, CompileLog log) {
|
||||
this.program = program;
|
||||
this.log = log;
|
||||
}
|
222
src/dk/camelot64/kickc/passes/Pass3LoopAnalysis.java
Normal file
222
src/dk/camelot64/kickc/passes/Pass3LoopAnalysis.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -29,6 +29,7 @@ public class Pass4ZeroPageAllocationLiveRange {
|
||||
for (LiveRangeEquivalenceClass liveRangeEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
|
||||
log.append(liveRangeEquivalenceClass.toString());
|
||||
}
|
||||
|
||||
// Coalesce over copy assignments
|
||||
EquivalenceClassCopyCoalescer equivalenceClassCopyCoalescer = new EquivalenceClassCopyCoalescer(liveRangeEquivalenceClassSet);
|
||||
equivalenceClassCopyCoalescer.visitGraph(program.getGraph());
|
||||
|
@ -6,15 +6,7 @@ import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
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
|
||||
@ -64,8 +56,8 @@ public class TestCompilationOutput extends TestCase {
|
||||
Compiler.CompilationResult output = compiler.compile(input);
|
||||
boolean success = true;
|
||||
success &= helper.testOutput(fileName, ".asm", output.getAsmProgram().toString(false));
|
||||
success &= helper.testOutput(fileName, ".sym", output.getSymbols().getSymbolTableContents());
|
||||
success &= helper.testOutput(fileName, ".cfg", output.getGraph().toString(output.getSymbols()));
|
||||
success &= helper.testOutput(fileName, ".sym", output.getScope().getSymbolTableContents());
|
||||
success &= helper.testOutput(fileName, ".cfg", output.getGraph().toString(output.getScope()));
|
||||
success &= helper.testOutput(fileName, ".log", output.getLog().toString());
|
||||
if (!success) {
|
||||
fail("Output does not match reference!");
|
||||
|
Loading…
x
Reference in New Issue
Block a user