2 /*--------------------------------------------------------------------*/
3 /*--- Sets of words, with unique set identifiers. ---*/
4 /*--- hg_wordset.c ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Helgrind, a Valgrind tool for detecting errors
11 Copyright (C) 2007-2010 OpenWorks LLP
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 The GNU General Public License is contained in the file COPYING.
31 Neither the names of the U.S. Department of Energy nor the
32 University of California nor the names of its contributors may be
33 used to endorse or promote products derived from this software
34 without prior written permission.
37 #include "pub_tool_basics.h"
38 #include "pub_tool_libcassert.h"
39 #include "pub_tool_libcbase.h"
40 #include "pub_tool_libcprint.h"
41 #include "pub_tool_threadstate.h"
42 #include "pub_tool_wordfm.h"
44 #include "hg_basics.h"
45 #include "hg_wordset.h" /* self */
47 //------------------------------------------------------------------//
48 //--- Word Cache ---//
49 //------------------------------------------------------------------//
52 struct { UWord arg1; UWord arg2; UWord res; }
55 /* Each cache is a fixed sized array of N_WCACHE_STAT_MAX entries.
56 However only the first .dynMax are used. This is because at some
57 point, expanding the cache further overall gives a slowdown because
58 searching more entries more than negates any performance advantage
59 from caching those entries in the first place. Hence use .dynMax
60 to allow the size of the cache(s) to be set differently for each
61 different WordSetU. */
62 #define N_WCACHE_STAT_MAX 32
65 WCacheEnt ent[N_WCACHE_STAT_MAX];
66 UWord dynMax; /* 1 .. N_WCACHE_STAT_MAX inclusive */
67 UWord inUse; /* 0 .. dynMax inclusive */
71 #define WCache_INIT(_zzcache,_zzdynmax) \
73 tl_assert((_zzdynmax) >= 1); \
74 tl_assert((_zzdynmax) <= N_WCACHE_STAT_MAX); \
75 (_zzcache).dynMax = (_zzdynmax); \
76 (_zzcache).inUse = 0; \
79 #define WCache_LOOKUP_AND_RETURN(_retty,_zzcache,_zzarg1,_zzarg2) \
82 UWord _arg1 = (UWord)(_zzarg1); \
83 UWord _arg2 = (UWord)(_zzarg2); \
84 WCache* _cache = &(_zzcache); \
85 tl_assert(_cache->dynMax >= 1); \
86 tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
87 tl_assert(_cache->inUse >= 0); \
88 tl_assert(_cache->inUse <= _cache->dynMax); \
89 if (_cache->inUse > 0) { \
90 if (_cache->ent[0].arg1 == _arg1 \
91 && _cache->ent[0].arg2 == _arg2) \
92 return (_retty)_cache->ent[0].res; \
93 for (_i = 1; _i < _cache->inUse; _i++) { \
94 if (_cache->ent[_i].arg1 == _arg1 \
95 && _cache->ent[_i].arg2 == _arg2) { \
96 WCacheEnt tmp = _cache->ent[_i-1]; \
97 _cache->ent[_i-1] = _cache->ent[_i]; \
98 _cache->ent[_i] = tmp; \
99 return (_retty)_cache->ent[_i-1].res; \
105 #define WCache_UPDATE(_zzcache,_zzarg1,_zzarg2,_zzresult) \
108 UWord _arg1 = (UWord)(_zzarg1); \
109 UWord _arg2 = (UWord)(_zzarg2); \
110 UWord _res = (UWord)(_zzresult); \
111 WCache* _cache = &(_zzcache); \
112 tl_assert(_cache->dynMax >= 1); \
113 tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX); \
114 tl_assert(_cache->inUse >= 0); \
115 tl_assert(_cache->inUse <= _cache->dynMax); \
116 if (_cache->inUse < _cache->dynMax) \
118 for (_i = _cache->inUse-1; _i >= 1; _i--) \
119 _cache->ent[_i] = _cache->ent[_i-1]; \
120 _cache->ent[0].arg1 = _arg1; \
121 _cache->ent[0].arg2 = _arg2; \
122 _cache->ent[0].res = _res; \
126 //------------------------------------------------------------------//
128 //--- Implementation ---//
129 //------------------------------------------------------------------//
133 WordSetU* owner; /* for sanity checking */
135 UWord size; /* Really this should be SizeT */
139 /* ix2vec[0 .. ix2vec_used-1] are pointers to the lock sets (WordVecs)
140 really. vec2ix is the inverse mapping, mapping WordVec* to the
141 corresponding ix2vec entry number. The two mappings are mutually
144 void* (*alloc)(HChar*,SizeT);
146 void (*dealloc)(void*);
147 WordFM* vec2ix; /* WordVec-to-WordSet mapping tree */
148 WordVec** ix2vec; /* WordSet-to-WordVec mapping array */
151 WordSet empty; /* cached, for speed */
152 /* Caches for some operations */
154 WCache cache_delFrom;
155 WCache cache_intersect;
159 UWord n_add_uncached;
161 UWord n_del_uncached;
164 UWord n_intersect_uncached;
166 UWord n_minus_uncached;
171 UWord n_anyElementOf;
175 /* Create a new WordVec of the given size. */
177 static WordVec* new_WV_of_size ( WordSetU* wsu, UWord sz )
181 wv = wsu->alloc( wsu->cc, sizeof(WordVec) );
186 wv->words = wsu->alloc( wsu->cc, (SizeT)sz * sizeof(UWord) );
191 static void delete_WV ( WordVec* wv )
193 void (*dealloc)(void*) = wv->owner->dealloc;
199 static void delete_WV_for_FM ( UWord wv ) {
200 delete_WV( (WordVec*)wv );
203 static Word cmp_WordVecs_for_FM ( UWord wv1W, UWord wv2W )
206 WordVec* wv1 = (WordVec*)wv1W;
207 WordVec* wv2 = (WordVec*)wv2W;
209 // WordVecs with smaller size are smaller.
210 if (wv1->size < wv2->size) {
213 if (wv1->size > wv2->size) {
217 // Sizes are equal => order based on content.
218 for (i = 0; i < wv1->size; i++) {
219 if (wv1->words[i] == wv2->words[i])
221 if (wv1->words[i] < wv2->words[i])
223 if (wv1->words[i] > wv2->words[i])
227 return 0; /* identical */
230 static void ensure_ix2vec_space ( WordSetU* wsu )
234 tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
235 if (wsu->ix2vec_used < wsu->ix2vec_size)
237 new_sz = 2 * wsu->ix2vec_size;
238 if (new_sz == 0) new_sz = 2;
239 new_vec = wsu->alloc( wsu->cc, new_sz * sizeof(WordVec*) );
241 for (i = 0; i < wsu->ix2vec_size; i++)
242 new_vec[i] = wsu->ix2vec[i];
244 wsu->dealloc(wsu->ix2vec);
245 wsu->ix2vec = new_vec;
246 wsu->ix2vec_size = new_sz;
249 /* Index into a WordSetU, doing the obvious range check. Failure of
250 the assertions marked XXX and YYY is an indication of passing the
251 wrong WordSetU* in the public API of this module. */
252 static WordVec* do_ix2vec ( WordSetU* wsu, WordSet ws )
255 tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
256 if (wsu->ix2vec_used > 0)
257 tl_assert(wsu->ix2vec);
258 /* If this assertion fails, it may mean you supplied a 'ws'
259 that does not come from the 'wsu' universe. */
260 tl_assert(ws < wsu->ix2vec_used); /* XXX */
261 wv = wsu->ix2vec[ws];
262 /* Make absolutely sure that 'ws' is a member of 'wsu'. */
264 tl_assert(wv->owner == wsu); /* YYY */
268 /* See if wv is contained within wsu. If so, deallocate wv and return
269 the index of the already-present copy. If not, add wv to both the
270 vec2ix and ix2vec mappings and return its index.
272 static WordSet add_or_dealloc_WordVec( WordSetU* wsu, WordVec* wv_new )
276 UWord/*Set*/ ix_old = -1;
277 /* Really WordSet, but need something that can safely be casted to
278 a Word* in the lookupFM. Making it WordSet (which is 32 bits)
279 causes failures on a 64-bit platform. */
280 tl_assert(wv_new->owner == wsu);
281 have = VG_(lookupFM)( wsu->vec2ix,
282 (Word*)&wv_old, (Word*)&ix_old,
285 tl_assert(wv_old != wv_new);
287 tl_assert(wv_old->owner == wsu);
288 tl_assert(ix_old < wsu->ix2vec_used);
289 tl_assert(wsu->ix2vec[ix_old] == wv_old);
291 return (WordSet)ix_old;
293 ensure_ix2vec_space( wsu );
294 tl_assert(wsu->ix2vec);
295 tl_assert(wsu->ix2vec_used < wsu->ix2vec_size);
296 wsu->ix2vec[wsu->ix2vec_used] = wv_new;
297 VG_(addToFM)( wsu->vec2ix, (Word)wv_new, (Word)wsu->ix2vec_used );
298 if (0) VG_(printf)("aodW %d\n", (Int)wsu->ix2vec_used );
300 tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
301 return (WordSet)(wsu->ix2vec_used - 1);
306 WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( HChar*, SizeT ),
308 void (*dealloc)(void*),
314 wsu = alloc_nofail( cc, sizeof(WordSetU) );
315 VG_(memset)( wsu, 0, sizeof(WordSetU) );
316 wsu->alloc = alloc_nofail;
318 wsu->dealloc = dealloc;
319 wsu->vec2ix = VG_(newFM)( alloc_nofail, cc,
320 dealloc, cmp_WordVecs_for_FM );
321 wsu->ix2vec_used = 0;
322 wsu->ix2vec_size = 0;
324 WCache_INIT(wsu->cache_addTo, cacheSize);
325 WCache_INIT(wsu->cache_delFrom, cacheSize);
326 WCache_INIT(wsu->cache_intersect, cacheSize);
327 WCache_INIT(wsu->cache_minus, cacheSize);
328 empty = new_WV_of_size( wsu, 0 );
329 wsu->empty = add_or_dealloc_WordVec( wsu, empty );
334 void HG_(deleteWordSetU) ( WordSetU* wsu )
336 void (*dealloc)(void*) = wsu->dealloc;
337 tl_assert(wsu->vec2ix);
338 VG_(deleteFM)( wsu->vec2ix, delete_WV_for_FM, NULL/*val-finalizer*/ );
340 dealloc(wsu->ix2vec);
344 WordSet HG_(emptyWS) ( WordSetU* wsu )
349 Bool HG_(isEmptyWS) ( WordSetU* wsu, WordSet ws )
351 WordVec* wv = do_ix2vec( wsu, ws );
354 tl_assert(ws == wsu->empty);
357 tl_assert(ws != wsu->empty);
362 Bool HG_(isSingletonWS) ( WordSetU* wsu, WordSet ws, UWord w )
366 wsu->n_isSingleton++;
367 wv = do_ix2vec( wsu, ws );
368 return (Bool)(wv->size == 1 && wv->words[0] == w);
371 UWord HG_(cardinalityWS) ( WordSetU* wsu, WordSet ws )
375 wv = do_ix2vec( wsu, ws );
376 tl_assert(wv->size >= 0);
380 UWord HG_(anyElementOfWS) ( WordSetU* wsu, WordSet ws )
384 wsu->n_anyElementOf++;
385 wv = do_ix2vec( wsu, ws );
386 tl_assert(wv->size >= 1);
390 UWord HG_(cardinalityWSU) ( WordSetU* wsu )
393 return wsu->ix2vec_used;
396 void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords,
397 WordSetU* wsu, WordSet ws )
401 wv = do_ix2vec( wsu, ws );
402 tl_assert(wv->size >= 0);
407 Bool HG_(plausibleWS) ( WordSetU* wsu, WordSet ws )
409 if (wsu == NULL) return False;
410 if (ws < 0 || ws >= wsu->ix2vec_used)
415 Bool HG_(saneWS_SLOW) ( WordSetU* wsu, WordSet ws )
419 if (wsu == NULL) return False;
420 if (ws < 0 || ws >= wsu->ix2vec_used)
422 wv = do_ix2vec( wsu, ws );
423 /* can never happen .. do_ix2vec will assert instead. Oh well. */
424 if (wv->owner != wsu) return False;
425 if (wv->size < 0) return False;
427 for (i = 0; i < wv->size-1; i++) {
428 if (wv->words[i] >= wv->words[i+1])
435 Bool HG_(elemWS) ( WordSetU* wsu, WordSet ws, UWord w )
438 WordVec* wv = do_ix2vec( wsu, ws );
440 for (i = 0; i < wv->size; i++) {
441 if (wv->words[i] == w)
447 WordSet HG_(doubletonWS) ( WordSetU* wsu, UWord w1, UWord w2 )
452 wv = new_WV_of_size(wsu, 1);
456 wv = new_WV_of_size(wsu, 2);
462 wv = new_WV_of_size(wsu, 2);
466 return add_or_dealloc_WordVec( wsu, wv );
469 WordSet HG_(singletonWS) ( WordSetU* wsu, UWord w )
471 return HG_(doubletonWS)( wsu, w, w );
474 WordSet HG_(isSubsetOf) ( WordSetU* wsu, WordSet small, WordSet big )
477 return small == HG_(intersectWS)( wsu, small, big );
480 void HG_(ppWS) ( WordSetU* wsu, WordSet ws )
485 wv = do_ix2vec( wsu, ws );
487 for (i = 0; i < wv->size; i++) {
488 VG_(printf)("%p", (void*)wv->words[i]);
495 void HG_(ppWSUstats) ( WordSetU* wsu, HChar* name )
497 VG_(printf)(" WordSet \"%s\":\n", name);
498 VG_(printf)(" addTo %10lu (%lu uncached)\n",
499 wsu->n_add, wsu->n_add_uncached);
500 VG_(printf)(" delFrom %10lu (%lu uncached)\n",
501 wsu->n_del, wsu->n_del_uncached);
502 VG_(printf)(" union %10lu\n", wsu->n_union);
503 VG_(printf)(" intersect %10lu (%lu uncached) "
504 "[nb. incl isSubsetOf]\n",
505 wsu->n_intersect, wsu->n_intersect_uncached);
506 VG_(printf)(" minus %10lu (%lu uncached)\n",
507 wsu->n_minus, wsu->n_minus_uncached);
508 VG_(printf)(" elem %10lu\n", wsu->n_elem);
509 VG_(printf)(" doubleton %10lu\n", wsu->n_doubleton);
510 VG_(printf)(" isEmpty %10lu\n", wsu->n_isEmpty);
511 VG_(printf)(" isSingleton %10lu\n", wsu->n_isSingleton);
512 VG_(printf)(" anyElementOf %10lu\n", wsu->n_anyElementOf);
513 VG_(printf)(" isSubsetOf %10lu\n", wsu->n_isSubsetOf);
516 WordSet HG_(addToWS) ( WordSetU* wsu, WordSet ws, UWord w )
521 WordSet result = (WordSet)(-1); /* bogus */
524 WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_addTo, ws, w);
525 wsu->n_add_uncached++;
527 /* If already present, this is a no-op. */
528 wv = do_ix2vec( wsu, ws );
529 for (k = 0; k < wv->size; k++) {
530 if (wv->words[k] == w) {
535 /* Ok, not present. Build a new one ... */
536 wv_new = new_WV_of_size( wsu, wv->size + 1 );
538 for (; k < wv->size && wv->words[k] < w; k++) {
539 wv_new->words[j++] = wv->words[k];
541 wv_new->words[j++] = w;
542 for (; k < wv->size; k++) {
543 tl_assert(wv->words[k] > w);
544 wv_new->words[j++] = wv->words[k];
546 tl_assert(j == wv_new->size);
548 /* Find any existing copy, or add the new one. */
549 result = add_or_dealloc_WordVec( wsu, wv_new );
550 tl_assert(result != (WordSet)(-1));
553 WCache_UPDATE(wsu->cache_addTo, ws, w, result);
557 WordSet HG_(delFromWS) ( WordSetU* wsu, WordSet ws, UWord w )
561 WordSet result = (WordSet)(-1); /* bogus */
562 WordVec* wv = do_ix2vec( wsu, ws );
566 /* special case empty set */
568 tl_assert(ws == wsu->empty);
572 WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_delFrom, ws, w);
573 wsu->n_del_uncached++;
575 /* If not already present, this is a no-op. */
576 for (i = 0; i < wv->size; i++) {
577 if (wv->words[i] == w)
584 /* So w is present in ws, and the new set will be one element
586 tl_assert(i >= 0 && i < wv->size);
587 tl_assert(wv->size > 0);
589 wv_new = new_WV_of_size( wsu, wv->size - 1 );
591 for (; j < wv->size; j++) {
594 wv_new->words[k++] = wv->words[j];
596 tl_assert(k == wv_new->size);
598 result = add_or_dealloc_WordVec( wsu, wv_new );
600 tl_assert(result == wsu->empty);
604 WCache_UPDATE(wsu->cache_delFrom, ws, w, result);
608 WordSet HG_(unionWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
612 WordVec* wv1 = do_ix2vec( wsu, ws1 );
613 WordVec* wv2 = do_ix2vec( wsu, ws2 );
618 if (i1 >= wv1->size || i2 >= wv2->size)
621 if (wv1->words[i1] < wv2->words[i2]) {
624 if (wv1->words[i1] > wv2->words[i2]) {
631 tl_assert(i1 <= wv1->size);
632 tl_assert(i2 <= wv2->size);
633 tl_assert(i1 == wv1->size || i2 == wv2->size);
634 if (i1 == wv1->size && i2 < wv2->size) {
635 sz += (wv2->size - i2);
637 if (i2 == wv2->size && i1 < wv1->size) {
638 sz += (wv1->size - i1);
641 wv_new = new_WV_of_size( wsu, sz );
646 if (i1 >= wv1->size || i2 >= wv2->size)
648 if (wv1->words[i1] < wv2->words[i2]) {
649 wv_new->words[k++] = wv1->words[i1];
652 if (wv1->words[i1] > wv2->words[i2]) {
653 wv_new->words[k++] = wv2->words[i2];
656 wv_new->words[k++] = wv1->words[i1];
661 tl_assert(i1 <= wv1->size);
662 tl_assert(i2 <= wv2->size);
663 tl_assert(i1 == wv1->size || i2 == wv2->size);
664 if (i1 == wv1->size && i2 < wv2->size) {
665 while (i2 < wv2->size)
666 wv_new->words[k++] = wv2->words[i2++];
668 if (i2 == wv2->size && i1 < wv1->size) {
669 while (i1 < wv1->size)
670 wv_new->words[k++] = wv1->words[i1++];
675 return add_or_dealloc_WordVec( wsu, wv_new );
678 WordSet HG_(intersectWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
681 WordSet ws_new = (WordSet)(-1); /* bogus */
688 /* Deal with an obvious case fast. */
692 /* Since intersect(x,y) == intersect(y,x), convert both variants to
693 the same query. This reduces the number of variants the cache
696 WordSet wst = ws1; ws1 = ws2; ws2 = wst;
699 WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_intersect, ws1, ws2);
700 wsu->n_intersect_uncached++;
702 wv1 = do_ix2vec( wsu, ws1 );
703 wv2 = do_ix2vec( wsu, ws2 );
707 if (i1 >= wv1->size || i2 >= wv2->size)
709 if (wv1->words[i1] < wv2->words[i2]) {
712 if (wv1->words[i1] > wv2->words[i2]) {
720 tl_assert(i1 <= wv1->size);
721 tl_assert(i2 <= wv2->size);
722 tl_assert(i1 == wv1->size || i2 == wv2->size);
724 wv_new = new_WV_of_size( wsu, sz );
729 if (i1 >= wv1->size || i2 >= wv2->size)
731 if (wv1->words[i1] < wv2->words[i2]) {
734 if (wv1->words[i1] > wv2->words[i2]) {
737 wv_new->words[k++] = wv1->words[i1];
742 tl_assert(i1 <= wv1->size);
743 tl_assert(i2 <= wv2->size);
744 tl_assert(i1 == wv1->size || i2 == wv2->size);
748 ws_new = add_or_dealloc_WordVec( wsu, wv_new );
750 tl_assert(ws_new == wsu->empty);
753 tl_assert(ws_new != (WordSet)(-1));
754 WCache_UPDATE(wsu->cache_intersect, ws1, ws2, ws_new);
759 WordSet HG_(minusWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
762 WordSet ws_new = (WordSet)(-1); /* bogus */
768 WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_minus, ws1, ws2);
769 wsu->n_minus_uncached++;
771 wv1 = do_ix2vec( wsu, ws1 );
772 wv2 = do_ix2vec( wsu, ws2 );
776 if (i1 >= wv1->size || i2 >= wv2->size)
778 if (wv1->words[i1] < wv2->words[i2]) {
782 if (wv1->words[i1] > wv2->words[i2]) {
789 tl_assert(i1 <= wv1->size);
790 tl_assert(i2 <= wv2->size);
791 tl_assert(i1 == wv1->size || i2 == wv2->size);
792 if (i2 == wv2->size && i1 < wv1->size) {
793 sz += (wv1->size - i1);
796 wv_new = new_WV_of_size( wsu, sz );
801 if (i1 >= wv1->size || i2 >= wv2->size)
803 if (wv1->words[i1] < wv2->words[i2]) {
804 wv_new->words[k++] = wv1->words[i1];
807 if (wv1->words[i1] > wv2->words[i2]) {
814 tl_assert(i1 <= wv1->size);
815 tl_assert(i2 <= wv2->size);
816 tl_assert(i1 == wv1->size || i2 == wv2->size);
817 if (i2 == wv2->size && i1 < wv1->size) {
818 while (i1 < wv1->size)
819 wv_new->words[k++] = wv1->words[i1++];
824 ws_new = add_or_dealloc_WordVec( wsu, wv_new );
826 tl_assert(ws_new == wsu->empty);
829 tl_assert(ws_new != (WordSet)(-1));
830 WCache_UPDATE(wsu->cache_minus, ws1, ws2, ws_new);
835 static __attribute__((unused))
836 void show_WS ( WordSetU* wsu, WordSet ws )
839 WordVec* wv = do_ix2vec( wsu, ws );
840 VG_(printf)("#%u{", ws);
841 for (i = 0; i < wv->size; i++) {
842 VG_(printf)("%lu", wv->words[i]);
849 //------------------------------------------------------------------//
850 //--- end WordSet ---//
851 //--- Implementation ---//
852 //------------------------------------------------------------------//
854 /*--------------------------------------------------------------------*/
855 /*--- end hg_wordset.c ---*/
856 /*--------------------------------------------------------------------*/