/* * 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; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import polyglot.ast.Assign; import polyglot.ast.Binary; import polyglot.ast.Binary.Operator; import polyglot.ast.Block; import polyglot.ast.Call; import polyglot.ast.ClassDecl; import polyglot.ast.ConstructorDecl; import polyglot.ast.Do; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.Field; import polyglot.ast.FieldAssign; import polyglot.ast.For; import polyglot.ast.ForInit; import polyglot.ast.ForUpdate; import polyglot.ast.Formal; import polyglot.ast.Id; import polyglot.ast.If; import polyglot.ast.IntLit; import polyglot.ast.Local; import polyglot.ast.LocalAssign; import polyglot.ast.LocalDecl; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Receiver; import polyglot.ast.Return; import polyglot.ast.Stmt; import polyglot.ast.Switch; import polyglot.ast.Term; import polyglot.ast.TypeNode; import polyglot.ast.While; import polyglot.frontend.Job; import polyglot.types.ClassDef; import polyglot.types.ClassType; import polyglot.types.CodeDef; import polyglot.types.Context; import polyglot.types.Flags; import polyglot.types.LocalDef; import polyglot.types.LocalInstance; import polyglot.types.MemberDef; import polyglot.types.MethodDef; import polyglot.types.Name; 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.Pair; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import x10.ExtensionInfo; import x10.ast.Async; import x10.ast.AtEach; import x10.ast.AtStmt_c; import x10.ast.Closure; import x10.ast.FinishExpr; import x10.ast.ForLoop; import x10.ast.Offer; import x10.ast.StmtSeq; import x10.ast.X10Binary_c; import x10.ast.X10ClassDecl; import x10.ast.X10Formal; import x10.ast.X10MethodDecl; import x10.ast.X10Special; import x10.compiler.ws.WSTransformState.MethodType; import x10.compiler.ws.util.Triple; import x10.compiler.ws.util.WSTransformationContent; import x10.compiler.ws.util.WSUtil; import x10.compiler.ws.util.CodePatternDetector.Pattern; import x10.optimizations.ForLoopOptimizer; import x10.types.AsyncDef; import x10.types.AtDef; import x10.types.EnvironmentCapture; import x10.types.MethodInstance; import x10.types.ParameterType; import x10.types.ThisDef; import x10.types.X10MemberDef; import x10.types.X10MethodDef; import x10.util.AltSynthesizer; import x10.util.CollectionFactory; import x10.util.Synthesizer; import x10.util.synthesizer.CodeBlockSynth; import x10.util.synthesizer.MethodSynth; import x10.util.synthesizer.NewLocalVarSynth; import x10.visit.Desugarer; import x10.visit.ExpressionFlattener; import x10.visit.X10InnerClassRemover; /** * * ContextVisitor that pre-process AST code for work stealing. * @author Haichuan * * Some code cannot be transformed by WS Code Generator directly. * 1) Concurrent call in If condition, When/Do/While/Switch expr, * For's init/iterator/condition, Compound expressions. * 2) Concurrent call in ForLoop * 3) AtEach * Jobs performed in this pass * I: Transform Un-supported stmts * 1) transform compound stmts into simple stmts by ExpressionFlattener * 2) transform forloop (for(x in domain)) into standard for by forloopunrolloer * 3) transform AtEach by lowerer's code * II: Advanced optimization * 1) Transform simple for (e.g. for(var i:int=0; i < 1000; i++ ) async) * into divide-and-conquer style */ public class WSCodePreprocessor extends ContextVisitor { static final boolean debug = true; static final Position compilerPos = Position.COMPILER_GENERATED; /** * A small container to store the diviable for's information */ protected class DividableFor{ Type boundType; Expr lowerRef; Expr upperRef; Expr condLeft; Binary.Operator condOperator; LocalDecl iteratorDecl; Stmt iteratorAssign; Async forAsync; } protected class IteratorFinderVisitor extends NodeVisitor { private boolean found = false; private final LocalDef iDef; public IteratorFinderVisitor(LocalDef iDef){ this.iDef = iDef; } public boolean isFound(){ return found; } public Node leave(Node old, Node n, NodeVisitor v) { if(n instanceof Local){ LocalInstance li = ((Local) n).localInstance(); if(li.def() == iDef){ found = true; } } return n; } } /** * A visitor to locate all local vars referenced in for's body */ public static class LocalVarCaptureVisitor extends NodeVisitor { private final Context context; private final Set<Local> locals; public LocalVarCaptureVisitor(Context context) { this.context = context; this.locals = CollectionFactory.newHashSet(); } /** * @return non-duplicated locals */ public List<Local> getLocals(){ //the locals may contains duplicate locals. we need filter them out by name List<Local> ls = new ArrayList<Local>(); Set<String> names = CollectionFactory.newHashSet(); for(Local l : locals){ String name = l.name().id().toString(); if(names.contains(name)){ continue; } names.add(name); ls.add(l); } return ls; } @Override public Node leave(Node old, Node n, NodeVisitor v) { if (n instanceof Local) { LocalInstance li = ((Local) n).localInstance(); VarInstance<?> o = context.findVariableSilent(li.name()); if (li == o || (o != null && li.def() == o.def())) { locals.add((Local)n); } } return n; } } // Single static WSTransformState shared by all visitors (FIXME) public static WSTransformState wts; private final Set<X10MethodDecl> genMethodDecls; private final Synthesizer synth; private final AltSynthesizer altsynth; public WSCodePreprocessor(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); genMethodDecls = CollectionFactory.newHashSet(); synth = new Synthesizer(nf, ts); altsynth = new AltSynthesizer(ts, nf); } public static void setWALATransTarget(ExtensionInfo extensionInfo, WSTransformationContent target){ //DEBUG if(debug){ WSUtil.debug("Use WALA CallGraph Data..."); } wts = new WSTransformStateWALA(extensionInfo, target); WSCodeGenerator.wts = wts; } public static void buildCallGraph(ExtensionInfo extensionInfo) { //DEBUG if(debug){ WSUtil.debug("Build Simple Graph Graph..."); } wts = new WSTransformStateSimple(extensionInfo); WSCodeGenerator.wts = wts; } @Override protected Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException { if(n instanceof For){ return visitFor((For)n); } if(n instanceof ForLoop){ return visitForLoop((ForLoop)n); } if(n instanceof AtEach){ return visitAtEach((AtEach)n); } if(n instanceof Eval){ return visitEval((Eval)n); } if(n instanceof Do){ return visitDo((Do)n); } if(n instanceof While){ return visitWhile((While)n); } if(n instanceof Switch){ return visitSwitch((Switch)n); } if(n instanceof If){ return visitIf((If)n); } if(n instanceof Return){ return visitReturn((Return)n); } if(n instanceof LocalDecl){ return visitLocalDecl((LocalDecl)n); } if(n instanceof ConstructorDecl){ return visitConstructorDecl((ConstructorDecl)n); } if(n instanceof Closure){ return visitClosure((Closure)n); } if(n instanceof Offer){ return visitOffer((Offer)n); } if (n instanceof X10ClassDecl) { return visitClass((X10ClassDecl)n); } return n; } private Node visitClass(X10ClassDecl cDecl) throws SemanticException { ClassDef cDef = cDecl.classDef(); List<X10MethodDecl> methods = getMethodDecls(cDef); if(methods.isEmpty()){ return cDecl; } cDecl = Synthesizer.addMethods(cDecl, methods); Desugarer desugarer = ((x10.ExtensionInfo) job.extensionInfo()).makeDesugarer(job); desugarer.begin(); desugarer = (Desugarer) desugarer.context(context()); //copy current context X10InnerClassRemover innerclassRemover = new X10InnerClassRemover(job, ts, nf); innerclassRemover.begin(); innerclassRemover = (X10InnerClassRemover) innerclassRemover.context(context()); //copy current context cDecl = (X10ClassDecl) cDecl.visit(desugarer); cDecl = (X10ClassDecl) cDecl.visit(innerclassRemover); return cDecl; } protected List<X10MethodDecl> getMethodDecls(ClassDef cDef) throws SemanticException { List<X10MethodDecl> mDecls = new ArrayList<X10MethodDecl>(); for(X10MethodDecl mDecl : genMethodDecls){ ClassDef containerDef = ((ClassType) mDecl.methodDef().container().get()).def(); if(containerDef == cDef){ mDecls.add(mDecl); } } return mDecls; } private Node visitOffer(Offer n) throws SemanticException { if(WSUtil.isComplexCodeNode(n.expr(), wts)){ return flattenStmt(n); } return n; } private Node visitClosure(Closure n) throws SemanticException { if(wts.getMethodType(n) != MethodType.NORMAL){ WSUtil.err("X10 Work Stealing doesn't support concurrent closure", n); } return n; } private Node visitConstructorDecl(ConstructorDecl n) throws SemanticException { if(wts.getMethodType(n) != MethodType.NORMAL){ WSUtil.err("X10 Work-Stealing doesn's support concurrent constructor", n); } return n; } /** * @param call * @return true if the call is "foo(...)" style. The "..." has no any concurrent call */ private boolean isSimpleConcurrentCall(Call call){ int concurrentCallNum = WSUtil.calcConcurrentCallNums(call, wts); if(concurrentCallNum == 1 && wts.isConcurrentCallSite(call)){ return true; } else{ return false; } } private Node visitLocalDecl(LocalDecl n) { if(n.init() == null){ return n; } // a local decl's initializer Expr expr = n.init(); if(!WSUtil.isComplexCodeNode(expr, wts)){ return n; } if(expr instanceof Call && isSimpleConcurrentCall((Call)expr)){ return n; } if(expr instanceof FinishExpr){ return n; } return flattenStmt(n); } private Node visitReturn(Return n) { if(WSUtil.isComplexCodeNode(n.expr(), wts)){ return flattenStmt(n); } return n; } private Node visitIf(If n) { if(WSUtil.isComplexCodeNode(n.cond(), wts)){ return flattenStmt(n); } return n; } private Node visitSwitch(Switch n) { if(WSUtil.isComplexCodeNode(n.expr(), wts)){ return flattenStmt(n); } return n; } private Node visitWhile(While n) { if(WSUtil.isComplexCodeNode(n.cond(), wts)){ return flattenStmt(n); } return n; } private Node visitDo(Do n) { if(WSUtil.isComplexCodeNode(n.cond(), wts)){ return flattenStmt(n); } return n; } private Node visitEval(Eval n) { //if the node is simple, no need any flatten. if(! WSUtil.isComplexCodeNode(n, wts)){ return n; } Expr expr = n.expr(); //Simple case 1: directly concurrent call if(expr instanceof Call && isSimpleConcurrentCall((Call)expr)){ return n; } else if(expr instanceof LocalAssign || expr instanceof FieldAssign){ Assign assign = (Assign)expr; Expr rightExpr = assign.right(); if(rightExpr instanceof Call && isSimpleConcurrentCall((Call)rightExpr)){ return n; } if(rightExpr instanceof FinishExpr){ return n; //collecting-finish } } return flattenStmt(n); } private static final Name DIST = Name.make("dist"); private static final Name RESTRICTION = Name.make("restriction"); private static final Name PLACES = Name.make("places"); /** * @param a ateach (p in D) S; * @return val d = D.dist; for (p in d.places()) async (p) for (pt in d|here) async S; * @throws SemanticException * The code is similar to the visitAtEach in lowerer. But we create async stmt directly * Not lower it */ private Node visitAtEach(AtEach a) throws SemanticException { Position pos = a.position(); Position bpos = a.body().position(); AsyncDef asyncDef = (AsyncDef)AtStmt_c.createDummyAsync(bpos, ts, context.currentClass(), context.currentCode(), context.currentCode().staticContext(), true); AtDef atDef = a.atDef(); Name tmp = Context.getNewVarName(); //for the dist Expr domain = a.domain(); Type dType = domain.type(); if (ts.isX10DistArray(dType)) { domain = altsynth.createFieldRef(pos, domain, DIST); dType = domain.type(); } LocalDef lDef = ts.localDef(pos, ts.Final(), Types.ref(dType), tmp); LocalDecl local = nf.LocalDecl(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, dType), nf.Id(pos, tmp), domain).localDef(lDef); X10Formal formal = (X10Formal) a.formal(); Type fType = formal.type().type(); assert (ts.isPoint(fType)); assert (ts.isDistribution(dType)); // Have to desugar some newly-created nodes Type pType = ts.Place(); MethodInstance rmi = ts.findMethod(dType, ts.MethodMatcher(dType, RESTRICTION, Collections.singletonList(pType), context())); Expr here = nf.Here(bpos); //org:visitHere(nf.Here(bpos)); Expr dAtPlace = nf.Call(bpos, nf.Local(pos, nf.Id(pos, tmp)).localInstance(lDef.asInstance()).type(dType), nf.Id(bpos, RESTRICTION), here).methodInstance(rmi).type(rmi.returnType()); Expr here1 = nf.Here(bpos); //org:visitHere(nf.Here(bpos)); List<VarInstance<? extends VarDef>> env = a.atDef().capturedEnvironment(); //Stmt body = async(a.body().position(), a.body(), a.clocks(), here1, null, env); Stmt body = nf.Async(bpos, a.clocks(), a.body()).asyncDef(asyncDef); Stmt inner = nf.ForLoop(pos, formal, dAtPlace, body).locals(formal.explode(this)); MethodInstance pmi = ts.findMethod(dType, ts.MethodMatcher(dType, PLACES, Collections.<Type>emptyList(), context())); Expr places = nf.Call(bpos, nf.Local(pos, nf.Id(pos, tmp)).localInstance(lDef.asInstance()).type(dType), nf.Id(bpos, PLACES)).methodInstance(pmi).type(pmi.returnType()); Name pTmp = Context.getNewVarName();; LocalDef pDef = ts.localDef(pos, ts.Final(), Types.ref(pType), pTmp); Formal pFormal = nf.Formal(pos, nf.FlagsNode(pos, ts.Final()), nf.CanonicalTypeNode(pos, pType), nf.Id(pos, pTmp)).localDef(pDef); List<VarInstance<? extends VarDef>> env1 = new ArrayList<VarInstance<? extends VarDef>>(env); env1.remove(formal.localDef().asInstance()); for (int i = 0; i < formal.localInstances().length; i++) { env1.remove(formal.localInstances()[i].asInstance()); } env1.add(lDef.asInstance()); //outter async: async at(p) body Stmt body1 = nf.AtStmt(bpos, nf.Local(bpos, nf.Id(bpos, pTmp)).localInstance(pDef.asInstance()).type(pType), inner).atDef(atDef); Stmt outAsync = nf.Async(bpos, a.clocks(), body1).asyncDef(asyncDef); Stmt outer = nf.ForLoop(pos, pFormal, places, outAsync); // TODO: Instead of creating ForLoop's and then removing them, // change the code above to create simple For's in the first place. ForLoopOptimizer flo = new ForLoopOptimizer(job, ts, nf); For newLoop = (For)outer.visit(((ContextVisitor)flo.begin()).context(context())); List<Stmt> stmts = new ArrayList<Stmt>(); stmts.add(local); stmts.add(newLoop); return nf.StmtSeq(pos, stmts); //WSUtil.err("X10 Work-Stealing doesn's support AtEach", n); } /** * Need transform ForLoop to For * @param n * @return * @throws SemanticException */ private Node visitForLoop(ForLoop n) throws SemanticException { //Only process concurrent for if(! WSUtil.isComplexCodeNode(n, wts)){ return n; } //Step 1: translate the forloop by forloopoptimizer ForLoopOptimizer flo = new ForLoopOptimizer(job, ts, nf); flo.begin(); flo.context(context()); Node floOut = n.visit(flo); //floOut could be a block or a for statement // there are two cases from ForLoopOptimizer // 1. only for; 2. some local declarations and for // for cases 2, we need add these local declarations into for's init For forStmt = null; List<Stmt> forBlockStmts = new ArrayList<Stmt>(); int forStmtLoc = -1; //the place the for stmt in the block if (floOut instanceof Block) { // case 2, re-wrap it into a stmtseq forBlockStmts.addAll(((Block)floOut).statements()); for (int i = 0; i < forBlockStmts.size(); i++) { Stmt s = forBlockStmts.get(i); if (s instanceof For) { forStmt = (For) s; forStmtLoc = i; break; } } assert(forStmtLoc >= 0); } else { forStmt = (For) floOut; } // second step, it's highly possible the body of a for stmt contains // additional block // { stmt1 // { ... //remove this level // } //remove this level // } // just remove the first level Stmt forBodyStmt = forStmt.body(); if (forBodyStmt instanceof Block) { ArrayList<Stmt> stmts = new ArrayList<Stmt>(); for (Stmt s : ((Block) forBodyStmt).statements()) { if (s instanceof Block) { stmts.addAll(((Block) s).statements()); } else { stmts.add(s); } } // now insert it into the forBodyStmt again; forStmt = forStmt.body(nf.Block(forStmt.position(), stmts)); } //Final step: still need check the for loop Stmt newStmt = (Stmt) visitFor(forStmt); if(forStmtLoc >= 0){ forBlockStmts.set(forStmtLoc, newStmt); newStmt = nf.StmtSeq(n.position(), forBlockStmts); } return newStmt; } /** * We suppose the stmt is flattened. And only transform * for() async() style. * @param n * @return * @throws SemanticException */ public Node visitFor(For n) throws SemanticException { //Only process concurrent for if(! WSUtil.isComplexCodeNode(n, wts)){ return n; } //Detect the init/iterator/condition List<Term> condTerms = new ArrayList<Term>(); condTerms.addAll(n.inits()); condTerms.addAll(n.iters()); condTerms.add(n.cond()); boolean needFlattern = false; for(Term t : condTerms){ if(WSUtil.isComplexCodeNode(t, wts)){ needFlattern = true; break; } } if(needFlattern){ return flattenStmt(n); } if(wts.OPT_FOR_ASYNC){ //for a simple For, we could try to transform it into divide and conquer style. DividableFor df = isDividableFor(n); if(df != null){ return transformDividableFor(df, n); } } return n; } private Stmt transformDividableFor(DividableFor df, For n) throws SemanticException { if(debug){ WSUtil.debug("Found dividable for:", n); } //First need detect all locals for copy LocalVarCaptureVisitor visitor = new LocalVarCaptureVisitor(context); //the only part need not check locals is for's upper ref & init decl n.body().visit(visitor); for(ForUpdate fu: n.iters()){ fu.visit(visitor); } df.condLeft.visit(visitor); List<Local> locals = visitor.getLocals(); String mName = WSUtil.getDividableForMethodName(n) + genMethodDecls.size(); X10MethodDecl mDecl = synthDividableForMethod(n, mName, context, df, locals); X10MethodDef mDef = mDecl.methodDef(); //now generate the in-place call Call call = synthDividableForMethodCall(n.position(), context, mDef, df.lowerRef, df.upperRef, synth.intValueExpr(1, compilerPos), df, visitor.getLocals()); wts.addSynthesizedConcurrentMethod(mDecl); genMethodDecls.add(mDecl); if(debug){ WSUtil.debug("Transform to divid-and-conquer call", call); WSUtil.debug("Method Body:", mDecl); } return nf.Eval(n.position(), call); } private X10MethodDecl synthDividableForMethod(For n, String methodName, Context c, DividableFor df, List<Local> locals) throws SemanticException{ //define the synthesized method's type Flags flag = Flags.FINAL; if(c.inStaticContext()){ flag = flag.set(Flags.STATIC); } MethodSynth mSynth = new MethodSynth(nf, WSUtil.popToClassContext(c), n.position(), c.currentClassDef(), methodName); mSynth.setFlag(flag); for(ParameterType pt : c.currentCode().typeParameters()){ mSynth.addTypeParameter(pt, pt.getVariance()); } //Formals: Expr lowerRef = mSynth.addFormal(compilerPos, df.boundType, "_$lower"); Expr upperRef = mSynth.addFormal(compilerPos, df.boundType, "_$upper"); Expr sliceNumRef = mSynth.addFormal(compilerPos, ts.Int(), "_$sliceNum"); List<Local> formalLocals = new ArrayList<Local>(); for(Local local : locals){ Local formalLocal = mSynth.addFormal(local.position(), Flags.FINAL, local.type(), local.name().id()); formalLocals.add(formalLocal); } //the method's body CodeBlockSynth methodBodySynth = mSynth.getMethodBodySynth(n.body().position()); Context mContext = methodBodySynth.getContext(); //if's condition //if((sliceNum >> 2) < Runtime.NTHREADS Expr runtimeThreadsRef = synth.makeFieldAccess(compilerPos, nf.CanonicalTypeNode(compilerPos, ts.Runtime()), Name.make("NTHREADS"), mContext); Expr sliceNum4 = nf.Binary(compilerPos, sliceNumRef, Binary.SHR, synth.intValueExpr(2, compilerPos)).type(ts.Int()); Expr cond = nf.Binary(compilerPos, sliceNum4, Binary.LT, runtimeThreadsRef).type(ts.Boolean()); //val m:int = (s+e)/2; //val nSliceNum = sliceNum << 1; Expr totalBoundExpr = nf.Binary(compilerPos, lowerRef, Binary.ADD, upperRef).type(df.boundType); Expr middleBoundExpr = nf.Binary(compilerPos, totalBoundExpr, Binary.SHR, synth.intValueExpr(1, compilerPos)).type(df.boundType); NewLocalVarSynth mLocalSynth = new NewLocalVarSynth(nf, mContext, compilerPos, Flags.FINAL, middleBoundExpr); Expr middleBoundRef = mLocalSynth.getLocal(); Expr newSliceNumExpr = nf.Binary(compilerPos, sliceNumRef, Binary.SHL, synth.intValueExpr(1, compilerPos)).type(ts.Int()); NewLocalVarSynth nSliceLocalSynth = new NewLocalVarSynth(nf, mContext, compilerPos, Flags.FINAL, newSliceNumExpr); Expr newSliceNumRef = nSliceLocalSynth.getLocal(); //divide-and-conquer //async forR0(s,m, nSliceNum, a, load); Call call1 = synthDividableForMethodCall(n.position(), mContext, mSynth.getDef(), lowerRef, middleBoundRef, newSliceNumRef, df, formalLocals); Async async = df.forAsync.body(nf.Eval(compilerPos, call1)); //forR0(m,e, nSliceNum, a, load); Call call2 = synthDividableForMethodCall(n.position(), mContext, mSynth.getDef(), middleBoundRef, upperRef, newSliceNumRef, df, formalLocals); Block ifTrueBlock = nf.Block(compilerPos, mLocalSynth.genStmt(), nSliceLocalSynth.genStmt(), async, nf.Eval(compilerPos, call2)); //synth the //for(var i:int=s; i<e; i++){ // a(i) = dowork(a(i), load); //} List<ForInit> newForInits = new ArrayList<ForInit>(); newForInits.add(df.iteratorDecl.init(lowerRef)); For newFor = n.inits(newForInits); Expr condBinary = nf.Binary(compilerPos, df.condLeft, df.condOperator, upperRef).type(ts.Boolean()); newFor = newFor.cond(condBinary); //prepare for body List<Stmt> stmts = new ArrayList<Stmt>(); if(df.iteratorAssign != null){ stmts.add(df.iteratorAssign); } stmts.addAll(WSUtil.unwrapBodyBlockToStmtList(df.forAsync.body())); newFor = newFor.body(nf.Block(compilerPos, stmts)); //Final if If ifStmt = nf.If(n.position(), cond, ifTrueBlock, newFor); methodBodySynth.addStmt(ifStmt); return mSynth.close(); } private Call synthDividableForMethodCall(Position pos, Context ct, X10MethodDef mDef, Expr lowerRef, Expr upperRef, Expr sliceNumRef, DividableFor df, List<Local> locals){ Id callId = nf.Id(pos, mDef.name()); ArrayList<Expr> paras = new ArrayList<Expr>(); ArrayList<Type> formalTypes = new ArrayList<Type>(); ArrayList<TypeNode> typeArgs = new ArrayList<TypeNode>(); ArrayList<Type> typeActuals = new ArrayList<Type>(); for (ParameterType pt : mDef.typeParameters()) { typeActuals.add(pt); typeArgs.add(nf.CanonicalTypeNode(pos, pt)); } paras.add(lowerRef); formalTypes.add(df.boundType); paras.add(upperRef); formalTypes.add(df.boundType); paras.add(sliceNumRef); formalTypes.add(ts.Int()); for(Local local : locals){ paras.add(local); formalTypes.add(local.type()); } Receiver r; //decide the right method: instance of static if (mDef.flags().isStatic()) { r = nf.CanonicalTypeNode(compilerPos, ct.currentClass()); } else { r = synth.thisRef(ct.currentClass(), compilerPos); } Call call = (Call) nf.X10Call(pos, r, callId, typeArgs, paras).type(ts.Void()); MethodInstance mi = mDef.asInstance(); mi = (MethodInstance) mi.flags(mDef.flags()); mi = mi.name(mDef.name()); mi = mi.returnType( mDef.returnType().get()); mi = mi.formalTypes(formalTypes); mi = (MethodInstance) mi.typeParameters(typeActuals); return call.methodInstance(mi); } /** * Identify For patterns, like * 1) for(var i:int = lowerBound; i condition (<, <=, >, >=); i updater) { async {...}} * 2) for(var i:int = lowerBound; i condition (<, <=, >, >=); i updater) { val ii = i; async {...}} * @param n * @return <lowerBound, UpperBound, iterator type> */ private DividableFor isDividableFor(For n){ //Four condition //1: init has only one variables and the variable' //2: iterator is only related this single var's change //3: condition is larger or smaller //4: the body has only one stmt(aysnc) or two stmts(val assign, right is the iterator DividableFor df = new DividableFor(); //Condition 1: List<ForInit> forInits = n.inits(); if(forInits.size() != 1 || !(forInits.get(0) instanceof LocalDecl)) { return null; } LocalDecl ld = (LocalDecl) forInits.get(0); if(ld.init() == null){ return null; } df.iteratorDecl = ld; df.lowerRef = ld.init(); df.boundType = ld.type().type();; //Condition 2: seems no need //Condition 3: if(!(n.cond() instanceof Call)){ return null; } Call call = (Call) n.cond(); Name callName = call.name().id(); Name nameLE = X10Binary_c.binaryMethodName(Binary.LE); Name nameGE = X10Binary_c.binaryMethodName(Binary.GE); Name nameLT = X10Binary_c.binaryMethodName(Binary.LT); Name nameGT = X10Binary_c.binaryMethodName(Binary.GT); //The condition call should be compare operator if(callName.equals(nameLE)){ df.condOperator = Binary.LE; } else if(callName.equals(nameGE)){ df.condOperator = Binary.GE; } else if(callName.equals(nameLT)){ df.condOperator = Binary.LT; } else if(callName.equals(nameGT)){ df.condOperator = Binary.GT; } else { return null; } //Now detect iterator is in the left or right. Cannot be in either side Expr condLeftExpr = (Expr) call.target(); Expr condRightExpr = call.arguments().get(0); IteratorFinderVisitor iFinder = new IteratorFinderVisitor(ld.localDef()); condLeftExpr.visit(iFinder); boolean leftFound = iFinder.isFound(); iFinder = new IteratorFinderVisitor(ld.localDef()); condRightExpr.visit(iFinder); boolean rightFound = iFinder.isFound(); if(rightFound && leftFound || !rightFound && !leftFound){ //both found or not found return null; } else if(leftFound){ df.condLeft = condLeftExpr; df.upperRef = condRightExpr; } else { //right found - Switch left and right df.condLeft = condRightExpr; df.upperRef = condLeftExpr; //and need change operator if(df.condOperator == Binary.LE) {df.condOperator = Binary.GE; } else if(df.condOperator == Binary.GE) {df.condOperator = Binary.LE; } else if(df.condOperator == Binary.LT) {df.condOperator = Binary.GT; } else if(df.condOperator == Binary.GT) {df.condOperator = Binary.LT; } } //and the bound could be only integer type, and we need prepare a "1" for "GE"/"LE" case Expr uni; if(df.boundType == ts.Int()){ uni = nf.IntLit(compilerPos, IntLit.INT, 1).type(ts.Int()); } else if(df.boundType == ts.UInt()){ uni = nf.IntLit(compilerPos, IntLit.UINT, 1).type(ts.UInt()); } else if(df.boundType == ts.Long()){ uni = nf.IntLit(compilerPos, IntLit.LONG, 1).type(ts.Long()); } else if(df.boundType == ts.ULong()){ uni = nf.IntLit(compilerPos, IntLit.ULONG, 1).type(ts.ULong()); } else if(df.boundType == ts.Short()){ uni = nf.IntLit(compilerPos, IntLit.SHORT, 1).type(ts.Short()); } else if(df.boundType == ts.UShort()){ uni = nf.IntLit(compilerPos, IntLit.USHORT, 1).type(ts.UShort()); } else if(df.boundType == ts.Byte()){ uni = nf.IntLit(compilerPos, IntLit.BYTE, 1).type(ts.Byte()); } else if(df.boundType == ts.UByte()){ uni = nf.IntLit(compilerPos, IntLit.UBYTE, 1).type(ts.UByte()); } else{ return null; } //Need process GE or LE. Change to GT or LT, and change upperRef if(df.condOperator == Binary.GE){ df.condOperator = Binary.GT; df.upperRef = nf.Binary(compilerPos, df.upperRef, Binary.SUB, uni).type(df.boundType); } if(df.condOperator == Binary.LE){ df.condOperator = Binary.LT; df.upperRef = nf.Binary(compilerPos, df.upperRef, Binary.ADD, uni).type(df.boundType); } //Condition 4: Stmt body = WSUtil.unwrapToOneStmt(n.body()); //first case only one async if(body instanceof Async){ df.forAsync = (Async) body; return df; } //second case if(body instanceof Block){ List<Stmt> stmts = WSUtil.unwrapBodyBlockToStmtList(body); if(stmts.size() == 2 && stmts.get(1) instanceof Async){ df.forAsync = (Async) stmts.get(1); if(stmts.get(0) instanceof LocalDecl){ LocalDecl ld2 = (LocalDecl)stmts.get(0); if(ld2.init() != null && ld2.init() instanceof Local){ Local local = (Local)ld2.init(); if(local.localInstance().def() == ld.localDef()){ //just the iterator assignment df.iteratorAssign = ld2; return df; } } } } } return null; } private Node flattenStmt(Stmt n){ ExpressionFlattener ef = new ExpressionFlattener(job, ts, nf); ef.begin(); Stmt outS = (Stmt) n.visit(ef); if(debug){ WSUtil.debug("Flatten Input:", n); if(outS instanceof Block){ if(outS instanceof StmtSeq){ WSUtil.debug("Flatten Output is OK:StmtSeq"); } else{ WSUtil.debug("Flatten Output is WRONG:" + outS.getClass()); } } WSUtil.debug("Flatten Output:", outS); } return outS; } }