/*
* 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.HashSet;
import java.util.List;
import java.util.Set;
import polyglot.ast.Call;
import polyglot.ast.ConstructorDecl;
import polyglot.ast.Expr;
import polyglot.ast.Formal;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.frontend.Job;
import polyglot.main.Reporter;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LocalDef;
import polyglot.types.Name;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.ExtensionInfo;
import x10.util.CollectionFactory;
import x10.util.HierarchyUtils;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.AtEach;
import x10.ast.Closure;
import x10.ast.Here;
import x10.ast.Offer;
import x10.ast.PlacedClosure;
import x10.ast.RemoteActivityInvocation;
import x10.ast.X10Call;
import x10.ast.X10ClassDecl;
import x10.ast.X10MethodDecl;
import x10.compiler.ws.codegen.WSMethodFrameClassGen;
import x10.compiler.ws.util.ClosureDefReinstantiator;
import x10.compiler.ws.util.WSUtil;
import x10.compiler.ws.util.WSTransformationContent;
import x10.compiler.ws.WSTransformState.MethodType;
import x10.types.MethodInstance;
import x10.types.X10MethodDef;
import x10.util.Synthesizer;
import x10.visit.Desugarer;
import x10.visit.X10InnerClassRemover;
/**
* ContextVisitor that generates code for work stealing.
* @author Haibo
* @author Haichuan
* @author tardieu
*
* In work-stealing code transformation, all methods with finish-async statements,
* and all methods that invoke directly or indirectly the above methods,
* need rewriting.
*
* So it needs to build a static call-graph, and have a DFS on the reverse call-graph
* edges and mark all methods reachable.
*
* In the first step, we only mark all methods that contain finish-async as the target.
*/
public class WSCodeGenerator extends ContextVisitor {
public static final boolean debug = true;
public static WSTransformState wts; //Set by WSCodePreprocessor
private final Set<X10MethodDecl> genMethodDecls;
private final Set<X10ClassDecl> genClassDecls;
/**
* @param job
* @param ts
* @param nf
*/
public WSCodeGenerator(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf);
genMethodDecls = CollectionFactory.newHashSet();
genClassDecls = CollectionFactory.newHashSet();
}
/**
* WS codegen
* MethodDecl --> if it is a target method, transform it into an inner class
* X10ClassDecl --> add generated inner classes and methods if any
*/
protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
// transform call site
if(n instanceof Call){
Call call = (Call)n;
switch(wts.getCallSiteType(call)){
case MATCHED_CALL: //change the target
//two steps, create a new method def, and change the call
MethodInstance mi = WSUtil.createWSMethodInstance(call.methodInstance(), ts);
List<Expr> newArgs = new ArrayList<Expr>();
newArgs.add(nf.NullLit(Position.COMPILER_GENERATED).type(ts.Worker()));
newArgs.add(nf.NullLit(Position.COMPILER_GENERATED).type(ts.Frame()));
newArgs.add(nf.NullLit(Position.COMPILER_GENERATED).type(ts.FinishFrame()));
return WSUtil.replaceMethodCallWithWSMethodCall(nf, (X10Call) call, mi, newArgs);
case CONCURRENT_CALL: //do nothing, leave the transformation in method decl transformation
case NORMAL:
default:
}
}
// transform methods
if(n instanceof X10MethodDecl) {
X10MethodDecl mDecl = (X10MethodDecl)n;
X10MethodDef mDef = mDecl.methodDef();
switch(wts.getMethodType(mDecl)){
case BODYDEF_TRANSFORMATION:
//traditional transform
WSUtil.info("Start transforming target method: " + mDef.name());
Job job = ((ClassType) mDef.container().get()).def().job();
WSMethodFrameClassGen mFrame = new WSMethodFrameClassGen(job, (NodeFactory) nf, (Context) context,
mDef, mDecl, wts, HierarchyUtils.isMainMethod(mDef, context));
try{
n = mFrame.transform();
}
catch(SemanticException e){
e.printStackTrace();
WSUtil.err(e.getMessage(), n);
}
genClassDecls.addAll(mFrame.close());
genMethodDecls.add(mFrame.getWraperMethod());
WSUtil.info(mDef.name() + " frame Structure"
+ System.getProperty("line.separator")
+ mFrame.getFrameStructureDesc(4));
break;
case DEFONLY_TRANSFORMATION:
//only change the method's interface
n = changeMethodDefOnly(mDecl);
break;
case NORMAL:
default:
}
return n;
}
// transform classes
if (n instanceof X10ClassDecl) {
X10ClassDecl cDecl = (X10ClassDecl)n;
ClassDef cDef = cDecl.classDef();
List<X10ClassDecl> classes = getClassDecls(cDef);
if (classes.isEmpty()) {
return n; //no change
}
else{
WSUtil.info("Add new methods and nested classes to class: " + n);
List<X10MethodDecl> methods = getMethodDecls(cDef);
cDecl = Synthesizer.addNestedClasses(cDecl, classes);
cDecl = Synthesizer.addMethods(cDecl, methods);
ClosureDefReinstantiator closureProcessor = new ClosureDefReinstantiator(job, ts, nf);
closureProcessor.begin();
closureProcessor = (ClosureDefReinstantiator) closureProcessor.context(context());
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(closureProcessor);
cDecl = (X10ClassDecl) cDecl.visit(desugarer);
cDecl = (X10ClassDecl) cDecl.visit(innerclassRemover);
return cDecl;
}
}
return n;
}
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;
}
protected List<X10ClassDecl> getClassDecls(ClassDef cDef) throws SemanticException {
ArrayList<X10ClassDecl> cDecls = new ArrayList<X10ClassDecl>();
for(X10ClassDecl cDecl : genClassDecls){
ClassDef containerDef = cDecl.classDef().outer().get();
if(containerDef == cDef){
cDecls.add(cDecl);
}
}
return cDecls;
}
/**
* Transform a method, add worker/upframe/parent frame/ as formals.
* But no any other changes. Just for those methods that need match the WS interface change
* @param methodDecl
* @return
*/
protected X10MethodDecl changeMethodDefOnly(X10MethodDecl methodDecl){
//need change the def's formals definition and decl's formals
//three new formals, worker/upframe/finishframe
Position pos = Position.COMPILER_GENERATED;
Name workerName = Name.make("worker");
LocalDef workerLDef = ts.localDef(pos, Flags.FINAL, Types.ref(ts.Worker()), workerName);
Formal workerF = nf.Formal(pos,
nf.FlagsNode(pos, Flags.FINAL),
nf.CanonicalTypeNode(pos, ts.Worker()),
nf.Id(pos, workerName)).localDef(workerLDef);
Name upName = Name.make("up");
LocalDef upLDef = ts.localDef(pos, Flags.FINAL, Types.ref(ts.Frame()), upName);
Formal upF = nf.Formal(pos,
nf.FlagsNode(pos, Flags.FINAL),
nf.CanonicalTypeNode(pos, ts.Frame()),
nf.Id(pos, upName)).localDef(upLDef);
Name ffName = Name.make("ff");
LocalDef ffLDef = ts.localDef(pos, Flags.FINAL, Types.ref(ts.FinishFrame()), ffName);
Formal ffF = nf.Formal(pos,
nf.FlagsNode(pos, Flags.FINAL),
nf.CanonicalTypeNode(pos, ts.FinishFrame()),
nf.Id(pos, ffName)).localDef(ffLDef);
//now change the def
X10MethodDef methodDef = methodDecl.methodDef();
ArrayList<LocalDef> formalNames = new ArrayList<LocalDef>();
ArrayList<Ref<? extends Type>> formalRefs =
new ArrayList<Ref<? extends Type>>();
ArrayList<Formal> formals = new ArrayList<Formal>();
formalNames.add(workerLDef);
formalRefs.add(workerF.type().typeRef());
formals.add(workerF);
formalNames.add(upLDef);
formalRefs.add(upF.type().typeRef());
formals.add(upF);
formalNames.add(ffLDef);
formalRefs.add(ffF.type().typeRef());
formals.add(ffF);
formalNames.addAll(methodDef.formalNames());
formalRefs.addAll(methodDef.formalTypes());
formals.addAll(methodDecl.formals());
methodDef.setFormalNames(formalNames);
methodDef.setFormalTypes(formalRefs);
methodDecl = methodDecl.formals(formals);
//finally change the name;
Name name = Name.make(WSUtil.getMethodFastPathName(methodDef));
methodDef.setName(name);
methodDecl = methodDecl.name(nf.Id(pos, name));
return methodDecl;
}
}