/*
* Copyright (c) 2011, 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.Map;
import java.util.Set;
import java.util.Stack;
import net.sf.orcc.df.Action;
import net.sf.orcc.df.Actor;
import net.sf.orcc.df.Connection;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Pattern;
import net.sf.orcc.df.Port;
import net.sf.orcc.df.util.DfVisitor;
import net.sf.orcc.ir.BlockIf;
import net.sf.orcc.ir.BlockWhile;
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.Var;
import net.sf.orcc.ir.util.AbstractIrVisitor;
/**
* This class extracts the variables/ports needed to schedule a network. The
* resulting information is used by the promela backend to highlight the
* variables that we need to observe when generating schedules.
*
* @author Johan Ersfolk
*
*/
@Deprecated
public class NetworkStateDefExtractor extends DfVisitor<Void> {
private class InnerIrVisitor extends AbstractIrVisitor<Void> {
public InnerIrVisitor() {
super(true);
}
@Override
public Void caseBlockIf(BlockIf nodeIf) {
ifConditionVars.push(new HashSet<Var>());
inIfCondition = true;
doSwitch(nodeIf.getCondition());
inIfCondition = false;
doSwitch(nodeIf.getThenBlocks());
doSwitch(nodeIf.getElseBlocks());
ifConditionVars.pop();
doSwitch(nodeIf.getJoinBlock());
return null;
}
@Override
public Void caseBlockWhile(BlockWhile nodeWhile) {
whileConditionVars.push(new HashSet<Var>());
inWhileCondition = true;
doSwitch(nodeWhile.getCondition());
inWhileCondition = false;
doSwitch(nodeWhile.getBlocks());
whileConditionVars.pop();
doSwitch(nodeWhile.getJoinBlock());
return null;
}
@Override
public Void caseExprVar(ExprVar var) {
if (inIfCondition) {
ifConditionVars.peek().add(var.getUse().getVariable());
} else if (inWhileCondition) {
whileConditionVars.peek().add(var.getUse().getVariable());
} else {
addVariableDep(currentTargetVar, var.getUse().getVariable());
}
return null;
}
@Override
public Void caseInstAssign(InstAssign assign) {
addTargetVar(assign.getTarget().getVariable());
super.caseInstAssign(assign);
currentTargetVar = null;
return null;
}
@Override
public Void caseInstReturn(InstReturn inst) {
// skip returns
return null;
}
@Override
public Void caseInstCall(InstCall call) {
if (call.hasResult()) {
addTargetVar(call.getTarget().getVariable());
if (call.getProcedure().isNative()) {
varsFromNativeProcedures.add(call.getTarget().getVariable());
}
}
return super.caseInstCall(call);
}
@Override
public Void caseInstLoad(InstLoad load) {
addVariableDep(load.getTarget().getVariable(), load.getSource()
.getVariable());
for (Expression e : load.getIndexes()) {
doSwitch(e);
}
if (inScheduler) {
// this might not be needed as 'casePattern' should do the same
varsUsedInScheduling.add(load.getSource().getVariable());
}
return null;
}
@Override
public Void caseInstPhi(InstPhi phi) {
addTargetVar(phi.getTarget().getVariable());
return super.caseInstPhi(phi);
}
@Override
public Void caseInstStore(InstStore store) {
addTargetVar(store.getTarget().getVariable());
doSwitch(store.getValue());
for (Expression e : store.getIndexes()) {
doSwitch(e);
}
return null;
}
}
private Stack<Set<Var>> ifConditionVars = new Stack<Set<Var>>();
private Stack<Set<Var>> whileConditionVars = new Stack<Set<Var>>();
private Var currentTargetVar = null;
private Map<Port, Port> fifoTargetToSourceMap = new HashMap<Port, Port>();
public Set<Var> getVarsFromNativeProcedures() {
return varsFromNativeProcedures;
}
private boolean inIfCondition = false;
private boolean inWhileCondition = false;
private Set<Port> inputPortsUsedInScheduling = new HashSet<Port>();
private boolean inScheduler = false;
private Set<Port> outputPortsUsedInScheduling = new HashSet<Port>();
private Map<Port, Set<Var>> outputPortToDepVariablesMap = new HashMap<Port, Set<Var>>();
private Map<Port, Set<Port>> outputPortToInputPortMap = new HashMap<Port, Set<Port>>();
private Set<Port> portsUsedInScheduling = new HashSet<Port>();
private Map<Var, Set<Var>> variableDependency = new HashMap<Var, Set<Var>>();
private Map<Var, Set<Var>> variableDependencyNoIf = new HashMap<Var, Set<Var>>();
private Set<Var> variablesWithLoops = new HashSet<Var>();
private Set<Var> varsUsedInScheduling = new HashSet<Var>();
private Set<Var> varsFromNativeProcedures = new HashSet<Var>();
private Set<Var> visited = new HashSet<Var>();
private PromelaSchedulingModel schedulingModel;
public PromelaSchedulingModel getSchedulingModel() {
return schedulingModel;
}
public NetworkStateDefExtractor(PromelaSchedulingModel schedulingModel) {
this.irVisitor = new InnerIrVisitor();
this.schedulingModel = schedulingModel;
}
/*
* Also adds "target" 'depends on' condition, this is used if the variable
* in set within a loop or if-statement as the value of the target variable
* the also depends on the condition.
*/
private void addTargetVar(Var target) {
currentTargetVar = target;
if (!variableDependency.containsKey(target)) {
variableDependency.put(target, new HashSet<Var>());
variableDependencyNoIf.put(target, new HashSet<Var>());
}
for (Set<Var> s : whileConditionVars) {
variableDependency.get(target).addAll(s);
variableDependencyNoIf.get(target).addAll(s);
}
for (Set<Var> s : ifConditionVars) {
variableDependency.get(target).addAll(s);
}
}
/*
* Adds the "target" 'depends on' "source" to the relation.
*/
private void addVariableDep(Var target, Var source) {
if (!variableDependency.containsKey(target)) {
variableDependency.put(target, new HashSet<Var>());
variableDependencyNoIf.put(target, new HashSet<Var>());
}
variableDependency.get(target).add(source);
variableDependencyNoIf.get(target).add(source);
}
void analyzeVarDeps() {
visited.clear();
for (Var currentVar : variableDependency.keySet()) {
getTransitiveClosure(currentVar, visited, true);
if (visited.contains(currentVar)) {
variablesWithLoops.add(currentVar);
}
visited.clear();
}
}
@Override
public Void caseAction(Action action) {
// solve the port dependency, procedures and functions should also be
// handled
inScheduler = true;
doSwitch(action.getScheduler());
doSwitch(action.getPeekPattern());
inScheduler = false;
doSwitch(action.getBody());
return null;
}
@Override
public Void caseActor(Actor actor) {
for (Action action : actor.getActions()) {
doSwitch(action);
}
// Find self-loops in transitive closure (a var depending on itself)
analyzeVarDeps();
// For each output port, find the variables and input ports used to
// produce the output value
for (Action action : actor.getActions()) {
for (Port port : action.getOutputPattern().getPorts()) {
visited.clear();
Var portVar = action.getOutputPattern().getVariable(port);
getTransitiveClosure(portVar, visited, true);
if (!outputPortToInputPortMap.containsKey(port)) {
outputPortToDepVariablesMap.put(port, new HashSet<Var>());
outputPortToInputPortMap.put(port, new HashSet<Port>());
}
for (Var var : visited) {
outputPortToDepVariablesMap.get(port).add(var);
if (action.getInputPattern().contains(var)) {
outputPortToInputPortMap.get(port).add(
action.getInputPattern().getPort(var));
}
}
}
// for each variable mentioned in a guard, find input ports on which these depend
for (Var var : varsUsedInScheduling) {
visited.clear();
getTransitiveClosure(var, visited, true);
for (Var v : visited) {
if (action.getInputPattern().contains(v)) {
inputPortsUsedInScheduling.add(action.getInputPattern().getPort(v));
}
}
}
}
return null;
}
@Override
public Void caseNetwork(Network network) {
for (Actor actor : network.getAllActors()) {
this.actor = actor;
doSwitch(actor);
}
identifyControlTokenPorts(network);
identifySchedulingVars();
return null;
}
@Override
public Void casePattern(Pattern pattern) {
// Only Peek patterns will end up here, however this is done on visit actor
inputPortsUsedInScheduling.addAll(pattern.getPorts());
varsUsedInScheduling.addAll(pattern.getVariables());
return null;
}
/**
* @return the portsUsedInScheduling
*/
public Set<Port> getPortsUsedInScheduling() {
return portsUsedInScheduling;
}
public void getTransitiveClosure(Var variable, Set<Var> transitiveClosure, boolean includeIfConditions) {
Map<Var, Set<Var>> varDep = includeIfConditions ? variableDependency : variableDependencyNoIf;
if (varDep.containsKey(variable)) {
for (Var v : varDep.get(variable)) {
if (!transitiveClosure.contains(v)) {
transitiveClosure.add(v);
getTransitiveClosure(v, transitiveClosure, includeIfConditions);
}
}
}
}
public void getActionLocalTransitiveClosure(Var variable, Set<Var> transitiveClosure) {
Map<Var, Set<Var>> varDep = variableDependency;
if (varDep.containsKey(variable)) {
for (Var v : varDep.get(variable)) {
if (!transitiveClosure.contains(v)) {
transitiveClosure.add(v);
// We stop at global variables
if (v.isLocal()) {
getActionLocalTransitiveClosure(v, transitiveClosure);
}
}
}
}
}
/**
* @return the variableDependency
*/
public Map<Var, Set<Var>> getVariableDependency() {
return variableDependency;
}
/**
* @return the varsUsedInScheduling
*/
public Set<Var> getVarsUsedInScheduling() {
return varsUsedInScheduling;
}
public boolean hasLoop(Var var) {
return variablesWithLoops.contains(var);
}
/*
* 1) Collects a map from Actor input ports to the output port the
* corresponding fifo is connected to. The map describes where the inputs
* comes from. 2.1) Finds output ports that produce control tokens 2.2)
* Finds input ports connected to output ports found in (2.1)
*
* @param network
*/
private void identifyControlTokenPorts(Network network) {
for (Connection con : network.getConnections()) {
fifoTargetToSourceMap.put(con.getTargetPort(), con.getSourcePort());
}
Set<Port> temp = new HashSet<Port>();
while (true) {
for (Port port : inputPortsUsedInScheduling) {
if (fifoTargetToSourceMap.containsKey(port)) {
outputPortsUsedInScheduling.add(fifoTargetToSourceMap
.get(port));
if (fifoTargetToSourceMap.get(port) != null) {
for (Port in : outputPortToInputPortMap
.get(fifoTargetToSourceMap.get(port))) {
if (!inputPortsUsedInScheduling.contains(in)) {
temp.add(in);
}
}
}
}
}
if (temp.isEmpty()) {
break;
} else {
inputPortsUsedInScheduling.addAll(temp);
temp.clear();
}
}
portsUsedInScheduling.addAll(inputPortsUsedInScheduling);
portsUsedInScheduling.addAll(outputPortsUsedInScheduling);
}
private void identifySchedulingVars() {
for (Port port : outputPortsUsedInScheduling) {
if (outputPortToDepVariablesMap.containsKey(port)) {
varsUsedInScheduling.addAll(outputPortToDepVariablesMap
.get(port));
}
}
}
}