mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-06-02 00:41:42 +00:00
Compare commits
8 Commits
73a68a619d
...
8d6d5e06d8
Author | SHA1 | Date | |
---|---|---|---|
|
8d6d5e06d8 | ||
|
137053f093 | ||
|
6f0caee5fa | ||
|
efc9ed5fab | ||
|
7b856a0f74 | ||
|
f0dc36b205 | ||
|
8359ffde8a | ||
|
3b571bbe6a |
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -6,6 +6,10 @@
|
|||
*/*.brk
|
||||
*/*.prg
|
||||
*/*.sym
|
||||
*/src/*.md
|
||||
*/src/*.mmd
|
||||
*/src/*.mermaid
|
||||
*/src/*.markdown
|
||||
*/.tmpdirs
|
||||
*/bin/
|
||||
**/.DS_Store
|
||||
|
@ -15,6 +19,7 @@ kickc.iml
|
|||
**/.vscode/*
|
||||
**/.vscode/*.log
|
||||
**/target/*
|
||||
**/.target/*
|
||||
*/workspace.xml
|
||||
/gen/*
|
||||
/src/main/fragment/cache
|
|
@ -27,6 +27,12 @@ public class CompileLog {
|
|||
*/
|
||||
private boolean verboseAsmOptimize = false;
|
||||
|
||||
/**
|
||||
* Should ASM output be verbose.
|
||||
*/
|
||||
|
||||
private boolean verboseAsm = true;
|
||||
|
||||
/**
|
||||
* Should SSA optimization be verbose.
|
||||
*/
|
||||
|
@ -62,10 +68,6 @@ public class CompileLog {
|
|||
*/
|
||||
private boolean verboseParse = false;
|
||||
|
||||
/**
|
||||
* Should the creation of the SSA be verbose.
|
||||
*/
|
||||
private boolean verboseCreateSsa = false;
|
||||
|
||||
/**
|
||||
* Output information about struct unwinding
|
||||
|
@ -95,6 +97,24 @@ public class CompileLog {
|
|||
*/
|
||||
private boolean sysOut = false;
|
||||
|
||||
/**
|
||||
* Create .mmd files containing the Control Block Flow Graph in mermaid.
|
||||
*/
|
||||
|
||||
private boolean reportsCFGMermaid = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create .mmd files containing the Call Graph in mermaid.
|
||||
*/
|
||||
private boolean reportsCGMermaid = false;
|
||||
|
||||
/**
|
||||
* Create .md files containing the Register Interference Graph of the alive
|
||||
* variables in mermaid. The RIG is input for the register coalesce.
|
||||
*/
|
||||
private boolean reportsRIGMermaid = false;
|
||||
|
||||
public CompileLog() {
|
||||
this.log = new StringBuilder();
|
||||
}
|
||||
|
@ -273,6 +293,10 @@ public class CompileLog {
|
|||
return this;
|
||||
}
|
||||
|
||||
public boolean isVerboseAsm() { return verboseAsm; }
|
||||
|
||||
public void setVerboseAsm(boolean verboseAsm) { this.verboseAsm = verboseAsm; }
|
||||
|
||||
public boolean isVerboseAsmOptimize() {
|
||||
return verboseAsmOptimize;
|
||||
}
|
||||
|
@ -335,4 +359,30 @@ public class CompileLog {
|
|||
public boolean isVerboseComments() {
|
||||
return verboseComments;
|
||||
}
|
||||
|
||||
private boolean verboseCreateSsa = false;
|
||||
|
||||
public boolean isReportsCFGMermaid() {
|
||||
return reportsCFGMermaid;
|
||||
}
|
||||
|
||||
public void setReportsCFGMermaid(boolean reportsCFGMermaid) {
|
||||
this.reportsCFGMermaid = reportsCFGMermaid;
|
||||
}
|
||||
|
||||
public boolean isReportsCGMermaid() {
|
||||
return reportsCGMermaid;
|
||||
}
|
||||
|
||||
public void setReportsCGMermaid(boolean reportsCGMermaid) {
|
||||
this.reportsCGMermaid = reportsCGMermaid;
|
||||
}
|
||||
|
||||
public boolean isReportsRIGMermaid() {
|
||||
return reportsRIGMermaid;
|
||||
}
|
||||
|
||||
public void setReportsRIGMermaid(boolean reportsRIGMermaid) {
|
||||
this.reportsRIGMermaid = reportsRIGMermaid;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import dk.camelot64.kickc.model.values.StringEncoding;
|
|||
import dk.camelot64.kickc.parser.CParser;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import dk.camelot64.kickc.passes.*;
|
||||
import dk.camelot64.kickc.passes.reports.*;
|
||||
import dk.camelot64.kickc.preprocessor.CPreprocessor;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenSource;
|
||||
|
@ -220,6 +221,8 @@ public class Compiler {
|
|||
new Pass1GenerateControlFlowGraph(program).execute();
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("FIRST CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-after-parsing", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new Pass1ResolveForwardReferences(program).execute();
|
||||
|
@ -249,13 +252,17 @@ public class Compiler {
|
|||
new Pass1AssertNoLValueIntermediate(program).execute();
|
||||
new PassNAddTypeConversionAssignment(program, true).execute();
|
||||
new Pass1AddressOfHandling(program).execute();
|
||||
|
||||
new Pass1AsmUsesHandling(program).execute();
|
||||
|
||||
new Pass1AssertProcedureCallParameters(program).execute();
|
||||
new Pass1ModifiedVarsAnalysis(program).execute();
|
||||
new Pass1CallStackVarPrepare(program).execute();
|
||||
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("CONTROL FLOW GRAPH BEFORE SIZEOF FIX");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-before-sizeof-fix", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
|
||||
|
@ -270,6 +277,8 @@ public class Compiler {
|
|||
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("CONTROL FLOW GRAPH AFTER UNWIND");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program,"pass1-cfg-after-unwind", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
|
||||
|
@ -283,6 +292,8 @@ public class Compiler {
|
|||
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("CONTROL FLOW GRAPH BEFORE INLINING");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-before-inlining", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new Pass1ProcedureInline(program).execute();
|
||||
|
@ -293,6 +304,8 @@ public class Compiler {
|
|||
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("INITIAL CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-initial", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
|
||||
|
@ -314,6 +327,8 @@ public class Compiler {
|
|||
new Pass1CallStackVarConvert(program).execute();
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("PROCEDURE CALLS");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-procedure-calls", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new Pass1CallStack(program).execute();
|
||||
|
@ -321,6 +336,8 @@ public class Compiler {
|
|||
new Pass1CallPhiParameters(program).execute();
|
||||
if(getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("PROCEDURE PARAMETERS");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-procedure-parameters", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new PassNUnwindLValueLists(program).execute();
|
||||
|
@ -334,6 +351,8 @@ public class Compiler {
|
|||
|
||||
|
||||
getLog().append("\nCONTROL FLOW GRAPH SSA");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass1-cfg-final-ssa", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
|
||||
getLog().append("SYMBOL TABLE SSA");
|
||||
|
@ -465,6 +484,8 @@ public class Compiler {
|
|||
|
||||
if(getLog().isVerboseLoopUnroll()) {
|
||||
getLog().append("CONTROL FLOW GRAPH BEFORE UNROLLING");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass2-cfg-before-unrolling", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
|
||||
|
@ -474,6 +495,8 @@ public class Compiler {
|
|||
if(unrolled) {
|
||||
if(getLog().isVerboseLoopUnroll()) {
|
||||
getLog().append("UNROLLED CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass2-cfg-after-unrolling", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
pass2Optimize();
|
||||
|
@ -560,6 +583,8 @@ public class Compiler {
|
|||
getLog().append("Successful SSA optimization " + optimization.getClass().getSimpleName() + "");
|
||||
if(getLog().isVerboseSSAOptimize()) {
|
||||
getLog().append("CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass-2-cfg-ssa-" + optimization.getClass().getSimpleName(), "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
}
|
||||
|
@ -604,16 +629,22 @@ public class Compiler {
|
|||
|
||||
if(getLog().isVerboseSSAOptimize()) {
|
||||
getLog().append("CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass3x-cfg-optimized", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new PassNCullEmptyBlocks(program, false).step();
|
||||
if(getLog().isVerboseSSAOptimize()) {
|
||||
getLog().append("CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass3-cfg-empty-blocks-culled", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new PassNRenumberLabels(program, false).execute();
|
||||
if(getLog().isVerboseSSAOptimize()) {
|
||||
getLog().append("CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass3-cfg-labels-renumbered", "md").generateReport();
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
}
|
||||
new PassNBlockSequencePlanner(program).step();
|
||||
|
@ -637,6 +668,11 @@ public class Compiler {
|
|||
program.getLiveRangeVariablesEffective();
|
||||
|
||||
getLog().append("\nFINAL CONTROL FLOW GRAPH");
|
||||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass3-cfg-final", "md").generateReport();
|
||||
if(getLog().isReportsCGMermaid()) {
|
||||
new ReportCG(program, "pass3-cg-final", "md").generateReport();
|
||||
}
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
|
||||
}
|
||||
|
@ -717,6 +753,8 @@ public class Compiler {
|
|||
new Pass4RegisterUpliftRemains(program).performUplift(upliftCombinations);
|
||||
}
|
||||
|
||||
if(getLog().isReportsRIGMermaid())
|
||||
new ReportRIG(program, "pass4-rig", "md").generateReport();
|
||||
// Register coalesce on assignment (saving bytes & cycles)
|
||||
new Pass4MemoryCoalesceAssignment(program).coalesce();
|
||||
|
||||
|
@ -747,8 +785,10 @@ public class Compiler {
|
|||
|
||||
private void pass5GenerateAndOptimizeAsm() {
|
||||
|
||||
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
||||
}
|
||||
|
||||
getLog().append("ASSEMBLER OPTIMIZATIONS");
|
||||
List<Pass5AsmOptimization> pass5Optimizations = new ArrayList<>();
|
||||
|
@ -769,8 +809,10 @@ public class Compiler {
|
|||
getLog().append("Succesful ASM optimization " + optimization.getClass().getSimpleName());
|
||||
asmOptimized = true;
|
||||
if(getLog().isVerboseAsmOptimize()) {
|
||||
getLog().append("ASSEMBLER");
|
||||
getLog().append(program.getAsm().toString());
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("ASSEMBLER");
|
||||
getLog().append(program.getAsm().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -786,9 +828,11 @@ public class Compiler {
|
|||
getLog().append("\nFINAL SYMBOL TABLE");
|
||||
getLog().append(program.getScope().toStringVars(program, false));
|
||||
|
||||
getLog().append("\nFINAL ASSEMBLER");
|
||||
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("\nFINAL ASSEMBLER");
|
||||
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,9 @@ public class KickC implements Callable<Integer> {
|
|||
@CommandLine.Option(names = {"-v"}, description = "Verbose output describing the compilation process")
|
||||
private boolean verbose = false;
|
||||
|
||||
@CommandLine.Option(names = {"-vnoasm"}, description = "Verbosity Option. Don't show any generated assembler during compilation.")
|
||||
private boolean verboseAsm = false;
|
||||
|
||||
@CommandLine.Option(names = {"-vasmout"}, description = "Verbosity Option. Show KickAssembler standard output during compilation.")
|
||||
private boolean verboseAsmOut = false;
|
||||
|
||||
|
@ -123,6 +126,7 @@ public class KickC implements Callable<Integer> {
|
|||
@CommandLine.Option(names = {"-vcreate"}, description = "Verbosity Option. Creation of the Single Static Assignment Control Flow Graph.")
|
||||
private boolean verboseCreateSsa = false;
|
||||
|
||||
|
||||
@CommandLine.Option(names = {"-voptimize"}, description = "Verbosity Option. Control Flow Graph Optimization.")
|
||||
private boolean verboseSSAOptimize = false;
|
||||
|
||||
|
@ -156,6 +160,15 @@ public class KickC implements Callable<Integer> {
|
|||
@CommandLine.Option(names = {"-vfixlongbranch"}, description = "Verbosity Option. Fix Long ASM branches.")
|
||||
private boolean verboseFixLongBranch = false;
|
||||
|
||||
@CommandLine.Option(names = {"-rcfg", "-reportcfg"}, description = "Report Option. Visualize the Control block Flow Graph in Mermaid.")
|
||||
private boolean reportCFGMermaid = false;
|
||||
|
||||
@CommandLine.Option(names = {"-rcg", "-reportcg"}, description = "Report Option. Visualize the Call Graph between the different functions in Mermaid.")
|
||||
private boolean reportCGMermaid = false;
|
||||
|
||||
@CommandLine.Option(names = {"-rrig", "-reportrig"}, description = "Report Option. Visualize the Register Interference Graph between the different alive variables per procedure in Mermaid.")
|
||||
private boolean reportRIGMermaid = false;
|
||||
|
||||
@CommandLine.Option(names = {"-Wfragment"}, description = "Warning Option. Missing fragments produces a warning instead of an error.")
|
||||
private boolean warnFragmentMissing = false;
|
||||
|
||||
|
@ -192,6 +205,7 @@ public class KickC implements Callable<Integer> {
|
|||
@CommandLine.Option(names = {"-calling"}, description = "Configure calling convention. Default is __phicall. See #pragma calling")
|
||||
private String calling = null;
|
||||
|
||||
|
||||
/** Program Exit Code signaling a compile error. */
|
||||
public static final int COMPILE_ERROR = CommandLine.ExitCode.SOFTWARE;
|
||||
|
||||
|
@ -499,34 +513,25 @@ public class KickC implements Callable<Integer> {
|
|||
|
||||
// Execute the binary file if instructed
|
||||
if(emulator != null) {
|
||||
List<String> emulatorCommand = new ArrayList<>();
|
||||
emulatorCommand.addAll(Arrays.asList(emulator.split(" ")));
|
||||
|
||||
// Find commandline options for the emulator
|
||||
if( emulator.equals("C64Debugger") ) {
|
||||
String emuOptions = "";
|
||||
if(emulator.equals("C64Debugger")) {
|
||||
Path viceSymbolsPath = program.getOutputFileManager().getOutputFile("vs");
|
||||
emulatorCommand.add("-symbol");
|
||||
emulatorCommand.add(viceSymbolsPath.toString());
|
||||
emulatorCommand.add("-autojmp");
|
||||
emulatorCommand.add("-prg");
|
||||
emuOptions = "-symbols " + viceSymbolsPath + " -autojmp -prg ";
|
||||
}
|
||||
// The program names used by VICE emulators
|
||||
List<String> viceEmus = Arrays.asList("x64", "x64sc", "x128", "x64dtv", "xcbm2", "xcbm5x0", "xpet", "xplus4", "xscpu64", "xvic");
|
||||
if(viceEmus.contains(emulator)) {
|
||||
Path viceSymbolsPath = program.getOutputFileManager().getOutputFile("vs");
|
||||
emulatorCommand.add("-moncommands");
|
||||
emulatorCommand.add(viceSymbolsPath.toAbsolutePath().toString());
|
||||
emuOptions = "-moncommands " + viceSymbolsPath.toAbsolutePath().toString() + " ";
|
||||
}
|
||||
emulatorCommand.add(outputBinaryFilePath.toAbsolutePath().toString());
|
||||
System.out.println("Executing " + outputBinaryFilePath + " using " + emulator);
|
||||
String executeCommand = emulator + " " + emuOptions + outputBinaryFilePath.toAbsolutePath().toString();
|
||||
if(verbose) {
|
||||
System.out.println("Executing command: " + String.join(" ", emulatorCommand));
|
||||
System.out.println("Executing command: " + executeCommand);
|
||||
}
|
||||
try {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||
processBuilder.command(emulatorCommand);
|
||||
processBuilder.inheritIO();
|
||||
Process process = processBuilder.start();
|
||||
Process process = Runtime.getRuntime().exec(executeCommand);
|
||||
process.waitFor();
|
||||
} catch(Throwable e) {
|
||||
System.err.println("Executing emulator failed! " + e.getMessage());
|
||||
|
@ -594,11 +599,26 @@ public class KickC implements Callable<Integer> {
|
|||
compiler.getLog().setVerboseAsmOptimize(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(verboseAsm) {
|
||||
compiler.getLog().setVerboseAsm(false);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(verboseFixLongBranch) {
|
||||
compiler.getLog().setVerboseFixLongBranch(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
|
||||
if(reportCFGMermaid) {
|
||||
compiler.getLog().setReportsCFGMermaid(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(reportCGMermaid) {
|
||||
compiler.getLog().setReportsCGMermaid(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(reportRIGMermaid) {
|
||||
compiler.getLog().setReportsRIGMermaid(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -129,6 +129,20 @@ public class OutputFileManager {
|
|||
return outputFile.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extended output file name in a specific directory with a name and specific extension
|
||||
*
|
||||
* @return The output file.
|
||||
*/
|
||||
public Path getMermaidOutputFile(String name, String extension) {
|
||||
Path outputDir = getOutputDirectory();
|
||||
String fileName = getOutputBaseName();
|
||||
fileName += "-" + name;
|
||||
if(extension.length() > 0) fileName += "." + extension;
|
||||
final Path outputFile = outputDir.resolve(fileName);
|
||||
return outputFile.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output directory
|
||||
* <i>Output-path</i> is the directory where the output files are generated. The following is a prioritized list specifying how the compiler finds this folder:
|
||||
|
|
|
@ -5,10 +5,7 @@ import dk.camelot64.kickc.model.symbols.Procedure;
|
|||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.ScopeRef;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A named/labelled sequence of SSA statements connected to other basic blocks.
|
||||
|
@ -191,6 +188,7 @@ public class ControlFlowBlock implements Graph.Block {
|
|||
return out.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
|
|
|
@ -33,6 +33,17 @@ public class LiveRange {
|
|||
return s;
|
||||
}
|
||||
|
||||
public String toString(int maxIndex) {
|
||||
StringBuilder range = new StringBuilder();
|
||||
range.append(" ").append(String.format("%4d", size())).append(" : ");
|
||||
StringBuilder canvas = new StringBuilder(" ".repeat(maxIndex+1));
|
||||
for(LiveInterval interval : intervals) {
|
||||
interval.paintOn(canvas);
|
||||
}
|
||||
range.append(canvas);
|
||||
return range.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying statement intervals
|
||||
* @return The intervals
|
||||
|
@ -185,6 +196,11 @@ public class LiveRange {
|
|||
return lastStatementIdx - firstStatementIdx + 1;
|
||||
}
|
||||
|
||||
public void paintOn(StringBuilder canvas) {
|
||||
for(int i=firstStatementIdx; i<=lastStatementIdx;i++) {
|
||||
canvas.setCharAt(i, '*');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ public class LiveRangeEquivalenceClassSet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Consolidates two live range equivalence calsses into one.
|
||||
* Consolidates two live range equivalence classes into one.
|
||||
* All variables and live ranges from the other class is added to the first one - and the other one is deleted.
|
||||
*
|
||||
* @param equivalenceClass The first live range equivalence class.
|
||||
|
@ -73,7 +73,7 @@ public class LiveRangeEquivalenceClassSet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Informs the set that class of a variable has ben set - called by add/remove methods inside LiveRangeEquivalenceClass
|
||||
* Informs the set that class of a variable has been set - called by add/remove methods inside LiveRangeEquivalenceClass
|
||||
*
|
||||
* @param variable The variable
|
||||
* @param equivalenceClass The class
|
||||
|
|
|
@ -86,6 +86,20 @@ public class LiveRangeVariables {
|
|||
return liveRanges.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum index of all live ranges
|
||||
* @return The maximum index of all live ranges
|
||||
*/
|
||||
public int getMaxIndex() {
|
||||
int maxIndex = 0;
|
||||
for(LiveRange liveRange: liveRanges.values()) {
|
||||
int liveRangeMaxIndex = liveRange.getMaxIndex();
|
||||
if(maxIndex < liveRangeMaxIndex)
|
||||
maxIndex = liveRangeMaxIndex;
|
||||
}
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variable live ranges by statement index. Usable for quick access if you plan to iterate many statements.
|
||||
* @return variable live ranges by statement index
|
||||
|
|
|
@ -12,6 +12,8 @@ import dk.camelot64.kickc.model.values.ScopeRef;
|
|||
import dk.camelot64.kickc.passes.calcs.*;
|
||||
import dk.camelot64.kickc.passes.utils.ProcedureUtils;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -181,11 +183,14 @@ public class Program {
|
|||
public String prettyControlFlowGraph() {
|
||||
StringBuilder graphPretty = new StringBuilder();
|
||||
for(ProcedureCompilation procedureCompilation : getProcedureCompilations()) {
|
||||
graphPretty.append(procedureCompilation.getGraph().toString(this));
|
||||
Graph graph = procedureCompilation.getGraph();
|
||||
if(graph != null)
|
||||
graphPretty.append(graph.toString(this));
|
||||
}
|
||||
return graphPretty.toString();
|
||||
}
|
||||
|
||||
|
||||
public OutputFileManager getOutputFileManager() {
|
||||
return outputFileManager;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,25 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
|
|||
addToEquivalenceClassSet(variable.getVariableRef(), new ArrayList<>(), liveRangeEquivalenceClassSet);
|
||||
}
|
||||
|
||||
getLog().append("Overlapping live ranges variables");
|
||||
LiveRangeVariables liveRanges = getProgram().getLiveRangeVariables();
|
||||
int maxIndex = liveRanges.getMaxIndex();
|
||||
StringBuilder div = new StringBuilder();
|
||||
div.append(" ".repeat(8));
|
||||
for(int i=0; i<=maxIndex; i++) {
|
||||
if(i % 10 != 0)
|
||||
div.append("-");
|
||||
else
|
||||
div.append("x");
|
||||
}
|
||||
getLog().append(div.toString());
|
||||
for(Variable variable: getProgram().getScope().getAllVariables(true)) {
|
||||
LiveRange liveRange = liveRanges.getLiveRange(variable.getVariableRef());
|
||||
if (liveRange != null) {
|
||||
getLog().append(liveRange.toString(maxIndex) + " : " + variable.toString(getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
getLog().append("Complete equivalence classes");
|
||||
for(LiveRangeEquivalenceClass liveRangeEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
|
||||
getLog().append(liveRangeEquivalenceClass.toString());
|
||||
|
|
37
src/main/java/dk/camelot64/kickc/passes/reports/Mermaid.java
Normal file
37
src/main/java/dk/camelot64/kickc/passes/reports/Mermaid.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
|
||||
/**
|
||||
* A structure to generate reports expressed in the mermaid syntax for graphics generation.
|
||||
*/
|
||||
public abstract class Mermaid {
|
||||
|
||||
protected String id;
|
||||
protected String title;
|
||||
|
||||
|
||||
public Mermaid(String id, String title) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public static String toID(String label) {
|
||||
return label.replace(":","_").replace("@","_");
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
/**
|
||||
* Create the flow components for mermaid report generation.
|
||||
* Flows are interconnecting mermaid nodes or sub graphs.
|
||||
*/
|
||||
public class MermaidFlow extends Mermaid{
|
||||
|
||||
public enum Type {
|
||||
FULL, DOTTED, LINED
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
UNI, BI
|
||||
}
|
||||
|
||||
private MermaidNode from, to;
|
||||
|
||||
private Type type;
|
||||
|
||||
private Direction direction;
|
||||
|
||||
MermaidFlow(MermaidNode from, MermaidNode to, String title, Type type, Direction direction) {
|
||||
super(from + "_" + to, title);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.type = type;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
text.append(from.getId());
|
||||
if(direction == Direction.BI) {
|
||||
text.append("<");
|
||||
}
|
||||
switch(type) {
|
||||
case FULL -> {
|
||||
String title = getTitle();
|
||||
if(title != null)
|
||||
text.append("--").append(getTitle()).append("-->");
|
||||
else
|
||||
text.append("-->");
|
||||
}
|
||||
case DOTTED -> {
|
||||
String title = getTitle();
|
||||
if(title != null)
|
||||
text.append("-.").append(getTitle()).append(".->");
|
||||
else
|
||||
text.append("-.->");
|
||||
}
|
||||
}
|
||||
text.append(to.getId()).append("\n");
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public MermaidNode getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setFrom(MermaidNode from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public MermaidNode getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public void setTo(MermaidNode to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Create a flow chart for mermaid report generation.
|
||||
* Flowcharts contain nodes or sub graphs and flows, and can either be located as a single entity in root, or embedded in other sub graphs.
|
||||
*/
|
||||
public class MermaidGraph extends Mermaid {
|
||||
|
||||
public enum Direction {
|
||||
TOP_DOWN ("TD"),
|
||||
BOTTOM_UP ("BU"),
|
||||
LEFT_RIGHT("LR"),
|
||||
RIGHT_LEFT("RL");
|
||||
|
||||
private final String direction;
|
||||
|
||||
Direction(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.direction;
|
||||
}
|
||||
}
|
||||
|
||||
protected Direction direction;
|
||||
protected HashMap<String, MermaidNode> nodes;
|
||||
protected HashMap<String, MermaidFlow> flows;
|
||||
protected HashMap<String, MermaidSubGraph> subGraphs;
|
||||
|
||||
public MermaidGraph(Direction direction) {
|
||||
super(toID(""), "");
|
||||
this.direction = direction;
|
||||
this.nodes = new HashMap<String, MermaidNode>();
|
||||
this.flows = new HashMap<String, MermaidFlow>();
|
||||
this.subGraphs = new HashMap<String, MermaidSubGraph>();
|
||||
}
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
// graph with direction
|
||||
text.append("graph ").append(this.direction.toString()).append("\n");
|
||||
|
||||
// sub graphs
|
||||
for(MermaidSubGraph subGraph : getSubGraphs().values()) {
|
||||
text.append(subGraph.getText(program));
|
||||
}
|
||||
|
||||
// nodes of the graph
|
||||
for(MermaidNode node : nodes.values()) {
|
||||
text.append(node.getText(program));
|
||||
}
|
||||
|
||||
// flows to and from nodes
|
||||
for(MermaidFlow flow : flows.values()) {
|
||||
text.append(flow.getText(program));
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidNode> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(HashMap<String, MermaidNode> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(MermaidNode node) {
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(String nodeName) {
|
||||
MermaidNode node = new MermaidNode(nodeName);
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidFlow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void setFlows(HashMap<String, MermaidFlow> flows) {
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
public void addFlow(MermaidFlow flow) {
|
||||
this.flows.put(flow.getFrom().getId() + "::" + flow.getTo().getId(), flow);
|
||||
}
|
||||
|
||||
public void addFlow(MermaidNode from, MermaidNode to, String title, MermaidFlow.Type type, MermaidFlow.Direction direction) {
|
||||
MermaidFlow flow = new MermaidFlow(from, to, title, type, direction);
|
||||
this.flows.put(from.getId() + "::" + to.getId(), flow);
|
||||
}
|
||||
|
||||
public MermaidFlow getFlow(MermaidNode from, MermaidNode to) {
|
||||
return this.flows.get(from.getId() + "::" + to.getId());
|
||||
}
|
||||
|
||||
public void removeFlow(MermaidFlow flow) {
|
||||
this.flows.remove(flow.getFrom().getId() + "::" + flow.getTo().getId());
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidSubGraph> getSubGraphs() {
|
||||
return subGraphs;
|
||||
}
|
||||
|
||||
public void setSubGraphs(HashMap<String, MermaidSubGraph> subGraphs) {
|
||||
this.subGraphs = subGraphs;
|
||||
}
|
||||
|
||||
public MermaidSubGraph addProcedureSubGraph(MermaidSubGraph subGraph) {
|
||||
this.subGraphs.putIfAbsent(subGraph.id, subGraph);
|
||||
return subGraph;
|
||||
}
|
||||
}
|
137
src/main/java/dk/camelot64/kickc/passes/reports/MermaidNode.java
Normal file
137
src/main/java/dk/camelot64/kickc/passes/reports/MermaidNode.java
Normal file
|
@ -0,0 +1,137 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
||||
|
||||
import javax.swing.text.AbstractDocument;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Create nodes for mermaid report generation.
|
||||
* Nodes are the final element in sub graphs or root.
|
||||
* They are expressed with an Id and a Title. The Id represents the
|
||||
* key to be interconnected through flows, while the Title is displayed
|
||||
* inside the graphics box. The title is expressed in markdown.
|
||||
* Each node can have statements, which is a list of Statements and are
|
||||
* shown in the graphics box expressed in markdown.
|
||||
*/
|
||||
public class MermaidNode extends Mermaid {
|
||||
|
||||
public MermaidNode(String title) {
|
||||
super(toID(title), title);
|
||||
this.type = Type.SEQUENCE;
|
||||
this.statements = new LinkedList<Statement>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
}
|
||||
|
||||
public MermaidNode(String id, String title) {
|
||||
super(id, title);
|
||||
this.type = Type.SEQUENCE;
|
||||
this.statements = new LinkedList<Statement>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
}
|
||||
|
||||
public enum Type { SEQUENCE, CALL, BRANCH }
|
||||
|
||||
private Type type;
|
||||
|
||||
protected HashSet<MermaidFlow> flows;
|
||||
|
||||
private List<Statement> statements;
|
||||
|
||||
public List<Statement> getStatements() {
|
||||
return statements;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setStatements(List<Statement> statements) {
|
||||
this.statements = statements;
|
||||
if(!statements.isEmpty()) {
|
||||
Statement lastStatement = this.statements.get(this.statements.size() - 1);
|
||||
|
||||
if (lastStatement instanceof StatementCall) {
|
||||
type = Type.CALL;
|
||||
} else if (lastStatement instanceof StatementConditionalJump) {
|
||||
type = Type.BRANCH;
|
||||
} else {
|
||||
type = Type.SEQUENCE;
|
||||
}
|
||||
} else {
|
||||
type = Type.SEQUENCE;
|
||||
}
|
||||
}
|
||||
|
||||
public String toStringStatements() {
|
||||
StringBuilder stmtString = new StringBuilder();
|
||||
for(Statement statement : getStatements()) {
|
||||
stmtString.append(statement).append("\n");
|
||||
}
|
||||
return stmtString.toString();
|
||||
}
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
text.append(getId());
|
||||
|
||||
switch(getType()) {
|
||||
case SEQUENCE -> {
|
||||
text.append("[\"`");
|
||||
}
|
||||
case CALL -> {
|
||||
text.append("[[\"`");
|
||||
}
|
||||
case BRANCH -> {
|
||||
text.append("{{\"`");
|
||||
}
|
||||
}
|
||||
|
||||
text.append("**").append(getTitle()).append("**").append("\n\n");
|
||||
|
||||
for(Statement statement: getStatements()) {
|
||||
text.append(statement.toString(program, program.getLog().isVerboseLiveRanges())).append("\n");
|
||||
}
|
||||
|
||||
switch(getType()) {
|
||||
case SEQUENCE -> {
|
||||
text.append("`\"]\n");
|
||||
}
|
||||
case CALL -> {
|
||||
text.append("`\"]]\n");
|
||||
}
|
||||
case BRANCH -> {
|
||||
text.append("`\"}}\n");
|
||||
}
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public HashSet<MermaidFlow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void setFlows(HashSet<MermaidFlow> flows) {
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
public void addFlow(MermaidFlow flow) {
|
||||
this.flows.add(flow);
|
||||
}
|
||||
|
||||
public void addFlow(MermaidNode from, MermaidNode to, String title, MermaidFlow.Type type, MermaidFlow.Direction direction) {
|
||||
MermaidFlow flow = new MermaidFlow(from, to, title, type, direction);
|
||||
this.flows.add(flow);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Create sub graphics for mermaid report generation.
|
||||
* Sub graphs contain nodes, and can either be located as a single entity in root, or embedded in other sub graphs.
|
||||
* A sub graph models dependencies to other sub graphs.
|
||||
*/
|
||||
public class MermaidSubGraph extends Mermaid {
|
||||
|
||||
protected HashMap<String, MermaidNode> nodes;
|
||||
protected HashSet<MermaidFlow> flows;
|
||||
protected HashMap<String, MermaidSubGraph> procedureSubGraphs;
|
||||
|
||||
public MermaidSubGraph(String title) {
|
||||
super(toID(title), title);
|
||||
this.nodes = new HashMap<String, MermaidNode>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
this.procedureSubGraphs = new HashMap<String, MermaidSubGraph>();
|
||||
}
|
||||
|
||||
public MermaidSubGraph(String id, String title) {
|
||||
super(id, title);
|
||||
this.nodes = new HashMap<String, MermaidNode>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
this.procedureSubGraphs = new HashMap<String, MermaidSubGraph>();
|
||||
}
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
// subgraph and nodes
|
||||
text.append("subgraph ").append("sg_" + id).append("[\"").append(title).append("\"]\n");
|
||||
for(MermaidNode node : nodes.values()) {
|
||||
text.append(node.getText(program));
|
||||
}
|
||||
text.append("end\n\n");
|
||||
|
||||
// procedure subgraphs
|
||||
for(MermaidSubGraph procedureSubGraph : getProcedureSubGraphs().values()) {
|
||||
text.append(procedureSubGraph.getText(program));
|
||||
}
|
||||
|
||||
// flows to and from nodes (outside the subgraph)
|
||||
for(MermaidFlow flow : flows) {
|
||||
text.append(flow.getText(program));
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidNode> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(HashMap<String, MermaidNode> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(MermaidNode node) {
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(String nodeName) {
|
||||
MermaidNode node = new MermaidNode(nodeName);
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public HashSet<MermaidFlow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void setFlows(HashSet<MermaidFlow> flows) {
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
public void addFlow(MermaidFlow flow) {
|
||||
this.flows.add(flow);
|
||||
}
|
||||
|
||||
public void addFlow(MermaidNode from, MermaidNode to, String title, MermaidFlow.Type type, MermaidFlow.Direction direction) {
|
||||
MermaidFlow flow = new MermaidFlow(from, to, title, type, direction);
|
||||
this.flows.add(flow);
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidSubGraph> getProcedureSubGraphs() {
|
||||
return procedureSubGraphs;
|
||||
}
|
||||
|
||||
public void setProcedureSubGraphs(HashMap<String, MermaidSubGraph> procedureSubGraphs) {
|
||||
this.procedureSubGraphs = procedureSubGraphs;
|
||||
}
|
||||
|
||||
public MermaidSubGraph addProcedureSubGraph(MermaidSubGraph subGraph) {
|
||||
this.procedureSubGraphs.putIfAbsent(subGraph.id, subGraph);
|
||||
return subGraph;
|
||||
}
|
||||
|
||||
public MermaidSubGraph addProcedureSubGraph(String subGraphName) {
|
||||
MermaidSubGraph subGraph = new MermaidSubGraph(subGraphName);
|
||||
this.procedureSubGraphs.putIfAbsent(subGraph.id, subGraph);
|
||||
return subGraph;
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/dk/camelot64/kickc/passes/reports/Report.java
Normal file
57
src/main/java/dk/camelot64/kickc/passes/reports/Report.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.InternalError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Base class for compiler reports generated in separate files in the compiler output directory.
|
||||
*/
|
||||
public abstract class Report {
|
||||
|
||||
final protected Program program;
|
||||
protected String pass;
|
||||
protected String extension;
|
||||
|
||||
public String configureReport() {
|
||||
StringBuilder report = new StringBuilder();
|
||||
report.append("---\n");
|
||||
report.append("config:\n");
|
||||
report.append(" theme: dark\n");
|
||||
report.append(" flowchart:\n");
|
||||
report.append(" wrappingWidth: 800\n");
|
||||
report.append(" nodeSpacing : 40\n");
|
||||
report.append(" rankSpacing : 40\n");
|
||||
report.append(" titleTopMargin : 40\n");
|
||||
report.append(" padding : 10\n");
|
||||
report.append(" htmlLabels: false\n");
|
||||
report.append("---\n");
|
||||
return report.toString();
|
||||
}
|
||||
|
||||
abstract String generateReport(Program program);
|
||||
|
||||
Report(Program program, String pass, String extension) {
|
||||
this.program = program;
|
||||
this.pass = pass;
|
||||
this.extension = extension;
|
||||
};
|
||||
|
||||
public void generateReport() {
|
||||
Path reportPath = program.getOutputFileManager().getMermaidOutputFile(pass, extension);
|
||||
System.out.println("Generating report " + reportPath);
|
||||
try {
|
||||
FileOutputStream reportOutputStream = new FileOutputStream(reportPath.toFile());
|
||||
OutputStreamWriter reportWriter = new OutputStreamWriter(reportOutputStream);
|
||||
String reportDiagramString = generateReport(this.program);
|
||||
reportWriter.write(reportDiagramString);
|
||||
reportWriter.close();
|
||||
reportOutputStream.close();
|
||||
} catch(Exception e) {
|
||||
throw new InternalError("Error generating report " + reportPath, e);
|
||||
}
|
||||
}
|
||||
}
|
110
src/main/java/dk/camelot64/kickc/passes/reports/ReportCFG.java
Normal file
110
src/main/java/dk/camelot64/kickc/passes/reports/ReportCFG.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.model.ProcedureCompilation;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.ProcedureRef;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Create a report of the Control Block Flow Graph (CFG) of the whole Program.
|
||||
* This class generates a markdown report with embedded mermaid graphics of
|
||||
* the whole Program CFGs.
|
||||
*/
|
||||
public class ReportCFG extends Report {
|
||||
|
||||
private MermaidGraph mermaidGraph;
|
||||
|
||||
public ReportCFG(Program program, String pass, String extension) {
|
||||
super(program, pass, extension);
|
||||
}
|
||||
|
||||
public void mermaidCFGBlock(Graph graph, Graph.Block block, MermaidSubGraph mermaidSubGraph) {
|
||||
|
||||
String blockName = block.getLabel().getFullName();
|
||||
|
||||
MermaidNode node = mermaidSubGraph.addNode(new MermaidNode(blockName));
|
||||
|
||||
node.setStatements(block.getStatements());
|
||||
|
||||
List<Graph.Block> predecessors = program.getGraph().getPredecessors(block);
|
||||
if(!predecessors.isEmpty()) {
|
||||
for(Graph.Block predecessor : predecessors) {
|
||||
|
||||
String predecessorNodeName = predecessor.getLabel().getFullName();
|
||||
String predecessorLocalNodeName = predecessor.getScope().getLocalName();
|
||||
|
||||
if (program.isProcedureEntry(block)) {
|
||||
MermaidSubGraph procedureSubGraph = mermaidSubGraph.addProcedureSubGraph(predecessorLocalNodeName);
|
||||
MermaidNode nodePredecessor = procedureSubGraph.addNode(predecessorNodeName);
|
||||
mermaidSubGraph.addFlow(nodePredecessor, node, "call", MermaidFlow.Type.FULL, MermaidFlow.Direction.UNI);
|
||||
String blockReturnName = block.getLabel().getLocalName() + "::@return";
|
||||
MermaidNode nodeReturn = mermaidSubGraph.addNode(blockReturnName);
|
||||
mermaidSubGraph.addFlow(nodeReturn, nodePredecessor, "return", MermaidFlow.Type.FULL, MermaidFlow.Direction.UNI);
|
||||
} else {
|
||||
MermaidNode nodePredecessor = mermaidSubGraph.addNode(predecessorNodeName);
|
||||
LabelRef successorLabel = predecessor.getConditionalSuccessor();
|
||||
Graph.Block successor = null;
|
||||
if(successorLabel != null) {
|
||||
successor = graph.getBlock(successorLabel);
|
||||
}
|
||||
if( successor != null && successor.equals(block)) {
|
||||
mermaidSubGraph.addFlow(nodePredecessor, node, "true", MermaidFlow.Type.DOTTED, MermaidFlow.Direction.UNI);
|
||||
} else {
|
||||
mermaidSubGraph.addFlow(nodePredecessor, node, null, MermaidFlow.Type.DOTTED, MermaidFlow.Direction.UNI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!block.getStatements().isEmpty()) {
|
||||
for (Statement statement : block.getStatements()) {
|
||||
if(statement instanceof StatementCall) {
|
||||
String procedureName = ((StatementCall) statement).getProcedureName();
|
||||
MermaidSubGraph procedureSubGraph = mermaidSubGraph.addProcedureSubGraph(procedureName);
|
||||
MermaidNode procedureNode = procedureSubGraph.addNode(procedureName);
|
||||
mermaidSubGraph.addFlow(node, procedureNode,"call", MermaidFlow.Type.FULL, MermaidFlow.Direction.BI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String mermaidCFGProcedure(Procedure procedure, Graph graph) {
|
||||
String procedureName = procedure.getLocalName();
|
||||
String procedureTitle = procedure.toString(program);
|
||||
|
||||
MermaidSubGraph subGraphProcedure = new MermaidSubGraph(procedureName, procedureTitle);
|
||||
mermaidGraph.addProcedureSubGraph(subGraphProcedure);
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidCFGBlock(graph, block, subGraphProcedure);
|
||||
}
|
||||
|
||||
return mermaidGraph.getText(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateReport(Program program) {
|
||||
StringBuilder report = new StringBuilder();
|
||||
for(ProcedureCompilation procedureCompilation : program.getProcedureCompilations()) {
|
||||
Procedure procedure = program.getScope().getProcedure(procedureCompilation.getProcedureRef());
|
||||
Graph graph = procedureCompilation.getGraph();
|
||||
if(graph != null) {
|
||||
mermaidGraph = new MermaidGraph(MermaidGraph.Direction.TOP_DOWN);
|
||||
report.append("Procedure " + procedure.getFullName() + "\n");
|
||||
report.append("```mermaid\n");
|
||||
report.append(configureReport());
|
||||
report.append(mermaidCFGProcedure(procedure, graph));
|
||||
report.append("```\n\n");
|
||||
}
|
||||
}
|
||||
return report.toString();
|
||||
}
|
||||
}
|
100
src/main/java/dk/camelot64/kickc/passes/reports/ReportCG.java
Normal file
100
src/main/java/dk/camelot64/kickc/passes/reports/ReportCG.java
Normal file
|
@ -0,0 +1,100 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.model.ProcedureCompilation;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.ProcedureRef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Create a report of the Call Graph (CFG) of the procedures of the whole Program.
|
||||
* The report is generated in markdown with embedded mermaid graphics.
|
||||
*/
|
||||
public class ReportCG extends Report {
|
||||
|
||||
public MermaidGraph mermaidGraph;
|
||||
|
||||
public ReportCG(Program program, String pass, String extension) {
|
||||
super(program, pass, extension);
|
||||
this.mermaidGraph = new MermaidGraph(MermaidGraph.Direction.TOP_DOWN);
|
||||
generateReport();
|
||||
}
|
||||
|
||||
public ReportCG(Program program, String pass, String extension, MermaidGraph mermaidGraph) {
|
||||
super(program, pass, extension);
|
||||
this.mermaidGraph = mermaidGraph;
|
||||
generateReport();
|
||||
}
|
||||
|
||||
public void mermaidCGBlock(Graph.Block block, MermaidNode node) {
|
||||
|
||||
if(!block.getStatements().isEmpty()) {
|
||||
for (Statement statement : block.getStatements()) {
|
||||
if(statement instanceof StatementCall) {
|
||||
String procedureName = ((StatementCall) statement).getProcedureName();
|
||||
Procedure procedure = program.getScope().getProcedure(new ProcedureRef(procedureName));
|
||||
ProcedureCompilation procedureCompilation = program.getProcedureCompilation(new ProcedureRef(procedureName));
|
||||
Graph procedureGraph = procedureCompilation.getGraph();
|
||||
MermaidNode procedureNode = mermaidCGProcedure(procedure, procedureGraph);
|
||||
MermaidFlow flow = mermaidGraph.getFlow(node, procedureNode);
|
||||
String flowTitle = "1";
|
||||
if(flow != null) {
|
||||
flowTitle = flow.getTitle();
|
||||
Integer count = Integer.parseInt(flowTitle);
|
||||
count++;
|
||||
flowTitle = count.toString();
|
||||
mermaidGraph.removeFlow(flow);
|
||||
}
|
||||
mermaidGraph.addFlow(node, procedureNode, flowTitle, MermaidFlow.Type.FULL, MermaidFlow.Direction.BI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private MermaidNode mermaidCGProcedure(Procedure procedure, Graph graph) {
|
||||
String procedureName = procedure.getLocalName();
|
||||
String procedureTitle = procedure.toString(program);
|
||||
|
||||
MermaidNode node = new MermaidNode(procedureName, procedureTitle);
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidCGBlock(block, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private String mermaidCGRoot(Procedure procedure, Graph graph) {
|
||||
String procedureName = procedure.getLocalName();
|
||||
String procedureTitle = procedure.toString(program);
|
||||
|
||||
MermaidNode node = new MermaidNode(procedureName, procedureTitle);
|
||||
mermaidGraph.addNode(node);
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidCGBlock(block, node);
|
||||
}
|
||||
return mermaidGraph.getText(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateReport(Program program) {
|
||||
StringBuilder report = new StringBuilder();
|
||||
ProcedureCompilation procedureCompilation = program.getProcedureCompilation(new ProcedureRef("main"));
|
||||
if(procedureCompilation != null) {
|
||||
Procedure procedure = program.getScope().getProcedure(procedureCompilation.getProcedureRef());
|
||||
Graph graph = procedureCompilation.getGraph();
|
||||
if(graph != null) {
|
||||
report.append("Procedure " + procedure.getFullName() + "\n");
|
||||
report.append("```mermaid\n");
|
||||
report.append(configureReport());
|
||||
report.append(mermaidCGRoot(procedure, graph));
|
||||
report.append("```\n\n");
|
||||
}
|
||||
}
|
||||
return report.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.*;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.values.VariableRef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Creates a reference interference graph from the liveliness analysis and coalesce actions.
|
||||
*/
|
||||
public class ReportRIG extends Report {
|
||||
|
||||
public MermaidGraph mermaidGraph;
|
||||
|
||||
public ReportRIG(Program program, String pass, String extension) {
|
||||
super(program, pass, extension);
|
||||
}
|
||||
|
||||
public void mermaidRIGBlock(Graph.Block block, LiveRangeVariables liveRanges) {
|
||||
if(!block.getStatements().isEmpty()) {
|
||||
for (Statement statement : block.getStatements()) {
|
||||
List<VariableRef> aliveVariables = liveRanges.getAlive(statement.getIndex());
|
||||
int size = aliveVariables.size();
|
||||
if(size > 2) {
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
VariableRef varInner = aliveVariables.get(i);
|
||||
MermaidNode innerNode = mermaidGraph.addNode(varInner.getFullName());
|
||||
for (int o = i+1; o < size; o++) {
|
||||
VariableRef varOuter = aliveVariables.get(o);
|
||||
MermaidNode outerNode = mermaidGraph.addNode(varOuter.getFullName());
|
||||
mermaidGraph.addFlow(innerNode, outerNode, null, MermaidFlow.Type.FULL, MermaidFlow.Direction.BI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String mermaidLiveRanges(Procedure procedure, Graph graph, LiveRangeVariables liveRanges) {
|
||||
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidRIGBlock(block, liveRanges);
|
||||
}
|
||||
|
||||
return mermaidGraph.getText(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateReport(Program program) {
|
||||
StringBuilder report = new StringBuilder();
|
||||
LiveRangeVariables liveRanges = program.getLiveRangeVariables();
|
||||
for(ProcedureCompilation procedureCompilation : program.getProcedureCompilations()) {
|
||||
Procedure procedure = program.getScope().getProcedure(procedureCompilation.getProcedureRef());
|
||||
Graph graph = procedureCompilation.getGraph();
|
||||
if(graph != null) {
|
||||
report.append("Procedure " + procedure.getFullName() + "\n");
|
||||
report.append("```mermaid\n");
|
||||
mermaidGraph = new MermaidGraph(MermaidGraph.Direction.TOP_DOWN);
|
||||
report.append(configureReport());
|
||||
report.append(mermaidLiveRanges(procedure, graph, liveRanges));
|
||||
report.append("```\n\n");
|
||||
}
|
||||
}
|
||||
return report.toString();
|
||||
}
|
||||
}
|
26
src/test/kc/compile/coalesce/if.c
Normal file
26
src/test/kc/compile/coalesce/if.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
__export char p = *((char*)0x400);
|
||||
__export char a = *((char*)0x401);
|
||||
__export char b = *((char*)0x402);
|
||||
__export char r;
|
||||
|
||||
void main() {
|
||||
r = calc(a, b, p);
|
||||
}
|
||||
|
||||
char calc(char a, char b, char p) {
|
||||
|
||||
char x, y;
|
||||
|
||||
if(a<b) {
|
||||
x = 12;
|
||||
y = b;
|
||||
|
||||
} else {
|
||||
x = 42+b;
|
||||
y = a;
|
||||
}
|
||||
|
||||
return x+a+p;
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user