/* * 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.ArrayList; import java.util.List; import polyglot.ast.Block; import polyglot.ast.Branch; import polyglot.ast.Conditional; import polyglot.ast.Empty; import polyglot.ast.Expr; import polyglot.ast.Field; import polyglot.ast.FloatLit; import polyglot.ast.If; import polyglot.ast.IntLit; import polyglot.ast.Lit; import polyglot.ast.Local; import polyglot.ast.LocalDecl; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Return; import polyglot.ast.Stmt; import polyglot.ast.Throw; import polyglot.ast.VarDecl; import polyglot.frontend.Job; import polyglot.types.Context; import polyglot.types.LocalDef; import polyglot.types.Name; import polyglot.types.QName; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.Types; import polyglot.types.VarDef; import polyglot.types.VarInstance; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.ErrorHandlingVisitor; import polyglot.visit.NodeVisitor; import x10.ast.Async; import x10.ast.Async_c; import x10.ast.AtEach; import x10.ast.AtExpr; import x10.ast.AtStmt; import x10.ast.Closure; import x10.ast.Closure_c; import x10.ast.StmtExpr; import x10.ast.X10Formal; import x10.constraint.XLit; import x10.constraint.XTerm; import x10.constraint.XVar; import x10.extension.X10Ext; import x10.optimizations.ForLoopOptimizer; import x10.types.TypeParamSubst; import x10.types.checker.Converter; import x10.types.constants.BooleanValue; import x10.types.constants.ClosureValue; import x10.types.constants.ConstantValue; import x10.types.constants.StringValue; import x10.types.constraints.CConstraint; import x10.util.AltSynthesizer; import x10.visit.Desugarer.ClosureCaptureVisitor; /** * Very simple constant propagation pass. * <p> If an expr has a constant value, replace it with the value. * * <p> Replace branches on constants with the consequent or alternative as appropriate. * * <p> TODO: Dead code elimination. visitor. * * @author nystrom */ public class ConstantPropagator extends ContextVisitor { private static AltSynthesizer syn; private final Job job; private final TypeSystem xts; private final boolean propClosures; public ConstantPropagator(Job job, TypeSystem ts, NodeFactory nf, boolean propagateClosures) { super(job, ts, nf); syn = new AltSynthesizer(ts, nf); this.job = job; this.xts = ts; this.propClosures = propagateClosures; } @Override protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException { Position pos = n.position(); // AST nodes that capture lexical environments need to have the captured environment // recomputed in leaveCall because we may have done one of two things: // (a) Constant propagated away uses of a lexically captured variable, thus // reducing the captured environment. // (b) Propagated one or more closure literals into the body of the construct, // which can increase the captured environment. if (n instanceof Async) { ((Async)n).body().visit(new ClosureCaptureVisitor(this.context(), ((Async)n).asyncDef())); } else if (n instanceof AtStmt) { ((AtStmt)n).body().visit(new ClosureCaptureVisitor(this.context(), ((AtStmt)n).atDef())); } else if (n instanceof AtEach) { // Only want to visit the body to recompute capture, but need to simulate // visiting the formal so that it gets in scope for the body so env capture can find it. AtEach ateach = (AtEach)n; Context tmpContext = this.context().pushBlock(); ateach.formal().addDecls(tmpContext); ateach.body().visit(new ClosureCaptureVisitor(tmpContext, ateach.atDef())); } else if (n instanceof AtExpr) { ((AtExpr)n).body().visit(new ClosureCaptureVisitor(this.context(), ((AtExpr)n).closureDef())); } else if (n instanceof Closure) { n.visit(new ClosureCaptureVisitor(this.context(), ((Closure)n).closureDef())); } if (!(n instanceof Expr || n instanceof Stmt)) return n; if (n instanceof StmtExpr) return n; if (n instanceof Lit) return n; if (n instanceof LocalDecl) { LocalDecl d = (LocalDecl) n; if (d.flags().flags().isFinal()) { Expr init = d.init(); if (init != null && init.isConstant()) { ConstantValue cv = constantValue(init); if (propClosures || !(cv instanceof ClosureValue)) { d.localDef().setConstantValue(constantValue(init)); if (isConstant(init)) { return nf.Empty(d.position()); } } } } } if (n instanceof Local) { Local l = (Local) n; if (l.localInstance().def().isConstant()) { ConstantValue o = l.localInstance().def().constantValue(); if (null != o && !(o instanceof StringValue)) { if (o instanceof ClosureValue) { if (propClosures) { return makeClosureLiteral((ClosureValue)o); } else { return n; } } else { return o.toLit(nf, xts, l.type(), n.position()); } } } } if (n instanceof Expr) { Expr e = (Expr) n; if (isConstant(e)) { ConstantValue o = constantValue(e); if (null != o) { if (o instanceof ClosureValue) { if (propClosures) { return makeClosureLiteral((ClosureValue)o); } else { return n; } } else { return o.toLit(nf, xts, e.type(), e.position()); } } } } if (n instanceof Conditional) { Conditional c = (Conditional) n; Expr cond = c.cond(); if (isConstant(cond)) { boolean b = ((BooleanValue) constantValue(cond)).value(); if (b) return c.consequent(); else return c.alternative(); } } if (n instanceof If) { If c = (If) n; Expr cond = c.cond(); if (isConstant(cond)) { boolean b = ((BooleanValue) constantValue(cond)).value(); if (b) return c.consequent(); else return c.alternative() != null ? c.alternative() : nf.Empty(pos); } } if (n instanceof Block){ Block b = (Block) n; List<Stmt> stmts = new ArrayList<Stmt>(); for (Stmt s : b.statements()) { if (!(s instanceof Empty)) { stmts.add(s); if (divertsFlow(s)) { if (b instanceof StmtExpr) { // ExpressionFlattener will have eliminated these for the Java back-end b = ((StmtExpr) b).result(null); // result can't be reached, throw it away } return b.statements(stmts); } } } if (stmts.size() < b.statements().size()) return b.statements(stmts); return b; } return n; } private Closure_c makeClosureLiteral(ClosureValue cv) { Reinstantiator reinstantiator= new Reinstantiator(TypeParamSubst.IDENTITY); ContextVisitor visitor= new NodeTransformingVisitor(job, ts, nf, reinstantiator).context(context()); Closure_c cls = (Closure_c)cv.getClosure().visit(visitor); // reinstantiate locals in the body cls.visit(new ClosureCaptureVisitor(this.context(), cls.closureDef())); return cls; } public static ConstantValue constantValue(Expr e) { if (e.isConstant()) return e.constantValue(); if (e.type().isNull()) return ConstantValue.makeNull(); if (e instanceof Field) { Field f = (Field) e; if (f.target() instanceof Expr) { Expr target = (Expr) f.target(); Type t = target.type(); CConstraint c = Types.xclause(t); if (c != null) { XTerm val = c.bindingForSelfField(f); if (val instanceof XLit) { XLit l = (XLit) val; return ConstantValue.make(f.type(), l.val()); } } } } Type t = e.type(); CConstraint c = Types.xclause(t); if (c != null) { XVar r = c.self(); if (r instanceof XLit) { XLit l = (XLit) r; return ConstantValue.make(t, l.val()); } } return null; } private static boolean isConstant(Expr e) { Type type = e.type(); if (null == type) // TODO: this should never happen, determine if and why it does return false; TypeSystem ts = type.typeSystem(); if (isNative(e, ts)) return false; if (type.isNull()) return true; if (e.isConstant()) { ConstantValue cv = e.constantValue(); if (!type.isReference()) return true; return (cv instanceof ClosureValue); // ClosureLiterals are the only non-null reference type that we allow ourselves to constant propagate. } if (e instanceof Field) { Field f = (Field) e; if (f.target() instanceof Expr) { Expr target = (Expr) f.target(); if (isNative(target, ts)) return false; Type t = target.type(); CConstraint c = Types.xclause(t); if (c != null) { XTerm val = c.bindingForSelfField(f); if (val instanceof XLit) { return true; } } } } CConstraint c = Types.xclause(type); if (c != null) { XVar r = c.self(); if (r instanceof XLit) { XLit l = (XLit) r; return true; } } return false; } /** * Would a statement prevent control flow from reaching the sequentially next statement in a Block? * * @param s the Stmt that might divert the flow of control * @return true iff s changes normal control flow */ private boolean divertsFlow(Stmt s) { if (s instanceof Block) { List<Stmt> statements = ((Block) s).statements(); int size = statements.size(); if (0 == size) return false; return divertsFlow(statements.get(size-1)); } return s instanceof Return || s instanceof Throw || s instanceof Branch; } /** * Determine if a node is annotated "@Native". * * @param node a node which may appear constant but be native instead * @return true, if node has an "@Native" annotation; false, otherwise */ private static boolean isNative(Node node, TypeSystem ts) { return null != node.ext() && node.ext() instanceof X10Ext && !((X10Ext) node.ext()).annotationMatching(ts.NativeType()).isEmpty(); } }