/* * 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.Pattern; 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 an actor. * * @author Johan Ersfolk * */ public class VariableGraph extends DfVisitor<Void> { private Stack<Set<Var>> ifConditionVars = new Stack<Set<Var>>(); private Stack<Set<Var>> whileConditionVars = new Stack<Set<Var>>(); private Var currentTargetVar = null; private boolean inIfCondition = false; private boolean inWhileCondition = false; private boolean inScheduler = false; protected Map<Var, Set<Var>> variableDependency = new HashMap<Var, Set<Var>>(); protected Map<Var, Set<Var>> variableDependencyNoIf = new HashMap<Var, Set<Var>>(); protected Set<Var> localSchedulingVars = new HashSet<Var>(); private Set<Var> varsFromNativeProcedures = new HashSet<Var>(); private Map<Var, Set<Var>> tcCache = new HashMap<Var, Set<Var>>(); private Map<Var, Set<Var>> tcCacheNoIf = new HashMap<Var, Set<Var>>(); private Set<Var> reachableSchedVarsCache = null; public VariableGraph(Actor actor) { this.irVisitor = new InnerIrVisitor(); this.actor = actor; doSwitch(actor); } public void clearCaches() { tcCache.clear(); tcCacheNoIf.clear(); reachableSchedVarsCache=null; } /* * 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); } @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 init : actor.getInitializes()) { doSwitch(init); } for (Action action : actor.getActions()) { doSwitch(action); } 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()); localSchedulingVars.addAll(pattern.getVariables()); return null; } public Set<Var> getReachableVars(Var var) { Set<Var> vars = new HashSet<Var>(); getTransitiveClosure(var, vars, true); return vars; } public void getTransitiveClosure(Var variable, Set<Var> transitiveClosure, boolean includeIfConditions) { Map<Var, Set<Var>> varDep = includeIfConditions ? variableDependency : variableDependencyNoIf; Map<Var, Set<Var>> cache = includeIfConditions ? tcCache : tcCacheNoIf; if (cache.containsKey(variable)) { transitiveClosure.addAll(cache.get(variable)); } else if (varDep.containsKey(variable)) { for (Var v : varDep.get(variable)) { if (!transitiveClosure.contains(v)) { transitiveClosure.add(v); getTransitiveClosure(v, transitiveClosure, includeIfConditions); } } cache.put(variable, new HashSet<Var>(transitiveClosure)); } } /** * @return the variableDependency */ public Map<Var, Set<Var>> getVariableDependency() { return variableDependency; } public boolean hasLoop(Var var) { Set<Var> tc = new HashSet<Var>(); getTransitiveClosure(var, tc, true); if (tc.contains(var)) {return true;} return false; } /** * @return the varsUsedInScheduling */ public Set<Var> getVarsDirectlyUsedInScheduling() { return localSchedulingVars; } public Set<Var> getAllReacableSchedulingVars() { if (reachableSchedVarsCache!=null) { return reachableSchedVarsCache; } Set<Var> variables = new HashSet<Var>(localSchedulingVars); for (Var v : localSchedulingVars) { Set<Var> tc = new HashSet<Var>(); getTransitiveClosure(v, tc, true); variables.addAll(tc); } reachableSchedVarsCache=variables; return variables; } public Set<Var> getVarsFromNativeProcedures() { return varsFromNativeProcedures; } 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()); addTargetVar(load.getTarget().getVariable()); for (Expression e : load.getIndexes()) { doSwitch(e); } if (inScheduler) { // this might not be needed as 'casePattern' should do the same localSchedulingVars.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; } } }