/****************************************************************************** * 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.jmm; import static com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action.END; 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.START; 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.reflexive; import static com.ibm.wala.memsat.util.Graphs.restrict; import static com.ibm.wala.memsat.util.Programs.executionOrder; import static com.ibm.wala.memsat.util.Programs.filter; import static com.ibm.wala.memsat.util.Programs.instructions; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import com.ibm.wala.memsat.concurrent.Execution; import com.ibm.wala.memsat.concurrent.Justification; import com.ibm.wala.memsat.concurrent.MemoryModel; import com.ibm.wala.memsat.concurrent.Program; import com.ibm.wala.memsat.concurrent.Program.BoundsBuilder; import com.ibm.wala.memsat.frontEnd.InlinedInstruction; import com.ibm.wala.memsat.frontEnd.WalaInformation; import com.ibm.wala.types.MethodReference; import com.ibm.wala.util.graph.Graph; import kodkod.ast.Expression; import kodkod.ast.Formula; import kodkod.ast.Relation; import kodkod.ast.Variable; import kodkod.instance.Bounds; /** * A base implementation of the Java Memory Model. * Subclasses can override implementations of * individual rules. * * @author etorlak */ public abstract class JavaMemoryModel implements MemoryModel { private final Set<MethodReference> externs; final int maxSpeculations; /** * Constructs an instance of the JavaMemoryModel that will use * at most the given number of speculations to justify a program * and that will consider calls to the specified methods as * external instructions. * @effects this.memInstructions' = memInsts * @requires maxSpeculations > 0 */ protected JavaMemoryModel(int maxSpeculations, Set<MethodReference> memInsts) { assert maxSpeculations > 0; this.maxSpeculations = maxSpeculations; if (memInsts.isEmpty()) this.externs = Collections.emptySet(); else this.externs = Collections.unmodifiableSet(new LinkedHashSet<MethodReference>(memInsts)); } /** * Returns the bounds for the justification of the given (main) execution of the given program, * using the provided speculations and commits. * @return bounds for the justification of the given (main) execution of the given program, * using the provided speculations and commits. */ @SuppressWarnings("unchecked") private final Bounds bounds(Program prog, JMMExecution main, List<JMMExecution> speculations, List<Relation> commits) { final BoundsBuilder builder = prog.builder(); final WalaInformation info = prog.info(); final Set<InlinedInstruction> all = instructions(info); final Graph<InlinedInstruction> so = restrict(reflexive(executionOrder(info)), filter(START,END,VOLATILE_READ,VOLATILE_WRITE,LOCK,UNLOCK)); // assert Graphs.equal(so, Programs.syncOrder(info, EnumSet.of(START,END,VOLATILE_READ,VOLATILE_WRITE,LOCK,UNLOCK))); builder.boundExecution(main); builder.boundOrdering(main.so(), so); for(JMMExecution exec : speculations) { builder.boundExecution(exec); builder.boundOrdering(exec.so(), so); } builder.boundActions(commits.get(0), Collections.EMPTY_SET); for(int i = 1; i < maxSpeculations; i++) { builder.boundActions(commits.get(i), all); } return builder.build(); } /** * Returns a sequence of freshly created commit sets. * @return sequence of freshly created commit sets. */ private final List<Relation> commits() { final List<Relation> ret = new ArrayList<Relation>(maxSpeculations); for(int i = 0; i < maxSpeculations; i++) { ret.add(Relation.unary("C"+i)); } return ret; } /** * Returns the legality formula for the given program, main execution, speculations and commits. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return the legality formula for the given program, main execution, speculations and commits. */ private final Formula legal(Program prog, JMMExecution main, List<JMMExecution> speculations, List<Relation> commits) { final Collection<Formula> ret = new ArrayList<Formula>(30); // all executions are well formed ret.add( main.wellFormed() ); for(int i = 1; i < maxSpeculations; i++) { ret.add( speculations.get(i).wellFormed() ); } // C_0 is empty ret.add( commits.get(0).no() ); // C_i in C_{i+1} for(int i = 1; i < maxSpeculations; i++) { ret.add( commits.get(i-1).in(commits.get(i)) ); } // A = union(C_0, ..., C_n) ret.add( main.actions().eq(Expression.union(commits)) ); ret.add( locationConsistency(prog, main, speculations, commits) ); ret.add( monitorConsistency(prog, main, speculations, commits) ); ret.add( rule1(prog, main, speculations, commits) ); ret.add( rule2(prog, main, speculations, commits) ); ret.add( rule3(prog, main, speculations, commits) ); ret.add( rule4(prog, main, speculations, commits) ); ret.add( rule5(prog, main, speculations, commits) ); ret.add( rule6(prog, main, speculations, commits) ); ret.add( rule7(prog, main, speculations, commits) ); ret.add( rule8(prog, main, speculations, commits) ); ret.add( rule9(prog, main, speculations, commits) ); return Formula.and(ret); } /** * Returns the formula C_i <: location_i = C_i <: location for all i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the location consistency rule. */ private Formula locationConsistency(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); final Expression L = main.location(); for(int i = 1; i < maxSpeculations; i++) { final Expression C_i = commits.get(i); final Expression L_i = speculations.get(i).location(); final Expression Ci2All = C_i.product(Expression.UNIV); ret.add( Ci2All.intersection(L_i).eq(Ci2All.intersection(L)) ); } return Formula.and(ret); } /** * Returns the formula C_i <: monitor_i = C_i <: monitor for all i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the monitor consistency rule. */ private Formula monitorConsistency(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); final Expression M = main.monitor(); for(int i = 1; i < maxSpeculations; i++) { final Expression C_i = commits.get(i); final Expression M_i = speculations.get(i).monitor(); final Expression Ci2All = C_i.product(Expression.UNIV); ret.add( Ci2All.intersection(M_i).eq(Ci2All.intersection(M)) ); } return Formula.and(ret); } /** * Returns a formula that specifies the first rule of the JMM: C_i in A_i for all i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the first rule of the JMM: C_i in A_i for all i. */ protected Formula rule1(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); for(int i = 1; i < maxSpeculations; i++) { final Expression C_i = commits.get(i); final Expression A_i = speculations.get(i).actions(); ret.add( C_i.in(A_i) ); } return Formula.and(ret); } /** * Returns a formula that specifies the second rule of the JMM, which defines the relationship * between hb and each hb_i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the second rule of the JMM, which defines the relationship * between hb and each hb_i. */ protected abstract Formula rule2(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits); /** * Returns a formula that specifies the third rule of the JMM, which defines the relationship * between so and each so_i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the second rule of the JMM, which defines the relationship * between so and each so_i. */ protected abstract Formula rule3(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits); /** * Returns a formula that specifies the fourth rule of the JMM: C_i <: V_i = C_i <: V for all i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the fourth rule of the JMM: C_i <: V_i = C_i <: V for all i. */ protected Formula rule4(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); final Expression V = main.v(); for(int i = 1; i < maxSpeculations; i++) { final Expression C_i = commits.get(i); final Expression V_i = speculations.get(i).v(); final Expression Ci2All = C_i.product(Expression.UNIV); ret.add( Ci2All.intersection(V_i).eq(Ci2All.intersection(V)) ); } return Formula.and(ret); } /** * Returns a formula that specifies the fifth rule of the JMM: C_{i-1} <: W_i = C_{i-1} <: W for all i. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the fifth rule of the JMM: C_{i-1} <: W_i = C_{i-1} <: W for all i. */ protected Formula rule5(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); final Expression W = main.w(); for(int i = 1; i < maxSpeculations; i++) { final Expression C_j = commits.get(i-1); final Expression W_i = speculations.get(i).w(); final Expression Cj2All = C_j.product(Expression.UNIV); ret.add( Cj2All.intersection(W_i).eq(Cj2All.intersection(W)) ); } return Formula.and(ret); } /** * Returns a formula that specifies the sixth rule of the JMM: * for all reads r in A_i - C_{i-1}, W_i(r) <=_{hb_i} r. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the sixth rule of the JMM: for all reads r in A_i - C_{i-1}, W_i(r) <=_{hb_i} r. */ protected Formula rule6(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits) { final Collection<Formula> ret = new ArrayList<Formula>(maxSpeculations); final Expression reads = prog.allOf(NORMAL_READ, VOLATILE_READ); for(int i = 1; i < maxSpeculations; i++) { final JMMExecution E_i = speculations.get(i); final Expression C_j = commits.get(i-1); final Expression A_i = E_i.actions(); final Expression hb_i = E_i.hb(); final Expression relevantReads = A_i.difference(C_j).intersection(reads); final Variable r = Variable.unary("r"+i); ret.add( E_i.w(r).product(r).in(hb_i).forAll(r.oneOf(relevantReads)) ); } return Formula.and(ret); } /** * Returns a formula that specifies the seventh rule of the JMM, which constrains * the committing of reads and writes. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the seventh rule of the JMM, which constrains * on the committing of reads and writes. */ protected abstract Formula rule7(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits); /** * Returns a formula that specifies the eighth rule of the JMM, which constrains the * committing in the presence of sufficient synchronizes-with edges. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the eighth rule of the JMM, which constrains the * committing in the presence of sufficient synchronizes-with edges. */ protected abstract Formula rule8(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits); /** * Returns a formula that specifies the ninth rule of the JMM, which constrains the * committing of external actions. * @requires prog = (main + speculations[int]).prog * @requires commits.size() = speculations.size() * @return a formula that specifies the ninth rule of the JMM, which constrains the * committing of external actions. */ protected abstract Formula rule9(Program prog, JMMExecution main, List<JMMExecution> speculations, List<? extends Expression> commits); /** * {@inheritDoc} * @see com.ibm.wala.memsat.concurrent.MemoryModel#justify(com.ibm.wala.memsat.concurrent.Program) */ public final Justification justify(final Program prog) { final List<Relation> commits = commits(); final List<JMMExecution> speculations = JMMExecution.speculative(prog, maxSpeculations); final JMMExecution main = JMMExecution.main(prog); final Formula formula = legal(prog, main, speculations, commits); final Bounds bounds = bounds(prog, main, speculations, commits); return new Justification() { public Program program() { return prog; } public Execution execution() { return main; } public List<? extends Expression> commits() { return commits; } public List<? extends Execution> speculations() { return speculations; } public Formula formula() { return formula; } public Bounds bounds() { return bounds; } }; } /** * {@inheritDoc} * @see com.ibm.wala.memsat.concurrent.MemoryModel#memoryInstructions() */ public final Set<MethodReference> memoryInstructions() { return externs; } /** * {@inheritDoc} * @see com.ibm.wala.memsat.concurrent.MemoryModel#usesSpeculation() */ public final boolean usesSpeculation() { return true; } /** * Returns the maximum number of speculations used by this instance of the * JavaMemoryModel to justify a program. * @return maximum number of speculations used by this instance of the * JavaMemoryModel to justify a program. */ public final int maxSpeculations() { return maxSpeculations; } }