/*
* Copyright (c) 2011, IRISA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the IRISA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package net.sf.orcc.backends.transform;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.backends.ir.BlockFor;
import net.sf.orcc.backends.ir.IrSpecificFactory;
import net.sf.orcc.df.Actor;
import net.sf.orcc.df.util.DfVisitor;
import net.sf.orcc.ir.Block;
import net.sf.orcc.ir.BlockBasic;
import net.sf.orcc.ir.BlockIf;
import net.sf.orcc.ir.BlockWhile;
import net.sf.orcc.ir.CfgNode;
import net.sf.orcc.ir.Def;
import net.sf.orcc.ir.ExprVar;
import net.sf.orcc.ir.Expression;
import net.sf.orcc.ir.InstAssign;
import net.sf.orcc.ir.Instruction;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.transform.ControlFlowAnalyzer;
import net.sf.orcc.ir.util.AbstractIrVisitor;
import net.sf.orcc.ir.util.IrUtil;
import net.sf.orcc.util.Attribute;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* Replace BlockWhile by BlockFor when it is possible. Cfg must be built before
* any call to this transformation. To do that, please run the
* {@link ControlFlowAnalyzer} before this one.
*
* @author Jerome Gorin
* @author Antoine Lorence
* @see net.sf.orcc.ir.transform.ControlFlowAnalyzer
*/
public class BlockForAdder extends DfVisitor<Void> {
public BlockForAdder() {
irVisitor = new Builder(false);
}
public BlockForAdder(boolean fullReplacement) {
irVisitor = new Builder(fullReplacement);
}
/**
* Build the Cfg for procedure containing specific 1 or more "for" blocks.
*
* @author Antoine Lorence
*
*/
public class BlockForCfg extends ControlFlowAnalyzer {
/*
* (non-Javadoc)
*
* @see
* net.sf.orcc.ir.util.IrSwitch#defaultCase(org.eclipse.emf.ecore.EObject
* )
*/
@Override
public CfgNode defaultCase(EObject object) {
if (object instanceof BlockFor) {
return caseBlockFor((BlockFor) object);
}
return super.defaultCase(object);
}
public CfgNode caseBlockFor(BlockFor block) {
CfgNode join = addNode(block.getJoinBlock());
cfg.getVertices().add(join);
if (last != null) {
addEdge(join);
}
CfgNode cfgNode = addNode(block);
addEdge(cfgNode);
last = join;
flag = true;
last = doSwitch(block.getBlocks());
// reset flag (in case there are no nodes in "then" branch)
flag = false;
addEdge(join);
last = cfgNode;
return cfgNode;
}
}
/**
* Build a list of all variables used in an Expression.
*
*/
private class VarGetter extends AbstractIrVisitor<Void> {
private List<Var> vars;
private Expression expr;
public VarGetter(Expression expr) {
vars = new ArrayList<Var>();
this.expr = expr;
}
@Override
public Void caseExprVar(ExprVar expr) {
Var var = expr.getUse().getVariable();
if (!vars.contains(var)) {
vars.add(var);
}
return null;
}
public List<Var> get() {
doSwitch(expr);
return vars;
}
}
/**
* Main visitor. Effectively replace while loops by for loops
*
*/
private class Builder extends AbstractIrVisitor<Void> {
boolean fullReplacement = false;
public Builder(boolean fullReplacement) {
this.fullReplacement = fullReplacement;
}
@Override
public Void caseBlockWhile(BlockWhile blockWhile) {
// Ensure that inner while blocks are replaced by for blocks before
// executing this
super.caseBlockWhile(blockWhile);
// Get the list of variables used in while condition
Expression condition = blockWhile.getCondition();
List<Var> conditionVars = new VarGetter(condition).get();
// Get the last block of while's body
EList<Block> blocks = blockWhile.getBlocks();
if (blocks.isEmpty()) {
// Don't treat empty blocks
return null;
}
Block lastBlock = blocks.get(blocks.size() - 1);
// Get the block immediately preceding while loop
CfgNode joinNode = blockWhile.getJoinBlock().getCfgNode();
if (joinNode == null) {
throw new OrccRuntimeException(
"Control Flow Graph must be built. Please apply the ControlFlowAnalyzer before BlockForAdder (actor : "
+ actor.getName() + ").");
}
Block prevBlock = ((CfgNode) joinNode.getPredecessors().get(0))
.getNode();
List<Instruction> stepInstrList = new ArrayList<Instruction>();
List<Instruction> initInstrList = new ArrayList<Instruction>();
for (Var conditionVar : conditionVars) {
// Get the last assignment on each variable contained in the
// condition
Instruction initInstr = getLastAssign(conditionVar, prevBlock);
if (initInstr != null) {
initInstrList.add(initInstr);
}
Instruction stepInstr = getLastAssign(conditionVar, lastBlock);
if (stepInstr != null) {
stepInstrList.add(stepInstr);
}
}
// If fullReplacment == true, step or init can be
// empty but not both (for loop is created with blank fields)
// If fullReplacment == false, no empty field is allowed.
if (fullReplacement && stepInstrList.isEmpty()
&& initInstrList.isEmpty()) {
return null;
} else if (stepInstrList.isEmpty() || initInstrList.isEmpty()) {
return null;
}
// Create block for
BlockFor blockFor = IrSpecificFactory.eINSTANCE.createBlockFor();
blockFor.setCondition(blockWhile.getCondition());
blockFor.setLineNumber(blockWhile.getLineNumber());
blockFor.setJoinBlock(blockWhile.getJoinBlock());
blockFor.getBlocks().addAll(blockWhile.getBlocks());
// Add init and step instructions, after removing them from their
// original location
for (Instruction instr : initInstrList) {
EcoreUtil.remove(instr);
}
blockFor.getInit().addAll(initInstrList);
for (Instruction instr : stepInstrList) {
EcoreUtil.remove(instr);
}
blockFor.getStep().addAll(stepInstrList);
// Copy attributes
for (Attribute attribute : blockWhile.getAttributes()) {
blockFor.getAttributes().add(IrUtil.copy(attribute, false));
}
// Replace node
EcoreUtil.replace(blockWhile, blockFor);
return null;
}
/**
* Get the last <code>InstAssign</code> from <i>block</i> which targets
* <i>var</i>.
*
* @param var
* @param block
* @return the instruction
*/
private Instruction getLastAssign(Var var, Block block) {
EList<Def> defs = var.getDefs();
// Get the last block's BasicBlock
BlockBasic lastBlockBasic = null;
if (block.isBlockIf()) {
lastBlockBasic = ((BlockIf) block).getJoinBlock();
} else if (block.isBlockWhile()) {
lastBlockBasic = ((BlockWhile) block).getJoinBlock();
} else if (block.isBlockBasic()) {
lastBlockBasic = (BlockBasic) block;
} else {
lastBlockBasic = ((BlockFor) block).getJoinBlock();
}
// Return in case of an empty node
EList<Instruction> instructions = lastBlockBasic.getInstructions();
if (instructions.isEmpty()) {
return null;
}
// Search for the last InstAssign concerning var (def or use)
ListIterator<Instruction> it = instructions
.listIterator(instructions.size());
while (it.hasPrevious()) {
Instruction candidateInstr = it.previous();
if (candidateInstr.isInstAssign()) {
if (defs.contains(((InstAssign) candidateInstr).getTarget())) {
return candidateInstr;
} else if (new VarGetter(
((InstAssign) candidateInstr).getValue()).get()
.contains(var)) {
return null;
}
}
}
// No corresponding Instruction found, don't return anything
return null;
}
}
@Override
public Void caseActor(Actor actor) {
// Transform actor, try to replace WhileBlock by ForBlock
super.caseActor(actor);
// Rebuild CFG with for loops
new DfVisitor<CfgNode>(new BlockForCfg()).doSwitch(actor);
return null;
}
}