/*
* Copyright (c) 2009-2010, IETR/INSA of Rennes
* 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 IETR/INSA of Rennes 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 net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.backends.ir.InstCast;
import net.sf.orcc.backends.ir.IrSpecificFactory;
import net.sf.orcc.ir.Arg;
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.ExprBinary;
import net.sf.orcc.ir.ExprBool;
import net.sf.orcc.ir.ExprFloat;
import net.sf.orcc.ir.ExprInt;
import net.sf.orcc.ir.ExprList;
import net.sf.orcc.ir.ExprString;
import net.sf.orcc.ir.ExprUnary;
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.Instruction;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.OpBinary;
import net.sf.orcc.ir.Param;
import net.sf.orcc.ir.Type;
import net.sf.orcc.ir.TypeInt;
import net.sf.orcc.ir.TypeList;
import net.sf.orcc.ir.TypeUint;
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.util.EcoreHelper;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* Add cast in IR in the form of assign instruction where target's type differs
* from source type.
*
* @author Jerome Goring
* @author Herve Yviquel
* @author Matthieu Wipliez
*
*/
public class CastAdder extends AbstractIrVisitor<Expression> {
private final static IrFactory factory = IrFactory.eINSTANCE;
private final static IrSpecificFactory factorySpec = IrSpecificFactory.eINSTANCE;
private final boolean castToUnsigned;
private final boolean createEmptyBlockBasic;
protected Type parentType;
/**
* Creates a new cast transformation. By default, this constructor creates
* an empty BlockBasic for the then or else part of a BlockIf if it is empty
*
* @param castToUnsigned
* <code>true</code> if an explicit cast is needed between signed
* and unsigned
*/
public CastAdder(boolean castToUnsigned) {
this(castToUnsigned, true);
}
/**
* Creates a new cast transformation
*
* @param castToUnsigned
* <code>true</code> if an explicit cast is needed between signed
* and unsigned
* @param createEmptyBlockBasic
* <code>true</code> if an Empty BlockBasic should be introduced
* when a ifBlock has an empty thenBlock/elseBlock
*/
public CastAdder(boolean castToUnsigned, boolean createEmptyBlockBasic) {
this.castToUnsigned = castToUnsigned;
this.createEmptyBlockBasic = createEmptyBlockBasic;
}
@Override
public Expression caseBlockIf(BlockIf blockIf) {
Type oldParentType = parentType;
parentType = factory.createTypeBool();
blockIf.setCondition(doSwitch(blockIf.getCondition()));
doSwitch(blockIf.getThenBlocks());
doSwitch(blockIf.getElseBlocks());
doSwitch(blockIf.getJoinBlock());
parentType = oldParentType;
return null;
}
@Override
public Expression caseBlockWhile(BlockWhile blockWhile) {
Type oldParentType = parentType;
parentType = factory.createTypeBool();
blockWhile.setCondition(doSwitch(blockWhile.getCondition()));
doSwitch(blockWhile.getBlocks());
doSwitch(blockWhile.getJoinBlock());
parentType = oldParentType;
return null;
}
@Override
public Expression caseExprBinary(ExprBinary expr) {
Type oldParentType = parentType;
Expression e1 = expr.getE1();
Expression e2 = expr.getE2();
if (isTypeReducer(expr.getOp())) {
// FIXME: Probably a better solution
expr.setType(IrUtil.copy(getBigger(e1.getType(), e2.getType())));
}
if (expr.getOp().isComparison()) {
parentType = getBigger(e1.getType(), e2.getType());
} else {
parentType = expr.getType();
}
expr.setE1(doSwitch(e1));
expr.setE2(doSwitch(e2));
parentType = oldParentType;
return castExpression(expr);
}
@Override
public Expression caseExprBool(ExprBool expr) {
return expr;
}
@Override
public Expression caseExprFloat(ExprFloat expr) {
return expr;
}
@Override
public Expression caseExprInt(ExprInt expr) {
expr.getType().setSize(parentType.getSizeInBits());
return expr;
}
@Override
public Expression caseExprList(ExprList expr) {
castExpressionList(expr.getValue());
return expr;
}
@Override
public Expression caseExprString(ExprString expr) {
return expr;
}
@Override
public Expression caseExprUnary(ExprUnary expr) {
Type oldParentType = parentType;
parentType = expr.getType();
expr.setExpr(doSwitch(expr.getExpr()));
parentType = oldParentType;
return castExpression(expr);
}
@Override
public Expression caseExprVar(ExprVar expr) {
return castExpression(expr);
}
@Override
public Expression caseInstAssign(InstAssign assign) {
Type oldParentType = parentType;
parentType = assign.getTarget().getVariable().getType();
Expression newValue = doSwitch(assign.getValue());
parentType = oldParentType;
if (newValue != assign.getValue()) {
// The assignment is useless now, we replace it by the cast
// instruction
EList<Instruction> instructions = assign.getBlock()
.getInstructions();
InstCast cast = (InstCast) instructions.get(instructions
.indexOf(assign) - 1);
cast.setTarget(assign.getTarget());
IrUtil.delete(assign);
}
return null;
}
@Override
public Expression caseInstCall(InstCall call) {
if (!call.isPrint()) {
Type oldParentType = parentType;
Iterable<Expression> expressions = EcoreHelper.getObjects(call,
Expression.class);
EList<Expression> oldExpressions = new BasicEList<Expression>();
for (Expression expr : expressions) {
oldExpressions.add(expr);
}
EList<Expression> newExpressions = new BasicEList<Expression>();
for (int i = 0; i < oldExpressions.size(); i++) {
// Check call parameter type coherence
Param param = call.getProcedure().getParameters().get(i);
Var variable = param.getVariable();
parentType = variable.getType();
Expression expr = oldExpressions.get(i);
// Check argument if it's not a string
if (!parentType.isString()) {
expr = doSwitch(expr);
}
newExpressions.add(expr);
}
call.getArguments().clear();
call.getArguments().addAll(factory.createArgsByVal(newExpressions));
parentType = oldParentType;
if (call.getTarget() != null) {
Var target = call.getTarget().getVariable();
Type returnType = call.getProcedure().getReturnType();
if (needCast(target.getType(), returnType)) {
Var castedTarget = procedure.newTempLocalVariable(
target.getType(), "casted_" + target.getName());
castedTarget.setIndex(1);
target.setType(IrUtil.copy(returnType));
InstCast cast = factorySpec.createInstCast(target,
castedTarget);
call.getBlock().add(indexInst + 1, cast);
}
}
} else {
// Call to print procedure : see if integer parameter cast is
// necessary (LLVM only)
EList<Arg> arguments = call.getArguments();
List<Instruction> castInstrToAdd = new ArrayList<Instruction>();
List<String> varCasted = new ArrayList<String>();
// Evaluate every argument of call instruction
for (Arg callArg : arguments) {
Expression exprArg = null;
if (callArg.isByVal()) {
exprArg = ((ArgByVal) callArg).getValue();
// TODO: Cleaning
if (!(exprArg instanceof ExprVar)) {
/*
* Nothing to do, cast is added only for print arguments
* which are variables. If a verbose mode is added to
* backends in the future, this msg can be used to
* prevent useless print calls :
*
* String msg =
* "[Warn] Parameter of print call is not a String or a Variable : \n\tParameter type : "
* + exprArg.getClass().getName() + "\n\tActor : " +
* EcoreHelper.getContainerOfType(call,
* Actor.class).getName() + "\n\tLine number : " +
* call.getLineNumber();
*/
} else if ((exprArg.getType().isInt() || exprArg.getType()
.isUint())
&& exprArg.getType().getSizeInBits() != 32) {
Var source = ((ExprVar) exprArg).getUse().getVariable();
// If variable is used more than one time in call
// arguments list, we add a cast instruction only for
// the first occurence
if (varCasted.contains(source.getName())) {
continue;
} else {
// Add var name to the list of alredy casted vars
varCasted.add(source.getName());
}
// target type must be now on 32 bits
Type targetType = IrUtil.copy(exprArg.getType());
if (targetType.isInt()) {
((TypeInt) targetType).setSize(32);
} else if (targetType.isUint()) {
((TypeUint) targetType).setSize(32);
}
// target name and type are updated
Var target = procedure.newTempLocalVariable(targetType, "casted_32_" + source.getName());
target.setType(targetType);
// Update variable used in call parameter
((ExprVar) exprArg).getUse().setVariable(target);
// Create the concrete cast instruction
Instruction castInstr = factorySpec.createInstCast(
source, target);
// Append cast instruction to tempoary list
castInstrToAdd.add(castInstr);
}
} else {
throw new OrccRuntimeException("ArgByRef not supported.");
}
}
// Add cast instructions just before call
BlockBasic debugBlock = call.getBlock();
for (Instruction instr : castInstrToAdd) {
debugBlock.add(indexInst++, instr);
}
}
return null;
}
@Override
public Expression caseInstLoad(InstLoad load) {
// Indexes are not casted...
Var source = load.getSource().getVariable();
Var target = load.getTarget().getVariable();
Type uncastedType;
if (load.getIndexes().isEmpty()) {
// Load from a scalar variable
uncastedType = IrUtil.copy(source.getType());
} else {
// Load from an array variable
uncastedType = IrUtil.copy(((TypeList) source.getType())
.getInnermostType());
}
if (needCast(target.getType(), uncastedType)) {
Var castedTarget = procedure.newTempLocalVariable(target.getType(),
"casted_" + target.getName());
castedTarget.setIndex(1);
target.setType(uncastedType);
InstCast cast = factorySpec.createInstCast(target, castedTarget);
load.getBlock().add(indexInst + 1, cast);
}
return null;
}
@Override
public Expression caseInstPhi(InstPhi phi) {
Type oldParentType = parentType;
parentType = phi.getTarget().getVariable().getType();
EList<Expression> values = phi.getValues();
// FIXME: Need improvment/merging
Block containingBlock = (Block) phi.getBlock().eContainer();
Expression value0 = phi.getValues().get(0);
Expression value1 = phi.getValues().get(1);
if (containingBlock.isBlockIf()) {
BlockIf blockIf = (BlockIf) containingBlock;
if (value0.isExprVar()) {
if (createEmptyBlockBasic) {
BlockBasic block0 = factory.createBlockBasic();
blockIf.getThenBlocks().add(block0);
values.set(0, castExpression(value0, block0, 0));
}
}
if (value1.isExprVar()) {
if (createEmptyBlockBasic) {
BlockBasic block1 = factory.createBlockBasic();
blockIf.getElseBlocks().add(block1);
values.set(1, castExpression(value1, block1, 0));
}
}
} else {
BlockWhile blockWhile = (BlockWhile) containingBlock;
if (value0.isExprVar()) {
BlockBasic block = factory.createBlockBasic();
EcoreHelper.getContainingList(containingBlock).add(indexBlock,
block);
indexBlock++;
values.set(0, castExpression(value0, block, 0));
}
if (value1.isExprVar()) {
BlockBasic block = factory.createBlockBasic();
blockWhile.getBlocks().add(block);
values.set(1, castExpression(value1, block, 0));
}
}
parentType = oldParentType;
return null;
}
@Override
public Expression caseInstReturn(InstReturn returnInstr) {
Expression expr = returnInstr.getValue();
if (expr != null) {
Type oldParentType = parentType;
parentType = procedure.getReturnType();
returnInstr.setValue(doSwitch(expr));
parentType = oldParentType;
}
return null;
}
@Override
public Expression caseInstStore(InstStore store) {
// Indexes are not casted...
Type oldParentType = parentType;
if (store.getIndexes().isEmpty()) {
// Store to a scalar variable
parentType = store.getTarget().getVariable().getType();
} else {
// Store to an array variable
parentType = ((TypeList) store.getTarget().getVariable().getType())
.getInnermostType();
}
store.setValue(doSwitch(store.getValue()));
parentType = oldParentType;
return null;
}
private Expression castExpression(Expression expr) {
if (needCast(expr.getType(), parentType)) {
Var oldVar;
if (expr.isExprVar()) {
oldVar = ((ExprVar) expr).getUse().getVariable();
} else {
oldVar = procedure.newTempLocalVariable(
EcoreUtil.copy(expr.getType()),
"expr_" + procedure.getName());
InstAssign assign = factory.createInstAssign(oldVar,
IrUtil.copy(expr));
IrUtil.addInstBeforeExpr(expr, assign);
}
Var newVar = procedure.newTempLocalVariable(
EcoreUtil.copy(parentType),
"castedExpr_" + procedure.getName());
InstCast cast = factorySpec.createInstCast(oldVar, newVar);
if (IrUtil.addInstBeforeExpr(expr, cast)) {
indexInst++;
}
IrUtil.delete(expr);
return factory.createExprVar(newVar);
}
return expr;
}
private Expression castExpression(Expression expr, BlockBasic block,
int index) {
if (needCast(expr.getType(), parentType)) {
Var oldVar;
if (expr.isExprVar()) {
oldVar = ((ExprVar) expr).getUse().getVariable();
} else {
oldVar = procedure.newTempLocalVariable(
EcoreUtil.copy(expr.getType()),
"expr_" + procedure.getName());
InstAssign assign = factory.createInstAssign(oldVar,
IrUtil.copy(expr));
block.add(index, assign);
index++;
}
Var newVar = procedure.newTempLocalVariable(
EcoreUtil.copy(parentType),
"castedExpr_" + procedure.getName());
InstCast cast = factorySpec.createInstCast(oldVar, newVar);
block.add(index, cast);
return factory.createExprVar(newVar);
}
return expr;
}
private void castExpressionList(EList<Expression> expressions) {
EList<Expression> oldExpression = new BasicEList<Expression>(
expressions);
EList<Expression> newExpressions = new BasicEList<Expression>();
for (Expression expression : oldExpression) {
newExpressions.add(doSwitch(expression));
}
expressions.clear();
expressions.addAll(newExpressions);
}
private Type getBigger(Type type1, Type type2) {
if (type1.getSizeInBits() < type2.getSizeInBits()) {
return type2;
} else {
return type1;
}
}
private boolean isTypeReducer(OpBinary op) {
switch (op) {
case SHIFT_RIGHT:
case MOD:
return true;
default:
return false;
}
}
protected boolean needCast(Type type1, Type type2) {
if (type1.isList() && type2.isList()) {
TypeList typeList1 = (TypeList) type1;
TypeList typeList2 = (TypeList) type2;
List<Integer> dim1 = typeList1.getDimensions();
List<Integer> dim2 = typeList2.getDimensions();
for (int i = 0; i < dim1.size() && i < dim2.size(); i++) {
if (!dim1.get(i).equals(dim2.get(i))) {
return true;
}
}
return needCast(typeList1.getInnermostType(),
typeList2.getInnermostType());
} else {
return (type1.getSizeInBits() != type2.getSizeInBits())
|| (castToUnsigned && type1.getClass() != type2.getClass())
|| (!((type1.isInt() && type2.isUint()) || (type1.isUint() && type2
.isInt())) && (type1.getClass() != type2.getClass()));
}
}
}