/*
* Copyright (c) 2009-2010, 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.tools.merger.action;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.orcc.df.Action;
import net.sf.orcc.df.Actor;
import net.sf.orcc.df.DfFactory;
import net.sf.orcc.df.FSM;
import net.sf.orcc.df.Pattern;
import net.sf.orcc.df.Port;
import net.sf.orcc.df.State;
import net.sf.orcc.df.Transition;
import net.sf.orcc.graph.Edge;
import net.sf.orcc.ir.Block;
import net.sf.orcc.ir.BlockBasic;
import net.sf.orcc.ir.BlockIf;
import net.sf.orcc.ir.Def;
import net.sf.orcc.ir.Expression;
import net.sf.orcc.ir.InstLoad;
import net.sf.orcc.ir.InstReturn;
import net.sf.orcc.ir.InstStore;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.Procedure;
import net.sf.orcc.ir.Use;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.AbstractIrVisitor;
import net.sf.orcc.ir.util.IrUtil;
import net.sf.orcc.util.OrccLogger;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* This class defines a transformation that merges actions that have the same
* input/output patterns together. As a matter of fact, it is possible to
* represent SDF actors with several actions that have guards on input tokens,
* which means that when interpreted by the abstract interpreter, these actors
* would be classified as dynamic, and we do not want that.
*
* @author Matthieu Wipliez
* @author Herve Yviquel
*
*/
public class GuardInternalizer {
private class UpdatePortAccesses extends AbstractIrVisitor<Void> {
Pattern inputPattern;
Pattern outputPattern;
public UpdatePortAccesses(Pattern inputPattern, Pattern outputPattern) {
this.inputPattern = inputPattern;
this.outputPattern = outputPattern;
}
@Override
public Void caseInstLoad(InstLoad load) {
Use use = load.getSource();
Var var = use.getVariable();
if (var.eContainer() instanceof Pattern) {
Pattern oldPattern = (Pattern) var.eContainer();
Port port = oldPattern.getPort(var);
use.setVariable(inputPattern.getVariable(port));
}
return null;
}
@Override
public Void caseInstStore(InstStore store) {
Def def = store.getTarget();
Var var = def.getVariable();
if (var.eContainer() instanceof Pattern) {
Pattern oldPattern = (Pattern) var.eContainer();
Port port = oldPattern.getPort(var);
def.setVariable(outputPattern.getVariable(port));
}
return null;
}
}
private Actor actor;
private final DfFactory dfFactory = DfFactory.eINSTANCE;
private int i;
private final IrFactory irFactory = IrFactory.eINSTANCE;
/**
* Creates a new classifier
*/
public GuardInternalizer() {
}
private void clean(Actor actor) {
Set<Action> usedActions = new HashSet<Action>();
usedActions.addAll(actor.getActionsOutsideFsm());
usedActions.addAll(actor.getInitializes());
if (actor.hasFsm()) {
for (Edge edge : actor.getFsm().getEdges()) {
Transition transition = (Transition) edge;
usedActions.add(transition.getAction());
}
}
for (Action action : new ArrayList<Action>(actor.getActions())) {
if (!usedActions.contains(action)) {
IrUtil.delete(action);
}
}
}
/**
* Creates an isSchedulable.
*
* @return a procedure
*/
private Procedure createScheduler(String name) {
Procedure procedure = irFactory.createProcedure(
"isSchedulable_" + name, 0, irFactory.createTypeBool());
BlockBasic block = procedure.getLast();
block.add(irFactory.createInstReturn((irFactory.createExprBool(true))));
return procedure;
}
private Expression getCondition(Action action) {
BlockBasic block = IrUtil.getLast(action.getScheduler().getBlocks());
InstReturn ret = (InstReturn) block.getInstructions().get(
block.getInstructions().size() - 1);
EcoreUtil.remove(ret);
return ret.getValue();
}
private List<Action> getExecutableActions(State state) {
List<Action> executableActions = new ArrayList<Action>();
for (Edge edge : state.getOutgoing()) {
Transition transition = (Transition) edge;
executableActions.add(transition.getAction());
}
return executableActions;
}
private boolean internalizable(List<Action> actions) {
if (actions != null && !actions.isEmpty()) {
Pattern baseInput = actions.get(0).getInputPattern();
Pattern baseOutput = actions.get(0).getOutputPattern();
for (Action action : actions) {
Pattern input = action.getInputPattern();
Pattern output = action.getOutputPattern();
if (!(baseInput.isSupersetOf(input)
&& baseOutput.isSupersetOf(output)
&& input.isSupersetOf(baseInput) && output
.isSupersetOf(baseOutput))) {
return false;
}
}
return true;
}
return false;
}
private boolean internalizable(State state) {
if (state.getSuccessors().size() > 1) {
return false;
}
return internalizable(getExecutableActions(state));
}
/**
* Merges the given actions to a single action.
*
* @param actions
* a list of actions that have the same input/output patterns
* @return a new action
*/
private Action mergeActions(List<Action> actions) {
if (actions.size() > 1) {
OrccLogger.traceln("Internalize some actions of " + actor.getName()
+ " " + actions);
String name = "";
for (Action mergedAction : actions) {
name = name + mergedAction.getName();
}
name = name + i;
i++;
Pattern input = EcoreUtil.copy(actions.get(0).getInputPattern());
Pattern output = EcoreUtil.copy(actions.get(0).getOutputPattern());
Pattern peek = dfFactory.createPattern();
Procedure scheduler = createScheduler(name);
Procedure body = mergeBodies(name, input, output, actions);
Action action = dfFactory.createAction(name, input, output, peek,
scheduler, body);
actor.getActions().add(action);
return action;
} else {
return actions.get(0);
}
}
private Procedure mergeBodies(String name, Pattern input, Pattern output,
List<Action> actions) {
Procedure body = irFactory.createProcedure(name, 0,
irFactory.createTypeVoid());
List<Block> elseBlocks = body.getBlocks();
for (Action action : actions) {
Action actionCopy = IrUtil.copy(action);
new UpdatePortAccesses(input, output)
.doSwitch(actionCopy.getBody());
new UpdatePortAccesses(input, output).doSwitch(actionCopy
.getScheduler());
BlockIf blockIf = irFactory.createBlockIf();
blockIf.setCondition(getCondition(actionCopy));
renameVariables(actionCopy.getScheduler(), body);
body.getLocals().addAll(actionCopy.getScheduler().getLocals());
elseBlocks.addAll(actionCopy.getScheduler().getBlocks());
renameVariables(actionCopy.getBody(), body);
body.getLocals().addAll(actionCopy.getBody().getLocals());
blockIf.getThenBlocks().addAll(actionCopy.getBody().getBlocks());
blockIf.setJoinBlock(irFactory.createBlockBasic());
elseBlocks.add(blockIf);
elseBlocks = blockIf.getElseBlocks();
IrUtil.delete(actionCopy);
}
BlockBasic lastBlock = body.getLast();
lastBlock.add(irFactory.createInstReturn());
return body;
}
private void renameVariables(Procedure oldProc, Procedure newProc) {
for (Var var : oldProc.getLocals()) {
String varName = var.getName();
for (int i = 0; newProc.getLocal(varName) != null
|| actor.getStateVar(varName) != null
|| (oldProc.getLocal(varName) != null && oldProc
.getLocal(varName) != var); i++) {
varName = var.getName() + i;
}
var.setName(varName);
}
}
public void transform(Actor actor) {
this.actor = actor;
this.i = 0;
if (actor.hasFsm()) {
FSM fsm = actor.getFsm();
for (State currentState : fsm.getStates()) {
if (internalizable(currentState)) {
State target = (State) currentState.getSuccessors().get(0);
Action action = mergeActions(getExecutableActions(currentState));
fsm.removeEdges(currentState.getOutgoing());
fsm.addTransition(currentState, action, target);
}
}
}
if (internalizable(actor.getActionsOutsideFsm())) {
Action action = mergeActions(actor.getActionsOutsideFsm());
actor.getActionsOutsideFsm().clear();
actor.getActionsOutsideFsm().add(action);
}
clean(actor);
}
}