package net.sf.orcc.backends.c.dal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.df.Action;
import net.sf.orcc.df.Tag;
import net.sf.orcc.ir.Block;
import net.sf.orcc.ir.BlockBasic;
import net.sf.orcc.ir.ExprBinary;
import net.sf.orcc.ir.ExprUnary;
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.InstReturn;
import net.sf.orcc.ir.Instruction;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.OpBinary;
import net.sf.orcc.ir.OpUnary;
import net.sf.orcc.ir.Procedure;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.impl.IrFactoryImpl;
import net.sf.orcc.ir.util.AbstractIrVisitor;
import net.sf.orcc.ir.util.IrUtil;
/**
* Encapsulates an actor's guard constraints.
*
* @author James Guthrie
*
*/
public class GuardConstraint {
protected List<Instruction> instructions;
protected SortedSet<Token> tokens;
protected InstAssign compute;
protected InstReturn store;
protected String name;
public GuardConstraint(List<Instruction> instructions, String name) {
this.instructions = new ArrayList<Instruction>(IrUtil.copy(instructions));
setInstructions(this.instructions);
this.name = name;
}
public GuardConstraint(GuardConstraint g) {
this.instructions = new ArrayList<Instruction>(IrUtil.copy(g.instructions));
setInstructions(this.instructions);
this.name = g.name;
}
/**
* Given an Action a and set of tokens, return the constraints
* of a which are dependent on the given tokens. If tokens is
* <code>null</code> use all constraints.
*
* TODO: Compact this function
*
* @param a
* @param intersection
* @return
*/
public GuardConstraint(Action a, Set<Token> tokens) {
List<Instruction> constInst = new ArrayList<Instruction>();
Set<Var> vars = new HashSet<Var>();
Collection<Block> blocks = IrUtil.copy(a.getScheduler().getBlocks());
for (Block b : blocks) {
if (b.isBlockBasic()) {
// Build a list of tokens which are valid given the set of input tokens
// This covers token dependencies such as load(local_test_local_inA_1, test[local_inA_1])
int varsAdded;
Set<Token> processed = new TreeSet<Token>();
do {
varsAdded = 0;
for (Instruction i : ((BlockBasic) b).getInstructions()) {
if (i instanceof InstLoad || i instanceof InstCall) {
Token local_t;
if (i instanceof InstLoad) {
local_t = new LoadTokenImpl((InstLoad) i);
} else {
local_t = new CallTokenImpl((InstCall) i);
}
if (!processed.contains(local_t)){
// If null, throw all instructions in
if (tokens == null) {
processed.add(local_t);
varsAdded++;
vars.add(local_t.getTargetVar());
constInst.add(i);
} else {
if (tokens.contains(local_t) && local_t.depsFulfilledBy(vars)) {
processed.add(local_t);
varsAdded++;
vars.add(local_t.getTargetVar());
constInst.add(i);
tokens.remove(local_t);
}
}
}
}
}
} while(varsAdded > 0);
for (Instruction i : ((BlockBasic) b).getInstructions()) {
if (i instanceof InstAssign) {
Expression removedVars = new VarRemover(vars).doSwitch(((InstAssign) i).getValue());
((InstAssign) i).setValue(removedVars);
constInst.add(i);
} else if (i instanceof InstReturn) {
constInst.add(i);
}
}
}
}
this.instructions = constInst;
setInstructions(this.instructions);
this.name = a.getName();
}
/**
* Retains occurrences of Var in an Expression tree, returning
* the resulting expression.
*
* @author James Guthrie
*
*/
private class VarRemover extends AbstractIrVisitor<Expression> {
private List<Var> vars;
private Expression empty;
private IrFactory irFactory;
/**
* Creates an instance of the remover with the <code>Var</code> instances
* to keep stored in vars.
*
* @param vars The vars to retain in the expression tree
*/
VarRemover(Collection<? extends Var> vars) {
super(true);
this.vars = new ArrayList<Var>(vars);
irFactory = new IrFactoryImpl();
empty = irFactory.createExprBool(true);
}
@Override
public Expression caseExpression(Expression expr) {
return expr;
}
@Override
public Expression caseExprVar(ExprVar exprVar) {
for (Var v : vars) {
if (v.getName().equals(exprVar.getUse().getVariable().getName())) {
return exprVar;
}
}
return empty;
}
@Override
public Expression caseExprBinary(ExprBinary exprBinary) {
Expression left = doSwitch(exprBinary.getE1());
Expression right = doSwitch(exprBinary.getE2());
Expression nonNull = null;
if (left == empty) {
if (right == empty) {
return empty;
}
nonNull = right;
} else if (right == empty) {
nonNull = left;
}
if (nonNull == null) {
return irFactory.createExprBinary(left, exprBinary.getOp(), right, exprBinary.getType());
} else {
switch (exprBinary.getOp()) {
case LOGIC_AND:
case LOGIC_OR:
return nonNull;
default:
return empty;
}
}
}
@Override
public Expression caseExprUnary(ExprUnary exprUnary) {
Expression expr = doSwitch(exprUnary.getExpr());
if (expr == empty) {
return irFactory.createExprBool(true);
} else {
exprUnary.setExpr(expr);
return exprUnary;
}
}
}
private void setInstructions(List<Instruction> instructions) {
this.tokens = new TreeSet<Token>();
int count = 0;
Token t;
for (Instruction i : instructions) {
if (i instanceof InstLoad || i instanceof InstCall) {
if (i instanceof InstLoad) {
t = new LoadTokenImpl((InstLoad) i);
} else {
t = new CallTokenImpl((InstCall) i);
}
this.tokens.add(t);
} else if (i instanceof InstReturn) {
this.store = (InstReturn) i;
} else if (i instanceof InstAssign) {
this.compute = IrUtil.copy((InstAssign) i);
count++;
if (count > 1) {
throw new OrccRuntimeException("Cannot handle multiple compute instructions");
}
}
}
}
/**
* Given a GuardConstraint intersect the constraints return a constraint
* representing the common constraints.
*
* @param other
* @return
*/
public GuardConstraint intersect(GuardConstraint other) {
SortedSet<Token> allTokens = new TreeSet<Token>();
allTokens.addAll(this.tokens);
allTokens.addAll(other.tokens);
IrFactory irFactory = new IrFactoryImpl();
Expression thisVal = IrUtil.copy(this.compute.getValue());
Expression otherVal = IrUtil.copy(other.compute.getValue());
ExprBinary newVal = irFactory.createExprBinary(thisVal, OpBinary.LOGIC_AND, otherVal, irFactory.createTypeBool());
InstAssign newAssign = irFactory.createInstAssign(this.compute.getTarget().getVariable(), newVal);
List<Instruction> instructions = new ArrayList<Instruction>();
for (Token t : allTokens) {
instructions.add(t.getInstruction());
}
instructions.add(newAssign);
instructions.add(IrUtil.copy(this.store));
return new GuardConstraint(instructions, this.name + "_with_" + other.name);
}
/**
* Given a GuardConstraint return the difference of this constraint with other:
* this \other.
*
* @param other
* @return
*/
public GuardConstraint difference(GuardConstraint other) {
SortedSet<Token> allTokens = new TreeSet<Token>();
allTokens.addAll(this.tokens);
allTokens.addAll(other.tokens);
IrFactory irFactory = new IrFactoryImpl();
Expression thisVal = IrUtil.copy(this.compute.getValue());
Expression otherVal = IrUtil.copy(other.compute.getValue());
otherVal = irFactory.createExprUnary(OpUnary.LOGIC_NOT, otherVal, irFactory.createTypeBool());
ExprBinary newVal = irFactory.createExprBinary(thisVal, OpBinary.LOGIC_AND, otherVal, irFactory.createTypeBool());
InstAssign newAssign = irFactory.createInstAssign(this.compute.getTarget().getVariable(), newVal);
List<Instruction> instructions = new ArrayList<Instruction>();
for (Token t : allTokens) {
instructions.add(t.getInstruction());
}
instructions.add(newAssign);
instructions.add(IrUtil.copy(this.store));
return new GuardConstraint(instructions, this.name + "_without_" + other.name);
}
/**
* The instructions required to execute this constraint
* @return
*/
public Collection<Instruction> toInstruction() {
return new ArrayList<Instruction>(instructions);
}
/**
* Set the modify the instructions of action to reflect the
* constraints in this object
* @param action
*/
public boolean setConstraint(Action action) {
List<Instruction> instructions = new ArrayList<Instruction>();
SortedSet<Token> tokens = new TreeSet<Token>();
Collection<Var> deps = new HashSet<Var>();
// Gather all state tokens
for (Token t : this.tokens) {
if (t.isStateToken()) {
tokens.add(t);
deps.addAll(t.dependencies());
}
}
for (Block b : action.getScheduler().getBlocks()) {
if (b instanceof BlockBasic) {
for (Instruction i : ((BlockBasic) b).getInstructions()) {
if (i instanceof InstLoad || i instanceof InstCall) {
Token t;
if (i instanceof InstLoad) {
t = new LoadTokenImpl((InstLoad) i);
} else {
t = new CallTokenImpl((InstCall) i);
}
tokens.add(t);
deps.addAll(t.dependencies());
}
}
}
}
deps.addAll(new GetVars().doSwitch(compute));
for (Token t : tokens) {
if (t.in(deps)) {
instructions.add(t.getInstruction());
}
}
// Keep everything except for the new InstAssign
for (Block b : action.getScheduler().getBlocks()) {
if (b instanceof BlockBasic) {
for (Instruction i : ((BlockBasic) b).getInstructions()) {
if (i instanceof InstAssign) {
instructions.add(compute);
} else if (!(i instanceof InstLoad) && !(i instanceof InstCall)) {
instructions.add(i);
}
}
}
}
ConstraintValidator validator = new ConstraintValidator();
IrFactory irFactory = new IrFactoryImpl();
Procedure sched = irFactory.createProcedure("isSchedulable_" + this.name, 0, irFactory.createTypeBool());
BlockBasic lbb = irFactory.createBlockBasic();
lbb.getInstructions().addAll(instructions);
sched.getBlocks().add(lbb);
action.setScheduler(sched);
Tag t = IrUtil.copy(action.getTag());
t.add(this.name);
action.setTag(t);
return validator.validate(compute, tokens);
}
/**
* Whether the constraints in this are equivalent to those
* in action.
*
* @param action
* @return
*/
public boolean equivalent(Action action) {
/**
* This evaluates whether the constructed constraints are equivalent to the
* action's constraints based on whether the tokens and compute contain the
* same vars. If they don't, they're definitely not equivalent, if they do
* there is still a chance that they're not equivalent but this doesn't
* perform an exhaustive check.
*/
GetVars varGetter = new GetVars();
Collection<Var> thisVars = varGetter.doSwitch(this.compute);
for (Token t : this.tokens) {
thisVars.addAll(varGetter.doSwitch(t.getInstruction()));
}
GuardConstraint actionGuards = new GuardConstraint(action, null);
Collection<Var> actionVars = varGetter.doSwitch(actionGuards.compute);
for (Token t : actionGuards.tokens) {
actionVars.addAll(varGetter.doSwitch(t.getInstruction()));
}
for (Var actionV : actionVars) {
boolean flag = false;
for (Var thisV : thisVars) {
if (thisV.getName().equals(actionV.getName())) {
flag = true;
}
}
if (flag == false) {
return false;
}
}
return true;
}
@Override
public String toString() {
return instructions.toString();
}
}