/*
* Copyright (c) 2010-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.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.orcc.backends.ir.InstTernary;
import net.sf.orcc.df.Action;
import net.sf.orcc.ir.Arg;
import net.sf.orcc.ir.ArgByRef;
import net.sf.orcc.ir.ArgByVal;
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.Def;
import net.sf.orcc.ir.ExprList;
import net.sf.orcc.ir.ExprVar;
import net.sf.orcc.ir.Expression;
import net.sf.orcc.ir.InstAssign;
import net.sf.orcc.ir.InstCall;
import net.sf.orcc.ir.InstLoad;
import net.sf.orcc.ir.InstPhi;
import net.sf.orcc.ir.InstReturn;
import net.sf.orcc.ir.InstStore;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.Param;
import net.sf.orcc.ir.Procedure;
import net.sf.orcc.ir.Type;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.AbstractIrVisitor;
import net.sf.orcc.ir.util.IrUtil;
import net.sf.orcc.util.Attributable;
import net.sf.orcc.util.Attribute;
import net.sf.orcc.util.Void;
import net.sf.orcc.util.util.EcoreHelper;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* This class defines an actor transformation that inline function(s) and/or
* procedure(s)
*
* @author Herve Yviquel
* @author Matthieu Wipliez
* @author Thavot Richard
*
* @version 1.1
*
*/
public class Inliner extends AbstractIrVisitor<Void> {
private class ExpressionUpdater extends AbstractIrVisitor<Void> {
public ExpressionUpdater() {
super(true);
}
/**
* Replace the local variable name if a reference already exists to a
* global variable
*/
@Override
public Void caseExprVar(ExprVar exprVar) {
Var var = exprVar.getUse().getVariable();
if (localToLocalsMap.containsKey(var)) {
exprVar.getUse().setVariable(localToLocalsMap.get(var));
}
return null;
}
/*
* (non-Javadoc)
*
* @see
* net.sf.orcc.ir.util.IrSwitch#caseExprList(net.sf.orcc.ir.ExprList)
*/
@Override
public Void caseExprList(ExprList object) {
for (Expression expression : object.getValue()) {
doSwitch(expression);
}
return null;
}
@Override
public Void caseInstAssign(InstAssign assign) {
// Replace the local variable name by visiting caseExprVar
super.doSwitch(assign.getValue());
Var var = assign.getTarget().getVariable();
if (localToLocalsMap.containsKey(var)) {
assign.getTarget().setVariable(localToLocalsMap.get(var));
}
return null;
}
@Override
public Void caseInstCall(InstCall call) {
Def def = call.getTarget();
if (def != null) {
Var var = def.getVariable();
if (localToLocalsMap.containsKey(var)) {
call.getTarget().setVariable(localToLocalsMap.get(var));
}
}
for (Arg arg : call.getArguments()) {
doSwitch(arg);
}
return null;
}
@Override
public Void caseInstLoad(InstLoad load) {
// Replace the local variable name by visiting caseExprVar
Var var = load.getTarget().getVariable();
if (localToLocalsMap.containsKey(var)) {
load.getTarget().setVariable(localToLocalsMap.get(var));
}
var = load.getSource().getVariable();
if (localToLocalsMap.containsKey(var)) {
load.getSource().setVariable(localToLocalsMap.get(var));
}
for (Expression e : load.getIndexes()) {
super.doSwitch(e);
}
return null;
}
@Override
public Void caseInstPhi(InstPhi instPhi) {
// Replace the local variable name by visiting caseExprVar
Var var = instPhi.getTarget().getVariable();
if (localToLocalsMap.containsKey(var)) {
instPhi.getTarget().setVariable(localToLocalsMap.get(var));
}
for (Expression e : instPhi.getValues()) {
super.doSwitch(e);
}
return null;
}
@Override
public Void caseInstReturn(InstReturn inst) {
// Replace the local variable name by visiting caseExprVar
instReturn = inst;
return super.doSwitch(inst.getValue());
}
@Override
public Void caseInstStore(InstStore store) {
// Replace the local variable name by visiting caseExprVar
super.doSwitch(store.getValue());
Var var = store.getTarget().getVariable();
if (localToLocalsMap.containsKey(var)) {
store.getTarget().setVariable(localToLocalsMap.get(var));
}
for (Expression e : store.getIndexes()) {
super.doSwitch(e);
}
return null;
}
@Override
public Void defaultCase(EObject object) {
if (object instanceof InstTernary) {
InstTernary ternary = (InstTernary) object;
Var var = ternary.getTarget().getVariable();
if (localToLocalsMap.containsKey(var)) {
ternary.getTarget().setVariable(localToLocalsMap.get(var));
}
doSwitch(ternary.getConditionValue());
doSwitch(ternary.getTrueValue());
doSwitch(ternary.getFalseValue());
}
return null;
}
/*
* (non-Javadoc)
*
* @see net.sf.orcc.ir.util.IrSwitch#caseAttributable(net.sf.orcc.util.
* Attributable)
*/
@Override
public Void caseAttributable(Attributable object) {
for (Attribute attribute : object.getAttributes()) {
caseAttributable(attribute);
EObject containedValue = attribute.getContainedValue();
if (containedValue != null) {
doSwitch(containedValue);
}
EObject referencedValue = attribute.getReferencedValue();
if (referencedValue != null) {
doSwitch(referencedValue);
}
}
return null;
}
}
private boolean inlineFunction;
private boolean inlineProcedure;
private boolean inlineActionBodyProcedure;
private InstReturn instReturn;
private Map<Var, Var> localToLocalsMap;
private ExpressionUpdater updater;
public Inliner(boolean inlineProcedure, boolean inlineFunction) {
this.inlineProcedure = inlineProcedure;
this.inlineFunction = inlineFunction;
this.inlineActionBodyProcedure = true;
this.updater = new ExpressionUpdater();
}
public Inliner(boolean inlineActionBodyProcedure, boolean inlineProcedure,
boolean inlineFunction) {
this.inlineProcedure = inlineProcedure;
this.inlineFunction = inlineFunction;
this.inlineActionBodyProcedure = inlineActionBodyProcedure;
this.updater = new ExpressionUpdater();
}
@Override
public Void caseInstCall(InstCall call) {
Type returnType = call.getProcedure().getReturnType();
if (returnType.isVoid() && inlineProcedure || !returnType.isVoid()
&& inlineFunction) {
if (!call.getProcedure().isNative()) {
if (inlineActionBodyProcedure) {
inlineProcedure(call);
} else {
Procedure procedure = call.getProcedure();
Action action = EcoreHelper.getContainerOfType(procedure,
Action.class);
if (action != null) {
if (procedure != action.getBody()) {
inlineProcedure(call);
}
} else {
inlineProcedure(call);
}
}
}
}
return null;
}
/**
* Find the given block into the given blocks location.
*
* @param locationBlocks
* @param blockToFind
* @return
*/
public List<Block> findBlock(List<Block> locationBlocks, Block blockToFind) {
if (locationBlocks.contains(blockToFind)) {
return locationBlocks;
} else {
List<Block> n = null;
for (Block block : locationBlocks) {
if (block.isBlockIf()) {
n = findBlock(((BlockIf) block).getElseBlocks(),
blockToFind);
if (n == null) {
n = findBlock(((BlockIf) block).getThenBlocks(),
blockToFind);
}
} else if (block.isBlockWhile()) {
n = findBlock(((BlockWhile) block).getBlocks(), blockToFind);
}
if (n != null) {
return n;
}
}
}
return null;
}
/**
* Inline the currentCall;
*/
private void inlineProcedure(InstCall currentCall) {
// 1. Clone procedure
Procedure calledProc = IrUtil.copy(currentCall.getProcedure());
// 2. Get current procedure
Procedure callerProc = EcoreHelper.getContainerOfType(currentCall,
Procedure.class);
// 3.1 Create a new local variable
localToLocalsMap = new HashMap<Var, Var>();
for (Var var : calledProc.getLocals()) {
Var newVar = callerProc.newTempLocalVariable(var.getType(),
"inlined_" + var.getName());
newVar.setIndex(var.getIndex());
// newVar.setLineNumber(var.getLineNumber());
newVar.setAssignable(var.isAssignable());
localToLocalsMap.put(var, newVar);
}
// 3.2
int i = 0;
BlockBasic assignBlock = IrFactory.eINSTANCE.createBlockBasic();
for (Arg arg : currentCall.getArguments()) {
Param param = calledProc.getParameters().get(i);
if (arg.isByRef()) {
ArgByRef ref = (ArgByRef) arg;
localToLocalsMap.put(param.getVariable(), ref.getUse()
.getVariable());
} else {
ArgByVal val = (ArgByVal) arg;
if (val.getValue().isExprVar()) {
Var var = ((ExprVar) val.getValue()).getUse().getVariable();
localToLocalsMap.put(param.getVariable(), var);
} else {
Var newVar = callerProc.newTempLocalVariable(param
.getVariable().getType(), "inlined_"
+ param.getVariable().getName());
newVar.setAssignable(true);
localToLocalsMap.put(param.getVariable(), newVar);
InstAssign instAssign = IrFactory.eINSTANCE
.createInstAssign(newVar, val.getValue());
assignBlock.add(instAssign);
}
}
i++;
}
// 4. Rename local variable
updater.doSwitch(calledProc);
// 5. Transform return
if ((currentCall.getTarget() == null)
| calledProc.getReturnType().isVoid()) {
IrUtil.delete(instReturn);
} else {
InstAssign instAssign = IrFactory.eINSTANCE.createInstAssign(
currentCall.getTarget().getVariable(),
IrUtil.copy(instReturn.getValue()));
EcoreUtil.replace(instReturn, instAssign);
}
// 6. Cut the block containing call instruction in two parts
BlockBasic beginningBlock = currentCall.getBlock();
BlockBasic followingBlock = IrFactory.eINSTANCE.createBlockBasic();
indexInst = 0;
while (indexInst < beginningBlock.getInstructions().size()) {
if (beginningBlock.getInstructions().get(indexInst)
.equals(currentCall)) {
break;
}
indexInst++;
}
while (indexInst < beginningBlock.getInstructions().size()) {
followingBlock.add(beginningBlock.getInstructions().get(indexInst));
}
// 7. Add all inlined blocks
List<Block> blocks = findBlock(callerProc.getBlocks(), beginningBlock);
indexBlock = blocks.indexOf(beginningBlock);
List<Block> inlined = calledProc.getBlocks();
if (!assignBlock.getInstructions().isEmpty()) {
inlined.add(0, assignBlock);
}
inlined.add(followingBlock);
blocks.addAll(indexBlock + 1, inlined);
// 8.
IrUtil.delete(currentCall);
}
}