/*
* Copyright (c) 2011, IETR/INSA of Rennes and EPFL
* 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 and EPFL 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.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.sf.orcc.df.Action;
import net.sf.orcc.df.Actor;
import net.sf.orcc.df.util.DfVisitor;
import net.sf.orcc.ir.Arg;
import net.sf.orcc.ir.ArgByVal;
import net.sf.orcc.ir.BlockBasic;
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.InstStore;
import net.sf.orcc.ir.Instruction;
import net.sf.orcc.ir.IrFactory;
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.AbstractIrVisitor;
import net.sf.orcc.ir.util.ExpressionPrinter;
import net.sf.orcc.ir.util.IrUtil;
import org.eclipse.emf.common.util.EList;
/**
* This class defines an actor transformation that transforms code so that at
* most it contains at most one store and one per static global variable per
* cycle.
*
* @author Matthieu Wipliez
* @author Thavot Richard
* @version 1.1
*
*/
public class StoreOnceTransformation extends DfVisitor<Object> {
/**
* Locks a variable whether all accesses are not static. e.g x=0, x[10] are
* static but not x[i] or is call by a procedure
*
* @author Thavot Richard
*
*/
private class GlobalsLocked extends AbstractIrVisitor<Set<Var>> {
Set<Var> globalsLockedSet = new HashSet<Var>();
@Override
public Set<Var> caseInstCall(InstCall call) {
for (Arg arg : call.getArguments()) {
if (arg.isByVal()) {
ArgByVal argByVal = (ArgByVal) arg;
if (argByVal.getValue().isExprVar()) {
Var var = ((ExprVar) argByVal.getValue()).getUse()
.getVariable();
if (var.isGlobal())
globalsLockedSet.add(var);
}
}
}
return null;
}
@Override
public Set<Var> caseInstLoad(InstLoad load) {
Var loadedVar = load.getSource().getVariable();
if (loadedVar.isGlobal()) {
if (procedure.eContainer() instanceof Action) {
EList<Expression> indexes = load.getIndexes();
if (isStaticIndexes(indexes)) {
if (indexes == null & loadedVar.getType().isList()) {
globalsLockedSet.add(loadedVar);
} else if (indexes.isEmpty()
& loadedVar.getType().isList()) {
globalsLockedSet.add(loadedVar);
}
} else {
globalsLockedSet.add(loadedVar);
}
} else {
globalsLockedSet.add(loadedVar);
}
}
return null;
}
@Override
public Set<Var> caseInstStore(InstStore store) {
Var storedVar = store.getTarget().getVariable();
if (storedVar.isGlobal()) {
if (procedure.eContainer() instanceof Action) {
EList<Expression> indexes = store.getIndexes();
if (isStaticIndexes(indexes)) {
if (indexes == null & storedVar.getType().isList()) {
globalsLockedSet.add(storedVar);
} else if (indexes.isEmpty()
& storedVar.getType().isList()) {
globalsLockedSet.add(storedVar);
}
} else {
globalsLockedSet.add(storedVar);
}
} else {
globalsLockedSet.add(storedVar);
}
}
return null;
}
@Override
public Set<Var> caseProcedure(Procedure procedure) {
globalsLockedSet.clear();
super.caseProcedure(procedure);
return globalsLockedSet;
}
/**
* Returns true whether the indexes is null or empty and whether every
* indexes are an ExprInt
*
* @param indexes
*
*/
private boolean isStaticIndexes(Collection<Expression> indexes) {
boolean s = true;
if (indexes != null) {
for (Expression expr : indexes) {
s &= expr.isExprInt();
}
}
return s;
}
}
/**
*
* @author thavot
*
*/
private class OverrideProcedure extends AbstractIrVisitor<Void> {
private int index;
private List<Var> loadedVarsList;
private Map<Procedure, List<Var>> procedureToLoadedVarsMap;
OverrideProcedure() {
procedureToLoadedVarsMap = new HashMap<Procedure, List<Var>>();
}
OverrideProcedure(Map<Procedure, List<Var>> procedureToLoadedVarsMap) {
this.procedureToLoadedVarsMap = procedureToLoadedVarsMap;
}
/**
* Adds one parameter to the given procedure for each loaded global
* variables by the given procedure.
*
* @param procedure
* a procedure
*/
private void addParameters(Procedure procedure) {
index = 0;
for (Var var : loadedVarsList) {
procedure.getParameters().add(index++,
IrFactory.eINSTANCE.createParam(IrUtil.copy(var)));
}
}
@Override
public Void caseInstCall(InstCall call) {
Procedure calledProc = call.getProcedure();
List<Var> loadedVars = procedureToLoadedVarsMap.get(calledProc);
if (loadedVars == null) {
// transform the called procedure
new OverrideProcedure(procedureToLoadedVarsMap)
.doSwitch(calledProc);
loadedVars = procedureToLoadedVarsMap.get(calledProc);
}
index = 0;
for (Var var : loadedVars) {
call.getArguments().add(index++,
IrFactory.eINSTANCE.createArgByVal(var));
}
return null;
}
@Override
public Void caseInstLoad(InstLoad load) {
Var loadedVar = load.getSource().getVariable();
if (loadedVar.isGlobal()) {
EList<Expression> indexes = load.getIndexes();
if (!loadedVarsList.contains(loadedVar)) {
loadedVarsList.add(loadedVar);
if (!loadedVar.getType().isList()) {
setGlobalAsList(loadedVar);
}
}
if (indexes.isEmpty()) {
indexes.add(IrFactory.eINSTANCE.createExprInt(0));
}
}
return null;
}
@Override
public Void caseInstStore(InstStore store) {
Var storedVar = store.getTarget().getVariable();
if (storedVar.isGlobal()) {
EList<Expression> indexes = store.getIndexes();
if (!loadedVarsList.contains(storedVar)) {
loadedVarsList.add(storedVar);
if (!storedVar.getType().isList()) {
setGlobalAsList(storedVar);
}
}
if (indexes.isEmpty()) {
indexes.add(IrFactory.eINSTANCE.createExprInt(0));
}
}
return null;
}
@Override
public Void caseProcedure(Procedure procedure) {
loadedVarsList = new ArrayList<Var>();
if (procedure.eContainer() instanceof Action) {
super.caseProcedure(procedure);
} else {
if (!procedureToLoadedVarsMap.containsKey(procedure)) {
super.caseProcedure(procedure);
addParameters(procedure);
procedureToLoadedVarsMap.put(procedure, loadedVarsList);
}
}
return null;
}
/**
* Modify the type as a list of on one value
*
* @param var
*/
private void setGlobalAsList(Var var) {
var.setType(IrFactory.eINSTANCE.createTypeList(1, var.getType()));
if (var.getInitialValue() != null) {
ExprList initValue = IrFactory.eINSTANCE.createExprList();
initValue.getValue().add(var.getInitialValue());
var.setInitialValue(initValue);
}
}
}
/**
* Transform the procedure to keep only one store
*
* @author Thavot Richard
*
*/
private class StoreTransformer extends AbstractIrVisitor<Void> {
public StoreTransformer() {
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;
}
@Override
public Void caseInstLoad(InstLoad load) {
Var loadedVar = load.getSource().getVariable();
if (loadedVar.isGlobal()) {
EList<Expression> indexes = load.getIndexes();
for (Expression e : indexes) {
super.doSwitch(e);
}
if (!globalsLockedSet.contains(loadedVar)) {
// if (isStaticIndexes(indexes)) {
Var localVar = load.getTarget().getVariable();
String key = getKey(loadedVar, indexes);
if (!keyToGlobalsMap.containsKey(key)) {
keyToGlobalsMap.put(key, loadedVar);
keyToLocalsMap.put(key, localVar);
if (!indexes.isEmpty())
keyToIndexesMap.put(key, indexes);
} else {
localToLocalsMap.put(localVar, keyToLocalsMap.get(key));
}
IrUtil.delete(load);
indexInst--;
// }
}
}
return null;
}
@Override
public Void caseInstStore(InstStore store) {
// Replace the local variable name by visiting caseExprVar
super.doSwitch(store.getValue());
//
Var storedVar = store.getTarget().getVariable();
if (storedVar.isGlobal()) {
EList<Expression> indexes = store.getIndexes();
for (Expression e : indexes) {
super.doSwitch(e);
}
if (!globalsLockedSet.contains(storedVar)) {
// if (isStaticIndexes(indexes)) {
String key = getKey(storedVar, indexes);
Var localVar = keyToLocalsMap.get(key);
if (localVar == null) {
Type type = storedVar.getType();
if (!indexes.isEmpty()) {
type = ((TypeList) type).getInnermostType();
}
localVar = procedure.newTempLocalVariable(type,
"local_" + storedVar.getName());
keyToGlobalsMap.put(key, storedVar);
keyToIndexesMap.put(key, indexes);
keyToLocalsMap.put(key, localVar);
}
BlockBasic block = store.getBlock();
keyToStoreSet.add(key);
InstAssign assign = IrFactory.eINSTANCE.createInstAssign(
localVar, store.getValue());
IrUtil.delete(store);
block.getInstructions().add(indexInst, assign);
// }
}
}
return null;
}
@Override
public Void caseProcedure(Procedure procedure) {
// if a variable is locked then no transformation is applied
globalsLockedSet = new GlobalsLocked().doSwitch(procedure);
localToLocalsMap = new HashMap<Var, Var>();
keyToGlobalsMap = new HashMap<String, Var>();
keyToIndexesMap = new HashMap<String, Collection<Expression>>();
keyToLocalsMap = new HashMap<String, Var>();
keyToStoreSet = new HashSet<String>();
this.procedure = procedure;
super.caseProcedure(procedure);
addLoads(procedure);
addStores(procedure);
return null;
}
}
private Set<Var> globalsLockedSet;
private Map<String, Var> keyToGlobalsMap;
private Map<String, Collection<Expression>> keyToIndexesMap;
private Map<String, Var> keyToLocalsMap;
private Set<String> keyToStoreSet;
private Map<Var, Var> localToLocalsMap;
/**
* Add Load instructions at the beginning of the given procedure.
*
* @param procedure
* a procedure
*/
private void addLoads(Procedure procedure) {
for (Entry<String, Var> entry : keyToGlobalsMap.entrySet()) {
String key = entry.getKey();
InstLoad load = IrFactory.eINSTANCE.createInstLoad(
keyToLocalsMap.get(key), entry.getValue());
Collection<Expression> indexes = keyToIndexesMap.get(key);
if (indexes != null) {
load.getIndexes().addAll(IrUtil.copy(indexes));
}
BlockBasic block = procedure.getFirst();
block.getInstructions().add(0, load);
}
}
/**
* Add Store instructions at the end of the given procedure.
*
* @param procedure
* a procedure
*/
private void addStores(Procedure procedure) {
for (Entry<String, Var> entry : keyToGlobalsMap.entrySet()) {
String key = entry.getKey();
if (keyToStoreSet.contains(key)) {
InstStore store = IrFactory.eINSTANCE.createInstStore(
entry.getValue(), keyToLocalsMap.get(key));
Collection<Expression> indexes = keyToIndexesMap.get(key);
if (indexes != null) {
store.getIndexes().addAll(IrUtil.copy(indexes));
}
EList<Instruction> block = procedure.getLast()
.getInstructions();
int lastInstIndex = block.size() - 1;
block.add(lastInstIndex, store);
}
}
}
@Override
public Object caseActor(Actor actor) {
new DfVisitor<Void>(new OverrideProcedure()).doSwitch(actor);
new DfVisitor<Void>(new StoreTransformer()).doSwitch(actor);
return null;
}
/**
* Return a String key according the varName and the indexes
*/
private String getKey(Var var, Collection<Expression> indexes) {
String s = var.getName();
if (indexes != null) {
for (Expression expr : indexes) {
s += "[";
s += new ExpressionPrinter().doSwitch(expr);
s += "]";
}
}
return s;
}
}