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 {
|
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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
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()) {
|
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());
|
||||||
|
@ -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!");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user