rework DataFlowGraph

This commit is contained in:
ChUrl
2021-01-31 15:55:54 +01:00
parent 6aa3976468
commit c4382aae38
5 changed files with 163 additions and 117 deletions

View File

@ -3,7 +3,6 @@ package codegen.analysis.dataflow;
import codegen.flowgraph.FlowBasicBlock;
import codegen.flowgraph.FlowGraph;
import codegen.flowgraph.FlowInstruction;
import util.Logger;
import java.io.IOException;
import java.nio.file.Files;
@ -13,73 +12,86 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Die Instruktionen repräsentiert durch einen Graphen.
*/
public final class DataFlowGraph implements Iterable<DataFlowNode> {
// TODO: Why use list, its a graph
private final List<DataFlowNode> graph;
// List for easy indexing
private final List<DataFlowNode> dataFlowNodes;
private DataFlowGraph(List<DataFlowNode> graph) {
this.graph = Collections.unmodifiableList(graph);
Logger.call(this::printToImage);
private DataFlowGraph(List<DataFlowNode> dataFlowNodes) {
this.dataFlowNodes = Collections.unmodifiableList(dataFlowNodes);
}
public static DataFlowGraph fromSourceGraph(FlowGraph srcGraph) {
final List<DataFlowNode> graph = new LinkedList<>();
public static DataFlowGraph fromFlowGraph(FlowGraph flowGraph) {
final List<DataFlowNode> dataFlowNodes = new LinkedList<>();
for (FlowBasicBlock block : srcGraph) {
for (FlowInstruction inst : block) {
graph.add(DataFlowNode.fromFlowNode(inst, block));
// Initialize all DataFlowNodes
for (FlowBasicBlock basicBlock : flowGraph) {
for (FlowInstruction instruction : basicBlock) {
dataFlowNodes.add(DataFlowNode.fromFlowNode(instruction));
}
}
initSuccPred(srcGraph, graph);
final DataFlowGraph dataFlowGraph = new DataFlowGraph(dataFlowNodes);
initNodePosition(flowGraph, dataFlowGraph);
return new DataFlowGraph(graph);
return dataFlowGraph;
}
private static void initSuccPred(FlowGraph srcGraph, List<DataFlowNode> graph) {
for (FlowBasicBlock block : srcGraph) {
for (FlowInstruction inst : block) {
final DataFlowNode current = getNodeByInstruction(inst, graph);
/**
* Jeder {@link DataFlowNode} im {@link DataFlowGraph} wird anhand des {@link FlowGraph} positioniert.
* Dabei werden für den Node die Predecessors und Successors gesetzt.
*/
private static void initNodePosition(FlowGraph flowGraph, DataFlowGraph dataFlowGraph) {
for (FlowBasicBlock basicBlock : flowGraph) {
for (FlowInstruction instruction : basicBlock) {
for (FlowInstruction pred : block.getPredecessors(inst)) {
final DataFlowNode currentPred = getNodeByInstruction(pred, graph);
final Optional<DataFlowNode> currentNode = getNodeByInstructionId(instruction, dataFlowGraph);
current.addPredecessor(currentPred);
currentPred.addSuccessor(current);
if (currentNode.isEmpty()) {
continue;
}
for (FlowInstruction succ : block.getSuccessors(inst)) {
final DataFlowNode currentSucc = getNodeByInstruction(succ, graph);
for (FlowInstruction predecessor : basicBlock.getInstructionPredecessorSet(instruction)) {
final Optional<DataFlowNode> currentPredecessor = getNodeByInstructionId(predecessor, dataFlowGraph);
currentPredecessor.ifPresent(dataFlowNode -> currentNode.get().addPredecessor(dataFlowNode));
}
Logger.log("INST: " + current.getInst() + ", SUCC: " + currentSucc.getInst());
current.addSuccessor(currentSucc);
currentSucc.addPredecessor(current);
for (FlowInstruction successor : basicBlock.getInstructionSuccessorSet(instruction)) {
final Optional<DataFlowNode> currentSuccessor = getNodeByInstructionId(successor, dataFlowGraph);
currentSuccessor.ifPresent(dataFlowNode -> currentNode.get().addSuccessor(dataFlowNode));
}
}
}
}
private static DataFlowNode getNodeByInstruction(FlowInstruction inst, List<DataFlowNode> graph) {
return graph.stream()
.filter(node -> node.getId().equals(inst.getId()))
.findFirst()
.orElse(null);
private static Optional<DataFlowNode> getNodeByInstructionId(FlowInstruction instruction, DataFlowGraph dataFlowGraph) {
return dataFlowGraph.stream()
.filter(node -> node.getId().equals(instruction.getId()))
.findFirst();
}
public int indexOf(DataFlowNode node) {
return this.graph.indexOf(node);
return this.dataFlowNodes.indexOf(node);
}
public int size() {
return this.graph.size();
return this.dataFlowNodes.size();
}
private Stream<DataFlowNode> stream() {
return this.dataFlowNodes.stream();
}
// Printing
public String printToImage() {
if (this.graph.isEmpty()) {
if (this.dataFlowNodes.isEmpty()) {
return "Empty Graph";
}
@ -88,10 +100,10 @@ public final class DataFlowGraph implements Iterable<DataFlowNode> {
dot.append("digraph dfd {\n")
.append("node[shape=Mrecord]\n");
for (DataFlowNode node : this.graph) {
for (DataFlowNode node : this.dataFlowNodes) {
dot.append(node.getId())
.append(" [label=\"{<f0> ")
.append(this.graph.indexOf(node))
.append(this.dataFlowNodes.indexOf(node))
.append("|<f1> ")
.append(node.getInst())
.append("}\"];\n");
@ -100,26 +112,13 @@ public final class DataFlowGraph implements Iterable<DataFlowNode> {
dot.append("START[label=\"START\"];\n")
.append("END[label=\"END\"];\n");
dot.append("START -> ").append(this.graph.get(0).getId()).append(";\n");
dot.append(this.graph.get(this.graph.size() - 1).getId()).append(" -> END;\n");
dot.append("START -> ").append(this.dataFlowNodes.get(0).getId()).append(";\n");
dot.append(this.dataFlowNodes.get(this.dataFlowNodes.size() - 1).getId()).append(" -> END;\n");
for (DataFlowNode node : this.graph) {
// Successors
for (DataFlowNode succ : node.getSuccessors()) {
if (!dot.toString().contains(node.getId() + " -> " + succ.getId())) {
// No duplicate arrows
for (DataFlowNode node : this.dataFlowNodes) {
for (DataFlowNode successor : node.getSuccessorSet()) {
dot.append(node.getId()).append(" -> ").append(succ.getId()).append(";\n");
}
}
// Predecessors
for (DataFlowNode pred : node.getPredecessors()) {
if (!dot.toString().contains(pred.getId() + " -> " + node.getId())) {
// No duplicates
dot.append(pred.getId()).append(" -> ").append(node.getId()).append(";\n");
}
dot.append(node.getId()).append(" -> ").append(successor.getId()).append(";\n");
}
}
@ -148,6 +147,6 @@ public final class DataFlowGraph implements Iterable<DataFlowNode> {
@Override
public Iterator<DataFlowNode> iterator() {
return this.graph.iterator();
return this.dataFlowNodes.iterator();
}
}

View File

@ -1,26 +1,53 @@
package codegen.analysis.dataflow;
import codegen.flowgraph.FlowBasicBlock;
import codegen.flowgraph.FlowInstruction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Die Datenflussrepräsentation einer Instruktion.
*/
public final class DataFlowNode {
// General graph structure information
private final String id;
private final String blockId;
private final String inst;
private final String use;
private final String def;
private final Set<String> in;
private final Set<String> out;
private final Set<DataFlowNode> predecessors;
private final Set<DataFlowNode> successors;
private DataFlowNode(String id, String blockId, String inst, String use, String def) {
/**
* Die Instruction, welche auch die zugehörige {@link FlowInstruction} enthält.
*/
private final String inst;
/**
* Die Variable, die von diesem Block verwendet wird.
* Da wir keinen 3-Address-Code, sondern Jasmin-Assembler haben, ist das maximal eine.
*/
private final String use;
/**
* Die Variable, die von diesem Block definiert wird.
* Da wir keinen 3-Address-Code, sondern Jasmin-Assembler haben, ist das maximal eine.
*/
private final String def;
/**
* Alle Variablen, welche live in diesem Node ankommen.
*/
private final Set<String> in;
/**
* Alle Variablen , welche diesen Node live verlassen.
*/
private final Set<String> out;
private DataFlowNode(String id, String inst, String use, String def) {
this.id = id;
this.blockId = blockId;
this.inst = inst;
this.use = use;
this.def = def;
@ -30,7 +57,7 @@ public final class DataFlowNode {
this.successors = new HashSet<>();
}
public static DataFlowNode fromFlowNode(FlowInstruction srcInst, FlowBasicBlock block) {
public static DataFlowNode fromFlowNode(FlowInstruction srcInst) {
final String instType = switch (srcInst.getInstruction()) {
case "aload", "iload" -> "use";
case "astore", "istore" -> "def";
@ -48,74 +75,95 @@ public final class DataFlowNode {
def = srcInst.getArgs()[0];
}
return new DataFlowNode(srcInst.getId(), block.getId(), srcInst.getInstruction(), use, def);
return new DataFlowNode(srcInst.getId(), srcInst.getInstruction(), use, def);
}
// Getters, Setters
public String getId() {
return this.id;
}
public String getBlockId() {
return this.blockId;
}
public Set<String> getUse() {
return Set.of(this.use);
}
public Set<String> getDef() {
return Set.of(this.def);
}
public Set<String> getIn() {
return this.in;
}
public Set<String> getOut() {
return this.out;
}
public String getInst() {
return this.inst;
}
public Set<DataFlowNode> getPredecessorSet() {
return Collections.unmodifiableSet(this.predecessors);
}
public void addPredecessor(DataFlowNode node) {
this.predecessors.add(node);
}
public Set<DataFlowNode> getPredecessors() {
return this.predecessors;
}
public Set<DataFlowNode> getSuccessors() {
return this.successors;
public Set<DataFlowNode> getSuccessorSet() {
return Collections.unmodifiableSet(this.successors);
}
public void addSuccessor(DataFlowNode node) {
this.successors.add(node);
}
public void addOut(Set<String> out) {
if (out.isEmpty()) {
return;
}
if (out.stream().allMatch(String::isEmpty)) {
return;
}
this.out.addAll(out);
public Set<String> getUseSet() {
return Set.of(this.use);
}
public boolean addIn(Set<String> in) {
public Set<String> getDefSet() {
return Set.of(this.def);
}
public Set<String> getInSet() {
return Collections.unmodifiableSet(this.in);
}
public boolean addIn(Collection<String> in) {
if (in.isEmpty()) {
return false;
}
if (in.stream().allMatch(String::isEmpty)) {
if (in.stream().allMatch(String::isBlank)) {
return false;
}
return this.in.addAll(in);
return this.in.addAll(in.stream()
.filter(string -> !string.isBlank())
.collect(Collectors.toSet()));
}
public Set<String> getOutSet() {
return Collections.unmodifiableSet(this.out);
}
public void addOut(Collection<String> out) {
if (out.isEmpty()) {
return;
}
if (out.stream().allMatch(String::isBlank)) {
return;
}
this.out.addAll(out.stream()
.filter(string -> !string.isBlank())
.collect(Collectors.toSet()));
}
// Overrides
@Override
public int hashCode() {
return Objects.hash(this.id, this.inst);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final DataFlowNode that = (DataFlowNode) o;
return this.id.equals(that.id) && this.inst.equals(that.inst);
}
}

View File

@ -15,7 +15,6 @@ import java.util.Map;
public final class InterferenceGraph implements Iterable<InterferenceNode> {
// TODO: Why use list, its a graph
private final List<InterferenceNode> nodes;
private InterferenceGraph(List<InterferenceNode> nodes) {
@ -32,14 +31,14 @@ public final class InterferenceGraph implements Iterable<InterferenceNode> {
// Determine neighbours
for (DataFlowNode node : graph) {
Logger.log("NODE " + node.getInst() + " - OUT: " + node.getOut());
Logger.log("NODE " + node.getInst() + " - OUT: " + node.getOutSet());
for (String left : node.getOut()) {
for (String left : node.getOutSet()) {
if (left.isBlank()) {
continue;
}
for (String right : node.getOut()) {
for (String right : node.getOutSet()) {
if (right.isBlank()) {
continue;
}

View File

@ -43,8 +43,8 @@ public final class LivenessAnalysis {
// TODO: Indexof mega unnötig
Logger.log("IN, OUT Sets:");
for (DataFlowNode node : graph) {
Logger.log(graph.indexOf(node) + ": " + node.getInst() + " IN: " + node.getIn());
Logger.log(graph.indexOf(node) + ": " + node.getInst() + " OUT: " + node.getOut());
Logger.log(graph.indexOf(node) + ": " + node.getInst() + " IN: " + node.getInSet());
Logger.log(graph.indexOf(node) + ": " + node.getInst() + " OUT: " + node.getOutSet());
}
Logger.log("\n");
}
@ -52,16 +52,16 @@ public final class LivenessAnalysis {
private static boolean updateInOut(DataFlowNode node) {
boolean change;
for (DataFlowNode succ : node.getSuccessors()) {
for (DataFlowNode succ : node.getSuccessorSet()) {
// A variable going live into the successor implies it going live out of the predecessor
node.addOut(succ.getIn());
node.addOut(succ.getInSet());
}
final Set<String> addIN = new HashSet<>(node.getOut()); // Copy important
addIN.removeAll(node.getDef()); // If a variable that is live-out is defined in the node, it doesn't have to be live-in
final Set<String> addIN = new HashSet<>(node.getOutSet()); // Copy important
addIN.removeAll(node.getDefSet()); // If a variable that is live-out is defined in the node, it doesn't have to be live-in
change = node.addIn(node.getUse()); // A variable being used implies it going in live
change = node.addIn(node.getUseSet()); // A variable being used implies it going in live
change = change || node.addIn(addIN); // A variable that is live-out and isn't defined in the node must be live-in
return change;

View File

@ -54,7 +54,7 @@ class LivenessAnalysisTest {
final Map<ASTNode, String> nodeTable = TypeChecker.validate(tree);
final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput");
final FlowGraph graph = gen.generateGraph();
final DataFlowGraph dataGraph = DataFlowGraph.fromSourceGraph(graph);
final DataFlowGraph dataGraph = DataFlowGraph.fromFlowGraph(graph);
return LivenessAnalysis.fromDataFlowGraph(dataGraph, gen.getVarMap());
}