├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── AUTHORS ├── Doxyfile ├── HACKING.rst ├── LICENSE ├── Makefile ├── README.rst ├── apidoc └── mainpage.md ├── contrib └── mcversi │ ├── README.rst │ ├── guest_workload.c │ ├── host_support.h │ ├── m5_host_support.h │ ├── run-10-8KB-1synonym.sh │ └── run-10-8KB.sh ├── include └── mc2lib │ ├── codegen │ ├── cats.hpp │ ├── compiler.hpp │ ├── ops │ │ ├── armv7.hpp │ │ ├── strong.hpp │ │ └── x86_64.hpp │ └── rit.hpp │ ├── mcversi.hpp │ ├── memconsistency │ ├── cats.hpp │ ├── eventsets.hpp │ └── model12.hpp │ ├── sets.hpp │ ├── simplega.hpp │ └── types.hpp └── src ├── test_codegen_armv7.cpp ├── test_codegen_x86_64.cpp ├── test_main.cpp ├── test_mcversi.cpp ├── test_memconsistency.cpp ├── test_sets.cpp └── test_simplega.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | .clang_complete 4 | apidoc/html 5 | build 6 | compile_commands.json 7 | test_mc2lib 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/googletest"] 2 | path = third_party/googletest 3 | url = https://github.com/google/googletest.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 sts=2 et : 2 | 3 | language: cpp 4 | 5 | matrix: 6 | include: 7 | # Oldest supported GCC 8 | - os: linux 9 | addons: 10 | apt: 11 | sources: ubuntu-toolchain-r-test 12 | packages: g++-4.7 13 | compiler: gcc-4.7 14 | env: CXX_=g++-4.7 15 | 16 | # Latest GCC 17 | - os: linux 18 | addons: 19 | apt: 20 | sources: ubuntu-toolchain-r-test 21 | packages: g++-5 22 | compiler: gcc-5 23 | env: CXX_=g++-5 24 | 25 | # Latest Clang 26 | - os: linux 27 | addons: 28 | apt: 29 | sources: 30 | - ubuntu-toolchain-r-test 31 | - llvm-toolchain-precise-3.7 32 | packages: clang-3.7 33 | compiler: clang-3.7 34 | env: CXX_=clang++-3.7 35 | 36 | script: 37 | # Travis exports CXX after set via env. Fix. 38 | - export CXX=$CXX_ 39 | - $CXX --version 40 | # Test library 41 | - make clean check 42 | - make clean check BUILDFLAGS='-O2 -DNDEBUG' 43 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | git shortlog -se 2 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | Guidelines 2 | ========== 3 | 4 | Following `C++ Core Guidelines `_ 5 | where possible. 6 | 7 | Naming and formatting is according to the `Google C++ Style Guide 8 | `_. Use ``make format``. 9 | 10 | New standards-compliant language features in C++11 are encouraged if they make 11 | the code safer, more maintainable and/or easier to understand/use. The only 12 | limiting factor is the oldest to be supported compiler versions (currently GCC 13 | 4.7, Clang 3.3). 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016, Marco Elver 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the 14 | distribution. 15 | 16 | * Neither the name of the software nor the names of its contributors 17 | may be used to endorse or promote products derived from this 18 | software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Test Makefile for mc2lib 3 | # 4 | 5 | CXX ?= g++ 6 | DOXYGEN ?= doxygen 7 | CLANG_TIDY ?= clang-tidy 8 | CLANG_FORMAT ?= clang-format 9 | CPPLINT ?= cpplint 10 | 11 | GTEST_DIR = third_party/googletest/googletest 12 | GMOCK_DIR = third_party/googletest/googlemock 13 | 14 | BUILDFLAGS = -g 15 | WARNFLAGS = -Wall -Werror 16 | INCLUDES = -isystem $(GTEST_DIR)/include -isystem $(GMOCK_DIR)/include -I./include 17 | CXXFLAGS = -std=c++11 -pthread 18 | LIBS = 19 | 20 | gtestmock_MODULES = build/$(GTEST_DIR)/src/gtest-all.cc.o build/$(GMOCK_DIR)/src/gmock-all.cc.o 21 | 22 | HEADER_FILES = $(shell find include -name "*.hpp") 23 | 24 | test_mc2lib_MODULES = $(shell find src -name "test_*.cpp" | while read line; do echo "build/$${line}.o"; done) 25 | 26 | .PHONY: all 27 | all: test_mc2lib 28 | 29 | test_mc2lib: $(gtestmock_MODULES) $(test_mc2lib_MODULES) 30 | $(CXX) $(CXXFLAGS) $(BUILDFLAGS) $(WARNFLAGS) $(LIBS) $^ -o $@ 31 | 32 | build/src/%.cpp.o: src/%.cpp $(HEADER_FILES) 33 | @mkdir -pv $$(dirname $@) 34 | $(CXX) $(CXXFLAGS) $(BUILDFLAGS) $(WARNFLAGS) $(INCLUDES) -c -o $@ $< 35 | 36 | build/third_party/googletest/%.cc.o: third_party/googletest/%.cc 37 | @mkdir -pv $$(dirname $@) 38 | $(CXX) $(CXXFLAGS) $(BUILDFLAGS) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(INCLUDES) -c -o $@ $^ 39 | 40 | .PHONY: tidy 41 | tidy: compile_commands.json 42 | $(CLANG_TIDY) \ 43 | -header-filter='.*' \ 44 | -checks='-*,clang-analyzer-*,google*,misc*,-misc-unused-parameters,performance*' \ 45 | $(shell find src -name "*.cpp") 46 | 47 | compile_commands.json: Makefile 48 | echo "[" > compile_commands.json 49 | for obj in $(test_mc2lib_MODULES); do \ 50 | src=$${obj%.o}; src=$${src#build/}; \ 51 | echo "$${delim}{ \"directory\" : \"$(PWD)\"," \ 52 | "\"command\" : \"/usr/bin/clang++ $(CXXFLAGS) $(BUILDFLAGS) $(WARNFLAGS) $(INCLUDES) -c -o $${obj} $${src}\"," \ 53 | "\"file\": \"$${src}\" }" >> compile_commands.json; \ 54 | delim=","; \ 55 | done 56 | echo "]" >> compile_commands.json 57 | 58 | .PHONY: format 59 | format: 60 | @echo "Modifying files in-place..." 61 | $(CLANG_FORMAT) \ 62 | -style=file \ 63 | -i \ 64 | $(shell git ls-files | grep -E '\.(hpp|cpp)') 65 | 66 | .PHONY: lint 67 | lint: 68 | $(CPPLINT) \ 69 | --extensions=cpp,hpp \ 70 | --filter=-build/c++11 \ 71 | $(shell git ls-files | grep -E '\.(hpp|cpp)') 72 | 73 | .PHONY: check 74 | check: test_mc2lib 75 | ./test_mc2lib 76 | 77 | .PHONY: doc 78 | doc: 79 | $(RM) -r apidoc/html 80 | $(DOXYGEN) Doxyfile 81 | 82 | .PHONY: clean 83 | clean: 84 | $(RM) $(test_mc2lib_MODULES) 85 | $(RM) test_mc2lib 86 | 87 | .PHONY: cleanall 88 | cleanall: clean 89 | $(RM) compile_commands.json 90 | $(RM) -r build 91 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | mc2lib 3 | ====== 4 | 5 | .. image:: https://travis-ci.org/melver/mc2lib.svg?branch=master 6 | :target: https://travis-ci.org/melver/mc2lib 7 | 8 | A memory consistency model checking library implemented in C++11. This library 9 | provides the building blocks for the `McVerSi 10 | `_ framework [1]_. 11 | 12 | The McVerSi guest workload can be found in ``_. 13 | 14 | Usage 15 | ===== 16 | 17 | The library is header-only. 18 | 19 | API documentation can be found `here 20 | `_. 21 | 22 | The provided ``_ is for unit testing and static analysis. 23 | 24 | Citation 25 | ======== 26 | 27 | If you use this library or the McVerSi framework in your work, we would 28 | appreciate if you cite the McVerSi paper: 29 | 30 | .. code-block:: 31 | 32 | @inproceedings{ElverN2016, 33 | author = {Marco Elver and Vijay Nagarajan}, 34 | title = {{McVerSi}: {A} {T}est {G}eneration {F}ramework for {F}ast 35 | {M}emory {C}onsistency {V}erification in {S}imulation}, 36 | booktitle = {IEEE International Symposium on 37 | High Performance Computer Architecture (HPCA)}, 38 | month = mar, 39 | year = {2016}, 40 | venue = {Barcelona, Spain} 41 | } 42 | 43 | Extensions 44 | ========== 45 | 46 | Notable extensions that have been added after publication of the McVerSi paper: 47 | 48 | * Support for synonym sets of virtual addresses mapping to same physical 49 | address -- see `EvtStateCats `_ and 50 | `guest_workload `_. 51 | 52 | References 53 | ========== 54 | 55 | .. [1] Marco Elver and Vijay Nagarajan. `McVerSi: A Test Generation Framework 56 | for Fast Memory Consistency Verification in Simulation 57 | `_. In IEEE 58 | International Symposium on High Performance Computer Architecture 59 | (HPCA). Barcelona, Spain, March 2016. 60 | -------------------------------------------------------------------------------- /apidoc/mainpage.md: -------------------------------------------------------------------------------- 1 | mc2lib {#mainpage} 2 | ====== 3 | 4 | This is the API documentation for [mc2lib](https://github.com/melver/mc2lib). 5 | -------------------------------------------------------------------------------- /contrib/mcversi/README.rst: -------------------------------------------------------------------------------- 1 | McVerSi Guest Workload 2 | ====================== 3 | 4 | This is the guest workload as used in the McVerSi paper, with modification to 5 | enable support on more architectures. 6 | -------------------------------------------------------------------------------- /contrib/mcversi/guest_workload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _GNU_SOURCE 35 | # define _GNU_SOURCE 36 | #endif 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "host_support.h" 50 | 51 | #ifndef MAX_CODE_SIZE 52 | # define MAX_CODE_SIZE (4096*16) 53 | #endif 54 | 55 | #ifndef MAX_THREADS 56 | # define MAX_THREADS 64 57 | #endif 58 | 59 | static size_t num_threads = 0; 60 | 61 | static char *test_mem = NULL; 62 | static size_t test_mem_bytes = 0; 63 | static size_t test_mem_stride = 0; 64 | 65 | static pthread_barrier_t barrier; 66 | static pthread_t thread_main_id; 67 | 68 | static inline void 69 | reset_test_mem(void **used_addrs, size_t len) 70 | { 71 | #if !HOST_ZERO_TEST_MEM 72 | if (used_addrs != NULL) { 73 | // NULL marks end of list. 74 | for (size_t i = 0; i < len && used_addrs[i]; ++i) { 75 | // Mask stride due encoding (see mc2lib RandomFactory) 76 | memset(used_addrs[i], 0, (test_mem_stride & 0xffff)); 77 | } 78 | } else { 79 | memset(test_mem, 0, test_mem_bytes); 80 | } 81 | 82 | full_memory_barrier(); 83 | #endif 84 | 85 | if (used_addrs != NULL) { 86 | // NULL marks end of list. 87 | for (size_t i = 0; i < len && used_addrs[i]; ++i) { 88 | flush_cache_line(used_addrs[i]); 89 | } 90 | } else { 91 | #if defined(CACHELINE_SIZE) && CACHELINE_SIZE != 0 92 | // Fallback 93 | for (size_t i = 0; i < test_mem_bytes; i += CACHELINE_SIZE) { 94 | flush_cache_line(&test_mem[i]); 95 | } 96 | #endif 97 | } 98 | 99 | full_memory_barrier(); 100 | } 101 | 102 | static inline void 103 | barrier_wait_pthread(void) 104 | { 105 | int rc = pthread_barrier_wait(&barrier); 106 | assert(rc == 0 || rc == PTHREAD_BARRIER_SERIAL_THREAD); 107 | } 108 | 109 | void* 110 | thread_func(void *arg) 111 | { 112 | const size_t test_iterations = (size_t) arg; 113 | const pthread_t thread_self = pthread_self(); 114 | 115 | void *code = mmap(NULL, MAX_CODE_SIZE, 116 | PROT_READ | PROT_WRITE | PROT_EXEC, 117 | MAP_ANONYMOUS | MAP_PRIVATE, 118 | -1, 0); 119 | assert(code != NULL); 120 | memset(code, 0, MAX_CODE_SIZE); 121 | 122 | void (*thread_test)() = GET_CALLABLE_THREAD(code); 123 | 124 | void **used_addrs = NULL; 125 | #if MAX_USED_ADDRS_SIZE 126 | if (thread_self == thread_main_id) { 127 | used_addrs = (void**)malloc(MAX_USED_ADDRS_SIZE); 128 | assert(used_addrs != NULL); 129 | memset(used_addrs, 0, MAX_USED_ADDRS_SIZE); 130 | } 131 | #endif 132 | 133 | barrier_wait_pthread(); 134 | 135 | while (1) { 136 | host_make_test_thread(code, MAX_CODE_SIZE); 137 | full_memory_barrier(); 138 | 139 | for (size_t i = 0; i < test_iterations; ++i) { 140 | barrier_wait_precise(num_threads); 141 | 142 | full_memory_barrier(); 143 | 144 | thread_test(); 145 | 146 | full_memory_barrier(); 147 | 148 | barrier_wait_coarse(num_threads); 149 | 150 | if (i + 1 < test_iterations 151 | && thread_self == thread_main_id) { 152 | host_verify_reset_conflict(used_addrs, 153 | MAX_USED_ADDRS_SIZE); 154 | reset_test_mem(used_addrs, MAX_USED_ADDRS_SIZE); 155 | } 156 | } 157 | 158 | if (thread_self == thread_main_id) { 159 | host_verify_reset_all(used_addrs, MAX_USED_ADDRS_SIZE); 160 | reset_test_mem(used_addrs, MAX_USED_ADDRS_SIZE); 161 | } 162 | 163 | barrier_wait_coarse(num_threads); 164 | } 165 | 166 | #if MAX_USED_ADDRS_SIZE 167 | if (thread_self == thread_main_id) { 168 | free(used_addrs); 169 | } 170 | #endif 171 | 172 | munmap(code, MAX_CODE_SIZE); 173 | return NULL; 174 | } 175 | 176 | static void 177 | setaffinity_thread(size_t pid, pthread_t thread) 178 | { 179 | cpu_set_t cpuset; 180 | CPU_ZERO(&cpuset); 181 | CPU_SET(pid, &cpuset); 182 | int rc = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); 183 | assert(!rc); 184 | } 185 | 186 | static void 187 | setaffinity_attr(size_t pid, pthread_attr_t *attr) 188 | { 189 | cpu_set_t cpuset; 190 | CPU_ZERO(&cpuset); 191 | CPU_SET(pid, &cpuset); 192 | int rc = pthread_attr_setaffinity_np(attr, sizeof(cpu_set_t), &cpuset); 193 | assert(!rc); 194 | } 195 | 196 | static void 197 | spawn_threads(size_t test_iterations) 198 | { 199 | assert(MAX_THREADS <= CPU_SETSIZE); 200 | assert(num_threads <= MAX_THREADS); 201 | assert(num_threads > 0); 202 | 203 | // Reuse main thread. 204 | pthread_t thread_ids[MAX_THREADS - 1]; 205 | pthread_attr_t attr; 206 | 207 | int rc = pthread_barrier_init(&barrier, NULL, num_threads); 208 | assert(!rc); 209 | 210 | thread_main_id = pthread_self(); 211 | setaffinity_thread(0, thread_main_id); 212 | 213 | printf("Spawning threads ...\n"); 214 | 215 | for (size_t i = 0; i < num_threads - 1; ++i) { 216 | rc = pthread_attr_init(&attr); 217 | assert(!rc); 218 | 219 | setaffinity_attr(i + 1, &attr); 220 | 221 | pthread_attr_getinheritsched(&attr, &rc); 222 | assert(rc == PTHREAD_INHERIT_SCHED); 223 | 224 | rc = pthread_create(&thread_ids[i], &attr, thread_func, 225 | (void*) test_iterations); 226 | assert(!rc); 227 | 228 | rc = pthread_attr_destroy(&attr); 229 | assert(!rc); 230 | } 231 | 232 | roi_begin(); 233 | 234 | printf("Running tests ...\n"); 235 | thread_func((void*) test_iterations); 236 | 237 | for (size_t i = 0; i < num_threads - 1; ++i) { 238 | pthread_join(thread_ids[i], NULL); 239 | } 240 | 241 | printf("All tests complete.\n"); 242 | 243 | roi_end(); 244 | 245 | pthread_barrier_destroy(&barrier); 246 | } 247 | 248 | static void 249 | setup_sched(void) 250 | { 251 | const char *env_policy = getenv("MC2_SCHED_POLICY"); 252 | if (!env_policy) { 253 | // Do nothing. 254 | return; 255 | } 256 | 257 | // defaults 258 | int policy = SCHED_FIFO; 259 | struct sched_param sp = { 260 | .sched_priority = sched_get_priority_max(policy) - 20 261 | }; 262 | 263 | if (env_policy[0] != '\0') { 264 | if (!strcmp(env_policy, "SCHED_RR")) { 265 | policy = SCHED_RR; 266 | } else if (!strcmp(env_policy, "SCHED_FIFO")) { 267 | policy = SCHED_FIFO; 268 | } else { 269 | perror("Invalid MC2_SCHED_POLICY!"); 270 | exit(1); 271 | } 272 | } 273 | 274 | const char *env_prio = getenv("MC2_SCHED_PRIO"); 275 | if (env_prio) { 276 | sp.sched_priority = atoi(env_prio); 277 | assert(sp.sched_priority != 0); 278 | } 279 | 280 | if (sched_setscheduler(0, policy, &sp) == -1) { 281 | perror("sched_setscheduler failed!"); 282 | exit(1); 283 | } else { 284 | printf("Set RT scheduler: %d @ %d\n", policy, 285 | sp.sched_priority); 286 | } 287 | } 288 | 289 | static void 290 | usage(const char *progname) 291 | { 292 | printf("Usage: %s " 293 | " [ []]\n", 294 | progname); 295 | 296 | exit(42); 297 | } 298 | 299 | int 300 | main(int argc, char *argv[]) 301 | { 302 | if (argc <= 4) usage(argv[0]); 303 | 304 | host_init(); 305 | setup_sched(); 306 | 307 | // Get number of threads 308 | num_threads = strtoull(argv[1], NULL, 0); 309 | if (!num_threads) usage(argv[0]); 310 | printf("Threads: %zu\n", num_threads); 311 | 312 | // Get iterations per test 313 | size_t test_iterations = strtoull(argv[2], NULL, 0); 314 | if (!test_iterations) usage(argv[0]); 315 | printf("Test iterations: %zu\n", test_iterations); 316 | 317 | // Initialize test memory 318 | test_mem_bytes = strtoull(argv[3], NULL, 0); 319 | if (!test_mem_bytes) usage(argv[0]); 320 | 321 | test_mem_stride = strtoull(argv[4], NULL, 0); 322 | if (!test_mem_stride) usage(argv[0]); 323 | 324 | assert(test_mem == NULL); 325 | size_t synonym_count = 0; 326 | if (argc > 5) { 327 | // Optionally set test-memory base address. 328 | test_mem = (char*)strtoull(argv[5], NULL, 0); 329 | 330 | if (test_mem && argc > 6) { 331 | synonym_count = strtoull(argv[6], NULL, 0); 332 | } 333 | } 334 | 335 | int shm_id; 336 | if (synonym_count) { 337 | shm_id = shmget(IPC_PRIVATE, test_mem_bytes, 338 | IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); 339 | assert(shm_id != -1); 340 | test_mem = (char*)shmat(shm_id, test_mem, 0); 341 | 342 | for (size_t i = 0; i < synonym_count; ++i) { 343 | char* synonym_mem = (char*)shmat(shm_id, 344 | test_mem + (test_mem_bytes * (i+1)), 0); 345 | assert(synonym_mem != (char*)-1); 346 | printf("Test memory synonym @ 0x%tx\n", 347 | (ptrdiff_t)synonym_mem); 348 | } 349 | } else if (test_mem) { 350 | test_mem = (char*)mmap(test_mem, test_mem_bytes, 351 | PROT_READ | PROT_WRITE, 352 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 353 | 0, 0); 354 | assert(test_mem != MAP_FAILED); 355 | } else { 356 | test_mem = (char*)malloc(test_mem_bytes); 357 | assert(test_mem != NULL); 358 | } 359 | 360 | printf("Test memory: %zu bytes (stride=0x%zx) @ 0x%tx\n", 361 | test_mem_bytes, test_mem_stride, (ptrdiff_t)test_mem); 362 | 363 | // Let host know of memory range 364 | #if HOST_ZERO_TEST_MEM 365 | // Need to access memory once if host wants to access page-table 366 | // entries. 367 | # if defined(CACHELINE_SIZE) && CACHELINE_SIZE != 0 368 | for (size_t i = 0; i < test_mem_bytes; i += CACHELINE_SIZE) { 369 | test_mem[i] = 0x42; 370 | } 371 | # else 372 | memset(test_mem, 0, test_mem_bytes); 373 | # endif 374 | full_memory_barrier(); 375 | #endif 376 | 377 | host_mark_test_mem_range(test_mem, 378 | (test_mem + (test_mem_bytes * (synonym_count+1)) - 1), 379 | test_mem_stride, 380 | (synonym_count ? (void*)(test_mem_bytes - 1) : NULL)); 381 | 382 | reset_test_mem(NULL, 0); 383 | 384 | spawn_threads(test_iterations); 385 | 386 | // cleanup 387 | if (synonym_count) { 388 | shmdt(test_mem); 389 | for (size_t i = 0; i < synonym_count; ++i) { 390 | char* synonym_mem = test_mem + (test_mem_bytes * (i+1)); 391 | shmdt(synonym_mem); 392 | } 393 | shmctl(shm_id, IPC_RMID, 0); 394 | } else if (argc > 5 && strtoull(argv[5], NULL, 0)) { 395 | munmap(test_mem, test_mem_bytes); 396 | } else { 397 | free(test_mem); 398 | } 399 | 400 | return EXIT_SUCCESS; 401 | } 402 | 403 | -------------------------------------------------------------------------------- /contrib/mcversi/host_support.h: -------------------------------------------------------------------------------- 1 | m5_host_support.h -------------------------------------------------------------------------------- /contrib/mcversi/m5_host_support.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef HOST_SUPPORT_H_ 35 | #define HOST_SUPPORT_H_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "m5op.h" 45 | 46 | #ifndef CACHELINE_SIZE 47 | # define CACHELINE_SIZE 64 48 | #endif 49 | 50 | #ifndef HOST_ZERO_TEST_MEM 51 | // 0 -- Host does not zero test-memory. 52 | // 1 -- Host zeroes test-memory in *reset* and mark_test_mem_range functions. 53 | # define HOST_ZERO_TEST_MEM 1 54 | #endif 55 | 56 | #ifndef MAX_USED_ADDRS_SIZE 57 | // >0 -- Host must provide used addresses with *reset* functions. 58 | # define MAX_USED_ADDRS_SIZE (4096*8) 59 | #endif 60 | 61 | #ifndef BARRIER_USE_QUIESCE 62 | // This is a performance optimization and (with the evaluated version of Gem5 63 | // used for McVerSi) speeds up execution (when using barrier_wait_coarse) 64 | // significantly with larger number of processors. 65 | // 66 | // With latest Gem5 and current async barrier implementation, using quiesce 67 | // seems to cause lock up, regardless of ISA; disable by default. 68 | # define BARRIER_USE_QUIESCE 0 69 | #endif 70 | 71 | #ifdef M5OP_ADDR 72 | void *m5_mem = NULL; 73 | #endif 74 | 75 | #if defined(__x86_64__) 76 | 77 | inline void 78 | full_memory_barrier(void) 79 | { 80 | __asm__ __volatile__ ( 81 | "mfence\n\t" 82 | "mov $0, %%eax\n\t" 83 | "cpuid\n\t" ::: "memory", "cc", 84 | "rax", "rbx", "rcx", "rdx"); 85 | } 86 | 87 | inline void 88 | flush_cache_line(volatile void *addr) 89 | { 90 | // Note that, not all protocols support this; in the McVerSi paper, we 91 | // implemented support for clflush for the protocols we used. 92 | __asm__ __volatile__ ("clflush (%0)" :: "r" (addr) : "memory"); 93 | } 94 | 95 | inline uint64_t 96 | host_make_test_thread(void *code, uint64_t len) 97 | { 98 | len = m5_make_test_thread(code, len); 99 | for (int i = 0; i < 0x42; ++i) { 100 | // It seems that Gem5's O3 CPU still prefetches instructions somehow; 101 | // work-around by not giving it the chance to prefetch the previous 102 | // test. My assumption is, that if we had actual self-modifying code 103 | // (and not the host writing the instructions to memory), it should not 104 | // have this problem. 105 | full_memory_barrier(); 106 | } 107 | return len; 108 | } 109 | 110 | #define GET_CALLABLE_THREAD(code) ((void (*)()) code) 111 | 112 | #elif defined(__arm__) 113 | 114 | inline void 115 | full_memory_barrier(void) 116 | { 117 | __asm__ __volatile__ ( 118 | "dsb; isb" ::: 119 | "memory", "cc", 120 | // Clobber registers used by test generator. 121 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"); 122 | } 123 | 124 | inline void 125 | flush_cache_line(volatile void *addr) 126 | { 127 | // TODO: Implement me! 128 | } 129 | 130 | inline uint64_t 131 | host_make_test_thread(void *code, uint64_t len) 132 | { 133 | len = m5_make_test_thread(code, len); 134 | __clear_cache(code, code + len); 135 | return len; 136 | } 137 | 138 | // Thumb 139 | #define GET_CALLABLE_THREAD(code) ((void (*)()) ((ptrdiff_t)code | 1)) 140 | 141 | #else 142 | # error "Unsupported architecture!" 143 | #endif 144 | 145 | #define host_mark_test_mem_range m5_mark_test_mem_range 146 | 147 | inline void 148 | host_verify_reset_conflict(void **used_addrs, uint64_t len) 149 | { 150 | while(!m5_verify_reset_conflict(used_addrs, len)); 151 | } 152 | 153 | inline void 154 | host_verify_reset_all(void **used_addrs, uint64_t len) 155 | { 156 | while(!m5_verify_reset_all(used_addrs, len)); 157 | } 158 | 159 | inline void 160 | barrier_wait_precise(uint64_t nt) 161 | { 162 | full_memory_barrier(); 163 | 164 | #if BARRIER_USE_QUIESCE 165 | while(m5_barrier_async(nt, 1)); 166 | 167 | full_memory_barrier(); 168 | #endif 169 | 170 | while(m5_barrier_async(nt, 0)); 171 | 172 | full_memory_barrier(); 173 | } 174 | 175 | inline void 176 | barrier_wait_coarse(uint64_t nt) 177 | { 178 | #if BARRIER_USE_QUIESCE 179 | full_memory_barrier(); 180 | 181 | while(m5_barrier_async(nt, 1)); 182 | 183 | full_memory_barrier(); 184 | #else 185 | barrier_wait_precise(nt); 186 | #endif 187 | } 188 | 189 | #ifdef M5OP_ADDR 190 | inline void 191 | map_m5_mem(void) 192 | { 193 | int fd; 194 | 195 | fd = open("/dev/mem", O_RDWR | O_SYNC); 196 | if (fd == -1) { 197 | perror("Cannot open /dev/mem"); 198 | exit(1); 199 | } 200 | 201 | m5_mem = mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, M5OP_ADDR); 202 | if (!m5_mem) { 203 | perror("Cannot mmap /dev/mem"); 204 | exit(1); 205 | } 206 | } 207 | #endif 208 | 209 | inline void 210 | host_init(void) 211 | { 212 | #ifdef M5OP_ADDR 213 | map_m5_mem(); 214 | #endif 215 | } 216 | 217 | inline void 218 | roi_begin(void) 219 | { 220 | m5_dumpreset_stats(0, 0); 221 | } 222 | 223 | inline void 224 | roi_end(void) 225 | { 226 | m5_fail(0, 42); 227 | } 228 | 229 | #endif /* HOST_SUPPORT_H_ */ 230 | -------------------------------------------------------------------------------- /contrib/mcversi/run-10-8KB-1synonym.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This code is licensed under the BSD 3-Clause license. See the LICENSE file in 4 | # the project root for license terms. 5 | # 6 | # 8KB configuration with virtual address synonyms (2 virtual addresses per 1 7 | # physical). 8 | 9 | /mcversi/guest_workload $(nproc) 10 0x1000000 0x09140010 0x100000000 1 10 | -------------------------------------------------------------------------------- /contrib/mcversi/run-10-8KB.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This code is licensed under the BSD 3-Clause license. See the LICENSE file in 4 | # the project root for license terms. 5 | # 6 | # 8KB configuration from McVerSi paper. 7 | 8 | # Optionally set this to one of: 9 | # - SCHED_RR 10 | # - SCHED_FIFO 11 | export MC2_SCHED_POLICY= 12 | 13 | # Run with as many threads as reported by nproc; 10 iterations per test; 14 | # allocate 15729152 bytes of memory, with 15 | # 16 | # - stride = (0x09140010 & ((1 << 16) - 1)) = 16 bytes; 17 | # - chunks of size (1 << ((0x09140010 & (0xff << 24)) >> 24)) = 512 bytes; 18 | # - and valid chunk base addresses offset by multiples of 19 | # (1 << ((0x09140010 & (0xff << 16)) >> 16)) = 1048576 bytes from first base address. 20 | # 21 | # and first base address of memory is 0x100000000 (optionally set, but used 22 | # here to force 64bit address instructions by the code generator). This 23 | # effectively uses 8KB of memory distributed across 16MB. 24 | 25 | /mcversi/guest_workload $(nproc) 10 0xf00200 0x09140010 0x100000000 26 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/cats.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_CATS_HPP_ 35 | #define MC2LIB_CODEGEN_CATS_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "../memconsistency/cats.hpp" 45 | #include "compiler.hpp" 46 | 47 | namespace mc2lib { 48 | 49 | namespace codegen { 50 | 51 | // Workaround for Wtype-limits warning. 52 | template 53 | constexpr bool lt__(T1 a, T2 b) { 54 | return a < b; 55 | } 56 | 57 | /** 58 | * @brief Interface to memconsistency::cats data structures. 59 | * 60 | * This class serves as a helper to interface with data structures in 61 | * memconsistency::cats. Its primary function is the creation of new events by 62 | * Operations. 63 | * 64 | * Furthermore, as this is the interface to the consistency model descriptions 65 | * and checker, we can encode efficient support for virtual address synonyms 66 | * here by transforming the address used to construct events (see 67 | * EvtStateCats::set_addr_mask). As described in [1], if we are working at the 68 | * virtual address level, we are checking VAMC (Virtual Address 69 | * Memory Consistency). Here we only consider the problem of synonym sets of 70 | * virtual addresses mapping to the same physical address, and add simple 71 | * support for it. 72 | * 73 | * [1] 74 | * Bogdan F. Romanescu, Alvin R. Lebeck, Daniel J. Sorin, "Specifying and 75 | * dynamically verifying address translation-aware memory consistency", 76 | * 2010. 77 | */ 78 | class EvtStateCats { 79 | public: 80 | // 1 Op can at most emit 2 write Events 81 | static constexpr std::size_t kMaxOpSize = sizeof(types::WriteID) * 2; 82 | static constexpr std::size_t kMaxOpEvents = 83 | kMaxOpSize / sizeof(types::WriteID); 84 | 85 | static constexpr types::Poi kMinOther = static_cast(1) 86 | << (sizeof(types::Poi) * 8 - 1); 87 | static constexpr types::Poi kMaxOther = 88 | std::numeric_limits::max() - (kMaxOpEvents - 1); 89 | 90 | static constexpr types::WriteID kInitWrite = 91 | std::numeric_limits::min(); 92 | static constexpr types::WriteID kMinWrite = kInitWrite + 1; 93 | 94 | static constexpr types::WriteID kMaxWrite = 95 | (lt__(std::numeric_limits::max(), kMinOther) 96 | ? std::numeric_limits::max() 97 | : kMinOther - 1) - 98 | (kMaxOpEvents - 1); 99 | 100 | static_assert(kMinOther > kMaxWrite, "Invalid read/write ID limits!"); 101 | 102 | explicit EvtStateCats(mc::cats::ExecWitness *ew, mc::cats::Architecture *arch) 103 | : ew_(ew), arch_(arch), addr_mask_(~0) {} 104 | 105 | void Reset() { 106 | last_write_id_ = kMinWrite - 1; 107 | last_other_id = kMinOther - 1; 108 | 109 | writes_.clear(); 110 | ew_->Clear(); 111 | arch_->Clear(); 112 | } 113 | 114 | bool Exhausted() const { 115 | return last_write_id_ >= kMaxWrite || last_other_id >= kMaxOther; 116 | } 117 | 118 | template 119 | EventPtrs MakeEvent(types::Pid pid, mc::Event::Type type, 120 | std::size_t size, Func mkevt) { 121 | static_assert(max_size_bytes <= kMaxOpSize, "Invalid size!"); 122 | static_assert(sizeof(types::WriteID) <= max_size_bytes, "Invalid size!"); 123 | static_assert(max_size_bytes % sizeof(types::WriteID) == 0, 124 | "Invalid size!"); 125 | assert(size <= max_size_bytes); 126 | assert(sizeof(types::WriteID) <= size); 127 | assert(size % sizeof(types::WriteID) == 0); 128 | 129 | // Initialize to avoid uninitialized warning with some older compilers. 130 | EventPtrs result{{nullptr}}; 131 | 132 | for (std::size_t i = 0; i < size / sizeof(types::WriteID); ++i) { 133 | result[i] = mkevt(i * sizeof(types::WriteID)); 134 | } 135 | 136 | return result; 137 | } 138 | 139 | mc::Event MakeOther(types::Pid pid, mc::Event::Type type, types::Addr addr) { 140 | assert(!Exhausted()); 141 | addr &= addr_mask_; 142 | return mc::Event(type, addr, mc::Iiid(pid, ++last_other_id)); 143 | } 144 | 145 | template 146 | EventPtrs MakeRead(types::Pid pid, mc::Event::Type type, 147 | types::Addr addr, 148 | std::size_t size = max_size_bytes) { 149 | assert(!Exhausted()); 150 | addr &= addr_mask_; 151 | ++last_other_id; 152 | return MakeEvent(pid, type, size, [&](types::Addr offset) { 153 | const mc::Event event = 154 | mc::Event(type, addr + offset, mc::Iiid(pid, last_other_id)); 155 | 156 | return &ew_->events.Insert(event, true); 157 | }); 158 | } 159 | 160 | template 161 | EventPtrs MakeWrite(types::Pid pid, mc::Event::Type type, 162 | types::Addr addr, types::WriteID *data, 163 | std::size_t size = max_size_bytes) { 164 | assert(!Exhausted()); 165 | addr &= addr_mask_; 166 | ++last_write_id_; 167 | return MakeEvent(pid, type, size, [&](types::Addr offset) { 168 | const types::WriteID write_id = last_write_id_; 169 | 170 | const mc::Event event = 171 | mc::Event(type, addr + offset, mc::Iiid(pid, write_id)); 172 | 173 | *(data + offset) = write_id; 174 | return (writes_[write_id] = &ew_->events.Insert(event, true)); 175 | }); 176 | } 177 | 178 | template 179 | EventPtrs GetWrite(const EventPtrs &after, 180 | types::Addr addr, 181 | const types::WriteID *from_id, 182 | std::size_t size = max_size_bytes) { 183 | static_assert(max_size_bytes <= kMaxOpSize, "Invalid size!"); 184 | static_assert(sizeof(types::WriteID) <= max_size_bytes, "Invalid size!"); 185 | static_assert(max_size_bytes % sizeof(types::WriteID) == 0, 186 | "Invalid size!"); 187 | assert(size <= max_size_bytes); 188 | assert(sizeof(types::WriteID) <= size); 189 | assert(size % sizeof(types::WriteID) == 0); 190 | addr &= addr_mask_; 191 | 192 | EventPtrs result; 193 | result.fill(nullptr); // init 194 | 195 | for (std::size_t i = 0; i < size / sizeof(types::WriteID); ++i) { 196 | WriteID_EventPtr::const_iterator write; 197 | 198 | const bool valid = from_id[i] != kInitWrite && 199 | (write = writes_.find(from_id[i])) != writes_.end() && 200 | write->second->addr == addr && 201 | write->second->iiid != after[i]->iiid; 202 | if (valid) { 203 | result[i] = write->second; 204 | } else { 205 | if (from_id[i] != kInitWrite) { 206 | // While the checker works even if memory is not 0'ed out 207 | // completely, as the chances of reading a write-id from a 208 | // previous test that has already been used in this test is 209 | // low and doesn't necessarily cause a false positive, it is 210 | // recommended that memory is 0'ed out for every new test. 211 | // 212 | // This does also provides limited checking for single-copy 213 | // atomicity violations where sizeof(WriteID) > 1. 214 | 215 | std::ostringstream oss; 216 | oss << __func__ << ": Invalid write!" 217 | << " A=" << std::hex << addr << " S=" << size; 218 | 219 | if (write != writes_.end()) { 220 | oss << ((write->second->addr != addr) ? " (addr mismatch)" : "") 221 | << ((write->second->iiid == after[i]->iiid) ? " (same iiid)" 222 | : ""); 223 | } 224 | 225 | throw std::logic_error(oss.str()); 226 | } 227 | 228 | auto initial = mc::Event(mc::Event::kWrite, addr, mc::Iiid(-1, addr)); 229 | result[i] = &ew_->events.Insert(initial); 230 | } 231 | 232 | addr += sizeof(types::WriteID); 233 | } 234 | 235 | return result; 236 | } 237 | 238 | mc::cats::ExecWitness *ew() { return ew_; } 239 | 240 | const mc::cats::ExecWitness *ew() const { return ew_; } 241 | 242 | mc::cats::Architecture *arch() { return arch_; } 243 | 244 | const mc::cats::Architecture *arch() const { return arch_; } 245 | 246 | /** 247 | * When using virtual addresses, this can be used to mask test memory 248 | * addresses, s.t. synonyms map to the same address used by the checker. 249 | * Although we could modify the checker to permit sets of addresses, this 250 | * would be much more expensive in terms of storage and hash-map lookup by 251 | * the checker. Assumes that synonym range start addresses are multiples of 252 | * 2**n (the size of memory). 253 | */ 254 | void set_addr_mask(types::Addr val) { addr_mask_ = val; } 255 | 256 | types::Addr addr_mask() const { return addr_mask_; } 257 | 258 | private: 259 | typedef std::unordered_map 260 | WriteID_EventPtr; 261 | 262 | mc::cats::ExecWitness *ew_; 263 | mc::cats::Architecture *arch_; 264 | 265 | WriteID_EventPtr writes_; 266 | 267 | types::WriteID last_write_id_; 268 | types::Poi last_other_id; 269 | 270 | types::Addr addr_mask_; 271 | }; 272 | 273 | } // namespace codegen 274 | } // namespace mc2lib 275 | 276 | #endif /* MC2LIB_CODEGEN_CATS_HPP_ */ 277 | 278 | /* vim: set ts=2 sts=2 sw=2 et : */ 279 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/compiler.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_COMPILER_HPP_ 35 | #define MC2LIB_CODEGEN_COMPILER_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "../memconsistency/eventsets.hpp" 50 | #include "../types.hpp" 51 | 52 | namespace mc2lib { 53 | 54 | /** 55 | * @namespace mc2lib::codegen 56 | * @brief Code generation for memory consistency verification. 57 | */ 58 | namespace codegen { 59 | 60 | namespace mc = memconsistency; 61 | 62 | template 63 | using EventPtrs = 64 | std::array; 65 | 66 | template 67 | inline auto MakeEventPtrs(const mc::Event *e1, Ts... en) 68 | -> EventPtrs<(1 + sizeof...(Ts)) * sizeof(types::WriteID)> { 69 | EventPtrs<(1 + sizeof...(Ts)) * sizeof(types::WriteID)> es{{e1, en...}}; 70 | return es; 71 | } 72 | 73 | /** 74 | * Baseclass for Operation implementations. 75 | */ 76 | template 77 | class Op { 78 | public: 79 | typedef EvtStateT EvtState; 80 | 81 | // Types 82 | typedef std::shared_ptr Ptr; 83 | typedef std::vector Thread; 84 | typedef typename Thread::const_iterator ThreadIt; 85 | typedef std::unordered_map Threads; 86 | typedef std::vector> ThreadItStack; 87 | 88 | // Read-only thread types: new Ops can access previous Ops. 89 | typedef std::vector ThreadConst; 90 | typedef typename ThreadConst::const_iterator ThreadConstIt; 91 | 92 | // Callback type: optionally, previous Ops get called back with new Ops. 93 | // E.g. for lazily constructing control flow graph with random branches. 94 | typedef std::function 96 | Callback; 97 | typedef std::vector CallbackStack; 98 | 99 | template 100 | friend Threads ExtractThreads(T *container) { 101 | Threads result; 102 | std::unordered_set used; 103 | 104 | for (auto &op : (*container)) { 105 | assert(op != nullptr); 106 | 107 | if (used.insert(op.get()).second) { 108 | // Using same instance of Op multiple times is not permitted. 109 | op = op->Clone(); 110 | } 111 | 112 | result[op->pid()].emplace_back(op); 113 | } 114 | 115 | return result; 116 | } 117 | 118 | friend std::size_t threads_size(const Threads &threads) { 119 | std::size_t result = 0; 120 | 121 | for (const auto &thread : threads) { 122 | result += thread.second.size(); 123 | } 124 | 125 | return result; 126 | } 127 | 128 | public: 129 | explicit Op(types::Pid pid) : pid_(pid) {} 130 | 131 | virtual ~Op() {} 132 | 133 | virtual void AdvanceThread(ThreadItStack *it_stack) const { 134 | ++(it_stack->back().first); 135 | } 136 | 137 | /** 138 | * Clone the instance. 139 | */ 140 | virtual Ptr Clone() const = 0; 141 | 142 | /** 143 | * Provide Reset, as emit functions may modify the state of an Op to store 144 | * information to map instructions to events. 145 | */ 146 | virtual void Reset() = 0; 147 | 148 | /** 149 | * Prepares the operation for emit; common emit code. 150 | * 151 | * @param[in,out] evts Pointer to EvtState instance of calling Compiler. 152 | * 153 | * @return true if can emit; false otherwise. 154 | */ 155 | virtual bool EnableEmit(EvtState *evts) = 0; 156 | 157 | /** 158 | * Generate static program-order relation. 159 | * 160 | * @param before Pointer to last Op; nullptr if none exists. 161 | * @param[in,out] evts Pointer to EvtState instance maintained by 162 | * Compiler. 163 | */ 164 | virtual void InsertPo(ThreadConstIt before, EvtState *evts) = 0; 165 | 166 | /** 167 | * Optionally register callback. 168 | * 169 | * @param[out] callback_stack Pointer to callback_stack with which to 170 | * register the callback. 171 | */ 172 | virtual void RegisterCallback(CallbackStack *callback_stack) {} 173 | 174 | /** 175 | * Emit machine code. 176 | * 177 | * @param start Instruction pointer to first instruction when executing. 178 | * @param[in,out] backend Architecture backend. 179 | * @param[in,out] evts Pointer to EvtState instance of calling Compiler. 180 | * @param[out] code Pointer to memory to be copied into. 181 | * @param len Maximum lenth of code. 182 | * 183 | * @return Size of emitted code. 184 | */ 185 | virtual std::size_t Emit(types::InstPtr start, Backend *backend, 186 | EvtState *evts, void *code, std::size_t len) = 0; 187 | 188 | /** 189 | * Accessor for last event generated. Also used to insert additional 190 | * ordering based on passed next_event (e.g. fences). 191 | * 192 | * @param next_event Event after last in program order; nullptr if none 193 | * exists. 194 | * @param[in,out] evts Pointer to EvtState instance maintained by 195 | * Compiler. 196 | * 197 | * @return Last event in program-order; nullptr if none exists. 198 | */ 199 | virtual const mc::Event *LastEvent(const mc::Event *next_event, 200 | EvtState *evts) const = 0; 201 | 202 | /** 203 | * Accessor for first event generated. 204 | * 205 | * @param prev_event Event before first in program order; nullptr if none 206 | * exists. 207 | * @param[in,out] evts Pointer to EvtState instance maintained by 208 | * Compiler. 209 | * 210 | * @return First event in program-order; nullptr if none exists. 211 | */ 212 | virtual const mc::Event *FirstEvent(const mc::Event *prev_event, 213 | EvtState *evts) const = 0; 214 | 215 | /** 216 | * Updates dynamic observation for instruction's memory operation. 217 | * 218 | * @param ip Instruction pointer of instruction for which a value was 219 | * observed. 220 | * @param part Which part of an instruction; e.g., if an instruction 221 | * generates multiple memory events, part can be used to denote 222 | * which. 223 | * @param addr Address for observed operation. 224 | * @param from_id Pointer to observed memory (WriteIDs). 225 | * @param size Total size of observed memory operations in from_id; 226 | * implementation should assert expected size. 227 | * @param[in,out] evts Pointer to EvtState instance maintained by 228 | * Compiler. 229 | * 230 | * @return Success or not. 231 | */ 232 | virtual bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 233 | const types::WriteID *from_id, std::size_t size, 234 | EvtState *evts) = 0; 235 | 236 | types::Pid pid() const { return pid_; } 237 | 238 | void set_pid(types::Pid pid) { pid_ = pid; } 239 | 240 | private: 241 | types::Pid pid_; 242 | }; 243 | 244 | template 245 | class MemOp : public Op { 246 | public: 247 | explicit MemOp(types::Pid pid) : Op(pid) {} 248 | 249 | virtual types::Addr addr() const = 0; 250 | }; 251 | 252 | template 253 | class NullOp : public Op { 254 | public: 255 | explicit NullOp(types::Pid pid) : Op(pid) {} 256 | 257 | bool EnableEmit(EvtState *evts) override { return false; } 258 | 259 | void InsertPo(typename Op::ThreadConstIt before, 260 | EvtState *evts) override { 261 | throw std::logic_error("Not supported"); 262 | } 263 | 264 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtState *evts, 265 | void *code, std::size_t len) override { 266 | throw std::logic_error("Not supported"); 267 | return 0; 268 | } 269 | 270 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 271 | const types::WriteID *from_id, std::size_t size, 272 | EvtState *evts) override { 273 | throw std::logic_error("Not supported"); 274 | return false; 275 | } 276 | 277 | const mc::Event *LastEvent(const mc::Event *next_event, 278 | EvtState *evts) const override { 279 | throw std::logic_error("Not supported"); 280 | return nullptr; 281 | } 282 | 283 | const mc::Event *FirstEvent(const mc::Event *prev_event, 284 | EvtState *evts) const override { 285 | throw std::logic_error("Not supported"); 286 | return nullptr; 287 | } 288 | }; 289 | 290 | /** 291 | * @brief Top level class used to manage code generation (compiler). 292 | */ 293 | template 294 | class Compiler { 295 | public: 296 | typedef typename Operation::EvtState EvtState; 297 | typedef typename Operation::Threads Threads; 298 | typedef typename Operation::ThreadIt ThreadIt; 299 | typedef typename Operation::ThreadItStack ThreadItStack; 300 | typedef typename Operation::ThreadConst ThreadConst; 301 | typedef typename Operation::Callback Callback; 302 | typedef typename Operation::CallbackStack CallbackStack; 303 | 304 | /** 305 | * Implies a reset of state in evts. 306 | * 307 | * @param evts Pointer to instance of EvtState as required by Operation. The 308 | * choice of unique_ptr here is deliberate, in that the class 309 | * that Operation requires may be a virtual base class, and 310 | * Compiler takes ownership. 311 | */ 312 | explicit Compiler(std::unique_ptr evts) : evts_(std::move(evts)) { 313 | Reset(); 314 | } 315 | 316 | /** 317 | * Implies a reset of state in evts and threads (the Ops contained). 318 | * 319 | * @param evts Pointer to instance of EvtState as required by Operation. The 320 | * choice of unique_ptr here is deliberate, in that the class 321 | * that Operation requires may be a virtual base class. 322 | * Takes ownership. 323 | * @param[in,out] threads Threads container. The Ops pointed to by Threads may 324 | * be modified. 325 | */ 326 | explicit Compiler(std::unique_ptr evts, Threads &&threads) 327 | : evts_(std::move(evts)) { 328 | Reset(std::move(threads)); 329 | } 330 | 331 | void Reset() { 332 | evts_->Reset(); 333 | backend_.Reset(); 334 | ip_to_op_.clear(); 335 | } 336 | 337 | void Reset(Threads &&threads) { 338 | threads_ = std::move(threads); 339 | 340 | // Must ensure all Operation instances have been reset. 341 | for (const auto &thread : threads_) { 342 | for (const auto &op : thread.second) { 343 | op->Reset(); 344 | } 345 | } 346 | 347 | Reset(); 348 | } 349 | 350 | const Threads &threads() { return threads_; } 351 | 352 | const EvtState *evts() const { return evts_.get(); } 353 | 354 | EvtState *evts() { return evts_.get(); } 355 | 356 | std::size_t Emit(types::InstPtr base, Operation *op, void *code, 357 | std::size_t len, ThreadConst *thread_const_ops, 358 | CallbackStack *callback_stack) { 359 | // Prepare op for emit. 360 | if (!op->EnableEmit(evts_.get())) { 361 | return 0; 362 | } 363 | 364 | // Generate program-order. 365 | if (thread_const_ops != nullptr) { 366 | assert(!thread_const_ops->empty()); 367 | op->InsertPo(--thread_const_ops->end(), evts_.get()); 368 | thread_const_ops->push_back(op); 369 | } else { 370 | ThreadConst invalid{nullptr}; 371 | op->InsertPo(invalid.begin(), evts_.get()); 372 | } 373 | 374 | std::size_t ctrl_len = 0; 375 | 376 | if (callback_stack != nullptr) { 377 | // Call all registered callbacks 378 | for (auto &callback : (*callback_stack)) { 379 | // Pass in current base, e.g. to allow late resolving of branch 380 | // targets; allows inserting code for flow control. 381 | const std::size_t s = 382 | callback(op, base, &backend_, evts_.get(), code, len); 383 | 384 | assert(s < len); 385 | base += s; 386 | code = static_cast(code) + s; 387 | len -= s; 388 | ctrl_len += s; 389 | } 390 | 391 | // Register callback 392 | op->RegisterCallback(callback_stack); 393 | } 394 | 395 | // Must be called *after* InsertPo and callback! 396 | const std::size_t op_len = 397 | op->Emit(base, &backend_, evts_.get(), code, len); 398 | assert(op_len != 0); 399 | 400 | // Base IP must be unique! 401 | assert(IpToOp(base) == nullptr); 402 | // Insert IP to Operation mapping. 403 | ip_to_op_[base] = std::make_pair(base + op_len, op); 404 | 405 | return op_len + ctrl_len; 406 | } 407 | 408 | std::size_t Emit(types::Pid pid, types::InstPtr base, void *code, 409 | std::size_t len) { 410 | auto thread = threads_.find(pid); 411 | 412 | if (thread == threads_.end()) { 413 | return 0; 414 | } 415 | 416 | std::size_t emit_len = 0; 417 | 418 | // Maintain const sequence of *emitted* ops; nullptr denotes beginning 419 | // of sequence (in absence of thread_const_ops.begin()). 420 | // 421 | // This will be a flattened sequence of ops (evaluated recursive ops). 422 | ThreadConst thread_const_ops{nullptr}; 423 | thread_const_ops.reserve(thread->second.size() + 1); 424 | 425 | // Callback function list 426 | CallbackStack callback_stack; 427 | 428 | // Enable recursive, nested sequences. 429 | ThreadItStack it_stack; 430 | it_stack.emplace_back(thread->second.begin(), thread->second.end()); 431 | 432 | while (!it_stack.empty()) { 433 | auto &it = it_stack.back().first; 434 | auto &end = it_stack.back().second; 435 | 436 | if (it == end) { 437 | it_stack.pop_back(); 438 | continue; 439 | } 440 | 441 | const auto &op = *it; 442 | 443 | // Generate code and architecture-specific ordering relations. 444 | const std::size_t op_len = 445 | Emit(base + emit_len, op.get(), code, len - emit_len, 446 | &thread_const_ops, &callback_stack); 447 | 448 | emit_len += op_len; 449 | assert(emit_len <= len); 450 | code = static_cast(code) + op_len; 451 | 452 | op->AdvanceThread(&it_stack); 453 | } 454 | 455 | // Notify ops of completion 456 | for (auto &callback : callback_stack) { 457 | const std::size_t s = callback(nullptr, base + emit_len, &backend_, 458 | evts_.get(), code, len - emit_len); 459 | 460 | emit_len += s; 461 | assert(emit_len <= len); 462 | code = static_cast(code) + s; 463 | } 464 | 465 | return emit_len; 466 | } 467 | 468 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 469 | const types::WriteID *from_id, std::size_t size) { 470 | auto op = IpToOp(ip); 471 | 472 | if (op == nullptr) { 473 | return false; 474 | } 475 | 476 | return op->UpdateObs(ip, part, addr, from_id, size, evts_.get()); 477 | } 478 | 479 | Operation *IpToOp(types::InstPtr ip) const { 480 | if (ip_to_op_.empty()) { 481 | // Can be legally empty if no code has yet been emitted, i.e. right 482 | // after host system startup. By not faulting here, the host can 483 | // still use ip_to_op to check if an instruction needs to be 484 | // treated specially: before any code has been emitted, no 485 | // instructions will be treated specially. 486 | return nullptr; 487 | } 488 | 489 | auto e = ip_to_op_.upper_bound(ip); 490 | if (e != ip_to_op_.begin()) { 491 | e--; 492 | } 493 | 494 | if (!(e->first <= ip && ip < e->second.first)) { 495 | return nullptr; 496 | } 497 | 498 | return e->second.second; 499 | } 500 | 501 | private: 502 | typedef std::map> 503 | InstPtr_Op; 504 | 505 | std::unique_ptr evts_; 506 | Backend backend_; 507 | Threads threads_; 508 | 509 | // Each processor executes unique code, hence IP must be unique. Only stores 510 | // the start IP of Op's code. 511 | InstPtr_Op ip_to_op_; 512 | }; 513 | 514 | } // namespace codegen 515 | } // namespace mc2lib 516 | 517 | #endif /* MC2LIB_CODEGEN_COMPILER_HPP_ */ 518 | 519 | /* vim: set ts=2 sts=2 sw=2 et : */ 520 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/ops/armv7.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_OPS_ARMv7_HPP_ 35 | #define MC2LIB_CODEGEN_OPS_ARMv7_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "../cats.hpp" 42 | #include "../compiler.hpp" 43 | 44 | namespace mc2lib { 45 | namespace codegen { 46 | 47 | /** 48 | * @namespace mc2lib::codegen::armv7 49 | * @brief Implementations of Operations for ARMv7 (incomplete). 50 | * 51 | * The current operations do not exercise all aspects of the MCM. 52 | */ 53 | namespace armv7 { 54 | 55 | #define ASM_PRELUDE char *cnext__ = static_cast(code); 56 | #define ASM_LEN (static_cast(cnext__ - static_cast(code))) 57 | #define ASM_AT (start + ASM_LEN) 58 | 59 | #define ASM16(v) \ 60 | do { \ 61 | assert(ASM_LEN + 2 <= len); \ 62 | *reinterpret_cast(cnext__) = (v); \ 63 | cnext__ += 2; \ 64 | } while (0) 65 | 66 | #define ASM_PROLOGUE return ASM_LEN; 67 | 68 | // Thumb 69 | class Backend { 70 | public: 71 | void Reset() {} 72 | 73 | // Currently supports single byte operations only; to test for single-copy 74 | // atomicity, implement multi-byte operations support. 75 | static_assert(sizeof(types::WriteID) == 1, "Unsupported read/write size!"); 76 | 77 | enum Reg { 78 | r0 = 0, 79 | r1, 80 | r2, 81 | r3, 82 | r4, 83 | 84 | // reserved 85 | r5__, 86 | r6__, 87 | r7__ 88 | }; 89 | 90 | std::size_t Return(void *code, std::size_t len) const { 91 | ASM_PRELUDE; 92 | ASM16(0x4770); // bx lr 93 | ASM_PROLOGUE; 94 | } 95 | 96 | std::size_t Delay(std::size_t length, void *code, std::size_t len) const { 97 | ASM_PRELUDE; 98 | for (std::size_t i = 0; i < length; ++i) { 99 | ASM16(0xbf00); // nop 100 | } 101 | ASM_PROLOGUE; 102 | } 103 | 104 | std::size_t DMB_ST(void *code, std::size_t len) const { 105 | ASM_PRELUDE; 106 | // dmb st 107 | ASM16(0xf3bf); 108 | ASM16(0x8f5e); 109 | ASM_PROLOGUE; 110 | } 111 | 112 | std::size_t Read(types::Addr addr, Reg out, types::InstPtr start, void *code, 113 | std::size_t len, types::InstPtr *at) const { 114 | ASM_PRELUDE; 115 | 116 | Helper h(cnext__, code, len); 117 | h.MovImm32(r6__, addr); 118 | 119 | // ldrb out, [r6, #0] 120 | *at = ASM_AT; 121 | ASM16(0x7830 | out); 122 | 123 | ASM_PROLOGUE; 124 | } 125 | 126 | std::size_t ReadAddrDp(types::Addr addr, Reg out, Reg dp, 127 | types::InstPtr start, void *code, std::size_t len, 128 | types::InstPtr *at) const { 129 | ASM_PRELUDE; 130 | 131 | Helper h(cnext__, code, len); 132 | h.MovImm32(r6__, addr); 133 | 134 | // eor dp, dp 135 | ASM16(0x4040 | (dp << 3) | dp); 136 | 137 | // ldrb out, [r6, dp] 138 | *at = ASM_AT; 139 | ASM16(0x5c30 | (dp << 6) | out); 140 | 141 | ASM_PROLOGUE; 142 | } 143 | 144 | std::size_t Write(types::Addr addr, types::WriteID write_id, 145 | types::InstPtr start, void *code, std::size_t len, 146 | types::InstPtr *at) const { 147 | ASM_PRELUDE; 148 | 149 | Helper h(cnext__, code, len); 150 | h.MovImm32(r6__, addr); 151 | 152 | // movs r7, #write_id 153 | ASM16(0x2700 | write_id); 154 | 155 | // strb r7, [r6, #0] 156 | *at = ASM_AT; 157 | ASM16(0x7037); 158 | 159 | ASM_PROLOGUE; 160 | } 161 | 162 | protected: 163 | class Helper { 164 | public: 165 | Helper(char *&cnext, void *&code, std::size_t len) 166 | : cnext__(cnext), code(code), len(len) {} 167 | 168 | void MovImm32(Reg reg, std::uint32_t imm32) { 169 | // movw reg, #(imm32 & 0xffff) 170 | std::uint16_t imm32_w = imm32 & 0xffff; 171 | ASM16(0xf240 172 | // [10:10] 173 | | ((imm32_w & 0x0800) >> 1) 174 | // [3:0] 175 | | ((imm32_w & 0xf000) >> 12)); 176 | ASM16( // [14:12] 177 | ((imm32_w & 0x0700) << 4) 178 | // [11:8] 179 | | (reg << 8) 180 | // [7:0] 181 | | (imm32_w & 0x00ff)); 182 | 183 | // movt reg, #((imm32 & 0xffff0000) >> 16) 184 | std::uint16_t imm32_t = (imm32 & 0xffff0000) >> 16; 185 | ASM16(0xf2c0 186 | // [10:10] 187 | | ((imm32_t & 0x0800) >> 1) 188 | // [3:0] 189 | | ((imm32_t & 0xf000) >> 12)); 190 | ASM16( // [14:12] 191 | ((imm32_t & 0x0700) << 4) 192 | // [11:8] 193 | | (reg << 8) 194 | // [7:0] 195 | | (imm32_t & 0x00ff)); 196 | } 197 | 198 | protected: 199 | char *&cnext__; 200 | void *&code; 201 | const std::size_t len; 202 | }; 203 | }; 204 | 205 | #undef ASM_PRELUDE 206 | #undef ASM_LEN 207 | #undef ASM_AT 208 | #undef ASM16 209 | #undef ASM_PROLOGUE 210 | 211 | typedef Op Operation; 212 | typedef MemOp MemOperation; 213 | typedef NullOp NullOperation; 214 | 215 | class Return : public Operation { 216 | public: 217 | explicit Return(types::Pid pid = -1) : Operation(pid) {} 218 | 219 | Operation::Ptr Clone() const override { 220 | return std::make_shared(*this); 221 | } 222 | 223 | void Reset() override {} 224 | 225 | bool EnableEmit(EvtStateCats *evts) override { return true; } 226 | 227 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override {} 228 | 229 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 230 | void *code, std::size_t len) override { 231 | return backend->Return(code, len); 232 | } 233 | 234 | const mc::Event *LastEvent(const mc::Event *next_event, 235 | EvtStateCats *evts) const override { 236 | return nullptr; 237 | } 238 | 239 | const mc::Event *FirstEvent(const mc::Event *prev_event, 240 | EvtStateCats *evts) const override { 241 | return nullptr; 242 | } 243 | 244 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 245 | const types::WriteID *from_id, std::size_t size, 246 | EvtStateCats *evts) override { 247 | return true; 248 | } 249 | }; 250 | 251 | class Delay : public Operation { 252 | public: 253 | explicit Delay(std::size_t length, types::Pid pid = -1) 254 | : Operation(pid), length_(length), before_(nullptr) {} 255 | 256 | Operation::Ptr Clone() const override { 257 | return std::make_shared(*this); 258 | } 259 | 260 | void Reset() override { before_ = nullptr; } 261 | 262 | bool EnableEmit(EvtStateCats *evts) override { return true; } 263 | 264 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 265 | before_ = *before; 266 | } 267 | 268 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 269 | void *code, std::size_t len) override { 270 | return backend->Delay(length_, code, len); 271 | } 272 | 273 | const mc::Event *LastEvent(const mc::Event *next_event, 274 | EvtStateCats *evts) const override { 275 | // Forward 276 | if (before_ != nullptr) { 277 | return before_->LastEvent(next_event, evts); 278 | } 279 | 280 | return nullptr; 281 | } 282 | 283 | const mc::Event *FirstEvent(const mc::Event *prev_event, 284 | EvtStateCats *evts) const override { 285 | // Forward 286 | if (before_ != nullptr) { 287 | return before_->FirstEvent(prev_event, evts); 288 | } 289 | 290 | return nullptr; 291 | } 292 | 293 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 294 | const types::WriteID *from_id, std::size_t size, 295 | EvtStateCats *evts) override { 296 | throw std::logic_error("Unexpected UpdateObs"); 297 | return false; 298 | } 299 | 300 | protected: 301 | std::size_t length_; 302 | const Operation *before_; 303 | }; 304 | 305 | class Read : public MemOperation { 306 | public: 307 | explicit Read(types::Addr addr, Backend::Reg out, types::Pid pid = -1) 308 | : MemOperation(pid), 309 | addr_(addr), 310 | out_(out), 311 | event_(nullptr), 312 | from_(nullptr) {} 313 | 314 | Operation::Ptr Clone() const override { 315 | return std::make_shared(*this); 316 | } 317 | 318 | void Reset() override { 319 | event_ = nullptr; 320 | from_ = nullptr; 321 | } 322 | 323 | bool EnableEmit(EvtStateCats *evts) override { return !evts->Exhausted(); } 324 | 325 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 326 | event_ = evts->MakeRead(pid(), mc::Event::kRead, addr_)[0]; 327 | 328 | if (*before != nullptr) { 329 | auto event_before = (*before)->LastEvent(event_, evts); 330 | if (event_before != nullptr) { 331 | evts->ew()->po.Insert(*event_before, *event_); 332 | } 333 | } 334 | } 335 | 336 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 337 | void *code, std::size_t len) override { 338 | return backend->Read(addr_, out(), start, code, len, &at_); 339 | } 340 | 341 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 342 | const types::WriteID *from_id, std::size_t size, 343 | EvtStateCats *evts) override { 344 | assert(event_ != nullptr); 345 | assert(ip == at_); 346 | assert(addr == addr_); 347 | assert(size == sizeof(types::WriteID)); 348 | 349 | const mc::Event *from = 350 | evts->GetWrite(MakeEventPtrs(event_), addr_, from_id)[0]; 351 | 352 | if (from_ != nullptr) { 353 | // If from_ == from, we still need to continue to try to erase and 354 | // insert, in case the from-relation has been cleared. 355 | 356 | evts->ew()->rf.Erase(*from_, *event_); 357 | } 358 | 359 | from_ = from; 360 | evts->ew()->rf.Insert(*from_, *event_, true); 361 | 362 | return true; 363 | } 364 | 365 | const mc::Event *LastEvent(const mc::Event *next_event, 366 | EvtStateCats *evts) const override { 367 | return event_; 368 | } 369 | 370 | const mc::Event *FirstEvent(const mc::Event *prev_event, 371 | EvtStateCats *evts) const override { 372 | return event_; 373 | } 374 | 375 | types::Addr addr() const override { return addr_; } 376 | 377 | Backend::Reg out() const { return out_; } 378 | 379 | protected: 380 | types::Addr addr_; 381 | Backend::Reg out_; 382 | const mc::Event *event_; 383 | const mc::Event *from_; 384 | types::InstPtr at_; 385 | }; 386 | 387 | class ReadAddrDp : public Read { 388 | public: 389 | explicit ReadAddrDp(types::Addr addr, Backend::Reg reg, Backend::Reg dp, 390 | types::Pid pid = -1) 391 | : Read(addr, reg, pid), dp_(dp) {} 392 | 393 | Operation::Ptr Clone() const override { 394 | return std::make_shared(*this); 395 | } 396 | 397 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 398 | event_ = evts->MakeRead(pid(), mc::Event::kRead | mc::Event::kRegInAddr, 399 | addr_)[0]; 400 | 401 | if (*before != nullptr) { 402 | auto event_before = (*before)->LastEvent(event_, evts); 403 | if (event_before != nullptr) { 404 | evts->ew()->po.Insert(*event_before, *event_); 405 | 406 | // Find read dependency. 407 | auto arch = dynamic_cast(evts->arch()); 408 | if (arch != nullptr) { 409 | do { 410 | auto potential_dp_read = dynamic_cast(*before); 411 | if (potential_dp_read != nullptr) { 412 | if (potential_dp_read->out() == dp_) { 413 | auto event_dp = potential_dp_read->LastEvent(event_, evts); 414 | arch->dd_reg.Insert(*event_dp, *event_); 415 | break; 416 | } 417 | } 418 | } while (*(--before) != nullptr); 419 | } 420 | } 421 | } 422 | } 423 | 424 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 425 | void *code, std::size_t len) override { 426 | return backend->ReadAddrDp(addr_, out(), dp_, start, code, len, &at_); 427 | } 428 | 429 | protected: 430 | Backend::Reg dp_; 431 | }; 432 | 433 | class Write : public MemOperation { 434 | public: 435 | explicit Write(types::Addr addr, types::Pid pid = -1) 436 | : MemOperation(pid), addr_(addr), write_id_(0) {} 437 | 438 | Operation::Ptr Clone() const override { 439 | return std::make_shared(*this); 440 | } 441 | 442 | void Reset() override { 443 | event_ = nullptr; 444 | from_ = nullptr; 445 | write_id_ = 0; 446 | } 447 | 448 | bool EnableEmit(EvtStateCats *evts) override { return !evts->Exhausted(); } 449 | 450 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 451 | event_ = evts->MakeWrite(pid(), mc::Event::kWrite, addr_, &write_id_)[0]; 452 | 453 | if (*before != nullptr) { 454 | auto event_before = (*before)->LastEvent(event_, evts); 455 | if (event_before != nullptr) { 456 | evts->ew()->po.Insert(*event_before, *event_); 457 | } 458 | } 459 | } 460 | 461 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 462 | void *code, std::size_t len) override { 463 | return backend->Write(addr_, write_id_, start, code, len, &at_); 464 | } 465 | 466 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 467 | const types::WriteID *from_id, std::size_t size, 468 | EvtStateCats *evts) override { 469 | assert(event_ != nullptr); 470 | assert(ip == at_); 471 | assert(addr == addr_); 472 | assert(size == sizeof(types::WriteID)); 473 | 474 | const mc::Event *from = 475 | evts->GetWrite(MakeEventPtrs(event_), addr_, from_id)[0]; 476 | 477 | if (from_ != nullptr) { 478 | // If from_ == from, we still need to continue to try to erase and 479 | // insert, in case the from-relation has been cleared. 480 | 481 | evts->ew()->co.Erase(*from_, *event_); 482 | } 483 | 484 | from_ = from; 485 | evts->ew()->co.Insert(*from_, *event_, true); 486 | 487 | return true; 488 | } 489 | 490 | const mc::Event *LastEvent(const mc::Event *next_event, 491 | EvtStateCats *evts) const override { 492 | return event_; 493 | } 494 | 495 | const mc::Event *FirstEvent(const mc::Event *prev_event, 496 | EvtStateCats *evts) const override { 497 | return event_; 498 | } 499 | 500 | types::Addr addr() const override { return addr_; } 501 | 502 | protected: 503 | types::Addr addr_; 504 | types::WriteID write_id_; 505 | const mc::Event *event_; 506 | const mc::Event *from_; 507 | types::InstPtr at_; 508 | }; 509 | 510 | class DMB_ST : public Operation { 511 | public: 512 | explicit DMB_ST(types::Pid pid = -1) 513 | : Operation(pid), before_(nullptr), first_write_before_(nullptr) {} 514 | 515 | Operation::Ptr Clone() const override { 516 | return std::make_shared(*this); 517 | } 518 | 519 | void Reset() override { 520 | before_ = nullptr; 521 | first_write_before_ = nullptr; 522 | } 523 | 524 | bool EnableEmit(EvtStateCats *evts) override { return true; } 525 | 526 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 527 | before_ = *before; 528 | 529 | while (*before != nullptr) { 530 | auto potential_write = dynamic_cast(*before); 531 | if (potential_write != nullptr) { 532 | first_write_before_ = potential_write; 533 | break; 534 | } 535 | --before; 536 | } 537 | } 538 | 539 | void RegisterCallback(Operation::CallbackStack *callback_stack) override { 540 | callback_stack->push_back([this](Operation *after, types::InstPtr start, 541 | Backend *backend, EvtStateCats *evts, 542 | void *code, std::size_t len) { 543 | if (first_write_before_ != nullptr) { 544 | auto potential_write = dynamic_cast(after); 545 | if (potential_write != nullptr) { 546 | auto arch = dynamic_cast(evts->arch()); 547 | if (arch != nullptr) { 548 | auto event_before = first_write_before_->LastEvent(nullptr, evts); 549 | auto event_after = potential_write->FirstEvent(nullptr, evts); 550 | assert(event_before != nullptr); 551 | assert(event_after != nullptr); 552 | 553 | arch->dmb_st.Insert(*event_before, *event_after); 554 | // cats::ARMv7 takes care of transitivity. 555 | } 556 | first_write_before_ = nullptr; 557 | } 558 | } 559 | return 0; 560 | }); 561 | } 562 | 563 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 564 | void *code, std::size_t len) override { 565 | return backend->DMB_ST(code, len); 566 | } 567 | 568 | const mc::Event *LastEvent(const mc::Event *next_event, 569 | EvtStateCats *evts) const override { 570 | // Forward 571 | if (before_ != nullptr) { 572 | return before_->LastEvent(next_event, evts); 573 | } 574 | 575 | return nullptr; 576 | } 577 | 578 | const mc::Event *FirstEvent(const mc::Event *prev_event, 579 | EvtStateCats *evts) const override { 580 | // Forward 581 | if (before_ != nullptr) { 582 | return before_->FirstEvent(prev_event, evts); 583 | } 584 | 585 | return nullptr; 586 | } 587 | 588 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 589 | const types::WriteID *from_id, std::size_t size, 590 | EvtStateCats *evts) override { 591 | throw std::logic_error("Unexpected UpdateObs"); 592 | return false; 593 | } 594 | 595 | protected: 596 | const Operation *before_; 597 | const Operation *first_write_before_; 598 | }; 599 | 600 | /** 601 | * RandomFactory. 602 | */ 603 | struct RandomFactory { 604 | typedef Operation ResultType; 605 | 606 | explicit RandomFactory(types::Pid min_pid, types::Pid max_pid, 607 | types::Addr min_addr, types::Addr max_addr, 608 | std::size_t stride = sizeof(types::WriteID), 609 | std::size_t max_sequence = 50) 610 | : min_pid_(min_pid), 611 | max_pid_(max_pid), 612 | min_addr_(min_addr), 613 | max_addr_(max_addr), 614 | stride_(stride), 615 | max_sequence_(max_sequence) { 616 | assert(this->stride() >= sizeof(types::WriteID)); 617 | assert(this->stride() % sizeof(types::WriteID) == 0); 618 | } 619 | 620 | void Reset(types::Pid min_pid, types::Pid max_pid, types::Addr min_addr, 621 | types::Addr max_addr, 622 | std::size_t stride = sizeof(types::WriteID)) { 623 | min_pid_ = min_pid; 624 | max_pid_ = max_pid; 625 | min_addr_ = min_addr; 626 | max_addr_ = max_addr; 627 | stride_ = stride; 628 | } 629 | 630 | template 631 | Operation::Ptr operator()(URNG &urng, AddrFilterFunc addr_filter_func, 632 | std::size_t max_fails = 0) const { 633 | // Choice distribution 634 | std::uniform_int_distribution dist_choice(0, 1000 - 1); 635 | 636 | // Pid distribution 637 | std::uniform_int_distribution dist_pid(min_pid_, max_pid_); 638 | 639 | // Addr distribution 640 | auto chunk_min_addr = min_addr_; 641 | auto chunk_max_addr = max_addr_; 642 | 643 | if (ChunkSize() > 1 && HoleSize() > 1) { 644 | std::size_t chunk_cnt = 645 | for_each_AddrRange([](types::Addr a, types::Addr b) {}); 646 | std::size_t select_chunk = 647 | std::uniform_int_distribution(0, chunk_cnt - 1)(urng); 648 | 649 | chunk_min_addr = min_addr_ + (select_chunk * HoleSize()); 650 | chunk_max_addr = chunk_min_addr + ChunkSize() - 1; 651 | 652 | assert(chunk_min_addr >= min_addr_); 653 | assert(chunk_max_addr <= max_addr_); 654 | } 655 | 656 | std::uniform_int_distribution dist_addr( 657 | chunk_min_addr, chunk_max_addr - EvtStateCats::kMaxOpSize); 658 | 659 | // Sequence distribution 660 | std::uniform_int_distribution dist_sequence(1, max_sequence_); 661 | 662 | // Register distribution 663 | std::uniform_int_distribution dist_reg(Backend::r0, Backend::r4); 664 | 665 | // select op 666 | const auto choice = dist_choice(urng); 667 | 668 | // pid 669 | const auto pid = dist_pid(urng); 670 | 671 | // addr (lazy) 672 | auto addr = [&]() { 673 | types::Addr result = 0; 674 | 675 | for (std::size_t tries = 0; tries < max_fails + 1; ++tries) { 676 | result = dist_addr(urng); 677 | result -= result % stride(); 678 | if (result < chunk_min_addr) result += stride(); 679 | assert(result >= chunk_min_addr); 680 | assert(result <= chunk_max_addr - EvtStateCats::kMaxOpSize); 681 | 682 | if (addr_filter_func(result)) { 683 | return result; 684 | } 685 | } 686 | 687 | return result; 688 | }; 689 | 690 | // sequence (lazy) 691 | auto sequence = [&dist_sequence, &urng]() { return dist_sequence(urng); }; 692 | 693 | // sequence (lazy) 694 | auto reg = [&dist_reg, &urng]() { 695 | return static_cast(dist_reg(urng)); 696 | }; 697 | 698 | if (choice < 320) { // 32% 699 | return std::make_shared(addr(), reg(), pid); 700 | } else if (choice < 560) { // 24% 701 | return std::make_shared(addr(), reg(), reg(), pid); 702 | } else if (choice < 980) { // 42% 703 | return std::make_shared(addr(), pid); 704 | } else if (choice < 990) { // 1% 705 | return std::make_shared(pid); 706 | } else if (choice < 1000) { // 1% 707 | return std::make_shared(sequence(), pid); 708 | } 709 | 710 | // should never get here 711 | throw std::logic_error("Not exhaustive"); 712 | return nullptr; 713 | } 714 | 715 | template 716 | Operation::Ptr operator()(URNG &urng) const { 717 | return (*this)(urng, [](types::Addr addr) { return true; }); 718 | } 719 | 720 | types::Pid min_pid() const { return min_pid_; } 721 | 722 | types::Pid max_pid() const { return max_pid_; } 723 | 724 | types::Addr min_addr() const { return min_addr_; } 725 | 726 | types::Addr max_addr() const { return max_addr_; } 727 | 728 | std::size_t stride() const { return stride_ & ((1ULL << 16) - 1); } 729 | 730 | std::size_t ChunkSize() const { 731 | return 1ULL << ((stride_ & (0xffULL << 24)) >> 24); 732 | } 733 | 734 | std::size_t HoleSize() const { 735 | return 1ULL << ((stride_ & (0xffULL << 16)) >> 16); 736 | } 737 | 738 | template 739 | std::size_t for_each_AddrRange(Func func) const { 740 | if (ChunkSize() > 1 && HoleSize() > 1) { 741 | assert(HoleSize() >= ChunkSize()); 742 | assert(ChunkSize() <= (max_addr_ - min_addr_ + 1)); 743 | 744 | std::size_t chunk_cnt = 0; 745 | 746 | for (;; ++chunk_cnt) { 747 | types::Addr min = min_addr_ + (chunk_cnt * HoleSize()); 748 | types::Addr max = min + ChunkSize() - 1; 749 | if (max > max_addr_) break; 750 | 751 | func(min, max); 752 | } 753 | 754 | return chunk_cnt; 755 | } 756 | 757 | func(min_addr_, max_addr_); 758 | return 1; 759 | } 760 | 761 | std::size_t max_sequence() const { return max_sequence_; } 762 | 763 | void set_max_sequence(std::size_t val) { max_sequence_ = val; } 764 | 765 | private: 766 | types::Pid min_pid_; 767 | types::Pid max_pid_; 768 | types::Addr min_addr_; 769 | types::Addr max_addr_; 770 | std::size_t stride_; 771 | std::size_t max_sequence_; 772 | }; 773 | 774 | } // namespace armv7 775 | } // namespace codegen 776 | } // namespace mc2lib 777 | 778 | #endif /* MC2LIB_CODEGEN_OPS_ARMv7_HPP_ */ 779 | 780 | /* vim: set ts=2 sts=2 sw=2 et : */ 781 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/ops/strong.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_OPS_STRONG_HPP_ 35 | #define MC2LIB_CODEGEN_OPS_STRONG_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "../cats.hpp" 43 | #include "../compiler.hpp" 44 | 45 | namespace mc2lib { 46 | namespace codegen { 47 | 48 | /** 49 | * @namespace mc2lib::codegen::strong 50 | * @brief Implementations of Operations for strong memory consistency models. 51 | */ 52 | namespace strong { 53 | 54 | struct Backend { 55 | virtual ~Backend() {} 56 | 57 | virtual void Reset() {} 58 | 59 | virtual std::size_t Return(void *code, std::size_t len) const = 0; 60 | 61 | virtual std::size_t Delay(std::size_t length, void *code, 62 | std::size_t len) const = 0; 63 | 64 | virtual std::size_t Read(types::Addr addr, types::InstPtr start, void *code, 65 | std::size_t len, types::InstPtr *at) const = 0; 66 | 67 | virtual std::size_t ReadAddrDp(types::Addr addr, types::InstPtr start, 68 | void *code, std::size_t len, 69 | types::InstPtr *at) const = 0; 70 | 71 | virtual std::size_t Write(types::Addr addr, types::WriteID write_id, 72 | types::InstPtr start, void *code, std::size_t len, 73 | types::InstPtr *at) const = 0; 74 | 75 | virtual std::size_t ReadModifyWrite(types::Addr addr, types::WriteID write_id, 76 | types::InstPtr start, void *code, 77 | std::size_t len, 78 | types::InstPtr *at) const = 0; 79 | 80 | virtual std::size_t CacheFlush(types::Addr addr, void *code, 81 | std::size_t len) const = 0; 82 | }; 83 | 84 | typedef Op Operation; 85 | typedef MemOp MemOperation; 86 | typedef NullOp NullOperation; 87 | 88 | class Return : public Operation { 89 | public: 90 | explicit Return(types::Pid pid = -1) : Operation(pid) {} 91 | 92 | Operation::Ptr Clone() const override { 93 | return std::make_shared(*this); 94 | } 95 | 96 | void Reset() override {} 97 | 98 | bool EnableEmit(EvtStateCats *evts) override { return true; } 99 | 100 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override {} 101 | 102 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 103 | void *code, std::size_t len) override { 104 | return backend->Return(code, len); 105 | } 106 | 107 | const mc::Event *LastEvent(const mc::Event *next_event, 108 | EvtStateCats *evts) const override { 109 | return nullptr; 110 | } 111 | 112 | const mc::Event *FirstEvent(const mc::Event *prev_event, 113 | EvtStateCats *evts) const override { 114 | return nullptr; 115 | } 116 | 117 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 118 | const types::WriteID *from_id, std::size_t size, 119 | EvtStateCats *evts) override { 120 | return true; 121 | } 122 | }; 123 | 124 | class Delay : public Operation { 125 | public: 126 | explicit Delay(std::size_t length, types::Pid pid = -1) 127 | : Operation(pid), length_(length), before_(nullptr) {} 128 | 129 | Operation::Ptr Clone() const override { 130 | return std::make_shared(*this); 131 | } 132 | 133 | void Reset() override { before_ = nullptr; } 134 | 135 | bool EnableEmit(EvtStateCats *evts) override { return true; } 136 | 137 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 138 | before_ = *before; 139 | } 140 | 141 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 142 | void *code, std::size_t len) override { 143 | return backend->Delay(length_, code, len); 144 | } 145 | 146 | const mc::Event *LastEvent(const mc::Event *next_event, 147 | EvtStateCats *evts) const override { 148 | // Forward 149 | if (before_ != nullptr) { 150 | return before_->LastEvent(next_event, evts); 151 | } 152 | 153 | return nullptr; 154 | } 155 | 156 | const mc::Event *FirstEvent(const mc::Event *prev_event, 157 | EvtStateCats *evts) const override { 158 | // Forward 159 | if (before_ != nullptr) { 160 | return before_->FirstEvent(prev_event, evts); 161 | } 162 | 163 | return nullptr; 164 | } 165 | 166 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 167 | const types::WriteID *from_id, std::size_t size, 168 | EvtStateCats *evts) override { 169 | throw std::logic_error("Unexpected UpdateObs"); 170 | return false; 171 | } 172 | 173 | protected: 174 | std::size_t length_; 175 | const Operation *before_; 176 | }; 177 | 178 | class Read : public MemOperation { 179 | public: 180 | explicit Read(types::Addr addr, types::Pid pid = -1) 181 | : MemOperation(pid), addr_(addr), event_(nullptr), from_(nullptr) {} 182 | 183 | Operation::Ptr Clone() const override { 184 | return std::make_shared(*this); 185 | } 186 | 187 | void Reset() override { 188 | event_ = nullptr; 189 | from_ = nullptr; 190 | } 191 | 192 | bool EnableEmit(EvtStateCats *evts) override { return !evts->Exhausted(); } 193 | 194 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 195 | event_ = evts->MakeRead(pid(), mc::Event::kRead, addr_)[0]; 196 | 197 | if (*before != nullptr) { 198 | auto event_before = (*before)->LastEvent(event_, evts); 199 | if (event_before != nullptr) { 200 | evts->ew()->po.Insert(*event_before, *event_); 201 | } 202 | } 203 | } 204 | 205 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 206 | void *code, std::size_t len) override { 207 | return backend->Read(addr_, start, code, len, &at_); 208 | } 209 | 210 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 211 | const types::WriteID *from_id, std::size_t size, 212 | EvtStateCats *evts) override { 213 | assert(event_ != nullptr); 214 | assert(ip == at_); 215 | assert(addr == addr_); 216 | assert(size == sizeof(types::WriteID)); 217 | 218 | const mc::Event *from = 219 | evts->GetWrite(MakeEventPtrs(event_), addr_, from_id)[0]; 220 | 221 | if (from_ != nullptr) { 222 | // If from_ == from, we still need to continue to try to erase and 223 | // insert, in case the from-relation has been cleared. 224 | 225 | EraseObsHelper(from_, event_, evts->ew()); 226 | } 227 | 228 | from_ = from; 229 | InsertObsHelper(from_, event_, evts->ew()); 230 | 231 | return true; 232 | } 233 | 234 | const mc::Event *LastEvent(const mc::Event *next_event, 235 | EvtStateCats *evts) const override { 236 | return event_; 237 | } 238 | 239 | const mc::Event *FirstEvent(const mc::Event *prev_event, 240 | EvtStateCats *evts) const override { 241 | return event_; 242 | } 243 | 244 | types::Addr addr() const override { return addr_; } 245 | 246 | protected: 247 | virtual void InsertObsHelper(const mc::Event *e1, const mc::Event *e2, 248 | mc::cats::ExecWitness *ew) { 249 | ew->rf.Insert(*e1, *e2, true); 250 | } 251 | 252 | virtual void EraseObsHelper(const mc::Event *e1, const mc::Event *e2, 253 | mc::cats::ExecWitness *ew) { 254 | ew->rf.Erase(*e1, *e2); 255 | } 256 | 257 | types::Addr addr_; 258 | const mc::Event *event_; 259 | const mc::Event *from_; 260 | types::InstPtr at_; 261 | }; 262 | 263 | class ReadAddrDp : public Read { 264 | public: 265 | explicit ReadAddrDp(types::Addr addr, types::Pid pid = -1) 266 | : Read(addr, pid) {} 267 | 268 | Operation::Ptr Clone() const override { 269 | return std::make_shared(*this); 270 | } 271 | 272 | // TODO(melver): InsertPo: if we start supporting an Arch which does not 273 | // order Read->Read, add a dependency-hb between this and the last Read -- 274 | // this assumes all Reads are reading into the same register, and this read 275 | // computes the address with this one register. 276 | // NOTE: before can be used to traverse operations backwards before "before". 277 | 278 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 279 | void *code, std::size_t len) override { 280 | return backend->ReadAddrDp(addr_, start, code, len, &at_); 281 | } 282 | }; 283 | 284 | class Write : public Read { 285 | public: 286 | explicit Write(types::Addr addr, types::Pid pid = -1) 287 | : Read(addr, pid), write_id_(0) {} 288 | 289 | Operation::Ptr Clone() const override { 290 | return std::make_shared(*this); 291 | } 292 | 293 | void Reset() override { 294 | event_ = nullptr; 295 | from_ = nullptr; 296 | write_id_ = 0; 297 | } 298 | 299 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 300 | event_ = evts->MakeWrite(pid(), mc::Event::kWrite, addr_, &write_id_)[0]; 301 | 302 | if (*before != nullptr) { 303 | auto event_before = (*before)->LastEvent(event_, evts); 304 | if (event_before != nullptr) { 305 | evts->ew()->po.Insert(*event_before, *event_); 306 | } 307 | } 308 | } 309 | 310 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 311 | void *code, std::size_t len) override { 312 | return backend->Write(addr_, write_id_, start, code, len, &at_); 313 | } 314 | 315 | protected: 316 | void InsertObsHelper(const mc::Event *e1, const mc::Event *e2, 317 | mc::cats::ExecWitness *ew) override { 318 | ew->co.Insert(*e1, *e2); 319 | } 320 | 321 | void EraseObsHelper(const mc::Event *e1, const mc::Event *e2, 322 | mc::cats::ExecWitness *ew) override { 323 | ew->co.Erase(*e1, *e2); 324 | } 325 | 326 | types::WriteID write_id_; 327 | }; 328 | 329 | class ReadModifyWrite : public MemOperation { 330 | public: 331 | explicit ReadModifyWrite(types::Addr addr, types::Pid pid = -1) 332 | : MemOperation(pid), addr_(addr), last_part_(-1) {} 333 | 334 | Operation::Ptr Clone() const override { 335 | return std::make_shared(*this); 336 | } 337 | 338 | void Reset() override { 339 | last_part_ = -1; 340 | event_r_ = nullptr; 341 | event_w_ = nullptr; 342 | from_ = nullptr; 343 | write_id_ = 0; 344 | } 345 | 346 | bool EnableEmit(EvtStateCats *evts) override { return !evts->Exhausted(); } 347 | 348 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 349 | event_r_ = evts->MakeRead(pid(), mc::Event::kRead, addr_)[0]; 350 | event_w_ = evts->MakeWrite(pid(), mc::Event::kWrite, addr_, &write_id_)[0]; 351 | 352 | if (*before != nullptr) { 353 | auto event_before = (*before)->LastEvent(event_r_, evts); 354 | if (event_before != nullptr) { 355 | evts->ew()->po.Insert(*event_before, *event_r_); 356 | 357 | if (dynamic_cast(evts->arch()) != nullptr) { 358 | // Implied fence before atomic 359 | auto arch_tso = dynamic_cast(evts->arch()); 360 | arch_tso->mfence.Insert(*event_before, *event_r_); 361 | } 362 | } 363 | } 364 | 365 | evts->ew()->po.Insert(*event_r_, *event_w_); 366 | } 367 | 368 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 369 | void *code, std::size_t len) override { 370 | return backend->ReadModifyWrite(addr_, write_id_, start, code, len, &at_); 371 | } 372 | 373 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 374 | const types::WriteID *from_id, std::size_t size, 375 | EvtStateCats *evts) override { 376 | assert(event_r_ != nullptr); 377 | assert(event_w_ != nullptr); 378 | assert(ip == at_); 379 | assert(addr == addr_); 380 | assert(size == sizeof(types::WriteID)); 381 | 382 | // This also alerts us if the read would be seeing the write's data. 383 | const mc::Event *from = 384 | evts->GetWrite(MakeEventPtrs(event_w_), addr_, from_id)[0]; 385 | 386 | auto part_event = event_r_; 387 | auto obs_rel = &evts->ew()->rf; 388 | 389 | // TODO(melver): clean this up! Do we need ability to update squashed? 390 | 391 | if (last_part_ == -1 || part <= last_part_) { 392 | // First part: read 393 | 394 | if (from_ != nullptr) { 395 | // Restart 396 | evts->ew()->rf.Erase(*from_, *event_r_); 397 | evts->ew()->co.Erase(*from_, *event_w_); 398 | } 399 | } else { 400 | // Second part: write 401 | assert(part > last_part_); 402 | 403 | // Check atomicity. 404 | if (*from != *from_) { 405 | std::ostringstream oss; 406 | oss << "RMW NOT ATOMIC: expected <" << static_cast(*from_) 407 | << ">, but overwriting <" << static_cast(*from) 408 | << ">!"; 409 | throw mc::Error(oss.str()); 410 | } 411 | 412 | part_event = event_w_; 413 | obs_rel = &evts->ew()->co; 414 | } 415 | 416 | obs_rel->Insert(*from, *part_event, true); 417 | 418 | from_ = from; 419 | last_part_ = part; 420 | return true; 421 | } 422 | 423 | const mc::Event *LastEvent(const mc::Event *next_event, 424 | EvtStateCats *evts) const override { 425 | if (dynamic_cast(evts->arch()) != nullptr) { 426 | // Implied fence after atomic 427 | auto arch_tso = dynamic_cast(evts->arch()); 428 | arch_tso->mfence.Insert(*event_w_, *next_event); 429 | } 430 | 431 | return event_w_; 432 | } 433 | 434 | const mc::Event *FirstEvent(const mc::Event *prev_event, 435 | EvtStateCats *evts) const override { 436 | return event_r_; 437 | } 438 | 439 | types::Addr addr() const override { return addr_; } 440 | 441 | protected: 442 | types::Addr addr_; 443 | int last_part_; 444 | const mc::Event *event_r_; 445 | const mc::Event *event_w_; 446 | const mc::Event *from_; 447 | types::WriteID write_id_; 448 | types::InstPtr at_; 449 | }; 450 | 451 | class CacheFlush : public MemOperation { 452 | public: 453 | explicit CacheFlush(types::Addr addr, types::Pid pid = -1) 454 | : MemOperation(pid), addr_(addr), before_(nullptr) {} 455 | 456 | Operation::Ptr Clone() const override { 457 | return std::make_shared(*this); 458 | } 459 | 460 | void Reset() override { before_ = nullptr; } 461 | 462 | bool EnableEmit(EvtStateCats *evts) override { return true; } 463 | 464 | void InsertPo(Operation::ThreadConstIt before, EvtStateCats *evts) override { 465 | before_ = *before; 466 | } 467 | 468 | std::size_t Emit(types::InstPtr start, Backend *backend, EvtStateCats *evts, 469 | void *code, std::size_t len) override { 470 | return backend->CacheFlush(addr_, code, len); 471 | } 472 | 473 | const mc::Event *LastEvent(const mc::Event *next_event, 474 | EvtStateCats *evts) const override { 475 | // Forward 476 | if (before_ != nullptr) { 477 | return before_->LastEvent(next_event, evts); 478 | } 479 | 480 | return nullptr; 481 | } 482 | 483 | const mc::Event *FirstEvent(const mc::Event *prev_event, 484 | EvtStateCats *evts) const override { 485 | // Forward 486 | if (before_ != nullptr) { 487 | return before_->FirstEvent(prev_event, evts); 488 | } 489 | 490 | return nullptr; 491 | } 492 | 493 | bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, 494 | const types::WriteID *from_id, std::size_t size, 495 | EvtStateCats *evts) override { 496 | return true; 497 | } 498 | 499 | types::Addr addr() const override { return addr_; } 500 | 501 | protected: 502 | types::Addr addr_; 503 | const Operation *before_; 504 | }; 505 | 506 | class ReadSequence : public NullOperation { 507 | public: 508 | explicit ReadSequence(types::Addr min_addr, types::Addr max_addr, 509 | types::Pid pid = -1) 510 | : NullOperation(pid), min_addr_(min_addr), max_addr_(max_addr) { 511 | while (min_addr <= max_addr) { 512 | sequence_.emplace_back(std::make_shared(min_addr, pid)); 513 | min_addr += 64; 514 | } 515 | } 516 | 517 | void AdvanceThread(Operation::ThreadItStack *it_stack) const override { 518 | ++(it_stack->back().first); 519 | it_stack->emplace_back(sequence_.begin(), sequence_.end()); 520 | } 521 | 522 | Operation::Ptr Clone() const override { 523 | // Don't just copy, need deep clone 524 | return std::make_shared(min_addr_, max_addr_, pid()); 525 | } 526 | 527 | void Reset() override { 528 | for (const auto &op : sequence_) { 529 | op->Reset(); 530 | } 531 | } 532 | 533 | protected: 534 | types::Addr min_addr_; 535 | types::Addr max_addr_; 536 | Operation::Thread sequence_; 537 | }; 538 | 539 | /** 540 | * RandomFactory. 541 | */ 542 | struct RandomFactory { 543 | typedef Operation ResultType; 544 | 545 | explicit RandomFactory(types::Pid min_pid, types::Pid max_pid, 546 | types::Addr min_addr, types::Addr max_addr, 547 | std::size_t stride = sizeof(types::WriteID), 548 | std::size_t max_sequence = 50, bool extended = false) 549 | : min_pid_(min_pid), 550 | max_pid_(max_pid), 551 | min_addr_(min_addr), 552 | max_addr_(max_addr), 553 | stride_(stride), 554 | max_sequence_(max_sequence), 555 | extended_(extended) { 556 | assert(this->stride() >= sizeof(types::WriteID)); 557 | assert(this->stride() % sizeof(types::WriteID) == 0); 558 | } 559 | 560 | void Reset(types::Pid min_pid, types::Pid max_pid, types::Addr min_addr, 561 | types::Addr max_addr, 562 | std::size_t stride = sizeof(types::WriteID)) { 563 | min_pid_ = min_pid; 564 | max_pid_ = max_pid; 565 | min_addr_ = min_addr; 566 | max_addr_ = max_addr; 567 | stride_ = stride; 568 | } 569 | 570 | template 571 | Operation::Ptr operator()(URNG &urng, AddrFilterFunc addr_filter_func, 572 | std::size_t max_fails = 0) const { 573 | // Choice distribution 574 | const std::size_t max_choice = (extended_ ? 1005 : 1000) - 1; 575 | std::uniform_int_distribution dist_choice(0, max_choice); 576 | 577 | // Pid distribution 578 | std::uniform_int_distribution dist_pid(min_pid_, max_pid_); 579 | 580 | // Addr distribution 581 | auto chunk_min_addr = min_addr_; 582 | auto chunk_max_addr = max_addr_; 583 | 584 | if (ChunkSize() > 1 && HoleSize() > 1) { 585 | std::size_t chunk_cnt = 586 | for_each_AddrRange([](types::Addr a, types::Addr b) {}); 587 | std::size_t select_chunk = 588 | std::uniform_int_distribution(0, chunk_cnt - 1)(urng); 589 | 590 | chunk_min_addr = min_addr_ + (select_chunk * HoleSize()); 591 | chunk_max_addr = chunk_min_addr + ChunkSize() - 1; 592 | 593 | assert(chunk_min_addr >= min_addr_); 594 | assert(chunk_max_addr <= max_addr_); 595 | } 596 | 597 | std::uniform_int_distribution dist_addr( 598 | chunk_min_addr, chunk_max_addr - EvtStateCats::kMaxOpSize); 599 | 600 | // Sequence distribution 601 | std::uniform_int_distribution dist_sequence(1, max_sequence_); 602 | 603 | // select op 604 | const auto choice = dist_choice(urng); 605 | 606 | // pid 607 | const auto pid = dist_pid(urng); 608 | 609 | // addr (lazy) 610 | auto addr = [&]() { 611 | types::Addr result = 0; 612 | 613 | for (std::size_t tries = 0; tries < max_fails + 1; ++tries) { 614 | result = dist_addr(urng); 615 | result -= result % stride(); 616 | if (result < chunk_min_addr) result += stride(); 617 | assert(result >= chunk_min_addr); 618 | assert(result <= chunk_max_addr - EvtStateCats::kMaxOpSize); 619 | 620 | if (addr_filter_func(result)) { 621 | return result; 622 | } 623 | } 624 | 625 | return result; 626 | }; 627 | 628 | // sequence (lazy) 629 | auto sequence = [&dist_sequence, &urng]() { return dist_sequence(urng); }; 630 | 631 | if (choice < 500) { // 50% 632 | return std::make_shared(addr(), pid); 633 | } else if (choice < 550) { // 5% 634 | return std::make_shared(addr(), pid); 635 | } else if (choice < 970) { // 42% 636 | return std::make_shared(addr(), pid); 637 | } else if (choice < 980) { // 1% 638 | return std::make_shared(addr(), pid); 639 | } else if (choice < 990) { // 1% 640 | return std::make_shared(addr(), pid); 641 | } else if (choice < 1000) { // 1% 642 | return std::make_shared(sequence(), pid); 643 | } else if (extended_) { 644 | // REAL_PERCENTAGE_OF_100 = PERC * (1000 / MAX_CHOICE) 645 | 646 | if (choice < 1005) { // 0.5% 647 | auto min_a = addr(); 648 | // TODO(melver): do not hard-code stride 649 | auto max_a = min_a + sequence() * 64; 650 | if (max_a > max_addr()) { 651 | max_a = max_addr(); 652 | } 653 | 654 | return std::make_shared(min_a, max_a, pid); 655 | } 656 | } 657 | 658 | // should never get here 659 | throw std::logic_error("Not exhaustive"); 660 | return nullptr; 661 | } 662 | 663 | template 664 | Operation::Ptr operator()(URNG &urng) const { 665 | return (*this)(urng, [](types::Addr addr) { return true; }); 666 | } 667 | 668 | types::Pid min_pid() const { return min_pid_; } 669 | 670 | types::Pid max_pid() const { return max_pid_; } 671 | 672 | types::Addr min_addr() const { return min_addr_; } 673 | 674 | types::Addr max_addr() const { return max_addr_; } 675 | 676 | std::size_t stride() const { return stride_ & ((1ULL << 16) - 1); } 677 | 678 | std::size_t ChunkSize() const { 679 | return 1ULL << ((stride_ & (0xffULL << 24)) >> 24); 680 | } 681 | 682 | std::size_t HoleSize() const { 683 | return 1ULL << ((stride_ & (0xffULL << 16)) >> 16); 684 | } 685 | 686 | template 687 | std::size_t for_each_AddrRange(Func func) const { 688 | if (ChunkSize() > 1 && HoleSize() > 1) { 689 | assert(HoleSize() >= ChunkSize()); 690 | assert(ChunkSize() <= (max_addr_ - min_addr_ + 1)); 691 | 692 | std::size_t chunk_cnt = 0; 693 | 694 | for (;; ++chunk_cnt) { 695 | types::Addr min = min_addr_ + (chunk_cnt * HoleSize()); 696 | types::Addr max = min + ChunkSize() - 1; 697 | if (max > max_addr_) break; 698 | 699 | func(min, max); 700 | } 701 | 702 | return chunk_cnt; 703 | } 704 | 705 | func(min_addr_, max_addr_); 706 | return 1; 707 | } 708 | 709 | std::size_t max_sequence() const { return max_sequence_; } 710 | 711 | void set_max_sequence(std::size_t val) { max_sequence_ = val; } 712 | 713 | bool extended() const { return extended_; } 714 | 715 | void set_extended(bool val) { extended_ = val; } 716 | 717 | private: 718 | types::Pid min_pid_; 719 | types::Pid max_pid_; 720 | types::Addr min_addr_; 721 | types::Addr max_addr_; 722 | std::size_t stride_; 723 | std::size_t max_sequence_; 724 | bool extended_; 725 | }; 726 | 727 | } // namespace strong 728 | } // namespace codegen 729 | } // namespace mc2lib 730 | 731 | #endif /* MC2LIB_CODEGEN_OPS_STRONG_HPP_ */ 732 | 733 | /* vim: set ts=2 sts=2 sw=2 et : */ 734 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/ops/x86_64.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_OPS_X86_64_HPP_ 35 | #define MC2LIB_CODEGEN_OPS_X86_64_HPP_ 36 | 37 | #include 38 | #include 39 | 40 | #include "strong.hpp" 41 | 42 | namespace mc2lib { 43 | namespace codegen { 44 | namespace strong { 45 | 46 | struct Backend_X86_64 : Backend { 47 | std::size_t Return(void *code, std::size_t len) const override; 48 | 49 | std::size_t Delay(std::size_t length, void *code, 50 | std::size_t len) const override; 51 | 52 | std::size_t Read(types::Addr addr, types::InstPtr start, void *code, 53 | std::size_t len, types::InstPtr *at) const override; 54 | 55 | std::size_t ReadAddrDp(types::Addr addr, types::InstPtr start, void *code, 56 | std::size_t len, types::InstPtr *at) const override; 57 | 58 | std::size_t Write(types::Addr addr, types::WriteID write_id, 59 | types::InstPtr start, void *code, std::size_t len, 60 | types::InstPtr *at) const override; 61 | 62 | std::size_t ReadModifyWrite(types::Addr addr, types::WriteID write_id, 63 | types::InstPtr start, void *code, std::size_t len, 64 | types::InstPtr *at) const override; 65 | 66 | std::size_t CacheFlush(types::Addr addr, void *code, 67 | std::size_t len) const override; 68 | }; 69 | 70 | inline std::size_t Backend_X86_64::Return(void *code, std::size_t len) const { 71 | assert(len >= 1); 72 | // ASM> retq ; 73 | *static_cast(code) = 0xc3; 74 | return 1; 75 | } 76 | 77 | inline std::size_t Backend_X86_64::Delay(std::size_t length, void *code, 78 | std::size_t len) const { 79 | char *cnext = static_cast(code); 80 | 81 | assert(len >= length); 82 | for (std::size_t i = 0; i < length; ++i) { 83 | // ASM> nop ; 84 | *cnext++ = 0x90; 85 | } 86 | 87 | assert((cnext - static_cast(code)) == 88 | static_cast(length)); 89 | return length; 90 | } 91 | 92 | inline std::size_t Backend_X86_64::Read(types::Addr addr, types::InstPtr start, 93 | void *code, std::size_t len, 94 | types::InstPtr *at) const { 95 | char *cnext = static_cast(code); 96 | std::size_t expected_len = 0; 97 | 98 | if (addr <= static_cast(0xffffffff)) { 99 | switch (sizeof(types::WriteID)) { 100 | case 1: 101 | // ASM @0> movzbl addr, %eax ; 102 | expected_len = 8; 103 | assert(len >= expected_len); 104 | *at = start; 105 | 106 | // @0 107 | *cnext++ = 0x0f; 108 | *cnext++ = 0xb6; 109 | *cnext++ = 0x04; 110 | *cnext++ = 0x25; 111 | *reinterpret_cast(cnext) = 112 | static_cast(addr); 113 | cnext += sizeof(std::uint32_t); 114 | break; 115 | 116 | default: 117 | throw std::logic_error("Not supported"); 118 | } 119 | } else { 120 | switch (sizeof(types::WriteID)) { 121 | case 1: 122 | // ASM @0> movabs addr, %al ; 123 | expected_len = 9; 124 | assert(len >= expected_len); 125 | *at = start; 126 | 127 | // @0 128 | *cnext++ = 0xa0; 129 | break; 130 | 131 | case 2: 132 | // ASM @0> movabs addr, %ax ; 133 | expected_len = 10; 134 | assert(len >= expected_len); 135 | *at = start; 136 | 137 | // @0 138 | *cnext++ = 0x66; 139 | *cnext++ = 0xa1; 140 | break; 141 | 142 | default: 143 | throw std::logic_error("Not supported"); 144 | } 145 | 146 | *reinterpret_cast(cnext) = 147 | static_cast(addr); 148 | cnext += sizeof(std::uint64_t); 149 | } 150 | 151 | assert((cnext - static_cast(code)) == 152 | static_cast(expected_len)); 153 | return expected_len; 154 | } 155 | 156 | inline std::size_t Backend_X86_64::ReadAddrDp(types::Addr addr, 157 | types::InstPtr start, void *code, 158 | std::size_t len, 159 | types::InstPtr *at) const { 160 | char *cnext = static_cast(code); 161 | std::size_t expected_len = 0; 162 | 163 | // ASM @0> xor %rax, %rax 164 | expected_len = 3; 165 | assert(len >= expected_len); 166 | 167 | // @0 168 | *cnext++ = 0x48; 169 | *cnext++ = 0x31; 170 | *cnext++ = 0xc0; 171 | 172 | if (addr <= static_cast(0xffffffff)) { 173 | switch (sizeof(types::WriteID)) { 174 | case 1: 175 | // ASM @3> movzbl addr(%rax), %eax ; 176 | expected_len = 10; 177 | assert(len >= expected_len); 178 | *at = start + 3; 179 | 180 | // @3 181 | *cnext++ = 0x0f; 182 | *cnext++ = 0xb6; 183 | *cnext++ = 0x80; 184 | *reinterpret_cast(cnext) = 185 | static_cast(addr); 186 | cnext += sizeof(std::uint32_t); 187 | break; 188 | 189 | default: 190 | throw std::logic_error("Not supported"); 191 | } 192 | } else { 193 | // ASM @03> movabs addr, %rdx ; 194 | // @0d> add %rdx, %rax ; 195 | expected_len = 19; 196 | assert(len >= expected_len); 197 | *at = start + 0x10; 198 | 199 | // @03 200 | *cnext++ = 0x48; 201 | *cnext++ = 0xba; 202 | *reinterpret_cast(cnext) = 203 | static_cast(addr); 204 | cnext += sizeof(std::uint64_t); 205 | 206 | // @0d 207 | *cnext++ = 0x48; 208 | *cnext++ = 0x01; 209 | *cnext++ = 0xd0; 210 | 211 | switch (sizeof(types::WriteID)) { 212 | case 1: 213 | // ASM @10> movzbl (%rax), %eax ; 214 | // @10 215 | *cnext++ = 0x0f; 216 | *cnext++ = 0xb6; 217 | *cnext++ = 0x00; 218 | break; 219 | 220 | case 2: 221 | // ASM @10> movzwl (%rax), %eax ; 222 | // @10 223 | *cnext++ = 0x0f; 224 | *cnext++ = 0xb7; 225 | *cnext++ = 0x00; 226 | break; 227 | 228 | default: 229 | throw std::logic_error("Not supported"); 230 | } 231 | } 232 | 233 | assert((cnext - static_cast(code)) == 234 | static_cast(expected_len)); 235 | return expected_len; 236 | } 237 | 238 | inline std::size_t Backend_X86_64::Write(types::Addr addr, 239 | types::WriteID write_id, 240 | types::InstPtr start, void *code, 241 | std::size_t len, 242 | types::InstPtr *at) const { 243 | char *cnext = static_cast(code); 244 | std::size_t expected_len = 0; 245 | 246 | assert(write_id != 0); 247 | 248 | if (addr <= static_cast(0xffffffff)) { 249 | switch (sizeof(types::WriteID)) { 250 | case 1: 251 | // ASM @0> movb write_id, addr ; 252 | expected_len = 8; 253 | assert(len >= expected_len); 254 | *at = start; 255 | 256 | // @0 257 | *cnext++ = 0xc6; 258 | *cnext++ = 0x04; 259 | *cnext++ = 0x25; 260 | 261 | *reinterpret_cast(cnext) = 262 | static_cast(addr); 263 | cnext += sizeof(std::uint32_t); 264 | 265 | *reinterpret_cast(cnext) = write_id; 266 | cnext += sizeof(types::WriteID); 267 | break; 268 | 269 | default: 270 | throw std::logic_error("Not supported"); 271 | } 272 | } else { 273 | switch (sizeof(types::WriteID)) { 274 | case 1: 275 | // ASM @0> movabs addr, %rax ; 276 | // @a> movb write_id, (%rax) ; 277 | expected_len = 13; 278 | assert(len >= expected_len); 279 | *at = start + 0xa; 280 | 281 | // @0 282 | *cnext++ = 0x48; 283 | *cnext++ = 0xb8; 284 | *reinterpret_cast(cnext) = 285 | static_cast(addr); 286 | cnext += sizeof(std::uint64_t); 287 | 288 | // @a 289 | *cnext++ = 0xc6; 290 | *cnext++ = 0x00; 291 | *reinterpret_cast(cnext) = write_id; 292 | cnext += sizeof(types::WriteID); 293 | break; 294 | 295 | case 2: 296 | // ASM @0> movabs addr, %rax ; 297 | // @a> mov write_id, %edx ; 298 | // @f> mov %dx, (%rax) ; 299 | expected_len = 18; 300 | assert(len >= expected_len); 301 | *at = start + 0xf; 302 | 303 | // @0 304 | *cnext++ = 0x48; 305 | *cnext++ = 0xb8; 306 | *reinterpret_cast(cnext) = 307 | static_cast(addr); 308 | cnext += sizeof(std::uint64_t); 309 | 310 | // @a 311 | *cnext++ = 0xba; 312 | *reinterpret_cast(cnext) = write_id; 313 | cnext += sizeof(std::uint32_t); 314 | 315 | // @f 316 | *cnext++ = 0x66; 317 | *cnext++ = 0x89; 318 | *cnext++ = 0x10; 319 | break; 320 | 321 | default: 322 | throw std::logic_error("Not supported"); 323 | } 324 | } 325 | 326 | assert((cnext - static_cast(code)) == 327 | static_cast(expected_len)); 328 | return expected_len; 329 | } 330 | 331 | inline std::size_t Backend_X86_64::ReadModifyWrite(types::Addr addr, 332 | types::WriteID write_id, 333 | types::InstPtr start, 334 | void *code, std::size_t len, 335 | types::InstPtr *at) const { 336 | char *cnext = static_cast(code); 337 | std::size_t expected_len = 0; 338 | 339 | assert(write_id != 0); 340 | 341 | switch (sizeof(types::WriteID)) { 342 | case 1: 343 | // ASM @0> mov write_id, %al 344 | expected_len = 2; 345 | assert(len >= expected_len); 346 | 347 | // @0 348 | *cnext++ = 0xb0; 349 | *reinterpret_cast(cnext) = write_id; 350 | cnext += sizeof(types::WriteID); 351 | break; 352 | 353 | case 2: 354 | // ASM @0> mov write_id, %eax 355 | expected_len = 5; 356 | assert(len >= expected_len); 357 | 358 | // @0 359 | *cnext++ = 0xb8; 360 | *reinterpret_cast(cnext) = write_id; 361 | cnext += sizeof(std::uint32_t); 362 | break; 363 | 364 | default: 365 | throw std::logic_error("Not supported"); 366 | } 367 | 368 | if (addr <= static_cast(0xffffffff)) { 369 | switch (sizeof(types::WriteID)) { 370 | case 1: 371 | // ASM @2> mov addr, %edx 372 | // @7> lock xchg %al, (%rdx) 373 | expected_len = 10; 374 | assert(len >= expected_len); 375 | *at = start + 0x7; 376 | 377 | // @2 378 | *cnext++ = 0xba; 379 | *reinterpret_cast(cnext) = 380 | static_cast(addr); 381 | cnext += sizeof(std::uint32_t); 382 | 383 | // @7 384 | *cnext++ = 0xf0; 385 | *cnext++ = 0x86; 386 | *cnext++ = 0x02; 387 | break; 388 | 389 | default: 390 | throw std::logic_error("Not supported"); 391 | } 392 | } else { 393 | switch (sizeof(types::WriteID)) { 394 | case 1: 395 | // ASM @2> movabs addr, %rdx ; 396 | // @c> lock xchg %al, (%rdx) ; 397 | expected_len = 15; 398 | assert(len >= expected_len); 399 | *at = start + 0xc; 400 | 401 | // @2 402 | *cnext++ = 0x48; 403 | *cnext++ = 0xba; 404 | *reinterpret_cast(cnext) = 405 | static_cast(addr); 406 | cnext += sizeof(std::uint64_t); 407 | 408 | // @c 409 | *cnext++ = 0xf0; 410 | *cnext++ = 0x86; 411 | *cnext++ = 0x02; 412 | break; 413 | 414 | case 2: 415 | // ASM @5> movabs addr, %rdx ; 416 | // @f> lock xchg %ax, (%rdx) ; 417 | expected_len = 19; 418 | assert(len >= expected_len); 419 | *at = start + 0xf; 420 | 421 | // @2 422 | *cnext++ = 0x48; 423 | *cnext++ = 0xba; 424 | *reinterpret_cast(cnext) = 425 | static_cast(addr); 426 | cnext += sizeof(std::uint64_t); 427 | 428 | // @c 429 | *cnext++ = 0x66; 430 | *cnext++ = 0xf0; 431 | *cnext++ = 0x87; 432 | *cnext++ = 0x02; 433 | break; 434 | 435 | default: 436 | throw std::logic_error("Not supported"); 437 | } 438 | } 439 | 440 | assert((cnext - static_cast(code)) == 441 | static_cast(expected_len)); 442 | return expected_len; 443 | } 444 | 445 | inline std::size_t Backend_X86_64::CacheFlush(types::Addr addr, void *code, 446 | std::size_t len) const { 447 | char *cnext = static_cast(code); 448 | std::size_t expected_len = 0; 449 | 450 | if (addr <= static_cast(0xffffffff)) { 451 | // ASM @0> clflush addr ; 452 | expected_len = 8; 453 | assert(len >= expected_len); 454 | 455 | // @0 456 | *cnext++ = 0x0f; 457 | *cnext++ = 0xae; 458 | *cnext++ = 0x3c; 459 | *cnext++ = 0x25; 460 | *reinterpret_cast(cnext) = 461 | static_cast(addr); 462 | cnext += sizeof(std::uint32_t); 463 | } else { 464 | // ASM @0> mov addr, %rdx ; 465 | // @a> clflush (%rdx) ; 466 | expected_len = 13; 467 | assert(len >= expected_len); 468 | 469 | // @0 470 | *cnext++ = 0x48; 471 | *cnext++ = 0xba; 472 | *reinterpret_cast(cnext) = 473 | static_cast(addr); 474 | cnext += sizeof(std::uint64_t); 475 | 476 | // @a 477 | *cnext++ = 0x0f; 478 | *cnext++ = 0xae; 479 | *cnext++ = 0x3a; 480 | } 481 | 482 | assert((cnext - static_cast(code)) == 483 | static_cast(expected_len)); 484 | return expected_len; 485 | } 486 | 487 | } // namespace strong 488 | } // namespace codegen 489 | } // namespace mc2lib 490 | 491 | #endif /* MC2LIB_CODEGEN_OPS_X86_64_HPP_ */ 492 | 493 | /* vim: set ts=2 sts=2 sw=2 et : */ 494 | -------------------------------------------------------------------------------- /include/mc2lib/codegen/rit.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_CODEGEN_RIT_HPP_ 35 | #define MC2LIB_CODEGEN_RIT_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "../sets.hpp" 42 | #include "../simplega.hpp" 43 | #include "../types.hpp" 44 | 45 | namespace mc2lib { 46 | namespace codegen { 47 | 48 | template 49 | class RandInstTest 50 | : public simplega::Genome { 51 | public: 52 | typedef typename OperationFactory::ResultType Operation; 53 | typedef sets::Set>> AddrSet; 54 | 55 | explicit RandInstTest(URNG& urng, const OperationFactory* factory, 56 | std::size_t len) 57 | : urng_(urng), factory_(factory), fitness_(0.0f) { 58 | this->genome_.resize(len); 59 | 60 | for (auto& op_ptr : this->genome_) { 61 | op_ptr = (*factory)(urng); 62 | } 63 | } 64 | 65 | explicit RandInstTest(const RandInstTest& parent1, 66 | const RandInstTest& parent2, 67 | std::vector g) 68 | : simplega::Genome(std::move(g)), 69 | urng_(parent1.urng_), 70 | factory_(parent1.factory_), 71 | fitness_(0.0f) {} 72 | 73 | void Mutate(float rate) override { 74 | std::uniform_int_distribution dist_idx( 75 | 0, this->genome_.size() - 1); 76 | std::unordered_set used; 77 | std::size_t selection_count = static_cast( 78 | static_cast(this->genome_.size()) * rate); 79 | 80 | while (selection_count) { 81 | auto idx = dist_idx(urng_); 82 | if (used.find(idx) != used.end()) { 83 | continue; 84 | } 85 | 86 | this->genome_[idx] = MakeRandom(); 87 | 88 | used.insert(idx); 89 | --selection_count; 90 | } 91 | } 92 | 93 | float Fitness() const override { return fitness_; } 94 | 95 | void set_fitness(float fitness) { fitness_ = fitness; } 96 | 97 | const AddrSet& fitaddrs() const { return fitaddrs_; } 98 | 99 | AddrSet* fitaddrsptr() { return &fitaddrs_; } 100 | 101 | typename Operation::Ptr MakeRandom() const { return (*factory_)(urng_); } 102 | 103 | typename Operation::Ptr MakeRandom(const AddrSet& subset_addrs, 104 | std::size_t max_tries = 1000) const { 105 | return (*factory_)(urng_, 106 | [&subset_addrs](types::Addr addr) { 107 | return subset_addrs.Contains(addr); 108 | }, 109 | max_tries); 110 | } 111 | 112 | typename Operation::Threads threads() { 113 | return ExtractThreads(this->get_ptr()); 114 | } 115 | 116 | private: 117 | URNG& urng_; 118 | const OperationFactory* factory_; 119 | 120 | float fitness_; 121 | AddrSet fitaddrs_; 122 | }; 123 | 124 | } // namespace codegen 125 | } // namespace mc2lib 126 | 127 | #endif /* MC2LIB_CODEGEN_RIT_HPP_ */ 128 | 129 | /* vim: set ts=2 sts=2 sw=2 et : */ 130 | -------------------------------------------------------------------------------- /include/mc2lib/mcversi.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_MCVERSI_HPP_ 35 | #define MC2LIB_MCVERSI_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "simplega.hpp" 42 | 43 | namespace mc2lib { 44 | 45 | /** 46 | * @namespace mc2lib::mcversi 47 | * @brief Implementations of algorithms from McVerSi paper. 48 | */ 49 | namespace mcversi { 50 | 51 | /** 52 | * Crossover and mutation (Algorithm 1) from McVerSi paper. 53 | */ 54 | template 55 | class CrossoverMutate { 56 | public: 57 | explicit CrossoverMutate(double P_USEL, double P_BFA) 58 | : P_USEL_(P_USEL), P_BFA_(P_BFA) {} 59 | 60 | void operator()( 61 | URNG& urng, const RandInstTest& test1, const RandInstTest& test2, 62 | float mutation_rate, 63 | typename simplega::GenePool::Population* container) { 64 | assert(test1.get().size() == test2.get().size()); 65 | 66 | // Probability with which we unconditionally select a particular gene. We 67 | // don't always just want to pick fitaddr operations, as surrounding 68 | // operations may contribute to coverage or other timing effects. 69 | std::bernoulli_distribution mem_unconditional_select(P_USEL_); 70 | 71 | const double faf1 = FitaddrFraction(test1); 72 | const double faf2 = FitaddrFraction(test2); 73 | 74 | // Same rate as selecting a memory-operation. 75 | std::bernoulli_distribution nonmem_select1(faf1 + P_USEL_ - 76 | (faf1 * P_USEL_)); 77 | std::bernoulli_distribution nonmem_select2(faf2 + P_USEL_ - 78 | (faf2 * P_USEL_)); 79 | 80 | // Tiebreaker: If both are valid, pick one consistently and do not break up 81 | // a good genome's dominant genes as they may interact in an imporant way. 82 | const bool prefer_test2 = std::bernoulli_distribution(0.5)(urng); 83 | 84 | // In case both operations are invalid, probability with which we make a 85 | // new operation with the address-set restricted to fitaddrs. 86 | std::bernoulli_distribution new_from_fitaddrs(P_BFA_); 87 | const auto all_fitaddrs = test1.fitaddrs() | test2.fitaddrs(); 88 | 89 | auto child = test1.get(); 90 | std::size_t mutations = 0; 91 | 92 | for (std::size_t i = 0; i < child.size(); ++i) { 93 | bool select1 = false; 94 | bool select2 = false; 95 | 96 | auto mem_op1 = dynamic_cast(test1.get()[i].get()); 97 | auto mem_op2 = dynamic_cast(test2.get()[i].get()); 98 | 99 | // Decide validity of genes 100 | if (mem_op1 != nullptr) { 101 | select1 = test1.fitaddrs().Contains(mem_op1->addr()) || 102 | mem_unconditional_select(urng); 103 | } else { 104 | select1 = nonmem_select1(urng); 105 | } 106 | 107 | if (mem_op2 != nullptr) { 108 | select2 = test2.fitaddrs().Contains(mem_op2->addr()) || 109 | mem_unconditional_select(urng); 110 | } else { 111 | select2 = nonmem_select2(urng); 112 | } 113 | 114 | // Pick gene 115 | if (select1 && select2) { 116 | if (prefer_test2) { 117 | child[i] = test2.get()[i]; 118 | } 119 | } else if (!select1 && select2) { 120 | child[i] = test2.get()[i]; 121 | } else if (!select1 && !select2) { 122 | ++mutations; 123 | 124 | if (new_from_fitaddrs(urng)) { 125 | // Make new random operation, but only select from set of 126 | // fitaddrs. 127 | child[i] = test1.MakeRandom(all_fitaddrs); 128 | } else { 129 | // By deciding to discard fit addresses, we control where the 130 | // tests mutate, so that in the initial phases the good 131 | // operations do not get mutated away. 132 | // 133 | child[i] = test1.MakeRandom(); 134 | } 135 | } else { 136 | assert(select1); 137 | // child[i] is valid, don't do anything. 138 | } 139 | } 140 | 141 | auto result = RandInstTest(test1, test2, child); 142 | 143 | float cur_mutation = 144 | static_cast(mutations) / static_cast(child.size()); 145 | if (cur_mutation < mutation_rate) { 146 | // cur_mutation is the fraction of newly generated instructions, i.e. 147 | // replaced non-dominant genes. We must mutate full mutation_rate 148 | // again, as newly generated instructions are considered for mutation 149 | // as well (redundant). 150 | result.Mutate(mutation_rate); 151 | } else { 152 | // No mutation neccessary. 153 | } 154 | 155 | container->push_back(std::move(result)); 156 | } 157 | 158 | private: 159 | double P_USEL_; 160 | double P_BFA_; 161 | 162 | double FitaddrFraction(const RandInstTest& rit) { 163 | std::size_t mem_ops = 0; 164 | std::size_t fitaddr_ops = 0; 165 | 166 | for (const auto& op : rit.get()) { 167 | auto mem_op = dynamic_cast(op.get()); 168 | if (mem_op != nullptr) { 169 | ++mem_ops; 170 | if (rit.fitaddrs().Contains(mem_op->addr())) { 171 | ++fitaddr_ops; 172 | } 173 | } 174 | } 175 | 176 | return static_cast(fitaddr_ops) / static_cast(mem_ops); 177 | } 178 | }; 179 | 180 | } // namespace mcversi 181 | } // namespace mc2lib 182 | 183 | #endif /* MCVERSI_HPP_ */ 184 | 185 | /* vim: set ts=2 sts=2 sw=2 et : */ 186 | -------------------------------------------------------------------------------- /include/mc2lib/memconsistency/cats.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_MEMCONSISTENCY_CATS_HPP_ 35 | #define MC2LIB_MEMCONSISTENCY_CATS_HPP_ 36 | 37 | #include 38 | 39 | #include "eventsets.hpp" 40 | 41 | namespace mc2lib { 42 | namespace memconsistency { 43 | 44 | /** 45 | * @namespace mc2lib::memconsistency::cats 46 | * @brief Memory consistency model framework based on "Herding cats". 47 | * 48 | * This memory consistency model framework is based upon [1], and [2]. 49 | * 50 | * [1] 51 | * J. Alglave, L. Maranget, M. Tautschnig, "Herding cats: Modelling, 52 | * Simulation, Testing, and Data Mining for Weak Memory", 2014. 53 | * 54 | * [2] 55 | * J. Alglave, L. Maranget, S. Sarkar, and P. Sewell. "Fences in weak 56 | * memory models", 2012. 57 | */ 58 | namespace cats { 59 | 60 | class ExecWitness { 61 | public: 62 | template 63 | EventRel fr(FilterFunc filter_func) const { 64 | EventRel er; 65 | 66 | // Use of get() is justified, as we do not expect (according to wf_rf), the 67 | // rf-relation to have any additional properties. 68 | for (const auto& rf_tuples : rf.get()) { 69 | const auto co_reach = co.Reachable(rf_tuples.first); 70 | for (const auto& co_w : co_reach.get()) { 71 | for (const auto& rf_r : rf_tuples.second.get()) { 72 | if (filter_func(std::make_pair(rf_tuples.first, rf_r), 73 | std::make_pair(rf_tuples.first, co_w))) { 74 | er.Insert(rf_r, co_w); 75 | } 76 | } 77 | } 78 | } 79 | 80 | return er; 81 | } 82 | 83 | EventRel fr() const { 84 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 85 | return true; 86 | }); 87 | } 88 | 89 | EventRel fri() const { 90 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 91 | return t1.second.iiid.pid == t2.second.iiid.pid; 92 | }); 93 | } 94 | 95 | EventRel fre() const { 96 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 97 | return t1.second.iiid.pid != t2.second.iiid.pid; 98 | }); 99 | } 100 | 101 | EventRel rfi() const { 102 | return rf.Filter([](const Event& e1, const Event& e2) { 103 | return e1.iiid.pid == e2.iiid.pid; 104 | }); 105 | } 106 | 107 | EventRel rfe() const { 108 | return rf.Filter([](const Event& e1, const Event& e2) { 109 | return e1.iiid.pid != e2.iiid.pid; 110 | }); 111 | } 112 | 113 | EventRel coi() const { 114 | return co.Filter([](const Event& e1, const Event& e2) { 115 | return e1.iiid.pid == e2.iiid.pid; 116 | }); 117 | } 118 | 119 | EventRel coe() const { 120 | return co.Filter([](const Event& e1, const Event& e2) { 121 | return e1.iiid.pid != e2.iiid.pid; 122 | }); 123 | } 124 | 125 | EventRel com() const { return rf | co | fr(); } 126 | 127 | EventRel po_loc() const { 128 | return po.Filter( 129 | [](const Event& e1, const Event& e2) { return e1.addr == e2.addr; }); 130 | } 131 | 132 | void Clear() { 133 | events.Clear(); 134 | po.Clear(); 135 | co.Clear(); 136 | rf.Clear(); 137 | } 138 | 139 | public: 140 | EventSet events; 141 | EventRel po; 142 | EventRel co; 143 | EventRel rf; 144 | }; 145 | 146 | class Checker; 147 | 148 | class Architecture { 149 | public: 150 | Architecture() : proxy_(this) {} 151 | 152 | virtual ~Architecture() { assert(proxy_ == this); } 153 | 154 | virtual void Clear() {} 155 | 156 | /** 157 | * Creates a checker compatible with this Architecture. 158 | */ 159 | virtual std::unique_ptr MakeChecker( 160 | const Architecture* arch, const ExecWitness* exec) const = 0; 161 | 162 | virtual EventRel ppo(const ExecWitness& ew) const = 0; 163 | virtual EventRel fences(const ExecWitness& ew) const = 0; 164 | virtual EventRel prop(const ExecWitness& ew) const = 0; 165 | 166 | virtual EventRel hb(const ExecWitness& ew) const { 167 | return ew.rfe() | proxy_->ppo(ew) | proxy_->fences(ew); 168 | } 169 | 170 | /** 171 | * Should return the mask of all types that are classed as read. 172 | */ 173 | virtual Event::Type EventTypeRead() const = 0; 174 | 175 | /** 176 | * Should return the mask of all types that are classed as write. 177 | */ 178 | virtual Event::Type EventTypeWrite() const = 0; 179 | 180 | void set_proxy(const Architecture* proxy) { 181 | assert(proxy != nullptr); 182 | proxy_ = proxy; 183 | } 184 | 185 | protected: 186 | const Architecture* proxy_; 187 | }; 188 | 189 | template 190 | class ArchProxy : public Architecture { 191 | public: 192 | explicit ArchProxy(ConcreteArch* arch) 193 | : arch_(arch), 194 | memoized_ppo_(false), 195 | memoized_fences_(false), 196 | memoized_prop_(false), 197 | memoized_hb_(false) { 198 | arch_->set_proxy(this); 199 | } 200 | 201 | ~ArchProxy() override { arch_->set_proxy(arch_); } 202 | 203 | void Clear() override { 204 | arch_->Clear(); 205 | memoized_ppo_ = false; 206 | memoized_fences_ = false; 207 | memoized_prop_ = false; 208 | memoized_hb_ = false; 209 | } 210 | 211 | std::unique_ptr MakeChecker(const Architecture* arch, 212 | const ExecWitness* exec) const override { 213 | return arch_->MakeChecker(arch, exec); 214 | } 215 | 216 | std::unique_ptr MakeChecker(const ExecWitness* exec) const { 217 | return MakeChecker(this, exec); 218 | } 219 | 220 | void Memoize(const ExecWitness& ew) { 221 | // fences and ppo are likely used by hb and prop 222 | fences_ = arch_->fences(ew); 223 | memoized_fences_ = true; 224 | 225 | ppo_ = arch_->ppo(ew); 226 | memoized_ppo_ = true; 227 | 228 | hb_ = arch_->hb(ew); 229 | memoized_hb_ = true; 230 | 231 | prop_ = arch_->prop(ew); 232 | memoized_prop_ = true; 233 | } 234 | 235 | EventRel ppo(const ExecWitness& ew) const override { 236 | return memoized_ppo_ ? ppo_ : arch_->ppo(ew); 237 | } 238 | 239 | EventRel fences(const ExecWitness& ew) const override { 240 | return memoized_fences_ ? fences_ : arch_->fences(ew); 241 | } 242 | 243 | EventRel prop(const ExecWitness& ew) const override { 244 | return memoized_prop_ ? prop_ : arch_->prop(ew); 245 | } 246 | 247 | EventRel hb(const ExecWitness& ew) const override { 248 | return memoized_hb_ ? hb_ : arch_->hb(ew); 249 | } 250 | 251 | Event::Type EventTypeRead() const override { return arch_->EventTypeRead(); } 252 | 253 | Event::Type EventTypeWrite() const override { 254 | return arch_->EventTypeWrite(); 255 | } 256 | 257 | protected: 258 | ConcreteArch* arch_; 259 | 260 | bool memoized_ppo_; 261 | bool memoized_fences_; 262 | bool memoized_prop_; 263 | bool memoized_hb_; 264 | 265 | EventRel ppo_; 266 | EventRel fences_; 267 | EventRel prop_; 268 | EventRel hb_; 269 | }; 270 | 271 | class Checker { 272 | public: 273 | Checker(const Architecture* arch, const ExecWitness* exec) 274 | : arch_(arch), exec_(exec) {} 275 | 276 | virtual ~Checker() {} 277 | 278 | virtual void wf_rf() const { 279 | EventSet reads; 280 | 281 | for (const auto& tuples : exec_->rf.get()) { 282 | if (!tuples.first.AnyType(arch_->EventTypeWrite())) { 283 | throw Error("WF_RF_NOT_FROM_WRITE"); 284 | } 285 | 286 | for (const auto& e : tuples.second.get()) { 287 | if (!e.AnyType(arch_->EventTypeRead()) || tuples.first.addr != e.addr) { 288 | throw Error("WF_RF_NOT_SAME_LOC"); 289 | } 290 | 291 | // For every read, there exists only 1 source! 292 | if (reads.Contains(e)) { 293 | throw Error("WF_RF_MULTI_SOURCE"); 294 | } 295 | reads.Insert(e); 296 | } 297 | } 298 | } 299 | 300 | virtual void wf_co() const { 301 | std::unordered_set addrs; 302 | 303 | // Assert writes ordered captured in ws are to the same location. 304 | for (const auto& tuples : exec_->co.get()) { 305 | addrs.insert(tuples.first.addr); 306 | 307 | for (const auto& e : tuples.second.get()) { 308 | if (tuples.first.addr != e.addr) { 309 | throw Error("WF_CO_NOT_SAME_LOC"); 310 | } 311 | } 312 | } 313 | 314 | auto writes = exec_->events.Filter( 315 | [&](const Event& e) { return e.AnyType(arch_->EventTypeWrite()); }); 316 | if (!exec_->co.StrictPartialOrder(writes)) { 317 | throw Error("WF_CO_NOT_STRICT_PARTIAL_ORDER"); 318 | } 319 | 320 | for (const auto& addr : addrs) { 321 | auto same_addr_writes = 322 | writes.Filter([&](const Event& e) { return e.addr == addr; }); 323 | if (!exec_->co.ConnexOn(same_addr_writes)) { 324 | throw Error("WF_CO_NOT_CONNEX"); 325 | } 326 | } 327 | } 328 | 329 | virtual void wf() const { 330 | wf_rf(); 331 | wf_co(); 332 | } 333 | 334 | virtual bool sc_per_location(EventRel::Path* cyclic = nullptr) const { 335 | return (exec_->com() | exec_->po_loc()).Acyclic(cyclic); 336 | } 337 | 338 | virtual bool no_thin_air(EventRel::Path* cyclic = nullptr) const { 339 | return arch_->hb(*exec_).Acyclic(cyclic); 340 | } 341 | 342 | virtual bool observation(EventRel::Path* cyclic = nullptr) const { 343 | const EventRel prop = arch_->prop(*exec_); 344 | const EventRel hbstar = 345 | arch_->hb(*exec_).set_props(EventRel::kReflexiveTransitiveClosure); 346 | 347 | // Not eval'ing hbstar causes performance to degrade substantially, as 348 | // EventRelSeq recomputes reachability from nodes from prop to hbstar 349 | // several times! 350 | bool r = EventRelSeq({exec_->fre(), prop, hbstar.Eval()}).Irreflexive(); 351 | 352 | if (!r && cyclic != nullptr) { 353 | // However, here we want hbstar unevald, as otherwise the graph is 354 | // too collapsed. 355 | EventRelSeq({exec_->fre(), prop, hbstar}).Irreflexive(cyclic); 356 | } 357 | 358 | return r; 359 | } 360 | 361 | virtual bool propagation(EventRel::Path* cyclic = nullptr) const { 362 | return (exec_->co | arch_->prop(*exec_)).Acyclic(cyclic); 363 | } 364 | 365 | virtual void valid_exec(EventRel::Path* cyclic = nullptr) const { 366 | wf(); 367 | 368 | if (!sc_per_location(cyclic)) { 369 | throw Error("SC_PER_LOCATION"); 370 | } 371 | 372 | if (!no_thin_air(cyclic)) { 373 | throw Error("NO_THIN_AIR"); 374 | } 375 | 376 | if (!observation(cyclic)) { 377 | throw Error("OBSERVATION"); 378 | } 379 | 380 | if (!propagation(cyclic)) { 381 | throw Error("PROPAGATION"); 382 | } 383 | } 384 | 385 | protected: 386 | const Architecture* arch_; 387 | const ExecWitness* exec_; 388 | }; 389 | 390 | /* 391 | ============================= 392 | Some common memory models. 393 | ============================= 394 | */ 395 | 396 | class Arch_SC : public Architecture { 397 | public: 398 | std::unique_ptr MakeChecker(const Architecture* arch, 399 | const ExecWitness* exec) const override { 400 | return std::unique_ptr(new Checker(arch, exec)); 401 | } 402 | 403 | EventRel ppo(const ExecWitness& ew) const override { 404 | assert(ew.po.Transitive()); 405 | return ew.po.Eval(); 406 | } 407 | 408 | EventRel fences(const ExecWitness& ew) const override { return EventRel(); } 409 | 410 | EventRel prop(const ExecWitness& ew) const override { 411 | return proxy_->ppo(ew) | proxy_->fences(ew) | ew.rf | ew.fr(); 412 | } 413 | 414 | Event::Type EventTypeRead() const override { return Event::kRead; } 415 | 416 | Event::Type EventTypeWrite() const override { return Event::kWrite; } 417 | }; 418 | 419 | class Arch_TSO : public Architecture { 420 | public: 421 | void Clear() override { mfence.Clear(); } 422 | 423 | std::unique_ptr MakeChecker(const Architecture* arch, 424 | const ExecWitness* exec) const override { 425 | return std::unique_ptr(new Checker(arch, exec)); 426 | } 427 | 428 | EventRel ppo(const ExecWitness& ew) const override { 429 | assert(ew.po.Transitive()); 430 | return ew.po.Filter([](const Event& e1, const Event& e2) { 431 | return !e1.AllType(Event::kWrite) || !e2.AllType(Event::kRead); 432 | }); 433 | } 434 | 435 | EventRel fences(const ExecWitness& ew) const override { 436 | if (mfence.empty()) { 437 | return mfence; 438 | } 439 | 440 | // Filter postar by only those events which are possibly relevent. 441 | const auto postar = 442 | ew.po 443 | .Filter([&](const Event& e1, const Event& e2) { 444 | // Only include those where first event is write or second 445 | // is a read, all other are included in po regardless. 446 | return e1.AllType(Event::kWrite) || e2.AllType(Event::kRead); 447 | }) 448 | .set_props(EventRel::kReflexiveClosure); 449 | 450 | return EventRelSeq({postar, mfence, postar}).EvalClear(); 451 | } 452 | 453 | EventRel prop(const ExecWitness& ew) const override { 454 | return proxy_->ppo(ew) | proxy_->fences(ew) | ew.rfe() | ew.fr(); 455 | } 456 | 457 | Event::Type EventTypeRead() const override { return Event::kRead; } 458 | 459 | Event::Type EventTypeWrite() const override { return Event::kWrite; } 460 | 461 | public: 462 | EventRel mfence; 463 | }; 464 | 465 | /** 466 | * ARMv7 as defined in [1] 467 | */ 468 | class Arch_ARMv7 : public Architecture { 469 | public: 470 | Arch_ARMv7() { dd_reg.set_props(EventRel::kTransitiveClosure); } 471 | 472 | void Clear() override { 473 | dd_reg.Clear(); 474 | dsb.Clear(); 475 | dmb.Clear(); 476 | dsb_st.Clear(); 477 | dmb_st.Clear(); 478 | isb.Clear(); 479 | } 480 | 481 | std::unique_ptr MakeChecker(const Architecture* arch, 482 | const ExecWitness* exec) const override { 483 | return std::unique_ptr(new Checker(arch, exec)); 484 | } 485 | 486 | EventRel ppo(const ExecWitness& ew) const override { 487 | assert(ew.po.Transitive()); 488 | assert(dd_reg.SubsetEq(ew.po)); 489 | 490 | // 1. Obtain dependencies 491 | // 492 | EventRel addr, data, ctrl_part; 493 | dd_reg.for_each( 494 | [&addr, &data, &ctrl_part, this](const Event& e1, const Event& e2) { 495 | if (!e1.AnyType(EventTypeRead())) { 496 | return; 497 | } 498 | 499 | if (e2.AnyType(Event::kMemoryOperation)) { 500 | if (e2.AllType(Event::kRegInAddr)) { 501 | addr.Insert(e1, e2); 502 | } 503 | 504 | if (e2.AllType(Event::kRegInData)) { 505 | data.Insert(e1, e2); 506 | } 507 | } 508 | 509 | if (e2.AllType(Event::kBranch)) { 510 | ctrl_part.Insert(e1, e2); 511 | } 512 | }); 513 | 514 | EventRel ctrl = EventRelSeq({ctrl_part, ew.po}).EvalClear(); 515 | EventRel ctrl_cfence = EventRelSeq({ctrl_part, isb}).EvalClear(); 516 | 517 | // 2. Compute helper relations 518 | // 519 | const auto po_loc = ew.po_loc(); 520 | const auto rfe = ew.rfe(); 521 | EventRel dd = addr | data; 522 | EventRel rdw = po_loc & EventRelSeq({ew.fre(), rfe}).EvalClear(); 523 | EventRel detour = po_loc & EventRelSeq({ew.coe(), rfe}).EvalClear(); 524 | EventRel addrpo = EventRelSeq({addr, ew.po}).EvalClear(); 525 | 526 | // 3. Compute ppo 527 | // 528 | // Init 529 | EventRel ci = ctrl_cfence | detour; 530 | EventRel ii = dd | ew.rfi() | rdw; 531 | EventRel cc = dd | ctrl | addrpo | po_loc; 532 | EventRel ic; 533 | 534 | std::size_t total_size = ci.size() + ii.size() + cc.size() + ic.size(); 535 | std::size_t prev_total_size; 536 | 537 | // Fix-point computation 538 | do { 539 | prev_total_size = total_size; 540 | 541 | ci |= 542 | EventRelSeq({ci, ii}).EvalClear() | EventRelSeq({cc, ci}).EvalClear(); 543 | 544 | ii |= ci | EventRelSeq({ic, ci}).EvalClear() | 545 | EventRelSeq({ii, ii}).EvalClear(); 546 | 547 | cc |= ci | EventRelSeq({ci, ic}).EvalClear() | 548 | EventRelSeq({cc, cc}).EvalClear(); 549 | 550 | ic |= ii | cc | EventRelSeq({ic, cc}).EvalClear() | 551 | EventRelSeq({ii, ic}).EvalClear(); 552 | 553 | total_size = ci.size() + ii.size() + cc.size() + ic.size(); 554 | assert(prev_total_size <= total_size); 555 | } while (total_size != prev_total_size); 556 | 557 | EventRel result = ic.Filter([this](const Event& e1, const Event& e2) { 558 | return e1.AnyType(EventTypeRead()) && e2.AnyType(EventTypeWrite()); 559 | }); 560 | result |= ii.Filter([this](const Event& e1, const Event& e2) { 561 | return e1.AnyType(EventTypeRead()) && e2.AnyType(EventTypeRead()); 562 | }); 563 | 564 | return result; 565 | } 566 | 567 | // Ensure fences is transitive 568 | EventRel fences(const ExecWitness& ew) const override { 569 | const auto postar = ew.po.Eval().set_props(EventRel::kReflexiveClosure); 570 | const auto postar_WW = postar.Filter([&](const Event& e1, const Event& e2) { 571 | return e1.AllType(Event::kWrite) && e2.AllType(Event::kWrite); 572 | }); 573 | 574 | auto ff = EventRelSeq({postar, (dmb | dsb), postar}).EvalClear(); 575 | ff |= EventRelSeq({postar_WW, (dmb_st | dsb_st), postar_WW}).EvalClear(); 576 | return ff; 577 | } 578 | 579 | EventRel prop(const ExecWitness& ew) const override { 580 | EventRel hbstar = proxy_->hb(ew) 581 | .set_props(EventRel::kReflexiveTransitiveClosure) 582 | .EvalInplace(); 583 | EventRel A_cumul = EventRelSeq({ew.rfe(), proxy_->fences(ew)}).EvalClear(); 584 | EventRel propbase = 585 | EventRelSeq({(proxy_->fences(ew) | A_cumul), hbstar}).EvalClear(); 586 | 587 | EventRel comstar = ew.com().set_props(EventRel::kReflexiveClosure); 588 | 589 | EventRel result = propbase.Filter([this](const Event& e1, const Event& e2) { 590 | return e1.AnyType(EventTypeWrite()) && e2.AnyType(EventTypeWrite()); 591 | }); 592 | 593 | propbase.set_props(EventRel::kReflexiveTransitiveClosure).EvalInplace(); 594 | result |= 595 | EventRelSeq({comstar, propbase /*star*/, proxy_->fences(ew), hbstar}) 596 | .EvalClear(); 597 | return result; 598 | } 599 | 600 | Event::Type EventTypeRead() const override { return Event::kRead; } 601 | 602 | Event::Type EventTypeWrite() const override { return Event::kWrite; } 603 | 604 | public: 605 | EventRel dd_reg; 606 | EventRel dsb; 607 | EventRel dmb; 608 | EventRel dsb_st; 609 | EventRel dmb_st; 610 | EventRel isb; 611 | }; 612 | 613 | } // namespace cats 614 | } // namespace memconsistency 615 | } // namespace mc2lib 616 | 617 | #endif /* MEMCONSISTENCY_CATS_HPP_ */ 618 | 619 | /* vim: set ts=2 sts=2 sw=2 et : */ 620 | -------------------------------------------------------------------------------- /include/mc2lib/memconsistency/eventsets.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_MEMCONSISTENCY_EVENTSETS_HPP_ 35 | #define MC2LIB_MEMCONSISTENCY_EVENTSETS_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "../sets.hpp" 43 | #include "../types.hpp" 44 | 45 | namespace mc2lib { 46 | 47 | /** 48 | * @namespace mc2lib::memconsistency 49 | * @brief Various formal models for expressing memory consistency semantics. 50 | */ 51 | namespace memconsistency { 52 | 53 | class Iiid { 54 | public: 55 | struct Hash { 56 | typedef std::hash::result_type result_type; 57 | result_type operator()(const Iiid& k) const { 58 | return std::hash()(k.poi); 59 | } 60 | }; 61 | 62 | Iiid() : pid(0), poi(0) {} 63 | 64 | Iiid(types::Pid pid_, types::Poi poi_) : pid(pid_), poi(poi_) {} 65 | 66 | operator std::string() const { 67 | std::ostringstream oss; 68 | oss << "P" << std::setfill('0') << std::setw(2) << pid << ": " 69 | << std::setfill('0') << std::setw(sizeof(types::Poi) * 2) << std::hex 70 | << poi; 71 | return oss.str(); 72 | } 73 | 74 | bool operator==(const Iiid& rhs) const { 75 | return pid == rhs.pid && poi == rhs.poi; 76 | } 77 | 78 | bool operator!=(const Iiid& rhs) const { 79 | return pid != rhs.pid || poi != rhs.poi; 80 | } 81 | 82 | bool operator<(const Iiid& rhs) const { 83 | return pid < rhs.pid || (pid == rhs.pid && poi < rhs.poi); 84 | } 85 | 86 | Iiid& operator++() { 87 | ++poi; 88 | return *this; 89 | } 90 | 91 | Iiid Next() const { return Iiid(pid, poi + 1); } 92 | 93 | Iiid Prev() const { 94 | assert(poi > 0); 95 | return Iiid(pid, poi - 1); 96 | } 97 | 98 | public: 99 | types::Pid pid; 100 | types::Poi poi; 101 | }; 102 | 103 | class Event { 104 | public: 105 | struct Hash { 106 | Iiid::Hash::result_type operator()(const Event& k) const { 107 | return Iiid::Hash()(k.iiid); 108 | } 109 | }; 110 | 111 | typedef std::uint32_t Type; 112 | 113 | // TYPE DEFINITIONS {{{ 114 | 115 | static constexpr Type kNone = 0x00000000; 116 | 117 | // Memory operations 118 | static constexpr Type kRead = 0x00000001; 119 | static constexpr Type kWrite = 0x00000002; 120 | static constexpr Type kAcquire = 0x00000004; 121 | static constexpr Type kRelease = 0x00000008; 122 | static constexpr Type kMemoryOperation = kRead | kWrite | kAcquire | kRelease; 123 | 124 | // Auxiliary attributes 125 | static constexpr Type kRegInAddr = 0x00000010; 126 | static constexpr Type kRegInData = 0x00000020; 127 | static constexpr Type kRegOut = 0x00000040; 128 | static constexpr Type kBranch = 0x00000080; 129 | 130 | // User declared attributes 131 | static constexpr Type kNext = 0x00000100; 132 | 133 | // }}} 134 | 135 | Event() : addr(0), type(kNone) {} 136 | 137 | Event(Type type_, types::Addr addr_, const Iiid& iiid_) 138 | : addr(addr_), type(type_), iiid(iiid_) {} 139 | 140 | operator std::string() const { 141 | std::ostringstream oss; 142 | oss << "[" << static_cast(iiid) << "] "; 143 | 144 | std::ostringstream memtype; 145 | 146 | if (type == kNone) { 147 | memtype << "None"; 148 | } else { 149 | bool found_type = false; 150 | 151 | if (AllType(kRead)) { 152 | memtype << "Read"; 153 | found_type = true; 154 | } 155 | 156 | if (AllType(kWrite)) { 157 | memtype << (found_type ? "|" : "") << "Write"; 158 | found_type = true; 159 | } 160 | 161 | if (AllType(kAcquire)) { 162 | memtype << (found_type ? "|" : "") << "Acquire"; 163 | found_type = true; 164 | } 165 | 166 | if (AllType(kRelease)) { 167 | memtype << (found_type ? "|" : "") << "Release"; 168 | found_type = true; 169 | } 170 | 171 | if (AllType(kRegInAddr)) { 172 | memtype << (found_type ? "|" : "") << "RegInAddr"; 173 | found_type = true; 174 | } 175 | 176 | if (AllType(kRegInData)) { 177 | memtype << (found_type ? "|" : "") << "RegInData"; 178 | found_type = true; 179 | } 180 | 181 | if (AllType(kRegOut)) { 182 | memtype << (found_type ? "|" : "") << "RegOut"; 183 | found_type = true; 184 | } 185 | 186 | if (AllType(kBranch)) { 187 | memtype << (found_type ? "|" : "") << "Branch"; 188 | // found_type = true; 189 | } 190 | } 191 | 192 | oss << std::setfill(' ') << std::setw(8) << memtype.str() << " @ " 193 | << std::hex << addr; 194 | return oss.str(); 195 | } 196 | 197 | bool operator==(const Event& rhs) const { 198 | return type == rhs.type && addr == rhs.addr && iiid == rhs.iiid; 199 | } 200 | 201 | bool operator!=(const Event& rhs) const { 202 | return type != rhs.type || addr != rhs.addr || iiid != rhs.iiid; 203 | } 204 | 205 | // This function in no way says anything about event ordering. Used for 206 | // ordered map. 207 | bool operator<(const Event& rhs) const { return iiid < rhs.iiid; } 208 | 209 | bool AllType(Type type_mask) const { 210 | return sets::AllBitmask(type, type_mask); 211 | } 212 | 213 | bool AnyType(Type type_mask) const { 214 | return sets::AnyBitmask(type, type_mask); 215 | } 216 | 217 | public: 218 | types::Addr addr; 219 | Type type; 220 | Iiid iiid; 221 | }; 222 | 223 | typedef sets::Set> EventSet; 224 | typedef sets::Relation> EventRel; 225 | typedef sets::RelationSeq> EventRelSeq; 226 | 227 | class Error : public std::logic_error { 228 | public: 229 | #if 0 230 | // constructor inheritance not supported by gcc 4.7 231 | using std::logic_error::logic_error; 232 | #else 233 | explicit Error(const std::string& what_arg) : std::logic_error(what_arg) {} 234 | 235 | explicit Error(const char* what_arg) : std::logic_error(what_arg) {} 236 | #endif 237 | }; 238 | 239 | } // namespace memconsistency 240 | } // namespace mc2lib 241 | 242 | #endif /* MEMCONSISTENCY_EVENTSETS_HPP_ */ 243 | 244 | /* vim: set ts=2 sts=2 sw=2 et : */ 245 | -------------------------------------------------------------------------------- /include/mc2lib/memconsistency/model12.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_MEMCONSISTENCY_MODEL12_HPP_ 35 | #define MC2LIB_MEMCONSISTENCY_MODEL12_HPP_ 36 | 37 | #include 38 | 39 | #include "eventsets.hpp" 40 | 41 | namespace mc2lib { 42 | namespace memconsistency { 43 | 44 | /** 45 | * @namespace mc2lib::memconsistency::model12 46 | * @brief Memory consistency model framework based on 2012 FMSD paper. 47 | * 48 | * This memory consistency model framework is based upon [1]. 49 | * 50 | * [1] 51 | * J. Alglave, L. Maranget, S. Sarkar, and P. Sewell. "Fences in weak 52 | * memory models", 2012. 53 | */ 54 | namespace model12 { 55 | 56 | class ExecWitness { 57 | public: 58 | template 59 | EventRel fr(FilterFunc filter_func) const { 60 | EventRel er; 61 | 62 | for (const auto& rf_tuples : rf.get()) { 63 | const auto ws_reach = ws.Reachable(rf_tuples.first); 64 | for (const auto& ws_w : ws_reach.get()) { 65 | for (const auto& rf_r : rf_tuples.second.get()) { 66 | if (filter_func(std::make_pair(rf_tuples.first, rf_r), 67 | std::make_pair(rf_tuples.first, ws_w))) { 68 | er.Insert(rf_r, ws_w); 69 | } 70 | } 71 | } 72 | } 73 | 74 | return er; 75 | } 76 | 77 | EventRel fr() const { 78 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 79 | return true; 80 | }); 81 | } 82 | 83 | EventRel fri() const { 84 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 85 | return t1.second.iiid.pid == t2.second.iiid.pid; 86 | }); 87 | } 88 | 89 | EventRel fre() const { 90 | return fr([](const EventRel::Tuple& t1, const EventRel::Tuple& t2) { 91 | return t1.second.iiid.pid != t2.second.iiid.pid; 92 | }); 93 | } 94 | 95 | EventRel rfi() const { 96 | return rf.Filter([](const Event& e1, const Event& e2) { 97 | return e1.iiid.pid == e2.iiid.pid; 98 | }); 99 | } 100 | 101 | EventRel rfe() const { 102 | return rf.Filter([](const Event& e1, const Event& e2) { 103 | return e1.iiid.pid != e2.iiid.pid; 104 | }); 105 | } 106 | 107 | EventRel wsi() const { 108 | return ws.Filter([](const Event& e1, const Event& e2) { 109 | return e1.iiid.pid == e2.iiid.pid; 110 | }); 111 | } 112 | 113 | EventRel wse() const { 114 | return ws.Filter([](const Event& e1, const Event& e2) { 115 | return e1.iiid.pid != e2.iiid.pid; 116 | }); 117 | } 118 | 119 | EventRel com() const { return rf | ws | fr(); } 120 | 121 | EventRel po_loc() const { 122 | return po.Filter( 123 | [](const Event& e1, const Event& e2) { return e1.addr == e2.addr; }); 124 | } 125 | 126 | void Clear() { 127 | events.Clear(); 128 | po.Clear(); 129 | dp.Clear(); 130 | rf.Clear(); 131 | ws.Clear(); 132 | } 133 | 134 | public: 135 | EventSet events; 136 | EventRel po; 137 | EventRel dp; 138 | EventRel rf; 139 | EventRel ws; 140 | }; 141 | 142 | class Checker; 143 | 144 | class Architecture { 145 | public: 146 | virtual ~Architecture() {} 147 | 148 | virtual void Clear() {} 149 | 150 | /** 151 | * Creates a checker compatible with this Architecture. 152 | */ 153 | virtual std::unique_ptr MakeChecker( 154 | const Architecture* arch, const ExecWitness* exec) const = 0; 155 | 156 | virtual EventRel ppo(const ExecWitness& ew) const = 0; 157 | virtual EventRel grf(const ExecWitness& ew) const = 0; 158 | virtual EventRel ab(const ExecWitness& ew) const = 0; 159 | 160 | virtual EventRel ghb(const ExecWitness& ew) const { 161 | return ew.ws | ew.fr() | ppo(ew) | grf(ew) | ab(ew); 162 | } 163 | 164 | /** 165 | * Should return the mask of all types that are classed as read. 166 | */ 167 | virtual Event::Type EventTypeRead() const = 0; 168 | 169 | /** 170 | * Should return the mask of all types that are classed as write. 171 | */ 172 | virtual Event::Type EventTypeWrite() const = 0; 173 | }; 174 | 175 | class Checker { 176 | public: 177 | Checker(const Architecture* arch, const ExecWitness* exec) 178 | : arch_(arch), exec_(exec) {} 179 | 180 | virtual ~Checker() {} 181 | 182 | virtual void wf_rf() const { 183 | EventSet reads; 184 | 185 | for (const auto& tuples : exec_->rf.get()) { 186 | if (!tuples.first.AnyType(arch_->EventTypeWrite())) { 187 | throw Error("WF_RF_NOT_FROM_WRITE"); 188 | } 189 | 190 | for (const auto& e : tuples.second.get()) { 191 | if (!e.AnyType(arch_->EventTypeRead()) || tuples.first.addr != e.addr) { 192 | throw Error("WF_RF_NOT_SAME_LOC"); 193 | } 194 | 195 | // For every read, there exists only 1 source! 196 | if (reads.Contains(e)) { 197 | throw Error("WF_RF_MULTI_SOURCE"); 198 | } 199 | reads.Insert(e); 200 | } 201 | } 202 | } 203 | 204 | virtual void wf_ws() const { 205 | std::unordered_set addrs; 206 | 207 | // Assert writes ordered captured in ws are to the same location. 208 | for (const auto& tuples : exec_->ws.get()) { 209 | addrs.insert(tuples.first.addr); 210 | 211 | for (const auto& e : tuples.second.get()) { 212 | if (tuples.first.addr != e.addr) { 213 | throw Error("WF_WS_NOT_SAME_LOC"); 214 | } 215 | } 216 | } 217 | 218 | auto writes = exec_->events.Filter( 219 | [&](const Event& e) { return e.AnyType(arch_->EventTypeWrite()); }); 220 | if (!exec_->ws.StrictPartialOrder(writes)) { 221 | throw Error("WF_WS_NOT_STRICT_PARTIAL_ORDER"); 222 | } 223 | 224 | for (const auto& addr : addrs) { 225 | auto same_addr_writes = 226 | writes.Filter([&](const Event& e) { return e.addr == addr; }); 227 | if (!exec_->ws.ConnexOn(same_addr_writes)) { 228 | throw Error("WF_WS_NOT_CONNEX"); 229 | } 230 | } 231 | } 232 | 233 | virtual void wf() const { 234 | wf_rf(); 235 | wf_ws(); 236 | } 237 | 238 | virtual bool uniproc(EventRel::Path* cyclic = nullptr) const { 239 | return (exec_->com() | exec_->po_loc()).Acyclic(cyclic); 240 | } 241 | 242 | virtual bool thin(EventRel::Path* cyclic = nullptr) const { 243 | return (exec_->rf | exec_->dp).Acyclic(cyclic); 244 | } 245 | 246 | virtual bool check_exec(EventRel::Path* cyclic = nullptr) const { 247 | return arch_->ghb(*exec_).Acyclic(cyclic); 248 | } 249 | 250 | virtual void valid_exec(EventRel::Path* cyclic = nullptr) const { 251 | wf(); 252 | 253 | if (!uniproc(cyclic)) { 254 | throw Error("UNIPROC"); 255 | } 256 | 257 | if (!thin(cyclic)) { 258 | throw Error("THIN"); 259 | } 260 | 261 | if (!check_exec(cyclic)) { 262 | throw Error("CHECK_EXEC"); 263 | } 264 | } 265 | 266 | protected: 267 | const Architecture* arch_; 268 | const ExecWitness* exec_; 269 | }; 270 | 271 | /* 272 | ============================= 273 | Some common memory models. 274 | ============================= 275 | */ 276 | 277 | class Arch_SC : public Architecture { 278 | public: 279 | std::unique_ptr MakeChecker(const Architecture* arch, 280 | const ExecWitness* exec) const override { 281 | return std::unique_ptr(new Checker(arch, exec)); 282 | } 283 | 284 | EventRel ppo(const ExecWitness& ew) const override { 285 | assert(ew.po.Transitive()); 286 | return ew.po.Eval(); 287 | } 288 | 289 | EventRel grf(const ExecWitness& ew) const override { return ew.rf; } 290 | 291 | EventRel ab(const ExecWitness& ew) const override { return EventRel(); } 292 | 293 | Event::Type EventTypeRead() const override { return Event::kRead; } 294 | 295 | Event::Type EventTypeWrite() const override { return Event::kWrite; } 296 | }; 297 | 298 | class Arch_TSO : public Architecture { 299 | public: 300 | void Clear() override { mfence.Clear(); } 301 | 302 | std::unique_ptr MakeChecker(const Architecture* arch, 303 | const ExecWitness* exec) const override { 304 | return std::unique_ptr(new Checker(arch, exec)); 305 | } 306 | 307 | EventRel ppo(const ExecWitness& ew) const override { 308 | assert(ew.po.Transitive()); 309 | return ew.po.Filter([](const Event& e1, const Event& e2) { 310 | return !e1.AllType(Event::kWrite) || !e2.AllType(Event::kRead); 311 | }); 312 | } 313 | 314 | EventRel grf(const ExecWitness& ew) const override { return ew.rfe(); } 315 | 316 | EventRel ab(const ExecWitness& ew) const override { 317 | if (mfence.empty()) { 318 | return mfence; 319 | } 320 | 321 | // Filter postar by only those events which are possibly relevent. 322 | const auto postar = 323 | ew.po 324 | .Filter([&](const Event& e1, const Event& e2) { 325 | // Only include those where first event is write or second 326 | // is a read, all other are included in po regardless. 327 | return e1.AllType(Event::kWrite) || e2.AllType(Event::kRead); 328 | }) 329 | .set_props(EventRel::kReflexiveClosure); 330 | 331 | return EventRelSeq({postar, mfence, postar}).EvalClear(); 332 | } 333 | 334 | Event::Type EventTypeRead() const override { return Event::kRead; } 335 | 336 | Event::Type EventTypeWrite() const override { return Event::kWrite; } 337 | 338 | public: 339 | EventRel mfence; 340 | }; 341 | 342 | } // namespace model12 343 | } // namespace memconsistency 344 | } // namespace mc2lib 345 | 346 | #endif /* MEMCONSISTENCY_MODEL12_HPP_ */ 347 | 348 | /* vim: set ts=2 sts=2 sw=2 et : */ 349 | -------------------------------------------------------------------------------- /include/mc2lib/simplega.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_SIMPLEGA_HPP_ 35 | #define MC2LIB_SIMPLEGA_HPP_ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | namespace mc2lib { 49 | 50 | /** 51 | * @namespace mc2lib::simplega 52 | * @brief Simple Genetic Algorithm library. 53 | */ 54 | namespace simplega { 55 | 56 | /** 57 | * @namespace mc2lib::simplega::evolve 58 | * @brief Example CrossoverMutateFunc implementations. 59 | */ 60 | namespace evolve { 61 | 62 | /** 63 | * Cut & splice. If template parameter one_point is set, turns this into 64 | * one-point crossover. 65 | * 66 | * Assumes that GenomeT implements get() and uses a vector-like structure to 67 | * represent the genome. 68 | */ 69 | template 71 | inline void CutSpliceMutate(URNG& urng, const GenomeT& mate1, 72 | const GenomeT& mate2, float mutation_rate, 73 | C* container) { 74 | assert(!mate1.get().empty()); 75 | assert(!mate2.get().empty()); 76 | 77 | std::uniform_int_distribution dist1(0, mate1.get().size() - 1); 78 | std::uniform_int_distribution dist2(0, mate2.get().size() - 1); 79 | 80 | const std::size_t cut1 = dist1(urng); 81 | const std::size_t cut2 = one_point ? cut1 : dist2(urng); 82 | 83 | // a[0:cut_a] + b[cut_b:] 84 | auto cut_and_splice = [](const GenomeT& a, const GenomeT& b, 85 | std::size_t cut_a, std::size_t cut_b) { 86 | auto result = a.get(); 87 | auto ita = result.begin(); 88 | std::advance(ita, cut_a); 89 | result.erase(ita, result.end()); 90 | auto itb = b.get().begin(); 91 | std::advance(itb, cut_b); 92 | result.insert(result.end(), itb, b.get().end()); 93 | return GenomeT(a, b, result); 94 | }; 95 | 96 | // child1 = mate1[0:cut1] + mate2[cut2:] 97 | GenomeT child1 = cut_and_splice(mate1, mate2, cut1, cut2); 98 | if (child1.get().size()) { 99 | child1.Mutate(mutation_rate); 100 | container->push_back(std::move(child1)); 101 | } 102 | 103 | if (!theone) { 104 | // child2 = mate2[0:cut2] + mate1[cut1:] 105 | GenomeT child2 = cut_and_splice(mate2, mate1, cut2, cut1); 106 | if (child2.get().size()) { 107 | child2.Mutate(mutation_rate); 108 | container->push_back(std::move(child2)); 109 | } 110 | } 111 | } 112 | 113 | } // namespace evolve 114 | 115 | /** 116 | * @brief Simple Genome interface. 117 | * 118 | * Use as a baseclass for genome implementations, but this is optional, as long 119 | * as the interface is implemented (see GenePool). 120 | * 121 | * Note that this class is virtual, as it is possible that we could have a 122 | * heterogeneous collection of genomes, all based off of the same baseclass, 123 | * e.g., where a specialized crossover_mutate operator yields children of 124 | * different classes. 125 | * 126 | * @tparam T The type of genes in this Genome. 127 | */ 128 | template 129 | class Genome { 130 | public: 131 | typedef std::vector Container; 132 | 133 | /** 134 | * @brief Default constructor. 135 | * 136 | * Yields an empty genome. 137 | */ 138 | Genome() {} 139 | 140 | /** 141 | * @brief Converting constructor. 142 | * 143 | * @param g A raw vector of type T genes forming this new Genome. 144 | */ 145 | explicit Genome(Container g) : genome_(std::move(g)) {} 146 | 147 | virtual ~Genome() {} 148 | 149 | /** 150 | * @brief Read-only genome accessor. 151 | * 152 | * @return Const reference to vector of genes. 153 | */ 154 | const Container& get() const { return genome_; } 155 | 156 | /** 157 | * @brief Modifiable genome accessor. 158 | * 159 | * @return Pointer to vector of genes. 160 | */ 161 | Container* get_ptr() { return &genome_; } 162 | 163 | /** 164 | * @brief Less than comparison operator. 165 | * 166 | * Use to rank genomes from best fitness to worst fitness. Assumes higher 167 | * fitness means better. 168 | * 169 | * @return True if this instance (lhs) is fitter than rhs, false otherwise. 170 | */ 171 | virtual bool operator<(const Genome& rhs) const { 172 | return Fitness() > rhs.Fitness(); 173 | } 174 | 175 | /** 176 | * @brief Mutate this Genome. 177 | * 178 | * @param rate Mutation rate. 179 | */ 180 | virtual void Mutate(float rate) = 0; 181 | 182 | /** 183 | * @brief Fitness accessor. 184 | * 185 | * @return Current fitness. 186 | */ 187 | virtual float Fitness() const = 0; 188 | 189 | /** 190 | * @brief Converting operator to float. 191 | * 192 | * As this is also used for the roulette selection, the assumption is that 193 | * higher fitness means better. 194 | */ 195 | virtual operator float() const { return Fitness(); } 196 | 197 | /** 198 | * @brief Converting operator to std::string. 199 | */ 200 | virtual operator std::string() const { 201 | std::ostringstream oss; 202 | oss << "["; 203 | bool first = true; 204 | for (const auto& v : genome_) { 205 | oss << (first ? "" : ", ") << v; 206 | first = false; 207 | } 208 | oss << "]"; 209 | return oss.str(); 210 | } 211 | 212 | protected: 213 | /** 214 | * @brief Raw genome of individual genes of T. 215 | */ 216 | Container genome_; 217 | }; 218 | 219 | /** 220 | * @brief Helper to manages and evolve a populates. 221 | * 222 | * Helps manage and evolve a population, and provides various primitives for 223 | * implementing selection operators. 224 | */ 225 | template 226 | class GenePool { 227 | public: 228 | /** 229 | * Using a list for the population pool, as this permits O(1) removal of 230 | * elements. 231 | */ 232 | typedef std::list Population; 233 | typedef std::vector Selection; 234 | 235 | explicit GenePool(std::size_t target_population_size = 80, 236 | float mutation_rate = 0.02f) 237 | : target_population_size_(target_population_size), 238 | mutation_rate_(mutation_rate), 239 | steps_(0) { 240 | // mutation rate is a percentage 241 | if (mutation_rate_ > 1.0f) { 242 | mutation_rate_ = 1.0f; 243 | } else if (mutation_rate_ < 0.0f) { 244 | mutation_rate_ = 0.0f; 245 | } 246 | 247 | // Initialize with defaults (e.g. random) 248 | population_.resize(target_population_size_); 249 | } 250 | 251 | explicit GenePool(Population population, float mutation_rate = 0.02f) 252 | : target_population_size_(population.size()), 253 | mutation_rate_(mutation_rate), 254 | steps_(0), 255 | population_(std::move(population)) {} 256 | 257 | explicit GenePool(Selection selection, float mutation_rate = 0.02f) 258 | : target_population_size_(selection.size()), 259 | mutation_rate_(mutation_rate), 260 | steps_(0) { 261 | for (const auto& gptr : selection) { 262 | population_.push_back(*gptr); 263 | } 264 | } 265 | 266 | operator std::string() const { 267 | std::ostringstream oss; 268 | oss << "{ "; 269 | 270 | bool first = true; 271 | for (const auto& genome : population_) { 272 | oss << (first ? "" : ", ") << static_cast(genome); 273 | first = false; 274 | } 275 | 276 | oss << " }"; 277 | return oss.str(); 278 | } 279 | 280 | const Population& get() const { return population_; } 281 | 282 | Population* get_ptr() { return &population_; } 283 | 284 | float mutation_rate() const { return mutation_rate_; } 285 | 286 | void set_mutation_rate(float mutation_rate) { 287 | mutation_rate_ = mutation_rate; 288 | } 289 | 290 | std::size_t target_population_size() const { return target_population_size_; } 291 | 292 | std::size_t population_size() const { return population_.size(); } 293 | 294 | std::size_t steps() const { return steps_; } 295 | 296 | float AverageFitness() const { 297 | float result = 0.0f; 298 | for (const auto& g : population_) { 299 | result += g.Fitness(); 300 | } 301 | return result / population_.size(); 302 | } 303 | 304 | float WorstFitness() const { 305 | return std::max_element(population_.begin(), population_.end())->Fitness(); 306 | } 307 | 308 | float BestFitness() const { 309 | return std::min_element(population_.begin(), population_.end())->Fitness(); 310 | } 311 | 312 | /** 313 | * Sorts (in-place) the population based on fitness. 314 | */ 315 | void Sort() { population_.Sort(); } 316 | 317 | const GenomeT& SelectBest() const { 318 | return *std::min_element(population_.begin(), population_.end()); 319 | } 320 | 321 | /** 322 | * @return Entire population as Selection. 323 | */ 324 | Selection SelectAll() { 325 | Selection result; 326 | result.reserve(population_.size()); 327 | for (GenomeT& g : population_) { 328 | result.push_back(&g); 329 | } 330 | return result; 331 | } 332 | 333 | /** 334 | * @return Random subset of population, using distribution dist to select. 335 | */ 336 | template 337 | Selection SelectDist(URNG& urng, DIST& dist, std::size_t count) { 338 | assert(population_.size() >= count); 339 | 340 | std::unordered_set used; 341 | Selection result; 342 | result.reserve(count); 343 | 344 | while (result.size() < count) { 345 | std::size_t idx = dist(urng); 346 | if (used.find(idx) != used.end()) { 347 | continue; 348 | } 349 | 350 | auto it = population_.begin(); 351 | std::advance(it, idx); 352 | result.push_back(&(*it)); 353 | used.insert(idx); 354 | } 355 | 356 | return result; 357 | } 358 | 359 | /** 360 | * @return Random subset of population, where a higher fitness means an 361 | * individual is more likely to be selected. 362 | */ 363 | template 364 | Selection SelectRoulette(URNG& urng, std::size_t count) { 365 | std::discrete_distribution dist(population_.begin(), 366 | population_.end()); 367 | return SelectDist(urng, dist, count); 368 | } 369 | 370 | /** 371 | * @return Random subset of the population, where each individual has the 372 | * same probability of being selected. 373 | */ 374 | template 375 | Selection SelectUniform(URNG& urng, std::size_t count) { 376 | std::uniform_int_distribution dist(0, population_.size() - 1); 377 | return SelectDist(urng, dist, count); 378 | } 379 | 380 | /** 381 | * Sorts (in-place) a Selection based on fitness. 382 | */ 383 | void SelectionSort(Selection* v) { 384 | std::sort(v->begin(), v->end(), [](const GenomeT* lhs, const GenomeT* rhs) { 385 | return (*lhs) < (*rhs); 386 | }); 387 | } 388 | 389 | /** 390 | * Takes a selection and mates the initial selection[:mates] individuals. 391 | * 392 | * The elements in selection also determine which individuals are to be 393 | * removed from the population; selection[keep:] are removed from population 394 | * (can e.g. be used for elitism). 395 | */ 396 | template 397 | void Step(URNG& urng, CrossoverMutateFunc crossover_mutate, 398 | const Selection& selection, std::size_t mates, std::size_t keep = 0, 399 | std::size_t mate1_stride = 1, std::size_t mate2_stride = 1) { 400 | assert(selection.size() >= mates); 401 | assert(selection.size() >= keep); 402 | 403 | std::size_t replace = selection.size() - keep; 404 | assert(replace > 0); 405 | 406 | const std::size_t target_population_size = 407 | target_population_size_ + replace; 408 | 409 | // Add offspring 410 | for (std::size_t i = 0; i < mates; i += mate1_stride) { 411 | const auto mate1 = selection[i]; 412 | 413 | // j = i: avoid mating 2 individuals twice 414 | for (std::size_t j = i + 1; j < mates; j += mate2_stride) { 415 | const auto mate2 = selection[j]; 416 | 417 | crossover_mutate(urng, *mate1, *mate2, mutation_rate_, &population_); 418 | 419 | if (population_.size() >= target_population_size) { 420 | goto target_reached; 421 | } 422 | } 423 | } 424 | target_reached: 425 | 426 | // Remove selection[keep:] 427 | auto selection_start = selection.begin(); 428 | std::advance(selection_start, keep); 429 | 430 | for (auto it = population_.begin(); 431 | it != population_.end() && replace > 0;) { 432 | const GenomeT* val = &(*it); 433 | auto match = std::find(selection_start, selection.end(), val); 434 | 435 | if (match != selection.end()) { 436 | if (match == selection_start) { 437 | ++selection_start; 438 | } 439 | 440 | it = population_.erase(it); 441 | --replace; 442 | } else { 443 | ++it; 444 | } 445 | } 446 | 447 | assert(population_.size() >= target_population_size_); 448 | // The population might be larger than the target, if crossover 449 | // generates more than one offspring. 450 | 451 | ++steps_; 452 | } 453 | 454 | protected: 455 | std::size_t target_population_size_; 456 | float mutation_rate_; 457 | std::size_t steps_; 458 | Population population_; 459 | }; 460 | 461 | } // namespace simplega 462 | } // namespace mc2lib 463 | 464 | #endif /* SIMPLEGA_HPP_ */ 465 | 466 | /* vim: set ts=2 sts=2 sw=2 et : */ 467 | -------------------------------------------------------------------------------- /include/mc2lib/types.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016, Marco Elver 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * * Neither the name of the software nor the names of its contributors 18 | * may be used to endorse or promote products derived from this 19 | * software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef MC2LIB_TYPES_HPP_ 35 | #define MC2LIB_TYPES_HPP_ 36 | 37 | #include 38 | 39 | namespace mc2lib { 40 | 41 | /** 42 | * @namespace mc2lib::types 43 | * @brief Common types. 44 | */ 45 | namespace types { 46 | 47 | /** 48 | * @brief Template class of common types, permitting specialization. 49 | * 50 | * Can be specialized to declare custom types without overwriting types.hh; 51 | * however, this appraoch depends on user specializing before including any 52 | * mc2lib header file. Do *not* define more than 1 per project! 53 | */ 54 | template 55 | struct Types { 56 | typedef std::uint64_t Addr; 57 | typedef std::uint16_t Pid; 58 | typedef std::uint16_t Poi; 59 | typedef Addr InstPtr; 60 | typedef std::uint8_t WriteID; 61 | }; 62 | 63 | /** 64 | * @brief Address type. 65 | */ 66 | typedef typename Types::Addr Addr; 67 | 68 | /** 69 | * @brief Processor/thread ID type. 70 | */ 71 | typedef typename Types::Pid Pid; 72 | 73 | /** 74 | * @brief Program order index type. 75 | */ 76 | typedef typename Types::Poi Poi; 77 | 78 | /** 79 | * @brief Instruction pointer type. 80 | */ 81 | typedef typename Types::InstPtr InstPtr; 82 | 83 | /** 84 | * @brief Write ID type. 85 | */ 86 | typedef typename Types::WriteID WriteID; 87 | 88 | } // namespace types 89 | } // namespace mc2lib 90 | 91 | #endif /* MC2LIB_TYPES_HPP_ */ 92 | 93 | /* vim: set ts=2 sts=2 sw=2 et : */ 94 | -------------------------------------------------------------------------------- /src/test_codegen_armv7.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/codegen/ops/armv7.hpp" 5 | #include "mc2lib/codegen/rit.hpp" 6 | #include "mc2lib/memconsistency/cats.hpp" 7 | 8 | #include 9 | 10 | using namespace mc2lib; 11 | using namespace mc2lib::codegen; 12 | using namespace mc2lib::memconsistency; 13 | 14 | TEST(CodeGen, ARMv7_Short) { 15 | std::default_random_engine urng(42); 16 | 17 | cats::ExecWitness ew; 18 | cats::Arch_ARMv7 arch; 19 | 20 | armv7::RandomFactory factory(0, 1, 0xccc0, 0xccca); 21 | RandInstTest rit( 22 | urng, &factory, 200); 23 | 24 | auto threads = [&rit]() { 25 | auto result = rit.threads(); 26 | EXPECT_EQ(result.size(), 2); 27 | EXPECT_EQ(threads_size(result), rit.get().size()); 28 | return result; 29 | }; 30 | 31 | Compiler compiler( 32 | std::unique_ptr(new EvtStateCats(&ew, &arch)), threads()); 33 | 34 | constexpr std::size_t MAX_CODE_SIZE = 4096 / sizeof(std::uint16_t); 35 | std::uint16_t code[MAX_CODE_SIZE]; 36 | 37 | std::size_t emit_len = compiler.Emit(0, 0xffff, code, sizeof(code)); 38 | ASSERT_NE(emit_len, 0); 39 | emit_len = compiler.Emit(1, 0, code, sizeof(code)); 40 | ASSERT_NE(emit_len, 0); 41 | 42 | #ifdef OUTPUT_BIN_TO_TMP 43 | std::fill(code + (emit_len / sizeof(std::uint16_t)), code + MAX_CODE_SIZE, 44 | 0xbf00); 45 | auto f = fopen("/tmp/mc2lib-armv7-test.bin", "wb"); 46 | fwrite(code, sizeof(code), 1, f); 47 | fclose(f); 48 | #endif 49 | } 50 | 51 | // Should be able to handle exhausting write-ids gracefully. 52 | TEST(CodeGen, ARMv7_Exhaust) { 53 | std::default_random_engine urng(42); 54 | 55 | cats::ExecWitness ew; 56 | cats::Arch_ARMv7 arch; 57 | 58 | armv7::RandomFactory factory(0, 1, 0xbeef, 0xfeed); 59 | RandInstTest rit( 60 | urng, &factory, 1234); 61 | 62 | Compiler compiler( 63 | std::unique_ptr(new EvtStateCats(&ew, &arch)), 64 | rit.threads()); 65 | 66 | constexpr std::size_t MAX_CODE_SIZE = 4096 * 2; 67 | char code[MAX_CODE_SIZE]; 68 | 69 | ASSERT_NE(0, compiler.Emit(0, 0, code, sizeof(code))); 70 | ASSERT_NE(0, compiler.Emit(1, MAX_CODE_SIZE << 1, code, sizeof(code))); 71 | } 72 | 73 | TEST(CodeGen, ARMv7_SC_PER_LOCATION) { 74 | std::vector threads = { 75 | // p0 76 | std::make_shared(0xf0, 0), // @0x0a 77 | std::make_shared(0xf0, armv7::Backend::r1, 0), // @0x14 78 | std::make_shared(0xf0, armv7::Backend::r2, 0), // @0x1e 79 | std::make_shared(0xf1, armv7::Backend::r3, 0), // @0x28 80 | std::make_shared(0xf1, armv7::Backend::r4, 0), // @0x32 81 | 82 | // p1 83 | std::make_shared(0xf1, 1), 84 | }; 85 | 86 | cats::ExecWitness ew; 87 | cats::Arch_ARMv7 arch; 88 | Compiler compiler( 89 | std::unique_ptr(new EvtStateCats(&ew, &arch)), 90 | ExtractThreads(&threads)); 91 | 92 | char code[128]; 93 | 94 | ASSERT_NE(0, compiler.Emit(0, 0, code, sizeof(code))); 95 | ASSERT_NE(0, compiler.Emit(1, 0xffff, code, sizeof(code))); 96 | 97 | auto checker = arch.MakeChecker(&arch, &ew); 98 | ew.po.set_props(mc::EventRel::kTransitiveClosure); 99 | ew.co.set_props(mc::EventRel::kTransitiveClosure); 100 | 101 | types::WriteID wid = 0; 102 | ASSERT_TRUE(compiler.UpdateObs(0x0a, 0, 0xf0, &wid, 1)); 103 | ASSERT_TRUE(compiler.UpdateObs(0x14, 0, 0xf0, &wid, 1)); 104 | ASSERT_FALSE(checker->sc_per_location()); 105 | 106 | // Check update works. 107 | wid = 1; 108 | ASSERT_TRUE(compiler.UpdateObs(0x14, 0, 0xf0, &wid, 1)); 109 | ASSERT_TRUE(checker->sc_per_location()); 110 | 111 | // Read-from external 112 | wid = 0; 113 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x0a, 0, 0xf1, &wid, 1)); 114 | wid = 2; 115 | ASSERT_TRUE(compiler.UpdateObs(0x28, 0, 0xf1, &wid, 1)); 116 | wid = 0; 117 | ASSERT_TRUE(compiler.UpdateObs(0x32, 0, 0xf1, &wid, 1)); 118 | ASSERT_FALSE(checker->sc_per_location()); 119 | 120 | // update 121 | wid = 2; 122 | ASSERT_TRUE(compiler.UpdateObs(0x32, 0, 0xf1, &wid, 1)); 123 | ASSERT_TRUE(checker->sc_per_location()); 124 | 125 | ASSERT_TRUE(checker->no_thin_air()); 126 | ASSERT_TRUE(checker->observation()); 127 | ASSERT_TRUE(checker->propagation()); 128 | } 129 | 130 | TEST(CodeGen, ARMv7_OBSERVATION) { 131 | std::vector threads = { 132 | // p0 133 | std::make_shared(0xf0, armv7::Backend::r1, 0), // @0x08 134 | std::make_shared(0xf1, armv7::Backend::r3, 0), // @0x12 135 | std::make_shared(0xf1, armv7::Backend::r2, 136 | armv7::Backend::r1, 0), // @0x1e 137 | 138 | // p1 139 | std::make_shared(1), // check boundary condition 140 | std::make_shared(0xf1, 1), // 0x0e 141 | std::make_shared(1, 1), // 142 | std::make_shared(1), // 143 | std::make_shared(1, 1), // 144 | std::make_shared(0xfa, 1), // 0x20 145 | std::make_shared(0xf0, 1), // 0x2c 146 | std::make_shared(1), // check boundary condition 147 | }; 148 | 149 | cats::ExecWitness ew; 150 | cats::Arch_ARMv7 arch; 151 | Compiler compiler( 152 | std::unique_ptr(new EvtStateCats(&ew, &arch)), 153 | ExtractThreads(&threads)); 154 | 155 | char code[128]; 156 | 157 | ASSERT_NE(0, compiler.Emit(0, 0, code, sizeof(code))); 158 | ASSERT_NE(0, compiler.Emit(1, 0xffff, code, sizeof(code))); 159 | 160 | auto checker = arch.MakeChecker(&arch, &ew); 161 | ew.po.set_props(mc::EventRel::kTransitiveClosure); 162 | ew.co.set_props(mc::EventRel::kTransitiveClosure); 163 | 164 | types::WriteID wid = 0; 165 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x0e, 0, 0xf1, &wid, 1)); 166 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x22, 0, 0xfa, &wid, 1)); 167 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x2e, 0, 0xf0, &wid, 1)); 168 | 169 | wid = 3; 170 | ASSERT_TRUE(compiler.UpdateObs(0x08, 0, 0xf0, &wid, 1)); 171 | wid = 0; 172 | ASSERT_TRUE(compiler.UpdateObs(0x12, 0, 0xf1, &wid, 1)); 173 | ASSERT_TRUE(compiler.UpdateObs(0x1e, 0, 0xf1, &wid, 1)); 174 | ASSERT_FALSE(checker->observation()); 175 | 176 | wid = 1; 177 | ASSERT_TRUE(compiler.UpdateObs(0x1e, 0, 0xf1, &wid, 1)); 178 | ASSERT_TRUE(checker->observation()); 179 | 180 | ASSERT_TRUE(checker->no_thin_air()); 181 | ASSERT_TRUE(checker->sc_per_location()); 182 | ASSERT_TRUE(checker->propagation()); 183 | } 184 | -------------------------------------------------------------------------------- /src/test_codegen_x86_64.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/codegen/ops/x86_64.hpp" 5 | #include "mc2lib/codegen/rit.hpp" 6 | #include "mc2lib/memconsistency/cats.hpp" 7 | 8 | #include 9 | 10 | using namespace mc2lib; 11 | using namespace mc2lib::codegen; 12 | using namespace mc2lib::memconsistency; 13 | 14 | TEST(CodeGen, X86_64) { 15 | std::default_random_engine urng(1238); 16 | 17 | cats::ExecWitness ew; 18 | cats::Arch_TSO arch; 19 | 20 | strong::RandomFactory factory(0, 1, 0xccc0, 0xccca); 21 | RandInstTest rit( 22 | urng, &factory, 150); 23 | 24 | auto threads = [&rit]() { 25 | auto result = rit.threads(); 26 | EXPECT_EQ(result.size(), 2); 27 | EXPECT_EQ(threads_size(result), rit.get().size()); 28 | return result; 29 | }; 30 | 31 | Compiler compiler( 32 | std::unique_ptr(new EvtStateCats(&ew, &arch)), threads()); 33 | 34 | char code[1024]; 35 | 36 | std::size_t emit_len = compiler.Emit(0, 0xffff, code, sizeof(code)); 37 | ASSERT_NE(emit_len, 0); 38 | ASSERT_TRUE(compiler.IpToOp(0xffff - 1) == nullptr); 39 | ASSERT_TRUE(compiler.IpToOp(0xffff + emit_len) == nullptr); 40 | ASSERT_TRUE(compiler.IpToOp(0x1234) == nullptr); 41 | ASSERT_TRUE(compiler.IpToOp(0xffff) != nullptr); 42 | ASSERT_TRUE(compiler.IpToOp(0xffff + emit_len - 1) != nullptr); 43 | 44 | emit_len = compiler.Emit(1, 0, code, sizeof(code)); 45 | ASSERT_NE(emit_len, 0); 46 | 47 | #ifdef OUTPUT_BIN_TO_TMP 48 | memset(code + emit_len, 0x90, sizeof(code) - emit_len); 49 | auto f = fopen("/tmp/mc2lib-test1.bin", "wb"); 50 | fwrite(code, sizeof(code), 1, f); 51 | fclose(f); 52 | #endif 53 | } 54 | 55 | TEST(CodeGen, X86_64_SC_PER_LOCATION) { 56 | std::vector threads = { 57 | // p0 58 | std::make_shared(0xf0, 0), // @0x0 59 | std::make_shared(0xf0, 0), // @0x8 60 | std::make_shared(0xf1, 0), // 0x17 61 | 62 | // p1 63 | std::make_shared(0xf1, 1), // 0x0 64 | }; 65 | 66 | cats::ExecWitness ew; 67 | cats::Arch_TSO arch; 68 | Compiler compiler( 69 | std::unique_ptr(new EvtStateCats(&ew, &arch)), 70 | ExtractThreads(&threads)); 71 | 72 | char code[128]; 73 | 74 | ASSERT_NE(0, compiler.Emit(0, 0, code, sizeof(code))); 75 | ASSERT_NE(0, compiler.Emit(1, 0xffff, code, sizeof(code))); 76 | 77 | auto checker = arch.MakeChecker(&arch, &ew); 78 | ew.po.set_props(mc::EventRel::kTransitiveClosure); 79 | ew.co.set_props(mc::EventRel::kTransitiveClosure); 80 | 81 | types::WriteID wid = 0; 82 | ASSERT_TRUE(compiler.UpdateObs(0x0, 0, 0xf0, &wid, 1)); 83 | ASSERT_TRUE(compiler.UpdateObs(0x8, 0, 0xf0, &wid, 1)); 84 | ASSERT_FALSE(checker->sc_per_location()); 85 | 86 | wid = 0x1; // check replacement/update works 87 | ASSERT_TRUE(compiler.UpdateObs(0x8, 0, 0xf0, &wid, 1)); 88 | ASSERT_TRUE(checker->sc_per_location()); 89 | 90 | // Check atomic works 91 | wid = 0; 92 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x0, 0, 0xf1, &wid, 1)); 93 | ASSERT_TRUE(compiler.UpdateObs(0x17, 0, 0xf1, &wid, 1)); 94 | 95 | try { 96 | wid = 3; 97 | compiler.UpdateObs(0x17, 1, 0xf1, &wid, 1); 98 | FAIL(); 99 | } catch (const Error& e) { 100 | ASSERT_NE(std::string(e.what()).find("NOT ATOMIC"), std::string::npos); 101 | } 102 | 103 | wid = 3; // restart atomic 104 | ASSERT_TRUE(compiler.UpdateObs(0x17, 0, 0xf1, &wid, 1)); 105 | ASSERT_TRUE(compiler.UpdateObs(0x17, 1, 0xf1, &wid, 1)); 106 | 107 | ASSERT_TRUE(checker->sc_per_location()); 108 | ASSERT_TRUE(checker->no_thin_air()); 109 | ASSERT_TRUE(checker->observation()); 110 | ASSERT_TRUE(checker->propagation()); 111 | } 112 | 113 | TEST(CodeGen, X86_64_VA_Synonyms) { 114 | std::vector threads = { 115 | // p0 116 | std::make_shared(0x1ff, 0), // @0x0 117 | std::make_shared(0x2ff, 0), // @0x8 118 | 119 | // p1 120 | std::make_shared(0x3ff, 1), // 0x0 121 | std::make_shared(0x3fe, 1), // 0x8 122 | }; 123 | 124 | cats::ExecWitness ew; 125 | cats::Arch_TSO arch; 126 | Compiler compiler( 127 | std::unique_ptr(new EvtStateCats(&ew, &arch)), 128 | ExtractThreads(&threads)); 129 | compiler.evts()->set_addr_mask(0xff); 130 | 131 | char code[128]; 132 | 133 | ASSERT_NE(0, compiler.Emit(0, 0, code, sizeof(code))); 134 | ASSERT_NE(0, compiler.Emit(1, 0xffff, code, sizeof(code))); 135 | 136 | auto checker = arch.MakeChecker(&arch, &ew); 137 | ew.po.set_props(mc::EventRel::kTransitiveClosure); 138 | ew.co.set_props(mc::EventRel::kTransitiveClosure); 139 | 140 | types::WriteID wid = 0; 141 | ASSERT_TRUE(compiler.UpdateObs(0x0, 0, 0x1ff, &wid, 1)); 142 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x8, 0, 0x3fe, &wid, 1)); 143 | wid = 1; 144 | ASSERT_TRUE(compiler.UpdateObs(0xffff + 0x0, 0, 0x3ff, &wid, 1)); 145 | wid = 2; 146 | ASSERT_TRUE(compiler.UpdateObs(0x8, 0, 0x2ff, &wid, 1)); 147 | 148 | ASSERT_NO_THROW(checker->wf()); 149 | ASSERT_TRUE(checker->sc_per_location()); 150 | ASSERT_TRUE(checker->no_thin_air()); 151 | ASSERT_TRUE(checker->observation()); 152 | ASSERT_TRUE(checker->propagation()); 153 | } 154 | 155 | #if defined(__linux__) && defined(__x86_64__) 156 | #include 157 | TEST(CodeGen, X86_64_ExecLinux) { 158 | cats::ExecWitness ew; 159 | cats::Arch_TSO arch; 160 | 161 | Compiler compiler( 162 | std::unique_ptr(new EvtStateCats(&ew, &arch))); 163 | 164 | unsigned char test_mem[] = {0x03, 0x14, 0x25, 0x36, 0x47, 0x58, 0x69, 0x7a, 165 | 0x8b, 0x9c, 0xad, 0xbe, 0xcf, 0xd0, 0xe1, 0xf2}; 166 | 167 | strong::Operation::Ptr ops[] = { 168 | std::make_shared( 169 | reinterpret_cast(&test_mem[0xf])), 170 | std::make_shared()}; 171 | 172 | const std::size_t MAX_CODE_SIZE = 4096; 173 | char* code = reinterpret_cast(mmap(NULL, MAX_CODE_SIZE, 174 | PROT_READ | PROT_WRITE | PROT_EXEC, 175 | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0)); 176 | memset(code, 0x90, MAX_CODE_SIZE); 177 | 178 | std::size_t emit_len = 0; 179 | for (auto& op : ops) { 180 | emit_len += compiler.Emit(emit_len, op.get(), code + emit_len, 181 | MAX_CODE_SIZE - emit_len, nullptr, nullptr); 182 | } 183 | 184 | unsigned char (*func)() = reinterpret_cast(code); 185 | unsigned result = func(); 186 | 187 | ASSERT_EQ(result, test_mem[0xf]); 188 | 189 | #ifdef OUTPUT_BIN_TO_TMP 190 | auto f = fopen("/tmp/mc2lib-test2.bin", "wb"); 191 | fwrite(code, MAX_CODE_SIZE, 1, f); 192 | fclose(f); 193 | #endif 194 | 195 | munmap(code, MAX_CODE_SIZE); 196 | } 197 | #endif 198 | -------------------------------------------------------------------------------- /src/test_main.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include 5 | 6 | int main(int argc, char **argv) { 7 | ::testing::InitGoogleTest(&argc, argv); 8 | return RUN_ALL_TESTS(); 9 | } 10 | -------------------------------------------------------------------------------- /src/test_mcversi.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/codegen/ops/strong.hpp" 5 | #include "mc2lib/codegen/rit.hpp" 6 | #include "mc2lib/mcversi.hpp" 7 | 8 | #include 9 | 10 | using namespace mc2lib; 11 | 12 | // This can only check syntax and type correctness: most literals are 13 | // arbitrary. 14 | TEST(McVerSi, CrossoverMutate) { 15 | std::default_random_engine urng; 16 | codegen::strong::RandomFactory factory(0, 2, 0, 10); 17 | 18 | typedef codegen::RandInstTest 20 | RandInstTest; 21 | 22 | simplega::GenePool::Population initial_population; 23 | 24 | for (size_t i = 0; i < 10; ++i) { 25 | initial_population.emplace_back(urng, &factory, 20); 26 | } 27 | 28 | simplega::GenePool pool(initial_population, 0.1f); 29 | 30 | auto selection = pool.SelectUniform(urng, 3); 31 | 32 | // Same values as in McVerSi paper. 33 | mcversi::CrossoverMutate 35 | crossover_mutate(0.2, 0.05); 36 | 37 | pool.Step(urng, crossover_mutate, selection, selection.size()); 38 | } 39 | -------------------------------------------------------------------------------- /src/test_memconsistency.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/memconsistency/cats.hpp" 5 | #include "mc2lib/memconsistency/model12.hpp" 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace mc2lib::memconsistency; 12 | 13 | TEST(MemConsistency, Model12Empty) { 14 | model12::ExecWitness ew; 15 | model12::Arch_SC sc; 16 | auto c = sc.MakeChecker(&sc, &ew); 17 | 18 | ASSERT_NO_THROW(c->wf()); 19 | ASSERT_TRUE(c->uniproc()); 20 | ASSERT_TRUE(c->thin()); 21 | ASSERT_TRUE(c->check_exec()); 22 | ASSERT_NO_THROW(c->valid_exec()); 23 | } 24 | 25 | TEST(MemConsistency, CatsEmpty) { 26 | cats::ExecWitness ew; 27 | cats::Arch_SC sc; 28 | auto c = sc.MakeChecker(&sc, &ew); 29 | 30 | ASSERT_NO_THROW(c->wf()); 31 | ASSERT_TRUE(c->sc_per_location()); 32 | ASSERT_TRUE(c->no_thin_air()); 33 | ASSERT_TRUE(c->observation()); 34 | ASSERT_TRUE(c->propagation()); 35 | ASSERT_NO_THROW(c->valid_exec()); 36 | } 37 | 38 | TEST(MemConsistency, Model12DekkerValidSC) { 39 | model12::ExecWitness ew; 40 | model12::Arch_SC sc; 41 | auto c = sc.MakeChecker(&sc, &ew); 42 | 43 | Event Ix = Event(Event::kWrite, 10, Iiid(-1, 0)); 44 | Event Iy = Event(Event::kWrite, 20, Iiid(-1, 1)); 45 | 46 | Event Wx0 = Event(Event::kWrite, 10, Iiid(0, 12)); 47 | Event Wy1 = Event(Event::kWrite, 20, Iiid(1, 33)); 48 | Event Ry0 = Event(Event::kRead, 20, Iiid(0, 55)); 49 | Event Rx1 = Event(Event::kRead, 10, Iiid(1, 22)); 50 | 51 | ew.events |= EventSet({Ix, Iy, Wx0, Wy1, Ry0, Rx1}); 52 | 53 | ew.po.Insert(Wx0, Ry0); 54 | ew.po.Insert(Wy1, Rx1); 55 | 56 | ew.ws.Insert(Ix, Wx0); 57 | ew.ws.Insert(Iy, Wy1); 58 | 59 | ew.rf.Insert(Wx0, Rx1); 60 | ew.rf.Insert(Wy1, Ry0); 61 | 62 | ASSERT_NO_THROW(c->wf()); 63 | ASSERT_TRUE(c->uniproc()); 64 | ASSERT_TRUE(c->thin()); 65 | ASSERT_TRUE(c->check_exec()); 66 | ASSERT_NO_THROW(c->valid_exec()); 67 | } 68 | 69 | TEST(MemConsistency, CatsDekkerInvalidSCValidTSO) { 70 | cats::ExecWitness ew; 71 | cats::Arch_SC sc; 72 | cats::Arch_TSO tso; 73 | auto c_sc = sc.MakeChecker(&sc, &ew); 74 | auto c_tso = tso.MakeChecker(&tso, &ew); 75 | 76 | Event Ix = Event(Event::kWrite, 10, Iiid(-1, 0)); 77 | Event Iy = Event(Event::kWrite, 20, Iiid(-1, 1)); 78 | 79 | Event Wx0 = Event(Event::kWrite, 10, Iiid(0, 12)); 80 | Event Wy1 = Event(Event::kWrite, 20, Iiid(1, 33)); 81 | Event Ry0 = Event(Event::kRead, 20, Iiid(0, 55)); 82 | Event Rx1 = Event(Event::kRead, 10, Iiid(1, 22)); 83 | 84 | ew.events |= EventSet({Ix, Iy, Wx0, Wy1, Ry0, Rx1}); 85 | 86 | ew.po.Insert(Wx0, Ry0); 87 | ew.po.Insert(Wy1, Rx1); 88 | 89 | ew.co.Insert(Ix, Wx0); 90 | ew.co.Insert(Iy, Wy1); 91 | 92 | ew.rf.Insert(Ix, Rx1); 93 | ew.rf.Insert(Iy, Ry0); 94 | 95 | ASSERT_NO_THROW(c_sc->wf()); 96 | ASSERT_TRUE(c_sc->sc_per_location()); 97 | ASSERT_TRUE(c_sc->no_thin_air()); 98 | ASSERT_TRUE(c_sc->observation()); 99 | ASSERT_FALSE(c_sc->propagation()); 100 | 101 | try { 102 | c_sc->valid_exec(); 103 | FAIL(); 104 | } catch (const Error& e) { 105 | ASSERT_EQ(std::string("PROPAGATION"), e.what()); 106 | } 107 | 108 | ASSERT_NO_THROW(c_tso->wf()); 109 | ASSERT_TRUE(c_tso->sc_per_location()); 110 | ASSERT_TRUE(c_tso->no_thin_air()); 111 | ASSERT_TRUE(c_tso->observation()); 112 | ASSERT_TRUE(c_tso->propagation()); 113 | ASSERT_NO_THROW(c_tso->valid_exec()); 114 | } 115 | -------------------------------------------------------------------------------- /src/test_sets.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/memconsistency/eventsets.hpp" 5 | #include "mc2lib/sets.hpp" 6 | 7 | #include 8 | 9 | using namespace mc2lib; 10 | using namespace mc2lib::memconsistency; 11 | 12 | static Event base_event; 13 | static Event ResetEvt() { 14 | base_event.iiid.poi = 0; 15 | return base_event; 16 | } 17 | static Event NextEvt() { 18 | ++base_event.iiid; 19 | return base_event; 20 | } 21 | 22 | TEST(Sets, SimpleSet) { 23 | EventSet s = EventSet({NextEvt(), NextEvt(), NextEvt()}); 24 | ASSERT_EQ((s * s).size(), 9); 25 | ASSERT_FALSE(s.Subset(s)); 26 | ASSERT_TRUE(s.SubsetEq(s)); 27 | ASSERT_TRUE(s == (s * s).Range()); 28 | ASSERT_FALSE((s | s) != s); 29 | } 30 | 31 | TEST(Sets, CycleDetectionUnionNo) { 32 | Event e1 = ResetEvt(); 33 | Event e2, e3; 34 | 35 | EventRel er1; 36 | er1.Insert(e3 = e1, e2 = NextEvt()); 37 | er1.Insert(e2, e1 = NextEvt()); 38 | er1.Insert(e3, e1); 39 | 40 | ASSERT_FALSE(er1.props()); 41 | ASSERT_TRUE(er1.Acyclic()); 42 | ASSERT_FALSE(er1.props()); 43 | 44 | EventRel er2; 45 | er2.Insert(e1, e2 = NextEvt()); 46 | er2.Insert(e1, e2 = NextEvt()); 47 | er2.Insert(e1, e2 = NextEvt()); 48 | 49 | ASSERT_TRUE(er1.Transitive()); 50 | ASSERT_TRUE(er2.Transitive()); 51 | ASSERT_TRUE((er1 | er2).Acyclic()); 52 | er1 |= er2; 53 | ASSERT_EQ(er1.size(), 6); 54 | 55 | er2.set_props(EventRel::kTransitiveClosure); 56 | ASSERT_TRUE((EventRel() | er2).Acyclic()); 57 | } 58 | 59 | TEST(Sets, CycleDetectionYes) { 60 | Event e1 = ResetEvt(); 61 | Event e2; 62 | Event e3; 63 | 64 | EventRel er; 65 | er.Insert(e1, e2 = NextEvt()); 66 | er.Insert(e3 = e2, e1 = NextEvt()); 67 | er.Insert(e1, e2 = NextEvt()); 68 | er.Insert(e1, e2 = NextEvt()); 69 | 70 | EventRel er_; 71 | er_.Insert(e1, e2 = NextEvt()); 72 | er_.Insert(e2, e3); 73 | er_.Insert(e2, NextEvt()); 74 | er_.Insert(e2, NextEvt()); 75 | er_.Insert(e2, NextEvt()); 76 | er_.Insert(e2, NextEvt()); 77 | er |= er_; 78 | 79 | ASSERT_FALSE(er.Acyclic()); 80 | 81 | er.set_props(EventRel::kReflexiveTransitiveClosure); 82 | ASSERT_EQ(er.size(), 43); 83 | } 84 | 85 | TEST(Sets, CycleDetectionYes2) { 86 | Event e1 = ResetEvt(); 87 | Event e2; 88 | Event e3; 89 | 90 | EventRel er; 91 | er.Insert(e1, e2 = NextEvt()); 92 | er.Insert(e3 = e2, e1 = NextEvt()); 93 | er.Insert(e1, e2 = NextEvt()); 94 | er.Insert(e2, e1 = NextEvt()); 95 | er.Insert(e1, e3); 96 | 97 | for (int i = 0; i < 30; ++i) { 98 | // Catch out buggy cycle detection implementations, where they do not 99 | // consider a visisted node after being visited once before. 100 | // 101 | // As unordered maps are used as backing store, by adding more edges 102 | // that do not contribute to the cycle, we are likely to traverse these 103 | // first. 104 | er.Insert(NextEvt(), e3); 105 | } 106 | 107 | EventRel::Path p; 108 | ASSERT_FALSE(er.Acyclic(&p)); 109 | ASSERT_TRUE(p[1] == p.back()); 110 | ASSERT_EQ(p.size(), 6); 111 | 112 | p.clear(); 113 | er.set_props(EventRel::kTransitiveClosure); 114 | ASSERT_TRUE(er.R(e3, e1, &p)); 115 | ASSERT_TRUE(p.front() == e3); 116 | ASSERT_TRUE(p.back() == e1); 117 | ASSERT_EQ(p.size(), 4); 118 | } 119 | 120 | TEST(Sets, EventRelDiff) { 121 | Event e1 = ResetEvt(); 122 | Event e2; 123 | 124 | EventRel er; 125 | er.Insert(e1, e2 = NextEvt()); 126 | er.Insert(e2, e1 = NextEvt()); 127 | er.Insert(e1, e2 = NextEvt()); 128 | er.Insert(e1, e2 = NextEvt()); 129 | er.Insert(e1, e2 = NextEvt()); 130 | 131 | EventRel d; 132 | d.Insert(e1, e2); 133 | d.Insert(e1, NextEvt()); 134 | er -= d; 135 | ASSERT_EQ(er.size(), 4); 136 | 137 | d.set_props(EventRel::kReflexiveTransitiveClosure); 138 | auto evald = d.Eval(); 139 | ASSERT_TRUE(d == evald); 140 | ASSERT_TRUE(d.get() != evald.get()); 141 | ASSERT_EQ(d.size(), 5); 142 | } 143 | 144 | TEST(Sets, EventRelDiffProps) { 145 | Event e1 = ResetEvt(); 146 | Event e2, start; 147 | 148 | EventRel er; 149 | er.Insert(start = e1, e2 = NextEvt()); 150 | er.Insert(e2, e1 = NextEvt()); 151 | er.Insert(e1, e2 = NextEvt()); 152 | er.Insert(e1, e2 = NextEvt()); 153 | er.Insert(e1, e2 = NextEvt()); 154 | 155 | EventRel d; 156 | d.Insert(start, e2); 157 | d.Insert(e2, e2); 158 | d.Insert(e2, start); 159 | er.add_props(EventRel::kTransitiveClosure); 160 | d -= er; 161 | ASSERT_EQ(d.size(), 2); 162 | 163 | er.add_props(EventRel::kReflexiveClosure); 164 | d -= er; 165 | ASSERT_EQ(d.size(), 1); 166 | } 167 | 168 | TEST(Sets, EventRelIntersect) { 169 | Event e1 = ResetEvt(); 170 | Event e2; 171 | 172 | EventRel er; 173 | er.Insert(e1, e2 = NextEvt()); 174 | er.Insert(e2, e1 = NextEvt()); 175 | er.Insert(e1, e2 = NextEvt()); 176 | er.Insert(e1, e2 = NextEvt()); 177 | er.Insert(e1, e2 = NextEvt()); 178 | er.Insert(e1, e1); 179 | 180 | EventRel d; 181 | d.Insert(e1, e2); 182 | d.Insert(e1, NextEvt()); 183 | d.Insert(e1, NextEvt()); 184 | ASSERT_EQ((d & er).size(), 1); 185 | 186 | d.set_props(EventRel::kReflexiveClosure); 187 | d &= er; 188 | ASSERT_FALSE(d.props()); 189 | ASSERT_EQ(d.size(), 2); 190 | 191 | ASSERT_TRUE(d.Domain().Subset(er.Domain())); 192 | } 193 | 194 | TEST(Sets, EventRelInverse) { 195 | Event e1 = ResetEvt(); 196 | Event e2; 197 | 198 | EventRel er; 199 | er.Insert(e1, e2 = NextEvt()); 200 | er.Insert(e2, e1 = NextEvt()); 201 | er.Insert(e2, e1 = NextEvt()); 202 | er.Insert(e1, e2 = NextEvt()); 203 | er.Insert(e1, e2 = NextEvt()); 204 | 205 | EventRel inv = er.Inverse(); 206 | ASSERT_TRUE(er.Domain() == inv.Range()); 207 | ASSERT_TRUE(er.Range() == inv.Domain()); 208 | ASSERT_EQ(er.size(), inv.size()); 209 | 210 | er.for_each([&inv](const Event& e1, const Event& e2) { 211 | ASSERT_TRUE(inv.R(e2, e1)); 212 | ASSERT_FALSE(inv.R(e1, e2)); 213 | }); 214 | 215 | er.set_props(EventRel::kReflexiveTransitiveClosure); 216 | inv = er.Inverse(); 217 | ASSERT_TRUE(er.Domain() == inv.Range()); 218 | ASSERT_TRUE(er.Range() == inv.Domain()); 219 | ASSERT_EQ(er.size(), inv.size()); 220 | } 221 | 222 | TEST(Sets, EventRelSeqR) { 223 | Event e1 = ResetEvt(); 224 | Event e2; 225 | Event start, end; 226 | 227 | EventRelSeq ers; 228 | EventRel er; 229 | er.Insert(start = e1, e2 = NextEvt()); 230 | ers += er; 231 | 232 | er = EventRel(); 233 | er.Insert(e2, e1 = NextEvt()); 234 | ers += EventRel(er); 235 | 236 | er = EventRel(); 237 | er.Insert(e1, end = e2 = NextEvt()); 238 | er.Insert(e2, e1 = NextEvt()); 239 | er.add_props(EventRel::kTransitiveClosure); 240 | ers += er; 241 | 242 | // First unevald 243 | ASSERT_TRUE(ers.R(start, end)); 244 | ASSERT_TRUE(ers.R(start, e1)); 245 | ASSERT_TRUE(ers.Irreflexive()); 246 | 247 | const EventRel evald = ers.Eval(); 248 | auto ers_copy = ers; 249 | const EventRel evald_inplace = ers_copy.EvalClear(); 250 | ASSERT_TRUE(evald == evald_inplace); 251 | ASSERT_FALSE(evald != evald_inplace); 252 | 253 | ers.EvalInplace(); 254 | // Should be same result after eval_inplace 255 | ASSERT_TRUE(ers.R(start, end)); 256 | ASSERT_TRUE(ers.R(start, e1)); 257 | ASSERT_TRUE(ers.Irreflexive()); 258 | 259 | // Check evald 260 | ASSERT_TRUE(evald.R(start, end)); 261 | ASSERT_TRUE(evald.R(start, e1)); 262 | ASSERT_TRUE(evald.Irreflexive()); 263 | } 264 | 265 | TEST(Sets, EventRelSeqIrrefl1) { 266 | Event e1 = ResetEvt(); 267 | Event e2; 268 | Event start, end; 269 | 270 | EventRelSeq ers; 271 | EventRel er; 272 | er.Insert(e1, NextEvt()); 273 | er.Insert(e1, NextEvt()); 274 | er.Insert(e1, NextEvt()); 275 | er.Insert(start = e1, e2 = NextEvt()); 276 | ers += er; 277 | 278 | er = EventRel(); 279 | er.Insert(e2, e1 = NextEvt()); 280 | er.Insert(e1, e2 = NextEvt()); 281 | er.add_props(EventRel::kTransitiveClosure); 282 | ers += er; 283 | 284 | er = EventRel(); 285 | er.Insert(e2, start); 286 | ers += er; 287 | 288 | ASSERT_FALSE(ers.Irreflexive()); 289 | 290 | const EventRel evald = ers.Eval(); 291 | ASSERT_FALSE(evald.Irreflexive()); 292 | 293 | EventRel::Path p; 294 | ers.Irreflexive(&p); 295 | ASSERT_EQ(p.size(), 5); 296 | } 297 | 298 | TEST(Sets, EventRelSeqIrrefl2) { 299 | Event e1 = ResetEvt(); 300 | Event e2; 301 | Event start; 302 | 303 | EventRelSeq ers; 304 | EventRel er; 305 | er.Insert(start = e1, e2 = NextEvt()); 306 | ers += er; 307 | 308 | er = EventRel(); 309 | er.Insert(e2, start); 310 | ers += er; 311 | 312 | er = EventRel(); 313 | er.Insert(start, NextEvt()); 314 | ASSERT_TRUE(er.Irreflexive()); 315 | 316 | er.add_props(EventRel::kReflexiveClosure); 317 | ASSERT_FALSE(er.Irreflexive()); 318 | ers += er; 319 | 320 | ASSERT_FALSE(ers.Irreflexive()); 321 | 322 | const EventRel evald = ers.Eval(); 323 | ASSERT_FALSE(evald.Irreflexive()); 324 | } 325 | 326 | TEST(Sets, EventRelReflexivePath) { 327 | Event e1 = ResetEvt(); 328 | Event e2; 329 | 330 | EventRelSeq ers; 331 | 332 | EventRel er; 333 | er.Insert(e1, e2 = NextEvt()); 334 | er.Insert(e1, e2 = NextEvt()); 335 | er.set_props(EventRel::kReflexiveClosure); 336 | ers += er; 337 | 338 | er = EventRel(); 339 | er.Insert(e2, e1 = NextEvt()); 340 | er.set_props(EventRel::kReflexiveClosure); 341 | ers += er; 342 | 343 | EventRel::Path p; 344 | ASSERT_FALSE(er.Irreflexive(&p)); 345 | ASSERT_EQ(p.size(), 2); 346 | ASSERT_TRUE(p[0] == p[1]); 347 | 348 | p.clear(); 349 | ASSERT_FALSE(ers.Irreflexive(&p)); 350 | ASSERT_EQ(p.size(), 3); 351 | ASSERT_TRUE(p[0] == p[1]); 352 | ASSERT_TRUE(p[1] == p[2]); 353 | } 354 | 355 | TEST(Sets, EventRelSubset) { 356 | Event e1 = ResetEvt(); 357 | Event e2; 358 | 359 | EventRel er1; 360 | er1.Insert(e1, e2 = NextEvt()); 361 | er1.Insert(e2, e1 = NextEvt()); 362 | er1.Insert(e2, e1 = NextEvt()); 363 | er1.set_props(EventRel::kReflexiveTransitiveClosure); 364 | 365 | EventRel er2 = er1.Eval(); 366 | ASSERT_TRUE(er2.SubsetEq(er1)); 367 | ASSERT_TRUE(er1.SubsetEq(er2)); 368 | ASSERT_FALSE(er2.Subset(er1)); 369 | 370 | er2.Insert(e1, e2 = NextEvt()); 371 | er2.Insert(e1, e2 = NextEvt()); 372 | 373 | ASSERT_FALSE(er2.Subset(er1)); 374 | ASSERT_TRUE(er1.Subset(er2)); 375 | } 376 | -------------------------------------------------------------------------------- /src/test_simplega.cpp: -------------------------------------------------------------------------------- 1 | // This code is licensed under the BSD 3-Clause license. See the LICENSE file 2 | // in the project root for license terms. 3 | 4 | #include "mc2lib/simplega.hpp" 5 | 6 | #include 7 | 8 | #include 9 | 10 | using namespace mc2lib::simplega; 11 | 12 | class GenomeAdd : public Genome { 13 | public: 14 | static std::default_random_engine* urng; 15 | 16 | GenomeAdd() { 17 | genome_.resize(5); 18 | MutateImpl(1.0f); 19 | } 20 | 21 | GenomeAdd(const GenomeAdd& parent1, const GenomeAdd& parent2, 22 | const std::vector& g) 23 | : Genome(g) {} 24 | 25 | void Mutate(float rate) override { MutateImpl(rate); } 26 | 27 | float Fitness() const override { 28 | float total = 0.0; 29 | for (const auto& f : genome_) { 30 | total += f; 31 | } 32 | 33 | // restrict size 34 | if (genome_.size() > 10) { 35 | return 999.0f; 36 | } 37 | 38 | // want to get sum closest to 24. 39 | return (24 - total) * (24 - total); 40 | } 41 | 42 | bool operator<(const Genome& rhs) const override { 43 | return Fitness() < rhs.Fitness(); 44 | } 45 | 46 | operator float() const override { return 1000.f - Fitness(); } 47 | 48 | private: 49 | void MutateImpl(float rate) { 50 | std::uniform_int_distribution dist_idx(0, genome_.size() - 1); 51 | std::uniform_real_distribution dist_mut(-2.0f, 2.0f); 52 | std::unordered_set used; 53 | std::size_t selection_count = 54 | static_cast(static_cast(genome_.size()) * rate); 55 | 56 | while (selection_count) { 57 | auto idx = dist_idx(*urng); 58 | if (used.find(idx) != used.end()) { 59 | continue; 60 | } 61 | 62 | genome_[idx] += dist_mut(*urng); 63 | 64 | used.insert(idx); 65 | --selection_count; 66 | } 67 | } 68 | }; 69 | 70 | // TODO(melver): Use proper test harness. 71 | std::default_random_engine* GenomeAdd::urng = nullptr; 72 | 73 | TEST(SimpleGA, Add24) { 74 | std::default_random_engine urng(1234); 75 | GenomeAdd::urng = &urng; 76 | 77 | GenePool pool(25, // population_size 78 | 0.3f); // mutation_rate 79 | 80 | std::size_t tournament_size = 10; 81 | std::size_t tournament_winners = 5; 82 | std::size_t elite = tournament_winners; 83 | 84 | for (int i = 0; i < 50; ++i) { 85 | auto tournament_population = pool.SelectUniform(urng, tournament_size); 86 | pool.SelectionSort(&tournament_population); 87 | pool.Step(urng, 88 | evolve::CutSpliceMutate::Population>, 90 | tournament_population, tournament_winners, elite); 91 | ASSERT_TRUE(pool.population_size() <= pool.target_population_size() + 1); 92 | } 93 | 94 | // This mainly checks that the discrete_distribution implementation works 95 | // as expected. 96 | ASSERT_TRUE(GenePool(pool.SelectRoulette(urng, tournament_size)) 97 | .AverageFitness() < 98 | GenePool(pool.SelectUniform(urng, tournament_size)) 99 | .AverageFitness()); 100 | 101 | ASSERT_TRUE(pool.BestFitness() < pool.WorstFitness()); 102 | 103 | auto gene = pool.SelectBest(); 104 | float sum = 0.0f; 105 | for (const auto& f : gene.get()) { 106 | sum += f; 107 | } 108 | 109 | ASSERT_TRUE(sum >= 23.1f && sum <= 24.9); 110 | } 111 | --------------------------------------------------------------------------------