/*
* 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.HashSet;
import java.util.List;
import java.util.Set;
import polyglot.ast.Block;
import polyglot.ast.Call;
import polyglot.ast.Catch;
import polyglot.ast.Eval;
import polyglot.ast.Expr;
import polyglot.ast.FieldAssign;
import polyglot.ast.Formal;
import polyglot.ast.LocalAssign;
import polyglot.ast.LocalDecl;
import polyglot.ast.Stmt;
import polyglot.ast.Try;
import polyglot.ast.TypeNode;
import polyglot.types.ClassDef;
import polyglot.types.Flags;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.util.Pair;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import x10.ast.Async;
import x10.ast.Offer;
import x10.ast.StmtSeq;
import x10.compiler.ws.WSCodeGenerator;
import x10.compiler.ws.util.AddIndirectLocalDeclareVisitor;
import x10.compiler.ws.util.CodePatternDetector;
import x10.compiler.ws.util.TransCodes;
import x10.compiler.ws.util.WSUtil;
import x10.compiler.ws.util.CodePatternDetector.Pattern;
import x10.types.X10ClassType;
import x10.util.synthesizer.ClassSynth;
import x10.util.synthesizer.CodeBlockSynth;
import x10.util.synthesizer.ConstructorSynth;
import x10.util.synthesizer.InstanceCallSynth;
import x10.util.synthesizer.MethodSynth;
import x10.util.synthesizer.SuperCallSynth;
import x10.util.synthesizer.SwitchSynth;
/**
* @author Haichuan
*
* Generate the async frame class
*
* Process
* pre-process the stmt, if it is a block, transform it into List<Stmt>
* If the stmts contains concurrent construct,
* all the statements will be transformed into a regular frame.
*
* If the stmts doesn't contain concurrent construct, transform all statements one by one
*
*
*
*/
public class WSAsyncClassGen extends AbstractWSClassGen {
protected final AbstractWSClassGen parentK; //used to store its parent continuation
protected final List<Pair<Name,Type>> formals; //the formals are not real formals, but local var copied from parent frames;
protected final List<LocalAssign> outFinishScopeLocalAssign;//all the locals in this scope need be processed in move
protected boolean inFrameTransform; //record whether the code block can be transformed in the async frame
public WSAsyncClassGen(AbstractWSClassGen parent, Async a) {
//Note in building the tree, we use parentFinish as async frame's up frame
super(parent, getFinishFrameOfAsyncFrame(parent),
WSUtil.getAsyncStmtClassName(parent.getClassName()),
parent.xts.AsyncFrame(), a.body());
inFrameTransform = canInFrameTransform(codeBlock);
if(!wts.OPT_PC_FIELD){
wsynth.createPCField(classSynth);
}
parentK = parent; //record parent continuation
formals = new ArrayList<Pair<Name, Type>>();
outFinishScopeLocalAssign = new ArrayList<LocalAssign>();
}
/**
* Will generate fast, back and move method
*
* @throws SemanticException
*/
@Override
protected void genMethods() throws SemanticException {
CodeBlockSynth fastBodySynth;
CodeBlockSynth resumeBodySynth;
CodeBlockSynth backBodySynth = backMSynth.getMethodBodySynth(compilerPos);
if(wts.DISABLE_EXCEPTION_HANDLE){
fastBodySynth = fastMSynth.getMethodBodySynth(compilerPos);
resumeBodySynth = resumeMSynth.getMethodBodySynth(compilerPos);
}
else{
fastBodySynth = new CodeBlockSynth(xnf, xct, compilerPos);
resumeBodySynth = new CodeBlockSynth(xnf, xct, compilerPos);
}
//the pc and switch table are only set value if we turn off pc field optimizatoin
Expr pcRef = null;
SwitchSynth resumeSwitchSynth = null;
SwitchSynth backSwitchSynth = null;
if(!wts.OPT_PC_FIELD){
pcRef = wsynth.genPCRef(classSynth);
resumeSwitchSynth = resumeBodySynth.createSwitchStmt(compilerPos, pcRef);
backSwitchSynth = backBodySynth.createSwitchStmt(compilerPos, pcRef);
}
//Used for in frame transformation: record local variables that are not transformed as fields
Set<Name> localDeclaredVars = CollectionFactory.newHashSet(); //all locals with these names will not be replaced
if(!inFrameTransform){
//we create a new frame to transform the async's body
AbstractWSClassGen childFrameGen = genChildFrame(xts.RegularFrame(), codeBlock, WSUtil.getBlockFrameClassName(getClassName()));
List<Stmt> callCodes = wsynth.genInvocateFrameStmts(1, classSynth, fastMSynth, childFrameGen);
fastBodySynth.addStmts(callCodes);
//no codes in resume path here
}
else{
//transform code one by one in the async frame. No more deeper frames will be generated
ArrayList<Stmt> bodyStmts = new ArrayList<Stmt>(codeBlock.statements());
int pcValue = 0; //The current pc value. Will increase every time an inner class is created
int prePcValue = 0; //the last time's pc value. If pc value is changed, need generate a switch case
while (bodyStmts.size() > 0) {
Stmt s = bodyStmts.remove(0); //always remove the first one
TransCodes codes;
// need process local declare first
if (s instanceof LocalDecl) { // Pre-processing
s = transLocalDecl((LocalDecl) s);
if(s == null) continue;
if (s instanceof LocalDecl)
localDeclaredVars.add(((LocalDecl) s).name().id());
}
// need analyze out-finish scope local assign
if(s instanceof Eval){
localAssignEscapeProcess((Eval)s);
}
//use code pattern detector to detect
CodePatternDetector.Pattern pattern = CodePatternDetector.detectAndTransform(s, wts);
switch(pattern){
case Simple:
codes = transNormalStmt(s, prePcValue, localDeclaredVars);
break;
case StmtSeq:
//Unwrapp the stmts, and add them back
bodyStmts.addAll(0, ((StmtSeq)s).statements()); //put them into target
continue;
case Call:
codes = transCall((Call)((Eval)s).expr(), prePcValue, localDeclaredVars);
break;
case AssignCall:
codes = transAssignCall(((Eval)s), prePcValue, localDeclaredVars);
break;
default:
WSUtil.err("X10 WorkStealing cannot support:", s);
continue;
}
fastBodySynth.addStmts(codes.getFastStmts());
pcValue = codes.pcValue();
if(!wts.OPT_PC_FIELD){
resumeSwitchSynth.insertStatementsInCondition(prePcValue, codes.getResumeStmts());
if(codes.getBackStmts().size() > 0){ //only assign call has back
backSwitchSynth.insertStatementsInCondition(pcValue, codes.getBackStmts());
backSwitchSynth.insertStatementInCondition(pcValue, xnf.Break(compilerPos));
}
}
else{
if(prePcValue == 0 && codes.getResumePostStmts().size() > 0){
resumeBodySynth.addStmts(codes.getResumePostStmts());
}
if(prePcValue == 1){
resumeBodySynth.addStmts(codes.getResumeStmts());
}
//because there is only one possible assign call, its safe to add the statement to back path
if(codes.getBackStmts().size() > 0){ //only assign call has back
backBodySynth.addStmts(codes.getBackStmts());
}
}
prePcValue = pcValue;
}//while end
} //in frame transform end
//Put the codes into a try block
if(!wts.DISABLE_EXCEPTION_HANDLE){
Block fastBlock = (Block) fastBodySynth.genStmt();
Block resumeBlock = (Block) resumeBodySynth.genStmt();
fastMSynth.getMethodBodySynth(compilerPos).addStmt(wsynth.genExceptionHandler(fastBlock.statements(), classSynth));
if(resumeBlock.statements().size() > 0){
resumeMSynth.getMethodBodySynth(compilerPos).addStmt(wsynth.genExceptionHandler(resumeBlock.statements(), classSynth));
}
}
//After fast body, there should be a poll
fastMSynth.getMethodBodySynth(compilerPos).addStmt(wsynth.genPollStmt(classSynth, fastMSynth));
genMoveMethod(localDeclaredVars);
}
private void genMoveMethod(Set<Name> localDeclaredVar) throws SemanticException {
//Move method - Used to move data for all out scope assign statements
MethodSynth moveMSynth = classSynth.createMethod(classSynth.pos(), WSSynthesizer.MOVE.toString());
moveMSynth.setFlag(Flags.PUBLIC);
Expr moveFfRef = moveMSynth.addFormal(compilerPos, Flags.FINAL, xts.FinishFrame(), WSSynthesizer.FF.toString());
CodeBlockSynth moveBodySynth = moveMSynth.getMethodBodySynth(compilerPos);
//How to move
// Get the assign expression, the left will be the right, and
// The new left will be finish frame's parent's field, the name of the field should be the same
for(LocalAssign assign : outFinishScopeLocalAssign){
//left redirected ff frame's parent
//right this current frame's parent
Name name = assign.local().name().id();
//then process the original local, and infact the reference is built
FieldAssign fAssign = (FieldAssign) this.replaceLocalVarRefWithFieldAccess(assign, localDeclaredVar);
Expr leftContainerRef = getFieldContainerRef(name, getUpFrame(), moveFfRef); //search the field from async's parent(finish)
Expr moveAssign = synth.makeFieldToFieldAssign(compilerPos, leftContainerRef, name, fAssign.target(), name, xct);
moveBodySynth.addStmt(xnf.Eval(compilerPos, moveAssign));
}
//final processing
moveBodySynth.addCodeProcessingJob(new AddIndirectLocalDeclareVisitor(job, this.getRefToDeclMap()).context(xct));
}
protected void genClassConstructor() throws SemanticException {
ConstructorSynth conSynth = wsynth.genClassConstructorType1Base(classSynth);
CodeBlockSynth codeBlockSynth = conSynth.createConstructorBody(compilerPos);
//process all the formals. Assign fields with formals
Expr thisRef = getThisRef();
for(Pair<Name, Type> formal: formals){
Name formalName = formal.fst();
Type formalType = formal.snd();
Expr fRef = conSynth.addFormal(compilerPos, Flags.FINAL, formalType, formalName);
//make a field access
Stmt s = xnf.Eval(compilerPos,
synth.makeFieldAssign(compilerPos, thisRef, formalName, fRef, xct));
codeBlockSynth.addStmt(s);
}
}
/**
* This method will be called by localvartofieldaccess replacer
* If a local var is found in async frame, but defined above async and within finish
* it should be copied into the async frame as a formal
*
* For a local var, it should be called only once. This is enforced by the cache in localvartofieldaccess
* @param name
* @param type
*/
protected void addFormal(Name name, Type type){
formals.add(new Pair<Name, Type>(name, type));
//now create a field
classSynth.createField(compilerPos, name.toString(), type);;
fieldNames.add(name);
}
/**
* For all local assigns, need detect whether this local assign is to
* a local var that is not in the finish scope.
* If in this case, the local var need to be moved in the async frame
*
* @param localAssign
*/
public void addOutFinishScopeLocals(LocalAssign localAssign){
outFinishScopeLocalAssign.add(localAssign);
}
/**
* Detect whether the stmts in the block can be transformed in just the async frame.
* If the stmts satisfy the following conditions, they could be transformed in the async frame
* 1) all stmts are simple stmts,
* 2) no concurrent construct, such as finish/async/when
* 3) only contains one concurrent method call, and the call is not in a control flow, such as if, loop, block
*
* Other wise, WS code gen will create a new regular frame to transform the stmts.
*
* If the frame can be transformed in async frame, we could use pc field optimization tech.
*
* @param block the code block to be analyzed
* @return
*/
protected boolean canInFrameTransform(Block block){
boolean containsConcurrent = WSUtil.containsConcurrentConstruct(block);
int concurrentCallNum = WSUtil.calcConcurrentCallNums(block, wts);
if(containsConcurrent || concurrentCallNum > 1){
return false;
}
if(concurrentCallNum == 0){
return true; //no concurrent call and no concurrent construts;
}
//now it contains only one concurrent call, we must make sure it is not in a loop body or other control flows.
for(Stmt s : block.statements()){
CodePatternDetector.Pattern pattern = CodePatternDetector.detectAndTransform(s, wts);
if(CodePatternDetector.isControlFlowPattern(pattern)){
return false;
}
}
return true; //the only concurrent call is not in a control flow.
}
}