Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #146 from lizthegrey/skishore-arraybounds

Skishore arraybounds
  • Loading branch information...
commit b63aa4778dba876d4d9f481bc05a12881fbc8f92 2 parents 4ff6f13 + fdb4510
@lizthegrey authored
View
8 src/edu/mit/compilers/le02/Main.java
@@ -67,7 +67,9 @@ public int numericCode() {
COPY_PROPAGATION("cp"),
DEAD_CODE("dc"),
CONSECUTIVE_COPY("cc"),
- REGISTER_ALLOCATION("regalloc");
+ LOOP_ARRAY_BOUNDS_CHECKS("abc"),
+ REGISTER_ALLOCATION("regalloc"),
+ ;
private String flagName;
private Optimization(String flag) {
@@ -366,7 +368,7 @@ protected static boolean generateCFG(InputStream inputStream,
return false;
}
- ControlFlowGraph lowCfg = CFGGenerator.generateCFG(parent);
+ ControlFlowGraph lowCfg = CFGGenerator.generateCFG(parent, opts);
ControlFlowGraph cfg = BasicBlockGraph.makeBasicBlockGraph(lowCfg, opts);
if (CLI.graphics) {
@@ -409,7 +411,7 @@ protected static boolean generateAsm(InputStream inputStream,
return false;
}
- ControlFlowGraph lowCfg = CFGGenerator.generateCFG(parent);
+ ControlFlowGraph lowCfg = CFGGenerator.generateCFG(parent, opts);
ControlFlowGraph cfg = BasicBlockGraph.makeBasicBlockGraph(lowCfg, opts);
for (FieldDescriptor global : st.getFields()) {
cfg.putGlobal("." + global.getId(), global);
View
4 src/edu/mit/compilers/le02/cfg/ArgumentStatement.java
@@ -15,6 +15,10 @@ public Argument getArgument() {
return arg;
}
+ public void setArgument(Argument newArg) {
+ arg = newArg;
+ }
+
@Override
public String toString() {
return "ArgumentStatement(" + arg + ")";
View
176 src/edu/mit/compilers/le02/cfg/CFGGenerator.java
@@ -1,12 +1,16 @@
package edu.mit.compilers.le02.cfg;
import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import edu.mit.compilers.le02.CompilerException;
import edu.mit.compilers.le02.DecafType;
import edu.mit.compilers.le02.ErrorReporting;
import edu.mit.compilers.le02.GlobalLocation;
+import edu.mit.compilers.le02.Main.Optimization;
import edu.mit.compilers.le02.RegisterLocation;
import edu.mit.compilers.le02.RegisterLocation.Register;
import edu.mit.compilers.le02.SourceLocation;
@@ -39,6 +43,8 @@
import edu.mit.compilers.le02.ast.SystemCallNode;
import edu.mit.compilers.le02.ast.VariableNode;
import edu.mit.compilers.le02.cfg.OpStatement.AsmOp;
+import edu.mit.compilers.le02.opt.ArrayBoundsChecks;
+import edu.mit.compilers.le02.opt.LoopMonotonicCode;
import edu.mit.compilers.le02.symboltable.AnonymousDescriptor;
import edu.mit.compilers.le02.symboltable.LocalDescriptor;
import edu.mit.compilers.le02.symboltable.SymbolTable;
@@ -48,11 +54,15 @@
import edu.mit.compilers.le02.symboltable.TypedDescriptor;
public final class CFGGenerator extends ASTNodeVisitor<CFGFragment> {
+ private static boolean arrayBoundsChecksOpt;
private static CFGGenerator instance = null;
private static String curMethod;
+ private static boolean inFlatFor;
+ private static boolean skipBoundsChecks;
private ControlFlowGraph cfg;
private SimpleCFGNode increment, loopExit;
+
public static CFGGenerator getInstance() {
if (instance == null) {
instance = new CFGGenerator();
@@ -76,7 +86,16 @@ public static LocalDescriptor makeTemp(ASTNode node, DecafType type) {
return ld;
}
- public static ControlFlowGraph generateCFG(ASTNode root) {
+ public static ControlFlowGraph generateCFG(ASTNode root,
+ EnumSet<Optimization> opts) {
+ arrayBoundsChecksOpt =
+ opts.contains(Optimization.LOOP_ARRAY_BOUNDS_CHECKS);
+ if (arrayBoundsChecksOpt) {
+ LoopMonotonicCode.findMonotonicCode(root);
+ }
+ inFlatFor = false;
+ skipBoundsChecks = false;
+
assert(root instanceof ClassNode);
root.accept(getInstance());
return getInstance().cfg;
@@ -241,6 +260,146 @@ public CFGFragment visit(ReturnNode node) {
@Override
public CFGFragment visit(ForNode node) {
+ CFGFragment loopFrag = forNodeHelper(node);
+
+ if (arrayBoundsChecksOpt && !inFlatFor &&
+ (LoopMonotonicCode.getFlatFors().contains(node))) {
+ inFlatFor = true;
+
+ skipBoundsChecks = true;
+ CFGFragment skipFrag = forNodeHelper(node);
+ skipBoundsChecks = false;
+ return precheckArrayBounds(node, loopFrag, skipFrag);
+ }
+
+ return loopFrag;
+ }
+
+ private CFGFragment precheckArrayBounds(
+ ForNode node, CFGFragment loopFrag, CFGFragment skipFrag) {
+ CFGFragment frag = null;
+
+ ArrayBoundsChecks.findArrayAccesses(node);
+ for (ArrayLocationNode array : ArrayBoundsChecks.getAccesses()) {
+ ExpressionNode index = array.getIndex();
+
+ if (LoopMonotonicCode.getMonotonicExprs().contains(index)) {
+
+ // Get the size of the array and make a fragment which prints oob errors
+ int size = ((FieldDescriptor)(array.getDesc())).getLength();
+
+ // Create a branch node where the array lower-bound is evaluated
+ CFGFragment lowerCheck = replaceVars(node, index.accept(this),
+ ArrayBoundsChecks.getLowerBounds(), 0);
+ BasicStatement lowerBoundCheck = new OpStatement(node,
+ AsmOp.LESS_THAN, lowerCheck.getExit().getResult(),
+ new ConstantArgument(0), null);
+ SimpleCFGNode lowerBound = new SimpleCFGNode(lowerBoundCheck);
+ lowerBound.setBranchTarget(loopFrag.getEnter());
+ lowerCheck = lowerCheck.append(lowerBound);
+
+ // Create a branch node where the array upper-bound is evaluated
+ CFGFragment upperCheck = replaceVars(node, index.accept(this),
+ ArrayBoundsChecks.getUpperBounds(), 1);
+ BasicStatement upperBoundCheck = new OpStatement(node,
+ AsmOp.GREATER_OR_EQUAL, upperCheck.getExit().getResult(),
+ new ConstantArgument(size), null);
+ SimpleCFGNode upperBound = new SimpleCFGNode(upperBoundCheck);
+ upperBound.setBranchTarget(loopFrag.getEnter());
+ upperCheck = upperCheck.append(upperBound);
+
+ if (frag == null) {
+ frag = lowerCheck.link(upperCheck);
+ } else {
+ frag = frag.link(lowerCheck).link(upperCheck);
+ }
+ }
+ }
+
+ if (frag == null) {
+ return skipFrag;
+ }
+
+ SimpleCFGNode exit = new SimpleCFGNode(new NOPStatement(node));
+ loopFrag.append(exit);
+ skipFrag.append(exit);
+ frag = frag.link(skipFrag);
+ return new CFGFragment(frag.getEnter(), exit);
+ }
+
+ private CFGFragment replaceVars(ForNode node, CFGFragment frag,
+ Map<TypedDescriptor, ExpressionNode> replacements, int offset) {
+ Map<Descriptor, Argument> subs = new HashMap<Descriptor, Argument>();
+ Descriptor desc;
+
+ SimpleCFGNode enter = new SimpleCFGNode(new NOPStatement(node));
+ frag = new CFGFragment(enter, enter).link(frag);
+
+ SimpleCFGNode cur = enter;
+ SimpleCFGNode next = cur.getNext();
+
+ while (next != null) {
+ if (next.getStatement() instanceof OpStatement) {
+ OpStatement op = (OpStatement)next.getStatement();
+
+ desc = op.getArg1().getDesc();
+ if (replacements.keySet().contains(desc)) {
+ if (!subs.keySet().contains(desc)) {
+ cur = substitute(node, cur, next,
+ replacements.get(desc).accept(this), offset);
+ subs.put(desc, cur.getResult());
+ }
+ op.setArg1(subs.get(desc));
+ }
+
+ desc = op.getArg2().getDesc();
+ if (replacements.keySet().contains(desc)) {
+ if (!subs.keySet().contains(desc)) {
+ cur = substitute(node, cur, next,
+ replacements.get(desc).accept(this), offset);
+ subs.put(desc, cur.getResult());
+ }
+ op.setArg2(subs.get(desc));
+ }
+ } else if (next.getStatement() instanceof ArgumentStatement) {
+ ArgumentStatement arg = (ArgumentStatement)next.getStatement();
+ desc = arg.getArgument().getDesc();
+ if (replacements.keySet().contains(desc)) {
+ if (!subs.keySet().contains(desc)) {
+ cur = substitute(node, cur, next,
+ replacements.get(desc).accept(this), offset);
+ subs.put(desc, cur.getResult());
+ }
+ arg.setArgument(subs.get(desc));
+ next.setResult(subs.get(desc));
+ }
+ }
+
+ cur = next;
+ next = cur.getNext();
+ }
+
+ return frag;
+ }
+
+ private SimpleCFGNode substitute(ASTNode node, SimpleCFGNode cur,
+ SimpleCFGNode next, CFGFragment frag, int offset) {
+ cur.setNext(frag.getEnter());
+ cur = frag.getExit();
+
+ if (offset != 0) {
+ TypedDescriptor loc = makeTemp(node, DecafType.INT);
+ SimpleCFGNode subtractOffset = new SimpleCFGNode(new OpStatement(node,
+ AsmOp.SUBTRACT, cur.getResult(), new ConstantArgument(offset), loc));
+ cur.setNext(subtractOffset);
+ cur = subtractOffset;
+ }
+
+ cur.setNext(next);
+ return cur;
+ }
+
+ private CFGFragment forNodeHelper(ForNode node) {
// Save increment and exit nodes of any outer for loop
SimpleCFGNode oldIncrement = increment;
SimpleCFGNode oldExit = loopExit;
@@ -510,6 +669,16 @@ public CFGFragment visit(ArrayLocationNode node) {
new OpStatement(node, AsmOp.MOVE, ava, index, null)));
}
+ // Create the final argument statement fragment
+ Argument array = Argument.makeArgument(node.getDesc(), index);
+ ArgumentStatement as = new ArgumentStatement(node, array);
+ SimpleCFGNode cfgNode = new SimpleCFGNode(as);
+
+ if (skipBoundsChecks &&
+ (LoopMonotonicCode.getMonotonicExprs().contains(node.getIndex()))) {
+ return indexFrag.append(cfgNode);
+ }
+
// Get the size of the array and make a fragment which prints oob errors
Descriptor arrayDesc =
node.getSymbolTable().get(node.getName(), SymbolType.VARIABLE);
@@ -528,11 +697,6 @@ public CFGFragment visit(ArrayLocationNode node) {
SimpleCFGNode upperBound = new SimpleCFGNode(upperBoundCheck);
upperBound.setBranchTarget(violation.getEnter());
- // Create the final argument statement fragment
- Argument array = Argument.makeArgument(node.getDesc(), index);
- ArgumentStatement as = new ArgumentStatement(node, array);
- SimpleCFGNode cfgNode = new SimpleCFGNode(as);
-
// Link all the fragments together and return the result
return indexFrag.append(lowerBound).append(upperBound).append(cfgNode);
}
View
8 src/edu/mit/compilers/le02/cfg/OpStatement.java
@@ -65,10 +65,18 @@ public Argument getArg1() {
return arg1;
}
+ public void setArg1(Argument arg) {
+ arg1 = arg;
+ }
+
public Argument getArg2() {
return arg2;
}
+ public void setArg2(Argument arg) {
+ arg2 = arg;
+ }
+
public Argument getTarget() {
if (op == AsmOp.MOVE) {
return arg2;
View
67 src/edu/mit/compilers/le02/opt/ArrayBoundsChecks.java
@@ -0,0 +1,67 @@
+package edu.mit.compilers.le02.opt;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import edu.mit.compilers.le02.ast.ASTNode;
+import edu.mit.compilers.le02.ast.ASTNodeVisitor;
+import edu.mit.compilers.le02.ast.ArrayLocationNode;
+import edu.mit.compilers.le02.ast.ExpressionNode;
+import edu.mit.compilers.le02.ast.ForNode;
+import edu.mit.compilers.le02.symboltable.TypedDescriptor;
+
+public class ArrayBoundsChecks extends ASTNodeVisitor<Boolean> {
+ private static ArrayBoundsChecks instance;
+ private static List<ArrayLocationNode> arrays;
+ private static Map<TypedDescriptor, ExpressionNode> lowerBounds;
+ private static Map<TypedDescriptor, ExpressionNode> upperBounds;
+
+ private static ArrayBoundsChecks getInstance() {
+ if (instance == null) {
+ instance = new ArrayBoundsChecks();
+ }
+ return instance;
+ }
+
+ /**
+ * Returns the list of expressions which appear as array indices in
+ * a subtree of the AST
+ */
+ public static void findArrayAccesses(ASTNode root) {
+ arrays = new ArrayList<ArrayLocationNode>();
+ lowerBounds = new HashMap<TypedDescriptor, ExpressionNode>();
+ upperBounds = new HashMap<TypedDescriptor, ExpressionNode>();
+
+ root.accept(getInstance());
+ }
+
+ public static List<ArrayLocationNode> getAccesses() {
+ return arrays;
+ }
+
+ public static Map<TypedDescriptor, ExpressionNode> getLowerBounds() {
+ return lowerBounds;
+ }
+
+ public static Map<TypedDescriptor, ExpressionNode> getUpperBounds() {
+ return upperBounds;
+ }
+
+ @Override
+ public Boolean visit(ForNode node) {
+ TypedDescriptor loopVar = node.getInit().getLoc().getDesc();
+ lowerBounds.put(loopVar, node.getInit().getValue());
+ upperBounds.put(loopVar, node.getEnd());
+ defaultBehavior(node);
+ return true;
+ }
+
+ @Override
+ public Boolean visit(ArrayLocationNode node) {
+ arrays.add(node);
+ defaultBehavior(node);
+ return true;
+ }
+}
View
293 src/edu/mit/compilers/le02/opt/LoopMonotonicCode.java
@@ -0,0 +1,293 @@
+package edu.mit.compilers.le02.opt;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import edu.mit.compilers.le02.ast.ASTNode;
+import edu.mit.compilers.le02.ast.ASTNodeVisitor;
+import edu.mit.compilers.le02.ast.ArrayLocationNode;
+import edu.mit.compilers.le02.ast.AssignNode;
+import edu.mit.compilers.le02.ast.ClassNode;
+import edu.mit.compilers.le02.ast.ExpressionNode;
+import edu.mit.compilers.le02.ast.ForNode;
+import edu.mit.compilers.le02.ast.IntNode;
+import edu.mit.compilers.le02.ast.LocationNode;
+import edu.mit.compilers.le02.ast.MathOpNode;
+import edu.mit.compilers.le02.ast.MethodCallNode;
+import edu.mit.compilers.le02.ast.VariableNode;
+import edu.mit.compilers.le02.ast.MathOpNode.MathOp;
+import edu.mit.compilers.le02.symboltable.FieldDescriptor;
+import edu.mit.compilers.le02.symboltable.TypedDescriptor;
+
+public class LoopMonotonicCode extends ASTNodeVisitor<Boolean> {
+ private static LoopMonotonicCode instance;
+ private static List<ForNode> fors;
+ private static Set<TypedDescriptor> loopVars;
+ private static Set<ExpressionNode> monotonicExprs;
+
+ private static Map<ForNode, ForNode> pullup;
+ private static Set<ForNode> flatFors;
+ private static ForNode highestFlatFor;
+
+ private class UntouchedLoopVariable extends ASTNodeVisitor<Boolean> {
+ private TypedDescriptor loopVar;
+ private boolean isField;
+ private boolean untouched;
+
+ private class UnpackExpression extends ASTNodeVisitor<Boolean> {
+ private Set<TypedDescriptor> vars;
+
+ // Returns a list of variables in an expression
+ // Returns null if and only if the expression includes any
+ // array accesses or method calls
+ public Set<TypedDescriptor> listVars(ExpressionNode root) {
+ vars = new HashSet<TypedDescriptor>();
+ root.accept(this);
+ return vars;
+ }
+
+ @Override
+ public Boolean visit(MethodCallNode node) {
+ vars = null;
+ return true;
+ }
+
+ @Override
+ public Boolean visit(VariableNode node) {
+ if (node.getLoc() instanceof ArrayLocationNode) {
+ vars = null;
+ } else if (vars != null) {
+ vars.add(node.getLoc().getDesc());
+ }
+ return true;
+ }
+ }
+
+ // Checks if an expression is loop-invariant
+ public boolean check(ForNode root, ExpressionNode expr) {
+ Set<TypedDescriptor> vars = new UnpackExpression().listVars(expr);
+ if (vars == null) {
+ return false;
+ }
+
+ for (TypedDescriptor var : vars) {
+ if (!check(root, var)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Checks if a variable is loop-invariant
+ public boolean check(ForNode root, TypedDescriptor var) {
+ loopVar = var;
+ isField = (var instanceof FieldDescriptor);
+ untouched = true;
+
+ root.getBody().accept(this);
+ return untouched;
+ }
+
+ @Override
+ public Boolean visit(AssignNode node) {
+ if (node.getLoc().getDesc() == loopVar) {
+ untouched = false;
+ }
+ return true;
+ }
+
+ @Override
+ public Boolean visit(MethodCallNode node) {
+ if (isField) {
+ untouched = false;
+ }
+ return true;
+ }
+ }
+
+ public static LoopMonotonicCode getInstance() {
+ if (instance == null) {
+ instance = new LoopMonotonicCode();
+ }
+ return instance;
+ }
+
+ /**
+ * Calculates upper and lower bounds for every integer ExpressionNode
+ * in the AST, using conservative logic
+ */
+ public static void findMonotonicCode(ASTNode root) {
+ fors = new ArrayList<ForNode>();
+ loopVars = new HashSet<TypedDescriptor>();
+ monotonicExprs = new HashSet<ExpressionNode>();
+
+ pullup = new HashMap<ForNode, ForNode>();
+ flatFors = new HashSet<ForNode>();
+ highestFlatFor = null;
+
+ assert(root instanceof ClassNode);
+ root.accept(getInstance());
+
+ /*
+ for (ForNode node : pullup.keySet()) {
+ System.out.println("Loop " + node.getInit().getLoc().getDesc() +
+ " can be pulled up to " +
+ pullup.get(node).getInit().getLoc().getDesc());
+ }
+
+ System.out.println("");
+ for (ForNode node : flatFors) {
+ System.out.println("Loop " + node.getInit().getLoc().getDesc() +
+ " is flat");
+ }
+ */
+ }
+
+ public static Set<ExpressionNode> getMonotonicExprs() {
+ return monotonicExprs;
+ }
+
+ public static Set<ForNode> getFlatFors() {
+ return flatFors;
+ }
+
+ @Override
+ public Boolean visit(ForNode node) {
+ // If no higher nodes are flat, than this node is the current
+ // highest-known flat node
+ if (highestFlatFor == null) {
+ highestFlatFor = node;
+ }
+ pullupForNode(node);
+
+ fors.add(node);
+ TypedDescriptor loopVar = node.getInit().getLoc().getDesc();
+ if (new UntouchedLoopVariable().check(node, loopVar)) {
+ loopVars.add(loopVar);
+ }
+
+ defaultBehavior(node);
+ loopVars.remove(loopVar);
+ fors.remove(node);
+
+ // The current node is flat if and only if the current highest
+ // flat node is at least as high as it
+ if (highestFlatFor != null) {
+ flatFors.add(node);
+ // If this node is the highest flat node, then after popping
+ // it, no higher nodes are flat
+ if (highestFlatFor == node) {
+ highestFlatFor = null;
+ }
+ }
+ return true;
+ }
+
+ // A ForNode A can be pulled up to a ForNode B above it if the bounds
+ // of node A are invariant while B is executing
+ // This method pulls up every ForNode as far as possible
+ // A node is flat if every ForNode in its subtree can be pulled up to
+ // its level, or higher
+ private void pullupForNode(ForNode node) {
+ // If this ForNode is an outer loop, it cannot be pulled up
+ if (fors.isEmpty()) {
+ pullup.put(node, node);
+ return;
+ }
+
+ ExpressionNode lowerBound = node.getInit().getValue();
+ ExpressionNode upperBound = node.getEnd();
+ UntouchedLoopVariable visitor = new UntouchedLoopVariable();
+
+ int size = fors.size();
+ ForNode cur = node;
+ ForNode next;
+
+ for (int i = 0; i < size; i++) {
+ // We do not pull up ForNodes above loops which are not
+ // completely flat
+ if (cur == highestFlatFor) {
+ break;
+ }
+
+ // If the lower and upper bounds of this loop are invariant
+ // in the next higher loop, pull it up
+ next = fors.get(size - 1 - i);
+ if (visitor.check(next, lowerBound) &&
+ visitor.check(next, upperBound)) {
+ cur = next;
+ } else {
+ break;
+ }
+ }
+
+ // The level to which this node can be pulled up is an upper
+ // bound on the current flattest node
+ highestFlatFor = cur;
+ pullup.put(node, cur);
+ }
+
+ @Override
+ public Boolean visit(MathOpNode node) {
+ if (fors.isEmpty()) {
+ return false;
+ }
+
+ MathOp op = node.getOp();
+ boolean left = (node.getLeft().accept(this) == true);
+ boolean right = (node.getRight().accept(this) == true);
+ boolean monotonic = false;
+
+ if (op == MathOp.ADD) {
+ monotonic = left && right;
+ } else if (op == MathOp.SUBTRACT) {
+ monotonic = left && (node.getRight() instanceof IntNode);
+ } else if (op == MathOp.MULTIPLY) {
+ monotonic = left && (node.getRight() instanceof IntNode) &&
+ (((IntNode)node.getRight()).getValue() >= 0);
+ if (!monotonic) {
+ monotonic = right && (node.getLeft() instanceof IntNode) &&
+ (((IntNode)node.getLeft()).getValue() >= 0);
+ }
+ } else if (op == MathOp.DIVIDE) {
+ monotonic = left && (node.getRight() instanceof IntNode) &&
+ (((IntNode)node.getRight()).getValue() > 0);
+ }
+
+ if (monotonic) {
+ monotonicExprs.add(node);
+ /*
+ System.out.println("Found monotonic math op:");
+ node.accept(new AstPrettyPrinter());
+ System.out.println("");
+ */
+ }
+ return monotonic;
+ }
+
+ @Override
+ public Boolean visit(VariableNode node) {
+ LocationNode loc = node.getLoc();
+ if (loc instanceof ArrayLocationNode) {
+ defaultBehavior(node);
+ return false;
+ } else {
+ if (loopVars.contains(loc.getDesc())) {
+ monotonicExprs.add(node);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public Boolean visit(IntNode node) {
+ monotonicExprs.add(node);
+ return true;
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.