WIP Code Gen

This commit is contained in:
Gašper Dobrovoljc
2025-05-19 14:48:38 +02:00
parent b50325d3a5
commit 7fd97ed0b3
3 changed files with 1684 additions and 0 deletions

View File

@@ -0,0 +1,566 @@
package pins25.phase;
import java.util.*;
import pins25.common.*;
/**
* Generiranje kode.
*/
public class CodeGen {
@SuppressWarnings({"doclint:missing"})
public CodeGen() {
throw new Report.InternalError();
}
/**
* Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
* <p>
* Atributi:
* <ol>
* <li>({@link Abstr}) lokacija kode, ki pripada posameznemu vozliscu;</li>
* <li>({@link SemAn}) definicija uporabljenega imena;</li>
* <li>({@link SemAn}) ali je dani izraz levi izraz;</li>
* <li>({@link Memory}) klicni zapis funkcije;</li>
* <li>({@link Memory}) dostop do parametra;</li>
* <li>({@link Memory}) dostop do spremenljivke;</li>
* <li>({@link CodeGen}) seznam ukazov, ki predstavljajo kodo programa;</li>
* <li>({@link CodeGen}) seznam ukazov, ki predstavljajo podatke programa.</li>
* </ol>
*/
public static class AttrAST extends Memory.AttrAST {
/**
* Atribut: seznam ukazov, ki predstavljajo kodo programa.
*/
public final Map<AST.Node, List<PDM.CodeInstr>> attrCode;
/**
* Atribut: seznam ukazov, ki predstavljajo podatke programa.
*/
public final Map<AST.Node, List<PDM.DataInstr>> attrData;
/**
* Ustvari novo abstraktno sintaksno drevo z dodanimi atributi generiranja kode.
*
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi pomnilniske
* predstavitve.
* @param attrCode Attribut: seznam ukazov, ki predstavljajo kodo programa.
* @param attrData Attribut: seznam ukazov, ki predstavljajo podatke programa.
*/
public AttrAST(final Memory.AttrAST attrAST, final Map<AST.Node, List<PDM.CodeInstr>> attrCode,
final Map<AST.Node, List<PDM.DataInstr>> attrData) {
super(attrAST);
this.attrCode = attrCode;
this.attrData = attrData;
}
/**
* Ustvari novo abstraktno sintaksno drevo z dodanimi atributi generiranja kode.
*
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi generiranja
* kode.
*/
public AttrAST(final AttrAST attrAST) {
super(attrAST);
this.attrCode = attrAST.attrCode;
this.attrData = attrAST.attrData;
}
@Override
public String head(final AST.Node node, final boolean highlighted) {
final StringBuffer head = new StringBuffer();
head.append(super.head(node, false));
return head.toString();
}
@Override
public void desc(final int indent, final AST.Node node, final boolean highlighted) {
super.desc(indent, node, false);
System.out.print(highlighted ? "\033[31m" : "");
if (attrCode.get(node) != null) {
List<PDM.CodeInstr> instrs = attrCode.get(node);
if (instrs != null) {
if (indent > 0)
System.out.printf("%" + indent + "c", ' ');
System.out.printf("--- Code: ---\n");
for (final PDM.CodeInstr instr : instrs) {
if (indent > 0)
System.out.printf("%" + indent + "c", ' ');
System.out.println((instr instanceof PDM.LABEL ? "" : " ") + instr.toString());
}
}
}
if (attrData.get(node) != null) {
List<PDM.DataInstr> instrs = attrData.get(node);
if (instrs != null) {
if (indent > 0)
System.out.printf("%" + indent + "c", ' ');
System.out.printf("--- Data: ---\n");
for (final PDM.DataInstr instr : instrs) {
if (indent > 0)
System.out.printf("%" + indent + "c", ' ');
System.out.println((instr instanceof PDM.LABEL ? "" : " ") + instr.toString());
}
}
}
System.out.print(highlighted ? "\033[30m" : "");
return;
}
}
/**
* Izracuna kodo programa
*
* @param memoryAttrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna
* pomnilniske predstavitve.
* @return Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
*/
public static AttrAST generate(final Memory.AttrAST memoryAttrAST) {
AttrAST attrAST = new AttrAST(memoryAttrAST, new HashMap<AST.Node, List<PDM.CodeInstr>>(),
new HashMap<AST.Node, List<PDM.DataInstr>>());
(new CodeGenerator(attrAST)).generate();
return attrAST;
}
/**
* Generiranje kode v abstraktnem sintaksnem drevesu.
*/
private static class CodeGenerator {
/**
* Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
*/
private final AttrAST attrAST;
/**
* Stevec anonimnih label.
*/
private int labelCounter = 0;
/**
* Ustvari nov generator kode v abstraktnem sintaksnem drevesu.
*
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna
* pomnilniske predstavitve.
*/
public CodeGenerator(final AttrAST attrAST) {
this.attrAST = attrAST;
}
/**
* Sprozi generiranje kode v abstraktnem sintaksnem drevesu.
*
* @return Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
*/
public AttrAST generate() {
attrAST.ast.accept(new Generator(), null);
return new AttrAST(attrAST, Collections.unmodifiableMap(attrAST.attrCode),
Collections.unmodifiableMap(attrAST.attrData));
}
/**
* Obiskovalec, ki generira kodo v abstraktnem sintaksnem drevesu.
*/
private class Generator implements AST.FullVisitor<List<PDM.CodeInstr>, Mem.Frame> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public List<PDM.CodeInstr> visit(AST.FunDef funDef, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(funDef);
List<PDM.CodeInstr> code = new ArrayList<>();
code.add(new PDM.LABEL(funDef.name, loc));
code.addAll(funDef.stmts.accept(this, frame));
code.add(new PDM.PUSH(attrAST.attrFrame.get(funDef).parsSize, loc));
code.add(new PDM.RETN(attrAST.attrFrame.get(funDef), loc));
attrAST.attrCode.put(funDef, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.AtomExpr atomExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(atomExpr);
List<PDM.CodeInstr> code = new ArrayList<>();
switch (atomExpr.type) {
case INTCONST:
code.add(new PDM.PUSH(Memory.decodeIntConst(atomExpr, loc), loc));
break;
case CHRCONST:
code.add(new PDM.PUSH(Memory.decodeChrConst(atomExpr, loc), loc));
break;
case STRCONST:
List<PDM.DataInstr> data = new ArrayList<>();
String label = String.valueOf(labelCounter++);
data.add(new PDM.LABEL(label, loc));
for (Integer value : Memory.decodeStrConst(atomExpr, loc)) {
data.add(new PDM.DATA(value, loc));
}
attrAST.attrData.put(atomExpr, data);
code.add(new PDM.NAME(label, loc));
code.add(new PDM.LOAD(loc));
break;
}
attrAST.attrCode.put(atomExpr, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.BinExpr binExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(binExpr);
List<PDM.CodeInstr> code = new ArrayList<>();
// TODO: invert
code.addAll(binExpr.fstExpr.accept(this, frame));
code.addAll(binExpr.sndExpr.accept(this, frame));
PDM.OPER.Oper oper = switch (binExpr.oper) {
case OR -> PDM.OPER.Oper.OR;
case AND -> PDM.OPER.Oper.AND;
case EQU -> PDM.OPER.Oper.EQU;
case NEQ -> PDM.OPER.Oper.NEQ;
case GTH -> PDM.OPER.Oper.GTH;
case LTH -> PDM.OPER.Oper.LTH;
case GEQ -> PDM.OPER.Oper.GEQ;
case LEQ -> PDM.OPER.Oper.LEQ;
case ADD -> PDM.OPER.Oper.ADD;
case SUB -> PDM.OPER.Oper.SUB;
case MUL -> PDM.OPER.Oper.MUL;
case DIV -> PDM.OPER.Oper.DIV;
case MOD -> PDM.OPER.Oper.MOD;
};
code.add(new PDM.OPER(oper, loc));
attrAST.attrCode.put(binExpr, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.Nodes<? extends AST.Node> nodes, Mem.Frame frame) {
List<PDM.CodeInstr> code = new ArrayList<>();
for (AST.Node node : nodes) {
code.addAll(node.accept(this, frame));
}
attrAST.attrCode.put(nodes, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.ExprStmt exprStmt, Mem.Frame frame) {
return exprStmt.expr.accept(this, frame);
}
@Override
public List<PDM.CodeInstr> visit(AST.VarExpr varExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(varExpr);
List<PDM.CodeInstr> code = new ArrayList<>();
AST.VarDef def = (AST.VarDef) attrAST.attrDef.get(varExpr);
Mem.Access access = attrAST.attrVarAccess.get(def);
switch (access) {
case Mem.AbsAccess absAccess:
code.add(new PDM.NAME(absAccess.name, loc));
code.add(new PDM.LOAD(loc));
break;
case Mem.RelAccess relAccess:
code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc));
code.add(new PDM.PUSH(relAccess.offset, loc));
code.add(new PDM.OPER(PDM.OPER.Oper.ADD, loc));
code.add(new PDM.LOAD(loc));
break;
default:
}
attrAST.attrCode.put(varExpr, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.UnExpr unExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(unExpr);
List<PDM.CodeInstr> code = new ArrayList<>(unExpr.expr.accept(this, frame));
code.add(switch (unExpr.oper) {
case NOT -> new PDM.OPER(PDM.OPER.Oper.NOT, loc);
case ADD -> new PDM.OPER(PDM.OPER.Oper.ADD, loc); // TODO
case SUB -> new PDM.OPER(PDM.OPER.Oper.NEG, loc);
case MEMADDR -> new PDM.REGN(PDM.REGN.Reg.SP, loc);
case VALUEAT -> new PDM.LOAD(loc);
});
attrAST.attrCode.put(unExpr, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.CallExpr callExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(callExpr);
List<PDM.CodeInstr> code = new ArrayList<>();
for (int i = 0; i < callExpr.args.size(); i++) {
AST.Expr expr = (AST.Expr) callExpr.args.get(i);
code.addAll(expr.accept(this, frame));
code.add(new PDM.REGN(PDM.REGN.Reg.SP, loc));
code.add(new PDM.PUSH(0, loc));
code.add(new PDM.OPER(PDM.OPER.Oper.ADD, loc));
}
code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc)); // SL
code.add(new PDM.NAME(callExpr.name, loc));
code.add(new PDM.LOAD(loc));
code.add(new PDM.CALL(frame, loc));
attrAST.attrCode.put(callExpr, code);
return code;
}
}
}
/**
* Generator seznama ukazov, ki predstavljajo kodo programa.
*/
public static class CodeSegmentGenerator {
/**
* Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
*/
private final AttrAST attrAST;
/**
* Seznam ukazov za inicializacijo staticnih spremenljivk.
*/
private final Vector<PDM.CodeInstr> codeInitSegment = new Vector<PDM.CodeInstr>();
/**
* Seznam ukazov funkcij.
*/
private final Vector<PDM.CodeInstr> codeFunsSegment = new Vector<PDM.CodeInstr>();
/**
* Klicni zapis funkcije {@code main}.
*/
private Mem.Frame main = null;
/**
* Ustvari nov generator seznama ukazov, ki predstavljajo kodo programa.
*
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna
* pomnilniske predstavitve.
*/
public CodeSegmentGenerator(final AttrAST attrAST) {
this.attrAST = attrAST;
}
/**
* Izracuna seznam ukazov, ki predstavljajo kodo programa.
*
* @return Seznam ukazov, ki predstavljajo kodo programa.
*/
public List<PDM.CodeInstr> codeSegment() {
attrAST.ast.accept(new Generator(), null);
codeInitSegment.addLast(new PDM.PUSH(0, null));
codeInitSegment.addLast(new PDM.NAME("main", null));
codeInitSegment.addLast(new PDM.CALL(main, null));
codeInitSegment.addLast(new PDM.PUSH(0, null));
codeInitSegment.addLast(new PDM.NAME("exit", null));
codeInitSegment.addLast(new PDM.CALL(null, null));
final Vector<PDM.CodeInstr> codeSegment = new Vector<PDM.CodeInstr>();
codeSegment.addAll(codeInitSegment);
codeSegment.addAll(codeFunsSegment);
return Collections.unmodifiableList(codeSegment);
}
/**
* Obiskovalec, ki izracuna seznam ukazov, ki predstavljajo kodo programa.
*/
private class Generator implements AST.FullVisitor<Object, Object> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public Object visit(final AST.FunDef funDef, final Object arg) {
if (funDef.stmts.size() == 0)
return null;
List<PDM.CodeInstr> code = attrAST.attrCode.get(funDef);
codeFunsSegment.addAll(code);
funDef.pars.accept(this, arg);
funDef.stmts.accept(this, arg);
switch (funDef.name) {
case "main" -> main = attrAST.attrFrame.get(funDef);
}
return null;
}
@Override
public Object visit(final AST.VarDef varDef, final Object arg) {
switch (attrAST.attrVarAccess.get(varDef)) {
case Mem.AbsAccess __: {
List<PDM.CodeInstr> code = attrAST.attrCode.get(varDef);
codeInitSegment.addAll(code);
break;
}
case Mem.RelAccess __: {
break;
}
default:
throw new Report.InternalError();
}
return null;
}
}
}
/**
* Generator seznama ukazov, ki predstavljajo podatke programa.
*/
public static class DataSegmentGenerator {
/**
* Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske
* predstavitve.
*/
private final AttrAST attrAST;
/**
* Seznam ukazov, ki predstavljajo podatke programa.
*/
private final Vector<PDM.DataInstr> dataSegment = new Vector<PDM.DataInstr>();
/**
* Ustvari nov generator seznama ukazov, ki predstavljajo podatke programa.
*
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna
* pomnilniske predstavitve.
*/
public DataSegmentGenerator(final AttrAST attrAST) {
this.attrAST = attrAST;
}
/**
* Izracuna seznam ukazov, ki predstavljajo podatke programa.
*
* @return Seznam ukazov, ki predstavljajo podatke programa.
*/
public List<PDM.DataInstr> dataSegment() {
attrAST.ast.accept(new Generator(), null);
return Collections.unmodifiableList(dataSegment);
}
/**
* Obiskovalec, ki izracuna seznam ukazov, ki predstavljajo podatke programa.
*/
private class Generator implements AST.FullVisitor<Object, Object> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public Object visit(final AST.VarDef varDef, final Object arg) {
List<PDM.DataInstr> data = attrAST.attrData.get(varDef);
if (data != null)
dataSegment.addAll(data);
varDef.inits.accept(this, arg);
return null;
}
@Override
public Object visit(final AST.AtomExpr atomExpr, final Object arg) {
List<PDM.DataInstr> data = attrAST.attrData.get(atomExpr);
if (data != null)
dataSegment.addAll(data);
return null;
}
}
}
// --- ZAGON ---
/**
* Zagon izracuna pomnilniske predstavitve kot samostojnega programa.
*
* @param cmdLineArgs Argumenti v ukazni vrstici.
*/
public static void main(final String[] cmdLineArgs) {
System.out.println("This is PINS'25 compiler (code generation):");
try {
if (cmdLineArgs.length == 0)
throw new Report.Error("No source file specified in the command line.");
if (cmdLineArgs.length > 1)
Report.warning("Unused arguments in the command line.");
try (SynAn synAn = new SynAn(cmdLineArgs[0])) {
// abstraktna sintaksa:
final Abstr.AttrAST abstrAttrAST = Abstr.constructAST(synAn);
// semanticna analiza:
final SemAn.AttrAST semanAttrAST = SemAn.analyze(abstrAttrAST);
// pomnilniska predstavitev:
final Memory.AttrAST memoryAttrAST = Memory.organize(semanAttrAST);
// generiranje kode:
final AttrAST codegenAttrAST = CodeGen.generate(memoryAttrAST);
(new AST.Logger(codegenAttrAST)).log();
{
int addr = 0;
final List<PDM.CodeInstr> codeSegment = (new CodeSegmentGenerator(codegenAttrAST)).codeSegment();
{
System.out.println("\n\033[1mCODE SEGMENT:\033[0m");
for (final PDM.CodeInstr instr : codeSegment) {
System.out.printf("%8d [%s] %s\n", addr, instr.size(),
(instr instanceof PDM.LABEL ? "" : " ") + instr.toString());
addr += instr.size();
}
}
final List<PDM.DataInstr> dataSegment = (new DataSegmentGenerator(codegenAttrAST)).dataSegment();
{
System.out.println("\n\033[1mDATA SEGMENT:\033[0m");
for (final PDM.DataInstr instr : dataSegment) {
System.out.printf("%8d [%s] %s\n", addr, (instr instanceof PDM.SIZE) ? " " : instr.size(),
(instr instanceof PDM.LABEL ? "" : " ") + instr.toString());
addr += instr.size();
}
}
System.out.println();
}
}
// Upajmo, da kdaj pridemo to te tocke.
// A zavedajmo se sledecega:
// 1. Prevod je zaradi napak v programu lahko napacen :-o
// 2. Izvorni program se zdalec ni tisto, kar je programer hotel, da bi bil ;-)
Report.info("Done.");
} catch (Report.Error error) {
// Izpis opisa napake.
System.err.println(error.getMessage());
System.exit(1);
}
}
}