/* * 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.compiler.ws.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import polyglot.ast.Assign; import polyglot.ast.Block; import polyglot.ast.Call; import polyglot.ast.ClassBody; import polyglot.ast.ConstructorCall; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.For; import polyglot.ast.Local; import polyglot.ast.LocalAssign; import polyglot.ast.LocalDecl; import polyglot.ast.New; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Receiver; import polyglot.ast.Return; import polyglot.ast.Special; import polyglot.ast.Stmt; import polyglot.ast.TypeNode; import polyglot.types.ClassDef; import polyglot.types.ClassType; import polyglot.types.Flags; import polyglot.types.LocalDef; import polyglot.types.LocalInstance; import polyglot.types.MethodDef; import polyglot.types.Name; import polyglot.types.ProcedureDef; import polyglot.types.QName; import polyglot.types.Ref; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.Types; import polyglot.util.Pair; import polyglot.util.Position; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.visit.NodeVisitor; import x10.ast.Async; import x10.ast.AtStmt; import x10.ast.Closure; import x10.ast.ClosureCall; import x10.ast.Finish; import x10.ast.PlacedClosure; import x10.ast.StmtSeq; import x10.ast.When; import x10.ast.X10Call; import x10.ast.X10MethodDecl; import x10.compiler.ws.WSTransformState; import x10.types.ClosureDef; import x10.types.MethodInstance; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.X10Def; import x10.types.X10MethodDef; import polyglot.types.Context; import polyglot.types.TypeSystem; /** * @author Haichuan * * This class contains some utility functions, such as forming the standard name * */ public class WSUtil { //------------- Section I: Debug & Log Info Mation ------------- static boolean turnOffAllDebugMsg = false; static boolean turnOffAllInfoMsg = false; static public void debug(String msg, Node node){ if(!turnOffAllDebugMsg){ System.out.println("[WS_DEBUG] " + msg); node.prettyPrint(System.out); System.out.println(); } } static public void debug(String msg){ if(!turnOffAllDebugMsg){ System.out.println("[WS_DEBUG] " + msg); } } static public void info(String msg){ if(!turnOffAllInfoMsg){ System.out.println("[WS_INFO] " + msg); } } static public void err(String msg, Node n) throws SemanticException{ System.err.println("[WS_ERR]" + msg); if(n != null){ System.err.println(" "); n.prettyPrint(System.err); System.err.println(); throw new SemanticException(msg, n.position()); } else{ throw new SemanticException(msg); } } //------------------ Section II: Naming Management static Map<ClassType, Map<String, Integer>> container2MethodNameMap; // classType methodName, number static Map<String, Integer> dupNameCountMap; private static String getMethodName(MethodDef methodDef){ return methodDef.name().toString(); } /** * Check whether the current name is used, if used, add the count number to it. * @param name * @return */ public static String checkDupName(String name){ if(dupNameCountMap == null){ dupNameCountMap = new HashMap<String, Integer>(); } if(dupNameCountMap.containsKey(name)){ //the name is used, int count = dupNameCountMap.get(name); String newName = name + count; dupNameCountMap.put(name, count+1); dupNameCountMap.put(newName, 1); //in the newname is not used by user return newName; } else{ dupNameCountMap.put(name, 1); return name; } } /** * @param f The dividable For * @return the name for the divide-and-conquer method */ public static String getDividableForMethodName(For f){ return "_$dcFor"; //divide-and-conquer for } /** * Form the closure's name by line & column number. Should be identical for a inner class * @param closure * @return */ public static String getClosureBodyClassName(Closure closure){ Position pos = closure.position(); String posStr = "L" + pos.line() + "_" + pos.column(); String tempName = "_$Closure_" + posStr; return tempName; } public static String getMethodBodyClassName(MethodDef methodDef){ if(container2MethodNameMap == null){ container2MethodNameMap = CollectionFactory.newHashMap(); } String tempName = "_$" + getMethodName(methodDef); ClassType classType = (ClassType) methodDef.container().get(); Map<String, Integer> nameNumberMap = container2MethodNameMap.get(classType); if(nameNumberMap == null){ nameNumberMap = CollectionFactory.newHashMap(); nameNumberMap.put(tempName, 1); container2MethodNameMap.put(classType, nameNumberMap); return tempName; } if(nameNumberMap.containsKey(tempName)){ int num = nameNumberMap.get(tempName); nameNumberMap.put(tempName, num + 1); return tempName+"$"+num; } else{ nameNumberMap.put(tempName, 1); return tempName; } } public static String getMethodFastPathName(MethodDef methodDef){ return getMethodName(methodDef) + "_F"; } public static String getMethodSlowPathName(MethodDef methodDef){ return getMethodName(methodDef) + "_S"; } public static String getAsyncStmtClassName(String parentName){ return parentName + "A"; } public static String getRemoteRemoteClassName(String parentName){ return parentName + "R"; } public static String getFinishStmtClassName(String parentName){ return parentName + "F"; } public static String getBlockFrameClassName(String parentName){ return parentName + "B"; } public static String getExceptionFrameClassName(String parentName){ return parentName + "E"; } public static String getIFBlockClassName(String parentName, boolean condition){ return parentName + "IF" + (condition ? "T" : "F" ); } public static String getLoopClassName(String parentName){ return parentName + "L"; } public static String getSwitchClassName(String parentName){ return parentName + "S"; } public static String getWhenClassName(String parentName){ return parentName + "W"; } /** * Recursively scan the block of statements to find all local declaration * @param block * @return */ public static List<LocalDecl> recursiveScanForLocals(Block block){ ArrayList<LocalDecl> locals = new ArrayList<LocalDecl>(); LocalDeclVisitor ldVisitor = new LocalDeclVisitor(locals); block.visit(ldVisitor); return locals; } /** * Check the x10def's annotation contains "WS" or not * @param def * @return true if the def contains "WS" annotation */ protected static boolean isWSTarget(X10Def def) { try { Type t = def.typeSystem().systemResolver().findOne(QName.make("x10.compiler.WS")); return !def.annotationsMatching(t).isEmpty(); } catch (SemanticException e) { return false; } } /** * Check whether the code node contains concurrent construct or not * Concurrent construct includes: * async/finish/etc. * NOTE: if it contains a closure/inner class /etc, and the closure has some concurrent, * the current node will not be visited as a concurrent node * @param node * @return */ public static boolean containsConcurrentConstruct(Node node){ if(node == null){ return false; //no data } if (node instanceof X10MethodDecl && isWSTarget(((X10MethodDecl) node).methodDef())) { return true; } if(node instanceof Closure){ //need remove closure's shell, other wise the visitor will not work node = ((Closure)node).body(); } ConcurrentConstructBlockVisitor ccbv = new ConcurrentConstructBlockVisitor(); node.visit(ccbv); return ccbv.isConcurrent(); } /** * Check whether one code node is complex code block or not. * A complex code block is one code block containing async/finish/method invocation * @param block * @return */ public static boolean isComplexCodeNode(Node node, WSTransformState wsState){ if(node == null){ return false; //blank node } if(containsConcurrentConstruct(node)){ return true; //if it contains async/finish/etc. } //check whether it contains method call to other target method if(node instanceof Closure){ //need remove closure's shell, other wise the visitor will not work node = ((Closure)node).body(); } MethodCallVisitor cbv = new MethodCallVisitor(wsState); node.visit(cbv); return cbv.isComplex(); } /** * Calculate how many concurrent calls invoked during the input code node * @param node the node to search * @param wsState the WorkStealing state with concurrent call maps * @return the number of concurrent call invocations */ public static int calcConcurrentCallNums(Node node, WSTransformState wsState){ if(node == null){ return 0; //blank node } if(node instanceof Closure){ //need remove closure's shell, other wise the visitor will not work node = ((Closure)node).body(); } //check whether it contains method call to other target method MethodCallVisitor cbv = new MethodCallVisitor(wsState); node.visit(cbv); return cbv.getConcurrentCallNums(); } /** * Scan the block of statements, and check whether it contains a return statement * @param node * @return */ public static boolean hasReturnStatement(Node node){ ScanReturnStatementVisitor srsv = new ScanReturnStatementVisitor(); if(node instanceof Closure){ //need remove closure's shell, other wise the visitor will not work node = ((Closure)node).body(); } node.visit(srsv); return srsv.isHasReturn(); } /** * Scan the block of statements to find all local declaration * @param block * @return */ public static List<LocalDecl> scanForLocals(Block block){ ArrayList<LocalDecl> locals = new ArrayList<LocalDecl>(); for (Stmt s : block.statements()){ if (s instanceof LocalDecl) { locals.add((LocalDecl) s); } } return locals; } /** * Find all callees from an input node, e.g. methoddecl * @param node * @return all callees' methoddef */ public static List<ProcedureDef> scanForCallees(Node node){ MethodCallFindingVisitor mcfv = new MethodCallFindingVisitor(); if(node instanceof Closure){ //need remove closure's shell, other wise the visitor will not work node = ((Closure)node).body(); } node.visit(mcfv); return mcfv.getCallees(); } /** * Identify whether one statement is a local assign * If it is a local assign, it may need move() in async frame * @param s * @return the local */ static public LocalAssign identifyLocalAssign(Stmt s){ if(s == null || !(s instanceof Eval)){ return null; } Expr e = ((Eval)s).expr(); if(e instanceof LocalAssign){ return ((LocalAssign)e); } return null; } /** * Identify receiver = aCall() statement * @param s * @return Pair<Assign, Call> pair. If null, not such an expression */ static public Pair<Assign, Call> identifyAssignByCall(Stmt s){ if(s == null){ return null; } Pair<Assign, Call> result = null; if(s instanceof Eval){ Expr expr = ((Eval)s).expr(); if(expr instanceof Assign){ Assign assignExpr = (Assign)expr; Expr rightExpr = assignExpr.right(); if(rightExpr instanceof Call){ result = new Pair<Assign, Call>(assignExpr, (Call)rightExpr); } } } return result; } /** * Transform original call's def to ws call's def * e.g. foo(abc:int) -> foo_F(w:Worker, up:Frame, abc:int); * @param methodDef original method def * @param wts WSTransformState * @return */ static public X10MethodDef createWSCallMethodDef(MethodDef methodDef, TypeSystem ts) { X10ClassType containerClassType = (X10ClassType) methodDef.container().get(); X10ClassDef containerClassDef = containerClassType.x10Def(); List<Ref<? extends Type>> formalTypes = new ArrayList<Ref<? extends Type>>(); formalTypes.add(Types.ref(ts.Worker())); formalTypes.add(Types.ref(ts.Frame())); formalTypes.add(Types.ref(ts.FinishFrame())); for (Ref<? extends Type> f : methodDef.formalTypes()) { formalTypes.add(f); //all formals are added in } TypeSystem xts = methodDef.typeSystem(); X10MethodDef mDef = (X10MethodDef) xts.methodDef(methodDef.position(), methodDef.errorPosition(), Types.ref(containerClassDef.asType()), methodDef.flags(), methodDef.returnType(), Name.make(WSUtil.getMethodFastPathName(methodDef)), formalTypes, Collections.<Ref<? extends Type>>emptyList()); mDef.setTypeParameters(methodDef.typeParameters()); List<LocalDef> formalNames = new ArrayList<LocalDef>(); Name workerName = Name.make("worker"); LocalDef workerLDef = methodDef.typeSystem().localDef(methodDef.position(), Flags.FINAL, Types.ref(xts.Worker()), workerName); Name upName = Name.make("up"); LocalDef upLDef = methodDef.typeSystem().localDef(methodDef.position(), Flags.FINAL, Types.ref(ts.Frame()), upName); Name ffName = Name.make("ff"); LocalDef ffLDef = methodDef.typeSystem().localDef(methodDef.position(), Flags.FINAL, Types.ref(ts.FinishFrame()), ffName); formalNames.add(workerLDef); formalNames.add(upLDef); formalNames.add(ffLDef); for (LocalDef f : methodDef.formalNames()) { formalNames.add(f); //all formals are added in } mDef.setFormalNames(formalNames); return mDef; } static public MethodInstance createWSMethodInstance(MethodInstance mi, TypeSystem ts) { X10MethodDef mDef = createWSCallMethodDef(mi.def(), ts); MethodInstance m = mDef.asInstance(); m = (MethodInstance) m.container(mi.container()); List<Type> formalTypes = new ArrayList<Type>(); formalTypes.add((ts.Worker())); formalTypes.add((ts.Frame())); formalTypes.add((ts.FinishFrame())); for (Type f : mi.formalTypes()) { formalTypes.add(f); //all formals are added in } m = m.formalTypes(formalTypes); List<LocalInstance> formalNames = new ArrayList<LocalInstance>(); formalNames.add(ts.localDef(mi.position(), Flags.FINAL, Types.ref(ts.Worker()), Name.make("worker")).asInstance()); formalNames.add(ts.localDef(mi.position(), Flags.FINAL, Types.ref(ts.Frame()), Name.make("up")).asInstance()); formalNames.add(ts.localDef(mi.position(), Flags.FINAL, Types.ref(ts.FinishFrame()), Name.make("ff")).asInstance()); for (LocalInstance f : mi.formalNames()) { formalNames.add(f); //all formals are added in } m = (MethodInstance) m.formalNames(formalNames); m = (MethodInstance) m.typeParameters(mi.typeParameters()); return m; } /** * * Replace original call, e.g. fib(n) with generated WS call * --> fib_fast(worker, this, this, 1, n); * The newArgs are worker/this/this/1 * @param xnf node factory * @param aCall Original call * @param methodDef the new methodDef * @param newArgTypes additional arguments's types * @param newArgs additional arguments, including worker/frame/upframe * @return new method call */ public static X10Call replaceMethodCallWithWSMethodCall(NodeFactory xnf, X10Call aCall, MethodInstance mi, List<Expr> newArgs) { ArrayList<Expr> args = new ArrayList<Expr>(newArgs); args.addAll(aCall.arguments()); //for the name Name name = mi.name(); //new name //build new call aCall = (X10Call) aCall.methodInstance(mi); aCall = (X10Call) aCall.name(xnf.Id(aCall.name().position(), name)); aCall = (X10Call) aCall.arguments(args); return aCall; } /** * Identify receiver = async aCall() statement * In the WS step, the code is transformed into a future. * The assign is the assign, but the call is just the return part of the future * @param s * @return Pair<Assign, Call> pair. If null, not such an expression * / static public Pair<Assign, Call> identifyAssignByAsyncCall(Stmt s, Context context){ TypeSystem xts = (TypeSystem) context.typeSystem(); Pair<Assign, Call> result = null; if(s instanceof Eval){ Expr expr = ((Eval)s).expr(); if(expr instanceof Assign){ Assign assignExpr = (Assign)expr; Expr rightExpr = assignExpr.right(); if(rightExpr instanceof Call){ Call aCall = (Call)rightExpr; Name forceName = Name.make("force"); Type futureType = xts.load("x10.util.Future"); MethodInstance futureMI = null; try { futureMI = xts.findMethod(futureType, xts.MethodMatcher(futureType, forceName, Collections .<Type> emptyList(), Collections.<Type> emptyList(), context)); MethodDef futureMD = futureMI.def(); if(futureMD.equals(aCall.methodInstance().def())){ //this call is a async, now we need find the return call Receiver r = aCall.target(); Stmt s1 = ((Future)r).body().statements().get(0); //s1 should be return Call bCall = (Call)((Return)s1).expr(); result = new Pair<Assign, Call>(assignExpr, bCall); } } catch (SemanticException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } return result; } */ /** * Used to transform a seq stmt into a block * If the input is not a StmtSeq, just return * @param xnf * @param s * @return only one stmt, maybe a block */ static public Stmt seqStmtsToOneStmt(NodeFactory xnf, Stmt s){ if(s instanceof StmtSeq){ return xnf.Block(s.position(),((StmtSeq)s).statements()); } else{ return s; } } /** * Used to transform a stmt into a Block * If the input is not a block, wrap it into a block. * @param xnf * @param s * @return only one stmt, should be a block, cannot be a StmtSeq */ static public Block stmtToBlock(NodeFactory xnf, Stmt s){ if(s instanceof StmtSeq){ return xnf.Block(s.position(), ((StmtSeq)s).statements()); } else if(s instanceof Block){ return (Block)s; } else { return xnf.Block(s.position(), s); } } /** * @param stmt a construct's body, such as if/for/async * @return a list of stmts */ static public List<Stmt> unwrapBodyBlockToStmtList(Stmt stmt){ if(stmt instanceof Block){ List<Stmt> stmts = new ArrayList<Stmt>(); for(Stmt s : ((Block)stmt).statements()){ stmts.addAll(unwrapToStmtList(s)); } return stmts; } else{ return Collections.singletonList(stmt); } } /** * @param stmt that may contain StmtSeq * @return stmts without StmtSeq */ static public List<Stmt> unwrapToStmtList(Stmt stmt){ if(stmt instanceof Block){ if(stmt instanceof StmtSeq){ List<Stmt> stmts = new ArrayList<Stmt>(); for(Stmt s : ((StmtSeq)stmt).statements()){ stmts.addAll(unwrapToStmtList(s)); } return stmts; } else{ return Collections.singletonList(stmt); //pure block not unwrapp } } //other case return Collections.singletonList(stmt); } /** * Unroll block until it either a single stmt, or a block with more than one stmts * If the top level is StmtSeq, still return StmtSeq * @param block * @return */ static public Stmt unwrapToOneStmt(Stmt stmt){ if(!(stmt instanceof Block)){ return stmt; //non block just return; } List<Stmt> blockSS = ((Block)stmt).statements(); if(blockSS.size() > 1){ return stmt; //return current block } else{ //size == 1; stmt = blockSS.get(0); if(stmt instanceof Block){ return unwrapToOneStmt((Block)stmt); } else{ return stmt; } } } /** * @param c the current Context * @return the current context's container class context; */ static public Context popToClassContext(Context c){ while (c != null && !c.isClass()) { c = c.pop(); } return c; } /** * Set all special's qualifier with the outer class type node if the special's qualifier is null * @param s * @param outerDef * @param xnf * @return */ static public Stmt setSpeicalQualifier(Stmt s, ClassDef outerDef, NodeFactory xnf){ SpecialQualifierSetter sqs = new SpecialQualifierSetter(xnf, outerDef); if (s == null) return null; return (Stmt) s.visit(sqs); } static class ScanReturnStatementVisitor extends NodeVisitor{ private boolean hasReturn; ScanReturnStatementVisitor(){ } private int noVisitDepth;//if noVisitDepth > 0, not visit public NodeVisitor enter(Node n) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth++; } return super.enter(n); } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth--; return n; } if(noVisitDepth > 0){ return n; } if(n instanceof Return){ hasReturn = true; } return n; } public boolean isHasReturn() { return hasReturn; } } static class ConcurrentConstructBlockVisitor extends NodeVisitor{ private boolean isConcurrent; private int noVisitDepth;//if noVisitDepth > 0, not visit public NodeVisitor enter(Node n) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth++; } return super.enter(n); } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth--; return n; } if(noVisitDepth > 0){ return n; } if (n instanceof Finish //|| n instanceof Future //Future is translated from Async || n instanceof PlacedClosure // is An abstraction for at(p) Expr || n instanceof AtStmt // at stmt || n instanceof Async //direct async || n instanceof When) { isConcurrent = true; } return n; } public boolean isConcurrent() { return isConcurrent; } } /** * * Locate all callees from one node; * @author Haichuan * */ static class MethodCallFindingVisitor extends NodeVisitor{ private List<ProcedureDef> callees; public MethodCallFindingVisitor(){ callees = new ArrayList<ProcedureDef>(); } private int noVisitDepth;//if noVisitDepth > 0, not visit public NodeVisitor enter(Node n) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth++; } return super.enter(n); } public Node leave(Node old, Node n, NodeVisitor v) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth--; return n; } if(noVisitDepth > 0){ return n; } if(n instanceof Call){ Call aCall = (Call)n; callees.add(aCall.methodInstance().def()); } else if(n instanceof ClosureCall){ ClosureCall aCall = (ClosureCall)n; System.out.println("[WS_WARNING]Cannot build closure call relationship"); System.out.println(" ClosureDef = "+aCall.closureInstance().def()); System.out.println(" Target = " + aCall.target()); callees.add(aCall.closureInstance().def()); } else if(n instanceof ConstructorCall){ ConstructorCall aCall = (ConstructorCall)n; callees.add(aCall.constructorInstance().def()); } else if(n instanceof New){ New aCall = ((New)n); callees.add(aCall.constructorInstance().def()); } return n; } protected List<ProcedureDef> getCallees() { return callees; } } static class MethodCallVisitor extends NodeVisitor{ MethodCallVisitor(WSTransformState wsState){ this.wsState = wsState; } private WSTransformState wsState; private int complexCallNum; private int noVisitDepth;//if noVisitDepth > 0, not visit public NodeVisitor enter(Node n) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth++; } return super.enter(n); } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { if(n instanceof Closure || n instanceof ClassBody){ noVisitDepth--; return n; } if(noVisitDepth > 0){ return n; } //FIXME: need consider call to closure/constructor/new with concurrent if(n instanceof Call){ if((wsState != null)){ Call aCall = (Call)n; if(wsState.isConcurrentCallSite(aCall)){ complexCallNum++; } } else{ //no wsState, always treat it as a complex node complexCallNum++; } } return n; } public int getConcurrentCallNums(){ return complexCallNum; } public boolean isComplex() { return complexCallNum > 0; } } /** * @author Haibo * * This class is used to collect local variable declarations. * */ static class LocalDeclVisitor extends NodeVisitor { protected ArrayList<LocalDecl> lDeclList; public LocalDeclVisitor(ArrayList<LocalDecl> lDeclList){ this.lDeclList = lDeclList; } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { if (n instanceof LocalDecl) { lDeclList.add((LocalDecl) n); } return n; } } /** * @author Haichuan * Set all special's qualifier to outer class if the special's qualifier is null */ static class SpecialQualifierSetter extends NodeVisitor{ //protected ClassDef outerDef; TypeNode tn ; public SpecialQualifierSetter(NodeFactory xnf, ClassDef outerDef){ tn = xnf.CanonicalTypeNode(Position.COMPILER_GENERATED, outerDef.asType()); } public Node leave(Node parent, Node old, Node n, NodeVisitor v){ if(n instanceof Special){ Special s = (Special)n; if(s.qualifier() == null){ return s.qualifier(tn); } } return n; } } }