/* * Copyright (c) 2010-2011, 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.frontend; import static net.sf.orcc.ir.IrFactory.eINSTANCE; import static net.sf.orcc.ir.OpBinary.MINUS; import static net.sf.orcc.ir.OpBinary.PLUS; import static net.sf.orcc.ir.OpBinary.TIMES; import static net.sf.orcc.util.OrccAttributes.COPY_OF_TOKENS; import static net.sf.orcc.util.OrccAttributes.REMOVABLE_COPY; import java.util.ArrayList; import java.util.List; import net.sf.orcc.cal.cal.AstExpression; import net.sf.orcc.cal.cal.ExpressionBinary; import net.sf.orcc.cal.cal.ExpressionBoolean; import net.sf.orcc.cal.cal.ExpressionCall; import net.sf.orcc.cal.cal.ExpressionElsif; import net.sf.orcc.cal.cal.ExpressionFloat; import net.sf.orcc.cal.cal.ExpressionIf; import net.sf.orcc.cal.cal.ExpressionIndex; import net.sf.orcc.cal.cal.ExpressionInteger; import net.sf.orcc.cal.cal.ExpressionList; import net.sf.orcc.cal.cal.ExpressionString; import net.sf.orcc.cal.cal.ExpressionUnary; import net.sf.orcc.cal.cal.ExpressionVariable; import net.sf.orcc.cal.cal.Generator; import net.sf.orcc.cal.cal.StatementCall; import net.sf.orcc.cal.cal.Variable; import net.sf.orcc.cal.cal.util.CalSwitch; import net.sf.orcc.cal.services.Evaluator; import net.sf.orcc.cal.services.Typer; import net.sf.orcc.cal.util.Util; import net.sf.orcc.df.Action; import net.sf.orcc.df.Pattern; import net.sf.orcc.df.Port; import net.sf.orcc.ir.Block; import net.sf.orcc.ir.BlockIf; import net.sf.orcc.ir.BlockWhile; 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.InstStore; import net.sf.orcc.ir.Instruction; import net.sf.orcc.ir.OpBinary; import net.sf.orcc.ir.OpUnary; import net.sf.orcc.ir.Procedure; import net.sf.orcc.ir.Type; import net.sf.orcc.ir.TypeList; import net.sf.orcc.ir.Var; import net.sf.orcc.ir.util.IrUtil; import net.sf.orcc.util.OrccUtil; import org.eclipse.emf.ecore.EObject; /** * This class transforms an AST expression into one or more IR instructions * and/or blocks, and returns an IR expression. The main idea is that an * expression will assign to a target with indexes unless target is * <code>null</code>. So simple arithmetic (binary and unary) sub-expressions * are translated with target set to <code>null</code> to avoid further * assignments. * * <p> * The assignments are appended to the given List of block called * <code>blocks</code>. The Procedure passed as a parameter is used for lookup * and creation of local variables. * </p> * * @author Matthieu Wipliez * @author Hervé Yviquel */ public class ExprTransformer extends CalSwitch<Expression> { private List<Expression> indexes; private final List<Block> blocks; private final Procedure procedure; private Var target; /** * Creates a new transformer with the given procedure and blocks, and with a * <code>null</code> target. To be used as a last resort (or when the target * is unknown, e.g. condition of if and while statements, expression of a * function). * * @param procedure * procedure in which the expression is transformed * @param blocks * a list of blocks to which instructions and other blocks may be * appended. In general, this is a subset of the procedure's * block list. */ public ExprTransformer(Procedure procedure, List<Block> blocks) { this(procedure, blocks, null); } /** * Creates a new transformer with the given procedure, blocks, and target. * To be used when the expression to be translated is to be assigned to the * target without indexes. * * @param procedure * procedure in which the expression is transformed * @param blocks * a list of blocks to which instructions and other blocks may be * appended. In general, this is a subset of the procedure's * block list. * @param target * the variable to which the expression should be assigned */ public ExprTransformer(Procedure procedure, List<Block> blocks, Var target) { this(procedure, blocks, target, null); } /** * Creates a new transformer with the given procedure, blocks, target, and * indexes. To be used when the expression to be translated is to be * assigned to the target with indexes. * * @param procedure * procedure in which the expression is transformed * @param blocks * a list of blocks to which instructions and other blocks may be * appended. In general, this is a subset of the procedure's * block list. * @param target * the variable to which the expression should be assigned * @param indexes * a list of expression to use when creating assignments (Store * instructions) to the target */ public ExprTransformer(Procedure procedure, List<Block> blocks, Var target, List<Expression> indexes) { this.procedure = procedure; this.blocks = blocks; this.target = target; this.indexes = indexes; } @Override public Expression caseExpressionBinary(ExpressionBinary expression) { OpBinary op = OpBinary.getOperator(expression.getOperator()); Expression e1 = new ExprTransformer(procedure, blocks) .doSwitch(expression.getLeft()); Expression e2 = new ExprTransformer(procedure, blocks) .doSwitch(expression.getRight()); Expression value = eINSTANCE.createExprBinary(e1, op, e2, Typer.getType(expression)); return storeExpr(expression, value); } @Override public Expression caseExpressionBoolean(ExpressionBoolean expression) { Expression value = eINSTANCE.createExprBool(expression.isValue()); return storeExpr(expression, value); } @Override public Expression caseExpressionCall(ExpressionCall exprCall) { int lineNumber = Util.getLocation(exprCall); // retrieve IR procedure Procedure calledProc = Frontend.instance.getMapping(exprCall .getFunction()); // transform parameters List<Expression> parameters = AstIrUtil.transformExpressions(procedure, blocks, exprCall.getParameters()); // set call target and add call Var callTarget = getScalar(calledProc.getReturnType(), calledProc.getName()); InstCall call = eINSTANCE.createInstCall(lineNumber, callTarget, calledProc, parameters); Util.transformAnnotations(call, exprCall.getAnnotations()); IrUtil.getLast(blocks).add(call); // return expr if (callTarget != target) { // need to store back into target return storeExpr(exprCall, eINSTANCE.createExprVar(callTarget)); } else { return null; } } @Override public Expression caseExpressionIf(ExpressionIf expression) { int lineNumber = Util.getLocation(expression); Expression condition = new ExprTransformer(procedure, blocks) .doSwitch(expression.getCondition()); // transforms "then" statements and "else" statements BlockIf blockIf = eINSTANCE.createBlockIf(); blockIf.setJoinBlock(eINSTANCE.createBlockBasic()); blockIf.setLineNumber(lineNumber); blockIf.setCondition(condition); blocks.add(blockIf); Var ifTarget; if (target == null) { ifTarget = procedure.newTempLocalVariable( Typer.getType(expression), "tmp_if"); } else { ifTarget = target; } // transforms "then" expression new ExprTransformer(procedure, blockIf.getThenBlocks(), ifTarget, indexes).doSwitch(expression.getThen()); // add elsif expressions for (ExpressionElsif elsif : expression.getElsifs()) { condition = new ExprTransformer(procedure, blockIf.getElseBlocks()) .doSwitch(elsif.getCondition()); // creates inner if lineNumber = Util.getLocation(elsif); BlockIf innerIf = eINSTANCE.createBlockIf(); innerIf.setJoinBlock(eINSTANCE.createBlockBasic()); innerIf.setLineNumber(lineNumber); innerIf.setCondition(condition); new ExprTransformer(procedure, innerIf.getThenBlocks(), ifTarget, indexes).doSwitch(elsif.getThen()); // adds elsif to block's else blocks, and assign elsif to block blockIf.getElseBlocks().add(innerIf); blockIf = innerIf; } new ExprTransformer(procedure, blockIf.getElseBlocks(), ifTarget, indexes).doSwitch(expression.getElse()); // return expr if (target == null) { return eINSTANCE.createExprVar(ifTarget); } else { return null; } } @Override public Expression caseExpressionIndex(ExpressionIndex expression) { // we always load in this case int lineNumber = Util.getLocation(expression); Variable variable = expression.getSource().getVariable(); Var var = Frontend.instance.getMapping(variable); List<Expression> indexes = AstIrUtil.transformExpressions(procedure, blocks, expression.getIndexes()); // set load target and add load Var loadTarget = getScalar(Typer.getType(expression), var.getName()); InstLoad load = eINSTANCE.createInstLoad(lineNumber, loadTarget, var, indexes); IrUtil.getLast(blocks).add(load); // return expr if (loadTarget != target) { // need to store back into target return storeExpr(expression, eINSTANCE.createExprVar(loadTarget)); } else { return null; } } @Override public Expression caseExpressionInteger(ExpressionInteger expression) { Expression value = eINSTANCE.createExprInt(expression.getValue()); return storeExpr(expression, value); } @Override public Expression caseExpressionFloat(ExpressionFloat expression) { Expression value = eINSTANCE.createExprFloat(expression.getValue()); return storeExpr(expression, value); } @Override public Expression caseExpressionList(ExpressionList expression) { if (indexes == null) { indexes = new ArrayList<Expression>(); } // Return new ExprVar only in case we need one boolean needReturn = false; if (target == null) { target = procedure.newTempLocalVariable(Typer.getType(expression), "tmp_list"); needReturn = true; } List<AstExpression> expressions = expression.getExpressions(); List<Generator> generators = expression.getGenerators(); if (generators.isEmpty()) { transformListSimple(expressions); } else { transformListGenerators(expressions, generators); } if (needReturn) { return eINSTANCE.createExprVar(target); } else { return null; } } @Override public Expression caseExpressionString(ExpressionString expression) { Expression value = eINSTANCE.createExprString(OrccUtil .getEscapedString(expression.getValue())); return storeExpr(expression, value); } @Override public Expression caseExpressionUnary(ExpressionUnary expression) { OpUnary op = OpUnary.getOperator(expression.getUnaryOperator()); if (OpUnary.NUM_ELTS == op) { return Evaluator.getValue(expression); } Expression expr = new ExprTransformer(procedure, blocks) .doSwitch(expression.getExpression()); Expression value = eINSTANCE.createExprUnary(op, expr, Typer.getType(expression)); return storeExpr(expression, value); } @Override public Expression caseExpressionVariable(ExpressionVariable expression) { Variable variable = expression.getValue().getVariable(); Var var = Frontend.instance.getMapping(variable); Expression value; if (var.getType().isList()) { if (target == null) { EObject exprContainer = expression.eContainer(); EObject procContainer = procedure.eContainer(); // Check if the list variable need to be copied locally (when // the variable represents input data and is used as procedure // argument). Then, annotate the IR to allow optimizations at // back-end level. if (exprContainer instanceof StatementCall && procContainer instanceof Action) { Pattern inputPattern = ((Action) procContainer) .getInputPattern(); if (inputPattern.contains(var)) { target = procedure.newTempLocalVariable(var.getType(), "local_" + var.getName()); copyList(var, true); // Mark the variable as a local copy of input data and // reference the associated port. Port port = inputPattern.getPort(var); target.setAttribute(COPY_OF_TOKENS, port); return eINSTANCE.createExprVar(target); } } return eINSTANCE.createExprVar(var); } else { Expression expr; // Check if the variable has been marked before by the // ActorTransformer as an optimizable copy of output variable // (when it used as procedure argument). Then, annotate the IR // to allow optimizations at back-end level. if (var.hasAttribute(COPY_OF_TOKENS)) { expr = copyList(var, true); // Mark the variable as a local copy of output data and // reference the associated port. Pattern outputPattern = ((Action) procedure.eContainer()) .getOutputPattern(); var.setAttribute(COPY_OF_TOKENS, outputPattern.getPort(target)); } else { expr = copyList(var, false); } return expr; } } else { if (procedure != null) { if (!AstIrUtil.isLocal(var)) { Var global = var; var = procedure.getLocal("local_" + global.getName()); if (var == null) { var = procedure.newTempLocalVariable(global.getType(), "local_" + global.getName()); } InstLoad load = eINSTANCE.createInstLoad(var, global); IrUtil.getLast(blocks).add(load); } } value = eINSTANCE.createExprVar(var); } return storeExpr(expression, value); } /** * Copies the given variable to the current target. Will use the current * indexes if they exist. * * @param var * a variable of type list. * @param removable * true if the copy can be removed when the FIFO is accessed * directly (according to the backend) * @return <code>null</code> */ private Expression copyList(Var var, boolean removable) { TypeList typeList = (TypeList) target.getType(); List<Block> blocks = this.blocks; List<BlockWhile> whiles = new ArrayList<BlockWhile>(); List<Var> loopVars = new ArrayList<Var>(); List<Expression> indexes = new ArrayList<Expression>(); for (int size : typeList.getDimensions()) { // add loop variable Var loopVar = procedure.newTempLocalVariable( eINSTANCE.createTypeInt(32), "idx_" + var.getName()); loopVars.add(loopVar); InstAssign assign = eINSTANCE.createInstAssign(loopVar, eINSTANCE.createExprInt(0)); IrUtil.getLast(blocks).add(assign); // add index indexes.add(eINSTANCE.createExprVar(loopVar)); // create while block Expression condition = eINSTANCE.createExprBinary( eINSTANCE.createExprVar(loopVar), OpBinary.LT, eINSTANCE.createExprInt(size), eINSTANCE.createTypeBool()); BlockWhile blockWhile = eINSTANCE.createBlockWhile(); blockWhile.setJoinBlock(eINSTANCE.createBlockBasic()); blockWhile.setCondition(condition); whiles.add(blockWhile); blocks.add(blockWhile); blocks = blockWhile.getBlocks(); if (removable) { blockWhile.addAttribute(REMOVABLE_COPY); } } // load Var loadTarget = procedure.newTempLocalVariable( typeList.getInnermostType(), "local_" + var.getName()); InstLoad load = eINSTANCE.createInstLoad(0, loadTarget, var, indexes); IrUtil.getLast(blocks).add(load); // store if (this.indexes == null) { indexes = new ArrayList<Expression>(IrUtil.copy(indexes)); } else { indexes = this.indexes; } InstStore store = eINSTANCE.createInstStore(0, target, indexes, eINSTANCE.createExprVar(loadTarget)); IrUtil.getLast(blocks).add(store); // add increments int size = typeList.getDimensions().size(); for (int i = 0; i < size; i++) { Var loopVar = loopVars.get(i); IrUtil.getLast(whiles.get(i).getBlocks()).add( eINSTANCE.createInstAssign(loopVar, eINSTANCE .createExprBinary(eINSTANCE.createExprVar(loopVar), OpBinary.PLUS, eINSTANCE.createExprInt(1), eINSTANCE.createTypeInt(32)))); } return null; } /** * If the current target is a local scalar variable, returns the current * target. Otherwise, creates a new temporary local variable to hold the * result of a Call or a Load. * * @param type * type of the temp variable to create * @param name * indication for the variable name (will be prefixed with * "tmp_") * @return a scalar variable that can be used as a target */ private Var getScalar(Type type, String name) { if (target != null && AstIrUtil.isLocal(target) && indexes == null) { // local scalar => ok return target; } else { // otherwise, create temporary local scalar return procedure.newTempLocalVariable(type, "tmp_" + name); } } /** * If target is <code>null</code>, returns the given value. Otherwise, * creates an Assign or Store instruction depending on the target and * indexes. Copies the indexes if necessary. * * @param astObject * an AST block used to get a location (line number) * @param value * an expression * @return <code>value</code> or <code>null</code> */ private Expression storeExpr(EObject astObject, Expression value) { if (target == null) { return value; } int lineNumber = Util.getLocation(astObject); Instruction instruction; if (AstIrUtil.isLocal(target) && indexes == null) { instruction = eINSTANCE.createInstAssign(lineNumber, target, value); } else { if (indexes == null) { // a store with an empty list of indexes instruction = eINSTANCE.createInstStore(lineNumber, target, new ArrayList<Expression>(0), value); } else { boolean copyNeeded = false; for (Expression index : indexes) { if (index.eContainer() != null) { copyNeeded = true; break; } } if (copyNeeded) { indexes = new ArrayList<Expression>(IrUtil.copy(indexes)); } instruction = eINSTANCE.createInstStore(lineNumber, target, indexes, value); } } IrUtil.getLast(blocks).add(instruction); return null; } /** * Transforms the given expressions and generators. * * @param expressions * a list of expressions * @param generators * a list of generators */ private void transformListGenerators(List<AstExpression> expressions, List<Generator> generators) { // first add local variables Expression index = null; for (Generator generator : generators) { Variable variable = generator.getVariable(); Var loopVar = procedure.newTempLocalVariable( Typer.getType(variable), variable.getName()); Frontend.instance.putMapping(variable, loopVar); int lower = Evaluator.getIntValue(generator.getLower()); Expression thisIndex = eINSTANCE.createExprVar(loopVar); if (lower != 0) { thisIndex = eINSTANCE.createExprBinary(thisIndex, MINUS, eINSTANCE.createExprInt(lower), thisIndex.getType()); } int higher = Evaluator.getIntValue(generator.getHigher()); if (index == null) { index = thisIndex; } else { index = eINSTANCE.createExprBinary(eINSTANCE.createExprBinary( index, TIMES, eINSTANCE.createExprInt(higher - lower + 1), index.getType()), PLUS, thisIndex, index.getType()); } } indexes.add(index); // build the loops List<Block> blocks = this.blocks; List<BlockWhile> whiles = new ArrayList<BlockWhile>(); for (Generator generator : generators) { // assigns the loop variable its initial value Var loopVar = Frontend.instance.getMapping(generator.getVariable()); new ExprTransformer(procedure, blocks, loopVar).doSwitch(generator .getLower()); // condition Expression higher = new ExprTransformer(procedure, blocks) .doSwitch(generator.getHigher()); Expression condition = eINSTANCE.createExprBinary( eINSTANCE.createExprVar(loopVar), OpBinary.LE, higher, eINSTANCE.createTypeBool()); // create while BlockWhile blockWhile = eINSTANCE.createBlockWhile(); blockWhile.setJoinBlock(eINSTANCE.createBlockBasic()); blockWhile.setCondition(condition); whiles.add(blockWhile); blocks.add(blockWhile); blocks = blockWhile.getBlocks(); } // translates the expression (add to the innermost blocks) ExprTransformer tfer = new ExprTransformer(procedure, blocks, target, indexes); for (AstExpression astExpression : expressions) { tfer.doSwitch(astExpression); } // add increments int i = 0; for (Generator generator : generators) { int lineNumber = Util.getLocation(generator); Var loopVar = Frontend.instance.getMapping(generator.getVariable()); IrUtil.getLast(whiles.get(i).getBlocks()).add( eINSTANCE.createInstAssign(lineNumber, loopVar, eINSTANCE .createExprBinary(eINSTANCE.createExprVar(loopVar), OpBinary.PLUS, eINSTANCE.createExprInt(1), loopVar.getType()))); i++; } } /** * Transforms the list of expressions of an AstExpressionList (without * generators). * * @param expressions * a list of AST expressions */ private void transformListSimple(List<AstExpression> expressions) { int i = 0; for (AstExpression expression : expressions) { List<Expression> newIndexes = new ArrayList<Expression>(indexes); newIndexes.add(eINSTANCE.createExprInt(i)); new ExprTransformer(procedure, blocks, target, newIndexes) .doSwitch(expression); i++; } } }