Homework B (benchmarks)

This commit is contained in:
Carlos Galindo 2020-01-15 22:38:07 +01:00
commit 76fbabdf53
Signed by: kauron
GPG key ID: 83E68706DEE119A3
141 changed files with 7540 additions and 2032 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
package cd.ir;
import cd.ir.Ast.Expr;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import java.util.Map;
/**
* A visitor that replaces AST nodes in toBeReplaced
* used in ConstantAnalysis, deletes all the variables that have a constant value and replace them with a constant
*/
public class AstReplaceVisitor extends AstRewriteVisitor<Ast> {
// use this information to replace each Var node with it's correct constant value
public Map<Ast.Expr, Ast.LeafExpr> toBeReplaced;
public Map<Expr, Integer> initializePositions;
public int currentPosition;
public Ast.LeafExpr getReplacement(Ast.Var arg) {
// Var was declared constant but initialization happens later
// replace it with IntConst or BooleanConst and their default value
if (toBeReplaced.containsKey(arg) && currentPosition <= initializePositions.get(arg)) {
//if (toBeReplaced.containsKey(arg)) {
Ast.LeafExpr leafEpr = (Ast.LeafExpr) arg;
if (leafEpr.type == PrimitiveTypeSymbol.intType) {
return new Ast.IntConst(0);
} else {
return new Ast.BooleanConst(false);
}
}
// Var was declared constant and initialization already happened
else if (toBeReplaced.containsKey(arg) && currentPosition > initializePositions.get(arg)) {
return toBeReplaced.get(arg);
}
// Var was not declared to be a constant
else {
return arg;
}
}
public Ast var(Ast.Var ast, Void arg) {
return getReplacement(ast);
}
}

View file

@ -19,13 +19,14 @@ public class AstRewriteVisitor<A> extends AstVisitor<Ast, A> {
}
return ast;
}
/**
* This method is called when a node is replaced. Subclasses can override it to do some
* bookkeeping.
* <p>
* The default implementation does nothing.
*/
protected void nodeReplaced(Ast oldNode, Ast newNode) {}
protected void nodeReplaced(Ast oldNode, Ast newNode) {
}
}

View file

@ -4,18 +4,21 @@ import cd.ir.Ast.Decl;
import cd.ir.Ast.Expr;
import cd.ir.Ast.Stmt;
/** A visitor that visits any kind of node */
public class AstVisitor<R,A> extends ExprVisitor<R,A> {
/**
* Recurse and process {@code ast}. It is preferred to
/**
* A visitor that visits any kind of node
*/
public class AstVisitor<R, A> extends ExprVisitor<R, A> {
/**
* Recurse and process {@code ast}. It is preferred to
* call this rather than calling accept directly, since
* it can be overloaded to introduce memoization,
* for example. */
* it can be overloaded to introduce memoization,
* for example.
*/
public R visit(Ast ast, A arg) {
return ast.accept(this, arg);
}
/**
* Overrides {@link ExprVisitor#visitChildren(Expr, Object)} and
* delegates to the more general {@link #visitChildren(Ast, Object)}
@ -26,11 +29,11 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public final R visitChildren(Expr ast, A arg) {
return visitChildren((Ast) ast, arg);
}
/**
* A handy function which visits the children of {@code ast},
* providing "arg" to each of them. It returns the result of
* the last child in the list. It is invoked by the method
* the last child in the list. It is invoked by the method
* {@link #dflt(Ast, Object)} by default.
*/
public R visitChildren(Ast ast, A arg) {
@ -39,35 +42,39 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
lastValue = visit(child, arg);
return lastValue;
}
/**
/**
* The default action for default actions is to call this,
* which simply recurses to any children. Also called
* by seq() by default. */
* by seq() by default.
*/
protected R dflt(Ast ast, A arg) {
return visitChildren(ast, arg);
}
/**
* The default action for statements is to call this */
/**
* The default action for statements is to call this
*/
protected R dfltStmt(Stmt ast, A arg) {
return dflt(ast, arg);
}
/**
* The default action for expressions is to call this */
/**
* The default action for expressions is to call this
*/
@Override
protected R dfltExpr(Expr ast, A arg) {
return dflt(ast, arg);
}
/**
/**
* The default action for AST nodes representing declarations
* is to call this function */
* is to call this function
*/
protected R dfltDecl(Decl ast, A arg) {
return dflt(ast, arg);
}
public R assign(Ast.Assign ast, A arg) {
return dfltStmt(ast, arg);
}
@ -79,23 +86,23 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public R builtInWriteln(Ast.BuiltInWriteln ast, A arg) {
return dfltStmt(ast, arg);
}
public R classDecl(Ast.ClassDecl ast, A arg) {
return dfltDecl(ast, arg);
}
public R methodDecl(Ast.MethodDecl ast, A arg) {
return dfltDecl(ast, arg);
}
public R varDecl(Ast.VarDecl ast, A arg) {
return dfltDecl(ast, arg);
}
public R ifElse(Ast.IfElse ast, A arg) {
return dfltStmt(ast, arg);
}
public R returnStmt(Ast.ReturnStmt ast, A arg) {
return dfltStmt(ast, arg);
}
@ -107,11 +114,11 @@ public class AstVisitor<R,A> extends ExprVisitor<R,A> {
public R nop(Ast.Nop ast, A arg) {
return dfltStmt(ast, arg);
}
public R seq(Ast.Seq ast, A arg) {
return dflt(ast, arg);
}
public R whileLoop(Ast.WhileLoop ast, A arg) {
return dfltStmt(ast, arg);
}

113
src/cd/ir/BasicBlock.java Normal file
View file

@ -0,0 +1,113 @@
package cd.ir;
import cd.ir.Ast.Expr;
import cd.ir.Ast.Stmt;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Node in a control flow graph. New instances should be created
* via the methods in {@link ControlFlowGraph}.
* Basic blocks consist of a list of statements ({@link #stmts}) which are
* executed at runtime. When the basic block ends, control flows into its
* {@link #successors}. If the block has more than one successor, it must also
* have a non-{@code null} value for {@link #condition}, which describes an expression
* that will determine which successor to take. Basic blocks also have fields
* for storing the parent and children in the dominator tree. These are generally computed
* in a second pass once the graph is fully built.
* <p>
* Your team will have to write code that builds the control flow graph and computes the
* relevant dominator information.
*/
public class BasicBlock {
/**
* Unique numerical index assigned by CFG builder between 0 and the total number of
* basic blocks. Useful for indexing into arrays and the like.
*/
public final int index;
/**
* List of predecessor blocks in the flow graph (i.e., blocks for
* which {@code this} is a successor).
*/
public final List<BasicBlock> predecessors = new ArrayList<BasicBlock>();
/**
* List of successor blocks in the flow graph (those that come after the
* current block). This list is always either of size 0, 1 or 2: 1 indicates
* that control flow continues directly into the next block, and 2 indicates
* that control flow goes in one of two directions, depending on the
* value that results when {@link #condition} is evaluated at runtime.
* If there are two successors, then the 0th entry is taken when {@code condition}
* evaluates to {@code true}.
*
* @see #trueSuccessor()
* @see #falseSuccessor()
*/
public final List<BasicBlock> successors = new ArrayList<BasicBlock>();
/**
* List of statements in this basic block.
*/
public final List<Stmt> stmts = new ArrayList<>();
/**
* If non-null, indicates that this basic block should have
* two successors. Control flows to the first successor if
* this condition evaluates at runtime to true, otherwise to
* the second successor. If null, the basic block should have
* only one successor.
*/
public Expr condition;
/**
* Parent of this basic block in the dominator tree (initially null until computed).
* Otherwise known as the immediate dominator.
*/
public BasicBlock dominatorTreeParent = null;
/**
* Children of this basic block in the dominator tree (initially empty until
* computed).
*/
public final List<BasicBlock> dominatorTreeChildren = new ArrayList<BasicBlock>();
/**
* Contains the dominance frontier of this block. A block b is in the dominance
* frontier of another block c if c does not dominate b, but c DOES dominate a
* predecessor of b.
*/
public final Set<BasicBlock> dominanceFrontier = new HashSet<BasicBlock>();
public BasicBlock(int index) {
this.index = index;
}
public BasicBlock trueSuccessor() {
assert this.condition != null;
return this.successors.get(0);
}
public BasicBlock falseSuccessor() {
assert this.condition != null;
return this.successors.get(1);
}
public <A, B> A accept(AstVisitor<A, B> visitor, B arg) {
A lastA = null;
for (Stmt stmt : stmts)
lastA = visitor.visit(stmt, arg);
if (condition != null)
lastA = visitor.visit(condition, arg);
return lastA;
}
@Override
public String toString() {
return "BB" + index;
}
}

View file

@ -0,0 +1,135 @@
package cd.ir;
import cd.ir.Ast.BinaryOp;
import cd.ir.Ast.BooleanConst;
import cd.ir.Ast.Expr;
import cd.ir.Ast.UnaryOp;
import cd.ir.Symbol.VariableSymbol;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static cd.Config.FALSE;
import static cd.Config.TRUE;
/**
* A visitor that only visits {@link Expr} nodes.
* calculates the compile time value of an rhs in an assignment statement or the boolean value of a condition
*/
public class CompileTimeEvaluator extends ExprVisitor<Integer, Map<VariableSymbol, Integer>> {
// if the expression contains non constant variables, field or index; or if the expression is a read()
// no compile Time value can be computed
private boolean failedToEvaluate;
public Optional<Integer> calc(Ast.Expr ast) {
return calc(ast, new HashMap<>());
}
public Optional<Integer> calc(Ast.Expr ast, Map<VariableSymbol, Integer> constantMap) {
try {
failedToEvaluate = false;
Integer result = visit(ast, constantMap);
if (failedToEvaluate)
return Optional.empty();
else
return Optional.of(result);
} catch (ArithmeticException e) {
return Optional.empty();
}
}
@Override
protected Integer dfltExpr(Expr ast, Map<VariableSymbol, Integer> arg) {
failedToEvaluate = true;
return Integer.MIN_VALUE;
}
@Override
public Integer binaryOp(BinaryOp ast, Map<VariableSymbol, Integer> arg) {
boolean tempResult;
int left = visit(ast.left(), arg);
int right = visit(ast.right(), arg);
switch (ast.operator) {
case B_TIMES:
return left * right;
case B_PLUS:
return left + right;
case B_MINUS:
return left - right;
case B_DIV:
return left / right;
case B_MOD:
return left % right;
case B_AND:
tempResult = (left == TRUE) && (right == TRUE);
break;
case B_OR:
tempResult = (left == TRUE) || (right == TRUE);
break;
case B_EQUAL:
tempResult = left == right;
break;
case B_NOT_EQUAL:
tempResult = left != right;
break;
case B_LESS_THAN:
tempResult = left < right;
break;
case B_LESS_OR_EQUAL:
tempResult = left <= right;
break;
case B_GREATER_THAN:
tempResult = left > right;
break;
case B_GREATER_OR_EQUAL:
tempResult = left >= right;
break;
default:
throw new RuntimeException("Invalid binary operator");
}
return tempResult ? TRUE : FALSE;
}
@Override
public Integer unaryOp(UnaryOp ast, Map<VariableSymbol, Integer> arg) {
int result = visit(ast.arg(), arg);
switch (ast.operator) {
case U_PLUS:
return result;
case U_MINUS:
return -result;
case U_BOOL_NOT:
return result == FALSE ? TRUE : FALSE;
default:
throw new RuntimeException("Invalid unary operator");
}
}
@Override
public Integer booleanConst(BooleanConst ast, Map<VariableSymbol, Integer> arg) {
return ast.value ? TRUE : FALSE;
}
@Override
public Integer intConst(Ast.IntConst ast, Map<VariableSymbol, Integer> arg) {
return ast.value;
}
// check if a given Variable has a constant value
@Override
public Integer var(Ast.Var ast, Map<VariableSymbol, Integer> arg) {
if (arg.containsKey(ast.sym)) {
return arg.get(ast.sym);
} else {
failedToEvaluate = true;
return Integer.MIN_VALUE;
}
}
}

View file

@ -0,0 +1,61 @@
package cd.ir;
import cd.ir.Ast.Expr;
import java.util.ArrayList;
import java.util.List;
/**
* Represents the control flow graph of a single method.
*/
public class ControlFlowGraph {
public BasicBlock start, end;
public final List<BasicBlock> allBlocks = new ArrayList<BasicBlock>();
public int count() {
return allBlocks.size();
}
public BasicBlock newBlock() {
BasicBlock blk = new BasicBlock(count());
allBlocks.add(blk);
return blk;
}
/**
* Given a list of basic blocks that do not yet have successors,
* merges their control flows into a single successor and returns
* the new successor.
*/
public BasicBlock join(BasicBlock... pred) {
BasicBlock result = newBlock();
for (BasicBlock p : pred) {
assert p.condition == null;
assert p.successors.size() == 0;
p.successors.add(result);
result.predecessors.add(p);
}
return result;
}
/**
* Terminates {@code blk} so that it evaluates {@code cond},
* and creates two new basic blocks, one for the case where
* the result is true, and one for the case where the result is
* false.
*/
public void terminateInCondition(BasicBlock blk, Expr cond) {
assert blk.condition == null;
assert blk.successors.size() == 0;
blk.condition = cond;
blk.successors.add(newBlock());
blk.successors.add(newBlock());
blk.trueSuccessor().predecessors.add(blk);
blk.falseSuccessor().predecessors.add(blk);
}
public void connect(BasicBlock from, BasicBlock to) {
to.predecessors.add(from);
from.successors.add(to);
}
}

View file

@ -5,16 +5,17 @@ import cd.ir.Ast.Expr;
/**
* A visitor that only visits {@link Expr} nodes.
*/
public class ExprVisitor<R,A> {
/**
* Recurse and process {@code ast}. It is preferred to
public class ExprVisitor<R, A> {
/**
* Recurse and process {@code ast}. It is preferred to
* call this rather than calling accept directly, since
* it can be overloaded to introduce memoization,
* for example. */
* it can be overloaded to introduce memoization,
* for example.
*/
public R visit(Expr ast, A arg) {
return ast.accept(this, arg);
}
/**
* Visits all children of the expression. Relies on the fact
* that {@link Expr} nodes only contain other {@link Expr} nodes.
@ -22,18 +23,19 @@ public class ExprVisitor<R,A> {
public R visitChildren(Expr ast, A arg) {
R lastValue = null;
for (Ast child : ast.children())
lastValue = visit((Expr)child, arg);
lastValue = visit((Expr) child, arg);
return lastValue;
}
/**
/**
* The default action for default actions is to call this,
* which simply recurses to any children. Also called
* by seq() by default. */
* by seq() by default.
*/
protected R dfltExpr(Expr ast, A arg) {
return visitChildren(ast, arg);
}
public R binaryOp(Ast.BinaryOp ast, A arg) {
return dfltExpr(ast, arg);
}
@ -41,15 +43,15 @@ public class ExprVisitor<R,A> {
public R booleanConst(Ast.BooleanConst ast, A arg) {
return dfltExpr(ast, arg);
}
public R builtInRead(Ast.BuiltInRead ast, A arg) {
return dfltExpr(ast, arg);
}
public R cast(Ast.Cast ast, A arg) {
return dfltExpr(ast, arg);
}
public R field(Ast.Field ast, A arg) {
return dfltExpr(ast, arg);
}
@ -61,7 +63,7 @@ public class ExprVisitor<R,A> {
public R intConst(Ast.IntConst ast, A arg) {
return dfltExpr(ast, arg);
}
public R methodCall(Ast.MethodCallExpr ast, A arg) {
return dfltExpr(ast, arg);
}
@ -73,7 +75,7 @@ public class ExprVisitor<R,A> {
public R newArray(Ast.NewArray ast, A arg) {
return dfltExpr(ast, arg);
}
public R nullConst(Ast.NullConst ast, A arg) {
return dfltExpr(ast, arg);
}

View file

@ -1,43 +1,45 @@
package cd.ir;
import cd.backend.codegen.AstCodeGenerator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class Symbol {
public final String name;
public static abstract class TypeSymbol extends Symbol {
public TypeSymbol(String name) {
super(name);
}
public abstract boolean isReferenceType();
@Override
public String toString() {
public String toString() {
return name;
}
public abstract TypeSymbol getSuperType();
public boolean isSuperTypeOf(TypeSymbol sub) {
// "void" is not a subtype of any type not even itself
if(this == PrimitiveTypeSymbol.voidType || sub == PrimitiveTypeSymbol.voidType)
return false;
// "void" is not a subtype of any type not even itself
if (this == PrimitiveTypeSymbol.voidType || sub == PrimitiveTypeSymbol.voidType)
return false;
if (sub == this)
return true;
if (this instanceof PrimitiveTypeSymbol || sub instanceof PrimitiveTypeSymbol)
return false; // no hierarchy with primitive types
if (sub == ClassSymbol.nullType && this.isReferenceType())
return true;
TypeSymbol curr = sub;
while (curr != null) {
if (curr == this)
@ -46,94 +48,106 @@ public abstract class Symbol {
}
return false;
}
}
public static class PrimitiveTypeSymbol extends TypeSymbol {
/** Symbols for the built-in primitive types */
/**
* Symbols for the built-in primitive types
*/
public static final PrimitiveTypeSymbol intType = new PrimitiveTypeSymbol("int");
public static final PrimitiveTypeSymbol voidType = new PrimitiveTypeSymbol("void");
public static final PrimitiveTypeSymbol booleanType = new PrimitiveTypeSymbol("boolean");
public PrimitiveTypeSymbol(String name) {
super(name);
}
}
@Override
public boolean isReferenceType() {
public boolean isReferenceType() {
return false;
}
@Override
public TypeSymbol getSuperType() {
public TypeSymbol getSuperType() {
throw new RuntimeException("should not call this on PrimitiveTypeSymbol");
}
}
public static class ArrayTypeSymbol extends TypeSymbol {
public final TypeSymbol elementType;
public ArrayTypeSymbol(TypeSymbol elementType) {
super(elementType.name+"[]");
super(elementType.name + "[]");
this.elementType = elementType;
}
@Override
public boolean isReferenceType() {
public boolean isReferenceType() {
return true;
}
@Override
public TypeSymbol getSuperType() {
public TypeSymbol getSuperType() {
return ClassSymbol.objectType;
}
}
public static class ClassSymbol extends TypeSymbol {
public final Ast.ClassDecl ast;
public ClassSymbol superClass;
public final VariableSymbol thisSymbol =
new VariableSymbol("this", this);
public final Map<String, VariableSymbol> fields =
new HashMap<String, VariableSymbol>();
new VariableSymbol("this", this);
public final Map<String, VariableSymbol> fields =
new HashMap<String, VariableSymbol>();
public final Map<String, MethodSymbol> methods =
new HashMap<String, MethodSymbol>();
new HashMap<String, MethodSymbol>();
/** Symbols for the built-in Object and null types */
public int totalMethods = -1;
public int totalFields = -1;
public int sizeof = -1;
/**
* Symbols for the built-in Object and null types
*/
public static final ClassSymbol nullType = new ClassSymbol("<null>");
public static final ClassSymbol objectType = new ClassSymbol("Object");
public static final ClassSymbol objectType = new ClassSymbol("Object");
public ClassSymbol(Ast.ClassDecl ast) {
super(ast.name);
this.ast = ast;
}
/** Used to create the default {@code Object}
* and {@code <null>} types */
/**
* Used to create the default {@code Object}
* and {@code <null>} types
*/
public ClassSymbol(String name) {
super(name);
this.ast = null;
}
@Override
public boolean isReferenceType() {
public boolean isReferenceType() {
return true;
}
@Override
public TypeSymbol getSuperType() {
public TypeSymbol getSuperType() {
return superClass;
}
public VariableSymbol getField(String name) {
VariableSymbol fsym = fields.get(name);
if (fsym == null && superClass != null)
return superClass.getField(name);
return fsym;
}
public MethodSymbol getMethod(String name) {
MethodSymbol msym = methods.get(name);
if (msym == null && superClass != null)
@ -143,32 +157,53 @@ public abstract class Symbol {
}
public static class MethodSymbol extends Symbol {
public final Ast.MethodDecl ast;
public final Map<String, VariableSymbol> locals =
new HashMap<String, VariableSymbol>();
new HashMap<String, VariableSymbol>();
public final List<VariableSymbol> parameters =
new ArrayList<VariableSymbol>();
new ArrayList<VariableSymbol>();
public TypeSymbol returnType;
public ClassSymbol owner;
public int vtableIndex = -1;
public MethodSymbol overrides;
public MethodSymbol(Ast.MethodDecl ast) {
super(ast.name);
this.ast = ast;
}
@Override
public String toString() {
public String toString() {
return name + "(...)";
}
}
public static class VariableSymbol extends Symbol {
public static enum Kind { PARAM, LOCAL, FIELD };
public static enum Kind {PARAM, LOCAL, FIELD}
;
public final TypeSymbol type;
public final Kind kind;
/**
* Meaning depends on the kind of variable, but generally refers
* to the offset in bytes from some base ptr to where the variable
* is found.
* <ul>
* <li>{@code PARAM}, {@code LOCAL}: Offset from BP
* <li>{@code FIELD}: Offset from object
* <li>{@code CONSTANT}: N/A
* </ul>
* Computed in {@link AstCodeGenerator}.
*/
public int offset = -1;
public VariableSymbol(String name, TypeSymbol type) {
this(name, type, Kind.PARAM);
}
@ -176,11 +211,11 @@ public abstract class Symbol {
public VariableSymbol(String name, TypeSymbol type, Kind kind) {
super(name);
this.type = type;
this.kind = kind;
this.kind = kind;
}
@Override
public String toString() {
public String toString() {
return name;
}
}