/* * 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.codegen; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Map; import polyglot.ast.Assign; import polyglot.ast.Binary; import polyglot.ast.Block; import polyglot.ast.Call; import polyglot.ast.Catch; import polyglot.ast.ClassBody; import polyglot.ast.Do; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.For; import polyglot.ast.Formal; import polyglot.ast.Local; import polyglot.ast.LocalAssign; import polyglot.ast.LocalDecl; import polyglot.ast.Loop; 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.Try; import polyglot.ast.TypeNode; import polyglot.ast.While; import polyglot.frontend.Job; import polyglot.types.ClassDef; import polyglot.types.ClassType; import polyglot.types.FieldDef; import polyglot.types.FieldInstance; import polyglot.types.Flags; import polyglot.types.LocalDef; import polyglot.types.MethodDef; import polyglot.types.Name; 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.ErrorHandlingVisitor; import polyglot.visit.NodeVisitor; import x10.ast.AnnotationNode; import x10.ast.Async; import x10.ast.AtStmt; import x10.ast.Closure; import x10.ast.Finish; import x10.ast.Offer; import x10.ast.When; import x10.ast.X10Call; import x10.ast.X10ClassDecl; import x10.extension.X10Ext_c; import x10.compiler.ws.WSCodeGenerator; import x10.compiler.ws.WSTransformState; import x10.compiler.ws.util.AddIndirectLocalDeclareVisitor; import x10.compiler.ws.util.AdvLocalAccessToFieldAccessReplacer; import x10.compiler.ws.util.ClosureDefReinstantiator; import x10.compiler.ws.util.CodePatternDetector; import x10.compiler.ws.util.ILocalToFieldContainerMap; import x10.compiler.ws.util.ReferenceContainer; import x10.compiler.ws.util.TransCodes; import x10.compiler.ws.util.WSUtil; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.MethodInstance; import polyglot.types.Context; import x10.types.X10LocalDef; import x10.types.X10MethodDef; import polyglot.types.TypeSystem; import x10.types.checker.PlaceChecker; import x10.util.Synthesizer; import x10.util.synthesizer.ClassSynth; import x10.util.synthesizer.CodeBlockSynth; import x10.util.synthesizer.ConstructorSynth; import x10.util.synthesizer.FieldSynth; import x10.util.synthesizer.InstanceCallSynth; import x10.util.synthesizer.MethodSynth; import x10.util.synthesizer.NewInstanceSynth; import x10.util.synthesizer.NewLocalVarSynth; import x10.util.synthesizer.SuperCallSynth; import x10.visit.ExpressionFlattener; /** * The base of different kinds of WS class generator * * Because one method will be divided into different segments (control flows) * and each segment will be transformed into one inner class, * these inner classes are chained together in a tree structure. * * In order to represent the tree structure, the AbstractWSClassGen * is a tree node, too. It has parent and child * * @author Haichuan */ public abstract class AbstractWSClassGen implements ILocalToFieldContainerMap { static final protected Position compilerPos = Position.COMPILER_GENERATED; final protected Job job; final protected NodeFactory xnf; final protected TypeSystem xts; final protected Context xct; final protected WSTransformState wts; final protected Synthesizer synth; //stateless synthesizer final protected WSSynthesizer wsynth; //synthesizer for work-stealing final protected Block codeBlock; // store all code block final protected Set<Name> fieldNames; //store names of all fields in current frame final protected String className; //used for child to query, and form child's name final protected ClassSynth classSynth; //stateful synthesizer for class gen final protected int frameDepth; //the depth to parent; final protected MethodSynth fastMSynth; final protected MethodSynth resumeMSynth; final protected MethodSynth backMSynth; //Fields to maintain the tree final private AbstractWSClassGen up; private List<AbstractWSClassGen> children; //lazy initialization private AbstractWSClassGen(Job job, Context xct, WSTransformState wts, AbstractWSClassGen up, String className, ClassType frameType, int frameDepth, Flags flags, X10ClassDef outer, Stmt stmt) { this.job = job; xnf = (NodeFactory) job.extensionInfo().nodeFactory(); xts = (TypeSystem) job.extensionInfo().typeSystem(); synth = new Synthesizer(xnf, xts); wsynth = new WSSynthesizer(xnf, xts); this.xct = xct; this.wts = wts; this.up = up; fieldNames = CollectionFactory.newHashSet(); //used to store all other fields' names this.frameDepth = frameDepth; this.codeBlock = stmt == null ? null : synth.toBlock(stmt); // switch statement has null codeBlock this.className = className; classSynth = wsynth.createClassSynth(job, xct, frameType, outer, flags, className); fastMSynth = wsynth.createFastMethodSynth(classSynth, isFastPathInline(frameType)); resumeMSynth = wsynth.createResumeMethodSynth(classSynth); backMSynth = wsynth.createBackMethodSynth(classSynth); } // method frames protected AbstractWSClassGen(Job job, NodeFactory xnf, Context xct, WSTransformState wts, String className, ClassType frameType, Flags flags, X10ClassDef outer, Stmt stmt) { this(job, xct, wts, null, className, frameType, 0, flags, outer, stmt); } // nested frames protected AbstractWSClassGen(AbstractWSClassGen parent, AbstractWSClassGen up, String classNamePrefix, ClassType frameType, Stmt stmt) { this(parent.job, parent.xct, parent.wts, up, classNamePrefix + parent.assignChildId(), frameType, parent.frameDepth + 1, parent.classSynth.getFlags(), parent.classSynth.getOuter(), stmt); parent.addChild(this); //here we process the type parameters. For a target method that contains type parameters, //all the generated inner classes should contain the type parameters; X10ClassDef parentDef = parent.classSynth.getClassDef(); classSynth.addTypeParameters(parentDef.typeParameters(), parentDef.variances()); } /** * Return direct children * @return */ public AbstractWSClassGen[] getChildren() { if(children == null){ return new AbstractWSClassGen[0]; } else{ return children.toArray(new AbstractWSClassGen[children.size()]); } } /** * Used to pretty print * @param indent the indent before the output * @return */ public String getFrameStructureDesc(int indent){ StringBuffer sb = new StringBuffer(); for(int i = 0; i < indent; i++){ sb.append(' '); } sb.append(this.className); if(this instanceof WSAsyncClassGen){ WSAsyncClassGen aFrame = (WSAsyncClassGen)this; sb.append(" (k = ").append(aFrame.parentK.className).append(')'); } if(this instanceof WSRemoteMainFrameClassGen){ WSRemoteMainFrameClassGen rFrame = (WSRemoteMainFrameClassGen)this; sb.append(" (r = ").append(rFrame.parentR.className).append(')'); } sb.append(System.getProperty("line.separator")); AbstractWSClassGen[] children = getChildren(); for(AbstractWSClassGen child : children){ sb.append(child.getFrameStructureDesc(indent + 2)); } return sb.toString(); } /** * Close and return all the inner classes rooted by the current class as one list * @return */ public List<X10ClassDecl> close() throws SemanticException { ArrayList<X10ClassDecl> classes = new ArrayList<X10ClassDecl>(); recursiveAddClasses(classes, this); return classes; } public int getFrameDepth() { return frameDepth; } private void recursiveAddClasses(ArrayList<X10ClassDecl> classes, AbstractWSClassGen parent) throws SemanticException { // This method is only used by public AbstractWSClassGen[] genAllOffString() classes.add(parent.classSynth.close()); AbstractWSClassGen[] children = parent.getChildren(); for(AbstractWSClassGen child : children){ recursiveAddClasses(classes, child); } } /** * And one inner class into the current class to build the frame tree * @param child * @return the child's sequence id used form the child frame's name */ private void addChild(AbstractWSClassGen child) { if(children == null){ children = new ArrayList<AbstractWSClassGen>(); } children.add(child); } int childrenNameCount; //start from 0. not the same as children's number //since finish frame has more child classFrames /** * Query the child's sequence id in parent * @param child * @return the id num; */ public synchronized int assignChildId() { int retId = childrenNameCount; childrenNameCount ++; return retId; } public AbstractWSClassGen getUpFrame() { return up; } public X10ClassDef getClassDef(){ return classSynth.getClassDef(); } //TODO: interfaces to query one specific parent //From the list, we could query all added inner classes public String getClassName() { return className; } public WSFinishStmtClassGen getDirectFinishFrameClassGen(){ AbstractWSClassGen frame = this; while(frame != null) { if(frame instanceof WSFinishStmtClassGen){ return (WSFinishStmtClassGen)frame; } frame = frame.getUpFrame(); } return null; } /** * Use the generator to generate all kinds of classes. * Need add the new inner class to its parent frame class * @throws SemanticException */ public final void genClass() throws SemanticException { genMethods(); //fast/resume/back, and async frame's move methods methodCodeProcessing(); //process the codes genClassConstructor(); if (wts.__CPP__) { wsynth.genCopyConstructor(classSynth); wsynth.genRemapMethod(classSynth, codeBlock == null); } } /** * Final code processing for fast/resume/back path */ private void methodCodeProcessing(){ CodeBlockSynth fastBodySynth = fastMSynth.getMethodBodySynth(compilerPos); CodeBlockSynth resumeBodySynth = resumeMSynth.getMethodBodySynth(compilerPos); CodeBlockSynth backBodySynth = backMSynth.getMethodBodySynth(compilerPos); // add change locals to fields fastBodySynth.addCodeProcessingJob(new AddIndirectLocalDeclareVisitor(job, this.getRefToDeclMap()).context(xct)); resumeBodySynth.addCodeProcessingJob(new AddIndirectLocalDeclareVisitor(job, this.getRefToDeclMap()).context(xct)); backBodySynth.addCodeProcessingJob(new AddIndirectLocalDeclareVisitor(job, this.getRefToDeclMap()).context(xct)); } protected abstract void genMethods() throws SemanticException; protected abstract void genClassConstructor() throws SemanticException; /** * Trigger class generation, and close the class synth * @return the generated class decl * @throws SemanticException */ public X10ClassType getClassType(){ return (X10ClassType) classSynth.getClassDef().asType(); } public Expr getThisRef(){ return synth.thisRef(getClassType(), compilerPos); } /** * It will decide the right child frame's class gen, and return the right type to caller * childSuperType could be only: asyncFrame, finishFrame, regularFrame * asyncFrame: WSAsyncClassGen * finishFrame: WSFinishStmtClassGen * regularFrame:WSRegularFrameClassGen or ForLoopClassGen or DoLoopClassGen or SwitchClassGen * @param childSuperType: could be only three: async, finish, regular * @param stmt * @param namePrefix: only useful for regularFrame type * @return * @throws SemanticException */ protected AbstractWSClassGen genChildFrame(Type childSuperType, Stmt stmt, String namePrefix) throws SemanticException{ AbstractWSClassGen childClassGen = null; if(childSuperType == xts.FinishFrame()){ //stmt is Finish childClassGen = new WSFinishStmtClassGen(this, (Finish)stmt); } else{ //first unroll to one stmt; //remove additional no use stmt stmt = WSUtil.unwrapToOneStmt(stmt); if(stmt instanceof For){ childClassGen = new WSForLoopClassGen(this, (For)stmt); } else if(stmt instanceof While || stmt instanceof Do){ childClassGen = new WSWhileDoLoopClassGen(this, (Loop)stmt); } else if(stmt instanceof Switch){ childClassGen = new WSSwitchClassGen(this, (Switch)stmt); } else if(stmt instanceof When){ childClassGen = new WSWhenFrameClassGen(this, (When)stmt); } else if(stmt instanceof Try){ childClassGen = new WSTryStmtClassGen(this, (Try)stmt); } else if(stmt instanceof Async){ //Two situations: //1) current frame is aysnc frame stmt or finish frame, we need wrap async into a block, // no matter the async is async or async at, and use the current name as prefix //2) current frame is normal stmt, just transform it directly if(xts.isSubtype(getClassType(), xts.AsyncFrame()) || xts.isSubtype(getClassType(), xts.FinishFrame())){ childClassGen = new WSRegularFrameClassGen(this, synth.toBlock(stmt), WSUtil.getBlockFrameClassName(this.getClassName())); } else{ Async async = (Async)stmt; Stmt asyncBody = WSUtil.unwrapToOneStmt(async.body()); //need check the frame is pure async or async at(p) if(asyncBody instanceof AtStmt){ //async at(p), transform it as a remote frame childClassGen = new WSRemoteMainFrameClassGen(this, (AtStmt)asyncBody); } else{ //pure async childClassGen = new WSAsyncClassGen(this, (Async)stmt); } } } else if(stmt instanceof AtStmt){ //Transform it as at remoteframe childClassGen = new WSRemoteMainFrameClassGen(this, (AtStmt)stmt); } else{ //stmt.prettyPrint(System.out); //TODO: optimization point: if from finish frame, the stmt is a loop //could unloop it and call for frame directly //assert(stmt instanceof Block); //definetly need to be block childClassGen = new WSRegularFrameClassGen(this, stmt, namePrefix); } } childClassGen.genClass(); // return childClassGen; } /** * Normal codes, no concurrent method/construct * Just replace local refs as field access */ protected TransCodes transNormalStmt(Stmt s, int pcValue, Set<Name> declaredLocals) throws SemanticException { assert(!WSUtil.isComplexCodeNode(s, wts)); TransCodes transCodes = new TransCodes(pcValue); s = (Stmt) this.replaceLocalVarRefWithFieldAccess(s, declaredLocals); //need process the return's & offer issue; TransOfferVisitor fastOV = new TransOfferVisitor(true); Stmt fStmt = (Stmt) s.visit(fastOV); TransOfferVisitor resumeOV = new TransOfferVisitor(false); Stmt rStmt = (Stmt) s.visit(resumeOV); TransReturnVisitor fastRV = new TransReturnVisitor(true); transCodes.addFast((Stmt)fStmt.visit(fastRV)); TransReturnVisitor resumeRV = new TransReturnVisitor(false); transCodes.addResume((Stmt)rStmt.visit(resumeRV)); return transCodes; } //Process offer in normal stmts class TransOfferVisitor extends ErrorHandlingVisitor{ private boolean fastPath; //fast:true or resume path:false Type reducerType; TransOfferVisitor(boolean fastPath){ super(AbstractWSClassGen.this.job, xts, xnf); this.fastPath = fastPath; WSFinishStmtClassGen finishFrameGen = getDirectFinishFrameClassGen(); if(finishFrameGen != null){ reducerType = finishFrameGen.getReducerBaseType(); } } public Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException { if(n instanceof Offer){ Offer offer = (Offer)n; if(reducerType == null){ WSUtil.debug("Cannot find Collecting-Finish type in Finish-Expr. Use offer's type", offer); reducerType = offer.expr().type(); } MethodSynth mSynth = fastPath ? fastMSynth : resumeMSynth; return wsynth.genOfferCallStmt(classSynth, mSynth, offer.expr(), reducerType); } return n; } } //Process return in normal stmts class TransReturnVisitor extends NodeVisitor{ private boolean fastPath; //fast or resume path private int noTransformDepth; //not transform "return" in Closure or anonymous class body TransReturnVisitor(boolean fastPath){ this.fastPath = fastPath; } @Override public NodeVisitor enter(Node n) { if(n instanceof Closure || n instanceof ClassBody){ noTransformDepth++; } return super.enter(n); } public Node leave(Node old, Node n, NodeVisitor v) { if(n instanceof Closure || n instanceof ClassBody){ noTransformDepth--; return n; } if(n instanceof Return && noTransformDepth == 0){ //return not in closure Return r = (Return)n; try { if(AbstractWSClassGen.this instanceof WSMethodFrameClassGen){ if(fastPath){ //directly return, no others return n; } else{ //slow path: result = expr(); return; List<Stmt> stmts = new ArrayList<Stmt>(); if (r.expr() != null && r.expr().type() != xts.Void()) { WSMethodFrameClassGen methodFrameGen = (WSMethodFrameClassGen)AbstractWSClassGen.this; Expr returnValueAssign; returnValueAssign = synth.makeFieldAssign(r.position(), getThisRef(), methodFrameGen.getReturnFieldName(), r.expr(), xct); stmts.add(xnf.Eval(r.position(), returnValueAssign)); } stmts.add(xnf.Return(r.position())); //return return xnf.StmtSeq(r.position(), stmts); } } else{ //non method frame List<Stmt> stmts = new ArrayList<Stmt>(); Name resultName = WSSynthesizer.RETURN_VALUE; Name returnFlagName = WSSynthesizer.RETURN_FLAG; Expr methodFrameRef = getFieldContainerRef(returnFlagName, xts.Boolean()); // parent.returnFlag = true; Expr returnFlagAssign = synth.makeFieldAssign(r.position(), methodFrameRef, returnFlagName, synth.booleanValueExpr(true, r.position()), xct); stmts.add(xnf.Eval(compilerPos, returnFlagAssign)); // parent.result = expr(); if (r.expr() != null && r.expr().type() != xts.Void()) { Expr returnValueAssign = synth.makeFieldAssign(r.position(), methodFrameRef, resultName, r.expr(), xct); stmts.add(xnf.Eval(compilerPos, returnValueAssign)); } stmts.add(xnf.Return(r.position())); //return return xnf.StmtSeq(r.position(), stmts); } } catch (SemanticException e) { try { e.printStackTrace(); WSUtil.err("Return transformation error:", n); } catch (SemanticException e1) { } return n; } } else{ //non return others return n; } } } /** * Change local declare with initializer e.g. "var n:Int = 10" as only local initialize "n = 10" * And create an inner class fields "n"; * Here, the assign right local access will is still local access, not replaced by field access * And then the stmt need to be processed by other transformations, * e.g. replace local access with field access. * @param ld * @return null if just a decl or stmt of the intializor */ protected Stmt transLocalDecl(LocalDecl ld){ //Create fields if (((X10LocalDef) ld.localDef()).annotationsMatching(xts.Ephemeral()).isEmpty()) { //The local is not annotated as "@Transient", and should be transformed as field Name fieldName = wsynth.createFieldFromLocal(classSynth, ld) ; fieldNames.add(fieldName); //put it into current frame's fields name lists //and check the intializor Expr localInit = ld.init(); if(localInit == null){ return null; //just a pure declare, no initialization; } //now create a tmp local ref to this localDecl Local local = xnf.Local(ld.position(), xnf.Id(ld.position(), ld.name().id())).localInstance(ld.localDef().asInstance()); Expr assign = xnf.LocalAssign(localInit.position(), local, Assign.ASSIGN, localInit).type(localInit.type()); return xnf.Eval(ld.position(), assign); } else { //the local is annotated as "@Transient", no need transformation return ld; } } /** * This one is direct call and assign call transformation * Transform a call to fast/resume/back path * This callee is a concurrent method */ protected TransCodes transCall(Call call, int prePcValue, Set<Name> declaredLocals) throws SemanticException { TransCodes transCodes = new TransCodes(prePcValue); //increase the pc value; //pc = x; try{ //check whether the frame contains pc field. For optimized finish frame and async frame, there is no pc field Stmt pcAssign = wsynth.genPCAssign(classSynth, prePcValue + 1); transCodes.addFast(pcAssign); transCodes.addResume(pcAssign); } catch(polyglot.types.NoMemberException e){ //Just ignore the pc assign statement if there is no pc field in the frame } //replace local access with field access //FIXME: async frame's local decl specific processing Call aCall = (Call) this.replaceLocalVarRefWithFieldAccess(call, declaredLocals); Stmt fastCall = wsynth.genWSCallStmt(aCall, classSynth, fastMSynth); Stmt resumeCall = wsynth.genWSCallStmt(aCall, classSynth, resumeMSynth); transCodes.addFast(fastCall); transCodes.addResume(resumeCall); transCodes.increasePC(); return transCodes; } /** * Transform aVar = foo(...) style. The foo() is a target method */ protected TransCodes transAssignCall(Eval eval, int prePcValue, Set<Name> declaredLocals) throws SemanticException { Assign aAssign = (Assign)eval.expr(); Call call = (Call) aAssign.right(); aAssign = (Assign) this.replaceLocalVarRefWithFieldAccess(aAssign, declaredLocals); // trigger trans call TransCodes callTransCodes = transCall(call, prePcValue, declaredLocals); TransCodes transCodes = new TransCodes(prePcValue); //Extract the call evaluation //If the pc_field optimization is turned on, only return 1 statement (call) //other wise, two stmts, 1st, the pc assign; 2nd, the call int codesSize = callTransCodes.getFastStmts().size(); if(codesSize == 2){ //add pc change statement transCodes.addFast(callTransCodes.getFastStmts().get(0)); transCodes.addResume(callTransCodes.getResumeStmts().get(0)); } //the call is the last stmt Call fastCall = (Call) ((Eval) callTransCodes.getFastStmts().get(codesSize - 1)).expr(); Call slowCall = (Call) ((Eval) callTransCodes.getResumeStmts().get(codesSize - 1)).expr(); transCodes.addFast(xnf.Eval(compilerPos, aAssign.right(fastCall))); transCodes.addResume(xnf.Eval(compilerPos, aAssign.right(slowCall))); transCodes.increasePC(); // back path // construct move content statements // _m_fib.t1 = cast[Frame,()=>Int](frame)(); Stmt backAssign = wsynth.genBackAssignStmt(backMSynth, slowCall.type(), aAssign); transCodes.addBack(backAssign); return transCodes; } /** * Generate cast[type1, type2](expr) * @param type1 * @param type2 * @param expr * @return cast[type1, type2](expr) * @throws SemanticException */ Expr genCastCall(Type type1, Type type2, Expr expr) throws SemanticException{ return genCastCall("cast", type1, type2, expr); } Expr genCastCall(String name, Type type1, Type type2, Expr expr) throws SemanticException{ List<TypeNode> genericTN = new ArrayList<TypeNode>(); genericTN.add(xnf.CanonicalTypeNode(compilerPos, Types.ref(type1))); genericTN.add(xnf.CanonicalTypeNode(compilerPos, Types.ref(type2))); Call aCall = synth.makeStaticCall(compilerPos, xts.Frame(), Name.make(name), genericTN, // This is for generic type, if any Collections.singletonList(expr), PlaceChecker.AddIsHereClause(type2, xct), Collections.singletonList(type1), xct); return aCall; } // /** // * For a method gen, originally, there are local variables access // * However, all these local variables are stored as method body class's fields // * We need replace local as field ref // * E.g. n ==> instanceRef.n // * @param s the statement // * @param instanceRef reference to the class's instance // * @return // */ // protected Term replaceLocalVarRefWithFieldAccess(Term s, Expr instanceRef){ // LocalAccessToFieldAccessReplacer rep = new LocalAccessToFieldAccessReplacer(instanceRef, synth, context); // s = (Term) s.visit(rep); // return s; // } /** * Replace local var access with field access. * @param s the statement * @return */ protected Term replaceLocalVarRefWithFieldAccess(Term s){ return replaceLocalVarRefWithFieldAccess(s, CollectionFactory.<Name>newHashSet()); } /** * Replace local var access with field access. * @param s the statement * @param declaredLocals: an ignore list. All these vars have been declared in the scope * @return */ protected Term replaceLocalVarRefWithFieldAccess(Term s, Set<Name> declaredLocals){ AdvLocalAccessToFieldAccessReplacer rep = new AdvLocalAccessToFieldAccessReplacer(this, synth, xct, declaredLocals); Term newS = (Term) s.visit(rep); if(rep.isReplaceError()){ System.err.println("[WS_WARNING] ReplaceLocalVarRefWithFieldAccess:"); s.prettyPrint(System.err); Exception e = new Exception(); e.printStackTrace(System.err); System.err.println(); } return newS; } /** * Check whether the current frame contains the input field's name * @param fieldName * @return */ public boolean containField(Name fieldName){ return fieldNames.contains(fieldName); } // /** // * For any method generate, need provide a reference to methodBodyInnerClass // * So that any other inner class could reference to that instance, and access fields // * // * @return // * @throws SemanticException // */ // protected Pair<LocalDecl, Local> genMethodBodyClassRefAsLocalVariable() throws SemanticException{ // //It will use innerClass as start // AbstractWSClassGen selfClass = this; // AbstractWSClassGen parentClass = selfClass.getParent(); // X10ClassDef parentDef; // X10ClassDef selfDef; // Expr selfRef = xnf.This(compilerPos); // Name upName = Name.make("up"); // while(parentClass != null){ // parentDef = parentClass.getClassDef(); // selfDef = selfClass.getClassDef(); // selfRef = selfRef.type(selfDef.asType()); // //need cast current's up to parent's reference // Expr upField = synth.makeFieldAccess(compilerPos, selfRef, upName, xct); // selfRef = genCastCall(xts.Frame(), parentDef.asType(), upField); // selfClass = parentClass; // parentClass = selfClass.getParent(); //here it is possible parent == null; // } // //now selfRef should point to the method's frame // return synth.makeLocalVarWithAnnotation(compilerPos, Flags.NONE, selfRef, null, xct); // // } /** * Change local declare with initializer e.g. "var n:Int = 10" as only local initialize "n = 10" * Here, the assign right local access will is still local access, not replaced by field access * And then the stmt need to be processed by other transformations, * e.g. replace local access with field access. * @param ld local declare * @return null if it is a pure local declare, or a local assign */ public Stmt removeLocalDeclare(LocalDecl ld){ Expr localInit = ld.init(); if(localInit == null){ return null; //just a pure declare, no initialization; } //now create a local ref to this localDecl Local local = xnf.Local(ld.position(), xnf.Id(ld.position(), ld.name().id())).localInstance(ld.localDef().asInstance()); Expr assign = xnf.LocalAssign(localInit.position(), local, Assign.ASSIGN, localInit).type(localInit.type()); return xnf.Eval(ld.position(), assign); } /** * * 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 aCall * @param methodDef * @param newArgTypes * @param newArgs * @return */ public X10Call replaceMethodCallWithWSMethodCall(X10Call aCall, X10MethodDef methodDef, List<Expr> newArgs){ //for arguments & new method instance's formal types ArrayList<Expr> args = new ArrayList<Expr>(newArgs); args.addAll(aCall.arguments()); ArrayList<Type> argTypes = new ArrayList<Type>(); for(Expr e : newArgs){ argTypes.add(e.type()); } argTypes.addAll(aCall.methodInstance().formalTypes()); //for the name Name name = methodDef.name(); //new name //new method instance with original properties MethodInstance mi = methodDef.asInstance(); mi = (MethodInstance) mi.formalTypes(argTypes); mi = mi.returnType(aCall.methodInstance().returnType()); mi = (MethodInstance) mi.container(aCall.methodInstance().container()); //build new call aCall = (X10Call) aCall.methodInstance(mi); aCall = (X10Call) aCall.name(xnf.Id(aCall.name().position(), name)); aCall = (X10Call) aCall.arguments(args); aCall.type(methodDef.returnType().get()); return aCall; } /** * Looking for an async frame's finish frame as parent frame * If it found an finish frame, the parent frame is a finish frame. * If the async has no direct finish frame, only null * Since an async frame is a finish frame look for both WSFinishStmtClassGen and WSAsyncClassGen * @param frameClass the start location to look for * @return AbstractWSClassGen from the chain of frameClass or null */ static protected AbstractWSClassGen getFinishFrameOfAsyncFrame(AbstractWSClassGen directParentFrame){ AbstractWSClassGen parentFrame = directParentFrame; while(parentFrame != null){ if(parentFrame instanceof WSFinishStmtClassGen){ return parentFrame; } parentFrame = parentFrame.getUpFrame(); } return null; //not found, return null; } //================Below codes are used to implement ILocalToFieldContainerMap =========== //Map relationship // (1..n)field --> (1)frame --> (1)frameRef // (1)frameRef will has only one local decl protected Map<Name, AbstractWSClassGen> localToFieldFrameMap; protected Map<AbstractWSClassGen, Expr> fieldFrameToRefMap; protected Map<Expr, Stmt> refToDeclMap ; /** * Return the whole local expr to local decl map * @return */ public Map<Expr, Stmt> getRefToDeclMap(){ return refToDeclMap; } //search the fields from a start classFrame, and store it into a path structure //a path structure is a arraylist //[[fieldName, classType], [fieldName, classType] , ...] protected void lookForField(Name fieldName, ReferenceContainer refContainer, AbstractWSClassGen classFrame){ if(classFrame.containField(fieldName)){ refContainer.setFound(); //current is a found one; } //now still need do full search //typical frames have only up field to point to its' parent //but async frames have up to point its finish, and k points to its continuation //look for up field first, then continuation AbstractWSClassGen parentClassFrame = classFrame.getUpFrame(); if(parentClassFrame != null){ //it's possible the parent is null, for an async has no direct finish, and remote main frame refContainer.push(UP_REF, parentClassFrame); lookForField(fieldName, refContainer, parentClassFrame); refContainer.pop(); } //async frame type could search continuation frame to if(classFrame instanceof WSAsyncClassGen){ WSAsyncClassGen asyncFrame = (WSAsyncClassGen)classFrame; parentClassFrame = asyncFrame.parentK; assert(parentClassFrame != null); refContainer.push(K_REF, parentClassFrame); lookForField(fieldName, refContainer, parentClassFrame); refContainer.pop(); } //remote frame type should search it's remote parent frame if(classFrame instanceof WSRemoteMainFrameClassGen){ WSRemoteMainFrameClassGen remoteFrame = (WSRemoteMainFrameClassGen)classFrame; parentClassFrame = remoteFrame.parentR; assert(parentClassFrame != null); refContainer.push(R_REF, parentClassFrame); lookForField(fieldName, refContainer, parentClassFrame); refContainer.pop(); } } /** * Analyze an eval, if it is a local assign, * the method will check whether the target local is an out finish scope local assign * If it is, WS code gen will look for all the upper async frames to move the local var * @param e * @throws SemanticException */ protected void localAssignEscapeProcess(Eval e) throws SemanticException{ LocalAssign localAssign = WSUtil.identifyLocalAssign(e); if(localAssign == null){ return; //it is not a local assign, just ignore it } Name localName = localAssign.local().name().id(); ReferenceContainer refContainer = new ReferenceContainer(this); lookForField(localName, refContainer, this); //start from here List<Pair<String, Type>> refStructure = refContainer.getBestRefStructure(); if(refStructure == null){ WSUtil.err("Cannot find the reference to a local variable:" + localName.toString(), e); } //now check whether this one is outfinish scope ClassType lastFinishFrameType = null; for(Pair<String, Type> p : refStructure){ ClassType cType = (ClassType) p.snd(); if(cType.superClass() == xts.FinishFrame()){ //the local is out of the finish scope because finish frame doesn't contain locals lastFinishFrameType = cType; //not break, and find out the last finish } } if(lastFinishFrameType == null){ //WSUtil.debug("Find out-finish scope assign. But it is not in an async frame, and no move needed.", localAssign); } else{ //an out finish assign, need find all async frames between the current frame and the finish frame //and add the move statements List<WSAsyncClassGen> asyncFrames = new ArrayList<WSAsyncClassGen>(); AbstractWSClassGen curFrame = this; while(curFrame != null && curFrame.getClassType() != lastFinishFrameType){ if(curFrame instanceof WSAsyncClassGen){ ((WSAsyncClassGen)curFrame).addOutFinishScopeLocals(localAssign); asyncFrames.add((WSAsyncClassGen) curFrame); curFrame = ((WSAsyncClassGen)curFrame).parentK; } else{ curFrame = curFrame.up; } } //below just for debug info if(asyncFrames.size() > 0){ StringBuffer sb = new StringBuffer(); for(WSAsyncClassGen aFrame : asyncFrames){ sb.append(aFrame.getClassName()).append(", "); } WSUtil.debug("Find out-finish assign expr. The value will be moved into " + sb.toString(), localAssign); } } } /** * A field (fieldName) may be in any upper frame of current frame * Search a field from a start location (startRef, startContainer), * and generate a reference to the frame that contains the field * * This method is only used in move() of async frame, where the startRef is a FinishFrame * @param fieldName * @param startContainer * @param startRef, should be a finish frame type * @return * @throws SemanticException */ public Expr getFieldContainerRef(Name fieldName, AbstractWSClassGen startFrame, Expr startRef) throws SemanticException{ ReferenceContainer refContainer = new ReferenceContainer(startFrame); lookForField(fieldName, refContainer, startFrame); //start from here List<Pair<String, Type>> refStructure = refContainer.getBestRefStructure(); if(refStructure != null){ //first generate the cast [FinishFrame, startFrame.type]startRef Type castedType = startFrame.getClassDef().asType(); Expr castedRef = genCastCall(xts.FinishFrame(), castedType, startRef); //cast[Frame, _finish](up) //now gothourgh the refStructure AbstractWSClassGen classFrame = startFrame; for(Pair<String, Type> p : refStructure){ assert(p.fst().equals(UP_REF));//the out-finish assign's ref should only contains "up" classFrame = classFrame.getUpFrame(); Expr upField = synth.makeFieldAccess(compilerPos, castedRef, Name.make(p.fst()), xct); castedRef = genCastCall(xts.Frame(), p.snd(), upField); } return castedRef; } WSUtil.err("Cannot find reference to local variable name: "+ fieldName.toString(), null); return null; } /* Gen field container ref. All ref is a local var. * If it is new, create the local * @see x10.compiler.ws.util.ILocalToFieldContainerMap#getFieldContainerRef(polyglot.types.Name) */ public Expr getFieldContainerRef(Name fieldName, Type type) throws SemanticException{ if(localToFieldFrameMap == null){ //first time initialize localToFieldFrameMap = CollectionFactory.newHashMap(); fieldFrameToRefMap = CollectionFactory.newHashMap(); refToDeclMap = CollectionFactory.newHashMap(); } AbstractWSClassGen frame = localToFieldFrameMap.get(fieldName); //use cache if(frame != null){ //found it in cache, no need build the reference again return fieldFrameToRefMap.get(frame); } else { ReferenceContainer refContainer = new ReferenceContainer(this); lookForField(fieldName, refContainer, this); //start from here //here the refStructure should be both the shortest, but also has no "k/r" //So we use some other logical to process the refStructure, remove "k/r" and create local in async or remote frames List<Pair<String, Type>> refStructure = refContainer.getBestRefStructure(); refStructure = processRefStructureForAsyncAndRemoteFrame(refStructure, fieldName, type); Expr ref = xnf.This(compilerPos).type(this.getClassDef().asType()); if(refStructure != null){ //found the ref if(refStructure.size() == 0){ //found in current frame localToFieldFrameMap.put(fieldName, this); fieldFrameToRefMap.put(this, ref); return ref; //just this } else{ AbstractWSClassGen classFrame = this; for(Pair<String, Type> p : refStructure){ assert(p.fst().equals(UP_REF)); //after processing async & remote frame. the only ref is "up" classFrame = classFrame.getUpFrame(); Expr upField = synth.makeFieldAccess(compilerPos, ref, Name.make(p.fst()), xct); ref = genCastCall(xts.Frame(), p.snd(), upField); } localToFieldFrameMap.put(fieldName, classFrame); //now have found the classFrame and the ref if(fieldFrameToRefMap.containsKey(classFrame)){ return fieldFrameToRefMap.get(classFrame); } else{ //need create the new local Pair<LocalDecl, Local> tmpPair = synth.makeLocalVarWithAnnotation(compilerPos, Flags.FINAL, ref, null, xct); ref = tmpPair.snd(); refToDeclMap.put(ref, tmpPair.fst()); fieldFrameToRefMap.put(classFrame, ref); return ref; } } } } WSUtil.err("Cannot find reference to local variable name:" + fieldName.toString(), null); return null; } //Async frame's "up" field point to the finish frame //And the local variables between finish frame and itself should be copied into the async frame as formals //Remote frame has no up, all ref structure contains up should be copied into the remote frame as formals //The return structure will only contains "up". No "k" or "r" private List<Pair<String, Type>> processRefStructureForAsyncAndRemoteFrame(List<Pair<String, Type>> refStructure, Name name, Type type){ if(refStructure == null || refStructure.size() == 0){ return refStructure; // no need process } ArrayList<Pair<String, Type>> result = new ArrayList<Pair<String, Type>>(); //identify the field container frame according to the refstructure boolean foundKRInRef = false; //if found, no need additional search AbstractWSClassGen classFrame = this; for(Pair<String, Type> pair : refStructure){ if(pair.fst().equals(UP_REF)){ classFrame = classFrame.up; } else{ //"from parentK" or from "parentR" foundKRInRef = true; if(pair.fst().equals(K_REF)){ WSAsyncClassGen asyncFrame = (WSAsyncClassGen)classFrame; classFrame = asyncFrame.parentK; //and we need create the fields in the async frame asyncFrame.addFormal(name, type); } else{ //"r" WSRemoteMainFrameClassGen remoteFrame = (WSRemoteMainFrameClassGen)classFrame; classFrame = remoteFrame.parentR; //and we need create the fields in the async frame remoteFrame.addFormal(name, type); } //cannot break yet, still do more search } if(!foundKRInRef){ //as soon as found "k/r", we stop copy the ref to result. result.add(pair); } } return result; } //=============================== This Section is used to control optimizaiton config //Sub class gen should override the method to control the opimization parameter. public boolean isFastPathInline(ClassType frameType){ return true; //default true; } }