1 /*
2 * This file is part of the Yices SMT Solver.
3 * Copyright (C) 2018 SRI International.
4 *
5 * Yices is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Yices is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Yices. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * STACK TO STORE ASSUMPTIONS IN A CONTEXT
21 */
22
23 #include <assert.h>
24 #include <stdbool.h>
25
26 #include "context/assumption_stack.h"
27 #include "utils/hash_functions.h"
28 #include "utils/memalloc.h"
29
30
31 /*
32 * INDEX STRUCTURES
33 */
34
35 /*
36 * Match functions: it takes a key k and an index i and checks whether
37 * the assumption at index i in the stack has key k. For the first
38 * variant, the key is a literal. For the second variant, the key
39 * is a term.
40 */
41 typedef bool (*match_fun_t)(const assumption_elem_t *a, int32_t key, int32_t i);
42
match_literal(const assumption_elem_t * a,int32_t key,int32_t i)43 static bool match_literal(const assumption_elem_t *a, int32_t key, int32_t i) {
44 assert(i >= 0);
45 return a[i].lit == key;
46 }
47
match_term(const assumption_elem_t * a,int32_t key,int32_t i)48 static bool match_term(const assumption_elem_t *a, int32_t key, int32_t i) {
49 assert(i >= 0);
50 return a[i].term == key;
51 }
52
53 /*
54 * Get key functions: takes an index i and either returns the literal or the term.
55 */
56 typedef int32_t (*get_key_fun_t)(const assumption_elem_t *a, int32_t i);
57
key_literal(const assumption_elem_t * a,int32_t i)58 static int32_t key_literal(const assumption_elem_t *a, int32_t i) {
59 assert(i >= 0);
60 return a[i].lit;
61 }
62
key_term(const assumption_elem_t * a,int32_t i)63 static int32_t key_term(const assumption_elem_t *a, int32_t i) {
64 assert(i >= 0);
65 return a[i].term;
66 }
67
68
69 /*
70 * For debugging: check whether n is 0 or a power of 2
71 */
72 #ifndef NDEBUG
is_power_of_two(uint32_t n)73 static bool is_power_of_two(uint32_t n) {
74 return (n & (n - 1)) == 0;
75 }
76 #endif
77
78
79 /*
80 * Initialize an index: nothing allocated
81 */
init_hash_index(hash_index_t * index)82 static void init_hash_index(hash_index_t *index) {
83 index->data = NULL;
84 index->size = 0;
85 index->nelems = 0;
86 index->ndeleted = 0;
87 index->resize_threshold = 0;
88 index->cleanup_threshold = 0;
89 }
90
91
92 /*
93 * Free memory
94 */
delete_hash_index(hash_index_t * index)95 static void delete_hash_index(hash_index_t *index) {
96 safe_free(index->data);
97 index->data = NULL;
98 }
99
100
101 /*
102 * Reset: empty the index
103 */
reset_hash_index(hash_index_t * index)104 static void reset_hash_index(hash_index_t *index) {
105 uint32_t i, n;
106
107 n = index->size;
108 for (i=0; i<n; i++) {
109 index->data[i] = -1;
110 }
111 index->nelems = 0;
112 index->ndeleted = 0;
113 }
114
115
116 /*
117 * Allocate an array of n elements and initialize all of them to -1
118 */
new_index_array(uint32_t n)119 static int32_t *new_index_array(uint32_t n) {
120 int32_t *tmp;
121 uint32_t i;
122
123 assert(is_power_of_two(n));
124 tmp = (int32_t *) safe_malloc(n * sizeof(int32_t));
125 for (i=0; i<n; i++) {
126 tmp[i] = -1;
127 }
128 return tmp;
129 }
130
131 /*
132 * Add mapping [key -> x] into array a
133 * - x must be non-negative
134 * - mask = size of a - 1 (the size of a is a power of 2)
135 * - a must not be full
136 */
clean_array_add(int32_t * a,int32_t key,int32_t x,uint32_t mask)137 static void clean_array_add(int32_t *a, int32_t key, int32_t x, uint32_t mask) {
138 uint32_t i;
139
140 assert(x >= 0);
141
142 i = jenkins_hash_int32(key) & mask;
143 while (a[i] >= 0) {
144 i ++;
145 i &= mask;
146 }
147 a[i] = x;
148 }
149
150
151 /*
152 * First allocation
153 */
alloc_hash_index(hash_index_t * index)154 static void alloc_hash_index(hash_index_t *index) {
155 uint32_t n;
156
157 assert(index->size == 0);
158
159 n = DEF_ASSUMPTION_INDEX_SIZE;
160 index->data = new_index_array(n);
161 index->size = n;
162 index->resize_threshold = (uint32_t)(n * ASSUMPTION_INDEX_RESIZE_RATIO);
163 index->cleanup_threshold = (uint32_t)(n * ASSUMPTION_INDEX_CLEANUP_RATIO);
164 }
165
166
167 /*
168 * Double the hash table size and copy its content into a fresh array
169 * - key returns the key for element of index i in the stack
170 */
resize_hash_index(hash_index_t * index,const assumption_elem_t * a,get_key_fun_t key)171 static void resize_hash_index(hash_index_t *index, const assumption_elem_t *a, get_key_fun_t key) {
172 uint32_t i, n, new_size, mask;
173 int32_t x, k;
174 int32_t *tmp;
175
176 n = index->size;
177 new_size = n << 1;
178 if (new_size >= MAX_ASSUMPTION_INDEX_SIZE) {
179 out_of_memory();
180 }
181
182 assert(is_power_of_two(new_size));
183
184 tmp = new_index_array(new_size);
185 mask = new_size - 1;
186 for (i=0; i<n; i++) {
187 x = index->data[i];
188 if (x >= 0) {
189 k = key(a, x);
190 clean_array_add(tmp, k, x, mask);
191 }
192 }
193
194 safe_free(index->data);
195
196 index->data = tmp;
197 index->size = new_size;
198 index->ndeleted = 0;
199 index->resize_threshold = (uint32_t) (new_size * ASSUMPTION_INDEX_RESIZE_RATIO);
200 index->cleanup_threshold = (uint32_t) (new_size * ASSUMPTION_INDEX_CLEANUP_RATIO);
201 }
202
203
204 /*
205 * Cleanup the table: remove the deletion marks (i.e., elements set to -2)
206 */
cleanup_hash_index(hash_index_t * index,const assumption_elem_t * a,get_key_fun_t key)207 static void cleanup_hash_index(hash_index_t *index, const assumption_elem_t *a, get_key_fun_t key) {
208 uint32_t i, n, mask;
209 int32_t x, k;
210 int32_t *tmp;
211
212 n = index->size;
213 tmp = new_index_array(n);
214 mask = n - 1;
215 for (i=0; i<n; i++) {
216 x = index->data[i];
217 if (x >= 0) {
218 k = key(a, x);
219 clean_array_add(tmp, k, x, mask);
220 }
221 }
222
223 safe_free(index->data);
224 index->data = tmp;
225 index->ndeleted = 0;
226 }
227
228
229 /*
230 * Check whether we need to resize/cleanup
231 */
hash_index_needs_resize(const hash_index_t * index)232 static inline bool hash_index_needs_resize(const hash_index_t *index) {
233 return index->nelems + index->ndeleted >= index->resize_threshold;
234 }
235
hash_index_needs_cleanup(const hash_index_t * index)236 static inline bool hash_index_needs_cleanup(const hash_index_t *index) {
237 return index->ndeleted >= index->cleanup_threshold;
238 }
239
240
241 /*
242 * Search for an entry mapped to k in the index
243 * - return -1 if not found
244 */
find_in_hash_index(const hash_index_t * index,int32_t k,const assumption_elem_t * a,match_fun_t match)245 static int32_t find_in_hash_index(const hash_index_t *index, int32_t k, const assumption_elem_t *a, match_fun_t match) {
246 uint32_t i, mask;
247 int32_t j;
248
249 assert(is_power_of_two(index->size) && index->size > 0);
250
251 mask = index->size - 1;
252 i = jenkins_hash_int32(k) & mask;
253 for (;;) {
254 j = index->data[i];
255 if (j == -1 || (j >= 0 && match(a, k, j))) break;
256 i ++;
257 i &= mask;
258 }
259 return j;
260 }
261
262
263 /*
264 * Remove entry (k, x) from the index
265 * - no change if the entry is not present
266 * - note: there's at most one occurrence of x in the index
267 */
remove_from_hash_index(hash_index_t * index,int32_t k,int32_t x)268 static void remove_from_hash_index(hash_index_t *index, int32_t k, int32_t x) {
269 uint32_t i, mask;
270 int32_t j;
271
272 assert(is_power_of_two(index->size) && index->size > 0);
273 assert(x >= 0);
274
275 mask = index->size - 1;
276 i = jenkins_hash_int32(k) & mask;
277 for (;;) {
278 j = index->data[i];
279 if (j == x) break;
280 if (j == -1) return;
281 i ++;
282 i &= mask;
283 }
284
285 index->data[i] = -2;
286 index->nelems --;
287 index->ndeleted ++;
288 }
289
290
291 /*
292 * Add (k, x) to the index
293 * - no change if there's already an entry for k
294 */
add_to_hash_index(hash_index_t * index,int32_t k,int32_t x,const assumption_elem_t * a,match_fun_t match)295 static void add_to_hash_index(hash_index_t *index, int32_t k, int32_t x, const assumption_elem_t *a, match_fun_t match) {
296 uint32_t i, mask;
297 int32_t j, free;
298
299 assert(is_power_of_two(index->size) && index->size > 0);
300 assert(x >= 0);
301
302 free = -1;
303 mask = index->size - 1;
304 i = jenkins_hash_int32(k) & mask;
305 for (;;) {
306 j = index->data[i];
307 if (j < 0) {
308 if (free < 0) free = i; // free slot
309 if (j == -1) break;
310 } else if (match(a, k, j)) {
311 return;
312 }
313 i ++;
314 i &= mask;
315 }
316
317 assert(0 <= free && free < (int32_t) index->size && index->data[free] < 0);
318
319 if (index->data[free] == -2) {
320 assert(index->ndeleted > 0);
321 index->ndeleted --;
322 }
323
324 index->data[free] = x;
325 index->nelems ++;
326 }
327
328
329 /*
330 * ASSUMPTION STACK
331 */
332
333 /*
334 * Initialize the stack:
335 * - nothing is allocated yet
336 * - level is 0
337 */
init_assumption_stack(assumption_stack_t * stack)338 void init_assumption_stack(assumption_stack_t *stack) {
339 stack->data = NULL;
340 stack->size = 0;
341 stack->top = 0;
342 stack->level = 0;
343 init_hash_index(&stack->lit_index);
344 init_hash_index(&stack->term_index);
345 }
346
347 /*
348 * Free memory
349 */
delete_assumption_stack(assumption_stack_t * stack)350 void delete_assumption_stack(assumption_stack_t *stack) {
351 safe_free(stack->data);
352 stack->data = NULL;
353 delete_hash_index(&stack->lit_index);
354 delete_hash_index(&stack->term_index);
355 }
356
357 /*
358 * Empty the stack
359 */
reset_assumption_stack(assumption_stack_t * stack)360 void reset_assumption_stack(assumption_stack_t *stack) {
361 stack->top = 0;
362 stack->level = 0;
363 reset_hash_index(&stack->lit_index);
364 reset_hash_index(&stack->term_index);
365 }
366
367
368 /*
369 * Make the stack larger
370 */
extend_assumption_stack(assumption_stack_t * stack)371 static void extend_assumption_stack(assumption_stack_t *stack) {
372 uint32_t n;
373
374 n = stack->size;
375 if (n == 0) {
376 // first allocation
377 n = DEF_ASSUMPTION_STACK_SIZE;
378 assert(n <= MAX_ASSUMPTION_STACK_SIZE);
379 stack->data = (assumption_elem_t *) safe_malloc(n * sizeof(assumption_elem_t));
380 stack->size = n;
381 alloc_hash_index(&stack->lit_index);
382 alloc_hash_index(&stack->term_index);
383
384 } else {
385 // try to make the stack 50% larger
386 n += (n >> 1);
387 if (n > MAX_ASSUMPTION_STACK_SIZE) {
388 out_of_memory();
389 }
390 assert(n > stack->size);
391
392 stack->data = (assumption_elem_t *) safe_realloc(stack->data, n * sizeof(assumption_elem_t));
393 stack->size = n;
394 }
395 }
396
397 /*
398 * Close the current level and remove all assumptions added at this
399 * level. stack->level must be positive.
400 */
assumption_stack_pop(assumption_stack_t * stack)401 void assumption_stack_pop(assumption_stack_t *stack) {
402 uint32_t i;
403
404 assert(stack->level > 0);
405
406 i = stack->top;
407 while (i>0 && stack->data[i-1].level == stack->level) {
408 i --;
409 remove_from_hash_index(&stack->lit_index, stack->data[i].lit, i);
410 remove_from_hash_index(&stack->term_index, stack->data[i].term, i);
411 }
412
413 stack->top = i;
414 stack->level --;
415
416 if (hash_index_needs_cleanup(&stack->lit_index)) {
417 cleanup_hash_index(&stack->lit_index, stack->data, key_literal);
418 }
419 if (hash_index_needs_cleanup(&stack->term_index)) {
420 cleanup_hash_index(&stack->term_index, stack->data, key_term);
421 }
422 }
423
424
425 /*
426 * Add a pair (t, l) on top of the stack: at the current level
427 */
assumption_stack_add(assumption_stack_t * stack,term_t t,literal_t l)428 void assumption_stack_add(assumption_stack_t *stack, term_t t, literal_t l) {
429 uint32_t i;
430
431 i = stack->top;
432 if (i == stack->size) {
433 extend_assumption_stack(stack);
434 }
435 assert(i < stack->size);
436
437 stack->data[i].term = t;
438 stack->data[i].lit = l;
439 stack->data[i].level = stack->level;
440 stack->top = i+1;
441
442 add_to_hash_index(&stack->lit_index, l, i, stack->data, match_literal);
443 add_to_hash_index(&stack->term_index, t, i, stack->data, match_term);
444
445 if (hash_index_needs_resize(&stack->lit_index)) {
446 resize_hash_index(&stack->lit_index, stack->data, key_literal);
447 }
448 if (hash_index_needs_resize(&stack->term_index)) {
449 resize_hash_index(&stack->term_index, stack->data, key_term);
450 }
451 }
452
453
454 /*
455 * Search for a term t attached to literal l in the stack:
456 * - this searches for an element of the form (t, l, k) in the stack
457 * and return t;
458 * - if several terms are mapped to l, the function returns the first one
459 * (i.e., with the lowest level k).
460 * - if there's no such element, the function returns NULL_TERM (i.e., -1)
461 */
assumption_term_for_literal(const assumption_stack_t * stack,literal_t l)462 term_t assumption_term_for_literal(const assumption_stack_t *stack, literal_t l) {
463 int32_t i;
464 term_t t;
465
466 t = NULL_TERM;
467 if (stack->size > 0) {
468 i = find_in_hash_index(&stack->lit_index, l, stack->data, match_literal);
469 if (i >= 0) {
470 assert(i < stack->size);
471 t = stack->data[i].term;
472 }
473 }
474
475 return t;
476 }
477
478 /*
479 * Search for a literal l attached to term t in the stack:
480 * - search for a triple (t, l, k) in the stack and return l
481 * - return null_literal if there's no such triple
482 */
assumption_literal_for_term(const assumption_stack_t * stack,term_t t)483 literal_t assumption_literal_for_term(const assumption_stack_t *stack, term_t t) {
484 int32_t i;
485 literal_t l;
486
487 l = null_literal;
488 if (stack->size > 0) {
489 i = find_in_hash_index(&stack->term_index, t, stack->data, match_term);
490 if (i >= 0) {
491 assert(i < stack->size);
492 l = stack->data[i].lit;
493 }
494 }
495 return l;
496 }
497
498