/******************************************************************************
* 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.concurrent.memory.simple;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.LOCK;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.NORMAL_READ;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.NORMAL_WRITE;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.UNLOCK;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.VOLATILE_READ;
import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.VOLATILE_WRITE;
import static com.ibm.wala.memsat.util.Graphs.roots;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.memsat.concurrent.Execution;
import com.ibm.wala.memsat.concurrent.Program;
import com.ibm.wala.memsat.concurrent.memory.AbstractExecution;
import com.ibm.wala.memsat.util.Graphs;
import com.ibm.wala.memsat.util.Nodes;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.ast.Variable;
/**
* A base implementation of the {@linkplain Execution} interface,
* suitable for the specification of simple (non-hybrid) memory models
* defined by Yang et al [1].
*
* @see [1] Y.�Yang, G.�Gopalakrishnan, G.�Lindstrom, and K.�Slind.
* Nemos: a framework for axiomatic and executable specifications of memory consistency models.
* In IPDPS �04, pages 26�30, 2004.
*
* @specfield ords: Object ->lone Relation // ordering relations
*
* @author etorlak
*
*/
final class SimpleExecution<T> extends AbstractExecution {
private final Program prog;
private final Map<T, Relation> ords;
private final Map<Expression, String> viz;
/**
* Constructs a simple execution for the given program, using the
* given ordering relations and the given visualization expressions.
* This constructor assumes that the given maps will not change while
* in use by the class. It also assumes that the viz map is unmodifiable.
* @requires all r: ords.values | r.arity = 2
* @effects this.ords' = ords
*/
public SimpleExecution(Program prog, Map<T, Relation> ords, Map<Expression,String> viz) {
super(prog, "");
this.prog = prog;
this.ords = ords;
this.viz = viz;
}
/**
* Constructs a simple execution for the given program, using the
* given ordering relations and the given visualization expressions.
* This constructor assumes that the given map will not change while
* in use by the class. The viz map for this execution is obtained by
* mapping the transitive reduction of each Relation in ords.values() to its name.
* @requires all r: ords.values | r.arity = 2
* @effects this.ords' = ords
*/
public SimpleExecution(Program prog, Map<T, Relation> ords) { this(prog, ords, viz(ords)); }
/**
* Returns a map from each relation in ords to its name.
* @return a map from each relation in ords to its name.
*/
private static final Map<Expression, String> viz(Map<?, Relation> ords) {
final Map<Expression,String> viz = new LinkedHashMap<Expression, String>();
for(Relation ord : ords.values()) {
viz.put(Nodes.transitiveReduction(ord), ord.name());
// viz.put(ord, ord.name());
}
return Collections.unmodifiableMap(viz);
}
/**
* Returns the ordering relation that corresponds to the given object.
* @return this.ords[o]
*/
protected final Relation ordering(T o) { return ords.get(o); }
/**
* Returns the set of objects that are mapped to orderings in this.ords
* @return this.ords.Relation
*/
protected final Set<T> ordered() { return Collections.unmodifiableSet(ords.keySet()); }
/**
* Returns the ordering relations in this ords.
* @return this.ords[Object]
*/
protected final Collection<Relation> orderings() { return Collections.unmodifiableCollection(ords.values()); }
/**
* Returns the requireWeakTotalOrder constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @return all i, j: acts | i != j => (i->j in ord or j->i in ord)
*/
protected final Formula weakTotalOrder(Expression acts, Expression ord) {
final Variable i = Variable.unary("i"), j = Variable.unary("j");
return i.eq(j).not().implies(i.product(j).in(ord).or(j.product(i).in(ord))).forAll(i.oneOf(acts).and(j.oneOf(acts)));
}
/**
* Returns the requireTransitiveOrder constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @return all i, j, k: acts | i->j in ord and j->k in ord => i->k in ord
*/
protected final Formula transitiveOrder(Expression acts, Expression ord) {
// replacing the original definition with a more efficient relational one ...
// final Variable i = Variable.unary("i"), j = Variable.unary("j"), k = Variable.unary("k");
// return i.product(j).in(ord).and(j.product(k).in(ord)).implies(i.product(k).in(ord)).forAll(i.oneOf(acts).and(j.oneOf(acts)).and(k.oneOf(acts)));
final Expression actOrd = ord.intersection(acts.product(acts));
return actOrd.join(actOrd).in(actOrd);
}
/**
* Returns the requireAsymmetricOrder constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @return all i, j: acts | i->j in ord => !(j->i in ord)
*/
protected final Formula asymmetricOrder(Expression acts, Expression ord) {
final Variable i = Variable.unary("i"), j = Variable.unary("j");
return i.product(j).in(ord).implies(j.product(i).in(ord).not()).forAll(i.oneOf(acts).and(j.oneOf(acts)));
}
/**
* Returns the requireReadValue constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @requires [[acts]] in [[this.actions]]
* @return all k: acts & Read |
* one this.w[k] and this.w[k] in acts and this.location[k] = this.location[this.w[k]] and !(k->this.w[k] in ord) and
* (all j: acts & Write |
* !(this.location[k] = this.location[j] and i->j in ord and j->k in ord) )
*/
protected final Formula readValue(Expression acts, Expression ord) {
final Variable k = Variable.unary("k"), j = Variable.unary("j");
final Expression i = w(k);
final List<Formula> formulas = new ArrayList<Formula>();
formulas.add( i.one().forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))) );
formulas.add( i.in(acts).forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))) );
formulas.add( locationOf(k).eq(locationOf(i)).forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))) );
formulas.add( k.product(i).in(ord).not().forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))) );
formulas.add( Formula.and(locationOf(k).eq(locationOf(j)), i.product(j).in(ord), j.product(k).in(ord)).not().
forAll(j.oneOf(acts.intersection(prog.allOf(NORMAL_WRITE,VOLATILE_WRITE)))).
forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))) );
return Formula.and(formulas);
// formulas.add( i.one() );
// formulas.add( i.in(acts) );
// formulas.add( locationOf(k).eq(locationOf(i)) );
// formulas.add( k.product(i).in(ord).not() );
// formulas.add( Formula.and(locationOf(k).eq(locationOf(j)), i.product(j).in(ord), j.product(k).in(ord)).not().forAll(j.oneOf(acts.intersection(prog.allOf(NORMAL_WRITE,VOLATILE_WRITE)))) );
// return Formula.and(formulas).forAll(k.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ))));
}
/**
* Returns the requireSerialization constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @requires [[acts]] in [[this.actions]]
* @return this.weakTotalOrder(acts, ord) and this.transitiveOrder(acts, ord) and this.asymmetricOrder(acts,ord) and this.readValue(acts,ord)
*/
protected final Formula serialization(Expression acts, Expression ord) {
return Formula.and(weakTotalOrder(acts, ord), transitiveOrder(acts, ord), asymmetricOrder(acts, ord), readValue(acts, ord));
}
/**
* Returns the requireProgramOrder constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @requires [[acts]] in [[this.actions]]
* @return
* let init = prog.threads(root(prog.info.threads)) |
* (all i: acts, j: acts & this.prog.actionsOf(this.prog.threadOf(i)) | i->j in ord + ~ord) and
* (all i: acts & prog.actionsOf(init), j: acts - prog.actionsOf(init) | i->j in ord)
*/
protected final Formula programOrder(Expression acts, Expression ord) {
final Variable i = Variable.unary("i"), j = Variable.unary("j");
final Expression init = prog.threads(roots(prog.info().threads()));
final Formula f0 = i.product(j).in(ord.union(ord.transpose())).forAll(i.oneOf(acts).and(j.oneOf(acts.intersection(prog.actionsOf(prog.threadOf(i))).difference(i))));
final Formula f1 = i.product(j).in(ord).forAll(i.oneOf(acts.intersection(prog.actionsOf(init))).and(j.oneOf(acts.difference(prog.actionsOf(init)))));
return f0.and(f1);
}
/**
* Returns the requireWriteIntoOrder constraint from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and ord.arity = 2
* @requires [[acts]] in [[this.actions]]
* @return all i: acts & Write, j: acts & Read | i = this.w[j] => i->j in ord
*/
protected final Formula writeIntoOrder(Expression acts, Expression ord) {
final Variable i = Variable.unary("i"), j = Variable.unary("j");
return (i.eq(w(j))).implies(i.product(j).in(ord)).
forAll(i.oneOf(acts.intersection(prog.allOf(NORMAL_WRITE, VOLATILE_WRITE))).
and(j.oneOf(acts.intersection(prog.allOf(NORMAL_READ, VOLATILE_READ)))));
}
/**
* Returns a formula stating that locking is proper in the given ordering over this.actions
* @return all l: this.actions & Lock, t: Thread - l.thread | let prevSyncs = (t.actions & (l.(this.monitor)).~(this.monitor) & ^ord.l) |
* #(prevSyncs & Lock) = #(prevSyncs & Unlock)
*/
protected final Formula properLocking(Expression ord) {
final Variable l = Variable.unary("l");
final Variable t = Variable.unary("t");
final Expression threads = prog.threads(Graphs.nodes(prog.info().threads()));
final Expression locks = actions().intersection(prog.allOf(LOCK));
final Expression prevSyncs = Expression.intersection(ord.closure().join(l), syncsOn(monitorOf(l)), prog.actionsOf(t));
return prevSyncs.intersection(prog.allOf(LOCK)).count().eq(prevSyncs.intersection(prog.allOf(UNLOCK)).count()).
forAll(l.oneOf(locks).and(t.oneOf(threads.difference(prog.threadOf(l))))) ;
}
/**
* Returns the restrictVar expression from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and var = 1
* @requires [[acts]] in [[this.actions]]
* @return { i: acts | this.location[i] = var }
*/
protected final Expression restrictVar(Expression acts, Expression var) {
final Variable i = Variable.unary("i");
return locationOf(i).eq(var).comprehension(i.oneOf(acts));
}
/**
* Returns the restrictVarWr expression from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and var = 1
* @requires [[acts]] in [[this.actions]]
* @return { i: acts & Write | this.location[i] = var }
*/
protected final Expression restrictVarWr(Expression acts, Expression var) {
final Variable i = Variable.unary("i");
return locationOf(i).eq(var).comprehension(i.oneOf(acts.intersection(prog.allOf(NORMAL_WRITE, VOLATILE_WRITE))));
}
/**
* Returns the restrictProc expression from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts.arity = 1 and p = 1
* @return { i: acts | this.prog.threadOf(i) = p or (this.prog.threadOf(i) != p and !(i in Read) ) }
*/
protected final Expression restrictProc(Expression acts, Expression p) {
final Variable i = Variable.unary("i");
final Formula f = prog.threadOf(i).eq(p);
return f.or(f.not().and(i.in(prog.allOf(NORMAL_READ,VOLATILE_READ)).not())).comprehension(i.oneOf(acts));
}
/**
* Returns the mapConstraints predicate from the Nemos formalization of non-hybrid memory consistency models.
* @requires acts1.arity = acts2.arity = 1 and ord1.arity = ord2.arity = 2
* @return all i, j: acts1 & acts2 | i->j in ord1 iff i->j in ord2
*/
protected final Formula mapConstraints(Expression acts1, Expression ord1, Expression acts2, Expression ord2) {
final Variable i = Variable.unary("i"), j = Variable.unary("j");
return i.product(j).in(ord1).implies(i.product(j).in(ord2)).
forAll(i.oneOf(acts1.intersection(acts2)).and(j.oneOf(acts1.intersection(acts2))));
}
/**
* Returns a formula that constrains this execution to be sequentially valid.
* @return a formula that constrains this execution to be sequentially valid.
*/
protected final Formula wellFormed() { return prog.sequentiallyValid(this); }
/**
* Returns false.
* @return false
*/
public final boolean isSpeculative() { return false; }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.concurrent.Execution#viz()
*/
public Map<Expression, String> viz() { return viz; }
}