diff --git a/CMakeLists.txt b/CMakeLists.txt index 2be241f252bebfba2ebd0923018c332ccb655741..1b8f7bb25e20e8f5c0cff9dd035099f934ed51b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,14 +11,8 @@ add_library(sylvan avl.h lace.h lace.c - llmsset.c - llmsset.h - refs.h - refs.c sha2.h sha2.c - stats.h - stats.c sylvan.h sylvan_bdd.h sylvan_bdd.c @@ -29,19 +23,31 @@ add_library(sylvan sylvan_common.c sylvan_gmp.h sylvan_gmp.c + sylvan_int.h sylvan_ldd.h sylvan_ldd.c + sylvan_mt.h + sylvan_mt.c sylvan_mtbdd.h sylvan_mtbdd.c sylvan_mtbdd_int.h sylvan_obj.hpp sylvan_obj.cpp + sylvan_refs.h + sylvan_refs.c sylvan_seq.h sylvan_seq.c + sylvan_sl.h + sylvan_sl.c + sylvan_table.h + sylvan_table.c + sylvan_stats.h + sylvan_stats.c tls.h ) -target_link_libraries(sylvan m pthread) +target_link_libraries(sylvan -lpthread) +target_link_libraries(sylvan -lgmp) if(UNIX AND NOT APPLE) target_link_libraries(sylvan rt) diff --git a/Makefile.am b/Makefile.am old mode 100644 new mode 100755 diff --git a/atomics.h b/atomics.h old mode 100644 new mode 100755 diff --git a/avl.h b/avl.h old mode 100644 new mode 100755 index 68b4ea22009dd296fb0632933da4b44e0eec4d5a..858859d6b48d3daf4ee7e949221c65772d228269 --- a/avl.h +++ b/avl.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2014 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -282,7 +283,7 @@ NAME##_put(avl_node_t **root, TYPE *data, int *inserted) static __attribute__((unused)) int \ NAME##_insert(avl_node_t **root, TYPE *data) \ { \ - int inserted; \ + int inserted = 0; \ NAME##_put(root, data, &inserted); \ return inserted; \ } \ diff --git a/lace.c b/lace.c old mode 100644 new mode 100755 index 43135ac016182e59b2b3566231ae70042feb2f61..08e9f8843b2b26216a4ec8515ca08f99350f408c --- a/lace.c +++ b/lace.c @@ -1,5 +1,6 @@ /* - * Copyright 2013-2015 Formal Methods and Tools, University of Twente + * Copyright 2013-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +15,7 @@ * limitations under the License. */ +#define _GNU_SOURCE #include <errno.h> // for errno #include <sched.h> // for sched_getaffinity #include <stdio.h> // for fprintf @@ -27,46 +29,97 @@ #include <lace.h> -#ifndef USE_HWLOC -#define USE_HWLOC 0 -#endif - -#if USE_HWLOC +#if LACE_USE_HWLOC #include <hwloc.h> -#endif -// public Worker data -static Worker **workers; -static size_t default_stacksize = 0; // set by lace_init -static size_t default_dqsize = 100000; - -#if USE_HWLOC +/** + * HWLOC information + */ static hwloc_topology_t topo; static unsigned int n_nodes, n_cores, n_pus; #endif +/** + * (public) Worker data + */ +static Worker **workers = NULL; + +/** + * Default sizes for program stack and task deque + */ +static size_t default_stacksize = 0; // 0 means "set by lace_init" +static size_t default_dqsize = 100000; + +/** + * Verbosity flag, set with lace_set_verbosity + */ static int verbosity = 0; -static int n_workers = 0; -static int enabled_workers = 0; +/** + * Number of workers and number of enabled/active workers + */ +static unsigned int n_workers = 0; +static unsigned int enabled_workers = 0; + +/** + * Datastructure of the task deque etc for each worker. + * - first public cachelines (accessible via global "workers" variable) + * - then private cachelines + * - then the deque array + */ +typedef struct { + Worker worker_public; + char pad1[PAD(sizeof(Worker), LINE_SIZE)]; + WorkerP worker_private; + char pad2[PAD(sizeof(WorkerP), LINE_SIZE)]; + Task deque[]; +} worker_data; + +/** + * (Secret) holds pointers to the memory block allocated for each worker + */ +static worker_data **workers_memory = NULL; + +/** + * Number of bytes allocated for each worker's worker data. + */ +static size_t workers_memory_size = 0; -// private Worker data (just for stats at end ) +/** + * (Secret) holds pointer to private Worker data, just for stats collection at end + */ static WorkerP **workers_p; -// set to 0 when quitting +/** + * Flag to signal all workers to quit. + */ static int lace_quits = 0; -// for storing private Worker data +/** + * Thread-specific mechanism to access current worker data + */ #ifdef __linux__ // use gcc thread-local storage (i.e. __thread variables) static __thread WorkerP *current_worker; #else static pthread_key_t worker_key; #endif + +/** + * worker_attr used for creating threads + * - initialized by lace_init + * - used by lace_spawn_worker + */ static pthread_attr_t worker_attr; +/** + * The condition/mutex pair for when the root thread sleeps until the end of the program + */ static pthread_cond_t wait_until_done = PTHREAD_COND_INITIALIZER; static pthread_mutex_t wait_until_done_mutex = PTHREAD_MUTEX_INITIALIZER; +/** + * Data structure that contains the stack and stack size for each worker. + */ struct lace_worker_init { void* stack; @@ -75,8 +128,14 @@ struct lace_worker_init static struct lace_worker_init *workers_init; +/** + * Global newframe variable used for the implementation of NEWFRAME and TOGETHER + */ lace_newframe_t lace_newframe; +/** + * Get the private Worker data of the current thread + */ WorkerP* lace_get_worker() { @@ -87,14 +146,20 @@ lace_get_worker() #endif } +/** + * Find the head of the task deque, using the given private Worker data + */ Task* lace_get_head(WorkerP *self) { Task *dq = self->dq; + + /* First check the first tasks linearly */ if (dq[0].thief == 0) return dq; if (dq[1].thief == 0) return dq+1; if (dq[2].thief == 0) return dq+2; + /* Then fast search for a low/high bound using powers of 2: 4, 8, 16... */ size_t low = 2; size_t high = self->end - self->dq; @@ -109,6 +174,7 @@ lace_get_head(WorkerP *self) } } + /* Finally zoom in using binary search */ while (low < high) { size_t mid = low + (high-low)/2; if (dq[mid].thief == 0) high = mid; @@ -118,22 +184,27 @@ lace_get_head(WorkerP *self) return dq+low; } -size_t +/** + * Get the number of workers + */ +unsigned int lace_workers() { return n_workers; } +/** + * Get the default stack size (or 0 for automatically determine) + */ size_t lace_default_stacksize() { return default_stacksize; } -#ifndef cas -#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) -#endif - +/** + * If we are collecting PIE times, then we need some helper functions. + */ #if LACE_PIE_TIMES static uint64_t count_at_start, count_at_end; static long long unsigned us_elapsed_timer; @@ -160,109 +231,185 @@ us_elapsed(void) } #endif -#if USE_HWLOC -// Lock used only during parallel lace_init_worker... -static volatile int __attribute__((aligned(64))) lock = 0; -static inline void -lock_acquire() -{ - while (1) { - while (lock) {} - if (cas(&lock, 0, 1)) return; - } -} -static inline void -lock_release() -{ - lock=0; -} -#endif - -/* Barrier */ -#define BARRIER_MAX_THREADS 128 - -typedef union __attribute__((__packed__)) -{ - volatile size_t val; - char pad[LINE_SIZE]; -} asize_t; - +/** + * Lace barrier implementation, that synchronizes on all currently enabled workers. + */ typedef struct { volatile int __attribute__((aligned(LINE_SIZE))) count; + volatile int __attribute__((aligned(LINE_SIZE))) leaving; volatile int __attribute__((aligned(LINE_SIZE))) wait; - /* the following is needed only for destroy: */ - asize_t entered[BARRIER_MAX_THREADS]; } barrier_t; barrier_t lace_bar; +/** + * Enter the Lace barrier and wait until all workers have entered the Lace barrier. + */ void lace_barrier() { - int id = lace_get_worker()->worker; - - lace_bar.entered[id].val = 1; // signal entry - int wait = lace_bar.wait; - if (enabled_workers == __sync_add_and_fetch(&lace_bar.count, 1)) { - lace_bar.count = 0; // reset counter + if ((int)enabled_workers == __sync_add_and_fetch(&lace_bar.count, 1)) { + lace_bar.count = 0; + lace_bar.leaving = enabled_workers; lace_bar.wait = 1 - wait; // flip wait - lace_bar.entered[id].val = 0; // signal exit } else { while (wait == lace_bar.wait) {} // wait - lace_bar.entered[id].val = 0; // signal exit } + + __sync_add_and_fetch(&lace_bar.leaving, -1); } +/** + * Initialize the Lace barrier + */ static void lace_barrier_init() { - assert(n_workers <= BARRIER_MAX_THREADS); memset(&lace_bar, 0, sizeof(barrier_t)); } +/** + * Destroy the Lace barrier (just wait until all are exited) + */ static void lace_barrier_destroy() { // wait for all to exit - int i; - for ( i=0; i<n_workers; i++) { - while (1 == lace_bar.entered[i].val) {} + while (lace_bar.leaving != 0) continue; +} + +/** + * For debugging purposes, check if memory is allocated on the correct memory nodes. + */ +static void __attribute__((unused)) +lace_check_memory(void) +{ +#if LACE_USE_HWLOC + // get our current worker + WorkerP *w = lace_get_worker(); + void* mem = workers_memory[w->worker]; + + // get pinned PUs + hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); + hwloc_get_cpubind(topo, cpuset, HWLOC_CPUBIND_THREAD); + + // get nodes of pinned PUs + hwloc_nodeset_t cpunodes = hwloc_bitmap_alloc(); + hwloc_cpuset_to_nodeset(topo, cpuset, cpunodes); + + // get location of memory + hwloc_nodeset_t memlocation = hwloc_bitmap_alloc(); +#ifdef hwloc_get_area_memlocation + hwloc_get_area_memlocation(topo, mem, sizeof(worker_data), memlocation, HWLOC_MEMBIND_BYNODESET); +#else + hwloc_membind_policy_t policy; + int res = hwloc_get_area_membind_nodeset(topo, mem, sizeof(worker_data), memlocation, &policy, HWLOC_MEMBIND_STRICT); + if (res == -1) { + fprintf(stderr, "Lace warning: hwloc_get_area_membind_nodeset returned -1!\n"); } + if (policy != HWLOC_MEMBIND_BIND) { + fprintf(stderr, "Lace warning: Lace worker memory not bound with BIND policy!\n"); + } +#endif + + // check if CPU and node are on the same place + if (!hwloc_bitmap_isincluded(memlocation, cpunodes)) { + fprintf(stderr, "Lace warning: Lace thread not on same memory domain as data!\n"); + + char *strp, *strp2, *strp3; + hwloc_bitmap_list_asprintf(&strp, cpuset); + hwloc_bitmap_list_asprintf(&strp2, cpunodes); + hwloc_bitmap_list_asprintf(&strp3, memlocation); + fprintf(stderr, "Worker %d is pinned on PUs %s, node %s; memory is pinned on node %s\n", w->worker, strp, strp2, strp3); + free(strp); + free(strp2); + free(strp3); + } + + // free allocated memory + hwloc_bitmap_free(cpuset); + hwloc_bitmap_free(cpunodes); + hwloc_bitmap_free(memlocation); +#endif } void -lace_init_worker(int worker, size_t dq_size) +lace_pin_worker(void) { - Worker *wt = NULL; - WorkerP *w = NULL; +#if LACE_USE_HWLOC + // Get our worker + unsigned int worker = lace_get_worker()->worker; - if (dq_size == 0) dq_size = default_dqsize; + // Get our core (hwloc object) + hwloc_obj_t pu = hwloc_get_obj_by_type(topo, HWLOC_OBJ_CORE, worker % n_cores); -#if USE_HWLOC - // Get our logical processor - hwloc_obj_t pu = hwloc_get_obj_by_type(topo, HWLOC_OBJ_PU, worker % n_pus); + // Get our copy of the bitmap + hwloc_cpuset_t bmp = hwloc_bitmap_dup(pu->cpuset); + + // Get number of PUs in bitmap + int n = -1, count=0; + while ((n=hwloc_bitmap_next(bmp, n)) != -1) count++; + + // Check if we actually have any logical processors + if (count == 0) { + fprintf(stderr, "Lace error: trying to pin a worker on an empty core?\n"); + exit(-1); + } + + // Select the correct PU on the core (in case of hyperthreading) + int idx = worker / n_cores; + if (idx >= count) { + fprintf(stderr, "Lace warning: more workers than available logical processors!\n"); + idx %= count; + } + + // Find index of PU and restrict bitmap + n = -1; + for (int i=0; i<=idx; i++) n = hwloc_bitmap_next(bmp, n); + hwloc_bitmap_only(bmp, n); // Pin our thread... - hwloc_set_cpubind(topo, pu->cpuset, HWLOC_CPUBIND_THREAD); + if (hwloc_set_cpubind(topo, bmp, HWLOC_CPUBIND_THREAD) == -1) { + fprintf(stderr, "Lace warning: hwloc_set_cpubind returned -1!\n"); + } + + // Free our copy of the bitmap + hwloc_bitmap_free(bmp); + + // Pin the memory area (using the appropriate hwloc function) +#ifdef HWLOC_MEMBIND_BYNODESET + int res = hwloc_set_area_membind(topo, workers_memory[worker], workers_memory_size, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_STRICT | HWLOC_MEMBIND_MIGRATE | HWLOC_MEMBIND_BYNODESET); +#else + int res = hwloc_set_area_membind_nodeset(topo, workers_memory[worker], workers_memory_size, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_STRICT | HWLOC_MEMBIND_MIGRATE); +#endif + if (res != 0) { + fprintf(stderr, "Lace error: Unable to bind worker memory to node!\n"); + } + + // Check if everything is on the correct node + lace_check_memory(); +#endif +} - // Allocate memory on our node... - lock_acquire(); - wt = (Worker *)hwloc_alloc_membind(topo, sizeof(Worker), pu->cpuset, HWLOC_MEMBIND_BIND, 0); - w = (WorkerP *)hwloc_alloc_membind(topo, sizeof(WorkerP), pu->cpuset, HWLOC_MEMBIND_BIND, 0); - if (wt == NULL || w == NULL || (w->dq = (Task*)hwloc_alloc_membind(topo, dq_size * sizeof(Task), pu->cpuset, HWLOC_MEMBIND_BIND, 0)) == NULL) { +void +lace_init_worker(unsigned int worker) +{ + // Allocate our memory + workers_memory[worker] = mmap(NULL, workers_memory_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (workers_memory[worker] == MAP_FAILED) { fprintf(stderr, "Lace error: Unable to allocate memory for the Lace worker!\n"); exit(1); } - lock_release(); + + // Set pointers + Worker *wt = workers[worker] = &workers_memory[worker]->worker_public; + WorkerP *w = workers_p[worker] = &workers_memory[worker]->worker_private; + w->dq = workers_memory[worker]->deque; +#ifdef __linux__ + current_worker = w; #else - // Allocate memory... - if (posix_memalign((void**)&wt, LINE_SIZE, sizeof(Worker)) || - posix_memalign((void**)&w, LINE_SIZE, sizeof(WorkerP)) || - posix_memalign((void**)&w->dq, LINE_SIZE, dq_size * sizeof(Task))) { - fprintf(stderr, "Lace error: Unable to allocate memory for the Lace worker!\n"); - exit(1); - } + pthread_setspecific(worker_key, w); #endif // Initialize public worker data @@ -273,12 +420,12 @@ lace_init_worker(int worker, size_t dq_size) // Initialize private worker data w->_public = wt; - w->end = w->dq + dq_size; + w->end = w->dq + default_dqsize; w->split = w->dq; w->allstolen = 0; w->worker = worker; -#if USE_HWLOC - w->pu = worker % n_pus; +#if LACE_USE_HWLOC + w->pu = worker % n_cores; #else w->pu = -1; #endif @@ -288,21 +435,13 @@ lace_init_worker(int worker, size_t dq_size) } else { w->stack_trigger = 0; } + w->rng = (((uint64_t)rand())<<32 | rand()); #if LACE_COUNT_EVENTS - // Reset counters + // Initialize counters { int k; for (k=0; k<CTR_MAX; k++) w->ctr[k] = 0; } #endif - // Set pointers -#ifdef __linux__ - current_worker = w; -#else - pthread_setspecific(worker_key, w); -#endif - workers[worker] = wt; - workers_p[worker] = w; - // Synchronize with others lace_barrier(); @@ -310,8 +449,15 @@ lace_init_worker(int worker, size_t dq_size) w->time = gethrtime(); w->level = 0; #endif + + if (worker == 0) { + lace_time_event(w, 1); + } } +/** + * Some OSX systems do not implement pthread_barrier_t, so we provide an implementation here. + */ #if defined(__APPLE__) && !defined(pthread_barrier_t) typedef int pthread_barrierattr_t; @@ -401,13 +547,13 @@ lace_resume() } /** - * With set_workers, all workers 0..(N-1) are enabled and N..max are disabled. - * You can never disable the current worker or reduce the number of workers below 1. + * Disable worker <worker>. + * If the given worker is the current worker, this function does nothing. */ void -lace_disable_worker(int worker) +lace_disable_worker(unsigned int worker) { - int self = lace_get_worker()->worker; + unsigned int self = lace_get_worker()->worker; if (worker == self) return; if (workers_p[worker]->enabled == 1) { workers_p[worker]->enabled = 0; @@ -415,10 +561,14 @@ lace_disable_worker(int worker) } } +/** + * Enable worker <worker>. + * If the given worker is the current worker, this function does nothing. + */ void -lace_enable_worker(int worker) +lace_enable_worker(unsigned int worker) { - int self = lace_get_worker()->worker; + unsigned int self = lace_get_worker()->worker; if (worker == self) return; if (workers_p[worker]->enabled == 0) { workers_p[worker]->enabled = 1; @@ -426,25 +576,38 @@ lace_enable_worker(int worker) } } +/** + * Enables all workers 0..(N-1) and disables workers N..max. + * This function _should_ be called by worker 0. + * Ignores the current worker if >= N. + * The number of workers is never reduces below 1. + */ void -lace_set_workers(int workercount) -{ int i; +lace_set_workers(unsigned int workercount) +{ if (workercount < 1) workercount = 1; if (workercount > n_workers) workercount = n_workers; enabled_workers = workercount; - int self = lace_get_worker()->worker; + unsigned int self = lace_get_worker()->worker; if (self >= workercount) workercount--; - for ( i=0; i<n_workers; i++) { + for (unsigned int i=0; i<n_workers; i++) { workers_p[i]->enabled = (i < workercount || i == self) ? 1 : 0; } } -int +/** + * Get the number of currently enabled workers. + */ +unsigned int lace_enabled_workers() { return enabled_workers; } +/** + * Simple random number generated (like rand) using the given seed. + * (Used for thread-specific (scalable) random number generation. + */ static inline uint32_t rng(uint32_t *seed, int max) { @@ -458,7 +621,10 @@ rng(uint32_t *seed, int max) return next % max; } -VOID_TASK_IMPL_0(lace_steal_random) +/** + * (Try to) steal and execute a task from a random worker. + */ +VOID_TASK_0(lace_steal_random) { Worker *victim = workers[(__lace_worker->worker + 1 + rng(&__lace_worker->seed, n_workers-1)) % n_workers]; @@ -473,41 +639,36 @@ VOID_TASK_IMPL_0(lace_steal_random) } } -VOID_TASK_IMPL_1(lace_steal_random_loop, int*, quit) -{ - while(!(*(volatile int*)quit)) { - lace_steal_random(); - - if (must_suspend) { - lace_barrier(); - do { - pthread_barrier_wait(&suspend_barrier); - } while (__lace_worker->enabled == 0); - } - } -} - +/** + * Variable to hold the main/root task. + */ static lace_startup_cb main_cb; +/** + * Wrapper around the main/root task. + */ static void* lace_main_wrapper(void *arg) { - lace_init_worker(0, 0); - WorkerP *self = lace_get_worker(); - -#if LACE_PIE_TIMES - self->time = gethrtime(); -#endif - - lace_time_event(self, 1); - main_cb(self, self->dq, arg); + lace_init_worker(0); + lace_pin_worker(); + LACE_ME; + WRAP(main_cb, arg); lace_exit(); + + // Now signal that we're done + pthread_mutex_lock(&wait_until_done_mutex); pthread_cond_broadcast(&wait_until_done); + pthread_mutex_unlock(&wait_until_done_mutex); return NULL; } -VOID_TASK_IMPL_1(lace_steal_loop, int*, quit) +/** + * Main Lace worker implementation. + * Steal from random victims until "quit" is set. + */ +VOID_TASK_1(lace_steal_loop, int*, quit) { // Determine who I am const int worker_id = __lace_worker->worker; @@ -556,15 +717,43 @@ VOID_TASK_IMPL_1(lace_steal_loop, int*, quit) } } -static void* -lace_default_worker(void* arg) +/** + * Initialize worker 0. + * Calls lace_init_worker and then signals the event. + */ +void +lace_init_main() +{ + lace_init_worker(0); +} + +/** + * Initialize the current thread as a Lace thread, and perform work-stealing + * as worker <worker> until lace_exit() is called. + * + * For worker 0, use lace_init_main + */ +void +lace_run_worker(void) { - lace_init_worker((size_t)arg, 0); - WorkerP *__lace_worker = lace_get_worker(); - Task *__lace_dq_head = __lace_worker->dq; - lace_steal_loop(&lace_quits); + // Run the steal loop + LACE_ME; + CALL(lace_steal_loop, &lace_quits); + + // Time worker exit event lace_time_event(__lace_worker, 9); + + // Synchronize with lace_exit lace_barrier(); +} + +static void* +lace_default_worker_thread(void* arg) +{ + int worker = (int)(size_t)arg; + lace_init_worker(worker); + lace_pin_worker(); + lace_run_worker(); return NULL; } @@ -577,24 +766,18 @@ lace_spawn_worker(int worker, size_t stacksize, void* (*fun)(void*), void* arg) size_t pagesize = sysconf(_SC_PAGESIZE); stacksize = (stacksize + pagesize - 1) & ~(pagesize - 1); // ceil(stacksize, pagesize) -#if USE_HWLOC +#if LACE_USE_HWLOC // Get our logical processor hwloc_obj_t pu = hwloc_get_obj_by_type(topo, HWLOC_OBJ_PU, worker % n_pus); // Allocate memory for the program stack - lock_acquire(); void *stack_location = hwloc_alloc_membind(topo, stacksize + pagesize, pu->cpuset, HWLOC_MEMBIND_BIND, 0); - lock_release(); if (stack_location == 0) { fprintf(stderr, "Lace error: Unable to allocate memory for the pthread stack!\n"); exit(1); } #else - void *stack_location = mmap(NULL, stacksize + pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - if (stack_location == MAP_FAILED) { - fprintf(stderr, "Lace error: Cannot allocate program stack: %s!\n", strerror(errno)); - exit(1); - } + void *stack_location = mmap(NULL, stacksize+ pagesize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); #endif if (0 != mprotect(stack_location, pagesize, PROT_NONE)) { @@ -611,7 +794,7 @@ lace_spawn_worker(int worker, size_t stacksize, void* (*fun)(void*), void* arg) workers_init[worker].stacksize = stacksize; if (fun == 0) { - fun = lace_default_worker; + fun = lace_default_worker_thread; arg = (void*)(size_t)worker; } @@ -620,53 +803,47 @@ lace_spawn_worker(int worker, size_t stacksize, void* (*fun)(void*), void* arg) return res; } -static int -get_cpu_count() -{ -#if USE_HWLOC - int count = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_PU); -#elif defined(sched_getaffinity) - /* Best solution: find actual available cpus */ - cpu_set_t cs; - CPU_ZERO(&cs); - sched_getaffinity(0, sizeof(cs), &cs); - int count = CPU_COUNT(&cs); -#elif defined(_SC_NPROCESSORS_ONLN) - /* Fallback */ - int count = sysconf(_SC_NPROCESSORS_ONLN); -#else - /* Okay... */ - int count = 1; -#endif - return count < 1 ? 1 : count; -} - +/** + * Set the verbosity of Lace. + */ void lace_set_verbosity(int level) { verbosity = level; } +/** + * Initialize Lace for work-stealing with <n> workers, where + * each worker gets a task deque with <dqsize> elements. + */ void -lace_init(int n, size_t dqsize) +lace_init(unsigned int _n_workers, size_t dqsize) { -#if USE_HWLOC +#if LACE_USE_HWLOC + // Initialize topology and information about cpus hwloc_topology_init(&topo); hwloc_topology_load(topo); n_nodes = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_NODE); n_cores = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_CORE); n_pus = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_PU); +#elif defined(sched_getaffinity) + cpu_set_t cs; + CPU_ZERO(&cs); + sched_getaffinity(0, sizeof(cs), &cs); + unsigned int n_pus = CPU_COUNT(&cs); +#else + unsigned int n_pus = sysconf(_SC_NPROCESSORS_ONLN); #endif // Initialize globals - n_workers = n; - if (n_workers == 0) n_workers = get_cpu_count(); + n_workers = _n_workers == 0 ? n_pus : _n_workers; enabled_workers = n_workers; if (dqsize != 0) default_dqsize = dqsize; + else dqsize = default_dqsize; lace_quits = 0; - // Create barrier for all workers + // Initialize Lace barrier lace_barrier_init(); // Create suspend barrier @@ -674,11 +851,15 @@ lace_init(int n, size_t dqsize) // Allocate array with all workers if (posix_memalign((void**)&workers, LINE_SIZE, n_workers*sizeof(Worker*)) != 0 || - posix_memalign((void**)&workers_p, LINE_SIZE, n_workers*sizeof(WorkerP*)) != 0) { + posix_memalign((void**)&workers_p, LINE_SIZE, n_workers*sizeof(WorkerP*)) != 0 || + posix_memalign((void**)&workers_memory, LINE_SIZE, n_workers*sizeof(worker_data*)) != 0) { fprintf(stderr, "Lace error: unable to allocate memory!\n"); exit(1); } + // Compute memory size for each worker + workers_memory_size = sizeof(worker_data) + sizeof(Task) * dqsize; + // Create pthread key #ifndef __linux__ pthread_key_create(&worker_key, NULL); @@ -697,10 +878,10 @@ lace_init(int n, size_t dqsize) } if (verbosity) { -#if USE_HWLOC +#if LACE_USE_HWLOC fprintf(stderr, "Initializing Lace, %u nodes, %u cores, %u logical processors, %d workers.\n", n_nodes, n_cores, n_pus, n_workers); #else - fprintf(stderr, "Initializing Lace, %d workers.\n", n_workers); + fprintf(stderr, "Initializing Lace, %u available cores, %d workers.\n", n_pus, n_workers); #endif } @@ -716,11 +897,18 @@ lace_init(int n, size_t dqsize) #endif } +/** + * Start the worker threads. + * If cb is set, then the current thread is suspended and Worker 0 is a new thread that starts with + * the given cb(arg) as the root task. + * If cb is not set, then the current thread is Worker 0 and this function returns. + */ void lace_startup(size_t stacksize, lace_startup_cb cb, void *arg) { if (stacksize == 0) stacksize = default_stacksize; + /* Report startup if verbose */ if (verbosity) { if (cb != 0) { fprintf(stderr, "Lace startup, creating %d worker threads with program stack %zu bytes.\n", n_workers, stacksize); @@ -731,22 +919,21 @@ lace_startup(size_t stacksize, lace_startup_cb cb, void *arg) } } - /* Spawn workers */ - int i; - for (i=1; i<n_workers; i++) lace_spawn_worker(i, stacksize, 0, 0); + /* Spawn all other workers */ + for (unsigned int i=1; i<n_workers; i++) lace_spawn_worker(i, stacksize, 0, 0); if (cb != 0) { + /* If cb set, spawn worker 0 */ main_cb = cb; lace_spawn_worker(0, stacksize, lace_main_wrapper, arg); - // Suspend this thread until cb returns + /* Suspend this thread until cb returns */ pthread_mutex_lock(&wait_until_done_mutex); - pthread_cond_wait(&wait_until_done, &wait_until_done_mutex); + if (lace_quits == 0) pthread_cond_wait(&wait_until_done, &wait_until_done_mutex); pthread_mutex_unlock(&wait_until_done_mutex); } else { - // use this thread as worker and return control - lace_init_worker(0, 0); - lace_time_event(lace_get_worker(), 1); + /* If cb not set, use current thread as worker 0 */ + lace_init_worker(0); } } @@ -754,6 +941,9 @@ lace_startup(size_t stacksize, lace_startup_cb cb, void *arg) static uint64_t ctr_all[CTR_MAX]; #endif +/** + * Reset the counters of Lace. + */ void lace_count_reset() { @@ -779,6 +969,9 @@ lace_count_reset() #endif } +/** + * Report counters to the given file. + */ void lace_count_report_file(FILE *file) { @@ -809,7 +1002,7 @@ lace_count_report_file(FILE *file) workers_p[i]->ctr[CTR_steal_tries], workers_p[i]->ctr[CTR_leaps], workers_p[i]->ctr[CTR_leap_busy], workers_p[i]->ctr[CTR_leap_tries]); } - fprintf(file, "Steals (sum): %zu good/%zu busy of %zu tries; leaps: %zu good/%zu busy of %zu tries\n", + fprintf(file, "Steals (sum): %zu good/%zu busy of %zu tries; leaps: %zu good/%zu busy of %zu tries\n", ctr_all[CTR_steals], ctr_all[CTR_steal_busy], ctr_all[CTR_steal_tries], ctr_all[CTR_leaps], ctr_all[CTR_leap_busy], ctr_all[CTR_leap_tries]); @@ -876,11 +1069,15 @@ lace_count_report_file(FILE *file) (void)file; } +/** + * End Lace. All disabled threads are re-enabled, and then all Workers are signaled to quit. + * This function waits until all threads are done, then returns. + */ void lace_exit() { lace_time_event(lace_get_worker(), 2); - // first suspend all other threads + // first suspend all enabled threads lace_suspend(); // now enable all threads and tell them to quit @@ -946,7 +1143,7 @@ lace_exec_in_new_frame(WorkerP *__lace_worker, Task *__lace_dq_head, Task *root) } } -VOID_TASK_IMPL_2(lace_steal_loop_root, Task*, t, int*, done) +VOID_TASK_2(lace_steal_loop_root, Task*, t, int*, done) { t->f(__lace_worker, __lace_dq_head, t); *done = 1; @@ -958,7 +1155,7 @@ VOID_TASK_2(lace_together_helper, Task*, t, volatile int*, finished) for (;;) { int f = *finished; - if (cas(finished, f, f-1)) break; + if (__sync_bool_compare_and_swap(finished, f, f-1)) break; } while (*finished != 0) STEAL_RANDOM(); @@ -972,7 +1169,7 @@ lace_sync_and_exec(WorkerP *__lace_worker, Task *__lace_dq_head, Task *root) // one worker sets t to 0 again if (LACE_WORKER_ID == 0) lace_newframe.t = 0; - // else while (*(volatile Task**)&lace_newframe.t != 0) {} + // else while (*(Task* volatile *)&lace_newframe.t != 0) {} // the above line is commented out since lace_exec_in_new_frame includes // a lace_barrier before the task is executed @@ -992,7 +1189,7 @@ lace_yield(WorkerP *__lace_worker, Task *__lace_dq_head) // one worker sets t to 0 again if (LACE_WORKER_ID == 0) lace_newframe.t = 0; - // else while (*(volatile Task**)&lace_newframe.t != 0) {} + // else while (*(Task* volatile *)&lace_newframe.t != 0) {} // the above line is commented out since lace_exec_in_new_frame includes // a lace_barrier before the task is executed @@ -1014,7 +1211,7 @@ lace_do_together(WorkerP *__lace_worker, Task *__lace_dq_head, Task *t) t2->d.args.arg_1 = t; t2->d.args.arg_2 = &done; - while (!cas(&lace_newframe.t, 0, &_t2)) lace_yield(__lace_worker, __lace_dq_head); + while (!__sync_bool_compare_and_swap(&lace_newframe.t, 0, &_t2)) lace_yield(__lace_worker, __lace_dq_head); lace_sync_and_exec(__lace_worker, __lace_dq_head, &_t2); } @@ -1041,6 +1238,16 @@ lace_do_newframe(WorkerP *__lace_worker, Task *__lace_dq_head, Task *t) compiler_barrier(); - while (!cas(&lace_newframe.t, 0, &_s)) lace_yield(__lace_worker, __lace_dq_head); + while (!__sync_bool_compare_and_swap(&lace_newframe.t, 0, &_s)) lace_yield(__lace_worker, __lace_dq_head); lace_sync_and_exec(__lace_worker, __lace_dq_head, &_t2); } + +/** + * Called by _SPAWN functions when the Task stack is full. + */ +void +lace_abort_stack_overflow(void) +{ + fprintf(stderr, "Lace fatal error: Task stack overflow! Aborting.\n"); + exit(-1); +} diff --git a/lace.h b/lace.h old mode 100644 new mode 100755 index 645baa4c94fbc28cb5dfd8f973406df2b392adc0..0b4b7a06f61b07911ddc10a7935fd2aa3d5fad6c --- a/lace.h +++ b/lace.h @@ -1,5 +1,6 @@ -/* - * Copyright 2013-2015 Formal Methods and Tools, University of Twente +/* + * Copyright 2013-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -22,40 +23,281 @@ #ifndef __LACE_H__ #define __LACE_H__ +#ifdef __has_include +# if __has_include("lace_config.h") +# include <lace_config.h> +# else +# define LACE_PIE_TIMES 0 +# define LACE_COUNT_TASKS 0 +# define LACE_COUNT_STEALS 0 +# define LACE_COUNT_SPLITS 0 +# define LACE_USE_HWLOC 0 +# endif +#endif + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -/* Some flags */ +/** + * Using Lace. + * + * Optionally set the verbosity level with lace_set_verbosity. + * Then call lace_init to initialize the system. + * - lace_init(n_workers, deque_size); + * set both parameters to 0 for reasonable defaults, using all available cores. + * + * You can create Worker threads yourself or let Lace create threads with lace_startup. + * + * When creating threads yourself, call the following functions: + * - lace_init_worker to allocate and initialize the worker data structures + * this method returns when all workers have called lace_init_worker + * - lace_pin_worker (optional) to pin the thread and memory to a core + * The main worker can now start its root task. All other workers: + * - lace_run_worker to perform work-stealing until the main worker calls lace_exit + * + * When letting Lace create threads with lace_startup + * - Call lace_startup with a callback to create N threads. + * Returns after the callback has returned and all created threads are destroyed + * - Call lace_startup without a callback to create N-1 threads. + * Returns control to the caller. When lace_exit is called, all created threads are terminated. + */ -#ifndef LACE_DEBUG_PROGRAMSTACK /* Write to stderr when 95% program stack reached */ -#define LACE_DEBUG_PROGRAMSTACK 0 -#endif +/** + * Type definitions used in the functions below. + * - WorkerP contains the (private) Worker data + * - Task contains a single Task + */ +typedef struct _WorkerP WorkerP; +typedef struct _Task Task; -#ifndef LACE_LEAP_RANDOM /* Use random leaping when leapfrogging fails */ -#define LACE_LEAP_RANDOM 1 -#endif +/** + * The macro LACE_TYPEDEF_CB(typedefname, taskname, parametertypes) defines + * a Task for use as a callback function. + */ +#define LACE_TYPEDEF_CB(t, f, ...) typedef t (*f)(WorkerP *, Task *, ##__VA_ARGS__); -#ifndef LACE_PIE_TIMES /* Record time spent stealing and leapfrogging */ -#define LACE_PIE_TIMES 0 -#endif +/** + * The lace_startup_cb type for a void Task with one void* parameter. + */ +LACE_TYPEDEF_CB(void, lace_startup_cb, void*); -#ifndef LACE_COUNT_TASKS /* Count number of tasks executed */ -#define LACE_COUNT_TASKS 0 -#endif +/** + * Set verbosity level (0 = no startup messages, 1 = startup messages) + * Default level: 0 + */ +void lace_set_verbosity(int level); + +/** + * Initialize Lace for <n_workers> workers with a deque size of <dqsize> per worker. + * If <n_workers> is set to 0, automatically detects available cores. + * If <dqsize> is est to 0, uses a reasonable default value. + */ +void lace_init(unsigned int n_workers, size_t dqsize); + +/** + * Let Lace create worker threads. + * If <stacksize> is set to 0, uses a reaonable default value. + * If cb, arg are set to 0, then the current thread is initialized as the main Worker (Worker 0). + * + * If cb,arg are set, then the current thread is suspended. A new thread is made for Worker 0 and + * the task cb with paremeter arg is called; when cb returns, Lace is exited automatically. + */ +void lace_startup(size_t stacksize, lace_startup_cb, void* arg); + +/** + * Initialize worker <worker>, allocating memory. + * If <worker> is 0, then the current thread is the main worker. + */ +void lace_init_worker(unsigned int worker); + +/** + * Use hwloc to pin the current thread to a CPU and its allocated memory in the closest domain. + * Call this *after* lace_init_worker and *before* lace_run_worker. + */ +void lace_pin_worker(void); + +/** + * Perform work-stealing until lace_exit is called. + */ +void lace_run_worker(void); + +/** + * Steal a random task. + */ +#define lace_steal_random() CALL(lace_steal_random) +void lace_steal_random_CALL(WorkerP*, Task*); + +/** + * Enter the Lace barrier. (all active workers must enter it before we can continue) + */ +void lace_barrier(); + +/** + * Suspend all workers except the current worker. + * May only be used when all other workers are idle. + */ +void lace_suspend(); + +/** + * Resume all workers. + */ +void lace_resume(); + +/** + * When all other workers are suspended, some workers can be disabled using the following functions. + * With set_workers, all workers 0..(N-1) are enabled and N..max are disabled. + * You can never disable the current worker or reduce the number of workers below 1. + * You cannot add workers. + */ +void lace_set_workers(unsigned int workercount); + +/** + * Disable a suspended worker. + */ +void lace_disable_worker(unsigned int worker); + +/** + * Enable a suspended worker. + */ +void lace_enable_worker(unsigned int worker); + +/** + * Retrieve the number of enabled/active workers. + */ +unsigned int lace_enabled_workers(); + +/** + * Retrieve the number of Lace workers + */ +unsigned int lace_workers(); + +/** + * Retrieve the default program stack size + */ +size_t lace_default_stacksize(); + +/** + * Retrieve the current worker data. + */ +WorkerP *lace_get_worker(); + +/** + * Retrieve the current head of the deque + */ +Task *lace_get_head(WorkerP *); + +/** + * Exit Lace. + * This function is automatically called when lace_startup is called with a callback. + * This function must be called to exit Lace when lace_startup is called without a callback. + */ +void lace_exit(); + +/** + * Create a pointer to a Tasks main function. + */ +#define TASK(f) ( f##_CALL ) + +/** + * Call a Tasks implementation (adds Lace variables to call) + */ +#define WRAP(f, ...) ( f((WorkerP *)__lace_worker, (Task *)__lace_dq_head, ##__VA_ARGS__) ) + +/** + * Sync a task. + */ +#define SYNC(f) ( __lace_dq_head--, WRAP(f##_SYNC) ) + +/** + * Sync a task, but if the task is not stolen, then do not execute it. + */ +#define DROP() ( __lace_dq_head--, WRAP(lace_drop) ) + +/** + * Spawn a task. + */ +#define SPAWN(f, ...) ( WRAP(f##_SPAWN, ##__VA_ARGS__), __lace_dq_head++ ) + +/** + * Directly execute a task. + */ +#define CALL(f, ...) ( WRAP(f##_CALL, ##__VA_ARGS__) ) + +/** + * Signal all workers to interrupt their current tasks and instead perform (a personal copy of) the given task. + */ +#define TOGETHER(f, ...) ( WRAP(f##_TOGETHER, ##__VA_ARGS__) ) + +/** + * Signal all workers to interrupt their current tasks and help the current thread with the given task. + */ +#define NEWFRAME(f, ...) ( WRAP(f##_NEWFRAME, ##__VA_ARGS__) ) + +/** + * (Try to) steal a task from a random worker. + */ +#define STEAL_RANDOM() ( CALL(lace_steal_random) ) -#ifndef LACE_COUNT_STEALS /* Count number of steals performed */ -#define LACE_COUNT_STEALS 0 +/** + * Get the current worker id. + */ +#define LACE_WORKER_ID ( __lace_worker->worker ) + +/** + * Get the core where the current worker is pinned. + */ +#define LACE_WORKER_PU ( __lace_worker->pu ) + +/** + * Initialize local variables __lace_worker and __lace_dq_head which are required for most Lace functionality. + */ +#define LACE_ME WorkerP * __attribute__((unused)) __lace_worker = lace_get_worker(); Task * __attribute__((unused)) __lace_dq_head = lace_get_head(__lace_worker); + +/** + * Check if current tasks must be interrupted, and if so, interrupt. + */ +void lace_yield(WorkerP *__lace_worker, Task *__lace_dq_head); +#define YIELD_NEWFRAME() { if (unlikely((*(Task* volatile *)&lace_newframe.t) != NULL)) lace_yield(__lace_worker, __lace_dq_head); } + +/** + * True if the given task is stolen, False otherwise. + */ +#define TASK_IS_STOLEN(t) ((size_t)t->thief > 1) + +/** + * True if the given task is completed, False otherwise. + */ +#define TASK_IS_COMPLETED(t) ((size_t)t->thief == 2) + +/** + * Retrieves a pointer to the result of the given task. + */ +#define TASK_RESULT(t) (&t->d[0]) + +/** + * Compute a random number, thread-local (so scalable) + */ +#define LACE_TRNG (__lace_worker->rng = 2862933555777941757ULL * __lace_worker->rng + 3037000493ULL) + +/* Some flags that influence Lace behavior */ + +#ifndef LACE_DEBUG_PROGRAMSTACK /* Write to stderr when 95% program stack reached */ +#define LACE_DEBUG_PROGRAMSTACK 0 #endif -#ifndef LACE_COUNT_SPLITS /* Count number of times the split point is moved */ -#define LACE_COUNT_SPLITS 0 +#ifndef LACE_LEAP_RANDOM /* Use random leaping when leapfrogging fails */ +#define LACE_LEAP_RANDOM 1 #endif #ifndef LACE_COUNT_EVENTS #define LACE_COUNT_EVENTS (LACE_PIE_TIMES || LACE_COUNT_TASKS || LACE_COUNT_STEALS || LACE_COUNT_SPLITS) #endif +/** + * Now follows the implementation of Lace + */ + /* Typical cacheline size of system architectures */ #ifndef LINE_SIZE #define LINE_SIZE 64 @@ -166,10 +408,6 @@ typedef enum { CTR_MAX } CTR_index; -struct _WorkerP; -struct _Worker; -struct _Task; - #define THIEF_EMPTY ((struct _Worker*)0x0) #define THIEF_TASK ((struct _Worker*)0x1) #define THIEF_COMPLETED ((struct _Worker*)0x2) @@ -212,8 +450,9 @@ typedef struct _WorkerP { Task *end; // dq+dq_size Worker *_public; // pointer to public Worker struct size_t stack_trigger; // for stack overflow detection - int16_t worker; // what is my worker id? - int16_t pu; // my pu (for HWLOC) + uint64_t rng; // my random seed (for lace_trng) + uint32_t seed; // my random seed (for lace_steal_random) + uint16_t worker; // what is my worker id? uint8_t allstolen; // my allstolen volatile int8_t enabled; // if this worker is enabled @@ -223,131 +462,13 @@ typedef struct _WorkerP { volatile int level; #endif - uint32_t seed; // my random seed (for lace_steal_random) + int16_t pu; // my pu (for HWLOC) } WorkerP; -#define LACE_TYPEDEF_CB(t, f, ...) typedef t (*f)(WorkerP *, Task *, ##__VA_ARGS__); -LACE_TYPEDEF_CB(void, lace_startup_cb, void*); - -/** - * Set verbosity level (0 = no startup messages, 1 = startup messages) - * Default level: 0 - */ -void lace_set_verbosity(int level); - -/** - * Initialize master structures for Lace with <n_workers> workers - * and default deque size of <dqsize>. - * Does not create new threads. - * Tries to detect number of cpus, if n_workers equals 0. - */ -void lace_init(int n_workers, size_t dqsize); - -/** - * After lace_init, start all worker threads. - * If cb,arg are set, suspend this thread, call cb(arg) in a new thread - * and exit Lace upon return - * Otherwise, the current thread is initialized as a Lace thread. - */ -void lace_startup(size_t stacksize, lace_startup_cb, void* arg); - -/** - * Initialize current thread as worker <idx> and allocate a deque with size <dqsize>. - * Use this when manually creating worker threads. - */ -void lace_init_worker(int idx, size_t dqsize); - -/** - * Manually spawn worker <idx> with (optional) program stack size <stacksize>. - * If fun,arg are set, overrides default startup method. - * Typically: for workers 1...(n_workers-1): lace_spawn_worker(i, stack_size, 0, 0); - */ -pthread_t lace_spawn_worker(int idx, size_t stacksize, void *(*fun)(void*), void* arg); - -/** - * Steal a random task. - */ -#define lace_steal_random() CALL(lace_steal_random) -void lace_steal_random_CALL(WorkerP*, Task*); - -/** - * Steal random tasks until parameter *quit is set - * Note: task declarations at end; quit is of type int* - */ -#define lace_steal_random_loop(quit) CALL(lace_steal_random_loop, quit) -#define lace_steal_loop(quit) CALL(lace_steal_loop, quit) - -/** - * Barrier (all workers must enter it before progressing) - */ -void lace_barrier(); - -/** - * Suspend and resume all other workers. - * May only be used when all other workers are idle. - */ -void lace_suspend(); -void lace_resume(); - -/** - * When all tasks are suspended, workers can be temporarily disabled. - * With set_workers, all workers 0..(N-1) are enabled and N..max are disabled. - * You can never disable the current worker or reduce the number of workers below 1. - * You cannot add workers. - */ -void lace_disable_worker(int worker); -void lace_enable_worker(int worker); -void lace_set_workers(int workercount); -int lace_enabled_workers(); - -/** - * Retrieve number of Lace workers - */ -size_t lace_workers(); - -/** - * Retrieve default program stack size - */ -size_t lace_default_stacksize(); - -/** - * Retrieve current worker. - */ -WorkerP *lace_get_worker(); - -/** - * Retrieve the current head of the deque - */ -Task *lace_get_head(WorkerP *); - -/** - * Exit Lace. Automatically called when started with cb,arg. - */ -void lace_exit(); - #define LACE_STOLEN ((Worker*)0) #define LACE_BUSY ((Worker*)1) #define LACE_NOWORK ((Worker*)2) -#define TASK(f) ( f##_CALL ) -#define WRAP(f, ...) ( f((WorkerP *)__lace_worker, (Task *)__lace_dq_head, ##__VA_ARGS__) ) -#define SYNC(f) ( __lace_dq_head--, WRAP(f##_SYNC) ) -#define DROP() ( __lace_dq_head--, WRAP(lace_drop) ) -#define SPAWN(f, ...) ( WRAP(f##_SPAWN, ##__VA_ARGS__), __lace_dq_head++ ) -#define CALL(f, ...) ( WRAP(f##_CALL, ##__VA_ARGS__) ) -#define TOGETHER(f, ...) ( WRAP(f##_TOGETHER, ##__VA_ARGS__) ) -#define NEWFRAME(f, ...) ( WRAP(f##_NEWFRAME, ##__VA_ARGS__) ) -#define STEAL_RANDOM() ( CALL(lace_steal_random) ) -#define LACE_WORKER_ID ( __lace_worker->worker ) -#define LACE_WORKER_PU ( __lace_worker->pu ) - -/* Use LACE_ME to initialize Lace variables, in case you want to call multiple Lace tasks */ -#define LACE_ME WorkerP * __attribute__((unused)) __lace_worker = lace_get_worker(); Task * __attribute__((unused)) __lace_dq_head = lace_get_head(__lace_worker); - -#define TASK_IS_STOLEN(t) ((size_t)t->thief > 1) -#define TASK_IS_COMPLETED(t) ((size_t)t->thief == 2) -#define TASK_RESULT(t) (&t->d[0]) - #if LACE_DEBUG_PROGRAMSTACK static inline void CHECKSTACK(WorkerP *w) { @@ -364,6 +485,8 @@ static inline void CHECKSTACK(WorkerP *w) #define CHECKSTACK(w) {} #endif +void lace_abort_stack_overflow(void) __attribute__((noreturn)); + typedef struct { Task *t; @@ -381,8 +504,18 @@ extern lace_newframe_t lace_newframe; void lace_do_together(WorkerP *__lace_worker, Task *__lace_dq_head, Task *task); void lace_do_newframe(WorkerP *__lace_worker, Task *__lace_dq_head, Task *task); -void lace_yield(WorkerP *__lace_worker, Task *__lace_dq_head); -#define YIELD_NEWFRAME() { if (unlikely((*(volatile Task**)&lace_newframe.t) != NULL)) lace_yield(__lace_worker, __lace_dq_head); } +/** + * Make all tasks of the current worker shared. + */ +#define LACE_MAKE_ALL_SHARED() lace_make_all_shared(__lace_worker, __lace_dq_head) +static inline void __attribute__((unused)) +lace_make_all_shared( WorkerP *w, Task *__lace_dq_head) +{ + if (w->split != __lace_dq_head) { + w->split = __lace_dq_head; + w->_public->ts.ts.split = __lace_dq_head - w->dq; + } +} #if LACE_PIE_TIMES static void lace_time_event( WorkerP *w, int event ) @@ -636,7 +769,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head ) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -786,7 +919,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head ) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -939,7 +1072,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1089,7 +1222,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1242,7 +1375,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1392,7 +1525,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2) TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1545,7 +1678,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1695,7 +1828,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1848,7 +1981,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -1998,7 +2131,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -2151,7 +2284,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -2301,7 +2434,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -2454,7 +2587,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -2604,7 +2737,7 @@ void NAME##_SPAWN(WorkerP *w, Task *__dq_head , ATYPE_1 arg_1, ATYPE_2 arg_2, AT TailSplit ts; \ uint32_t head, split, newsplit; \ \ - /* assert(__dq_head < w->end); */ /* Assuming to be true */ \ + if (__dq_head == w->end) lace_abort_stack_overflow(); \ \ t = (TD_##NAME *)__dq_head; \ t->f = &NAME##_WRAP; \ @@ -2731,10 +2864,6 @@ void NAME##_WORK(WorkerP *__lace_worker __attribute__((unused)), Task *__lace_dq #define VOID_TASK_6(NAME, ATYPE_1, ARG_1, ATYPE_2, ARG_2, ATYPE_3, ARG_3, ATYPE_4, ARG_4, ATYPE_5, ARG_5, ATYPE_6, ARG_6) VOID_TASK_DECL_6(NAME, ATYPE_1, ATYPE_2, ATYPE_3, ATYPE_4, ATYPE_5, ATYPE_6) VOID_TASK_IMPL_6(NAME, ATYPE_1, ARG_1, ATYPE_2, ARG_2, ATYPE_3, ARG_3, ATYPE_4, ARG_4, ATYPE_5, ARG_5, ATYPE_6, ARG_6) -VOID_TASK_DECL_0(lace_steal_random); -VOID_TASK_DECL_1(lace_steal_random_loop, int*); -VOID_TASK_DECL_1(lace_steal_loop, int*); -VOID_TASK_DECL_2(lace_steal_loop_root, Task *, int*); #ifdef __cplusplus } diff --git a/sha2.c b/sha2.c old mode 100644 new mode 100755 index db17dbb2d726b42586cc1169e6f08a7eb8397c3f..69c7e7d5afaeb298f285170e687e9323864969f7 --- a/sha2.c +++ b/sha2.c @@ -509,9 +509,6 @@ void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { context->state[5] += f; context->state[6] += g; context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; } #endif /* SHA2_UNROLL_TRANSFORM */ @@ -543,8 +540,6 @@ void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { /* The buffer is not yet full */ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); context->bitcount += len << 3; - /* Clean up: */ - usedspace = freespace = 0; return; } } @@ -560,8 +555,6 @@ void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { MEMCPY_BCOPY(context->buffer, data, len); context->bitcount += len << 3; } - /* Clean up: */ - usedspace = freespace = 0; } void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { @@ -625,7 +618,6 @@ void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { /* Clean up state data: */ MEMSET_BZERO(context, sizeof(SHA256_CTX)); - usedspace = 0; } char *SHA256_End(SHA256_CTX* context, char buffer[]) { @@ -832,9 +824,6 @@ void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { context->state[5] += f; context->state[6] += g; context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; } #endif /* SHA2_UNROLL_TRANSFORM */ @@ -866,8 +855,6 @@ void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { /* The buffer is not yet full */ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); ADDINC128(context->bitcount, len << 3); - /* Clean up: */ - usedspace = freespace = 0; return; } } @@ -883,8 +870,6 @@ void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { MEMCPY_BCOPY(context->buffer, data, len); ADDINC128(context->bitcount, len << 3); } - /* Clean up: */ - usedspace = freespace = 0; } void SHA512_Last(SHA512_CTX* context) { diff --git a/sha2.h b/sha2.h old mode 100644 new mode 100755 diff --git a/stats.c b/stats.c old mode 100644 new mode 100755 diff --git a/stats.h b/stats.h old mode 100644 new mode 100755 diff --git a/sylvan.h b/sylvan.h old mode 100644 new mode 100755 index 25d8fc15ea7414f0528a3f986995b415228489d5..21d70c6003bd66be969bed570e005242f1a4f642 --- a/sylvan.h +++ b/sylvan.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,169 +16,50 @@ */ /** - * Sylvan: parallel BDD/ListDD package. - * - * This is a multi-core implementation of BDDs with complement edges. - * - * This package requires parallel the work-stealing framework Lace. - * Lace must be initialized before initializing Sylvan - * - * This package uses explicit referencing. - * Use sylvan_ref and sylvan_deref to manage external references. - * - * Garbage collection requires all workers to cooperate. Garbage collection is either initiated - * by the user (calling sylvan_gc) or when the nodes table is full. All Sylvan operations - * check whether they need to cooperate on garbage collection. Garbage collection cannot occur - * otherwise. This means that it is perfectly fine to do this: - * BDD a = sylvan_ref(sylvan_and(b, c)); - * since it is not possible that garbage collection occurs between the two calls. - * - * To temporarily disable garbage collection, use sylvan_gc_disable() and sylvan_gc_enable(). + * Sylvan: parallel MTBDD/ListDD package. + * Include this file. */ #include <sylvan_config.h> +#include <assert.h> +#include <stddef.h> #include <stdint.h> #include <stdio.h> // for FILE -#include <stdlib.h> -#include <lace.h> // for definitions - -#include <sylvan_cache.h> -#include <llmsset.h> -#include <stats.h> - -#ifndef SYLVAN_H -#define SYLVAN_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef SYLVAN_SIZE_FIBONACCI -#define SYLVAN_SIZE_FIBONACCI 0 +#include <stdlib.h> // for realloc +#include <unistd.h> +#include <pthread.h> + +#if SYLVAN_STATS +#ifdef __MACH__ +#include <mach/mach_time.h> +#else +#include <time.h> +#endif #endif - -// For now, only support 64-bit systems -typedef char __sylvan_check_size_t_is_8_bytes[(sizeof(uint64_t) == sizeof(size_t))?1:-1]; - -/** - * Initialize the Sylvan parallel decision diagrams package. - * - * After initialization, call sylvan_init_bdd and/or sylvan_init_ldd if you want to use - * the BDD and/or LDD functionality. - * - * BDDs and LDDs share a common node table and operations cache. - * - * The node table is resizable. - * The table is resized automatically when >50% of the table is filled during garbage collection. - * This behavior can be customized by overriding the gc hook. - * - * Memory usage: - * Every node requires 24 bytes memory. (16 bytes data + 8 bytes overhead) - * Every operation cache entry requires 36 bytes memory. (32 bytes data + 4 bytes overhead) - * - * Reasonable defaults: datasize of 1L<<26 (2048 MB), cachesize of 1L<<25 (1152 MB) - */ -void sylvan_init_package(size_t initial_tablesize, size_t max_tablesize, size_t initial_cachesize, size_t max_cachesize); - -/** - * Frees all Sylvan data (also calls the quit() functions of BDD/MDD parts) - */ -void sylvan_quit(); - -/** - * Return number of occupied buckets in nodes table and total number of buckets. - */ -VOID_TASK_DECL_2(sylvan_table_usage, size_t*, size_t*); -#define sylvan_table_usage(filled, total) (CALL(sylvan_table_usage, filled, total)) - -/** - * Perform garbage collection. - * - * Garbage collection is performed in a new Lace frame, interrupting all ongoing work - * until garbage collection is completed. - * - * Garbage collection procedure: - * 1) The operation cache is cleared and the hash table is reset. - * 2) All live nodes are marked (to be rehashed). This is done by the "mark" callbacks. - * 3) The "hook" callback is called. - * By default, this doubles the hash table size when it is >50% full. - * 4) All live nodes are rehashed into the hash table. - * - * The behavior of garbage collection can be customized by adding "mark" callbacks and - * replacing the "hook" callback. - */ -VOID_TASK_DECL_0(sylvan_gc); -#define sylvan_gc() (CALL(sylvan_gc)) - -/** - * Enable or disable garbage collection. - * - * This affects both automatic and manual garbage collection, i.e., - * calling sylvan_gc() while garbage collection is disabled does not have any effect. - */ -void sylvan_gc_enable(); -void sylvan_gc_disable(); - -/** - * Add a "mark" callback to the list of callbacks. - * - * These are called during garbage collection to recursively mark nodes. - * - * Default "mark" functions that mark external references (via sylvan_ref) and internal - * references (inside operations) are added by sylvan_init_bdd/sylvan_init_bdd. - * - * Functions are called in order. - * level 10: marking functions of Sylvan (external/internal references) - * level 20: call the hook function (for resizing) - * level 30: rehashing - */ -LACE_TYPEDEF_CB(void, gc_mark_cb); -void sylvan_gc_add_mark(int order, gc_mark_cb callback); - -/** - * Set "hook" callback. There can be only one. - * - * The hook is called after the "mark" phase and before the "rehash" phase. - * This allows users to perform certain actions, such as resizing the nodes table - * and the operation cache. Also, dynamic resizing could be performed then. - */ -LACE_TYPEDEF_CB(void, gc_hook_cb); -void sylvan_gc_set_hook(gc_hook_cb new_hook); /** - * One of the hooks for resizing behavior. - * Default if SYLVAN_AGGRESSIVE_RESIZE is set. - * Always double size on gc() until maximum reached. + * Sylvan header files outside the namespace */ -VOID_TASK_DECL_0(sylvan_gc_aggressive_resize); -/** - * One of the hooks for resizing behavior. - * Default if SYLVAN_AGGRESSIVE_RESIZE is not set. - * Double size on gc() whenever >50% is used. - */ -VOID_TASK_DECL_0(sylvan_gc_default_hook); +#include <lace.h> +#include <sylvan_tls.h> -/** - * Set "notify on dead" callback for the nodes table. - * See also documentation in llmsset.h - */ -#define sylvan_set_ondead(cb, ctx) llmsset_set_ondead(nodes, cb, ctx) +#ifdef __cplusplus +namespace sylvan { +#endif /** - * Global variables (number of workers, nodes table) + * Sylvan header files inside the namespace */ -extern llmsset_t nodes; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - +#include <sylvan_common.h> +#include <sylvan_stats.h> +#include <sylvan_mt.h> +#include <sylvan_mtbdd.h> #include <sylvan_bdd.h> #include <sylvan_ldd.h> -#include <sylvan_seq.h> -#include <sylvan_mtbdd.h> +#ifdef __cplusplus +} #endif diff --git a/sylvan_bdd.c b/sylvan_bdd.c old mode 100644 new mode 100755 index 45b59c411bae3820126710dd45497f2f81f29882..2d910e295b7a1164cdd230c0a61e36b864764f44 --- a/sylvan_bdd.c +++ b/sylvan_bdd.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2014 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,373 +15,32 @@ * limitations under the License. */ -#include <sylvan_config.h> +#include <sylvan_int.h> -#include <assert.h> #include <inttypes.h> #include <math.h> -#include <pthread.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> #include <avl.h> -#include <refs.h> -#include <sha2.h> -#include <sylvan.h> -#include <sylvan_common.h> - -/** - * Complement handling macros - */ -#define BDD_HASMARK(s) (s&sylvan_complement?1:0) -#define BDD_TOGGLEMARK(s) (s^sylvan_complement) -#define BDD_STRIPMARK(s) (s&~sylvan_complement) -#define BDD_TRANSFERMARK(from, to) (to ^ (from & sylvan_complement)) -// Equal under mark -#define BDD_EQUALM(a, b) ((((a)^(b))&(~sylvan_complement))==0) - -/** - * BDD node structure - */ -typedef struct __attribute__((packed)) bddnode { - uint64_t a, b; -} * bddnode_t; // 16 bytes - -#define GETNODE(bdd) ((bddnode_t)llmsset_index_to_ptr(nodes, bdd&0x000000ffffffffff)) - -static inline int __attribute__((unused)) -bddnode_getcomp(bddnode_t n) -{ - return n->a & 0x8000000000000000 ? 1 : 0; -} - -static inline uint64_t -bddnode_getlow(bddnode_t n) -{ - return n->b & 0x000000ffffffffff; // 40 bits -} - -static inline uint64_t -bddnode_gethigh(bddnode_t n) -{ - return n->a & 0x800000ffffffffff; // 40 bits plus high bit of first -} - -static inline uint32_t -bddnode_getvariable(bddnode_t n) -{ - return (uint32_t)(n->b >> 40); -} - -static inline int -bddnode_getmark(bddnode_t n) -{ - return n->a & 0x2000000000000000 ? 1 : 0; -} - -static inline void -bddnode_setmark(bddnode_t n, int mark) -{ - if (mark) n->a |= 0x2000000000000000; - else n->a &= 0xdfffffffffffffff; -} - -static inline void -bddnode_makenode(bddnode_t n, uint32_t var, uint64_t low, uint64_t high) -{ - n->a = high; - n->b = ((uint64_t)var)<<40 | low; -} - -/** - * Implementation of garbage collection. - */ - -/* Recursively mark BDD nodes as 'in use' */ -VOID_TASK_IMPL_1(sylvan_gc_mark_rec, syBDD, bdd) -{ - if (bdd == sylvan_false || bdd == sylvan_true) return; - - if (llmsset_mark(nodes, bdd&0x000000ffffffffff)) { - bddnode_t n = GETNODE(bdd); - SPAWN(sylvan_gc_mark_rec, bddnode_getlow(n)); - CALL(sylvan_gc_mark_rec, bddnode_gethigh(n)); - SYNC(sylvan_gc_mark_rec); - } -} - -/** - * External references - */ - -refs_table_t bdd_refs; -refs_table_t bdd_protected; -static int bdd_protected_created = 0; - -syBDD -sylvan_ref(syBDD a) -{ - if (a == sylvan_false || a == sylvan_true) return a; - refs_up(&bdd_refs, BDD_STRIPMARK(a)); - return a; -} - -void -sylvan_deref(syBDD a) -{ - if (a == sylvan_false || a == sylvan_true) return; - refs_down(&bdd_refs, BDD_STRIPMARK(a)); -} - -void -sylvan_protect(syBDD *a) -{ - if (!bdd_protected_created) { - // In C++, sometimes sylvan_protect is called before Sylvan is initialized. Just create a table. - protect_create(&bdd_protected, 4096); - bdd_protected_created = 1; - } - protect_up(&bdd_protected, (size_t)a); -} - -void -sylvan_unprotect(syBDD *a) -{ - if (bdd_protected.refs_table != NULL) protect_down(&bdd_protected, (size_t)a); -} - -size_t -sylvan_count_refs() -{ - return refs_count(&bdd_refs); -} - -size_t -sylvan_count_protected() -{ - return protect_count(&bdd_protected); -} - -/* Called during garbage collection */ -VOID_TASK_0(sylvan_gc_mark_external_refs) -{ - // iterate through refs hash table, mark all found - size_t count=0; - uint64_t *it = refs_iter(&bdd_refs, 0, bdd_refs.refs_size); - while (it != NULL) { - syBDD to_mark = refs_next(&bdd_refs, &it, bdd_refs.refs_size); - SPAWN(sylvan_gc_mark_rec, to_mark); - count++; - } - while (count--) { - SYNC(sylvan_gc_mark_rec); - } -} - -VOID_TASK_0(sylvan_gc_mark_protected) -{ - // iterate through refs hash table, mark all found - size_t count=0; - uint64_t *it = protect_iter(&bdd_protected, 0, bdd_protected.refs_size); - while (it != NULL) { - syBDD *to_mark = (syBDD*)protect_next(&bdd_protected, &it, bdd_protected.refs_size); - SPAWN(sylvan_gc_mark_rec, *to_mark); - count++; - } - while (count--) { - SYNC(sylvan_gc_mark_rec); - } -} - -/* Infrastructure for internal markings */ -DECLARE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - -VOID_TASK_0(bdd_refs_mark_task) -{ - LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - size_t i, j=0; - for (i=0; i<bdd_refs_key->r_count; i++) { - if (j >= 40) { - while (j--) SYNC(sylvan_gc_mark_rec); - j=0; - } - SPAWN(sylvan_gc_mark_rec, bdd_refs_key->results[i]); - j++; - } - for (i=0; i<bdd_refs_key->s_count; i++) { - Task *t = bdd_refs_key->spawns[i]; - if (!TASK_IS_STOLEN(t)) break; - if (TASK_IS_COMPLETED(t)) { - if (j >= 40) { - while (j--) SYNC(sylvan_gc_mark_rec); - j=0; - } - SPAWN(sylvan_gc_mark_rec, *(syBDD*)TASK_RESULT(t)); - j++; - } - } - while (j--) SYNC(sylvan_gc_mark_rec); -} - -VOID_TASK_0(bdd_refs_mark) -{ - TOGETHER(bdd_refs_mark_task); -} - -VOID_TASK_0(bdd_refs_init_task) -{ - bdd_refs_internal_t s = (bdd_refs_internal_t)malloc(sizeof(struct bdd_refs_internal)); - s->r_size = 128; - s->r_count = 0; - s->s_size = 128; - s->s_count = 0; - s->results = (syBDD*)malloc(sizeof(syBDD) * 128); - s->spawns = (Task**)malloc(sizeof(Task*) * 128); - SET_THREAD_LOCAL(bdd_refs_key, s); -} - -VOID_TASK_0(bdd_refs_init) -{ - INIT_THREAD_LOCAL(bdd_refs_key); - TOGETHER(bdd_refs_init_task); - sylvan_gc_add_mark(10, TASK(bdd_refs_mark)); -} - -/** - * Initialize and quit functions - */ static int granularity = 1; // default -static void -sylvan_quit_bdd() -{ - refs_free(&bdd_refs); - if (bdd_protected_created) { - protect_free(&bdd_protected); - bdd_protected_created = 0; - } -} - void -sylvan_init_bdd(int _granularity) +sylvan_set_granularity(int value) { - sylvan_register_quit(sylvan_quit_bdd); - sylvan_gc_add_mark(10, TASK(sylvan_gc_mark_external_refs)); - sylvan_gc_add_mark(10, TASK(sylvan_gc_mark_protected)); - - granularity = _granularity; - - // Sanity check - if (sizeof(struct bddnode) != 16) { - fprintf(stderr, "Invalid size of bdd nodes: %ld\n", sizeof(struct bddnode)); - exit(1); - } - - refs_create(&bdd_refs, 1024); - if (!bdd_protected_created) { - protect_create(&bdd_protected, 4096); - bdd_protected_created = 1; - } - - LACE_ME; - CALL(bdd_refs_init); + granularity = value; } -/** - * Core BDD operations - */ - -syBDD -sylvan_makenode(BDDVAR level, syBDD low, syBDD high) -{ - if (low == high) return low; - - // Normalization to keep canonicity - // low will have no mark - - struct bddnode n; - int mark; - - if (BDD_HASMARK(low)) { - mark = 1; - low = BDD_TOGGLEMARK(low); - high = BDD_TOGGLEMARK(high); - } else { - mark = 0; - } - - bddnode_makenode(&n, level, low, high); - - syBDD result; - int created; - uint64_t index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { - LACE_ME; - - bdd_refs_push(low); - bdd_refs_push(high); - sylvan_gc(); - bdd_refs_pop(2); - - index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { - fprintf(stderr, "BDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); - exit(1); - } - } - - if (created) sylvan_stats_count(BDD_NODES_CREATED); - else sylvan_stats_count(BDD_NODES_REUSED); - - result = index; - return mark ? result | sylvan_complement : result; -} - -syBDD -sylvan_ithvar(BDDVAR level) -{ - return sylvan_makenode(level, sylvan_false, sylvan_true); -} - -BDDVAR -sylvan_var(syBDD bdd) -{ - return bddnode_getvariable(GETNODE(bdd)); -} - -static syBDD -node_low(syBDD bdd, bddnode_t node) -{ - return BDD_TRANSFERMARK(bdd, bddnode_getlow(node)); -} - -static syBDD -node_high(syBDD bdd, bddnode_t node) -{ - return BDD_TRANSFERMARK(bdd, bddnode_gethigh(node)); -} - -syBDD -sylvan_low(syBDD bdd) -{ - if (sylvan_isconst(bdd)) return bdd; - return node_low(bdd, GETNODE(bdd)); -} - -syBDD -sylvan_high(syBDD bdd) +int +sylvan_get_granularity() { - if (sylvan_isconst(bdd)) return bdd; - return node_high(bdd, GETNODE(bdd)); + return granularity; } /** * Implementation of unary, binary and if-then-else operators. */ -TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_and, SyBDD, a, SyBDD, b, BDDVAR, prev_level) { /* Terminal cases */ if (a == sylvan_true) return b; @@ -392,17 +52,18 @@ TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_AND); /* Improve for caching */ if (BDD_STRIPMARK(a) > BDD_STRIPMARK(b)) { - syBDD t = b; + SyBDD t = b; b = a; a = t; } - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); + bddnode_t na = MTBDD_GETNODE(a); + bddnode_t nb = MTBDD_GETNODE(b); BDDVAR va = bddnode_getvariable(na); BDDVAR vb = bddnode_getvariable(nb); @@ -410,7 +71,7 @@ TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_AND, a, b, sylvan_false, &result)) { sylvan_stats_count(BDD_AND_CACHED); return result; @@ -418,8 +79,8 @@ TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) } // Get cofactors - syBDD aLow = a, aHigh = a; - syBDD bLow = b, bHigh = b; + SyBDD aLow = a, aHigh = a; + SyBDD bLow = b, bHigh = b; if (level == va) { aLow = node_low(a, na); aHigh = node_high(a, na); @@ -430,7 +91,7 @@ TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) } // Recursive computation - syBDD low=sylvan_invalid, high=sylvan_invalid, result; + SyBDD low=sylvan_invalid, high=sylvan_invalid, result; int n=0; @@ -470,7 +131,7 @@ TASK_IMPL_3(syBDD, sylvan_and, syBDD, a, syBDD, b, BDDVAR, prev_level) return result; } -TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_xor, SyBDD, a, SyBDD, b, BDDVAR, prev_level) { /* Terminal cases */ if (a == sylvan_false) return b; @@ -482,11 +143,12 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_XOR); /* Improve for caching */ if (BDD_STRIPMARK(a) > BDD_STRIPMARK(b)) { - syBDD t = b; + SyBDD t = b; b = a; a = t; } @@ -497,8 +159,8 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) b = sylvan_not(b); } - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); + bddnode_t na = MTBDD_GETNODE(a); + bddnode_t nb = MTBDD_GETNODE(b); BDDVAR va = bddnode_getvariable(na); BDDVAR vb = bddnode_getvariable(nb); @@ -506,7 +168,7 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_XOR, a, b, sylvan_false, &result)) { sylvan_stats_count(BDD_XOR_CACHED); return result; @@ -514,8 +176,8 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) } // Get cofactors - syBDD aLow = a, aHigh = a; - syBDD bLow = b, bHigh = b; + SyBDD aLow = a, aHigh = a; + SyBDD bLow = b, bHigh = b; if (level == va) { aLow = node_low(a, na); aHigh = node_high(a, na); @@ -526,7 +188,7 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) } // Recursive computation - syBDD low, high, result; + SyBDD low, high, result; bdd_refs_spawn(SPAWN(sylvan_xor, aHigh, bHigh, level)); low = CALL(sylvan_xor, aLow, bLow, level); @@ -544,7 +206,7 @@ TASK_IMPL_3(syBDD, sylvan_xor, syBDD, a, syBDD, b, BDDVAR, prev_level) } -TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) +TASK_IMPL_4(SyBDD, sylvan_ite, SyBDD, a, SyBDD, b, SyBDD, c, BDDVAR, prev_level) { /* Terminal cases */ if (a == sylvan_true) return b; @@ -581,7 +243,7 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) // ITE(~A,B,C) => ITE(A,C,B) if (BDD_HASMARK(a)) { a = BDD_STRIPMARK(a); - syBDD t = c; + SyBDD t = c; c = b; b = t; } @@ -594,9 +256,9 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) mark = 1; } - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); - bddnode_t nc = GETNODE(c); + bddnode_t na = MTBDD_GETNODE(a); + bddnode_t nb = MTBDD_GETNODE(b); + bddnode_t nc = MTBDD_GETNODE(c); BDDVAR va = bddnode_getvariable(na); BDDVAR vb = bddnode_getvariable(nb); @@ -607,7 +269,7 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) // Fast case if (va < level && node_low(a, na) == sylvan_false && node_high(a, na) == sylvan_true) { - syBDD result = sylvan_makenode(va, c, b); + SyBDD result = sylvan_makenode(va, c, b); return mark ? sylvan_not(result) : result; } @@ -615,11 +277,12 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_ITE); int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_ITE, a, b, c, &result)) { sylvan_stats_count(BDD_ITE_CACHED); return mark ? sylvan_not(result) : result; @@ -627,9 +290,9 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) } // Get cofactors - syBDD aLow = a, aHigh = a; - syBDD bLow = b, bHigh = b; - syBDD cLow = c, cHigh = c; + SyBDD aLow = a, aHigh = a; + SyBDD bLow = b, bHigh = b; + SyBDD cLow = c, cHigh = c; if (level == va) { aLow = node_low(a, na); aHigh = node_high(a, na); @@ -644,7 +307,7 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) } // Recursive computation - syBDD low=sylvan_invalid, high=sylvan_invalid, result; + SyBDD low=sylvan_invalid, high=sylvan_invalid, result; int n=0; @@ -681,16 +344,17 @@ TASK_IMPL_4(syBDD, sylvan_ite, syBDD, a, syBDD, b, syBDD, c, BDDVAR, prev_level) } /** - * Calculate constrain a @ c + * Compute constrain f@c, also called the generalized co-factor. + * c is the "care function" - f@c equals f when c evaluates to True. */ -TASK_IMPL_3(syBDD, sylvan_constrain, syBDD, a, syBDD, b, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_constrain, SyBDD, f, SyBDD, c, BDDVAR, prev_level) { /* Trivial cases */ - if (b == sylvan_true) return a; - if (b == sylvan_false) return sylvan_false; - if (sylvan_isconst(a)) return a; - if (a == b) return sylvan_true; - if (a == sylvan_not(b)) return sylvan_false; + if (c == sylvan_true) return f; + if (c == sylvan_false) return sylvan_false; + if (sylvan_isconst(f)) return f; + if (f == c) return sylvan_true; + if (f == sylvan_not(c)) return sylvan_false; /* Perhaps execute garbage collection */ sylvan_gc_test(); @@ -698,88 +362,92 @@ TASK_IMPL_3(syBDD, sylvan_constrain, syBDD, a, syBDD, b, BDDVAR, prev_level) /* Count operation */ sylvan_stats_count(BDD_CONSTRAIN); - // a != constant and b != constant - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); + bddnode_t nf = MTBDD_GETNODE(f); + bddnode_t nc = MTBDD_GETNODE(c); - BDDVAR va = bddnode_getvariable(na); - BDDVAR vb = bddnode_getvariable(nb); - BDDVAR level = va < vb ? va : vb; + BDDVAR vf = bddnode_getvariable(nf); + BDDVAR vc = bddnode_getvariable(nc); + BDDVAR level = vf < vc ? vf : vc; - // CONSULT CACHE + /* Make canonical */ + int mark = 0; + if (BDD_HASMARK(f)) { + f = BDD_STRIPMARK(f); + mark = 1; + } + /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; - if (cache_get3(CACHE_BDD_CONSTRAIN, a, b, 0, &result)) { + SyBDD result; + if (cache_get3(CACHE_BDD_CONSTRAIN, f, c, 0, &result)) { sylvan_stats_count(BDD_CONSTRAIN_CACHED); - return result; + return mark ? sylvan_not(result) : result; } } - // DETERMINE TOP BDDVAR AND COFACTORS - - syBDD aLow, aHigh, bLow, bHigh; + SyBDD fLow, fHigh, cLow, cHigh; - if (level == va) { - aLow = node_low(a, na); - aHigh = node_high(a, na); + if (level == vf) { + fLow = node_low(f, nf); + fHigh = node_high(f, nf); } else { - aLow = aHigh = a; + fLow = fHigh = f; } - if (level == vb) { - bLow = node_low(b, nb); - bHigh = node_high(b, nb); + if (level == vc) { + cLow = node_low(c, nc); + cHigh = node_high(c, nc); } else { - bLow = bHigh = b; - } - - syBDD result; - - syBDD low=sylvan_invalid, high=sylvan_invalid; - if (bLow == sylvan_false) return CALL(sylvan_constrain, aHigh, bHigh, level); - if (bLow == sylvan_true) { - if (bHigh == sylvan_false) return aLow; - if (bHigh == sylvan_true) { - result = sylvan_makenode(level, aLow, bHigh); - } else { - high = CALL(sylvan_constrain, aHigh, bHigh, level); - result = sylvan_makenode(level, aLow, high); - } + cLow = cHigh = c; + } + + SyBDD result; + + if (cLow == sylvan_false) { + /* cLow is False, so result equals fHigh @ cHigh */ + if (cHigh == sylvan_true) result = fHigh; + else result = CALL(sylvan_constrain, fHigh, cHigh, level); + } else if (cHigh == sylvan_false) { + /* cHigh is False, so result equals fLow @ cLow */ + if (cLow == sylvan_true) result = fLow; + else result = CALL(sylvan_constrain, fLow, cLow, level); + } else if (cLow == sylvan_true) { + /* cLow is True, so low result equals fLow */ + SyBDD high = CALL(sylvan_constrain, fHigh, cHigh, level); + result = sylvan_makenode(level, fLow, high); + } else if (cHigh == sylvan_true) { + /* cHigh is True, so high result equals fHigh */ + SyBDD low = CALL(sylvan_constrain, fLow, cLow, level); + result = sylvan_makenode(level, low, fHigh); } else { - if (bHigh == sylvan_false) return CALL(sylvan_constrain, aLow, bLow, level); - if (bHigh == sylvan_true) { - low = CALL(sylvan_constrain, aLow, bLow, level); - result = sylvan_makenode(level, low, bHigh); - } else { - bdd_refs_spawn(SPAWN(sylvan_constrain, aLow, bLow, level)); - high = CALL(sylvan_constrain, aHigh, bHigh, level); - bdd_refs_push(high); - low = bdd_refs_sync(SYNC(sylvan_constrain)); - bdd_refs_pop(1); - result = sylvan_makenode(level, low, high); - } + /* cLow and cHigh are not constrants... normal parallel recursion */ + bdd_refs_spawn(SPAWN(sylvan_constrain, fLow, cLow, level)); + SyBDD high = CALL(sylvan_constrain, fHigh, cHigh, level); + bdd_refs_push(high); + SyBDD low = bdd_refs_sync(SYNC(sylvan_constrain)); + bdd_refs_pop(1); + result = sylvan_makenode(level, low, high); } if (cachenow) { - if (cache_put3(CACHE_BDD_CONSTRAIN, a, b, 0, result)) sylvan_stats_count(BDD_CONSTRAIN_CACHEDPUT); + if (cache_put3(CACHE_BDD_CONSTRAIN, f, c, 0, result)) sylvan_stats_count(BDD_CONSTRAIN_CACHEDPUT); } - return result; + return mark ? sylvan_not(result) : result; } /** - * Calculate restrict a @ b + * Compute restrict f@c, which uses a heuristic to try and minimize a SyBDD f with respect to a care function c */ -TASK_IMPL_3(syBDD, sylvan_restrict, syBDD, a, syBDD, b, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_restrict, SyBDD, f, SyBDD, c, BDDVAR, prev_level) { /* Trivial cases */ - if (b == sylvan_true) return a; - if (b == sylvan_false) return sylvan_false; - if (sylvan_isconst(a)) return a; - if (a == b) return sylvan_true; - if (a == sylvan_not(b)) return sylvan_false; + if (c == sylvan_true) return f; + if (c == sylvan_false) return sylvan_false; + if (sylvan_isconst(f)) return f; + if (f == c) return sylvan_true; + if (f == sylvan_not(c)) return sylvan_false; /* Perhaps execute garbage collection */ sylvan_gc_test(); @@ -787,62 +455,75 @@ TASK_IMPL_3(syBDD, sylvan_restrict, syBDD, a, syBDD, b, BDDVAR, prev_level) /* Count operation */ sylvan_stats_count(BDD_RESTRICT); - // a != constant and b != constant - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); + bddnode_t nf = MTBDD_GETNODE(f); + bddnode_t nc = MTBDD_GETNODE(c); - BDDVAR va = bddnode_getvariable(na); - BDDVAR vb = bddnode_getvariable(nb); - BDDVAR level = va < vb ? va : vb; + BDDVAR vf = bddnode_getvariable(nf); + BDDVAR vc = bddnode_getvariable(nc); + BDDVAR level = vf < vc ? vf : vc; + + /* Make canonical */ + int mark = 0; + if (BDD_HASMARK(f)) { + f = BDD_STRIPMARK(f); + mark = 1; + } /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; - if (cache_get3(CACHE_BDD_RESTRICT, a, b, 0, &result)) { + SyBDD result; + if (cache_get3(CACHE_BDD_RESTRICT, f, c, 0, &result)) { sylvan_stats_count(BDD_RESTRICT_CACHED); - return result; + return mark ? sylvan_not(result) : result; } } - syBDD result; + SyBDD result; - if (vb < va) { - syBDD c = CALL(sylvan_ite, node_low(b,nb), sylvan_true, node_high(b,nb), 0); - bdd_refs_push(c); - result = CALL(sylvan_restrict, a, c, level); + if (vc < vf) { + /* f is independent of c, so result is f @ (cLow \/ cHigh) */ + SyBDD new_c = sylvan_or(node_low(c, nc), node_high(c, nc)); + bdd_refs_push(new_c); + result = CALL(sylvan_restrict, f, new_c, level); bdd_refs_pop(1); } else { - syBDD aLow=node_low(a,na),aHigh=node_high(a,na),bLow=b,bHigh=b; - if (va == vb) { - bLow = node_low(b,nb); - bHigh = node_high(b,nb); + SyBDD fLow = node_low(f,nf), fHigh = node_high(f,nf); + SyBDD cLow, cHigh; + if (vf == vc) { + cLow = node_low(c, nc); + cHigh = node_high(c, nc); + } else { + cLow = cHigh = c; } - if (bLow == sylvan_false) { - result = CALL(sylvan_restrict, aHigh, bHigh, level); - } else if (bHigh == sylvan_false) { - result = CALL(sylvan_restrict, aLow, bLow, level); + if (cLow == sylvan_false) { + /* sibling-substitution */ + result = CALL(sylvan_restrict, fHigh, cHigh, level); + } else if (cHigh == sylvan_false) { + /* sibling-substitution */ + result = CALL(sylvan_restrict, fLow, cLow, level); } else { - bdd_refs_spawn(SPAWN(sylvan_restrict, aLow, bLow, level)); - syBDD high = CALL(sylvan_restrict, aHigh, bHigh, level); + /* parallel recursion */ + bdd_refs_spawn(SPAWN(sylvan_restrict, fLow, cLow, level)); + SyBDD high = CALL(sylvan_restrict, fHigh, cHigh, level); bdd_refs_push(high); - syBDD low = bdd_refs_sync(SYNC(sylvan_restrict)); + SyBDD low = bdd_refs_sync(SYNC(sylvan_restrict)); bdd_refs_pop(1); result = sylvan_makenode(level, low, high); } } if (cachenow) { - if (cache_put3(CACHE_BDD_RESTRICT, a, b, 0, result)) sylvan_stats_count(BDD_RESTRICT_CACHEDPUT); + if (cache_put3(CACHE_BDD_RESTRICT, f, c, 0, result)) sylvan_stats_count(BDD_RESTRICT_CACHEDPUT); } - return result; + return mark ? sylvan_not(result) : result; } /** * Calculates \exists variables . a */ -TASK_IMPL_3(syBDD, sylvan_exists, syBDD, a, syBDD, variables, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_exists, SyBDD, a, SyBDD, variables, BDDVAR, prev_level) { /* Terminal cases */ if (a == sylvan_true) return sylvan_true; @@ -850,25 +531,26 @@ TASK_IMPL_3(syBDD, sylvan_exists, syBDD, a, syBDD, variables, BDDVAR, prev_level if (sylvan_set_isempty(variables)) return a; // a != constant - bddnode_t na = GETNODE(a); + bddnode_t na = MTBDD_GETNODE(a); BDDVAR level = bddnode_getvariable(na); - bddnode_t nv = GETNODE(variables); + bddnode_t nv = MTBDD_GETNODE(variables); BDDVAR vv = bddnode_getvariable(nv); while (vv < level) { variables = node_high(variables, nv); if (sylvan_set_isempty(variables)) return a; - nv = GETNODE(variables); + nv = MTBDD_GETNODE(variables); vv = bddnode_getvariable(nv); } sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_EXISTS); int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_EXISTS, a, variables, 0, &result)) { sylvan_stats_count(BDD_EXISTS_CACHED); return result; @@ -876,23 +558,23 @@ TASK_IMPL_3(syBDD, sylvan_exists, syBDD, a, syBDD, variables, BDDVAR, prev_level } // Get cofactors - syBDD aLow = node_low(a, na); - syBDD aHigh = node_high(a, na); + SyBDD aLow = node_low(a, na); + SyBDD aHigh = node_high(a, na); - syBDD result; + SyBDD result; if (vv == level) { // level is in variable set, perform abstraction if (aLow == sylvan_true || aHigh == sylvan_true || aLow == sylvan_not(aHigh)) { result = sylvan_true; } else { - syBDD _v = sylvan_set_next(variables); - syBDD low = CALL(sylvan_exists, aLow, _v, level); + SyBDD _v = sylvan_set_next(variables); + SyBDD low = CALL(sylvan_exists, aLow, _v, level); if (low == sylvan_true) { result = sylvan_true; } else { bdd_refs_push(low); - syBDD high = CALL(sylvan_exists, aHigh, _v, level); + SyBDD high = CALL(sylvan_exists, aHigh, _v, level); if (high == sylvan_true) { result = sylvan_true; bdd_refs_pop(1); @@ -908,7 +590,7 @@ TASK_IMPL_3(syBDD, sylvan_exists, syBDD, a, syBDD, variables, BDDVAR, prev_level } } else { // level is not in variable set - syBDD low, high; + SyBDD low, high; bdd_refs_spawn(SPAWN(sylvan_exists, aHigh, variables, level)); low = CALL(sylvan_exists, aLow, variables, level); bdd_refs_push(low); @@ -924,10 +606,100 @@ TASK_IMPL_3(syBDD, sylvan_exists, syBDD, a, syBDD, variables, BDDVAR, prev_level return result; } + +/** + * Calculate projection of <a> unto <v> + * (Expects Boolean <a>) + */ +TASK_IMPL_2(MTBDD, sylvan_project, MTBDD, a, MTBDD, v) +{ + /** + * Terminal cases + */ + if (a == sylvan_false) return sylvan_false; + if (a == sylvan_true) return sylvan_true; + if (sylvan_set_isempty(v)) return sylvan_true; + + /** + * Obtain variables + */ + const mtbddnode_t a_node = MTBDD_GETNODE(a); + const uint32_t a_var = mtbddnode_getvariable(a_node); + + /** + * Skip <vars> + */ + mtbddnode_t v_node = MTBDD_GETNODE(v); + uint32_t v_var = mtbddnode_getvariable(v_node); + MTBDD v_next = mtbddnode_followhigh(v, v_node); + + while (v_var < a_var) { + if (sylvan_set_isempty(v_next)) return sylvan_true; + v = v_next; + v_node = MTBDD_GETNODE(v); + v_var = mtbddnode_getvariable(v_node); + v_next = mtbddnode_followhigh(v, v_node); + } + + /** + * Maybe perform garbage collection + */ + sylvan_gc_test(); + + /** + * Count operation + */ + sylvan_stats_count(BDD_PROJECT); + + /** + * Check the cache + */ + MTBDD result; + if (cache_get3(CACHE_BDD_PROJECT, a, 0, v, &result)) { + sylvan_stats_count(BDD_PROJECT_CACHED); + return result; + } + + /** + * Get cofactors + */ + const MTBDD a0 = mtbddnode_followlow(a, a_node); + const MTBDD a1 = mtbddnode_followhigh(a, a_node); + + /** + * Compute recursive result + */ + if (v_var == a_var) { + // variable in projection variables + mtbdd_refs_spawn(SPAWN(sylvan_project, a0, v_next)); + const MTBDD high = mtbdd_refs_push(sylvan_project(a1, v_next)); + const MTBDD low = mtbdd_refs_sync(SYNC(sylvan_project)); + mtbdd_refs_pop(1); + result = mtbdd_makenode(a_var, low, high); + } else { + // variable not in projection variables + mtbdd_refs_spawn(SPAWN(sylvan_project, a0, v)); + const MTBDD high = mtbdd_refs_push(sylvan_project(a1, v)); + const MTBDD low = mtbdd_refs_push(mtbdd_refs_sync(SYNC(sylvan_project))); + result = sylvan_or(low, high); + mtbdd_refs_pop(2); + } + + /** + * Put in cache + */ + if (cache_put3(CACHE_BDD_PROJECT, a, 0, v, result)) { + sylvan_stats_count(BDD_PROJECT_CACHEDPUT); + } + + return result; +} + + /** * Calculate exists(a AND b, v) */ -TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, prev_level) +TASK_IMPL_4(SyBDD, sylvan_and_exists, SyBDD, a, SyBDD, b, BDDSET, v, BDDVAR, prev_level) { /* Terminal cases */ if (a == sylvan_false) return sylvan_false; @@ -945,7 +717,7 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre /* Improve for caching */ if (BDD_STRIPMARK(a) > BDD_STRIPMARK(b)) { - syBDD t = b; + SyBDD t = b; b = a; a = t; } @@ -953,12 +725,13 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_AND_EXISTS); // a != constant - bddnode_t na = GETNODE(a); - bddnode_t nb = GETNODE(b); - bddnode_t nv = GETNODE(v); + bddnode_t na = MTBDD_GETNODE(a); + bddnode_t nb = MTBDD_GETNODE(b); + bddnode_t nv = MTBDD_GETNODE(v); BDDVAR va = bddnode_getvariable(na); BDDVAR vb = bddnode_getvariable(nb); @@ -969,11 +742,11 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre while (vv < level) { v = node_high(v, nv); // get next variable in conjunction if (sylvan_set_isempty(v)) return sylvan_and(a, b); - nv = GETNODE(v); + nv = MTBDD_GETNODE(v); vv = bddnode_getvariable(nv); } - syBDD result; + SyBDD result; int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { @@ -984,7 +757,7 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre } // Get cofactors - syBDD aLow, aHigh, bLow, bHigh; + SyBDD aLow, aHigh, bLow, bHigh; if (level == va) { aLow = node_low(a, na); aHigh = node_high(a, na); @@ -1002,13 +775,13 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre if (level == vv) { // level is in variable set, perform abstraction - syBDD _v = node_high(v, nv); - syBDD low = CALL(sylvan_and_exists, aLow, bLow, _v, level); + SyBDD _v = node_high(v, nv); + SyBDD low = CALL(sylvan_and_exists, aLow, bLow, _v, level); if (low == sylvan_true || low == aHigh || low == bHigh) { result = low; } else { bdd_refs_push(low); - syBDD high; + SyBDD high; if (low == sylvan_not(aHigh)) { high = CALL(sylvan_exists, bHigh, _v, 0); } else if (low == sylvan_not(bHigh)) { @@ -1032,24 +805,134 @@ TASK_IMPL_4(syBDD, sylvan_and_exists, syBDD, a, syBDD, b, BDDSET, v, BDDVAR, pre } } } else { - // level is not in variable set - bdd_refs_spawn(SPAWN(sylvan_and_exists, aHigh, bHigh, v, level)); - syBDD low = CALL(sylvan_and_exists, aLow, bLow, v, level); - bdd_refs_push(low); - syBDD high = bdd_refs_sync(SYNC(sylvan_and_exists)); - bdd_refs_pop(1); - result = sylvan_makenode(level, low, high); + // level is not in variable set + bdd_refs_spawn(SPAWN(sylvan_and_exists, aHigh, bHigh, v, level)); + SyBDD low = CALL(sylvan_and_exists, aLow, bLow, v, level); + bdd_refs_push(low); + SyBDD high = bdd_refs_sync(SYNC(sylvan_and_exists)); + bdd_refs_pop(1); + result = sylvan_makenode(level, low, high); + } + + if (cachenow) { + if (cache_put3(CACHE_BDD_AND_EXISTS, a, b, v, result)) sylvan_stats_count(BDD_AND_EXISTS_CACHEDPUT); + } + + return result; +} + + +/** + * Calculate projection of (<a> AND <b>) unto <v> + * (Expects Boolean <a> and <b>) + */ +TASK_IMPL_3(MTBDD, sylvan_and_project, MTBDD, a, MTBDD, b, MTBDD, v) +{ + /** + * Terminal cases + */ + if (a == sylvan_false) return sylvan_false; + if (b == sylvan_false) return sylvan_false; + if (a == sylvan_not(b)) return sylvan_false; + if (a == sylvan_true && b == sylvan_true) return sylvan_true; + if (sylvan_set_isempty(v)) return sylvan_true; + + /** + * Cases that reduce to sylvan_project + */ + if (a == sylvan_true || b == sylvan_true || a == b) return sylvan_project(b, v); + + /** + * Normalization (only for caching) + */ + if (BDD_STRIPMARK(a) > BDD_STRIPMARK(b)) { + SyBDD t = b; + b = a; + a = t; + } + + /** + * Maybe perform garbage collection + */ + sylvan_gc_test(); + + /** + * Count operation + */ + sylvan_stats_count(BDD_AND_PROJECT); + + /** + * Obtain variables + */ + const mtbddnode_t a_node = MTBDD_GETNODE(a); + const mtbddnode_t b_node = MTBDD_GETNODE(b); + const uint32_t a_var = mtbddnode_getvariable(a_node); + const uint32_t b_var = mtbddnode_getvariable(b_node); + const uint32_t minvar = a_var < b_var ? a_var : b_var; + + /** + * Skip <vars> + */ + mtbddnode_t v_node = MTBDD_GETNODE(v); + uint32_t v_var = mtbddnode_getvariable(v_node); + MTBDD v_next = mtbddnode_followhigh(v, v_node); + + while (v_var < minvar) { + if (sylvan_set_isempty(v_next)) return sylvan_true; + v = v_next; + v_node = MTBDD_GETNODE(v); + v_var = mtbddnode_getvariable(v_node); + v_next = mtbddnode_followhigh(v, v_node); + } + + /** + * Check the cache + */ + MTBDD result; + if (cache_get3(CACHE_BDD_AND_PROJECT, a, b, v, &result)) { + sylvan_stats_count(BDD_AND_PROJECT_CACHED); + return result; + } + + /** + * Get cofactors + */ + const MTBDD a0 = a_var == minvar ? mtbddnode_followlow(a, a_node) : a; + const MTBDD a1 = a_var == minvar ? mtbddnode_followhigh(a, a_node) : a; + const MTBDD b0 = b_var == minvar ? mtbddnode_followlow(b, b_node) : b; + const MTBDD b1 = b_var == minvar ? mtbddnode_followhigh(b, b_node) : b; + + /** + * Compute recursive result + */ + if (v_var == minvar) { + // variable in projection variables + mtbdd_refs_spawn(SPAWN(sylvan_and_project, a0, b0, v_next)); + const MTBDD high = mtbdd_refs_push(sylvan_and_project(a1, b1, v_next)); + const MTBDD low = mtbdd_refs_sync(SYNC(sylvan_and_project)); + mtbdd_refs_pop(1); + result = mtbdd_makenode(minvar, low, high); + } else { + // variable not in projection variables + mtbdd_refs_spawn(SPAWN(sylvan_and_project, a0, b0, v)); + const MTBDD high = mtbdd_refs_push(sylvan_and_project(a1, b1, v)); + const MTBDD low = mtbdd_refs_push(mtbdd_refs_sync(SYNC(sylvan_and_project))); + result = sylvan_or(low, high); + mtbdd_refs_pop(2); } - if (cachenow) { - if (cache_put3(CACHE_BDD_AND_EXISTS, a, b, v, result)) sylvan_stats_count(BDD_AND_EXISTS_CACHEDPUT); + /** + * Put in cache + */ + if (cache_put3(CACHE_BDD_AND_PROJECT, a, b, v, result)) { + sylvan_stats_count(BDD_AND_PROJECT_CACHEDPUT); } return result; } -TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, prev_level) +TASK_IMPL_4(SyBDD, sylvan_relnext, SyBDD, a, SyBDD, b, BDDSET, vars, BDDVAR, prev_level) { /* Compute R(s) = \exists x: A(x) \and B(x,s) with support(result) = s, support(A) = s, support(B) = s+t * if vars == sylvan_false, then every level is in s or t @@ -1069,8 +952,8 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre sylvan_stats_count(BDD_RELNEXT); /* Determine top level */ - bddnode_t na = sylvan_isconst(a) ? 0 : GETNODE(a); - bddnode_t nb = sylvan_isconst(b) ? 0 : GETNODE(b); + bddnode_t na = sylvan_isconst(a) ? 0 : MTBDD_GETNODE(a); + bddnode_t nb = sylvan_isconst(b) ? 0 : MTBDD_GETNODE(b); BDDVAR va = na ? bddnode_getvariable(na) : 0xffffffff; BDDVAR vb = nb ? bddnode_getvariable(nb) : 0xffffffff; @@ -1082,7 +965,7 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre if (vars == sylvan_false) { is_s_or_t = 1; } else { - nv = GETNODE(vars); + nv = MTBDD_GETNODE(vars); for (;;) { /* check if level is s/t */ BDDVAR vv = bddnode_getvariable(nv); @@ -1094,28 +977,28 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre if (level < vv) break; vars = node_high(vars, nv); // get next in vars if (sylvan_set_isempty(vars)) return a; - nv = GETNODE(vars); + nv = MTBDD_GETNODE(vars); } } /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_RELNEXT, a, b, vars, &result)) { sylvan_stats_count(BDD_RELNEXT_CACHED); return result; } } - syBDD result; + SyBDD result; if (is_s_or_t) { /* Get s and t */ BDDVAR s = level & (~1); BDDVAR t = s+1; - syBDD a0, a1, b0, b1; + SyBDD a0, a1, b0, b1; if (na && va == s) { a0 = node_low(a, na); a1 = node_high(a, na); @@ -1129,9 +1012,9 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b0 = b1 = b; } - syBDD b00, b01, b10, b11; + SyBDD b00, b01, b10, b11; if (!sylvan_isconst(b0)) { - bddnode_t nb0 = GETNODE(b0); + bddnode_t nb0 = MTBDD_GETNODE(b0); if (bddnode_getvariable(nb0) == t) { b00 = node_low(b0, nb0); b01 = node_high(b0, nb0); @@ -1142,7 +1025,7 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b00 = b01 = b0; } if (!sylvan_isconst(b1)) { - bddnode_t nb1 = GETNODE(b1); + bddnode_t nb1 = MTBDD_GETNODE(b1); if (bddnode_getvariable(nb1) == t) { b10 = node_low(b1, nb1); b11 = node_high(b1, nb1); @@ -1153,17 +1036,17 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b10 = b11 = b1; } - syBDD _vars = vars == sylvan_false ? sylvan_false : node_high(vars, nv); + SyBDD _vars = vars == sylvan_false ? sylvan_false : node_high(vars, nv); bdd_refs_spawn(SPAWN(sylvan_relnext, a0, b00, _vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b10, _vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a0, b01, _vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b11, _vars, level)); - syBDD f = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(f); - syBDD e = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(e); - syBDD d = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(d); - syBDD c = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(c); + SyBDD f = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(f); + SyBDD e = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(e); + SyBDD d = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(d); + SyBDD c = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(c); bdd_refs_spawn(SPAWN(sylvan_ite, c, sylvan_true, d, 0)); /* a0 b00 \or a1 b01 */ bdd_refs_spawn(SPAWN(sylvan_ite, e, sylvan_true, f, 0)); /* a0 b01 \or a1 b11 */ @@ -1175,7 +1058,7 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre result = sylvan_makenode(s, c, d); } else { /* Variable not in vars! Take a, quantify b */ - syBDD a0, a1, b0, b1; + SyBDD a0, a1, b0, b1; if (na && va == level) { a0 = node_low(a, na); a1 = node_high(a, na); @@ -1195,9 +1078,9 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relnext, a0, b0, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b1, vars, level)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r0); result = sylvan_or(r0, r1); bdd_refs_pop(2); @@ -1208,21 +1091,21 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b0, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b1, vars, level)); - syBDD r11 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r11 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r11); - syBDD r10 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r10 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r10); - syBDD r01 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r01 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r01); - syBDD r00 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r00 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r00); bdd_refs_spawn(SPAWN(sylvan_ite, r00, sylvan_true, r01, 0)); bdd_refs_spawn(SPAWN(sylvan_ite, r10, sylvan_true, r11, 0)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_ite)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_ite)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_ite)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_ite)); bdd_refs_pop(5); result = sylvan_makenode(level, r0, r1); @@ -1232,9 +1115,9 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relnext, a0, b0, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b1, vars, level)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_pop(1); result = sylvan_makenode(level, r0, r1); } @@ -1247,7 +1130,7 @@ TASK_IMPL_4(syBDD, sylvan_relnext, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre return result; } -TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, prev_level) +TASK_IMPL_4(SyBDD, sylvan_relprev, SyBDD, a, SyBDD, b, BDDSET, vars, BDDVAR, prev_level) { /* Compute \exists x: A(s,x) \and B(x,t) * if vars == sylvan_false, then every level is in s or t @@ -1267,8 +1150,8 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre sylvan_stats_count(BDD_RELPREV); /* Determine top level */ - bddnode_t na = sylvan_isconst(a) ? 0 : GETNODE(a); - bddnode_t nb = sylvan_isconst(b) ? 0 : GETNODE(b); + bddnode_t na = sylvan_isconst(a) ? 0 : MTBDD_GETNODE(a); + bddnode_t nb = sylvan_isconst(b) ? 0 : MTBDD_GETNODE(b); BDDVAR va = na ? bddnode_getvariable(na) : 0xffffffff; BDDVAR vb = nb ? bddnode_getvariable(nb) : 0xffffffff; @@ -1280,7 +1163,7 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre if (vars == sylvan_false) { is_s_or_t = 1; } else { - nv = GETNODE(vars); + nv = MTBDD_GETNODE(vars); for (;;) { /* check if level is s/t */ BDDVAR vv = bddnode_getvariable(nv); @@ -1292,28 +1175,28 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre if (level < vv) break; vars = node_high(vars, nv); // get next in vars if (sylvan_set_isempty(vars)) return b; - nv = GETNODE(vars); + nv = MTBDD_GETNODE(vars); } } /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_RELPREV, a, b, vars, &result)) { sylvan_stats_count(BDD_RELPREV_CACHED); return result; } } - syBDD result; + SyBDD result; if (is_s_or_t) { /* Get s and t */ BDDVAR s = level & (~1); BDDVAR t = s+1; - syBDD a0, a1, b0, b1; + SyBDD a0, a1, b0, b1; if (na && va == s) { a0 = node_low(a, na); a1 = node_high(a, na); @@ -1327,9 +1210,9 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b0 = b1 = b; } - syBDD a00, a01, a10, a11; + SyBDD a00, a01, a10, a11; if (!sylvan_isconst(a0)) { - bddnode_t na0 = GETNODE(a0); + bddnode_t na0 = MTBDD_GETNODE(a0); if (bddnode_getvariable(na0) == t) { a00 = node_low(a0, na0); a01 = node_high(a0, na0); @@ -1340,7 +1223,7 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre a00 = a01 = a0; } if (!sylvan_isconst(a1)) { - bddnode_t na1 = GETNODE(a1); + bddnode_t na1 = MTBDD_GETNODE(a1); if (bddnode_getvariable(na1) == t) { a10 = node_low(a1, na1); a11 = node_high(a1, na1); @@ -1351,9 +1234,9 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre a10 = a11 = a1; } - syBDD b00, b01, b10, b11; + SyBDD b00, b01, b10, b11; if (!sylvan_isconst(b0)) { - bddnode_t nb0 = GETNODE(b0); + bddnode_t nb0 = MTBDD_GETNODE(b0); if (bddnode_getvariable(nb0) == t) { b00 = node_low(b0, nb0); b01 = node_high(b0, nb0); @@ -1364,7 +1247,7 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b00 = b01 = b0; } if (!sylvan_isconst(b1)) { - bddnode_t nb1 = GETNODE(b1); + bddnode_t nb1 = MTBDD_GETNODE(b1); if (bddnode_getvariable(nb1) == t) { b10 = node_low(b1, nb1); b11 = node_high(b1, nb1); @@ -1375,10 +1258,10 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre b10 = b11 = b1; } - syBDD _vars; + SyBDD _vars; if (vars != sylvan_false) { _vars = node_high(vars, nv); - if (sylvan_set_var(_vars) == t) _vars = sylvan_set_next(_vars); + if (sylvan_set_first(_vars) == t) _vars = sylvan_set_next(_vars); } else { _vars = sylvan_false; } @@ -1403,19 +1286,19 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relprev, a11, b11, _vars, level)); } - syBDD r00, r01, r10, r11; + SyBDD r00, r01, r10, r11; if (b10 == b11) { r11 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r01 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); } else { - syBDD r111 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); - syBDD r110 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r111 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r110 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r11 = sylvan_makenode(t, r110, r111); bdd_refs_pop(2); bdd_refs_push(r11); - syBDD r011 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); - syBDD r010 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r011 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r010 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r01 = sylvan_makenode(t, r010, r011); bdd_refs_pop(2); bdd_refs_push(r01); @@ -1425,13 +1308,13 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre r10 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r00 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); } else { - syBDD r101 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); - syBDD r100 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r101 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r100 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r10 = sylvan_makenode(t, r100, r101); bdd_refs_pop(2); bdd_refs_push(r10); - syBDD r001 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); - syBDD r000 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r001 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); + SyBDD r000 = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_relprev))); r00 = sylvan_makenode(t, r000, r001); bdd_refs_pop(2); bdd_refs_push(r00); @@ -1440,12 +1323,12 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_and, sylvan_not(r00), sylvan_not(r01), 0)); bdd_refs_spawn(SPAWN(sylvan_and, sylvan_not(r10), sylvan_not(r11), 0)); - syBDD r1 = sylvan_not(bdd_refs_push(bdd_refs_sync(SYNC(sylvan_and)))); - syBDD r0 = sylvan_not(bdd_refs_sync(SYNC(sylvan_and))); + SyBDD r1 = sylvan_not(bdd_refs_push(bdd_refs_sync(SYNC(sylvan_and)))); + SyBDD r0 = sylvan_not(bdd_refs_sync(SYNC(sylvan_and))); bdd_refs_pop(5); result = sylvan_makenode(s, r0, r1); } else { - syBDD a0, a1, b0, b1; + SyBDD a0, a1, b0, b1; if (na && va == level) { a0 = node_low(a, na); a1 = node_high(a, na); @@ -1465,9 +1348,9 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relprev, a0, b0, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relprev, a1, b1, vars, level)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_relprev)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_relprev)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_relprev)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_relprev)); bdd_refs_push(r0); result = CALL(sylvan_ite, r0, sylvan_true, r1, 0); bdd_refs_pop(2); @@ -1479,21 +1362,21 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relnext, a0, b1, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relnext, a1, b1, vars, level)); - syBDD r11 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r11 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r11); - syBDD r01 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r01 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r01); - syBDD r10 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r10 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r10); - syBDD r00 = bdd_refs_sync(SYNC(sylvan_relnext)); + SyBDD r00 = bdd_refs_sync(SYNC(sylvan_relnext)); bdd_refs_push(r00); bdd_refs_spawn(SPAWN(sylvan_ite, r00, sylvan_true, r10, 0)); bdd_refs_spawn(SPAWN(sylvan_ite, r01, sylvan_true, r11, 0)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_ite)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_ite)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_ite)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_ite)); bdd_refs_pop(5); result = sylvan_makenode(level, r0, r1); @@ -1502,9 +1385,9 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre bdd_refs_spawn(SPAWN(sylvan_relprev, a0, b0, vars, level)); bdd_refs_spawn(SPAWN(sylvan_relprev, a1, b1, vars, level)); - syBDD r1 = bdd_refs_sync(SYNC(sylvan_relprev)); + SyBDD r1 = bdd_refs_sync(SYNC(sylvan_relprev)); bdd_refs_push(r1); - syBDD r0 = bdd_refs_sync(SYNC(sylvan_relprev)); + SyBDD r0 = bdd_refs_sync(SYNC(sylvan_relprev)); bdd_refs_pop(1); result = sylvan_makenode(level, r0, r1); } @@ -1518,12 +1401,12 @@ TASK_IMPL_4(syBDD, sylvan_relprev, syBDD, a, syBDD, b, BDDSET, vars, BDDVAR, pre } /** - * Computes the transitive closure by traversing the BDD recursively. + * Computes the transitive closure by traversing the SyBDD recursively. * See Y. Matsunaga, P. C. McGeer, R. K. Brayton * On Computing the Transitive Closre of a State Transition Relation * 30th ACM Design Automation Conference, 1993. */ -TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) +TASK_IMPL_2(SyBDD, sylvan_closure, SyBDD, a, BDDVAR, prev_level) { /* Terminals */ if (a == sylvan_true) return a; @@ -1536,13 +1419,13 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) sylvan_stats_count(BDD_CLOSURE); /* Determine top level */ - bddnode_t n = GETNODE(a); + bddnode_t n = MTBDD_GETNODE(a); BDDVAR level = bddnode_getvariable(n); /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_CLOSURE, a, 0, 0, &result)) { sylvan_stats_count(BDD_CLOSURE_CACHED); return result; @@ -1552,7 +1435,7 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) BDDVAR s = level & (~1); BDDVAR t = s+1; - syBDD a0, a1; + SyBDD a0, a1; if (level == s) { a0 = node_low(a, n); a1 = node_high(a, n); @@ -1560,9 +1443,9 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) a0 = a1 = a; } - syBDD a00, a01, a10, a11; + SyBDD a00, a01, a10, a11; if (!sylvan_isconst(a0)) { - bddnode_t na0 = GETNODE(a0); + bddnode_t na0 = MTBDD_GETNODE(a0); if (bddnode_getvariable(na0) == t) { a00 = node_low(a0, na0); a01 = node_high(a0, na0); @@ -1573,7 +1456,7 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) a00 = a01 = a0; } if (!sylvan_isconst(a1)) { - bddnode_t na1 = GETNODE(a1); + bddnode_t na1 = MTBDD_GETNODE(a1); if (bddnode_getvariable(na1) == t) { a10 = node_low(a1, na1); a11 = node_high(a1, na1); @@ -1584,12 +1467,12 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) a10 = a11 = a1; } - syBDD u1 = CALL(sylvan_closure, a11, level); + SyBDD u1 = CALL(sylvan_closure, a11, level); bdd_refs_push(u1); /* u3 = */ bdd_refs_spawn(SPAWN(sylvan_relprev, a01, u1, sylvan_false, level)); - syBDD u2 = CALL(sylvan_relprev, u1, a10, sylvan_false, level); + SyBDD u2 = CALL(sylvan_relprev, u1, a10, sylvan_false, level); bdd_refs_push(u2); - syBDD e = CALL(sylvan_relprev, a01, u2, sylvan_false, level); + SyBDD e = CALL(sylvan_relprev, a01, u2, sylvan_false, level); bdd_refs_push(e); e = CALL(sylvan_ite, a00, sylvan_true, e, level); bdd_refs_pop(1); @@ -1597,25 +1480,25 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) e = CALL(sylvan_closure, e, level); bdd_refs_pop(1); bdd_refs_push(e); - syBDD g = CALL(sylvan_relprev, u2, e, sylvan_false, level); + SyBDD g = CALL(sylvan_relprev, u2, e, sylvan_false, level); bdd_refs_push(g); - syBDD u3 = bdd_refs_sync(SYNC(sylvan_relprev)); + SyBDD u3 = bdd_refs_sync(SYNC(sylvan_relprev)); bdd_refs_push(u3); - syBDD f = CALL(sylvan_relprev, e, u3, sylvan_false, level); + SyBDD f = CALL(sylvan_relprev, e, u3, sylvan_false, level); bdd_refs_push(f); - syBDD h = CALL(sylvan_relprev, u2, f, sylvan_false, level); + SyBDD h = CALL(sylvan_relprev, u2, f, sylvan_false, level); bdd_refs_push(h); h = CALL(sylvan_ite, u1, sylvan_true, h, level); bdd_refs_pop(1); bdd_refs_push(h); - syBDD r0, r1; + SyBDD r0, r1; /* R0 */ r0 = sylvan_makenode(t, e, f); bdd_refs_pop(7); bdd_refs_push(r0); /* R1 */ r1 = sylvan_makenode(t, g, h); bdd_refs_pop(1); - syBDD result = sylvan_makenode(s, r0, r1); + SyBDD result = sylvan_makenode(s, r0, r1); if (cachenow) { if (cache_put3(CACHE_BDD_CLOSURE, a, 0, 0, result)) sylvan_stats_count(BDD_CLOSURE_CACHEDPUT); @@ -1628,7 +1511,7 @@ TASK_IMPL_2(syBDD, sylvan_closure, syBDD, a, BDDVAR, prev_level) /** * Function composition */ -TASK_IMPL_3(syBDD, sylvan_compose, syBDD, a, BDDMAP, map, BDDVAR, prev_level) +TASK_IMPL_3(SyBDD, sylvan_compose, SyBDD, a, BDDMAP, map, BDDVAR, prev_level) { /* Trivial cases */ if (a == sylvan_false || a == sylvan_true) return a; @@ -1641,23 +1524,23 @@ TASK_IMPL_3(syBDD, sylvan_compose, syBDD, a, BDDMAP, map, BDDVAR, prev_level) sylvan_stats_count(BDD_COMPOSE); /* Determine top level */ - bddnode_t n = GETNODE(a); + bddnode_t n = MTBDD_GETNODE(a); BDDVAR level = bddnode_getvariable(n); /* Skip map */ - bddnode_t map_node = GETNODE(map); + bddnode_t map_node = MTBDD_GETNODE(map); BDDVAR map_var = bddnode_getvariable(map_node); while (map_var < level) { map = node_low(map, map_node); if (sylvan_map_isempty(map)) return a; - map_node = GETNODE(map); + map_node = MTBDD_GETNODE(map); map_var = bddnode_getvariable(map_node); } /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; if (cachenow) { - syBDD result; + SyBDD result; if (cache_get3(CACHE_BDD_COMPOSE, a, map, 0, &result)) { sylvan_stats_count(BDD_COMPOSE_CACHED); return result; @@ -1666,15 +1549,15 @@ TASK_IMPL_3(syBDD, sylvan_compose, syBDD, a, BDDMAP, map, BDDVAR, prev_level) /* Recursively calculate low and high */ bdd_refs_spawn(SPAWN(sylvan_compose, node_low(a, n), map, level)); - syBDD high = CALL(sylvan_compose, node_high(a, n), map, level); + SyBDD high = CALL(sylvan_compose, node_high(a, n), map, level); bdd_refs_push(high); - syBDD low = bdd_refs_sync(SYNC(sylvan_compose)); + SyBDD low = bdd_refs_sync(SYNC(sylvan_compose)); bdd_refs_push(low); /* Calculate result */ - syBDD root = map_var == level ? node_high(map, map_node) : sylvan_ithvar(level); + SyBDD root = map_var == level ? node_high(map, map_node) : sylvan_ithvar(level); bdd_refs_push(root); - syBDD result = CALL(sylvan_ite, root, high, low, 0); + SyBDD result = CALL(sylvan_ite, root, high, low, 0); bdd_refs_pop(3); if (cachenow) { @@ -1684,42 +1567,10 @@ TASK_IMPL_3(syBDD, sylvan_compose, syBDD, a, BDDMAP, map, BDDVAR, prev_level) return result; } -/** - * Count number of nodes in BDD - */ -uint64_t sylvan_nodecount_do_1(syBDD a) -{ - if (sylvan_isconst(a)) return 0; - bddnode_t na = GETNODE(a); - if (bddnode_getmark(na)) return 0; - bddnode_setmark(na, 1); - uint64_t result = 1; - result += sylvan_nodecount_do_1(bddnode_getlow(na)); - result += sylvan_nodecount_do_1(bddnode_gethigh(na)); - return result; -} - -void sylvan_nodecount_do_2(syBDD a) -{ - if (sylvan_isconst(a)) return; - bddnode_t na = GETNODE(a); - if (!bddnode_getmark(na)) return; - bddnode_setmark(na, 0); - sylvan_nodecount_do_2(bddnode_getlow(na)); - sylvan_nodecount_do_2(bddnode_gethigh(na)); -} - -size_t sylvan_nodecount(syBDD a) -{ - uint32_t result = sylvan_nodecount_do_1(a); - sylvan_nodecount_do_2(a); - return result; -} - /** * Calculate the number of distinct paths to True. */ -TASK_IMPL_2(double, sylvan_pathcount, syBDD, bdd, BDDVAR, prev_level) +TASK_IMPL_2(double, sylvan_pathcount, SyBDD, bdd, BDDVAR, prev_level) { /* Trivial cases */ if (bdd == sylvan_false) return 0.0; @@ -1728,9 +1579,10 @@ TASK_IMPL_2(double, sylvan_pathcount, syBDD, bdd, BDDVAR, prev_level) /* Perhaps execute garbage collection */ sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_PATHCOUNT); - syBDD level = sylvan_var(bdd); + SyBDD level = sylvan_var(bdd); /* Consult cache */ int cachenow = granularity < 2 || prev_level == 0 ? 1 : prev_level / granularity != level / granularity; @@ -1757,7 +1609,7 @@ TASK_IMPL_2(double, sylvan_pathcount, syBDD, bdd, BDDVAR, prev_level) /** * Calculate the number of satisfying variable assignments according to <variables>. */ -TASK_IMPL_3(double, sylvan_satcount, syBDD, bdd, BDDSET, variables, BDDVAR, prev_level) +TASK_IMPL_3(double, sylvan_satcount, SyBDD, bdd, BDDSET, variables, BDDVAR, prev_level) { /* Trivial cases */ if (bdd == sylvan_false) return 0.0; @@ -1766,19 +1618,20 @@ TASK_IMPL_3(double, sylvan_satcount, syBDD, bdd, BDDSET, variables, BDDVAR, prev /* Perhaps execute garbage collection */ sylvan_gc_test(); + /* Count operation */ sylvan_stats_count(BDD_SATCOUNT); /* Count variables before var(bdd) */ size_t skipped = 0; BDDVAR var = sylvan_var(bdd); - bddnode_t set_node = GETNODE(variables); + bddnode_t set_node = MTBDD_GETNODE(variables); BDDVAR set_var = bddnode_getvariable(set_node); while (var != set_var) { skipped++; variables = node_high(variables, set_node); // if this assertion fails, then variables is not the support of <bdd> assert(!sylvan_set_isempty(variables)); - set_node = GETNODE(variables); + set_node = MTBDD_GETNODE(variables); set_var = bddnode_getvariable(set_node); } @@ -1809,18 +1662,18 @@ TASK_IMPL_3(double, sylvan_satcount, syBDD, bdd, BDDSET, variables, BDDVAR, prev } int -sylvan_sat_one(syBDD bdd, BDDSET vars, uint8_t *str) +sylvan_sat_one(SyBDD bdd, BDDSET vars, uint8_t *str) { if (bdd == sylvan_false) return 0; if (str == NULL) return 0; if (sylvan_set_isempty(vars)) return 1; for (;;) { - bddnode_t n_vars = GETNODE(vars); + bddnode_t n_vars = MTBDD_GETNODE(vars); if (bdd == sylvan_true) { *str = 0; } else { - bddnode_t n_bdd = GETNODE(bdd); + bddnode_t n_bdd = MTBDD_GETNODE(bdd); if (bddnode_getvariable(n_bdd) != bddnode_getvariable(n_vars)) { *str = 0; } else { @@ -1843,8 +1696,8 @@ sylvan_sat_one(syBDD bdd, BDDSET vars, uint8_t *str) return 1; } -syBDD -sylvan_sat_single(syBDD bdd, BDDSET vars) +SyBDD +sylvan_sat_single(SyBDD bdd, BDDSET vars) { if (bdd == sylvan_false) return sylvan_false; if (sylvan_set_isempty(vars)) { @@ -1852,44 +1705,44 @@ sylvan_sat_single(syBDD bdd, BDDSET vars) return sylvan_true; } - bddnode_t n_vars = GETNODE(vars); + bddnode_t n_vars = MTBDD_GETNODE(vars); uint32_t var = bddnode_getvariable(n_vars); - syBDD next_vars = node_high(vars, n_vars); + SyBDD next_vars = node_high(vars, n_vars); if (bdd == sylvan_true) { // take false - syBDD res = sylvan_sat_single(bdd, next_vars); + SyBDD res = sylvan_sat_single(bdd, next_vars); return sylvan_makenode(var, res, sylvan_false); } - bddnode_t n_bdd = GETNODE(bdd); + bddnode_t n_bdd = MTBDD_GETNODE(bdd); if (bddnode_getvariable(n_bdd) != var) { assert(bddnode_getvariable(n_bdd)>var); // take false - syBDD res = sylvan_sat_single(bdd, next_vars); + SyBDD res = sylvan_sat_single(bdd, next_vars); return sylvan_makenode(var, res, sylvan_false); } if (node_high(bdd, n_bdd) == sylvan_false) { // take false - syBDD res = sylvan_sat_single(node_low(bdd, n_bdd), next_vars); + SyBDD res = sylvan_sat_single(node_low(bdd, n_bdd), next_vars); return sylvan_makenode(var, res, sylvan_false); } // take true - syBDD res = sylvan_sat_single(node_high(bdd, n_bdd), next_vars); + SyBDD res = sylvan_sat_single(node_high(bdd, n_bdd), next_vars); return sylvan_makenode(var, sylvan_false, res); } -syBDD -sylvan_sat_one_bdd(syBDD bdd) +SyBDD +sylvan_sat_one_bdd(SyBDD bdd) { if (bdd == sylvan_false) return sylvan_false; if (bdd == sylvan_true) return sylvan_true; - bddnode_t node = GETNODE(bdd); - syBDD low = node_low(bdd, node); - syBDD high = node_high(bdd, node); + bddnode_t node = MTBDD_GETNODE(bdd); + SyBDD low = node_low(bdd, node); + SyBDD high = node_high(bdd, node); - syBDD m; + SyBDD m; - syBDD result; + SyBDD result; if (low == sylvan_false) { m = sylvan_sat_one_bdd(high); result = sylvan_makenode(bddnode_getvariable(node), sylvan_false, m); @@ -1909,16 +1762,16 @@ sylvan_sat_one_bdd(syBDD bdd) return result; } -syBDD +SyBDD sylvan_cube(BDDSET vars, uint8_t *cube) { if (sylvan_set_isempty(vars)) return sylvan_true; - bddnode_t n = GETNODE(vars); + bddnode_t n = MTBDD_GETNODE(vars); BDDVAR v = bddnode_getvariable(n); vars = node_high(vars, n); - syBDD result = sylvan_cube(vars, cube+1); + SyBDD result = sylvan_cube(vars, cube+1); if (*cube == 0) { result = sylvan_makenode(v, result, sylvan_false); } else if (*cube == 1) { @@ -1928,14 +1781,14 @@ sylvan_cube(BDDSET vars, uint8_t *cube) return result; } -TASK_IMPL_3(syBDD, sylvan_union_cube, syBDD, bdd, BDDSET, vars, uint8_t *, cube) +TASK_IMPL_3(SyBDD, sylvan_union_cube, SyBDD, bdd, BDDSET, vars, uint8_t *, cube) { /* Terminal cases */ if (bdd == sylvan_true) return sylvan_true; if (bdd == sylvan_false) return sylvan_cube(vars, cube); if (sylvan_set_isempty(vars)) return sylvan_true; - bddnode_t nv = GETNODE(vars); + bddnode_t nv = MTBDD_GETNODE(vars); for (;;) { if (*cube == 0 || *cube == 1) break; @@ -1943,15 +1796,15 @@ TASK_IMPL_3(syBDD, sylvan_union_cube, syBDD, bdd, BDDSET, vars, uint8_t *, cube) cube++; vars = node_high(vars, nv); if (sylvan_set_isempty(vars)) return sylvan_true; - nv = GETNODE(vars); + nv = MTBDD_GETNODE(vars); } sylvan_gc_test(); // missing: SV_CNT_OP - bddnode_t n = GETNODE(bdd); - syBDD result = bdd; + bddnode_t n = MTBDD_GETNODE(bdd); + SyBDD result = bdd; BDDVAR v = bddnode_getvariable(nv); BDDVAR n_level = bddnode_getvariable(n); @@ -1965,27 +1818,27 @@ TASK_IMPL_3(syBDD, sylvan_union_cube, syBDD, bdd, BDDSET, vars, uint8_t *, cube) result = sylvan_makenode(v, bdd, result); } } else if (v > n_level) { - syBDD high = node_high(bdd, n); - syBDD low = node_low(bdd, n); - SPAWN(sylvan_union_cube, high, vars, cube); - syBDD new_low = sylvan_union_cube(low, vars, cube); + SyBDD high = node_high(bdd, n); + SyBDD low = node_low(bdd, n); + bdd_refs_spawn(SPAWN(sylvan_union_cube, high, vars, cube)); + SyBDD new_low = sylvan_union_cube(low, vars, cube); bdd_refs_push(new_low); - syBDD new_high = SYNC(sylvan_union_cube); + SyBDD new_high = bdd_refs_sync(SYNC(sylvan_union_cube)); bdd_refs_pop(1); if (new_low != low || new_high != high) { result = sylvan_makenode(n_level, new_low, new_high); } } else /* v == n_level */ { vars = node_high(vars, nv); - syBDD high = node_high(bdd, n); - syBDD low = node_low(bdd, n); + SyBDD high = node_high(bdd, n); + SyBDD low = node_low(bdd, n); if (*cube == 0) { - syBDD new_low = sylvan_union_cube(low, vars, cube+1); + SyBDD new_low = sylvan_union_cube(low, vars, cube+1); if (new_low != low) { result = sylvan_makenode(n_level, new_low, high); } } else /* *cube == 1 */ { - syBDD new_high = sylvan_union_cube(high, vars, cube+1); + SyBDD new_high = sylvan_union_cube(high, vars, cube+1); if (new_high != high) { result = sylvan_makenode(n_level, low, new_high); } @@ -2002,7 +1855,7 @@ struct bdd_path int8_t val; // 0=false, 1=true, 2=both }; -VOID_TASK_5(sylvan_enum_do, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context, struct bdd_path*, path) +VOID_TASK_5(sylvan_enum_do, SyBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context, struct bdd_path*, path) { if (bdd == sylvan_false) return; @@ -2050,7 +1903,7 @@ VOID_TASK_5(sylvan_enum_do, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, contex } } -VOID_TASK_5(sylvan_enum_par_do, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context, struct bdd_path*, path) +VOID_TASK_5(sylvan_enum_par_do, SyBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context, struct bdd_path*, path) { if (bdd == sylvan_false) return; @@ -2077,9 +1930,9 @@ VOID_TASK_5(sylvan_enum_par_do, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, co return; } - syBDD var = sylvan_var(vars); + SyBDD var = sylvan_var(vars); vars = sylvan_set_next(vars); - syBDD bdd_var = sylvan_var(bdd); + SyBDD bdd_var = sylvan_var(bdd); /* assert var <= bdd_var */ if (var < bdd_var) { @@ -2099,474 +1952,81 @@ VOID_TASK_5(sylvan_enum_par_do, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, co } } -VOID_TASK_IMPL_4(sylvan_enum, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context) +VOID_TASK_IMPL_4(sylvan_enum, SyBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context) { CALL(sylvan_enum_do, bdd, vars, cb, context, 0); } -VOID_TASK_IMPL_4(sylvan_enum_par, syBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context) +VOID_TASK_IMPL_4(sylvan_enum_par, SyBDD, bdd, BDDSET, vars, enum_cb, cb, void*, context) { CALL(sylvan_enum_par_do, bdd, vars, cb, context, 0); } -TASK_5(syBDD, sylvan_collect_do, syBDD, bdd, BDDSET, vars, sylvan_collect_cb, cb, void*, context, struct bdd_path*, path) +TASK_5(SyBDD, sylvan_collect_do, SyBDD, bdd, BDDSET, vars, sylvan_collect_cb, cb, void*, context, struct bdd_path*, path) { - if (bdd == sylvan_false) return sylvan_false; - - if (sylvan_set_isempty(vars)) { - /* compute length of path */ - int i=0; - struct bdd_path *pp; - for (pp = path; pp != NULL; pp = pp->prev) i++; - /* if length is 0 (enum called with empty vars??), return */ - if (i == 0) return WRAP(cb, context, NULL); - /* fill cube and vars with trace */ - uint8_t cube[i]; - int j=0; - for (pp = path; pp != NULL; pp = pp->prev) { - cube[i-j-1] = pp->val; - j++; + if (bdd == sylvan_false) { + return sylvan_false; + } else if (sylvan_set_isempty(vars)) { + /** + * Compute trace length + */ + size_t len = 0; + struct bdd_path *p = path; + while (p != NULL) { + len++; + p = p->prev; } - /* call callback */ - return WRAP(cb, context, cube); - } else { - syBDD var = sylvan_var(vars); - vars = sylvan_set_next(vars); - syBDD bdd_var = sylvan_var(bdd); - - /* if fails, then <bdd> has variables not in <vars> */ - assert(var <= bdd_var); - - struct bdd_path pp1 = (struct bdd_path){path, var, 1}; - struct bdd_path pp0 = (struct bdd_path){path, var, 0}; - if (var < bdd_var) { - bdd_refs_spawn(SPAWN(sylvan_collect_do, bdd, vars, cb, context, &pp1)); - syBDD low = bdd_refs_push(CALL(sylvan_collect_do, bdd, vars, cb, context, &pp0)); - syBDD high = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_collect_do))); - syBDD res = sylvan_or(low, high); - bdd_refs_pop(2); - return res; - } else if (var == bdd_var) { - bdd_refs_spawn(SPAWN(sylvan_collect_do, sylvan_high(bdd), vars, cb, context, &pp1)); - syBDD low = bdd_refs_push(CALL(sylvan_collect_do, sylvan_low(bdd), vars, cb, context, &pp0)); - syBDD high = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_collect_do))); - syBDD res = sylvan_or(low, high); - bdd_refs_pop(2); - return res; - } else { - return sylvan_invalid; // unreachable + /** + * Fill array + */ + uint8_t arr[len]; + for (size_t i=0; i<len; i++) { + arr[len-i-1] = path->val; + path = path->prev; } - } -} - -TASK_IMPL_4(syBDD, sylvan_collect, syBDD, bdd, BDDSET, vars, sylvan_collect_cb, cb, void*, context) -{ - return CALL(sylvan_collect_do, bdd, vars, cb, context, 0); -} - -/** - * IMPLEMENTATION OF BDDSET - */ - -int -sylvan_set_in(BDDSET set, BDDVAR level) -{ - while (!sylvan_set_isempty(set)) { - bddnode_t n = GETNODE(set); - BDDVAR n_level = bddnode_getvariable(n); - if (n_level == level) return 1; - if (n_level > level) return 0; // BDDs are ordered - set = node_high(set, n); - } - - return 0; -} - -size_t -sylvan_set_count(BDDSET set) -{ - size_t result = 0; - for (;!sylvan_set_isempty(set);set = sylvan_set_next(set)) result++; - return result; -} - -void -sylvan_set_toarray(BDDSET set, BDDVAR *arr) -{ - size_t i = 0; - while (!sylvan_set_isempty(set)) { - bddnode_t n = GETNODE(set); - arr[i++] = bddnode_getvariable(n); - set = node_high(set, n); - } -} - -TASK_IMPL_2(BDDSET, sylvan_set_fromarray, BDDVAR*, arr, size_t, length) -{ - if (length == 0) return sylvan_set_empty(); - BDDSET sub = sylvan_set_fromarray(arr+1, length-1); - bdd_refs_push(sub); - BDDSET result = sylvan_set_add(sub, *arr); - bdd_refs_pop(1); - return result; -} - -void -sylvan_test_isset(BDDSET set) -{ - while (set != sylvan_false) { - assert(set != sylvan_true); - assert(llmsset_is_marked(nodes, set)); - bddnode_t n = GETNODE(set); - assert(node_low(set, n) == sylvan_true); - set = node_high(set, n); - } -} - -/** - * IMPLEMENTATION OF BDDMAP - */ - -BDDMAP -sylvan_map_add(BDDMAP map, BDDVAR key, syBDD value) -{ - if (sylvan_map_isempty(map)) return sylvan_makenode(key, sylvan_map_empty(), value); - - bddnode_t n = GETNODE(map); - BDDVAR key_m = bddnode_getvariable(n); - - if (key_m < key) { - // add recursively and rebuild tree - BDDMAP low = sylvan_map_add(node_low(map, n), key, value); - BDDMAP result = sylvan_makenode(key_m, low, node_high(map, n)); - return result; - } else if (key_m > key) { - return sylvan_makenode(key, map, value); - } else { - // replace old - return sylvan_makenode(key, node_low(map, n), value); - } -} - -BDDMAP -sylvan_map_addall(BDDMAP map_1, BDDMAP map_2) -{ - // one of the maps is empty - if (sylvan_map_isempty(map_1)) return map_2; - if (sylvan_map_isempty(map_2)) return map_1; - - bddnode_t n_1 = GETNODE(map_1); - BDDVAR key_1 = bddnode_getvariable(n_1); - - bddnode_t n_2 = GETNODE(map_2); - BDDVAR key_2 = bddnode_getvariable(n_2); - - BDDMAP result; - if (key_1 < key_2) { - // key_1, recurse on n_1->low, map_2 - BDDMAP low = sylvan_map_addall(node_low(map_1, n_1), map_2); - result = sylvan_makenode(key_1, low, node_high(map_1, n_1)); - } else if (key_1 > key_2) { - // key_2, recurse on map_1, n_2->low - BDDMAP low = sylvan_map_addall(map_1, node_low(map_2, n_2)); - result = sylvan_makenode(key_2, low, node_high(map_2, n_2)); - } else { - // equal: key_2, recurse on n_1->low, n_2->low - BDDMAP low = sylvan_map_addall(node_low(map_1, n_1), node_low(map_2, n_2)); - result = sylvan_makenode(key_2, low, node_high(map_2, n_2)); - } - return result; -} - -BDDMAP -sylvan_map_remove(BDDMAP map, BDDVAR key) -{ - if (sylvan_map_isempty(map)) return map; - - bddnode_t n = GETNODE(map); - BDDVAR key_m = bddnode_getvariable(n); - - if (key_m < key) { - BDDMAP low = sylvan_map_remove(node_low(map, n), key); - BDDMAP result = sylvan_makenode(key_m, low, node_high(map, n)); - return result; - } else if (key_m > key) { - return map; - } else { - return node_low(map, n); - } -} - -BDDMAP -sylvan_map_removeall(BDDMAP map, BDDSET toremove) -{ - if (sylvan_map_isempty(map)) return map; - if (sylvan_set_isempty(toremove)) return map; - - bddnode_t n_1 = GETNODE(map); - BDDVAR key_1 = bddnode_getvariable(n_1); - - bddnode_t n_2 = GETNODE(toremove); - BDDVAR key_2 = bddnode_getvariable(n_2); - - if (key_1 < key_2) { - BDDMAP low = sylvan_map_removeall(node_low(map, n_1), toremove); - BDDMAP result = sylvan_makenode(key_1, low, node_high(map, n_1)); - return result; - } else if (key_1 > key_2) { - return sylvan_map_removeall(map, node_high(toremove, n_2)); + /** + * Call callback + */ + return WRAP(cb, context, arr); } else { - return sylvan_map_removeall(node_low(map, n_1), node_high(toremove, n_2)); - } -} - -int -sylvan_map_in(BDDMAP map, BDDVAR key) -{ - while (!sylvan_map_isempty(map)) { - bddnode_t n = GETNODE(map); - BDDVAR n_level = bddnode_getvariable(n); - if (n_level == key) return 1; - if (n_level > key) return 0; // BDDs are ordered - map = node_low(map, n); - } - - return 0; -} - -size_t -sylvan_map_count(BDDMAP map) -{ - size_t r=0; - while (!sylvan_map_isempty(map)) { r++; map=sylvan_map_next(map); } - return r; -} - -BDDMAP -sylvan_set_to_map(BDDSET set, syBDD value) -{ - if (sylvan_set_isempty(set)) return sylvan_map_empty(); - bddnode_t set_n = GETNODE(set); - syBDD sub = sylvan_set_to_map(node_high(set, set_n), value); - syBDD result = sylvan_makenode(sub, bddnode_getvariable(set_n), value); - return result; -} - -/** - * Determine the support of a BDD (all variables used in the BDD) - */ -TASK_IMPL_1(syBDD, sylvan_support, syBDD, bdd) -{ - if (bdd == sylvan_true || bdd == sylvan_false) return sylvan_set_empty(); // return empty set - - sylvan_gc_test(); - - sylvan_stats_count(BDD_SUPPORT); - - syBDD result; - if (cache_get3(CACHE_BDD_SUPPORT, bdd, 0, 0, &result)) { - sylvan_stats_count(BDD_SUPPORT_CACHED); - return result; - } - - bddnode_t n = GETNODE(bdd); - syBDD high, low, set; - - /* compute recursively */ - bdd_refs_spawn(SPAWN(sylvan_support, bddnode_getlow(n))); - high = bdd_refs_push(CALL(sylvan_support, bddnode_gethigh(n))); - low = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_support))); - - /* take intersection of support of low and support of high */ - set = sylvan_and(low, high); - bdd_refs_pop(2); - - /* add current level to set */ - result = sylvan_makenode(bddnode_getvariable(n), sylvan_false, set); - - if (cache_put3(CACHE_BDD_SUPPORT, bdd, 0, 0, result)) sylvan_stats_count(BDD_SUPPORT_CACHEDPUT); - return result; -} - -static void -sylvan_unmark_rec(bddnode_t node) -{ - if (bddnode_getmark(node)) { - bddnode_setmark(node, 0); - if (!sylvan_isconst(bddnode_getlow(node))) sylvan_unmark_rec(GETNODE(bddnode_getlow(node))); - if (!sylvan_isconst(bddnode_gethigh(node))) sylvan_unmark_rec(GETNODE(bddnode_gethigh(node))); - } -} - -/** - * fprint, print - */ -void -sylvan_fprint(FILE *f, syBDD bdd) -{ - sylvan_serialize_reset(); - size_t v = sylvan_serialize_add(bdd); - fprintf(f, "%s%zu,", bdd&sylvan_complement?"!":"", v); - sylvan_serialize_totext(f); -} - -void -sylvan_print(syBDD bdd) -{ - sylvan_fprint(stdout, bdd); -} - -/** - * Output to .DOT files - */ - -/*** - * We keep a set [level -> [node]] using AVLset - */ -struct level_to_nodeset { - BDDVAR level; - avl_node_t *set; -}; - -AVL(level_to_nodeset, struct level_to_nodeset) -{ - if (left->level > right->level) return 1; - if (right->level > left->level) return -1; - return 0; -} - -AVL(nodeset, syBDD) -{ - if (*left > *right) return 1; - if (*right > *left) return -1; - return 0; -} - -/* returns 1 if inserted, 0 if already existed */ -static int __attribute__((noinline)) -sylvan_dothelper_register(avl_node_t **set, syBDD bdd) -{ - struct level_to_nodeset s, *ss; - s.level = sylvan_var(bdd); - ss = level_to_nodeset_search(*set, &s); - if (ss == NULL) { - s.set = NULL; - ss = level_to_nodeset_put(set, &s, NULL); - } - assert(ss != NULL); - return nodeset_insert(&ss->set, &bdd); -} - -static void -sylvan_fprintdot_rec(FILE *out, syBDD bdd, avl_node_t **levels) -{ - bdd = BDD_STRIPMARK(bdd); - if (bdd == sylvan_false) return; - if (!sylvan_dothelper_register(levels, bdd)) return; - - syBDD low = sylvan_low(bdd); - syBDD high = sylvan_high(bdd); - fprintf(out, "\"%" PRIx64 "\" [label=\"%d\"];\n", bdd, sylvan_var(bdd)); - fprintf(out, "\"%" PRIx64 "\" -> \"%" PRIx64 "\" [style=dashed];\n", bdd, low); - fprintf(out, "\"%" PRIx64 "\" -> \"%" PRIx64 "\" [style=solid dir=both arrowtail=%s];\n", bdd, BDD_STRIPMARK(high), BDD_HASMARK(high) ? "dot" : "none"); - sylvan_fprintdot_rec(out, low, levels); - sylvan_fprintdot_rec(out, high, levels); -} - -void -sylvan_fprintdot(FILE *out, syBDD bdd) -{ - fprintf(out, "digraph \"DD\" {\n"); - fprintf(out, "graph [dpi = 300];\n"); - fprintf(out, "center = true;\n"); - fprintf(out, "edge [dir = forward];\n"); - fprintf(out, "0 [label=\"0\", style=filled, shape=box, height=0.3, width=0.3];\n"); - fprintf(out, "root [style=invis];\n"); - fprintf(out, "root -> \"%" PRIx64 "\" [style=solid dir=both arrowtail=%s];\n", BDD_STRIPMARK(bdd), BDD_HASMARK(bdd) ? "dot" : "none"); - - avl_node_t *levels = NULL; - sylvan_fprintdot_rec(out, bdd, &levels); - - if (levels != NULL) { - size_t levels_count = avl_count(levels); - struct level_to_nodeset *arr = level_to_nodeset_toarray(levels); - size_t i; - for (i=0;i<levels_count;i++) { - fprintf(out, "{ rank=same; "); - size_t node_count = avl_count(arr[i].set); - size_t j; - syBDD *arr_j = nodeset_toarray(arr[i].set); - for (j=0;j<node_count;j++) { - fprintf(out, "\"%" PRIx64 "\"; ", arr_j[j]); - } - fprintf(out, "}\n"); - } - level_to_nodeset_free(&levels); - } - - fprintf(out, "}\n"); -} - -void -sylvan_printdot(syBDD bdd) -{ - sylvan_fprintdot(stdout, bdd); -} - -static void -sylvan_fprintdot_nc_rec(FILE *out, syBDD bdd, avl_node_t **levels) -{ - if (bdd == sylvan_true || bdd == sylvan_false) return; - if (!sylvan_dothelper_register(levels, bdd)) return; - - syBDD low = sylvan_low(bdd); - syBDD high = sylvan_high(bdd); - fprintf(out, "\"%" PRIx64 "\" [label=\"%d\"];\n", bdd, sylvan_var(bdd)); - fprintf(out, "\"%" PRIx64 "\" -> \"%" PRIx64 "\" [style=dashed];\n", bdd, low); - fprintf(out, "\"%" PRIx64 "\" -> \"%" PRIx64 "\" [style=solid];\n", bdd, high); - sylvan_fprintdot_nc_rec(out, low, levels); - sylvan_fprintdot_nc_rec(out, high, levels); -} - -void -sylvan_fprintdot_nc(FILE *out, syBDD bdd) -{ - fprintf(out, "digraph \"DD\" {\n"); - fprintf(out, "graph [dpi = 300];\n"); - fprintf(out, "center = true;\n"); - fprintf(out, "edge [dir = forward];\n"); - fprintf(out, "\"%" PRIx64 "\" [shape=box, label=\"F\", style=filled, shape=box, height=0.3, width=0.3];\n", sylvan_false); - fprintf(out, "\"%" PRIx64 "\" [shape=box, label=\"T\", style=filled, shape=box, height=0.3, width=0.3];\n", sylvan_true); - fprintf(out, "root [style=invis];\n"); - fprintf(out, "root -> \"%" PRIx64 "\" [style=solid];\n", bdd); - - avl_node_t *levels = NULL; - sylvan_fprintdot_nc_rec(out, bdd, &levels); - - if (levels != NULL) { - size_t levels_count = avl_count(levels); - struct level_to_nodeset *arr = level_to_nodeset_toarray(levels); - size_t i; - for (i=0;i<levels_count;i++) { - fprintf(out, "{ rank=same; "); - size_t node_count = avl_count(arr[i].set); - size_t j; - syBDD *arr_j = nodeset_toarray(arr[i].set); - for (j=0;j<node_count;j++) { - fprintf(out, "\"%" PRIx64 "\"; ", arr_j[j]); + /** + * Obtain domain variable + */ + const uint32_t dom_var = sylvan_var(vars); + const SyBDD dom_next = sylvan_set_next(vars); + /** + * Obtain cofactors + */ + SyBDD bdd0, bdd1; + if (bdd == sylvan_true) { + bdd0 = bdd1 = bdd; + } else { + const uint32_t bdd_var = sylvan_var(bdd); + assert(dom_var <= bdd_var); + if (dom_var < bdd_var) { + bdd0 = bdd1 = bdd; + } else { + bdd0 = sylvan_low(bdd); + bdd1 = sylvan_high(bdd); } - fprintf(out, "}\n"); - } - level_to_nodeset_free(&levels); + } + /** + * Call recursive functions + */ + struct bdd_path p0 = (struct bdd_path){path, dom_var, 0}; + struct bdd_path p1 = (struct bdd_path){path, dom_var, 1}; + bdd_refs_spawn(SPAWN(sylvan_collect_do, bdd1, dom_next, cb, context, &p1)); + SyBDD low = bdd_refs_push(CALL(sylvan_collect_do, bdd0, dom_next, cb, context, &p0)); + SyBDD high = bdd_refs_push(bdd_refs_sync(SYNC(sylvan_collect_do))); + SyBDD res = sylvan_or(low, high); + bdd_refs_pop(2); + return res; } - - fprintf(out, "}\n"); } -void -sylvan_printdot_nc(syBDD bdd) +TASK_IMPL_4(SyBDD, sylvan_collect, SyBDD, bdd, BDDSET, vars, sylvan_collect_cb, cb, void*, context) { - sylvan_fprintdot_nc(stdout, bdd); + return CALL(sylvan_collect_do, bdd, vars, cb, context, NULL); } /** @@ -2574,7 +2034,7 @@ sylvan_printdot_nc(syBDD bdd) */ struct sylvan_ser { - syBDD bdd; + SyBDD bdd; size_t assigned; }; @@ -2604,12 +2064,12 @@ static avl_node_t *sylvan_ser_reversed_set = NULL; static size_t sylvan_ser_counter = 1; static size_t sylvan_ser_done = 0; -// Given a BDD, assign unique numbers to all nodes +// Given a SyBDD, assign unique numbers to all nodes static size_t -sylvan_serialize_assign_rec(syBDD bdd) +sylvan_serialize_assign_rec(SyBDD bdd) { if (sylvan_isnode(bdd)) { - bddnode_t n = GETNODE(bdd); + bddnode_t n = MTBDD_GETNODE(bdd); struct sylvan_ser s, *ss; s.bdd = BDD_STRIPMARK(bdd); @@ -2637,7 +2097,7 @@ sylvan_serialize_assign_rec(syBDD bdd) } size_t -sylvan_serialize_add(syBDD bdd) +sylvan_serialize_add(SyBDD bdd) { return BDD_TRANSFERMARK(bdd, sylvan_serialize_assign_rec(bdd)); } @@ -2652,7 +2112,7 @@ sylvan_serialize_reset() } size_t -sylvan_serialize_get(syBDD bdd) +sylvan_serialize_get(SyBDD bdd) { if (!sylvan_isnode(bdd)) return bdd; struct sylvan_ser s, *ss; @@ -2662,7 +2122,7 @@ sylvan_serialize_get(syBDD bdd) return BDD_TRANSFERMARK(bdd, ss->assigned); } -syBDD +SyBDD sylvan_serialize_get_reversed(size_t value) { if (!sylvan_isnode(value)) return value; @@ -2681,8 +2141,8 @@ sylvan_serialize_totext(FILE *out) struct sylvan_ser *s; while ((s=sylvan_ser_reversed_iter_next(it))) { - syBDD bdd = s->bdd; - bddnode_t n = GETNODE(bdd); + SyBDD bdd = s->bdd; + bddnode_t n = MTBDD_GETNODE(bdd); fprintf(out, "(%zu,%u,%zu,%zu,%u),", s->assigned, bddnode_getvariable(n), (size_t)bddnode_getlow(n), @@ -2717,7 +2177,7 @@ sylvan_serialize_tofile(FILE *out) index++; assert(s->assigned == index); - bddnode_t n = GETNODE(s->bdd); + bddnode_t n = MTBDD_GETNODE(s->bdd); struct bddnode node; bddnode_makenode(&node, bddnode_getvariable(n), sylvan_serialize_get(bddnode_getlow(n)), sylvan_serialize_get(bddnode_gethigh(n))); @@ -2747,8 +2207,8 @@ sylvan_serialize_fromfile(FILE *in) exit(-1); } - syBDD low = sylvan_serialize_get_reversed(bddnode_getlow(&node)); - syBDD high = sylvan_serialize_get_reversed(bddnode_gethigh(&node)); + SyBDD low = sylvan_serialize_get_reversed(bddnode_getlow(&node)); + SyBDD high = sylvan_serialize_get_reversed(bddnode_gethigh(&node)); struct sylvan_ser s; s.bdd = sylvan_makenode(bddnode_getvariable(&node), low, high); @@ -2759,96 +2219,3 @@ sylvan_serialize_fromfile(FILE *in) } } -/** - * Generate SHA2 structural hashes. - * Hashes are independent of location. - * Mainly useful for debugging purposes. - */ -static void -sylvan_sha2_rec(syBDD bdd, SHA256_CTX *ctx) -{ - if (bdd == sylvan_true || bdd == sylvan_false) { - SHA256_Update(ctx, (void*)&bdd, sizeof(syBDD)); - return; - } - - bddnode_t node = GETNODE(bdd); - if (bddnode_getmark(node) == 0) { - bddnode_setmark(node, 1); - uint32_t level = bddnode_getvariable(node); - if (BDD_STRIPMARK(bddnode_gethigh(node))) level |= 0x80000000; - SHA256_Update(ctx, (void*)&level, sizeof(uint32_t)); - sylvan_sha2_rec(bddnode_gethigh(node), ctx); - sylvan_sha2_rec(bddnode_getlow(node), ctx); - } -} - -void -sylvan_printsha(syBDD bdd) -{ - sylvan_fprintsha(stdout, bdd); -} - -void -sylvan_fprintsha(FILE *f, syBDD bdd) -{ - char buf[80]; - sylvan_getsha(bdd, buf); - fprintf(f, "%s", buf); -} - -void -sylvan_getsha(syBDD bdd, char *target) -{ - SHA256_CTX ctx; - SHA256_Init(&ctx); - sylvan_sha2_rec(bdd, &ctx); - if (bdd != sylvan_true && bdd != sylvan_false) sylvan_unmark_rec(GETNODE(bdd)); - SHA256_End(&ctx, target); -} - -/** - * Debug tool to check that a BDD is properly ordered. - * Also that every BDD node is marked 'in-use' in the hash table. - */ -TASK_2(int, sylvan_test_isbdd_rec, syBDD, bdd, BDDVAR, parent_var) -{ - if (bdd == sylvan_true || bdd == sylvan_false) return 1; - assert(llmsset_is_marked(nodes, BDD_STRIPMARK(bdd))); - - sylvan_stats_count(BDD_ISBDD); - - uint64_t result; - if (cache_get3(CACHE_BDD_ISBDD, bdd, 0, 0, &result)) { - sylvan_stats_count(BDD_ISBDD_CACHED); - return result; - } - - bddnode_t n = GETNODE(bdd); - BDDVAR var = bddnode_getvariable(n); - if (var <= parent_var) { - result = 0; - } else { - SPAWN(sylvan_test_isbdd_rec, node_low(bdd, n), var); - result = (uint64_t)CALL(sylvan_test_isbdd_rec, node_high(bdd, n), var); - if (!SYNC(sylvan_test_isbdd_rec)) result = 0; - } - - if (cache_put3(CACHE_BDD_ISBDD, bdd, 0, 0, result)) sylvan_stats_count(BDD_ISBDD_CACHEDPUT); - return result; -} - -TASK_IMPL_1(int, sylvan_test_isbdd, syBDD, bdd) -{ - if (bdd == sylvan_true) return 1; - if (bdd == sylvan_false) return 1; - - assert(llmsset_is_marked(nodes, BDD_STRIPMARK(bdd))); - - bddnode_t n = GETNODE(bdd); - BDDVAR var = bddnode_getvariable(n); - SPAWN(sylvan_test_isbdd_rec, node_low(bdd, n), var); - int result = CALL(sylvan_test_isbdd_rec, node_high(bdd, n), var); - if (!SYNC(sylvan_test_isbdd_rec)) result = 0; - return result; -} diff --git a/sylvan_bdd.h b/sylvan_bdd.h old mode 100644 new mode 100755 index ee36526ce3c3d4d473482676e3799d8a21b9e033..e8d935ea5423472206ee0998751a4d41a334bb4c --- a/sylvan_bdd.h +++ b/sylvan_bdd.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,86 +17,57 @@ /* Do not include this file directly. Instead, include sylvan.h */ -#include <tls.h> - #ifndef SYLVAN_BDD_H #define SYLVAN_BDD_H -typedef uint64_t syBDD; // low 40 bits used for index, highest bit for complement, rest 0 -// BDDSET uses the BDD node hash table. A BDDSET is an ordered BDD. -typedef uint64_t BDDSET; // encodes a set of variables (e.g. for exists etc.) -// BDDMAP also uses the BDD node hash table. A BDDMAP is *not* an ordered BDD. -typedef uint64_t BDDMAP; // encodes a function of variable->BDD (e.g. for substitute) -typedef uint32_t BDDVAR; // low 24 bits only - #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/* For strictly non-MT BDDs */ +static inline int +sylvan_isconst(MTBDD bdd) +{ + return bdd == mtbdd_true || bdd == mtbdd_false ? 1 : 0; +} - -#define sylvan_complement ((uint64_t)0x8000000000000000) -#define sylvan_false ((syBDD)0x0000000000000000) -#define sylvan_true (sylvan_false|sylvan_complement) -#define sylvan_invalid ((syBDD)0x7fffffffffffffff) - -#define sylvan_isconst(bdd) (bdd == sylvan_true || bdd == sylvan_false) -#define sylvan_isnode(bdd) (bdd != sylvan_true && bdd != sylvan_false) +static inline int +sylvan_isnode(MTBDD bdd) +{ + return bdd != mtbdd_true && bdd != mtbdd_false ? 1 : 0; +} /** - * Initialize BDD functionality. - * - * Granularity (BDD only) determines usage of operation cache. Smallest value is 1: use the operation cache always. - * Higher values mean that the cache is used less often. Variables are grouped such that - * the cache is used when going to the next group, i.e., with granularity=3, variables [0,1,2] are in the - * first group, [3,4,5] in the next, etc. Then no caching occur between 0->1, 1->2, 0->2. Caching occurs - * on 0->3, 1->4, 2->3, etc. + * Granularity (SyBDD only) determines usage of operation cache. + * The smallest value is 1: use the operation cache always. + * Higher values mean that the cache is used less often. Variables are grouped + * such that the cache is used when going to the next group, i.e., with + * granularity=3, variables [0,1,2] are in the first group, [3,4,5] in the next, etc. + * Then no caching occur between 0->1, 1->2, 0->2. Caching occurs on 0->3, 1->4, 2->3, etc. * - * A reasonable default is a granularity of 4-16, strongly depending on the structure of the BDDs. + * The appropriate value depends on the number of variables and the structure of + * the decision diagrams. When in doubt, choose a low value (1-5). The performance + * gain can be around 0-10%, so it is not extremely important. */ -void sylvan_init_bdd(int granularity); - -/* Create a BDD representing just <var> or the negation of <var> */ -syBDD sylvan_ithvar(BDDVAR var); -static inline syBDD sylvan_nithvar(syBDD var) { return sylvan_ithvar(var) ^ sylvan_complement; } - -/* Retrieve the <var> of the BDD node <bdd> */ -BDDVAR sylvan_var(syBDD bdd); - -/* Follow <low> and <high> edges */ -syBDD sylvan_low(syBDD bdd); -syBDD sylvan_high(syBDD bdd); - -/* Add or remove external reference to BDD */ -syBDD sylvan_ref(syBDD a); -void sylvan_deref(syBDD a); - -/* For use in custom mark functions */ -VOID_TASK_DECL_1(sylvan_gc_mark_rec, syBDD); -#define sylvan_gc_mark_rec(mdd) CALL(sylvan_gc_mark_rec, mdd) - -/* Return the number of external references */ -size_t sylvan_count_refs(); - -/* Add or remove BDD pointers to protect (indirect external references) */ -void sylvan_protect(syBDD* ptr); -void sylvan_unprotect(syBDD* ptr); - -/* Return the number of protected BDD pointers */ -size_t sylvan_count_protected(); +void sylvan_set_granularity(int granularity); +int sylvan_get_granularity(void); -/* Mark BDD for "notify on dead" */ -#define sylvan_notify_ondead(bdd) llmsset_notify_ondead(nodes, bdd&~sylvan_complement) +/* + * Unary, binary and if-then-else operations. + * These operations are all implemented by NOT, AND and XOR. + */ +static inline SyBDD +sylvan_not(SyBDD a) +{ + return a ^ sylvan_complement; +} -/* Unary, binary and if-then-else operations */ -#define sylvan_not(a) (((syBDD)a)^sylvan_complement) -TASK_DECL_4(syBDD, sylvan_ite, syBDD, syBDD, syBDD, BDDVAR); +TASK_DECL_4(SyBDD, sylvan_ite, SyBDD, SyBDD, SyBDD, BDDVAR); #define sylvan_ite(a,b,c) (CALL(sylvan_ite,a,b,c,0)) -TASK_DECL_3(syBDD, sylvan_and, syBDD, syBDD, BDDVAR); +TASK_DECL_3(SyBDD, sylvan_and, SyBDD, SyBDD, BDDVAR); #define sylvan_and(a,b) (CALL(sylvan_and,a,b,0)) -TASK_DECL_3(syBDD, sylvan_xor, syBDD, syBDD, BDDVAR); +TASK_DECL_3(SyBDD, sylvan_xor, SyBDD, SyBDD, BDDVAR); #define sylvan_xor(a,b) (CALL(sylvan_xor,a,b,0)) -/* Do not use nested calls for xor/equiv parameter b! */ #define sylvan_equiv(a,b) sylvan_not(sylvan_xor(a,b)) #define sylvan_or(a,b) sylvan_not(sylvan_and(sylvan_not(a),sylvan_not(b))) #define sylvan_nand(a,b) sylvan_not(sylvan_and(a,b)) @@ -106,18 +78,38 @@ TASK_DECL_3(syBDD, sylvan_xor, syBDD, syBDD, BDDVAR); #define sylvan_diff(a,b) sylvan_and(a,sylvan_not(b)) #define sylvan_less(a,b) sylvan_and(sylvan_not(a),b) -/* Existential and Universal quantifiers */ -TASK_DECL_3(syBDD, sylvan_exists, syBDD, syBDD, BDDVAR); +/* Create a SyBDD representing just <var> or the negation of <var> */ +static inline SyBDD +sylvan_nithvar(uint32_t var) +{ + return sylvan_not(sylvan_ithvar(var)); +} + +/** + * Existential and universal quantification. + */ +TASK_DECL_3(SyBDD, sylvan_exists, SyBDD, SyBDD, BDDVAR); #define sylvan_exists(a, vars) (CALL(sylvan_exists, a, vars, 0)) #define sylvan_forall(a, vars) (sylvan_not(CALL(sylvan_exists, sylvan_not(a), vars, 0))) /** - * Compute \exists v: A(...) \and B(...) - * Parameter vars is the cube (conjunction) of all v variables. + * Projection. (Same as existential quantification, but <vars> contains variables to keep. + */ +TASK_DECL_2(SyBDD, sylvan_project, SyBDD, SyBDD); +#define sylvan_project(a, vars) CALL(sylvan_project, a, vars) + +/** + * Compute \exists <vars>: <a> \and <b> */ -TASK_DECL_4(syBDD, sylvan_and_exists, syBDD, syBDD, BDDSET, BDDVAR); +TASK_DECL_4(SyBDD, sylvan_and_exists, SyBDD, SyBDD, BDDSET, BDDVAR); #define sylvan_and_exists(a,b,vars) CALL(sylvan_and_exists,a,b,vars,0) +/** + * Compute and_exists, but as a projection (only keep given variables) + */ +TASK_DECL_3(SyBDD, sylvan_and_project, SyBDD, SyBDD, BDDSET); +#define sylvan_and_project(a,b,vars) CALL(sylvan_and_project,a,b,vars) + /** * Compute R(s,t) = \exists x: A(s,x) \and B(x,t) * or R(s) = \exists x: A(s,x) \and B(x) @@ -130,7 +122,7 @@ TASK_DECL_4(syBDD, sylvan_and_exists, syBDD, syBDD, BDDSET, BDDVAR); * Use this function to concatenate two relations --> --> * or to take the 'previous' of a set --> S */ -TASK_DECL_4(syBDD, sylvan_relprev, syBDD, syBDD, BDDSET, BDDVAR); +TASK_DECL_4(SyBDD, sylvan_relprev, SyBDD, SyBDD, BDDSET, BDDVAR); #define sylvan_relprev(a,b,vars) CALL(sylvan_relprev,a,b,vars,0) /** @@ -144,139 +136,66 @@ TASK_DECL_4(syBDD, sylvan_relprev, syBDD, syBDD, BDDSET, BDDVAR); * * Use this function to take the 'next' of a set S --> */ -TASK_DECL_4(syBDD, sylvan_relnext, syBDD, syBDD, BDDSET, BDDVAR); +TASK_DECL_4(SyBDD, sylvan_relnext, SyBDD, SyBDD, BDDSET, BDDVAR); #define sylvan_relnext(a,b,vars) CALL(sylvan_relnext,a,b,vars,0) /** - * Computes the transitive closure by traversing the BDD recursively. + * Computes the transitive closure by traversing the SyBDD recursively. * See Y. Matsunaga, P. C. McGeer, R. K. Brayton * On Computing the Transitive Closure of a State Transition Relation * 30th ACM Design Automation Conference, 1993. * - * The input BDD must be a transition relation that only has levels of s,t + * The input SyBDD must be a transition relation that only has levels of s,t * with s,t interleaved with s even and t odd, i.e. * s level 0,2,4 matches with t level 1,3,5 and so forth. */ -TASK_DECL_2(syBDD, sylvan_closure, syBDD, BDDVAR); +TASK_DECL_2(SyBDD, sylvan_closure, SyBDD, BDDVAR); #define sylvan_closure(a) CALL(sylvan_closure,a,0); /** - * Calculate a@b (a constrain b), such that (b -> a@b) = (b -> a) + * Compute f@c (f constrain c), such that f and f@c are the same when c is true + * The SyBDD c is also called the "care function" * Special cases: - * - a@0 = 0 - * - a@1 = f - * - 0@b = 0 - * - 1@b = 1 - * - a@a = 1 - * - a@not(a) = 0 - */ -TASK_DECL_3(syBDD, sylvan_constrain, syBDD, syBDD, BDDVAR); -#define sylvan_constrain(f,c) (CALL(sylvan_constrain, (f), (c), 0)) - -TASK_DECL_3(syBDD, sylvan_restrict, syBDD, syBDD, BDDVAR); -#define sylvan_restrict(f,c) (CALL(sylvan_restrict, (f), (c), 0)) - -TASK_DECL_3(syBDD, sylvan_compose, syBDD, BDDMAP, BDDVAR); -#define sylvan_compose(f,m) (CALL(sylvan_compose, (f), (m), 0)) - -/** - * Calculate the support of a BDD. - * A variable v is in the support of a Boolean function f iff f[v<-0] != f[v<-1] - * It is also the set of all variables in the BDD nodes of the BDD. + * - f@0 = 0 + * - f@1 = f + * - 0@c = 0 + * - 1@c = 1 + * - f@f = 1 + * - f@not(f) = 0 */ -TASK_DECL_1(syBDD, sylvan_support, syBDD); -#define sylvan_support(bdd) (CALL(sylvan_support, bdd)) +TASK_DECL_3(SyBDD, sylvan_constrain, SyBDD, SyBDD, BDDVAR); +#define sylvan_constrain(f,c) (CALL(sylvan_constrain, f, c, 0)) /** - * A set of BDD variables is a cube (conjunction) of variables in their positive form. - * Note 2015-06-10: This used to be a union (disjunction) of variables in their positive form. + * Compute restrict f@c, which uses a heuristic to try and minimize a SyBDD f with respect to a care function c + * Similar to constrain, but avoids introducing variables from c into f. */ -// empty bddset -#define sylvan_set_empty() sylvan_true -#define sylvan_set_isempty(set) (set == sylvan_true) -// add variables to the bddset -#define sylvan_set_add(set, var) sylvan_and(set, sylvan_ithvar(var)) -#define sylvan_set_addall(set, set_to_add) sylvan_and(set, set_to_add) -// remove variables from the bddset -#define sylvan_set_remove(set, var) sylvan_exists(set, var) -#define sylvan_set_removeall(set, set_to_remove) sylvan_exists(set, set_to_remove) -// iterate through all variables -#define sylvan_set_var(set) (sylvan_var(set)) -#define sylvan_set_next(set) (sylvan_high(set)) -int sylvan_set_in(BDDSET set, BDDVAR var); -size_t sylvan_set_count(BDDSET set); -void sylvan_set_toarray(BDDSET set, BDDVAR *arr); -// variables in arr should be ordered -TASK_DECL_2(BDDSET, sylvan_set_fromarray, BDDVAR*, size_t); -#define sylvan_set_fromarray(arr, length) ( CALL(sylvan_set_fromarray, arr, length) ) -void sylvan_test_isset(BDDSET set); +TASK_DECL_3(SyBDD, sylvan_restrict, SyBDD, SyBDD, BDDVAR); +#define sylvan_restrict(f,c) (CALL(sylvan_restrict, f, c, 0)) /** - * BDDMAP maps BDDVAR-->BDD, implemented using BDD nodes. - * Based on disjunction of variables, but with high edges to BDDs instead of True terminals. + * Function composition. + * For each node with variable <key> which has a <key,value> pair in <map>, + * replace the node by the result of sylvan_ite(<value>, <low>, <high>). */ -// empty bddmap -static inline BDDMAP sylvan_map_empty() { return sylvan_false; } -static inline int sylvan_map_isempty(BDDMAP map) { return map == sylvan_false ? 1 : 0; } -// add key-value pairs to the bddmap -BDDMAP sylvan_map_add(BDDMAP map, BDDVAR key, syBDD value); -BDDMAP sylvan_map_addall(BDDMAP map_1, BDDMAP map_2); -// remove key-value pairs from the bddmap -BDDMAP sylvan_map_remove(BDDMAP map, BDDVAR key); -BDDMAP sylvan_map_removeall(BDDMAP map, BDDSET toremove); -// iterate through all pairs -static inline BDDVAR sylvan_map_key(BDDMAP map) { return sylvan_var(map); } -static inline syBDD sylvan_map_value(BDDMAP map) { return sylvan_high(map); } -static inline BDDMAP sylvan_map_next(BDDMAP map) { return sylvan_low(map); } -// is a key in the map -int sylvan_map_in(BDDMAP map, BDDVAR key); -// count number of keys -size_t sylvan_map_count(BDDMAP map); -// convert a BDDSET (cube of variables) to a map, with all variables pointing on the value -BDDMAP sylvan_set_to_map(BDDSET set, syBDD value); - -/** - * Node creation primitive. - * Careful: does not check ordering! - * Preferably only use when debugging! - */ -syBDD sylvan_makenode(BDDVAR level, syBDD low, syBDD high); - -/** - * Write a DOT representation of a BDD - */ -void sylvan_printdot(syBDD bdd); -void sylvan_fprintdot(FILE *out, syBDD bdd); - -/** - * Write a DOT representation of a BDD. - * This variant does not print complement edges. - */ -void sylvan_printdot_nc(syBDD bdd); -void sylvan_fprintdot_nc(FILE *out, syBDD bdd); - -void sylvan_print(syBDD bdd); -void sylvan_fprint(FILE *f, syBDD bdd); - -void sylvan_printsha(syBDD bdd); -void sylvan_fprintsha(FILE *f, syBDD bdd); -void sylvan_getsha(syBDD bdd, char *target); // target must be at least 65 bytes... +TASK_DECL_3(SyBDD, sylvan_compose, SyBDD, BDDMAP, BDDVAR); +#define sylvan_compose(f,m) (CALL(sylvan_compose, (f), (m), 0)) /** * Calculate number of satisfying variable assignments. - * The set of variables must be >= the support of the BDD. + * The set of variables must be >= the support of the SyBDD. */ -TASK_DECL_3(double, sylvan_satcount, syBDD, BDDSET, BDDVAR); +TASK_DECL_3(double, sylvan_satcount, SyBDD, BDDSET, BDDVAR); #define sylvan_satcount(bdd, variables) CALL(sylvan_satcount, bdd, variables, 0) /** - * Create a BDD cube representing the conjunction of variables in their positive or negative + * Create a SyBDD cube representing the conjunction of variables in their positive or negative * form depending on whether the cube[idx] equals 0 (negative), 1 (positive) or 2 (any). * CHANGED 2014/09/19: vars is now a BDDSET (ordered!) */ -syBDD sylvan_cube(BDDSET variables, uint8_t *cube); -TASK_DECL_3(syBDD, sylvan_union_cube, syBDD, BDDSET, uint8_t*); +SyBDD sylvan_cube(BDDSET variables, uint8_t *cube); +TASK_DECL_3(SyBDD, sylvan_union_cube, SyBDD, BDDSET, uint8_t*); #define sylvan_union_cube(bdd, variables, cube) CALL(sylvan_union_cube, bdd, variables, cube) /** @@ -291,55 +210,50 @@ TASK_DECL_3(syBDD, sylvan_union_cube, syBDD, BDDSET, uint8_t*); * * Returns 1 when succesful, or 0 when no assignment is found (i.e. bdd==sylvan_false). */ -int sylvan_sat_one(syBDD bdd, BDDSET variables, uint8_t* str); +int sylvan_sat_one(SyBDD bdd, BDDSET variables, uint8_t* str); /** * Pick one satisfying variable assignment randomly from the given <bdd>. * Functionally equivalent to performing sylvan_cube on the result of sylvan_sat_one. * For the result: sylvan_and(res, bdd) = res. */ -syBDD sylvan_sat_one_bdd(syBDD bdd); +SyBDD sylvan_sat_one_bdd(SyBDD bdd); #define sylvan_pick_cube sylvan_sat_one_bdd -syBDD sylvan_sat_single(syBDD bdd, BDDSET vars); +SyBDD sylvan_sat_single(SyBDD bdd, BDDSET vars); #define sylvan_pick_single_cube sylvan_sat_single /** * Enumerate all satisfying variable assignments from the given <bdd> using variables <vars>. - * Calls <cb> with four parameters: a user-supplied context, the array of BDD variables in <vars>, + * Calls <cb> with four parameters: a user-supplied context, the array of SyBDD variables in <vars>, * the cube (array of values 0 and 1 for each variables in <vars>) and the length of the two arrays. */ LACE_TYPEDEF_CB(void, enum_cb, void*, BDDVAR*, uint8_t*, int); -VOID_TASK_DECL_4(sylvan_enum, syBDD, BDDSET, enum_cb, void*); +VOID_TASK_DECL_4(sylvan_enum, SyBDD, BDDSET, enum_cb, void*); #define sylvan_enum(bdd, vars, cb, context) CALL(sylvan_enum, bdd, vars, cb, context) -VOID_TASK_DECL_4(sylvan_enum_par, syBDD, BDDSET, enum_cb, void*); +VOID_TASK_DECL_4(sylvan_enum_par, SyBDD, BDDSET, enum_cb, void*); #define sylvan_enum_par(bdd, vars, cb, context) CALL(sylvan_enum_par, bdd, vars, cb, context) /** * Enumerate all satisfyable variable assignments of the given <bdd> using variables <vars>. * Calls <cb> with two parameters: a user-supplied context and the cube (array of * values 0 and 1 for each variable in <vars>). - * The BDD that <cb> returns is pair-wise merged (using or) and returned. + * The SyBDD that <cb> returns is pair-wise merged (using or) and returned. */ -LACE_TYPEDEF_CB(syBDD, sylvan_collect_cb, void*, uint8_t*); -TASK_DECL_4(syBDD, sylvan_collect, syBDD, BDDSET, sylvan_collect_cb, void*); +LACE_TYPEDEF_CB(SyBDD, sylvan_collect_cb, void*, uint8_t*); +TASK_DECL_4(SyBDD, sylvan_collect, SyBDD, BDDSET, sylvan_collect_cb, void*); #define sylvan_collect(bdd, vars, cb, context) CALL(sylvan_collect, bdd, vars, cb, context) /** - * Compute the number of distinct paths to sylvan_true in the BDD + * Compute the number of distinct paths to sylvan_true in the SyBDD */ -TASK_DECL_2(double, sylvan_pathcount, syBDD, BDDVAR); +TASK_DECL_2(double, sylvan_pathcount, SyBDD, BDDVAR); #define sylvan_pathcount(bdd) (CALL(sylvan_pathcount, bdd, 0)) -/** - * Compute the number of BDD nodes in the BDD - */ -size_t sylvan_nodecount(syBDD a); - /** * SAVING: - * use sylvan_serialize_add on every BDD you want to store - * use sylvan_serialize_get to retrieve the key of every stored BDD + * use sylvan_serialize_add on every SyBDD you want to store + * use sylvan_serialize_get to retrieve the key of every stored SyBDD * use sylvan_serialize_tofile * * LOADING: @@ -350,73 +264,28 @@ size_t sylvan_nodecount(syBDD a); * use sylvan_serialize_reset to free all allocated structures * use sylvan_serialize_totext to write a textual list of tuples of all BDDs. * format: [(<key>,<level>,<key_low>,<key_high>,<complement_high>),...] - * - * for the old sylvan_print functions, use sylvan_serialize_totext */ -size_t sylvan_serialize_add(syBDD bdd); -size_t sylvan_serialize_get(syBDD bdd); -syBDD sylvan_serialize_get_reversed(size_t value); -void sylvan_serialize_reset(); +size_t sylvan_serialize_add(SyBDD bdd); +size_t sylvan_serialize_get(SyBDD bdd); +SyBDD sylvan_serialize_get_reversed(size_t value); +void sylvan_serialize_reset(void); void sylvan_serialize_totext(FILE *out); void sylvan_serialize_tofile(FILE *out); void sylvan_serialize_fromfile(FILE *in); -/** - * For debugging - * if (part of) the BDD is not 'marked' in the nodes table, assertion fails - * if the BDD is not ordered, returns 0 - * if nicely ordered, returns 1 - */ -TASK_DECL_1(int, sylvan_test_isbdd, syBDD); -#define sylvan_test_isbdd(bdd) CALL(sylvan_test_isbdd, bdd) - -/* Infrastructure for internal markings */ -typedef struct bdd_refs_internal -{ - size_t r_size, r_count; - size_t s_size, s_count; - syBDD *results; - Task **spawns; -} *bdd_refs_internal_t; - -extern DECLARE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - -static inline syBDD -bdd_refs_push(syBDD bdd) -{ - LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - if (bdd_refs_key->r_count >= bdd_refs_key->r_size) { - bdd_refs_key->r_size *= 2; - bdd_refs_key->results = (syBDD*)realloc(bdd_refs_key->results, sizeof(syBDD) * bdd_refs_key->r_size); - } - bdd_refs_key->results[bdd_refs_key->r_count++] = bdd; - return bdd; -} - -static inline void -bdd_refs_pop(int amount) -{ - LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - bdd_refs_key->r_count-=amount; -} - -static inline void -bdd_refs_spawn(Task *t) +static void __attribute__((unused)) +sylvan_fprint(FILE *f, SyBDD bdd) { - LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - if (bdd_refs_key->s_count >= bdd_refs_key->s_size) { - bdd_refs_key->s_size *= 2; - bdd_refs_key->spawns = (Task**)realloc(bdd_refs_key->spawns, sizeof(Task*) * bdd_refs_key->s_size); - } - bdd_refs_key->spawns[bdd_refs_key->s_count++] = t; + sylvan_serialize_reset(); + size_t v = sylvan_serialize_add(bdd); + fprintf(f, "%s%zu,", bdd&sylvan_complement?"!":"", v); + sylvan_serialize_totext(f); } -static inline syBDD -bdd_refs_sync(syBDD result) +static void __attribute__((unused)) +sylvan_print(SyBDD bdd) { - LOCALIZE_THREAD_LOCAL(bdd_refs_key, bdd_refs_internal_t); - bdd_refs_key->s_count--; - return result; + return sylvan_fprint(stdout, bdd); } #ifdef __cplusplus diff --git a/sylvan_cache.c b/sylvan_cache.c old mode 100644 new mode 100755 index 39dadac6bfaa34a26507c13aebc10b63ea52ee7c..8a39d751a80856ec94bc32dc91b60c68ca5c9d8a --- a/sylvan_cache.c +++ b/sylvan_cache.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +15,20 @@ * limitations under the License. */ +#include <sylvan_int.h> + #include <errno.h> // for errno -#include <stdio.h> // for fprintf -#include <stdint.h> // for uint32_t etc -#include <stdlib.h> // for exit #include <string.h> // for strerror #include <sys/mman.h> // for mmap -#include <sylvan_cache.h> - #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif +#ifndef CACHE_MASK +#define CACHE_MASK 1 +#endif + #ifndef compiler_barrier #define compiler_barrier() { asm volatile("" ::: "memory"); } #endif @@ -43,6 +45,18 @@ * Therefore, size 2^N = 36*(2^N) bytes. */ +struct __attribute__((packed)) cache6_entry { + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t res; + uint64_t d; + uint64_t e; + uint64_t f; + uint64_t res2; +}; +typedef struct cache6_entry *cache6_entry_t; + struct __attribute__((packed)) cache_entry { uint64_t a; uint64_t b; @@ -83,6 +97,84 @@ cache_hash(uint64_t a, uint64_t b, uint64_t c) return hash; } +static uint64_t +cache_hash6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f) +{ + const uint64_t prime = 1099511628211; + uint64_t hash = 14695981039346656037LLU; + hash = (hash ^ (a>>32)); + hash = (hash ^ a) * prime; + hash = (hash ^ b) * prime; + hash = (hash ^ c) * prime; + hash = (hash ^ d) * prime; + hash = (hash ^ e) * prime; + hash = (hash ^ f) * prime; + return hash; +} + +int +cache_get6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t *res1, uint64_t *res2) +{ + const uint64_t hash = cache_hash6(a, b, c, d, e, f); +#if CACHE_MASK + volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash & cache_mask)/2; + cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash & cache_mask)/2; +#else + volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash % cache_size)/2; + cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash % cache_size)/2; +#endif + const uint64_t s = *s_bucket; + compiler_barrier(); + // abort if locked or second part of 2-part entry or if different hash + uint64_t x = ((hash>>32) & 0x7fff0000) | 0x04000000; + x = x | (x<<32); + if ((s & 0xffff0000ffff0000) != x) return 0; + // abort if key different + if (bucket->a != a || bucket->b != b || bucket->c != c) return 0; + if (bucket->d != d || bucket->e != e || bucket->f != f) return 0; + *res1 = bucket->res; + if (res2) *res2 = bucket->res2; + compiler_barrier(); + // abort if status field changed after compiler_barrier() + return *s_bucket == s ? 1 : 0; +} + +int +cache_put6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t res1, uint64_t res2) +{ + const uint64_t hash = cache_hash6(a, b, c, d, e, f); +#if CACHE_MASK + volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash & cache_mask)/2; + cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash & cache_mask)/2; +#else + volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash % cache_size)/2; + cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash % cache_size)/2; +#endif + const uint64_t s = *s_bucket; + // abort if locked + if (s & 0x8000000080000000LL) return 0; + // create new + uint64_t new_s = ((hash>>32) & 0x7fff0000) | 0x04000000; + new_s |= (new_s<<32); + new_s |= (((s>>32)+1)&0xffff)<<32; + new_s |= (s+1)&0xffff; + // use cas to claim bucket + if (!cas(s_bucket, s, new_s | 0x8000000080000000LL)) return 0; + // cas succesful: write data + bucket->a = a; + bucket->b = b; + bucket->c = c; + bucket->d = d; + bucket->e = e; + bucket->f = f; + bucket->res = res1; + bucket->res2 = res2; + compiler_barrier(); + // after compiler_barrier(), unlock status field + *s_bucket = new_s; + return 1; +} + int cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res) { @@ -96,10 +188,10 @@ cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res) #endif const uint32_t s = *s_bucket; compiler_barrier(); - // abort if locked - if (s & 0x80000000) return 0; + // abort if locked or if part of a 2-part cache entry + if (s & 0xc0000000) return 0; // abort if different hash - if ((s ^ (hash>>32)) & 0x7fff0000) return 0; + if ((s ^ (hash>>32)) & 0x3fff0000) return 0; // abort if key different if (bucket->a != a || bucket->b != b || bucket->c != c) return 0; *res = bucket->res; @@ -123,7 +215,7 @@ cache_put(uint64_t a, uint64_t b, uint64_t c, uint64_t res) // abort if locked if (s & 0x80000000) return 0; // abort if hash identical -> no: in iscasmc this occasionally causes timeouts?! - const uint32_t hash_mask = (hash>>32) & 0x7fff0000; + const uint32_t hash_mask = (hash>>32) & 0x3fff0000; // if ((s & 0x7fff0000) == hash_mask) return 0; // use cas to claim bucket const uint32_t new_s = ((s+1) & 0x0000ffff) | hash_mask; @@ -205,8 +297,7 @@ size_t cache_getused() { size_t result = 0; - size_t i; - for ( i=0;i<cache_size;i++) { + for (size_t i=0;i<cache_size;i++) { uint32_t s = cache_status[i]; if (s & 0x80000000) fprintf(stderr, "cache_getuser: cache in use during cache_getused()\n"); if (s) result++; diff --git a/sylvan_cache.h b/sylvan_cache.h old mode 100644 new mode 100755 index babc74f65ca5703ab648153c07fee4291a158966..ba0bc83ad4567a3f00fab9b778a96da8bc688e2b --- a/sylvan_cache.h +++ b/sylvan_cache.h @@ -1,18 +1,29 @@ -#include <stdint.h> // for uint32_t etc +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -#include <sylvan_config.h> +/* Do not include this file directly. Instead, include sylvan_int.h */ -#ifndef CACHE_H -#define CACHE_H +#ifndef SYLVAN_CACHE_H +#define SYLVAN_CACHE_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -#ifndef CACHE_MASK -#define CACHE_MASK 1 -#endif - /** * Operation cache * @@ -20,7 +31,7 @@ extern "C" { * You should store these operation identifiers as static globals in your implementation .C/.CPP file. * * For custom operations, just use the following functions: - * - cache_get3/cache_put3 for any operation with 1 BDD and 2 other values (that can be BDDs) + * - cache_get3/cache_put3 for any operation with 1 SyBDD and 2 other values (that can be BDDs) * int success = cache_get3(opid, dd1, value2, value3, &result); * int success = cache_put3(opid, dd1, value2, value3, result); * - cache_get4/cache_put4 for any operation with 4 BDDs @@ -40,10 +51,16 @@ typedef struct cache_entry *cache_entry_t; int cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res); int cache_put(uint64_t a, uint64_t b, uint64_t c, uint64_t res); +/** + * Primitives for cache get/put that use two buckets + */ +int cache_get6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t *res1, uint64_t *res2); +int cache_put6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t res1, uint64_t res2); + /** * Helper function to get next 'operation id' (during initialization of modules) */ -uint64_t cache_next_opid(); +uint64_t cache_next_opid(void); /** * dd must be MTBDD, d2/d3 can be anything @@ -94,17 +111,17 @@ cache_put4(uint64_t opid, uint64_t dd, uint64_t dd2, uint64_t dd3, uint64_t dd4, void cache_create(size_t _cache_size, size_t _max_size); -void cache_free(); +void cache_free(void); -void cache_clear(); +void cache_clear(void); void cache_setsize(size_t size); -size_t cache_getused(); +size_t cache_getused(void); -size_t cache_getsize(); +size_t cache_getsize(void); -size_t cache_getmaxsize(); +size_t cache_getmaxsize(void); #ifdef __cplusplus } diff --git a/sylvan_common.c b/sylvan_common.c old mode 100644 new mode 100755 index aa0374a7b7ea0046e8d0888c12b6b6fbc830641c..7aa7d7ed291e8307e0db886264abe2948b6c2e07 --- a/sylvan_common.c +++ b/sylvan_common.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,166 +15,194 @@ * limitations under the License. */ -#include <sylvan_config.h> - -#include <sylvan.h> -#include <sylvan_common.h> +#include <sylvan_int.h> #ifndef cas #define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) #endif /** - * Static global variables + * Implementation of garbage collection */ -llmsset_t nodes; - /** - * Retrieve nodes + * Whether garbage collection is enabled or not. */ +static int gc_enabled = 1; -llmsset_t -__sylvan_get_internal_data() +/** + * Enable garbage collection (both automatic and manual). + */ +void +sylvan_gc_enable() { - return nodes; + gc_enabled = 1; } /** - * Calculate table usage (in parallel) + * Disable garbage collection (both automatic and manual). */ -VOID_TASK_IMPL_2(sylvan_table_usage, size_t*, filled, size_t*, total) +void +sylvan_gc_disable() { - size_t tot = llmsset_get_size(nodes); - if (filled != NULL) *filled = llmsset_count_marked(nodes); - if (total != NULL) *total = tot; + gc_enabled = 0; } /** - * Implementation of garbage collection + * This variable is used for a cas flag so only one gc runs at one time */ -static int gc_enabled = 1; -static volatile int gc; // variable used in cas switch to ensure only one gc at a time +static volatile int gc; -struct reg_gc_mark_entry +/** + * Structures for the marking mechanisms + */ +typedef struct gc_hook_entry { - struct reg_gc_mark_entry *next; - gc_mark_cb cb; - int order; -}; + struct gc_hook_entry *next; + gc_hook_cb cb; +} * gc_hook_entry_t; -static struct reg_gc_mark_entry *gc_mark_register = NULL; +static gc_hook_entry_t mark_list; +static gc_hook_entry_t pregc_list; +static gc_hook_entry_t postgc_list; +static gc_hook_cb main_hook; void -sylvan_gc_add_mark(int order, gc_mark_cb cb) +sylvan_gc_hook_pregc(gc_hook_cb callback) { - struct reg_gc_mark_entry *e = (struct reg_gc_mark_entry*)malloc(sizeof(struct reg_gc_mark_entry)); - e->cb = cb; - e->order = order; - if (gc_mark_register == NULL || gc_mark_register->order>order) { - e->next = gc_mark_register; - gc_mark_register = e; - return; - } - struct reg_gc_mark_entry *f = gc_mark_register; - for (;;) { - if (f->next == NULL) { - e->next = NULL; - f->next = e; - return; - } - if (f->next->order > order) { - e->next = f->next; - f->next = e; - return; - } - f = f->next; - } + gc_hook_entry_t e = (gc_hook_entry_t)malloc(sizeof(struct gc_hook_entry)); + e->cb = callback; + e->next = pregc_list; + pregc_list = e; } -static gc_hook_cb gc_hook; - void -sylvan_gc_set_hook(gc_hook_cb new_hook) +sylvan_gc_hook_postgc(gc_hook_cb callback) { - gc_hook = new_hook; + gc_hook_entry_t e = (gc_hook_entry_t)malloc(sizeof(struct gc_hook_entry)); + e->cb = callback; + e->next = postgc_list; + postgc_list = e; } void -sylvan_gc_enable() +sylvan_gc_add_mark(gc_hook_cb callback) { - gc_enabled = 1; + gc_hook_entry_t e = (gc_hook_entry_t)malloc(sizeof(struct gc_hook_entry)); + e->cb = callback; + e->next = mark_list; + mark_list = e; } void -sylvan_gc_disable() +sylvan_gc_hook_main(gc_hook_cb callback) { - gc_enabled = 0; + main_hook = callback; } -/* Mark hook for cache */ -VOID_TASK_0(sylvan_gc_mark_cache) +/** + * Clear the operation cache. + */ +VOID_TASK_IMPL_0(sylvan_clear_cache) { - /* We simply clear the cache. - * Alternatively, we could implement for example some strategy - * where part of the cache is cleared and part is marked - */ - cache_clear(); + cache_clear(); } -/* Default hook */ +/** + * Clear the nodes table and mark all referenced nodes. + * + * This does not clear the hash data or rehash the nodes. + * After marking, the "destroy" hooks are called for all unmarked nodes, + * for example to free data of custom MTBDD leaves. + */ +VOID_TASK_IMPL_0(sylvan_clear_and_mark) +{ + llmsset_clear_data(nodes); + + for (gc_hook_entry_t e = mark_list; e != NULL; e = e->next) { + //e->cb(); + WRAP(e->cb); + } + + llmsset_destroy_unmarked(nodes); +} + +/** + * Clear the hash array of the nodes table and rehash all marked buckets. + */ +VOID_TASK_IMPL_0(sylvan_rehash_all) +{ + // clear hash array + llmsset_clear_hashes(nodes); + + // rehash marked nodes + if (llmsset_rehash(nodes) != 0) { + fprintf(stderr, "sylvan_gc_rehash error: not all nodes could be rehashed!\n"); + exit(1); + } +} + +/** + * Logic for resizing the nodes table and operation cache + */ +/** + * Helper routine to compute the next size.... + */ size_t -next_size(size_t n) +next_size(size_t current_size) { #if SYLVAN_SIZE_FIBONACCI size_t f1=1, f2=1; for (;;) { f2 += f1; - if (f2 > n) return f2; + if (f2 > current_size) return f2; f1 += f2; - if (f1 > n) return f1; + if (f1 > current_size) return f1; } #else - return n*2; + return current_size*2; #endif } +/** + * Resizing heuristic that always doubles the tables when running gc (until max). + * The nodes table and operation cache are both resized until their maximum size. + */ VOID_TASK_IMPL_0(sylvan_gc_aggressive_resize) { - /** - * Always resize when gc called - */ - size_t max_size = llmsset_get_max_size(nodes); - size_t size = llmsset_get_size(nodes); - if (size < max_size) { - size_t new_size = next_size(size); - if (new_size > max_size) new_size = max_size; + size_t nodes_size = llmsset_get_size(nodes); + size_t nodes_max = llmsset_get_max_size(nodes); + if (nodes_size < nodes_max) { + size_t new_size = next_size(nodes_size); + if (new_size > nodes_max) new_size = nodes_max; llmsset_set_size(nodes, new_size); - size_t cache_size = cache_getsize(); - size_t cache_max = cache_getmaxsize(); - if (cache_size < cache_max) { - new_size = next_size(cache_size); - if (new_size > cache_max) new_size = cache_max; - cache_setsize(new_size); - } + } + size_t cache_size = cache_getsize(); + size_t cache_max = cache_getmaxsize(); + if (cache_size < cache_max) { + size_t new_size = next_size(cache_size); + if (new_size > cache_max) new_size = cache_max; + cache_setsize(new_size); } } -VOID_TASK_IMPL_0(sylvan_gc_default_hook) +/** + * Resizing heuristic that only resizes when more than 50% is marked. + * The operation cache is only resized if the nodes table is resized. + */ +VOID_TASK_IMPL_0(sylvan_gc_normal_resize) { - /** - * Default behavior: - * if we can resize the nodes set, and if we use more than 50%, then increase size - */ - size_t max_size = llmsset_get_max_size(nodes); - size_t size = llmsset_get_size(nodes); - if (size < max_size) { + size_t nodes_size = llmsset_get_size(nodes); + size_t nodes_max = llmsset_get_max_size(nodes); + if (nodes_size < nodes_max) { size_t marked = llmsset_count_marked(nodes); - if (marked*2 > size) { - size_t new_size = next_size(size); - if (new_size > max_size) new_size = max_size; + if (marked*2 > nodes_size) { + size_t new_size = next_size(nodes_size); + if (new_size > nodes_max) new_size = nodes_max; llmsset_set_size(nodes, new_size); + + // also increase the operation cache size_t cache_size = cache_getsize(); size_t cache_max = cache_getmaxsize(); if (cache_size < cache_max) { @@ -185,82 +214,163 @@ VOID_TASK_IMPL_0(sylvan_gc_default_hook) } } -VOID_TASK_0(sylvan_gc_call_hook) +/** + * Actual implementation of garbage collection + */ +VOID_TASK_0(sylvan_gc_go) { - // call hook function (resizing, reordering, etc) - WRAP(gc_hook); + sylvan_stats_count(SYLVAN_GC_COUNT); + sylvan_timer_start(SYLVAN_GC); + + // call pre gc hooks + for (gc_hook_entry_t e = pregc_list; e != NULL; e = e->next) { + WRAP(e->cb); + } + + /* + * This simply clears the cache. + * Alternatively, we could implement for example some strategy + * where part of the cache is cleared and part is marked + */ + CALL(sylvan_clear_cache); + + CALL(sylvan_clear_and_mark); + + // call hooks for resizing and all that + WRAP(main_hook); + + CALL(sylvan_rehash_all); + + // call post gc hooks + for (gc_hook_entry_t e = postgc_list; e != NULL; e = e->next) { + WRAP(e->cb); + } + + sylvan_timer_stop(SYLVAN_GC); } -VOID_TASK_0(sylvan_gc_rehash) +/** + * Perform garbage collection + */ +VOID_TASK_IMPL_0(sylvan_gc) { - // rehash marked nodes - llmsset_rehash(nodes); + if (gc_enabled) { + if (cas(&gc, 0, 1)) { + NEWFRAME(sylvan_gc_go); + gc = 0; + } else { + /* wait for new frame to appear */ + while (*(Task* volatile*)&(lace_newframe.t) == 0) {} + lace_yield(__lace_worker, __lace_dq_head); + } + } } -VOID_TASK_0(sylvan_gc_destroy_unmarked) +/** + * The unique table + */ + +llmsset_t nodes; + +static size_t table_min = 0, table_max = 0, cache_min = 0, cache_max = 0; + +static int +is_power_of_two(size_t size) { - llmsset_destroy_unmarked(nodes); + return __builtin_popcount(size) == 1 ? 1 : 0; } -VOID_TASK_0(sylvan_gc_go) +void +sylvan_set_sizes(size_t min_tablesize, size_t max_tablesize, size_t min_cachesize, size_t max_cachesize) { - sylvan_stats_count(SYLVAN_GC_COUNT); - sylvan_timer_start(SYLVAN_GC); + /* Some sanity checks */ + if (min_tablesize > max_tablesize) min_tablesize = max_tablesize; + if (min_cachesize > max_cachesize) min_cachesize = max_cachesize; - // clear hash array - llmsset_clear(nodes); + if (!is_power_of_two(min_tablesize) || !is_power_of_two(max_tablesize) || + !is_power_of_two(min_cachesize) || !is_power_of_two(max_cachesize)) { + fprintf(stderr, "sylvan_set_sizes error: parameters not powers of 2!\n"); + exit(1); + } - // call mark functions, hook and rehash - struct reg_gc_mark_entry *e = gc_mark_register; - while (e != NULL) { - WRAP(e->cb); - e = e->next; + if (max_tablesize > 0x0000040000000000) { + fprintf(stderr, "sylvan_set_sizes error: tablesize must be <= 42 bits!\n"); + exit(1); } - sylvan_timer_stop(SYLVAN_GC); + table_min = min_tablesize; + table_max = max_tablesize; + cache_min = min_cachesize; + cache_max = max_cachesize; } -/* Perform garbage collection */ -VOID_TASK_IMPL_0(sylvan_gc) +void +sylvan_set_limits(size_t memorycap, int table_ratio, int initial_ratio) { - if (!gc_enabled) return; - if (cas(&gc, 0, 1)) { - NEWFRAME(sylvan_gc_go); - gc = 0; + if (table_ratio > 10 && table_ratio < 10) { + fprintf(stderr, "sylvan_set_limits: table_ratio unreasonable (between -10 and 10)\n"); + exit(1); + } + + size_t max_t = 1; + size_t max_c = 1; + if (table_ratio > 0) { + max_t <<= table_ratio; } else { - /* wait for new frame to appear */ - while (*(Task* volatile*)&(lace_newframe.t) == 0) {} - lace_yield(__lace_worker, __lace_dq_head); + max_c <<= -table_ratio; } + + size_t cur = max_t * 24 + max_c * 36; + if (cur > memorycap) { + fprintf(stderr, "sylvan_set_limits: memory cap incompatible with requested table ratio\n"); + } + + while (2*cur < memorycap && max_t < 0x0000040000000000) { + max_t *= 2; + max_c *= 2; + cur *= 2; + } + + if (initial_ratio < 0) { + fprintf(stderr, "sylvan_set_limits: initial_ratio unreasonable (may not be negative)\n"); + exit(1); + } + + size_t min_t = max_t, min_c = max_c; + while (initial_ratio > 0 && min_t > 0x1000 && min_c > 0x1000) { + min_t >>= 1; + min_c >>= 1; + initial_ratio--; + } + + table_min = min_t; + table_max = max_t; + cache_min = min_c; + cache_max = max_c; } /** - * Package init and quit functions + * Initializes Sylvan. */ void -sylvan_init_package(size_t tablesize, size_t maxsize, size_t cachesize, size_t max_cachesize) +sylvan_init_package(void) { - if (tablesize > maxsize) tablesize = maxsize; - if (cachesize > max_cachesize) cachesize = max_cachesize; - - if (maxsize > 0x000003ffffffffff) { - fprintf(stderr, "sylvan_init_package error: tablesize must be <= 42 bits!\n"); + if (table_max == 0) { + fprintf(stderr, "sylvan_init_package error: table sizes not set (sylvan_set_sizes or sylvan_set_limits)!"); exit(1); } - nodes = llmsset_create(tablesize, maxsize); - cache_create(cachesize, max_cachesize); + /* Create tables */ + nodes = llmsset_create(table_min, table_max); + cache_create(cache_min, cache_max); + /* Initialize garbage collection */ gc = 0; #if SYLVAN_AGGRESSIVE_RESIZE - gc_hook = TASK(sylvan_gc_aggressive_resize); + main_hook = TASK(sylvan_gc_aggressive_resize); #else - gc_hook = TASK(sylvan_gc_default_hook); + main_hook = TASK(sylvan_gc_normal_resize); #endif - sylvan_gc_add_mark(10, TASK(sylvan_gc_mark_cache)); - sylvan_gc_add_mark(19, TASK(sylvan_gc_destroy_unmarked)); - sylvan_gc_add_mark(20, TASK(sylvan_gc_call_hook)); - sylvan_gc_add_mark(30, TASK(sylvan_gc_rehash)); LACE_ME; sylvan_stats_init(); @@ -293,12 +403,63 @@ sylvan_quit() free(e); } - while (gc_mark_register != NULL) { - struct reg_gc_mark_entry *e = gc_mark_register; - gc_mark_register = e->next; + while (pregc_list != NULL) { + gc_hook_entry_t e = pregc_list; + pregc_list = e->next; + free(e); + } + + while (postgc_list != NULL) { + gc_hook_entry_t e = postgc_list; + postgc_list = e->next; + free(e); + } + + while (mark_list != NULL) { + gc_hook_entry_t e = mark_list; + mark_list = e->next; free(e); } cache_free(); llmsset_free(nodes); } + +/** + * Calculate table usage (in parallel) + */ +VOID_TASK_IMPL_2(sylvan_table_usage, size_t*, filled, size_t*, total) +{ + size_t tot = llmsset_get_size(nodes); + if (filled != NULL) *filled = llmsset_count_marked(nodes); + if (total != NULL) *total = tot; + sylvan_gc_seq(); +} + +/*******************************************************************/ +/***************SEQUENTIAL*****************************************/ +/******************************************************************/ +void mark_nodes() { +//for (gc_hook_entry_t e = mark_list; e != NULL; e = e->next) { + //e->cb(); + ldd_gc_mark_protected(); + // } + +} + + +/*** + * Sequential VERSION of rehash_all + */ + void sylvan_rehash_all_seq() +{ + // clear hash array + llmsset_clear_hashes_seq(nodes); + + // rehash marked nodes + if (llmsset_rehash_seq(nodes) != 0) { + fprintf(stderr, "sylvan_gc_rehash error: not all nodes could be rehashed!\n"); + exit(1); + } +} + diff --git a/sylvan_common.h b/sylvan_common.h old mode 100644 new mode 100755 index 61f34cc5b50bb3df027722a657aa14b46d5aefd0..432adf200f9eb01f34b8a0585b07c3a9b506cb4c --- a/sylvan_common.h +++ b/sylvan_common.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +15,8 @@ * limitations under the License. */ +/* Do not include this file directly. Instead, include sylvan.h */ + #ifndef SYLVAN_COMMON_H #define SYLVAN_COMMON_H @@ -21,62 +24,198 @@ extern "C" { #endif /* __cplusplus */ -/* Garbage collection test task - t */ -#define sylvan_gc_test() YIELD_NEWFRAME() +/** + * Initialize the Sylvan parallel decision diagrams package. + * + * First, Sylvan must know how big the nodes table and cache may be. + * Either use sylvan_set_sizes to explicitly set the table sizes, or use sylvan_set_limits + * to let Sylvan compute the sizes for you. + * + * Then, call sylvan_init_package. This allocates the tables and other support structures. + * Sylvan allocates virtual memory to accomodate the maximum sizes of both tables. + * Initially, Sylvan only uses the minimum sizes. + * During garbage collection, table sizes may be doubled until the maximum size is reached. + * + * Then, call initialization functions for the MTBDD/LDD modules like sylvan_init_mtbdd + * and sylvan_init_ldd. + * + * Memory usage: + * Every node requires 24 bytes memory. (16 bytes data + 8 bytes table overhead) + * Every operation cache entry requires 36 bytes memory. (32 bytes data + 4 bytes table overhead) + */ +void sylvan_init_package(void); + +/** + * Explicitly set the sizes of the nodes table and the operation cache. + * The sizes are in bytes, but they must be powers of two. + * The minimum size is the size initially used. + * The maximum size is the size allocated in virtual memory. + */ +void sylvan_set_sizes(size_t min_tablesize, size_t max_tablesize, size_t min_cachesize, size_t max_cachesize); -// BDD operations -#define CACHE_BDD_ITE (0LL<<40) -#define CACHE_BDD_AND (1LL<<40) -#define CACHE_BDD_XOR (2LL<<40) -#define CACHE_BDD_EXISTS (3LL<<40) -#define CACHE_BDD_AND_EXISTS (4LL<<40) -#define CACHE_BDD_RELNEXT (5LL<<40) -#define CACHE_BDD_RELPREV (6LL<<40) -#define CACHE_BDD_SATCOUNT (7LL<<40) -#define CACHE_BDD_COMPOSE (8LL<<40) -#define CACHE_BDD_RESTRICT (9LL<<40) -#define CACHE_BDD_CONSTRAIN (10LL<<40) -#define CACHE_BDD_CLOSURE (11LL<<40) -#define CACHE_BDD_ISBDD (12LL<<40) -#define CACHE_BDD_SUPPORT (13LL<<40) -#define CACHE_BDD_PATHCOUNT (14LL<<40) - -// MDD operations -#define CACHE_MDD_RELPROD (20LL<<40) -#define CACHE_MDD_MINUS (21LL<<40) -#define CACHE_MDD_UNION (22LL<<40) -#define CACHE_MDD_INTERSECT (23LL<<40) -#define CACHE_MDD_PROJECT (24LL<<40) -#define CACHE_MDD_JOIN (25LL<<40) -#define CACHE_MDD_MATCH (26LL<<40) -#define CACHE_MDD_RELPREV (27LL<<40) -#define CACHE_MDD_SATCOUNT (28LL<<40) -#define CACHE_MDD_SATCOUNTL1 (29LL<<40) -#define CACHE_MDD_SATCOUNTL2 (30LL<<40) - -// MTBDD operations -#define CACHE_MTBDD_APPLY (40LL<<40) -#define CACHE_MTBDD_UAPPLY (41LL<<40) -#define CACHE_MTBDD_ABSTRACT (42LL<<40) -#define CACHE_MTBDD_ITE (43LL<<40) -#define CACHE_MTBDD_AND_EXISTS (44LL<<40) -#define CACHE_MTBDD_SUPPORT (45LL<<40) -#define CACHE_MTBDD_COMPOSE (46LL<<40) -#define CACHE_MTBDD_EQUAL_NORM (47LL<<40) -#define CACHE_MTBDD_EQUAL_NORM_REL (48LL<<40) -#define CACHE_MTBDD_MINIMUM (49LL<<40) -#define CACHE_MTBDD_MAXIMUM (50LL<<40) -#define CACHE_MTBDD_LEQ (51LL<<40) -#define CACHE_MTBDD_LESS (52LL<<40) -#define CACHE_MTBDD_GEQ (53LL<<40) -#define CACHE_MTBDD_GREATER (54LL<<40) - -/** - * Registration of quit functions - */ -typedef void (*quit_cb)(); +/** + * Implicitly compute and set the sizes of the nodes table and the operation cache. + * + * This function computes max_tablesize and max_cachesize to fit the memory cap. + * The memory cap is in bytes. + * + * The parameter table_ratio controls the ratio between the nodes table and the cache. + * For the value 0, both tables are of the same size. + * For values 1, 2, 3 ... the nodes table will be 2x, 4x, 8x ... as big as the cache + * For values -1, -2, -3 ... the cache will be 2x, 4x, 8x ... as big as the nodes table + * + * The parameter initial_ratio controls how much smaller the initial table sizes are. + * For values of 1, 2, 3, 4 the tables will initially be 2, 4, 8, 16 times smaller. + */ +void sylvan_set_limits(size_t memory_cap, int table_ratio, int initial_ratio); + +/** + * Frees all Sylvan data (also calls the quit() functions of SyBDD/LDD parts) + */ +void sylvan_quit(void); + +/** + * Registers a hook callback called during sylvan_quit() + */ +typedef void (*quit_cb)(void); void sylvan_register_quit(quit_cb cb); +/** + * Return number of occupied buckets in nodes table and total number of buckets. + */ +VOID_TASK_DECL_2(sylvan_table_usage, size_t*, size_t*); +#define sylvan_table_usage(filled, total) (CALL(sylvan_table_usage, filled, total)) + +/** + * GARBAGE COLLECTION + * + * Garbage collection is performed in a new Lace frame, interrupting all ongoing work + * until garbage collection is completed. + * + * By default, garbage collection is triggered when no new nodes can be added to the nodes table. + * This is detected when there are no more available buckets in the bounded probe sequence. + * Garbage collection can also be triggered manually with sylvan_gc() + * + * Garbage collection procedure: + * 1) All installed pre_gc hooks are called. + * See sylvan_gc_hook_pre to add hooks. + * 2) The operation cache is cleared. + * 3) The nodes table (data part) is cleared. + * 4) All nodes are marked (to be rehashed) using the various marking callbacks. + * See sylvan_gc_add_mark to add marking callbacks. + * Afterwards, the ondead hook is called for all now-dead nodes with the custom flag set. + * 5) The main gc hook is called. The function of this hook is to perform resizing. + * The default implementation doubles the nodes table and operation cache sizes. + * See sylvan_gc_hook_main to set the hook. + * 5) The nodes table (hash part) is cleared. + * 6) All marked nodes are rehashed. + * 7) All installed post_gc hooks are called. + * See sylvan_gc_hook_post to add hooks. + * + * For parts of the garbage collection process, specific methods exist. + * - sylvan_clear_cache() clears the operation cache (step 2) + * - sylvan_clear_and_mark() performs steps 3 and 4. + * - sylvan_rehash_all() performs steps 5 and 6. + */ + +/** + * Trigger garbage collection manually. + */ + +/** + * Trigger garbage collection manually. + */ +VOID_TASK_DECL_0(sylvan_gc); +#define sylvan_gc() (CALL(sylvan_gc)) + +/** + * Enable or disable garbage collection. + * + * This affects both automatic and manual garbage collection, i.e., + * calling sylvan_gc() while garbage collection is disabled does not have any effect. + * If no new nodes can be added, Sylvan will write an error and abort. + */ +void sylvan_gc_enable(void); +void sylvan_gc_disable(void); + +/** + * Test if garbage collection must happen now. + * This is just a call to the Lace framework to see if NEWFRAME has been used. + * Before calling this, make sure all used BDDs are referenced. + */ +#define sylvan_gc_test() YIELD_NEWFRAME() + +/** + * Clear the operation cache. + */ +VOID_TASK_DECL_0(sylvan_clear_cache); +#define sylvan_clear_cache() CALL(sylvan_clear_cache) + +/** + * Clear the nodes table (data part) and mark all nodes with the marking mechanisms. + */ +VOID_TASK_DECL_0(sylvan_clear_and_mark); +#define sylvan_clear_and_mark() CALL(sylvan_clear_and_mark) + +/** + * Clear the nodes table (hash part) and rehash all marked nodes. + */ +VOID_TASK_DECL_0(sylvan_rehash_all); +#define sylvan_rehash_all() CALL(sylvan_rehash_all) + +/** + * Callback type + */ +LACE_TYPEDEF_CB(void, gc_hook_cb); + +/** + * Add a hook that is called before garbage collection begins. + */ +void sylvan_gc_hook_pregc(gc_hook_cb callback); + +/** + * Add a hook that is called after garbage collection is finished. + */ +void sylvan_gc_hook_postgc(gc_hook_cb callback); + +/** + * Replace the hook called between node marking and rehashing. + * Typically, the hook resizes the hash table and operation cache according to some heuristic. + */ +void sylvan_gc_hook_main(gc_hook_cb callback); + +/** + * Add a marking mechanism. + * + * The mark_cb callback is called during garbage collection and should call the + * appropriate recursive marking functions for the decision diagram nodes, for example + * mtbdd_gc_mark_rec() for MTBDDs or lddmc_gc_mark_rec() for LDDs. + * + * The sylvan_count_refs() function uses the count_cb callbacks to compute the number + * of references. + */ +void sylvan_gc_add_mark(gc_hook_cb mark_cb); + +/** + * One of the hooks for resizing behavior. + * Default if SYLVAN_AGGRESSIVE_RESIZE is set. + * Always double size on gc() until maximum reached. + * Use sylvan_gc_hook_main() to set this heuristic. + */ +VOID_TASK_DECL_0(sylvan_gc_aggressive_resize); + +/** + * One of the hooks for resizing behavior. + * Default if SYLVAN_AGGRESSIVE_RESIZE is not set. + * Double size on gc() whenever >50% is used. + * Use sylvan_gc_hook_main() to set this heuristic. + */ +VOID_TASK_DECL_0(sylvan_gc_normal_resize); + + + +void mark_nodes(); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/sylvan_config.h b/sylvan_config.h old mode 100644 new mode 100755 diff --git a/sylvan_gmp.c b/sylvan_gmp.c old mode 100644 new mode 100755 index 2613d50a44f1b266fb372c26e863553efab6f2a3..7d6da54634483e3e9e7e7b2fce5740f937a96222 --- a/sylvan_gmp.c +++ b/sylvan_gmp.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +15,13 @@ * limitations under the License. */ -#include <sylvan_config.h> +#include <sylvan_int.h> +#include <sylvan_gmp.h> -#include <assert.h> -#include <inttypes.h> #include <math.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> -#include <sylvan.h> -#include <sylvan_common.h> -#include <sylvan_mtbdd_int.h> -#include <sylvan_gmp.h> -#include <gmp.h> - +static uint32_t gmp_type; /** * helper function for hash @@ -57,8 +49,7 @@ gmp_hash(const uint64_t v, const uint64_t seed) // hash "numerator" limbs limbs = x[0]._mp_num._mp_d; - int i; - for ( i=0; i<x[0]._mp_num._mp_size; i++) { + for (int i=0; i<x[0]._mp_num._mp_size; i++) { hash = hash ^ limbs[i]; hash = rotl64(hash, 47); hash = hash * prime; @@ -66,8 +57,7 @@ gmp_hash(const uint64_t v, const uint64_t seed) // hash "denominator" limbs limbs = x[0]._mp_den._mp_d; - - for ( i=0; i<x[0]._mp_den._mp_size; i++) { + for (int i=0; i<x[0]._mp_den._mp_size; i++) { hash = hash ^ limbs[i]; hash = rotl64(hash, 31); hash = hash * prime; @@ -108,8 +98,50 @@ gmp_destroy(uint64_t val) free((void*)val); } -static uint32_t gmp_type; -static uint64_t CACHE_GMP_AND_EXISTS; +static char* +gmp_to_str(int comp, uint64_t val, char *buf, size_t buflen) +{ + mpq_ptr op = (mpq_ptr)val; + size_t minsize = mpz_sizeinbase(mpq_numref(op), 10) + mpz_sizeinbase (mpq_denref(op), 10) + 3; + if (buflen >= minsize) return mpq_get_str(buf, 10, op); + else return mpq_get_str(NULL, 10, op); + (void)comp; +} + +static int +gmp_write_binary(FILE* out, uint64_t val) +{ + mpq_ptr op = (mpq_ptr)val; + + mpz_t i; + mpz_init(i); + mpq_get_num(i, op); + if (mpz_out_raw(out, i) == 0) return -1; + mpq_get_den(i, op); + if (mpz_out_raw(out, i) == 0) return -1; + mpz_clear(i); + + return 0; +} + +static int +gmp_read_binary(FILE* in, uint64_t *val) +{ + mpq_ptr mres = (mpq_ptr)malloc(sizeof(__mpq_struct)); + mpq_init(mres); + + mpz_t i; + mpz_init(i); + if (mpz_inp_raw(i, in) == 0) return -1; + mpq_set_num(mres, i); + if (mpz_inp_raw(i, in) == 0) return -1; + mpq_set_den(mres, i); + mpz_clear(i); + + *(mpq_ptr*)val = mres; + + return 0; +} /** * Initialize gmp custom leaves @@ -117,9 +149,15 @@ static uint64_t CACHE_GMP_AND_EXISTS; void gmp_init() { - /* Register custom leaf 3 */ - gmp_type = mtbdd_register_custom_leaf(gmp_hash, gmp_equals, gmp_create, gmp_destroy); - CACHE_GMP_AND_EXISTS = cache_next_opid(); + /* Register custom leaf */ + gmp_type = sylvan_mt_create_type(); + sylvan_mt_set_hash(gmp_type, gmp_hash); + sylvan_mt_set_equals(gmp_type, gmp_equals); + sylvan_mt_set_create(gmp_type, gmp_create); + sylvan_mt_set_destroy(gmp_type, gmp_destroy); + sylvan_mt_set_to_str(gmp_type, gmp_to_str); + sylvan_mt_set_write_binary(gmp_type, gmp_write_binary); + sylvan_mt_set_read_binary(gmp_type, gmp_read_binary); } /** @@ -146,6 +184,8 @@ TASK_IMPL_2(MTBDD, gmp_op_plus, MTBDD*, pa, MTBDD*, pb) /* If both leaves, compute plus */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); @@ -180,6 +220,8 @@ TASK_IMPL_2(MTBDD, gmp_op_minus, MTBDD*, pa, MTBDD*, pb) /* If both leaves, compute plus */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); @@ -196,7 +238,7 @@ TASK_IMPL_2(MTBDD, gmp_op_minus, MTBDD*, pa, MTBDD*, pb) /** * Operation "times" for two mpq MTBDDs. - * One of the parameters can be a BDD, then it is interpreted as a filter. + * One of the parameters can be a SyBDD, then it is interpreted as a filter. * For partial functions, domain is intersection */ TASK_IMPL_2(MTBDD, gmp_op_times, MTBDD*, pa, MTBDD*, pb) @@ -212,6 +254,8 @@ TASK_IMPL_2(MTBDD, gmp_op_times, MTBDD*, pa, MTBDD*, pb) /* Handle multiplication of leaves */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); @@ -246,6 +290,8 @@ TASK_IMPL_2(MTBDD, gmp_op_divide, MTBDD*, pa, MTBDD*, pb) /* Handle division of leaves */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); @@ -277,6 +323,8 @@ TASK_IMPL_2(MTBDD, gmp_op_min, MTBDD*, pa, MTBDD*, pb) /* Compute result for leaves */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); int cmp = mpq_cmp(ma, mb); @@ -308,6 +356,8 @@ TASK_IMPL_2(MTBDD, gmp_op_max, MTBDD*, pa, MTBDD*, pb) /* Compute result for leaves */ if (mtbdd_isleaf(a) && mtbdd_isleaf(b)) { + assert(mtbdd_gettype(a) == gmp_type && mtbdd_gettype(b) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); int cmp = mpq_cmp(ma, mb); @@ -333,6 +383,8 @@ TASK_IMPL_2(MTBDD, gmp_op_neg, MTBDD, dd, size_t, p) /* Compute result for leaf */ if (mtbdd_isleaf(dd)) { + assert(mtbdd_gettype(dd) == gmp_type); + mpq_ptr m = (mpq_ptr)mtbdd_getvalue(dd); mpq_t mres; @@ -357,6 +409,8 @@ TASK_IMPL_2(MTBDD, gmp_op_abs, MTBDD, dd, size_t, p) /* Compute result for leaf */ if (mtbdd_isleaf(dd)) { + assert(mtbdd_gettype(dd) == gmp_type); + mpq_ptr m = (mpq_ptr)mtbdd_getvalue(dd); mpq_t mres; @@ -383,8 +437,7 @@ TASK_IMPL_3(MTBDD, gmp_abstract_op_plus, MTBDD, a, MTBDD, b, int, k) return mtbdd_apply(a, b, TASK(gmp_op_plus)); } else { MTBDD res = a; - int i; - for ( i=0; i<k; i++) { + for (int i=0; i<k; i++) { mtbdd_refs_push(res); res = mtbdd_apply(res, res, TASK(gmp_op_plus)); mtbdd_refs_pop(1); @@ -399,8 +452,7 @@ TASK_IMPL_3(MTBDD, gmp_abstract_op_times, MTBDD, a, MTBDD, b, int, k) return mtbdd_apply(a, b, TASK(gmp_op_times)); } else { MTBDD res = a; - int i; - for ( i=0; i<k; i++) { + for (int i=0; i<k; i++) { mtbdd_refs_push(res); res = mtbdd_apply(res, res, TASK(gmp_op_times)); mtbdd_refs_pop(1); @@ -439,6 +491,8 @@ TASK_2(MTBDD, gmp_op_threshold_d, MTBDD, a, size_t, svalue) /* Compute result */ if (mtbdd_isleaf(a)) { + assert(mtbdd_gettype(a) == gmp_type); + double value = *(double*)&svalue; mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); return mpq_get_d(ma) >= value ? mtbdd_true : mtbdd_false; @@ -457,6 +511,8 @@ TASK_2(MTBDD, gmp_op_strict_threshold_d, MTBDD, a, size_t, svalue) /* Compute result */ if (mtbdd_isleaf(a)) { + assert(mtbdd_gettype(a) == gmp_type); + double value = *(double*)&svalue; mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); return mpq_get_d(ma) > value ? mtbdd_true : mtbdd_false; @@ -488,6 +544,8 @@ TASK_IMPL_2(MTBDD, gmp_op_threshold, MTBDD*, pa, MTBDD*, pb) /* Handle comparison of leaves */ if (mtbdd_isleaf(a)) { + assert(mtbdd_gettype(a) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); int cmp = mpq_cmp(ma, mb); @@ -510,6 +568,8 @@ TASK_IMPL_2(MTBDD, gmp_op_strict_threshold, MTBDD*, pa, MTBDD*, pb) /* Handle comparison of leaves */ if (mtbdd_isleaf(a)) { + assert(mtbdd_gettype(a) == gmp_type); + mpq_ptr ma = (mpq_ptr)mtbdd_getvalue(a); mpq_ptr mb = (mpq_ptr)mtbdd_getvalue(b); int cmp = mpq_cmp(ma, mb); @@ -523,7 +583,7 @@ TASK_IMPL_2(MTBDD, gmp_op_strict_threshold, MTBDD*, pa, MTBDD*, pb) * Multiply <a> and <b>, and abstract variables <vars> using summation. * This is similar to the "and_exists" operation in BDDs. */ -TASK_IMPL_3(MTBDD, gmp_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) +TASK_IMPL_3(MTBDD, gmp_and_abstract_plus, MTBDD, a, MTBDD, b, MTBDD, v) { /* Check terminal cases */ @@ -545,26 +605,32 @@ TASK_IMPL_3(MTBDD, gmp_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS); + /* Check cache. Note that we do this now, since the times operator might swap a and b (commutative) */ - if (cache_get3(CACHE_GMP_AND_EXISTS, a, b, v, &result)) return result; + if (cache_get3(CACHE_MTBDD_AND_ABSTRACT_PLUS, a, b, v, &result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS_CACHED); + return result; + } /* Now, v is not a constant, and either a or b is not a constant */ /* Get top variable */ int la = mtbdd_isleaf(a); int lb = mtbdd_isleaf(b); - mtbddnode_t na = la ? 0 : GETNODE(a); - mtbddnode_t nb = lb ? 0 : GETNODE(b); + mtbddnode_t na = la ? 0 : MTBDD_GETNODE(a); + mtbddnode_t nb = lb ? 0 : MTBDD_GETNODE(b); uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); uint32_t vb = lb ? 0xffffffff : mtbddnode_getvariable(nb); uint32_t var = va < vb ? va : vb; - mtbddnode_t nv = GETNODE(v); + mtbddnode_t nv = MTBDD_GETNODE(v); uint32_t vv = mtbddnode_getvariable(nv); if (vv < var) { /* Recursive, then abstract result */ - result = CALL(gmp_and_exists, a, b, node_gethigh(v, nv)); + result = CALL(gmp_and_abstract_plus, a, b, node_gethigh(v, nv)); mtbdd_refs_push(result); result = mtbdd_apply(result, result, TASK(gmp_op_plus)); mtbdd_refs_pop(1); @@ -578,22 +644,112 @@ TASK_IMPL_3(MTBDD, gmp_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) if (vv == var) { /* Recursive, then abstract result */ - mtbdd_refs_spawn(SPAWN(gmp_and_exists, ahigh, bhigh, node_gethigh(v, nv))); - MTBDD low = mtbdd_refs_push(CALL(gmp_and_exists, alow, blow, node_gethigh(v, nv))); - MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(gmp_and_exists))); + mtbdd_refs_spawn(SPAWN(gmp_and_abstract_plus, ahigh, bhigh, node_gethigh(v, nv))); + MTBDD low = mtbdd_refs_push(CALL(gmp_and_abstract_plus, alow, blow, node_gethigh(v, nv))); + MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(gmp_and_abstract_plus))); result = CALL(mtbdd_apply, low, high, TASK(gmp_op_plus)); mtbdd_refs_pop(2); } else /* vv > v */ { /* Recursive, then create node */ - mtbdd_refs_spawn(SPAWN(gmp_and_exists, ahigh, bhigh, v)); - MTBDD low = mtbdd_refs_push(CALL(gmp_and_exists, alow, blow, v)); - MTBDD high = mtbdd_refs_sync(SYNC(gmp_and_exists)); + mtbdd_refs_spawn(SPAWN(gmp_and_abstract_plus, ahigh, bhigh, v)); + MTBDD low = mtbdd_refs_push(CALL(gmp_and_abstract_plus, alow, blow, v)); + MTBDD high = mtbdd_refs_sync(SYNC(gmp_and_abstract_plus)); mtbdd_refs_pop(1); result = mtbdd_makenode(var, low, high); } } /* Store in cache */ - cache_put3(CACHE_GMP_AND_EXISTS, a, b, v, result); + if (cache_put3(CACHE_MTBDD_AND_ABSTRACT_PLUS, a, b, v, result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS_CACHEDPUT); + } + + return result; +} + +/** + * Multiply <a> and <b>, and abstract variables <vars> by taking the maximum. + */ +TASK_IMPL_3(MTBDD, gmp_and_abstract_max, MTBDD, a, MTBDD, b, MTBDD, v) +{ + /* Check terminal cases */ + + /* If v == true, then <vars> is an empty set */ + if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(gmp_op_times)); + + /* Try the times operator on a and b */ + MTBDD result = CALL(gmp_op_times, &a, &b); + if (result != mtbdd_invalid) { + /* Times operator successful, store reference (for garbage collection) */ + mtbdd_refs_push(result); + /* ... and perform abstraction */ + result = mtbdd_abstract(result, v, TASK(gmp_abstract_op_max)); + mtbdd_refs_pop(1); + /* Note that the operation cache is used in mtbdd_abstract */ + return result; + } + + /* Now, v is not a constant, and either a or b is not a constant */ + + /* Get top variable */ + int la = mtbdd_isleaf(a); + int lb = mtbdd_isleaf(b); + mtbddnode_t na = la ? 0 : MTBDD_GETNODE(a); + mtbddnode_t nb = lb ? 0 : MTBDD_GETNODE(b); + uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); + uint32_t vb = lb ? 0xffffffff : mtbddnode_getvariable(nb); + uint32_t var = va < vb ? va : vb; + + mtbddnode_t nv = MTBDD_GETNODE(v); + uint32_t vv = mtbddnode_getvariable(nv); + + while (vv < var) { + /* we can skip variables, because max(r,r) = r */ + v = node_high(v, nv); + if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(gmp_op_times)); + nv = MTBDD_GETNODE(v); + vv = mtbddnode_getvariable(nv); + } + + /* Maybe perform garbage collection */ + sylvan_gc_test(); + + /* Count operation */ + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX); + + /* Check cache. Note that we do this now, since the times operator might swap a and b (commutative) */ + if (cache_get3(CACHE_MTBDD_AND_ABSTRACT_MAX, a, b, v, &result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX_CACHED); + return result; + } + + /* Get cofactors */ + MTBDD alow, ahigh, blow, bhigh; + alow = (!la && va == var) ? node_getlow(a, na) : a; + ahigh = (!la && va == var) ? node_gethigh(a, na) : a; + blow = (!lb && vb == var) ? node_getlow(b, nb) : b; + bhigh = (!lb && vb == var) ? node_gethigh(b, nb) : b; + + if (vv == var) { + /* Recursive, then abstract result */ + mtbdd_refs_spawn(SPAWN(gmp_and_abstract_max, ahigh, bhigh, node_gethigh(v, nv))); + MTBDD low = mtbdd_refs_push(CALL(gmp_and_abstract_max, alow, blow, node_gethigh(v, nv))); + MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(gmp_and_abstract_max))); + result = CALL(mtbdd_apply, low, high, TASK(gmp_op_max)); + mtbdd_refs_pop(2); + } else /* vv > v */ { + /* Recursive, then create node */ + mtbdd_refs_spawn(SPAWN(gmp_and_abstract_max, ahigh, bhigh, v)); + MTBDD low = mtbdd_refs_push(CALL(gmp_and_abstract_max, alow, blow, v)); + MTBDD high = mtbdd_refs_sync(SYNC(gmp_and_abstract_max)); + mtbdd_refs_pop(1); + result = mtbdd_makenode(var, low, high); + } + + /* Store in cache */ + if (cache_put3(CACHE_MTBDD_AND_ABSTRACT_MAX, a, b, v, result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX_CACHEDPUT); + } + return result; } diff --git a/sylvan_gmp.h b/sylvan_gmp.h old mode 100644 new mode 100755 index fbf6bc2ad67d7a2375f5c2fe35b6b5921fe92762..8bf3b909a6304217a4c726f4177f4e01d5d325a0 --- a/sylvan_gmp.h +++ b/sylvan_gmp.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,20 +19,21 @@ * This is an implementation of GMP mpq custom leaves of MTBDDs */ -#ifndef SYLVAN_GMP_H -#define SYLVAN_GMP_H - #include <sylvan.h> #include <gmp.h> +#ifndef SYLVAN_GMP_H +#define SYLVAN_GMP_H + #ifdef __cplusplus +namespace sylvan { extern "C" { #endif /* __cplusplus */ /** * Initialize GMP custom leaves */ -void gmp_init(); +void gmp_init(void); /** * Create MPQ leaf @@ -146,8 +148,15 @@ TASK_DECL_2(MTBDD, gmp_op_abs, MTBDD, size_t); * Multiply <a> and <b>, and abstract variables <vars> using summation. * This is similar to the "and_exists" operation in BDDs. */ -TASK_DECL_3(MTBDD, gmp_and_exists, MTBDD, MTBDD, MTBDD); -#define gmp_and_exists(a, b, vars) CALL(gmp_and_exists, a, b, vars) +TASK_DECL_3(MTBDD, gmp_and_abstract_plus, MTBDD, MTBDD, MTBDD); +#define gmp_and_abstract_plus(a, b, vars) CALL(gmp_and_abstract_plus, a, b, vars) +#define gmp_and_exists gmp_and_abstract_plus + +/** + * Multiply <a> and <b>, and abstract variables <vars> by taking the maximum. + */ +TASK_DECL_3(MTBDD, gmp_and_abstract_max, MTBDD, MTBDD, MTBDD); +#define gmp_and_abstract_max(a, b, vars) CALL(gmp_and_abstract_max, a, b, vars) /** * Convert to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; @@ -177,6 +186,7 @@ TASK_DECL_2(MTBDD, gmp_strict_threshold_d, MTBDD, double); #ifdef __cplusplus } +} #endif /* __cplusplus */ #endif diff --git a/sylvan_int.h b/sylvan_int.h new file mode 100755 index 0000000000000000000000000000000000000000..61ea5707c1b5d3110a26a89a30ca558b447ee6ed --- /dev/null +++ b/sylvan_int.h @@ -0,0 +1,114 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Sylvan: parallel MTBDD/ListDD package. + * Include this file for access to internals. + */ + +#include <sylvan.h> + +#ifdef __cplusplus +namespace sylvan { +#endif + +/** + * Sylvan internal header files inside the namespace + */ + +#include <sylvan_cache.h> +#include <sylvan_table.h> + +#ifndef SYLVAN_INT_H +#define SYLVAN_INT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Nodes table. + */ +extern llmsset_t nodes; + +/** + * Macros for all operation identifiers for the operation cache + */ + +// SyBDD operations +static const uint64_t CACHE_BDD_ITE = (0LL<<40); +static const uint64_t CACHE_BDD_AND = (1LL<<40); +static const uint64_t CACHE_BDD_XOR = (2LL<<40); +static const uint64_t CACHE_BDD_EXISTS = (3LL<<40); +static const uint64_t CACHE_BDD_PROJECT = (4LL<<40); +static const uint64_t CACHE_BDD_AND_EXISTS = (5LL<<40); +static const uint64_t CACHE_BDD_AND_PROJECT = (6LL<<40); +static const uint64_t CACHE_BDD_RELNEXT = (7LL<<40); +static const uint64_t CACHE_BDD_RELPREV = (8LL<<40); +static const uint64_t CACHE_BDD_SATCOUNT = (9LL<<40); +static const uint64_t CACHE_BDD_COMPOSE = (10LL<<40); +static const uint64_t CACHE_BDD_RESTRICT = (11LL<<40); +static const uint64_t CACHE_BDD_CONSTRAIN = (12LL<<40); +static const uint64_t CACHE_BDD_CLOSURE = (13LL<<40); +static const uint64_t CACHE_BDD_ISBDD = (14LL<<40); +static const uint64_t CACHE_BDD_SUPPORT = (15LL<<40); +static const uint64_t CACHE_BDD_PATHCOUNT = (16LL<<40); + +// MDD operations +static const uint64_t CACHE_MDD_RELPROD = (20LL<<40); +static const uint64_t CACHE_MDD_MINUS = (21LL<<40); +static const uint64_t CACHE_MDD_UNION = (22LL<<40); +static const uint64_t CACHE_MDD_INTERSECT = (23LL<<40); +static const uint64_t CACHE_MDD_PROJECT = (24LL<<40); +static const uint64_t CACHE_MDD_JOIN = (25LL<<40); +static const uint64_t CACHE_MDD_MATCH = (26LL<<40); +static const uint64_t CACHE_MDD_RELPREV = (27LL<<40); +static const uint64_t CACHE_MDD_SATCOUNT = (28LL<<40); +static const uint64_t CACHE_MDD_SATCOUNTL1 = (29LL<<40); +static const uint64_t CACHE_MDD_SATCOUNTL2 = (30LL<<40); + +// MTBDD operations +static const uint64_t CACHE_MTBDD_APPLY = (40LL<<40); +static const uint64_t CACHE_MTBDD_UAPPLY = (41LL<<40); +static const uint64_t CACHE_MTBDD_ABSTRACT = (42LL<<40); +static const uint64_t CACHE_MTBDD_ITE = (43LL<<40); +static const uint64_t CACHE_MTBDD_AND_ABSTRACT_PLUS = (44LL<<40); +static const uint64_t CACHE_MTBDD_AND_ABSTRACT_MAX = (45LL<<40); +static const uint64_t CACHE_MTBDD_SUPPORT = (46LL<<40); +static const uint64_t CACHE_MTBDD_COMPOSE = (47LL<<40); +static const uint64_t CACHE_MTBDD_EQUAL_NORM = (48LL<<40); +static const uint64_t CACHE_MTBDD_EQUAL_NORM_REL = (49LL<<40); +static const uint64_t CACHE_MTBDD_MINIMUM = (50LL<<40); +static const uint64_t CACHE_MTBDD_MAXIMUM = (51LL<<40); +static const uint64_t CACHE_MTBDD_LEQ = (52LL<<40); +static const uint64_t CACHE_MTBDD_LESS = (53LL<<40); +static const uint64_t CACHE_MTBDD_GEQ = (54LL<<40); +static const uint64_t CACHE_MTBDD_GREATER = (55LL<<40); +static const uint64_t CACHE_MTBDD_EVAL_COMPOSE = (56LL<<40); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include <sylvan_mtbdd_int.h> +#include <sylvan_ldd_int.h> + +#ifdef __cplusplus +} /* namespace */ +#endif + +#endif diff --git a/sylvan_ldd.c b/sylvan_ldd.c old mode 100644 new mode 100755 index 618834d7c0b1d88cd83f4734c87c6b1a73f18ea5..a42f83d4cef9a06c4d0780e447c790c8988afd49 --- a/sylvan_ldd.c +++ b/sylvan_ldd.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2014 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,106 +15,15 @@ * limitations under the License. */ -#include <sylvan_config.h> +#include <sylvan_int.h> -#include <assert.h> #include <inttypes.h> #include <math.h> -#include <pthread.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> - +#include <sys/mman.h> #include <avl.h> -#include <refs.h> +#include <sylvan_refs.h> #include <sha2.h> -#include <sylvan.h> -#include <sylvan_common.h> - -/** - * MDD node structure - */ -typedef struct __attribute__((packed)) mddnode { - uint64_t a, b; -} * mddnode_t; // 16 bytes - -// RmRR RRRR RRRR VVVV | VVVV DcDD DDDD DDDD (little endian - in memory) -// VVVV RRRR RRRR RRRm | DDDD DDDD DDDc VVVV (big endian) - -// Ensure our mddnode is 16 bytes -typedef char __lddmc_check_mddnode_t_is_16_bytes[(sizeof(struct mddnode)==16) ? 1 : -1]; - -static inline uint32_t __attribute__((unused)) -mddnode_getvalue(mddnode_t n) -{ - return *(uint32_t*)((uint8_t*)n+6); -} - -static inline uint8_t __attribute__((unused)) -mddnode_getmark(mddnode_t n) -{ - return n->a & 1; -} - -static inline uint8_t __attribute__((unused)) -mddnode_getcopy(mddnode_t n) -{ - return n->b & 0x10000 ? 1 : 0; -} - -static inline uint64_t __attribute__((unused)) -mddnode_getright(mddnode_t n) -{ - return (n->a & 0x0000ffffffffffff) >> 1; -} - -static inline uint64_t __attribute__((unused)) -mddnode_getdown(mddnode_t n) -{ - return n->b >> 17; -} - -static inline void __attribute__((unused)) -mddnode_setvalue(mddnode_t n, uint32_t value) -{ - *(uint32_t*)((uint8_t*)n+6) = value; -} - -static inline void __attribute__((unused)) -mddnode_setmark(mddnode_t n, uint8_t mark) -{ - n->a = (n->a & 0xfffffffffffffffe) | (mark ? 1 : 0); -} - -static inline void __attribute__((unused)) -mddnode_setright(mddnode_t n, uint64_t right) -{ - n->a = (n->a & 0xffff000000000001) | (right << 1); -} - -static inline void __attribute__((unused)) -mddnode_setdown(mddnode_t n, uint64_t down) -{ - n->b = (n->b & 0x000000000001ffff) | (down << 17); -} - -static inline void __attribute__((unused)) -mddnode_make(mddnode_t n, uint32_t value, uint64_t right, uint64_t down) -{ - n->a = right << 1; - n->b = down << 17; - *(uint32_t*)((uint8_t*)n+6) = value; -} - -static inline void __attribute__((unused)) -mddnode_makecopy(mddnode_t n, uint64_t right, uint64_t down) -{ - n->a = right << 1; - n->b = ((down << 1) | 1) << 16; -} - -#define GETNODE(mdd) ((mddnode_t)llmsset_index_to_ptr(nodes, mdd)) /** * Implementation of garbage collection @@ -124,8 +34,9 @@ VOID_TASK_IMPL_1(lddmc_gc_mark_rec, MDD, mdd) { if (mdd <= lddmc_true) return; - if (llmsset_mark(nodes, mdd)) { - mddnode_t n = GETNODE(mdd); + if (llmsset_mark(nodes, mdd)) + { + mddnode_t n = LDD_GETNODE(mdd); SPAWN(lddmc_gc_mark_rec, mddnode_getright(n)); CALL(lddmc_gc_mark_rec, mddnode_getdown(n)); SYNC(lddmc_gc_mark_rec); @@ -136,13 +47,15 @@ VOID_TASK_IMPL_1(lddmc_gc_mark_rec, MDD, mdd) * External references */ -refs_table_t mdd_refs; +refs_table_t lddmc_refs; +refs_table_t lddmc_protected; +static int lddmc_protected_created = 0; MDD lddmc_ref(MDD a) { if (a == lddmc_true || a == lddmc_false) return a; - refs_up(&mdd_refs, a); + refs_up(&lddmc_refs, a); return a; } @@ -150,13 +63,37 @@ void lddmc_deref(MDD a) { if (a == lddmc_true || a == lddmc_false) return; - refs_down(&mdd_refs, a); + refs_down(&lddmc_refs, a); } size_t lddmc_count_refs() { - return refs_count(&mdd_refs); + return refs_count(&lddmc_refs); +} + +void +lddmc_protect(MDD *a) +{ + if (!lddmc_protected_created) + { + // In C++, sometimes lddmc_protect is called before Sylvan is initialized. Just create a table. + protect_create(&lddmc_protected, 4096); + lddmc_protected_created = 1; + } + protect_up(&lddmc_protected, (size_t)a); +} + +void +lddmc_unprotect(MDD *a) +{ + if (lddmc_protected.refs_table != NULL) protect_down(&lddmc_protected, (size_t)a); +} + +size_t +lddmc_count_protected(void) +{ + return protect_count(&lddmc_protected); } /* Called during garbage collection */ @@ -164,44 +101,124 @@ VOID_TASK_0(lddmc_gc_mark_external_refs) { // iterate through refs hash table, mark all found size_t count=0; - uint64_t *it = refs_iter(&mdd_refs, 0, mdd_refs.refs_size); - while (it != NULL) { - SPAWN(lddmc_gc_mark_rec, refs_next(&mdd_refs, &it, mdd_refs.refs_size)); + uint64_t *it = refs_iter(&lddmc_refs, 0, lddmc_refs.refs_size); + while (it != NULL) + { + SPAWN(lddmc_gc_mark_rec, refs_next(&lddmc_refs, &it, lddmc_refs.refs_size)); count++; } - while (count--) { + while (count--) + { + SYNC(lddmc_gc_mark_rec); + } +} + +VOID_TASK_0(lddmc_gc_mark_protected) +{ + // iterate through refs hash table, mark all found + size_t count=0; + uint64_t *it = protect_iter(&lddmc_protected, 0, lddmc_protected.refs_size); + while (it != NULL) + { + MDD *to_mark = (MDD*)protect_next(&lddmc_protected, &it, lddmc_protected.refs_size); + SPAWN(lddmc_gc_mark_rec, *to_mark); + count++; + } + while (count--) + { SYNC(lddmc_gc_mark_rec); } } /* Infrastructure for internal markings */ +typedef struct lddmc_refs_task +{ + Task *t; + void *f; +} *lddmc_refs_task_t; + +typedef struct lddmc_refs_internal +{ + const MDD **pbegin, **pend, **pcur; + MDD *rbegin, *rend, *rcur; + lddmc_refs_task_t sbegin, send, scur; +} *lddmc_refs_internal_t; + + + +lddmc_refs_internal_t sequentiel_refs; + DECLARE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); -VOID_TASK_0(lddmc_refs_mark_task) +VOID_TASK_2(lddmc_refs_mark_p_par, const MDD**, begin, size_t, count) { - LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - size_t i, j=0; - for (i=0; i<lddmc_refs_key->r_count; i++) { - if (j >= 40) { - while (j--) SYNC(lddmc_gc_mark_rec); - j=0; + if (count < 32) + { + while (count) + { + lddmc_gc_mark_rec(**(begin++)); + count--; } - SPAWN(lddmc_gc_mark_rec, lddmc_refs_key->results[i]); - j++; - } - for (i=0; i<lddmc_refs_key->s_count; i++) { - Task *t = lddmc_refs_key->spawns[i]; - if (!TASK_IS_STOLEN(t)) break; - if (TASK_IS_COMPLETED(t)) { - if (j >= 40) { - while (j--) SYNC(lddmc_gc_mark_rec); - j=0; + } + else + { + SPAWN(lddmc_refs_mark_p_par, begin, count / 2); + CALL(lddmc_refs_mark_p_par, begin + (count / 2), count - count / 2); + SYNC(lddmc_refs_mark_p_par); + } +} + +VOID_TASK_2(lddmc_refs_mark_r_par, MDD*, begin, size_t, count) +{ + if (count < 32) + { + while (count) + { + lddmc_gc_mark_rec(*begin++); + count--; + } + } + else + { + SPAWN(lddmc_refs_mark_r_par, begin, count / 2); + CALL(lddmc_refs_mark_r_par, begin + (count / 2), count - count / 2); + SYNC(lddmc_refs_mark_r_par); + } +} + +VOID_TASK_2(lddmc_refs_mark_s_par, lddmc_refs_task_t, begin, size_t, count) +{ + if (count < 32) + { + while (count) + { + Task *t = begin->t; + if (!TASK_IS_STOLEN(t)) return; + if (t->f == begin->f && TASK_IS_COMPLETED(t)) + { + lddmc_gc_mark_rec(*(SyBDD*)TASK_RESULT(t)); } - SPAWN(lddmc_gc_mark_rec, *(syBDD*)TASK_RESULT(t)); - j++; + begin += 1; + count -= 1; } } - while (j--) SYNC(lddmc_gc_mark_rec); + else + { + if (!TASK_IS_STOLEN(begin->t)) return; + SPAWN(lddmc_refs_mark_s_par, begin, count / 2); + CALL(lddmc_refs_mark_s_par, begin + (count / 2), count - count / 2); + SYNC(lddmc_refs_mark_s_par); + } +} + +VOID_TASK_0(lddmc_refs_mark_task) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + SPAWN(lddmc_refs_mark_p_par, lddmc_refs_key->pbegin, lddmc_refs_key->pcur-lddmc_refs_key->pbegin); + SPAWN(lddmc_refs_mark_r_par, lddmc_refs_key->rbegin, lddmc_refs_key->rcur-lddmc_refs_key->rbegin); + CALL(lddmc_refs_mark_s_par, lddmc_refs_key->sbegin, lddmc_refs_key->scur-lddmc_refs_key->sbegin); + SYNC(lddmc_refs_mark_r_par); + SYNC(lddmc_refs_mark_p_par); } VOID_TASK_0(lddmc_refs_mark) @@ -212,12 +229,12 @@ VOID_TASK_0(lddmc_refs_mark) VOID_TASK_0(lddmc_refs_init_task) { lddmc_refs_internal_t s = (lddmc_refs_internal_t)malloc(sizeof(struct lddmc_refs_internal)); - s->r_size = 128; - s->r_count = 0; - s->s_size = 128; - s->s_count = 0; - s->results = (syBDD*)malloc(sizeof(syBDD) * 128); - s->spawns = (Task**)malloc(sizeof(Task*) * 128); + s->pcur = s->pbegin = (const MDD**)malloc(sizeof(MDD*) * 1024); + s->pend = s->pbegin + 1024; + s->rcur = s->rbegin = (MDD*)malloc(sizeof(MDD) * 1024); + s->rend = s->rbegin + 1024; + s->scur = s->sbegin = (lddmc_refs_task_t)malloc(sizeof(struct lddmc_refs_task) * 1024); + s->send = s->sbegin + 1024; SET_THREAD_LOCAL(lddmc_refs_key, s); } @@ -225,9 +242,89 @@ VOID_TASK_0(lddmc_refs_init) { INIT_THREAD_LOCAL(lddmc_refs_key); TOGETHER(lddmc_refs_init_task); - sylvan_gc_add_mark(10, TASK(lddmc_refs_mark)); + sylvan_gc_add_mark(TASK(lddmc_refs_mark)); +} + +void +lddmc_refs_ptrs_up(lddmc_refs_internal_t lddmc_refs_key) +{ + size_t size = lddmc_refs_key->pend - lddmc_refs_key->pbegin; + lddmc_refs_key->pbegin = (const MDD**)realloc(lddmc_refs_key->pbegin, sizeof(MDD*) * size * 2); + lddmc_refs_key->pcur = lddmc_refs_key->pbegin + size; + lddmc_refs_key->pend = lddmc_refs_key->pbegin + (size * 2); +} + +MDD __attribute__((noinline)) +lddmc_refs_refs_up(lddmc_refs_internal_t lddmc_refs_key, MDD res) +{ + long size = lddmc_refs_key->rend - lddmc_refs_key->rbegin; + lddmc_refs_key->rbegin = (MDD*)realloc(lddmc_refs_key->rbegin, sizeof(MDD) * size * 2); + lddmc_refs_key->rcur = lddmc_refs_key->rbegin + size; + lddmc_refs_key->rend = lddmc_refs_key->rbegin + (size * 2); + return res; +} + +void __attribute__((noinline)) +lddmc_refs_tasks_up(lddmc_refs_internal_t lddmc_refs_key) +{ + long size = lddmc_refs_key->send - lddmc_refs_key->sbegin; + lddmc_refs_key->sbegin = (lddmc_refs_task_t)realloc(lddmc_refs_key->sbegin, sizeof(struct lddmc_refs_task) * size * 2); + lddmc_refs_key->scur = lddmc_refs_key->sbegin + size; + lddmc_refs_key->send = lddmc_refs_key->sbegin + (size * 2); +} + +void __attribute__((unused)) +lddmc_refs_pushptr(const MDD *ptr) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + *lddmc_refs_key->pcur++ = ptr; + if (lddmc_refs_key->pcur == lddmc_refs_key->pend) lddmc_refs_ptrs_up(lddmc_refs_key); +} + +void __attribute__((unused)) +lddmc_refs_popptr(size_t amount) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + lddmc_refs_key->pcur -= amount; +} + +MDD __attribute__((unused)) +lddmc_refs_push(MDD lddmc) +{ + + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + *(lddmc_refs_key->rcur++) = lddmc; + if (lddmc_refs_key->rcur == lddmc_refs_key->rend) return lddmc_refs_refs_up(lddmc_refs_key, lddmc); + else return lddmc; +} + +void __attribute__((unused)) +lddmc_refs_pop(long amount) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + lddmc_refs_key->rcur -= amount; +} + +void __attribute__((unused)) +lddmc_refs_spawn(Task *t) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + lddmc_refs_key->scur->t = t; + lddmc_refs_key->scur->f = t->f; + lddmc_refs_key->scur += 1; + if (lddmc_refs_key->scur == lddmc_refs_key->send) lddmc_refs_tasks_up(lddmc_refs_key); } +MDD __attribute__((unused)) +lddmc_refs_sync(MDD result) +{ + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + lddmc_refs_key->scur -= 1; + return result; +} + +VOID_TASK_DECL_0(lddmc_gc_mark_serialize); + /** * Initialize and quit functions */ @@ -235,23 +332,24 @@ VOID_TASK_0(lddmc_refs_init) static void lddmc_quit() { - refs_free(&mdd_refs); + refs_free(&lddmc_refs); } void sylvan_init_ldd() { sylvan_register_quit(lddmc_quit); - sylvan_gc_add_mark(10, TASK(lddmc_gc_mark_external_refs)); + sylvan_gc_add_mark(TASK(lddmc_gc_mark_external_refs)); + sylvan_gc_add_mark(TASK(lddmc_gc_mark_protected)); + sylvan_gc_add_mark(TASK(lddmc_gc_mark_serialize)); - // Sanity check - if (sizeof(struct mddnode) != 16) { - fprintf(stderr, "Invalid size of mdd nodes: %ld\n", sizeof(struct mddnode)); - exit(1); + refs_create(&lddmc_refs, 1024); + if (!lddmc_protected_created) + { + protect_create(&lddmc_protected, 4096); + lddmc_protected_created = 1; } - refs_create(&mdd_refs, 1024); - LACE_ME; CALL(lddmc_refs_init); } @@ -265,16 +363,17 @@ lddmc_makenode(uint32_t value, MDD ifeq, MDD ifneq) { if (ifeq == lddmc_false) return ifneq; - // check if correct (should be false, or next in value) + assert(ifneq != lddmc_true); - if (ifneq != lddmc_false) assert(value < mddnode_getvalue(GETNODE(ifneq))); + if (ifneq != lddmc_false) assert(value < mddnode_getvalue(LDD_GETNODE(ifneq))); struct mddnode n; mddnode_make(&n, value, ifneq, ifeq); int created; uint64_t index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { + if (index == 0) + { lddmc_refs_push(ifeq); lddmc_refs_push(ifneq); LACE_ME; @@ -282,7 +381,8 @@ lddmc_makenode(uint32_t value, MDD ifeq, MDD ifneq) lddmc_refs_pop(1); index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { + if (index == 0) + { fprintf(stderr, "MDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); exit(1); } @@ -302,7 +402,8 @@ lddmc_make_copynode(MDD ifeq, MDD ifneq) int created; uint64_t index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { + if (index == 0) + { lddmc_refs_push(ifeq); lddmc_refs_push(ifneq); LACE_ME; @@ -310,7 +411,8 @@ lddmc_make_copynode(MDD ifeq, MDD ifneq) lddmc_refs_pop(1); index = llmsset_lookup(nodes, n.a, n.b, &created); - if (index == 0) { + if (index == 0) + { fprintf(stderr, "MDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); exit(1); } @@ -327,7 +429,7 @@ lddmc_extendnode(MDD mdd, uint32_t value, MDD ifeq) { if (mdd <= lddmc_true) return lddmc_makenode(value, ifeq, mdd); - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); if (mddnode_getcopy(n)) return lddmc_make_copynode(mddnode_getdown(n), lddmc_extendnode(mddnode_getright(n), value, ifeq)); uint32_t n_value = mddnode_getvalue(n); if (n_value < value) return lddmc_makenode(n_value, mddnode_getdown(n), lddmc_extendnode(mddnode_getright(n), value, ifeq)); @@ -338,28 +440,30 @@ lddmc_extendnode(MDD mdd, uint32_t value, MDD ifeq) uint32_t lddmc_getvalue(MDD mdd) { - return mddnode_getvalue(GETNODE(mdd)); + return mddnode_getvalue(LDD_GETNODE(mdd)); } MDD lddmc_getdown(MDD mdd) { - return mddnode_getdown(GETNODE(mdd)); + return mddnode_getdown(LDD_GETNODE(mdd)); } MDD lddmc_getright(MDD mdd) { - return mddnode_getright(GETNODE(mdd)); + return mddnode_getright(LDD_GETNODE(mdd)); } MDD lddmc_follow(MDD mdd, uint32_t value) { - for (;;) { + for (;;) + { if (mdd <= lddmc_true) return mdd; - const mddnode_t n = GETNODE(mdd); - if (!mddnode_getcopy(n)) { + const mddnode_t n = LDD_GETNODE(mdd); + if (!mddnode_getcopy(n)) + { const uint32_t v = mddnode_getvalue(n); if (v == value) return mddnode_getdown(n); if (v > value) return lddmc_false; @@ -373,7 +477,7 @@ lddmc_iscopy(MDD mdd) { if (mdd <= lddmc_true) return 0; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); return mddnode_getcopy(n) ? 1 : 0; } @@ -382,7 +486,7 @@ lddmc_followcopy(MDD mdd) { if (mdd <= lddmc_true) return lddmc_false; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); if (mddnode_getcopy(n)) return mddnode_getdown(n); else return lddmc_false; } @@ -395,18 +499,22 @@ match_ldds(MDD *one, MDD *two) { MDD m1 = *one, m2 = *two; if (m1 == lddmc_false || m2 == lddmc_false) return 0; - mddnode_t n1 = GETNODE(m1), n2 = GETNODE(m2); + mddnode_t n1 = LDD_GETNODE(m1), n2 = LDD_GETNODE(m2); uint32_t v1 = mddnode_getvalue(n1), v2 = mddnode_getvalue(n2); - while (v1 != v2) { - if (v1 < v2) { + while (v1 != v2) + { + if (v1 < v2) + { m1 = mddnode_getright(n1); if (m1 == lddmc_false) return 0; - n1 = GETNODE(m1); + n1 = LDD_GETNODE(m1); v1 = mddnode_getvalue(n1); - } else if (v1 > v2) { + } + else if (v1 > v2) + { m2 = mddnode_getright(n2); if (m2 == lddmc_false) return 0; - n2 = GETNODE(m2); + n2 = LDD_GETNODE(m2); v2 = mddnode_getvalue(n2); } } @@ -418,7 +526,6 @@ match_ldds(MDD *one, MDD *two) TASK_IMPL_2(MDD, lddmc_union, MDD, a, MDD, b) { /* Terminal cases */ - if (a == b) return a; if (a == lddmc_false) return b; if (b == lddmc_false) return a; @@ -430,18 +537,24 @@ TASK_IMPL_2(MDD, lddmc_union, MDD, a, MDD, b) sylvan_stats_count(LDD_UNION); /* Improve cache behavior */ - if (a < b) { MDD tmp=b; b=a; a=tmp; } + if (a < b) + { + MDD tmp=b; + b=a; + a=tmp; + } /* Access cache */ MDD result; - if (cache_get3(CACHE_MDD_UNION, a, b, 0, &result)) { + if (cache_get3(CACHE_MDD_UNION, a, b, 0, &result)) + { sylvan_stats_count(LDD_UNION_CACHED); return result; } /* Get nodes */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + mddnode_t na = LDD_GETNODE(a); + mddnode_t nb = LDD_GETNODE(b); const int na_copy = mddnode_getcopy(na) ? 1 : 0; const int nb_copy = mddnode_getcopy(nb) ? 1 : 0; @@ -449,30 +562,41 @@ TASK_IMPL_2(MDD, lddmc_union, MDD, a, MDD, b) const uint32_t nb_value = mddnode_getvalue(nb); /* Perform recursive calculation */ - if (na_copy && nb_copy) { + if (na_copy && nb_copy) + { lddmc_refs_spawn(SPAWN(lddmc_union, mddnode_getdown(na), mddnode_getdown(nb))); MDD right = CALL(lddmc_union, mddnode_getright(na), mddnode_getright(nb)); lddmc_refs_push(right); MDD down = lddmc_refs_sync(SYNC(lddmc_union)); lddmc_refs_pop(1); result = lddmc_make_copynode(down, right); - } else if (na_copy) { + } + else if (na_copy) + { MDD right = CALL(lddmc_union, mddnode_getright(na), b); result = lddmc_make_copynode(mddnode_getdown(na), right); - } else if (nb_copy) { + } + else if (nb_copy) + { MDD right = CALL(lddmc_union, a, mddnode_getright(nb)); result = lddmc_make_copynode(mddnode_getdown(nb), right); - } else if (na_value < nb_value) { + } + else if (na_value < nb_value) + { MDD right = CALL(lddmc_union, mddnode_getright(na), b); result = lddmc_makenode(na_value, mddnode_getdown(na), right); - } else if (na_value == nb_value) { + } + else if (na_value == nb_value) + { lddmc_refs_spawn(SPAWN(lddmc_union, mddnode_getdown(na), mddnode_getdown(nb))); MDD right = CALL(lddmc_union, mddnode_getright(na), mddnode_getright(nb)); lddmc_refs_push(right); MDD down = lddmc_refs_sync(SYNC(lddmc_union)); lddmc_refs_pop(1); result = lddmc_makenode(na_value, down, right); - } else /* na_value > nb_value */ { + } + else /* na_value > nb_value */ + { MDD right = CALL(lddmc_union, a, mddnode_getright(nb)); result = lddmc_makenode(nb_value, mddnode_getdown(nb), right); } @@ -483,10 +607,6 @@ TASK_IMPL_2(MDD, lddmc_union, MDD, a, MDD, b) return result; } - - - - TASK_IMPL_2(MDD, lddmc_minus, MDD, a, MDD, b) { /* Terminal cases */ @@ -503,29 +623,35 @@ TASK_IMPL_2(MDD, lddmc_minus, MDD, a, MDD, b) /* Access cache */ MDD result; - if (cache_get3(CACHE_MDD_MINUS, a, b, 0, &result)) { + if (cache_get3(CACHE_MDD_MINUS, a, b, 0, &result)) + { sylvan_stats_count(LDD_MINUS_CACHED); return result; } /* Get nodes */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + mddnode_t na = LDD_GETNODE(a); + mddnode_t nb = LDD_GETNODE(b); uint32_t na_value = mddnode_getvalue(na); uint32_t nb_value = mddnode_getvalue(nb); /* Perform recursive calculation */ - if (na_value < nb_value) { + if (na_value < nb_value) + { MDD right = CALL(lddmc_minus, mddnode_getright(na), b); result = lddmc_makenode(na_value, mddnode_getdown(na), right); - } else if (na_value == nb_value) { + } + else if (na_value == nb_value) + { lddmc_refs_spawn(SPAWN(lddmc_minus, mddnode_getright(na), mddnode_getright(nb))); MDD down = CALL(lddmc_minus, mddnode_getdown(na), mddnode_getdown(nb)); lddmc_refs_push(down); MDD right = lddmc_refs_sync(SYNC(lddmc_minus)); lddmc_refs_pop(1); result = lddmc_makenode(na_value, down, right); - } else /* na_value > nb_value */ { + } + else /* na_value > nb_value */ + { result = CALL(lddmc_minus, a, mddnode_getright(nb)); } @@ -539,15 +665,18 @@ TASK_IMPL_2(MDD, lddmc_minus, MDD, a, MDD, b) TASK_IMPL_3(MDD, lddmc_zip, MDD, a, MDD, b, MDD*, res2) { /* Terminal cases */ - if (a == b) { + if (a == b) + { *res2 = lddmc_false; return a; } - if (a == lddmc_false) { + if (a == lddmc_false) + { *res2 = b; return b; } - if (b == lddmc_false) { + if (b == lddmc_false) + { *res2 = lddmc_false; return a; } @@ -563,22 +692,26 @@ TASK_IMPL_3(MDD, lddmc_zip, MDD, a, MDD, b, MDD*, res2) /* Access cache */ MDD result; if (cache_get3(CACHE_MDD_UNION, a, b, 0, &result) && - cache_get3(CACHE_MDD_MINUS, b, a, 0, res2)) { + cache_get3(CACHE_MDD_MINUS, b, a, 0, res2)) + { sylvan_stats_count(LDD_ZIP); return result; } /* Get nodes */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + mddnode_t na = LDD_GETNODE(a); + mddnode_t nb = LDD_GETNODE(b); uint32_t na_value = mddnode_getvalue(na); uint32_t nb_value = mddnode_getvalue(nb); /* Perform recursive calculation */ - if (na_value < nb_value) { + if (na_value < nb_value) + { MDD right = CALL(lddmc_zip, mddnode_getright(na), b, res2); result = lddmc_makenode(na_value, mddnode_getdown(na), right); - } else if (na_value == nb_value) { + } + else if (na_value == nb_value) + { MDD down2, right2; lddmc_refs_spawn(SPAWN(lddmc_zip, mddnode_getdown(na), mddnode_getdown(nb), &down2)); MDD right = CALL(lddmc_zip, mddnode_getright(na), mddnode_getright(nb), &right2); @@ -588,7 +721,9 @@ TASK_IMPL_3(MDD, lddmc_zip, MDD, a, MDD, b, MDD*, res2) lddmc_refs_pop(2); result = lddmc_makenode(na_value, down, right); *res2 = lddmc_makenode(na_value, down2, right2); - } else /* na_value > nb_value */ { + } + else /* na_value > nb_value */ + { MDD right2; MDD right = CALL(lddmc_zip, a, mddnode_getright(nb), &right2); result = lddmc_makenode(nb_value, mddnode_getdown(nb), right); @@ -616,30 +751,34 @@ TASK_IMPL_2(MDD, lddmc_intersect, MDD, a, MDD, b) sylvan_stats_count(LDD_INTERSECT); /* Get nodes */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + mddnode_t na = LDD_GETNODE(a); + mddnode_t nb = LDD_GETNODE(b); uint32_t na_value = mddnode_getvalue(na); uint32_t nb_value = mddnode_getvalue(nb); /* Skip nodes if possible */ - while (na_value != nb_value) { - if (na_value < nb_value) { + while (na_value != nb_value) + { + if (na_value < nb_value) + { a = mddnode_getright(na); if (a == lddmc_false) return lddmc_false; - na = GETNODE(a); + na = LDD_GETNODE(a); na_value = mddnode_getvalue(na); } - if (nb_value < na_value) { + if (nb_value < na_value) + { b = mddnode_getright(nb); if (b == lddmc_false) return lddmc_false; - nb = GETNODE(b); + nb = LDD_GETNODE(b); nb_value = mddnode_getvalue(nb); } } /* Access cache */ MDD result; - if (cache_get3(CACHE_MDD_INTERSECT, a, b, 0, &result)) { + if (cache_get3(CACHE_MDD_INTERSECT, a, b, 0, &result)) + { sylvan_stats_count(LDD_INTERSECT_CACHED); return result; } @@ -664,7 +803,7 @@ TASK_IMPL_3(MDD, lddmc_match, MDD, a, MDD, b, MDD, proj) if (a == b) return a; if (a == lddmc_false || b == lddmc_false) return lddmc_false; - mddnode_t p_node = GETNODE(proj); + mddnode_t p_node = LDD_GETNODE(proj); uint32_t p_val = mddnode_getvalue(p_node); if (p_val == (uint32_t)-1) return a; @@ -675,7 +814,8 @@ TASK_IMPL_3(MDD, lddmc_match, MDD, a, MDD, b, MDD, proj) sylvan_gc_test(); /* Skip nodes if possible */ - if (p_val == 1) { + if (p_val == 1) + { if (!match_ldds(&a, &b)) return lddmc_false; } @@ -683,19 +823,23 @@ TASK_IMPL_3(MDD, lddmc_match, MDD, a, MDD, b, MDD, proj) /* Access cache */ MDD result; - if (cache_get3(CACHE_MDD_MATCH, a, b, proj, &result)) { + if (cache_get3(CACHE_MDD_MATCH, a, b, proj, &result)) + { sylvan_stats_count(LDD_MATCH_CACHED); return result; } /* Perform recursive calculation */ - mddnode_t na = GETNODE(a); + mddnode_t na = LDD_GETNODE(a); MDD down; - if (p_val == 1) { - mddnode_t nb = GETNODE(b); + if (p_val == 1) + { + mddnode_t nb = LDD_GETNODE(b); /* right = */ lddmc_refs_spawn(SPAWN(lddmc_match, mddnode_getright(na), mddnode_getright(nb), proj)); down = CALL(lddmc_match, mddnode_getdown(na), mddnode_getdown(nb), mddnode_getdown(p_node)); - } else { + } + else + { /* right = */ lddmc_refs_spawn(SPAWN(lddmc_match, mddnode_getright(na), b, proj)); down = CALL(lddmc_match, mddnode_getdown(na), b, mddnode_getdown(p_node)); } @@ -715,111 +859,7 @@ TASK_4(MDD, lddmc_relprod_help, uint32_t, val, MDD, set, MDD, rel, MDD, proj) return lddmc_makenode(val, CALL(lddmc_relprod, set, rel, proj), lddmc_false); } - - -TASK_4(MDD, lddmc_firing_help, uint32_t, val, MDD, mark, MDD, minus, MDD, plus) -{ - return lddmc_makenode(val, CALL(lddmc_firing, mark, minus, plus), lddmc_false); -} -// cmark : markings enabling ; minus : tokens to remove ; plus : tokens to add -TASK_IMPL_3(MDD,lddmc_firing, MDD, cmark, MDD, minus, MDD, plus) { - - - - // for an empty set of source states, or an empty transition relation, return the empty set - if (cmark == lddmc_true) return lddmc_true; - if (minus == lddmc_false) return lddmc_false; - if (plus == lddmc_false) return lddmc_false; // we assume that if meta is finished, then the rest is not in rel - - - - - - - /* Test gc */ - sylvan_gc_test(); - - // sylvan_stats_count(LDD_RELPROD); - - /* Access cache */ - MDD result; - MDD _cmark=cmark, _minus=minus, _plus=plus; - if (cache_get3(CACHE_MDD_RELPROD, cmark, minus, plus, &result)) { - sylvan_stats_count(LDD_RELPROD_CACHED); - return result; - } - - mddnode_t n_cmark = GETNODE(cmark); - mddnode_t n_plus = GETNODE(plus); - mddnode_t n_minus = GETNODE(minus); - // meta: -1 (end; rest not in rel), 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write) - - /* Recursive operations */ - - // write, only-write -// if (m_val == 4) { -// // only-write, so we need to include 'for all variables' -// // the reason is that we did not have a read phase, so we need to 'insert' a read phase here -// -// } - - // if we're here and we are only-write, then we read the current value - - // spawn for every value to write (rel) - - //lddmc_refs_spawn(SPAWN(lddmc_firing, mddnode_getright(n_cmark), minus, plus)); // next in set - int count = 0; - for (;;) { - uint32_t value; - value = mddnode_getvalue(n_cmark); - uint32_t value_minus = mddnode_getvalue(n_minus); - uint32_t value_plus = mddnode_getvalue(n_plus); - if (value>=value_minus) { - lddmc_refs_spawn(SPAWN(lddmc_firing_help, value-value_minus+value_plus, mddnode_getdown(n_cmark), mddnode_getdown(n_minus), mddnode_getdown(n_plus))); - count++; - } - /*else - return lddmc_false;*/ - - - // minus = mddnode_getright(n_minus); - cmark = mddnode_getright(n_cmark); - if (cmark == lddmc_false) break; - n_cmark = GETNODE(cmark); - // n_plus = GETNODE(plus); - } - - // sync+union (one by one) - result = lddmc_false; - while (count--) { - lddmc_refs_push(result); - MDD result2 = lddmc_refs_sync(SYNC(lddmc_firing_help)); - lddmc_refs_push(result2); - result = CALL(lddmc_union, result, result2); - //lddmc_fprintdot(stdout,result2); - lddmc_refs_pop(2); - } -// -// if (m_val == 4) { -// // sync+union with other variables -// lddmc_refs_push(result); -// MDD result2 = lddmc_refs_sync(SYNC(lddmc_firing)); -// lddmc_refs_push(result2); -// result = CALL(lddmc_union, result, result2); - // lddmc_refs_pop(2); -// } - - - /* Write to cache */ - if (cache_put3(CACHE_MDD_RELPROD, _cmark, _minus, _plus, result)) sylvan_stats_count(LDD_RELPROD_CACHEDPUT); - - return result; -} - - - - -// meta: -1 (end; rest not in rel), 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write) +// meta: -1 (end; rest not in rel), 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write), 5 (action label) TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) { // for an empty set of source states, or an empty transition relation, return the empty set @@ -827,20 +867,22 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) if (rel == lddmc_false) return lddmc_false; if (meta == lddmc_true) return set; // we assume that if meta is finished, then the rest is not in rel - mddnode_t n_meta = GETNODE(meta); + mddnode_t n_meta = LDD_GETNODE(meta); uint32_t m_val = mddnode_getvalue(n_meta); // if meta is -1, then no other variables are in the transition relation if (m_val == (uint32_t)-1) return set; // if meta is not 0, then both set and rel must be an internal LDD node - if (m_val != 0) assert(set != lddmc_true && rel != lddmc_true); + if (m_val != 0 && m_val != 5) assert(set != lddmc_true && rel != lddmc_true); /* Skip nodes if possible */ - if (!mddnode_getcopy(GETNODE(rel))) { + if (!mddnode_getcopy(LDD_GETNODE(rel))) + { // if we "read" or "only-read", then match LDDs set and rel // if no match, then return the empty set - if (m_val == 1 || m_val == 3) { + if (m_val == 1 || m_val == 3) + { if (!match_ldds(&set, &rel)) return lddmc_false; } } @@ -853,49 +895,68 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) /* Access cache */ MDD result; MDD _set=set, _rel=rel; - if (cache_get3(CACHE_MDD_RELPROD, set, rel, meta, &result)) { + if (cache_get3(CACHE_MDD_RELPROD, set, rel, meta, &result)) + { sylvan_stats_count(LDD_RELPROD_CACHED); return result; } - mddnode_t n_set = GETNODE(set); - mddnode_t n_rel = GETNODE(rel); + mddnode_t n_set = LDD_GETNODE(set); + mddnode_t n_rel = LDD_GETNODE(rel); /* Recursive operations */ - if (m_val == 0) { // not in rel + if (m_val == 0) // not in rel + { lddmc_refs_spawn(SPAWN(lddmc_relprod, mddnode_getright(n_set), rel, meta)); MDD down = CALL(lddmc_relprod, mddnode_getdown(n_set), rel, mddnode_getdown(n_meta)); lddmc_refs_push(down); MDD right = lddmc_refs_sync(SYNC(lddmc_relprod)); lddmc_refs_pop(1); result = lddmc_makenode(mddnode_getvalue(n_set), down, right); - } else if (m_val == 1) { // read + } + else if (m_val == 5) // action label + { + lddmc_refs_spawn(SPAWN(lddmc_relprod, set, mddnode_getright(n_rel), meta)); + MDD down = CALL(lddmc_relprod, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta)); + lddmc_refs_push(down); + MDD right = lddmc_refs_sync(SYNC(lddmc_relprod)); + lddmc_refs_push(right); + result = CALL(lddmc_union, down, right); + lddmc_refs_pop(2); + } + else if (m_val == 1) // read + { // read layer: if not copy, then set&rel are already matched lddmc_refs_spawn(SPAWN(lddmc_relprod, set, mddnode_getright(n_rel), meta)); // spawn next read in list // for this read, either it is copy ('for all') or it is normal match - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // spawn for every value to copy (set) int count = 0; - for (;;) { + for (;;) + { // stay same level of set (for write) lddmc_refs_spawn(SPAWN(lddmc_relprod, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta))); count++; set = mddnode_getright(n_set); if (set == lddmc_false) break; - n_set = GETNODE(set); + n_set = LDD_GETNODE(set); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod)); lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); } - } else { + } + else + { // stay same level of set (for write) result = CALL(lddmc_relprod, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta)); } @@ -905,25 +966,30 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else if (m_val == 3) { // only-read - if (mddnode_getcopy(n_rel)) { + } + else if (m_val == 3) // only-read + { + if (mddnode_getcopy(n_rel)) + { // copy on read ('for any value') // result = union(result_with_copy, result_without_copy) lddmc_refs_spawn(SPAWN(lddmc_relprod, set, mddnode_getright(n_rel), meta)); // spawn without_copy // spawn for every value to copy (set) int count = 0; - for (;;) { + for (;;) + { lddmc_refs_spawn(SPAWN(lddmc_relprod_help, mddnode_getvalue(n_set), mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta))); count++; set = mddnode_getright(n_set); if (set == lddmc_false) break; - n_set = GETNODE(set); + n_set = LDD_GETNODE(set); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_help)); lddmc_refs_push(result2); @@ -937,7 +1003,9 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else { + } + else + { // only-read, without copy lddmc_refs_spawn(SPAWN(lddmc_relprod, mddnode_getright(n_set), mddnode_getright(n_rel), meta)); MDD down = CALL(lddmc_relprod, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta)); @@ -946,9 +1014,12 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) lddmc_refs_pop(1); result = lddmc_makenode(mddnode_getvalue(n_set), down, right); } - } else if (m_val == 2 || m_val == 4) { + } + else if (m_val == 2 || m_val == 4) + { // write, only-write - if (m_val == 4) { + if (m_val == 4) + { // only-write, so we need to include 'for all variables' // the reason is that we did not have a read phase, so we need to 'insert' a read phase here lddmc_refs_spawn(SPAWN(lddmc_relprod, mddnode_getright(n_set), rel, meta)); // next in set @@ -958,7 +1029,8 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) // spawn for every value to write (rel) int count = 0; - for (;;) { + for (;;) + { uint32_t value; if (mddnode_getcopy(n_rel)) value = mddnode_getvalue(n_set); else value = mddnode_getvalue(n_rel); @@ -966,12 +1038,13 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) count++; rel = mddnode_getright(n_rel); if (rel == lddmc_false) break; - n_rel = GETNODE(rel); + n_rel = LDD_GETNODE(rel); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_help)); lddmc_refs_push(result2); @@ -979,7 +1052,8 @@ TASK_IMPL_3(MDD, lddmc_relprod, MDD, set, MDD, rel, MDD, meta) lddmc_refs_pop(2); } - if (m_val == 4) { + if (m_val == 4) + { // sync+union with other variables lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod)); @@ -1000,12 +1074,6 @@ TASK_5(MDD, lddmc_relprod_union_help, uint32_t, val, MDD, set, MDD, rel, MDD, pr return lddmc_makenode(val, CALL(lddmc_relprod_union, set, rel, proj, un), lddmc_false); } -// cmark : markings enabling ; minus : tokens to remove ; plus : tokens to add -TASK_IMPL_3(MDD,lddmc_firing_union, MDD, cmark, MDD, minus, MDD, plus) { - return lddmc_union(cmark,lddmc_firing(cmark,minus,plus)); -} - - // meta: -1 (end; rest not in rel), 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write) TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) { @@ -1014,18 +1082,20 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) if (un == lddmc_false) return CALL(lddmc_relprod, set, rel, meta); if (meta == lddmc_true) return CALL(lddmc_union, set, un); - mddnode_t n_meta = GETNODE(meta); + mddnode_t n_meta = LDD_GETNODE(meta); uint32_t m_val = mddnode_getvalue(n_meta); if (m_val == (uint32_t)-1) return CALL(lddmc_union, set, un); // check depths (this triggers on logic error) - if (m_val != 0) assert(set != lddmc_true && rel != lddmc_true && un != lddmc_true); + if (m_val != 0 && m_val != 5) assert(set != lddmc_true && rel != lddmc_true && un != lddmc_true); /* Skip nodes if possible */ - if (!mddnode_getcopy(GETNODE(rel))) { + if (!mddnode_getcopy(LDD_GETNODE(rel))) + { // if we "read" or "only-read", then match LDDs set and rel // if no match, then return un (the empty set union un) - if (m_val == 1 || m_val == 3) { + if (m_val == 1 || m_val == 3) + { if (!match_ldds(&set, &rel)) return un; } } @@ -1038,34 +1108,41 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) /* Access cache */ MDD result; MDD _set=set, _rel=rel, _un=un; - if (cache_get4(CACHE_MDD_RELPROD, set, rel, meta, un, &result)) { + if (cache_get4(CACHE_MDD_RELPROD, set, rel, meta, un, &result)) + { sylvan_stats_count(LDD_RELPROD_UNION_CACHED); return result; } /* Get nodes */ - mddnode_t n_set = GETNODE(set); - mddnode_t n_rel = GETNODE(rel); - mddnode_t n_un = GETNODE(un); + mddnode_t n_set = LDD_GETNODE(set); + mddnode_t n_rel = LDD_GETNODE(rel); + mddnode_t n_un = LDD_GETNODE(un); /* Now check the special cases where we can determine that un.value < result.value */ - if (m_val == 0 || m_val == 3) { + if (m_val == 0 || m_val == 3) + { // if m_val == 0, no read/write, then result.value = set.value // if m_val == 3, only read (write same regardless of copy), then result.value = set.value uint32_t set_value = mddnode_getvalue(n_set); uint32_t un_value = mddnode_getvalue(n_un); - if (un_value < set_value) { + if (un_value < set_value) + { MDD right = CALL(lddmc_relprod_union, set, rel, meta, mddnode_getright(n_un)); if (right == mddnode_getright(n_un)) return un; else return lddmc_makenode(mddnode_getvalue(n_un), mddnode_getdown(n_un), right); } - } else if (m_val == 2 || m_val == 4) { + } + else if (m_val == 2 || m_val == 4) + { // if we write, then we only know for certain that un.value < result.value if // the root of rel is not a copy node - if (!mddnode_getcopy(n_rel)) { + if (!mddnode_getcopy(n_rel)) + { uint32_t rel_value = mddnode_getvalue(n_rel); uint32_t un_value = mddnode_getvalue(n_un); - if (un_value < rel_value) { + if (un_value < rel_value) + { MDD right = CALL(lddmc_relprod_union, set, rel, meta, mddnode_getright(n_un)); if (right == mddnode_getright(n_un)) return un; else return lddmc_makenode(mddnode_getvalue(n_un), mddnode_getdown(n_un), right); @@ -1074,12 +1151,14 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) } /* Recursive operations */ - if (m_val == 0) { + if (m_val == 0) + { // current <set> is not in the transition relation uint32_t set_value = mddnode_getvalue(n_set); uint32_t un_value = mddnode_getvalue(n_un); // set_value > un_value already checked above - if (set_value < un_value) { + if (set_value < un_value) + { lddmc_refs_spawn(SPAWN(lddmc_relprod_union, mddnode_getright(n_set), rel, meta, un)); // going down, we don't need _union, since un does not contain this subtree MDD down = CALL(lddmc_relprod, mddnode_getdown(n_set), rel, mddnode_getdown(n_meta)); @@ -1087,7 +1166,9 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) MDD right = lddmc_refs_sync(SYNC(lddmc_relprod_union)); lddmc_refs_pop(1); result = lddmc_makenode(mddnode_getvalue(n_set), down, right); - } else /* set_value == un_value */ { + } + else /* set_value == un_value */ + { assert(set_value == un_value); lddmc_refs_spawn(SPAWN(lddmc_relprod_union, mddnode_getright(n_set), rel, meta, mddnode_getright(n_un))); MDD down = CALL(lddmc_relprod_union, mddnode_getdown(n_set), rel, mddnode_getdown(n_meta), mddnode_getdown(n_un)); @@ -1097,33 +1178,50 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) if (right == mddnode_getright(n_un) && down == mddnode_getdown(n_un)) result = un; else result = lddmc_makenode(mddnode_getvalue(n_set), down, right); } - } else if (m_val == 1) { + } + else if (m_val == 5) + { + lddmc_refs_spawn(SPAWN(lddmc_relprod_union, set, mddnode_getright(n_rel), meta, un)); + MDD down = CALL(lddmc_relprod_union, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), un); + lddmc_refs_push(down); + MDD right = lddmc_refs_sync(SYNC(lddmc_relprod_union)); + lddmc_refs_push(right); + result = CALL(lddmc_union, down, right); + lddmc_refs_pop(2); + } + else if (m_val == 1) + { // First we also spawn for the next read value, and merge results after lddmc_refs_spawn(SPAWN(lddmc_relprod_union, set, mddnode_getright(n_rel), meta, un)); // for this read, either it is a copy read ('for all') or it is normal match - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // spawn for every value in set (copy = for all) int count = 0; - for (;;) { + for (;;) + { // stay same level of set and un (for write level, this was no only-read) lddmc_refs_spawn(SPAWN(lddmc_relprod_union, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), un)); count++; set = mddnode_getright(n_set); if (set == lddmc_false) break; - n_set = GETNODE(set); + n_set = LDD_GETNODE(set); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_union)); lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); } - } else { + } + else + { // read level: if not copy read, then set and rel are already matched // stay same level of set and un (for write level, this was no only-read) result = CALL(lddmc_relprod_union, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), un); @@ -1135,9 +1233,12 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else if (m_val == 3) { // only-read + } + else if (m_val == 3) // only-read + { // un < set already checked above - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // copy on read ('for any value') // result = union(result_with_copy, result_without_copy) lddmc_refs_spawn(SPAWN(lddmc_relprod_union, set, mddnode_getright(n_rel), meta, un)); // spawn without_copy @@ -1145,54 +1246,66 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) // spawn for every value to copy (iterate over set) int count = 0; //result = lddmc_false; - for (;;) { + for (;;) + { uint32_t set_value = mddnode_getvalue(n_set); uint32_t un_value = mddnode_getvalue(n_un); - if (un_value < set_value) { + if (un_value < set_value) + { // this is a bit tricky because the SYNC assumes we SPAWN a relprod_union_help // the result of this will simply be "un_value, mddnode_getdown(n_un), false" which is intended lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, un_value, lddmc_false, lddmc_false, lddmc_true, mddnode_getdown(n_un))); count++; un = mddnode_getright(n_un); - if (un == lddmc_false) { + if (un == lddmc_false) + { // if un is now false, then we have a normal relprod for the rest... result = CALL(lddmc_relprod, set, rel, meta); break; } - n_un = GETNODE(un); - } else if (un_value > set_value) { + n_un = LDD_GETNODE(un); + } + else if (un_value > set_value) + { // this is a bit tricky because the SYNC assumes we SPAWN a relprod_union_help // the result of this will simply be a normal relprod lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, set_value, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), lddmc_false)); count++; set = mddnode_getright(n_set); - if (set == lddmc_false) { + if (set == lddmc_false) + { // if set is now false, then the tail result to merge with is un result = un; break; } - n_set = GETNODE(set); - } else /* un_value == set_value */ { + n_set = LDD_GETNODE(set); + } + else /* un_value == set_value */ + { lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, set_value, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_un))); count++; set = mddnode_getright(n_set); un = mddnode_getright(n_un); - if (set == lddmc_false) { + if (set == lddmc_false) + { // if set is now false, then the tail result to merge with is un result = un; break; - } else if (un == lddmc_false) { + } + else if (un == lddmc_false) + { // if un is now false, then we have a normal relprod for the rest... result = CALL(lddmc_relprod, set, rel, meta); break; } - n_set = GETNODE(set); - n_un = GETNODE(un); + n_set = LDD_GETNODE(set); + n_un = LDD_GETNODE(un); } } // sync+union (one by one) - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_union_help)); lddmc_refs_push(result2); @@ -1206,20 +1319,25 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else { + } + else + { // only-read, not a copy node uint32_t set_value = mddnode_getvalue(n_set); uint32_t un_value = mddnode_getvalue(n_un); // we already checked un_value < set_value - if (un_value > set_value) { + if (un_value > set_value) + { lddmc_refs_spawn(SPAWN(lddmc_relprod_union, mddnode_getright(n_set), mddnode_getright(n_rel), meta, un)); MDD down = CALL(lddmc_relprod, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta)); lddmc_refs_push(down); MDD right = lddmc_refs_sync(SYNC(lddmc_relprod_union)); lddmc_refs_pop(1); result = lddmc_makenode(set_value, down, right); - } else /* un_value == set_value */ { + } + else /* un_value == set_value */ + { assert(un_value == set_value); lddmc_refs_spawn(SPAWN(lddmc_relprod_union, mddnode_getright(n_set), mddnode_getright(n_rel), meta, mddnode_getright(n_un))); MDD down = CALL(lddmc_relprod_union, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_un)); @@ -1229,58 +1347,73 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) result = lddmc_makenode(set_value, down, right); } } - } else if (m_val == 2 || m_val == 4) { // write, only-write - if (m_val == 4) { + } + else if (m_val == 2 || m_val == 4) // write, only-write + { + if (m_val == 4) + { // only-write, so we need to include 'for all variables' because we did not 'read' lddmc_refs_spawn(SPAWN(lddmc_relprod_union, mddnode_getright(n_set), rel, meta, un)); // next in set } // spawn for every value to write (rel) int count = 0; - for (;;) { + for (;;) + { uint32_t value; // we write 'set' if copy node, or 'rel' otherwise if (mddnode_getcopy(n_rel)) value = mddnode_getvalue(n_set); else value = mddnode_getvalue(n_rel); uint32_t un_value = mddnode_getvalue(n_un); - if (un_value < value) { + if (un_value < value) + { // the result of this will simply be "un_value, mddnode_getdown(n_un), false" which is intended lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, un_value, lddmc_false, lddmc_false, lddmc_true, mddnode_getdown(n_un))); count++; un = mddnode_getright(n_un); - if (un == lddmc_false) { + if (un == lddmc_false) + { result = CALL(lddmc_relprod, set, rel, meta); break; } - n_un = GETNODE(un); - } else if (un_value > value) { + n_un = LDD_GETNODE(un); + } + else if (un_value > value) + { lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, value, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), lddmc_false)); count++; rel = mddnode_getright(n_rel); - if (rel == lddmc_false) { + if (rel == lddmc_false) + { result = un; break; } - n_rel = GETNODE(rel); - } else /* un_value == value */ { + n_rel = LDD_GETNODE(rel); + } + else /* un_value == value */ + { lddmc_refs_spawn(SPAWN(lddmc_relprod_union_help, value, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_un))); count++; rel = mddnode_getright(n_rel); un = mddnode_getright(n_un); - if (rel == lddmc_false) { + if (rel == lddmc_false) + { result = un; break; - } else if (un == lddmc_false) { + } + else if (un == lddmc_false) + { result = CALL(lddmc_relprod, set, rel, meta); break; } - n_rel = GETNODE(rel); - n_un = GETNODE(un); + n_rel = LDD_GETNODE(rel); + n_un = LDD_GETNODE(un); } } // sync+union (one by one) - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_union_help)); lddmc_refs_push(result2); @@ -1288,7 +1421,8 @@ TASK_IMPL_4(MDD, lddmc_relprod_union, MDD, set, MDD, rel, MDD, meta, MDD, un) lddmc_refs_pop(2); } - if (m_val == 4) { + if (m_val == 4) + { // sync+union with other variables lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprod_union)); @@ -1312,7 +1446,7 @@ TASK_5(MDD, lddmc_relprev_help, uint32_t, val, MDD, set, MDD, rel, MDD, proj, MD /** * Calculate all predecessors to a in uni according to rel[meta] * <meta> follows the same semantics as relprod - * i.e. 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write), -1 (end; rest=0) + * i.e. 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write), -1 (end; rest=0), 5 (action label) */ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) { @@ -1320,62 +1454,79 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) if (rel == lddmc_false) return lddmc_false; if (uni == lddmc_false) return lddmc_false; - mddnode_t n_meta = GETNODE(meta); + mddnode_t n_meta = LDD_GETNODE(meta); uint32_t m_val = mddnode_getvalue(n_meta); - if (m_val == (uint32_t)-1) { + if (m_val == (uint32_t)-1) + { if (set == uni) return set; else return lddmc_intersect(set, uni); } - if (m_val != 0) assert(set != lddmc_true && rel != lddmc_true && uni != lddmc_true); + if (m_val != 0 && m_val != 5) assert(set != lddmc_true && rel != lddmc_true && uni != lddmc_true); /* Skip nodes if possible */ - if (m_val == 0) { + if (m_val == 0) + { // not in rel: match set and uni ('intersect') if (!match_ldds(&set, &uni)) return lddmc_false; - } else if (mddnode_getcopy(GETNODE(rel))) { + } + else if (mddnode_getcopy(LDD_GETNODE(rel))) + { // read+copy: no matching (pre is everything in uni) // write+copy: no matching (match after split: set and uni) // only-read+copy: match set and uni // only-write+copy: no matching (match after split: set and uni) - if (m_val == 3) { + if (m_val == 3) + { if (!match_ldds(&set, &uni)) return lddmc_false; } - } else if (m_val == 1) { + } + else if (m_val == 1) + { // read: match uni and rel if (!match_ldds(&uni, &rel)) return lddmc_false; - } else if (m_val == 2) { + } + else if (m_val == 2) + { // write: match set and rel if (!match_ldds(&set, &rel)) return lddmc_false; - } else if (m_val == 3) { + } + else if (m_val == 3) + { // only-read: match uni and set and rel - mddnode_t n_set = GETNODE(set); - mddnode_t n_rel = GETNODE(rel); - mddnode_t n_uni = GETNODE(uni); + mddnode_t n_set = LDD_GETNODE(set); + mddnode_t n_rel = LDD_GETNODE(rel); + mddnode_t n_uni = LDD_GETNODE(uni); uint32_t n_set_value = mddnode_getvalue(n_set); uint32_t n_rel_value = mddnode_getvalue(n_rel); uint32_t n_uni_value = mddnode_getvalue(n_uni); - while (n_uni_value != n_rel_value || n_rel_value != n_set_value) { - if (n_uni_value < n_rel_value || n_uni_value < n_set_value) { + while (n_uni_value != n_rel_value || n_rel_value != n_set_value) + { + if (n_uni_value < n_rel_value || n_uni_value < n_set_value) + { uni = mddnode_getright(n_uni); if (uni == lddmc_false) return lddmc_false; - n_uni = GETNODE(uni); + n_uni = LDD_GETNODE(uni); n_uni_value = mddnode_getvalue(n_uni); } - if (n_set_value < n_rel_value || n_set_value < n_uni_value) { + if (n_set_value < n_rel_value || n_set_value < n_uni_value) + { set = mddnode_getright(n_set); if (set == lddmc_false) return lddmc_false; - n_set = GETNODE(set); + n_set = LDD_GETNODE(set); n_set_value = mddnode_getvalue(n_set); } - if (n_rel_value < n_set_value || n_rel_value < n_uni_value) { + if (n_rel_value < n_set_value || n_rel_value < n_uni_value) + { rel = mddnode_getright(n_rel); if (rel == lddmc_false) return lddmc_false; - n_rel = GETNODE(rel); + n_rel = LDD_GETNODE(rel); n_rel_value = mddnode_getvalue(n_rel); } } - } else if (m_val == 4) { + } + else if (m_val == 4) + { // only-write: match set and rel (then use whole universe) if (!match_ldds(&set, &rel)) return lddmc_false; } @@ -1388,17 +1539,19 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) /* Access cache */ MDD result; MDD _set=set, _rel=rel, _uni=uni; - if (cache_get4(CACHE_MDD_RELPREV, set, rel, meta, uni, &result)) { + if (cache_get4(CACHE_MDD_RELPREV, set, rel, meta, uni, &result)) + { sylvan_stats_count(LDD_RELPREV_CACHED); return result; } - mddnode_t n_set = GETNODE(set); - mddnode_t n_rel = GETNODE(rel); - mddnode_t n_uni = GETNODE(uni); + mddnode_t n_set = LDD_GETNODE(set); + mddnode_t n_rel = LDD_GETNODE(rel); + mddnode_t n_uni = LDD_GETNODE(uni); /* Recursive operations */ - if (m_val == 0) { // not in rel + if (m_val == 0) // not in rel + { // m_val == 0 : not in rel (intersection set and universe) lddmc_refs_spawn(SPAWN(lddmc_relprev, mddnode_getright(n_set), rel, meta, mddnode_getright(n_uni))); MDD down = CALL(lddmc_relprev, mddnode_getdown(n_set), rel, mddnode_getdown(n_meta), mddnode_getdown(n_uni)); @@ -1406,32 +1559,49 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) MDD right = lddmc_refs_sync(SYNC(lddmc_relprev)); lddmc_refs_pop(1); result = lddmc_makenode(mddnode_getvalue(n_set), down, right); - } else if (m_val == 1) { // read level + } + else if (m_val == 5) + { + lddmc_refs_spawn(SPAWN(lddmc_relprev, set, mddnode_getright(n_rel), meta, uni)); + MDD down = CALL(lddmc_relprev, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), uni); + lddmc_refs_push(down); + MDD right = lddmc_refs_sync(SYNC(lddmc_relprev)); + lddmc_refs_push(right); + result = CALL(lddmc_union, down, right); + lddmc_refs_pop(2); + } + else if (m_val == 1) // read level + { // result value is in case of copy: everything in uni! // result value is in case of not-copy: match uni and rel! lddmc_refs_spawn(SPAWN(lddmc_relprev, set, mddnode_getright(n_rel), meta, uni)); // next in rel - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // result is everything in uni // spawn for every value to have been read (uni) int count = 0; - for (;;) { + for (;;) + { lddmc_refs_spawn(SPAWN(lddmc_relprev_help, mddnode_getvalue(n_uni), set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), uni)); count++; uni = mddnode_getright(n_uni); if (uni == lddmc_false) break; - n_uni = GETNODE(uni); + n_uni = LDD_GETNODE(uni); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprev_help)); lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); } - } else { + } + else + { // already matched MDD down = CALL(lddmc_relprev, set, mddnode_getdown(n_rel), mddnode_getdown(n_meta), uni); result = lddmc_makenode(mddnode_getvalue(n_uni), down, lddmc_false); @@ -1441,32 +1611,39 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else if (m_val == 3) { // only-read level + } + else if (m_val == 3) // only-read level + { // result value is in case of copy: match set and uni! (already done first match) // result value is in case of not-copy: match set and uni and rel! lddmc_refs_spawn(SPAWN(lddmc_relprev, set, mddnode_getright(n_rel), meta, uni)); // next in rel - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // spawn for every matching set+uni int count = 0; - for (;;) { + for (;;) + { lddmc_refs_spawn(SPAWN(lddmc_relprev_help, mddnode_getvalue(n_uni), mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni))); count++; uni = mddnode_getright(n_uni); if (!match_ldds(&set, &uni)) break; - n_set = GETNODE(set); - n_uni = GETNODE(uni); + n_set = LDD_GETNODE(set); + n_uni = LDD_GETNODE(uni); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprev_help)); lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); } - } else { + } + else + { // already matched MDD down = CALL(lddmc_relprev, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni)); result = lddmc_makenode(mddnode_getvalue(n_uni), down, lddmc_false); @@ -1476,19 +1653,27 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else if (m_val == 2) { // write level + } + else if (m_val == 2) // write level + { // note: the read level has already matched the uni that was read. // write+copy: only for the one set equal to uni... // write: match set and rel (already done) lddmc_refs_spawn(SPAWN(lddmc_relprev, set, mddnode_getright(n_rel), meta, uni)); - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { MDD down = lddmc_follow(set, mddnode_getvalue(n_uni)); - if (down != lddmc_false) { + if (down != lddmc_false) + { result = CALL(lddmc_relprev, down, mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni)); - } else { + } + else + { result = lddmc_false; } - } else { + } + else + { result = CALL(lddmc_relprev, mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni)); } lddmc_refs_push(result); @@ -1496,17 +1681,21 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); - } else if (m_val == 4) { // only-write level + } + else if (m_val == 4) // only-write level + { // only-write+copy: match set and uni after spawn // only-write: match set and rel (already done) lddmc_refs_spawn(SPAWN(lddmc_relprev, set, mddnode_getright(n_rel), meta, uni)); - if (mddnode_getcopy(n_rel)) { + if (mddnode_getcopy(n_rel)) + { // spawn for every matching set+uni int count = 0; - for (;;) { + for (;;) + { if (!match_ldds(&set, &uni)) break; - n_set = GETNODE(set); - n_uni = GETNODE(uni); + n_set = LDD_GETNODE(set); + n_uni = LDD_GETNODE(uni); lddmc_refs_spawn(SPAWN(lddmc_relprev_help, mddnode_getvalue(n_uni), mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni))); count++; uni = mddnode_getright(n_uni); @@ -1514,27 +1703,32 @@ TASK_IMPL_4(MDD, lddmc_relprev, MDD, set, MDD, rel, MDD, meta, MDD, uni) // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprev_help)); lddmc_refs_push(result2); result = CALL(lddmc_union, result, result2); lddmc_refs_pop(2); } - } else { + } + else + { // spawn for every value in universe!! int count = 0; - for (;;) { + for (;;) + { lddmc_refs_spawn(SPAWN(lddmc_relprev_help, mddnode_getvalue(n_uni), mddnode_getdown(n_set), mddnode_getdown(n_rel), mddnode_getdown(n_meta), mddnode_getdown(n_uni))); count++; uni = mddnode_getright(n_uni); if (uni == lddmc_false) break; - n_uni = GETNODE(uni); + n_uni = LDD_GETNODE(uni); } // sync+union (one by one) result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD result2 = lddmc_refs_sync(SYNC(lddmc_relprev_help)); lddmc_refs_push(result2); @@ -1563,16 +1757,17 @@ TASK_IMPL_4(MDD, lddmc_join, MDD, a, MDD, b, MDD, a_proj, MDD, b_proj) /* Test gc */ sylvan_gc_test(); - mddnode_t n_a_proj = GETNODE(a_proj); - mddnode_t n_b_proj = GETNODE(b_proj); + mddnode_t n_a_proj = LDD_GETNODE(a_proj); + mddnode_t n_b_proj = LDD_GETNODE(b_proj); uint32_t a_proj_val = mddnode_getvalue(n_a_proj); uint32_t b_proj_val = mddnode_getvalue(n_b_proj); - while (a_proj_val == 0 && b_proj_val == 0) { + while (a_proj_val == 0 && b_proj_val == 0) + { a_proj = mddnode_getdown(n_a_proj); b_proj = mddnode_getdown(n_b_proj); - n_a_proj = GETNODE(a_proj); - n_b_proj = GETNODE(b_proj); + n_a_proj = LDD_GETNODE(a_proj); + n_b_proj = LDD_GETNODE(b_proj); a_proj_val = mddnode_getvalue(n_a_proj); b_proj_val = mddnode_getvalue(n_b_proj); } @@ -1585,7 +1780,8 @@ TASK_IMPL_4(MDD, lddmc_join, MDD, a, MDD, b, MDD, a_proj, MDD, b_proj) const int keep_a = a_proj_val != 0; const int keep_b = b_proj_val != 0; - if (keep_a && keep_b) { + if (keep_a && keep_b) + { // If both 'keep', then match values if (!match_ldds(&a, &b)) return lddmc_false; } @@ -1594,34 +1790,41 @@ TASK_IMPL_4(MDD, lddmc_join, MDD, a, MDD, b, MDD, a_proj, MDD, b_proj) /* Access cache */ MDD result; - if (cache_get4(CACHE_MDD_JOIN, a, b, a_proj, b_proj, &result)) { + if (cache_get4(CACHE_MDD_JOIN, a, b, a_proj, b_proj, &result)) + { sylvan_stats_count(LDD_JOIN_CACHED); return result; } /* Perform recursive calculation */ - const mddnode_t na = GETNODE(a); - const mddnode_t nb = GETNODE(b); + const mddnode_t na = LDD_GETNODE(a); + const mddnode_t nb = LDD_GETNODE(b); uint32_t val; MDD down; // Make copies (for cache) MDD _a_proj = a_proj, _b_proj = b_proj; - if (keep_a) { - if (keep_b) { + if (keep_a) + { + if (keep_b) + { val = mddnode_getvalue(nb); lddmc_refs_spawn(SPAWN(lddmc_join, mddnode_getright(na), mddnode_getright(nb), a_proj, b_proj)); if (a_proj_val != (uint32_t)-1) a_proj = mddnode_getdown(n_a_proj); if (b_proj_val != (uint32_t)-1) b_proj = mddnode_getdown(n_b_proj); down = CALL(lddmc_join, mddnode_getdown(na), mddnode_getdown(nb), a_proj, b_proj); - } else { + } + else + { val = mddnode_getvalue(na); lddmc_refs_spawn(SPAWN(lddmc_join, mddnode_getright(na), b, a_proj, b_proj)); if (a_proj_val != (uint32_t)-1) a_proj = mddnode_getdown(n_a_proj); if (b_proj_val != (uint32_t)-1) b_proj = mddnode_getdown(n_b_proj); down = CALL(lddmc_join, mddnode_getdown(na), b, a_proj, b_proj); } - } else { + } + else + { val = mddnode_getvalue(nb); lddmc_refs_spawn(SPAWN(lddmc_join, a, mddnode_getright(nb), a_proj, b_proj)); if (a_proj_val != (uint32_t)-1) a_proj = mddnode_getdown(n_a_proj); @@ -1646,7 +1849,7 @@ TASK_IMPL_2(MDD, lddmc_project, const MDD, mdd, const MDD, proj) if (mdd == lddmc_false) return lddmc_false; // projection of empty is empty if (mdd == lddmc_true) return lddmc_true; // projection of universe is universe... - mddnode_t p_node = GETNODE(proj); + mddnode_t p_node = LDD_GETNODE(proj); uint32_t p_val = mddnode_getvalue(p_node); if (p_val == (uint32_t)-1) return mdd; if (p_val == (uint32_t)-2) return lddmc_true; // because we always end with true. @@ -1656,36 +1859,45 @@ TASK_IMPL_2(MDD, lddmc_project, const MDD, mdd, const MDD, proj) sylvan_stats_count(LDD_PROJECT); MDD result; - if (cache_get3(CACHE_MDD_PROJECT, mdd, proj, 0, &result)) { + if (cache_get3(CACHE_MDD_PROJECT, mdd, proj, 0, &result)) + { sylvan_stats_count(LDD_PROJECT_CACHED); return result; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); - if (p_val == 1) { // keep + if (p_val == 1) // keep + { lddmc_refs_spawn(SPAWN(lddmc_project, mddnode_getright(n), proj)); MDD down = CALL(lddmc_project, mddnode_getdown(n), mddnode_getdown(p_node)); lddmc_refs_push(down); MDD right = lddmc_refs_sync(SYNC(lddmc_project)); lddmc_refs_pop(1); result = lddmc_makenode(mddnode_getvalue(n), down, right); - } else { // quantify - if (mddnode_getdown(n) == lddmc_true) { // assume lowest level + } + else // quantify + { + if (mddnode_getdown(n) == lddmc_true) // assume lowest level + { result = lddmc_true; - } else { + } + else + { int count = 0; MDD p_down = mddnode_getdown(p_node), _mdd=mdd; - while (1) { + while (1) + { lddmc_refs_spawn(SPAWN(lddmc_project, mddnode_getdown(n), p_down)); count++; _mdd = mddnode_getright(n); assert(_mdd != lddmc_true); if (_mdd == lddmc_false) break; - n = GETNODE(_mdd); + n = LDD_GETNODE(_mdd); } result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD down = lddmc_refs_sync(SYNC(lddmc_project)); lddmc_refs_push(down); @@ -1709,7 +1921,7 @@ TASK_IMPL_3(MDD, lddmc_project_minus, const MDD, mdd, const MDD, proj, MDD, avoi if (mdd == lddmc_false) return lddmc_false; // projection of empty is empty if (mdd == lddmc_true) return lddmc_true; // avoid != lddmc_true - mddnode_t p_node = GETNODE(proj); + mddnode_t p_node = LDD_GETNODE(proj); uint32_t p_val = mddnode_getvalue(p_node); if (p_val == (uint32_t)-1) return lddmc_minus(mdd, avoid); if (p_val == (uint32_t)-2) return lddmc_true; @@ -1719,23 +1931,29 @@ TASK_IMPL_3(MDD, lddmc_project_minus, const MDD, mdd, const MDD, proj, MDD, avoi sylvan_stats_count(LDD_PROJECT_MINUS); MDD result; - if (cache_get3(CACHE_MDD_PROJECT, mdd, proj, avoid, &result)) { + if (cache_get3(CACHE_MDD_PROJECT, mdd, proj, avoid, &result)) + { sylvan_stats_count(LDD_PROJECT_MINUS_CACHED); return result; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); - if (p_val == 1) { // keep + if (p_val == 1) // keep + { // move 'avoid' until it matches uint32_t val = mddnode_getvalue(n); MDD a_down = lddmc_false; - while (avoid != lddmc_false) { - mddnode_t a_node = GETNODE(avoid); + while (avoid != lddmc_false) + { + mddnode_t a_node = LDD_GETNODE(avoid); uint32_t a_val = mddnode_getvalue(a_node); - if (a_val > val) { + if (a_val > val) + { break; - } else if (a_val == val) { + } + else if (a_val == val) + { a_down = mddnode_getdown(a_node); break; } @@ -1747,22 +1965,29 @@ TASK_IMPL_3(MDD, lddmc_project_minus, const MDD, mdd, const MDD, proj, MDD, avoi MDD right = lddmc_refs_sync(SYNC(lddmc_project_minus)); lddmc_refs_pop(1); result = lddmc_makenode(val, down, right); - } else { // quantify - if (mddnode_getdown(n) == lddmc_true) { // assume lowest level + } + else // quantify + { + if (mddnode_getdown(n) == lddmc_true) // assume lowest level + { result = lddmc_true; - } else { + } + else + { int count = 0; MDD p_down = mddnode_getdown(p_node), _mdd=mdd; - while (1) { + while (1) + { lddmc_refs_spawn(SPAWN(lddmc_project_minus, mddnode_getdown(n), p_down, avoid)); count++; _mdd = mddnode_getright(n); assert(_mdd != lddmc_true); if (_mdd == lddmc_false) break; - n = GETNODE(_mdd); + n = LDD_GETNODE(_mdd); } result = lddmc_false; - while (count--) { + while (count--) + { lddmc_refs_push(result); MDD down = lddmc_refs_sync(SYNC(lddmc_project_minus)); lddmc_refs_push(down); @@ -1781,26 +2006,32 @@ MDD lddmc_union_cube(MDD a, uint32_t* values, size_t count) { if (a == lddmc_false) return lddmc_cube(values, count); - if (a == lddmc_true) { + if (a == lddmc_true) + { assert(count == 0); return lddmc_true; } assert(count != 0); - mddnode_t na = GETNODE(a); + mddnode_t na = LDD_GETNODE(a); uint32_t na_value = mddnode_getvalue(na); /* Only create a new node if something actually changed */ - if (na_value < *values) { + if (na_value < *values) + { MDD right = lddmc_union_cube(mddnode_getright(na), values, count); if (right == mddnode_getright(na)) return a; // no actual change return lddmc_makenode(na_value, mddnode_getdown(na), right); - } else if (na_value == *values) { + } + else if (na_value == *values) + { MDD down = lddmc_union_cube(mddnode_getdown(na), values+1, count-1); if (down == mddnode_getdown(na)) return a; // no actual change return lddmc_makenode(na_value, down, mddnode_getright(na)); - } else /* na_value > *values */ { + } + else /* na_value > *values */ + { return lddmc_makenode(*values, lddmc_cube(values+1, count-1), a); } } @@ -1809,39 +2040,50 @@ MDD lddmc_union_cube_copy(MDD a, uint32_t* values, int* copy, size_t count) { if (a == lddmc_false) return lddmc_cube_copy(values, copy, count); - if (a == lddmc_true) { + if (a == lddmc_true) + { assert(count == 0); return lddmc_true; } assert(count != 0); - mddnode_t na = GETNODE(a); + mddnode_t na = LDD_GETNODE(a); /* Only create a new node if something actually changed */ int na_copy = mddnode_getcopy(na); - if (na_copy && *copy) { + if (na_copy && *copy) + { MDD down = lddmc_union_cube_copy(mddnode_getdown(na), values+1, copy+1, count-1); if (down == mddnode_getdown(na)) return a; // no actual change return lddmc_make_copynode(down, mddnode_getright(na)); - } else if (na_copy) { + } + else if (na_copy) + { MDD right = lddmc_union_cube_copy(mddnode_getright(na), values, copy, count); if (right == mddnode_getright(na)) return a; // no actual change return lddmc_make_copynode(mddnode_getdown(na), right); - } else if (*copy) { + } + else if (*copy) + { return lddmc_make_copynode(lddmc_cube_copy(values+1, copy+1, count-1), a); } uint32_t na_value = mddnode_getvalue(na); - if (na_value < *values) { + if (na_value < *values) + { MDD right = lddmc_union_cube_copy(mddnode_getright(na), values, copy, count); if (right == mddnode_getright(na)) return a; // no actual change return lddmc_makenode(na_value, mddnode_getdown(na), right); - } else if (na_value == *values) { + } + else if (na_value == *values) + { MDD down = lddmc_union_cube_copy(mddnode_getdown(na), values+1, copy+1, count-1); if (down == mddnode_getdown(na)) return a; // no actual change return lddmc_makenode(na_value, down, mddnode_getright(na)); - } else /* na_value > *values */ { + } + else /* na_value > *values */ + { return lddmc_makenode(*values, lddmc_cube_copy(values+1, copy+1, count-1), a); } } @@ -1849,7 +2091,8 @@ lddmc_union_cube_copy(MDD a, uint32_t* values, int* copy, size_t count) int lddmc_member_cube(MDD a, uint32_t* values, size_t count) { - while (1) { + while (1) + { if (a == lddmc_false) return 0; if (a == lddmc_true) return 1; assert(count > 0); // size mismatch @@ -1863,7 +2106,8 @@ lddmc_member_cube(MDD a, uint32_t* values, size_t count) int lddmc_member_cube_copy(MDD a, uint32_t* values, int* copy, size_t count) { - while (1) { + while (1) + { if (a == lddmc_false) return 0; if (a == lddmc_true) return 1; assert(count > 0); // size mismatch @@ -1898,8 +2142,9 @@ static void lddmc_nodecount_levels_mark(MDD mdd, size_t *variables) { if (mdd <= lddmc_true) return; - mddnode_t n = GETNODE(mdd); - if (!mddnode_getmark(n)) { + mddnode_t n = LDD_GETNODE(mdd); + if (!mddnode_getmark(n)) + { mddnode_setmark(n, 1); (*variables) += 1; lddmc_nodecount_levels_mark(mddnode_getright(n), variables); @@ -1911,8 +2156,9 @@ static void lddmc_nodecount_levels_unmark(MDD mdd) { if (mdd <= lddmc_true) return; - mddnode_t n = GETNODE(mdd); - if (mddnode_getmark(n)) { + mddnode_t n = LDD_GETNODE(mdd); + if (mddnode_getmark(n)) + { mddnode_setmark(n, 0); lddmc_nodecount_levels_unmark(mddnode_getright(n)); lddmc_nodecount_levels_unmark(mddnode_getdown(n)); @@ -1934,7 +2180,7 @@ static size_t lddmc_nodecount_mark(MDD mdd) { if (mdd <= lddmc_true) return 0; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); if (mddnode_getmark(n)) return 0; mddnode_setmark(n, 1); return 1 + lddmc_nodecount_mark(mddnode_getdown(n)) + lddmc_nodecount_mark(mddnode_getright(n)); @@ -1944,8 +2190,9 @@ static void lddmc_nodecount_unmark(MDD mdd) { if (mdd <= lddmc_true) return; - mddnode_t n = GETNODE(mdd); - if (mddnode_getmark(n)) { + mddnode_t n = LDD_GETNODE(mdd); + if (mddnode_getmark(n)) + { mddnode_setmark(n, 0); lddmc_nodecount_unmark(mddnode_getright(n)); lddmc_nodecount_unmark(mddnode_getdown(n)); @@ -1972,19 +2219,21 @@ TASK_IMPL_1(lddmc_satcount_double_t, lddmc_satcount_cached, MDD, mdd) /* Perhaps execute garbage collection */ sylvan_gc_test(); - union { + union + { lddmc_satcount_double_t d; uint64_t s; } hack; sylvan_stats_count(LDD_SATCOUNT); - if (cache_get3(CACHE_MDD_SATCOUNT, mdd, 0, 0, &hack.s)) { + if (cache_get3(CACHE_MDD_SATCOUNT, mdd, 0, 0, &hack.s)) + { sylvan_stats_count(LDD_SATCOUNT_CACHED); return hack.d; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); SPAWN(lddmc_satcount_cached, mddnode_getdown(n)); lddmc_satcount_double_t right = CALL(lddmc_satcount_cached, mddnode_getright(n)); @@ -2005,21 +2254,24 @@ TASK_IMPL_1(long double, lddmc_satcount, MDD, mdd) sylvan_stats_count(LDD_SATCOUNTL); - union { + union + { long double d; - struct { + struct + { uint64_t s1; uint64_t s2; } s; } hack; if (cache_get3(CACHE_MDD_SATCOUNTL1, mdd, 0, 0, &hack.s.s1) && - cache_get3(CACHE_MDD_SATCOUNTL2, mdd, 0, 0, &hack.s.s2)) { + cache_get3(CACHE_MDD_SATCOUNTL2, mdd, 0, 0, &hack.s.s2)) + { sylvan_stats_count(LDD_SATCOUNTL_CACHED); return hack.d; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); SPAWN(lddmc_satcount, mddnode_getdown(n)); long double right = CALL(lddmc_satcount, mddnode_getright(n)); @@ -2035,11 +2287,12 @@ TASK_IMPL_1(long double, lddmc_satcount, MDD, mdd) TASK_IMPL_5(MDD, lddmc_collect, MDD, mdd, lddmc_collect_cb, cb, void*, context, uint32_t*, values, size_t, count) { if (mdd == lddmc_false) return lddmc_false; - if (mdd == lddmc_true) { + if (mdd == lddmc_true) + { return WRAP(cb, values, count, context); } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); lddmc_refs_spawn(SPAWN(lddmc_collect, mddnode_getright(n), cb, context, values, count)); @@ -2048,7 +2301,8 @@ TASK_IMPL_5(MDD, lddmc_collect, MDD, mdd, lddmc_collect_cb, cb, void*, context, newvalues[count] = mddnode_getvalue(n); MDD down = CALL(lddmc_collect, mddnode_getdown(n), cb, context, newvalues, count+1); - if (down == lddmc_false) { + if (down == lddmc_false) + { MDD result = lddmc_refs_sync(SYNC(lddmc_collect)); return result; } @@ -2056,10 +2310,13 @@ TASK_IMPL_5(MDD, lddmc_collect, MDD, mdd, lddmc_collect_cb, cb, void*, context, lddmc_refs_push(down); MDD right = lddmc_refs_sync(SYNC(lddmc_collect)); - if (right == lddmc_false) { + if (right == lddmc_false) + { lddmc_refs_pop(1); return down; - } else { + } + else + { lddmc_refs_push(right); MDD result = CALL(lddmc_union, down, right); lddmc_refs_pop(2); @@ -2070,12 +2327,13 @@ TASK_IMPL_5(MDD, lddmc_collect, MDD, mdd, lddmc_collect_cb, cb, void*, context, VOID_TASK_5(_lddmc_sat_all_nopar, MDD, mdd, lddmc_enum_cb, cb, void*, context, uint32_t*, values, size_t, count) { if (mdd == lddmc_false) return; - if (mdd == lddmc_true) { + if (mdd == lddmc_true) + { WRAP(cb, values, count, context); return; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); values[count] = mddnode_getvalue(n); CALL(_lddmc_sat_all_nopar, mddnode_getdown(n), cb, context, values, count+1); CALL(_lddmc_sat_all_nopar, mddnode_getright(n), cb, context, values, count); @@ -2086,8 +2344,9 @@ VOID_TASK_IMPL_3(lddmc_sat_all_nopar, MDD, mdd, lddmc_enum_cb, cb, void*, contex // determine depth size_t count=0; MDD _mdd = mdd; - while (_mdd > lddmc_true) { - _mdd = mddnode_getdown(GETNODE(_mdd)); + while (_mdd > lddmc_true) + { + _mdd = mddnode_getdown(LDD_GETNODE(_mdd)); assert(_mdd != lddmc_false); count++; } @@ -2099,12 +2358,13 @@ VOID_TASK_IMPL_3(lddmc_sat_all_nopar, MDD, mdd, lddmc_enum_cb, cb, void*, contex VOID_TASK_IMPL_5(lddmc_sat_all_par, MDD, mdd, lddmc_enum_cb, cb, void*, context, uint32_t*, values, size_t, count) { if (mdd == lddmc_false) return; - if (mdd == lddmc_true) { + if (mdd == lddmc_true) + { WRAP(cb, values, count, context); return; } - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); SPAWN(lddmc_sat_all_par, mddnode_getright(n), cb, context, values, count); @@ -2132,39 +2392,45 @@ VOID_TASK_3(lddmc_match_sat, struct lddmc_match_sat_info *, info, lddmc_enum_cb, if (a == lddmc_false || b == lddmc_false) return; - if (a == lddmc_true) { + if (a == lddmc_true) + { assert(b == lddmc_true); WRAP(cb, info->values, info->count, context); return; } - mddnode_t p_node = GETNODE(proj); + mddnode_t p_node = LDD_GETNODE(proj); uint32_t p_val = mddnode_getvalue(p_node); - if (p_val == (uint32_t)-1) { + if (p_val == (uint32_t)-1) + { assert(b == lddmc_true); CALL(lddmc_sat_all_par, a, cb, context, info->values, info->count); return; } /* Get nodes */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + mddnode_t na = LDD_GETNODE(a); + mddnode_t nb = LDD_GETNODE(b); uint32_t na_value = mddnode_getvalue(na); uint32_t nb_value = mddnode_getvalue(nb); /* Skip nodes if possible */ - if (p_val == 1) { - while (na_value != nb_value) { - if (na_value < nb_value) { + if (p_val == 1) + { + while (na_value != nb_value) + { + if (na_value < nb_value) + { a = mddnode_getright(na); if (a == lddmc_false) return; - na = GETNODE(a); + na = LDD_GETNODE(a); na_value = mddnode_getvalue(na); } - if (nb_value < na_value) { + if (nb_value < na_value) + { b = mddnode_getright(nb); if (b == lddmc_false) return; - nb = GETNODE(b); + nb = LDD_GETNODE(b); nb_value = mddnode_getvalue(nb); } } @@ -2176,7 +2442,7 @@ VOID_TASK_3(lddmc_match_sat, struct lddmc_match_sat_info *, info, lddmc_enum_cb, ri->mdd = mddnode_getright(na); di->mdd = mddnode_getdown(na); ri->match = b; - di->match = mddnode_getdown(nb); + di->match = p_val == 1 ? mddnode_getdown(nb) : b; ri->proj = proj; di->proj = mddnode_getdown(p_node); ri->count = info->count; @@ -2206,7 +2472,7 @@ lddmc_sat_one(MDD mdd, uint32_t* values, size_t count) if (mdd == lddmc_false) return 0; if (mdd == lddmc_true) return 1; assert(count != 0); - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); *values = mddnode_getvalue(n); return lddmc_sat_one(mddnode_getdown(n), values+1, count-1); } @@ -2216,17 +2482,20 @@ lddmc_sat_one_mdd(MDD mdd) { if (mdd == lddmc_false) return lddmc_false; if (mdd == lddmc_true) return lddmc_true; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); MDD down = lddmc_sat_one_mdd(mddnode_getdown(n)); return lddmc_makenode(mddnode_getvalue(n), down, lddmc_false); } TASK_IMPL_4(MDD, lddmc_compose, MDD, mdd, lddmc_compose_cb, cb, void*, context, int, depth) { - if (depth == 0 || mdd == lddmc_false || mdd == lddmc_true) { + if (depth == 0 || mdd == lddmc_false || mdd == lddmc_true) + { return WRAP(cb, mdd, context); - } else { - mddnode_t n = GETNODE(mdd); + } + else + { + mddnode_t n = LDD_GETNODE(mdd); lddmc_refs_spawn(SPAWN(lddmc_compose, mddnode_getright(n), cb, context, depth)); MDD down = lddmc_compose(mddnode_getdown(n), cb, context, depth-1); lddmc_refs_push(down); @@ -2245,8 +2514,8 @@ VOID_TASK_IMPL_4(lddmc_visit_seq, MDD, mdd, lddmc_visit_callbacks_t*, cbs, size_ WRAP(cbs->lddmc_visit_init_context, context_down, context, 1); WRAP(cbs->lddmc_visit_init_context, context_right, context, 0); - CALL(lddmc_visit_seq, mddnode_getdown(GETNODE(mdd)), cbs, ctx_size, context_down); - CALL(lddmc_visit_seq, mddnode_getright(GETNODE(mdd)), cbs, ctx_size, context_right); + CALL(lddmc_visit_seq, mddnode_getdown(LDD_GETNODE(mdd)), cbs, ctx_size, context_down); + CALL(lddmc_visit_seq, mddnode_getright(LDD_GETNODE(mdd)), cbs, ctx_size, context_right); WRAP(cbs->lddmc_visit_post, mdd, context); } @@ -2260,8 +2529,8 @@ VOID_TASK_IMPL_4(lddmc_visit_par, MDD, mdd, lddmc_visit_callbacks_t*, cbs, size_ WRAP(cbs->lddmc_visit_init_context, context_down, context, 1); WRAP(cbs->lddmc_visit_init_context, context_right, context, 0); - SPAWN(lddmc_visit_par, mddnode_getdown(GETNODE(mdd)), cbs, ctx_size, context_down); - CALL(lddmc_visit_par, mddnode_getright(GETNODE(mdd)), cbs, ctx_size, context_right); + SPAWN(lddmc_visit_par, mddnode_getdown(LDD_GETNODE(mdd)), cbs, ctx_size, context_down); + CALL(lddmc_visit_par, mddnode_getright(LDD_GETNODE(mdd)), cbs, ctx_size, context_right); SYNC(lddmc_visit_par); WRAP(cbs->lddmc_visit_post, mdd, context); @@ -2282,10 +2551,13 @@ lddmc_mark(mddnode_t node) static inline int lddmc_unmark(mddnode_t node) { - if (mddnode_getmark(node)) { + if (mddnode_getmark(node)) + { mddnode_setmark(node, 0); return 1; - } else { + } + else + { return 0; } } @@ -2293,11 +2565,12 @@ lddmc_unmark(mddnode_t node) static void lddmc_unmark_rec(mddnode_t node) { - if (lddmc_unmark(node)) { + if (lddmc_unmark(node)) + { MDD node_right = mddnode_getright(node); - if (node_right > lddmc_true) lddmc_unmark_rec(GETNODE(node_right)); + if (node_right > lddmc_true) lddmc_unmark_rec(LDD_GETNODE(node_right)); MDD node_down = mddnode_getdown(node); - if (node_down > lddmc_true) lddmc_unmark_rec(GETNODE(node_down)); + if (node_down > lddmc_true) lddmc_unmark_rec(LDD_GETNODE(node_down)); } } @@ -2311,7 +2584,7 @@ lddmc_fprintdot_rec(FILE* out, MDD mdd) // assert(mdd > lddmc_true); // check mark - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); if (mddnode_getmark(n)) return; mddnode_setmark(n, 1); @@ -2321,8 +2594,9 @@ lddmc_fprintdot_rec(FILE* out, MDD mdd) if (mddnode_getcopy(n)) fprintf(out, "<c> *"); else fprintf(out, "<%u> %u", val, val); MDD right = mddnode_getright(n); - while (right != lddmc_false) { - mddnode_t n2 = GETNODE(right); + while (right != lddmc_false) + { + mddnode_t n2 = LDD_GETNODE(right); uint32_t val2 = mddnode_getvalue(n2); fprintf(out, "|<%u> %u", val2, val2); right = mddnode_getright(n2); @@ -2331,25 +2605,33 @@ lddmc_fprintdot_rec(FILE* out, MDD mdd) fprintf(out, "\"];\n"); // recurse and print the edges - for (;;) { + for (;;) + { MDD down = mddnode_getdown(n); // assert(down != lddmc_false); - if (down > lddmc_true) { - // lddmc_fprintdot_rec(out, down); - if (mddnode_getcopy(n)) { + if (down > lddmc_true) + { + lddmc_fprintdot_rec(out, down); + if (mddnode_getcopy(n)) + { fprintf(out, "%" PRIu64 ":c -> ", mdd); - } else { + } + else + { fprintf(out, "%" PRIu64 ":%u -> ", mdd, mddnode_getvalue(n)); } - if (mddnode_getcopy(GETNODE(down))) { + if (mddnode_getcopy(LDD_GETNODE(down))) + { fprintf(out, "%" PRIu64 ":c [style=solid];\n", down); - } else { - fprintf(out, "%" PRIu64 ":%u [style=solid];\n", down, mddnode_getvalue(GETNODE(down))); + } + else + { + fprintf(out, "%" PRIu64 ":%u [style=solid];\n", down, mddnode_getvalue(LDD_GETNODE(down))); } } MDD right = mddnode_getright(n); if (right == lddmc_false) break; - n = GETNODE(right); + n = LDD_GETNODE(right); } } @@ -2357,14 +2639,16 @@ static void lddmc_fprintdot_unmark(MDD mdd) { if (mdd <= lddmc_true) return; - mddnode_t n = GETNODE(mdd); - if (mddnode_getmark(n)) { + mddnode_t n = LDD_GETNODE(mdd); + if (mddnode_getmark(n)) + { mddnode_setmark(n, 0); - for (;;) { + for (;;) + { lddmc_fprintdot_unmark(mddnode_getdown(n)); mdd = mddnode_getright(n); if (mdd == lddmc_false) return; - n = GETNODE(mdd); + n = LDD_GETNODE(mdd); } } } @@ -2378,20 +2662,22 @@ lddmc_fprintdot(FILE *out, MDD mdd) fprintf(out, "edge [dir = forward];\n"); // Special case: false - if (mdd == lddmc_false) { + if (mdd == lddmc_false) + { fprintf(out, "0 [shape=record, label=\"False\"];\n"); fprintf(out, "}\n"); return; } // Special case: true - if (mdd == lddmc_true) { + if (mdd == lddmc_true) + { fprintf(out, "1 [shape=record, label=\"True\"];\n"); fprintf(out, "}\n"); return; } - //lddmc_fprintdot_rec(out, mdd); + lddmc_fprintdot_rec(out, mdd); lddmc_fprintdot_unmark(mdd); fprintf(out, "}\n"); @@ -2425,7 +2711,8 @@ lddmc_print(MDD mdd) * SERIALIZATION */ -struct lddmc_ser { +struct lddmc_ser +{ MDD mdd; size_t assigned; }; @@ -2462,12 +2749,13 @@ lddmc_serialize_assign_rec(MDD mdd) { if (mdd <= lddmc_true) return mdd; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); struct lddmc_ser s, *ss; s.mdd = mdd; ss = lddmc_ser_search(lddmc_ser_set, &s); - if (ss == NULL) { + if (ss == NULL) + { // assign dummy value s.assigned = 0; ss = lddmc_ser_put(&lddmc_ser_set, &s, NULL); @@ -2530,13 +2818,14 @@ lddmc_serialize_totext(FILE *out) struct lddmc_ser *s; fprintf(out, "["); - while ((s=lddmc_ser_reversed_iter_next(it))) { + while ((s=lddmc_ser_reversed_iter_next(it))) + { MDD mdd = s->mdd; - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); fprintf(out, "(%zu,v=%u,d=%zu,r=%zu),", s->assigned, - mddnode_getvalue(n), - lddmc_serialize_get(mddnode_getdown(n)), - lddmc_serialize_get(mddnode_getright(n))); + mddnode_getvalue(n), + lddmc_serialize_get(mddnode_getdown(n)), + lddmc_serialize_get(mddnode_getright(n))); } fprintf(out, "]"); @@ -2557,16 +2846,18 @@ lddmc_serialize_tofile(FILE *out) /* Skip already written entries */ size_t index = 0; - while (index < lddmc_ser_done && (s=lddmc_ser_reversed_iter_next(it))) { + while (index < lddmc_ser_done && (s=lddmc_ser_reversed_iter_next(it))) + { assert(s->assigned == index+2); index++; } - while ((s=lddmc_ser_reversed_iter_next(it))) { + while ((s=lddmc_ser_reversed_iter_next(it))) + { assert(s->assigned == index+2); index++; - mddnode_t n = GETNODE(s->mdd); + mddnode_t n = LDD_GETNODE(s->mdd); struct mddnode node; uint64_t right = lddmc_serialize_get(mddnode_getright(n)); @@ -2588,15 +2879,18 @@ void lddmc_serialize_fromfile(FILE *in) { size_t count, i; - if (fread(&count, sizeof(size_t), 1, in) != 1) { + if (fread(&count, sizeof(size_t), 1, in) != 1) + { // TODO FIXME return error printf("sylvan_serialize_fromfile: file format error, giving up\n"); exit(-1); } - for (i=1; i<=count; i++) { + for (i=1; i<=count; i++) + { struct mddnode node; - if (fread(&node, sizeof(struct mddnode), 1, in) != 1) { + if (fread(&node, sizeof(struct mddnode), 1, in) != 1) + { // TODO FIXME return error printf("sylvan_serialize_fromfile: file format error, giving up\n"); exit(-1); @@ -2619,16 +2913,30 @@ lddmc_serialize_fromfile(FILE *in) } } +VOID_TASK_IMPL_0(lddmc_gc_mark_serialize) +{ + struct lddmc_ser *s; + avl_iter_t *it = lddmc_ser_iter(lddmc_ser_set); + + /* Iterate through nodes in serialization */ + while ((s=lddmc_ser_iter_next(it))) + { + CALL(lddmc_gc_mark_rec, s->mdd); + } +} + static void lddmc_sha2_rec(MDD mdd, SHA256_CTX *ctx) { - if (mdd <= lddmc_true) { + if (mdd <= lddmc_true) + { SHA256_Update(ctx, (void*)&mdd, sizeof(uint64_t)); return; } - mddnode_t node = GETNODE(mdd); - if (lddmc_mark(node)) { + mddnode_t node = LDD_GETNODE(mdd); + if (lddmc_mark(node)) + { uint32_t val = mddnode_getvalue(node); SHA256_Update(ctx, (void*)&val, sizeof(uint32_t)); lddmc_sha2_rec(mddnode_getdown(node), ctx); @@ -2656,7 +2964,7 @@ lddmc_getsha(MDD mdd, char *target) SHA256_CTX ctx; SHA256_Init(&ctx); lddmc_sha2_rec(mdd, &ctx); - if (mdd > lddmc_true) lddmc_unmark_rec(GETNODE(mdd)); + if (mdd > lddmc_true) lddmc_unmark_rec(LDD_GETNODE(mdd)); SHA256_End(&ctx, target); } @@ -2670,9 +2978,11 @@ lddmc_test_ismdd(MDD mdd) int first = 1; size_t depth = 0; - if (mdd != lddmc_false) { - mddnode_t n = GETNODE(mdd); - if (mddnode_getcopy(n)) { + if (mdd != lddmc_false) + { + mddnode_t n = LDD_GETNODE(mdd); + if (mddnode_getcopy(n)) + { mdd = mddnode_getright(n); depth = lddmc_test_ismdd(mddnode_getdown(n)); assert(depth >= 1); @@ -2680,17 +2990,21 @@ lddmc_test_ismdd(MDD mdd) } uint32_t value = 0; - while (mdd != lddmc_false) { + while (mdd != lddmc_false) + { assert(llmsset_is_marked(nodes, mdd)); - mddnode_t n = GETNODE(mdd); + mddnode_t n = LDD_GETNODE(mdd); uint32_t next_value = mddnode_getvalue(n); assert(mddnode_getcopy(n) == 0); - if (first) { + if (first) + { first = 0; depth = lddmc_test_ismdd(mddnode_getdown(n)); assert(depth >= 1); - } else { + } + else + { assert(value < next_value); assert(depth == lddmc_test_ismdd(mddnode_getdown(n))); } @@ -2701,58 +3015,208 @@ lddmc_test_ismdd(MDD mdd) return 1 + depth; } -int ldd_equal(MDD a, MDD b) +///////////////////////////////////////////////////////////// + +void init_gc_seq() { + LACE_ME; + LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); + sequentiel_refs=lddmc_refs_key; +} - if (a == lddmc_false && b == lddmc_false) return 1; - else if (a==lddmc_false || b == lddmc_false) return 0; +MDD __attribute__((unused)) +ldd_refs_push(MDD lddmc) +{ + *(sequentiel_refs->rcur++) = lddmc; + if (sequentiel_refs->rcur == sequentiel_refs->rend) return lddmc_refs_refs_up(sequentiel_refs, lddmc); + else return lddmc; +} +void __attribute__((unused)) +ldd_refs_pop(long amount) +{ - /* Perform recursive calculation */ - mddnode_t na = GETNODE(a); - mddnode_t nb = GETNODE(b); + lddmc_refs_key->rcur -= amount; +} + +llmsset_destroy_seq(llmsset_t dbs, size_t first, size_t count) +{ + if (count > 1024) + { + size_t split = count/2; + llmsset_destroy_seq( dbs, first, split); + llmsset_destroy_seq( dbs, first + split, count - split); - uint32_t va,vb; - MDD mdda,mddb; + } + else + { + for (size_t k=first; k<first+count; k++) + { + volatile uint64_t *ptr2 = dbs->bitmap2 + (k/64); + volatile uint64_t *ptrc = dbs->bitmapc + (k/64); + uint64_t mask = 0x8000000000000000LL >> (k&63); - do { - va = mddnode_getvalue(na); - vb = mddnode_getvalue(nb); + // if not marked but is custom + if ((*ptr2 & mask) == 0 && (*ptrc & mask)) + { + uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*k; + dbs->destroy_cb(d_ptr[0], d_ptr[1]); + *ptrc &= ~mask; + } + } + } +} + +void llmsset_destroy_unmarked_seq( llmsset_t dbs) +{ + if (dbs->destroy_cb == NULL) return; // no custom function + llmsset_destroy_seq( dbs, 0, dbs->table_size); +} - if (va==vb) { +void sylvan_gc_seq() +{ + if (seq_llmsset_count_marked(nodes)>llmsset_get_size(nodes)/2) + { + printf("GC active \n "); + cache_clear(); + llmsset_clear_data_seq(nodes); + ldd_gc_mark_protected(); + llmsset_destroy_unmarked_seq(nodes); + sylvan_rehash_all_seq(); + } +} - MDD mddnar = mddnode_getright(na); - MDD mddnbr = mddnode_getright(nb); - if (mddnar == lddmc_false && mddnar == lddmc_false) ; - else if (mddnar==lddmc_false || mddnbr == lddmc_false) return 0; +void ldd_refs_mark_p_par( const MDD** begin, size_t count) +{ + if (count < 32) + { + while (count) + { + ldd_gc_mark_rec(**(begin++)); + count--; + } + } + else + { + ldd_refs_mark_p_par( begin, count / 2); + ldd_refs_mark_p_par( begin + (count / 2), count - count / 2); - uint32_t var,vbr; - while (mddnar!=lddmc_false && mddnbr!=lddmc_false) { - mddnode_t nar=GETNODE(mddnar); - mddnode_t nbr=GETNODE(mddnbr); - var = mddnode_getvalue(nar); - vbr = mddnode_getvalue(nbr); + } +} - if (var==vbr) { - if (ldd_equal(mddnode_getdown(nar),mddnode_getdown(nbr))) { - mddnar = mddnode_getright(nar); - mddnbr = mddnode_getright(nbr); - } - else return 0; - } - else return 0; +void ldd_refs_mark_r_par( MDD* begin, size_t count) +{ + if (count < 32) + { + while (count) + { + ldd_gc_mark_rec(*begin++); + count--; + } + } + else + { + ldd_refs_mark_r_par( begin, count / 2); + ldd_refs_mark_r_par( begin + (count / 2), count - count / 2); + + } +} + + +void ldd_refs_mark_s_par( lddmc_refs_task_t begin, size_t count) +{ + LACE_ME + if (count < 32) + { + while (count) + { + Task *t = begin->t; + if (!TASK_IS_STOLEN(t)) return; + if (t->f == begin->f && TASK_IS_COMPLETED(t)) + { + ldd_gc_mark_rec(*(SyBDD*)TASK_RESULT(t)); } - mdda=mddnode_getdown(na); - mddb=mddnode_getdown(nb); - na=GETNODE(mdda); - nb=GETNODE(mddb); + begin += 1; + count -= 1; } - else - return 0; + } + else + { + if (!TASK_IS_STOLEN(begin->t)) return; + ldd_refs_mark_s_par( begin, count / 2); + ldd_refs_mark_s_par( begin + (count / 2), count - count / 2); - } while (mdda!=lddmc_false && mddb!=lddmc_false); - return 1; + } } + + +void ldd_gc_mark_protected() +{ + ldd_refs_mark_p_par( sequentiel_refs->pbegin, sequentiel_refs->pcur-sequentiel_refs->pbegin); + ldd_refs_mark_r_par( sequentiel_refs->rbegin, sequentiel_refs->rcur-sequentiel_refs->rbegin); + + + +// ldd_refs_mark_s_par( lddmc_refs_key->sbegin, lddmc_refs_key->scur-lddmc_refs_key->sbegin); + //SYNC(lddmc_refs_mark_r_par); + //SYNC(lddmc_refs_mark_p_par); + //***********************external + // iterate through refs hash table, mark all found + /*size_t count=0; + uint64_t *it = refs_iter(&lddmc_refs, 0, lddmc_refs.refs_size); + + while (it != NULL) { + ldd_gc_mark_rec(refs_next(&lddmc_refs, &it, lddmc_refs.refs_size)); + count++; + } + /*while (count--) { + SYNC(lddmc_gc_mark_rec); + }*/ + // iterate through refs hash table, mark all found + /* count=0; + *it = protect_iter(&lddmc_protected, 0, lddmc_protected.refs_size); + + while (it != NULL) { + MDD *to_mark = (MDD*)protect_next(&lddmc_protected, &it, lddmc_protected.refs_size); + ldd_gc_mark_rec(*to_mark); + count++; + } + /* while (count--) { + SYNC(lddmc_gc_mark_rec); + }*/ + /******************************************/ + /* struct lddmc_ser *s; + avl_iter_t *it_avl = lddmc_ser_iter(lddmc_ser_set); + + /* Iterate through nodes in serialization */ + /*while ((s=lddmc_ser_iter_next(it_avl))) { + ldd_gc_mark_rec(s->mdd); + }*/ +} + +ldd_gc_mark_rec(MDD mdd) +{ + if (mdd <= lddmc_true) return; + + if (llmsset_mark(nodes, mdd)) + { + mddnode_t n = LDD_GETNODE(mdd); + ldd_gc_mark_rec(mddnode_getright(n)); + ldd_gc_mark_rec(mddnode_getdown(n)); + + } +} + +void displayMDDTableInfo() +{ + + printf("%zu of %zu buckets filled!\n", seq_llmsset_count_marked(nodes), llmsset_get_size(nodes)); +} +int isGCRequired() +{ + return (seq_llmsset_count_marked(nodes)>llmsset_get_size(nodes)/2); +} + #endif diff --git a/sylvan_ldd.h b/sylvan_ldd.h old mode 100644 new mode 100755 index 4b69eb693b1d560d2d9418a965834f289174964f..62d76f87305894a20832fe953d29c115e86de0af --- a/sylvan_ldd.h +++ b/sylvan_ldd.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2014 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,14 +24,13 @@ extern "C" { #endif /* __cplusplus */ - typedef uint64_t MDD; // Note: low 40 bits only -#define lddmc_false ((MDD)0) -#define lddmc_true ((MDD)1) +static const MDD lddmc_false = 0; +static const MDD lddmc_true = 1; /* Initialize LDD functionality */ -void sylvan_init_ldd(); +void sylvan_init_ldd(void); /* Primitives */ MDD lddmc_makenode(uint32_t value, MDD ifeq, MDD ifneq); @@ -52,19 +52,49 @@ MDD lddmc_make_copynode(MDD ifeq, MDD ifneq); int lddmc_iscopy(MDD mdd); MDD lddmc_followcopy(MDD mdd); -/* Add or remove external reference to MDD */ -MDD lddmc_ref(MDD a); -void lddmc_deref(MDD a); +/** + * Infrastructure for external references using a hash table. + * Two hash tables store external references: a pointers table and a values table. + * The pointers table stores pointers to MDD variables, manipulated with protect and unprotect. + * The values table stores MDD, manipulated with ref and deref. + * We strongly recommend using the pointers table whenever possible. + */ -/* For use in custom mark functions */ -VOID_TASK_DECL_1(lddmc_gc_mark_rec, MDD) -#define lddmc_gc_mark_rec(mdd) CALL(lddmc_gc_mark_rec, mdd) +/** + * Store the pointer <ptr> in the pointers table. + */ +void lddmc_protect(MDD* ptr); + +/** + * Delete the pointer <ptr> from the pointers table. + */ +void lddmc_unprotect(MDD* ptr); + +/** + * Compute the number of pointers in the pointers table. + */ +size_t lddmc_count_protected(void); -/* Return the number of external references */ -size_t lddmc_count_refs(); +/** + * Store the MDD <dd> in the values table. + */ +MDD lddmc_ref(MDD dd); -/* Mark MDD for "notify on dead" */ -#define lddmc_notify_ondead(mdd) llmsset_notify_ondead(nodes, mdd) +/** + * Delete the MDD <dd> from the values table. + */ +void lddmc_deref(MDD dd); + +/** + * Compute the number of values in the values table. + */ +size_t lddmc_count_refs(void); + +/** + * Call mtbdd_gc_mark_rec for every mtbdd you want to keep in your custom mark functions. + */ +VOID_TASK_DECL_1(lddmc_gc_mark_rec, MDD) +#define lddmc_gc_mark_rec(mdd) CALL(lddmc_gc_mark_rec, mdd) /* Sanity check - returns depth of MDD including 'true' terminal or 0 for empty set */ #ifndef NDEBUG @@ -98,16 +128,9 @@ MDD lddmc_cube_copy(uint32_t* values, int* copy, size_t count); TASK_DECL_3(MDD, lddmc_relprod, MDD, MDD, MDD); #define lddmc_relprod(a, b, proj) CALL(lddmc_relprod, a, b, proj) -TASK_DECL_3(MDD, lddmc_firing, MDD, MDD, MDD); -#define lddmc_firing(cmark, minus, plus) CALL(lddmc_firing, cmark, minus, plus) - TASK_DECL_4(MDD, lddmc_relprod_union, MDD, MDD, MDD, MDD); #define lddmc_relprod_union(a, b, meta, un) CALL(lddmc_relprod_union, a, b, meta, un) - -TASK_DECL_3(MDD, lddmc_firing_union, MDD, MDD, MDD); -#define lddmc_firing_union(cmark, minus, plus) CALL(lddmc_firing_union, cmark, minus, plus) - /** * Calculate all predecessors to a in uni according to rel[proj] * <proj> follows the same semantics as relprod @@ -234,60 +257,63 @@ TASK_DECL_4(MDD, lddmc_compose, MDD, lddmc_compose_cb, void*, int); size_t lddmc_serialize_add(MDD mdd); size_t lddmc_serialize_get(MDD mdd); MDD lddmc_serialize_get_reversed(size_t value); -void lddmc_serialize_reset(); +void lddmc_serialize_reset(void); void lddmc_serialize_totext(FILE *out); void lddmc_serialize_tofile(FILE *out); void lddmc_serialize_fromfile(FILE *in); -/* Infrastructure for internal markings */ -typedef struct lddmc_refs_internal -{ - size_t r_size, r_count; - size_t s_size, s_count; - MDD *results; - Task **spawns; -} *lddmc_refs_internal_t; - -extern DECLARE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - -static inline MDD -lddmc_refs_push(MDD ldd) -{ - LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - if (lddmc_refs_key->r_count >= lddmc_refs_key->r_size) { - lddmc_refs_key->r_size *= 2; - lddmc_refs_key->results = (MDD*)realloc(lddmc_refs_key->results, sizeof(MDD) * lddmc_refs_key->r_size); - } - lddmc_refs_key->results[lddmc_refs_key->r_count++] = ldd; - return ldd; -} +/** + * Infrastructure for internal references. + * Every thread has its own reference stacks. There are three stacks: pointer, values, tasks stack. + * The pointers stack stores pointers to LDD variables, manipulated with pushptr and popptr. + * The values stack stores LDD, manipulated with push and pop. + * The tasks stack stores Lace tasks (that return LDD), manipulated with spawn and sync. + * + * It is recommended to use the pointers stack for local variables and the tasks stack for tasks. + */ -static inline void -lddmc_refs_pop(int amount) -{ - LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - lddmc_refs_key->r_count-=amount; -} +/** + * Push a LDD variable to the pointer reference stack. + * During garbage collection the variable will be inspected and the contents will be marked. + */ +void lddmc_refs_pushptr(const MDD *ptr); + +/** + * Pop the last <amount> LDD variables from the pointer reference stack. + */ +void lddmc_refs_popptr(size_t amount); + +/** + * Push an LDD to the values reference stack. + * During garbage collection the references LDD will be marked. + */ +MDD lddmc_refs_push(MDD dd); + +/** + * Pop the last <amount> LDD from the values reference stack. + */ +void lddmc_refs_pop(long amount); + +/** + * Push a Task that returns an LDD to the tasks reference stack. + * Usage: lddmc_refs_spawn(SPAWN(function, ...)); + */ +void lddmc_refs_spawn(Task *t); + +/** + * Pop a Task from the task reference stack. + * Usage: MDD result = lddmc_refs_sync(SYNC(function)); + */ +MDD lddmc_refs_sync(MDD dd); +/****************************************************************************/ +MDD __attribute__((unused)) ldd_refs_push(MDD lddmc); +void init_gc_seq(); +void __attribute__((unused)) ldd_refs_pop(long amount); -static inline void -lddmc_refs_spawn(Task *t) -{ - LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - if (lddmc_refs_key->s_count >= lddmc_refs_key->s_size) { - lddmc_refs_key->s_size *= 2; - lddmc_refs_key->spawns = (Task**)realloc(lddmc_refs_key->spawns, sizeof(Task*) * lddmc_refs_key->s_size); - } - lddmc_refs_key->spawns[lddmc_refs_key->s_count++] = t; -} -static inline MDD -lddmc_refs_sync(MDD result) -{ - LOCALIZE_THREAD_LOCAL(lddmc_refs_key, lddmc_refs_internal_t); - lddmc_refs_key->s_count--; - return result; -} +void displayMDDTableInfo(); +int isGCRequired(); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/sylvan_ldd_int.h b/sylvan_ldd_int.h new file mode 100755 index 0000000000000000000000000000000000000000..7cf65e3ba786aa8ce816b54d53222dba19f67b0e --- /dev/null +++ b/sylvan_ldd_int.h @@ -0,0 +1,112 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Do not include this file directly. Instead, include sylvan_int.h */ + +/** + * Internals for LDDs + */ + +#ifndef SYLVAN_LDD_INT_H +#define SYLVAN_LDD_INT_H + +/** + * LDD node structure + * + * RmRR RRRR RRRR VVVV | VVVV DcDD DDDD DDDD (little endian - in memory) + * VVVV RRRR RRRR RRRm | DDDD DDDD DDDc VVVV (big endian) + */ +typedef struct __attribute__((packed)) mddnode { + uint64_t a, b; +} * mddnode_t; // 16 bytes + +static inline mddnode_t +LDD_GETNODE(MDD mdd) +{ + return ((mddnode_t)llmsset_index_to_ptr(nodes, mdd)); +} + +static inline uint32_t __attribute__((unused)) +mddnode_getvalue(mddnode_t n) +{ + return *(uint32_t*)((uint8_t*)n+6); +} + +static inline uint8_t __attribute__((unused)) +mddnode_getmark(mddnode_t n) +{ + return n->a & 1; +} + +static inline uint8_t __attribute__((unused)) +mddnode_getcopy(mddnode_t n) +{ + return n->b & 0x10000 ? 1 : 0; +} + +static inline uint64_t __attribute__((unused)) +mddnode_getright(mddnode_t n) +{ + return (n->a & 0x0000ffffffffffff) >> 1; +} + +static inline uint64_t __attribute__((unused)) +mddnode_getdown(mddnode_t n) +{ + return n->b >> 17; +} + +static inline void __attribute__((unused)) +mddnode_setvalue(mddnode_t n, uint32_t value) +{ + *(uint32_t*)((uint8_t*)n+6) = value; +} + +static inline void __attribute__((unused)) +mddnode_setmark(mddnode_t n, uint8_t mark) +{ + n->a = (n->a & 0xfffffffffffffffe) | (mark ? 1 : 0); +} + +static inline void __attribute__((unused)) +mddnode_setright(mddnode_t n, uint64_t right) +{ + n->a = (n->a & 0xffff000000000001) | (right << 1); +} + +static inline void __attribute__((unused)) +mddnode_setdown(mddnode_t n, uint64_t down) +{ + n->b = (n->b & 0x000000000001ffff) | (down << 17); +} + +static inline void __attribute__((unused)) +mddnode_make(mddnode_t n, uint32_t value, uint64_t right, uint64_t down) +{ + n->a = right << 1; + n->b = down << 17; + *(uint32_t*)((uint8_t*)n+6) = value; +} + +static inline void __attribute__((unused)) +mddnode_makecopy(mddnode_t n, uint64_t right, uint64_t down) +{ + n->a = right << 1; + n->b = ((down << 1) | 1) << 16; +} + +#endif diff --git a/sylvan_mt.c b/sylvan_mt.c new file mode 100755 index 0000000000000000000000000000000000000000..543a81e985f84d969a7bb24b180197db841ca419 --- /dev/null +++ b/sylvan_mt.c @@ -0,0 +1,261 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sylvan_int.h> // for llmsset*, nodes, sylvan_register_quit + +#include <inttypes.h> +#include <string.h> + +/** + * Handling of custom leaves "registry" + */ + +typedef struct +{ + sylvan_mt_hash_cb hash_cb; + sylvan_mt_equals_cb equals_cb; + sylvan_mt_create_cb create_cb; + sylvan_mt_destroy_cb destroy_cb; + sylvan_mt_to_str_cb to_str_cb; + sylvan_mt_write_binary_cb write_binary_cb; + sylvan_mt_read_binary_cb read_binary_cb; +} customleaf_t; + +static customleaf_t *cl_registry; +static size_t cl_registry_count; +static size_t cl_registry_size; + +/** + * Implementation of hooks for llmsset + */ + +/** + * Internal helper function + */ +static inline customleaf_t* +sylvan_mt_from_node(uint64_t a, uint64_t b) +{ + uint32_t type = a & 0xffffffff; + assert(type < cl_registry_count); + return cl_registry + type; + (void)b; +} + +static void +_sylvan_create_cb(uint64_t *a, uint64_t *b) +{ + customleaf_t *c = sylvan_mt_from_node(*a, *b); + if (c->create_cb != NULL) c->create_cb(b); +} + +static void +_sylvan_destroy_cb(uint64_t a, uint64_t b) +{ + // for leaf + customleaf_t *c = sylvan_mt_from_node(a, b); + if (c->destroy_cb != NULL) c->destroy_cb(b); +} + +static uint64_t +_sylvan_hash_cb(uint64_t a, uint64_t b, uint64_t seed) +{ + customleaf_t *c = sylvan_mt_from_node(a, b); + if (c->hash_cb != NULL) return c->hash_cb(b, seed ^ a); + else return llmsset_hash(a, b, seed); +} + +static int +_sylvan_equals_cb(uint64_t a, uint64_t b, uint64_t aa, uint64_t bb) +{ + if (a != aa) return 0; + customleaf_t *c = sylvan_mt_from_node(a, b); + if (c->equals_cb != NULL) return c->equals_cb(b, bb); + else return b == bb ? 1 : 0; +} + +uint32_t +sylvan_mt_create_type() +{ + if (cl_registry_count == cl_registry_size) { + // resize registry array + cl_registry_size += 8; + cl_registry = (customleaf_t *)realloc(cl_registry, sizeof(customleaf_t) * (cl_registry_size)); + memset(cl_registry + cl_registry_count, 0, sizeof(customleaf_t) * (cl_registry_size-cl_registry_count)); + } + return cl_registry_count++; +} + +void sylvan_mt_set_hash(uint32_t type, sylvan_mt_hash_cb hash_cb) +{ + customleaf_t *c = cl_registry + type; + c->hash_cb = hash_cb; +} + +void sylvan_mt_set_equals(uint32_t type, sylvan_mt_equals_cb equals_cb) +{ + customleaf_t *c = cl_registry + type; + c->equals_cb = equals_cb; +} + +void sylvan_mt_set_create(uint32_t type, sylvan_mt_create_cb create_cb) +{ + customleaf_t *c = cl_registry + type; + c->create_cb = create_cb; +} + +void sylvan_mt_set_destroy(uint32_t type, sylvan_mt_destroy_cb destroy_cb) +{ + customleaf_t *c = cl_registry + type; + c->destroy_cb = destroy_cb; +} + +void sylvan_mt_set_to_str(uint32_t type, sylvan_mt_to_str_cb to_str_cb) +{ + customleaf_t *c = cl_registry + type; + c->to_str_cb = to_str_cb; +} + +void sylvan_mt_set_write_binary(uint32_t type, sylvan_mt_write_binary_cb write_binary_cb) +{ + customleaf_t *c = cl_registry + type; + c->write_binary_cb = write_binary_cb; +} + +void sylvan_mt_set_read_binary(uint32_t type, sylvan_mt_read_binary_cb read_binary_cb) +{ + customleaf_t *c = cl_registry + type; + c->read_binary_cb = read_binary_cb; +} + +/** + * Initialize and quit functions + */ + +static int mt_initialized = 0; + +static void +sylvan_mt_quit() +{ + if (mt_initialized == 0) return; + mt_initialized = 0; + + free(cl_registry); + cl_registry = NULL; + cl_registry_count = 0; + cl_registry_size = 0; +} + +void +sylvan_init_mt() +{ + if (mt_initialized) return; + mt_initialized = 1; + + // Register quit handler to free structures + sylvan_register_quit(sylvan_mt_quit); + + // Tell llmsset to use our custom hooks + llmsset_set_custom(nodes, _sylvan_hash_cb, _sylvan_equals_cb, _sylvan_create_cb, _sylvan_destroy_cb); + + // Initialize data structures + cl_registry_size = 8; + cl_registry = (customleaf_t *)calloc(sizeof(customleaf_t), cl_registry_size); + cl_registry_count = 3; // 0, 1, 2 are taken +} + +/** + * Return 1 if the given <type> has a custom hash callback, or 0 otherwise. + */ +int +sylvan_mt_has_custom_hash(uint32_t type) +{ + assert(type < cl_registry_count); + customleaf_t *c = cl_registry + type; + return c->hash_cb != NULL ? 1 : 0; +} + +/** + * Convert a leaf (possibly complemented) to a string representation. + * If it does not fit in <buf> of size <buflen>, returns a freshly allocated char* array. + */ +char* +sylvan_mt_to_str(int complement, uint32_t type, uint64_t value, char* buf, size_t buflen) +{ + assert(type < cl_registry_count); + customleaf_t *c = cl_registry + type; + if (type == 0) { + size_t required = (size_t)snprintf(NULL, 0, "%" PRId64, (int64_t)value); + char *ptr = buf; + if (buflen < required) { + ptr = (char*)malloc(required); + buflen = required; + } + if (ptr != NULL) snprintf(ptr, buflen, "%" PRId64, (int64_t)value); + return ptr; + } else if (type == 1) { + size_t required = (size_t)snprintf(NULL, 0, "%f", *(double*)&value); + char *ptr = buf; + if (buflen < required) { + ptr = (char*)malloc(required); + buflen = required; + } + if (ptr != NULL) snprintf(ptr, buflen, "%f", *(double*)&value); + return ptr; + } else if (type == 2) { + int32_t num = (int32_t)(value>>32); + uint32_t denom = value&0xffffffff; + size_t required = (size_t)snprintf(NULL, 0, "%" PRId32 "/%" PRIu32, num, denom); + char *ptr = buf; + if (buflen < required) { + ptr = (char*)malloc(required); + buflen = required; + } + if (ptr != NULL) snprintf(ptr, buflen, "%" PRId32 "/%" PRIu32, num, denom); + return ptr; + } else if (c->to_str_cb != NULL) { + return c->to_str_cb(complement, value, buf, buflen); + } else { + return NULL; + } +} + +uint64_t +sylvan_mt_hash(uint32_t type, uint64_t value, uint64_t seed) +{ + assert(type < cl_registry_count); + customleaf_t *c = cl_registry + type; + if (c->hash_cb != NULL) return c->hash_cb(value, seed); + else return llmsset_hash((uint64_t)type, value, seed); +} + +int +sylvan_mt_write_binary(uint32_t type, uint64_t value, FILE *out) +{ + assert(type < cl_registry_count); + customleaf_t *c = cl_registry + type; + if (c->write_binary_cb != NULL) return c->write_binary_cb(out, value); + else return 0; +} + +int +sylvan_mt_read_binary(uint32_t type, uint64_t *value, FILE *in) +{ + assert(type < cl_registry_count); + customleaf_t *c = cl_registry + type; + if (c->read_binary_cb != NULL) return c->read_binary_cb(in, value); + else return 0; +} diff --git a/sylvan_mt.h b/sylvan_mt.h new file mode 100755 index 0000000000000000000000000000000000000000..280bc620ba73eb5ba7b44beeea606d9c829b82ac --- /dev/null +++ b/sylvan_mt.h @@ -0,0 +1,130 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This file contains declarations for custom Multi-Terminal support. + */ + +/* Do not include this file directly. Instead, include sylvan.h */ + +#ifndef SYLVAN_MT_H +#define SYLVAN_MT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Helper implementation for custom terminal (multi-terminal support) + * Types can implement the following callback functions: + * + * hash(value, seed) + * return hash of value with given seed + * equals(value1, value2) + * return 1 if equal, 0 if not equal + * create(&value) + * optionally allocate object and update value with the pointer + * destroy(value) + * optionally destroy/free value if value points to an allocated object + * to_str(complemented, value, buf, bufsize) + * return string representation of (complemented) value to buf if bufsize large enough, + * otherwise return newly allocated string + * write_binary(fp, value) + * write binary representation of a leaf to given FILE* fp + * return 0 if successful + * read_binary(fp, &value) + * read binary representation of a leaf from given FILE* fp + * treat allocated objects like create (and destroy) + * return 0 if successful + * + * If the 64-byte value already completely describes the leaf, then the functions + * write_binary and read_binary should be set to NULL. + * + * If the 64-byte value is also already a canonical representation, then the functions + * hash, equals, create and destroy should be set to NULL. + * + * Two values are equal (with equals) iff they have the same hash (with hash) + * + * A value v obtained due to create must be equal to the original value (with equals): + * create(v) => equals(\old(v), \new(v)) + * + * NOTE ON EXPECTED LEAF NODE STRUCTURE: the implementation expects leaves in a specific format: + * - 16-byte node { uint64_t a, b; } + * - type == a & 0x00000000ffffffff + * - value == b + */ +typedef uint64_t (*sylvan_mt_hash_cb)(uint64_t, uint64_t); +typedef int (*sylvan_mt_equals_cb)(uint64_t, uint64_t); +typedef void (*sylvan_mt_create_cb)(uint64_t*); +typedef void (*sylvan_mt_destroy_cb)(uint64_t); +typedef char* (*sylvan_mt_to_str_cb)(int, uint64_t, char*, size_t); +typedef int (*sylvan_mt_write_binary_cb)(FILE*, uint64_t); +typedef int (*sylvan_mt_read_binary_cb)(FILE*, uint64_t*); + +/** + * Initialize the multi-terminal subsystem + */ +void sylvan_init_mt(void); + +/** + * Register a new leaf type. + */ +uint32_t sylvan_mt_create_type(void); + +/** + * Set the callback handlers for <type> + */ +void sylvan_mt_set_hash(uint32_t type, sylvan_mt_hash_cb hash_cb); +void sylvan_mt_set_equals(uint32_t type, sylvan_mt_equals_cb equals_cb); +void sylvan_mt_set_create(uint32_t type, sylvan_mt_create_cb create_cb); +void sylvan_mt_set_destroy(uint32_t type, sylvan_mt_destroy_cb destroy_cb); +void sylvan_mt_set_to_str(uint32_t type, sylvan_mt_to_str_cb to_str_cb); +void sylvan_mt_set_write_binary(uint32_t type, sylvan_mt_write_binary_cb write_binary_cb); +void sylvan_mt_set_read_binary(uint32_t type, sylvan_mt_read_binary_cb read_binary_cb); + +/** + * Returns 1 if the given type implements hash, or 0 otherwise. + * (used when inserting into the unique table) + */ +int sylvan_mt_has_custom_hash(uint32_t type); + +/** + * Get a hash for given value (calls hash callback of type). + * If the type does not implement hash, then this is the same hash as used by the unique table. + */ +uint64_t sylvan_mt_hash(uint32_t type, uint64_t value, uint64_t seed); + +/** + * Get text representation of leaf (calls to_str callback of type). + */ +char *sylvan_mt_to_str(int complement, uint32_t type, uint64_t value, char *buf, size_t buflen); + +/** + * Write a leaf in binary form (calls write_binary callback of type). + */ +int sylvan_mt_write_binary(uint32_t type, uint64_t value, FILE *out); + +/** + * Read a leaf in binary form (calls read_binary callback of type). + */ +int sylvan_mt_read_binary(uint32_t type, uint64_t *value, FILE *in); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/sylvan_mtbdd.c b/sylvan_mtbdd.c old mode 100644 new mode 100755 index 2ac6bfe0989f7aeefcc7ae233547f254e452475b..49602dcfc558207769f9593a7546110326c191f0 --- a/sylvan_mtbdd.c +++ b/sylvan_mtbdd.c @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,61 +15,54 @@ * limitations under the License. */ -#include <sylvan_config.h> +#include <sylvan_int.h> -#include <assert.h> #include <inttypes.h> #include <math.h> -#include <pthread.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> -#include <refs.h> +#include <sylvan_refs.h> +#include <sylvan_sl.h> #include <sha2.h> -#include <sylvan.h> -#include <sylvan_common.h> -#include <sylvan_mtbdd_int.h> /* Primitives */ int mtbdd_isleaf(MTBDD bdd) { if (bdd == mtbdd_true || bdd == mtbdd_false) return 1; - return mtbddnode_isleaf(GETNODE(bdd)); + return mtbddnode_isleaf(MTBDD_GETNODE(bdd)); } // for nodes uint32_t mtbdd_getvar(MTBDD node) { - return mtbddnode_getvariable(GETNODE(node)); + return mtbddnode_getvariable(MTBDD_GETNODE(node)); } MTBDD mtbdd_getlow(MTBDD mtbdd) { - return node_getlow(mtbdd, GETNODE(mtbdd)); + return node_getlow(mtbdd, MTBDD_GETNODE(mtbdd)); } MTBDD mtbdd_gethigh(MTBDD mtbdd) { - return node_gethigh(mtbdd, GETNODE(mtbdd)); + return node_gethigh(mtbdd, MTBDD_GETNODE(mtbdd)); } // for leaves uint32_t mtbdd_gettype(MTBDD leaf) { - return mtbddnode_gettype(GETNODE(leaf)); + return mtbddnode_gettype(MTBDD_GETNODE(leaf)); } uint64_t mtbdd_getvalue(MTBDD leaf) { - return mtbddnode_getvalue(GETNODE(leaf)); + return mtbddnode_getvalue(MTBDD_GETNODE(leaf)); } // for leaf type 0 (integer) @@ -97,8 +91,8 @@ VOID_TASK_IMPL_1(mtbdd_gc_mark_rec, MDD, mtbdd) if (mtbdd == mtbdd_true) return; if (mtbdd == mtbdd_false) return; - if (llmsset_mark(nodes, mtbdd&(~mtbdd_complement))) { - mtbddnode_t n = GETNODE(mtbdd); + if (llmsset_mark(nodes, MTBDD_STRIPMARK(mtbdd))) { + mtbddnode_t n = MTBDD_GETNODE(mtbdd); if (!mtbddnode_isleaf(n)) { SPAWN(mtbdd_gc_mark_rec, mtbddnode_getlow(n)); CALL(mtbdd_gc_mark_rec, mtbddnode_gethigh(n)); @@ -119,7 +113,7 @@ MDD mtbdd_ref(MDD a) { if (a == mtbdd_true || a == mtbdd_false) return a; - refs_up(&mtbdd_refs, a); + refs_up(&mtbdd_refs, MTBDD_STRIPMARK(a)); return a; } @@ -127,7 +121,7 @@ void mtbdd_deref(MDD a) { if (a == mtbdd_true || a == mtbdd_false) return; - refs_down(&mtbdd_refs, a); + refs_down(&mtbdd_refs, MTBDD_STRIPMARK(a)); } size_t @@ -180,7 +174,7 @@ VOID_TASK_0(mtbdd_gc_mark_protected) size_t count=0; uint64_t *it = protect_iter(&mtbdd_protected, 0, mtbdd_protected.refs_size); while (it != NULL) { - syBDD *to_mark = (syBDD*)protect_next(&mtbdd_protected, &it, mtbdd_protected.refs_size); + SyBDD *to_mark = (SyBDD*)protect_next(&mtbdd_protected, &it, mtbdd_protected.refs_size); SPAWN(mtbdd_gc_mark_rec, *to_mark); count++; } @@ -190,33 +184,77 @@ VOID_TASK_0(mtbdd_gc_mark_protected) } /* Infrastructure for internal markings */ +typedef struct mtbdd_refs_task +{ + Task *t; + void *f; +} *mtbdd_refs_task_t; + +typedef struct mtbdd_refs_internal +{ + const MTBDD **pbegin, **pend, **pcur; + MTBDD *rbegin, *rend, *rcur; + mtbdd_refs_task_t sbegin, send, scur; +} *mtbdd_refs_internal_t; + DECLARE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); -VOID_TASK_0(mtbdd_refs_mark_task) +VOID_TASK_2(mtbdd_refs_mark_p_par, const MTBDD**, begin, size_t, count) { - LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); - size_t i, j=0; - for (i=0; i<mtbdd_refs_key->r_count; i++) { - if (j >= 40) { - while (j--) SYNC(mtbdd_gc_mark_rec); - j=0; + if (count < 32) { + while (count) { + mtbdd_gc_mark_rec(**(begin++)); + count--; + } + } else { + SPAWN(mtbdd_refs_mark_p_par, begin, count / 2); + CALL(mtbdd_refs_mark_p_par, begin + (count / 2), count - count / 2); + SYNC(mtbdd_refs_mark_p_par); + } +} + +VOID_TASK_2(mtbdd_refs_mark_r_par, MTBDD*, begin, size_t, count) +{ + if (count < 32) { + while (count) { + mtbdd_gc_mark_rec(*begin++); + count--; } - SPAWN(mtbdd_gc_mark_rec, mtbdd_refs_key->results[i]); - j++; - } - for (i=0; i<mtbdd_refs_key->s_count; i++) { - Task *t = mtbdd_refs_key->spawns[i]; - if (!TASK_IS_STOLEN(t)) break; - if (TASK_IS_COMPLETED(t)) { - if (j >= 40) { - while (j--) SYNC(mtbdd_gc_mark_rec); - j=0; + } else { + SPAWN(mtbdd_refs_mark_r_par, begin, count / 2); + CALL(mtbdd_refs_mark_r_par, begin + (count / 2), count - count / 2); + SYNC(mtbdd_refs_mark_r_par); + } +} + +VOID_TASK_2(mtbdd_refs_mark_s_par, mtbdd_refs_task_t, begin, size_t, count) +{ + if (count < 32) { + while (count > 0) { + Task *t = begin->t; + if (!TASK_IS_STOLEN(t)) return; + if (t->f == begin->f && TASK_IS_COMPLETED(t)) { + mtbdd_gc_mark_rec(*(MTBDD*)TASK_RESULT(t)); } - SPAWN(mtbdd_gc_mark_rec, *(syBDD*)TASK_RESULT(t)); - j++; + begin += 1; + count -= 1; } + } else { + if (!TASK_IS_STOLEN(begin->t)) return; + SPAWN(mtbdd_refs_mark_s_par, begin, count / 2); + CALL(mtbdd_refs_mark_s_par, begin + (count / 2), count - count / 2); + SYNC(mtbdd_refs_mark_s_par); } - while (j--) SYNC(mtbdd_gc_mark_rec); +} + +VOID_TASK_0(mtbdd_refs_mark_task) +{ + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + SPAWN(mtbdd_refs_mark_p_par, mtbdd_refs_key->pbegin, mtbdd_refs_key->pcur-mtbdd_refs_key->pbegin); + SPAWN(mtbdd_refs_mark_r_par, mtbdd_refs_key->rbegin, mtbdd_refs_key->rcur-mtbdd_refs_key->rbegin); + CALL(mtbdd_refs_mark_s_par, mtbdd_refs_key->sbegin, mtbdd_refs_key->scur-mtbdd_refs_key->sbegin); + SYNC(mtbdd_refs_mark_r_par); + SYNC(mtbdd_refs_mark_p_par); } VOID_TASK_0(mtbdd_refs_mark) @@ -227,12 +265,12 @@ VOID_TASK_0(mtbdd_refs_mark) VOID_TASK_0(mtbdd_refs_init_task) { mtbdd_refs_internal_t s = (mtbdd_refs_internal_t)malloc(sizeof(struct mtbdd_refs_internal)); - s->r_size = 128; - s->r_count = 0; - s->s_size = 128; - s->s_count = 0; - s->results = (syBDD*)malloc(sizeof(syBDD) * 128); - s->spawns = (Task**)malloc(sizeof(Task*) * 128); + s->pcur = s->pbegin = (const MTBDD**)malloc(sizeof(MTBDD*) * 1024); + s->pend = s->pbegin + 1024; + s->rcur = s->rbegin = (MTBDD*)malloc(sizeof(MTBDD) * 1024); + s->rend = s->rbegin + 1024; + s->scur = s->sbegin = (mtbdd_refs_task_t)malloc(sizeof(struct mtbdd_refs_task) * 1024); + s->send = s->sbegin + 1024; SET_THREAD_LOCAL(mtbdd_refs_key, s); } @@ -240,100 +278,93 @@ VOID_TASK_0(mtbdd_refs_init) { INIT_THREAD_LOCAL(mtbdd_refs_key); TOGETHER(mtbdd_refs_init_task); - sylvan_gc_add_mark(10, TASK(mtbdd_refs_mark)); + sylvan_gc_add_mark(TASK(mtbdd_refs_mark)); } -/** - * Handling of custom leaves "registry" - */ +void +mtbdd_refs_ptrs_up(mtbdd_refs_internal_t mtbdd_refs_key) +{ + size_t cur = mtbdd_refs_key->pcur - mtbdd_refs_key->pbegin; + size_t size = mtbdd_refs_key->pend - mtbdd_refs_key->pbegin; + mtbdd_refs_key->pbegin = (const MTBDD**)realloc(mtbdd_refs_key->pbegin, sizeof(MTBDD*) * size * 2); + mtbdd_refs_key->pcur = mtbdd_refs_key->pbegin + cur; + mtbdd_refs_key->pend = mtbdd_refs_key->pbegin + (size * 2); +} -typedef struct +MTBDD __attribute__((noinline)) +mtbdd_refs_refs_up(mtbdd_refs_internal_t mtbdd_refs_key, MTBDD res) { - mtbdd_hash_cb hash_cb; - mtbdd_equals_cb equals_cb; - mtbdd_create_cb create_cb; - mtbdd_destroy_cb destroy_cb; -} customleaf_t; + long size = mtbdd_refs_key->rend - mtbdd_refs_key->rbegin; + mtbdd_refs_key->rbegin = (MTBDD*)realloc(mtbdd_refs_key->rbegin, sizeof(MTBDD) * size * 2); + mtbdd_refs_key->rcur = mtbdd_refs_key->rbegin + size; + mtbdd_refs_key->rend = mtbdd_refs_key->rbegin + (size * 2); + return res; +} -static customleaf_t *cl_registry; -static size_t cl_registry_count; +void __attribute__((noinline)) +mtbdd_refs_tasks_up(mtbdd_refs_internal_t mtbdd_refs_key) +{ + long size = mtbdd_refs_key->send - mtbdd_refs_key->sbegin; + mtbdd_refs_key->sbegin = (mtbdd_refs_task_t)realloc(mtbdd_refs_key->sbegin, sizeof(struct mtbdd_refs_task) * size * 2); + mtbdd_refs_key->scur = mtbdd_refs_key->sbegin + size; + mtbdd_refs_key->send = mtbdd_refs_key->sbegin + (size * 2); +} -static void -_mtbdd_create_cb(uint64_t *a, uint64_t *b) +void __attribute__((unused)) +mtbdd_refs_pushptr(const MTBDD *ptr) { - // for leaf - if ((*a & 0x4000000000000000) == 0) return; // huh? - uint32_t type = *a & 0xffffffff; - if (type >= cl_registry_count) return; // not in registry - customleaf_t *c = cl_registry + type; - if (c->create_cb == NULL) return; // not in registry - c->create_cb(b); + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + *mtbdd_refs_key->pcur++ = ptr; + if (mtbdd_refs_key->pcur == mtbdd_refs_key->pend) mtbdd_refs_ptrs_up(mtbdd_refs_key); } -static void -_mtbdd_destroy_cb(uint64_t a, uint64_t b) +void __attribute__((unused)) +mtbdd_refs_popptr(size_t amount) { - // for leaf - if ((a & 0x4000000000000000) == 0) return; // huh? - uint32_t type = a & 0xffffffff; - if (type >= cl_registry_count) return; // not in registry - customleaf_t *c = cl_registry + type; - if (c->destroy_cb == NULL) return; // not in registry - c->destroy_cb(b); + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + mtbdd_refs_key->pcur -= amount; } -static uint64_t -_mtbdd_hash_cb(uint64_t a, uint64_t b, uint64_t seed) +MTBDD __attribute__((unused)) +mtbdd_refs_push(MTBDD mtbdd) { - // for leaf - if ((a & 0x4000000000000000) == 0) return llmsset_hash(a, b, seed); - uint32_t type = a & 0xffffffff; - if (type >= cl_registry_count) return llmsset_hash(a, b, seed); - customleaf_t *c = cl_registry + type; - if (c->hash_cb == NULL) return llmsset_hash(a, b, seed); - return c->hash_cb(b, seed ^ a); + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + *(mtbdd_refs_key->rcur++) = mtbdd; + if (mtbdd_refs_key->rcur == mtbdd_refs_key->rend) return mtbdd_refs_refs_up(mtbdd_refs_key, mtbdd); + else return mtbdd; } -static int -_mtbdd_equals_cb(uint64_t a, uint64_t b, uint64_t aa, uint64_t bb) +void __attribute__((unused)) +mtbdd_refs_pop(long amount) { - // for leaf - if (a != aa) return 0; - if ((a & 0x4000000000000000) == 0) return b == bb ? 1 : 0; - if ((aa & 0x4000000000000000) == 0) return b == bb ? 1 : 0; - uint32_t type = a & 0xffffffff; - if (type >= cl_registry_count) return b == bb ? 1 : 0; - customleaf_t *c = cl_registry + type; - if (c->equals_cb == NULL) return b == bb ? 1 : 0; - return c->equals_cb(b, bb); + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + mtbdd_refs_key->rcur -= amount; } -uint32_t -mtbdd_register_custom_leaf(mtbdd_hash_cb hash_cb, mtbdd_equals_cb equals_cb, mtbdd_create_cb create_cb, mtbdd_destroy_cb destroy_cb) +void +mtbdd_refs_spawn(Task *t) { - uint32_t type = cl_registry_count; - if (type == 0) type = 3; - if (cl_registry == NULL) { - cl_registry = (customleaf_t *)calloc(sizeof(customleaf_t), (type+1)); - cl_registry_count = type+1; - llmsset_set_custom(nodes, _mtbdd_hash_cb, _mtbdd_equals_cb, _mtbdd_create_cb, _mtbdd_destroy_cb); - } else if (cl_registry_count <= type) { - cl_registry = (customleaf_t *)realloc(cl_registry, sizeof(customleaf_t) * (type+1)); - memset(cl_registry + cl_registry_count, 0, sizeof(customleaf_t) * (type+1-cl_registry_count)); - cl_registry_count = type+1; - } - customleaf_t *c = cl_registry + type; - c->hash_cb = hash_cb; - c->equals_cb = equals_cb; - c->create_cb = create_cb; - c->destroy_cb = destroy_cb; - return type; + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + mtbdd_refs_key->scur->t = t; + mtbdd_refs_key->scur->f = t->f; + mtbdd_refs_key->scur += 1; + if (mtbdd_refs_key->scur == mtbdd_refs_key->send) mtbdd_refs_tasks_up(mtbdd_refs_key); +} + +MTBDD +mtbdd_refs_sync(MTBDD result) +{ + LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); + mtbdd_refs_key->scur -= 1; + return result; } /** * Initialize and quit functions */ +static int mtbdd_initialized = 0; + static void mtbdd_quit() { @@ -342,25 +373,21 @@ mtbdd_quit() protect_free(&mtbdd_protected); mtbdd_protected_created = 0; } - if (cl_registry != NULL) { - free(cl_registry); - cl_registry = NULL; - cl_registry_count = 0; - } + + mtbdd_initialized = 0; } void sylvan_init_mtbdd() { - sylvan_register_quit(mtbdd_quit); - sylvan_gc_add_mark(10, TASK(mtbdd_gc_mark_external_refs)); - sylvan_gc_add_mark(10, TASK(mtbdd_gc_mark_protected)); + sylvan_init_mt(); - // Sanity check - if (sizeof(struct mtbddnode) != 16) { - fprintf(stderr, "Invalid size of mtbdd nodes: %ld\n", sizeof(struct mtbddnode)); - exit(1); - } + if (mtbdd_initialized) return; + mtbdd_initialized = 1; + + sylvan_register_quit(mtbdd_quit); + sylvan_gc_add_mark(TASK(mtbdd_gc_mark_external_refs)); + sylvan_gc_add_mark(TASK(mtbdd_gc_mark_protected)); refs_create(&mtbdd_refs, 1024); if (!mtbdd_protected_created) { @@ -370,9 +397,6 @@ sylvan_init_mtbdd() LACE_ME; CALL(mtbdd_refs_init); - - cl_registry = NULL; - cl_registry_count = 0; } /** @@ -384,7 +408,7 @@ mtbdd_makeleaf(uint32_t type, uint64_t value) struct mtbddnode n; mtbddnode_makeleaf(&n, type, value); - int custom = type < cl_registry_count && cl_registry[type].hash_cb != NULL ? 1 : 0; + int custom = sylvan_mt_has_custom_hash(type); int created; uint64_t index = custom ? llmsset_lookupc(nodes, n.a, n.b, &created) : llmsset_lookup(nodes, n.a, n.b, &created); @@ -395,19 +419,20 @@ mtbdd_makeleaf(uint32_t type, uint64_t value) index = custom ? llmsset_lookupc(nodes, n.a, n.b, &created) : llmsset_lookup(nodes, n.a, n.b, &created); if (index == 0) { - fprintf(stderr, "BDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + fprintf(stderr, "SyBDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); exit(1); } } + if (created) sylvan_stats_count(BDD_NODES_CREATED); + else sylvan_stats_count(BDD_NODES_REUSED); + return (MTBDD)index; } MTBDD -mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high) +_mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high) { - if (low == high) return low; - // Normalization to keep canonicity // low will have no mark @@ -436,15 +461,57 @@ mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high) index = llmsset_lookup(nodes, n.a, n.b, &created); if (index == 0) { - fprintf(stderr, "BDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + fprintf(stderr, "SyBDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); exit(1); } } + if (created) sylvan_stats_count(BDD_NODES_CREATED); + else sylvan_stats_count(BDD_NODES_REUSED); + result = index; return mark ? result | mtbdd_complement : result; } +MTBDD +mtbdd_makemapnode(uint32_t var, MTBDD low, MTBDD high) +{ + struct mtbddnode n; + uint64_t index; + int created; + + // in an MTBDDMAP, the low edges eventually lead to 0 and cannot have a low mark + assert(!MTBDD_HASMARK(low)); + + mtbddnode_makemapnode(&n, var, low, high); + index = llmsset_lookup(nodes, n.a, n.b, &created); + if (index == 0) { + LACE_ME; + + mtbdd_refs_push(low); + mtbdd_refs_push(high); + sylvan_gc(); + mtbdd_refs_pop(2); + + index = llmsset_lookup(nodes, n.a, n.b, &created); + if (index == 0) { + fprintf(stderr, "SyBDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + exit(1); + } + } + + if (created) sylvan_stats_count(BDD_NODES_CREATED); + else sylvan_stats_count(BDD_NODES_REUSED); + + return index; +} + +MTBDD +mtbdd_ithvar(uint32_t var) +{ + return mtbdd_makenode(var, mtbdd_false, mtbdd_true); +} + /* Operations */ /** @@ -500,17 +567,6 @@ mtbdd_fraction(int64_t nom, uint64_t denom) return mtbdd_makeleaf(2, (nom<<32)|denom); } -/** - * Create the cube of variables in arr. - */ -MTBDD -mtbdd_fromarray(uint32_t* arr, size_t length) -{ - if (length == 0) return mtbdd_true; - else if (length == 1) return mtbdd_makenode(*arr, mtbdd_false, mtbdd_true); - else return mtbdd_makenode(*arr, mtbdd_false, mtbdd_fromarray(arr+1, length-1)); -} - /** * Create a MTBDD cube representing the conjunction of variables in their positive or negative * form depending on whether the cube[idx] equals 0 (negative), 1 (positive) or 2 (any). @@ -521,9 +577,9 @@ MTBDD mtbdd_cube(MTBDD variables, uint8_t *cube, MTBDD terminal) { if (variables == mtbdd_true) return terminal; - mtbddnode_t n = GETNODE(variables); + mtbddnode_t n = MTBDD_GETNODE(variables); - syBDD result; + SyBDD result; switch (*cube) { case 0: result = mtbdd_cube(node_gethigh(variables, n), cube+1, terminal); @@ -538,12 +594,12 @@ mtbdd_cube(MTBDD variables, uint8_t *cube, MTBDD terminal) case 3: { MTBDD variables2 = node_gethigh(variables, n); - mtbddnode_t n2 = GETNODE(variables2); + mtbddnode_t n2 = MTBDD_GETNODE(variables2); uint32_t var2 = mtbddnode_getvariable(n2); result = mtbdd_cube(node_gethigh(variables2, n2), cube+2, terminal); - syBDD low = mtbdd_makenode(var2, result, mtbdd_false); + SyBDD low = mtbdd_makenode(var2, result, mtbdd_false); mtbdd_refs_push(low); - syBDD high = mtbdd_makenode(var2, mtbdd_false, result); + SyBDD high = mtbdd_makenode(var2, mtbdd_false, result); mtbdd_refs_pop(1); result = mtbdd_makenode(mtbddnode_getvariable(n), low, high); return result; @@ -566,19 +622,19 @@ TASK_IMPL_4(MTBDD, mtbdd_union_cube, MTBDD, mtbdd, MTBDD, vars, uint8_t*, cube, sylvan_gc_test(); - mtbddnode_t nv = GETNODE(vars); + mtbddnode_t nv = MTBDD_GETNODE(vars); uint32_t v = mtbddnode_getvariable(nv); - mtbddnode_t na = GETNODE(mtbdd); + mtbddnode_t na = MTBDD_GETNODE(mtbdd); uint32_t va = mtbddnode_getvariable(na); if (va < v) { MTBDD low = node_getlow(mtbdd, na); MTBDD high = node_gethigh(mtbdd, na); - SPAWN(mtbdd_union_cube, high, vars, cube, terminal); - syBDD new_low = mtbdd_union_cube(low, vars, cube, terminal); + mtbdd_refs_spawn(SPAWN(mtbdd_union_cube, high, vars, cube, terminal)); + SyBDD new_low = mtbdd_union_cube(low, vars, cube, terminal); mtbdd_refs_push(new_low); - syBDD new_high = SYNC(mtbdd_union_cube); + SyBDD new_high = mtbdd_refs_sync(SYNC(mtbdd_union_cube)); mtbdd_refs_pop(1); if (new_low != low || new_high != high) return mtbdd_makenode(va, new_low, new_high); else return mtbdd; @@ -600,10 +656,10 @@ TASK_IMPL_4(MTBDD, mtbdd_union_cube, MTBDD, mtbdd, MTBDD, vars, uint8_t*, cube, } case 2: { - SPAWN(mtbdd_union_cube, high, node_gethigh(vars, nv), cube+1, terminal); + mtbdd_refs_spawn(SPAWN(mtbdd_union_cube, high, node_gethigh(vars, nv), cube+1, terminal)); MTBDD new_low = mtbdd_union_cube(low, node_gethigh(vars, nv), cube+1, terminal); mtbdd_refs_push(new_low); - MTBDD new_high = SYNC(mtbdd_union_cube); + MTBDD new_high = mtbdd_refs_sync(SYNC(mtbdd_union_cube)); mtbdd_refs_pop(1); if (new_low != low || new_high != high) return mtbdd_makenode(v, new_low, new_high); return mtbdd; @@ -629,10 +685,10 @@ TASK_IMPL_4(MTBDD, mtbdd_union_cube, MTBDD, mtbdd, MTBDD, vars, uint8_t*, cube, } case 2: { - SPAWN(mtbdd_union_cube, mtbdd, node_gethigh(vars, nv), cube+1, terminal); + mtbdd_refs_spawn(SPAWN(mtbdd_union_cube, mtbdd, node_gethigh(vars, nv), cube+1, terminal)); MTBDD new_low = mtbdd_union_cube(mtbdd, node_gethigh(vars, nv), cube+1, terminal); mtbdd_refs_push(new_low); - MTBDD new_high = SYNC(mtbdd_union_cube); + MTBDD new_high = mtbdd_refs_sync(SYNC(mtbdd_union_cube)); mtbdd_refs_pop(1); return mtbdd_makenode(v, new_low, new_high); } @@ -658,23 +714,30 @@ TASK_IMPL_3(MTBDD, mtbdd_apply, MTBDD, a, MTBDD, b, mtbdd_apply_op, op) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_APPLY); + /* Check cache */ - if (cache_get3(CACHE_MTBDD_APPLY, a, b, (size_t)op, &result)) return result; + if (cache_get3(CACHE_MTBDD_APPLY, a, b, (size_t)op, &result)) { + sylvan_stats_count(MTBDD_APPLY_CACHED); + return result; + } /* Get top variable */ int la = mtbdd_isleaf(a); int lb = mtbdd_isleaf(b); + assert(!la || !lb); mtbddnode_t na, nb; uint32_t va, vb; if (!la) { - na = GETNODE(a); + na = MTBDD_GETNODE(a); va = mtbddnode_getvariable(na); } else { na = 0; va = 0xffffffff; } if (!lb) { - nb = GETNODE(b); + nb = MTBDD_GETNODE(b); vb = mtbddnode_getvariable(nb); } else { nb = 0; @@ -707,7 +770,10 @@ TASK_IMPL_3(MTBDD, mtbdd_apply, MTBDD, a, MTBDD, b, mtbdd_apply_op, op) result = mtbdd_makenode(v, low, high); /* Store in cache */ - cache_put3(CACHE_MTBDD_APPLY, a, b, (size_t)op, result); + if (cache_put3(CACHE_MTBDD_APPLY, a, b, (size_t)op, result)) { + sylvan_stats_count(MTBDD_APPLY_CACHEDPUT); + } + return result; } @@ -723,8 +789,14 @@ TASK_IMPL_5(MTBDD, mtbdd_applyp, MTBDD, a, MTBDD, b, size_t, p, mtbdd_applyp_op, /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_APPLY); + /* Check cache */ - if (cache_get3(opid, a, b, p, &result)) return result; + if (cache_get3(opid, a, b, p, &result)) { + sylvan_stats_count(MTBDD_APPLY_CACHED); + return result; + } /* Get top variable */ int la = mtbdd_isleaf(a); @@ -732,14 +804,14 @@ TASK_IMPL_5(MTBDD, mtbdd_applyp, MTBDD, a, MTBDD, b, size_t, p, mtbdd_applyp_op, mtbddnode_t na, nb; uint32_t va, vb; if (!la) { - na = GETNODE(a); + na = MTBDD_GETNODE(a); va = mtbddnode_getvariable(na); } else { na = 0; va = 0xffffffff; } if (!lb) { - nb = GETNODE(b); + nb = MTBDD_GETNODE(b); vb = mtbddnode_getvariable(nb); } else { nb = 0; @@ -772,7 +844,10 @@ TASK_IMPL_5(MTBDD, mtbdd_applyp, MTBDD, a, MTBDD, b, size_t, p, mtbdd_applyp_op, result = mtbdd_makenode(v, low, high); /* Store in cache */ - cache_put3(opid, a, b, p, result); + if (cache_put3(opid, a, b, p, result)) { + sylvan_stats_count(MTBDD_APPLY_CACHEDPUT); + } + return result; } @@ -784,20 +859,29 @@ TASK_IMPL_3(MTBDD, mtbdd_uapply, MTBDD, dd, mtbdd_uapply_op, op, size_t, param) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_UAPPLY); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, &result)) return result; + if (cache_get3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, &result)) { + sylvan_stats_count(MTBDD_UAPPLY_CACHED); + return result; + } /* Check terminal case */ result = WRAP(op, dd, param); if (result != mtbdd_invalid) { /* Store in cache */ - cache_put3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, result); + if (cache_put3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, result)) { + sylvan_stats_count(MTBDD_UAPPLY_CACHEDPUT); + } + return result; } /* Get cofactors */ - mtbddnode_t ndd = GETNODE(dd); + mtbddnode_t ndd = MTBDD_GETNODE(dd); MTBDD ddlow = node_getlow(dd, ndd); MTBDD ddhigh = node_gethigh(dd, ndd); @@ -809,7 +893,10 @@ TASK_IMPL_3(MTBDD, mtbdd_uapply, MTBDD, dd, mtbdd_uapply_op, op, size_t, param) result = mtbdd_makenode(mtbddnode_getvariable(ndd), low, high); /* Store in cache */ - cache_put3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, result); + if (cache_put3(CACHE_MTBDD_UAPPLY, dd, (size_t)op, param, result)) { + sylvan_stats_count(MTBDD_UAPPLY_CACHEDPUT); + } + return result; } @@ -819,7 +906,7 @@ TASK_2(MTBDD, mtbdd_uop_times_uint, MTBDD, a, size_t, k) if (a == mtbdd_true) return mtbdd_true; // a != constant - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { if (mtbddnode_gettype(na) == 0) { @@ -834,6 +921,8 @@ TASK_2(MTBDD, mtbdd_uop_times_uint, MTBDD, a, size_t, k) uint32_t d = v; uint32_t c = gcd(d, (uint32_t)k); return mtbdd_fraction(n*(k/c), d/c); + } else { + assert(0); // failure } } @@ -846,7 +935,7 @@ TASK_2(MTBDD, mtbdd_uop_pow_uint, MTBDD, a, size_t, k) if (a == mtbdd_true) return mtbdd_true; // a != constant - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { if (mtbddnode_gettype(na) == 0) { @@ -858,6 +947,8 @@ TASK_2(MTBDD, mtbdd_uop_pow_uint, MTBDD, a, size_t, k) } else if (mtbddnode_gettype(na) == 2) { uint64_t v = mtbddnode_getvalue(na); return mtbdd_fraction(pow((int32_t)(v>>32), k), (uint32_t)v); + } else { + assert(0); // failure } } @@ -907,31 +998,40 @@ TASK_IMPL_3(MTBDD, mtbdd_abstract, MTBDD, a, MTBDD, v, mtbdd_abstract_op, op) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_ABSTRACT); + /* a != constant, v != constant */ - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { /* Count number of variables */ uint64_t k = 0; while (v != mtbdd_true) { k++; - v = node_gethigh(v, GETNODE(v)); + v = node_gethigh(v, MTBDD_GETNODE(v)); } /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, &result)) return result; + if (cache_get3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, &result)) { + sylvan_stats_count(MTBDD_ABSTRACT_CACHED); + return result; + } /* Compute result */ result = WRAP(op, a, a, k); /* Store in cache */ - cache_put3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, result); + if (cache_put3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, result)) { + sylvan_stats_count(MTBDD_ABSTRACT_CACHEDPUT); + } + return result; } /* Possibly skip k variables */ - mtbddnode_t nv = GETNODE(v); + mtbddnode_t nv = MTBDD_GETNODE(v); uint32_t var_a = mtbddnode_getvariable(na); uint32_t var_v = mtbddnode_getvariable(nv); uint64_t k = 0; @@ -939,13 +1039,16 @@ TASK_IMPL_3(MTBDD, mtbdd_abstract, MTBDD, a, MTBDD, v, mtbdd_abstract_op, op) k++; v = node_gethigh(v, nv); if (v == mtbdd_true) break; - nv = GETNODE(v); + nv = MTBDD_GETNODE(v); var_v = mtbddnode_getvariable(nv); } /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, &result)) return result; + if (cache_get3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, &result)) { + sylvan_stats_count(MTBDD_ABSTRACT_CACHED); + return result; + } /* Recursive */ if (v == mtbdd_true) { @@ -971,7 +1074,10 @@ TASK_IMPL_3(MTBDD, mtbdd_abstract, MTBDD, a, MTBDD, v, mtbdd_abstract_op, op) } /* Store in cache */ - cache_put3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, result); + if (cache_put3(CACHE_MTBDD_ABSTRACT, a, v | (k << 40), (size_t)op, result)) { + sylvan_stats_count(MTBDD_ABSTRACT_CACHEDPUT); + } + return result; } @@ -990,8 +1096,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_plus, MTBDD*, pa, MTBDD*, pb) if (a == mtbdd_true) return mtbdd_true; if (b == mtbdd_true) return mtbdd_true; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { uint64_t val_a = mtbddnode_getvalue(na); @@ -1018,6 +1124,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_plus, MTBDD*, pa, MTBDD*, pb) denom_a *= denom_b/c; // add return mtbdd_fraction(nom_a + nom_b, denom_a); + } else { + assert(0); // failure } } @@ -1040,8 +1148,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_minus, MTBDD*, pa, MTBDD*, pb) if (a == mtbdd_false) return mtbdd_negate(b); if (b == mtbdd_false) return a; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { uint64_t val_a = mtbddnode_getvalue(na); @@ -1067,6 +1175,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_minus, MTBDD*, pa, MTBDD*, pb) denom_a *= denom_b/c; // subtract return mtbdd_fraction(nom_a - nom_b, denom_a); + } else { + assert(0); // failure } } @@ -1088,8 +1198,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_times, MTBDD*, pa, MTBDD*, pb) if (a == mtbdd_true) return b; if (b == mtbdd_true) return a; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { uint64_t val_a = mtbddnode_getvalue(na); @@ -1128,6 +1238,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_times, MTBDD*, pa, MTBDD*, pb) nom_a *= (nom_b/c); denom_a *= (denom_b/d); return mtbdd_fraction(nom_a, denom_a); + } else { + assert(0); // failure } } @@ -1153,11 +1265,11 @@ TASK_IMPL_2(MTBDD, mtbdd_op_min, MTBDD*, pa, MTBDD*, pb) if (a == b) return a; // Special case where "false" indicates a partial function - if (a == mtbdd_false && b != mtbdd_false && mtbddnode_isleaf(GETNODE(b))) return b; - if (b == mtbdd_false && a != mtbdd_false && mtbddnode_isleaf(GETNODE(a))) return a; + if (a == mtbdd_false && b != mtbdd_false && mtbddnode_isleaf(MTBDD_GETNODE(b))) return b; + if (b == mtbdd_false && a != mtbdd_false && mtbddnode_isleaf(MTBDD_GETNODE(a))) return a; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { uint64_t val_a = mtbddnode_getvalue(na); @@ -1184,6 +1296,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_min, MTBDD*, pa, MTBDD*, pb) nom_b *= denom_a/c; // compute lowest return nom_a < nom_b ? a : b; + } else { + assert(0); // failure } } @@ -1210,8 +1324,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_max, MTBDD*, pa, MTBDD*, pb) if (b == mtbdd_false) return a; if (a == b) return a; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); if (mtbddnode_isleaf(na) && mtbddnode_isleaf(nb)) { uint64_t val_a = mtbddnode_getvalue(na); @@ -1238,6 +1352,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_max, MTBDD*, pa, MTBDD*, pb) nom_b *= denom_a/c; // compute highest return nom_a > nom_b ? a : b; + } else { + assert(0); // failure } } @@ -1249,13 +1365,43 @@ TASK_IMPL_2(MTBDD, mtbdd_op_max, MTBDD*, pa, MTBDD*, pb) return mtbdd_invalid; } +TASK_IMPL_2(MTBDD, mtbdd_op_cmpl, MTBDD, a, size_t, k) +{ + // if a is false, then it is a partial function. Keep partial! + if (a == mtbdd_false) return mtbdd_false; + + // a != constant + mtbddnode_t na = MTBDD_GETNODE(a); + + if (mtbddnode_isleaf(na)) { + if (mtbddnode_gettype(na) == 0) { + int64_t v = mtbdd_getint64(a); + if (v == 0) return mtbdd_int64(1); + else return mtbdd_int64(0); + } else if (mtbddnode_gettype(na) == 1) { + double d = mtbdd_getdouble(a); + if (d == 0.0) return mtbdd_double(1.0); + else return mtbdd_double(0.0); + } else if (mtbddnode_gettype(na) == 2) { + uint64_t v = mtbddnode_getvalue(na); + if (v == 1) return mtbdd_fraction(1, 1); + else return mtbdd_fraction(0, 1); + } else { + assert(0); // failure + } + } + + return mtbdd_invalid; + (void)k; // unused variable +} + TASK_IMPL_2(MTBDD, mtbdd_op_negate, MTBDD, a, size_t, k) { // if a is false, then it is a partial function. Keep partial! if (a == mtbdd_false) return mtbdd_false; // a != constant - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { if (mtbddnode_gettype(na) == 0) { @@ -1267,6 +1413,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_negate, MTBDD, a, size_t, k) } else if (mtbddnode_gettype(na) == 2) { uint64_t v = mtbddnode_getvalue(na); return mtbdd_fraction(-(int32_t)(v>>32), (uint32_t)v); + } else { + assert(0); // failure } } @@ -1276,7 +1424,7 @@ TASK_IMPL_2(MTBDD, mtbdd_op_negate, MTBDD, a, size_t, k) /** * Compute IF <f> THEN <g> ELSE <h>. - * <f> must be a Boolean MTBDD (or standard BDD). + * <f> must be a Boolean MTBDD (or standard SyBDD). */ TASK_IMPL_3(MTBDD, mtbdd_ite, MTBDD, f, MTBDD, g, MTBDD, h) { @@ -1292,16 +1440,22 @@ TASK_IMPL_3(MTBDD, mtbdd_ite, MTBDD, f, MTBDD, g, MTBDD, h) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_ITE); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_ITE, f, g, h, &result)) return result; + if (cache_get3(CACHE_MTBDD_ITE, f, g, h, &result)) { + sylvan_stats_count(MTBDD_ITE_CACHED); + return result; + } /* Get top variable */ int lg = mtbdd_isleaf(g); int lh = mtbdd_isleaf(h); - mtbddnode_t nf = GETNODE(f); - mtbddnode_t ng = lg ? 0 : GETNODE(g); - mtbddnode_t nh = lh ? 0 : GETNODE(h); + mtbddnode_t nf = MTBDD_GETNODE(f); + mtbddnode_t ng = lg ? 0 : MTBDD_GETNODE(g); + mtbddnode_t nh = lh ? 0 : MTBDD_GETNODE(h); uint32_t vf = mtbddnode_getvariable(nf); uint32_t vg = lg ? 0 : mtbddnode_getvariable(ng); uint32_t vh = lh ? 0 : mtbddnode_getvariable(nh); @@ -1326,7 +1480,10 @@ TASK_IMPL_3(MTBDD, mtbdd_ite, MTBDD, f, MTBDD, g, MTBDD, h) result = mtbdd_makenode(v, low, high); /* Store in cache */ - cache_put3(CACHE_MTBDD_ITE, f, g, h, result); + if (cache_put3(CACHE_MTBDD_ITE, f, g, h, result)) { + sylvan_stats_count(MTBDD_ITE_CACHEDPUT); + } + return result; } @@ -1340,7 +1497,7 @@ TASK_IMPL_2(MTBDD, mtbdd_op_threshold_double, MTBDD, a, size_t, svalue) if (a == mtbdd_true) return mtbdd_invalid; // a != constant - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { double value = *(double*)&svalue; @@ -1350,6 +1507,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_threshold_double, MTBDD, a, size_t, svalue) double d = (double)mtbdd_getnumer(a); d /= mtbdd_getdenom(a); return d >= value ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } @@ -1357,7 +1516,7 @@ TASK_IMPL_2(MTBDD, mtbdd_op_threshold_double, MTBDD, a, size_t, svalue) } /** - * Monad that converts double/fraction to a Boolean BDD, translate terminals > value to 1 and to 0 otherwise; + * Monad that converts double/fraction to a Boolean SyBDD, translate terminals > value to 1 and to 0 otherwise; */ TASK_IMPL_2(MTBDD, mtbdd_op_strict_threshold_double, MTBDD, a, size_t, svalue) { @@ -1366,7 +1525,7 @@ TASK_IMPL_2(MTBDD, mtbdd_op_strict_threshold_double, MTBDD, a, size_t, svalue) if (a == mtbdd_true) return mtbdd_invalid; // a != constant - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) { double value = *(double*)&svalue; @@ -1376,6 +1535,8 @@ TASK_IMPL_2(MTBDD, mtbdd_op_strict_threshold_double, MTBDD, a, size_t, svalue) double d = (double)mtbdd_getnumer(a); d /= mtbdd_getdenom(a); return d > value ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } @@ -1405,8 +1566,8 @@ TASK_4(MTBDD, mtbdd_equal_norm_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, sho if (a == mtbdd_false) return mtbdd_false; if (b == mtbdd_false) return mtbdd_false; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1428,9 +1589,15 @@ TASK_4(MTBDD, mtbdd_equal_norm_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, sho /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_EQUAL_NORM); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_EQUAL_NORM, a, b, svalue, &result)) return result; + if (cache_get3(CACHE_MTBDD_EQUAL_NORM, a, b, svalue, &result)) { + sylvan_stats_count(MTBDD_EQUAL_NORM_CACHED); + return result; + } /* Get top variable */ uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); @@ -1451,7 +1618,10 @@ TASK_4(MTBDD, mtbdd_equal_norm_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, sho if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_EQUAL_NORM, a, b, svalue, result); + if (cache_put3(CACHE_MTBDD_EQUAL_NORM, a, b, svalue, result)) { + sylvan_stats_count(MTBDD_EQUAL_NORM_CACHEDPUT); + } + return result; } @@ -1477,8 +1647,8 @@ TASK_4(MTBDD, mtbdd_equal_norm_rel_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, if (a == mtbdd_false) return mtbdd_false; if (b == mtbdd_false) return mtbdd_false; - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1495,9 +1665,15 @@ TASK_4(MTBDD, mtbdd_equal_norm_rel_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_EQUAL_NORM_REL); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_EQUAL_NORM_REL, a, b, svalue, &result)) return result; + if (cache_get3(CACHE_MTBDD_EQUAL_NORM_REL, a, b, svalue, &result)) { + sylvan_stats_count(MTBDD_EQUAL_NORM_REL_CACHED); + return result; + } /* Get top variable */ uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); @@ -1518,7 +1694,10 @@ TASK_4(MTBDD, mtbdd_equal_norm_rel_d2, MTBDD, a, MTBDD, b, size_t, svalue, int*, if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_EQUAL_NORM_REL, a, b, svalue, result); + if (cache_put3(CACHE_MTBDD_EQUAL_NORM_REL, a, b, svalue, result)) { + sylvan_stats_count(MTBDD_EQUAL_NORM_REL_CACHEDPUT); + } + return result; } @@ -1549,12 +1728,18 @@ TASK_3(MTBDD, mtbdd_leq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_LEQ); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_LEQ, a, b, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_LEQ, a, b, 0, &result)) { + sylvan_stats_count(MTBDD_LEQ_CACHED); + return result; + } - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1581,6 +1766,8 @@ TASK_3(MTBDD, mtbdd_leq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) nom_a *= db/c; nom_b *= da/c; result = nom_a <= nom_b ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } else { /* Get top variable */ @@ -1603,7 +1790,10 @@ TASK_3(MTBDD, mtbdd_leq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_LEQ, a, b, 0, result); + if (cache_put3(CACHE_MTBDD_LEQ, a, b, 0, result)) { + sylvan_stats_count(MTBDD_LEQ_CACHEDPUT); + } + return result; } @@ -1634,12 +1824,18 @@ TASK_3(MTBDD, mtbdd_less_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_LESS); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_LESS, a, b, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_LESS, a, b, 0, &result)) { + sylvan_stats_count(MTBDD_LESS_CACHED); + return result; + } - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1666,6 +1862,8 @@ TASK_3(MTBDD, mtbdd_less_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) nom_a *= db/c; nom_b *= da/c; result = nom_a < nom_b ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } else { /* Get top variable */ @@ -1688,7 +1886,10 @@ TASK_3(MTBDD, mtbdd_less_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_LESS, a, b, 0, result); + if (cache_put3(CACHE_MTBDD_LESS, a, b, 0, result)) { + sylvan_stats_count(MTBDD_LESS_CACHEDPUT); + } + return result; } @@ -1719,12 +1920,18 @@ TASK_3(MTBDD, mtbdd_geq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_GEQ); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_GEQ, a, b, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_GEQ, a, b, 0, &result)) { + sylvan_stats_count(MTBDD_GEQ_CACHED); + return result; + } - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1751,6 +1958,8 @@ TASK_3(MTBDD, mtbdd_geq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) nom_a *= db/c; nom_b *= da/c; result = nom_a >= nom_b ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } else { /* Get top variable */ @@ -1773,7 +1982,10 @@ TASK_3(MTBDD, mtbdd_geq_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_GEQ, a, b, 0, result); + if (cache_put3(CACHE_MTBDD_GEQ, a, b, 0, result)) { + sylvan_stats_count(MTBDD_GEQ_CACHEDPUT); + } + return result; } @@ -1804,12 +2016,18 @@ TASK_3(MTBDD, mtbdd_greater_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_GREATER); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_GREATER, a, b, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_GREATER, a, b, 0, &result)) { + sylvan_stats_count(MTBDD_GREATER_CACHED); + return result; + } - mtbddnode_t na = GETNODE(a); - mtbddnode_t nb = GETNODE(b); + mtbddnode_t na = MTBDD_GETNODE(a); + mtbddnode_t nb = MTBDD_GETNODE(b); int la = mtbddnode_isleaf(na); int lb = mtbddnode_isleaf(nb); @@ -1836,6 +2054,8 @@ TASK_3(MTBDD, mtbdd_greater_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) nom_a *= db/c; nom_b *= da/c; result = nom_a > nom_b ? mtbdd_true : mtbdd_false; + } else { + assert(0); // failure } } else { /* Get top variable */ @@ -1858,7 +2078,10 @@ TASK_3(MTBDD, mtbdd_greater_rec, MTBDD, a, MTBDD, b, int*, shortcircuit) if (result == mtbdd_false) *shortcircuit = 1; /* Store in cache */ - cache_put3(CACHE_MTBDD_GREATER, a, b, 0, result); + if (cache_put3(CACHE_MTBDD_GREATER, a, b, 0, result)) { + sylvan_stats_count(MTBDD_GREATER_CACHEDPUT); + } + return result; } @@ -1874,7 +2097,7 @@ TASK_IMPL_2(MTBDD, mtbdd_greater, MTBDD, a, MTBDD, b) * Multiply <a> and <b>, and abstract variables <vars> using summation. * This is similar to the "and_exists" operation in BDDs. */ -TASK_IMPL_3(MTBDD, mtbdd_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) +TASK_IMPL_3(MTBDD, mtbdd_and_abstract_plus, MTBDD, a, MTBDD, b, MTBDD, v) { /* Check terminal case */ if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(mtbdd_op_times)); @@ -1889,26 +2112,32 @@ TASK_IMPL_3(MTBDD, mtbdd_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS); + /* Check cache */ - if (cache_get3(CACHE_MTBDD_AND_EXISTS, a, b, v, &result)) return result; + if (cache_get3(CACHE_MTBDD_AND_ABSTRACT_PLUS, a, b, v, &result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS_CACHED); + return result; + } /* Now, v is not a constant, and either a or b is not a constant */ /* Get top variable */ int la = mtbdd_isleaf(a); int lb = mtbdd_isleaf(b); - mtbddnode_t na = la ? 0 : GETNODE(a); - mtbddnode_t nb = lb ? 0 : GETNODE(b); + mtbddnode_t na = la ? 0 : MTBDD_GETNODE(a); + mtbddnode_t nb = lb ? 0 : MTBDD_GETNODE(b); uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); uint32_t vb = lb ? 0xffffffff : mtbddnode_getvariable(nb); uint32_t var = va < vb ? va : vb; - mtbddnode_t nv = GETNODE(v); + mtbddnode_t nv = MTBDD_GETNODE(v); uint32_t vv = mtbddnode_getvariable(nv); if (vv < var) { /* Recursive, then abstract result */ - result = CALL(mtbdd_and_exists, a, b, node_gethigh(v, nv)); + result = CALL(mtbdd_and_abstract_plus, a, b, node_gethigh(v, nv)); mtbdd_refs_push(result); result = mtbdd_apply(result, result, TASK(mtbdd_op_plus)); mtbdd_refs_pop(1); @@ -1922,23 +2151,106 @@ TASK_IMPL_3(MTBDD, mtbdd_and_exists, MTBDD, a, MTBDD, b, MTBDD, v) if (vv == var) { /* Recursive, then abstract result */ - mtbdd_refs_spawn(SPAWN(mtbdd_and_exists, ahigh, bhigh, node_gethigh(v, nv))); - MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_exists, alow, blow, node_gethigh(v, nv))); - MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(mtbdd_and_exists))); + mtbdd_refs_spawn(SPAWN(mtbdd_and_abstract_plus, ahigh, bhigh, node_gethigh(v, nv))); + MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_abstract_plus, alow, blow, node_gethigh(v, nv))); + MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(mtbdd_and_abstract_plus))); result = CALL(mtbdd_apply, low, high, TASK(mtbdd_op_plus)); mtbdd_refs_pop(2); } else /* vv > v */ { /* Recursive, then create node */ - mtbdd_refs_spawn(SPAWN(mtbdd_and_exists, ahigh, bhigh, v)); - MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_exists, alow, blow, v)); - MTBDD high = mtbdd_refs_sync(SYNC(mtbdd_and_exists)); + mtbdd_refs_spawn(SPAWN(mtbdd_and_abstract_plus, ahigh, bhigh, v)); + MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_abstract_plus, alow, blow, v)); + MTBDD high = mtbdd_refs_sync(SYNC(mtbdd_and_abstract_plus)); mtbdd_refs_pop(1); result = mtbdd_makenode(var, low, high); } } /* Store in cache */ - cache_put3(CACHE_MTBDD_AND_EXISTS, a, b, v, result); + if (cache_put3(CACHE_MTBDD_AND_ABSTRACT_PLUS, a, b, v, result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_PLUS_CACHEDPUT); + } + + return result; +} + +/** + * Multiply <a> and <b>, and abstract variables <vars> by taking the maximum. + */ +TASK_IMPL_3(MTBDD, mtbdd_and_abstract_max, MTBDD, a, MTBDD, b, MTBDD, v) +{ + /* Check terminal case */ + if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(mtbdd_op_times)); + MTBDD result = CALL(mtbdd_op_times, &a, &b); + if (result != mtbdd_invalid) { + mtbdd_refs_push(result); + result = mtbdd_abstract(result, v, TASK(mtbdd_abstract_op_max)); + mtbdd_refs_pop(1); + return result; + } + + /* Now, v is not a constant, and either a or b is not a constant */ + + /* Get top variable */ + int la = mtbdd_isleaf(a); + int lb = mtbdd_isleaf(b); + mtbddnode_t na = la ? 0 : MTBDD_GETNODE(a); + mtbddnode_t nb = lb ? 0 : MTBDD_GETNODE(b); + uint32_t va = la ? 0xffffffff : mtbddnode_getvariable(na); + uint32_t vb = lb ? 0xffffffff : mtbddnode_getvariable(nb); + uint32_t var = va < vb ? va : vb; + + mtbddnode_t nv = MTBDD_GETNODE(v); + uint32_t vv = mtbddnode_getvariable(nv); + + while (vv < var) { + /* we can skip variables, because max(r,r) = r */ + v = node_gethigh(v, nv); + if (v == mtbdd_true) return mtbdd_apply(a, b, TASK(mtbdd_op_times)); + nv = MTBDD_GETNODE(v); + vv = mtbddnode_getvariable(nv); + } + + /* Maybe perform garbage collection */ + sylvan_gc_test(); + + /* Count operation */ + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX); + + /* Check cache */ + if (cache_get3(CACHE_MTBDD_AND_ABSTRACT_MAX, a, b, v, &result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX_CACHED); + return result; + } + + /* Get cofactors */ + MTBDD alow, ahigh, blow, bhigh; + alow = (!la && va == var) ? node_getlow(a, na) : a; + ahigh = (!la && va == var) ? node_gethigh(a, na) : a; + blow = (!lb && vb == var) ? node_getlow(b, nb) : b; + bhigh = (!lb && vb == var) ? node_gethigh(b, nb) : b; + + if (vv == var) { + /* Recursive, then abstract result */ + mtbdd_refs_spawn(SPAWN(mtbdd_and_abstract_max, ahigh, bhigh, node_gethigh(v, nv))); + MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_abstract_max, alow, blow, node_gethigh(v, nv))); + MTBDD high = mtbdd_refs_push(mtbdd_refs_sync(SYNC(mtbdd_and_abstract_max))); + result = CALL(mtbdd_apply, low, high, TASK(mtbdd_op_max)); + mtbdd_refs_pop(2); + } else /* vv > v */ { + /* Recursive, then create node */ + mtbdd_refs_spawn(SPAWN(mtbdd_and_abstract_max, ahigh, bhigh, v)); + MTBDD low = mtbdd_refs_push(CALL(mtbdd_and_abstract_max, alow, blow, v)); + MTBDD high = mtbdd_refs_sync(SYNC(mtbdd_and_abstract_max)); + mtbdd_refs_pop(1); + result = mtbdd_makenode(var, low, high); + } + + /* Store in cache */ + if (cache_put3(CACHE_MTBDD_AND_ABSTRACT_MAX, a, b, v, result)) { + sylvan_stats_count(MTBDD_AND_ABSTRACT_MAX_CACHEDPUT); + } + return result; } @@ -1953,22 +2265,31 @@ TASK_IMPL_1(MTBDD, mtbdd_support, MTBDD, dd) /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(BDD_SUPPORT); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_SUPPORT, dd, 0, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_SUPPORT, dd, 0, 0, &result)) { + sylvan_stats_count(BDD_SUPPORT_CACHED); + return result; + } /* Recursive calls */ - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); mtbdd_refs_spawn(SPAWN(mtbdd_support, node_getlow(dd, n))); MTBDD high = mtbdd_refs_push(CALL(mtbdd_support, node_gethigh(dd, n))); MTBDD low = mtbdd_refs_push(mtbdd_refs_sync(SYNC(mtbdd_support))); /* Compute result */ - result = mtbdd_makenode(mtbddnode_getvariable(n), mtbdd_false, mtbdd_times(low, high)); + result = mtbdd_makenode(mtbddnode_getvariable(n), mtbdd_false, sylvan_and(low, high)); mtbdd_refs_pop(2); /* Write to cache */ - cache_put3(CACHE_MTBDD_SUPPORT, dd, 0, 0, result); + if (cache_put3(CACHE_MTBDD_SUPPORT, dd, 0, 0, result)) { + sylvan_stats_count(BDD_SUPPORT_CACHEDPUT); + } + return result; } @@ -1983,7 +2304,7 @@ TASK_IMPL_2(MTBDD, mtbdd_compose, MTBDD, a, MTBDDMAP, map) if (mtbdd_isleaf(a) || mtbdd_map_isempty(map)) return a; /* Determine top level */ - mtbddnode_t n = GETNODE(a); + mtbddnode_t n = MTBDD_GETNODE(a); uint32_t v = mtbddnode_getvariable(n); /* Find in map */ @@ -1995,9 +2316,15 @@ TASK_IMPL_2(MTBDD, mtbdd_compose, MTBDD, a, MTBDDMAP, map) /* Perhaps execute garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_COMPOSE); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_COMPOSE, a, map, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_COMPOSE, a, map, 0, &result)) { + sylvan_stats_count(MTBDD_COMPOSE_CACHED); + return result; + } /* Recursive calls */ mtbdd_refs_spawn(SPAWN(mtbdd_compose, node_getlow(a, n), map)); @@ -2011,7 +2338,10 @@ TASK_IMPL_2(MTBDD, mtbdd_compose, MTBDD, a, MTBDDMAP, map) mtbdd_refs_pop(3); /* Store in cache */ - cache_put3(CACHE_MTBDD_COMPOSE, a, map, 0, result); + if (cache_put3(CACHE_MTBDD_COMPOSE, a, map, 0, result)) { + sylvan_stats_count(MTBDD_COMPOSE_CACHEDPUT); + } + return result; } @@ -2022,15 +2352,21 @@ TASK_IMPL_1(MTBDD, mtbdd_minimum, MTBDD, a) { /* Check terminal case */ if (a == mtbdd_false) return mtbdd_false; - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) return a; /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_MINIMUM); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_MINIMUM, a, 0, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_MINIMUM, a, 0, 0, &result)) { + sylvan_stats_count(MTBDD_MINIMUM_CACHED); + return result; + } /* Call recursive */ SPAWN(mtbdd_minimum, node_getlow(a, na)); @@ -2038,8 +2374,8 @@ TASK_IMPL_1(MTBDD, mtbdd_minimum, MTBDD, a) MTBDD low = SYNC(mtbdd_minimum); /* Determine lowest */ - mtbddnode_t nl = GETNODE(low); - mtbddnode_t nh = GETNODE(high); + mtbddnode_t nl = MTBDD_GETNODE(low); + mtbddnode_t nh = MTBDD_GETNODE(high); if (mtbddnode_gettype(nl) == 0 && mtbddnode_gettype(nh) == 0) { result = mtbdd_getint64(low) < mtbdd_getint64(high) ? low : high; @@ -2056,11 +2392,16 @@ TASK_IMPL_1(MTBDD, mtbdd_minimum, MTBDD, a) nom_l *= denom_h/c; nom_h *= denom_l/c; result = nom_l < nom_h ? low : high; + } else { + assert(0); // failure } /* Store in cache */ - cache_put3(CACHE_MTBDD_MINIMUM, a, 0, 0, result); - return result; + if (cache_put3(CACHE_MTBDD_MINIMUM, a, 0, 0, result)) { + sylvan_stats_count(MTBDD_MINIMUM_CACHEDPUT); + } + + return result; } /** @@ -2070,15 +2411,21 @@ TASK_IMPL_1(MTBDD, mtbdd_maximum, MTBDD, a) { /* Check terminal case */ if (a == mtbdd_false) return mtbdd_false; - mtbddnode_t na = GETNODE(a); + mtbddnode_t na = MTBDD_GETNODE(a); if (mtbddnode_isleaf(na)) return a; /* Maybe perform garbage collection */ sylvan_gc_test(); + /* Count operation */ + sylvan_stats_count(MTBDD_MAXIMUM); + /* Check cache */ MTBDD result; - if (cache_get3(CACHE_MTBDD_MAXIMUM, a, 0, 0, &result)) return result; + if (cache_get3(CACHE_MTBDD_MAXIMUM, a, 0, 0, &result)) { + sylvan_stats_count(MTBDD_MAXIMUM_CACHED); + return result; + } /* Call recursive */ SPAWN(mtbdd_maximum, node_getlow(a, na)); @@ -2086,8 +2433,8 @@ TASK_IMPL_1(MTBDD, mtbdd_maximum, MTBDD, a) MTBDD low = SYNC(mtbdd_maximum); /* Determine highest */ - mtbddnode_t nl = GETNODE(low); - mtbddnode_t nh = GETNODE(high); + mtbddnode_t nl = MTBDD_GETNODE(low); + mtbddnode_t nh = MTBDD_GETNODE(high); if (mtbddnode_gettype(nl) == 0 && mtbddnode_gettype(nh) == 0) { result = mtbdd_getint64(low) > mtbdd_getint64(high) ? low : high; @@ -2104,10 +2451,15 @@ TASK_IMPL_1(MTBDD, mtbdd_maximum, MTBDD, a) nom_l *= denom_h/c; nom_h *= denom_l/c; result = nom_l > nom_h ? low : high; + } else { + assert(0); // failure } /* Store in cache */ - cache_put3(CACHE_MTBDD_MAXIMUM, a, 0, 0, result); + if (cache_put3(CACHE_MTBDD_MAXIMUM, a, 0, 0, result)) { + sylvan_stats_count(MTBDD_MAXIMUM_CACHEDPUT); + } + return result; } @@ -2118,7 +2470,17 @@ TASK_IMPL_2(double, mtbdd_satcount, MTBDD, dd, size_t, nvars) { /* Trivial cases */ if (dd == mtbdd_false) return 0.0; - if (mtbdd_isleaf(dd)) return powl(2.0L, nvars); + + if (mtbdd_isleaf(dd)) { + // test if 0 + mtbddnode_t dd_node = MTBDD_GETNODE(dd); + if (dd != mtbdd_true) { + if (mtbddnode_gettype(dd_node) == 0 && mtbdd_getint64(dd) == 0) return 0.0; + else if (mtbddnode_gettype(dd_node) == 1 && mtbdd_getdouble(dd) == 0.0) return 0.0; + else if (mtbddnode_gettype(dd_node) == 2 && mtbdd_getvalue(dd) == 1) return 0.0; + } + return powl(2.0L, nvars); + } /* Perhaps execute garbage collection */ sylvan_gc_test(); @@ -2138,7 +2500,10 @@ TASK_IMPL_2(double, mtbdd_satcount, MTBDD, dd, size_t, nvars) double low = CALL(mtbdd_satcount, mtbdd_getlow(dd), nvars-1); hack.d = low + SYNC(mtbdd_satcount); - cache_put3(CACHE_BDD_SATCOUNT, dd, 0, nvars, hack.s); + if (cache_put3(CACHE_BDD_SATCOUNT, dd, 0, nvars, hack.s)) { + sylvan_stats_count(BDD_SATCOUNT_CACHEDPUT); + } + return hack.d; } @@ -2167,7 +2532,7 @@ mtbdd_enum_first(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb f variables = mtbdd_gethigh(variables); // check if MTBDD is on this variable - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); if (mtbddnode_getvariable(n) != v) { *arr = 2; return mtbdd_enum_first(dd, variables, arr+1, filter_cb); @@ -2207,7 +2572,7 @@ mtbdd_enum_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb fi if (*arr == 0) { // previous was low - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); MTBDD res = mtbdd_enum_next(node_getlow(dd, n), variables, arr+1, filter_cb); if (res != mtbdd_false) { return res; @@ -2223,7 +2588,7 @@ mtbdd_enum_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb fi } } else if (*arr == 1) { // previous was high - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); return mtbdd_enum_next(node_gethigh(dd, n), variables, arr+1, filter_cb); } else { // previous was either @@ -2232,13 +2597,229 @@ mtbdd_enum_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb fi } } +MTBDD +mtbdd_enum_all_first(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb) +{ + if (dd == mtbdd_false) { + // the leaf False is skipped + return mtbdd_false; + } else if (variables == mtbdd_true) { + // if this assertion fails, then <variables> is not the support of <dd>. + assert(mtbdd_isleaf(dd)); + // for _first, just return the leaf; there is nothing to set, though. + return dd; + } else if (mtbdd_isleaf(dd)) { + // a leaf for which the filter returns 0 is skipped + if (filter_cb != NULL && filter_cb(dd) == 0) return mtbdd_false; + // for all remaining variables, set to 0 + while (variables != mtbdd_true) { + *arr++ = 0; + variables = mtbdd_gethigh(variables); + } + return dd; + } else { + // get next variable from <variables> + mtbddnode_t nv = MTBDD_GETNODE(variables); + variables = node_gethigh(variables, nv); + + // get cofactors + mtbddnode_t ndd = MTBDD_GETNODE(dd); + MTBDD low, high; + if (mtbddnode_getvariable(ndd) == mtbddnode_getvariable(nv)) { + low = node_getlow(dd, ndd); + high = node_gethigh(dd, ndd); + } else { + low = high = dd; + } + + // first maybe follow low + MTBDD res = mtbdd_enum_all_first(low, variables, arr+1, filter_cb); + if (res != mtbdd_false) { + *arr = 0; + return res; + } + + // if not low, try following high + res = mtbdd_enum_all_first(high, variables, arr+1, filter_cb); + if (res != mtbdd_false) { + *arr = 1; + return res; + } + + // we've tried low and high, return false + return mtbdd_false; + } +} + +MTBDD +mtbdd_enum_all_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb) +{ + if (dd == mtbdd_false) { + // the leaf False is skipped + return mtbdd_false; + } else if (variables == mtbdd_true) { + // if this assertion fails, then <variables> is not the support of <dd>. + assert(mtbdd_isleaf(dd)); + // no next if there are no variables + return mtbdd_false; + } else { + // get next variable from <variables> + mtbddnode_t nv = MTBDD_GETNODE(variables); + variables = node_gethigh(variables, nv); + + // filter leaf (if leaf) or get cofactors (if not leaf) + mtbddnode_t ndd = MTBDD_GETNODE(dd); + MTBDD low, high; + if (mtbdd_isleaf(dd)) { + // a leaf for which the filter returns 0 is skipped + if (filter_cb != NULL && filter_cb(dd) == 0) return mtbdd_false; + low = high = dd; + } else { + // get cofactors + if (mtbddnode_getvariable(ndd) == mtbddnode_getvariable(nv)) { + low = node_getlow(dd, ndd); + high = node_gethigh(dd, ndd); + } else { + low = high = dd; + } + } + + // try recursive next first + if (*arr == 0) { + MTBDD res = mtbdd_enum_all_next(low, variables, arr+1, filter_cb); + if (res != mtbdd_false) return res; + } else if (*arr == 1) { + return mtbdd_enum_all_next(high, variables, arr+1, filter_cb); + // if *arr was 1 and _next returns False, return False + } else { + // the array is invalid... + assert(*arr == 0 || *arr == 1); + return mtbdd_invalid; // in Release mode, the assertion is empty code + } + + // previous was low, try following high + MTBDD res = mtbdd_enum_all_first(high, variables, arr+1, filter_cb); + if (res == mtbdd_false) return mtbdd_false; + + // succesful, set arr + *arr = 1; + return res; + } +} + +/** + * Given a MTBDD <dd>, call <cb> with context <context> for every unique path in <dd> ending in leaf <leaf>. + * + * Usage: + * VOID_TASK_3(cb, mtbdd_enum_trace_t, trace, MTBDD, leaf, void*, context) { ... do something ... } + * mtbdd_enum_par(dd, cb, context); + */ +VOID_TASK_4(mtbdd_enum_par_do, MTBDD, dd, mtbdd_enum_cb, cb, void*, context, mtbdd_enum_trace_t, trace) +{ + if (mtbdd_isleaf(dd)) { + WRAP(cb, trace, dd, context); + return; + } + + mtbddnode_t ndd = MTBDD_GETNODE(dd); + uint32_t var = mtbddnode_getvariable(ndd); + + struct mtbdd_enum_trace t0 = (struct mtbdd_enum_trace){trace, var, 0}; + struct mtbdd_enum_trace t1 = (struct mtbdd_enum_trace){trace, var, 1}; + SPAWN(mtbdd_enum_par_do, node_getlow(dd, ndd), cb, context, &t0); + CALL(mtbdd_enum_par_do, node_gethigh(dd, ndd), cb, context, &t1); + SYNC(mtbdd_enum_par_do); +} + +VOID_TASK_IMPL_3(mtbdd_enum_par, MTBDD, dd, mtbdd_enum_cb, cb, void*, context) +{ + CALL(mtbdd_enum_par_do, dd, cb, context, NULL); +} + +/** + * Function composition after partial evaluation. + * + * Given a function F(X) = f, compute the composition F'(X) = g(f) for every assignment to X. + * All variables X in <vars> must appear before all variables in f and g(f). + * + * Usage: + * TASK_2(MTBDD, g, MTBDD, in) { ... return g of <in> ... } + * MTBDD x_vars = ...; // the cube of variables x + * MTBDD result = mtbdd_eval_compose(dd, x_vars, TASK(g)); + */ +TASK_IMPL_3(MTBDD, mtbdd_eval_compose, MTBDD, dd, MTBDD, vars, mtbdd_eval_compose_cb, cb) +{ + /* Maybe perform garbage collection */ + sylvan_gc_test(); + + /* Count operation */ + sylvan_stats_count(MTBDD_EVAL_COMPOSE); + + /* Check cache */ + MTBDD result; + if (cache_get3(CACHE_MTBDD_EVAL_COMPOSE, dd, vars, (size_t)cb, &result)) { + sylvan_stats_count(MTBDD_EVAL_COMPOSE_CACHED); + return result; + } + + if (mtbdd_isleaf(dd) || vars == mtbdd_true) { + /* Apply */ + result = WRAP(cb, dd); + } else { + /* Get top variable in dd */ + mtbddnode_t ndd = MTBDD_GETNODE(dd); + uint32_t var = mtbddnode_getvariable(ndd); + + /* Check if <var> is in <vars> */ + mtbddnode_t nvars = MTBDD_GETNODE(vars); + uint32_t vv = mtbddnode_getvariable(nvars); + + /* Search/forward <vars> */ + MTBDD _vars = vars; + while (vv < var) { + _vars = node_gethigh(_vars, nvars); + if (_vars == mtbdd_true) break; + nvars = MTBDD_GETNODE(_vars); + vv = mtbddnode_getvariable(nvars); + } + + if (_vars == mtbdd_true) { + /* Apply */ + result = WRAP(cb, dd); + } else { + /* If this fails, then there are variables in f/g BEFORE vars, which breaks functionality. */ + assert(vv == var); + if (vv != var) return mtbdd_invalid; + + /* Get cofactors */ + MTBDD ddlow = node_getlow(dd, ndd); + MTBDD ddhigh = node_gethigh(dd, ndd); + + /* Recursive */ + _vars = node_gethigh(_vars, nvars); + mtbdd_refs_spawn(SPAWN(mtbdd_eval_compose, ddhigh, _vars, cb)); + MTBDD low = mtbdd_refs_push(CALL(mtbdd_eval_compose, ddlow, _vars, cb)); + MTBDD high = mtbdd_refs_sync(SYNC(mtbdd_eval_compose)); + mtbdd_refs_pop(1); + result = mtbdd_makenode(var, low, high); + } + } + + /* Store in cache */ + if (cache_put3(CACHE_MTBDD_EVAL_COMPOSE, dd, vars, (size_t)cb, result)) { + sylvan_stats_count(MTBDD_EVAL_COMPOSE_CACHEDPUT); + } + + return result; +} + /** * Helper function for recursive unmarking */ static void mtbdd_unmark_rec(MTBDD mtbdd) { - mtbddnode_t n = GETNODE(mtbdd); + mtbddnode_t n = MTBDD_GETNODE(mtbdd); if (!mtbddnode_getmark(n)) return; mtbddnode_setmark(n, 0); if (mtbddnode_isleaf(n)) return; @@ -2255,7 +2836,7 @@ mtbdd_leafcount_mark(MTBDD mtbdd) { if (mtbdd == mtbdd_true) return 0; // do not count true/false leaf if (mtbdd == mtbdd_false) return 0; // do not count true/false leaf - mtbddnode_t n = GETNODE(mtbdd); + mtbddnode_t n = MTBDD_GETNODE(mtbdd); if (mtbddnode_getmark(n)) return 0; mtbddnode_setmark(n, 1); if (mtbddnode_isleaf(n)) return 1; // count leaf as 1 @@ -2263,10 +2844,11 @@ mtbdd_leafcount_mark(MTBDD mtbdd) } size_t -mtbdd_leafcount(MTBDD mtbdd) +mtbdd_leafcount_more(const MTBDD *mtbdds, size_t count) { - size_t result = mtbdd_leafcount_mark(mtbdd); - mtbdd_unmark_rec(mtbdd); + size_t result = 0, i; + for (i=0; i<count; i++) result += mtbdd_leafcount_mark(mtbdds[i]); + for (i=0; i<count; i++) mtbdd_unmark_rec(mtbdds[i]); return result; } @@ -2277,9 +2859,7 @@ mtbdd_leafcount(MTBDD mtbdd) static size_t mtbdd_nodecount_mark(MTBDD mtbdd) { - if (mtbdd == mtbdd_true) return 0; // do not count true/false leaf - if (mtbdd == mtbdd_false) return 0; // do not count true/false leaf - mtbddnode_t n = GETNODE(mtbdd); + mtbddnode_t n = MTBDD_GETNODE(mtbdd); if (mtbddnode_getmark(n)) return 0; mtbddnode_setmark(n, 1); if (mtbddnode_isleaf(n)) return 1; // count leaf as 1 @@ -2287,10 +2867,11 @@ mtbdd_nodecount_mark(MTBDD mtbdd) } size_t -mtbdd_nodecount(MTBDD mtbdd) +mtbdd_nodecount_more(const MTBDD *mtbdds, size_t count) { - size_t result = mtbdd_nodecount_mark(mtbdd); - mtbdd_unmark_rec(mtbdd); + size_t result = 0, i; + for (i=0; i<count; i++) result += mtbdd_nodecount_mark(mtbdds[i]); + for (i=0; i<count; i++) mtbdd_unmark_rec(mtbdds[i]); return result; } @@ -2310,7 +2891,7 @@ TASK_2(int, mtbdd_test_isvalid_rec, MTBDD, dd, uint32_t, parent_var) if (marked == 0) return 0; // check if leaf - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); if (mtbddnode_isleaf(n)) return 1; // we're fine // check variable order @@ -2321,6 +2902,7 @@ TASK_2(int, mtbdd_test_isvalid_rec, MTBDD, dd, uint32_t, parent_var) // check cache uint64_t result; if (cache_get3(CACHE_BDD_ISBDD, dd, 0, 0, &result)) { + sylvan_stats_count(BDD_ISBDD_CACHED); return result; } @@ -2330,7 +2912,10 @@ TASK_2(int, mtbdd_test_isvalid_rec, MTBDD, dd, uint32_t, parent_var) if (!SYNC(mtbdd_test_isvalid_rec)) result = 0; // put in cache and return result - cache_put3(CACHE_BDD_ISBDD, dd, 0, 0, result); + if (cache_put3(CACHE_BDD_ISBDD, dd, 0, 0, result)) { + sylvan_stats_count(BDD_ISBDD_CACHEDPUT); + } + return result; } @@ -2350,7 +2935,7 @@ TASK_IMPL_1(int, mtbdd_test_isvalid, MTBDD, dd) if (marked == 0) return 0; // check if leaf - mtbddnode_t n = GETNODE(dd); + mtbddnode_t n = MTBDD_GETNODE(dd); if (mtbddnode_isleaf(n)) return 1; // we're fine // check recursively @@ -2361,44 +2946,68 @@ TASK_IMPL_1(int, mtbdd_test_isvalid, MTBDD, dd) return result; } +/** + * Write a text representation of a leaf to the given file. + */ +void +mtbdd_fprint_leaf(FILE *out, MTBDD leaf) +{ + char buf[64]; + char *ptr = mtbdd_leaf_to_str(leaf, buf, 64); + if (ptr != NULL) { + fputs(ptr, out); + if (ptr != buf) free(ptr); + } +} + +/** + * Write a text representation of a leaf to stdout. + */ +void +mtbdd_print_leaf(MTBDD leaf) +{ + mtbdd_fprint_leaf(stdout, leaf); +} + +/** + * Obtain the textual representation of a leaf. + * The returned result is either equal to the given <buf> (if the results fits) + * or to a newly allocated array (with malloc). + */ +char * +mtbdd_leaf_to_str(MTBDD leaf, char *buf, size_t buflen) +{ + mtbddnode_t n = MTBDD_GETNODE(leaf); + uint32_t type = mtbddnode_gettype(n); + uint64_t value = mtbddnode_getvalue(n); + int complement = MTBDD_HASMARK(leaf) ? 1 : 0; + + return sylvan_mt_to_str(complement, type, value, buf, buflen); +} + /** * Export to .dot file */ static void -mtbdd_fprintdot_rec(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb) +mtbdd_fprintdot_rec(FILE *out, MTBDD mtbdd) { - mtbddnode_t n = GETNODE(mtbdd); // also works for mtbdd_false + mtbddnode_t n = MTBDD_GETNODE(mtbdd); // also works for mtbdd_false if (mtbddnode_getmark(n)) return; mtbddnode_setmark(n, 1); if (mtbdd == mtbdd_true || mtbdd == mtbdd_false) { fprintf(out, "0 [shape=box, style=filled, label=\"F\"];\n"); } else if (mtbddnode_isleaf(n)) { - uint32_t type = mtbddnode_gettype(n); - uint64_t value = mtbddnode_getvalue(n); fprintf(out, "%" PRIu64 " [shape=box, style=filled, label=\"", MTBDD_STRIPMARK(mtbdd)); - switch (type) { - case 0: - fprintf(out, "%" PRIu64, value); - break; - case 1: - fprintf(out, "%f", *(double*)&value); - break; - case 2: - fprintf(out, "%u/%u", (uint32_t)(value>>32), (uint32_t)value); - break; - default: - cb(out, type, value); - break; - } + mtbdd_fprint_leaf(out, mtbdd); fprintf(out, "\"];\n"); } else { fprintf(out, "%" PRIu64 " [label=\"%" PRIu32 "\"];\n", MTBDD_STRIPMARK(mtbdd), mtbddnode_getvariable(n)); - mtbdd_fprintdot_rec(out, mtbddnode_getlow(n), cb); - mtbdd_fprintdot_rec(out, mtbddnode_gethigh(n), cb); + mtbdd_fprintdot_rec(out, mtbddnode_getlow(n)); + mtbdd_fprintdot_rec(out, mtbddnode_gethigh(n)); fprintf(out, "%" PRIu64 " -> %" PRIu64 " [style=dashed];\n", MTBDD_STRIPMARK(mtbdd), mtbddnode_getlow(n)); @@ -2409,7 +3018,7 @@ mtbdd_fprintdot_rec(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb) } void -mtbdd_fprintdot(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb) +mtbdd_fprintdot(FILE *out, MTBDD mtbdd) { fprintf(out, "digraph \"DD\" {\n"); fprintf(out, "graph [dpi = 300];\n"); @@ -2419,12 +3028,509 @@ mtbdd_fprintdot(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb) fprintf(out, "root -> %" PRIu64 " [style=solid dir=both arrowtail=%s];\n", MTBDD_STRIPMARK(mtbdd), MTBDD_HASMARK(mtbdd) ? "dot" : "none"); - mtbdd_fprintdot_rec(out, mtbdd, cb); + mtbdd_fprintdot_rec(out, mtbdd); + mtbdd_unmark_rec(mtbdd); + + fprintf(out, "}\n"); +} + +/** + * Export to .dot file, but do not display complement edges. Expand instead. + */ + +static void +mtbdd_fprintdot_nc_rec(FILE *out, MTBDD mtbdd) +{ + mtbddnode_t n = MTBDD_GETNODE(mtbdd); // also works for mtbdd_false + if (mtbddnode_getmark(n)) return; + mtbddnode_setmark(n, 1); + + if (mtbdd == mtbdd_true) { + fprintf(out, "%" PRIu64 " [shape=box, style=filled, label=\"T\"];\n", mtbdd); + } else if (mtbdd == mtbdd_false) { + fprintf(out, "0 [shape=box, style=filled, label=\"F\"];\n"); + } else if (mtbddnode_isleaf(n)) { + fprintf(out, "%" PRIu64 " [shape=box, style=filled, label=\"", mtbdd); + mtbdd_fprint_leaf(out, mtbdd); + fprintf(out, "\"];\n"); + } else { + fprintf(out, "%" PRIu64 " [label=\"%" PRIu32 "\"];\n", mtbdd, mtbddnode_getvariable(n)); + + mtbdd_fprintdot_nc_rec(out, mtbddnode_getlow(n)); + mtbdd_fprintdot_nc_rec(out, mtbddnode_gethigh(n)); + + fprintf(out, "%" PRIu64 " -> %" PRIu64 " [style=dashed];\n", mtbdd, node_getlow(mtbdd, n)); + fprintf(out, "%" PRIu64 " -> %" PRIu64 " [style=solid];\n", mtbdd, node_gethigh(mtbdd, n)); + } +} + +void +mtbdd_fprintdot_nc(FILE *out, MTBDD mtbdd) +{ + fprintf(out, "digraph \"DD\" {\n"); + fprintf(out, "graph [dpi = 300];\n"); + fprintf(out, "center = true;\n"); + fprintf(out, "edge [dir = forward];\n"); + fprintf(out, "root [style=invis];\n"); + fprintf(out, "root -> %" PRIu64 " [style=solid];\n", mtbdd); + + mtbdd_fprintdot_nc_rec(out, mtbdd); mtbdd_unmark_rec(mtbdd); fprintf(out, "}\n"); } +/** + * Generate SHA2 structural hashes. + * Hashes are independent of location. + * Mainly useful for debugging purposes. + */ +static void +mtbdd_sha2_rec(MTBDD dd, SHA256_CTX *ctx) +{ + if (dd == mtbdd_true || dd == mtbdd_false) { + SHA256_Update(ctx, (void*)&dd, sizeof(MTBDD)); + return; + } + + mtbddnode_t node = MTBDD_GETNODE(dd); + if (mtbddnode_getmark(node) == 0) { + mtbddnode_setmark(node, 1); + if (mtbddnode_isleaf(node)) { + uint32_t type = mtbddnode_gettype(node); + SHA256_Update(ctx, (void*)&type, sizeof(uint32_t)); + uint64_t value = mtbddnode_getvalue(node); + value = sylvan_mt_hash(type, value, value); + SHA256_Update(ctx, (void*)&value, sizeof(uint64_t)); + } else { + uint32_t level = mtbddnode_getvariable(node); + if (MTBDD_STRIPMARK(mtbddnode_gethigh(node))) level |= 0x80000000; + SHA256_Update(ctx, (void*)&level, sizeof(uint32_t)); + mtbdd_sha2_rec(mtbddnode_gethigh(node), ctx); + mtbdd_sha2_rec(mtbddnode_getlow(node), ctx); + } + } +} + +void +mtbdd_printsha(MTBDD dd) +{ + mtbdd_fprintsha(stdout, dd); +} + +void +mtbdd_fprintsha(FILE *f, MTBDD dd) +{ + char buf[80]; + mtbdd_getsha(dd, buf); + fprintf(f, "%s", buf); +} + +void +mtbdd_getsha(MTBDD dd, char *target) +{ + SHA256_CTX ctx; + SHA256_Init(&ctx); + mtbdd_sha2_rec(dd, &ctx); + if (dd != mtbdd_true && dd != mtbdd_false) mtbdd_unmark_rec(dd); + SHA256_End(&ctx, target); +} + +/** + * Implementation of visitor operations + */ + +VOID_TASK_IMPL_4(mtbdd_visit_seq, MTBDD, dd, mtbdd_visit_pre_cb, pre_cb, mtbdd_visit_post_cb, post_cb, void*, ctx) +{ + int children = 1; + if (pre_cb != NULL) children = WRAP(pre_cb, dd, ctx); + if (children && !mtbdd_isleaf(dd)) { + CALL(mtbdd_visit_seq, mtbdd_getlow(dd), pre_cb, post_cb, ctx); + CALL(mtbdd_visit_seq, mtbdd_gethigh(dd), pre_cb, post_cb, ctx); + } + if (post_cb != NULL) WRAP(post_cb, dd, ctx); +} + +VOID_TASK_IMPL_4(mtbdd_visit_par, MTBDD, dd, mtbdd_visit_pre_cb, pre_cb, mtbdd_visit_post_cb, post_cb, void*, ctx) +{ + int children = 1; + if (pre_cb != NULL) children = WRAP(pre_cb, dd, ctx); + if (children && !mtbdd_isleaf(dd)) { + SPAWN(mtbdd_visit_par, mtbdd_getlow(dd), pre_cb, post_cb, ctx); + CALL(mtbdd_visit_par, mtbdd_gethigh(dd), pre_cb, post_cb, ctx); + SYNC(mtbdd_visit_par); + } + if (post_cb != NULL) WRAP(post_cb, dd, ctx); +} + +/** + * Writing MTBDD files using a skiplist as a backend + */ + +TASK_2(int, mtbdd_writer_add_visitor_pre, MTBDD, dd, sylvan_skiplist_t, sl) +{ + if (mtbdd_isleaf(dd)) return 0; + return sylvan_skiplist_get(sl, MTBDD_STRIPMARK(dd)) == 0 ? 1 : 0; +} + +VOID_TASK_2(mtbdd_writer_add_visitor_post, MTBDD, dd, sylvan_skiplist_t, sl) +{ + if (dd == mtbdd_true || dd == mtbdd_false) return; + sylvan_skiplist_assign_next(sl, MTBDD_STRIPMARK(dd)); +} + +sylvan_skiplist_t +mtbdd_writer_start() +{ + size_t sl_size = nodes->table_size > 0x7fffffff ? 0x7fffffff : nodes->table_size; + return sylvan_skiplist_alloc(sl_size); +} + +VOID_TASK_IMPL_2(mtbdd_writer_add, sylvan_skiplist_t, sl, MTBDD, dd) +{ + mtbdd_visit_seq(dd, (mtbdd_visit_pre_cb)TASK(mtbdd_writer_add_visitor_pre), (mtbdd_visit_post_cb)TASK(mtbdd_writer_add_visitor_post), (void*)sl); +} + +void +mtbdd_writer_writebinary(FILE *out, sylvan_skiplist_t sl) +{ + size_t nodecount = sylvan_skiplist_count(sl); + fwrite(&nodecount, sizeof(size_t), 1, out); + for (size_t i=1; i<=nodecount; i++) { + MTBDD dd = sylvan_skiplist_getr(sl, i); + + mtbddnode_t n = MTBDD_GETNODE(dd); + if (mtbddnode_isleaf(n)) { + /* write leaf */ + fwrite(n, sizeof(struct mtbddnode), 1, out); + uint32_t type = mtbddnode_gettype(n); + uint64_t value = mtbddnode_getvalue(n); + sylvan_mt_write_binary(type, value, out); + } else { + struct mtbddnode node; + MTBDD low = sylvan_skiplist_get(sl, mtbddnode_getlow(n)); + MTBDD high = mtbddnode_gethigh(n); + high = MTBDD_TRANSFERMARK(high, sylvan_skiplist_get(sl, MTBDD_STRIPMARK(high))); + mtbddnode_makenode(&node, mtbddnode_getvariable(n), low, high); + fwrite(&node, sizeof(struct mtbddnode), 1, out); + } + } +} + +uint64_t +mtbdd_writer_get(sylvan_skiplist_t sl, MTBDD dd) +{ + return MTBDD_TRANSFERMARK(dd, sylvan_skiplist_get(sl, MTBDD_STRIPMARK(dd))); +} + +void +mtbdd_writer_end(sylvan_skiplist_t sl) +{ + sylvan_skiplist_free(sl); +} + +VOID_TASK_IMPL_3(mtbdd_writer_tobinary, FILE *, out, MTBDD *, dds, int, count) +{ + sylvan_skiplist_t sl = mtbdd_writer_start(); + + for (int i=0; i<count; i++) { + CALL(mtbdd_writer_add, sl, dds[i]); + } + + mtbdd_writer_writebinary(out, sl); + + fwrite(&count, sizeof(int), 1, out); + + for (int i=0; i<count; i++) { + uint64_t v = mtbdd_writer_get(sl, dds[i]); + fwrite(&v, sizeof(uint64_t), 1, out); + } + + mtbdd_writer_end(sl); +} + +void +mtbdd_writer_writetext(FILE *out, sylvan_skiplist_t sl) +{ + fprintf(out, "[\n"); + size_t nodecount = sylvan_skiplist_count(sl); + for (size_t i=1; i<=nodecount; i++) { + MTBDD dd = sylvan_skiplist_getr(sl, i); + + mtbddnode_t n = MTBDD_GETNODE(dd); + if (mtbddnode_isleaf(n)) { + /* serialize leaf, does not support customs yet */ + fprintf(out, " leaf(%zu,%u,\"", i, mtbddnode_gettype(n)); + mtbdd_fprint_leaf(out, MTBDD_STRIPMARK(dd)); + fprintf(out, "\"),\n"); + } else { + MTBDD low = sylvan_skiplist_get(sl, mtbddnode_getlow(n)); + MTBDD high = mtbddnode_gethigh(n); + high = MTBDD_TRANSFERMARK(high, sylvan_skiplist_get(sl, MTBDD_STRIPMARK(high))); + fprintf(out, " node(%zu,%u,%zu,%s%zu),\n", i, mtbddnode_getvariable(n), (size_t)low, MTBDD_HASMARK(high)?"~":"", (size_t)MTBDD_STRIPMARK(high)); + } + } + + fprintf(out, "]"); +} + +VOID_TASK_IMPL_3(mtbdd_writer_totext, FILE *, out, MTBDD *, dds, int, count) +{ + sylvan_skiplist_t sl = mtbdd_writer_start(); + + for (int i=0; i<count; i++) { + CALL(mtbdd_writer_add, sl, dds[i]); + } + + mtbdd_writer_writetext(out, sl); + + fprintf(out, ",["); + + for (int i=0; i<count; i++) { + uint64_t v = mtbdd_writer_get(sl, dds[i]); + fprintf(out, "%s%zu,", MTBDD_HASMARK(v)?"~":"", (size_t)MTBDD_STRIPMARK(v)); + } + + fprintf(out, "]\n"); + + mtbdd_writer_end(sl); +} + +/** + * Reading a file earlier written with mtbdd_writer_writebinary + * Returns an array with the conversion from stored identifier to MTBDD + * This array is allocated with malloc and must be freed afterwards. + * This method does not support custom leaves. + */ +TASK_IMPL_1(uint64_t*, mtbdd_reader_readbinary, FILE*, in) +{ + size_t nodecount; + if (fread(&nodecount, sizeof(size_t), 1, in) != 1) { + return NULL; + } + + uint64_t *arr = malloc(sizeof(uint64_t)*(nodecount+1)); + arr[0] = 0; + for (size_t i=1; i<=nodecount; i++) { + struct mtbddnode node; + if (fread(&node, sizeof(struct mtbddnode), 1, in) != 1) { + free(arr); + return NULL; + } + + if (mtbddnode_isleaf(&node)) { + /* serialize leaf */ + uint32_t type = mtbddnode_gettype(&node); + uint64_t value = mtbddnode_getvalue(&node); + sylvan_mt_read_binary(type, &value, in); + arr[i] = mtbdd_makeleaf(type, value); + } else { + MTBDD low = arr[mtbddnode_getlow(&node)]; + MTBDD high = mtbddnode_gethigh(&node); + high = MTBDD_TRANSFERMARK(high, arr[MTBDD_STRIPMARK(high)]); + arr[i] = mtbdd_makenode(mtbddnode_getvariable(&node), low, high); + } + } + + return arr; +} + +/** + * Retrieve the MTBDD of the given stored identifier. + */ +MTBDD +mtbdd_reader_get(uint64_t* arr, uint64_t identifier) +{ + return MTBDD_TRANSFERMARK(identifier, arr[MTBDD_STRIPMARK(identifier)]); +} + +/** + * Free the allocated translation array + */ +void +mtbdd_reader_end(uint64_t *arr) +{ + free(arr); +} + +/** + * Reading a file earlier written with mtbdd_writer_tobinary + */ +TASK_IMPL_3(int, mtbdd_reader_frombinary, FILE*, in, MTBDD*, dds, int, count) +{ + uint64_t *arr = CALL(mtbdd_reader_readbinary, in); + if (arr == NULL) return -1; + + /* Read stored count */ + int actual_count; + if (fread(&actual_count, sizeof(int), 1, in) != 1) { + mtbdd_reader_end(arr); + return -1; + } + + /* If actual count does not agree with given count, abort */ + if (actual_count != count) { + mtbdd_reader_end(arr); + return -1; + } + + /* Read every stored identifier, and translate to MTBDD */ + for (int i=0; i<count; i++) { + uint64_t v; + if (fread(&v, sizeof(uint64_t), 1, in) != 1) { + mtbdd_reader_end(arr); + return -1; + } + dds[i] = mtbdd_reader_get(arr, v); + } + + mtbdd_reader_end(arr); + return 0; +} + +/** + * Implementation of variable sets, i.e., cubes of (positive) variables. + */ + +/** + * Create a set of variables, represented as the conjunction of (positive) variables. + */ +MTBDD +mtbdd_set_from_array(uint32_t* arr, size_t length) +{ + if (length == 0) return mtbdd_true; + else if (length == 1) return mtbdd_makenode(*arr, mtbdd_false, mtbdd_true); + else return mtbdd_set_add(mtbdd_fromarray(arr+1, length-1), *arr); +} + +/** + * Write all variables in a variable set to the given array. + * The array must be sufficiently large. + */ +void +mtbdd_set_to_array(MTBDD set, uint32_t *arr) +{ + while (set != mtbdd_true) { + mtbddnode_t n = MTBDD_GETNODE(set); + *arr++ = mtbddnode_getvariable(n); + set = node_gethigh(set, n); + } +} + +/** + * Add the variable <var> to <set>. + */ +MTBDD +mtbdd_set_add(MTBDD set, uint32_t var) +{ + if (set == mtbdd_true) return mtbdd_makenode(var, mtbdd_false, mtbdd_true); + + mtbddnode_t set_node = MTBDD_GETNODE(set); + uint32_t set_var = mtbddnode_getvariable(set_node); + if (var < set_var) return mtbdd_makenode(var, mtbdd_false, set); + else if (set_var == var) return set; + else { + MTBDD sub = mtbddnode_followhigh(set, set_node); + MTBDD res = mtbdd_set_add(sub, var); + res = sub == res ? set : mtbdd_makenode(set_var, mtbdd_false, res); + return res; + } +} + +/** + * Remove the variable <var> from <set>. + */ +MTBDD +mtbdd_set_remove(MTBDD set, uint32_t var) +{ + if (set == mtbdd_true) return mtbdd_true; + + mtbddnode_t set_node = MTBDD_GETNODE(set); + uint32_t set_var = mtbddnode_getvariable(set_node); + if (var < set_var) return set; + else if (set_var == var) return mtbddnode_followhigh(set, set_node); + else { + MTBDD sub = mtbddnode_followhigh(set, set_node); + MTBDD res = mtbdd_set_remove(sub, var); + res = sub == res ? set : mtbdd_makenode(set_var, mtbdd_false, res); + return res; + } +} + +/** + * Remove variables in <set2> from <set1>. + */ +TASK_IMPL_2(MTBDD, mtbdd_set_minus, MTBDD, set1, MTBDD, set2) +{ + if (set1 == mtbdd_true) return mtbdd_true; + if (set2 == mtbdd_true) return set1; + if (set1 == set2) return mtbdd_true; + + mtbddnode_t set1_node = MTBDD_GETNODE(set1); + mtbddnode_t set2_node = MTBDD_GETNODE(set2); + uint32_t set1_var = mtbddnode_getvariable(set1_node); + uint32_t set2_var = mtbddnode_getvariable(set2_node); + + if (set1_var == set2_var) { + return mtbdd_set_minus(mtbddnode_followhigh(set1, set1_node), mtbddnode_followhigh(set2, set2_node)); + } + + if (set1_var > set2_var) { + return mtbdd_set_minus(set1, mtbddnode_followhigh(set2, set2_node)); + } + + /* set1_var < set2_var */ + MTBDD sub = mtbddnode_followhigh(set1, set1_node); + MTBDD res = mtbdd_set_minus(sub, set2); + return res == sub ? set1 : mtbdd_makenode(set1_var, mtbdd_false, res); +} + +/** + * Return 1 if <set> contains <var>, 0 otherwise. + */ +int +mtbdd_set_contains(MTBDD set, uint32_t var) +{ + while (set != mtbdd_true) { + mtbddnode_t n = MTBDD_GETNODE(set); + uint32_t v = mtbddnode_getvariable(n); + if (v == var) return 1; + if (v > var) return 0; + set = node_gethigh(set, n); + } + return 0; +} + +/** + * Compute the number of variables in a given set of variables. + */ +size_t +mtbdd_set_count(MTBDD set) +{ + size_t result = 0; + while (set != mtbdd_true) { + result++; + set = mtbdd_gethigh(set); + } + return result; +} + +/** + * Sanity check if the given MTBDD is a conjunction of positive variables, + * and if all nodes are marked in the nodes table (detects violations after garbage collection). + */ +void +mtbdd_test_isset(MTBDD set) +{ + while (set != mtbdd_true) { + assert(set != mtbdd_false); + assert(llmsset_is_marked(nodes, set)); + mtbddnode_t n = MTBDD_GETNODE(set); + assert(node_getlow(set, n) == mtbdd_false); + set = node_gethigh(set, n); + } +} + /** * Return 1 if the map contains the key, 0 otherwise. */ @@ -2432,7 +3538,7 @@ int mtbdd_map_contains(MTBDDMAP map, uint32_t key) { while (!mtbdd_map_isempty(map)) { - mtbddnode_t n = GETNODE(map); + mtbddnode_t n = MTBDD_GETNODE(map); uint32_t k = mtbddnode_getvariable(n); if (k == key) return 1; if (k > key) return 0; @@ -2464,20 +3570,22 @@ mtbdd_map_count(MTBDDMAP map) MTBDDMAP mtbdd_map_add(MTBDDMAP map, uint32_t key, MTBDD value) { - if (mtbdd_map_isempty(map)) return mtbdd_makenode(key, mtbdd_map_empty(), value); + if (mtbdd_map_isempty(map)) { + return mtbdd_makemapnode(key, mtbdd_map_empty(), value); + } - mtbddnode_t n = GETNODE(map); + mtbddnode_t n = MTBDD_GETNODE(map); uint32_t k = mtbddnode_getvariable(n); if (k < key) { // add recursively and rebuild tree MTBDDMAP low = mtbdd_map_add(node_getlow(map, n), key, value); - return mtbdd_makenode(k, low, node_gethigh(map, n)); + return mtbdd_makemapnode(k, low, node_gethigh(map, n)); } else if (k > key) { - return mtbdd_makenode(key, map, value); + return mtbdd_makemapnode(key, map, value); } else { // replace old - return mtbdd_makenode(key, node_getlow(map, n), value); + return mtbdd_makemapnode(key, node_getlow(map, n), value); } } @@ -2485,26 +3593,26 @@ mtbdd_map_add(MTBDDMAP map, uint32_t key, MTBDD value) * Add all values from map2 to map1, overwrites if key already in map1. */ MTBDDMAP -mtbdd_map_addall(MTBDDMAP map1, MTBDDMAP map2) +mtbdd_map_update(MTBDDMAP map1, MTBDDMAP map2) { if (mtbdd_map_isempty(map1)) return map2; if (mtbdd_map_isempty(map2)) return map1; - mtbddnode_t n1 = GETNODE(map1); - mtbddnode_t n2 = GETNODE(map2); + mtbddnode_t n1 = MTBDD_GETNODE(map1); + mtbddnode_t n2 = MTBDD_GETNODE(map2); uint32_t k1 = mtbddnode_getvariable(n1); uint32_t k2 = mtbddnode_getvariable(n2); MTBDDMAP result; if (k1 < k2) { - MTBDDMAP low = mtbdd_map_addall(node_getlow(map1, n1), map2); - result = mtbdd_makenode(k1, low, node_gethigh(map1, n1)); + MTBDDMAP low = mtbdd_map_update(node_getlow(map1, n1), map2); + result = mtbdd_makemapnode(k1, low, node_gethigh(map1, n1)); } else if (k1 > k2) { - MTBDDMAP low = mtbdd_map_addall(map1, node_getlow(map2, n2)); - result = mtbdd_makenode(k2, low, node_gethigh(map2, n2)); + MTBDDMAP low = mtbdd_map_update(map1, node_getlow(map2, n2)); + result = mtbdd_makemapnode(k2, low, node_gethigh(map2, n2)); } else { - MTBDDMAP low = mtbdd_map_addall(node_getlow(map1, n1), node_getlow(map2, n2)); - result = mtbdd_makenode(k2, low, node_gethigh(map2, n2)); + MTBDDMAP low = mtbdd_map_update(node_getlow(map1, n1), node_getlow(map2, n2)); + result = mtbdd_makemapnode(k2, low, node_gethigh(map2, n2)); } return result; @@ -2518,12 +3626,12 @@ mtbdd_map_remove(MTBDDMAP map, uint32_t key) { if (mtbdd_map_isempty(map)) return map; - mtbddnode_t n = GETNODE(map); + mtbddnode_t n = MTBDD_GETNODE(map); uint32_t k = mtbddnode_getvariable(n); if (k < key) { MTBDDMAP low = mtbdd_map_remove(node_getlow(map, n), key); - return mtbdd_makenode(k, low, node_gethigh(map, n)); + return mtbdd_makemapnode(k, low, node_gethigh(map, n)); } else if (k > key) { return map; } else { @@ -2540,14 +3648,14 @@ mtbdd_map_removeall(MTBDDMAP map, MTBDD variables) if (mtbdd_map_isempty(map)) return map; if (variables == mtbdd_true) return map; - mtbddnode_t n1 = GETNODE(map); - mtbddnode_t n2 = GETNODE(variables); + mtbddnode_t n1 = MTBDD_GETNODE(map); + mtbddnode_t n2 = MTBDD_GETNODE(variables); uint32_t k1 = mtbddnode_getvariable(n1); uint32_t k2 = mtbddnode_getvariable(n2); if (k1 < k2) { MTBDDMAP low = mtbdd_map_removeall(node_getlow(map, n1), variables); - return mtbdd_makenode(k1, low, node_gethigh(map, n1)); + return mtbdd_makemapnode(k1, low, node_gethigh(map, n1)); } else if (k1 > k2) { return mtbdd_map_removeall(map, node_gethigh(variables, n2)); } else { diff --git a/sylvan_mtbdd.h b/sylvan_mtbdd.h old mode 100644 new mode 100755 index a56d68ebfe4d88e6ae3473174eaa928caf9ef3b5..bdcd26525a27ae88fd0108dfa5c5d92c582b9915 --- a/sylvan_mtbdd.h +++ b/sylvan_mtbdd.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,25 +46,97 @@ extern "C" { /** * An MTBDD is a 64-bit value. The low 40 bits are an index into the unique table. * The highest 1 bit is the complement edge, indicating negation. - * For Boolean MTBDDs, this means "not X", for Integer and Real MTBDDs, this means "-X". + * + * Currently, negation using complement edges is only implemented for Boolean MTBDDs. + * For Integer/Real MTBDDs, negation is not well-defined, as "-0" = "0". + * + * A MTBDD node has 24 bits for the variable. + * A set of MTBDD variables is represented by the MTBDD of the conjunction of these variables. + * A MTBDDMAP uses special "MAP" nodes in the MTBDD nodes table. */ typedef uint64_t MTBDD; typedef MTBDD MTBDDMAP; /** - * mtbdd_true is only used in Boolean MTBDDs. mtbdd_false has multiple roles (see above). - */ -#define mtbdd_complement ((MTBDD)0x8000000000000000LL) -#define mtbdd_false ((MTBDD)0) -#define mtbdd_true (mtbdd_false|mtbdd_complement) -#define mtbdd_invalid ((MTBDD)0xffffffffffffffffLL) + * mtbdd_true and mtbdd_false are the Boolean leaves representing True and False. + * False is also used in Integer/Real/Fraction MTBDDs for partially defined functions. + */ +static const MTBDD mtbdd_complement = 0x8000000000000000LL; +static const MTBDD mtbdd_false = 0; +static const MTBDD mtbdd_true = 0x8000000000000000LL; +static const MTBDD mtbdd_invalid = 0xffffffffffffffffLL; + +/** + * Definitions for backward compatibility... + * We now consider BDDs to be a special case of MTBDDs. + */ +typedef MTBDD SyBDD; +typedef MTBDDMAP BDDMAP; +typedef MTBDD BDDSET; +typedef uint32_t BDDVAR; +static const MTBDD sylvan_complement = 0x8000000000000000LL; +static const MTBDD sylvan_false = 0; +static const MTBDD sylvan_true = 0x8000000000000000LL; +static const MTBDD sylvan_invalid = 0xffffffffffffffffLL; +#define sylvan_init_bdd sylvan_init_mtbdd +#define sylvan_ref mtbdd_ref +#define sylvan_deref mtbdd_deref +#define sylvan_count_refs mtbdd_count_refs +#define sylvan_protect mtbdd_protect +#define sylvan_unprotect mtbdd_unprotect +#define sylvan_count_protected mtbdd_count_protected +#define sylvan_gc_mark_rec mtbdd_gc_mark_rec +#define sylvan_ithvar mtbdd_ithvar +#define bdd_refs_pushptr mtbdd_refs_pushptr +#define bdd_refs_popptr mtbdd_refs_popptr +#define bdd_refs_push mtbdd_refs_push +#define bdd_refs_pop mtbdd_refs_pop +#define bdd_refs_spawn mtbdd_refs_spawn +#define bdd_refs_sync mtbdd_refs_sync +#define sylvan_map_empty mtbdd_map_empty +#define sylvan_map_isempty mtbdd_map_isempty +#define sylvan_map_key mtbdd_map_key +#define sylvan_map_value mtbdd_map_value +#define sylvan_map_next mtbdd_map_next +#define sylvan_map_contains mtbdd_map_contains +#define sylvan_map_count mtbdd_map_count +#define sylvan_map_add mtbdd_map_add +#define sylvan_map_addall mtbdd_map_addall +#define sylvan_map_remove mtbdd_map_remove +#define sylvan_map_removeall mtbdd_map_removeall +#define sylvan_set_empty mtbdd_set_empty +#define sylvan_set_isempty mtbdd_set_isempty +#define sylvan_set_add mtbdd_set_add +#define sylvan_set_addall mtbdd_set_addall +#define sylvan_set_remove mtbdd_set_remove +#define sylvan_set_removeall mtbdd_set_removeall +#define sylvan_set_first mtbdd_set_first +#define sylvan_set_next mtbdd_set_next +#define sylvan_set_fromarray mtbdd_set_fromarray +#define sylvan_set_toarray mtbdd_set_toarray +#define sylvan_set_in mtbdd_set_in +#define sylvan_set_count mtbdd_set_count +#define sylvan_test_isset mtbdd_test_isset +#define sylvan_var mtbdd_getvar +#define sylvan_low mtbdd_getlow +#define sylvan_high mtbdd_gethigh +#define sylvan_makenode mtbdd_makenode +#define sylvan_makemapnode mtbdd_makemapnode +#define sylvan_support mtbdd_support +#define sylvan_test_isbdd mtbdd_test_isvalid +#define sylvan_nodecount mtbdd_nodecount +#define sylvan_printdot mtbdd_printdot +#define sylvan_fprintdot mtbdd_fprintdot +#define sylvan_printsha mtbdd_printsha +#define sylvan_fprintsha mtbdd_fprintsha +#define sylvan_getsha mtbdd_getsha /** * Initialize MTBDD functionality. * This initializes internal and external referencing datastructures, * and registers them in the garbage collection framework. */ -void sylvan_init_mtbdd(); +void sylvan_init_mtbdd(void); /** * Create a MTBDD terminal of type <type> and value <value>. @@ -74,56 +147,208 @@ MTBDD mtbdd_makeleaf(uint32_t type, uint64_t value); /** * Create an internal MTBDD node of Boolean variable <var>, with low edge <low> and high edge <high>. * <var> is a 24-bit integer. + * Please note that this does NOT check variable ordering! */ -MTBDD mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high); +MTBDD _mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high); +static inline MTBDD mtbdd_makenode(uint32_t var, MTBDD low, MTBDD high) +{ + return low == high ? low : _mtbdd_makenode(var, low, high); +} /** - * Returns 1 is the MTBDD is a terminal, or 0 otherwise. + * Return 1 if the MTBDD is a terminal, or 0 otherwise. */ int mtbdd_isleaf(MTBDD mtbdd); -#define mtbdd_isnode(mtbdd) (mtbdd_isleaf(mtbdd) ? 0 : 1) /** - * For MTBDD terminals, returns <type> and <value> + * Return 1 if the MTBDD is an internal node, or 0 otherwise. + */ +static inline int mtbdd_isnode(MTBDD mtbdd) { return mtbdd_isleaf(mtbdd) ? 0 : 1; } + +/** + * Return the <type> field of the given leaf. + */ +uint32_t mtbdd_gettype(MTBDD leaf); + +/** + * Return the <value> field of the given leaf. */ -uint32_t mtbdd_gettype(MTBDD terminal); -uint64_t mtbdd_getvalue(MTBDD terminal); +uint64_t mtbdd_getvalue(MTBDD leaf); /** - * For internal MTBDD nodes, returns <var>, <low> and <high> + * Return the variable field of the given internal node. */ uint32_t mtbdd_getvar(MTBDD node); + +/** + * Follow the low/false edge of the given internal node. + * Also takes complement edges into account. + */ MTBDD mtbdd_getlow(MTBDD node); + +/** + * Follow the high/true edge of the given internal node. + * Also takes complement edges into account. + */ MTBDD mtbdd_gethigh(MTBDD node); /** - * Compute the complement of the MTBDD. - * For Boolean MTBDDs, this means "not X". + * Obtain the complement of the MTBDD. + * This is only valid for Boolean MTBDDs or custom implementations that support it. */ -#define mtbdd_hascomp(dd) ((dd & mtbdd_complement) ? 1 : 0) -#define mtbdd_comp(dd) (dd ^ mtbdd_complement) -#define mtbdd_not(dd) (dd ^ mtbdd_complement) + +static inline int +mtbdd_hascomp(MTBDD dd) +{ + return (dd & mtbdd_complement) ? 1 : 0; +} + +static inline MTBDD +mtbdd_comp(MTBDD dd) +{ + return dd ^ mtbdd_complement; +} + +static inline MTBDD +mtbdd_not(MTBDD dd) +{ + return dd ^ mtbdd_complement; +} /** - * Create terminals representing int64_t (type 0), double (type 1), or fraction (type 2) values + * Create an Integer leaf with the given value. */ MTBDD mtbdd_int64(int64_t value); + +/** + * Create a Real leaf with the given value. + */ MTBDD mtbdd_double(double value); + +/** + * Create a Fraction leaf with the given numerator and denominator. + */ MTBDD mtbdd_fraction(int64_t numer, uint64_t denom); /** - * Get the value of a terminal (for Integer, Real and Fraction terminals, types 0, 1 and 2) + * Obtain the value of an Integer leaf. */ int64_t mtbdd_getint64(MTBDD terminal); + +/** + * Obtain the value of a Real leaf. + */ double mtbdd_getdouble(MTBDD terminal); -#define mtbdd_getnumer(terminal) ((int32_t)(mtbdd_getvalue(terminal)>>32)) -#define mtbdd_getdenom(terminal) ((uint32_t)(mtbdd_getvalue(terminal)&0xffffffff)) /** - * Create the conjunction of variables in arr. - * I.e. arr[0] \and arr[1] \and ... \and arr[length-1] + * Obtain the numerator of a Fraction leaf. + */ +static inline int32_t +mtbdd_getnumer(MTBDD terminal) +{ + return (int32_t)(mtbdd_getvalue(terminal)>>32); +} + +/** + * Obtain the denominator of a Fraction leaf. + */ +static inline uint32_t +mtbdd_getdenom(MTBDD terminal) +{ + return (uint32_t)(mtbdd_getvalue(terminal)&0xffffffff); +} + +/** + * Create the Boolean MTBDD representing "if <var> then True else False" */ -MTBDD mtbdd_fromarray(uint32_t* arr, size_t length); +MTBDD mtbdd_ithvar(uint32_t var); + +/** + * Functions to manipulate sets of MTBDD variables. + * + * A set of variables is represented by a cube/conjunction of (positive) variables. + */ +static inline MTBDD +mtbdd_set_empty() +{ + return mtbdd_true; +} + +static inline int +mtbdd_set_isempty(MTBDD set) +{ + return (set == mtbdd_true) ? 1 : 0; +} + +static inline uint32_t +mtbdd_set_first(MTBDD set) +{ + return mtbdd_getvar(set); +} + +static inline MTBDD +mtbdd_set_next(MTBDD set) +{ + return mtbdd_gethigh(set); +} + +/** + * Create a set of variables, represented as the conjunction of (positive) variables. + */ +MTBDD mtbdd_set_from_array(uint32_t* arr, size_t length); + +/** + * Write all variables in a variable set to the given array. + * The array must be sufficiently large. + */ +void mtbdd_set_to_array(MTBDD set, uint32_t *arr); + +/** + * Compute the number of variables in a given set of variables. + */ +size_t mtbdd_set_count(MTBDD set); + +/** + * Compute the union of <set1> and <set2> + */ +#define mtbdd_set_union(set1, set2) sylvan_and(set1, set2) + +/** + * Remove variables in <set2> from <set1> + */ +#define mtbdd_set_minus(set1, set2) CALL(mtbdd_set_minus, set1, set2) +TASK_DECL_2(MTBDD, mtbdd_set_minus, MTBDD, MTBDD); + +/** + * Return 1 if <set> contains <var>, 0 otherwise. + */ +int mtbdd_set_contains(MTBDD set, uint32_t var); + +/** + * Add the variable <var> to <set>. + */ +MTBDD mtbdd_set_add(MTBDD set, uint32_t var); + +/** + * Remove the variable <var> from <set>. + */ +MTBDD mtbdd_set_remove(MTBDD set, uint32_t var); + +/** + * Sanity check if the given MTBDD is a conjunction of positive variables, + * and if all nodes are marked in the nodes table (detects violations after garbage collection). + */ +void mtbdd_test_isset(MTBDD set); + +/** + * Definitions for backwards compatibility + */ +#define mtbdd_fromarray mtbdd_set_from_array +#define mtbdd_set_fromarray mtbdd_set_from_array +#define mtbdd_set_toarray mtbdd_set_to_array +#define mtbdd_set_addall mtbdd_set_union +#define mtbdd_set_removeall mtbdd_set_minus +#define mtbdd_set_in mtbdd_set_contains /** * Create a MTBDD cube representing the conjunction of variables in their positive or negative @@ -139,7 +364,7 @@ MTBDD mtbdd_cube(MTBDD variables, uint8_t *cube, MTBDD terminal); * Does not support cube[idx]==3. */ #define mtbdd_union_cube(mtbdd, variables, cube, terminal) CALL(mtbdd_union_cube, mtbdd, variables, cube, terminal) -TASK_DECL_4(syBDD, mtbdd_union_cube, MTBDD, MTBDD, uint8_t*, MTBDD); +TASK_DECL_4(SyBDD, mtbdd_union_cube, MTBDD, MTBDD, uint8_t*, MTBDD); /** * Count the number of satisfying assignments (minterms) leading to a non-false leaf @@ -148,14 +373,20 @@ TASK_DECL_2(double, mtbdd_satcount, MTBDD, size_t); #define mtbdd_satcount(dd, nvars) CALL(mtbdd_satcount, dd, nvars) /** - * Count the number of MTBDD leaves (excluding mtbdd_false and mtbdd_true) in the MTBDD + * Count the number of MTBDD leaves (excluding mtbdd_false and mtbdd_true) in the given <count> MTBDDs */ -size_t mtbdd_leafcount(MTBDD mtbdd); +size_t mtbdd_leafcount_more(const MTBDD *mtbdds, size_t count); +#define mtbdd_leafcount(dd) mtbdd_leafcount_more(&dd, 1) /** - * Count the number of MTBDD nodes and terminals (excluding mtbdd_false and mtbdd_true) in a MTBDD + * Count the number of MTBDD nodes and terminals (excluding mtbdd_false and mtbdd_true) in the given <count> MTBDDs */ -size_t mtbdd_nodecount(MTBDD mtbdd); +size_t mtbdd_nodecount_more(const MTBDD *mtbdds, size_t count); + +static inline size_t +mtbdd_nodecount(const MTBDD dd) { + return mtbdd_nodecount_more(&dd, 1); +} /** * Callback function types for binary ("dyadic") and unary ("monadic") operations. @@ -192,7 +423,7 @@ TASK_DECL_3(MTBDD, mtbdd_uapply, MTBDD, mtbdd_uapply_op, size_t); /** * Callback function types for abstraction. * MTBDD mtbdd_abstract_op(MTBDD a, MTBDD b, int k). - * The function is either called with k==0 (apply to two arguments) or k>0 (k skipped BDD variables) + * The function is either called with k==0 (apply to two arguments) or k>0 (k skipped SyBDD variables) * k == 0 => res := apply op to a and b * k > 0 => res := apply op to op(a, a, k-1) and op(a, a, k-1) */ @@ -210,6 +441,12 @@ TASK_DECL_3(MTBDD, mtbdd_abstract, MTBDD, MTBDD, mtbdd_abstract_op); */ TASK_DECL_2(MTBDD, mtbdd_op_negate, MTBDD, size_t); +/** + * Unary opeation Complement. + * Supported domains: Integer, Real, Fraction + */ +TASK_DECL_2(MTBDD, mtbdd_op_cmpl, MTBDD, size_t); + /** * Binary operation Plus (for MTBDDs of same type) * Only for MTBDDs where either all leaves are Boolean, or Integer, or Double. @@ -254,9 +491,17 @@ TASK_DECL_3(MTBDD, mtbdd_abstract_op_max, MTBDD, MTBDD, int); /** * Compute -a + * (negation, where 0 stays 0, and x into -x) */ #define mtbdd_negate(a) mtbdd_uapply(a, TASK(mtbdd_op_negate), 0) +/** + * Compute ~a for partial MTBDDs. + * Does not negate Boolean True/False. + * (complement, where 0 is turned into 1, and non-0 into 0) + */ +#define mtbdd_cmpl(a) mtbdd_uapply(a, TASK(mtbdd_op_cmpl), 0) + /** * Compute a + b */ @@ -304,7 +549,7 @@ TASK_DECL_3(MTBDD, mtbdd_abstract_op_max, MTBDD, MTBDD, int); /** * Compute IF <f> THEN <g> ELSE <h>. - * <f> must be a Boolean MTBDD (or standard BDD). + * <f> must be a Boolean MTBDD (or standard SyBDD). */ TASK_DECL_3(MTBDD, mtbdd_ite, MTBDD, MTBDD, MTBDD); #define mtbdd_ite(f, g, h) CALL(mtbdd_ite, f, g, h); @@ -313,8 +558,15 @@ TASK_DECL_3(MTBDD, mtbdd_ite, MTBDD, MTBDD, MTBDD); * Multiply <a> and <b>, and abstract variables <vars> using summation. * This is similar to the "and_exists" operation in BDDs. */ -TASK_DECL_3(MTBDD, mtbdd_and_exists, MTBDD, MTBDD, MTBDD); -#define mtbdd_and_exists(a, b, vars) CALL(mtbdd_and_exists, a, b, vars) +TASK_DECL_3(MTBDD, mtbdd_and_abstract_plus, MTBDD, MTBDD, MTBDD); +#define mtbdd_and_abstract_plus(a, b, vars) CALL(mtbdd_and_abstract_plus, a, b, vars) +#define mtbdd_and_exists mtbdd_and_abstract_plus + +/** + * Multiply <a> and <b>, and abstract variables <vars> by taking the maximum. + */ +TASK_DECL_3(MTBDD, mtbdd_and_abstract_max, MTBDD, MTBDD, MTBDD); +#define mtbdd_and_abstract_max(a, b, vars) CALL(mtbdd_and_abstract_max, a, b, vars) /** * Monad that converts double to a Boolean MTBDD, translate terminals >= value to 1 and to 0 otherwise; @@ -429,6 +681,60 @@ typedef int (*mtbdd_enum_filter_cb)(MTBDD); MTBDD mtbdd_enum_first(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); MTBDD mtbdd_enum_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); +/** + * Given an MTBDD <dd> and a cube of variables <variables> expected in <dd>, + * mtbdd_enum_all_first and mtbdd_enum_all_next enumerate all satisfying assignments in <dd> that lead + * to a non-False leaf. + * + * The functions return the leaf (or mtbdd_false if no new satisfying assignment is found) and encodes + * the assignment in the supplied array <arr>, 0 for False and 1 for True. + * + * The supplied array <arr> must be large enough for all variables in <variables>. + * + * Usage: + * MTBDD leaf = mtbdd_enum_first(dd, variables, arr, NULL); + * while (leaf != mtbdd_false) { + * .... // do something with arr/leaf + * leaf = mtbdd_enum_next(dd, variables, arr, NULL); + * } + * + * The callback is an optional function that returns 0 when the given terminal node should be skipped. + */ +MTBDD mtbdd_enum_all_first(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); +MTBDD mtbdd_enum_all_next(MTBDD dd, MTBDD variables, uint8_t *arr, mtbdd_enum_filter_cb filter_cb); + +/** + * Given a MTBDD <dd>, call <cb> with context <context> for every unique path in <dd> ending in leaf <leaf>. + * + * Usage: + * VOID_TASK_3(cb, mtbdd_enum_trace_t, trace, MTBDD, leaf, void*, context) { ... do something ... } + * mtbdd_enum_par(dd, cb, context); + */ +typedef struct mtbdd_enum_trace { + struct mtbdd_enum_trace *prev; + uint32_t var; + int val; // 0 or 1 +} * mtbdd_enum_trace_t; + +LACE_TYPEDEF_CB(void, mtbdd_enum_cb, mtbdd_enum_trace_t, MTBDD, void*) +VOID_TASK_DECL_3(mtbdd_enum_par, MTBDD, mtbdd_enum_cb, void*); +#define mtbdd_enum_par(dd, cb, context) CALL(mtbdd_enum_par, dd, cb, context) + +/** + * Function composition after partial evaluation. + * + * Given a function F(X) = f, compute the composition F'(X) = g(f) for every assignment to X. + * All variables X in <vars> must appear before all variables in f and g(f). + * + * Usage: + * TASK_2(MTBDD, g, MTBDD, in) { ... return g of <in> ... } + * MTBDD x_vars = ...; // the cube of variables x + * MTBDD result = mtbdd_eval_compose(dd, x_vars, TASK(g)); + */ +LACE_TYPEDEF_CB(MTBDD, mtbdd_eval_compose_cb, MTBDD); +TASK_DECL_3(MTBDD, mtbdd_eval_compose, MTBDD, MTBDD, mtbdd_eval_compose_cb); +#define mtbdd_eval_compose(dd, vars, cb) CALL(mtbdd_eval_compose, dd, vars, cb) + /** * For debugging. * Tests if all nodes in the MTBDD are correctly ``marked'' in the nodes table. @@ -440,22 +746,232 @@ TASK_DECL_1(int, mtbdd_test_isvalid, MTBDD); #define mtbdd_test_isvalid(mtbdd) CALL(mtbdd_test_isvalid, mtbdd) /** - * Write a DOT representation of a MTBDD + * Write a .dot representation of a given MTBDD * The callback function is required for custom terminals. */ -typedef void (*print_terminal_label_cb)(FILE *out, uint32_t type, uint64_t value); -void mtbdd_fprintdot(FILE *out, MTBDD mtbdd, print_terminal_label_cb cb); -#define mtbdd_printdot(mtbdd, cb) mtbdd_fprintdot(stdout, mtbdd, cb) +void mtbdd_fprintdot(FILE *out, MTBDD mtbdd); +#define mtbdd_printdot(mtbdd, cb) mtbdd_fprintdot(stdout, mtbdd) + +/** + * Write a .dot representation of a given MTBDD, but without complement edges. + */ +void mtbdd_fprintdot_nc(FILE *out, MTBDD mtbdd); +#define mtbdd_printdot_nc(mtbdd, cb) mtbdd_fprintdot_nc(stdout, mtbdd) + +/** + * Write a text representation of a leaf to the given file. + */ +void mtbdd_fprint_leaf(FILE *out, MTBDD leaf); + +/** + * Write a text representation of a leaf to stdout. + */ +void mtbdd_print_leaf(MTBDD leaf); + +/** + * Obtain the textual representation of a leaf. + * The returned result is either equal to the given <buf> (if the results fits) + * or to a newly allocated array (with malloc). + */ +char *mtbdd_leaf_to_str(MTBDD leaf, char *buf, size_t buflen); + +/** + * Some debugging functions that generate SHA2 hashes of MTBDDs. + * They are independent of where nodes are located in hash tables. + * Note that they are not "perfect", but they can be useful to run easy sanity checks. + */ + +/** + * Print SHA2 hash to stdout. + */ +void mtbdd_printsha(MTBDD dd); + +/** + * Print SHA2 hash to given file. + */ +void mtbdd_fprintsha(FILE *f, MTBDD dd); + +/** + * Obtain SHA2 hash; target array must be at least 65 bytes long. + */ +void mtbdd_getsha(MTBDD dd, char *target); + +/** + * Visitor functionality for MTBDDs. + * Visits internal nodes and leafs. + */ + +/** + * pre_cb callback: given input MTBDD and context, + * return whether to visit children (if not leaf) + * post_cb callback: given input MTBDD and context + */ +LACE_TYPEDEF_CB(int, mtbdd_visit_pre_cb, MTBDD, void*); +LACE_TYPEDEF_CB(void, mtbdd_visit_post_cb, MTBDD, void*); + +/** + * Sequential visit operation + */ +VOID_TASK_DECL_4(mtbdd_visit_seq, MTBDD, mtbdd_visit_pre_cb, mtbdd_visit_post_cb, void*); +#define mtbdd_visit_seq(...) CALL(mtbdd_visit_seq, __VA_ARGS__) + +/** + * Parallel visit operation + */ +VOID_TASK_DECL_4(mtbdd_visit_par, MTBDD, mtbdd_visit_pre_cb, mtbdd_visit_post_cb, void*); +#define mtbdd_visit_par(...) CALL(mtbdd_visit_par, __VA_ARGS__) + +/** + * Writing MTBDDs to file. + * + * Every node that is to be written is assigned a number, starting from 1, + * such that reading the result in the future can be done in one pass. + * + * We use a skiplist to store the assignment. + * + * The functions mtbdd_writer_tobinary and mtbdd_writer_totext can be used to + * store an array of MTBDDs to binary format or text format. + * + * One could also do the procedure manually instead. + * - call mtbdd_writer_start to allocate the skiplist. + * - call mtbdd_writer_add to add a given MTBDD to the skiplist + * - call mtbdd_writer_writebinary to write all added nodes to a file + * - OR: mtbdd_writer_writetext to write all added nodes in text format + * - call mtbdd_writer_get to obtain the MTBDD identifier as stored in the skiplist + * - call mtbdd_writer_end to free the skiplist + */ + +/** + * Write <count> decision diagrams given in <dds> in internal binary form to <file>. + * + * The internal binary format is as follows, to store <count> decision diagrams... + * uint64_t: nodecount -- number of nodes + * <nodecount> times uint128_t: each leaf/node + * uint64_t: count -- number of stored decision diagrams + * <count> times uint64_t: each stored decision diagram + */ +VOID_TASK_DECL_3(mtbdd_writer_tobinary, FILE *, MTBDD *, int); +#define mtbdd_writer_tobinary(file, dds, count) CALL(mtbdd_writer_tobinary, file, dds, count) + +/** + * Write <count> decision diagrams given in <dds> in ASCII form to <file>. + * Also supports custom leaves using the leaf_to_str callback. + * + * The text format writes in the same order as the binary format, except... + * [ + * node(id, var, low, high), -- for a normal node (no complement on high) + * node(id, var, low, ~high), -- for a normal node (complement on high) + * leaf(id, type, "value"), -- for a leaf (with value between "") + * ],[dd1, dd2, dd3, ...,] -- and each the stored decision diagram. + */ + +VOID_TASK_DECL_3(mtbdd_writer_totext, FILE *, MTBDD *, int); +#define mtbdd_writer_totext(file, dds, count) CALL(mtbdd_writer_totext, file, dds, count) + +/** + * Skeleton typedef for the skiplist + */ +typedef struct sylvan_skiplist *sylvan_skiplist_t; + +/** + * Allocate a skiplist for writing an MTBDD. + */ +sylvan_skiplist_t mtbdd_writer_start(void); + +/** + * Add the given MTBDD to the skiplist. + */ +VOID_TASK_DECL_2(mtbdd_writer_add, sylvan_skiplist_t, MTBDD); +#define mtbdd_writer_add(sl, dd) CALL(mtbdd_writer_add, sl, dd) + +/** + * Write all assigned MTBDD nodes in binary format to the file. + */ +void mtbdd_writer_writebinary(FILE *out, sylvan_skiplist_t sl); + +/** + * Retrieve the identifier of the given stored MTBDD. + * This is useful if you want to be able to retrieve the stored MTBDD later. + */ +uint64_t mtbdd_writer_get(sylvan_skiplist_t sl, MTBDD dd); + +/** + * Free the allocated skiplist. + */ +void mtbdd_writer_end(sylvan_skiplist_t sl); + +/** + * Reading MTBDDs from file. + * + * The function mtbdd_reader_frombinary is basically the reverse of mtbdd_writer_tobinary. + * + * One can also perform the procedure manually. + * - call mtbdd_reader_readbinary to read the nodes from file + * - call mtbdd_reader_get to obtain the MTBDD for the given identifier as stored in the file. + * - call mtbdd_reader_end to free the array returned by mtbdd_reader_readbinary + * + * Returns 0 if successful, -1 otherwise. + */ + +/* + * Read <count> decision diagrams to <dds> from <file> in internal binary form. + */ +TASK_DECL_3(int, mtbdd_reader_frombinary, FILE*, MTBDD*, int); +#define mtbdd_reader_frombinary(file, dds, count) CALL(mtbdd_reader_frombinary, file, dds, count) + +/** + * Reading a file earlier written with mtbdd_writer_writebinary + * Returns an array with the conversion from stored identifier to MTBDD + * This array is allocated with malloc and must be freed afterwards. + * Returns NULL if there was an error. + */ + +TASK_DECL_1(uint64_t*, mtbdd_reader_readbinary, FILE*); +#define mtbdd_reader_readbinary(file) CALL(mtbdd_reader_readbinary, file) + +/** + * Retrieve the MTBDD of the given stored identifier. + */ +MTBDD mtbdd_reader_get(uint64_t* arr, uint64_t identifier); + +/** + * Free the allocated translation array + */ +void mtbdd_reader_end(uint64_t *arr); /** * MTBDDMAP, maps uint32_t variables to MTBDDs. - * A MTBDDMAP node has variable level, low edge going to the next MTBDDMAP, high edge to the mapped MTBDD + * A MTBDDMAP node has variable level, low edge going to the next MTBDDMAP, high edge to the mapped MTBDD. */ -#define mtbdd_map_empty() mtbdd_false -#define mtbdd_map_isempty(map) (map == mtbdd_false ? 1 : 0) -#define mtbdd_map_key(map) mtbdd_getvar(map) -#define mtbdd_map_value(map) mtbdd_gethigh(map) -#define mtbdd_map_next(map) mtbdd_getlow(map) +static inline MTBDD +mtbdd_map_empty() +{ + return mtbdd_false; +} + +static inline int +mtbdd_map_isempty(MTBDD map) +{ + return (map == mtbdd_false) ? 1 : 0; +} + +static inline uint32_t +mtbdd_map_key(MTBDD map) +{ + return mtbdd_getvar(map); +} + +static inline MTBDD +mtbdd_map_value(MTBDD map) +{ + return mtbdd_gethigh(map); +} + +static inline MTBDD +mtbdd_map_next(MTBDD map) +{ + return mtbdd_getlow(map); +} /** * Return 1 if the map contains the key, 0 otherwise. @@ -475,7 +991,8 @@ MTBDDMAP mtbdd_map_add(MTBDDMAP map, uint32_t key, MTBDD value); /** * Add all values from map2 to map1, overwrites if key already in map1. */ -MTBDDMAP mtbdd_map_addall(MTBDDMAP map1, MTBDDMAP map2); +MTBDDMAP mtbdd_map_update(MTBDDMAP map1, MTBDDMAP map2); +#define mtbdd_map_addall mtbdd_map_update /** * Remove the key <key> from the map and return the result @@ -487,26 +1004,6 @@ MTBDDMAP mtbdd_map_remove(MTBDDMAP map, uint32_t key); */ MTBDDMAP mtbdd_map_removeall(MTBDDMAP map, MTBDD variables); -/** - * Custom node types - * Overrides standard hash/equality/notify_on_dead behavior - * hash(value, seed) return hash version - * equals(value1, value2) return 1 if equal, 0 if not equal - * create(&value) replace value by new value for object allocation - * destroy(value) - * NOTE: equals(value1, value2) must imply: hash(value1, seed) == hash(value2,seed) - * NOTE: new value of create must imply: equals(old, new) - */ -typedef uint64_t (*mtbdd_hash_cb)(uint64_t, uint64_t); -typedef int (*mtbdd_equals_cb)(uint64_t, uint64_t); -typedef void (*mtbdd_create_cb)(uint64_t*); -typedef void (*mtbdd_destroy_cb)(uint64_t); - -/** - * Registry callback handlers for <type>. - */ -uint32_t mtbdd_register_custom_leaf(mtbdd_hash_cb hash_cb, mtbdd_equals_cb equals_cb, mtbdd_create_cb create_cb, mtbdd_destroy_cb destroy_cb); - /** * Garbage collection * Sylvan supplies two default methods to handle references to nodes, but the user @@ -522,82 +1019,86 @@ VOID_TASK_DECL_1(mtbdd_gc_mark_rec, MTBDD); #define mtbdd_gc_mark_rec(mtbdd) CALL(mtbdd_gc_mark_rec, mtbdd) /** - * Default external referencing. During garbage collection, MTBDDs marked with mtbdd_ref will - * be kept in the forest. - * It is recommended to prefer mtbdd_protect and mtbdd_unprotect. + * Infrastructure for external references using a hash table. + * Two hash tables store external references: a pointers table and a values table. + * The pointers table stores pointers to MTBDD variables, manipulated with protect and unprotect. + * The values table stores MTBDDs, manipulated with ref and deref. + * We strongly recommend using the pointers table whenever possible. */ -MTBDD mtbdd_ref(MTBDD a); -void mtbdd_deref(MTBDD a); -size_t mtbdd_count_refs(); /** - * Default external pointer referencing. During garbage collection, the pointers are followed and the MTBDD - * that they refer to are kept in the forest. + * Store the pointer <ptr> in the pointers table. */ void mtbdd_protect(MTBDD* ptr); + +/** + * Delete the pointer <ptr> from the pointers table. + */ void mtbdd_unprotect(MTBDD* ptr); -size_t mtbdd_count_protected(); /** - * If sylvan_set_ondead is set to a callback, then this function marks MTBDDs (terminals). - * When they are dead after the mark phase in garbage collection, the callback is called for marked MTBDDs. - * The ondead callback can either perform cleanup or resurrect dead terminals. + * Compute the number of pointers in the pointers table. */ -#define mtbdd_notify_ondead(dd) llmsset_notify_ondead(nodes, dd&~mtbdd_complement) +size_t mtbdd_count_protected(void); /** - * Infrastructure for internal references (per-thread, e.g. during MTBDD operations) - * Use mtbdd_refs_push and mtbdd_refs_pop to put MTBDDs on a thread-local reference stack. - * Use mtbdd_refs_spawn and mtbdd_refs_sync around SPAWN and SYNC operations when the result - * of the spawned Task is a MTBDD that must be kept during garbage collection. + * Store the MTBDD <dd> in the values table. */ -typedef struct mtbdd_refs_internal -{ - size_t r_size, r_count; - size_t s_size, s_count; - MTBDD *results; - Task **spawns; -} *mtbdd_refs_internal_t; +MTBDD mtbdd_ref(MTBDD dd); -extern DECLARE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); +/** + * Delete the MTBDD <dd> from the values table. + */ +void mtbdd_deref(MTBDD dd); -static inline MTBDD -mtbdd_refs_push(MTBDD mtbdd) -{ - LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); - if (mtbdd_refs_key->r_count >= mtbdd_refs_key->r_size) { - mtbdd_refs_key->r_size *= 2; - mtbdd_refs_key->results = (MTBDD*)realloc(mtbdd_refs_key->results, sizeof(MTBDD) * mtbdd_refs_key->r_size); - } - mtbdd_refs_key->results[mtbdd_refs_key->r_count++] = mtbdd; - return mtbdd; -} +/** + * Compute the number of values in the values table. + */ +size_t mtbdd_count_refs(void); -static inline void -mtbdd_refs_pop(int amount) -{ - LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); - mtbdd_refs_key->r_count-=amount; -} +/** + * Infrastructure for internal references. + * Every thread has its own reference stacks. There are three stacks: pointer, values, tasks stack. + * The pointers stack stores pointers to MTBDD variables, manipulated with pushptr and popptr. + * The values stack stores MTBDDs, manipulated with push and pop. + * The tasks stack stores Lace tasks (that return MTBDDs), manipulated with spawn and sync. + * + * It is recommended to use the pointers stack for local variables and the tasks stack for tasks. + */ -static inline void -mtbdd_refs_spawn(Task *t) -{ - LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); - if (mtbdd_refs_key->s_count >= mtbdd_refs_key->s_size) { - mtbdd_refs_key->s_size *= 2; - mtbdd_refs_key->spawns = (Task**)realloc(mtbdd_refs_key->spawns, sizeof(Task*) * mtbdd_refs_key->s_size); - } - mtbdd_refs_key->spawns[mtbdd_refs_key->s_count++] = t; -} +/** + * Push a MTBDD variable to the pointer reference stack. + * During garbage collection the variable will be inspected and the contents will be marked. + */ +void mtbdd_refs_pushptr(const MTBDD *ptr); -static inline MTBDD -mtbdd_refs_sync(MTBDD result) -{ - LOCALIZE_THREAD_LOCAL(mtbdd_refs_key, mtbdd_refs_internal_t); - mtbdd_refs_key->s_count--; - return result; -} +/** + * Pop the last <amount> MTBDD variables from the pointer reference stack. + */ +void mtbdd_refs_popptr(size_t amount); + +/** + * Push an MTBDD to the values reference stack. + * During garbage collection the references MTBDD will be marked. + */ +MTBDD mtbdd_refs_push(MTBDD mtbdd); + +/** + * Pop the last <amount> MTBDDs from the values reference stack. + */ +void mtbdd_refs_pop(long amount); + +/** + * Push a Task that returns an MTBDD to the tasks reference stack. + * Usage: mtbdd_refs_spawn(SPAWN(function, ...)); + */ +void mtbdd_refs_spawn(Task *t); + +/** + * Pop a Task from the task reference stack. + * Usage: MTBDD result = mtbdd_refs_sync(SYNC(function)); + */ +MTBDD mtbdd_refs_sync(MTBDD mtbdd); #ifdef __cplusplus } diff --git a/sylvan_mtbdd_int.h b/sylvan_mtbdd_int.h old mode 100644 new mode 100755 index 940250b9a83eee2b20923ebfaeb78d28060647e0..36efe1b2189efd74b1f6fce1242e49f0c1829dac --- a/sylvan_mtbdd_int.h +++ b/sylvan_mtbdd_int.h @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +15,8 @@ * limitations under the License. */ +/* Do not include this file directly. Instead, include sylvan_int.h */ + /** * Internals for MTBDDs */ @@ -22,107 +25,177 @@ #define SYLVAN_MTBDD_INT_H /** - * MTBDD node structure + * SyBDD/MTBDD node structure */ typedef struct __attribute__((packed)) mtbddnode { uint64_t a, b; } * mtbddnode_t; // 16 bytes -#define GETNODE(mtbdd) ((mtbddnode_t)llmsset_index_to_ptr(nodes, mtbdd&0x000000ffffffffff)) +static inline mtbddnode_t +MTBDD_GETNODE(MTBDD dd) +{ + return (mtbddnode_t)llmsset_index_to_ptr(nodes, dd&0x000000ffffffffff); +} /** * Complement handling macros */ -#define MTBDD_HASMARK(s) (s&mtbdd_complement?1:0) -#define MTBDD_TOGGLEMARK(s) (s^mtbdd_complement) -#define MTBDD_STRIPMARK(s) (s&~mtbdd_complement) -#define MTBDD_TRANSFERMARK(from, to) (to ^ (from & mtbdd_complement)) -// Equal under mark -#define MTBDD_EQUALM(a, b) ((((a)^(b))&(~mtbdd_complement))==0) + +static inline int +MTBDD_HASMARK(MTBDD dd) +{ + return (dd & mtbdd_complement) ? 1 : 0; +} + +static inline MTBDD +MTBDD_TOGGLEMARK(MTBDD dd) +{ + return dd ^ mtbdd_complement; +} + +static inline MTBDD +MTBDD_STRIPMARK(MTBDD dd) +{ + return dd & (~mtbdd_complement); +} + +static inline MTBDD +MTBDD_TRANSFERMARK(MTBDD from, MTBDD to) +{ + return (to ^ (from & mtbdd_complement)); +} + +/** + * Are two MTBDDs equal modulo mark? + */ +static inline int +MTBDD_EQUALM(MTBDD a, MTBDD b) +{ + return ((a^b)&(~mtbdd_complement)) ? 0 : 1; +} // Leaf: a = L=1, M, type; b = value // Node: a = L=0, C, M, high; b = variable, low // Only complement edge on "high" -static inline int +static inline int __attribute__((unused)) mtbddnode_isleaf(mtbddnode_t n) { return n->a & 0x4000000000000000 ? 1 : 0; } -static inline uint32_t +static inline uint32_t __attribute__((unused)) mtbddnode_gettype(mtbddnode_t n) { return n->a & 0x00000000ffffffff; } -static inline uint64_t +static inline uint64_t __attribute__((unused)) mtbddnode_getvalue(mtbddnode_t n) { return n->b; } -static inline int +static inline int __attribute__((unused)) mtbddnode_getcomp(mtbddnode_t n) { return n->a & 0x8000000000000000 ? 1 : 0; } -static inline uint64_t +static inline uint64_t __attribute__((unused)) mtbddnode_getlow(mtbddnode_t n) { return n->b & 0x000000ffffffffff; // 40 bits } -static inline uint64_t +static inline uint64_t __attribute__((unused)) mtbddnode_gethigh(mtbddnode_t n) { return n->a & 0x800000ffffffffff; // 40 bits plus high bit of first } -static inline uint32_t +static inline uint32_t __attribute__((unused)) mtbddnode_getvariable(mtbddnode_t n) { return (uint32_t)(n->b >> 40); } -static inline int +static inline int __attribute__((unused)) mtbddnode_getmark(mtbddnode_t n) { return n->a & 0x2000000000000000 ? 1 : 0; } -static inline void +static inline void __attribute__((unused)) mtbddnode_setmark(mtbddnode_t n, int mark) { if (mark) n->a |= 0x2000000000000000; else n->a &= 0xdfffffffffffffff; } -static inline void +static inline void __attribute__((unused)) mtbddnode_makeleaf(mtbddnode_t n, uint32_t type, uint64_t value) { n->a = 0x4000000000000000 | (uint64_t)type; n->b = value; } -static inline void +static inline void __attribute__((unused)) mtbddnode_makenode(mtbddnode_t n, uint32_t var, uint64_t low, uint64_t high) { n->a = high; n->b = ((uint64_t)var)<<40 | low; } -static MTBDD -node_getlow(MTBDD mtbdd, mtbddnode_t node) +static inline void __attribute__((unused)) +mtbddnode_makemapnode(mtbddnode_t n, uint32_t var, uint64_t low, uint64_t high) +{ + n->a = high | 0x1000000000000000; + n->b = ((uint64_t)var)<<40 | low; +} + +static inline int __attribute__((unused)) +mtbddnode_ismapnode(mtbddnode_t n) +{ + return n->a & 0x1000000000000000 ? 1 : 0; +} + +static MTBDD __attribute__((unused)) +mtbddnode_followlow(MTBDD mtbdd, mtbddnode_t node) { return MTBDD_TRANSFERMARK(mtbdd, mtbddnode_getlow(node)); } -static MTBDD -node_gethigh(MTBDD mtbdd, mtbddnode_t node) +static MTBDD __attribute__((unused)) +mtbddnode_followhigh(MTBDD mtbdd, mtbddnode_t node) { return MTBDD_TRANSFERMARK(mtbdd, mtbddnode_gethigh(node)); } +/** + * Compatibility + */ + +#define node_getlow mtbddnode_followlow +#define node_gethigh mtbddnode_followhigh + +#define BDD_HASMARK MTBDD_HASMARK +#define BDD_TOGGLEMARK MTBDD_TOGGLEMARK +#define BDD_STRIPMARK MTBDD_STRIPMARK +#define BDD_TRANSFERMARK MTBDD_TRANSFERMARK +#define BDD_EQUALM MTBDD_EQUALM +#define bddnode mtbddnode +#define bddnode_t mtbddnode_t +#define bddnode_getcomp mtbddnode_getcomp +#define bddnode_getlow mtbddnode_getlow +#define bddnode_gethigh mtbddnode_gethigh +#define bddnode_getvariable mtbddnode_getvariable +#define bddnode_getmark mtbddnode_getmark +#define bddnode_setmark mtbddnode_setmark +#define bddnode_makenode mtbddnode_makenode +#define bddnode_makemapnode mtbddnode_makemapnode +#define bddnode_ismapnode mtbddnode_ismapnode +#define node_low node_getlow +#define node_high node_gethigh + #endif diff --git a/sylvan_obj.cpp b/sylvan_obj.cpp old mode 100644 new mode 100755 index 5d7d8dd53e92890b44b407726a6befae84a995ab..7f99f629a7df3b05a538bc547fd8db4606c69764 --- a/sylvan_obj.cpp +++ b/sylvan_obj.cpp @@ -1,5 +1,6 @@ /* - * Copyright 2011-2015 Formal Methods and Tools, University of Twente + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016 Tom van Dijk, Johannes Kepler University Linz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,16 +45,16 @@ Bdd::operator=(const Bdd& right) int Bdd::operator<=(const Bdd& other) const { - // TODO: better implementation, since we are not interested in the BDD result + // TODO: better implementation, since we are not interested in the SyBDD result LACE_ME; - syBDD r = sylvan_ite(this->bdd, sylvan_not(other.bdd), sylvan_false); + SyBDD r = sylvan_ite(this->bdd, sylvan_not(other.bdd), sylvan_false); return r == sylvan_false; } int Bdd::operator>=(const Bdd& other) const { - // TODO: better implementation, since we are not interested in the BDD result + // TODO: better implementation, since we are not interested in the SyBDD result return other <= *this; } @@ -244,9 +245,9 @@ Bdd::Xnor(const Bdd &g) const int Bdd::Leq(const Bdd &g) const { - // TODO: better implementation, since we are not interested in the BDD result + // TODO: better implementation, since we are not interested in the SyBDD result LACE_ME; - syBDD r = sylvan_ite(bdd, sylvan_not(g.bdd), sylvan_false); + SyBDD r = sylvan_ite(bdd, sylvan_not(g.bdd), sylvan_false); return r == sylvan_false; } @@ -313,7 +314,7 @@ Bdd::Support() const return sylvan_support(bdd); } -syBDD +SyBDD Bdd::GetBDD() const { return bdd; @@ -366,13 +367,13 @@ Bdd::PickOneCube(const BddSet &variables) const { std::vector<bool> result = std::vector<bool>(); - syBDD bdd = this->bdd; - syBDD vars = variables.set.bdd; + SyBDD bdd = this->bdd; + SyBDD vars = variables.set.bdd; if (bdd == sylvan_false) return result; for (; !sylvan_set_isempty(vars); vars = sylvan_set_next(vars)) { - uint32_t var = sylvan_set_var(vars); + uint32_t var = sylvan_set_first(vars); if (bdd == sylvan_true) { // pick 0 result.push_back(false); @@ -438,7 +439,7 @@ Bdd::VectorCube(const std::vector<Bdd> variables) Bdd Bdd::VariablesCube(std::vector<uint32_t> variables) { - syBDD result = sylvan_true; + SyBDD result = sylvan_true; for (int i=variables.size()-1; i>=0; i--) { result = sylvan_makenode(variables[i], sylvan_false, result); } @@ -1015,13 +1016,26 @@ MtbddMap::isEmpty() void Sylvan::initPackage(size_t initialTableSize, size_t maxTableSize, size_t initialCacheSize, size_t maxCacheSize) { - sylvan_init_package(initialTableSize, maxTableSize, initialCacheSize, maxCacheSize); + sylvan_set_sizes(initialTableSize, maxTableSize, initialCacheSize, maxCacheSize); + sylvan_init_package(); } void -Sylvan::initBdd(int granularity) +Sylvan::setGranularity(int granularity) { - sylvan_init_bdd(granularity); + sylvan_set_granularity(granularity); +} + +int +Sylvan::getGranularity() +{ + return sylvan_get_granularity(); +} + +void +Sylvan::initBdd() +{ + sylvan_init_bdd(); } void diff --git a/sylvan_obj.hpp b/sylvan_obj.hpp old mode 100644 new mode 100755 index 1ea0c2e49d3b72c8fecc4f8e9eb5fc2686bf425b..c4492f876673ab7904708b05cfbbadadbc8d24e7 --- a/sylvan_obj.hpp +++ b/sylvan_obj.hpp @@ -36,7 +36,7 @@ class Bdd { public: Bdd() { bdd = sylvan_false; sylvan_protect(&bdd); } - Bdd(const syBDD from) : bdd(from) { sylvan_protect(&bdd); } + Bdd(const SyBDD from) : bdd(from) { sylvan_protect(&bdd); } Bdd(const Bdd &from) : bdd(from.bdd) { sylvan_protect(&bdd); } Bdd(const uint32_t var) { bdd = sylvan_ithvar(var); sylvan_protect(&bdd); } ~Bdd() { sylvan_unprotect(&bdd); } @@ -217,7 +217,7 @@ public: Bdd RelNext(const Bdd& relation, const BddSet& cube) const; /** - * @brief Computes the transitive closure by traversing the BDD recursively. + * @brief Computes the transitive closure by traversing the SyBDD recursively. * See Y. Matsunaga, P. C. McGeer, R. K. Brayton * On Computing the Transitive Closre of a State Transition Relation * 30th ACM Design Automation Conference, 1993. @@ -230,12 +230,12 @@ public: Bdd Constrain(const Bdd &c) const; /** - * @brief Computes the BDD restrict according to Coudert and Madre's algorithm (ICCAD90). + * @brief Computes the SyBDD restrict according to Coudert and Madre's algorithm (ICCAD90). */ Bdd Restrict(const Bdd &c) const; /** - * @brief Functional composition. Whenever a variable v in the map m is found in the BDD, + * @brief Functional composition. Whenever a variable v in the map m is found in the SyBDD, * it is substituted by the associated function. * You can also use this function to implement variable reordering. */ @@ -252,9 +252,9 @@ public: Bdd Support() const; /** - * @brief Gets the BDD of this Bdd (for C functions) + * @brief Gets the SyBDD of this Bdd (for C functions) */ - syBDD GetBDD() const; + SyBDD GetBDD() const; /** * @brief Writes .dot file of this Bdd. Not thread-safe! @@ -264,7 +264,7 @@ public: /** * @brief Gets a SHA2 hash that describes the structure of this Bdd. * @param string a character array of at least 65 characters (includes zero-termination) - * This hash is 64 characters long and is independent of the memory locations of BDD nodes. + * This hash is 64 characters long and is independent of the memory locations of SyBDD nodes. */ void GetShaHash(char *string) const; @@ -325,7 +325,7 @@ public: size_t NodeCount() const; private: - syBDD bdd; + SyBDD bdd; }; class BddSet @@ -341,7 +341,7 @@ public: BddSet() : set(Bdd::bddOne()) {} /** - * @brief Wrap the BDD cube <other> in a set. + * @brief Wrap the SyBDD cube <other> in a set. */ BddSet(const Bdd &other) : set(other) {} @@ -481,8 +481,8 @@ public: class BddMap { friend class Bdd; - syBDD bdd; - BddMap(const syBDD from) : bdd(from) { sylvan_protect(&bdd); } + SyBDD bdd; + BddMap(const SyBDD from) : bdd(from) { sylvan_protect(&bdd); } BddMap(const Bdd &from) : bdd(from.bdd) { sylvan_protect(&bdd); } public: BddMap() : bdd(sylvan_map_empty()) { sylvan_protect(&bdd); } @@ -826,12 +826,22 @@ public: static void initPackage(size_t initialTableSize, size_t maxTableSize, size_t initialCacheSize, size_t maxCacheSize); /** - * @brief Initializes the BDD module of the Sylvan framework. + * @brief Set the granularity for the SyBDD operations. * @param granularity determins operation cache behavior; for higher values (2+) it will use the operation cache less often. * Values of 3-7 may result in better performance, since occasionally not using the operation cache is fine in practice. - * A granularity of 1 means that every BDD operation will be cached at every variable level. + * A granularity of 1 means that every SyBDD operation will be cached at every variable level. */ - static void initBdd(int granularity); + static void setGranularity(int granularity); + + /** + * @brief Retrieve the granularity for the SyBDD operations. + */ + static int getGranularity(); + + /** + * @brief Initializes the SyBDD module of the Sylvan framework. + */ + static void initBdd(); /** * @brief Initializes the MTBDD module of the Sylvan framework. diff --git a/sylvan_refs.c b/sylvan_refs.c new file mode 100755 index 0000000000000000000000000000000000000000..968721370c6460cb48eb601afd870d4686bd31db --- /dev/null +++ b/sylvan_refs.c @@ -0,0 +1,594 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sylvan.h> +#include <sylvan_refs.h> + +#include <errno.h> // for errno +#include <string.h> // for strerror +#include <sys/mman.h> // for mmap + +#ifndef compiler_barrier +#define compiler_barrier() { asm volatile("" ::: "memory"); } +#endif + +#ifndef cas +#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) +#endif + +/** + * Implementation of external references + * Based on a hash table for 40-bit non-null values, linear probing + * Use tombstones for deleting, higher bits for reference count + */ +static const uint64_t refs_ts = 0x7fffffffffffffff; // tombstone + +/* FNV-1a 64-bit hash */ +static inline uint64_t +fnv_hash(uint64_t a) +{ + const uint64_t prime = 1099511628211; + uint64_t hash = 14695981039346656037LLU; + hash = (hash ^ a) * prime; + hash = (hash ^ ((a << 25) | (a >> 39))) * prime; + return hash ^ (hash >> 32); +} + +// Count number of unique entries (not number of references) +size_t +refs_count(refs_table_t *tbl) +{ + size_t count = 0; + uint64_t *bucket = tbl->refs_table; + uint64_t * const end = bucket + tbl->refs_size; + while (bucket != end) { + if (*bucket != 0 && *bucket != refs_ts) count++; + bucket++; + } + return count; +} + +static inline void +refs_rehash(refs_table_t *tbl, uint64_t v) +{ + if (v == 0) return; // do not rehash empty value + if (v == refs_ts) return; // do not rehash tombstone + + volatile uint64_t *bucket = tbl->refs_table + (fnv_hash(v & 0x000000ffffffffff) % tbl->refs_size); + uint64_t * const end = tbl->refs_table + tbl->refs_size; + + int i = 128; // try 128 times linear probing + while (i--) { + if (*bucket == 0) { if (cas(bucket, 0, v)) return; } + if (++bucket == end) bucket = tbl->refs_table; + } + + // assert(0); // impossible! +} + +/** + * Called internally to assist resize operations + * Returns 1 for retry, 0 for done + */ +static int +refs_resize_help(refs_table_t *tbl) +{ + if (0 == (tbl->refs_control & 0xf0000000)) return 0; // no resize in progress (anymore) + if (tbl->refs_control & 0x80000000) return 1; // still waiting for preparation + + if (tbl->refs_resize_part >= tbl->refs_resize_size / 128) return 1; // all parts claimed + size_t part = __sync_fetch_and_add(&tbl->refs_resize_part, 1); + if (part >= tbl->refs_resize_size/128) return 1; // all parts claimed + + // rehash all + int i; + volatile uint64_t *bucket = tbl->refs_resize_table + part * 128; + for (i=0; i<128; i++) refs_rehash(tbl, *bucket++); + + __sync_fetch_and_add(&tbl->refs_resize_done, 1); + return 1; +} + +static void +refs_resize(refs_table_t *tbl) +{ + while (1) { + uint32_t v = tbl->refs_control; + if (v & 0xf0000000) { + // someone else started resize + // just rehash blocks until done + while (refs_resize_help(tbl)) continue; + return; + } + if (cas(&tbl->refs_control, v, 0x80000000 | v)) { + // wait until all users gone + while (tbl->refs_control != 0x80000000) continue; + break; + } + } + + tbl->refs_resize_table = tbl->refs_table; + tbl->refs_resize_size = tbl->refs_size; + tbl->refs_resize_part = 0; + tbl->refs_resize_done = 0; + + // calculate new size + size_t new_size = tbl->refs_size; + size_t count = refs_count(tbl); + if (count*4 > tbl->refs_size) new_size *= 2; + + // allocate new table + uint64_t *new_table = (uint64_t*)mmap(0, new_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (new_table == (uint64_t*)-1) { + fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } + + // set new data and go + tbl->refs_table = new_table; + tbl->refs_size = new_size; + compiler_barrier(); + tbl->refs_control = 0x40000000; + + // until all parts are done, rehash blocks + while (tbl->refs_resize_done != tbl->refs_resize_size/128) refs_resize_help(tbl); + + // done! + compiler_barrier(); + tbl->refs_control = 0; + + // unmap old table + munmap(tbl->refs_resize_table, tbl->refs_resize_size * sizeof(uint64_t)); +} + +/* Enter refs_modify */ +static inline void +refs_enter(refs_table_t *tbl) +{ + for (;;) { + uint32_t v = tbl->refs_control; + if (v & 0xf0000000) { + while (refs_resize_help(tbl)) continue; + } else { + if (cas(&tbl->refs_control, v, v+1)) return; + } + } +} + +/* Leave refs_modify */ +static inline void +refs_leave(refs_table_t *tbl) +{ + for (;;) { + uint32_t v = tbl->refs_control; + if (cas(&tbl->refs_control, v, v-1)) return; + } +} + +static inline int +refs_modify(refs_table_t *tbl, const uint64_t a, const int dir) +{ + volatile uint64_t *bucket; + volatile uint64_t *ts_bucket; + uint64_t v, new_v; + int res, i; + + refs_enter(tbl); + +ref_retry: + bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); + ts_bucket = NULL; // tombstone + i = 128; // try 128 times linear probing + + while (i--) { +ref_restart: + v = *bucket; + if (v == refs_ts) { + if (ts_bucket == NULL) ts_bucket = bucket; + } else if (v == 0) { + // not found + res = 0; + if (dir < 0) goto ref_exit; + if (ts_bucket != NULL) { + bucket = ts_bucket; + ts_bucket = NULL; + v = refs_ts; + } + new_v = a | (1ULL << 40); + goto ref_mod; + } else if ((v & 0x000000ffffffffff) == a) { + // found + res = 1; + uint64_t count = v >> 40; + if (count == 0x7fffff) goto ref_exit; + count += dir; + if (count == 0) new_v = refs_ts; + else new_v = a | (count << 40); + goto ref_mod; + } + + if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; + } + + // not found after linear probing + if (dir < 0) { + res = 0; + goto ref_exit; + } else if (ts_bucket != NULL) { + bucket = ts_bucket; + ts_bucket = NULL; + v = refs_ts; + new_v = a | (1ULL << 40); + if (!cas(bucket, v, new_v)) goto ref_retry; + res = 1; + goto ref_exit; + } else { + // hash table full + refs_leave(tbl); + refs_resize(tbl); + return refs_modify(tbl, a, dir); + } + +ref_mod: + if (!cas(bucket, v, new_v)) goto ref_restart; + +ref_exit: + refs_leave(tbl); + return res; +} + +void +refs_up(refs_table_t *tbl, uint64_t a) +{ + refs_modify(tbl, a, 1); +} + +void +refs_down(refs_table_t *tbl, uint64_t a) +{ +#ifdef NDEBUG + refs_modify(tbl, a, -1); +#else + int res = refs_modify(tbl, a, -1); + assert(res != 0); +#endif +} + +uint64_t* +refs_iter(refs_table_t *tbl, size_t first, size_t end) +{ + // assert(first < tbl->refs_size); + // assert(end <= tbl->refs_size); + + uint64_t *bucket = tbl->refs_table + first; + while (bucket != tbl->refs_table + end) { + if (*bucket != 0 && *bucket != refs_ts) return bucket; + bucket++; + } + return NULL; +} + +uint64_t +refs_next(refs_table_t *tbl, uint64_t **_bucket, size_t end) +{ + uint64_t *bucket = *_bucket; + // assert(bucket != NULL); + // assert(end <= tbl->refs_size); + uint64_t result = *bucket & 0x000000ffffffffff; + bucket++; + while (bucket != tbl->refs_table + end) { + if (*bucket != 0 && *bucket != refs_ts) { + *_bucket = bucket; + return result; + } + bucket++; + } + *_bucket = NULL; + return result; +} + +void +refs_create(refs_table_t *tbl, size_t _refs_size) +{ + if (__builtin_popcountll(_refs_size) != 1) { + fprintf(stderr, "refs: Table size must be a power of 2!\n"); + exit(1); + } + + tbl->refs_size = _refs_size; + tbl->refs_table = (uint64_t*)mmap(0, tbl->refs_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (tbl->refs_table == (uint64_t*)-1) { + fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } +} + +void +refs_free(refs_table_t *tbl) +{ + munmap(tbl->refs_table, tbl->refs_size * sizeof(uint64_t)); +} + +/** + * Simple implementation of a 64-bit resizable hash-table + * No idea if this is scalable... :( but it seems thread-safe + */ + +// Count number of unique entries (not number of references) +size_t +protect_count(refs_table_t *tbl) +{ + size_t count = 0; + uint64_t *bucket = tbl->refs_table; + uint64_t * const end = bucket + tbl->refs_size; + while (bucket != end) { + if (*bucket != 0 && *bucket != refs_ts) count++; + bucket++; + } + return count; +} + +static inline void +protect_rehash(refs_table_t *tbl, uint64_t v) +{ + if (v == 0) return; // do not rehash empty value + if (v == refs_ts) return; // do not rehash tombstone + + volatile uint64_t *bucket = tbl->refs_table + (fnv_hash(v) % tbl->refs_size); + uint64_t * const end = tbl->refs_table + tbl->refs_size; + + int i = 128; // try 128 times linear probing + while (i--) { + if (*bucket == 0 && cas(bucket, 0, v)) return; + if (++bucket == end) bucket = tbl->refs_table; + } + + assert(0); // whoops! +} + +/** + * Called internally to assist resize operations + * Returns 1 for retry, 0 for done + */ +static int +protect_resize_help(refs_table_t *tbl) +{ + if (0 == (tbl->refs_control & 0xf0000000)) return 0; // no resize in progress (anymore) + if (tbl->refs_control & 0x80000000) return 1; // still waiting for preparation + if (tbl->refs_resize_part >= tbl->refs_resize_size / 128) return 1; // all parts claimed + size_t part = __sync_fetch_and_add(&tbl->refs_resize_part, 1); + if (part >= tbl->refs_resize_size/128) return 1; // all parts claimed + + // rehash all + int i; + volatile uint64_t *bucket = tbl->refs_resize_table + part * 128; + for (i=0; i<128; i++) protect_rehash(tbl, *bucket++); + + __sync_fetch_and_add(&tbl->refs_resize_done, 1); + return 1; +} + +static void +protect_resize(refs_table_t *tbl) +{ + while (1) { + uint32_t v = tbl->refs_control; + if (v & 0xf0000000) { + // someone else started resize + // just rehash blocks until done + while (protect_resize_help(tbl)) continue; + return; + } + if (cas(&tbl->refs_control, v, 0x80000000 | v)) { + // wait until all users gone + while (tbl->refs_control != 0x80000000) continue; + break; + } + } + + tbl->refs_resize_table = tbl->refs_table; + tbl->refs_resize_size = tbl->refs_size; + tbl->refs_resize_part = 0; + tbl->refs_resize_done = 0; + + // calculate new size + size_t new_size = tbl->refs_size; + size_t count = refs_count(tbl); + if (count*4 > tbl->refs_size) new_size *= 2; + + // allocate new table + uint64_t *new_table = (uint64_t*)mmap(0, new_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (new_table == (uint64_t*)-1) { + fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } + + // set new data and go + tbl->refs_table = new_table; + tbl->refs_size = new_size; + compiler_barrier(); + tbl->refs_control = 0x40000000; + + // until all parts are done, rehash blocks + while (tbl->refs_resize_done < tbl->refs_resize_size/128) protect_resize_help(tbl); + + // done! + compiler_barrier(); + tbl->refs_control = 0; + + // unmap old table + munmap(tbl->refs_resize_table, tbl->refs_resize_size * sizeof(uint64_t)); +} + +static inline void +protect_enter(refs_table_t *tbl) +{ + for (;;) { + uint32_t v = tbl->refs_control; + if (v & 0xf0000000) { + while (protect_resize_help(tbl)) continue; + } else { + if (cas(&tbl->refs_control, v, v+1)) return; + } + } +} + +static inline void +protect_leave(refs_table_t *tbl) +{ + for (;;) { + uint32_t v = tbl->refs_control; + if (cas(&tbl->refs_control, v, v-1)) return; + } +} + +void +protect_up(refs_table_t *tbl, uint64_t a) +{ + volatile uint64_t *bucket; + volatile uint64_t *ts_bucket; + uint64_t v; + int i; + + protect_enter(tbl); + +ref_retry: + bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); + ts_bucket = NULL; // tombstone + i = 128; // try 128 times linear probing + + while (i--) { +ref_restart: + v = *bucket; + if (v == refs_ts) { + if (ts_bucket == NULL) ts_bucket = bucket; + } else if (v == 0) { + // go go go + if (ts_bucket != NULL) { + if (cas(ts_bucket, refs_ts, a)) { + protect_leave(tbl); + return; + } else { + goto ref_retry; + } + } else { + if (cas(bucket, 0, a)) { + protect_leave(tbl); + return; + } else { + goto ref_restart; + } + } + } + + if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; + } + + // not found after linear probing + if (ts_bucket != NULL) { + if (cas(ts_bucket, refs_ts, a)) { + protect_leave(tbl); + return; + } else { + goto ref_retry; + } + } else { + // hash table full + protect_leave(tbl); + protect_resize(tbl); + protect_enter(tbl); + goto ref_retry; + } +} + +void +protect_down(refs_table_t *tbl, uint64_t a) +{ + volatile uint64_t *bucket; + protect_enter(tbl); + + bucket = tbl->refs_table + (fnv_hash(a) & (tbl->refs_size - 1)); + int i = 128; // try 128 times linear probing + + while (i--) { + if (*bucket == a) { + *bucket = refs_ts; + protect_leave(tbl); + return; + } + if (++bucket == tbl->refs_table + tbl->refs_size) bucket = tbl->refs_table; + } + + // not found after linear probing + assert(0); +} + +uint64_t* +protect_iter(refs_table_t *tbl, size_t first, size_t end) +{ + // assert(first < tbl->refs_size); + // assert(end <= tbl->refs_size); + + uint64_t *bucket = tbl->refs_table + first; + while (bucket != tbl->refs_table + end) { + if (*bucket != 0 && *bucket != refs_ts) return bucket; + bucket++; + } + return NULL; +} + +uint64_t +protect_next(refs_table_t *tbl, uint64_t **_bucket, size_t end) +{ + uint64_t *bucket = *_bucket; + // assert(bucket != NULL); + // assert(end <= tbl->refs_size); + uint64_t result = *bucket; + bucket++; + while (bucket != tbl->refs_table + end) { + if (*bucket != 0 && *bucket != refs_ts) { + *_bucket = bucket; + return result; + } + bucket++; + } + *_bucket = NULL; + return result; +} + +void +protect_create(refs_table_t *tbl, size_t _refs_size) +{ + if (__builtin_popcountll(_refs_size) != 1) { + fprintf(stderr, "refs: Table size must be a power of 2!\n"); + exit(1); + } + + tbl->refs_size = _refs_size; + tbl->refs_table = (uint64_t*)mmap(0, tbl->refs_size * sizeof(uint64_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (tbl->refs_table == (uint64_t*)-1) { + fprintf(stderr, "refs: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } +} + +void +protect_free(refs_table_t *tbl) +{ + munmap(tbl->refs_table, tbl->refs_size * sizeof(uint64_t)); + tbl->refs_table = 0; +} diff --git a/sylvan_refs.h b/sylvan_refs.h new file mode 100755 index 0000000000000000000000000000000000000000..a0e20d90b7c1978a22140d114f47824954b2a96f --- /dev/null +++ b/sylvan_refs.h @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Do not include this file directly. Instead, include sylvan.h */ + +#ifndef REFS_INLINE_H +#define REFS_INLINE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Implementation of external references + * Based on a hash table for 40-bit non-null values, linear probing + * Use tombstones for deleting, higher bits for reference count + */ +typedef struct +{ + uint64_t *refs_table; // table itself + size_t refs_size; // number of buckets + + /* helpers during resize operation */ + volatile uint32_t refs_control; // control field + uint64_t *refs_resize_table; // previous table + size_t refs_resize_size; // size of previous table + size_t refs_resize_part; // which part is next + size_t refs_resize_done; // how many parts are done +} refs_table_t; + +// Count number of unique entries (not number of references) +size_t refs_count(refs_table_t *tbl); + +// Increase or decrease reference to 40-bit value a +// Will fail (assertion) if more down than up are called for a +void refs_up(refs_table_t *tbl, uint64_t a); +void refs_down(refs_table_t *tbl, uint64_t a); + +// Return a bucket or NULL to start iterating +uint64_t *refs_iter(refs_table_t *tbl, size_t first, size_t end); + +// Continue iterating, set bucket to next bucket or NULL +uint64_t refs_next(refs_table_t *tbl, uint64_t **bucket, size_t end); + +// User must supply a pointer, refs_create and refs_free handle initialization/destruction +void refs_create(refs_table_t *tbl, size_t _refs_size); +void refs_free(refs_table_t *tbl); + +// The same, but now for 64-bit values ("protect pointers") +size_t protect_count(refs_table_t *tbl); +void protect_up(refs_table_t *tbl, uint64_t a); +void protect_down(refs_table_t *tbl, uint64_t a); +uint64_t *protect_iter(refs_table_t *tbl, size_t first, size_t end); +uint64_t protect_next(refs_table_t *tbl, uint64_t **bucket, size_t end); +void protect_create(refs_table_t *tbl, size_t _refs_size); +void protect_free(refs_table_t *tbl); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/sylvan_seq.c b/sylvan_seq.c old mode 100644 new mode 100755 index bf7589cb09acb7649abd711f7b51a7d50523ed5b..ceaa9469a0a7c0deacc1d848867a2435653045cb --- a/sylvan_seq.c +++ b/sylvan_seq.c @@ -14,7 +14,18 @@ * limitations under the License. */ -#include <sylvan_config.h> +#include <sylvan.h> +#include <sylvan_int.h> + +#include <inttypes.h> +#include <math.h> +#include <string.h> +#include <stdint.h> +#include <avl.h> + +#include <sys/mman.h> + +#include <sha2.h> #include <assert.h> #include <inttypes.h> @@ -25,31 +36,27 @@ #include <stdlib.h> #include <string.h> -#include <avl.h> -#include <refs.h> -#include <sha2.h> -#include <sylvan.h> -#include <sylvan_common.h> + /** * MDD node structure */ -typedef struct __attribute__((packed)) mddnode +/*typedef struct __attribute__((packed)) mddnode { uint64_t a, b; -} * mddnode_t; // 16 bytes +} * mddnode_t; // 16 bytes*/ #define GETNODE(mdd) ((mddnode_t)llmsset_index_to_ptr(nodes, mdd)) -static inline uint64_t __attribute__((unused)) +/*static inline uint64_t __attribute__((unused)) mddnode_getright(mddnode_t n) { return (n->a & 0x0000ffffffffffff) >> 1; -} +}*/ -static inline uint64_t __attribute__((unused)) +/*static inline uint64_t __attribute__((unused)) mddnode_getdown(mddnode_t n) { return n->b >> 17; @@ -59,10 +66,10 @@ static inline uint8_t __attribute__((unused)) mddnode_getcopy(mddnode_t n) { return n->b & 0x10000 ? 1 : 0; -} +}*/ -static inline uint32_t __attribute__((unused)) +/*static inline uint32_t __attribute__((unused)) mddnode_getvalue(mddnode_t n) { return *(uint32_t*)((uint8_t*)n+6); @@ -81,7 +88,7 @@ mddnode_makecopy(mddnode_t n, uint64_t right, uint64_t down) n->a = right << 1; n->b = ((down << 1) | 1) << 16; } - +*/ /**************************************************/ /*************************************************/ @@ -100,17 +107,15 @@ MDD ldd_makenode(uint32_t value, MDD ifeq, MDD ifneq) uint64_t index = llmsset_lookup(nodes, n.a, n.b, &created); if (index == 0) { - //lddmc_refs_push(ifeq); - //lddmc_refs_push(ifneq); + /*LACE_ME; + sylvan_gc(); // --- MC*/ - // LACE_ME; - // sylvan_gc(); // --- MC - // lddmc_refs_pop(1); index = llmsset_lookup(nodes, n.a, n.b, &created); if (index == 0) { - // fprintf(stderr, "MDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + fprintf(stderr, "MDD Unique table full!\n"); + exit(1); } } @@ -135,13 +140,13 @@ ldd_make_copynode(MDD ifeq, MDD ifneq) lddmc_refs_push(ifeq); lddmc_refs_push(ifneq); // LACE_ME; - // sylvan_gc(); + //sylvan_gc(); lddmc_refs_pop(1); index = llmsset_lookup(nodes, n.a, n.b, &created); if (index == 0) { - // fprintf(stderr, "MDD Unique table full, %zu of %zu buckets filled!\n", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + fprintf(stderr, "MDD Unique table full, make_copy_node!\n"); exit(1); } } @@ -282,15 +287,7 @@ MDD ldd_minus(MDD a, MDD b) return result; } - - -MDD lddmc_firing_help_mono(uint32_t val, MDD mark, MDD minus, MDD plus) -{ - return ldd_makenode(val,lddmc_firing_mono(mark, minus, plus), lddmc_false); -} - - -MDD lddmc_firing_mono( MDD cmark, MDD minus, MDD plus) +MDD lddmc_firing_mono(MDD cmark, MDD minus, MDD plus) { // for an empty set of source states, or an empty transition relation, return the empty set if (cmark == lddmc_true) return lddmc_true; @@ -309,20 +306,7 @@ MDD lddmc_firing_mono( MDD cmark, MDD minus, MDD plus) mddnode_t n_cmark = GETNODE(_cmark); mddnode_t n_plus = GETNODE(_plus); mddnode_t n_minus = GETNODE(_minus); - // meta: -1 (end; rest not in rel), 0 (not in rel), 1 (read), 2 (write), 3 (only-read), 4 (only-write) - /* Recursive operations */ - - // write, only-write -// if (m_val == 4) { -// // only-write, so we need to include 'for all variables' -// // the reason is that we did not have a read phase, so we need to 'insert' a read phase here -// -// } - - // if we're here and we are only-write, then we read the current value - - // spawn for every value to write (rel) result = lddmc_false; @@ -347,6 +331,17 @@ MDD lddmc_firing_mono( MDD cmark, MDD minus, MDD plus) return result; } + +MDD lddmc_firing_help_mono(uint32_t val, MDD mark, MDD minus, MDD plus) +{ + return ldd_makenode(val,lddmc_firing_mono(mark, minus, plus), lddmc_false); +} + + + + + + void convert_to_string(MDD mdd,char *chaine_mdd) { unsigned int indice=0; @@ -428,8 +423,7 @@ lddmc_project_node(MDD mdd) int get_mddnbr(MDD mdd,int level) { mddnode_t node; - int j; - for (j=0; j<level; j++) + for (int j=0; j<level; j++) { node=GETNODE(mdd); mdd=mddnode_getdown(node); @@ -447,11 +441,11 @@ int get_mddnbr(MDD mdd,int level) return i; } -static inline void __attribute__((unused)) +/*static inline void __attribute__((unused)) mddnode_setdown(mddnode_t n, uint64_t down) { n->b = (n->b & 0x000000000001ffff) | (down << 17); -} +}*/ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) { @@ -459,8 +453,8 @@ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) mddnode_t node=GETNODE(mdd); MDD pred=mdd; - int i; - for (i=0; i<=level; i++) + + for (int i=0; i<=level; i++) { pred=mdd; uint32_t val = mddnode_getvalue(node); @@ -475,14 +469,13 @@ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) mddnode_t node_pred=GETNODE(pred); - //mddnode_t node_pred_right=GETNODE(mddnode_getright(node_pred)); + MDD copy_mdd1=mddnode_getdown(node_pred);//, mddnode_t node_mdd1,node_mdd2; MDD _mdd1=*mdd1; node_mdd1=GETNODE(_mdd1); - int j; - for (j=0; j<level; j++) + for (int j=0; j<level; j++) { _mdd1=mddnode_getdown(node_mdd1); node_mdd1=GETNODE(_mdd1); @@ -493,7 +486,7 @@ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) //mddnode_setdown //ldd_make_copynode - ; + mddnode_setdown(node_mdd1,ldd_make_copynode(copy_mdd1,lddmc_false)); /* FILE *fp0=fopen("copy.dot","w"); @@ -508,8 +501,7 @@ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) *mdd2=lddmc_cube(list_values,level); MDD _mdd2=*mdd2; node_mdd2=GETNODE(_mdd2); - int j; - for (j=0; j<level-1; j++) + for (int j=0; j<level-1; j++) { _mdd2=mddnode_getdown(node_mdd2); node_mdd2=GETNODE(_mdd2); @@ -524,7 +516,8 @@ void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2) int isSingleMDD(MDD mdd) { mddnode_t node; - while (mdd!=lddmc_false) + + while (mdd>lddmc_true) { node=GETNODE(mdd); if (mddnode_getright(node)!=lddmc_false) return 0; @@ -533,13 +526,6 @@ int isSingleMDD(MDD mdd) return 1; } - - -MDD ldd_divide_rec(MDD a, int level) -{ - return ldd_divide_internal(a,0,level); -} - MDD ldd_divide_internal(MDD a,int current_level,int level) { /* Terminal cases */ @@ -548,12 +534,6 @@ MDD ldd_divide_internal(MDD a,int current_level,int level) { MDD result; - /*if (cache_get3(CACHE_MDD_UNION, a, b, 0, &result)) { - sylvan_stats_count(LDD_UNION_CACHED); - return result; - }*/ - - /* Get nodes */ mddnode_t node_a = GETNODE(a); const uint32_t na_value = mddnode_getvalue(node_a); @@ -578,7 +558,16 @@ MDD ldd_divide_internal(MDD a,int current_level,int level) { } -//MDD createCopy() -#ifndef NDEBUG -#endif + +MDD ldd_divide_rec(MDD a, int level) +{ + return ldd_divide_internal(a,0,level); +} + + + + + + + diff --git a/sylvan_seq.h b/sylvan_seq.h old mode 100644 new mode 100755 index e60704c8949d555e0ec6a926aab0f2c313d0d28c..e355166e8fcdbea467ac8e364cc15fe5fdab521b --- a/sylvan_seq.h +++ b/sylvan_seq.h @@ -24,9 +24,10 @@ extern "C" { #endif /* __cplusplus */ MDD ldd_makenode(uint32_t value, MDD ifeq, MDD ifneq); -MDD lddmc_firing_mono(MDD cmark, MDD minus, MDD plus); +MDD lddmc_firing_mono(MDD cmark, MDD _minus, MDD _plus); MDD lddmc_union_mono(MDD a, MDD b); MDD ldd_minus(MDD a, MDD b); +void convert_wholemdd_string(MDD cmark,char **result,unsigned int &length); void convert_to_string(MDD mdd,char *chaine_mdd); void ldd_divide(MDD mdd,const int level,MDD *mdd1,MDD *mdd2); @@ -35,6 +36,8 @@ int get_mddnbr(MDD mdd,int level); int isSingleMDD(MDD mdd); MDD ldd_divide_rec(MDD a, int level); MDD ldd_divide_internal(MDD a,int current_level,int level); + +void sylvan_gc_seq(); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/sylvan_sl.c b/sylvan_sl.c new file mode 100755 index 0000000000000000000000000000000000000000..04812539d899619843d233f3f0b8ab3bf99b5738 --- /dev/null +++ b/sylvan_sl.c @@ -0,0 +1,170 @@ +/* + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sylvan.h> +#include <sylvan_sl.h> + +#include <sys/mman.h> // for mmap, munmap, etc + +/* A SL_DEPTH of 6 means 32 bytes per bucket, of 14 means 64 bytes per bucket. + However, there is a very large performance drop with only 6 levels. */ +#define SL_DEPTH 14 + +typedef struct +{ + SyBDD dd; + uint32_t next[SL_DEPTH]; +} sl_bucket; + +struct sylvan_skiplist +{ + sl_bucket *buckets; + size_t size; + size_t next; +}; + +#ifndef cas +#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) +#endif + +sylvan_skiplist_t +sylvan_skiplist_alloc(size_t size) +{ + if (size >= 0x80000000) { + fprintf(stderr, "sylvan: Trying to allocate a skiplist >= 0x80000000 buckets!\n"); + exit(1); + } + sylvan_skiplist_t l = malloc(sizeof(struct sylvan_skiplist)); + l->buckets = (sl_bucket*)mmap(0, sizeof(sl_bucket)*size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); + if (l->buckets == (sl_bucket*)-1) { + fprintf(stderr, "sylvan: Unable to allocate virtual memory (%'zu bytes) for the skiplist!\n", size*sizeof(sl_bucket)); + exit(1); + } + l->size = size; + l->next = 1; + return l; +} + +void +sylvan_skiplist_free(sylvan_skiplist_t l) +{ + munmap(l->buckets, sizeof(sl_bucket)*l->size); + free(l); +} + +/** + * Return the assigned number of the given dd, + * or 0 if not found. + */ +uint64_t +sylvan_skiplist_get(sylvan_skiplist_t l, MTBDD dd) +{ + if (dd == mtbdd_false || dd == mtbdd_true) return 0; + + uint32_t loc = 0, k = SL_DEPTH-1; + for (;;) { + /* invariant: [loc].dd < dd */ + /* note: this is always true for loc==0 */ + sl_bucket *e = l->buckets + loc; + uint32_t loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff; + if (loc_next != 0 && l->buckets[loc_next].dd == dd) { + /* found */ + return loc_next; + } else if (loc_next != 0 && l->buckets[loc_next].dd < dd) { + /* go right */ + loc = loc_next; + } else if (k > 0) { + /* go down */ + k--; + } else { + return 0; + } + } +} + +VOID_TASK_IMPL_2(sylvan_skiplist_assign_next, sylvan_skiplist_t, l, MTBDD, dd) +{ + if (dd == mtbdd_false || dd == mtbdd_true) return; + + uint32_t trace[SL_DEPTH]; + uint32_t loc = 0, loc_next = 0, k = SL_DEPTH-1; + for (;;) { + /* invariant: [loc].dd < dd */ + /* note: this is always true for loc==0 */ + sl_bucket *e = l->buckets + loc; + loc_next = (*(volatile uint32_t*)&e->next[k]) & 0x7fffffff; + if (loc_next != 0 && l->buckets[loc_next].dd == dd) { + /* found */ + return; + } else if (loc_next != 0 && l->buckets[loc_next].dd < dd) { + /* go right */ + loc = loc_next; + } else if (k > 0) { + /* go down */ + trace[k] = loc; + k--; + } else if (!(e->next[0] & 0x80000000) && cas(&e->next[0], loc_next, loc_next|0x80000000)) { + /* locked */ + break; + } + } + + /* claim next item */ + const uint64_t next = __sync_fetch_and_add(&l->next, 1); + if (next >= l->size) { + fprintf(stderr, "Out of cheese exception, no more blocks available\n"); + exit(1); + } + + /* fill next item */ + sl_bucket *a = l->buckets + next; + a->dd = dd; + a->next[0] = loc_next; + compiler_barrier(); + l->buckets[loc].next[0] = next; + + /* determine height */ + uint64_t h = 1 + __builtin_clz(LACE_TRNG) / 2; + if (h > SL_DEPTH) h = SL_DEPTH; + + /* go up and create links */ + for (k=1;k<h;k++) { + loc = trace[k]; + for (;;) { + sl_bucket *e = l->buckets + loc; + /* note, at k>0, no locks on edges */ + uint32_t loc_next = *(volatile uint32_t*)&e->next[k]; + if (loc_next != 0 && l->buckets[loc_next].dd < dd) { + loc = loc_next; + } else { + a->next[k] = loc_next; + if (cas(&e->next[k], loc_next, next)) break; + } + } + } +} + +size_t +sylvan_skiplist_count(sylvan_skiplist_t l) +{ + return l->next - 1; +} + +MTBDD +sylvan_skiplist_getr(sylvan_skiplist_t l, uint64_t index) +{ + return l->buckets[index].dd; +} diff --git a/sylvan_sl.h b/sylvan_sl.h new file mode 100755 index 0000000000000000000000000000000000000000..ad09c92a30ce849122c5860447d41071fb117a37 --- /dev/null +++ b/sylvan_sl.h @@ -0,0 +1,70 @@ +/* + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYLVAN_SKIPLIST_H +#define SYLVAN_SKIPLIST_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Implementation of a simple limited-depth skiplist. + * The skiplist is used by the serialization mechanism in Sylvan. + * Each stored MTBDD is assigned a number starting with 1. + * Each bucket takes 32 bytes. + */ + +typedef struct sylvan_skiplist *sylvan_skiplist_t; + +/** + * Allocate a new skiplist of maximum size <size>. + * Only supports at most 0x7fffffff (max int32) buckets + */ +sylvan_skiplist_t sylvan_skiplist_alloc(size_t size); + +/** + * Free the given skiplist. + */ +void sylvan_skiplist_free(sylvan_skiplist_t sl); + +/** + * Get the number assigned to the given node <dd>. + * Returns 0 if no number was assigned. + */ +uint64_t sylvan_skiplist_get(sylvan_skiplist_t sl, MTBDD dd); + +/** + * Assign the next number (starting at 1) to the given node <dd>. + */ +VOID_TASK_DECL_2(sylvan_skiplist_assign_next, sylvan_skiplist_t, MTBDD); +#define sylvan_skiplist_assign_next(sl, dd) CALL(sylvan_skiplist_assign_next, sl, dd) + +/** + * Give the number of assigned nodes. (numbers 1,2,...,N) + */ +size_t sylvan_skiplist_count(sylvan_skiplist_t sl); + +/** + * Get the MTBDD assigned to the number <index>, with the index 1,...,count. + */ +MTBDD sylvan_skiplist_getr(sylvan_skiplist_t sl, uint64_t index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/sylvan_stats.c b/sylvan_stats.c new file mode 100755 index 0000000000000000000000000000000000000000..72383410fa0c772082e0af49745a1c8e5ea629df --- /dev/null +++ b/sylvan_stats.c @@ -0,0 +1,287 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sylvan_int.h> + +#include <errno.h> // for errno +#include <string.h> // memset +#include <sys/mman.h> +#include <inttypes.h> + +#if SYLVAN_STATS + +#ifdef __ELF__ +__thread sylvan_stats_t sylvan_stats; +#else +pthread_key_t sylvan_stats_key; +#endif + +/** + * Instructions for sylvan_stats_report + */ +struct +{ + int type; /* 0 for print line, 1 for simple counter, 2 for operation with CACHED and CACHEDPUT */ + /* 3 for timer, 4 for report table data */ + int id; + const char *key; +} sylvan_report_info[] = +{ + {0, 0, "Tables"}, + {1, BDD_NODES_CREATED, "MTBDD nodes created"}, + {1, BDD_NODES_REUSED, "MTBDD nodes reused"}, + {1, LDD_NODES_CREATED, "LDD nodes created"}, + {1, LDD_NODES_REUSED, "LDD nodes reused"}, + {1, LLMSSET_LOOKUP, "Lookup iterations"}, + {4, 0, NULL}, /* trigger to report unique nodes and operation cache */ + + {0, 0, "Operation Count Cache get Cache put"}, + {2, BDD_AND, "SyBDD and"}, + {2, BDD_XOR, "SyBDD xor"}, + {2, BDD_ITE, "SyBDD ite"}, + {2, BDD_EXISTS, "SyBDD exists"}, + {2, BDD_PROJECT, "SyBDD project"}, + {2, BDD_AND_EXISTS, "SyBDD andexists"}, + {2, BDD_AND_PROJECT, "SyBDD andproject"}, + {2, BDD_RELNEXT, "SyBDD relnext"}, + {2, BDD_RELPREV, "SyBDD relprev"}, + {2, BDD_CLOSURE, "SyBDD closure"}, + {2, BDD_COMPOSE, "SyBDD compose"}, + {2, BDD_RESTRICT, "SyBDD restrict"}, + {2, BDD_CONSTRAIN, "SyBDD constrain"}, + {2, BDD_SUPPORT, "SyBDD support"}, + {2, BDD_SATCOUNT, "SyBDD satcount"}, + {2, BDD_PATHCOUNT, "SyBDD pathcount"}, + {2, BDD_ISBDD, "SyBDD isbdd"}, + + {2, MTBDD_APPLY, "MTBDD binary apply"}, + {2, MTBDD_UAPPLY, "MTBDD unary apply"}, + {2, MTBDD_ABSTRACT, "MTBDD abstract"}, + {2, MTBDD_ITE, "MTBDD ite"}, + {2, MTBDD_EQUAL_NORM, "MTBDD eq norm"}, + {2, MTBDD_EQUAL_NORM_REL, "MTBDD eq norm rel"}, + {2, MTBDD_LEQ, "MTBDD leq"}, + {2, MTBDD_LESS, "MTBDD less"}, + {2, MTBDD_GEQ, "MTBDD geq"}, + {2, MTBDD_GREATER, "MTBDD greater"}, + {2, MTBDD_AND_ABSTRACT_PLUS, "MTBDD and_abs_plus"}, + {2, MTBDD_AND_ABSTRACT_MAX, "MTBDD and_abs_max"}, + {2, MTBDD_COMPOSE, "MTBDD compose"}, + {2, MTBDD_MINIMUM, "MTBDD minimum"}, + {2, MTBDD_MAXIMUM, "MTBDD maximum"}, + {2, MTBDD_EVAL_COMPOSE, "MTBDD eval_compose"}, + + {2, LDD_UNION, "LDD union"}, + {2, LDD_MINUS, "LDD minus"}, + {2, LDD_INTERSECT, "LDD intersect"}, + {2, LDD_RELPROD, "LDD relprod"}, + {2, LDD_RELPREV, "LDD relprev"}, + {2, LDD_PROJECT, "LDD project"}, + {2, LDD_JOIN, "LDD join"}, + {2, LDD_MATCH, "LDD match"}, + {2, LDD_SATCOUNT, "LDD satcount"}, + {2, LDD_SATCOUNTL, "LDD satcountl"}, + {2, LDD_ZIP, "LDD zip"}, + {2, LDD_RELPROD_UNION, "LDD relprod_union"}, + {2, LDD_PROJECT_MINUS, "LDD project_minus"}, + + {0, 0, "Garbage collection"}, + {1, SYLVAN_GC_COUNT, "GC executions"}, + {3, SYLVAN_GC, "Total time spent"}, + + {-1, -1, NULL}, +}; + +VOID_TASK_0(sylvan_stats_reset_perthread) +{ +#ifdef __ELF__ + for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { + sylvan_stats.counters[i] = 0; + } + for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { + sylvan_stats.timers[i] = 0; + } +#else + sylvan_stats_t *sylvan_stats = pthread_getspecific(sylvan_stats_key); + if (sylvan_stats == NULL) { + sylvan_stats = mmap(0, sizeof(sylvan_stats_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (sylvan_stats == (sylvan_stats_t *)-1) { + fprintf(stderr, "sylvan_stats: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } + } + pthread_setspecific(sylvan_stats_key, sylvan_stats); + for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { + sylvan_stats->counters[i] = 0; + } + for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { + sylvan_stats->timers[i] = 0; + } +#endif +} + +VOID_TASK_IMPL_0(sylvan_stats_init) +{ +#ifndef __ELF__ + pthread_key_create(&sylvan_stats_key, NULL); +#endif + TOGETHER(sylvan_stats_reset_perthread); +} + +/** + * Reset all counters (for statistics) + */ +VOID_TASK_IMPL_0(sylvan_stats_reset) +{ + TOGETHER(sylvan_stats_reset_perthread); +} + +VOID_TASK_1(sylvan_stats_sum, sylvan_stats_t*, target) +{ +#ifdef __ELF__ + for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { + __sync_fetch_and_add(&target->counters[i], sylvan_stats.counters[i]); + } + for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { + __sync_fetch_and_add(&target->timers[i], sylvan_stats.timers[i]); + } +#else + sylvan_stats_t *sylvan_stats = pthread_getspecific(sylvan_stats_key); + if (sylvan_stats != NULL) { + for (int i=0; i<SYLVAN_COUNTER_COUNTER; i++) { + __sync_fetch_and_add(&target->counters[i], sylvan_stats->counters[i]); + } + for (int i=0; i<SYLVAN_TIMER_COUNTER; i++) { + __sync_fetch_and_add(&target->timers[i], sylvan_stats->timers[i]); + } + } +#endif +} + +VOID_TASK_IMPL_1(sylvan_stats_snapshot, sylvan_stats_t*, target) +{ + memset(target, 0, sizeof(sylvan_stats_t)); + TOGETHER(sylvan_stats_sum, target); +} + +#define BLACK "\33[22;30m" +#define GRAY "\33[1;30m" +#define RED "\33[22;31m" +#define LRED "\33[1;31m" +#define GREEN "\33[22;32m" +#define LGREEN "\33[1;32m" +#define BROWN "\33[22;33m" +#define YELLOW "\33[1;33m" +#define BLUE "\33[22;34m" +#define LBLUE "\33[1;34m" +#define MAGENTA "\33[22;35m" +#define LMAGENTA "\33[1;35m" +#define CYAN "\33[22;36m" +#define LCYAN "\33[1;36m" +#define LGRAY "\33[22;37m" +#define WHITE "\33[1;37m" +#define NC "\33[m" +#define BOLD "\33[1m" +#define ULINE "\33[4m" +#define PINK "\33[38;5;200m" + +static char* +to_h(double size, char *buf) +{ + const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int i = 0; + for (;size>1024;size/=1024) i++; + sprintf(buf, "%.*f %s", i, size, units[i]); + return buf; +} + +void +sylvan_stats_report(FILE *target) +{ + LACE_ME; + sylvan_stats_t totals; + sylvan_stats_snapshot(&totals); + + // fix timers for MACH +#ifdef __MACH__ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t c = timebase.numer/timebase.denom; + for (int i=0;i<SYLVAN_TIMER_COUNTER;i++) totals.timers[i]*=c; +#endif + + int color = isatty(fileno(target)) ? 1 : 0; + if (color) fprintf(target, ULINE WHITE "Sylvan statistics\n" NC); + else fprintf(target, "Sylvan statistics\n"); + + int i=0; + for (;;) { + if (sylvan_report_info[i].id == -1) break; + int id = sylvan_report_info[i].id; + int type = sylvan_report_info[i].type; + if (type == 0) { + if (color) fprintf(target, WHITE "\n%s\n" NC, sylvan_report_info[i].key); + else fprintf(target, "\n%s\n", sylvan_report_info[i].key); + } else if (type == 1) { + if (totals.counters[id] > 0) { + fprintf(target, "%-20s %'-16"PRIu64"\n", sylvan_report_info[i].key, totals.counters[id]); + } + } else if (type == 2) { + if (totals.counters[id] > 0) { + fprintf(target, "%-20s %'-16"PRIu64 " %'-16"PRIu64" %'-16"PRIu64 "\n", sylvan_report_info[i].key, totals.counters[id], totals.counters[id+1], totals.counters[id+2]); + } + } else if (type == 3) { + if (totals.timers[id] > 0) { + fprintf(target, "%-20s %'.6Lf sec.\n", sylvan_report_info[i].key, (long double)totals.timers[id]/1000000000); + } + } else if (type == 4) { + fprintf(target, "%-20s %'zu of %'zu buckets filled.\n", "Unique nodes table", llmsset_count_marked(nodes), llmsset_get_size(nodes)); + fprintf(target, "%-20s %'zu of %'zu buckets filled.\n", "Operation cache", cache_getused(), cache_getsize()); + char buf[64], buf2[64]; + to_h(24ULL * llmsset_get_size(nodes), buf); + to_h(24ULL * llmsset_get_max_size(nodes), buf2); + fprintf(target, "%-20s %s (max real) of %s (allocated virtual memory).\n", "Memory (nodes)", buf, buf2); + to_h(36ULL * cache_getsize(), buf); + to_h(36ULL * cache_getmaxsize(), buf2); + fprintf(target, "%-20s %s (max real) of %s (allocated virtual memory).\n", "Memory (cache)", buf, buf2); + } + i++; + } +} + +#else + +VOID_TASK_IMPL_0(sylvan_stats_init) +{ +} + +VOID_TASK_IMPL_0(sylvan_stats_reset) +{ +} + +VOID_TASK_IMPL_1(sylvan_stats_snapshot, sylvan_stats_t*, target) +{ + memset(target, 0, sizeof(sylvan_stats_t)); +} + +void +sylvan_stats_report(FILE* target) +{ + (void)target; +} + +#endif diff --git a/sylvan_stats.h b/sylvan_stats.h new file mode 100755 index 0000000000000000000000000000000000000000..bac2ba0103e256cf28c36fa58e15c70563079de6 --- /dev/null +++ b/sylvan_stats.h @@ -0,0 +1,239 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Do not include this file directly. Instead, include sylvan.h */ + +#ifndef SYLVAN_STATS_H +#define SYLVAN_STATS_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define OPCOUNTER(NAME) NAME, NAME ## _CACHEDPUT, NAME ## _CACHED + +typedef enum { + /* Creating nodes */ + BDD_NODES_CREATED, + BDD_NODES_REUSED, + LDD_NODES_CREATED, + LDD_NODES_REUSED, + + /* SyBDD operations */ + OPCOUNTER(BDD_ITE), + OPCOUNTER(BDD_AND), + OPCOUNTER(BDD_XOR), + OPCOUNTER(BDD_EXISTS), + OPCOUNTER(BDD_PROJECT), + OPCOUNTER(BDD_AND_EXISTS), + OPCOUNTER(BDD_AND_PROJECT), + OPCOUNTER(BDD_RELNEXT), + OPCOUNTER(BDD_RELPREV), + OPCOUNTER(BDD_SATCOUNT), + OPCOUNTER(BDD_COMPOSE), + OPCOUNTER(BDD_RESTRICT), + OPCOUNTER(BDD_CONSTRAIN), + OPCOUNTER(BDD_CLOSURE), + OPCOUNTER(BDD_ISBDD), + OPCOUNTER(BDD_SUPPORT), + OPCOUNTER(BDD_PATHCOUNT), + + /* MTBDD operations */ + OPCOUNTER(MTBDD_APPLY), + OPCOUNTER(MTBDD_UAPPLY), + OPCOUNTER(MTBDD_ABSTRACT), + OPCOUNTER(MTBDD_ITE), + OPCOUNTER(MTBDD_EQUAL_NORM), + OPCOUNTER(MTBDD_EQUAL_NORM_REL), + OPCOUNTER(MTBDD_LEQ), + OPCOUNTER(MTBDD_LESS), + OPCOUNTER(MTBDD_GEQ), + OPCOUNTER(MTBDD_GREATER), + OPCOUNTER(MTBDD_AND_ABSTRACT_PLUS), + OPCOUNTER(MTBDD_AND_ABSTRACT_MAX), + OPCOUNTER(MTBDD_COMPOSE), + OPCOUNTER(MTBDD_MINIMUM), + OPCOUNTER(MTBDD_MAXIMUM), + OPCOUNTER(MTBDD_EVAL_COMPOSE), + + /* LDD operations */ + OPCOUNTER(LDD_UNION), + OPCOUNTER(LDD_MINUS), + OPCOUNTER(LDD_INTERSECT), + OPCOUNTER(LDD_RELPROD), + OPCOUNTER(LDD_RELPREV), + OPCOUNTER(LDD_PROJECT), + OPCOUNTER(LDD_JOIN), + OPCOUNTER(LDD_MATCH), + OPCOUNTER(LDD_SATCOUNT), + OPCOUNTER(LDD_SATCOUNTL), + OPCOUNTER(LDD_ZIP), + OPCOUNTER(LDD_RELPROD_UNION), + OPCOUNTER(LDD_PROJECT_MINUS), + + /* Other counters */ + SYLVAN_GC_COUNT, + LLMSSET_LOOKUP, + + SYLVAN_COUNTER_COUNTER +} Sylvan_Counters; + +#undef OPCOUNTER + +typedef enum +{ + SYLVAN_GC, + SYLVAN_TIMER_COUNTER +} Sylvan_Timers; + +typedef struct +{ + uint64_t counters[SYLVAN_COUNTER_COUNTER]; + /* the timers are in ns */ + uint64_t timers[SYLVAN_TIMER_COUNTER]; + /* startstop is for internal use */ + uint64_t timers_startstop[SYLVAN_TIMER_COUNTER]; +} sylvan_stats_t; + +/** + * Initialize stats system (done by sylvan_init_package) + */ +VOID_TASK_DECL_0(sylvan_stats_init); +#define sylvan_stats_init() CALL(sylvan_stats_init) + +/** + * Reset all counters (for statistics) + */ +VOID_TASK_DECL_0(sylvan_stats_reset); +#define sylvan_stats_reset() CALL(sylvan_stats_reset) + +/** + * Obtain current counts (this stops the world during counting) + */ +VOID_TASK_DECL_1(sylvan_stats_snapshot, sylvan_stats_t*); +#define sylvan_stats_snapshot(target) CALL(sylvan_stats_snapshot, target) + +/** + * Write statistic report to file (stdout, stderr, etc) + */ +void sylvan_stats_report(FILE* target); + +#if SYLVAN_STATS + +#ifdef __MACH__ +#define getabstime() mach_absolute_time() +#else +static uint64_t +getabstime(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t t = ts.tv_sec; + t *= 1000000000UL; + t += ts.tv_nsec; + return t; +} +#endif + +#ifdef __ELF__ +extern __thread sylvan_stats_t sylvan_stats; +#else +extern pthread_key_t sylvan_stats_key; +#endif + +static inline void +sylvan_stats_count(size_t counter) +{ +#ifdef __ELF__ + sylvan_stats.counters[counter]++; +#else + sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); + sylvan_stats->counters[counter]++; +#endif +} + +static inline void +sylvan_stats_add(size_t counter, size_t amount) +{ +#ifdef __ELF__ + sylvan_stats.counters[counter]+=amount; +#else + sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); + sylvan_stats->counters[counter]+=amount; +#endif +} + +static inline void +sylvan_timer_start(size_t timer) +{ + uint64_t t = getabstime(); + +#ifdef __ELF__ + sylvan_stats.timers_startstop[timer] = t; +#else + sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); + sylvan_stats->timers_startstop[timer] = t; +#endif +} + +static inline void +sylvan_timer_stop(size_t timer) +{ + uint64_t t = getabstime(); + +#ifdef __ELF__ + sylvan_stats.timers[timer] += (t - sylvan_stats.timers_startstop[timer]); +#else + sylvan_stats_t *sylvan_stats = (sylvan_stats_t*)pthread_getspecific(sylvan_stats_key); + sylvan_stats->timers[timer] += (t - sylvan_stats->timers_startstop[timer]); +#endif +} + +#else + +static inline void +sylvan_stats_count(size_t counter) +{ + (void)counter; +} + +static inline void +sylvan_stats_add(size_t counter, size_t amount) +{ + (void)counter; + (void)amount; +} + +static inline void +sylvan_timer_start(size_t timer) +{ + (void)timer; +} + +static inline void +sylvan_timer_stop(size_t timer) +{ + (void)timer; +} + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/sylvan_table.c b/sylvan_table.c new file mode 100755 index 0000000000000000000000000000000000000000..623dadce91374b49aa70477dba8eedff53436b31 --- /dev/null +++ b/sylvan_table.c @@ -0,0 +1,645 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sylvan_int.h> + +#include <errno.h> // for errno +#include <string.h> // memset +#include <sys/mman.h> // for mmap + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +#ifndef cas +#define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new))) +#endif + +DECLARE_THREAD_LOCAL(my_region, uint64_t); + +VOID_TASK_0(llmsset_reset_region) +{ + LOCALIZE_THREAD_LOCAL(my_region, uint64_t); + my_region = (uint64_t)-1; // no region + SET_THREAD_LOCAL(my_region, my_region); +} + +static uint64_t +claim_data_bucket(const llmsset_t dbs) +{ + LOCALIZE_THREAD_LOCAL(my_region, uint64_t); + + for (;;) { + if (my_region != (uint64_t)-1) { + // find empty bucket in region <my_region> + uint64_t *ptr = dbs->bitmap2 + (my_region*8); + int i=0; + for (;i<8;) { + uint64_t v = *ptr; + if (v != 0xffffffffffffffffLL) { + int j = __builtin_clzll(~v); + *ptr |= (0x8000000000000000LL>>j); + return (8 * my_region + i) * 64 + j; + } + i++; + ptr++; + } + } else { + // special case on startup or after garbage collection + my_region += (lace_get_worker()->worker*(dbs->table_size/(64*8)))/lace_workers(); + } + uint64_t count = dbs->table_size/(64*8); + for (;;) { + // check if table maybe full + if (count-- == 0) return (uint64_t)-1; + + my_region += 1; + if (my_region >= (dbs->table_size/(64*8))) my_region = 0; + + // try to claim it + uint64_t *ptr = dbs->bitmap1 + (my_region/64); + uint64_t mask = 0x8000000000000000LL >> (my_region&63); + uint64_t v; +restart: + v = *ptr; + if (v & mask) continue; // taken + if (cas(ptr, v, v|mask)) break; + else goto restart; + } + SET_THREAD_LOCAL(my_region, my_region); + } +} + +static void +release_data_bucket(const llmsset_t dbs, uint64_t index) +{ + uint64_t *ptr = dbs->bitmap2 + (index/64); + uint64_t mask = 0x8000000000000000LL >> (index&63); + *ptr &= ~mask; +} + +static void +set_custom_bucket(const llmsset_t dbs, uint64_t index, int on) +{ + uint64_t *ptr = dbs->bitmapc + (index/64); + uint64_t mask = 0x8000000000000000LL >> (index&63); + if (on) *ptr |= mask; + else *ptr &= ~mask; +} + +static int +is_custom_bucket(const llmsset_t dbs, uint64_t index) +{ + uint64_t *ptr = dbs->bitmapc + (index/64); + uint64_t mask = 0x8000000000000000LL >> (index&63); + return (*ptr & mask) ? 1 : 0; +} + +uint64_t +llmsset_hash(const uint64_t a, const uint64_t b, const uint64_t seed) +{ + // The FNV-1a hash for 64 bits + const uint64_t prime = 1099511628211; + uint64_t hash = seed; + hash = (hash ^ a) * prime; + hash = (hash ^ b) * prime; + return hash ^ (hash>>32); +} + +/* + * CL_MASK and CL_MASK_R are for the probe sequence calculation. + * With 64 bytes per cacheline, there are 8 64-bit values per cacheline. + */ +// The LINE_SIZE is defined in lace.h +static const uint64_t CL_MASK = ~(((LINE_SIZE) / 8) - 1); +static const uint64_t CL_MASK_R = ((LINE_SIZE) / 8) - 1; + +/* 40 bits for the index, 24 bits for the hash */ +#define MASK_INDEX ((uint64_t)0x000000ffffffffff) +#define MASK_HASH ((uint64_t)0xffffff0000000000) + +static inline uint64_t +llmsset_lookup2(const llmsset_t dbs, uint64_t a, uint64_t b, int* created, const int custom) +{ + uint64_t hash_rehash = 14695981039346656037LLU; + if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); + else hash_rehash = llmsset_hash(a, b, hash_rehash); + + const uint64_t step = (((hash_rehash >> 20) | 1) << 3); + const uint64_t hash = hash_rehash & MASK_HASH; + uint64_t idx, last, cidx = 0; + int i=0; + +#if LLMSSET_MASK + last = idx = hash_rehash & dbs->mask; +#else + last = idx = hash_rehash % dbs->table_size; +#endif + + for (;;) { + volatile uint64_t *bucket = dbs->table + idx; + uint64_t v = *bucket; + + if (v == 0) { + if (cidx == 0) { + // Claim data bucket and write data + cidx = claim_data_bucket(dbs); + if (cidx == (uint64_t)-1) return 0; // failed to claim a data bucket + if (custom) dbs->create_cb(&a, &b); + uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*cidx; + d_ptr[0] = a; + d_ptr[1] = b; + } + if (cas(bucket, 0, hash | cidx)) { + if (custom) set_custom_bucket(dbs, cidx, custom); + *created = 1; + return cidx; + } else { + v = *bucket; + } + } + + if (hash == (v & MASK_HASH)) { + uint64_t d_idx = v & MASK_INDEX; + uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*d_idx; + if (custom) { + if (dbs->equals_cb(a, b, d_ptr[0], d_ptr[1])) { + if (cidx != 0) { + dbs->destroy_cb(a, b); + release_data_bucket(dbs, cidx); + } + *created = 0; + return d_idx; + } + } else { + if (d_ptr[0] == a && d_ptr[1] == b) { + if (cidx != 0) release_data_bucket(dbs, cidx); + *created = 0; + return d_idx; + } + } + } + + sylvan_stats_count(LLMSSET_LOOKUP); + + // find next idx on probe sequence + idx = (idx & CL_MASK) | ((idx+1) & CL_MASK_R); + if (idx == last) { + if (++i == dbs->threshold) return 0; // failed to find empty spot in probe sequence + + // go to next cache line in probe sequence + hash_rehash += step; + +#if LLMSSET_MASK + last = idx = hash_rehash & dbs->mask; +#else + last = idx = hash_rehash % dbs->table_size; +#endif + } + } +} + +uint64_t +llmsset_lookup(const llmsset_t dbs, const uint64_t a, const uint64_t b, int* created) +{ + return llmsset_lookup2(dbs, a, b, created, 0); +} + +uint64_t +llmsset_lookupc(const llmsset_t dbs, const uint64_t a, const uint64_t b, int* created) +{ + return llmsset_lookup2(dbs, a, b, created, 1); +} + +int +llmsset_rehash_bucket(const llmsset_t dbs, uint64_t d_idx) +{ + const uint64_t * const d_ptr = ((uint64_t*)dbs->data) + 2*d_idx; + const uint64_t a = d_ptr[0]; + const uint64_t b = d_ptr[1]; + + uint64_t hash_rehash = 14695981039346656037LLU; + const int custom = is_custom_bucket(dbs, d_idx) ? 1 : 0; + if (custom) hash_rehash = dbs->hash_cb(a, b, hash_rehash); + else hash_rehash = llmsset_hash(a, b, hash_rehash); + const uint64_t step = (((hash_rehash >> 20) | 1) << 3); + const uint64_t new_v = (hash_rehash & MASK_HASH) | d_idx; + int i=0; + + uint64_t idx, last; +#if LLMSSET_MASK + last = idx = hash_rehash & dbs->mask; +#else + last = idx = hash_rehash % dbs->table_size; +#endif + + for (;;) { + volatile uint64_t *bucket = &dbs->table[idx]; + if (*bucket == 0 && cas(bucket, 0, new_v)) return 1; + + // find next idx on probe sequence + idx = (idx & CL_MASK) | ((idx+1) & CL_MASK_R); + if (idx == last) { + if (++i == *(volatile int16_t*)&dbs->threshold) { + // failed to find empty spot in probe sequence + // solution: increase probe sequence length... + __sync_fetch_and_add(&dbs->threshold, 1); + } + + // go to next cache line in probe sequence + hash_rehash += step; + +#if LLMSSET_MASK + last = idx = hash_rehash & dbs->mask; +#else + last = idx = hash_rehash % dbs->table_size; +#endif + } + } +} + +llmsset_t +llmsset_create(size_t initial_size, size_t max_size) +{ + llmsset_t dbs = NULL; + if (posix_memalign((void**)&dbs, LINE_SIZE, sizeof(struct llmsset)) != 0) { + fprintf(stderr, "llmsset_create: Unable to allocate memory!\n"); + exit(1); + } + +#if LLMSSET_MASK + /* Check if initial_size and max_size are powers of 2 */ + if (__builtin_popcountll(initial_size) != 1) { + fprintf(stderr, "llmsset_create: initial_size is not a power of 2!\n"); + exit(1); + } + + if (__builtin_popcountll(max_size) != 1) { + fprintf(stderr, "llmsset_create: max_size is not a power of 2!\n"); + exit(1); + } +#endif + + if (initial_size > max_size) { + fprintf(stderr, "llmsset_create: initial_size > max_size!\n"); + exit(1); + } + + // minimum size is now 512 buckets (region size, but of course, n_workers * 512 is suggested as minimum) + + if (initial_size < 512) { + fprintf(stderr, "llmsset_create: initial_size too small!\n"); + exit(1); + } + + dbs->max_size = max_size; + llmsset_set_size(dbs, initial_size); + + /* This implementation of "resizable hash table" allocates the max_size table in virtual memory, + but only uses the "actual size" part in real memory */ + + dbs->table = (uint64_t*)mmap(0, dbs->max_size * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + dbs->data = (uint8_t*)mmap(0, dbs->max_size * 16, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + /* Also allocate bitmaps. Each region is 64*8 = 512 buckets. + Overhead of bitmap1: 1 bit per 4096 bucket. + Overhead of bitmap2: 1 bit per bucket. + Overhead of bitmapc: 1 bit per bucket. */ + + dbs->bitmap1 = (uint64_t*)mmap(0, dbs->max_size / (512*8), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + dbs->bitmap2 = (uint64_t*)mmap(0, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + dbs->bitmapc = (uint64_t*)mmap(0, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (dbs->table == (uint64_t*)-1 || dbs->data == (uint8_t*)-1 || dbs->bitmap1 == (uint64_t*)-1 || dbs->bitmap2 == (uint64_t*)-1 || dbs->bitmapc == (uint64_t*)-1) { + fprintf(stderr, "llmsset_create: Unable to allocate memory: %s!\n", strerror(errno)); + exit(1); + } + +#if defined(madvise) && defined(MADV_RANDOM) + madvise(dbs->table, dbs->max_size * 8, MADV_RANDOM); +#endif + + // forbid first two positions (index 0 and 1) + dbs->bitmap2[0] = 0xc000000000000000LL; + + dbs->hash_cb = NULL; + dbs->equals_cb = NULL; + dbs->create_cb = NULL; + dbs->destroy_cb = NULL; + + // yes, ugly. for now, we use a global thread-local value. + // that is a problem with multiple tables. + // so, for now, do NOT use multiple tables!! + + LACE_ME; + INIT_THREAD_LOCAL(my_region); + TOGETHER(llmsset_reset_region); + + return dbs; +} + +void +llmsset_free(llmsset_t dbs) +{ + munmap(dbs->table, dbs->max_size * 8); + munmap(dbs->data, dbs->max_size * 16); + munmap(dbs->bitmap1, dbs->max_size / (512*8)); + munmap(dbs->bitmap2, dbs->max_size / 8); + munmap(dbs->bitmapc, dbs->max_size / 8); + free(dbs); +} + +VOID_TASK_IMPL_1(llmsset_clear, llmsset_t, dbs) +{ + CALL(llmsset_clear_data, dbs); + CALL(llmsset_clear_hashes, dbs); +} + +VOID_TASK_IMPL_1(llmsset_clear_data, llmsset_t, dbs) +{ + if (mmap(dbs->bitmap1, dbs->max_size / (512*8), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { + } else { + memset(dbs->bitmap1, 0, dbs->max_size / (512*8)); + } + + if (mmap(dbs->bitmap2, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { + } else { + memset(dbs->bitmap2, 0, dbs->max_size / 8); + } + + // forbid first two positions (index 0 and 1) + dbs->bitmap2[0] = 0xc000000000000000LL; + + TOGETHER(llmsset_reset_region); +} + +VOID_TASK_IMPL_1(llmsset_clear_hashes, llmsset_t, dbs) +{ + // just reallocate... + if (mmap(dbs->table, dbs->max_size * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { +#if defined(madvise) && defined(MADV_RANDOM) + madvise(dbs->table, sizeof(uint64_t[dbs->max_size]), MADV_RANDOM); +#endif + } else { + // reallocate failed... expensive fallback + memset(dbs->table, 0, dbs->max_size * 8); + } +} + +int +llmsset_is_marked(const llmsset_t dbs, uint64_t index) +{ + volatile uint64_t *ptr = dbs->bitmap2 + (index/64); + uint64_t mask = 0x8000000000000000LL >> (index&63); + return (*ptr & mask) ? 1 : 0; +} + +int +llmsset_mark(const llmsset_t dbs, uint64_t index) +{ + volatile uint64_t *ptr = dbs->bitmap2 + (index/64); + uint64_t mask = 0x8000000000000000LL >> (index&63); + for (;;) { + uint64_t v = *ptr; + if (v & mask) return 0; + if (cas(ptr, v, v|mask)) return 1; + } +} + +TASK_3(int, llmsset_rehash_par, llmsset_t, dbs, size_t, first, size_t, count) +{ + if (count > 512) { + SPAWN(llmsset_rehash_par, dbs, first, count/2); + int bad = CALL(llmsset_rehash_par, dbs, first + count/2, count - count/2); + return bad + SYNC(llmsset_rehash_par); + } else { + int bad = 0; + uint64_t *ptr = dbs->bitmap2 + (first / 64); + uint64_t mask = 0x8000000000000000LL >> (first & 63); + for (size_t k=0; k<count; k++) { + if (*ptr & mask) { + if (llmsset_rehash_bucket(dbs, first+k) == 0) bad++; + } + mask >>= 1; + if (mask == 0) { + ptr++; + mask = 0x8000000000000000LL; + } + } + return bad; + } +} + +TASK_IMPL_1(int, llmsset_rehash, llmsset_t, dbs) +{ + return CALL(llmsset_rehash_par, dbs, 0, dbs->table_size); +} + +TASK_3(size_t, llmsset_count_marked_par, llmsset_t, dbs, size_t, first, size_t, count) +{ + if (count > 512) { + size_t split = count/2; + SPAWN(llmsset_count_marked_par, dbs, first, split); + size_t right = CALL(llmsset_count_marked_par, dbs, first + split, count - split); + size_t left = SYNC(llmsset_count_marked_par); + return left + right; + } else { + size_t result = 0; + uint64_t *ptr = dbs->bitmap2 + (first / 64); + if (count == 512) { + result += __builtin_popcountll(ptr[0]); + result += __builtin_popcountll(ptr[1]); + result += __builtin_popcountll(ptr[2]); + result += __builtin_popcountll(ptr[3]); + result += __builtin_popcountll(ptr[4]); + result += __builtin_popcountll(ptr[5]); + result += __builtin_popcountll(ptr[6]); + result += __builtin_popcountll(ptr[7]); + } else { + uint64_t mask = 0x8000000000000000LL >> (first & 63); + for (size_t k=0; k<count; k++) { + if (*ptr & mask) result += 1; + mask >>= 1; + if (mask == 0) { + ptr++; + mask = 0x8000000000000000LL; + } + } + } + return result; + } +} + +TASK_IMPL_1(size_t, llmsset_count_marked, llmsset_t, dbs) +{ + return CALL(llmsset_count_marked_par, dbs, 0, dbs->table_size); +} + +VOID_TASK_3(llmsset_destroy_par, llmsset_t, dbs, size_t, first, size_t, count) +{ + if (count > 1024) { + size_t split = count/2; + SPAWN(llmsset_destroy_par, dbs, first, split); + CALL(llmsset_destroy_par, dbs, first + split, count - split); + SYNC(llmsset_destroy_par); + } else { + for (size_t k=first; k<first+count; k++) { + volatile uint64_t *ptr2 = dbs->bitmap2 + (k/64); + volatile uint64_t *ptrc = dbs->bitmapc + (k/64); + uint64_t mask = 0x8000000000000000LL >> (k&63); + + // if not marked but is custom + if ((*ptr2 & mask) == 0 && (*ptrc & mask)) { + uint64_t *d_ptr = ((uint64_t*)dbs->data) + 2*k; + dbs->destroy_cb(d_ptr[0], d_ptr[1]); + *ptrc &= ~mask; + } + } + } +} + +VOID_TASK_IMPL_1(llmsset_destroy_unmarked, llmsset_t, dbs) +{ + if (dbs->destroy_cb == NULL) return; // no custom function + CALL(llmsset_destroy_par, dbs, 0, dbs->table_size); +} + +/** + * Set custom functions + */ +void llmsset_set_custom(const llmsset_t dbs, llmsset_hash_cb hash_cb, llmsset_equals_cb equals_cb, llmsset_create_cb create_cb, llmsset_destroy_cb destroy_cb) +{ + dbs->hash_cb = hash_cb; + dbs->equals_cb = equals_cb; + dbs->create_cb = create_cb; + dbs->destroy_cb = destroy_cb; +} + +/***************************************************************/ + +void llmsset_clear_data_seq(llmsset_t dbs) +{ + if (mmap(dbs->bitmap1, dbs->max_size / (512*8), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { + + } else { + + memset(dbs->bitmap1, 0, dbs->max_size / (512*8)); + } + + if (mmap(dbs->bitmap2, dbs->max_size / 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { + + } else { + memset(dbs->bitmap2, 0, dbs->max_size / 8); + + } + + // forbid first two positions (index 0 and 1) + dbs->bitmap2[0] = 0xc000000000000000LL; + /*LACE_ME; + TOGETHER(llmsset_reset_region);*/ +} + + + +void llmsset_clear_hashes_seq( llmsset_t dbs) +{ + // just reallocate... + if (mmap(dbs->table, dbs->max_size * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != (void*)-1) { +#if defined(madvise) && defined(MADV_RANDOM) + madvise(dbs->table, sizeof(uint64_t[dbs->max_size]), MADV_RANDOM); +#endif + } else { + // reallocate failed... expensive fallback + memset(dbs->table, 0, dbs->max_size * 8); + } +} + + + +int llmsset_rehash_seq (llmsset_t dbs) +{ + return llmsset_rehash_par_seq( dbs, 0, dbs->table_size); +} + + +int llmsset_rehash_par_seq(llmsset_t dbs, size_t first, size_t count) +{ + if (count > 512) { + + int bad = llmsset_rehash_par_seq(dbs, first + count/2, count - count/2); + return bad + llmsset_rehash_par_seq(dbs, first, count/2); + } else { + int bad = 0; + uint64_t *ptr = dbs->bitmap2 + (first / 64); + uint64_t mask = 0x8000000000000000LL >> (first & 63); + for (size_t k=0; k<count; k++) { + if (*ptr & mask) { + if (llmsset_rehash_bucket(dbs, first+k) == 0) bad++; + } + mask >>= 1; + if (mask == 0) { + ptr++; + mask = 0x8000000000000000LL; + } + } + return bad; + } +} + + + +/************** Sequential versions ************/ +size_t seq_llmsset_count_marked(llmsset_t dbs) +{ + return llmsset_count_marked_seq (dbs, 0, dbs->table_size); +} + +//******************************************** +size_t llmsset_count_marked_seq(llmsset_t dbs, size_t first, size_t count) +{ + if (count > 512) { + size_t split = count/2; + + size_t right = llmsset_count_marked_seq( dbs, first + split, count - split); + size_t left = llmsset_count_marked_seq( dbs, first, split); + return left + right; + } else { + size_t result = 0; + uint64_t *ptr = dbs->bitmap2 + (first / 64); + if (count == 512) { + result += __builtin_popcountll(ptr[0]); + result += __builtin_popcountll(ptr[1]); + result += __builtin_popcountll(ptr[2]); + result += __builtin_popcountll(ptr[3]); + result += __builtin_popcountll(ptr[4]); + result += __builtin_popcountll(ptr[5]); + result += __builtin_popcountll(ptr[6]); + result += __builtin_popcountll(ptr[7]); + } else { + uint64_t mask = 0x8000000000000000LL >> (first & 63); + for (size_t k=0; k<count; k++) { + if (*ptr & mask) result += 1; + mask >>= 1; + if (mask == 0) { + ptr++; + mask = 0x8000000000000000LL; + } + } + } + return result; + } +} diff --git a/sylvan_table.h b/sylvan_table.h new file mode 100755 index 0000000000000000000000000000000000000000..c0495a4f34e5efa7385c1a653e8a37adb61d977d --- /dev/null +++ b/sylvan_table.h @@ -0,0 +1,218 @@ +/* + * Copyright 2011-2016 Formal Methods and Tools, University of Twente + * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Do not include this file directly. Instead, include sylvan_int.h */ + +#ifndef SYLVAN_TABLE_H +#define SYLVAN_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Lockless hash table (set) to store 16-byte keys. + * Each unique key is associated with a 42-bit number. + * + * The set has support for stop-the-world garbage collection. + * Methods llmsset_clear, llmsset_mark and llmsset_rehash implement garbage collection. + * During their execution, llmsset_lookup is not allowed. + * + * WARNING: Originally, this table is designed to allow multiple tables. + * However, this is not compatible with thread local storage for now. + * Do not use multiple tables. + */ + +/** + * hash(a, b, seed) + * equals(lhs_a, lhs_b, rhs_a, rhs_b) + * create(a, b) -- with a,b pointers, allows changing pointers on create of node, + * but must keep hash/equals same! + * destroy(a, b) + */ +typedef uint64_t (*llmsset_hash_cb)(uint64_t, uint64_t, uint64_t); +typedef int (*llmsset_equals_cb)(uint64_t, uint64_t, uint64_t, uint64_t); +typedef void (*llmsset_create_cb)(uint64_t *, uint64_t *); +typedef void (*llmsset_destroy_cb)(uint64_t, uint64_t); + +typedef struct llmsset +{ + uint64_t *table; // table with hashes + uint8_t *data; // table with values + uint64_t *bitmap1; // ownership bitmap (per 512 buckets) + uint64_t *bitmap2; // bitmap for "contains data" + uint64_t *bitmapc; // bitmap for "use custom functions" + size_t max_size; // maximum size of the hash table (for resizing) + size_t table_size; // size of the hash table (number of slots) --> power of 2! +#if LLMSSET_MASK + size_t mask; // size-1 +#endif + size_t f_size; + llmsset_hash_cb hash_cb; // custom hash function + llmsset_equals_cb equals_cb; // custom equals function + llmsset_create_cb create_cb; // custom create function + llmsset_destroy_cb destroy_cb; // custom destroy function + int16_t threshold; // number of iterations for insertion until returning error +} *llmsset_t; + +/** + * Retrieve a pointer to the data associated with the 42-bit value. + */ +static inline void* +llmsset_index_to_ptr(const llmsset_t dbs, size_t index) +{ + return dbs->data + index * 16; +} + +/** + * Create the set. + * This will allocate a set of <max_size> buckets in virtual memory. + * The actual space used is <initial_size> buckets. + */ +llmsset_t llmsset_create(size_t initial_size, size_t max_size); + +/** + * Free the set. + */ +void llmsset_free(llmsset_t dbs); + +/** + * Retrieve the maximum size of the set. + */ +static inline size_t +llmsset_get_max_size(const llmsset_t dbs) +{ + return dbs->max_size; +} + +/** + * Retrieve the current size of the lockless MS set. + */ +static inline size_t +llmsset_get_size(const llmsset_t dbs) +{ + return dbs->table_size; +} + +/** + * Set the table size of the set. + * Typically called during garbage collection, after clear and before rehash. + * Returns 0 if dbs->table_size > dbs->max_size! + */ +static inline void +llmsset_set_size(llmsset_t dbs, size_t size) +{ + /* check bounds (don't be rediculous) */ + if (size > 128 && size <= dbs->max_size) { + dbs->table_size = size; +#if LLMSSET_MASK + /* Warning: if size is not a power of two, you will get interesting behavior */ + dbs->mask = dbs->table_size - 1; +#endif + /* Set threshold: number of cache lines to probe before giving up on node insertion */ + dbs->threshold = 192 - 2 * __builtin_clzll(dbs->table_size); + } +} + +/** + * Core function: find existing data or add new. + * Returns the unique 42-bit value associated with the data, or 0 when table is full. + * Also, this value will never equal 0 or 1. + * Note: garbage collection during lookup strictly forbidden + */ +uint64_t llmsset_lookup(const llmsset_t dbs, const uint64_t a, const uint64_t b, int *created); + +/** + * Same as lookup, but use the custom functions + */ +uint64_t llmsset_lookupc(const llmsset_t dbs, const uint64_t a, const uint64_t b, int *created); + +/** + * To perform garbage collection, the user is responsible that no lookups are performed during the process. + * + * 1) call llmsset_clear + * 2) call llmsset_mark for every bucket to rehash + * 3) call llmsset_rehash + */ +VOID_TASK_DECL_1(llmsset_clear, llmsset_t); +#define llmsset_clear(dbs) CALL(llmsset_clear, dbs) + +VOID_TASK_DECL_1(llmsset_clear_data, llmsset_t); +#define llmsset_clear_data(dbs) CALL(llmsset_clear_data, dbs) + +VOID_TASK_DECL_1(llmsset_clear_hashes, llmsset_t); +#define llmsset_clear_hashes(dbs) CALL(llmsset_clear_hashes, dbs) + +/** + * Check if a certain data bucket is marked (in use). + */ +int llmsset_is_marked(const llmsset_t dbs, uint64_t index); + +/** + * During garbage collection, buckets are marked (for rehashing) with this function. + * Returns 0 if the node was already marked, or non-zero if it was not marked. + * May also return non-zero if multiple workers marked at the same time. + */ +int llmsset_mark(const llmsset_t dbs, uint64_t index); + +/** + * Rehash all marked buckets. + * Returns 0 if successful, or the number of buckets not rehashed if not. + */ +TASK_DECL_1(int, llmsset_rehash, llmsset_t); +#define llmsset_rehash(dbs) CALL(llmsset_rehash, dbs) + +/** + * Rehash a single bucket. + * Returns 0 if successful, or 1 if not. + */ +int llmsset_rehash_bucket(const llmsset_t dbs, uint64_t d_idx); + +/** + * Retrieve number of marked buckets. + */ +TASK_DECL_1(size_t, llmsset_count_marked, llmsset_t); +#define llmsset_count_marked(dbs) CALL(llmsset_count_marked, dbs) + +/** + * During garbage collection, this method calls the destroy callback + * for all 'custom' data that is not kept. + */ +VOID_TASK_DECL_1(llmsset_destroy_unmarked, llmsset_t); +#define llmsset_destroy_unmarked(dbs) CALL(llmsset_destroy_unmarked, dbs) + +/** + * Set custom functions + */ +void llmsset_set_custom(const llmsset_t dbs, llmsset_hash_cb hash_cb, llmsset_equals_cb equals_cb, llmsset_create_cb create_cb, llmsset_destroy_cb destroy_cb); + +/** + * Default hashing function + */ +uint64_t llmsset_hash(const uint64_t a, const uint64_t b, const uint64_t seed); + + +void llmsset_clear_data_seq(llmsset_t dbs); + +size_t seq_llmsset_count_marked(llmsset_t dbs); +size_t llmsset_count_marked_seq(llmsset_t dbs, size_t first, size_t count); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/sylvan_tls.h b/sylvan_tls.h new file mode 100755 index 0000000000000000000000000000000000000000..09958b2d32fc1f7ecd96e024d424caab7cfdc5cf --- /dev/null +++ b/sylvan_tls.h @@ -0,0 +1,32 @@ +/* + * Written by Josh Dybnis and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + * + * A platform independant wrapper around thread-local storage. On platforms that don't support + * __thread variables (e.g. Mac OS X), we have to use the pthreads library for thread-local storage + */ + +#ifndef TLS_H +#define TLS_H + +#ifdef __ELF__ // use gcc thread-local storage (i.e. __thread variables) +#define DECLARE_THREAD_LOCAL(name, type) __thread type name +#define INIT_THREAD_LOCAL(name) +#define SET_THREAD_LOCAL(name, value) name = value +#define LOCALIZE_THREAD_LOCAL(name, type) + +#else//!__ELF__ + +#define DECLARE_THREAD_LOCAL(name, type) pthread_key_t name##_KEY + +#define INIT_THREAD_LOCAL(name) \ + do { \ + if (pthread_key_create(&name##_KEY, NULL) != 0) { assert(0); } \ + } while (0) + +#define SET_THREAD_LOCAL(name, value) pthread_setspecific(name##_KEY, (void *)(size_t)value); + +#define LOCALIZE_THREAD_LOCAL(name, type) type name = (type)(size_t)pthread_getspecific(name##_KEY) + +#endif//__ELF__ +#endif//TLS_H diff --git a/tls.h b/tls.h old mode 100644 new mode 100755