blob: 16255541b9d6ef8aa439798ccf59d0469c5d9711 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ptp.pldt.openmp.analysis.ompcfg.factory;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
import org.eclipse.cdt.core.dom.ast.IASTCaseStatement;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTContinueStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDoStatement;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
import org.eclipse.ptp.pldt.openmp.analysis.PAST.PASTOMPPragma;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.OMPBasicBlock;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.OMPCFG;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.OMPCFGNode;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.OMPExpressionBlock;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.OMPPragmaNode;
import org.eclipse.ptp.pldt.openmp.analysis.ompcfg.factory.OMPCFGResult.Chain;
/**
* Builds a section of the control flow graph (like CFGMaker)
* @author pazel
*
*/
public class OMPCFGMaker extends ASTVisitor
{
protected IASTNode tNode_ = null;
// This is the return structure
protected OMPCFGResult result_ = new OMPCFGResult();
// These get passed down to each level -
// pragmaRegionMap_ never changes
// pragmaContext increases/decreases on encountering pragma statements
protected static Hashtable pragmaRegionMap_ = null; // maps IASTNode to pragma, if node is a pragma region
protected static Hashtable pragmaLocationMap_ = null; // maps IASTnode to list of pragmas that may immed. succeed it
protected static Stack pragmaContext_ = null; // stack of pragma - indicating depth of OMP control
// Again the perspective is that we are building a list of a mix of
// basic blocks and pragma nodes - nested processing done recursively
// This two items manage the progress on the current chain
protected OMPCFGNode firstNode_ = null;
protected OMPCFGNode currentNode_ = null;
protected static boolean traceOn_ = false;
/**
* constructCFG - static method to build the cfg
* @param pragma - PASTOMPPragma (focus pragma for this graph
* @param statement - IASTStatement (region for pragma)
* @param pragmaRegionMap - Hashtable (mapping region to pragma)
* @param pragmaLocationMap - Hashtable (mapping IASTNode to list of immed. succ. pragmas)
* @return OMPCFG
*/
public static OMPCFG constructCFG(PASTOMPPragma pragma, IASTStatement statement, Hashtable pragmaRegionMap, Hashtable pragmaLocationMap)
{
pragmaRegionMap_ = pragmaRegionMap;
pragmaLocationMap_ = pragmaLocationMap;
pragmaContext_ = new Stack();
OMPCFGMaker maker = new OMPCFGMaker(statement);
maker.buildCFG();
OMPCFGResult result = maker.getResult();
result.resolve();
OMPCFG cfg = new OMPCFG(pragma,
statement,
result.getFirstChain(),
result.getUnconnectedChains(),
result.getUnresolvedControlFlow(),
result.getLabelMap());
pragmaRegionMap_ = null;
pragmaLocationMap_ = null;
pragmaContext_ = null;
return cfg;
}
/**
* OMOPCFGMaker - constructor
* @param tNode - IASTNode
*/
protected OMPCFGMaker(IASTStatement tNode)
{
tNode_ = tNode;
shouldVisitStatements = true;
shouldVisitDeclarations = true;
}
/**
* OMPCFGMaker - constructor
* @param tNode - IASTNode
*/
protected OMPCFGMaker(IASTExpression tNode)
{
tNode_ = tNode;
shouldVisitExpressions = true;
}
/**
* buildCFG - method to initiate building tree using tree walker
*
*/
public void buildCFG()
{
if (tNode_==null) // this can occur, e.g. blank else part of if
firstNode_=currentNode_=new OMPBasicBlock(); // with nothing in it
else
tNode_.accept(this);
// put the last chain on the list
if (currentNode_!=null) {
result_.addChain(firstNode_, currentNode_, OMPCFGResult.TermForward, null);
currentNode_ = null;
firstNode_ = null;
}
}
public OMPCFGResult getResult() { return result_; }
/**
* visit - for an expression block
* @param expression - IASTExpression
*/
public int visit(IASTExpression expression)
{
// simple for now - may need to drill down later
OMPExpressionBlock b = new OMPExpressionBlock();
b.setPragmaContext(getLastPragma());
b.addExpression(expression);
firstNode_=currentNode_=b;
return PROCESS_CONTINUE;
}
public int visit(IASTDeclaration declaration)
{
return PROCESS_CONTINUE;
}
public int visit(IASTStatement statement)
{
// first, check if statement is a region for a pragma
int checkValue = pragmaRegionCheck(statement);
if (checkValue!=-1) return checkValue; // -1 means carry foward in this code
if (statement instanceof IASTIfStatement) {
processIfStatement((IASTIfStatement)statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_SKIP;
}
else if (statement instanceof IASTSwitchStatement) {
processSwitchStatement((IASTSwitchStatement)statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_SKIP;
}
else if (statement instanceof IASTCaseStatement) {
processCaseStatement((IASTCaseStatement)statement);
return PROCESS_CONTINUE;
}
else if (statement instanceof IASTForStatement) {
processForStatement((IASTForStatement)statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_SKIP;
}
else if (statement instanceof IASTWhileStatement) {
processWhileStatement((IASTWhileStatement)statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_SKIP;
}
else if (statement instanceof IASTDoStatement) {
processDoStatement((IASTDoStatement)statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_SKIP;
}
else if (statement instanceof IASTCompoundStatement) {
pragmaRegionlessCheck(statement, PASTOMPPragma.ChildProximity);
return PROCESS_CONTINUE;
}
else if (statement instanceof IASTBreakStatement) {
addToBasicBlockAndChain(statement, OMPCFGResult.TermBreak);
return PROCESS_CONTINUE;
}
else if (statement instanceof IASTContinueStatement) {
addToBasicBlockAndChain(statement, OMPCFGResult.TermContinue);
return PROCESS_CONTINUE;
}
else if (statement instanceof IASTReturnStatement) {
addToBasicBlockAndChain(statement, OMPCFGResult.TermReturn);
return PROCESS_CONTINUE;
}
// just add to the current basic block if there is one
addToBasicBlock(statement);
pragmaRegionlessCheck(statement, PASTOMPPragma.NeighborProximity);
return PROCESS_CONTINUE;
}
/**
* pragmaRegionCheck - if stmt is region for a preceding pragma, add pragma
* @param statement - IASTStatement
* @return int (-1 means nothing done)
*/
protected int pragmaRegionCheck(IASTStatement statement)
{
PASTOMPPragma pragma = (PASTOMPPragma)pragmaRegionMap_.get(statement);
// The following avoids a nasty recursion loop, resulting from
// the recursion we invest on the following lines, i.e.
// we keep building makers for the same statement
if (pragmaContext_.size()!=0 && ((OMPPragmaNode)pragmaContext_.peek()).getPragma()==pragma)
return -1;
if (pragma!=null) {
OMPCFGNode pNode = new OMPPragmaNode(pragma, getLastPragma());
pNode.setPragmaContext(getLastPragma());
addToChain(pNode);
// TODO: Analyse the context here to see if legal OMP pragma makes sense (ret -1)
pragmaContext_.push(pNode); // bump the context
// Do a 1 level recursion to process this statement - so we can pop this context
OMPCFGMaker maker = new OMPCFGMaker(statement);
maker.buildCFG();
pragmaContext_.pop();
result_.merge(maker.getResult());
// in the case of null compound statements, there is nothing here
if (maker.getResult().getFirstChain()!=null) {
pNode.connectTo(maker.getResult().getFirstChain().getHeadNode());
currentNode_ = maker.getResult().getFirstChain().getTailNode();
}
// For certain pragmas, we will append an implicit barrier
int type = pragma.getOMPType();
if ((type==PASTOMPPragma.OmpParallel || type==PASTOMPPragma.OmpParallelFor ||
type==PASTOMPPragma.OmpParallelSections || type==PASTOMPPragma.OmpSingle) &&
pragma.getNoWait()==false) {
addToChain(new OMPPragmaNode(), true); // constructor set implicit barrier
}
return PROCESS_SKIP;
}
return -1;
}
/**
* pragmaRegionlessCheck - add things like barrier, etc. as a separate node
* @param statement - IASTStatement (preceeding the pragma)
* @param proximity - int (see PASTOMPPragma constants, child, neighbor)
*/
protected void pragmaRegionlessCheck(IASTStatement statement, int proximity)
{
LinkedList l = (LinkedList)pragmaLocationMap_.get(statement);
if (l==null)
return;
for(Iterator i=l.iterator(); i.hasNext();) {
PASTOMPPragma oPragma = (PASTOMPPragma)i.next();
if (oPragma.getProximity()==proximity && oPragma.getRegion()==null) {
OMPCFGNode pNode = new OMPPragmaNode(oPragma, getLastPragma());
pNode.setPragmaContext(getLastPragma());
addToChain(pNode);
// no pop nor push needs to be done here
}
}
}
/**
* processIfStatement - process the the then and else into a unified result
* @param ifs - IASATIfStatement
*/
private void processIfStatement(IASTIfStatement ifs)
{
OMPCFGMaker ift = new OMPCFGMaker(ifs.getThenClause());
OMPCFGMaker ife = new OMPCFGMaker(ifs.getElseClause());
ift.buildCFG();
//System.out.println("OMPCFGMaker-----------------------------Else");
ife.buildCFG();
//System.out.println("OMPCFGMaker-----------------------------Endif");
Chain thenChain = ift.getResult().getFirstChain();
Chain elseChain = ife.getResult().getFirstChain();
// The OMP CFG elides a lot of conditionals to save expense in analysis
// See if there is any reason to retain the structure, if not just add
// the statement to the current basic block
if (!getRelevance(ift.getResult()) && !getRelevance(ife.getResult())) {
addToBasicBlock(ifs);
return;
}
addBasicBlockBranch(ifs.getConditionExpression(), OMPBasicBlock.IfBranch);
if (traceOn_)
System.out.println("CFGFactory: Create IF BranchNode");
currentNode_.connectTo(thenChain.getHeadNode());
currentNode_.connectTo(elseChain.getHeadNode());
// Build a join node to collect results - makes things easier
OMPBasicBlock jn = new OMPBasicBlock();
jn.setPragmaContext(getLastPragma());
if (traceOn_)
System.out.println("CFGFactory: Create JoinNode");
// connect what they go through
if (thenChain.getTerminationReason()==OMPCFGResult.TermForward)
thenChain.getTailNode().connectTo(jn);
if (elseChain.getTerminationReason()==OMPCFGResult.TermForward)
elseChain.getTailNode().connectTo(jn);
// merge the nested results into the higher results
// note that IF does not settle any unresolved issues
result_.merge(ift.getResult());
result_.merge(ife.getResult());
// add the join to the chain
if (jn.hasPredecessors()) // only if used
addToChain(jn, false);
}
/**
* processSwitchStatement - build the cfg for a switch statement
* @param iss - IASTSwitchStatement
*/
private void processSwitchStatement(IASTSwitchStatement iss)
{
IASTStatement body = iss.getBody();
OMPCFGMaker caseBodyFactory = new OMPCFGMaker(body);
caseBodyFactory.buildCFG();
OMPCFGResult cBody = caseBodyFactory.getResult();
// We try to complete the structure to get rid of all breakages
// If there are none, and no barriers, we can just fold the whole switch into the case statement.
// otherwise, we attach the whole mess to the cfg.
//
// We expect a set of chains.
// In each chain, we will look for nodes that begin with a case statement.
// If they end in a break or return, they are distinct paths in the case
// otherwise they merge into each other.
//
// Build first, token basic blocks for the split and join
OMPBasicBlock bn = new OMPBasicBlock();
bn.setPragmaContext(getLastPragma());
OMPBasicBlock jn = new OMPBasicBlock();
jn.setPragmaContext(getLastPragma());
bn.setBranchingExpression(iss.getControllerExpression(), OMPBasicBlock.SwitchBranch);
LinkedList chains = cBody.getChains();
for(Iterator i=chains.iterator(); i.hasNext();) {
Chain chain = (Chain)i.next();
OMPCFGNode p = chain.getHeadNode();
while(p!=null) {
if (p instanceof OMPBasicBlock) {
if (((OMPBasicBlock)p).isCase())
bn.connectTo(p);
}
p = p.getSuccessor();
}
}
// for all the breaks, look at unresolved control flow list and deal with it
LinkedList ucf = cBody.getUnresolvedControlFlow();
Object [] ucfList = ucf.toArray(); // need this form instead of iterator, due to removal conflict
for(int i=0; i<ucfList.length; i++) {
Chain urcf = (Chain)ucfList[i];
if (urcf.getTerminationReason()==OMPCFGResult.TermBreak) {
urcf.getTailNode().connectTo(jn);
cBody.removeUnresolvedControlFlow(urcf);
}
}
// Now we see if we have anything of interest in this statement, if not, just fold it into the BB
if (!getRelevance(cBody)) {
addToBasicBlock(iss);
return;
}
// We need this structure, fold into the exising block structure
// merge results for all chains
result_.merge(cBody);
// add to current chain
addToChain(bn, true);
// the join does not follow the branch directly - there is stuff inbetween, make it the current node
addToChain(jn, false);
}
/**
* processWhileStatement - process the while statement into cfg
* @param whileStmt - IASTWhileStatement
*/
private void processWhileStatement(IASTWhileStatement whileStmt)
{
IASTExpression condition = whileStmt.getCondition();
IASTStatement body = whileStmt.getBody();
OMPCFGMaker bodyBuilder = new OMPCFGMaker(body);
bodyBuilder.buildCFG();
OMPCFGResult cBody = bodyBuilder.getResult();
// As in other stmts, build it all, do a check if we should fold in,
// otherwise, use the nodes
OMPBasicBlock bn = new OMPBasicBlock();
bn.setPragmaContext(getLastPragma());
bn.setBranchingExpression(condition, OMPBasicBlock.WhileBranch);
OMPBasicBlock jn = new OMPBasicBlock();
jn.setPragmaContext(getLastPragma());
// Build the control flow
bn.connectTo(jn);
bn.connectTo(cBody.getFirstChain().getHeadNode());
cBody.getLastChain().getTailNode().connectTo(bn);
// for all the breaks and continues, look at unresolved control flow list and deal with it
LinkedList ucf = cBody.getUnresolvedControlFlow();
for(Iterator i=ucf.iterator(); i.hasNext();) {
Chain urcf = (Chain)i.next();
if (urcf.getTerminationReason()==OMPCFGResult.TermBreak) {
urcf.getTailNode().connectTo(jn);
cBody.removeUnresolvedControlFlow(urcf);
}
else if (urcf.getTerminationReason()==OMPCFGResult.TermContinue) {
urcf.getTailNode().connectTo(bn);
cBody.removeUnresolvedControlFlow(urcf);
}
}
// Now we see if we have anything of interest in this statement, if not, just fold it into the BB
if (!getRelevance(cBody)) {
addToBasicBlock(whileStmt);
return;
}
// merge results for all chains
result_.merge(cBody);
// add to current chain
addToChain(bn, true);
addToChain(jn, false);
}
/**
* processDoStatement - process Do statement into cfg
* @param doStmt - IASTDoStatement
*/
private void processDoStatement(IASTDoStatement doStmt)
{
IASTExpression condition = doStmt.getCondition();
IASTStatement body = doStmt.getBody();
OMPBasicBlock bn = new OMPBasicBlock();
bn.setPragmaContext(getLastPragma());
bn.setBranchingExpression(condition, OMPBasicBlock.DoBranch);
OMPBasicBlock jn = new OMPBasicBlock();
jn.setPragmaContext(getLastPragma());
OMPCFGMaker bodyBuilder = new OMPCFGMaker(body);
bodyBuilder.buildCFG();
OMPCFGResult cBody = bodyBuilder.getResult();
// Build the graph [ we still put the join right after branch, to have a "break" target
cBody.getLastChain().getTailNode().connectTo(bn);
bn.connectTo(cBody.getFirstChain().getHeadNode());
bn.connectTo(jn);
// Now we see if we have anything of interest in this statement, if not, just fold it into the BB
if (!getRelevance(cBody)) {
addToBasicBlock(doStmt);
return;
}
// merge results for all chains
result_.merge(cBody);
// add to current chain
addToChain(cBody.getFirstChain().getHeadNode(), true);
addToChain(jn, true);
}
/**
* processCaseStatement - Start a new basic block beginning with this case
* @param ics - IASTCaseStatement
*/
private void processCaseStatement(IASTCaseStatement ics)
{
// Create a new basic block; but not a new chain
OMPBasicBlock b = new OMPBasicBlock();
b.setPragmaContext(getLastPragma());
addToChain(b);
if (traceOn_)
System.out.println("CFGFactory: Create BasicBlock due to case statement");
((OMPBasicBlock)currentNode_).addStatement(ics);
}
/**
* processForStatement - build the graph for the For statement
* @param ifs
*/
private void processForStatement(IASTForStatement forStmt)
{
// TODO: there are cases where any of the following can be empty - to handle
IASTStatement initializer = forStmt.getInitializerStatement();
IASTExpression condition = forStmt.getConditionExpression();
IASTExpression iteration = forStmt.getIterationExpression();
IASTStatement body = forStmt.getBody();
// We will build the final result, than analyse if we actually need this
OMPBasicBlock bn = new OMPBasicBlock();
bn.setBranchingExpression(condition, OMPBasicBlock.ForBranch);
OMPBasicBlock jn = new OMPBasicBlock();
OMPCFGMaker initBuilder = new OMPCFGMaker(initializer);
initBuilder.buildCFG();
OMPCFGResult cInit = initBuilder.getResult();
OMPCFGMaker forBodyFactory = new OMPCFGMaker(body);
forBodyFactory.buildCFG();
OMPCFGResult cBody = forBodyFactory.getResult();
OMPCFGMaker iterBodyFactory = new OMPCFGMaker(iteration);
iterBodyFactory.buildCFG();
OMPCFGResult cIter = iterBodyFactory.getResult();
// Build the primary structure of the loop
cInit.getLastChain().getTailNode().connectTo(bn);
bn.connectTo(jn);
bn.connectTo(cBody.getFirstChain().getHeadNode());
cBody.getLastChain().getTailNode().connectTo(cIter.getFirstChain().getHeadNode());
cIter.getLastChain().getTailNode().connectTo(bn);
// for all the breaks and continues, look at unresolved control flow list and deal with it
LinkedList ucf = cBody.getUnresolvedControlFlow();
Object [] uList = ucf.toArray();
for(int i=0; i<uList.length; i++) {
Chain urcf = (Chain)uList[i];
if (urcf.getTerminationReason()==OMPCFGResult.TermBreak) {
urcf.getTailNode().connectTo(jn);
cBody.removeUnresolvedControlFlow(urcf);
}
else if (urcf.getTerminationReason()==OMPCFGResult.TermContinue) {
urcf.getTailNode().connectTo(cIter.getFirstChain().getHeadNode());
cBody.removeUnresolvedControlFlow(urcf);
}
}
// Now we check if we are all resolved - only check the body
if (!getRelevance(cBody)) {
addToBasicBlock(forStmt);
return;
}
// Otherwise we add all this into the control flow graph
// merge results for all chains
result_.merge(cInit);
result_.merge(cIter);
result_.merge(cBody);
// add to current chain
addToChain(cInit.getFirstChain().getHeadNode(), true);
addToChain(bn, true);
addToChain(jn, false); // bn already connected to jn
}
/**
* getRelevance - Look at result for existence of pragmas, or
* of labels, or of unresolved control flow
* @param result - OMPCFGResult
* @return - boolean (true means any of the above exist
*/
protected boolean getRelevance(OMPCFGResult result)
{
boolean tf = (result.getNumberOfPragmas()!=0 ||
result.getNumberOfLabels()!=0 ||
result.getUnresolvedControlFlow().size()!=0);
return tf;
}
protected OMPPragmaNode getLastPragma()
{
return (pragmaContext_.isEmpty() ? null : (OMPPragmaNode)pragmaContext_.peek());
}
/**
* addToBasicBlock - add a statement to the basic block
* @param statement - IASTStatement
*/
private void addToBasicBlock(IASTStatement statement)
{
// default case, we are either adding a statement to a current basic block
if (currentNode_==null || !(currentNode_ instanceof OMPBasicBlock)) {
OMPBasicBlock b = new OMPBasicBlock();
b.setPragmaContext(getLastPragma());
addToChain(b);
// currentNode_ = new BasicBlock();
if (traceOn_)
System.out.println("CFGFactory: Create BasicBlock");
//firstNode_ = currentNode_;
}
((OMPBasicBlock)currentNode_).addStatement(statement);
}
private void addBasicBlockBranch(IASTExpression expression, int type)
{
// Add a new BB if the current is null, or is not a basic block
if (currentNode_==null || !(currentNode_ instanceof OMPBasicBlock)) {
addToChain(new OMPBasicBlock(), true);
if (traceOn_)
System.out.println("CFGFactory: Create BasicBlock");
}
assert(((OMPBasicBlock)currentNode_).getBranchingExpression()==null);
((OMPBasicBlock)currentNode_).setBranchingExpression(expression, type);
}
/**
* addToCurrentChain - add the CFGNode to the current chain
* @param node - OMPCFGNode
* @param connect - connect to prior node
*/
private void addToChain(OMPCFGNode node, boolean connect)
{
if (firstNode_==null) {
firstNode_ = node;
currentNode_ = node;
}
else {
if (connect)
currentNode_.connectTo(node);
currentNode_ = node;
}
}
/**
* addToChain - short hand to add and connect to chain
* @param node
*/
private void addToChain(OMPCFGNode node) { addToChain(node, true); }
private void addToBasicBlockAndChain(IASTStatement statement, int termReason)
{
addToBasicBlock(statement);
Chain chain = result_.addChain(firstNode_, currentNode_, termReason, statement);
if (termReason==OMPCFGResult.TermBreak || termReason==OMPCFGResult.TermContinue ||
termReason==OMPCFGResult.TermReturn)
result_.addUnresolvedControlFlow(chain);
currentNode_ = null;
firstNode_ = null;
}
}