diff --git a/src/pp/s1184725/boppi/Annotations.java b/src/pp/s1184725/boppi/Annotations.java index aeda0de..1be492b 100644 --- a/src/pp/s1184725/boppi/Annotations.java +++ b/src/pp/s1184725/boppi/Annotations.java @@ -1,7 +1,5 @@ package pp.s1184725.boppi; -import java.util.Stack; - import org.antlr.v4.runtime.tree.ParseTreeProperty; import pp.iloc.model.Reg; @@ -28,11 +26,7 @@ public class Annotations { /** * Maps nodes to their function scope. */ - public ParseTreeProperty> function; - /** - * A stack with the current function scope on top. - */ - public Stack> currentFunction; + public ParseTreeProperty> function; /** * A symbol table */ @@ -46,7 +40,6 @@ public class Annotations { types = new ParseTreeProperty<>(); function = new ParseTreeProperty<>(); variables = new ParseTreeProperty<>(); - currentFunction = new Stack<>(); symbols = new CachingSymbolTable<>(); } } diff --git a/src/pp/s1184725/boppi/BoppiChecker.java b/src/pp/s1184725/boppi/BoppiChecker.java index 99b3458..abb8cb0 100644 --- a/src/pp/s1184725/boppi/BoppiChecker.java +++ b/src/pp/s1184725/boppi/BoppiChecker.java @@ -1,6 +1,5 @@ package pp.s1184725.boppi; -import java.util.*; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -9,6 +8,7 @@ import org.antlr.v4.runtime.tree.ParseTree; import pp.s1184725.boppi.antlr.*; import pp.s1184725.boppi.antlr.BoppiParser.*; +import pp.s1184725.boppi.exception.*; import pp.s1184725.boppi.type.*; /** @@ -77,8 +77,6 @@ public class BoppiChecker extends BoppiBaseVisitor { public Type visit(ParseTree tree) { Type type = super.visit(tree); an.types.put(tree, type); - if (an.function.get(tree) == null) - an.function.put(tree, an.currentFunction.peek()); return type; } @@ -129,9 +127,7 @@ public class BoppiChecker extends BoppiBaseVisitor { if (!(var.getType() instanceof SimpleType)) log.warning("Be careful only to pass pure functions outside their scope."); - } catch (EmptyStackException e) { - log.severe(getError(ctx, "Declaring variable outside a program.")); - } catch (Exception e) { + } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); } return SimpleType.VOID; @@ -145,23 +141,21 @@ public class BoppiChecker extends BoppiBaseVisitor { FunctionType type = new FunctionType(visit(ctx.result), parameterTypes); Variable func = an.symbols.put(ctx.name.getText(), type); an.variables.put(ctx, func); - an.currentFunction.push(func); - type.setLocalDataSize(an.symbols.withFunctionScope(() -> { + an.function.put(ctx, an.symbols.withFunctionScope(() -> { for (int i = 1; i < ctx.type().size(); i++) try { an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))); - } catch (Exception e) { + } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); } + checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx); })); - } catch (EmptyStackException e) { - log.severe(getError(ctx, "Declaring function outside a program.")); - } catch (Exception e) { + } catch (SymbolTableException e) { log.severe(getError(ctx, e.getMessage())); } - an.currentFunction.pop(); + return SimpleType.VOID; } @@ -279,9 +273,7 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitProgram(ProgramContext ctx) { - FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT); - an.currentFunction.push(new Variable(main, 0, 0)); - main.setLocalDataSize(an.symbols.withFunctionScope(() -> super.visitProgram(ctx))); + an.function.put(ctx, an.symbols.withFunctionScope(() -> super.visitProgram(ctx))); return null; } @@ -319,12 +311,17 @@ public class BoppiChecker extends BoppiBaseVisitor { public Type visitVariable(VariableContext ctx) { try { Variable var = an.symbols.get(ctx.getText()); + + try { + an.function.put(ctx, an.symbols.getFunctionScope()); + } catch (NoProgramException e) { + log.severe(getError(ctx, e.getMessage())); + } + an.variables.put(ctx, var); return var.getType(); - } catch (EmptyStackException e) { - log.severe(getError(ctx, "Using variable outside a program.")); - } catch (Exception e) { - log.severe(getError(ctx, e.getMessage() != null ? e.getMessage() : "unknown error")); + } catch (SymbolTableException e) { + log.severe(getError(ctx, e.getMessage())); } return SimpleType.VOID; diff --git a/src/pp/s1184725/boppi/BoppiGenerator.java b/src/pp/s1184725/boppi/BoppiGenerator.java index eb4d61a..5c5b054 100644 --- a/src/pp/s1184725/boppi/BoppiGenerator.java +++ b/src/pp/s1184725/boppi/BoppiGenerator.java @@ -1,40 +1,40 @@ package pp.s1184725.boppi; +import java.nio.file.Files; import java.util.*; import java.util.logging.Logger; -import java.util.stream.Collectors; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.*; import org.apache.commons.lang3.StringUtils; +import pp.iloc.Assembler; import pp.iloc.eval.Machine; import pp.iloc.model.*; import pp.s1184725.boppi.antlr.*; import pp.s1184725.boppi.antlr.BoppiParser.*; import pp.s1184725.boppi.type.*; +import pp.s1184725.boppi.util.RegisterPool; /** * The generator stage of the Boppi toolchain. * * @author Frank Wibbelink */ -public class BoppiGenerator extends BoppiBaseVisitor { - private static final int ARBASESIZE = 16; - private static final Operand ZERO = new Num(0); - private static final Operand OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8), - OFFSET_RETURN_VAL = new Num(-12), OFFSET_AL = new Num(-16); +public class BoppiGenerator extends BoppiBaseVisitor { + private static final int ARBASESIZE = 16, FUNCREF_SIZE = 12; + private static final Num ZERO = new Num(0); + private static final Reg ZERO_REG = new Reg("zero"); + private static final Num OFFSET_ARP = new Num(-4), OFFSET_RETURN_ADDR = new Num(-8), + OFFSET_RETURN_VAL = new Num(-12), OFFSET_AL = new Num(-16), OFFSET_FUNCREF_ADDR = ZERO, + OFFSET_FUNCREF_ARP = new Num(4), OFFSET_FUNCREF_ARSIZE = new Num(8); - /** - * Recommended number of registers to use before throwing a warning. - */ - public static int RECOMMENDED_REGISTER_COUNT = 10; private Program prog; private Annotations an; - private int regNum; private final Reg tempArp = new Reg("ART"), arp = Machine.ARP_REG; - private List regFree, regInUse; private Logger logger; + private RegisterPool regPool; + private int labels = 0; /** * Maps operator tokens to ILOC opcodes. @@ -77,87 +77,26 @@ public class BoppiGenerator extends BoppiBaseVisitor { } protected BoppiGenerator(Annotations annotations, Logger logger) { - an = annotations; - prog = new Program(); - regNum = 0; - regFree = new ArrayList<>(); - regInUse = new ArrayList<>(); + try { + String memlib = new String(Files.readAllBytes(ToolChain.PATH.resolve("memlib.iloc"))); + prog = Assembler.instance().assemble(memlib); + } catch (Exception e) { + throw new RuntimeException(e); + } + + this.an = annotations; + this.regPool = new RegisterPool(logger); this.logger = logger; } - private Reg makeReg(ParserRuleContext ctx) { - Reg reg; - if (regFree.isEmpty()) { - reg = new Reg("__" + (regInUse.size() + 1)); - regInUse.add(reg); - - if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1) - logger.warning( - String.format("Using more than %d registers at %d:%d. Consider rebalancing your expressions.", - RECOMMENDED_REGISTER_COUNT, ctx.start.getLine(), ctx.start.getCharPositionInLine())); - } else { - if (regFree.remove(an.registers.get(ctx))) - reg = an.registers.get(ctx); - else - reg = regFree.remove(0); - regInUse.add(reg); - } - an.registers.put(ctx, reg); - return reg; - } - - private void freeReg(ParserRuleContext ctx) { - Reg reg = an.registers.get(ctx); - if (reg == null) - return; - if (!regInUse.contains(reg)) { - logger.severe(getError(ctx, "INTERNAL: cannot free register %s since it is not in use.", reg.getName())); - } else { - regInUse.remove(reg); - regFree.add(reg); - } - } - - /** - * Retrieves a register for a given tree node. - * - * @param parseTree - * the tree node - * @return a register for the tree node - */ - private Reg getReg(ParseTree parseTree) { - Reg reg = an.registers.get(parseTree); - if (reg == null) { - if (Math.max(3, 4) > 2) - throw new RuntimeException("???" + parseTree.getText()); - reg = new Reg("r_" + (regNum++)); - an.registers.put(parseTree, reg); - } - return reg; - } - - /** - * Attaches the register used in the {@code source} tree node to the - * {@link destination} node. - * - * @param source - * the node to retrieve the register from - * @param destination - * the node to copy the register to - * @return the register that was copied - */ - private Reg copyReg(ParseTree source, ParseTree destination) { - Reg reg = an.registers.get(source); - an.registers.put(destination, reg); - return reg; - } - - private Reg getVar(ParserRuleContext node) { + private Reg getVar(VariableContext node) { Variable var = an.variables.get(node); - if (var.getDepth() <= an.function.get(node).getDepth()) { + int functionDepth = an.function.get(node).getFunctionDepth(); + + if (var.getDepth() < functionDepth) { emit("travelling ALs", OpCode.i2i, arp, tempArp); - for (int i = var.getDepth(); i <= an.function.get(node).getDepth(); i++) + for (int i = var.getDepth(); i < functionDepth; i++) emit(StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, tempArp, OFFSET_AL, tempArp); return tempArp; @@ -171,9 +110,7 @@ public class BoppiGenerator extends BoppiBaseVisitor { */ private Op emit(String comment, Label label, OpCode opCode, Operand... args) { Op result = new Op(label, opCode, args); - result.setComment(String.format("%-40s | %20s : %s", comment, - regInUse.stream().map((reg) -> reg.getName().substring(2)).collect(Collectors.joining(" ")), - regFree.stream().map((reg) -> reg.getName().substring(2)).collect(Collectors.joining(" ")))); + result.setComment(String.format("%s", comment)); this.prog.addInstr(result); return result; } @@ -186,6 +123,123 @@ public class BoppiGenerator extends BoppiBaseVisitor { return emit(comment, (Label) null, opCode, args); } + /** + * Increments the chain of ARs. + * + * @param temp + * temporary register to use + * @param ar + * register containing the top-level AR + */ + private void incARReferences(Reg temp, Reg ar) { + String comment = "AR incRef"; + Label loop = makeLabel("aril"), done = makeLabel("arid"); + emit(comment, OpCode.i2i, ar, tempArp); + emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cbr, temp, loop, done); + emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); + emit(comment, OpCode.push, temp); + emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp); + emit(comment, OpCode.push, temp); + emit(comment, OpCode.jumpI, new Label("memaddref")); + + emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); + emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cbr, temp, loop, done); + emit(comment, done, OpCode.nop); + } + + /** + * Decrements the chain of ARs. + * + * @param temp + * temporary register to use + * @param ar + * register containing the top-level AR + */ + private void decARReferences(Reg temp, Reg ar) { + String comment = "AR decRef"; + Label loop = makeLabel("ardl"), done = makeLabel("ardd"); + emit(comment, OpCode.i2i, ar, tempArp); + emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cbr, temp, loop, done); + emit(comment, loop, OpCode.loadI, new Num(prog.size() + 5), temp); + emit(comment, OpCode.push, temp); + emit(comment, OpCode.subI, tempArp, new Num(ARBASESIZE), temp); + emit(comment, OpCode.push, temp); + emit(comment, OpCode.jumpI, new Label("memfree")); + + emit(comment, OpCode.loadAI, tempArp, OFFSET_AL, tempArp); + emit(comment, OpCode.cmp_NE, tempArp, ZERO_REG, temp); + emit(comment, OpCode.cbr, temp, loop, done); + emit(comment, done, OpCode.nop); + } + + /** + * Allocates a chunk of memory and returns a pointer to the memory in the + * given register. + * + * @param temp + * temporary register to use + * @param size + * the size of the memory chunk + */ + private void malloc(Reg temp, int size) { + emit("malloc", OpCode.loadI, new Num(prog.size() + 5), temp); + emit("malloc", OpCode.push, temp); + emit("malloc", OpCode.loadI, new Num(size), temp); + emit("malloc", OpCode.push, temp); + emit("malloc", OpCode.jumpI, new Label("memalloc")); + emit("malloc", OpCode.pop, temp); + } + + /** + * Allocates a chunk of memory and returns a pointer to the memory in the + * given register. + * + * @param temp + * temporary register to use + * @param size + * the register containing the size of the memory chunk + */ + private void malloc(Reg temp, Reg size) { + emit("malloc", OpCode.loadI, new Num(prog.size() + 4), temp); + emit("malloc", OpCode.push, temp); + emit("malloc", OpCode.push, size); + emit("malloc", OpCode.jumpI, new Label("memalloc")); + emit("malloc", OpCode.pop, temp); + } + + /** + * Frees a chunk of memory pointed at by {@code ptr}. + * + * @param temp + * temporary register to use + * @param ptr + * the register containing the memory pointer + */ + private void free(Reg temp, Reg ptr) { + emit("free", OpCode.loadI, new Num(prog.size() + 4), temp); + emit("free", OpCode.push, temp); + emit("free", OpCode.push, ptr); + emit("free", OpCode.jumpI, new Label("memfree")); + } + + /** + * Increments the reference count of a memory chunk. + * + * @param temp + * temporary register to use + * @param ptr + * the register containing the memory pointer + */ + private void incRef(Reg temp, Reg ptr) { + emit("memaddref", OpCode.loadI, new Num(prog.size() + 4), temp); + emit("memaddref", OpCode.push, temp); + emit("memaddref", OpCode.push, ptr); + emit("memaddref", OpCode.jumpI, new Label("memaddref")); + } + /** * Returns an error message for a given parse tree node. * @@ -202,413 +256,597 @@ public class BoppiGenerator extends BoppiBaseVisitor { return String.format("Line %d:%d - %s", line, column, String.format(message, args)); } - @Override - public Void visitAssign(AssignContext ctx) { - visit(ctx.singleExpr()); - copyReg(ctx.singleExpr(), ctx); + private Label makeLabel(String prefix) { + return new Label(prefix + (labels++)); + } - if (SimpleType.CHAR.equals(an.types.get(ctx))) - emit("to " + ctx.variable().getText(), OpCode.cstoreAI, getReg(ctx), getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset())); - else - emit("to " + ctx.variable().getText(), OpCode.storeAI, getReg(ctx), getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset())); + private void incrementReference(Type type, Reg addr) { + regPool.withReg((temp) -> { + String comment = "add new reference"; - return null; + incRef(temp, addr); + + if (type instanceof FunctionType) { + emit(comment, OpCode.loadAI, addr, OFFSET_FUNCREF_ARP, addr); + + incARReferences(temp, addr); + } + }); + } + + private void decrementReference(Type type, Reg addr) { + regPool.withReg((temp) -> { + Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); + String comment = "remove old reference"; + + emit(comment, OpCode.cmp_EQ, addr, ZERO_REG, temp); + emit(comment, OpCode.cbr, temp, isNull, notNull); + + emit(comment, notNull, OpCode.nop); + + free(temp, addr); + + if (type instanceof FunctionType) { + emit(comment, OpCode.loadAI, addr, OFFSET_FUNCREF_ARP, addr); + + decARReferences(temp, addr); + } + + emit(comment, isNull, OpCode.nop); + }); + } + + private void cleanScope(FunctionScope scope) { + regPool.withReg((addr, ft) -> { + for (Variable var : scope.getVars()) { + Type type = var.getType(); + + if (type instanceof ReferenceType) { + Label isNull = makeLabel("ynul"), notNull = makeLabel("nnul"); + String comment = "remove reference"; + emit(comment + " get var", OpCode.loadAI, arp, new Num(var.getOffset()), addr); + + emit(comment, OpCode.cmp_EQ, addr, ZERO_REG, ft); + emit(comment, OpCode.cbr, ft, isNull, notNull); + + emit(comment, notNull, OpCode.nop); + + free(ft, addr); + + if (type instanceof FunctionType) { + emit(comment, OpCode.loadAI, addr, OFFSET_FUNCREF_ARP, addr); + + decARReferences(ft, addr); + } + + emit(comment, isNull, OpCode.nop); + } + } + + }); } @Override - public Void visitBlock(BlockContext ctx) { - super.visitBlock(ctx); - copyReg(ctx.expr(), ctx); - return null; + public Reg visit(ParseTree tree) { + try { + return super.visit(tree); + } catch (Exception e) { + e.printStackTrace(); + System.out.println(prog.prettyPrint()); + if (tree instanceof ParserRuleContext) + logger.severe(getError((ParserRuleContext) tree, "%s: %s", e.getClass().getName(), e.getMessage())); + else + logger.severe(String.format("Error %s: %s", e.getClass().getName(), e.getMessage())); + return null; + } } @Override - public Void visitCall(CallContext ctx) { + public Reg visitAssign(AssignContext ctx) { + + Reg result = visit(ctx.singleExpr()); + + regPool.blockReg(result, () -> { + Type type = an.types.get(ctx); + + if (type instanceof ReferenceType) { + regPool.withReg((addr) -> { + emit("remove old ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), addr); + decrementReference(an.types.get(ctx), addr); + }); + } + + if (SimpleType.CHAR.equals(type)) + emit("to " + ctx.variable().getText(), OpCode.cstoreAI, result, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset())); + else + emit("to " + ctx.variable().getText(), OpCode.storeAI, result, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset())); + + if (type instanceof ReferenceType) { + regPool.withReg((addr) -> { + emit("add new ref for " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), addr); + + incrementReference(an.types.get(ctx), addr); + }); + } + }); + + return result; + } + + @Override + public Reg visitBlock(BlockContext ctx) { + return visit(ctx.expr()); + } + + @Override + public Reg visitCall(CallContext ctx) { + String base = "call " + ctx.variable().getText() + " - "; Variable function = an.variables.get(ctx.variable()); TupleType parameters = (TupleType) ((FunctionType) function.getType()).getParameter(); + visit(ctx.variable()); - freeReg(ctx.variable()); - emit(base + "target address", OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); - emit(base + "target address", OpCode.push, getReg(ctx)); - freeReg(ctx); - for (int i = 0; i < ctx.expr().size(); i++) { - visit(ctx.expr(i)); + Reg result = regPool.withReg((ar) -> { + regPool.withReg((rt) -> { + emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), rt); + emit(base + "load AR size", OpCode.loadAI, rt, OFFSET_FUNCREF_ARSIZE, rt); + malloc(ar, rt); + }); + emit(base + "shift AR", OpCode.addI, ar, new Num(ARBASESIZE), ar); - if (SimpleType.CHAR.equals(parameters.get(i))) - emit(base + "param " + i, OpCode.cpush, getReg(ctx.expr(i))); - else - emit(base + "param " + i, OpCode.push, getReg(ctx.expr(i))); - freeReg(ctx.expr(i)); - } + for (int i = 0; i < ctx.expr().size(); i++) { + Reg exprReg = visit(ctx.expr(i)); - emit(base + "set new ARP", OpCode.addI, arp, - new Num(((FunctionType) an.function.get(ctx).getType()).getLocalDataSize() + ARBASESIZE), tempArp); - - makeReg(ctx); - for (int i = ctx.expr().size() - 1; i >= 0; i--) { - if (SimpleType.CHAR.equals(parameters.get(i))) { - emit(base + "param " + i, OpCode.cpop, getReg(ctx)); - emit(base + "param " + i, OpCode.cstoreAI, getReg(ctx), tempArp, new Num(parameters.getOffset(i))); - } else { - emit(base + "param " + i, OpCode.pop, getReg(ctx)); - emit(base + "param " + i, OpCode.storeAI, getReg(ctx), tempArp, new Num(parameters.getOffset(i))); + if (SimpleType.CHAR.equals(parameters.get(i))) + emit(base + "store param " + i, OpCode.cstoreAI, exprReg, ar, new Num(parameters.getOffset(i))); + else + emit(base + "store param " + i, OpCode.storeAI, exprReg, ar, new Num(parameters.getOffset(i))); } - } - emit(base + "link caller ARP", OpCode.storeAI, arp, tempArp, OFFSET_ARP); + Stack inUse = new Stack<>(); + for (Reg reg : regPool.getInUse()) { + if (!reg.equals(ar)) { + inUse.push(reg); + emit(base + "register save " + reg.getName(), OpCode.push, reg); + } + } - emit(base + "travelling ALs", OpCode.i2i, arp, getReg(ctx)); - for (int i = an.variables.get(ctx.variable()).getDepth(); i <= an.function.get(ctx).getDepth(); i++) - emit(base + StringUtils.repeat(' ', i) + "\\", OpCode.loadAI, getReg(ctx), OFFSET_AL, getReg(ctx)); - emit(base + "link caller AL", OpCode.storeAI, getReg(ctx), tempArp, OFFSET_AL); + Reg res = regPool.withReg((r1, r2) -> { + emit(base + "load function reference", OpCode.loadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), r2); + emit(base + "link caller ARP", OpCode.storeAI, arp, ar, OFFSET_ARP); + emit(base + "load AL", OpCode.loadAI, r2, OFFSET_FUNCREF_ARP, r1); + emit(base + "link AL", OpCode.storeAI, r1, ar, OFFSET_AL); - Label label = new Label("ret" + ctx.hashCode()); - emit(base + "load return address", OpCode.loadI, new Num(label), getReg(ctx)); - emit(base + "set return address", OpCode.storeAI, getReg(ctx), tempArp, OFFSET_RETURN_ADDR); - emit(base + "move ARP", OpCode.i2i, tempArp, arp); - emit(base + "target address", OpCode.pop, getReg(ctx)); - freeReg(ctx); + emit("add ref for callee's AL", OpCode.loadAI, ar, OFFSET_AL, tempArp); + incARReferences(r1, tempArp); - Stack inUse = new Stack<>(); - for (Reg reg : regInUse) { - inUse.push(reg); - emit(base + "register save " + reg.getName(), OpCode.push, reg); - } + emit(base + "load return address", OpCode.loadI, new Num(prog.size() + 5), r1); + emit(base + "set return address", OpCode.storeAI, r1, ar, OFFSET_RETURN_ADDR); + emit(base + "move ARP", OpCode.i2i, ar, arp); - emit(base + "execute", OpCode.jump, getReg(ctx)); + emit(base + "load target address", OpCode.loadAI, r2, OFFSET_FUNCREF_ADDR, r1); + emit(base + "execute", OpCode.jump, r1); + + emit(base + "load result", OpCode.loadAI, arp, OFFSET_RETURN_VAL, r2); + }); + + while (!inUse.isEmpty()) { + Reg reg = inUse.pop(); + emit(base + "register unsave " + reg.getName(), OpCode.pop, reg); + } + + return res; + + }); + + regPool.withReg(result, (temp) -> { + decARReferences(temp, arp); + }); - emit(base + "load result", label, OpCode.loadAI, arp, OFFSET_RETURN_VAL, makeReg(ctx)); - while (!inUse.isEmpty()) { - Reg reg = inUse.pop(); - emit(base + "restoring register save " + reg.getName(), OpCode.pop, reg); - } emit(base + "reset ARP", OpCode.loadAI, arp, OFFSET_ARP, arp); - return null; + return result; } + /** + * Declare named function: + *
    + *
  1. generate jump over function body
  2. + *
  3. generate entry point
  4. + *
  5. visit function body
  6. + *
  7. generate return
  8. + *
  9. generate allocate function reference
  10. + *
  11. generate store entry point and AR to function reference
  12. + *
  13. generate store function reference in variable
  14. + *
+ */ @Override - public Void visitDeclare(DeclareContext ctx) { - return null; - } + public Reg visitDeclareFunction(DeclareFunctionContext ctx) { - @Override - public Void visitDeclareFunction(DeclareFunctionContext ctx) { String base = "define " + ctx.IDENTIFIER(0).getText() + " - "; - Label skip = new Label("s" + ctx.hashCode()); + Label skip = makeLabel("s"); emit(base + "jump over body", OpCode.jumpI, skip); int targetAddress = emit(base + "entry point", OpCode.nop).getLine(); - visit(ctx.body); - emit(base + "move result", OpCode.storeAI, getReg(ctx.body), arp, OFFSET_RETURN_VAL); - freeReg(ctx.body); - emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, makeReg(ctx)); - emit(base + "go to return address", OpCode.jump, getReg(ctx)); - emit(base + "load target address", skip, OpCode.loadI, new Num(targetAddress), getReg(ctx)); - emit(base + "set target address", OpCode.storeAI, getReg(ctx), getVar(ctx), - new Num(an.variables.get(ctx).getOffset())); - freeReg(ctx); - an.registers.removeFrom(ctx); + + Reg result = visit(ctx.body); + emit(base + "move result", OpCode.storeAI, result, arp, OFFSET_RETURN_VAL); + + cleanScope(an.function.get(ctx)); + + regPool.withReg((r1, r2) -> { + emit(base + "load return address", OpCode.loadAI, arp, OFFSET_RETURN_ADDR, r1); + emit(base + "go to return address", OpCode.jump, r1); + + emit(base + "skip target", skip, OpCode.nop); + malloc(r2, FUNCREF_SIZE); + emit(base + "load target address", OpCode.loadI, new Num(targetAddress), r1); + emit(base + "set target address", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ADDR); + emit(base + "copy ARP", OpCode.storeAI, arp, r2, OFFSET_FUNCREF_ARP); + emit(base + "load AR size", OpCode.loadI, new Num(ARBASESIZE + an.function.get(ctx).getLocalDataSize()), + r1); + emit(base + "set AR size", OpCode.storeAI, r1, r2, OFFSET_FUNCREF_ARSIZE); + + emit(base + "set function reference", OpCode.storeAI, r2, arp, new Num(an.variables.get(ctx).getOffset())); + incARReferences(r2, arp); + }); + return null; } @Override - public Void visitExpr(ExprContext ctx) { + public Reg visitExpr(ExprContext ctx) { + for (int i = 0; i < ctx.singleExpr().size() - 1; i++) { - visit(ctx.singleExpr(i)); - freeReg(ctx.singleExpr(i)); + ParserRuleContext expr = ctx.singleExpr(i); + Reg result = visit(expr); + + if (result != null) + regPool.blockReg(result, () -> { + Type type = an.types.get(expr); + + if (type instanceof ReferenceType) + decrementReference(type, result); + }); } ParserRuleContext last = ctx.singleExpr(ctx.singleExpr().size() - 1); - visit(last); - copyReg(last, ctx); - return null; + + return visit(last); } @Override - public Void visitIf(IfContext ctx) { - Label toTrue = new Label("if_t" + ctx.hashCode()); - Label toFalse = new Label("if_f" + ctx.hashCode()); - Label toEnd = new Label("if_e" + ctx.hashCode()); + public Reg visitIf(IfContext ctx) { + Label toTrue = makeLabel("if_t"); + Label toFalse = makeLabel("if_f"); + Label toEnd = makeLabel("if_e"); + Reg cond = visit(ctx.cond); - visit(ctx.cond); - if (ctx.onFalse == null) { - emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toEnd); - freeReg(ctx.cond); + if (an.types.get(ctx) != SimpleType.VOID) { + return regPool.withReg((target) -> { + emit("", OpCode.cbr, cond, toTrue, toFalse); - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - freeReg(ctx.onTrue); - } else { - emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toFalse); - freeReg(ctx.cond); + emit("", toTrue, OpCode.nop); + emit("result", OpCode.i2i, visit(ctx.onTrue), target); + emit("", OpCode.jumpI, toEnd); - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - freeReg(ctx.onTrue); - if (an.types.get(ctx) != SimpleType.VOID) - emit("result", OpCode.i2i, getReg(ctx.onTrue), makeReg(ctx)); - emit("", OpCode.jumpI, toEnd); - freeReg(ctx); - - emit("", toFalse, OpCode.nop); - visit(ctx.onFalse); - freeReg(ctx.onFalse); - if (an.types.get(ctx) != SimpleType.VOID) - emit("result", OpCode.i2i, getReg(ctx.onFalse), makeReg(ctx)); + emit("", toFalse, OpCode.nop); + emit("result", OpCode.i2i, visit(ctx.onFalse), target); + emit("end target", toEnd, OpCode.nop); + }); } - emit("end target", toEnd, OpCode.nop); + + if (ctx.onFalse == null) { + emit("", OpCode.cbr, cond, toTrue, toEnd); + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + emit("end target", toEnd, OpCode.nop); + } else { + emit("", OpCode.cbr, cond, toTrue, toFalse); + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + emit("", OpCode.jumpI, toEnd); + emit("", toFalse, OpCode.nop); + visit(ctx.onFalse); + emit("end target", toEnd, OpCode.nop); + } return null; } @Override - public Void visitInfix1(Infix1Context ctx) { - visitChildren(ctx); - emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), - copyReg(ctx.lhs, ctx)); - freeReg(ctx.rhs); - return null; + public Reg visitInfix1(Infix1Context ctx) { + Reg lhs = visit(ctx.lhs); + Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); + emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs); + + return lhs; } @Override - public Void visitInfix2(Infix2Context ctx) { - visitChildren(ctx); - emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), - copyReg(ctx.lhs, ctx)); - freeReg(ctx.rhs); - return null; + public Reg visitInfix2(Infix2Context ctx) { + Reg lhs = visit(ctx.lhs); + Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); + emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs); + + return lhs; } @Override - public Void visitInfix3(Infix3Context ctx) { - visitChildren(ctx); - emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), - copyReg(ctx.lhs, ctx)); - freeReg(ctx.rhs); - return null; + public Reg visitInfix3(Infix3Context ctx) { + Reg lhs = visit(ctx.lhs); + Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); + emit(ctx.getChild(1).getText(), ops.get(ctx.op.getType()), lhs, rhs, lhs); + + return lhs; } @Override - public Void visitInfix4(Infix4Context ctx) { - visitChildren(ctx); - emit(ctx.getChild(1).getText(), ops.get(ctx.AND().getSymbol().getType()), getReg(ctx.lhs), getReg(ctx.rhs), - copyReg(ctx.lhs, ctx)); - freeReg(ctx.rhs); - return null; + public Reg visitInfix4(Infix4Context ctx) { + Reg lhs = visit(ctx.lhs); + Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); + emit(ctx.getChild(1).getText(), ops.get(ctx.AND().getSymbol().getType()), lhs, rhs, lhs); + + return lhs; } @Override - public Void visitInfix5(Infix5Context ctx) { - visitChildren(ctx); - emit(ctx.getChild(1).getText(), ops.get(ctx.OR().getSymbol().getType()), getReg(ctx.lhs), getReg(ctx.rhs), - copyReg(ctx.lhs, ctx)); - freeReg(ctx.rhs); - return null; + public Reg visitInfix5(Infix5Context ctx) { + Reg lhs = visit(ctx.lhs); + Reg rhs = regPool.blockReg(lhs, () -> visit(ctx.rhs)); + emit(ctx.getChild(1).getText(), ops.get(ctx.OR().getSymbol().getType()), lhs, rhs, lhs); + + return lhs; } @Override - public Void visitLiteralBoolean(LiteralBooleanContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), makeReg(ctx)); - return null; + public Reg visitLiteralBoolean(LiteralBooleanContext ctx) { + return regPool.withReg((reg) -> { + emit(ctx.getText(), OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), reg); + }); } @Override - public Void visitLiteralCharacter(LiteralCharacterContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), makeReg(ctx)); - emit(ctx.getText(), OpCode.i2c, getReg(ctx), getReg(ctx)); - return null; + public Reg visitLiteralCharacter(LiteralCharacterContext ctx) { + return regPool.withReg((reg) -> { + emit(ctx.getText(), OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), reg); + emit(ctx.getText(), OpCode.i2c, reg, reg); + }); } @Override - public Void visitLiteralInteger(LiteralIntegerContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), makeReg(ctx)); - return null; + public Reg visitLiteralInteger(LiteralIntegerContext ctx) { + return regPool.withReg((reg) -> { + emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), reg); + }); } @Override - public Void visitParens(ParensContext ctx) { - super.visitParens(ctx); - copyReg(ctx.expr(), ctx); - return null; + public Reg visitParens(ParensContext ctx) { + return visit(ctx.expr()); } @Override - public Void visitPrefix1(Prefix1Context ctx) { - visitChildren(ctx); - copyReg(ctx.singleExpr(), ctx); + public Reg visitPrefix1(Prefix1Context ctx) { + Reg reg = visit(ctx.singleExpr()); switch (ctx.op.getType()) { case BoppiLexer.MINUS: - emit("unary -", OpCode.rsubI, getReg(ctx), ZERO, getReg(ctx)); + emit("unary -", OpCode.rsubI, reg, ZERO, reg); break; case BoppiLexer.NOT: - emit("not", OpCode.xorI, getReg(ctx), new Num(1), getReg(ctx)); + emit("not", OpCode.xorI, reg, new Num(1), reg); break; } + return reg; + } + + @Override + public Reg visitProgram(ProgramContext ctx) { + emit("initialise zero register", OpCode.loadI, ZERO, ZERO_REG); + + malloc(arp, an.function.get(ctx).getLocalDataSize() + ARBASESIZE); + emit("construct main AR", OpCode.addI, arp, new Num(ARBASESIZE), arp); + visitChildren(ctx); + + cleanScope(an.function.get(ctx)); + + emit("deconstruct main AR", OpCode.subI, arp, new Num(ARBASESIZE), arp); + regPool.withReg((temp) -> { + free(temp, arp); + }); + return null; } @Override - public Void visitProgram(ProgramContext ctx) { - emit("initialise temp ARP", OpCode.loadI, ZERO, tempArp); - - return visitChildren(ctx); - } - - @Override - public Void visitRead(ReadContext ctx) { + public Reg visitRead(ReadContext ctx) { if (ctx.variable().size() > 1) { for (VariableContext expr : ctx.variable()) { visit(expr); if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { - emit("", OpCode.in, new Str(""), makeReg(ctx)); - emit("var " + expr.getText(), OpCode.storeAI, getReg(ctx), getVar(expr), - new Num(an.variables.get(expr).getOffset())); + regPool.withReg((temp) -> { + emit("", OpCode.in, new Str(""), temp); + emit("var " + expr.getText(), OpCode.storeAI, temp, getVar(expr), + new Num(an.variables.get(expr).getOffset())); + }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { // Get input until at least 1 character - Label getTarget = new Label("lcin_l" + ctx.hashCode()); - Label continueTarget = new Label("lcin_e" + ctx.hashCode()); + Label getTarget = makeLabel("lcin_l"); + Label continueTarget = makeLabel("lcin_e"); - Reg countReg = makeReg(ctx); - makeReg(ctx); + regPool.withReg((temp, count) -> { + emit("", getTarget, OpCode.cin, new Str("")); + emit("str length", OpCode.pop, count); + emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); - emit("", getTarget, OpCode.cin, new Str("")); - emit("str length", OpCode.pop, countReg); - emit("repeat if 0 length", OpCode.cbr, countReg, continueTarget, getTarget); + // Get character + emit("pop whole string", continueTarget, OpCode.cpop, temp); + emit("var " + expr.getText(), OpCode.cstoreAI, temp, getVar(expr), + new Num(an.variables.get(expr).getOffset())); - // Get character - emit("pop whole string", continueTarget, OpCode.cpop, getReg(ctx)); - emit("var " + expr.getText(), OpCode.cstoreAI, getReg(ctx), getVar(expr), - new Num(an.variables.get(expr).getOffset())); - - // Pop all remaining characters - Label loopTarget = new Label("lcpop_l" + ctx.hashCode()); - Label iterTarget = new Label("lcpop_c" + ctx.hashCode()); - Label stopTarget = new Label("lcpop_e" + ctx.hashCode()); - emit("pop remaining", loopTarget, OpCode.subI, countReg, new Num(1), countReg); - emit("pop remaining", OpCode.cbr, countReg, iterTarget, stopTarget); - emit("pop remaining", iterTarget, OpCode.cpop, getReg(ctx)); - emit("pop remaining", OpCode.jumpI, loopTarget); - emit("pop remaining", stopTarget, OpCode.nop); + // Pop all remaining characters + Label loopTarget = makeLabel("lcpop_l"); + Label iterTarget = makeLabel("lcpop_c"); + Label stopTarget = makeLabel("lcpop_e"); + emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), temp); + emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); + emit("pop remaining", iterTarget, OpCode.cpop, temp); + emit("pop remaining", OpCode.jumpI, loopTarget); + emit("pop remaining", stopTarget, OpCode.nop); + }); } else { - emit("????????", OpCode.out, new Str("Unknown type: "), copyReg(expr, ctx)); + emit("reading unknown type", OpCode.haltI, new Num(1)); } - freeReg(ctx); } + + return null; } else { VariableContext expr = ctx.variable(0); visit(expr); if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { - emit("", OpCode.in, new Str(""), makeReg(ctx)); - emit("var " + expr.getText(), OpCode.storeAI, getReg(ctx), getVar(expr), - new Num(an.variables.get(expr).getOffset())); + return regPool.withReg((result) -> { + emit("", OpCode.in, new Str(""), result); + emit("var " + expr.getText(), OpCode.storeAI, result, getVar(expr), + new Num(an.variables.get(expr).getOffset())); + }); } else if (SimpleType.CHAR.equals(an.types.get(expr))) { - // Get input until at least 1 character - Label getTarget = new Label("lcin_l" + ctx.hashCode()); - Label continueTarget = new Label("lcin_e" + ctx.hashCode()); + return regPool.withReg((count, result) -> { + // Get input until at least 1 character + Label getTarget = makeLabel("lcin_l"); + Label continueTarget = makeLabel("lcin_e"); - Reg countReg = makeReg(ctx); - makeReg(ctx); + emit("", getTarget, OpCode.cin, new Str("")); + emit("str length", OpCode.pop, count); + emit("repeat if 0 length", OpCode.cbr, count, continueTarget, getTarget); - emit("", getTarget, OpCode.cin, new Str("")); - emit("str length", OpCode.pop, countReg); - emit("repeat if 0 length", OpCode.cbr, countReg, continueTarget, getTarget); + // Get character + emit("pop whole string", continueTarget, OpCode.cpop, result); + emit("var " + expr.getText(), OpCode.cstoreAI, result, getVar(expr), + new Num(an.variables.get(expr).getOffset())); - // Get character - emit("pop whole string", continueTarget, OpCode.cpop, getReg(ctx)); - emit("var " + expr.getText(), OpCode.cstoreAI, getReg(ctx), getVar(expr), - new Num(an.variables.get(expr).getOffset())); - - // Pop all remaining characters - Label loopTarget = new Label("lcpop_l" + ctx.hashCode()); - Label iterTarget = new Label("lcpop_c" + ctx.hashCode()); - Label stopTarget = new Label("lcpop_e" + ctx.hashCode()); - emit("pop remaining", loopTarget, OpCode.subI, countReg, new Num(1), countReg); - emit("pop remaining", OpCode.cbr, countReg, iterTarget, stopTarget); - emit("pop remaining", iterTarget, OpCode.cpop, getReg(ctx)); - emit("pop remaining", OpCode.jumpI, loopTarget); - emit("pop remaining", stopTarget, OpCode.nop); - // Reload value - emit("var " + expr.getText(), OpCode.cloadAI, getVar(expr), new Num(an.variables.get(expr).getOffset()), - getReg(ctx)); + // Pop all remaining characters + Label loopTarget = makeLabel("lcpop_l"); + Label iterTarget = makeLabel("lcpop_c"); + Label stopTarget = makeLabel("lcpop_e"); + regPool.withReg((temp) -> { + emit("pop remaining", loopTarget, OpCode.subI, count, new Num(1), count); + emit("pop remaining", OpCode.cbr, count, iterTarget, stopTarget); + emit("pop remaining", iterTarget, OpCode.cpop, temp); + emit("pop remaining", OpCode.jumpI, loopTarget); + emit("pop remaining", stopTarget, OpCode.nop); + }); + }); + } else { + emit("reading unknown type", OpCode.haltI, new Num(1)); + return null; } } - return null; } @Override - public Void visitVariableExpr(VariableExprContext ctx) { - if (SimpleType.CHAR.equals(an.types.get(ctx))) - emit("var " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); - else - emit("var " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); + public Reg visitVariableExpr(VariableExprContext ctx) { + Type type = an.types.get(ctx); + Num offset = new Num(an.variables.get(ctx.variable()).getOffset()); - return null; + return regPool.withReg((result) -> { + if (SimpleType.CHAR.equals(type)) + emit("get " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()), offset, result); + else + emit("get " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), offset, result); + + if (type instanceof ReferenceType) { + regPool.withReg((r1, r2) -> { + String comment = "add new ref for " + ctx.variable().getText(); + + incRef(r2, result); + + if (type instanceof FunctionType) { + emit(comment, OpCode.loadAI, result, OFFSET_FUNCREF_ARP, r1); + + incARReferences(r2, r1); + } + }); + } + + }); } @Override - public Void visitWhile(WhileContext ctx) { - Label toLoop = new Label("while_t" + ctx.hashCode()); - Label toCond = new Label("while_f" + ctx.hashCode()); - Label toEnd = new Label("while_e" + ctx.hashCode()); + public Reg visitWhile(WhileContext ctx) { + Label toLoop = makeLabel("while_t"); + Label toCond = makeLabel("while_f"); + Label toEnd = makeLabel("while_e"); emit("to condition", OpCode.jumpI, toCond); emit("loop target", toLoop, OpCode.nop); visit(ctx.onTrue); - freeReg(ctx.onTrue); + emit("condition target", toCond, OpCode.nop); - visit(ctx.cond); - freeReg(ctx.cond); - emit("", OpCode.cbr, getReg(ctx.cond), toLoop, toEnd); + emit("", OpCode.cbr, visit(ctx.cond), toLoop, toEnd); emit("end target", toEnd, OpCode.nop); return null; } @Override - public Void visitWrite(WriteContext ctx) { + public Reg visitWrite(WriteContext ctx) { if (ctx.expr().size() > 1) { for (ExprContext expr : ctx.expr()) { - visit(expr); - if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { - emit("", OpCode.out, new Str(""), getReg(expr)); - } else if (SimpleType.CHAR.equals(an.types.get(expr))) { - makeReg(ctx); - emit("push character", OpCode.cpush, getReg(expr)); - emit("load 1", OpCode.loadI, new Num(1), getReg(ctx)); - emit("push 1", OpCode.push, getReg(ctx)); - emit("print character", OpCode.cout, new Str("")); - freeReg(ctx); - } else { - emit("??????????????", OpCode.out, new Str("Unknown type: "), getReg(expr)); - } - freeReg(expr); + Reg result = visit(expr); + + regPool.blockReg(result, () -> { + Type type = an.types.get(expr); + + if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) { + emit("", OpCode.out, new Str(""), result); + } else if (SimpleType.CHAR.equals(type)) { + regPool.withReg((temp) -> { + emit("push character", OpCode.cpush, result); + emit("load 1", OpCode.loadI, new Num(1), temp); + emit("push 1", OpCode.push, temp); + emit("print character", OpCode.cout, new Str("")); + }); + } else { + emit("writing unknown type", OpCode.haltI, new Num(1)); + } + + if (!expr.equals(ctx.expr(ctx.expr().size() - 1))) + if (type instanceof ReferenceType) + decrementReference(type, result); + }); } - an.registers.removeFrom(ctx); + return null; } else { ExprContext expr = ctx.expr(0); - visit(expr); - if (SimpleType.BOOL.equals(an.types.get(expr)) || SimpleType.INT.equals(an.types.get(expr))) { - emit("", OpCode.out, new Str(""), getReg(expr)); - } else if (SimpleType.CHAR.equals(an.types.get(expr))) { - makeReg(ctx); - emit("push character", OpCode.cpush, getReg(expr)); - emit("load 1", OpCode.loadI, new Num(1), getReg(ctx)); - emit("push 1", OpCode.push, getReg(ctx)); - emit("print character", OpCode.cout, new Str("")); - freeReg(ctx); - } + Reg result = visit(expr); - copyReg(expr, ctx); + return regPool.blockReg(result, () -> { + Type type = an.types.get(expr); + + if (SimpleType.BOOL.equals(type) || SimpleType.INT.equals(type)) { + emit("", OpCode.out, new Str(""), result); + return result; + } else if (SimpleType.CHAR.equals(type)) { + regPool.withReg(result, (temp) -> { + emit("push character", OpCode.cpush, result); + emit("load 1", OpCode.loadI, new Num(1), temp); + emit("push 1", OpCode.push, temp); + emit("print character", OpCode.cout, new Str("")); + }); + return result; + } else { + emit("writing unknown type", OpCode.haltI, new Num(1)); + return null; + } + }); } - return null; } } diff --git a/src/pp/s1184725/boppi/CachingSymbolTable.java b/src/pp/s1184725/boppi/CachingSymbolTable.java index bf4af66..40f1eec 100644 --- a/src/pp/s1184725/boppi/CachingSymbolTable.java +++ b/src/pp/s1184725/boppi/CachingSymbolTable.java @@ -3,6 +3,7 @@ package pp.s1184725.boppi; import java.util.*; import java.util.function.Supplier; +import pp.s1184725.boppi.exception.*; import pp.s1184725.boppi.type.*; /** @@ -19,12 +20,8 @@ import pp.s1184725.boppi.type.*; */ public class CachingSymbolTable { protected Stack>> symbolMapStack; - protected Stack offsets; - protected Stack functionSizes; protected Map> symbolCache; - protected int offset; - protected int functionDepth; - protected int functionSize; + protected Stack> functionScope; /** * Creates an empty symbol table with no open scope. @@ -32,11 +29,7 @@ public class CachingSymbolTable { public CachingSymbolTable() { symbolMapStack = new Stack<>(); symbolCache = new HashMap<>(); - offsets = new Stack<>(); - functionSizes = new Stack<>(); - offset = 0; - functionDepth = 0; - functionSize = 0; + functionScope = new Stack<>(); } /** @@ -44,7 +37,6 @@ public class CachingSymbolTable { */ public void openScope() { symbolMapStack.push(new HashMap<>()); - offsets.push(offset); } /** @@ -53,28 +45,27 @@ public class CachingSymbolTable { */ public void openFunctionScope() { openScope(); - functionSizes.push(functionSize); - functionDepth++; - functionSize = 0; - offset = 0; + functionScope.push(new FunctionScope(functionScope.size())); } /** - * Closes a lexical scope, removing all declarations within this scope. + * Closes a lexical scope, removing all declarations within this scope. Runs + * in {@code O(k log k log n)} time, with {@code k} the number of + * identifiers going out of scope, when the stream is parallelized. * - * @throws EmptyStackException + * @throws NoProgramException * if no scope is open */ - public void closeScope() throws EmptyStackException { + public void closeScope() throws NoProgramException { Map> deletions = symbolMapStack.pop(); for (String identifier : deletions.keySet()) { - Optional> maybeType = symbolMapStack.stream().filter((map) -> map.containsKey(identifier)) - .map((map) -> map.get(identifier)).reduce((__, snd) -> snd); + Optional> maybeType = symbolMapStack.parallelStream() + .filter((map) -> map.containsKey(identifier)).map((map) -> map.get(identifier)) + .reduce((__, snd) -> snd); maybeType.ifPresent((var) -> symbolCache.replace(identifier, var)); if (!maybeType.isPresent()) symbolCache.remove(identifier); } - offset = offsets.pop(); } /** @@ -82,13 +73,14 @@ public class CachingSymbolTable { * declarations within this scope, restoring the local offset and decreasing * the lookup depth. * - * @throws EmptyStackException + * @return function scope details + * + * @throws NoProgramException * if no scope is open */ - public void closeFunctionScope() throws EmptyStackException { + public FunctionScope closeFunctionScope() throws NoProgramException { closeScope(); - functionSize = functionSizes.pop(); - functionDepth--; + return functionScope.pop(); } /** @@ -106,27 +98,32 @@ public class CachingSymbolTable { public U withScope(Supplier code) { openScope(); U result = code.get(); - closeScope(); + try { + closeScope(); + } catch (NoProgramException e) { + } return result; } /** * Opens a function scope, executes the given code and closes it. This is a * helper method to make sure calls to {@link #openFunctionScope()} and - * {@link #closeFunctionScope()} are balanced and returns the local data - * size (in bytes) of the function. {@code code} must take no arguments and - * must not return a value. + * {@link #closeFunctionScope()} are balanced and returns the function scope + * details. {@code code} must take no arguments and must not return a value. * * @param code * the code to execute within the scope - * @return the return value of the code + * @return the function scope details */ - public int withFunctionScope(Runnable code) { + public FunctionScope withFunctionScope(Runnable code) { openFunctionScope(); code.run(); - int result = functionSize; - closeFunctionScope(); - return result; + + try { + return closeFunctionScope(); + } catch (NoProgramException e) { + return null; + } } /** @@ -140,18 +137,19 @@ public class CachingSymbolTable { * @param type * the type of the identifier * @return the type of the identifier - * @throws Exception + * @throws DeclaredException * if the identifier is declared in the current scope already - * @throws EmptyStackException + * @throws NoProgramException * if no scope is open */ - public Variable put(String id, T type) throws Exception, EmptyStackException { - if (symbolMapStack.peek().containsKey(id)) - throw new Exception(String.format("Identifier '%s' already declared in this scope.", id)); + public Variable put(String id, T type) throws DeclaredException, NoProgramException { + if (symbolMapStack.isEmpty()) + throw new NoProgramException(); - Variable var = new Variable<>(type, functionDepth, offset); - offset += type.getSize(); - functionSize = Math.max(functionSize, offset); + if (symbolMapStack.peek().containsKey(id)) + throw new DeclaredException(String.format("Identifier '%s' already declared in this scope.", id)); + + Variable var = functionScope.peek().addVariable(type); symbolMapStack.peek().put(id, var); symbolCache.put(id, var); @@ -164,53 +162,46 @@ public class CachingSymbolTable { * @param id * the name of the identifier * @return true if the identifier has a declared type - * @throws EmptyStackException + * @throws NoProgramException * if no scope is open */ - public boolean has(String id) throws EmptyStackException { + public boolean has(String id) throws NoProgramException { if (symbolMapStack.isEmpty()) - throw new EmptyStackException(); + throw new NoProgramException(); return symbolCache.containsKey(id); } /** - * Retrieves the type of an identifier, if any. + * Returns the type of an identifier, if any. * * @param id * the name of the identifier * @return the type of the identifier - * @throws Exception + * @throws UndeclaredException * if the identifier is not declared - * @throws EmptyStackException + * @throws NoProgramException * if no scope is open */ - public Variable get(String id) throws Exception, EmptyStackException { + public Variable get(String id) throws UndeclaredException, NoProgramException { if (!this.has(id)) - throw new Exception(String.format("Identifier '%s' is undeclared.", id)); + throw new UndeclaredException(String.format("Identifier '%s' is undeclared.", id)); return symbolCache.get(id); } /** - * Returns the size, in bytes, required to store all the variables declared - * in the current function scope. Note that this is the size calculated up - * and including the last variable declaration, so make sure to call it - * right before leaving a function scope. + * Returns the current function scope, if any. * - * @return the number of bytes required to store all variables local to a - * function + * @return the current function scope + * @throws NoProgramException + * if no scope is open */ - public int getLocalDataSize() { - return functionSize; + public FunctionScope getFunctionScope() throws NoProgramException { + if (symbolMapStack.isEmpty()) + throw new NoProgramException(); + + return functionScope.peek(); } - /** - * Returns the current lexical scope/function depth. - * - * @return the function depth - */ - public int getFunctionDepth() { - return functionDepth; - } } diff --git a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java index 868fed9..54dc21c 100644 --- a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java +++ b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java @@ -1,7 +1,6 @@ package pp.s1184725.boppi; -import java.util.EmptyStackException; - +import pp.s1184725.boppi.exception.*; import pp.s1184725.boppi.type.*; /** @@ -18,26 +17,26 @@ public class DebugCachingSymbolTable extends CachingSymbolTable< @Override public void openFunctionScope() { super.openFunctionScope(); - System.out.println(this.getClass().getName() + ": entering scope depth " + functionDepth); + System.out.println(this.getClass().getName() + ": entering scope depth " + functionScope.size()); } @Override - public void closeFunctionScope() throws EmptyStackException { - System.out.println(this.getClass().getName() + ": leaving scope depth " + functionDepth); - super.closeFunctionScope(); + public FunctionScope closeFunctionScope() throws NoProgramException { + System.out.println(this.getClass().getName() + ": leaving scope depth " + functionScope.size()); + return super.closeFunctionScope(); } @Override - public Variable put(String id, T type) throws Exception, EmptyStackException { + public Variable put(String id, T type) throws DeclaredException, NoProgramException { System.out.println(this.getClass().getName() + ": declaring '" + id + "' (" + type.toString() + ") at scope " - + functionDepth); + + functionScope.size()); return super.put(id, type); } @Override - public Variable get(String id) throws Exception, EmptyStackException { + public Variable get(String id) throws UndeclaredException, NoProgramException { System.out.println(this.getClass().getName() + ": retrieving '" + id + "' (depth " - + symbolCache.get(id).getDepth() + ") at scope " + functionDepth); + + symbolCache.get(id).getDepth() + ") at scope " + functionScope.size()); return super.get(id); } diff --git a/src/pp/s1184725/boppi/FunctionScope.java b/src/pp/s1184725/boppi/FunctionScope.java new file mode 100644 index 0000000..6633b6e --- /dev/null +++ b/src/pp/s1184725/boppi/FunctionScope.java @@ -0,0 +1,76 @@ +package pp.s1184725.boppi; + +import java.util.*; +import java.util.stream.Collectors; + +import pp.s1184725.boppi.type.Type; + +/** + * Keeps track of local data for functions. + * + * @author Frank Wibbelink + * + * @param + * the type system to use + */ +public class FunctionScope { + private final int scopeDepth; + private List> localVars; + + /** + * Creates a function scope with the given depth. + * + * @param depth + * the lexical depth of this function + */ + public FunctionScope(int depth) { + scopeDepth = depth; + localVars = new ArrayList<>(); + } + + /** + * Creates a new variable in this function scope and returns it. + * + * @param type + * the type of variable to create + * @return the variable created + */ + public Variable addVariable(T type) { + Variable var = new Variable(type, scopeDepth, getLocalDataSize()); + localVars.add(var); + + return var; + } + + /** + * Returns the size, in bytes, required to store all the variables declared + * in the current function scope. Note that this is the size calculated up + * and including the last variable declaration, so make sure to call it + * right before leaving a function scope. + * + * @return the number of bytes required to store all variables local to a + * function + */ + public int getLocalDataSize() { + return localVars.stream().map(Variable::getType).collect(Collectors.summingInt(Type::getSize)); + } + + /** + * Returns the current lexical scope/function depth. + * + * @return the function depth + */ + public int getFunctionDepth() { + return scopeDepth; + } + + /** + * Returns the variables local to this function scope. + * + * @return a list of variables + */ + public List> getVars() { + return localVars; + } + +} diff --git a/src/pp/s1184725/boppi/ToolChain.java b/src/pp/s1184725/boppi/ToolChain.java index a0dd6e6..2d287c1 100644 --- a/src/pp/s1184725/boppi/ToolChain.java +++ b/src/pp/s1184725/boppi/ToolChain.java @@ -2,7 +2,7 @@ package pp.s1184725.boppi; import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; +import java.nio.file.*; import java.util.*; import java.util.logging.*; @@ -11,6 +11,7 @@ import org.antlr.v4.runtime.tree.*; import org.apache.commons.lang3.tuple.Pair; import pp.iloc.Simulator; +import pp.iloc.eval.Machine; import pp.iloc.model.Program; import pp.s1184725.boppi.antlr.*; @@ -20,6 +21,14 @@ import pp.s1184725.boppi.antlr.*; * @author Frank Wibbelink */ public class ToolChain { + /** + * The file system path of this class + */ + public static final Path PATH = Paths.get("src/" + ToolChain.class.getPackage().getName().replaceAll("\\.", "/")); + /** + * The last virtual machine used for executing a program + */ + public static Machine machine; /** * Opens a file for reading and returns its charstream. Throws an unhandled @@ -190,7 +199,8 @@ public class ToolChain { /** * Runs a program using the given input and output streams. Run errors will - * be logged as {@link Level#SEVERE}. + * be logged as {@link Level#SEVERE}. Instantiates a fresh machine and + * simulator. * * @param program * the program to run @@ -202,7 +212,27 @@ public class ToolChain { * the output stream the program can write to */ public static void execute(Program program, Logger logger, InputStream in, OutputStream out) { - Simulator s = new Simulator(program); + execute(program, logger, in, out, new Machine()); + } + + /** + * Runs a program using the given input and output streams. Run errors will + * be logged as {@link Level#SEVERE}. + * + * @param program + * the program to run + * @param logger + * the logger to write runtime errors to + * @param in + * the input stream the program can read from + * @param out + * the output stream the program can write to + * @param vm + * the virtual machine to use + */ + public static void execute(Program program, Logger logger, InputStream in, OutputStream out, Machine vm) { + machine = vm; + Simulator s = new Simulator(program, machine); s.setIn(in); s.setOut(out); try { diff --git a/src/pp/s1184725/boppi/exception/DeclaredException.java b/src/pp/s1184725/boppi/exception/DeclaredException.java new file mode 100644 index 0000000..9f7b202 --- /dev/null +++ b/src/pp/s1184725/boppi/exception/DeclaredException.java @@ -0,0 +1,23 @@ +package pp.s1184725.boppi.exception; + +/** + * Exception thrown when declaring a variable a second time in the same scope. + * + * @author Frank Wibbelink + */ +public class DeclaredException extends SymbolTableException { + private static final long serialVersionUID = 5769561320967096410L; + + /** + * Constructs a new exception with the specified detail message. The cause + * is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message + * the detail message. The detail message is saved for later + * retrieval by the {@link #getMessage()} method. + */ + public DeclaredException(String message) { + super(message); + } +} diff --git a/src/pp/s1184725/boppi/exception/NoProgramException.java b/src/pp/s1184725/boppi/exception/NoProgramException.java new file mode 100644 index 0000000..fe493c0 --- /dev/null +++ b/src/pp/s1184725/boppi/exception/NoProgramException.java @@ -0,0 +1,19 @@ +package pp.s1184725.boppi.exception; + +/** + * Exception thrown when trying to use a SymbolTable while no scope is open. + * + * @author Frank Wibbelink + */ +public class NoProgramException extends SymbolTableException { + private static final long serialVersionUID = 214588214046135357L; + + /** + * Constructs a new exception with {@code "No scope open."} as its detail + * message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public NoProgramException() { + super("No scope open."); + } +} diff --git a/src/pp/s1184725/boppi/exception/SymbolTableException.java b/src/pp/s1184725/boppi/exception/SymbolTableException.java new file mode 100644 index 0000000..a96d2f6 --- /dev/null +++ b/src/pp/s1184725/boppi/exception/SymbolTableException.java @@ -0,0 +1,23 @@ +package pp.s1184725.boppi.exception; + +import pp.s1184725.boppi.CachingSymbolTable; + +/** + * Exceptions that may be thrown by the {@link CachingSymbolTable}. + */ +public abstract class SymbolTableException extends Exception { + private static final long serialVersionUID = 2390361610733507914L; + + /** + * Constructs a new exception with the specified detail message. The cause + * is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message + * the detail message. The detail message is saved for later + * retrieval by the {@link #getMessage()} method. + */ + public SymbolTableException(String message) { + super(message); + } +} diff --git a/src/pp/s1184725/boppi/exception/UndeclaredException.java b/src/pp/s1184725/boppi/exception/UndeclaredException.java new file mode 100644 index 0000000..b1d31e7 --- /dev/null +++ b/src/pp/s1184725/boppi/exception/UndeclaredException.java @@ -0,0 +1,23 @@ +package pp.s1184725.boppi.exception; + +/** + * Exception thrown when declaring a variable a second time in the same scope. + * + * @author Frank Wibbelink + */ +public class UndeclaredException extends SymbolTableException { + private static final long serialVersionUID = -5333137316309067383L; + + /** + * Constructs a new exception with the specified detail message. The cause + * is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message + * the detail message. The detail message is saved for later + * retrieval by the {@link #getMessage()} method. + */ + public UndeclaredException(String message) { + super(message); + } +} diff --git a/src/pp/s1184725/boppi/memlib.iloc b/src/pp/s1184725/boppi/memlib.iloc index 2252194..9e249b7 100644 --- a/src/pp/s1184725/boppi/memlib.iloc +++ b/src/pp/s1184725/boppi/memlib.iloc @@ -100,8 +100,8 @@ memfree: loadI 0 => m_0 mf_ynul: haltI 1865442925 mf_nnul: loadAI m_n,@off_oref => m_1 subI m_1,1 => m_1 - cmp_GT m_1,m_0 => m_1 - cbr m_1 -> mf_exit,mf_free + cmp_GT m_1,m_0 => m_2 + cbr m_2 -> mf_exit,mf_free mf_exit: storeAI m_1 => m_n,@off_oref pop => m_1 // load return address diff --git a/src/pp/s1184725/boppi/test/BoppiTests.java b/src/pp/s1184725/boppi/test/BoppiTests.java index a887406..54c217c 100644 --- a/src/pp/s1184725/boppi/test/BoppiTests.java +++ b/src/pp/s1184725/boppi/test/BoppiTests.java @@ -22,7 +22,7 @@ import pp.s1184725.boppi.*; * @author Frank Wibbelink */ @RunWith(Suite.class) -@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class }) +@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class, ClosureTest.class }) public class BoppiTests { /** * The path for test programs diff --git a/src/pp/s1184725/boppi/test/ClosureTest.java b/src/pp/s1184725/boppi/test/ClosureTest.java new file mode 100644 index 0000000..db11b54 --- /dev/null +++ b/src/pp/s1184725/boppi/test/ClosureTest.java @@ -0,0 +1,77 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import org.junit.Test; + +import pp.s1184725.boppi.ToolChain; + +/** + * Tests for function closures, mostly testing runtime correctness and garbage collection. + * + * @author Frank Wibbelink + */ +public class ClosureTest { + + /** + * Correct closure parsing + */ + @Test + public void correctClosureParsing() { + BoppiTests.parseFile("closure1.boppi"); + assertThat(BoppiTests.log, is(empty())); + + BoppiTests.parseFile("closure2.boppi"); + assertThat(BoppiTests.log, is(empty())); + } + + /** + * Correct closure checking + */ + @Test + public void correctClosureChecking() { + BoppiTests.checkFile("closure1.boppi"); + assertThat(BoppiTests.log, is(empty())); + + BoppiTests.checkFile("closure2.boppi"); + assertThat(BoppiTests.log, is(empty())); + } + + /** + * Correct closure use + */ + @Test + public void correctClosureGeneration() { + BoppiTests.compileAndRunFile("closure1.boppi"); + assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("8", "9"))); + + BoppiTests.compileAndRunFile("closure2.boppi"); + assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("8", "7", "15", "2"))); + + BoppiTests.compileAndRunFile("closure3.boppi"); + assertThat(ToolChain.machine.getInterrupt(), is(0)); + assertThat(BoppiTests.log, is(empty())); + assertThat(BoppiTests.out, is(arrayContaining("7", "15", "2"))); + } + + /** + * Test garbago collection. Nothing must be allocated at the end of the program. + */ + @Test + public void correctClosureCleanup() { + BoppiTests.compileAndRunFile("closure1.boppi"); + assertThat(ToolChain.machine.load(0), is(4)); + + BoppiTests.compileAndRunFile("closure2.boppi"); + assertThat(ToolChain.machine.load(0), is(4)); + + BoppiTests.compileAndRunFile("closure3.boppi"); + assertThat(ToolChain.machine.load(0), is(4)); + } + + +} diff --git a/src/pp/s1184725/boppi/test/SimpleVariableTest.java b/src/pp/s1184725/boppi/test/SimpleVariableTest.java index 672770d..f1364f7 100644 --- a/src/pp/s1184725/boppi/test/SimpleVariableTest.java +++ b/src/pp/s1184725/boppi/test/SimpleVariableTest.java @@ -107,6 +107,7 @@ public class SimpleVariableTest { @Test public void correctVariableGeneration() { BoppiTests.compileAndRunFile("simpleVariable.boppi"); + BoppiTests.log.forEach((l)->System.out.println(l.getMessage())); assertThat(BoppiTests.log, is(empty())); } diff --git a/src/pp/s1184725/boppi/test/programs/closure1.boppi b/src/pp/s1184725/boppi/test/programs/closure1.boppi new file mode 100644 index 0000000..0c1af52 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/closure1.boppi @@ -0,0 +1,12 @@ +function (int)->int bindAdd(int a) { + var int left; + left := a; + function int return(int right) left+right; + return +}; + +var (int)->int add5; +add5 := bindAdd(5); + +print(add5(3), add5(4)); + diff --git a/src/pp/s1184725/boppi/test/programs/closure2.boppi b/src/pp/s1184725/boppi/test/programs/closure2.boppi new file mode 100644 index 0000000..0dffedd --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/closure2.boppi @@ -0,0 +1,31 @@ +function (int)->int bindInt((int,int)->int f, int first) { + function int return(int second) f(first,second); + return +}; + +function int add(int a, int b) a+b; + +var (int)->int add5; +add5 := bindInt(add, 5); + +print(add5(3)); + + +var ()->int getCalls; + +function (int,int)->int logCalls((int,int)->int f) { + var int calls; + calls := 0; + function int getCalls2() calls; + getCalls := getCalls2; + function int return(int first, int second) { + calls := calls + 1; + f(first,second) + }; + return +}; + +add := logCalls(add); + +print(add(1,6), add(10,5)); +print(getCalls()); diff --git a/src/pp/s1184725/boppi/test/programs/closure3.boppi b/src/pp/s1184725/boppi/test/programs/closure3.boppi new file mode 100644 index 0000000..2d099e0 --- /dev/null +++ b/src/pp/s1184725/boppi/test/programs/closure3.boppi @@ -0,0 +1,26 @@ +function int main1() { + function int add(int a, int b) a+b; + + var (int)->int add5; + + var ()->int getCalls; + + function (int,int)->int logCalls((int,int)->int f) { + var int calls; + calls := 0; + function int getCalls2() calls; + getCalls := getCalls2; + function int return(int first, int second) { + calls := calls + 1; + f(first,second) + }; + return + }; + + add := logCalls(add); + + print(add(1,6), add(10,5)); + print(getCalls()); +}; + +main1(); diff --git a/src/pp/s1184725/boppi/type/FunctionType.java b/src/pp/s1184725/boppi/type/FunctionType.java index a20cee6..41db290 100644 --- a/src/pp/s1184725/boppi/type/FunctionType.java +++ b/src/pp/s1184725/boppi/type/FunctionType.java @@ -7,9 +7,8 @@ import pp.iloc.eval.Machine; * * @author Frank Wibbelink */ -public class FunctionType implements Type { +public class FunctionType implements ReferenceType { private Type argument, result; - private int localDataSize; /** * Creates a new function type from the given parameter and return types. @@ -43,25 +42,6 @@ public class FunctionType implements Type { return argument.toString() + "->" + result.toString(); } - /** - * Sets the size (in bytes) of the local data segment for this function. - * - * @param newSize - * the size (in bytes) of the local data segment - */ - public void setLocalDataSize(int newSize) { - localDataSize = newSize; - } - - /** - * Returns the size (in bytes) of the local data segment for this function. - * - * @return the size (in bytes) of the local data segment - */ - public int getLocalDataSize() { - return localDataSize; - } - /** * Returns the result type of this function. * diff --git a/src/pp/s1184725/boppi/type/ReferenceType.java b/src/pp/s1184725/boppi/type/ReferenceType.java new file mode 100644 index 0000000..7331f9e --- /dev/null +++ b/src/pp/s1184725/boppi/type/ReferenceType.java @@ -0,0 +1,10 @@ +package pp.s1184725.boppi.type; + +/** + * An interface for types that are stored in dynamic memory. + * + * @author Frank Wibbelink + */ +public interface ReferenceType extends Type { + +} diff --git a/src/pp/s1184725/boppi/util/RegisterPool.java b/src/pp/s1184725/boppi/util/RegisterPool.java new file mode 100644 index 0000000..a3a72ce --- /dev/null +++ b/src/pp/s1184725/boppi/util/RegisterPool.java @@ -0,0 +1,196 @@ +package pp.s1184725.boppi.util; + +import java.util.ArrayList; +import java.util.*; +import java.util.function.*; +import java.util.logging.Logger; + +import pp.iloc.model.Reg; + +/** + * Simple class to manage registers. + */ +public class RegisterPool { + /** + * Recommended number of registers to use before throwing a warning. + */ + public static int RECOMMENDED_REGISTER_COUNT = 10; + + private Logger logger; + private List regFree, regInUse; + + /** + * Creates a new register pool + * + * @param logger + * the logger to use + */ + public RegisterPool(Logger logger) { + regFree = new ArrayList<>(); + regInUse = new ArrayList<>(); + this.logger = logger; + } + + private Reg makeReg() { + return makeRegThatIsNot(null); + } + + private Reg makeRegThatIsNot(Reg r1) { + Reg reg = regFree.stream().filter((r2) -> !r2.equals(r1)).findAny().orElseGet(() -> { + if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1) + logger.warning(String.format("Using more than %d registers. Consider rebalancing your expressions.", + RECOMMENDED_REGISTER_COUNT)); + + return new Reg("__" + (regInUse.size() + 1)); + }); + + regFree.remove(reg); + regInUse.add(reg); + + return reg; + } + + private void freeReg(Reg reg) { + if (reg == null) { + logger.severe("INTERNAL: cannot free null register."); + } else if (!regInUse.contains(reg)) { + logger.severe(String.format("INTERNAL: cannot free register %s because it is not in use.", reg.getName())); + } else { + regInUse.remove(reg); + regFree.add(reg); + } + } + + private void blockReg(Reg reg) { + if (reg == null) + logger.severe("INTERNAL: cannot reserve null register."); + else if (!regFree.contains(reg)) { + logger.severe(String.format("INTERNAL: cannot reserve register %s because it is in use.", reg.getName())); + } else { + regFree.remove(reg); + regInUse.add(reg); + } + } + + /** + * Returns the list of registers currently in use. + * + * @return the list of registers in use + */ + public List getInUse() { + return regInUse; + } + + /** + * Runs some code with a single register allocated. + * + * @param + * the return type of the code + * @param code + * the code to run + * @return the return value of the code + */ + public T withReg(Function code) { + Reg r = makeReg(); + T result = code.apply(r); + freeReg(r); + return result; + } + + /** + * Runs some code with a single register allocated. + * + * @param code + * the code to run + * @return the register used in the code + */ + public Reg withReg(Consumer code) { + Reg r = makeReg(); + code.accept(r); + freeReg(r); + return r; + } + + /** + * Runs some code with two registers allocated. + * + * @param code + * the code to run + * @return the second register used in the code + */ + public Reg withReg(BiConsumer code) { + Reg r1 = makeReg(); + Reg r2 = makeReg(); + code.accept(r1, r2); + freeReg(r1); + freeReg(r2); + return r2; + } + + /** + * Runs some code with one register given and one allocated. + * + * @param r1 + * the first register to use + * @param code + * the code to run + * @return the register used in the code + */ + public Reg withReg(Reg r1, Consumer code) { + Reg r = makeRegThatIsNot(r1); + code.accept(r); + freeReg(r); + return r; + } + + /** + * Runs some code with one register given and two allocated. + * + * @param r1 + * the first register to use + * @param code + * the code to run + * @return the second register used in the code + */ + public Reg withReg(Reg r1, BiConsumer code) { + Reg r2 = makeRegThatIsNot(r1); + Reg r3 = makeRegThatIsNot(r1); + code.accept(r2, r3); + freeReg(r2); + freeReg(r3); + return r3; + } + + /** + * Blocks a register while running some code. + * + * @param + * the return type of the code + * @param r1 + * the register to reserve + * @param code + * the code to run + * @return the return value of the code + */ + public T blockReg(Reg r1, Supplier code) { + blockReg(r1); + T result = code.get(); + freeReg(r1); + return result; + } + + /** + * Blocks a register while running some code. + * + * @param r1 + * the register to reserve + * @param code + * the code to run + */ + public void blockReg(Reg r1, Runnable code) { + blockReg(r1); + code.run(); + freeReg(r1); + } + +}