├── LICENSE ├── MainClass.java ├── Makefile ├── README.md ├── c11_perturbed.als ├── canon.py ├── power_perturbed.als ├── run.sh ├── scc_perturbed_scflip.als └── tso_perturbed.als /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, NVIDIA 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MainClass.java: -------------------------------------------------------------------------------- 1 | /* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 5 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 6 | * furnished to do so, subject to the following conditions: 7 | * 8 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 11 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 12 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 13 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | /* MainClass.java 17 | * 18 | * This is a wrapper that we repurposed from some example Alloy API code. 19 | * 20 | * This program takes in an Alloy model (-f alloymodelname.als), and the name 21 | * of a run statement (or multiple run statements) to generate instances 22 | * using. This will continue generating instances until there are none 23 | * remaining. This program prints each instance to stdout for the Alloy parser 24 | * to parse, filter, and turn into a litmus test. 25 | */ 26 | 27 | import java.util.*; 28 | import java.io.PrintWriter; 29 | import edu.mit.csail.sdg.alloy4.A4Reporter; 30 | import edu.mit.csail.sdg.alloy4.Err; 31 | import edu.mit.csail.sdg.alloy4.ErrorWarning; 32 | import edu.mit.csail.sdg.alloy4compiler.ast.Command; 33 | import edu.mit.csail.sdg.alloy4compiler.ast.CommandScope; 34 | import edu.mit.csail.sdg.alloy4compiler.ast.Module; 35 | import edu.mit.csail.sdg.alloy4compiler.parser.CompUtil; 36 | import edu.mit.csail.sdg.alloy4compiler.translator.A4Options; 37 | import edu.mit.csail.sdg.alloy4compiler.translator.A4Solution; 38 | import edu.mit.csail.sdg.alloy4compiler.translator.TranslateAlloyToKodkod; 39 | 40 | /** This class demonstrates how to access Alloy4 via the compiler methods. */ 41 | 42 | public final class MainClass { 43 | public static void print_usage() { 44 | System.err.println("Usage:"); 45 | System.err.println(" [{}]"); 46 | System.err.println(""); 47 | System.err.println("Options:"); 48 | System.err.println(" -h"); 49 | System.err.println(" Print this and exit"); 50 | System.err.println(" -f "); 51 | System.err.println(" Filename of Alloy model to run"); 52 | System.err.println(" REQUIRED"); 53 | System.err.println(" -b "); 54 | System.err.println(" Change the default scope bound."); 55 | System.err.println(" If value is 0, then the bound defaults to whatever is specified in the Alloy file"); 56 | System.err.println(" Optional, default value = 0"); 57 | System.err.println(" -n "); 58 | System.err.println(" Maximum number of instances to generate for each command."); 59 | System.err.println(" If value is 0, then all instances are generated."); 60 | System.err.println(" Optional, default value = 0"); 61 | System.err.println(""); 62 | System.err.println("If no commands to run are included, then all valid command names are printed for the given filename."); 63 | } 64 | 65 | /* 66 | * Executes commands in a given Alloy model file. 67 | * 68 | * This method parses the file given as the first argument, then executes the commands given in subsequent command line arguments. 69 | * If no other command line arguments are given, all commands are run. 70 | * 71 | * If there are syntax or type errors, it may throw 72 | * a ErrorSyntax or ErrorType or ErrorAPI or ErrorFatal exception. 73 | * You should catch them and display them, 74 | * and they may contain filename/line/column information. 75 | */ 76 | public static void main(String[] args) throws Err { 77 | // command line argument parsing 78 | String filename = ""; 79 | int num_instances_to_gen = 0; 80 | int bound_override = 0; 81 | List commands = new ArrayList(); 82 | for (int i = 0 ; i < args.length ; i ++) { 83 | if (args[i].equals("-f") || args[i].equals("--file")) { 84 | i++; 85 | if (i >= args.length) { 86 | System.err.println("ERROR: Expected file name after " + args[i-1]); 87 | print_usage(); 88 | System.exit(1); 89 | } else { 90 | filename = args[i]; 91 | } 92 | } else if (args[i].equals("-n")) { 93 | i++; 94 | if (i >= args.length) { 95 | System.err.println("ERROR: Expected number after " + args[i-1]); 96 | print_usage(); 97 | System.exit(1); 98 | } else { 99 | try { 100 | num_instances_to_gen = Integer.parseInt(args[i]); 101 | } catch (NumberFormatException e) { 102 | System.err.println("ERROR: Expected integer after " + args[i-1]); 103 | print_usage(); 104 | System.exit(1); 105 | } 106 | } 107 | } else if (args[i].equals("-b")) { 108 | i++; 109 | if (i >= args.length) { 110 | System.err.println("ERROR: Expected number after " + args[i-1]); 111 | print_usage(); 112 | System.exit(1); 113 | } else { 114 | try { 115 | bound_override = Integer.parseInt(args[i]); 116 | } catch (NumberFormatException e) { 117 | System.err.println("ERROR: Expected integer after " + args[i-1]); 118 | print_usage(); 119 | System.exit(1); 120 | } 121 | } 122 | } else { 123 | // assume it is an alloy run command 124 | commands.add(args[i]); 125 | } 126 | } 127 | // validate command line arguments 128 | if (filename.equals("")) { 129 | System.err.println("ERROR: Filename required"); 130 | print_usage(); 131 | System.exit(1); 132 | } 133 | if (num_instances_to_gen < 0) { 134 | System.err.println("ERROR: Negative number of instances to generate not allowed"); 135 | print_usage(); 136 | System.exit(1); 137 | } 138 | if (bound_override < 0) { 139 | System.err.println("ERROR: Negative scope bound."); 140 | print_usage(); 141 | System.exit(1); 142 | } 143 | 144 | // Alloy4 sends diagnostic messages and progress reports to the A4Reporter. 145 | // By default, the A4Reporter ignores all these events (but you can extend the A4Reporter to display the event for the user) 146 | A4Reporter rep = new A4Reporter() { 147 | // For example, here we choose to display each "warning" by printing it to System.out 148 | @Override public void warning(ErrorWarning msg) { 149 | System.err.print("Relevance Warning:\n"+(msg.toString().trim())+"\n\n"); 150 | System.err.flush(); 151 | } 152 | }; 153 | 154 | // Parse+typecheck the model 155 | Module world = CompUtil.parseEverything_fromFile(rep, null, filename); 156 | 157 | // Choose some default options for how you want to execute the commands 158 | A4Options options = new A4Options(); 159 | 160 | // This requires 32-bit java in windows 161 | //options.solver = A4Options.SatSolver.MiniSatJNI; 162 | 163 | if (commands.size() == 0) { 164 | // If there are no commands specified, print all commands 165 | System.err.println("No commands specified. List of all available commands:"); 166 | for (Command command: world.getAllCommands()) { 167 | System.err.println(" " + command.label); 168 | } 169 | } else { 170 | // If there are specified commands, run them 171 | for (String req_command : commands) { 172 | boolean command_found = false; 173 | // System.err.println("Looking for \"" + args[i] + "\""); 174 | for (Command command: world.getAllCommands()) { 175 | // System.err.println(" Candidate: " + command.label); 176 | if (command.label.equals(req_command)) { 177 | System.err.println(" Command match: " + command.label); 178 | System.out.println(""); 179 | command_found = true; 180 | 181 | if (bound_override > 0) { 182 | System.err.println("Scope bound " + bound_override + " overrides default bound of " + command.overall); 183 | command = new Command( 184 | command.pos, 185 | command.label, 186 | command.check, 187 | bound_override, // <-- 188 | command.bitwidth, 189 | command.maxseq, 190 | command.expects, 191 | command.scope, 192 | command.additionalExactScopes, 193 | command.formula, 194 | command.parent); 195 | } else { 196 | //System.err.println("Scope bound: " + command.overall); 197 | } 198 | 199 | // Execute the command 200 | A4Solution ans = TranslateAlloyToKodkod.execute_command(rep, world.getAllReachableSigs(), command, options); 201 | // Print the outcome 202 | if(ans.satisfiable()) { 203 | int num_instances = 0; 204 | do { 205 | // Print the outcome 206 | //System.out.println(ans); 207 | ans.writeXML(new PrintWriter(System.out), null, null); 208 | ans = ans.next(); 209 | num_instances++; 210 | } 211 | while(ans.satisfiable() && (num_instances_to_gen == 0 || num_instances < num_instances_to_gen)); 212 | } 213 | System.out.println(""); 214 | } 215 | } 216 | if (!command_found) { 217 | System.err.println("ERROR! command \"" + req_command + "\" not found. List of all available commands:"); 218 | for (Command command: world.getAllCommands()) { 219 | System.err.println(" " + command.label); 220 | } 221 | System.exit(1); 222 | } 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: MainClass.class 4 | 5 | MainClass.class: MainClass.java 6 | javac -classpath alloy4.2.jar -d . MainClass.java 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automatic Synthesis of Comprehensive Memory Model Litmus Test Suites 2 | 3 | The technique described in the paper and in the included files shows how to automatically generate a comprehensive suite of ``maximally-interesting'' litmus tests specific to a given (axiomatic specification of) memory model. This enables more rapid and more reliable exploration of sophisticated memory models such as those used by C/C++, IBM Power, and more. 4 | 5 | ## Installation 6 | 7 | None needed! Just open the `.als` files in [Alloy](http://alloy.mit.edu) and execute them. 8 | 9 | ## Usage 10 | 11 | To use our canonicalizer script: 12 | 13 | * a Java compiler `javac` 14 | * `python` (tested on version 2.7) 15 | * Download the Alloy 4.2 [.jar file](http://alloy.mit.edu/alloy/downloads/alloy4.2.jar) 16 | * run `make` to build our ever-so-slightly-customized command-line interface to Alloy. (All that is changed is the particular set of command line flags, and generation is run until exhaustion.) 17 | 18 | Basic usage: 19 | 20 | ./run.sh <.als input file> -m 21 | 22 | For example: 23 | 24 | $ ./run.sh tso_perturbed.als -m 4 causality 25 | # ['canon.py', '20170331-164702-causality-4'] 26 | Command match: causality 27 | Scope bound 4 overrides default bound of 4 28 | New hash (1/1): _T_Wa0_Wa0 29 | New hash (2/2): _T_Ra0_Wa1_T_Wa1_Wa0 30 | New hash (3/3): _T_Wa0_Wa1_T_Wa1_Wa0 31 | New hash (4/4): _T_Ra0_Ra0_T_Wa0_T_Wa0 32 | New hash (5/5): _T_Ra0_Ra0_T_Wa0_Wa0 33 | New hash (6/7): _T_Ra0_Ra0_T_Wa0 34 | New hash (7/9): _T_Wa0_T_Wa0_Ra0_Ra0 35 | New hash (8/10): _T_Ra0_Ra1_T_Wa1_Wa0 36 | New hash (9/11): _T_Ra0_Wa1_T_Ra1_Wa0 37 | New hash (10/12): _T_Ra0_Wa0_T_Wa0 38 | #,Filename,Command,Unique,Total 39 | Results,tso_perturbed.als,causality,10,12 40 | 41 | real 0m1.123s 42 | user 0m2.246s 43 | sys 0m0.073s 44 | 45 | This lists the ten tests that are minimal with respect to the TSO causality axiom, in no particular order: 46 | 47 | 1. CoWW 48 | 2. S 49 | 3. 2+2W 50 | 4. W+W+RR 51 | 5. CoMP 52 | 6. CoRR 53 | 7. W+WRR 54 | 8. MP 55 | 9. LB 56 | 10. CoRW 57 | 58 | This matches the set of ten described in the paper. In this case, Alloy generated 12 hashes, of which 10 were unique post-canonicalization. 59 | 60 | The hashes themselves are a bit obscure but should be human-readable: 61 | 62 | Hash item | Meaning 63 | ----------|-------- 64 | T | start of thread 65 | R | Read 66 | W | Write 67 | Aq | Acquire 68 | Rl | Release 69 | AA | Atomic read-modify-write 70 | F | Fence 71 | a*n* | Address *n* 72 | m | Read half of a read-modify-write 73 | s*n* | Source of dependency *n* 74 | ra*n* | Target of address dependency *n* 75 | rc*n* | Target of control dependency *n* 76 | rd*n* | Target of data dependency *n* 77 | na | non-atomic 78 | rx | memory_order_relaxed 79 | aq | memory_order_acquire 80 | rl | memory_order_release 81 | ar | memory_order_acq_rel 82 | sc | memory_order_seq_cst 83 | 84 | ## Experimenting with the Technique 85 | 86 | Feel free to play around with this and let us know what you think! If you find anything interesting, let us know! 87 | 88 | For example, consider this simple experiment: 89 | 90 | If `fr_p` is defined as follows: 91 | 92 | fun fr_p[p: PTag->univ] : Read->Write { (Read - p[RE]) <: fr :> (Write - p[RE]) } 93 | 94 | The seven tests above are emitted. If instead, `fr_p` is redefined as follow: 95 | 96 | fun fr_p[p: PTag->univ] : Read->Write { 97 | ( ~(rf_p[p]).^(co_p[p]) ) 98 | + 99 | ( (Read-(Write.rf)-p[RE]) <: address.~address :> (Write - p[RE]) ) 100 | } 101 | 102 | Then `fr_p` is more tightly constrained, and W+W+RR, W+WRR, and CoMP are no longer emitted. 103 | 104 | ## Extending the Infrastructure 105 | 106 | The canonicalizer is still somewhat primitive and has at least one known scenario where it misses some duplicates. It should be easy to extend/replace the script and/or our command line interface to Alloy to suit your own needs. 107 | 108 | ## Questions? 109 | 110 | Please contact Dan Lustig at dlustig@nvidia.com 111 | 112 | ## Attribution 113 | 114 | If you use these techniques in your work, please cite our ASPLOS 2017 paper: 115 | 116 | Daniel Lustig, Andy Wright, Alexandros Papakonstantinou, and Olivier Giroux, 117 | "Automatic Synthesis of Comprehensive Memory Model Litmus Test Suites", 118 | 22nd ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS), Xi'an, China, April 8-12, 2017 119 | 120 | ([Paper](fixme)) ([Slides](coming_soon)) coming soon! 121 | -------------------------------------------------------------------------------- /c11_perturbed.als: -------------------------------------------------------------------------------- 1 | // Automated Synthesis of Comprehensive Memory Model Litmus Test Suites 2 | // Daniel Lustig, Andrew Wright, Alexandros Papakonstantinou, Olivier Giroux 3 | // ASPLOS 2017 4 | // 5 | // Copyright (c) 2017, NVIDIA Corporation. All rights reserved. 6 | // 7 | // This file is licensed under the BSD-3 license. See LICENSE for details. 8 | 9 | // C11/C++11, based on a model from Batty et al. [POPL 2016] 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // =Perturbations= 13 | 14 | abstract sig PTag {} 15 | one sig RE extends PTag {} 16 | one sig LS extends PTag {} 17 | one sig DR extends PTag {} 18 | one sig DA extends PTag {} 19 | 20 | fun no_p : PTag->univ { // no_p - constant for no perturbation 21 | (PTag->univ) - (PTag->univ) 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // =C11 memory model= 26 | 27 | pred C11_p[p: PTag->univ] { 28 | DRF_p[no_p] // <-- !!! 29 | Hb_p[p] 30 | Coh_p[p] 31 | Rf_p[p] 32 | NaRf_p[p] 33 | Rmw_p[p] 34 | Ssimp_p[p] 35 | } 36 | 37 | pred Hb_p[p: PTag->univ] { irreflexive[hb_p[p]] } 38 | pred Coh_p[p: PTag->univ] { 39 | irreflexive[optional[~(rf_p[p])].(mo_p[p]).(optional[rf_p[p]]).(hb_p[p])] 40 | } 41 | pred Rf_p[p: PTag->univ] { irreflexive[(rf_p[p]).(hb_p[p])] } 42 | pred NaRf_p[p: PTag->univ] { 43 | no ((rf_p[p]).(ident[NA - p[RE]])) - vis_p[p] 44 | } 45 | pred Rmw_p[p: PTag->univ] { 46 | irreflexive[rf_p[p] + ((mo_p[p]).(mo_p[p]).~(rf_p[p])) + ((mo_p[p]).(rf_p[p]))] 47 | } 48 | 49 | pred Com_p[p: PTag->univ] { irreflexive[*(rf_p[p] + mo_p[p] + fr_p[p]).(hb_p[p])] } 50 | pred Ssimp_p[p: PTag->univ] { 51 | acyclic[ 52 | (allpairs[MemoryOrderSeqCst.(ord_p[p])] - iden) 53 | & 54 | (optional[Fsb_p[p]].(hb_p[p] + fr_p[p] + mo_p[p]).(optional[sbF_p[p]])) 55 | ] 56 | } 57 | 58 | pred DRF_p[p: PTag->univ] { 59 | no dr_p[p] 60 | // Not in the model, but in Batty's thesis, section 3.1.3 61 | Reads_p[p] in Writes_p[p].(rf_p[p]) // indeterminate reads 62 | ((address.~address) & ^(~(sb_p[p]) + sb_p[p])) - iden in sb_p[p] + ~(sb_p[p]) // unsequenced races 63 | } 64 | 65 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 66 | // =Model of memory= 67 | 68 | sig Address { } 69 | 70 | sig Thread { start: one Event } 71 | fact { start.~start in iden } 72 | fact { Event = Thread.start.*sb } 73 | fact { no start.~sb } 74 | 75 | abstract sig MemoryOrder {} 76 | one sig MemoryOrderNonAtomic extends MemoryOrder {} 77 | one sig MemoryOrderRelaxed extends MemoryOrder {} 78 | one sig MemoryOrderAcquire extends MemoryOrder {} 79 | one sig MemoryOrderRelease extends MemoryOrder {} 80 | one sig MemoryOrderAcqRel extends MemoryOrder {} 81 | one sig MemoryOrderSeqCst extends MemoryOrder {} 82 | 83 | fun A : Event { Event - MemoryOrderNonAtomic.ord } 84 | fun NA : set Event { MemoryOrderNonAtomic.ord } 85 | fun RLX : set Event { MemoryOrderRelaxed.ord } 86 | fun ACQ : set Event { MemoryOrderAcquire.ord } 87 | fun REL : set Event { MemoryOrderRelease.ord } 88 | fun AR : set Event { MemoryOrderAcqRel.ord } 89 | fun SC : set Event { MemoryOrderSeqCst.ord } 90 | 91 | fun acq : set Event { ACQ + AR + SC - Write } 92 | fun rel : set Event { REL + AR + SC - Read } 93 | 94 | fact WriteMO { 95 | all w: Write | w.memory_order in 96 | MemoryOrderNonAtomic + MemoryOrderRelaxed + 97 | MemoryOrderRelease + MemoryOrderSeqCst 98 | } 99 | fact ReadMO { 100 | Read.memory_order in 101 | MemoryOrderNonAtomic + MemoryOrderRelaxed + 102 | MemoryOrderAcquire + MemoryOrderSeqCst 103 | } 104 | fact RMWMO { 105 | RMW.memory_order in 106 | MemoryOrderRelaxed + MemoryOrderRelease + MemoryOrderAcquire + 107 | MemoryOrderAcqRel + MemoryOrderSeqCst 108 | } 109 | fact { 110 | Fence.memory_order in 111 | MemoryOrderRelease + MemoryOrderAcquire + MemoryOrderAcqRel + MemoryOrderSeqCst 112 | } 113 | 114 | abstract sig Event { 115 | sb: set Event, 116 | memory_order: one MemoryOrder, 117 | sc: set Event, 118 | } 119 | fun ord : MemoryOrder->Event { ~memory_order } 120 | abstract sig MemoryEvent extends Event { 121 | address : one Address, 122 | rf: set Event, 123 | mo: set Event, 124 | } 125 | fact rf_wr { rf in Writes->Reads } 126 | fact mo_writes { mo in Writes->Writes } 127 | sig Write extends MemoryEvent {} 128 | sig Read extends MemoryEvent {} 129 | sig RMW extends MemoryEvent {} 130 | sig Fence extends Event {} 131 | fun Reads : set Event { Read + RMW } 132 | fun Writes : set Event { Write + RMW } 133 | 134 | //////////////////////////////////////////////////////////////////////////////// 135 | // =Constraints on basic model of memory= 136 | 137 | // com 138 | fun loc : Event->Event { MemoryEvent->MemoryEvent & address.~address } 139 | fact { all r : Reads | lone r.~rf } 140 | fun fr : Read->Write { 141 | ~rf.mo 142 | + 143 | ((Reads - Writes.rf)->Writes & loc) 144 | } 145 | fact com_loc { rf + mo + fr in loc } 146 | 147 | // sb 148 | fact { irreflexive[sb] } 149 | fact { transitive[sb] } 150 | fact { all e: Event | one t: Thread | t->e in start.*sb } 151 | fun thd : Event->Event { ^(sb + ~sb) } 152 | 153 | // reads 154 | fact { rf.~rf in iden } 155 | 156 | // writes 157 | fact { all a: Address | total[mo, a.~address :> (Writes & A)] } 158 | //fact { no NA <: address.~address :> A } 159 | 160 | // sc 161 | fact { total[sc, SC] } 162 | fact { sc in allpairs[SC] } 163 | 164 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 165 | // =Model of memory, under perturbation= 166 | 167 | fun DR_step : MemoryOrder->MemoryOrder { 168 | MemoryOrderAcqRel->MemoryOrderSeqCst + 169 | MemoryOrderRelease->MemoryOrderAcqRel + 170 | MemoryOrderRelaxed->MemoryOrderAcquire + // should still work here 171 | MemoryOrderRelaxed->MemoryOrderRelease 172 | } 173 | 174 | fun DA_step : MemoryOrder->MemoryOrder { 175 | MemoryOrderAcqRel->MemoryOrderSeqCst + 176 | MemoryOrderAcquire->MemoryOrderAcqRel + 177 | MemoryOrderRelaxed->MemoryOrderRelease + // should still work here 178 | MemoryOrderRelaxed->MemoryOrderAcquire 179 | } 180 | 181 | fun ord_p[p: PTag->univ] : MemoryOrder->Event { 182 | ord :> (Event - p[RE+DR+DA]) 183 | + 184 | DR_step.ord :> ((Event & p[DR]) - p[RE]) 185 | + 186 | DA_step.ord :> ((Event & p[DA]) - p[RE]) 187 | } 188 | 189 | fun sb_p[p: PTag->univ] : Event->Event { 190 | (Event - p[RE]) <: sb :> (Event - p[RE]) 191 | } 192 | 193 | fun sc_p[p: PTag->univ] : Event->Event { 194 | (Event - p[RE+DR+DA]) <: sc :> (Event - p[RE+DR+DA]) 195 | } 196 | 197 | fun rf_p[p: PTag->univ] : Event->Event { 198 | (Event - p[RE]) <: rf :> (Event - p[RE]) 199 | } 200 | 201 | fun mo_p[p: PTag->univ] : Event->Event { 202 | (Event - p[RE]) <: mo :> (Event - p[RE]) 203 | } 204 | 205 | fun fr_p[p: PTag->univ] : Event->Event { 206 | (Event - p[RE]) <: fr :> (Event - p[RE]) 207 | } 208 | 209 | fun Writes_p[p: PTag->univ] : Event { 210 | Writes - p[RE] 211 | } 212 | 213 | fun Reads_p[p: PTag->univ] : Event { 214 | Reads - p[RE] 215 | } 216 | 217 | fun thd_p[p: PTag->univ] : Event->Event { 218 | (Event - p[RE]) <: thd :> (Event - p[RE]) 219 | } 220 | 221 | fun acq_p[p: PTag->univ] : set Event { 222 | (MemoryOrderAcquire.(ord_p[p])) + 223 | (MemoryOrderAcqRel.(ord_p[p])) + 224 | (MemoryOrderSeqCst.(ord_p[p]) & (Reads + Fence)) 225 | // + (F & con) // ignoring consume 226 | } 227 | fun rel_p[p: PTag->univ] : set Event { 228 | (MemoryOrderRelease.(ord_p[p])) + 229 | (MemoryOrderAcqRel.(ord_p[p])) + 230 | (MemoryOrderSeqCst.(ord_p[p]) & (Writes + Fence)) 231 | // + (F & con)? // ignoring consume 232 | } 233 | fun Fsb_p[p: PTag->univ] : Event->Event { ident[Fence].(sb_p[p]) } 234 | fun sbF_p[p: PTag->univ] : Event->Event { (sb_p[p]).(ident[Fence]) } 235 | fun rs_prime_p[p: PTag->univ] : Event->Event { 236 | thd_p[p] + (((Event - p[RE])->(Event - p[RE])).(ident[RMW])) 237 | } 238 | fun rs_p[p: PTag->univ] : Event->Event { 239 | (mo_p[p]) & (rs_prime_p[p]) - (((mo_p[p]) - (rs_prime_p[p])).(mo_p[p])) 240 | } 241 | fun sw_p[p: PTag->univ] : Event->Event { 242 | ( 243 | (ident[rel_p[p]]).(optional[Fsb_p[p]]).(ident[Writes]) 244 | .(optional[rs_p[p]]).(rf_p[p]).(ident[Reads]) 245 | .(optional[sbF_p[p]]).(ident[acq_p[p]]) 246 | ) - thd_p[p] 247 | } 248 | fun ithbr_p[p: PTag->univ] : Event->Event { sw_p[p] + (sw_p[p]).(sb_p[p]) } 249 | fun ithb_p[p: PTag->univ] : Event->Event { 250 | ^(ithbr_p[p] + (sb_p[p]).(ithbr_p[p])) 251 | } 252 | fun hb_p[p: PTag->univ] : Event->Event { 253 | ^((sb_p[p]) + ithb_p[p]) 254 | } 255 | fun hbl_p[p: PTag->univ] : Event->Event { hb_p[p] & loc } 256 | fun vis_p[p: PTag->univ] : Event->Event { 257 | (ident[Writes].(hbl_p[p]).(ident[Reads])) 258 | - 259 | ((hbl_p[p]).(ident[Writes]).(hbl_p[p])) 260 | } 261 | fun cnf_p[p: PTag->univ] : Event->Event { 262 | (((MemoryEvent - p[RE])->(MemoryEvent - p[RE])) & loc) 263 | } 264 | fun dr_p[p: PTag->univ] : Event->Event { 265 | cnf_p[p] - hb_p[p] - ~(hb_p[p]) - allpairs[Event - NA - p[RE]] - thd_p[p] 266 | } 267 | 268 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 269 | // =Alloy helpers= 270 | 271 | fun allpairs[e: univ] : univ->univ { e->e } 272 | fun ident[e: univ] : univ->univ { iden & e->e } 273 | fun optional[f: univ->univ] : univ->univ { iden + f } 274 | pred transitive[rel: Event->Event] { rel = ^rel } 275 | pred irreflexive[rel: Event->Event] { no iden & rel } 276 | pred acyclic[rel: Event->Event] { irreflexive[^rel] } 277 | pred total[rel: Event->Event, bag: Event] { 278 | all disj e, e2: bag | e->e2 in rel + ~rel 279 | acyclic[rel] 280 | } 281 | 282 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 283 | // =Constraints on the search space= 284 | 285 | fact { 286 | all a: Address | some (a.~address) :> Writes 287 | } 288 | // treat sb as if it were total per thread, even though it's not necessarily 289 | fact { 290 | all disj e1, e2: Event | e1->e2 in thd => e1->e2 in sb + ~sb 291 | } 292 | 293 | //////////////////////////////////////////////////////////////////////////////// 294 | 295 | let interesting_not_axiom[axiom] { 296 | not axiom[no_p] 297 | 298 | all e: Event | C11_p[RE->e] 299 | all e: A - RLX | C11_p[DR->e] 300 | all e: A - RLX | C11_p[DA->e] 301 | all e: Fence & (AR + SC) | C11_p[DR->e] 302 | all e: Fence & (AR + SC) | C11_p[DA->e] 303 | } 304 | 305 | //////////////////////////////////////////////////////////////////////////////// 306 | 307 | run Hb { 308 | interesting_not_axiom[Hb_p] 309 | } for 3 310 | 311 | run Coh { 312 | interesting_not_axiom[Coh_p] 313 | } for 3 314 | 315 | run Rf { 316 | interesting_not_axiom[Rf_p] 317 | } for 3 318 | 319 | run NaRf { 320 | interesting_not_axiom[NaRf_p] 321 | } for 3 322 | 323 | run Rmw { 324 | interesting_not_axiom[Rmw_p] 325 | } for 3 326 | 327 | run Simp { 328 | interesting_not_axiom[Ssimp_p] 329 | } for 3 330 | 331 | run Com { 332 | interesting_not_axiom[Com_p] 333 | } for 3 334 | 335 | run union { 336 | interesting_not_axiom[Hb_p] 337 | or 338 | interesting_not_axiom[Coh_p] 339 | or 340 | interesting_not_axiom[Rf_p] 341 | or 342 | interesting_not_axiom[NaRf_p] 343 | or 344 | interesting_not_axiom[Rmw_p] 345 | or 346 | interesting_not_axiom[Ssimp_p] 347 | } for 3 348 | 349 | run sanity { 350 | some Event 351 | C11_p[no_p] 352 | } for 3 353 | -------------------------------------------------------------------------------- /canon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Automated Synthesis of Comprehensive Memory Model Litmus Test Suites 4 | # Daniel Lustig, Andrew Wright, Alexandros Papakonstantinou, Olivier Giroux 5 | # ASPLOS 2017 6 | # 7 | # Copyright (c) 2017, NVIDIA Corporation. All rights reserved. 8 | # 9 | # This file is licensed under the BSD-3 license. See LICENSE for details. 10 | 11 | import sys 12 | import re 13 | import xml.etree.ElementTree as ET 14 | 15 | # a single node in an Alloy instance 16 | class Atom: 17 | def __init__(self, label, sig_label): 18 | self._label = label 19 | self._sig_label = sig_label 20 | def __repr__(self): 21 | "just list all the properties" 22 | d = {} 23 | for a in dir(self): # guaranteed to be alphabetical 24 | if a[:2] != "__": 25 | d[a] = getattr(self, a) 26 | return str(d) 27 | 28 | instances = {} 29 | unique = 0 30 | total = 0 31 | last = 0 32 | 33 | def follow_edge(atom, edge): 34 | "Return atom.edge. There should only be one choice" 35 | candidates = [] 36 | if not hasattr(atom, edge): 37 | return None 38 | for c in getattr(atom, edge): 39 | candidates.append(c) 40 | if len(candidates) != 1: 41 | raise Exception("multiple %s edges?" % edge) 42 | return candidates[0] 43 | 44 | def follow_edge_tr(atoms, atom, edge): 45 | "like follow_edge, but for transitively closed relations" 46 | candidates = [] 47 | if not hasattr(atom, edge): 48 | return None 49 | # first, add all adjacent atoms 50 | for c in getattr(atom, edge): 51 | candidates.append(c) 52 | # now, prune out all atoms which are at least two steps away 53 | for c1 in getattr(atom, edge): 54 | a1 = atoms[c1] 55 | if hasattr(a1, edge): 56 | for c2 in getattr(a1, edge): 57 | if c2 in candidates: 58 | candidates.remove(c2) 59 | # there should only be one candidate lett 60 | if len(candidates) != 1: 61 | raise Exception("multiple %s edges?" % edge) 62 | return candidates[0] 63 | 64 | def follow_po_edge(atoms, atom): 65 | "like follow_edge, but for po or sb specifically" 66 | x = follow_edge(atom, "po") 67 | if x: 68 | return x 69 | x = follow_edge_tr(atoms, atom, "sb") 70 | if x: 71 | return x 72 | return None 73 | 74 | event_hash_dict = { 75 | "Read": "R", 76 | "Write": "W", 77 | "Acquire": "Aq", 78 | "Release": "Rl", 79 | "Fence": "F", 80 | "FenceAll": "Far", 81 | "FenceAcqRel": "Far", 82 | "FenceAcq": "Faq", 83 | "FenceRel": "Frl", 84 | "FenceSC": "Fsc", 85 | "Branch": "Br", 86 | "lwsync": "L", 87 | "sync": "S", 88 | "CtrlFence": "C", 89 | "AtomicRead": "AR", 90 | "AtomicWrite": "AW", 91 | "AtomicRMW": "AA", 92 | "RMW": "AA", 93 | "NonAtomicRead": "NR", 94 | "NonAtomicWrite": "NW", 95 | } 96 | 97 | memory_order_dict = { 98 | "MemoryOrderSeqCst$0": "sc", 99 | "MemoryOrderAcqRel$0": "ar", 100 | "MemoryOrderRelease$0": "rl", 101 | "MemoryOrderAcquire$0": "aq", 102 | "MemoryOrderRelaxed$0": "rx", 103 | "MemoryOrderNonAtomic$0": "na" 104 | } 105 | 106 | def event_hash_without_addr(thread, atom): 107 | "Get a hash of the event. The address is calculated elsewhere in globally sorted order" 108 | if not hasattr(thread, "_memo"): 109 | thread._memo = {} 110 | 111 | # check if we've already calculated this hash 112 | if atom._label in thread._memo: 113 | return thread._memo[atom._label] 114 | 115 | # we haven't already calculated this hash, so start generating it 116 | if not hasattr(thread, "_addr_dep_dsts"): 117 | thread._addr_dep_dsts = {} 118 | if not hasattr(thread, "_ctrl_dep_dsts"): 119 | thread._ctrl_dep_dsts = {} 120 | if not hasattr(thread, "_data_dep_dsts"): 121 | thread._data_dep_dsts = {} 122 | if not hasattr(thread, "_next_dep_source_id"): 123 | thread._next_dep_source_id = 0 124 | 125 | # start with the event itself 126 | s = "_" + event_hash_dict[atom._sig_label] 127 | 128 | # memory order, in C/C++ 129 | if hasattr(atom, "memory_order"): 130 | s += memory_order_dict[follow_edge(atom, "memory_order")] 131 | 132 | # if this atom is the source of an address dependency 133 | if hasattr(atom, "addr"): 134 | dep_source_id = thread._next_dep_source_id 135 | s += "s" + str(dep_source_id) 136 | thread._next_dep_source_id += 1 137 | for addr in atom.addr: 138 | thread._addr_dep_dsts[addr] = dep_source_id 139 | 140 | # if this atom is the target of an address dependency 141 | if atom._label in thread._addr_dep_dsts: 142 | req = thread._addr_dep_dsts[atom._label] 143 | s += "ra" + str(req) 144 | 145 | # if this atom is the source of a control dependency 146 | if hasattr(atom, "ctrl"): 147 | if not hasattr(thread, "_next_dep_source_id"): 148 | thread._next_dep_source_id = 0 149 | dep_source_id = thread._next_dep_source_id 150 | s += "s" + str(dep_source_id) 151 | thread._next_dep_source_id += 1 152 | for ctrl in atom.ctrl: 153 | thread._ctrl_dep_dsts[ctrl] = dep_source_id 154 | 155 | # if this atom is the target of a control dependency 156 | if atom._label in thread._ctrl_dep_dsts: 157 | req = thread._ctrl_dep_dsts[atom._label] 158 | s += "rc" + str(req) 159 | 160 | # if this atom is the source of a data dependency 161 | if hasattr(atom, "data"): 162 | if not hasattr(thread, "_next_dep_source_id"): 163 | thread._next_dep_source_id = 0 164 | dep_source_id = thread._next_dep_source_id 165 | s += "s" + str(dep_source_id) 166 | thread._next_dep_source_id += 1 167 | for data in atom.data: 168 | thread._data_dep_dsts[data] = dep_source_id 169 | 170 | # if this atom is the target of a data dependency 171 | if atom._label in thread._data_dep_dsts: 172 | req = thread._data_dep_dsts[atom._label] 173 | s += "rd" + str(req) 174 | 175 | # if this atom is the source of an rmw 176 | if hasattr(atom, "rmw"): 177 | s += "m" 178 | # note: there is no need to also mark the target of an rmw, since it's 179 | # already unambiguous based on where the source is 180 | 181 | # save this hash for future reference 182 | thread._memo[atom._label] = s 183 | return s 184 | 185 | def get_instance_hash(atoms): 186 | # there might be more than one thread with the same hash, so track hashes 187 | # and threads separately 188 | thread_hashes = {} 189 | threads_with_hash = {} 190 | 191 | # search all the atoms for ones labeled "Thread" 192 | for t in atoms.iteritems(): 193 | if t[0][:7] == "Thread$": 194 | thread_hash = "" 195 | # iterate through the events in the thread, and add the event hash of 196 | # each to the thread's hash 197 | s = follow_edge(t[1], "start") 198 | while s is not None: 199 | a = atoms[s] 200 | thread_hash += event_hash_without_addr(t[1], a) 201 | s = follow_po_edge(atoms, a) 202 | thread_hashes[t[0]] = thread_hash 203 | threads_with_hash.setdefault(thread_hash, set()).add(t[0]) 204 | 205 | # addrmap: map each address into a name sorted by first appearance in the 206 | # instance hash 207 | addrmap = {} 208 | 209 | # generate the instance hash, which is the alphabetical sort of the thread 210 | # hashes 211 | instance_hash = "" 212 | for th in sorted(threads_with_hash.keys()): 213 | for t in threads_with_hash[th]: 214 | # precede each new thread with "_T" 215 | instance_hash += "_T" 216 | # re-iterate through the events in the thread, but this time add the 217 | # event's address to the hash too 218 | s = follow_edge(atoms[t], "start") 219 | while s is not None: 220 | a = atoms[s] 221 | instance_hash += event_hash_without_addr(atoms[t], a) 222 | addr = follow_edge(a, "address") 223 | if addr is not None: 224 | addrmap.setdefault(atoms[addr], len(addrmap.keys())) 225 | instance_hash += "a" + str(addrmap[atoms[addr]]) 226 | s = follow_po_edge(atoms, a) 227 | 228 | if instance_hash == "": 229 | raise Exception("Empty hash for instance %s" % str(atoms)) 230 | return instance_hash 231 | 232 | def parse(instance): 233 | global total, unique, last_message 234 | atoms = {} 235 | 236 | # Parse the Alloy XML dump 237 | 238 | # first, parse all the sigs in the instance 239 | for sig in instance.findall("sig"): 240 | sig_label = re.sub(".*/", "", sig.attrib["label"]) 241 | # parse all the atoms which are members of this sig 242 | for atom in sig.findall("atom"): 243 | label = re.sub(".*/", "", atom.attrib["label"]) 244 | atoms[label] = Atom(label, sig_label) 245 | 246 | # parse all the relations in the instance 247 | for field in instance.findall("field"): 248 | field_label = re.sub(".*/", "", field.attrib["label"]) 249 | # parse each individual edge in the relation 250 | for tup in field.findall("tuple"): 251 | # parse the atoms in each edge. there should be two: a source and a 252 | # destination 253 | tuple_atoms = [] 254 | for atom in tup.findall("atom"): 255 | tuple_atoms.append(re.sub(".*/", "", atom.attrib["label"])) 256 | if len(tuple_atoms) == 2: 257 | src = tuple_atoms[0] 258 | dst = tuple_atoms[1] 259 | if hasattr(atoms[src], field_label): 260 | val = getattr(atoms[src], field_label) 261 | else: 262 | val = set() 263 | val.add(dst) 264 | setattr(atoms[src], field_label, val) 265 | else: 266 | raise Exception("illegal tuple arity") 267 | 268 | # get a hash of the parsed Alloy instance 269 | instance_hash = get_instance_hash(atoms) 270 | 271 | # check whether we've seen this instance before 272 | total += 1 273 | if instance_hash in instances: 274 | # print a status message occasionally 275 | if total - last_message >= 500: 276 | sys.stdout.write(" (%d/%d)\n" % (unique, total)) 277 | last_message = total 278 | else: 279 | unique += 1 280 | last_message = total 281 | sys.stdout.write("New hash (%d/%d): %s\n" % (unique, total, instance_hash)) 282 | sys.stdout.flush() 283 | instances[instance_hash] = atoms 284 | 285 | def run(): 286 | instance_text = "" 287 | for ln in sys.stdin: 288 | if ln == "": #EOF 289 | break 290 | 291 | # pre-parse the XML to search for individual commands 292 | if "univ { // no_p - constant for no perturbation 20 | (PTag->univ) - (PTag->univ) 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // =Power memory model= 25 | 26 | pred sc_per_loc_p[p: PTag->univ] { 27 | acyclic[po_loc_p[p] + (com_p[p])] 28 | } 29 | pred no_thin_air_p[p: PTag->univ] { 30 | acyclic[hb_p[p]] 31 | } 32 | pred observation_p[p: PTag->univ] { 33 | irreflexive[fre_p[p].(prop_p[p]).*(hb_p[p])] 34 | } 35 | pred propagation_p[p: PTag->univ] { 36 | acyclic[co_p[p] + prop_p[p]] 37 | } 38 | pred power_p[p: PTag->univ] { 39 | sc_per_loc_p[p] 40 | no_thin_air_p[p] 41 | observation_p[p] 42 | propagation_p[p] 43 | } 44 | 45 | fun cfence_p[p: PTag->univ] : Event->Event { 46 | (^(po_p[p]) :> CtrlFence).^(po_p[p]) 47 | + 48 | (^(po_p[p]) :> (lwsync & p[DFSC])).^(po_p[p]) 49 | } 50 | fun lwfence_p[p: PTag->univ] : Event->Event { 51 | ((^(po_p[p]) :> (lwsync - p[DFSC])).^(po_p[p])) 52 | + 53 | ((^(po_p[p]) :> (sync & p[DFSC])).^(po_p[p])) 54 | - 55 | (^(po_p[p]) & Write->Read) 56 | } 57 | fun ffence_p[p: PTag->univ] : Event->Event { 58 | (^(po_p[p]) :> (sync - p[DFSC])).^(po_p[p]) 59 | } 60 | fun fences_p[p: PTag->univ] : Event->Event { 61 | lwfence_p[p] + ffence_p[p] 62 | } // not cfence 63 | 64 | fun A_cumul_p[p: PTag->univ] : Event->Event { 65 | (rfe_p[p]).(fences_p[p]) 66 | } 67 | fun prop_base_p[p: PTag->univ] : Event->Event { 68 | (fences_p[p] + A_cumul_p[p]).*(hb_p[p]) 69 | } 70 | fun prop_p[p: PTag->univ] : Event->Event { 71 | (prop_base_p[p] & Write->Write) 72 | + 73 | (*(com_p[p]).*(prop_base_p[p]).(ffence_p[p]).*(hb_p[p])) 74 | } 75 | 76 | fun rdw_p[p: PTag->univ] : Event->Event { po_loc_p[p] & (fre_p[p].(rfe_p[p])) } 77 | fun detour_p[p: PTag->univ] : Event->Event { po_loc_p[p] & (coe.(rfe_p[p])) } 78 | 79 | fun ii0_p[p: PTag->univ] : Event->Event { addr_p[p] + data_p[p] + rdw_p[p] + rfi_p[p] } 80 | fun ci0_p[p: PTag->univ] : Event->Event { ctrl_p[p].(cfence_p[p]) + detour_p[p] } 81 | fun ic0_p[p: PTag->univ] : Event->Event { iden - iden } 82 | fun cc0_p[p: PTag->univ] : Event->Event { 83 | addr_p[p] + data_p[p] + po_loc_p[p] + ctrl_p[p] + (addr_p[p].^(po_p[p])) 84 | } 85 | fun ii1_p[p: PTag->univ] : Event->Event { 86 | ii0_p[p] + (ci0_p[p]) + ((ic0_p[p]).(ci0_p[p])) + ((ii0_p[p]).(ii0_p[p])) 87 | } 88 | fun ci1_p[p: PTag->univ] : Event->Event { 89 | ci0_p[p] + ((ci0_p[p]).(ii0_p[p])) + ((cc0_p[p]).(ci0_p[p])) 90 | } 91 | fun ic1_p[p: PTag->univ] : Event->Event { 92 | ic0_p[p] + (ii0_p[p]) + (cc0_p[p]) + ((ic0_p[p]).(cc0_p[p])) + ((ii0_p[p]).(ic0_p[p])) 93 | } 94 | fun cc1_p[p: PTag->univ] : Event->Event { 95 | cc0_p[p] + (ci0_p[p]) + ((ci0_p[p]).(ic0_p[p])) + ((cc0_p[p]).(cc0_p[p])) 96 | } 97 | fun ii2_p[p: PTag->univ] : Event->Event { 98 | ii1_p[p] + (ci1_p[p]) + ((ic1_p[p]).(ci1_p[p])) + ((ii1_p[p]).(ii1_p[p])) 99 | } 100 | fun ci2_p[p: PTag->univ] : Event->Event { 101 | ci1_p[p] + ((ci1_p[p]).(ii1_p[p])) + ((cc1_p[p]).(ci1_p[p])) 102 | } 103 | fun ic2_p[p: PTag->univ] : Event->Event { 104 | ic1_p[p] + (ii1_p[p]) + (cc1_p[p]) + ((ic1_p[p]).(cc1_p[p])) + ((ii1_p[p]).(ic1_p[p])) 105 | } 106 | fun cc2_p[p: PTag->univ] : Event->Event { 107 | cc1_p[p] + (ci1_p[p]) + ((ci1_p[p]).(ic1_p[p])) + ((cc1_p[p]).(cc1_p[p])) 108 | } 109 | fun ii_p[p: PTag->univ] : Event->Event { 110 | ii2_p[p] + (ci2_p[p]) + ((ic2_p[p]).(ci2_p[p])) + ((ii2_p[p]).(ii2_p[p])) 111 | } 112 | fun ci_p[p: PTag->univ] : Event->Event { 113 | ci2_p[p] + ((ci2_p[p]).(ii2_p[p])) + ((cc2_p[p]).(ci2_p[p])) 114 | } 115 | fun ic_p[p: PTag->univ] : Event->Event { 116 | ic2_p[p] + (ii2_p[p]) + (cc2_p[p]) + ((ic2_p[p]).(cc2_p[p])) + ((ii2_p[p]).(ic2_p[p])) 117 | } 118 | fun cc_p[p: PTag->univ] : Event->Event { 119 | cc2_p[p] + (ci2_p[p]) + ((ci2_p[p]).(ic2_p[p])) + ((cc2_p[p]).(cc2_p[p])) 120 | } 121 | 122 | fun ppo_p[p: PTag->univ] : Event->Event { 123 | (ii_p[p] & ^(po_p[p]) & Read->Read) 124 | + 125 | (ic_p[p] & ^(po_p[p]) & Read->Write) 126 | } 127 | 128 | fun hb_p[p: PTag->univ] : Event->Event { 129 | ppo_p[p] + fences_p[p] + rfe_p[p] 130 | } 131 | 132 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 133 | // =Basic model of memory= 134 | 135 | sig Address { } 136 | 137 | sig Thread { start: one Event } 138 | 139 | abstract sig Event { 140 | po: lone Event, 141 | } 142 | 143 | abstract sig MemoryEvent extends Event { 144 | address: one Address, 145 | } 146 | sig Read extends MemoryEvent { 147 | rmw: lone Write, 148 | addr: set Event, 149 | ctrl: set Event, 150 | data: set Event, 151 | } 152 | fun dep : Read->Event { addr + ctrl + data } 153 | sig Write extends MemoryEvent { 154 | rf: set Read, 155 | co: set Write 156 | } 157 | 158 | abstract sig Fence extends Event { } 159 | sig CtrlFence extends Fence {} 160 | sig lwsync extends Fence {} 161 | sig sync extends Fence {} 162 | 163 | //address 164 | fact { co + rf + fr in address.~address } 165 | 166 | //po 167 | fact { acyclic[po] } 168 | fact { all e: Event | one t: Thread | t->e in start.*po } 169 | fun po_loc : Event->Event { ^po & address.~address } 170 | 171 | //writes 172 | fact { all a: Address | total[co, a.~address :> Write] } 173 | 174 | //reads 175 | fact { rf.~rf in iden } 176 | fun fr : Read->Write { 177 | ~rf.co 178 | + 179 | // also include reads that read from the initial state 180 | ((Read - (Write.rf)) <: (address.~address) :> Write) 181 | } 182 | 183 | //dep 184 | fact { dep in ^po } 185 | 186 | //rmws 187 | fact { rmw in po & dep & address.~address } 188 | 189 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 190 | // =Model of memory, under perturbation= 191 | 192 | // po is not transitive 193 | fun po_t[p: PTag->univ] : Event->Event { (Event - p[RE]) <: ^po :> (Event - p[RE]) } 194 | fun po_p[p: PTag->univ] : Event->Event { po_t[p] - (po_t[p]).(po_t[p]) } 195 | 196 | // po_loc is already transitive 197 | fun po_loc_p[p: PTag->univ] : MemoryEvent->MemoryEvent { (MemoryEvent - p[RE]) <: ^po_loc :> (MemoryEvent - p[RE]) } 198 | 199 | // no need for rmw_p, since rmws are removed in pairs FIXME 200 | fun rf_p[p: PTag->univ] : Write->Read { (Write - p[RE]) <: rf :> (Read - p[RE]) } 201 | fun co_p[p: PTag->univ] : Write->Write { (Write - p[RE]) <: co :> (Write - p[RE]) } 202 | fun fr_p[p: PTag->univ] : Read->Write { (Read - p[RE]) <: fr :> (Write - p[RE]) } 203 | //fun fr_p[p: PTag->univ] : Read->Write { 204 | // ( ~(rf_p[p]).^(co_p[p]) ) 205 | // + 206 | // ( (Read-(Write.rf)-p[RE]) <: address.~address :> (Write - p[RE]) ) 207 | // //((Read - p[RE])->(Write - p[RE]) & address.~address) - (~(rf_p[p]).*(co_p[p])) 208 | //} 209 | 210 | fun addr_p[p: PTag->univ] : Event->Event { 211 | (Event - p[RE+RD]) <: addr :> (Event - p[RE]) 212 | } 213 | fun ctrl_p[p: PTag->univ] : Event->Event { 214 | (Event - p[RE+RD]) <: ctrl :> (Event - p[RE]) 215 | + 216 | ((Event & p[RD]) - p[RE]) <: data :> (Event - p[RE]) 217 | } 218 | fun data_p[p: PTag->univ] : Event->Event { 219 | (Event - p[RE+RD]) <: data :> (Event - p[RE]) 220 | + 221 | ((Event & p[RD]) - p[RE]) <: addr :> (Event - p[RE]) 222 | } 223 | 224 | //////////////////////////////////////////////////////////////////////////////// 225 | // =Shortcuts= 226 | 227 | fun same_thread [rel: Event->Event] : Event->Event { 228 | rel & (iden + ^po + ~^po) 229 | } 230 | 231 | fun com : MemoryEvent->MemoryEvent { rf + fr + co } 232 | fun rfi : MemoryEvent->MemoryEvent { same_thread[rf] } 233 | fun rfe : MemoryEvent->MemoryEvent { rf - rfi } 234 | fun fri : MemoryEvent->MemoryEvent { same_thread[fr] } 235 | fun fre : MemoryEvent->MemoryEvent { fr - fri } 236 | fun coi : MemoryEvent->MemoryEvent { same_thread[co] } 237 | fun coe : MemoryEvent->MemoryEvent { co - coi } 238 | 239 | fun com_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] + fr_p[p] + co_p[p] } 240 | fun rfi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[rf_p[p]] } 241 | fun rfe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] - rfi_p[p] } 242 | fun fri_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[fr_p[p]] } 243 | fun fre_p[p: PTag->univ] : MemoryEvent->MemoryEvent { fr_p[p] - fri_p[p] } 244 | fun coi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[co_p[p]] } 245 | fun coe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { co_p[p] - coi_p[p] } 246 | 247 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 248 | // =Alloy helpers= 249 | 250 | pred irreflexive[rel: Event->Event] { no iden & rel } 251 | pred acyclic[rel: Event->Event] { irreflexive[^rel] } 252 | pred total[rel: Event->Event, bag: Event] { 253 | all disj e, e2: bag | e->e2 in rel + ~rel 254 | acyclic[rel] 255 | } 256 | 257 | //////////////////////////////////////////////////////////////////////////////// 258 | // =Perturbation auxiliaries= 259 | 260 | let interesting[axiom] { 261 | not axiom[no_p] 262 | all e: Event | power_p[RE->e] 263 | all f: lwsync+sync | power_p[DFSC->f] 264 | all r: Read | no r.dep or power_p[RD->r] 265 | } 266 | 267 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 268 | // =Constraints on the search space= 269 | 270 | fact { 271 | all a: Address | some (a.~address) :> Write 272 | } 273 | 274 | //////////////////////////////////////////////////////////////////////////////// 275 | 276 | pred sc_per_loc { 277 | interesting[sc_per_loc_p] 278 | } 279 | run sc_per_loc for 3 280 | 281 | pred no_thin_air { 282 | interesting[no_thin_air_p] 283 | } 284 | run no_thin_air for 3 285 | 286 | pred observation { 287 | interesting[observation_p] 288 | } 289 | run observation for 3 290 | 291 | pred propagation { 292 | interesting[propagation_p] 293 | } 294 | run propagation for 3 295 | 296 | pred union { 297 | interesting[sc_per_loc_p] 298 | or 299 | interesting[no_thin_air_p] 300 | or 301 | interesting[observation_p] 302 | or 303 | interesting[propagation_p] 304 | } 305 | run union for 3 306 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -z $3 ]; then 3 | echo "Usage: ./perturb.sh " 4 | exec /bin/false 5 | fi 6 | 7 | TESTSFILE=$1 8 | CANONICALIZER="$PYTHON canon.py" 9 | TIMESTAMP=`date +'%Y%m%d-%H%M%S'` 10 | ALLOYPATH=. 11 | #echo "# Tests file: $TESTSFILE" 12 | 13 | time java $JAVAFLAGS -classpath $ALLOYPATH:$ALLOYPATH/alloy4.2.jar MainClass -f $TESTSFILE -b ${@:2} | $CANONICALIZER $TIMESTAMP-$3-$2 14 | -------------------------------------------------------------------------------- /scc_perturbed_scflip.als: -------------------------------------------------------------------------------- 1 | // Automated Synthesis of Comprehensive Memory Model Litmus Test Suites 2 | // Daniel Lustig, Andrew Wright, Alexandros Papakonstantinou, Olivier Giroux 3 | // ASPLOS 2017 4 | // 5 | // Copyright (c) 2017, NVIDIA Corporation. All rights reserved. 6 | // 7 | // This file is licensed under the BSD-3 license. See LICENSE for details. 8 | 9 | // Streamlined Causal Consistency (SCC) Model 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // =Perturbations= 13 | 14 | abstract sig PTag {} 15 | 16 | one sig RE extends PTag {} // Remove Event 17 | one sig DRA extends PTag {} // Demote Release Acquire 18 | one sig DFSC extends PTag {} // Demote FenceSC 19 | one sig RD extends PTag {} // Remove Dependency 20 | 21 | fun no_p : PTag->univ { // no_p - constant for no perturbation 22 | (PTag->univ) - (PTag->univ) 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // =SCC memory model= 27 | 28 | pred scc_p[p: PTag->univ] { 29 | sc_per_loc_p[p] 30 | no_thin_air_p[p] 31 | rmw_atomicity_p[p] 32 | causality_p[p] 33 | } 34 | 35 | // HACK!!! see the paper 36 | fact { lone sc } 37 | pred scc_scflip[p: PTag->univ] { 38 | sc_per_loc_p[p] 39 | no_thin_air_p[p] 40 | rmw_atomicity_p[p] 41 | causality_scflip_p[p] 42 | } 43 | 44 | pred sc_per_loc_p[p: PTag->univ] { 45 | acyclic[com_p[p] + po_loc_p[p]] 46 | } 47 | pred no_thin_air_p[p: PTag->univ] { 48 | acyclic[rf_p[p] + dep_p[p]] 49 | } 50 | pred rmw_atomicity_p[p: PTag->univ] { 51 | no (fr_p[p]).(co_p[p]) & rmw_p[p] 52 | } 53 | pred causality_p[p: PTag->univ] { 54 | irreflexive[*(com_p[p]).^(cause_p[p])] 55 | } 56 | 57 | // HACK!!! see the paper 58 | pred causality_scflip_p[p: PTag->univ] { 59 | irreflexive[*(com_p[p]).^(cause_p[p])] 60 | or 61 | irreflexive[*(com_p[p]).^(cause_scflip_p[p])] 62 | } 63 | 64 | fun prefix_p[p: PTag->univ] : Event->Event { 65 | iden + ((Fence - p[RE]) <: po_p[p]) + ((Release - p[RE] - p[DRA]) <: po_loc_p[p]) 66 | } 67 | fun suffix_p[p: PTag->univ] : Event->Event { 68 | iden + (po_p[p] :> (Fence - p[RE])) + (po_loc_p[p] :> (Acquire - p[RE] - p[DRA])) 69 | } 70 | fun observation_p[p: PTag->univ] : Event->Event { 71 | rf_p[p] + rmw_p[p] 72 | } 73 | fun Releasers_p[p: PTag->univ] : Event { 74 | (Release - p[RE] - p[DRA]) + (Fence - p[RE]) 75 | } 76 | fun Acquirers_p[p: PTag->univ] : Event { 77 | (Acquire - p[RE] - p[DRA]) + (Fence - p[RE]) 78 | } 79 | fun sync_p[p: PTag->univ] : Event->Event { 80 | Releasers_p[p] <: (prefix_p[p]).^(observation_p[p]).(suffix_p[p]) :> Acquirers_p[p] 81 | } 82 | fun cause_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 83 | *(po_p[p]).(sc_p[p] + sync_p[p]).*(po_p[p]) 84 | } 85 | fun cause_scflip_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 86 | *(po_p[p]).(~(sc_p[p]) + sync_p[p]).*(po_p[p]) 87 | } 88 | 89 | //////////////////////////////////////////////////////////////////////////////// 90 | // =Basic model of memory= 91 | 92 | sig Address { } 93 | 94 | sig Thread { start: one Event } 95 | 96 | abstract sig Event { po: lone Event } 97 | 98 | abstract sig MemoryEvent extends Event { 99 | address: one Address, 100 | dep: set MemoryEvent, 101 | } 102 | sig Read extends MemoryEvent { rmw: lone Write } 103 | sig Acquire extends Read { } 104 | sig Write extends MemoryEvent { rf: set Read, co: set Write } 105 | sig Release extends Write { } 106 | 107 | abstract sig Fence extends Event {} 108 | sig FenceAll extends Fence { } 109 | sig FenceSC extends FenceAll { 110 | sc: set FenceSC 111 | } 112 | 113 | //////////////////////////////////////////////////////////////////////////////// 114 | // =Constraints on basic model of memory= 115 | 116 | // All communication is via accesses to the same address 117 | fact { com in address.~address } 118 | 119 | // Program order is sane 120 | fact { acyclic[po] } 121 | fact { all e: Event | one t: Thread | t->e in start.*po } 122 | fun po_loc : Event->Event { ^po & address.~address } 123 | 124 | // Dependencies go from Reads to Reads or Writes 125 | fact { dep in Read <: ^po } 126 | 127 | // co is a per-address total order 128 | fact { all a: Address | total[co, a.~address :> Write] } 129 | 130 | // Each read sources from at most one write 131 | // (could be zero if sourcing from the initial condition) 132 | fact { rf.~rf in iden } 133 | // fr is defined in the standard way 134 | fun fr : Read->Write { 135 | ~rf.co 136 | + 137 | // also include reads that read from the initial state 138 | ((Read - (Write.rf)) <: (address.~address) :> Write) 139 | } 140 | 141 | // RMW pairs are sane and overlap with dep 142 | fact { rmw in po & dep & address.~address } 143 | 144 | // sc is a total order on FenceSCs 145 | fact { total[sc, FenceSC] } 146 | 147 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 148 | // =Model of memory, under perturbation= 149 | 150 | // po is not transitive 151 | fun po_t[p: PTag->univ] : Event->Event { (Event - p[RE]) <: ^po :> (Event - p[RE]) } 152 | fun po_p[p: PTag->univ] : Event->Event { po_t[p] - (po_t[p]).(po_t[p]) } 153 | 154 | // po_loc is already transitive 155 | fun po_loc_p[p: PTag->univ] : MemoryEvent->MemoryEvent { (MemoryEvent - p[RE]) <: ^po_loc :> (MemoryEvent - p[RE]) } 156 | 157 | // dep is not transitive 158 | fun dep_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 159 | (Read - p[RE] - p[RD]) <: *(dep :> p[RE]).dep :> (MemoryEvent - p[RE]) 160 | } 161 | 162 | fun rf_p[p: PTag->univ] : Write->Read { (Write - p[RE]) <: rf :> (Read - p[RE]) } 163 | fun co_p[p: PTag->univ] : Write->Write { (Write - p[RE]) <: co :> (Write - p[RE]) } 164 | fun fr_p[p: PTag->univ] : Read->Write { (Read - p[RE]) <: fr :> (Write - p[RE]) } 165 | //fun fr_p[p: PTag->univ] : Read->Write { 166 | // ( ~(rf_p[p]).^(co_p[p]) ) 167 | // + 168 | // ( (Read-(Write.rf)-p[RE]) <: address.~address :> (Write - p[RE]) ) 169 | // //((Read - p[RE])->(Write - p[RE]) & address.~address) - (~(rf_p[p]).*(co_p[p])) 170 | //} 171 | fun rmw_p[p: PTag->univ] : Read->Write { (Read - p[RE] - p[RD]) <: rmw :> (Write - p[RE]) } 172 | 173 | // sc is not transitive 174 | fun sc_t[p: PTag->univ] : Event->Event { (Event - p[RE] - p[DFSC]) <: ^sc :> (Event - p[RE] - p[DFSC]) } 175 | fun sc_p[p: PTag->univ] : Event->Event { sc_t[p] - (sc_t[p]).(sc_t[p]) } 176 | 177 | //////////////////////////////////////////////////////////////////////////////// 178 | // =Shortcuts= 179 | 180 | fun same_thread [rel: Event->Event] : Event->Event { 181 | rel & ( iden + ^po + ~^po ) 182 | } 183 | 184 | fun com : MemoryEvent->MemoryEvent { rf + fr + co } 185 | fun rfi : MemoryEvent->MemoryEvent { same_thread[rf] } 186 | fun rfe : MemoryEvent->MemoryEvent { rf - rfi[] } 187 | fun fri : MemoryEvent->MemoryEvent { same_thread[fr] } 188 | fun fre : MemoryEvent->MemoryEvent { fr - fri } 189 | fun coi : MemoryEvent->MemoryEvent { same_thread[co] } 190 | fun coe : MemoryEvent->MemoryEvent { co - coi } 191 | 192 | fun com_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] + fr_p[p] + co_p[p] } 193 | fun rfi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[rf_p[p]] } 194 | fun rfe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] - rfi_p[p] } 195 | fun fri_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[fr_p[p]] } 196 | fun fre_p[p: PTag->univ] : MemoryEvent->MemoryEvent { fr_p[p] - fri_p[p] } 197 | fun coi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[co_p[p]] } 198 | fun coe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { co_p[p] - coi_p[p] } 199 | 200 | //////////////////////////////////////////////////////////////////////////////// 201 | // =Alloy helpers= 202 | 203 | pred irreflexive[rel: Event->Event] { no iden & rel } 204 | pred acyclic[rel: Event->Event] { irreflexive[^rel] } 205 | pred total[rel: Event->Event, bag: Event] { 206 | all disj e, e2: bag | e->e2 in rel + ~rel 207 | acyclic[rel] 208 | } 209 | 210 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 211 | // =Perturbation auxiliaries= 212 | 213 | let interesting_not_axiom[axiom] { 214 | not axiom[no_p] 215 | 216 | // All events must be relevant and minimal 217 | all e: Event | scc_p[RE->(e + e.rmw + e.~rmw)] 218 | all e: Release+Acquire | scc_p[DRA->e] 219 | all f: FenceSC | scc_p[DFSC->f] 220 | all r: Read | scc_p[RD->r] or no r.dep 221 | } 222 | 223 | let interesting_not_axiom_scflip[axiom] { 224 | not axiom[no_p] 225 | 226 | // All events must be relevant and minimal 227 | all e: Event | scc_scflip[RE->(e + e.rmw + e.~rmw)] 228 | all e: Release+Acquire | scc_scflip[DRA->e] 229 | all f: FenceSC | scc_scflip[DFSC->f] 230 | all r: Read | scc_scflip[RD->r] or no r.dep 231 | } 232 | 233 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 234 | // =Constraints on the search space= 235 | 236 | fact { 237 | all a: Address | some (a.~address) :> Write 238 | } 239 | fact { 240 | irreflexive[^po.sc] 241 | } 242 | 243 | //////////////////////////////////////////////////////////////////////////////// 244 | 245 | run sc_per_loc { 246 | interesting_not_axiom[sc_per_loc_p] 247 | } for 3 248 | 249 | run no_thin_air { 250 | interesting_not_axiom[no_thin_air_p] 251 | } for 3 252 | 253 | run rmw_atomicity { 254 | interesting_not_axiom[rmw_atomicity_p] 255 | } for 3 256 | 257 | run causality { 258 | interesting_not_axiom[causality_p] 259 | } for 3 260 | 261 | run causality_scflip { 262 | interesting_not_axiom_scflip[causality_scflip_p] 263 | } for 3 but 2 FenceSC 264 | 265 | run union { 266 | interesting_not_axiom[sc_per_loc_p] 267 | or 268 | interesting_not_axiom[no_thin_air_p] 269 | or 270 | interesting_not_axiom[rmw_atomicity_p] 271 | or 272 | interesting_not_axiom[causality_p] 273 | } for 3 274 | 275 | run union_scflip { 276 | interesting_not_axiom[sc_per_loc_p] 277 | or 278 | interesting_not_axiom[no_thin_air_p] 279 | or 280 | interesting_not_axiom[rmw_atomicity_p] 281 | or 282 | interesting_not_axiom_scflip[causality_scflip_p] 283 | } for 3 but 2 FenceSC 284 | -------------------------------------------------------------------------------- /tso_perturbed.als: -------------------------------------------------------------------------------- 1 | // Automated Synthesis of Comprehensive Memory Model Litmus Test Suites 2 | // Daniel Lustig, Andrew Wright, Alexandros Papakonstantinou, Olivier Giroux 3 | // ASPLOS 2017 4 | // 5 | // Copyright (c) 2017, NVIDIA Corporation. All rights reserved. 6 | // 7 | // This file is licensed under the BSD-3 license. See LICENSE for details. 8 | 9 | // Total Store Ordering (TSO) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | // =Perturbations= 13 | 14 | abstract sig PTag {} 15 | 16 | one sig RE extends PTag {} // Remove Event 17 | one sig RD extends PTag {} // Remove Dependency (RMW) 18 | 19 | fun no_p : PTag->univ { // no_p - constant for no perturbation 20 | (PTag->univ) - (PTag->univ) 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // =TSO memory model= 25 | 26 | pred sc_per_loc[p: PTag->univ] { 27 | acyclic[rf_p[p] + co_p[p] + fr_p[p] + po_loc_p[p]] 28 | } 29 | pred rmw_atomicity[p: PTag->univ] { 30 | no (fr_p[p]).(co_p[p]) & rmw_p[p] 31 | } 32 | pred causality[p: PTag->univ] { 33 | acyclic[rfe_p[p] + co_p[p] + fr_p[p] + ppo_p[p] + fence_p[p]] 34 | } 35 | 36 | pred tso_p[p: PTag->univ] { 37 | sc_per_loc[p] 38 | rmw_atomicity[p] 39 | causality[p] 40 | } 41 | 42 | fun fence_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 43 | ((MemoryEvent - p[RE]) <: ^po :> (Fence - p[RE])).(^po :> (MemoryEvent - p[RE])) 44 | } 45 | fun ppo_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 46 | ((MemoryEvent - p[RE]) <: ^po :> (MemoryEvent - p[RE])) 47 | - 48 | ((Write - ((Read - p[RD]).rmw))->((Read - p[RD]) - Write.~rmw)) 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | // =Basic model of memory= 53 | 54 | sig Address { } 55 | 56 | sig Thread { start: one Event } 57 | 58 | abstract sig Event { po: lone Event } 59 | 60 | abstract sig MemoryEvent extends Event { 61 | address: one Address, 62 | dep: set MemoryEvent, 63 | } 64 | sig Read extends MemoryEvent { rmw: lone Write } 65 | sig Write extends MemoryEvent { rf: set Read, co: set Write } 66 | 67 | sig Fence extends Event {} 68 | 69 | //////////////////////////////////////////////////////////////////////////////// 70 | // =Constraints on basic model of memory= 71 | 72 | // All communication is via accesses to the same address 73 | fact { com in address.~address } 74 | 75 | // Program order is sane 76 | fact { acyclic[po] } 77 | fact { all e: Event | one t: Thread | t->e in start.*po } 78 | fun po_loc : MemoryEvent->MemoryEvent { ^po & address.~address } 79 | 80 | // Dependencies go from Reads to Reads or Writes 81 | fact { dep in Read <: ^po } 82 | 83 | // co is a per-address total order 84 | fact { all a: Address | total[co, a.~address :> Write] } 85 | 86 | // Each read sources from at most one write 87 | // (could be zero if sourcing from the initial condition) 88 | fact { rf.~rf in iden } 89 | // fr is defined in the standard way 90 | fun fr : Read->Write { 91 | ~rf.co 92 | + 93 | // also include reads that read from the initial state 94 | ((Read - (Write.rf)) <: (address.~address) :> Write) 95 | } 96 | 97 | // RMW pairs are sane and overlap with dep 98 | fact { rmw in po & dep & address.~address } 99 | 100 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 101 | // =Model of memory, under perturbation= 102 | 103 | // po is not transitive 104 | fun po_t[p: PTag->univ] : Event->Event { (Event - p[RE]) <: ^po :> (Event - p[RE]) } 105 | fun po_p[p: PTag->univ] : Event->Event { po_t[p] - (po_t[p]).(po_t[p]) } 106 | 107 | // po_loc is already transitive 108 | fun po_loc_p[p: PTag->univ] : MemoryEvent->MemoryEvent { (MemoryEvent - p[RE]) <: po_loc :> (MemoryEvent - p[RE]) } 109 | 110 | // dep is not transitive 111 | fun dep_p[p: PTag->univ] : MemoryEvent->MemoryEvent { 112 | (Read - p[RE] - p[RD]) <: *(dep :> p[RE]).dep :> (MemoryEvent - p[RE]) 113 | } 114 | 115 | fun rf_p[p: PTag->univ] : Write->Read { (Write - p[RE]) <: rf :> (Read - p[RE]) } 116 | fun co_p[p: PTag->univ] : Write->Write { (Write - p[RE]) <: co :> (Write - p[RE]) } 117 | //fun fr_p[p: PTag->univ] : Read->Write { (Read - p[RE]) <: fr :> (Write - p[RE]) } 118 | fun fr_p[p: PTag->univ] : Read->Write { 119 | ( ~(rf_p[p]).^(co_p[p]) ) 120 | + 121 | ( (Read-(Write.rf)-p[RE]) <: address.~address :> (Write - p[RE]) ) 122 | } 123 | fun rmw_p[p: PTag->univ] : Read->Write { (Read - p[RE] - p[RD]) <: rmw :> (Write - p[RE]) } 124 | 125 | //////////////////////////////////////////////////////////////////////////////// 126 | // =Shortcuts= 127 | 128 | fun same_thread [rel: Event->Event] : Event->Event { 129 | rel & ( iden + ^po + ~^po ) 130 | } 131 | 132 | fun com[] : MemoryEvent->MemoryEvent { rf + fr + co } 133 | fun rfi[] : MemoryEvent->MemoryEvent { same_thread[rf] } 134 | fun rfe[] : MemoryEvent->MemoryEvent { rf - rfi[] } 135 | fun fri[] : MemoryEvent->MemoryEvent { same_thread[fr] } 136 | fun fre[] : MemoryEvent->MemoryEvent { fr - fri } 137 | fun coi[] : MemoryEvent->MemoryEvent { same_thread[co] } 138 | fun coe[] : MemoryEvent->MemoryEvent { co - coi } 139 | 140 | fun com_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] + fr_p[p] + co_p[p] } 141 | fun rfi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[rf_p[p]] } 142 | fun rfe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { rf_p[p] - rfi_p[p] } 143 | fun fri_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[fr_p[p]] } 144 | fun fre_p[p: PTag->univ] : MemoryEvent->MemoryEvent { fr_p[p] - fri_p[p] } 145 | fun coi_p[p: PTag->univ] : MemoryEvent->MemoryEvent { same_thread[co_p[p]] } 146 | fun coe_p[p: PTag->univ] : MemoryEvent->MemoryEvent { co_p[p] - coi_p[p] } 147 | 148 | //////////////////////////////////////////////////////////////////////////////// 149 | // =Alloy helpers= 150 | 151 | pred irreflexive[rel: Event->Event] { no iden & rel } 152 | pred acyclic[rel: Event->Event] { irreflexive[^rel] } 153 | pred total[rel: Event->Event, bag: Event] { 154 | all disj e, e2: bag | e->e2 in rel + ~rel 155 | acyclic[rel] 156 | } 157 | 158 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 159 | // =Constraints on the search space= 160 | 161 | fact { 162 | all a: Address | some (a.~address) :> Write 163 | } 164 | 165 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 166 | // =Perturbation auxiliaries= 167 | 168 | let interesting_not_axiom[axiom] { 169 | not axiom[no_p] 170 | 171 | // All events must be relevant and minimal 172 | all e: Event | tso_p[RE->(e + e.rmw + e.~rmw)] 173 | all e: Read | tso_p[RD->e] or no e.dep 174 | } 175 | 176 | //////////////////////////////////////////////////////////////////////////////// 177 | 178 | run sc_per_loc { 179 | interesting_not_axiom[sc_per_loc] 180 | } for 4 181 | 182 | run rmw_atomicity { 183 | interesting_not_axiom[rmw_atomicity] 184 | } for 4 185 | 186 | run causality { 187 | interesting_not_axiom[causality] 188 | } for 4 189 | 190 | run union { 191 | interesting_not_axiom[sc_per_loc] 192 | or 193 | interesting_not_axiom[rmw_atomicity] 194 | or 195 | interesting_not_axiom[causality] 196 | } for 4 197 | --------------------------------------------------------------------------------