1
2/*--------------------------------------------------------------------*/
3/*--- Sets of words, with unique set identifiers.                  ---*/
4/*---                                                 hg_wordset.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8   This file is part of Helgrind, a Valgrind tool for detecting errors
9   in threaded programs.
10
11   Copyright (C) 2007-2013 OpenWorks LLP
12       info@open-works.co.uk
13
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.
18
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.
23
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
27   02111-1307, USA.
28
29   The GNU General Public License is contained in the file COPYING.
30
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.
35*/
36
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"
43
44#include "hg_basics.h"
45#include "hg_wordset.h"     /* self */
46
47// define to 1 to have (a lot of) debugging of add/re-use/die WSU entries.
48#define HG_DEBUG 0
49
50//------------------------------------------------------------------//
51//--- Word Cache                                                 ---//
52//------------------------------------------------------------------//
53
54typedef
55   struct { UWord arg1; UWord arg2; UWord res; }
56   WCacheEnt;
57
58/* Each cache is a fixed sized array of N_WCACHE_STAT_MAX entries.
59   However only the first .dynMax are used.  This is because at some
60   point, expanding the cache further overall gives a slowdown because
61   searching more entries more than negates any performance advantage
62   from caching those entries in the first place.  Hence use .dynMax
63   to allow the size of the cache(s) to be set differently for each
64   different WordSetU. */
65#define N_WCACHE_STAT_MAX 32
66typedef
67   struct {
68      WCacheEnt ent[N_WCACHE_STAT_MAX];
69      UWord     dynMax; /* 1 .. N_WCACHE_STAT_MAX inclusive */
70      UWord     inUse;  /* 0 .. dynMax inclusive */
71   }
72   WCache;
73
74#define WCache_INIT(_zzcache,_zzdynmax)                              \
75   do {                                                              \
76      tl_assert((_zzdynmax) >= 1);                                   \
77      tl_assert((_zzdynmax) <= N_WCACHE_STAT_MAX);                   \
78      (_zzcache).dynMax = (_zzdynmax);                               \
79      (_zzcache).inUse = 0;                                          \
80   } while (0)
81
82#define WCache_LOOKUP_AND_RETURN(_retty,_zzcache,_zzarg1,_zzarg2)    \
83   do {                                                              \
84      UWord   _i;                                                    \
85      UWord   _arg1  = (UWord)(_zzarg1);                             \
86      UWord   _arg2  = (UWord)(_zzarg2);                             \
87      WCache* _cache = &(_zzcache);                                  \
88      tl_assert(_cache->dynMax >= 1);                                \
89      tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX);                \
90      tl_assert(_cache->inUse >= 0);                                 \
91      tl_assert(_cache->inUse <= _cache->dynMax);                    \
92      if (_cache->inUse > 0) {                                       \
93         if (_cache->ent[0].arg1 == _arg1                            \
94             && _cache->ent[0].arg2 == _arg2)                        \
95            return (_retty)_cache->ent[0].res;                       \
96         for (_i = 1; _i < _cache->inUse; _i++) {                    \
97            if (_cache->ent[_i].arg1 == _arg1                        \
98                && _cache->ent[_i].arg2 == _arg2) {                  \
99               WCacheEnt tmp     = _cache->ent[_i-1];                \
100               _cache->ent[_i-1] = _cache->ent[_i];                  \
101               _cache->ent[_i]   = tmp;                              \
102               return (_retty)_cache->ent[_i-1].res;                 \
103            }                                                        \
104         }                                                           \
105      }                                                              \
106   } while (0)
107
108#define WCache_UPDATE(_zzcache,_zzarg1,_zzarg2,_zzresult)            \
109   do {                                                              \
110      Word    _i;                                                    \
111      UWord   _arg1  = (UWord)(_zzarg1);                             \
112      UWord   _arg2  = (UWord)(_zzarg2);                             \
113      UWord   _res   = (UWord)(_zzresult);                           \
114      WCache* _cache = &(_zzcache);                                  \
115      tl_assert(_cache->dynMax >= 1);                                \
116      tl_assert(_cache->dynMax <= N_WCACHE_STAT_MAX);                \
117      tl_assert(_cache->inUse >= 0);                                 \
118      tl_assert(_cache->inUse <= _cache->dynMax);                    \
119      if (_cache->inUse < _cache->dynMax)                            \
120         _cache->inUse++;                                            \
121      for (_i = _cache->inUse-1; _i >= 1; _i--)                      \
122         _cache->ent[_i] = _cache->ent[_i-1];                        \
123      _cache->ent[0].arg1 = _arg1;                                   \
124      _cache->ent[0].arg2 = _arg2;                                   \
125      _cache->ent[0].res  = _res;                                    \
126   } while (0)
127
128
129//------------------------------------------------------------------//
130//---                          WordSet                           ---//
131//---                       Implementation                       ---//
132//------------------------------------------------------------------//
133
134typedef
135   struct {
136      WordSetU* owner; /* for sanity checking */
137      UWord*    words;
138      UWord     size; /* Really this should be SizeT */
139   }
140   WordVec;
141
142/* ix2vec[0 .. ix2vec_used-1] are pointers to the lock sets (WordVecs)
143   really.  vec2ix is the inverse mapping, mapping WordVec* to the
144   corresponding ix2vec entry number.  The two mappings are mutually
145   redundant.
146
147   If a WordVec WV is marked as dead by HG(dieWS), WV is removed from
148   vec2ix. The entry of the dead WVs in ix2vec are used to maintain a
149   linked list of free (to be re-used) ix2vec entries. */
150struct _WordSetU {
151      void*     (*alloc)(const HChar*,SizeT);
152      const HChar* cc;
153      void      (*dealloc)(void*);
154      WordFM*   vec2ix; /* WordVec-to-WordSet mapping tree */
155      WordVec** ix2vec; /* WordSet-to-WordVec mapping array */
156      UWord     ix2vec_size;
157      UWord     ix2vec_used;
158      WordVec** ix2vec_free;
159      WordSet   empty; /* cached, for speed */
160      /* Caches for some operations */
161      WCache    cache_addTo;
162      WCache    cache_delFrom;
163      WCache    cache_intersect;
164      WCache    cache_minus;
165      /* Stats */
166      UWord     n_add;
167      UWord     n_add_uncached;
168      UWord     n_del;
169      UWord     n_del_uncached;
170      UWord     n_die;
171      UWord     n_union;
172      UWord     n_intersect;
173      UWord     n_intersect_uncached;
174      UWord     n_minus;
175      UWord     n_minus_uncached;
176      UWord     n_elem;
177      UWord     n_doubleton;
178      UWord     n_isEmpty;
179      UWord     n_isSingleton;
180      UWord     n_anyElementOf;
181      UWord     n_isSubsetOf;
182   };
183
184/* Create a new WordVec of the given size. */
185
186static WordVec* new_WV_of_size ( WordSetU* wsu, UWord sz )
187{
188   WordVec* wv;
189   tl_assert(sz >= 0);
190   wv = wsu->alloc( wsu->cc, sizeof(WordVec) );
191   wv->owner = wsu;
192   wv->words = NULL;
193   wv->size = sz;
194   if (sz > 0) {
195     wv->words = wsu->alloc( wsu->cc, (SizeT)sz * sizeof(UWord) );
196   }
197   return wv;
198}
199
200static void delete_WV ( WordVec* wv )
201{
202   void (*dealloc)(void*) = wv->owner->dealloc;
203   if (wv->words) {
204      dealloc(wv->words);
205   }
206   dealloc(wv);
207}
208static void delete_WV_for_FM ( UWord wv ) {
209   delete_WV( (WordVec*)wv );
210}
211
212static Word cmp_WordVecs_for_FM ( UWord wv1W, UWord wv2W )
213{
214   UWord    i;
215   WordVec* wv1    = (WordVec*)wv1W;
216   WordVec* wv2    = (WordVec*)wv2W;
217
218   // WordVecs with smaller size are smaller.
219   if (wv1->size < wv2->size) {
220      return -1;
221   }
222   if (wv1->size > wv2->size) {
223      return 1;
224   }
225
226   // Sizes are equal => order based on content.
227   for (i = 0; i < wv1->size; i++) {
228      if (wv1->words[i] == wv2->words[i])
229         continue;
230      if (wv1->words[i] < wv2->words[i])
231         return -1;
232      if (wv1->words[i] > wv2->words[i])
233         return 1;
234      tl_assert(0);
235   }
236   return 0; /* identical */
237}
238
239static void ensure_ix2vec_space ( WordSetU* wsu )
240{
241   UInt      i, new_sz;
242   WordVec** new_vec;
243   tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
244   if (wsu->ix2vec_used < wsu->ix2vec_size)
245      return;
246   new_sz = 2 * wsu->ix2vec_size;
247   if (new_sz == 0) new_sz = 1;
248   new_vec = wsu->alloc( wsu->cc, new_sz * sizeof(WordVec*) );
249   tl_assert(new_vec);
250   for (i = 0; i < wsu->ix2vec_size; i++)
251      new_vec[i] = wsu->ix2vec[i];
252   if (wsu->ix2vec)
253      wsu->dealloc(wsu->ix2vec);
254   wsu->ix2vec = new_vec;
255   wsu->ix2vec_size = new_sz;
256}
257
258/* True if wv is a dead entry (i.e. is in the linked list of free to be re-used
259   entries in ix2vec). */
260static inline Bool is_dead ( WordSetU* wsu, WordVec* wv )
261{
262   if (wv == NULL) /* last element in free linked list in ix2vec */
263      return True;
264   else
265      return (WordVec**)wv >= &(wsu->ix2vec[1])
266         &&  (WordVec**)wv < &(wsu->ix2vec[wsu->ix2vec_size]);
267}
268/* Index into a WordSetU, doing the obvious range check.  Failure of
269   the assertions marked XXX and YYY is an indication of passing the
270   wrong WordSetU* in the public API of this module.
271   Accessing a dead ws will assert. */
272static WordVec* do_ix2vec ( WordSetU* wsu, WordSet ws )
273{
274   WordVec* wv;
275   tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
276   if (wsu->ix2vec_used > 0)
277      tl_assert(wsu->ix2vec);
278   /* If this assertion fails, it may mean you supplied a 'ws'
279      that does not come from the 'wsu' universe. */
280   tl_assert(ws < wsu->ix2vec_used); /* XXX */
281   wv = wsu->ix2vec[ws];
282   /* Make absolutely sure that 'ws' is a non dead member of 'wsu'. */
283   tl_assert(wv);
284   tl_assert(!is_dead(wsu,wv));
285   tl_assert(wv->owner == wsu); /* YYY */
286   return wv;
287}
288
289/* Same as do_ix2vec but returns NULL for a dead ws. */
290static WordVec* do_ix2vec_with_dead ( WordSetU* wsu, WordSet ws )
291{
292   WordVec* wv;
293   tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
294   if (wsu->ix2vec_used > 0)
295      tl_assert(wsu->ix2vec);
296   /* If this assertion fails, it may mean you supplied a 'ws'
297      that does not come from the 'wsu' universe. */
298   tl_assert(ws < wsu->ix2vec_used); /* XXX */
299   wv = wsu->ix2vec[ws];
300   /* Make absolutely sure that 'ws' is either dead or a member of 'wsu'. */
301   if (is_dead(wsu,wv))
302      wv = NULL;
303   else
304      tl_assert(wv->owner == wsu); /* YYY */
305   return wv;
306}
307
308/* See if wv is contained within wsu.  If so, deallocate wv and return
309   the index of the already-present copy.  If not, add wv to both the
310   vec2ix and ix2vec mappings and return its index.
311*/
312static WordSet add_or_dealloc_WordVec( WordSetU* wsu, WordVec* wv_new )
313{
314   Bool     have;
315   WordVec* wv_old;
316   UWord/*Set*/ ix_old = -1;
317   /* Really WordSet, but need something that can safely be casted to
318      a Word* in the lookupFM.  Making it WordSet (which is 32 bits)
319      causes failures on a 64-bit platform. */
320   tl_assert(wv_new->owner == wsu);
321   have = VG_(lookupFM)( wsu->vec2ix,
322                         (UWord*)&wv_old, (UWord*)&ix_old,
323                         (UWord)wv_new );
324   if (have) {
325      tl_assert(wv_old != wv_new);
326      tl_assert(wv_old);
327      tl_assert(wv_old->owner == wsu);
328      tl_assert(ix_old < wsu->ix2vec_used);
329      tl_assert(wsu->ix2vec[ix_old] == wv_old);
330      delete_WV( wv_new );
331      return (WordSet)ix_old;
332   } else if (wsu->ix2vec_free) {
333      WordSet ws;
334      tl_assert(is_dead(wsu,(WordVec*)wsu->ix2vec_free));
335      ws = wsu->ix2vec_free - &(wsu->ix2vec[0]);
336      tl_assert(wsu->ix2vec[ws] == NULL || is_dead(wsu,wsu->ix2vec[ws]));
337      wsu->ix2vec_free = (WordVec **) wsu->ix2vec[ws];
338      wsu->ix2vec[ws] = wv_new;
339      VG_(addToFM)( wsu->vec2ix, (UWord)wv_new, ws );
340      if (HG_DEBUG) VG_(printf)("aodW %s re-use free %d %p\n", wsu->cc, (Int)ws, wv_new );
341      return ws;
342   } else {
343      ensure_ix2vec_space( wsu );
344      tl_assert(wsu->ix2vec);
345      tl_assert(wsu->ix2vec_used < wsu->ix2vec_size);
346      wsu->ix2vec[wsu->ix2vec_used] = wv_new;
347      VG_(addToFM)( wsu->vec2ix, (Word)wv_new, (Word)wsu->ix2vec_used );
348      if (HG_DEBUG) VG_(printf)("aodW %s %d %p\n", wsu->cc, (Int)wsu->ix2vec_used, wv_new  );
349      wsu->ix2vec_used++;
350      tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size);
351      return (WordSet)(wsu->ix2vec_used - 1);
352   }
353}
354
355
356WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( const HChar*, SizeT ),
357                             const HChar* cc,
358                             void  (*dealloc)(void*),
359                             Word  cacheSize )
360{
361   WordSetU* wsu;
362   WordVec*  empty;
363
364   wsu          = alloc_nofail( cc, sizeof(WordSetU) );
365   VG_(memset)( wsu, 0, sizeof(WordSetU) );
366   wsu->alloc   = alloc_nofail;
367   wsu->cc      = cc;
368   wsu->dealloc = dealloc;
369   wsu->vec2ix  = VG_(newFM)( alloc_nofail, cc,
370                              dealloc, cmp_WordVecs_for_FM );
371   wsu->ix2vec_used = 0;
372   wsu->ix2vec_size = 0;
373   wsu->ix2vec      = NULL;
374   wsu->ix2vec_free = NULL;
375   WCache_INIT(wsu->cache_addTo,     cacheSize);
376   WCache_INIT(wsu->cache_delFrom,   cacheSize);
377   WCache_INIT(wsu->cache_intersect, cacheSize);
378   WCache_INIT(wsu->cache_minus,     cacheSize);
379   empty = new_WV_of_size( wsu, 0 );
380   wsu->empty = add_or_dealloc_WordVec( wsu, empty );
381
382   return wsu;
383}
384
385void HG_(deleteWordSetU) ( WordSetU* wsu )
386{
387   void (*dealloc)(void*) = wsu->dealloc;
388   tl_assert(wsu->vec2ix);
389   VG_(deleteFM)( wsu->vec2ix, delete_WV_for_FM, NULL/*val-finalizer*/ );
390   if (wsu->ix2vec)
391      dealloc(wsu->ix2vec);
392   dealloc(wsu);
393}
394
395WordSet HG_(emptyWS) ( WordSetU* wsu )
396{
397   return wsu->empty;
398}
399
400Bool HG_(isEmptyWS) ( WordSetU* wsu, WordSet ws )
401{
402   WordVec* wv = do_ix2vec( wsu, ws );
403   wsu->n_isEmpty++;
404   if (wv->size == 0) {
405      tl_assert(ws == wsu->empty);
406      return True;
407   } else {
408      tl_assert(ws != wsu->empty);
409      return False;
410   }
411}
412
413Bool HG_(isSingletonWS) ( WordSetU* wsu, WordSet ws, UWord w )
414{
415   WordVec* wv;
416   tl_assert(wsu);
417   wsu->n_isSingleton++;
418   wv = do_ix2vec( wsu, ws );
419   return (Bool)(wv->size == 1 && wv->words[0] == w);
420}
421
422UWord HG_(cardinalityWS) ( WordSetU* wsu, WordSet ws )
423{
424   WordVec* wv;
425   tl_assert(wsu);
426   wv = do_ix2vec( wsu, ws );
427   tl_assert(wv->size >= 0);
428   return wv->size;
429}
430
431UWord HG_(anyElementOfWS) ( WordSetU* wsu, WordSet ws )
432{
433   WordVec* wv;
434   tl_assert(wsu);
435   wsu->n_anyElementOf++;
436   wv = do_ix2vec( wsu, ws );
437   tl_assert(wv->size >= 1);
438   return wv->words[0];
439}
440
441UWord HG_(cardinalityWSU) ( WordSetU* wsu )
442{
443   tl_assert(wsu);
444   return wsu->ix2vec_used;
445}
446
447void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords,
448                         WordSetU* wsu, WordSet ws )
449{
450   WordVec* wv;
451   if (HG_DEBUG) VG_(printf)("getPayloadWS %s %d\n", wsu->cc, (Int)ws);
452   tl_assert(wsu);
453   wv = do_ix2vec( wsu, ws );
454   tl_assert(wv->size >= 0);
455   *nWords = wv->size;
456   *words  = wv->words;
457}
458
459void HG_(dieWS) ( WordSetU* wsu, WordSet ws )
460{
461   WordVec* wv = do_ix2vec_with_dead( wsu, ws );
462   WordVec* wv_in_vec2ix;
463   UWord/*Set*/ wv_ix = -1;
464
465   if (HG_DEBUG) VG_(printf)("dieWS %s %d %p\n", wsu->cc, (Int)ws, wv);
466
467   if (ws == 0)
468      return; // we never die the empty set.
469
470   if (!wv)
471      return; // already dead. (or a bug ?).
472
473   wsu->n_die++;
474
475
476   wsu->ix2vec[ws] = (WordVec*) wsu->ix2vec_free;
477   wsu->ix2vec_free = &wsu->ix2vec[ws];
478
479   VG_(delFromFM) ( wsu->vec2ix,
480                    (UWord*)&wv_in_vec2ix, (UWord*)&wv_ix,
481                    (UWord)wv );
482
483   if (HG_DEBUG) VG_(printf)("dieWS wv_ix %d\n", (Int)wv_ix);
484   tl_assert (wv_ix);
485   tl_assert (wv_ix == ws);
486
487   delete_WV( wv );
488
489   wsu->cache_addTo.inUse = 0;
490   wsu->cache_delFrom.inUse = 0;
491   wsu->cache_intersect.inUse = 0;
492   wsu->cache_minus.inUse = 0;
493}
494
495Bool HG_(plausibleWS) ( WordSetU* wsu, WordSet ws )
496{
497   if (wsu == NULL) return False;
498   if (ws < 0 || ws >= wsu->ix2vec_used)
499      return False;
500   return True;
501}
502
503Bool HG_(saneWS_SLOW) ( WordSetU* wsu, WordSet ws )
504{
505   WordVec* wv;
506   UWord    i;
507   if (wsu == NULL) return False;
508   if (ws < 0 || ws >= wsu->ix2vec_used)
509      return False;
510   wv = do_ix2vec( wsu, ws );
511   /* can never happen .. do_ix2vec will assert instead.  Oh well. */
512   if (wv->owner != wsu) return False;
513   if (wv->size < 0) return False;
514   if (wv->size > 0) {
515      for (i = 0; i < wv->size-1; i++) {
516         if (wv->words[i] >= wv->words[i+1])
517            return False;
518      }
519   }
520   return True;
521}
522
523Bool HG_(elemWS) ( WordSetU* wsu, WordSet ws, UWord w )
524{
525   UWord    i;
526   WordVec* wv = do_ix2vec( wsu, ws );
527   wsu->n_elem++;
528   for (i = 0; i < wv->size; i++) {
529      if (wv->words[i] == w)
530         return True;
531   }
532   return False;
533}
534
535WordSet HG_(doubletonWS) ( WordSetU* wsu, UWord w1, UWord w2 )
536{
537   WordVec* wv;
538   wsu->n_doubleton++;
539   if (w1 == w2) {
540      wv = new_WV_of_size(wsu, 1);
541      wv->words[0] = w1;
542   }
543   else if (w1 < w2) {
544      wv = new_WV_of_size(wsu, 2);
545      wv->words[0] = w1;
546      wv->words[1] = w2;
547   }
548   else {
549      tl_assert(w1 > w2);
550      wv = new_WV_of_size(wsu, 2);
551      wv->words[0] = w2;
552      wv->words[1] = w1;
553   }
554   return add_or_dealloc_WordVec( wsu, wv );
555}
556
557WordSet HG_(singletonWS) ( WordSetU* wsu, UWord w )
558{
559   return HG_(doubletonWS)( wsu, w, w );
560}
561
562WordSet HG_(isSubsetOf) ( WordSetU* wsu, WordSet small, WordSet big )
563{
564   wsu->n_isSubsetOf++;
565   return small == HG_(intersectWS)( wsu, small, big );
566}
567
568void HG_(ppWS) ( WordSetU* wsu, WordSet ws )
569{
570   UWord    i;
571   WordVec* wv;
572   tl_assert(wsu);
573   wv = do_ix2vec( wsu, ws );
574   VG_(printf)("{");
575   for (i = 0; i < wv->size; i++) {
576      VG_(printf)("%p", (void*)wv->words[i]);
577      if (i < wv->size-1)
578         VG_(printf)(",");
579   }
580   VG_(printf)("}");
581}
582
583void HG_(ppWSUstats) ( WordSetU* wsu, const HChar* name )
584{
585   VG_(printf)("   WordSet \"%s\":\n", name);
586   VG_(printf)("      addTo        %10lu (%lu uncached)\n",
587               wsu->n_add, wsu->n_add_uncached);
588   VG_(printf)("      delFrom      %10lu (%lu uncached)\n",
589               wsu->n_del, wsu->n_del_uncached);
590   VG_(printf)("      union        %10lu\n", wsu->n_union);
591   VG_(printf)("      intersect    %10lu (%lu uncached) "
592               "[nb. incl isSubsetOf]\n",
593               wsu->n_intersect, wsu->n_intersect_uncached);
594   VG_(printf)("      minus        %10lu (%lu uncached)\n",
595               wsu->n_minus, wsu->n_minus_uncached);
596   VG_(printf)("      elem         %10lu\n",   wsu->n_elem);
597   VG_(printf)("      doubleton    %10lu\n",   wsu->n_doubleton);
598   VG_(printf)("      isEmpty      %10lu\n",   wsu->n_isEmpty);
599   VG_(printf)("      isSingleton  %10lu\n",   wsu->n_isSingleton);
600   VG_(printf)("      anyElementOf %10lu\n",   wsu->n_anyElementOf);
601   VG_(printf)("      isSubsetOf   %10lu\n",   wsu->n_isSubsetOf);
602   VG_(printf)("      dieWS        %10lu\n",   wsu->n_die);
603}
604
605WordSet HG_(addToWS) ( WordSetU* wsu, WordSet ws, UWord w )
606{
607   UWord    k, j;
608   WordVec* wv_new;
609   WordVec* wv;
610   WordSet  result = (WordSet)(-1); /* bogus */
611
612   wsu->n_add++;
613   WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_addTo, ws, w);
614   wsu->n_add_uncached++;
615
616   /* If already present, this is a no-op. */
617   wv = do_ix2vec( wsu, ws );
618   for (k = 0; k < wv->size; k++) {
619      if (wv->words[k] == w) {
620         result = ws;
621         goto out;
622      }
623   }
624   /* Ok, not present.  Build a new one ... */
625   wv_new = new_WV_of_size( wsu, wv->size + 1 );
626   k = j = 0;
627   for (; k < wv->size && wv->words[k] < w; k++) {
628      wv_new->words[j++] = wv->words[k];
629   }
630   wv_new->words[j++] = w;
631   for (; k < wv->size; k++) {
632      tl_assert(wv->words[k] > w);
633      wv_new->words[j++] = wv->words[k];
634   }
635   tl_assert(j == wv_new->size);
636
637   /* Find any existing copy, or add the new one. */
638   result = add_or_dealloc_WordVec( wsu, wv_new );
639   tl_assert(result != (WordSet)(-1));
640
641  out:
642   WCache_UPDATE(wsu->cache_addTo, ws, w, result);
643   return result;
644}
645
646WordSet HG_(delFromWS) ( WordSetU* wsu, WordSet ws, UWord w )
647{
648   UWord    i, j, k;
649   WordVec* wv_new;
650   WordSet  result = (WordSet)(-1); /* bogus */
651   WordVec* wv = do_ix2vec( wsu, ws );
652
653   wsu->n_del++;
654
655   /* special case empty set */
656   if (wv->size == 0) {
657      tl_assert(ws == wsu->empty);
658      return ws;
659   }
660
661   WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_delFrom, ws, w);
662   wsu->n_del_uncached++;
663
664   /* If not already present, this is a no-op. */
665   for (i = 0; i < wv->size; i++) {
666      if (wv->words[i] == w)
667         break;
668   }
669   if (i == wv->size) {
670      result = ws;
671      goto out;
672   }
673   /* So w is present in ws, and the new set will be one element
674      smaller. */
675   tl_assert(i >= 0 && i < wv->size);
676   tl_assert(wv->size > 0);
677
678   wv_new = new_WV_of_size( wsu, wv->size - 1 );
679   j = k = 0;
680   for (; j < wv->size; j++) {
681      if (j == i)
682         continue;
683      wv_new->words[k++] = wv->words[j];
684   }
685   tl_assert(k == wv_new->size);
686
687   result = add_or_dealloc_WordVec( wsu, wv_new );
688   if (wv->size == 1) {
689      tl_assert(result == wsu->empty);
690   }
691
692  out:
693   WCache_UPDATE(wsu->cache_delFrom, ws, w, result);
694   return result;
695}
696
697WordSet HG_(unionWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
698{
699   UWord    i1, i2, k, sz;
700   WordVec* wv_new;
701   WordVec* wv1 = do_ix2vec( wsu, ws1 );
702   WordVec* wv2 = do_ix2vec( wsu, ws2 );
703   wsu->n_union++;
704   sz = 0;
705   i1 = i2 = 0;
706   while (1) {
707      if (i1 >= wv1->size || i2 >= wv2->size)
708         break;
709      sz++;
710      if (wv1->words[i1] < wv2->words[i2]) {
711         i1++;
712      } else
713      if (wv1->words[i1] > wv2->words[i2]) {
714         i2++;
715      } else {
716         i1++;
717         i2++;
718      }
719   }
720   tl_assert(i1 <= wv1->size);
721   tl_assert(i2 <= wv2->size);
722   tl_assert(i1 == wv1->size || i2 == wv2->size);
723   if (i1 == wv1->size && i2 < wv2->size) {
724      sz += (wv2->size - i2);
725   }
726   if (i2 == wv2->size && i1 < wv1->size) {
727      sz += (wv1->size - i1);
728   }
729
730   wv_new = new_WV_of_size( wsu, sz );
731   k = 0;
732
733   i1 = i2 = 0;
734   while (1) {
735      if (i1 >= wv1->size || i2 >= wv2->size)
736         break;
737      if (wv1->words[i1] < wv2->words[i2]) {
738         wv_new->words[k++] = wv1->words[i1];
739         i1++;
740      } else
741      if (wv1->words[i1] > wv2->words[i2]) {
742         wv_new->words[k++] = wv2->words[i2];
743         i2++;
744      } else {
745         wv_new->words[k++] = wv1->words[i1];
746         i1++;
747         i2++;
748      }
749   }
750   tl_assert(i1 <= wv1->size);
751   tl_assert(i2 <= wv2->size);
752   tl_assert(i1 == wv1->size || i2 == wv2->size);
753   if (i1 == wv1->size && i2 < wv2->size) {
754      while (i2 < wv2->size)
755         wv_new->words[k++] = wv2->words[i2++];
756   }
757   if (i2 == wv2->size && i1 < wv1->size) {
758      while (i1 < wv1->size)
759         wv_new->words[k++] = wv1->words[i1++];
760   }
761
762   tl_assert(k == sz);
763
764   return add_or_dealloc_WordVec( wsu, wv_new );
765}
766
767WordSet HG_(intersectWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
768{
769   UWord    i1, i2, k, sz;
770   WordSet  ws_new = (WordSet)(-1); /* bogus */
771   WordVec* wv_new;
772   WordVec* wv1;
773   WordVec* wv2;
774
775   wsu->n_intersect++;
776
777   /* Deal with an obvious case fast. */
778   if (ws1 == ws2)
779      return ws1;
780
781   /* Since intersect(x,y) == intersect(y,x), convert both variants to
782      the same query.  This reduces the number of variants the cache
783      has to deal with. */
784   if (ws1 > ws2) {
785      WordSet wst = ws1; ws1 = ws2; ws2 = wst;
786   }
787
788   WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_intersect, ws1, ws2);
789   wsu->n_intersect_uncached++;
790
791   wv1 = do_ix2vec( wsu, ws1 );
792   wv2 = do_ix2vec( wsu, ws2 );
793   sz = 0;
794   i1 = i2 = 0;
795   while (1) {
796      if (i1 >= wv1->size || i2 >= wv2->size)
797         break;
798      if (wv1->words[i1] < wv2->words[i2]) {
799         i1++;
800      } else
801      if (wv1->words[i1] > wv2->words[i2]) {
802         i2++;
803      } else {
804         sz++;
805         i1++;
806         i2++;
807      }
808   }
809   tl_assert(i1 <= wv1->size);
810   tl_assert(i2 <= wv2->size);
811   tl_assert(i1 == wv1->size || i2 == wv2->size);
812
813   wv_new = new_WV_of_size( wsu, sz );
814   k = 0;
815
816   i1 = i2 = 0;
817   while (1) {
818      if (i1 >= wv1->size || i2 >= wv2->size)
819         break;
820      if (wv1->words[i1] < wv2->words[i2]) {
821         i1++;
822      } else
823      if (wv1->words[i1] > wv2->words[i2]) {
824         i2++;
825      } else {
826         wv_new->words[k++] = wv1->words[i1];
827         i1++;
828         i2++;
829      }
830   }
831   tl_assert(i1 <= wv1->size);
832   tl_assert(i2 <= wv2->size);
833   tl_assert(i1 == wv1->size || i2 == wv2->size);
834
835   tl_assert(k == sz);
836
837   ws_new = add_or_dealloc_WordVec( wsu, wv_new );
838   if (sz == 0) {
839      tl_assert(ws_new == wsu->empty);
840   }
841
842   tl_assert(ws_new != (WordSet)(-1));
843   WCache_UPDATE(wsu->cache_intersect, ws1, ws2, ws_new);
844
845   return ws_new;
846}
847
848WordSet HG_(minusWS) ( WordSetU* wsu, WordSet ws1, WordSet ws2 )
849{
850   UWord    i1, i2, k, sz;
851   WordSet  ws_new = (WordSet)(-1); /* bogus */
852   WordVec* wv_new;
853   WordVec* wv1;
854   WordVec* wv2;
855
856   wsu->n_minus++;
857   WCache_LOOKUP_AND_RETURN(WordSet, wsu->cache_minus, ws1, ws2);
858   wsu->n_minus_uncached++;
859
860   wv1 = do_ix2vec( wsu, ws1 );
861   wv2 = do_ix2vec( wsu, ws2 );
862   sz = 0;
863   i1 = i2 = 0;
864   while (1) {
865      if (i1 >= wv1->size || i2 >= wv2->size)
866         break;
867      if (wv1->words[i1] < wv2->words[i2]) {
868         sz++;
869         i1++;
870      } else
871      if (wv1->words[i1] > wv2->words[i2]) {
872         i2++;
873      } else {
874         i1++;
875         i2++;
876      }
877   }
878   tl_assert(i1 <= wv1->size);
879   tl_assert(i2 <= wv2->size);
880   tl_assert(i1 == wv1->size || i2 == wv2->size);
881   if (i2 == wv2->size && i1 < wv1->size) {
882      sz += (wv1->size - i1);
883   }
884
885   wv_new = new_WV_of_size( wsu, sz );
886   k = 0;
887
888   i1 = i2 = 0;
889   while (1) {
890      if (i1 >= wv1->size || i2 >= wv2->size)
891         break;
892      if (wv1->words[i1] < wv2->words[i2]) {
893         wv_new->words[k++] = wv1->words[i1];
894         i1++;
895      } else
896      if (wv1->words[i1] > wv2->words[i2]) {
897         i2++;
898      } else {
899         i1++;
900         i2++;
901      }
902   }
903   tl_assert(i1 <= wv1->size);
904   tl_assert(i2 <= wv2->size);
905   tl_assert(i1 == wv1->size || i2 == wv2->size);
906   if (i2 == wv2->size && i1 < wv1->size) {
907      while (i1 < wv1->size)
908         wv_new->words[k++] = wv1->words[i1++];
909   }
910
911   tl_assert(k == sz);
912
913   ws_new = add_or_dealloc_WordVec( wsu, wv_new );
914   if (sz == 0) {
915      tl_assert(ws_new == wsu->empty);
916   }
917
918   tl_assert(ws_new != (WordSet)(-1));
919   WCache_UPDATE(wsu->cache_minus, ws1, ws2, ws_new);
920
921   return ws_new;
922}
923
924static __attribute__((unused))
925void show_WS ( WordSetU* wsu, WordSet ws )
926{
927   UWord i;
928   WordVec* wv = do_ix2vec( wsu, ws );
929   VG_(printf)("#%u{", ws);
930   for (i = 0; i < wv->size; i++) {
931      VG_(printf)("%lu", wv->words[i]);
932      if (i < wv->size-1)
933         VG_(printf)(",");
934   }
935   VG_(printf)("}\n");
936}
937
938//------------------------------------------------------------------//
939//---                        end WordSet                         ---//
940//---                       Implementation                       ---//
941//------------------------------------------------------------------//
942
943/*--------------------------------------------------------------------*/
944/*--- end                                             hg_wordset.c ---*/
945/*--------------------------------------------------------------------*/
946