/*
* Copyright (c) 2013, Abo Akademi University
* 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 Abo Akademi University 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.promela.transform;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.ExprVar;
import net.sf.orcc.ir.InstStore;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.AbstractIrVisitor;
/**
*
*
* @author Johan Ersfolk
*
*/
public class IdentifyStatelessActors extends DfVisitor<Void> {
private class ActionBeh {
private Set<Write> writes = new HashSet<Write>();
private Set<Var> resetVars = new HashSet<Var>();
private Set<Var> outputVars = new HashSet<Var>();
private Set<Var> inputVars = new HashSet<Var>();
private Set<Var> updateVars = new HashSet<Var>();
private Set<Var> usedVars = new HashSet<Var>();
public void buildStats() {
for (Write w : writes) {
if (!w.isPort(w.target)) {
if (w.sources.size()==0) { resetVars.add(w.target); }
for (Var v : w.sources) {
if (w.isPort(v)) { inputVars.add(w.target); }
else if (w.target==v) { updateVars.add(v); }
}
} else {
for (Var v : w.sources) {
if (!w.isPort(v)) { outputVars.add(v);}
}
}
usedVars.addAll(w.sources);
}
}
}
private class Write {
Action action;
Var target;
Set<Var> sources;
public Write(Action action, Var target) {
this.action=action;
this.target=target;
sources = new HashSet<Var>();
}
public boolean isPort(Var var){
if (action.getOutputPattern().getVariables().contains(var)) {
return true;
} else if (action.getInputPattern().getVariables().contains(var)){
return true;
}
return false;
}
public void removeLocalAndConstantVars() {
Iterator<Var> i = sources.iterator();
while (i.hasNext()) {
Var v = i.next();
if (!(isPort(v) || actor.getStateVars().contains(v)&&v.isAssignable())) {
i.remove();
}
}
}
@Override
public String toString() {
String s = "Action "+action.getName()+" Store-var: "+target.getName();
if (isPort(target)) {
s+="(P) -> {";
} else {
s+="(V) -> {";
}
for (Var v : sources) {
s+=v.getName();
if (isPort(v)) {
s+="(P), ";
} else {
s+="(V), ";
}
}
s+="}";
return s;
}
}
private class InnerIrVisitor extends AbstractIrVisitor<Void> {
private Write current;
public InnerIrVisitor() {
super(false);
}
@Override
public Void caseExprVar(ExprVar var) {
current.sources.add(var.getUse().getVariable());
return null;
}
@Override
public Void caseInstStore(InstStore store) {
current=new Write(action, store.getTarget().getVariable());
doSwitch(store.getValue());
Set<Var>tempSet=new HashSet<Var>();
for (Var v : current.sources) {
tempSet.addAll(varGraph.getReachableVars(v));
}
current.sources.addAll(tempSet);
current.removeLocalAndConstantVars();
if (!actionBehaviourMap.containsKey(action)) {
actionBehaviourMap.put(action, new ActionBeh());
}
actionBehaviourMap.get(action).writes.add(current);
return null;
}
}
private VariableGraph varGraph;
private Action action;
private Map<Action, ActionBeh> actionBehaviourMap = new HashMap<Action, ActionBeh>();
private Set<Var> pureControlVars;
public IdentifyStatelessActors(VariableGraph varGraph) {
super();
this.varGraph=varGraph;
this.irVisitor = new InnerIrVisitor();
}
@Override
public Void caseAction(Action action) {
this.action = action;
actionBehaviourMap.put(action, new ActionBeh());
doSwitch(action.getBody());
for (Var var : action.getOutputPattern().getVariables()) {
Write current=new Write(action, var);
current.sources.addAll(varGraph.getReachableVars(var));
current.removeLocalAndConstantVars();
actionBehaviourMap.get(action).writes.add(current);
}
actionBehaviourMap.get(action).buildStats();
return null;
}
@Override
public Void caseActor(Actor actor) {
this.actor = actor;
for (Action action : actor.getActions()) {
doSwitch(action);
//System.out.println(actionToStateChanges.get(action));
}
// find the vars not connected to output
pureControlVars = new HashSet<Var>(actor.getStateVars());
for (ActionBeh ab : actionBehaviourMap.values()) {
pureControlVars.removeAll(ab.outputVars);
}
isStatelessLevel0();
isStatelessLevel1();
return null;
}
private boolean isStatelessLevel0() {
if (actor.getStateVars().size()==0) {
System.out.println("Actor "+actor.getName()+" Is stateless level 0 (no state vars)");
return true;
}
return false;
}
private boolean isStatelessLevel1() {
for (Action action : actionBehaviourMap.keySet()) {
for (Write sc : actionBehaviourMap.get(action).writes){
if (sc.isPort(sc.target)) {
for (Var var : sc.sources) {
if (!sc.isPort(var)) {
return false;
}
}
}
}
}
System.out.println("Actor "+actor.getName()+" Is stateless level 1 (output does not depend on state vars)");
return true;
}
private Set<Var> getStateTransfer(Action action1, Action action2) {
Set<Var> state1 = new HashSet<Var>();
Set<Var> state2 = new HashSet<Var>();
state1.addAll(actionBehaviourMap.get(action1).resetVars);
state1.addAll(actionBehaviourMap.get(action1).updateVars);
state1.addAll(actionBehaviourMap.get(action1).inputVars);
state2.addAll(actionBehaviourMap.get(action2).usedVars);
state1.retainAll(state2);
return state1;
}
public boolean isActionSequenceStateless(List<Action> actions) {
// the variables that the first sets and the second uses
for (int i = 0, j = 1; j < actions.size(); i++, j++) {
Action action1 = actions.get(i);
Action action2 = actions.get(j);
Set<Var> transfer = getStateTransfer(action1, action2);
for (Var var : transfer) {
if (actionBehaviourMap.get(action1).inputVars.contains(var)) {
System.out.println("Actions "+ action1.getName() + " and " + action2.getName() + " cannot run in parallel");
return false;
}
}
System.out.println("Actions "+ action1.getName() + " and " + action2.getName() + " share state "+transfer);
}
return false;
}
}