diff --git a/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java b/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java new file mode 100644 index 0000000..83e933d --- /dev/null +++ b/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java @@ -0,0 +1,122 @@ +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; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public final class DataFlowGraph { + + private final List graph; + + private DataFlowGraph(List graph) { + this.graph = Collections.unmodifiableList(graph); + Logger.call(this::printToImage); + } + + public static DataFlowGraph fromSourceGraph(FlowGraph srcGraph) { + final List graph = new LinkedList<>(); + + for (FlowBasicBlock block : srcGraph.getBlocks()) { + for (FlowInstruction inst : block.getInstructions()) { + graph.add(DataFlowNode.fromFlowNode(inst, block)); + } + } + + initSuccPred(srcGraph, graph); + + return new DataFlowGraph(graph); + } + + private static void initSuccPred(FlowGraph srcGraph, List graph) { + for (FlowBasicBlock block : srcGraph.getBlocks()) { + for (FlowInstruction inst : block.getInstructions()) { + final DataFlowNode current = getNodeByInstruction(inst, graph); + + for (FlowInstruction pred : block.getPredecessors(inst)) { + final DataFlowNode currentPred = getNodeByInstruction(pred, graph); + + current.addPredecessor(currentPred); + currentPred.addSuccessor(current); + } + + for (FlowInstruction succ : block.getSuccessors(inst)) { + final DataFlowNode currentSucc = getNodeByInstruction(succ, graph); + + Logger.log("INST: " + current.getInst() + ", SUCC: " + currentSucc.getInst()); + current.addSuccessor(currentSucc); + currentSucc.addPredecessor(current); + } + } + } + } + + private static DataFlowNode getNodeByInstruction(FlowInstruction inst, List graph) { + return graph.stream() + .filter(node -> node.getId().equals(inst.getId())) + .findFirst() + .orElse(null); + } + + public List getNodes() { + return this.graph; + } + + // Printing + + public String printToImage() { + final StringBuilder dot = new StringBuilder(); + + dot.append("digraph dfd {\n") + .append("node[shape=Mrecord]\n"); + + for (DataFlowNode node : this.graph) { + dot.append(node.getId()) + .append(" [label=\"{ ") + .append(this.graph.indexOf(node)) + .append("| ") + .append(node.getInst()) + .append("}\"];\n"); + } + + 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"); + + for (DataFlowNode node : this.graph) { + for (DataFlowNode succ : node.getSuccessors()) { + dot.append(node.getId()).append(" -> ").append(succ.getId()).append(";\n"); + } + } + + dot.append("}"); + + final String dotOut = dot.toString(); + + final Path dotFile = Paths.get(System.getProperty("user.dir") + "/DataFlowGraph.dot"); + try { + Files.writeString(dotFile, dotOut); + } catch (IOException e) { + e.printStackTrace(); + } + + final ProcessBuilder dotCompile = new ProcessBuilder("dot", "-Tsvg", "-oDataFlowGraph.svg", "DataFlowGraph.dot"); + try { + dotCompile.start(); + } catch (IOException e) { + e.printStackTrace(); + } + + return "Finished."; + } +} diff --git a/src/main/java/codegen/analysis/dataflow/DataFlowNode.java b/src/main/java/codegen/analysis/dataflow/DataFlowNode.java new file mode 100644 index 0000000..e53d253 --- /dev/null +++ b/src/main/java/codegen/analysis/dataflow/DataFlowNode.java @@ -0,0 +1,121 @@ +package codegen.analysis.dataflow; + +import codegen.flowgraph.FlowBasicBlock; +import codegen.flowgraph.FlowInstruction; + +import java.util.HashSet; +import java.util.Set; + +public final class DataFlowNode { + + private final String id; + private final String blockId; + private final String inst; + private final String use; + private final String def; + private final Set in; + private final Set out; + private final Set predecessors; + private final Set successors; + + private DataFlowNode(String id, String blockId, String inst, String use, String def) { + this.id = id; + this.blockId = blockId; + this.inst = inst; + this.use = use; + this.def = def; + this.in = new HashSet<>(); + this.out = new HashSet<>(); + this.predecessors = new HashSet<>(); + this.successors = new HashSet<>(); + } + + public static DataFlowNode fromFlowNode(FlowInstruction srcInst, FlowBasicBlock block) { + final String instType = switch (srcInst.getInstruction()) { + case "aload", "iload" -> "use"; + case "astore", "istore" -> "def"; + case "goto", "ifeq", + "if_icmpeq", "if_acmpeq", "if_icmpne", "if_acmpne", + "if_icmplt", "if_icmple", "if_icmpgt", "if_icmpge" -> "jmp"; + default -> ""; + }; + + String use = ""; + String def = ""; + if ("use".equals(instType)) { + use = srcInst.getArgs()[0]; + } else if ("def".equals(instType)) { + def = srcInst.getArgs()[0]; + } + + return new DataFlowNode(srcInst.getId(), block.getId(), srcInst.getInstruction(), use, def); + } + + public String getId() { + return this.id; + } + + public String getBlockId() { + return this.blockId; + } + + public Set getUse() { + return Set.of(this.use); + } + + public Set getDef() { + return Set.of(this.def); + } + + public Set getIn() { + return this.in; + } + + public Set getOut() { + return this.out; + } + + public String getInst() { + return this.inst; + } + + public void addPredecessor(DataFlowNode node) { + this.predecessors.add(node); + } + + public Set getPredecessors() { + return this.predecessors; + } + + public Set getSuccessors() { + return this.successors; + } + + public void addSuccessor(DataFlowNode node) { + this.successors.add(node); + } + + public void addOut(Set out) { + if (out.isEmpty()) { + return; + } + + if (out.stream().allMatch(String::isEmpty)) { + return; + } + + this.out.addAll(out); + } + + public boolean addIn(Set in) { + if (in.isEmpty()) { + return false; + } + + if (in.stream().allMatch(String::isEmpty)) { + return false; + } + + return this.in.addAll(in); + } +}