/* * 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 2010. */ package x10.visit; import java.util.HashSet; import java.util.Set; import polyglot.ast.Assign; import polyglot.ast.Binary; import polyglot.ast.Cast; import polyglot.ast.Expr; import polyglot.ast.Local; import polyglot.ast.LocalDecl; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.ProcedureCall; import polyglot.ast.Unary; import polyglot.frontend.Job; import polyglot.types.LocalDef; import polyglot.types.ProcedureDef; import polyglot.types.QName; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.util.InternalCompilerError; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import x10.ast.X10Cast; import x10.errors.Errors; import x10.extension.X10Ext; import x10.types.X10Def; import x10.types.checker.Converter; /** * @author Bowen Alpern * */ public class SideEffectDetector extends ContextVisitor { public SideEffectDetector(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); } private final boolean sideEffectDetected[] = new boolean[1]; private final Set<LocalDef> captiveLocals = CollectionFactory.newHashSet(); /** * @param expr * @return */ public boolean hasSideEffects(Expr expr) { if (null == expr) return false; captiveLocals.clear(); sideEffectDetected[0] = false; expr.visit(this); return sideEffectDetected[0]; } /** * Annotation types. */ private Type ImpureType; private Type PureType; /** * Names of the annotation types that pertain to dead assignment elimination. */ private static final QName SIDE_EFFECTS_ANNOTATION = QName.make("x10.compiler.Impure"); private static final QName NO_SIDE_EFFECTS_ANNOTATION = QName.make("x10.compiler.Pure"); @Override public NodeVisitor begin() { try { ImpureType = ts.systemResolver().findOne(SIDE_EFFECTS_ANNOTATION); PureType = ts.systemResolver().findOne(NO_SIDE_EFFECTS_ANNOTATION); } catch (SemanticException e) { InternalCompilerError ice = new InternalCompilerError("Unable to find required Annotation Types"); SemanticException se = new SemanticException(ice); //TODO: Internal compiler error should be removed Errors.issue(job, se); PureType = null; } return super.begin(); } /* (non-Javadoc) * @see polyglot.visit.NodeVisitor#override(polyglot.ast.Node) */ @Override public Node override(Node n) { if (sideEffectDetected[0]) return n; if (hasPureAnnotation(n)) return n; if (n instanceof Expr && ((Expr) n).isConstant()) return n; if ( hasImpureAnnotation(n) || (n instanceof ProcedureCall && !isPure(((ProcedureCall) n).procedureInstance().def())) || (n instanceof Assign && isGlobalAssign((Assign) n)) || (n instanceof Unary && isGlobalIncrement((Unary) n)) || (n instanceof Cast) && mustCheck((Cast) n) || (n instanceof Binary && ((Binary) n).operator() == Binary.DIV) ) { sideEffectDetected[0] = true; return n; } if (n instanceof LocalDecl) { LocalDecl ld = (LocalDecl) n; captiveLocals.add(ld.localDef()); } return null; } /** * @param n * @return */ private boolean mustCheck(Cast n) { // if (n.expr().type is a subtype of n.castType()) return false; // TODO: implement this if (!(n instanceof X10Cast)) return true; X10Cast c = (X10Cast) n; if (c.conversionType() == Converter.ConversionType.CHECKED) { return true; } if (true) // DEBUG: for now, check all casts return true; return false; } /** * Determine if a procedure has one or more side effects. * * @param def the procedure in question * @return true, if def has a side effect; false, otherwise * TODO: examine the body of def for side effects. */ private boolean isPure(ProcedureDef def) { if (!(def instanceof X10Def)) return false; if (null == PureType) return false; try { return !((X10Def) def).annotationsMatching(PureType).isEmpty(); } catch (NullPointerException npe) { // don't know why this happens, but it does return false; // conservatively assume the worst } } /** * @param n * @return */ private boolean isGlobalAssign(Assign n) { if (!(n.left() instanceof Local)) return true; return !captiveLocals.contains(((Local) n.left()).localInstance().def()); } /** * @param n * @return */ private boolean isGlobalIncrement(Unary u) { if (u.expr() instanceof Local && captiveLocals.contains(((Local) u.expr()).localInstance().def())) return false; if (u.operator().equals(Unary.POST_DEC)) return true; if (u.operator().equals(Unary.PRE_DEC)) return true; if (u.operator().equals(Unary.POST_INC)) return true; if (u.operator().equals(Unary.PRE_INC)) return true; return false; } /** * @param node * @return */ private boolean hasImpureAnnotation(Node node) { if (node.ext() instanceof X10Ext){ X10Ext ext = (X10Ext) node.ext(); return !ext.annotationMatching(ImpureType).isEmpty(); } return false; } /** * @param node * @return */ private boolean hasPureAnnotation(Node node) { if (node.ext() instanceof X10Ext){ X10Ext ext = (X10Ext) node.ext(); return !ext.annotationMatching(PureType).isEmpty(); } return false; } }