/* * 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.HashSet; import java.util.List; import java.util.Set; import polyglot.ast.Node; import polyglot.ast.Return; import polyglot.ast.Stmt; import polyglot.ast.Block; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.Assign; import polyglot.ast.Receiver; import polyglot.ast.Call; import polyglot.ast.ProcedureCall; import polyglot.ast.New; import polyglot.ast.NewArray; import polyglot.visit.NodeVisitor; import x10.ast.Async; import x10.ast.Future; /** * The <code>AsyncElimination</code> runs over the AST and * 'inlines' the Async blocks in the current activity in case * the body of the async performs only a simple remote read / or write. * * This optimization can render the results of the sampling incorrect * because remote accesses may disappear in the code and look like * local accesses! * * Note 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 async and future. */ public class AsyncElimination 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 Async) { Async as = (Async) n; Stmt as_body = as.body(); Stmt simple_stmt = checkIfSimpleBlock_(as_body); if (simple_stmt != null && isOptimizableStmt_(simple_stmt)) { // replace the whole sync stmt with the simple smt; if (DEBUG_) System.out.println("AsyncElimination - for async=" + n); ret = simple_stmt; } } else if (n instanceof Call) { // Futures that are forced Call c = (Call) n; List args = c.arguments(); Receiver r = c.target(); if (("force".equals(c.name().id()) || "apply".equals(c.name().id())) && args.size() == 0 && r instanceof Future) { Future f = (Future) r; Block f_block = f.body(); if (isOptimizableClosureBlock_(f_block)) { if (DEBUG_) System.out.println("AsyncElimination - for future=" + n); ret = getReturnExpr_(f_block); } } } return ret; } /** * returns the single statement that may be replaced for the async * */ private Stmt checkIfSimpleBlock_(Stmt s) { Stmt the_one_stmt = null; if (s instanceof Block) { Block b = (Block) s; List 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 an async is optimizable if it is * a simple assignment and the rhs meets the criteraia of * method isOptimizableExpr_. */ 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 Assign) { Assign a_expr = (Assign) e_expr; ret = isOptimizableExpr_(a_expr.right()); } } return ret; } /** * The closure block is optimizable if it is empty or just returns an optimizable expr. */ private boolean isOptimizableClosureBlock_(Block b) { return getReturnExpr_(b) != null; } private Expr getReturnExpr_(Block b) { if (b.statements().size() == 1) { Stmt s = b.statements().get(0); if (s instanceof Return) { Return r = (Return) s; if (r.expr() != null) return r.expr(); } } return null; } /** * Traverses an expression and determines if it does not * - invoke another method * - is future * - does not allocate other objects / arrays **/ private boolean isOptimizableExpr_(Expr e) { final Set<Node> critical = CollectionFactory.newHashSet(); e.visit( new NodeVisitor() { public Node leave( Node old, Node n, NodeVisitor v) { if ( n instanceof ProcedureCall || n instanceof Future || n instanceof New || // implies call to constructor n instanceof NewArray) // implies execution of initializer { critical.add(n); } return n; } }); return critical.isEmpty(); } }