/*
* 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.classifier;
import java.util.HashMap;
import java.util.Map;
import net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.df.Action;
import net.sf.orcc.df.Actor;
import net.sf.orcc.df.Pattern;
import net.sf.orcc.df.Port;
import net.sf.orcc.df.transform.UnitImporter;
import net.sf.orcc.ir.BlockIf;
import net.sf.orcc.ir.BlockWhile;
import net.sf.orcc.ir.InstLoad;
import net.sf.orcc.ir.InstPhi;
import net.sf.orcc.ir.InstStore;
import net.sf.orcc.ir.Type;
import net.sf.orcc.ir.TypeList;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.ActorInterpreter;
import net.sf.orcc.ir.util.IrUtil;
import net.sf.orcc.ir.util.ValueUtil;
import net.sf.orcc.moc.CSDFMoC;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
/**
* This class defines an abstract interpreter of an actor. It refines the
* concrete interpreter by not relying on anything that is data-dependent.
*
* @author Matthieu Wipliez
*
*/
public class AbstractInterpreter extends ActorInterpreter {
private Map<String, Object> configuration;
protected Copier copier;
protected Action executedAction;
protected Map<Action, Action> originalActions;
private Actor originalActor;
private Map<String, Boolean> portRead;
private boolean schedulableMode;
/**
* Creates a new abstract interpreter.
*
* @param actor
* an actor
*/
public AbstractInterpreter(Actor actor) {
// save the original actor
originalActor = actor;
// create a copier, and copy the original actor
copier = new EcoreUtil.Copier();
Actor copyOfActor = IrUtil.copy(copier, actor);
new UnitImporter().doSwitch(copyOfActor);
// save actions of the original actor
originalActions = new HashMap<Action, Action>();
for (Action action : originalActor.getActions()) {
originalActions.put((Action) copier.get(action), action);
}
// also save initialize action
for (Action action : originalActor.getInitializes()) {
originalActions.put((Action) copier.get(action), action);
}
setActor(copyOfActor);
// abstract expression interpreter
exprInterpreter = new AbstractExpressionEvaluator();
initialize();
}
@Override
public Object caseInstLoad(InstLoad instr) {
try {
return super.caseInstLoad(instr);
} catch (OrccRuntimeException e) {
if (schedulableMode) {
throw e;
}
return null;
}
}
@Override
public Object caseInstPhi(InstPhi phi) {
if (branch != -1) {
return super.caseInstPhi(phi);
}
return null;
}
@Override
public Object caseInstStore(InstStore instr) {
try {
return super.caseInstStore(instr);
} catch (OrccRuntimeException e) {
if (schedulableMode) {
throw e;
}
return null;
}
}
@Override
public Object caseBlockIf(BlockIf block) {
// Interpret first expression ("if" condition)
Object condition = exprInterpreter.doSwitch(block.getCondition());
int oldBranch = branch;
if (ValueUtil.isBool(condition)) {
if (ValueUtil.isTrue(condition)) {
doSwitch(block.getThenBlocks());
branch = 0;
} else {
doSwitch(block.getElseBlocks());
branch = 1;
}
} else {
if (schedulableMode) {
// only throw exception in schedulable mode
throw new OrccRuntimeException("null condition");
}
branch = -1;
}
doSwitch(block.getJoinBlock());
branch = oldBranch;
return null;
}
@Override
public Object caseBlockWhile(BlockWhile block) {
int oldBranch = branch;
branch = 0;
doSwitch(block.getJoinBlock());
// Interpret first expression ("while" condition)
Object condition = exprInterpreter.doSwitch(block.getCondition());
if (ValueUtil.isBool(condition)) {
branch = 1;
while (ValueUtil.isTrue(condition)) {
doSwitch(block.getBlocks());
doSwitch(block.getJoinBlock());
// Interpret next value of "while" condition
condition = exprInterpreter.doSwitch(block.getCondition());
if (schedulableMode && !ValueUtil.isBool(condition)) {
throw new OrccRuntimeException(
"Condition not boolean at line "
+ block.getLineNumber() + "\n");
}
}
} else if (schedulableMode) {
// only throw exception in schedulable mode
throw new OrccRuntimeException("condition is data-dependent");
}
branch = oldBranch;
return null;
}
@Override
protected void execute(Action action) {
executedAction = action;
Pattern inputPattern = action.getInputPattern();
for (Port port : inputPattern.getPorts()) {
int numTokens = inputPattern.getNumTokens(port);
String portName = port.getName();
if (configuration != null && configuration.containsKey(portName)
&& !portRead.get(portName)) {
// Should we use a range of values in the spirit of
// "Accurate Static Branch Prediction by Value Range Propagation"?
// in the meantime, we only use the configuration value in the
// Peek
portRead.put(portName, true);
}
port.increaseTokenConsumption(numTokens);
}
// allocate output pattern (but not input pattern)
Pattern outputPattern = action.getOutputPattern();
allocatePattern(outputPattern);
// execute action
doSwitch(action.getBody());
// update token production
for (Port port : outputPattern.getPorts()) {
int numTokens = outputPattern.getNumTokens(port);
port.increaseTokenProduction(numTokens);
}
}
/**
* Returns the latest action that was executed by the latest call to
* {@link #execute()}.
*
* @return the latest executed action
*/
public Action getExecutedAction() {
// return the action of the original actor
return originalActions.get(executedAction);
}
@Override
protected boolean isSchedulable(Action action) {
// do not check the number of tokens present on FIFOs
Pattern pattern = action.getPeekPattern();
for (Port port : pattern.getPorts()) {
Var peeked = pattern.getVariable(port);
String portName = port.getName();
if (configuration != null && configuration.containsKey(portName)
&& !portRead.get(portName)) {
// allocates peeked variables
TypeList typeList = (TypeList) peeked.getType();
Object array = ValueUtil.createArray(typeList);
peeked.setValue(array);
Type type = typeList.getType();
Object value = configuration.get(portName);
ValueUtil.set(type, array, value, 0);
}
}
// enable schedulable mode
setSchedulableMode(true);
try {
Object result = doSwitch(action.getScheduler());
if (result == null) {
throw new OrccRuntimeException("could not determine if action "
+ action.toString() + " is schedulable");
}
return ValueUtil.isTrue(result);
} finally {
// disable schedulable mode
setSchedulableMode(false);
}
}
/**
* Sets the configuration that should be used by the interpreter.
*
* @param configuration
* a configuration as a map of ports and values
*/
public void setConfiguration(Map<String, Object> configuration) {
this.configuration = configuration;
portRead = new HashMap<String, Boolean>(configuration.size());
for (String port : configuration.keySet()) {
portRead.put(port, false);
}
}
/**
* Sets schedulable mode. When in schedulable mode, evaluations of null
* expressions is forbidden.
*
* @param schedulableMode
*/
public void setSchedulableMode(boolean schedulableMode) {
this.schedulableMode = schedulableMode;
((AbstractExpressionEvaluator) exprInterpreter)
.setSchedulableMode(schedulableMode);
}
/**
* Sets the token rates, i.e. token production/consumption rates, of the
* given CSDF MoC. Token rates are set using the ports of the original actor
* (the actor this interpreter was created with) and not the copy (the actor
* on which the interpreter is working).
*
* @param csdfMoc
* a CSDF MoC
*/
public void setTokenRates(CSDFMoC csdfMoc) {
// we use the ports of the original actor
Pattern inputPattern = csdfMoc.getInputPattern();
for (Port originalPort : originalActor.getInputs()) {
Port port = (Port) copier.get(originalPort);
int numTokens = port.getNumTokensConsumed();
inputPattern.setNumTokens(originalPort, numTokens);
}
Pattern outputPattern = csdfMoc.getOutputPattern();
for (Port originalPort : originalActor.getOutputs()) {
Port port = (Port) copier.get(originalPort);
int numTokens = port.getNumTokensProduced();
outputPattern.setNumTokens(originalPort, numTokens);
}
}
}