hg_wordset.c revision 54fe2021b87b9e5edb8ec8070f47b86d5cafb8aa
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-2012 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