/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * (C) Copyright IBM Corporation 2006-2010. */ package x10.visit; import java.util.List; import polyglot.ast.Node; import polyglot.ast.Stmt; import polyglot.ast.Block; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.Field; import polyglot.ast.Assign; import polyglot.ast.ArrayAccess; import polyglot.ast.ProcedureCall; import polyglot.ast.FieldAssign; import polyglot.ast.ArrayAccessAssign; import polyglot.ast.LocalAssign; import polyglot.ast.New; import polyglot.ast.NewArray; import polyglot.visit.NodeVisitor; import x10.ast.Atomic; import x10.ast.SettableAssign; /** * The <code>AsyncElimination</code> runs over the AST and * strips of 'atomic' form blocks that read or write only a single * shared variable. * * This transformation might cause programs with * await statements to hang when atomic blocks that modify variables checked * in await are removed (and hence also the notify associated with atomic). * * Note also that this optimization is not legal in real X10 programs! * We use it just for the simple shared memory variant of the X10 * prototype system to reduce the overheads of the very general * implementation of atomic. **/ public class AtomicElimination extends NodeVisitor { private final boolean DEBUG_ = false; public Node leave(Node old, Node n, NodeVisitor v) { Node ret = n; // If we have a labeled block consisting of just one statement, then // flatten the block and label the statement instead. We also flatten // labeled blocks when there is no reference to the label within the // block. if (n instanceof Atomic) { Atomic as = (Atomic) n; Stmt as_body = as.body(); Stmt simple_stmt = checkIfSimpleBlock_(as_body); if (simple_stmt != null && isOptimizableStmt_(simple_stmt)) { // replace the whole atomic stmt with its body; if (DEBUG_) System.out.println("AtomicElimination - for atomic=" + n); ret = simple_stmt; } } return ret; } /** * returns the single statement that may be replaced for the atomic */ private Stmt checkIfSimpleBlock_(Stmt s) { Stmt the_one_stmt = null; if (s instanceof Block) { Block b = (Block) s; List<Stmt> l = b.statements(); if (l.size() == 1) the_one_stmt = (Stmt) l.get(0); } else { the_one_stmt = s; } return the_one_stmt; } /** * The stmt in the body of atomic is optimizable if it is * a simple assignment and the rhs meets the criteria of * method isOptimizableExpr_. * * For writes to shared variables, the atomic block must be kept, because * the write might lead to a notfiy that is necessary to wake-up an activity * that is blocked on await. */ private boolean isOptimizableStmt_(Stmt s) { boolean ret = false; if (s instanceof Eval) { Eval e = (Eval) s; Expr e_expr = e.expr(); if (e_expr instanceof SettableAssign) { SettableAssign a; a = (SettableAssign) e_expr; ret = numFieldRefs_(a.array()) == 0 && // don't allow writes numFieldRefs_(a.index()) == 0 && // don't allow writes numFieldRefs_(a.right()) <= 1; } if (e_expr instanceof ArrayAccessAssign) { ArrayAccessAssign a; a = (ArrayAccessAssign) e_expr; ret = numFieldRefs_(a.array()) == 0 && // don't allow writes numFieldRefs_(a.index()) == 0 && // don't allow writes numFieldRefs_(a.right()) <= 1; } if (e_expr instanceof FieldAssign) { FieldAssign a; a = (FieldAssign) e_expr; ret = false; } if (e_expr instanceof LocalAssign) { LocalAssign a; a = (LocalAssign) e_expr; ret = numFieldRefs_(a.right()) <= 1; } } return ret; } /* cludge class */ private class Ctr { int ctr; } private int numFieldRefs_(List<Expr> es) { int c = 0; for (Expr e : es) { c += numFieldRefs_(e); } return c; } /** * Traverses an expression and determines if it does not * - invoke another method * - is future * - does not allocate other objects / arrays **/ private int numFieldRefs_(Expr e) { final Ctr c = new Ctr(); e.visit( new NodeVisitor() { public Node leave( Node old, Node n, NodeVisitor v) { if ( n instanceof Field || n instanceof ArrayAccess || n instanceof ArrayAccessAssign || n instanceof FieldAssign) c.ctr++; else if (n instanceof ProcedureCall || n instanceof New || // implies call to constructor n instanceof NewArray) // implies execution of initializer) c.ctr += 2; return n; } }); return c.ctr; } }