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