/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package com.ibm.wala.memsat.translation.concurrent;
import static com.ibm.wala.memsat.util.Graphs.nodes;
import static com.ibm.wala.memsat.util.Graphs.root;
import static com.ibm.wala.memsat.util.Graphs.transitiveClosure;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.memsat.concurrent.Execution;
import com.ibm.wala.memsat.concurrent.Program;
import com.ibm.wala.memsat.frontEnd.InlinedInstruction;
import com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action;
import com.ibm.wala.memsat.frontEnd.WalaConcurrentInformation;
import com.ibm.wala.memsat.frontEnd.WalaInformation;
import com.ibm.wala.memsat.translation.MethodTranslation;
import com.ibm.wala.memsat.util.Nodes;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.traverse.DFS;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.instance.Bounds;
import kodkod.instance.TupleFactory;
import kodkod.instance.TupleSet;
import kodkod.instance.Universe;
/**
* Implementation of the {@linkplain Program} interface based on the
* Miniatur translation.
* @author etorlak
*/
final class ConcurrentProgram implements Program {
private final ConcurrentMemoryHandler handler;
private final ConcurrentFactory factory;
private final WalaInformation info;
private final Map<CGNode,MethodTranslation> transls;
private final Map<CGNode, Relation> threads;
private final Relation /* Action ->one Thread */ thread;
private final Relation /* Thread -> Thread */ endsBefore;
/**
* Constructs a new ConcurrentProgram using the provided memory handler and translation that
* were generation using the given handler.
* @requires handler.factory.info.threads.nodes = transls.keySet()
*/
public ConcurrentProgram(ConcurrentMemoryHandler handler, Map<CGNode,MethodTranslation> transls) {
this.handler = handler;
this.factory = handler.factory;
this.info = factory.base().info();
this.transls = transls;
this.threads = new LinkedHashMap<CGNode, Relation>();
for(Iterator<CGNode> itr = DFS.iterateDiscoverTime(info.threads(), root(info.threads())); itr.hasNext(); ) {
final CGNode root = itr.next();
threads.put(root, Relation.unary(root.getMethod().getName().toString()));
}
this.thread = Relation.binary("thread");
this.endsBefore = Relation.binary("endsBefore");
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#allOf(com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action[])
*/
public Expression allOf(Action... kind) {
switch(kind.length) {
case 0 : return Expression.NONE;
case 1 : return factory.valueOf(kind[0]);
default :
final Set<Expression> acts = new LinkedHashSet<Expression>(kind.length*2);
for(Action a : kind) { acts.add(factory.valueOf(a)); }
return Expression.union(acts);
}
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#actionsOf(kodkod.ast.Expression)
*/
public Expression actionsOf(Expression thread) { return this.thread.join(thread); }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#threadOf(kodkod.ast.Expression)
*/
public Expression threadOf(Expression action) { return action.join(thread); }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#threads(java.util.Set)
*/
public Expression threads(Set<CGNode> roots) {
switch(roots.size()) {
case 0 : return Expression.NONE;
case 1 : return threads.get(roots.iterator().next());
default :
final Set<Expression> exprs = new LinkedHashSet<Expression>();
for(CGNode root : roots) {
exprs.add(threads.get(root));
}
return Expression.union(exprs);
}
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#endsBefore()
*/
public Expression endsBefore() { return endsBefore; }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#info()
*/
public WalaInformation info() { return info; }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#sequentiallyValid(com.ibm.wala.memsat.concurrent.Execution)
*/
public Formula sequentiallyValid(Execution exec) {
final Collection<Formula> sv = new LinkedHashSet<Formula>();
// System.out.println("----------------------------");
// System.out.println(handler);
for(Iterator<CGNode> nodes = DFS.iterateDiscoverTime(info.threads(), root(info.threads())); nodes.hasNext(); ) {
final CGNode node = nodes.next();
final MethodTranslation transl = transls.get(node);
final WalaConcurrentInformation tInfo = info.concurrentInformation(node);
final Graph<InlinedInstruction> to = tInfo.threadOrder();
final Graph<InlinedInstruction> toClosure = transitiveClosure(to);
for(Iterator<InlinedInstruction> insts = DFS.iterateDiscoverTime(to, tInfo.start()); insts.hasNext(); ) {
final InlinedInstruction inst = insts.next();
final Expression action = exec.action(inst);
final Formula guard = handler.guardFor(inst);
// System.out.println("----------------------------");
// System.out.println("INLINED INSTRUCTION: " + inst);
// System.out.println("CALL STACK: " + inst.callStack());
// System.out.println("INSTRUCTION: " + inst.instruction());
// System.out.println("ACTION: " + action);
// System.out.println("GUARD: " + guard);
sv.add( action.lone() );
sv.add( guard.iff(action.some()) );
final Expression location = handler.locationOf(inst);
if (location!=null) {
sv.add( guard.implies(action.join(exec.location()).eq(location)) );
final Expression value = handler.valueWritten(inst);
if (value!=null) {
sv.add( guard.implies(action.join(exec.v()).eq(value) ) );
}
} else {
final Expression monitor = handler.monitorOf(inst);
if (monitor!=null) {
sv.add( guard.implies(action.join(exec.monitor()).eq(monitor)) );
}
}
for(Iterator<? extends InlinedInstruction> succs = toClosure.getSuccNodes(inst); succs.hasNext(); ) {
final InlinedInstruction succ = succs.next();
if (factory.mayShareActions(inst, succ)) {
sv.add( action.intersection(exec.action(succ)).no() );
}
}
}
sv.addAll( transl.assumptions() );
if (!exec.isSpeculative()) {
if (factory.base().options().assertsAreAssumptions()) {
sv.addAll(transl.assertions());
} else {
sv.add(Formula.or(transl.assertions()));
}
}
}
return Nodes.replaceAll(Formula.and(sv), executedReads(exec));
}
/**
* Returns a map that maps each Relation representing the value of a read instruction
* to an expression for that value in terms of exec.w and exec.v.
* @return a map that maps each Relation representing the value of a read instruction
* to an expression for that value in terms of exec.w and exec.v.
*/
private final Map<Relation,Expression> executedReads(final Execution exec) {
final Map<Relation, Expression> substitution = new LinkedHashMap<Relation, Expression>();
for(Map.Entry<InlinedInstruction, Relation> read : handler.valuesRead().entrySet()) {
final Expression action = exec.action(read.getKey());
final Expression val = action.join(exec.w()).join(exec.v());
substitution.put(read.getValue(), val);
}
return substitution;
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Program#builder()
*/
public BoundsBuilder builder() {
final Set<Object> atoms = new LinkedHashSet<Object>(factory.atoms());
atoms.addAll(nodes(info.threads()));
final Bounds bounds = new Bounds(new Universe(atoms));
factory.boundAll(bounds);
boundThreads(bounds);
boundEndsBefore(bounds);
return new ConcurrentBoundsBuilder(bounds, factory);
}
/**
* Adds the bounds for this.thread and this.threads to the given bounds.
* @requires this.atoms in bounds.universe.atoms[int]
* @effects bounds.relations' = bounds.relations + this.thread + this.threads
*/
private final void boundThreads(Bounds bounds) {
final TupleFactory tuples = bounds.universe().factory();
for(CGNode root : info.threads()) {
bounds.boundExactly(threads.get(root), tuples.setOf(root));
}
final TupleSet threadBound = tuples.noneOf(2);
for(CGNode n : info.threads()) {
final TupleSet t = tuples.setOf(n);
for(InlinedInstruction inst : info.concurrentInformation(n).actions()) {
threadBound.addAll( factory.actionAtoms(tuples, inst).product(t) );
}
}
bounds.boundExactly(thread, threadBound);
}
/**
* Adds the bounds for this.endsBefore to the given bounds.
* @requires this.atoms in bounds.universe.atoms[int]
* @effects bounds.relations' = bounds.relations + this.endsBefore
*/
private final void boundEndsBefore(Bounds bounds) {
final TupleFactory tuples = bounds.universe().factory();
final Graph<CGNode> eb = transitiveClosure(info.threads());
final TupleSet endsBound = tuples.noneOf(2);
for(CGNode n : info.threads()) {
for(Iterator<? extends CGNode> succs = eb.getSuccNodes(n); succs.hasNext(); ) {
endsBound.add( tuples.tuple(n, succs.next()) );
}
}
bounds.boundExactly(endsBefore, endsBound);
}
}