OpenMP2CIVLWorker2.java

package dev.civl.mc.transform.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import dev.civl.abc.ast.IF.AST;
import dev.civl.abc.ast.IF.ASTFactory;
import dev.civl.abc.ast.entity.IF.Entity;
import dev.civl.abc.ast.entity.IF.Function;
import dev.civl.abc.ast.entity.IF.Variable;
import dev.civl.abc.ast.node.IF.ASTNode;
import dev.civl.abc.ast.node.IF.ASTNode.NodeKind;
import dev.civl.abc.ast.node.IF.IdentifierNode;
import dev.civl.abc.ast.node.IF.NodePredicate;
import dev.civl.abc.ast.node.IF.PairNode;
import dev.civl.abc.ast.node.IF.SequenceNode;
import dev.civl.abc.ast.node.IF.compound.CompoundInitializerNode;
import dev.civl.abc.ast.node.IF.compound.DesignationNode;
import dev.civl.abc.ast.node.IF.declaration.FunctionDefinitionNode;
import dev.civl.abc.ast.node.IF.declaration.InitializerNode;
import dev.civl.abc.ast.node.IF.declaration.VariableDeclarationNode;
import dev.civl.abc.ast.node.IF.expression.ExpressionNode;
import dev.civl.abc.ast.node.IF.expression.FunctionCallNode;
import dev.civl.abc.ast.node.IF.expression.IdentifierExpressionNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode;
import dev.civl.abc.ast.node.IF.expression.OperatorNode.Operator;
import dev.civl.abc.ast.node.IF.omp.OmpAtomicNode;
import dev.civl.abc.ast.node.IF.omp.OmpAtomicNode.OmpAtomicClause;
import dev.civl.abc.ast.node.IF.omp.OmpDeclarativeNode;
import dev.civl.abc.ast.node.IF.omp.OmpExecutableNode;
import dev.civl.abc.ast.node.IF.omp.OmpForNode;
import dev.civl.abc.ast.node.IF.omp.OmpNode;
import dev.civl.abc.ast.node.IF.omp.OmpParallelNode;
import dev.civl.abc.ast.node.IF.omp.OmpReductionNode;
import dev.civl.abc.ast.node.IF.omp.OmpReductionNode.OmpReductionOperator;
import dev.civl.abc.ast.node.IF.omp.OmpSymbolReductionNode;
import dev.civl.abc.ast.node.IF.omp.OmpSyncNode;
import dev.civl.abc.ast.node.IF.omp.OmpWorksharingNode;
import dev.civl.abc.ast.node.IF.statement.BlockItemNode;
import dev.civl.abc.ast.node.IF.statement.CivlForNode;
import dev.civl.abc.ast.node.IF.statement.CompoundStatementNode;
import dev.civl.abc.ast.node.IF.statement.DeclarationListNode;
import dev.civl.abc.ast.node.IF.statement.ExpressionStatementNode;
import dev.civl.abc.ast.node.IF.statement.ForLoopInitializerNode;
import dev.civl.abc.ast.node.IF.statement.ForLoopNode;
import dev.civl.abc.ast.node.IF.statement.StatementNode;
import dev.civl.abc.ast.node.IF.type.TypeNode;
import dev.civl.abc.front.IF.CivlcTokenConstant;
import dev.civl.abc.token.IF.Source;
import dev.civl.abc.token.IF.SyntaxException;
import dev.civl.mc.config.IF.CIVLConfiguration;
import dev.civl.mc.config.IF.CIVLConstants;
import dev.civl.mc.model.IF.CIVLSyntaxException;
import dev.civl.mc.model.IF.CIVLUnimplementedFeatureException;
import dev.civl.mc.transform.IF.OpenMP2CIVLTransformer;
import dev.civl.mc.transform.common.OmpRegion.OmpRgnKind;
import dev.civl.mc.util.IF.Triple;

/**
 * See
 * 
 * https://vsl.cis.udel.edu/trac/civl/wiki/OpenMPTransformation
 * 
 * for documentation on this transformation.
 * 
 *
 * @author wuwenhao (wuwenhao@udel.edu)
 */
public class OpenMP2CIVLWorker2 extends BaseWorker {
	// Fields
	/**
	 * The source information used by all nodes created by this OpenMP-to-CIVL
	 * transformer.
	 */
	static private final String SRC_INFO = "OpenMP2CIVLWorker2";

	static private final String FORTRAN_FILE_SUFFIX = ".f";
	/* Specific numbers used in this transformer */
	static private final int INDEX_PVT_DECLS = 0;
	static private final int INDEX_TMP_DECLS = 1;
	static private final int INDEX_RDC_INITS = 0;
	static private final int INDEX_RDC_COMBS = 1;
	static private final int ID_MASTER_THREAD = 0;
	/* OpenMP variable identifiers used */
	static private final String LOOP_ITER = "i";
	// function/type identifier prefix
	static private final String SIGN_DOLLAR = "$";
	// variable identifier prefix
	static private final String _OMP_ = "_omp_";
	static private final String ATOMIC_ = _OMP_ + "atomic_";
	static private final String FIRSTPRIVATE_ = _OMP_ + "fstpvt_";
	static private final String LASTPRIVATE_ = _OMP_ + "lstpvt_";
	static private final String REDUCTION_ = _OMP_ + "reduction_";
	static private final String CRITICAL_ = _OMP_ + "critical_";
	// variable identifier suffix
	static private final String _NEXT = "_next";
	// commonly used variable identifiers
	static private final String DOM = _OMP_ + "dom";
	static private final String GTEAM = _OMP_ + "gteam";
	static private final String RANGE = _OMP_ + "range";
	static private final String NTHREADS = _OMP_ + "nthreads";
	static private final String NUM_THREADS = _OMP_ + "num_threads";
	/** CIVL input variable for the maximum number of OpenMP threads */
	static private final String TEAM = _OMP_ + "team";
	static private final String THREAD_LAST = _OMP_ + "thread_last";
	static private final String THREAD_LAST_ITER = _OMP_ + "thread_last_iter";
	static private final String THREAD_MAX = _OMP_ + "thread_max";
	static private final String THREAD_RANGE = _OMP_ + "thread_range";
	static private final String TID = _OMP_ + "tid";
	// Construct loops
	static private final String DOM_LOOP = _OMP_ + "loop_domain";
	static private final String LOOP_DIST = _OMP_ + "loop_dist";
	static private final String ORDERED = _OMP_ + "ordered";
	// Construct sections
	static private final String SECTIONS_DIST = _OMP_ + "sections_dist";
	/** The variable name representing the OpenMP section block id */
	static private final String SID = _OMP_ + "sid";
	// Construct single
	static private final String SINGLE_DIST = _OMP_ + "single_dist";
	// Construct critical
	static private final String NAME_CRITICAL_UNSPEC = "";
	// clauses

	/* OpenMP helper types */
	static private final String OMP_HELPER_SIGNAL = "$omp_helper_signal";
	/* OpenMP function identifier */
	static private final String OMP_SET_NUM_THREADS = "omp_set_num_threads";
	static private final String OMP_GET_NUM_THREADS = "omp_get_num_threads";
	static private final String OMP_GET_MAX_THREADS = "omp_get_max_threads";
	static private final String OMP_GET_THREAD_NUM = "omp_get_thread_num";
	static private final String OMP_GET_NUM_PROCS = "omp_get_num_procs";
	static private final String OMP_SET_LOCK = "omp_set_lock";
	static private final String OMP_SET_NEST_LOCK = "omp_set_nest_lock";
	static private final String OMP_TEST_LOCK = "omp_test_lock";
	static private final String OMP_TEST_NEST_LOCK = "omp_test_nest_lock";
	static private final String OMP_UNSET_LOCK = "omp_unset_lock";
	static private final String OMP_UNSET_NEST_LOCK = "omp_unset_nest_lock";
	/* CIVL OpenMP verification helper function identifiers */
	static private final String CHECK_DATA_RACE = "$check_data_race";
	static private final String LOCAL_START = "$local_start";
	static private final String LOCAL_END = "$local_end";
	static private final String OMP_ARRIVE_SECTIONS = "$omp_arrive_sections";
	static private final String OMP_ARRIVE_SINGLE = "$omp_arrive_single";
	static private final String OMP_ATOMIC_EXECUTION_LOCK_ACQUIRE = "$omp_atomic_execution_lock_acquire";
	static private final String OMP_ATOMIC_EXECUTION_LOCK_RELEASE = "$omp_atomic_execution_lock_release";
	static private final String OMP_BARRIER = "$omp_barrier";
	static private final String OMP_GTEAM_CREATE = "$omp_gteam_create";
	static private final String OMP_GTEAM_DESTROY = "$omp_gteam_destroy";
	static private final String OMP_HELPER_SIGNAL_CREATE = "$omp_helper_signal_create";
	static private final String OMP_HELPER_SIGNAL_WAIT = "$omp_helper_signal_wait";
	static private final String OMP_HELPER_SIGNAL_SEND = "$omp_helper_signal_send";
	static private final String OMP_REDUCTION_COMBINE = "$omp_reduction_combine";
	static private final String OMP_TEAM_CREATE = "$omp_team_create";
	static private final String OMP_TEAM_DESTROY = "$omp_team_destroy";
	static private final String READ_AND_WRITE_SETS_POP = "$read_and_write_sets_pop";
	static private final String READ_AND_WRITE_SETS_PUSH = "$read_and_write_sets_push";
	static private final String READ_SET_POP = "$read_set_pop";
	static private final String READ_SET_PUSH = "$read_set_push";
	static private final String WRITE_SET_POP = "$write_set_pop";
	static private final String WRITE_SET_PUSH = "$write_set_push";
	static private final String YIELD = "$yield";

	static private final NodePredicate PREDICATE_BARRIER_AND_FLUSH = new NodePredicate() {
		@Override
		public boolean holds(ASTNode node) {
			if (node instanceof ExpressionStatementNode) {
				ExpressionNode expr = ((ExpressionStatementNode) node)
						.getExpression();

				if (expr instanceof FunctionCallNode) {
					ExpressionNode func = ((FunctionCallNode) expr)
							.getFunction();

					if (func instanceof IdentifierExpressionNode)
						return ((IdentifierExpressionNode) func).getIdentifier()
								.name().equals(OMP_BARRIER);
				}
			}
			return false;
		}
	};

	/**
	 * The kind of a OpenMp private variable specified by a
	 * private/firstprivate/lastprivate clause or threadprivate directive.
	 */
	private enum PrivateKind {
		/** OpenMP private clause */
		DEFAULT,
		/** OpenMP firstprivate clause */
		FIRST,
		/** OpenMP lastprivate clause */
		LAST, // Unsupported currently
		/** threadprivate directive */
		THREAD, // Unsupported currently
	}

	/**
	 * The command line configuration information for querying transformation
	 * conditions.
	 */
	private CIVLConfiguration config;

	/**
	 * A counter for all omp work sharing arrive checking functions including
	 * $omp_arrive_loop, $omp_arrive_sections, $omp_arrive_single
	 */
	private int ctrOmpWorkShareRecordSetId = 0;
	/** A counter for OpenMP reduction items */
	private int ctrOmpReductionItem = 0;
	/** A counter for OpenMP ordered construct */
	private int ctrOmpOrdered = 0;

	// private int levelParallel = 0;

	/** is the binding region specified with <code>ordered(concurrent)</code> */
	private boolean orderConcurrent = false;

	private boolean hasAtomicConstruct = false;

	private boolean hasReductionConstruct = false;

	private boolean hasLastPrivate = false;

	/**
	 * The stack storing current omp region information.
	 */
	private Stack<OmpRegion> ompRgn = new Stack<>();

	private Stack<ArrayList<OmpLoopInfo>> bindingLoopInfosRecords = new Stack<>();

	/**
	 * The root node of the input AST.
	 */
	private SequenceNode<BlockItemNode> root;

	/**
	 * A list of critical variable name for critical sections encountered
	 */
	private Set<String> criticalNames = new HashSet<>();

	private List<BlockItemNode> globalVarDecls = new LinkedList<>();

	private List<BlockItemNode> signalCreates = new LinkedList<>();

	Map<String, VariableDeclarationNode> reductionId2TempDecls;

	private OmpOrphanFunctions ompOrphanFuncs;

	// Constructors
	/**
	 * Constructs a new instance of {@link OpenMP2CIVLWorker2}
	 * 
	 * @param astFactory
	 *                       the {@link ASTFactory} instance used for performing
	 *                       transformation
	 * @param config
	 *                       the {@link CIVLConfiguration} instance used for
	 *                       querying transformation conditions
	 */
	public OpenMP2CIVLWorker2(ASTFactory astFactory, CIVLConfiguration config) {
		super(OpenMP2CIVLTransformer.LONG_NAME, astFactory);
		this.identifierPrefix = "$omp_";
		this.config = config;
	}

	// Helper Functions or methods

	/**
	 * Returns a list of {@link ExpressionNode}
	 * <p>
	 * 1. if (a) the given <code>expr</code> is an {@link OperatorNode}, <br>
	 * (b) its {@link Operator} is any of following ones:
	 * {@link Operator#POSTDECREMENT}, {@link Operator#POSTINCREMENT},
	 * {@link Operator#PREDECREMENT}, {@link Operator#PREINCREMENT} <br>
	 * and (c) the operand expression has a scalar type, <br>
	 * then returns a size:<strong>1</strong> list containing the exact operand
	 * expression.
	 * </p>
	 * <p>
	 * 2. if (a) the given <code>expr</code> is an {@link OperatorNode}, <br>
	 * (b) its {@link Operator} is {@link Operator#ASSIGN}, <br>
	 * (c) the left hand side expression is not in the right hand side,<br>
	 * and (d) both sides have scalar types, <br>
	 * then, returns a size:<strong>2</strong> list containing both left and
	 * right hand side expressions.
	 * </p>
	 * <p>
	 * 2. if (a) the given <code>expr</code> is an {@link OperatorNode}, <br>
	 * (b) its {@link Operator} is any of following binary-assign-operators:
	 * {@link Operator#BITANDEQ}, {@link Operator#BITOREQ},
	 * {@link Operator#BITXOREQ}, {@link Operator#DIVEQ},
	 * {@link Operator#MINUSEQ}, {@link Operator#PLUSEQ},
	 * {@link Operator#SHIFTLEFTEQ}, {@link Operator#SHIFTRIGHTEQ},
	 * {@link Operator#TIMESEQ} <br>
	 * and (c) both sides have scalar types, <br>
	 * then, returns a size:<strong>3</strong> list as {x, x.copy, expr}
	 * </p>
	 * <p>
	 * 4. if (a) the given <code>expr</code> is an {@link OperatorNode}, <br>
	 * (b) its {@link Operator} is {@link Operator#ASSIGN}, <br>
	 * (c) the left hand side expression immediately appears in the right hand
	 * side expression as: <code>x = x bin-op expr</code> or
	 * <code>x = expr bin-op x</code><br>
	 * and (d) both <code>x</code> and <code>expr</code> have scalar types, <br>
	 * then returns a size:<strong>3</strong> list as {x, x, expr}
	 * </p>
	 * <p>
	 * 4. if the given <code>expr</code> does <strong>NOT</strong> satisfy any
	 * conditions listed above, <br>
	 * then returns an empty list, whose size is <strong>0</strong>
	 * </p>
	 * 
	 * @param expr
	 *                 the expression required to be analyzed.
	 * @return see above
	 */
	private List<ExpressionNode> analyzeExprAssignScalar(ExpressionNode expr) {
		List<ExpressionNode> args = new LinkedList<>();

		if (expr instanceof OperatorNode) {
			OperatorNode opExpr = (OperatorNode) expr;
			ExpressionNode lhs = null;
			ExpressionNode rhs = null;

			switch (opExpr.getOperator()) {
				case POSTDECREMENT :
				case POSTINCREMENT :
				case PREDECREMENT :
				case PREINCREMENT :
					lhs = opExpr.getArgument(0);
					if (lhs.getType().isScalar()) {
						// Cond.1, 'args' is: {x}
						args.add(lhs);
					}
					return args;
				case ASSIGN :
					lhs = opExpr.getArgument(0);
					rhs = opExpr.getArgument(1);

					if (lhs.getType().isScalar() && rhs.getType().isScalar()) {
						if (rhs instanceof OperatorNode) {
							// x = x bin-op expr
							// x = expr bin-op x
							opExpr = (OperatorNode) rhs;

							switch (opExpr.getOperator()) {
								case BITAND :
								case BITOR :
								case BITXOR :
								case DIV :
								case MINUS :
								case PLUS :
								case SHIFTLEFT :
								case SHIFTRIGHT :
								case TIMES :
									ExpressionNode xExpr = lhs;

									lhs = opExpr.getArgument(0);
									rhs = opExpr.getArgument(1);
									if (verifyExprSameEntity(xExpr, lhs)) {
										// x = x bin-op expr
										args.add(xExpr); // x
										args.add(lhs); // x
										args.add(rhs); // expr
										return args;
									}
									if (verifyExprSameEntity(xExpr, rhs)) {
										// x = expr bin-op x
										args.add(xExpr); // x
										args.add(rhs); // x
										args.add(lhs); // expr
										return args;
									}
									// x = expr (and x is not in expr)
									args.add(xExpr);
									args.add(opExpr);
									return args;
								default :
							}
						}
						// v = x OR x = expr
						args.add(lhs); // v or x
						args.add(rhs); // x or expr
					}
					return args;
				case BITANDEQ :
				case BITOREQ :
				case BITXOREQ :
				case DIVEQ :
				case MINUSEQ :
				case PLUSEQ :
				case SHIFTLEFTEQ :
				case SHIFTRIGHTEQ :
				case TIMESEQ :
					lhs = opExpr.getArgument(0);
					rhs = opExpr.getArgument(1);

					if (lhs.getType().isScalar() && rhs.getType().isScalar()) {
						// x bin-op = expr
						args.add(lhs); // x
						args.add(rhs); // expr
					}
					return args;
				default :
			}
		}
		return args;
	}

	/** <code>$omp_atomic_execution_lock_acquire(team, &blockName);</code> */
	private BlockItemNode callLockAcquire(String srcMethod, String blockName) {
		ExpressionNode addrOfExprNode = nodeFactory.newOperatorNode(
				newSource(srcMethod, CivlcTokenConstant.EXPR),
				Operator.ADDRESSOF,
				Arrays.asList(nodeExprId(srcMethod, blockName)));

		return nodeStmtCall(srcMethod, OMP_ATOMIC_EXECUTION_LOCK_ACQUIRE,
				nodeExprId(srcMethod, TEAM), addrOfExprNode);
	}

	/** <code>$omp_atomic_execution_lock_release(team, &blockName);</code> */
	private BlockItemNode callLockRelease(String srcMethod, String blockName) {
		ExpressionNode addrOfExprNode = nodeFactory.newOperatorNode(
				newSource(srcMethod, CivlcTokenConstant.EXPR),
				Operator.ADDRESSOF,
				Arrays.asList(nodeExprId(srcMethod, blockName)));

		return nodeStmtCall(srcMethod, OMP_ATOMIC_EXECUTION_LOCK_RELEASE,
				nodeExprId(srcMethod, TEAM), addrOfExprNode);
	}

	/** returns: <code>$omp_barrier(_omp_team);</code> */
	private BlockItemNode callOmpBarrier(String srcMethod) {
		return nodeStmtCall(srcMethod, OMP_BARRIER,
				nodeExprId(srcMethod, TEAM));
	}

	/**
	 * Return {@link BlockItemNode}s representing:<br>
	 * <code>$read_set_pop();</code><br>
	 * <code>$write_set_pop();</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return see above
	 */
	private List<BlockItemNode> callRWSetPop(String srcMethod) {
		return Arrays.asList(//
				nodeStmtCall(srcMethod, READ_SET_POP),
				nodeStmtCall(srcMethod, WRITE_SET_POP));
	}

	/**
	 * Return a list of {@link BlockItemNode} representing:<br>
	 * <code>$read_set_push();</code><br>
	 * <code>$write_set_push();</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return see above
	 */
	private List<BlockItemNode> callRWSetPush(String srcMethod) {
		return Arrays.asList(//
				nodeStmtCall(srcMethod, READ_SET_PUSH),
				nodeStmtCall(srcMethod, WRITE_SET_PUSH));
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$domain(collapse) _omp_loop_dist = ($domain(collapse))
	 * $omp_arrive_loop(team, loop_id++, _omp_loop_domain, STRATEGY);</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param collapse
	 *                      the collapse value specified with the current OpenMP
	 *                      loop construct
	 * @return see above
	 */
	private VariableDeclarationNode declOmpDistLoop(String srcMethod,
			int collapse) {
		// type: $domain(collapse)
		TypeNode typeDom = nodeTypeDom(srcMethod, collapse);
		// id: _omp_loop_dist
		IdentifierNode _omp_loop_dist = nodeIdent(srcMethod, LOOP_DIST);
		// init: ($domain(collapse))$omp_arrive_loop(
		// team, FOR_LOC++, _omp_loop_domain, STRATEGY);
		ExpressionNode init = nodeExprCast(srcMethod, typeDom.copy(),
				nodeExprCall(srcMethod, "$omp_arrive_loop",
						nodeExprId(srcMethod, TEAM),
						nodeExprInt(srcMethod, ctrOmpWorkShareRecordSetId++),
						nodeExprCast(srcMethod, nodeTypeDom(srcMethod, 0),
								nodeExprId(srcMethod, DOM_LOOP)),
						nodeExprInt(srcMethod, config.ompLoopDecomp())));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION),
				_omp_loop_dist, typeDom, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$domain(1) _omp_sections_dist = ($domain(1)) 
	 * $omp_arrive_sections(_omp_team, section_id++, numSection);</code>
	 * 
	 * @param srcMethod
	 *                       Dummy {@link Source} information based on caller
	 *                       name
	 * @param numSection
	 *                       the number of OpenMP section block in related
	 *                       sections construct
	 * @return see above
	 */
	private BlockItemNode declOmpDistSections(String srcMethod,
			int numSection) {
		// type: $domain(1)
		TypeNode typeDom = nodeTypeDom(srcMethod, 1);
		// id: _omp_sections_dist
		IdentifierNode _omp_sections_dist = nodeIdent(srcMethod, SECTIONS_DIST);
		// init: ($domain(collapse))$omp_arrive_loop(
		// team, FOR_LOC++, _omp_loop_domain, STRATEGY);
		ExpressionNode init = nodeExprCast(srcMethod, typeDom.copy(),
				nodeExprCall(srcMethod, OMP_ARRIVE_SECTIONS,
						nodeExprId(srcMethod, TEAM),
						nodeExprInt(srcMethod, ctrOmpWorkShareRecordSetId++),
						nodeExprInt(srcMethod, numSection)));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION),
				_omp_sections_dist, typeDom, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>int _omp_single_dist = $omp_arrive_single(team, single_id++);</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return see above
	 */
	private BlockItemNode declOmpDistSingle(String srcMethod) {
		// type: int
		TypeNode type = nodeTypeInt(srcMethod);
		// id: _omp_single_dist
		IdentifierNode _omp_single_dist = nodeIdent(srcMethod, SINGLE_DIST);
		// init: $omp_arrive_single(team, single_id++);
		ExpressionNode init = nodeExprCall(srcMethod, OMP_ARRIVE_SINGLE,
				nodeExprId(srcMethod, TEAM),
				nodeExprInt(srcMethod, ctrOmpWorkShareRecordSetId++));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION),
				_omp_single_dist, type, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$domain(1) _omp_dom = ($domain){_omp_thread_range};</code> (if
	 * <code>numRanges</code> is 0) <strong>OR</strong><br>
	 * <code>$domain(1) _omp_loop_dom = ($domain){_omp_range1, ...};</code> (if
	 * <code>numRanges</code> is positive) <br>
	 * <strong>PRE-CONDITION</strong>: <code>numRanges</code> shall be
	 * non-negative.
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param numRanges
	 *                      <code>0</code> for OpenMP parallel region; or a
	 *                      positive number representing the number of
	 *                      associated for loops.
	 * @return See above
	 */
	private VariableDeclarationNode declOmpDomain(String srcMethod,
			int numRanges) {
		boolean isParallel = numRanges == 0;
		// type: $domain
		TypeNode typeDom = nodeTypeDom(srcMethod, 1);
		IdentifierNode idDom = null;
		List<PairNode<DesignationNode, InitializerNode>> initials = new ArrayList<>();

		if (isParallel) {
			// id: _omp_dom
			idDom = nodeIdent(srcMethod, DOM);
			// initials: _omp_thread_range
			initials.add(nodeFactory.newPairNode(
					/* src */ newSource(srcMethod, CivlcTokenConstant.STRUCT),
					/* dsgn */(DesignationNode) null, //
					/* init */ nodeExprId(srcMethod, THREAD_RANGE)));
		} else {
			// id: _omp_loop_domain
			idDom = nodeIdent(srcMethod, DOM_LOOP);
			// initials: _omp_range1, .. ,_omp_rangeX
			for (int i = 1; i <= numRanges; i++)
				initials.add(nodeFactory.newPairNode(
						/* src */ newSource(srcMethod,
								CivlcTokenConstant.STRUCT),
						/* dsgn */ (DesignationNode) null, //
						/* init */ nodeExprId(srcMethod,
								RANGE + Integer.toString(i))));
		}

		// initialList: {_omp_thread_range} OR {_omp_range1, .., _omp_rangeX}
		CompoundInitializerNode initialList = nodeFactory
				.newCompoundInitializerNode(newSource(srcMethod,
						CivlcTokenConstant.INITIALIZER_LIST), initials);
		// init: ($domain){..} or
		InitializerNode init = nodeFactory.newCompoundLiteralNode(
				/* src */ newSource(srcMethod,
						CivlcTokenConstant.COMPOUND_LITERAL),
				/* type */ nodeTypeDom(srcMethod, 0),
				/* initials */ initialList);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				idDom, typeDom, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$omp_gteam gteam = $omp_gteam_create($here, nthreads);</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return See above
	 */
	private VariableDeclarationNode declOmpGteam(String srcMethod) {
		// type: $omp_gteam
		TypeNode typeGteam = nodeTypeNamed(srcMethod, "$omp_gteam");
		// id: _omp_gteam
		IdentifierNode _omp_gteam = nodeIdent(srcMethod, GTEAM);
		// init: $omp_gteam_create($here, nthreads)
		InitializerNode init = nodeExprCall(srcMethod, OMP_GTEAM_CREATE,
				nodeFactory.newHereNode(
						newSource(srcMethod, CivlcTokenConstant.HERE)),
				nodeExprId(srcMethod, NTHREADS));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_gteam, typeGteam, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$omp_helper_signal /signalName/ = $omp_helper_signal_create(/initExpr/);</code>
	 * 
	 * @param srcMethod
	 *                       Dummy {@link Source} information based on caller
	 *                       name
	 * @param signalName
	 *                       The name of a <code>critical</code> signal struct
	 *                       variable for a critical section encountered.
	 * @param initExpr
	 *                       the initialization expression used as the argument
	 *                       for the function
	 *                       <code>$omp_helper_signal_create</code>.
	 * @return See above
	 */
	private VariableDeclarationNode declOmpHelperSignal(String srcMethod,
			String signalName, ExpressionNode initExpr) {
		// type: $omp_helper_signal
		TypeNode type = nodeTypeNamed(srcMethod, OMP_HELPER_SIGNAL);
		// id: /signalName/
		IdentifierNode id = nodeIdent(srcMethod, signalName);
		// init: $omp_helper_signal_create(/initExpr/);
		InitializerNode init = nodeExprCall(srcMethod, OMP_HELPER_SIGNAL_CREATE,
				initExpr);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				id, type, init);
	}

	private List<BlockItemNode> declOmpLoopVarNext(String srcMethod,
			List<OmpLoopInfo> loopInfos) {
		List<BlockItemNode> loopVarNextDecls = new LinkedList<>();

		for (OmpLoopInfo info : loopInfos) {
			TypeNode type = nodeTypeInt(srcMethod);
			IdentifierNode loop_var_next = nodeIdent(srcMethod,
					info.loopVarName + _NEXT);
			InitializerNode init = nodeFactory.newOperatorNode(
					/* src */ newSource(srcMethod, CivlcTokenConstant.EXPR),
					/* op */ Operator.PLUS,
					/* arg0 */ nodeExprId(srcMethod, info.loopVarName),
					/* arg1 */ info.range.third.copy());

			loopVarNextDecls.add(nodeFactory.newVariableDeclarationNode(
					newSource(srcMethod, CivlcTokenConstant.DECLARATION),
					loop_var_next, type, init));
		}
		return loopVarNextDecls;
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>int _omp_nthreads = 1+$choose_int(_omp_num_threads);</code> (if
	 * there is no explicit num_threads clause declared)<br>
	 * <code>int _omp_nthreads = _omp_num_threads;</code> (if an explicit
	 * num_threads clause is declared.)
	 * 
	 * @param srcMethod
	 *                       Dummy {@link Source} information based on caller
	 *                       name
	 * @param numThds
	 *                       The {@link ExpressionNode} for
	 *                       <code>_omp_num_threads</code>
	 * @param isDeclared
	 *                       <code>true</code> iff an explicit num_threads
	 *                       clause is declared with an exact constant value
	 *                       defining the number of threads.
	 * @return see above
	 */
	private VariableDeclarationNode declOmpNthreads(String srcMethod,
			ExpressionNode numThds, Boolean isDeclared) {
		// type: int
		TypeNode typeInt = nodeTypeInt(srcMethod);
		// id: _omp_nthreads
		IdentifierNode _omp_nthreads = nodeIdent(srcMethod, NTHREADS);
		// init: _omp_num_threads
		// OR
		// init: 1+$choose_int(_omp_num_threads)
		InitializerNode init = isDeclared
				? numThds
				: nodeFactory.newOperatorNode(
						/* src */ newSource(srcMethod, CivlcTokenConstant.EXPR),
						/* op */ Operator.PLUS,
						/* arg0 */ nodeExprInt(srcMethod, 1),
						/* arg1 */ nodeExprCall(srcMethod, "$choose_int",
								numThds));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_nthreads, typeInt, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>int _omp_num_threads = _omp_thread_max;</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return see above
	 */
	private VariableDeclarationNode declOmpNumThreads(String srcMethod) {
		// type: int
		TypeNode typeInt = nodeTypeInt(srcMethod);
		// id: _omp_nthreads
		IdentifierNode _omp_num_threads = nodeIdent(srcMethod, NUM_THREADS);
		// init: _omp_thread_max
		InitializerNode init = nodeExprId(srcMethod, THREAD_MAX);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_num_threads, typeInt, init);
	}

	/**
	 * Add signal struct variable declarations in global scope for each loop
	 * variables associated with an ordered clause
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param loopInfos
	 *                      a list of {@link OmpLoopInfo}s, each of them
	 *                      contains both lower and upper bounds and the
	 *                      incremental stride for a single associated loop
	 *                      clause
	 */
	private void declOmpOrderedSignals(String srcMethod,
			List<OmpLoopInfo> loopInfos) {
		for (int i = 0; i < loopInfos.size(); i++) {
			signalCreates.add(
					declOmpHelperSignal(srcMethod, ORDERED + (ctrOmpOrdered++),
							loopInfos.get(i).range.first.copy()));
		}
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$range _omp_rangeX = {lo .. hi#step};</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param num
	 *                      The identifier number X of the declared $range type
	 *                      variable
	 * @return See above
	 */
	private VariableDeclarationNode declOmpRange(String srcMethod, int num,
			Triple<ExpressionNode, ExpressionNode, ExpressionNode> range,
			boolean isFortran) {
		// type: $range
		TypeNode typeInt = nodeTypeRange(srcMethod);
		// id: _omp_rangeX
		String rangeIdName = RANGE + Integer.toString(num);
		IdentifierNode _omp_rangeX = nodeIdent(srcMethod, rangeIdName);
		ExpressionNode init = null;

		if (isFortran) {
			// step >=0
			ExpressionNode cond = nodeFactory.newOperatorNode(
					newSource(srcMethod, CivlcTokenConstant.EXPR), Operator.GTE,
					range.third.copy(), nodeExprInt(srcMethod, 0));
			// first .. second#step
			ExpressionNode rangePosStep = nodeExprRange(srcMethod,
					range.first.copy(), range.second.copy(),
					range.third.copy());
			// second .. first#step
			ExpressionNode rangeNegStep = nodeExprRange(srcMethod,
					range.second.copy(), range.first.copy(),
					range.third.copy());

			init = nodeFactory.newOperatorNode(
					newSource(srcMethod, CivlcTokenConstant.EXPR),
					Operator.CONDITIONAL,
					Arrays.asList(cond, rangePosStep, rangeNegStep));
		} else {
			// {first .. second#step};
			init = nodeExprRange(srcMethod, range.first.copy(),
					range.second.copy(), range.third.copy());
		}
		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_rangeX, typeInt, init);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$omp_team _omp_team = $omp_team_create($here, _omp_gteam, _omp_tid);</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return See above
	 */
	private VariableDeclarationNode declOmpTeam(String srcMethod) {
		// type: "$omp_team"
		TypeNode typeTeam = nodeTypeNamed(srcMethod, "$omp_team");
		// id: _omp_team
		IdentifierNode _omp_team = nodeIdent(srcMethod, TEAM);
		// init: $omp_team_create($here, gteam, _tid);
		InitializerNode init = nodeExprCall(srcMethod, OMP_TEAM_CREATE,
				nodeExprHere(srcMethod), nodeExprId(srcMethod, GTEAM),
				nodeExprId(srcMethod, TID));

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_team, typeTeam, init);
	}

	private VariableDeclarationNode declOmpThreadLast(String srcMethod) {
		// type: int
		TypeNode type = nodeTypeInt(srcMethod);
		// id: _omp_thread_max
		IdentifierNode _omp_thread_last = nodeIdent(srcMethod, THREAD_LAST);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_thread_last, type);
	}

	private VariableDeclarationNode declOmpThreadLastIter(String srcMethod) {
		// type: int
		TypeNode type = nodeTypeInt(srcMethod);
		// id: _omp_thread_max
		IdentifierNode _omp_thread_last = nodeIdent(srcMethod,
				THREAD_LAST_ITER);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_thread_last, type, nodeExprInt(srcMethod, -1));
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$input int _omp_thread_max;</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return
	 */
	private VariableDeclarationNode declOmpThreadMax(String srcMethod) {
		// type: int
		TypeNode type = nodeTypeInt(srcMethod);
		// id: _omp_thread_max
		IdentifierNode _omp_thread_max = nodeIdent(srcMethod, THREAD_MAX);

		// set $input
		type.setInputQualified(true);
		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_thread_max, type);
	}

	/**
	 * Return {@link VariableDeclarationNode} representing:<br>
	 * <code>$range _omp_thread_range = {0 .. _omp_nthreads-1};</code>
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @return See above
	 */
	private VariableDeclarationNode declOmpThreadRange(String srcMethod) {
		// type: $range
		TypeNode typeInt = nodeTypeRange(srcMethod);
		// id: _omp_thread_range
		IdentifierNode _omp_thread_range = nodeIdent(srcMethod, THREAD_RANGE);
		// init: {0 .. _omp_nthreads-1}
		InitializerNode init = nodeExprRange(srcMethod,
				/* lb */ nodeExprInt(srcMethod, 0),
				/* ub */ nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.SUB),
						Operator.MINUS, nodeExprId(srcMethod, NTHREADS),
						nodeExprInt(srcMethod, 1)),
				/* step */ null);

		return nodeFactory.newVariableDeclarationNode(
				newSource(srcMethod, CivlcTokenConstant.DECLARATION), //
				_omp_thread_range, typeInt, init);
	}

	/**
	 * Process a list of {@link OmpLoopInfo}s to retrieve all involved loop
	 * variables, so that CIVL <code>$for</code> can declare them correctly in
	 * its loop initial expression.
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param loopInfos
	 *                      A list of {@link OmpLoopInfo}s, each of which
	 *                      contains a single loop variable.
	 * @return A list of {@link VariableDeclarationNode} representing
	 *         declarations of all involved loop variables.
	 */
	private List<VariableDeclarationNode> declVarsLoopInit(String srcMethod,
			List<OmpLoopInfo> loopInfos) {
		List<VariableDeclarationNode> loopVarDecls = new LinkedList<>();

		for (OmpLoopInfo loopInfo : loopInfos)
			loopVarDecls.add(nodeDeclVarInt(srcMethod, loopInfo.loopVarName));
		return loopVarDecls;
	}

	/**
	 * Process dummy declarations for private variables in thread-local scope.
	 * 
	 * @param srcMethod
	 *                      Dummy {@link Source} information based on caller
	 *                      name
	 * @param varIds
	 *                      a sequence of {@link IdentifierExpressionNode}
	 *                      representing a list of variables specified by a
	 *                      single privatization clause or directive.
	 * @param kind
	 *                      the kind of the related privatization clause or
	 *                      directive.
	 * @return a non-empty list containing at least one non-<code>null</code>
	 *         list of {@link VariableDeclarationNode}s for dummy declarations
	 *         of private variables. A second optional list for temporary
	 *         declarations that shall be inserted before the OpenMP region.
	 */
	private List<List<VariableDeclarationNode>> declVarsPrivate(
			String srcMethod, SequenceNode<IdentifierExpressionNode> varIds,
			List<BlockItemNode> lstpvtAssignments, PrivateKind kind) {
		Source declSrc = newSource(srcMethod, CivlcTokenConstant.DECLARATION);
		List<List<VariableDeclarationNode>> privateVarDecls = new LinkedList<List<VariableDeclarationNode>>();
		VariableDeclarationNode actualVarDecl = null;
		IdentifierNode actualVarId = null;
		VariableDeclarationNode pvtVarDecl = null;
		IdentifierNode pvtVarId = null;
		TypeNode pvtVarType = null;
		VariableDeclarationNode tmpVarDecl = null;
		IdentifierNode tmpVarId = null;
		TypeNode tmpVarType = null;
		String pvtVarName, tmpVarName;

		// The first list is for private variable declarations,
		// which is required for all situations
		privateVarDecls.add(new LinkedList<VariableDeclarationNode>());
		// The second list for temporary declarations that
		// shall be inserted before the parallel region
		privateVarDecls.add(new LinkedList<VariableDeclarationNode>());
		// If there is no private/firstprivate/lastprivate/threadprivate,
		// or no actual variable are specified with them.
		if (varIds == null || varIds.numChildren() == 0)
			// The first list for private variables is empty.
			return privateVarDecls;
		// else at least one private variable is specified
		switch (kind) {
			case DEFAULT :
				for (ASTNode varId : varIds.children()) {
					// get actual declaration for private variables
					actualVarId = ((IdentifierExpressionNode) varId)
							.getIdentifier();
					actualVarDecl = (VariableDeclarationNode) ((Variable) actualVarId
							.getEntity()).getFirstDeclaration();
					// create dummy declaration for private variables
					pvtVarId = nodeIdent(srcMethod, actualVarId.name());
					pvtVarType = actualVarDecl.getTypeNode().copy();
					pvtVarDecl = nodeFactory.newVariableDeclarationNode(//
							declSrc, pvtVarId, pvtVarType);
					// ADD: private variable declarations with same names
					privateVarDecls.get(0).add(pvtVarDecl);
				}
				break;
			case FIRST :
				// ADD: local variable declarations for each firstprivate ones
				// with a temporary variable transferring the value from its
				// original variable to the newly declared local one.
				// E.g.,
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				// int n = n_val;
				// int *p = p_val;
				// #pragma omp parallel firstprivate(n, p)
				// { ..
				// }
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				// int n = n_val;
				// int* p = p_val;
				// int _omp_fstpvt_n = n; // get the value of the outer 'n'
				// int* _omp_fstpvt_p = p; // get the value of the outer 'p'
				// $parfor ( .. )
				// { ..
				// int n = _omp_fstpvt_n; // assign the outer 'n' to inner 'n'
				// int* p = _omp_fstpvt_p; // assign the outer 'p' to inner 'p'
				// ..
				// }
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				for (ASTNode varId : varIds.children()) {
					// get actual declaration for private variables
					actualVarId = ((IdentifierExpressionNode) varId)
							.getIdentifier();
					actualVarDecl = (VariableDeclarationNode) ((Variable) actualVarId
							.getEntity()).getFirstDeclaration();
					// create dummy declaration for private variables
					pvtVarName = actualVarId.name();
					tmpVarName = FIRSTPRIVATE_ + pvtVarName;
					pvtVarId = nodeIdent(srcMethod, pvtVarName);
					pvtVarType = actualVarDecl.getTypeNode().copy();
					pvtVarDecl = nodeFactory.newVariableDeclarationNode(//
							declSrc, pvtVarId, pvtVarType,
							nodeExprId(srcMethod, tmpVarName));
					// create dummy declaration for temporary variables
					tmpVarId = nodeIdent(srcMethod, tmpVarName);
					tmpVarType = pvtVarType.copy();
					tmpVarDecl = nodeFactory.newVariableDeclarationNode(//
							declSrc, tmpVarId, tmpVarType,
							nodeExprId(srcMethod, pvtVarName));
					// ADD: private variable declarations with same names
					privateVarDecls.get(INDEX_PVT_DECLS).add(pvtVarDecl);
					privateVarDecls.get(INDEX_TMP_DECLS).add(tmpVarDecl);
				}
				break;
			case LAST :
				// ADD: local variable declarations for each lastprivate ones
				// with a temporary variable transferring the value from its
				// original variable to the newly declared local one.
				// E.g.,
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				// int n = n_val;
				// int *p = p_val;
				// #pragma omp parallel firstprivate(n, p)
				// { ..
				// }
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				// int n = n_val;
				// int* p = p_val;
				// int* _omp_lstpvt_n = &n; // get the value of the outer 'n'
				// int** _omp_lstpvt_p = &p; // get the value of the outer 'p'
				// int _omp_lstpvt_i = MIN;
				// $parfor ( .. )
				// { ..
				// int n = *_omp_lstpvt_n; // assign the outer 'n' to inner 'n'
				// int* p = *_omp_lstpvt_p; // assign the outer 'p' to inner 'p'
				// ..
				// acquire
				// if (i > _omp_loop_i) {
				// *_omp_lstpvt_n = n;
				// *_omp_lstpvt_p = p;
				// }
				// release
				// }
				// ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
				for (ASTNode varId : varIds.children()) {
					hasLastPrivate = true;
					// get actual declaration for private variables
					actualVarId = ((IdentifierExpressionNode) varId)
							.getIdentifier();
					actualVarDecl = (VariableDeclarationNode) ((Variable) actualVarId
							.getEntity()).getFirstDeclaration();
					// create dummy declaration for private variables
					pvtVarName = actualVarId.name();
					tmpVarName = LASTPRIVATE_ + pvtVarName;
					pvtVarId = nodeIdent(srcMethod, pvtVarName);
					pvtVarType = actualVarDecl.getTypeNode().copy();
					pvtVarDecl = nodeFactory.newVariableDeclarationNode(//
							declSrc, pvtVarId, pvtVarType);
					// create dummy declaration for temporary variables
					tmpVarId = nodeIdent(srcMethod, tmpVarName);
					tmpVarType = pvtVarType.copy();
					tmpVarType = nodeFactory.newPointerTypeNode(declSrc,
							tmpVarType);
					tmpVarDecl = nodeFactory.newVariableDeclarationNode(//
							declSrc, tmpVarId, tmpVarType,
							nodeFactory.newOperatorNode(declSrc,
									Operator.ADDRESSOF,
									nodeExprId(srcMethod, pvtVarName)));
					// ADD: private variable declarations with same names
					privateVarDecls.get(INDEX_PVT_DECLS).add(pvtVarDecl);
					privateVarDecls.get(INDEX_TMP_DECLS).add(tmpVarDecl);
					// Add lastprivate assignments
					ExpressionNode lhsExpr = nodeFactory.newOperatorNode(
							declSrc, Operator.DEREFERENCE,
							nodeExprId(srcMethod, tmpVarName));
					ExpressionNode rhsExpr = nodeExprId(srcMethod, pvtVarName);

					lstpvtAssignments.add(
							nodeFactory.newExpressionStatementNode(nodeFactory
									.newOperatorNode(declSrc, Operator.ASSIGN,
											lhsExpr, rhsExpr)));
				}
				lstpvtAssignments.add(nodeFactory.newExpressionStatementNode(
						nodeFactory.newOperatorNode(declSrc, Operator.ASSIGN,
								nodeExprId(srcMethod, THREAD_LAST_ITER),
								nodeExprInt(srcMethod, -1))));
				break;
			case THREAD :
				assert false;
		}
		return privateVarDecls;
	}

	/**
	 * Extract lb, b, and incr values for OpenMP loop from the associated
	 * canonical {@link ForLoopNode} <br>
	 * <strong>PRE-CONDITION</strong>: the given node <code>forLoop</code> shall
	 * be a strict canonical <code>for</code> loop. (See OpenMP 5.0 Sec.
	 * 2.9.1)<br>
	 * <strong>POST-CONDITION</strong>: the lower bound (the first ) of the
	 * range triple is always less than the upper bound (the second). So, for
	 * decreasing loop variable, the range is from b to lb.
	 * 
	 * @param forLoop
	 *                    A CIVL-AST node representing a canonical for loop.
	 * @return A triple of {@link ASTNode} representing a range's lower bound,
	 *         upper bound and step
	 */
	private OmpLoopInfo extractLoopInfo(ForLoopNode forLoop)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".extractLoopInfo";
		Source exprSrc = newSource(srcMethod, CivlcTokenConstant.EXPR);
		ExpressionNode one = nodeFactory.newIntegerConstantNode(exprSrc, "1");
		ForLoopInitializerNode initExpr = forLoop.getInitializer();
		ExpressionNode testExpr = forLoop.getCondition();
		ExpressionNode incrExpr = forLoop.getIncrementer();
		IdentifierNode var = null;
		ExpressionNode lb = null, b = null, incr = null;
		OperatorNode expr = null;
		ExpressionNode lhsExpr = null, rhsExpr = null;
		boolean isOpenBound = false;

		// GET: var (loop variable) and lb (the initial value of var)
		if (initExpr instanceof OperatorNode) {
			expr = (OperatorNode) initExpr;
			if (expr.getOperator() == Operator.ASSIGN) {
				// var = lb
				lhsExpr = expr.getArgument(0);
				rhsExpr = expr.getArgument(1);
				var = ((IdentifierExpressionNode) lhsExpr).getIdentifier();
				lb = rhsExpr;
			} // else non-canonical init-expr
		} else if (initExpr instanceof DeclarationListNode) {
			// type var = lb
			VariableDeclarationNode decl = (VariableDeclarationNode) initExpr
					.child(0);

			var = decl.getIdentifier();
			lb = (ExpressionNode) decl.getInitializer();
		}
		if (var == null || lb == null)
			throw new CIVLSyntaxException(
					"Non-canonical OpenMP loop init-expr.");

		// GET: b (the bound value of var)
		if (testExpr instanceof OperatorNode) {
			expr = (OperatorNode) testExpr;
			switch (expr.getOperator()) {
				case GT :
				case LT :
					isOpenBound = true;
				case LTE :
				case GTE :
				case NEQ :
					lhsExpr = expr.getArgument(0);
					rhsExpr = expr.getArgument(1);
					if (isSameVarEntity(lhsExpr, var))
						// var rel-op b
						b = rhsExpr;
					else if (isSameVarEntity(rhsExpr, var))
						// b rel-op var
						b = lhsExpr;
				default :
			}
		}
		if (b == null)
			throw new CIVLSyntaxException(
					"Non-canonical OpenMP loop test-expr.");

		// GET: incr (the step value of var)
		if (incrExpr instanceof OperatorNode) {
			expr = (OperatorNode) incrExpr;
			if (expr.getOperator() == Operator.ASSIGN) {
				// var = var + incr
				// var = incr + var
				// var = var - incr
				incrExpr = expr.getArgument(1);
				if (incrExpr instanceof OperatorNode)
					expr = (OperatorNode) incrExpr;
				else
					throw new CIVLSyntaxException(
							"Non-canonical OpenMP loop incr-expr.");
			}
			if (expr.getNumberOfArguments() == 2) {
				lhsExpr = expr.getArgument(0);
				rhsExpr = expr.getArgument(1);
				if (isSameVarEntity(lhsExpr, var))
					// var + incr | var - incr
					// var += incr | var -= incr
					incr = rhsExpr;
				else if (isSameVarEntity(rhsExpr, var))
					// incr + var
					incr = lhsExpr;
			} else if (expr.getNumberOfArguments() == 1)
				// ++va | var++ | --var | var--
				incr = one;
		}
		if (incr == null)
			throw new CIVLSyntaxException(
					"Non-canonical OpenMP loop incr-expr.");
		lb = lb.copy();
		b = b.copy();
		incr = incr.copy();
		// CREATE: triple<rangeLower, rangeUpper, rangeStep>
		switch (expr.getOperator()) {
			case PLUS : // var + incr | incr + var
			case PLUSEQ : // var += incr
			case PREINCREMENT : // ++var
			case POSTINCREMENT : // var++
				// open test bound b is decreased by 1
				if (isOpenBound)
					b = nodeFactory.newOperatorNode(b.getSource(),
							Operator.MINUS, b, one.copy());
				return new OmpLoopInfo(var.name(), new Triple<>(lb, b, incr));
			case MINUS : // var - incr
			case MINUSEQ : // var -= incr
			case PREDECREMENT : // --var
			case POSTDECREMENT : // var--
				// negate incr
				incr = nodeFactory.newOperatorNode(incr.getSource(),
						Operator.UNARYMINUS, incr);
				// open test bound b is increased by 1
				if (isOpenBound)
					b = nodeFactory.newOperatorNode(b.getSource(),
							Operator.PLUS, b, one.copy());
				// b < lb, so range should be [b, lb]
				return new OmpLoopInfo(var.name(), new Triple<>(b, lb, incr));
			default :
				throw new CIVLSyntaxException(
						"Non-canonical OpenMP loop incr-expr.");
		}
	}

	/**
	 * 
	 * @param srcMethod
	 * @param isParallel
	 * @param domainVarName
	 * @param loopVarDecls
	 * @param loopBodyItems
	 * @return
	 */
	private CivlForNode genCivlFor(String srcMethod, boolean isParallel,
			String domainVarName, List<VariableDeclarationNode> loopVarDecls,
			List<BlockItemNode> loopBodyItems) {
		// create CIVL loop var. decl.: int loopVar1, .. , loopVarX
		ForLoopInitializerNode loopInit = nodeFactory.newForLoopInitializerNode(
				newSource(srcMethod, CivlcTokenConstant.INITIALIZER_LIST),
				loopVarDecls);

		// if (isParallel): $parfor(int _omp_tid : _omp_domain) { .. }
		// else: $for(int loopVar1, .. loopVarX : _omp_loop_dist) { .. }
		return nodeFactory.newCivlForNode(
				newSource(srcMethod, CivlcTokenConstant.CIVLFOR), isParallel,
				(DeclarationListNode) loopInit,
				nodeExprId(srcMethod, domainVarName),
				nodeBlock(srcMethod, loopBodyItems), null);
	}

	/**
	 * Return <code>true</code> iff <code>sourceFile</code> indicating that the
	 * corresponding node is imported from library source files including: <br>
	 * *.cvh, *.h (except for stdio.h), civlc.cvl, concurrency.cvl, omp.cvl,
	 * pthread.cvl, stdio.cvl, string.cvl
	 * 
	 * @param sourceFileName
	 *                           the name of a source file.
	 * @return see above.
	 */
	private boolean isImported(String sourceFileName) {
		return sourceFileName.endsWith(".cvh")
				|| sourceFileName.equals("civlc.cvl")
				|| sourceFileName.equals("concurrency.cvl")
				|| sourceFileName.equals("omp.cvl")
				|| sourceFileName.equals("pthread.cvl")
				|| sourceFileName.equals("stdio.cvl")
				|| sourceFileName.equals("string.cvl")
				|| (sourceFileName.endsWith(".h"));
	}

	/**
	 * Return <code>true</code> iff the {@link IdentifierNode} wrapped by
	 * <code>varExpr</code> refers to a same variable entity identified by
	 * <code>varId</code>.
	 * 
	 * @param varExpr
	 *                    A variable expression holding a single variable
	 * @param varId
	 *                    An identifier of a variable
	 * @return See above.
	 */
	private boolean isSameVarEntity(ExpressionNode varExpr,
			IdentifierNode varId) {
		return varExpr instanceof IdentifierExpressionNode
				&& ((IdentifierExpressionNode) varExpr).getIdentifier()
						.getEntity().equals(varId.getEntity());
	}

	/** @return {@link ExpressionNode} for omp reduction init val */
	private ExpressionNode nodeExprReductionInit(String srcMethod,
			OmpReductionOperator reductionOp, TypeNode type) {
		// TODO: an initial value shall comply with its bonding types
		switch (type.kind()) {
			case BASIC :
				switch (reductionOp) {
					case MAX : // TYPE.MIN_VALUE
						return nodeExprInt(srcMethod, Integer.MIN_VALUE);
					case MIN : // TYPE.MAX_VALUE
						return nodeExprInt(srcMethod, Integer.MAX_VALUE);
					case BAND : // ~0
						return nodeExprInt(srcMethod, ~0);
					case LAND : // true (or !0)
					case EQV : // true (or !0)
					case PROD : // 1
						return nodeExprInt(srcMethod, 1);
					case LOR : // false (or 0)
					case NEQ : // false (or 0)
					case SUM : // 0
					case MINUS :// 0
					case BOR : // 0
					case BXOR : // 0
						return nodeExprInt(srcMethod, 0);
					default :
						throw new CIVLUnimplementedFeatureException(
								"Unsupported OpenMP reduction operator: "
										+ reductionOp);
				}
			case ARRAY :
			case POINTER :
			default :
				throw new CIVLUnimplementedFeatureException("Unsupported type: "
						+ type + " for OpenMP reduction operator: "
						+ reductionOp);

		}
	}

	/**
	 * Returns a list of {@link BlockItemNode}s representing that the given
	 * <code>nodes</code> wrapped as a protected interleave block tagged with
	 * <code>blockName</code> as follow:<br>
	 * <code>$omp_atomic_execution_lock_acquire(team, &blockName)</code><br>
	 * <code>{nodes...} // e.g., x += 1;</code><br>
	 * <code>$omp_atomic_execution_lock_release(team, &blockName);</code><br>
	 * 
	 * @param blockName
	 *                      the CIVL's Omp helper signal name associated with
	 *                      given <code>stmt</code>
	 * @param nodes
	 *                      the list of {@link BlockItemNode} shall be wrapped.
	 * @return see above.
	 * @throws SyntaxException
	 */
	private List<BlockItemNode> wrapProtectedStmts(String blockName,
			List<BlockItemNode> nodes) {
		String srcMethod = SRC_INFO + ".wrapProtectedStmts";
		ArrayList<BlockItemNode> wrappedNodes = new ArrayList<>();

		// ADD: $omp_atomic_execution_lock_acquire(team, &blockName);
		wrappedNodes.add(callLockAcquire(srcMethod, blockName));
		// ADD: nodes
		wrappedNodes.addAll(nodes);
		// ADD: $omp_atomic_execution_lock_release(team, &blockName);
		wrappedNodes.add(callLockRelease(srcMethod, blockName));
		return wrappedNodes;
	}

	private void procOmpBarrierNode(OmpSyncNode ompBarrierNode) {
		String srcMethod = SRC_INFO + ".procOmpBarrierNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();

		// TRANS: $omp_barrier(team);
		ompBlockItems.add(callOmpBarrier(srcMethod));
		replaceOmpNode(srcMethod, ompBarrierNode, ompBlockItems);
	}

	/**
	 * yield => check => wait(0) => send(1) => block => send(0)
	 * 
	 * @param ompCriticalNode
	 * @throws SyntaxException
	 */
	private void procOmpCriticalNode(OmpSyncNode ompCriticalNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".procOmpCriticalNode";
		List<BlockItemNode> ompCriticalBlockNodes = new LinkedList<>();
		List<BlockItemNode> transformedOmpCritical = new LinkedList<>();
		IdentifierNode criticalNameId = ompCriticalNode.criticalName();
		StatementNode criticalBlock = ompCriticalNode.statementNode();
		String criticalName = null;

		if (criticalNameId == null) // Unspecified critical name
			criticalName = CRITICAL_ + NAME_CRITICAL_UNSPEC;
		else
			criticalName = CRITICAL_ + criticalNameId.name();
		// Store encountered critical names for declaring them
		if (!criticalNames.contains(criticalName))
			criticalNames.add(criticalName);
		searchOmpInstructions(criticalBlock);
		criticalBlock.remove();
		ompCriticalBlockNodes.add(criticalBlock);
		transformedOmpCritical.addAll(
				wrapProtectedStmts(criticalName, ompCriticalBlockNodes));
		replaceOmpNode(srcMethod, ompCriticalNode, transformedOmpCritical);
	}

	/**
	 * Perform transformation on OpenMP Parallel Region (see:
	 * https://vsl.cis.udel.edu/trac/civl/wiki/OpenMPTransformation#Translatingfor
	 * )
	 * 
	 * @param ompForNode
	 * @throws SyntaxException
	 */
	private void procOmpForNode(OmpForNode ompForNode) throws SyntaxException {
		// TODO: lastprivate clause
		String srcMethod = SRC_INFO + ".procOmpForNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();
		List<BlockItemNode> lstpvtAssignments = new LinkedList<>();
		boolean isFortran = ompForNode.getSource().getFirstToken()
				.getSourceFile().getName().toLowerCase()
				.contains(FORTRAN_FILE_SUFFIX);
		// PROC: collapse
		int collapse = ompForNode.collapse();
		int numLoopRanges = 0;
		ArrayList<OmpLoopInfo> loopInfos = new ArrayList<>();
		ForLoopNode curLoop = (ForLoopNode) ompForNode.statementNode();
		OmpLoopInfo curLoopInfo = extractLoopInfo(curLoop);

		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.LOOP));
		loopInfos.add(curLoopInfo);
		for (int i = 1; i < collapse; i++) {
			curLoop = (ForLoopNode) curLoop.getBody();
			curLoopInfo = extractLoopInfo(curLoop);
			loopInfos.add(curLoopInfo);
		}
		bindingLoopInfosRecords.push(loopInfos);

		// ADD: $read_set_push();
		// ADD: $write_set_push();
		// TODO: shouold be push and then pop to omit
		// but collecting domian_decomp will cause CIVL internal error.
		ompBlockItems.addAll(callRWSetPush(srcMethod));
		// ADD: $range _omp_rangeX = {lo .. hi, step};
		// * note that X is [1 .. numLoopRanges]
		for (OmpLoopInfo info : loopInfos)
			ompBlockItems.add(declOmpRange(srcMethod, ++numLoopRanges,
					info.range, isFortran));
		assert numLoopRanges == loopInfos.size();
		// ADD: $domain(1) _omp_loop_domain = ($domain){_omp_range1, ...};
		ompBlockItems.add(declOmpDomain(srcMethod, numLoopRanges));
		// ADD: $domain(collapse) _omp_loop_dist = ($domain(collapse))
		// $omp_arrive_loop(_omp_team, loop_id++, _omp_loop_domain, STRATEGY);
		ompBlockItems.add(declOmpDistLoop(srcMethod, collapse));

		// PROC: shared, private and firstprivate variabe list.
		// NOTE: an item can appear in both firstprivate and last private.
		List<List<VariableDeclarationNode>> pvtDeclsList = declVarsPrivate(
				srcMethod, ompForNode.privateList(), lstpvtAssignments,
				PrivateKind.DEFAULT);
		List<List<VariableDeclarationNode>> fstpvtDeclsList = declVarsPrivate(
				srcMethod, ompForNode.firstprivateList(), lstpvtAssignments,
				PrivateKind.FIRST);
		List<List<VariableDeclarationNode>> lstpvtDeclsList = declVarsPrivate(
				srcMethod, ompForNode.firstprivateList(), lstpvtAssignments,
				PrivateKind.LAST);
		List<List<BlockItemNode>> rdcItemsList = transOmpReduction(
				ompForNode.reductionList());

		// ADD: dummy decl. for pvt. var.
		ompBlockItems.addAll(pvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: temp. decl. for holding val. of pvt.1st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: dummy decl. for pvt.st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: temp. decl. for holding val. of pvt.1st var.
		ompBlockItems.addAll(lstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: dummy decl. for pvt.st var.
		ompBlockItems.addAll(lstpvtDeclsList.get(INDEX_PVT_DECLS));
		// dummy decl. and init. for reduction items
		ompBlockItems.addAll(rdcItemsList.get(INDEX_RDC_INITS));

		// ADD: $read_set_pop();
		// ADD: $write_set_pop();
		ompBlockItems.addAll(callRWSetPop(srcMethod));
		// TRANS: OMP loop Region -> CIVL $for construct
		List<BlockItemNode> cvlForBodyItems = new LinkedList<>();
		StatementNode loopBody = null;

		int numOrderedLoops = ompForNode.ordered();
		// IF ordered, declare order counter variables in global scope

		if (numOrderedLoops > 1) // ordered(X), unsupported
			throw new CIVLUnimplementedFeatureException(
					"'ordered' clause with parameters for "
							+ "'doacross loop nest' feature (in OpenMP 4.5)");
		else if (numOrderedLoops == 1) {
			// ADD: int /loop_var/_next = /loop_var/ + /incr_expr/;
			cvlForBodyItems.addAll(declOmpLoopVarNext(srcMethod, loopInfos));
			// global decl:
			// $omp_helper_signal ordered_0 =
			// $omp_helper_signal_create(init_expr);
			declOmpOrderedSignals(srcMethod, loopInfos);
		}
		// else // NO ordered clause
		// process and transfer all other children
		searchOmpInstructions(curLoop);
		// get processed body
		loopBody = curLoop.getBody();
		// transfer into civl $for loop body
		if (loopBody instanceof CompoundStatementNode)
			for (ASTNode child : loopBody.children()) {
				child.remove();
				cvlForBodyItems.add((BlockItemNode) child);
			}
		else {
			loopBody.remove();
			cvlForBodyItems.add(loopBody);
		}
		if (hasLastPrivate) {
			List<BlockItemNode> lstpvtProcess = new ArrayList<>();

			// push
			lstpvtProcess.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_PUSH,
					nodeExprId(srcMethod, TEAM)));
			// if (THREAD_LAST_ITER < i) {
			// THREAD_LAST_ITER = i;
			// THREAD_LAST = TID;
			// }
			lstpvtAssignments.add(nodeFactory
					.newExpressionStatementNode(nodeFactory.newOperatorNode(
							newSource(srcMethod, CivlcTokenConstant.EXPR),
							Operator.ASSIGN,
							nodeExprId(srcMethod, THREAD_LAST_ITER),
							nodeExprId(srcMethod, LOOP_ITER))));
			lstpvtAssignments.add(nodeFactory
					.newExpressionStatementNode(nodeFactory.newOperatorNode(
							newSource(srcMethod, CivlcTokenConstant.EXPR),
							Operator.ASSIGN, nodeExprId(srcMethod, THREAD_LAST),
							nodeExprId(srcMethod, TID))));
			lstpvtProcess.add(nodeFactory.newIfNode(
					newSource(srcMethod, CivlcTokenConstant.IF),
					nodeFactory.newOperatorNode(
							newSource(srcMethod, CivlcTokenConstant.EXPR),
							Operator.LTE,
							nodeExprId(srcMethod, THREAD_LAST_ITER),
							nodeExprId(srcMethod, LOOP_ITER)),
					nodeBlock(srcMethod, lstpvtAssignments)));
			// pop
			lstpvtProcess.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_POP,
					nodeExprId(srcMethod, TEAM)));
			cvlForBodyItems.addAll(lstpvtProcess);
		}
		ompBlockItems.add(genCivlFor(//
				srcMethod, false, /* domName */ LOOP_DIST,
				/* loopVarDecls */ declVarsLoopInit(srcMethod, loopInfos),
				/* loopBodyItems */ cvlForBodyItems));
		// dummy decl. and init. for reduction items
		ompBlockItems.addAll(rdcItemsList.get(INDEX_RDC_COMBS));
		if (!ompForNode.nowait())
			// ADD: $omp_barrier(team);
			ompBlockItems.add(callOmpBarrier(srcMethod));

		// TRANS: replace parallel region with transformed block
		replaceOmpNode(srcMethod, ompForNode, ompBlockItems);
		bindingLoopInfosRecords.pop();
		ompRgn.pop();
	}

	/**
	 * 
	 * @param ompMasterNode
	 * @throws SyntaxException
	 */
	private void procOmpMasterNode(OmpSyncNode ompMasterNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".procOmpMasterNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();

		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.MASTER));
		// TRANS: recursively transforms child nodes.
		searchOmpInstructions(ompMasterNode);

		// TRANS: adds the condition ' _omp_tid == 0 ' to
		// the associated block so that exactly one thread executes it.
		StatementNode masterBody = ompMasterNode.statementNode();
		// cond_expr: _omp_tid == 0
		ExpressionNode condExpr = nodeFactory.newOperatorNode(
				newSource(srcMethod, CivlcTokenConstant.EXPR), Operator.EQUALS,
				nodeExprId(srcMethod, TID),
				nodeExprInt(srcMethod, ID_MASTER_THREAD));

		masterBody.remove();
		ompBlockItems.add(nodeFactory.newIfNode(
				newSource(srcMethod, CivlcTokenConstant.STATEMENT), condExpr,
				masterBody));
		// TRANS: replace master construct with transformed block
		replaceOmpNode(srcMethod, ompMasterNode, ompBlockItems);
		ompRgn.pop();
	}

	private void procOmpOrderedNode(OmpSyncNode ompOrderedNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".procOmpOrderedNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();

		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.MASTER));
		// TRANS: recursively transforms child nodes.
		searchOmpInstructions(ompOrderedNode);

		StatementNode orderedBody = ompOrderedNode.statementNode();

		orderedBody.remove();
		if (orderConcurrent)
			// Omp Std. 2.9.2 Worksharing loop: Pg. 104 Ln. 10-12
			// If an order(concurrent) clause is present, ..
			// the iterations may be executed in any order,
			// including concurrently
			// Thus, pragma 'omp ordered' is omitted.
			// ADD: the block associated with ordered clause
			ompBlockItems.add(orderedBody);
		else {
			ompBlockItems.addAll(transOmpOrdered(srcMethod,
					bindingLoopInfosRecords.peek(), orderedBody));
		}
		// TRANS: replace master construct with transformed block
		replaceOmpNode(srcMethod, ompOrderedNode, ompBlockItems);
		ompRgn.pop();
	}

	/**
	 * 
	 * @param srcMethod
	 * @param loopInfos
	 * @param orderedBody
	 * @return
	 */
	private List<BlockItemNode> transOmpOrdered(String srcMethod,
			List<OmpLoopInfo> loopInfos, StatementNode orderedBody) {
		Source exprSrc = newSource(srcMethod, CivlcTokenConstant.EXPR);
		Source stmtSrc = newSource(srcMethod, CivlcTokenConstant.STATEMENT);
		List<BlockItemNode> orderedBodyItems = new LinkedList<>();
		int numOrderedCtr = loopInfos.size();
		int orderId = ctrOmpOrdered - numOrderedCtr;
		int infoIdx = 0;
		OmpLoopInfo info = loopInfos.get(infoIdx);
		ExpressionNode loopVarExpr = nodeExprId(srcMethod, info.loopVarName);
		ExpressionNode loopVarNextExpr = nodeExprId(srcMethod,
				info.loopVarName + _NEXT);
		ExpressionNode orderedCtrExpr = nodeExprId(srcMethod,
				ORDERED + orderId);
		ExpressionNode addrOfOrderedCtrExpr = nodeFactory.newOperatorNode(
				newSource(srcMethod, CivlcTokenConstant.EXPR),
				Operator.ADDRESSOF, Arrays.asList(orderedCtrExpr));
		StatementNode sendNext = nodeStmtCall(srcMethod, OMP_HELPER_SIGNAL_SEND,
				addrOfOrderedCtrExpr, loopVarNextExpr);
		StatementNode sendInit = null;
		ExpressionNode condExpr = null;

		// ADD: pop
		orderedBodyItems.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_POP,
				nodeExprId(srcMethod, TEAM)));
		// ADD: check
		orderedBodyItems.add(nodeStmtCall(srcMethod, CHECK_DATA_RACE,
				nodeExprId(srcMethod, TEAM)));
		// ADD: $yield();
		orderedBodyItems.add(nodeStmtCall(srcMethod, YIELD));
		// ADD: $omp_helper_signal_wait(_omp_order_counter0, /loop_var/);
		orderedBodyItems.add(nodeStmtCall(srcMethod, OMP_HELPER_SIGNAL_WAIT,
				addrOfOrderedCtrExpr.copy(), loopVarExpr));
		for (infoIdx = 1; infoIdx < numOrderedCtr; infoIdx++) {
			orderId++;
			info = loopInfos.get(infoIdx);
			loopVarNextExpr = nodeExprId(srcMethod, info.loopVarName + _NEXT);
			loopVarExpr = nodeExprId(srcMethod, info.loopVarName);
			orderedCtrExpr = nodeExprId(srcMethod, ORDERED + orderId);
			addrOfOrderedCtrExpr = nodeFactory.newOperatorNode(
					newSource(srcMethod, CivlcTokenConstant.EXPR),
					Operator.ADDRESSOF, Arrays.asList(orderedCtrExpr));
			// ADD: $omp_helper_signal_wait(_omp_order_counterX, /loop_var/);
			orderedBodyItems.add(nodeStmtCall(srcMethod, OMP_HELPER_SIGNAL_WAIT,
					addrOfOrderedCtrExpr.copy(), loopVarExpr));
			// GEN: $omp_helper_signal_send calls
			// $omp_helper_signal_send(_omp_order_counterX, init);
			sendInit = nodeStmtCall(srcMethod, OMP_HELPER_SIGNAL_SEND,
					addrOfOrderedCtrExpr.copy(), info.range.first.copy());
			// { $send(_omp_order_counter/X/, /inner_loop_var_init/);
			// $send(_omp_order_counter/X-1/, /outer_loop_var_next/);
			// }
			sendInit = nodeBlock(srcMethod, sendInit, sendNext);
			// $send(_omp_order_counter/X/, /inner_loop_var_next/);
			sendNext = nodeStmtCall(srcMethod, OMP_HELPER_SIGNAL_SEND,
					addrOfOrderedCtrExpr, loopVarNextExpr);
			// /inner_loop_var_next/ < /inner_loop_var_bound/
			condExpr = nodeFactory.newOperatorNode(exprSrc, Operator.LT,
					loopVarNextExpr.copy(), info.range.second.copy());
			// if (/inner_loop_var_next/ < /inner_loop_var_bound/)
			// $send(_omp_order_counter/X/, /inner_loop_var_next/);
			// else {
			// $send(_omp_order_counter/X/, /inner_loop_var_init/);
			// $send(_omp_order_counter/X-1/, /outer_loop_var_next/);
			// }
			// NOTE:
			// if the next iteration logical number 'j_next' of an inner
			// loop variable 'j' is less than its bound,
			// THEN: signal_send(signal_/X/, j_next) to issue the
			// execution of current iteration's sequentially next one.
			// ELSE: reset the current inner loop variable by
			// signal_send(signal_/X/, j_init) and increase the outer
			// loop variable 'i' by signal_send(signal_/X-1/, i_next).
			sendNext = nodeFactory.newIfNode(stmtSrc, condExpr, sendNext,
					sendInit);
		}
		// ADD: push
		orderedBodyItems.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_PUSH,
				nodeExprId(srcMethod, TEAM)));
		orderedBodyItems.add(orderedBody);
		// ADD: pop
		orderedBodyItems.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_POP,
				nodeExprId(srcMethod, TEAM)));
		// ADD: check
		orderedBodyItems.add(nodeStmtCall(srcMethod, CHECK_DATA_RACE,
				nodeExprId(srcMethod, TEAM)));
		// ADD: $yield();
		orderedBodyItems.add(nodeStmtCall(srcMethod, YIELD));
		// ADD: release
		orderedBodyItems.add(sendNext);
		// ADD: push
		orderedBodyItems.add(nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_PUSH,
				nodeExprId(srcMethod, TEAM)));
		return orderedBodyItems;
	}

	/**
	 * Perform transformation on OpenMP Parallel Region (see:
	 * https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingparallel
	 * )
	 * 
	 * @param ompParallelNode
	 * @throws SyntaxException
	 */
	private void procOmpParallelNode(OmpParallelNode ompParallelNode)
			throws SyntaxException {
		// TODO: num_threads clause
		String srcMethod = SRC_INFO + ".procOmpParallelNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();
		List<BlockItemNode> lstpvtAssignments = new LinkedList<>();

		ompOrphanFuncs.init();
		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.PARALLEL));
		// levelParallel += 1;
		// PROC: _omp_num_threads
		ExpressionNode _omp_num_threads = ompParallelNode.numThreads();
		// PROC: shared, private and firstprivate variabe list.
		// NOTE: an item can appear in both firstprivate and last private.
		List<List<VariableDeclarationNode>> pvtDeclsList = declVarsPrivate(
				srcMethod, ompParallelNode.privateList(), lstpvtAssignments,
				PrivateKind.DEFAULT);
		List<List<VariableDeclarationNode>> fstpvtDeclsList = declVarsPrivate(
				srcMethod, ompParallelNode.firstprivateList(),
				lstpvtAssignments, PrivateKind.FIRST);
		List<List<VariableDeclarationNode>> lstpvtDeclsList = declVarsPrivate(
				srcMethod, ompParallelNode.lastprivateList(), lstpvtAssignments,
				PrivateKind.LAST);
		List<List<BlockItemNode>> rdcItemsList = transOmpReduction(
				ompParallelNode.reductionList());
		Boolean hasExplicitNumThreadsClause = _omp_num_threads != null;

		if (hasExplicitNumThreadsClause)
			_omp_num_threads.remove();
		else // If absent
			_omp_num_threads = nodeExprId(srcMethod, _OMP_ + "num_threads");
		ompBlockItems.add(elaborateExpression(_omp_num_threads).copy());
		// ADD: int _omp_nthreads = 1+$choose_int(_omp_num_threads);
		ompBlockItems.add(declOmpNthreads(srcMethod, _omp_num_threads,
				hasExplicitNumThreadsClause));
		// ADD: temporary variable declarations for firstprivate variables
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: temporary variable declarations for lastprivate variables
		ompBlockItems.addAll(lstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: $range _omp_thread_range = {0 .. _omp_nthreads-1};
		ompBlockItems.add(declOmpThreadRange(srcMethod));
		// ADD: $domain(1) dom = ($domain){thread_range};
		ompBlockItems.add(declOmpDomain(srcMethod, 0)); // 0 for parallel region
		// ADD: $omp_gteam gteam = $omp_gteam_create($here, nthreads);
		ompBlockItems.add(declOmpGteam(srcMethod));

		// TRANS: OMP Parallel Region -> CIVL $parfor construct
		List<BlockItemNode> parForBodyItems = new LinkedList<>();
		StatementNode bodyStatement = null;

		// ADD to $parfor:
		// $local_start();
		parForBodyItems.add(nodeStmtCall(srcMethod, LOCAL_START));
		// $omp_team _omp_team = $omp_team_create($here, _omp_gteam, _omp_tid);
		parForBodyItems.add(declOmpTeam(srcMethod));
		// reduction items: dummy decl. and init.
		parForBodyItems.addAll(rdcItemsList.get(INDEX_RDC_INITS));
		// private items: dummy decl. for pvt. var.
		parForBodyItems.addAll(pvtDeclsList.get(INDEX_PVT_DECLS));
		// first private items: dummy decl. and init. for pvt. var.
		parForBodyItems.addAll(fstpvtDeclsList.get(INDEX_PVT_DECLS));
		// first private items: dummy decl. and init. for pvt. var.
		parForBodyItems.addAll(lstpvtDeclsList.get(INDEX_PVT_DECLS));
		// $read_set_push();
		// $write_set_push();
		parForBodyItems.addAll(callRWSetPush(srcMethod));
		// DEPRECATED: transformation for shared variables, due to R/W set
		// process and transfer all other children
		searchOmpInstructions(ompParallelNode);
		bodyStatement = ompParallelNode.statementNode();
		bodyStatement.remove();
		parForBodyItems.add(bodyStatement);
		// TODO: reduction impl. in civl-omp.cvl should push-red-pop
		// dummy decl. and init. for reduction items
		parForBodyItems.addAll(rdcItemsList.get(INDEX_RDC_COMBS));
		// $omp_barrier(team);
		parForBodyItems.add(callOmpBarrier(srcMethod));
		// $read_set_pop();
		// $write_set_pop();
		parForBodyItems.addAll(callRWSetPop(srcMethod));
		if (lstpvtAssignments.size() != 0) {
			parForBodyItems.add(nodeFactory.newIfNode(
					newSource(srcMethod, CivlcTokenConstant.IF),
					nodeFactory.newOperatorNode(
							newSource(srcMethod, CivlcTokenConstant.EQUALS),
							Operator.EQUALS, nodeExprId(srcMethod, THREAD_LAST),
							nodeExprId(srcMethod, TID)),
					nodeBlock(srcMethod, lstpvtAssignments)));
			hasLastPrivate = false;
		}

		// ADD: data race checking on the termination of each thread.
		parForBodyItems.add(nodeStmtCall(srcMethod, CHECK_DATA_RACE,
				nodeExprId(srcMethod, TEAM)));
		// $omp_team_destroy(team);
		parForBodyItems.add(nodeStmtCall(srcMethod, OMP_TEAM_DESTROY,
				nodeExprId(srcMethod, TEAM)));
		// $local_end();
		parForBodyItems.add(nodeStmtCall(srcMethod, LOCAL_END));

		// Get Orphan functions
		// ExpressionNode ompTeamNode = nodeExprId(srcMethod, TEAM);

		for (BlockItemNode n : parForBodyItems) {
			ompOrphanFuncs.searchOmpOrphanFunctions(n);
		}
		for (FunctionDefinitionNode n : ompOrphanFuncs.getOrphanFuncDefs()) {
			// SequenceNode<VariableDeclarationNode> nParams = n.getTypeNode()
			// .getParameters();
			// int indexParam = nParams.numChildren();
			// VariableDeclarationNode ompTeamParamNode = nodeDeclVar(srcMethod,
			// TEAM, nodeTypeNamed(srcMethod, "$omp_team"));

			// nParams.setSequenceChild(indexParam, ompTeamParamNode);
			// ompOrphanFuncs.updateOmpOrphanFunctions(n, ompTeamNode);
			parForBodyItems.add(2, n.copy());
		}
		// for (BlockItemNode n : parForBodyItems) {
		// ompOrphanFuncs.updateOmpOrphanFunctions(n, ompTeamNode);
		// }

		// ADD: creates of OpenMP helper signals used by this transformer
		ompBlockItems.addAll(signalCreates);
		// ADD: $parfor (int _omp_tid : _omp_dom) { .. }
		ompBlockItems
				.add(genCivlFor(srcMethod, true, /* domName */ DOM,
						/* loopVarDecls */ Arrays.asList(//
								nodeDeclVarInt(srcMethod, TID)),
						parForBodyItems));
		// ADD: $omp_gteam_destroy(gteam);
		ompBlockItems.add(nodeStmtCall(srcMethod, OMP_GTEAM_DESTROY,
				nodeExprId(srcMethod, GTEAM)));
		// TRANS: replace parallel region with transformed block
		replaceOmpNode(srcMethod, ompParallelNode, ompBlockItems);
		ompRgn.pop();
	}

	private void procOmpSectionsNode(OmpWorksharingNode ompSectionsNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".procOmpSectionsNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();
		List<BlockItemNode> lstpvtAssignments = new LinkedList<>();

		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.SECTIONS));

		// get each section block in the current sections construct
		List<StatementNode> sectionBlocks = new LinkedList<>();
		// PROC: shared, private and firstprivate variabe list.
		// NOTE: an item can appear in both firstprivate and last private.
		List<List<VariableDeclarationNode>> pvtDeclsList = declVarsPrivate(
				srcMethod, ompSectionsNode.privateList(), lstpvtAssignments,
				PrivateKind.DEFAULT);
		List<List<VariableDeclarationNode>> fstpvtDeclsList = declVarsPrivate(
				srcMethod, ompSectionsNode.firstprivateList(),
				lstpvtAssignments, PrivateKind.FIRST);
		// PROC: analysis the number of section constructs
		StatementNode sectionsItems = ompSectionsNode.statementNode();
		int numItems = sectionsItems.numChildren();
		int idx = 0;
		ASTNode item = null;

		// Check the first item
		item = sectionsItems.child(idx++);
		if (item instanceof OmpWorksharingNode)
			// explicit section block
			sectionBlocks.add(((OmpWorksharingNode) item).statementNode());
		else
			// implicit section block
			sectionBlocks.add((StatementNode) item);
		while (idx < numItems) {
			item = sectionsItems.child(idx++);
			if (item instanceof OmpWorksharingNode)
				sectionBlocks.add(((OmpWorksharingNode) item).statementNode());
			else
				throw new CIVLSyntaxException(
						"Non-section item in OpenMP sections construct.");
		}
		// ADD: $read_set_push();
		// ADD: $write_set_push();
		ompBlockItems.addAll(callRWSetPush(srcMethod));
		// ADD: $domain(1) _omp_sections_dist = ($domain(1))
		// $omp_arrive_sections(_omp_team, section_id++, numSection);
		ompBlockItems.add(declOmpDistSections(srcMethod, sectionBlocks.size()));
		// ADD: dummy decl. for pvt. var.
		ompBlockItems.addAll(pvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: temp. decl. for holding val. of pvt.1st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: dummy decl. for pvt.1st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: $read_set_pop();
		// ADD: $write_set_pop();
		ompBlockItems.addAll(callRWSetPop(srcMethod));

		// TRANS: OMP sections Region -> CIVL $for construct
		// ADD:
		// $for(int _omp_sid : _omp_sections_dist) {
		// if (_omp_sid == 1) { BLOCK1 }
		// ..
		// }
		// all section blocks are recursively processed in 'transOmpSection'
		ompBlockItems.add(genCivlFor(//
				srcMethod, false, /* domName */ SECTIONS_DIST,
				/* loopVarDecls */ Arrays.asList(//
						nodeDeclVarInt(srcMethod, SID)),
				/* loopBodyItems */ transOmpSection(sectionBlocks)));
		if (!ompSectionsNode.nowait())
			// ADD: $omp_barrier(team);
			ompBlockItems.add(callOmpBarrier(srcMethod));
		// TRANS: replace sections region with transformed block
		replaceOmpNode(srcMethod, ompSectionsNode, ompBlockItems);
		ompRgn.pop();
	}

	private void procOmpSingleNode(OmpWorksharingNode ompSingleNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".procOmpSingleNode";
		List<BlockItemNode> ompBlockItems = new LinkedList<>();
		List<BlockItemNode> lstpvtAssignments = new LinkedList<>();

		// PRE: Record the current OpenMP region info
		ompRgn.push(new OmpRegion(OmpRgnKind.SINGLE));

		List<List<VariableDeclarationNode>> pvtDeclsList = declVarsPrivate(
				srcMethod, ompSingleNode.privateList(), lstpvtAssignments,
				PrivateKind.DEFAULT);
		List<List<VariableDeclarationNode>> fstpvtDeclsList = declVarsPrivate(
				srcMethod, ompSingleNode.firstprivateList(), lstpvtAssignments,
				PrivateKind.FIRST);

		// TODO: copypvt clause, copypvt
		assert ompSingleNode.copyprivateList() == null;
		assert ompSingleNode.copyinList() == null;

		// ADD: $read_set_push();
		// ADD: $write_set_push();
		ompBlockItems.addAll(callRWSetPush(srcMethod));
		// ADD: int _omp_single_dist = $omp_arrive_single(team, single_id++);
		ompBlockItems.add(declOmpDistSingle(srcMethod));
		// ADD: dummy decl. for pvt. var.
		ompBlockItems.addAll(pvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: temp. decl. for holding val. of pvt.1st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_TMP_DECLS));
		// ADD: dummy decl. for pvt.1st var.
		ompBlockItems.addAll(fstpvtDeclsList.get(INDEX_PVT_DECLS));
		// ADD: $read_set_pop();
		// ADD: $write_set_pop();
		ompBlockItems.addAll(callRWSetPop(srcMethod));
		// TRANS: recursively transforms child nodes.
		searchOmpInstructions(ompSingleNode);

		// TRANS: adds the condition ' _omp_tid == _omp_single_dist ' to
		// the associated block so that exactly one thread executes it.
		StatementNode singleBody = ompSingleNode.statementNode();
		// cond_expr: _omp_tid == _omp_single_dist
		ExpressionNode condExpr = nodeFactory.newOperatorNode(
				newSource(srcMethod, CivlcTokenConstant.EXPR), Operator.EQUALS,
				nodeExprId(srcMethod, TID), nodeExprId(srcMethod, SINGLE_DIST));

		singleBody.remove();
		ompBlockItems.add(nodeFactory.newIfNode(
				newSource(srcMethod, CivlcTokenConstant.STATEMENT), condExpr,
				singleBody));
		if (!ompSingleNode.nowait())
			// ADD: $omp_barrier(team);
			ompBlockItems.add(callOmpBarrier(srcMethod));
		// TRANS: replace single construct with transformed block
		replaceOmpNode(srcMethod, ompSingleNode, ompBlockItems);
		ompRgn.pop();
	}

	private void recognizeOmpFunctionCalls(FunctionCallNode ompFunctionCallNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".recognizeOmpFunctionCalls: ";
		ExpressionNode functionExpr = ompFunctionCallNode.getFunction();
		ExpressionNode transformedFunctionCall = null;
		StatementNode functionCallStmt = null;
		List<BlockItemNode> wrappedFunctionCall = new LinkedList<>();

		if (functionExpr instanceof IdentifierExpressionNode) {
			String funcName = ((IdentifierExpressionNode) functionExpr)
					.getIdentifier().name();

			switch (funcName) {
				case OMP_GET_MAX_THREADS :
					transformedFunctionCall = nodeExprId(
							srcMethod + OMP_GET_MAX_THREADS, THREAD_MAX);
					break;
				case OMP_GET_NUM_PROCS :
					transformedFunctionCall = nodeExprInt(
							srcMethod + OMP_GET_NUM_PROCS, 1);
					break;
				case OMP_GET_NUM_THREADS :
					transformedFunctionCall = nodeExprId(
							srcMethod + OMP_GET_MAX_THREADS, NTHREADS);
					break;
				case OMP_GET_THREAD_NUM :
					transformedFunctionCall = nodeExprId(
							srcMethod + OMP_GET_MAX_THREADS, TID);
					break;
				case OMP_SET_NUM_THREADS :
					transformedFunctionCall = nodeFactory.newOperatorNode(
							newSource(srcMethod, CivlcTokenConstant.EXPR),
							Operator.ASSIGN, nodeExprId(srcMethod, NUM_THREADS),
							ompFunctionCallNode.getArgument(0).copy());
					break;
				// The following four are directly implemented in omp.cvl
				// case OMP_INIT_LOCK :
				// case OMP_DESTROY_LOCK :
				// case OMP_DESTROY_NEST_LOCK :
				// case OMP_DESTROY_NEST_LOCK :
				// The following six OpenMP lock function is translated as:
				// 1. add '$' as a prefix
				// 2. add a second argument, which is '_omp_tid'
				case OMP_SET_LOCK :
				case OMP_SET_NEST_LOCK :
					functionCallStmt = nodeStmtCall(srcMethod,
							SIGN_DOLLAR + funcName,
							ompFunctionCallNode.getArgument(0).copy(),
							nodeExprId(srcMethod, TID));

					// ADD: pop
					wrappedFunctionCall.add(
							nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_POP,
									nodeExprId(srcMethod, TEAM)));
					// ADD: check
					wrappedFunctionCall.add(nodeStmtCall(srcMethod,
							CHECK_DATA_RACE, nodeExprId(srcMethod, TEAM)));
					// ADD: yield and then set lock
					wrappedFunctionCall.add(functionCallStmt);
					// ADD: push
					wrappedFunctionCall.add(
							nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_PUSH,
									nodeExprId(srcMethod, TEAM)));
					break;
				case OMP_UNSET_LOCK :
				case OMP_UNSET_NEST_LOCK :
					functionCallStmt = nodeStmtCall(srcMethod,
							SIGN_DOLLAR + funcName,
							ompFunctionCallNode.getArgument(0).copy(),
							nodeExprId(srcMethod, TID));
					// ADD: pop
					wrappedFunctionCall.add(
							nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_POP,
									nodeExprId(srcMethod, TEAM)));
					// ADD: check
					wrappedFunctionCall.add(nodeStmtCall(srcMethod,
							CHECK_DATA_RACE, nodeExprId(srcMethod, TEAM)));
					// ADD: yield
					wrappedFunctionCall.add(nodeStmtCall(srcMethod, YIELD));
					// release
					wrappedFunctionCall.add(functionCallStmt);
					// push
					wrappedFunctionCall.add(
							nodeStmtCall(srcMethod, READ_AND_WRITE_SETS_PUSH,
									nodeExprId(srcMethod, TEAM)));
					break;
				case OMP_TEST_LOCK :
				case OMP_TEST_NEST_LOCK :
					transformedFunctionCall = nodeExprCall(srcMethod,
							SIGN_DOLLAR + funcName,
							ompFunctionCallNode.getArgument(0).copy(),
							nodeExprId(srcMethod, TID));
					break;
				default :
					// do nothing
			}
			if (transformedFunctionCall != null)
				ompFunctionCallNode.parent().setChild(
						ompFunctionCallNode.childIndex(),
						transformedFunctionCall);
			else if (functionCallStmt != null) {
				ompFunctionCallNode.parent().parent().setChild(
						ompFunctionCallNode.parent().childIndex(),
						nodeBlock(srcMethod, wrappedFunctionCall));
			}
			// Check args of function call statement
			searchOmpInstructions(ompFunctionCallNode);
		}
	}

	private void recognizeOmpInstructions(OmpNode ompNode)
			throws SyntaxException {
		switch (ompNode.ompNodeKind()) {
			case EXECUTABLE :
				OmpExecutableNode ompExecNode = (OmpExecutableNode) ompNode;

				switch (ompExecNode.ompExecutableKind()) {
					case PARALLEL :
						procOmpParallelNode((OmpParallelNode) ompExecNode);
						return;
					case SIMD :
						assert false;
					case SYNCHRONIZATION :
						OmpSyncNode ompSyncNode = (OmpSyncNode) ompExecNode;

						switch (ompSyncNode.ompSyncNodeKind()) {
							case MASTER :
								procOmpMasterNode(ompSyncNode);
								return;
							case CRITICAL :
								procOmpCriticalNode(ompSyncNode);
								return;
							case BARRIER :
								procOmpBarrierNode(ompSyncNode);
								return;
							case FLUSH : // Omitted
								ompSyncNode.remove();
								return;
							case ORDERED :
								procOmpOrderedNode(ompSyncNode);
								return;
							case OMPATOMIC :
								procOmpAtomicNode((OmpAtomicNode) ompSyncNode);
								return;
						}
					case WORKSHARING :
						OmpWorksharingNode ompWorkSNode = (OmpWorksharingNode) ompExecNode;

						switch (ompWorkSNode.ompWorkshareNodeKind()) {
							case SECTIONS :
								procOmpSectionsNode(ompWorkSNode);
								return;
							// case SECTION : processed in 'procOmpSectionsNode'
							case SINGLE :
								procOmpSingleNode(ompWorkSNode);
								return;
							case FOR :
								procOmpForNode((OmpForNode) ompWorkSNode);
								return;
							default :
								assert false;
						}
				}
			case DECLARATIVE :
				OmpDeclarativeNode ompDeclNode = (OmpDeclarativeNode) ompNode;

				assert false;
				switch (ompDeclNode.ompDeclarativeNodeKind()) {
					case REDUCTION :
					case SIMD :
					case TARGET :
					case THREADPRIVATE :
				}
		}
		// DFS: recursively search for OmpNode among successors of this OmpNode
		searchOmpInstructions(ompNode);
	}

	private final static int MAX_EXPR_IN_OMP_ATOMIC_BLOCK = 2;
	private final static String DUMMY_ATOMIC_VAR_PREFIX = "$dummy_atomic_var_";
	// private final static String DUMMY_ATOMIC_VAL_PREFIX =
	// "$dummy_atomic_val_";
	private long dummyAtomicVarCtr = 0;
	// private long dummyAtomicValCtr = 0;

	private void procOmpAtomicNode(OmpAtomicNode ompAtomicNode)
			throws SyntaxException {
		String srcMethod = SRC_INFO + ".transOmpAtomicNode";
		List<BlockItemNode> transformedOmpAtomicNodes = new ArrayList<>();
		List<BlockItemNode> ompBlockNodes = new ArrayList<>();
		StatementNode atomicStmt = ompAtomicNode.statementNode();
		ExpressionNode atomicExpr[] = new ExpressionNode[MAX_EXPR_IN_OMP_ATOMIC_BLOCK];

		if (atomicStmt instanceof CompoundStatementNode
				&& atomicStmt.numChildren() <= MAX_EXPR_IN_OMP_ATOMIC_BLOCK) {
			CompoundStatementNode block = ((CompoundStatementNode) atomicStmt);

			for (int i = 0; i < block.numChildren(); i++) {
				ASTNode stmt = block.child(i);

				if (stmt instanceof ExpressionStatementNode) {
					atomicExpr[i] = ((ExpressionStatementNode) stmt)
							.getExpression();
				} else {
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), stmt);
				}
			}
		} else if (atomicStmt instanceof ExpressionStatementNode) {
			atomicExpr[0] = ((ExpressionStatementNode) atomicStmt)
					.getExpression();
		} else {
			throwIllegalOmpAtomicExprPatternException(
					ompAtomicNode.atomicClause(), atomicStmt);
		}
		switch (ompAtomicNode.atomicClause()) {
			case CAPTURE :
				// !$OMP atomic capture
				// v = x OP; or v = OP x;
				// or v = x; x = x binOP update_expr;
				// or x = x binOP update_expr; v = x;
				// ->
				// T atomic_capture_dummy_var_1 = v;
				// T atomic_capture_dummy_val_1 = update_expr;
				// update; yield; acquire;
				// {atomic_capture_dummy_var_1 = x OP;}
				// or {atomic_capture_dummy_var_1 = OP x;}
				// or {atomic_capture_dummy_var_1 = x; x = x OP
				// atomic_capture_dummy_val_1;}
				// or {x = x OP atomic_capture_dummy_val_1;
				// atomic_capture_dummy_var_1 = x; }
				// update; yield; check_datarace; clear; release;
				if (!verifyOmpAtomicCapture(atomicExpr))
					throw new CIVLSyntaxException(
							"Illegal pattern for the body statement associated "
									+ "with OpenMP atomic CAPTURE construct: "
									+ atomicStmt.prettyRepresentation());
				break;
			case READ :
				// !$OMP atomic read
				// v = x;
				// ->
				// T atomic_read_dummy_var_N;
				// update; yield; acquire;
				// {atomic_read_dummy_var_N = x;}
				// update; yield; check_datarace; clear; release;
				// v = atomic_read_dummy_var_N;
				ExpressionNode rExprNode = atomicExpr[0];
				List<ExpressionNode> rArgs = analyzeExprAssignScalar(rExprNode);

				if (!(rExprNode instanceof OperatorNode) || rArgs.size() != 2)
					// The read expr should have exactly two operands for the
					// assignment op.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), rExprNode);

				Operator rOp = ((OperatorNode) rExprNode).getOperator();
				ExpressionNode rv = rArgs.get(0);
				ExpressionNode rx = rArgs.get(1);

				if (rOp != Operator.ASSIGN || !rv.isLvalue() || !rx.isLvalue()
						|| !verifyExprSeparatedEntity(rx, rv))
					// both v and x shall be l-vals and separated in the memory.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), rExprNode);

				// T dummy_atomic_var_N;
				String dummyRVarName = DUMMY_ATOMIC_VAR_PREFIX
						+ dummyAtomicVarCtr++;
				TypeNode dummyRTypeNode = nodeTypeFromExpr(srcMethod, rv);
				VariableDeclarationNode dummyRVarDeclNode = nodeDeclVar(
						srcMethod, dummyRVarName, dummyRTypeNode);

				transformedOmpAtomicNodes.add(dummyRVarDeclNode);
				// Proc ompBlockNodes
				// v = x; -> dummy_atomic_var_N = x;
				ExpressionNode newRExprNode = nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.EXPR),
						Operator.ASSIGN, nodeExprId(srcMethod, dummyRVarName),
						rx.copy());

				ompBlockNodes.add(
						nodeFactory.newExpressionStatementNode(newRExprNode));
				// Wrap ompBlockNodes;
				transformedOmpAtomicNodes
						.addAll(wrapProtectedStmts(ATOMIC_, ompBlockNodes));
				// v = atomic_read_dummy_var_N;
				ExpressionNode dummyRExpr = nodeExprId(srcMethod,
						dummyRVarName);
				ExpressionNode postOmpAtomicRExpr = nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.EXPR),
						Operator.ASSIGN, Arrays.asList(rv.copy(), dummyRExpr));
				StatementNode postOmpAtomicReadStmt = nodeFactory
						.newExpressionStatementNode(postOmpAtomicRExpr);

				transformedOmpAtomicNodes.add(postOmpAtomicReadStmt);
				break;
			case UPDATE :
				// !$OMP atomic update
				// x OP= update_expr;
				// ->
				// T atomic_update_dummy_val_N = update_expr;
				// update; yield; acquire;
				// {x OP= atomic_update_dummy_val_N;}
				// update; yield; check_datarace; clear; release;
				ExpressionNode uExprNode = atomicExpr[0];
				List<ExpressionNode> uArgs = analyzeExprAssignScalar(uExprNode);

				if (uArgs.size() > 3)
					// The read expr should have exactly two operands for the
					// assignment op.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), uExprNode);

				ExpressionNode ux = uArgs.get(0);
				ExpressionNode uExpr = nodeFactory.newIntConstantNode(newSource(
						srcMethod, CivlcTokenConstant.INTEGER_CONSTANT), 1);

				if (uArgs.size() > 1)
					uExpr = uArgs.get(uArgs.size() - 1);

				if (!ux.isLvalue() || !verifyExprSeparatedEntity(ux, uExpr))
					// both v and x shall be l-vals and separated in the memory.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), uExprNode);

				// T dummy_atomic_var_N = expr;
				String dummyUVarName = DUMMY_ATOMIC_VAR_PREFIX
						+ dummyAtomicVarCtr++;
				TypeNode dummyUTypeNode = nodeTypeFromExpr(srcMethod, uExpr);
				VariableDeclarationNode dummyUVarDeclNode = nodeDeclVarInit(
						srcMethod, dummyUVarName, dummyUTypeNode, uExpr.copy());

				transformedOmpAtomicNodes.add(dummyUVarDeclNode);

				// Proc ompBlockNodes
				// x = v; -> x = dummy_atomic_var_N;
				ExpressionNode newUExprNode = nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.EXPR),
						((OperatorNode) uExprNode).getOperator(), ux.copy(),
						nodeExprId(srcMethod, dummyUVarName));

				ompBlockNodes.add(
						nodeFactory.newExpressionStatementNode(newUExprNode));
				// Wrap ompBlockNodes;
				transformedOmpAtomicNodes
						.addAll(wrapProtectedStmts(ATOMIC_, ompBlockNodes));
				break;
			case WRITE :
				// !$OMP atomic write
				// x = expr;
				// ->
				// type dummy_atomic_var_N = expr;
				// update; yield; acquire;
				// {x = dummy_atomic_var_N;}
				// update; yield; check_datarace; clear; release;

				ExpressionNode wExprNode = atomicExpr[0];
				List<ExpressionNode> wArgs = analyzeExprAssignScalar(wExprNode);

				if (!(wExprNode instanceof OperatorNode) || wArgs.size() != 2)
					// The read expr should have exactly two operands for the
					// assignment op.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), wExprNode);

				Operator wOp = ((OperatorNode) wExprNode).getOperator();
				ExpressionNode wx = wArgs.get(0);
				ExpressionNode wExpr = wArgs.get(1);

				if (wOp != Operator.ASSIGN || !wx.isLvalue()
						|| !verifyExprSeparatedEntity(wx, wExpr))
					// x shall be l-vals and separated in the memory.
					throwIllegalOmpAtomicExprPatternException(
							ompAtomicNode.atomicClause(), wExprNode);

				// T dummy_atomic_var_N = v;
				String dummyWVarName = DUMMY_ATOMIC_VAR_PREFIX
						+ dummyAtomicVarCtr++;
				TypeNode dummyWTypeNode = nodeTypeFromExpr(srcMethod, wExpr);
				VariableDeclarationNode dummyWVarDeclNode = nodeDeclVarInit(
						srcMethod, dummyWVarName, dummyWTypeNode, wExpr.copy());

				transformedOmpAtomicNodes.add(dummyWVarDeclNode);
				// Proc ompBlockNodes
				// x = v; -> x = dummy_atomic_var_N;
				ExpressionNode newWExprNode = nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.EXPR),
						Operator.ASSIGN, wx.copy(),
						nodeExprId(srcMethod, dummyWVarName));

				ompBlockNodes.add(
						nodeFactory.newExpressionStatementNode(newWExprNode));
				// Wrap ompBlockNodes;
				transformedOmpAtomicNodes
						.addAll(wrapProtectedStmts(ATOMIC_, ompBlockNodes));
				break;
			default :
				assert false;

		}
		// searchOmpInstructions(atomicStmt);
		// atomicStmt.remove();
		hasAtomicConstruct = true;
		replaceOmpNode(srcMethod, ompAtomicNode, transformedOmpAtomicNodes);
	}

	/**
	 * Replace the processed {@link OmpNode} <code>ompNode</code> with a list of
	 * {@link BlockItemNode} transformed.
	 * 
	 * @param srcMethod
	 *                       Dummy {@link Source} information based on caller
	 *                       name
	 * @param ompNode
	 *                       The processed {@link OmpNode}
	 * @param blockItems
	 *                       A list of {@link BlockItemNode} representing
	 *                       behaviors peformed by the processed OpenMP pragma
	 *                       and its associated block constructs.
	 */
	private void replaceOmpNode(String srcMethod, OmpNode ompNode,
			List<BlockItemNode> blockItems) {
		ASTNode parent = ompNode.parent();
		int indexOld = ompNode.childIndex();

		parent.setChild(indexOld, nodeBlock(srcMethod, blockItems));
	}

	/**
	 * Perform recursive DFS for searching {@link OmpNode}s among successors of
	 * the given {@link ASTNode} <code>root</code>. If an {@link OmpNode} is
	 * found, then it is processed and this function is applied for continuing
	 * exploring its successors
	 * 
	 * @param root
	 *                 a {@link ASTNode}, if it is an {@link OmpNode} then it
	 *                 must be processed by <code>this</code> transformer.
	 * @throws SyntaxException
	 */
	private void searchOmpInstructions(ASTNode root) throws SyntaxException {
		// DFS: recursively search for OmpNode
		for (ASTNode child : root.children()) {
			if (child == null)
				continue;
			else if (child instanceof OmpNode) // recognize and process OmpNode
				recognizeOmpInstructions((OmpNode) child);
			else if (child instanceof FunctionCallNode)
				recognizeOmpFunctionCalls((FunctionCallNode) child);
			else // Explore non null omp node
				searchOmpInstructions(child);
		}
	}

	private List<List<BlockItemNode>> transOmpReduction(
			SequenceNode<OmpReductionNode> reductionClauses) {
		String srcMethod = SRC_INFO + ".transOmpReduction";
		Source ptrSrc = newSource(srcMethod, CivlcTokenConstant.POINTER);
		Source exprSrc = newSource(srcMethod, CivlcTokenConstant.EXPR);
		List<BlockItemNode> varPtrDecls = new LinkedList<>();
		List<BlockItemNode> varTmpDecls = new LinkedList<>();
		List<BlockItemNode> varRdcInits = new LinkedList<>();
		List<BlockItemNode> initialItems = new LinkedList<>();
		List<BlockItemNode> combineItems = new LinkedList<>();
		Set<String> rcVarDeclName = new HashSet<>();
		OmpSymbolReductionNode symbRc = null;
		OmpReductionOperator rcOp = null;

		hasReductionConstruct = true;
		if (reductionClauses == null)
			return Arrays.asList(initialItems, combineItems);
		for (OmpReductionNode rc : reductionClauses) {
			// Trans each reduction clause
			symbRc = (OmpSymbolReductionNode) rc;
			rcOp = symbRc.operator();

			for (IdentifierExpressionNode rcVar : symbRc.variables()) {
				String vName = rcVar.getIdentifier().name();
				String vpName = REDUCTION_ + ctrOmpReductionItem + "_" + vName;
				TypeNode vType = ((VariableDeclarationNode) ( //
				(Variable) rcVar.getIdentifier().getEntity())
						.getFirstDeclaration()).getTypeNode().copy();
				TypeNode vpType = nodeFactory.newPointerTypeNode( //
						ptrSrc, vType.copy());
				ExpressionNode vpInit = nodeFactory.newOperatorNode(exprSrc,
						Operator.ADDRESSOF, nodeExprId(srcMethod, vName));
				// type *_omp_reduction_x_var = &var;
				VariableDeclarationNode vpDecl = nodeDeclVarInit(srcMethod,
						vpName, vpType, vpInit);
				// type var;
				VariableDeclarationNode vDecl = nodeDeclVar(srcMethod, vName,
						vType);
				// var = [omp_priv];
				ExpressionNode varInitExpr = nodeFactory.newOperatorNode(
						exprSrc, Operator.ASSIGN, nodeExprId(srcMethod, vName),
						nodeExprReductionInit(srcMethod, rcOp, vType));
				ExpressionStatementNode varInitStmt = nodeFactory
						.newExpressionStatementNode(varInitExpr);
				// $omp_rdc(OPERATOR, VAR_PTR, VAR_TMP);
				ExpressionNode addrOfVExprNode = nodeFactory.newOperatorNode(
						newSource(srcMethod, CivlcTokenConstant.EXPR),
						Operator.ADDRESSOF,
						Arrays.asList(nodeExprId(srcMethod, vName)));
				StatementNode combineItem = nodeStmtCall(srcMethod,
						OMP_REDUCTION_COMBINE,
						nodeExprInt(srcMethod, rcOp.civlOp()),
						nodeExprId(srcMethod, vpName), addrOfVExprNode);

				varPtrDecls.add(vpDecl);
				if (!rcVarDeclName.contains(vName)) {
					varTmpDecls.add(vDecl);
					rcVarDeclName.add(vName);
				} else
					throw new CIVLSyntaxException("Reduction item identifier "
							+ vName + "shall appear only once in data clause.");
				varRdcInits.add(varInitStmt);
				combineItems.addAll(wrapProtectedStmts(REDUCTION_,
						Arrays.asList(combineItem)));
			}
			ctrOmpReductionItem++;
		}
		initialItems.addAll(varPtrDecls);
		initialItems.addAll(varTmpDecls);
		initialItems.addAll(varRdcInits);
		return Arrays.asList(initialItems, combineItems);
	}

	/**
	 * Return a list of {@link BlockItemNode} transformed from each of
	 * structured-blocks<br>
	 * (see:
	 * https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation )
	 * 
	 * @param sectionBlocks
	 *                          a list of {@link StatementNode} representing
	 *                          each associated section block
	 * @return see above
	 * @throws SyntaxException
	 */
	private List<BlockItemNode> transOmpSection(
			List<StatementNode> sectionBlocks) throws SyntaxException {
		// sections {
		// [section] { BLOCK0 }
		// [section { BLCOK1 }] ..
		// }
		/* **** **** is transformed to **** **** */
		// $for(int _omp_sid : _omp_sec_dist) {
		// if (_omp_sid==0) { BLOCK0 }
		// if (_omp_sid==1) { BLCOK1 } ..
		// }
		String srcMethod = SRC_INFO + ".transOmpSection";
		Source exprSrc = newSource(srcMethod, CivlcTokenConstant.EXPR);
		Source ifSrc = newSource(srcMethod, CivlcTokenConstant.STATEMENT);
		List<BlockItemNode> sections = new LinkedList<>();
		ExpressionNode condExpr = null;
		int sectionId = 0;

		for (StatementNode block : sectionBlocks) {
			// _omp_sid == N
			condExpr = nodeFactory.newOperatorNode(exprSrc, Operator.EQUALS,
					nodeExprId(srcMethod, SID),
					nodeExprInt(srcMethod, sectionId++));
			searchOmpInstructions(block);
			block.remove();
			// if (_omp_sid == N) { BLOCK_N }
			sections.add(nodeFactory.newIfNode(ifSrc, condExpr, block));
		}
		return sections;
	}

	/**
	 * Returns <code>true</code> iff the storage location designated by both
	 * <code>expr0</code> and <code>expr1</code> is a same location. (i.e.,
	 * <code>expr1</code> accesses the exact storage location (entity)
	 * designated by <code>expr0</code>); else returns <code>false</code>
	 * 
	 * @param expr0
	 *                  an expression designating a storage location
	 * @param expr1
	 *                  an expression designating a storage location
	 * @return
	 */
	private boolean verifyExprSameEntity(ExpressionNode expr0,
			ExpressionNode expr1) {
		boolean areBothLValues = expr0.isLvalue() && expr1.isLvalue();
		// TODO: Implement the verification as described:
		// TBD: verifies that for both expr0 and expr1
		// 1. they designate a exactly same storage location (i.e. Entity).
		Entity entity0 = null;
		Entity entity1 = null;

		// Both exor0 and expr1 are l-values.
		if (!areBothLValues)
			return false;
		if (expr0 instanceof IdentifierExpressionNode)
			entity0 = ((IdentifierExpressionNode) expr0).getIdentifier()
					.getEntity();
		if (expr1 instanceof IdentifierExpressionNode)
			entity1 = ((IdentifierExpressionNode) expr1).getIdentifier()
					.getEntity();
		if (entity0 != null && entity1 != null)
			return entity0.equals(entity1);
		return true;
	}

	/**
	 * Returns <code>true</code> iff the storage location designated by
	 * <code>expr0</code> is isolated from <code>expr1</code> (i.e.,
	 * <code>expr1</code> does not access the storage location (entity)
	 * designated by <code>expr0</code>); else returns <code>false</code>
	 * <p>
	 * <strong>NOTE</strong>: this function is <strong>NOT implemented
	 * yet</strong>, and will always return <code>true</code>
	 * </p>
	 * 
	 * @param expr0
	 *                  an l-value expression designating a storage location
	 * @param expr1
	 *                  any valid expression
	 * @return see above.
	 */
	private boolean verifyExprSeparatedEntity(ExpressionNode expr0,
			ExpressionNode expr1) {
		// TODO: Implement the verification as described:
		// TBD: verifies that for all OpenMP atomic constructs:
		// 1. Neither of 'v' and 'expr' (as applicable) may access the storage
		// location designated by 'x'. (OpenMP Std. 4.5.0: Sec. 2.17.7)
		// 2. Neither of 'x' and 'expr' (as applicable) may access the storage
		// location designated by 'v'. (OpenMP Std. 4.5.0: Sec. 2.17.7)
		// *. any two atomic constructs associated with a same storage location
		// shall have a same type on their 'x'. (OpenMP Eg. 4.5.0: Sec. 6.5)
		return expr0.isLvalue();
	}

	/**
	 * <p>
	 * Verify the pattern of <code>atomicCaptureExprs</code>, which is/are
	 * associated with an OpenMP <code>atomic</code> construct with
	 * <code>update</code> clause.
	 * </p>
	 * <p>
	 * The pattern shall be one of following:<br>
	 * <code>v = update_expr;</code><br>
	 * <code>v = x; update_expr;</code><br>
	 * <code>update_expr; v = x;</code><br>
	 * 1. <code>update_expr</code> can be verified as a 'legal' update
	 * expression. (i.e. <code>true</code> is returned as result by
	 * {@link #verifyOmpAtomicUpdate(update_expr)})<br>
	 * 2. Both <code>v</code> and <code>x</code> (including ones involved in
	 * <code>update_expr</code>) are l-values<br>
	 * 3. They designate separated storage locations (i.e., <code>v</code> and
	 * <code>x</code> shall not access a same storage location.) <br>
	 * 4. The storage location designated by either of <code>x</code> and
	 * <code>v</code> shall not be accessed by <code>expr</code> involved in
	 * <code>update_expr</code> (if it exists).
	 * </p>
	 * 
	 * @param atomicCaptureExprs
	 *                               expression(s) associated with an OpenMP
	 *                               atomic capture construct
	 * @return <code>true</code> iff the pattern of
	 *         <code>atomicCaptureExprs</code> is legal.
	 */
	private boolean verifyOmpAtomicCapture(
			ExpressionNode... atomicCaptureExprs) {
		// v = update_expr;
		// {v = x; update_expr;}
		// {update_expr; v = x;}
		ExpressionNode expr0 = atomicCaptureExprs[0];
		ExpressionNode expr1 = atomicCaptureExprs[1];
		boolean isSingleExpr = expr1 == null;

		// 1. a single expr
		if (isSingleExpr) {
			List<ExpressionNode> args = analyzeExprAssignScalar(expr0);
			boolean isScalarAssignmentExpr = args.size() == 2;

			if (isScalarAssignmentExpr) {
				ExpressionNode v = args.get(0);
				ExpressionNode update = args.get(1);

				return verifyOmpAtomicUpdate(update) && v.isLvalue()
						&& verifyExprSeparatedEntity(v, update);
			} else
				return false;
		}

		// 2. a block with two expr
		List<ExpressionNode> args0 = analyzeExprAssignScalar(expr0);
		List<ExpressionNode> args1 = analyzeExprAssignScalar(expr1);
		boolean isScalarAssignmentExpr0 = args0.size() == 2;
		boolean isScalarAssignmentExpr1 = args1.size() == 2;
		boolean isBinaryUpdate = false;
		ExpressionNode v = null;
		ExpressionNode x = null; // x in v = x
		ExpressionNode xLHS = null; // LHS x in update_expr
		ExpressionNode xRHS = null; // RHS x in update_Expr (may be null)
		ExpressionNode expr = null; // expr in update_expr (may be null)

		// Get 'v', 'x', and 'expr' (if it exists).
		if (isScalarAssignmentExpr0) {
			v = args0.get(0);
			x = args0.get(1);
			isBinaryUpdate = args1.size() == 3;
			if (isBinaryUpdate) {
				xLHS = args1.get(0);
				xRHS = args1.get(1);
				expr = args1.get(2);
			} else
				xLHS = args1.get(0);
		} else if (isScalarAssignmentExpr1) {
			v = args1.get(0);
			x = args1.get(1);
			isBinaryUpdate = args0.size() == 3;
			if (isBinaryUpdate) {
				xLHS = args0.get(0);
				xRHS = args0.get(1);
				expr = args0.get(2);
			} else
				xLHS = args1.get(0);
		} else // illegal: at least one scalar assignment
			return false;
		// Verify
		if (isBinaryUpdate)
			return v.isLvalue() && x.isLvalue() //
					&& verifyExprSameEntity(x, xLHS)
					&& verifyExprSameEntity(xLHS, xRHS)
					&& verifyExprSeparatedEntity(v, x)
					&& verifyExprSeparatedEntity(v, expr)
					&& verifyExprSeparatedEntity(x, expr);
		else
			return v.isLvalue() && x.isLvalue() //
					&& verifyExprSameEntity(x, xLHS)
					&& verifyExprSeparatedEntity(v, x);
	}

	/**
	 * <p>
	 * Verify the pattern of <code>atomicUpdateExpr</code>, which is associated
	 * with an OpenMP <code>atomic</code> construct with <code>update</code>
	 * clause.
	 * </p>
	 * <p>
	 * The pattern shall be one of following:<br>
	 * <code>x++;</code><br>
	 * <code>x--;</code><br>
	 * <code>++x;</code><br>
	 * <code>--x;</code><br>
	 * <code>x bin-op = expr;</code><br>
	 * <code>x = x bin-op expr;</code><br>
	 * <code>x = expr bin-op x;</code><br>
	 * 1. <code>x</code> is a l-value<br>
	 * 2. The storage location designated by <code>x</code> shall not be
	 * accessed by <code>expr</code> (if it exists).
	 * </p>
	 * 
	 * @param atomicUpdateExpr
	 *                             the expression associated with an OpenMP
	 *                             atomic update construct
	 * @return <code>true</code> iff the pattern of
	 *         <code>atomicUpdateExpr</code> is legal.
	 */
	private boolean verifyOmpAtomicUpdate(ExpressionNode atomicUpdateExpr) {
		// x++
		// x--
		// ++x
		// --x
		// x bin-op = expr
		// x = x binop expr
		// x = expr binop x
		List<ExpressionNode> args = analyzeExprAssignScalar(atomicUpdateExpr);
		boolean isUnaryScalarUpdate = args.size() == 1;
		boolean isBinaryScalarUpdate = args.size() == 3;

		if (isUnaryScalarUpdate) // args = {x}
			return args.get(0).isLvalue();
		else if (isBinaryScalarUpdate) {
			// args = {x, x, expr} or {x, x.copy, expr}
			ExpressionNode xLHS = args.get(0);
			ExpressionNode xRHS = args.get(1);

			return xLHS.isLvalue() && xRHS.isLvalue()
					&& verifyExprSameEntity(xLHS, xRHS)
					&& verifyExprSeparatedEntity(xLHS, args.get(2));
		}
		return false;
	}

	private void throwIllegalOmpAtomicExprPatternException(
			OmpAtomicClause clause, ASTNode node) {
		throw new CIVLSyntaxException(
				"Illegal pattern for the body statement associated "
						+ "with OpenMP atomic " + clause.name() + " construct: "
						+ node.prettyRepresentation());
	}

	/**
	 * Transform an AST of a OpenMP program in C into an equivalent AST of
	 * CIVL-C program.<br>
	 * 
	 * @param oldAst
	 *                   The AST of the original OpenMP program in C.
	 * @return An AST of CIVL-C program equivalent to the original OpenMP
	 *         program.
	 * @throws SyntaxException
	 */
	@Override
	protected AST transformCore(AST oldAst) throws SyntaxException {
		assert super.astFactory == oldAst.getASTFactory();
		assert super.nodeFactory == astFactory.getNodeFactory();
		// Check the inclusion of CIVL's OpenMP Implementation file.
		if (!super.hasHeader(oldAst, CIVLConstants.CIVL_OMP_SRC))
			return oldAst;
		root = oldAst.getRootNode();
		oldAst.release();

		String srcMethod = SRC_INFO;
		// A list holding all processed block items for building new AST.
		List<BlockItemNode> newItems = new LinkedList<>();
		List<BlockItemNode> oldItems = new LinkedList<>();
		List<BlockItemNode> importedItems = new LinkedList<>();
		List<BlockItemNode> declaredItems = new LinkedList<>();
		List<String> ioVarNames = new LinkedList<>();
		String srcFile = null;

		ompOrphanFuncs = new OmpOrphanFunctions(oldAst);
		// Search and Transform all OpenMP Nodes
		searchOmpInstructions((ASTNode) root);
		reduceDuplicateNode(root, PREDICATE_BARRIER_AND_FLUSH);
		for (BlockItemNode item : root) {
			if (item == null)
				continue;
			item.remove();
			srcFile = item.getSource().getFirstToken().getSourceFile()
					.getName();
			if (srcFile.equals("stdio.h")) {
				if (item.nodeKind() == NodeKind.VARIABLE_DECLARATION)
					oldItems.add(item);
				else
					importedItems.add(item);
			} else if (isImported(srcFile))
				// Extract items imported from library files.
				importedItems.add(item);
			else if (isRelatedAssumptionNode(item, ioVarNames))
				// Extract assumptions related with input/output var. decl.
				declaredItems.add(item);
			else if (item.nodeKind() == NodeKind.VARIABLE_DECLARATION) {
				VariableDeclarationNode varDecl = (VariableDeclarationNode) item;
				TypeNode varType = varDecl.getTypeNode();

				if (varType.isInputQualified() || varType.isOutputQualified()) {
					// Extract input/output var. decl.
					ioVarNames.add(varDecl.getName());
					declaredItems.add(item);
				} else
					oldItems.add(item);
			} else
				oldItems.add(item);
		}

		/* **** **** **** **** Build the new AST **** **** **** **** */
		// ADD: Nodes from header files
		newItems.addAll(importedItems);
		// ADD: Nodes of input/output var. dec. and their assumptions
		newItems.addAll(declaredItems);
		// ADD: $input int _omp_thread_max
		newItems.add(declOmpThreadMax(srcMethod));
		// ADD: int last_tid
		newItems.add(declOmpThreadLast(srcMethod));
		newItems.add(declOmpThreadLastIter(srcMethod));
		// ADD: generated global variables for verification
		newItems.addAll(globalVarDecls);
		// ADD: decls for OpenMP critical variables
		for (String signalNameCritical : criticalNames)
			newItems.add(declOmpHelperSignal(srcMethod, signalNameCritical,
					nodeExprInt(srcMethod, 0)));
		if (hasAtomicConstruct) {
			newItems.add(declOmpHelperSignal(srcMethod, ATOMIC_,
					nodeExprInt(srcMethod, 0)));
		}
		if (hasReductionConstruct) {
			newItems.add(declOmpHelperSignal(srcMethod, REDUCTION_,
					nodeExprInt(srcMethod, 0)));
		}
		// ADD: int omp_num_threads = _omp_thread_max;
		newItems.add(declOmpNumThreads(srcMethod));
		// ADD: transformed Civl AST nodes from the old AST.
		newItems.addAll(oldItems);
		// Remove: all origin orphan functions
		oldItems = newItems;
		newItems = new LinkedList<>();
		for (BlockItemNode n : oldItems) {
			if (!(n instanceof FunctionDefinitionNode) || !(ompOrphanFuncs
					.isOriginOmpOrphanFuncDef((FunctionDefinitionNode) n))) {
				newItems.add(n);
			}
		}

		// CREATE: a new AST from the old one by transforming all OpenMP nodes
		SequenceNode<BlockItemNode> newRoot = nodeFactory
				.newSequenceNode(root.getSource(), "Omp2CivlProgram", newItems);
		AST newAst = astFactory.newAST(newRoot, oldAst.getSourceFiles(),
				oldAst.isWholeProgram());

		// newAst.prettyPrint(System.out, true);
		return newAst;
	}
}

class OmpRegion {
	protected enum OmpRgnKind {
		PARALLEL, // parallel
		SECTIONS, SECTION, SINGLE, WORKSHARE, // workshare
		FOR, SIMD, SIMD_DECL, LOOP, // loop
		BARRIER, CRITICAL, ATOMIC, MASTER, ORDERED, // sync
	}

	OmpRgnKind ompRgns[];

	OmpRegion(OmpRgnKind... OmpRegions) {
		assert OmpRegions.length > 0;
		this.ompRgns = OmpRegions;
	}

	OmpRgnKind[] getOmpRegions() {
		return this.ompRgns;
	}
}

class OmpLoopInfo {
	String loopVarName;
	Triple<ExpressionNode, ExpressionNode, ExpressionNode> range;

	OmpLoopInfo(String varName,
			Triple<ExpressionNode, ExpressionNode, ExpressionNode> range) {
		this.loopVarName = varName;
		this.range = range;
	}
}

class OmpOrphanFunctions {
	AST ast;
	ArrayList<FunctionDefinitionNode> orphanFuncDefs;
	HashSet<String> searchedFuncNames;
	HashSet<FunctionDefinitionNode> allOrphanFuncDefs;

	OmpOrphanFunctions(AST ast) {
		this.ast = ast;
		this.allOrphanFuncDefs = new HashSet<>();
	}

	void init() {
		this.orphanFuncDefs = new ArrayList<>();
		this.searchedFuncNames = new HashSet<>();
	}

	boolean isOriginOmpOrphanFuncDef(FunctionDefinitionNode funcDefNode) {
		return allOrphanFuncDefs.contains(funcDefNode);
	}

	boolean searchOmpOrphanFunctions(ASTNode node) {
		boolean isOmpOrphanFunction = false;

		if (node == null)
			return false;
		if (node instanceof FunctionCallNode) {
			// 1. Check the validity of the function called.
			FunctionCallNode funcCall = (FunctionCallNode) node;
			ExpressionNode funcExpr = funcCall.getFunction();

			if (!(funcExpr instanceof IdentifierExpressionNode)) {
				String msg = "The transformation of function pointers in OpenMP orphan constructs.";

				throw new CIVLUnimplementedFeatureException(
						"The following feature is not yet implemented: " + msg);
			}

			// 2. Extract the function name to exclude duplicated ones.
			IdentifierNode funcIdent = ((IdentifierExpressionNode) funcExpr)
					.getIdentifier();
			Function orphanFunc = (Function) funcIdent.getEntity();
			String funcName = funcIdent.name();

			// 3. Check Omp Functions
			if (funcName.startsWith("omp_") || funcName.startsWith("$omp")) {
				return true;
			} else if (funcName.startsWith("$") || funcName.equals("assert")) {
				return false;
			}

			// 4. Check validity and duplication
			if (searchedFuncNames.contains(funcName) || orphanFunc == null
					|| orphanFunc.getDefinition() == null) {
				return false;
			}
			searchedFuncNames.add(funcName);

			// 4. Extract the function definition
			FunctionDefinitionNode orphanFuncDef = orphanFunc.getDefinition();
			CompoundStatementNode body = orphanFuncDef.getBody();
			Iterator<BlockItemNode> bodyIter = body.iterator();

			while (bodyIter.hasNext()) {
				boolean useOrphanFunction = searchOmpOrphanFunctions(
						bodyIter.next());

				isOmpOrphanFunction = useOrphanFunction || isOmpOrphanFunction
						|| node instanceof OmpWorksharingNode
						|| node instanceof OmpSyncNode;
			}
			if (isOmpOrphanFunction)
				orphanFuncDefs.add(0, orphanFuncDef);
		} else {
			for (ASTNode n : node.children()) {
				boolean useOrphanFunction = searchOmpOrphanFunctions(n);

				isOmpOrphanFunction = useOrphanFunction || isOmpOrphanFunction
						|| node instanceof OmpWorksharingNode
						|| node instanceof OmpSyncNode;
			}
		}
		return isOmpOrphanFunction;
	}

	ArrayList<FunctionDefinitionNode> getOrphanFuncDefs() {
		for (FunctionDefinitionNode defNode : orphanFuncDefs) {
			allOrphanFuncDefs.add(defNode);
		}
		return orphanFuncDefs;
	}
}