The source code and dockerfile for the GSW2024 AI Lab.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

1462 lines
34 KiB

2 months ago
  1. /**
  2. @file
  3. @ingroup cudd
  4. @brief Functions for local caches.
  5. @author Fabio Somenzi
  6. @copyright@parblock
  7. Copyright (c) 1995-2015, Regents of the University of Colorado
  8. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in the
  16. documentation and/or other materials provided with the distribution.
  17. Neither the name of the University of Colorado nor the names of its
  18. contributors may be used to endorse or promote products derived from
  19. this software without specific prior written permission.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  27. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  28. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  30. ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. POSSIBILITY OF SUCH DAMAGE.
  32. @endparblock
  33. */
  34. #include "util.h"
  35. #include "cuddInt.h"
  36. /*---------------------------------------------------------------------------*/
  37. /* Constant declarations */
  38. /*---------------------------------------------------------------------------*/
  39. #define DD_MAX_HASHTABLE_DENSITY 2 /* tells when to resize a table */
  40. /*---------------------------------------------------------------------------*/
  41. /* Stucture declarations */
  42. /*---------------------------------------------------------------------------*/
  43. /*---------------------------------------------------------------------------*/
  44. /* Type declarations */
  45. /*---------------------------------------------------------------------------*/
  46. /*---------------------------------------------------------------------------*/
  47. /* Variable declarations */
  48. /*---------------------------------------------------------------------------*/
  49. /*---------------------------------------------------------------------------*/
  50. /* Macro declarations */
  51. /*---------------------------------------------------------------------------*/
  52. /**
  53. @brief Computes hash function for keys of one operand.
  54. @sideeffect None
  55. @see ddLCHash3 ddLCHash
  56. */
  57. #if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4
  58. #define ddLCHash1(f,shift) \
  59. (((unsigned)(ptruint)(f) * DD_P1) >> (shift))
  60. #else
  61. #define ddLCHash1(f,shift) \
  62. (((unsigned)(f) * DD_P1) >> (shift))
  63. #endif
  64. /**
  65. @brief Computes hash function for keys of two operands.
  66. @sideeffect None
  67. @see ddLCHash3 ddLCHash
  68. */
  69. #if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4
  70. #define ddLCHash2(f,g,shift) \
  71. ((((unsigned)(ptruint)(f) * DD_P1 + \
  72. (unsigned)(ptruint)(g)) * DD_P2) >> (shift))
  73. #else
  74. #define ddLCHash2(f,g,shift) \
  75. ((((unsigned)(f) * DD_P1 + (unsigned)(g)) * DD_P2) >> (shift))
  76. #endif
  77. /**
  78. @brief Computes hash function for keys of three operands.
  79. @sideeffect None
  80. @see ddLCHash2 ddLCHash
  81. */
  82. #define ddLCHash3(f,g,h,shift) ddCHash2(f,g,h,shift)
  83. /** \cond */
  84. /*---------------------------------------------------------------------------*/
  85. /* Static function prototypes */
  86. /*---------------------------------------------------------------------------*/
  87. static void cuddLocalCacheResize (DdLocalCache *cache);
  88. static unsigned int ddLCHash (DdNodePtr *key, unsigned int keysize, int shift);
  89. static void cuddLocalCacheAddToList (DdLocalCache *cache);
  90. static void cuddLocalCacheRemoveFromList (DdLocalCache *cache);
  91. static int cuddHashTableResize (DdHashTable *hash);
  92. static DdHashItem * cuddHashTableAlloc (DdHashTable *hash);
  93. /** \endcond */
  94. /*---------------------------------------------------------------------------*/
  95. /* Definition of exported functions */
  96. /*---------------------------------------------------------------------------*/
  97. /*---------------------------------------------------------------------------*/
  98. /* Definition of internal functions */
  99. /*---------------------------------------------------------------------------*/
  100. /**
  101. @brief Initializes a local computed table.
  102. @return a pointer the the new local cache in case of success; NULL
  103. otherwise.
  104. @sideeffect None
  105. @see cuddInitCache
  106. */
  107. DdLocalCache *
  108. cuddLocalCacheInit(
  109. DdManager * manager /**< manager */,
  110. unsigned int keySize /**< size of the key (number of operands) */,
  111. unsigned int cacheSize /**< Initial size of the cache */,
  112. unsigned int maxCacheSize /**< Size of the cache beyond which no resizing occurs */)
  113. {
  114. DdLocalCache *cache;
  115. int logSize;
  116. cache = ALLOC(DdLocalCache,1);
  117. if (cache == NULL) {
  118. manager->errorCode = CUDD_MEMORY_OUT;
  119. return(NULL);
  120. }
  121. cache->manager = manager;
  122. cache->keysize = keySize;
  123. cache->itemsize = (keySize + 1) * sizeof(DdNode *);
  124. #ifdef DD_CACHE_PROFILE
  125. cache->itemsize += sizeof(ptrint);
  126. #endif
  127. logSize = cuddComputeFloorLog2(ddMax(cacheSize,manager->slots/2));
  128. cacheSize = 1U << logSize;
  129. cache->item = (DdLocalCacheItem *)
  130. ALLOC(char, cacheSize * cache->itemsize);
  131. if (cache->item == NULL) {
  132. manager->errorCode = CUDD_MEMORY_OUT;
  133. FREE(cache);
  134. return(NULL);
  135. }
  136. cache->slots = cacheSize;
  137. cache->shift = sizeof(int) * 8 - logSize;
  138. cache->maxslots = ddMin(maxCacheSize,manager->slots);
  139. cache->minHit = manager->minHit;
  140. /* Initialize to avoid division by 0 and immediate resizing. */
  141. cache->lookUps = (double) (int) (cacheSize * cache->minHit + 1);
  142. cache->hits = 0;
  143. manager->memused += cacheSize * cache->itemsize + sizeof(DdLocalCache);
  144. /* Initialize the cache. */
  145. memset(cache->item, 0, cacheSize * cache->itemsize);
  146. /* Add to manager's list of local caches for GC. */
  147. cuddLocalCacheAddToList(cache);
  148. return(cache);
  149. } /* end of cuddLocalCacheInit */
  150. /**
  151. @brief Shuts down a local computed table.
  152. @sideeffect None
  153. @see cuddLocalCacheInit
  154. */
  155. void
  156. cuddLocalCacheQuit(
  157. DdLocalCache * cache /**< cache to be shut down */)
  158. {
  159. cache->manager->memused -=
  160. cache->slots * cache->itemsize + sizeof(DdLocalCache);
  161. cuddLocalCacheRemoveFromList(cache);
  162. FREE(cache->item);
  163. FREE(cache);
  164. return;
  165. } /* end of cuddLocalCacheQuit */
  166. /**
  167. @brief Inserts a result in a local cache.
  168. @sideeffect None
  169. */
  170. void
  171. cuddLocalCacheInsert(
  172. DdLocalCache * cache,
  173. DdNodePtr * key,
  174. DdNode * value)
  175. {
  176. unsigned int posn;
  177. DdLocalCacheItem *entry;
  178. posn = ddLCHash(key,cache->keysize,cache->shift);
  179. entry = (DdLocalCacheItem *) ((char *) cache->item +
  180. posn * cache->itemsize);
  181. memcpy(entry->key,key,cache->keysize * sizeof(DdNode *));
  182. entry->value = value;
  183. #ifdef DD_CACHE_PROFILE
  184. entry->count++;
  185. #endif
  186. } /* end of cuddLocalCacheInsert */
  187. /**
  188. @brief Looks up in a local cache.
  189. @return the result if found; it returns NULL if no result is found.
  190. @sideeffect None
  191. */
  192. DdNode *
  193. cuddLocalCacheLookup(
  194. DdLocalCache * cache,
  195. DdNodePtr * key)
  196. {
  197. unsigned int posn;
  198. DdLocalCacheItem *entry;
  199. DdNode *value;
  200. cache->lookUps++;
  201. posn = ddLCHash(key,cache->keysize,cache->shift);
  202. entry = (DdLocalCacheItem *) ((char *) cache->item +
  203. posn * cache->itemsize);
  204. if (entry->value != NULL &&
  205. memcmp(key,entry->key,cache->keysize*sizeof(DdNode *)) == 0) {
  206. cache->hits++;
  207. value = Cudd_Regular(entry->value);
  208. if (value->ref == 0) {
  209. cuddReclaim(cache->manager,value);
  210. }
  211. return(entry->value);
  212. }
  213. /* Cache miss: decide whether to resize */
  214. if (cache->slots < cache->maxslots &&
  215. cache->hits > cache->lookUps * cache->minHit) {
  216. cuddLocalCacheResize(cache);
  217. }
  218. return(NULL);
  219. } /* end of cuddLocalCacheLookup */
  220. /**
  221. @brief Clears the dead entries of the local caches of a manager.
  222. @details Used during garbage collection.
  223. @sideeffect None
  224. */
  225. void
  226. cuddLocalCacheClearDead(
  227. DdManager * manager)
  228. {
  229. DdLocalCache *cache = manager->localCaches;
  230. unsigned int keysize;
  231. unsigned int itemsize;
  232. unsigned int slots;
  233. DdLocalCacheItem *item;
  234. DdNodePtr *key;
  235. unsigned int i, j;
  236. while (cache != NULL) {
  237. keysize = cache->keysize;
  238. itemsize = cache->itemsize;
  239. slots = cache->slots;
  240. item = cache->item;
  241. for (i = 0; i < slots; i++) {
  242. if (item->value != NULL) {
  243. if (Cudd_Regular(item->value)->ref == 0) {
  244. item->value = NULL;
  245. } else {
  246. key = item->key;
  247. for (j = 0; j < keysize; j++) {
  248. if (Cudd_Regular(key[j])->ref == 0) {
  249. item->value = NULL;
  250. break;
  251. }
  252. }
  253. }
  254. }
  255. item = (DdLocalCacheItem *) ((char *) item + itemsize);
  256. }
  257. cache = cache->next;
  258. }
  259. return;
  260. } /* end of cuddLocalCacheClearDead */
  261. /**
  262. @brief Clears the local caches of a manager.
  263. @details Used before reordering.
  264. @sideeffect None
  265. */
  266. void
  267. cuddLocalCacheClearAll(
  268. DdManager * manager)
  269. {
  270. DdLocalCache *cache = manager->localCaches;
  271. while (cache != NULL) {
  272. memset(cache->item, 0, cache->slots * cache->itemsize);
  273. cache = cache->next;
  274. }
  275. return;
  276. } /* end of cuddLocalCacheClearAll */
  277. #ifdef DD_CACHE_PROFILE
  278. #define DD_HYSTO_BINS 8
  279. /**
  280. @brief Computes and prints a profile of a local cache usage.
  281. @return 1 if successful; 0 otherwise.
  282. @sideeffect None
  283. */
  284. int
  285. cuddLocalCacheProfile(
  286. DdLocalCache * cache)
  287. {
  288. double count, mean, meansq, stddev, expected;
  289. long max, min;
  290. int imax, imin;
  291. int i, retval, slots;
  292. long *hystogram;
  293. int nbins = DD_HYSTO_BINS;
  294. int bin;
  295. long thiscount;
  296. double totalcount;
  297. int nzeroes;
  298. DdLocalCacheItem *entry;
  299. FILE *fp = cache->manager->out;
  300. slots = cache->slots;
  301. meansq = mean = expected = 0.0;
  302. max = min = (long) cache->item[0].count;
  303. imax = imin = nzeroes = 0;
  304. totalcount = 0.0;
  305. hystogram = ALLOC(long, nbins);
  306. if (hystogram == NULL) {
  307. return(0);
  308. }
  309. for (i = 0; i < nbins; i++) {
  310. hystogram[i] = 0;
  311. }
  312. for (i = 0; i < slots; i++) {
  313. entry = (DdLocalCacheItem *) ((char *) cache->item +
  314. i * cache->itemsize);
  315. thiscount = (long) entry->count;
  316. if (thiscount > max) {
  317. max = thiscount;
  318. imax = i;
  319. }
  320. if (thiscount < min) {
  321. min = thiscount;
  322. imin = i;
  323. }
  324. if (thiscount == 0) {
  325. nzeroes++;
  326. }
  327. count = (double) thiscount;
  328. mean += count;
  329. meansq += count * count;
  330. totalcount += count;
  331. expected += count * (double) i;
  332. bin = (i * nbins) / slots;
  333. hystogram[bin] += thiscount;
  334. }
  335. mean /= (double) slots;
  336. meansq /= (double) slots;
  337. stddev = sqrt(meansq - mean*mean);
  338. retval = fprintf(fp,"Cache stats: slots = %d average = %g ", slots, mean);
  339. if (retval == EOF) return(0);
  340. retval = fprintf(fp,"standard deviation = %g\n", stddev);
  341. if (retval == EOF) return(0);
  342. retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax);
  343. if (retval == EOF) return(0);
  344. retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin);
  345. if (retval == EOF) return(0);
  346. retval = fprintf(fp,"Cache unused slots = %d\n", nzeroes);
  347. if (retval == EOF) return(0);
  348. if (totalcount) {
  349. expected /= totalcount;
  350. retval = fprintf(fp,"Cache access hystogram for %d bins", nbins);
  351. if (retval == EOF) return(0);
  352. retval = fprintf(fp," (expected bin value = %g)\n# ", expected);
  353. if (retval == EOF) return(0);
  354. for (i = nbins - 1; i>=0; i--) {
  355. retval = fprintf(fp,"%ld ", hystogram[i]);
  356. if (retval == EOF) return(0);
  357. }
  358. retval = fprintf(fp,"\n");
  359. if (retval == EOF) return(0);
  360. }
  361. FREE(hystogram);
  362. return(1);
  363. } /* end of cuddLocalCacheProfile */
  364. #endif
  365. /**
  366. @brief Initializes a hash table.
  367. @details The table associates tuples of DdNode pointers to one DdNode pointer.
  368. This type of table is used for functions that cannot (or prefer not to) use
  369. the main computed table. The package also provides functions that allow the
  370. caller to store arbitrary pointers in the table.
  371. @return a pointer to the new table if successful; NULL otherwise.
  372. @sideeffect None
  373. @see cuddHashTableQuit cuddHashTableGenericQuit
  374. */
  375. DdHashTable *
  376. cuddHashTableInit(
  377. DdManager * manager /**< %DD manager */,
  378. unsigned int keySize /**< number of pointers in the key */,
  379. unsigned int initSize /**< initial size of the table */)
  380. {
  381. DdHashTable *hash;
  382. int logSize;
  383. hash = ALLOC(DdHashTable, 1);
  384. if (hash == NULL) {
  385. manager->errorCode = CUDD_MEMORY_OUT;
  386. return(NULL);
  387. }
  388. hash->keysize = keySize;
  389. hash->manager = manager;
  390. hash->memoryList = NULL;
  391. hash->nextFree = NULL;
  392. hash->itemsize = (keySize + 1) * sizeof(DdNode *) +
  393. sizeof(ptrint) + sizeof(DdHashItem *);
  394. /* We have to guarantee that the shift be < 32. */
  395. if (initSize < 2) initSize = 2;
  396. logSize = cuddComputeFloorLog2(initSize);
  397. hash->numBuckets = 1U << logSize;
  398. hash->shift = sizeof(int) * 8 - logSize;
  399. hash->bucket = ALLOC(DdHashItem *, hash->numBuckets);
  400. if (hash->bucket == NULL) {
  401. manager->errorCode = CUDD_MEMORY_OUT;
  402. FREE(hash);
  403. return(NULL);
  404. }
  405. memset(hash->bucket, 0, hash->numBuckets * sizeof(DdHashItem *));
  406. hash->size = 0;
  407. hash->maxsize = hash->numBuckets * DD_MAX_HASHTABLE_DENSITY;
  408. return(hash);
  409. } /* end of cuddHashTableInit */
  410. /**
  411. @brief Shuts down a hash table.
  412. @details Dereferences all the values.
  413. @sideeffect None
  414. @see cuddHashTableInit
  415. */
  416. void
  417. cuddHashTableQuit(
  418. DdHashTable * hash)
  419. {
  420. unsigned int i;
  421. DdManager *dd = hash->manager;
  422. DdHashItem *bucket;
  423. DdHashItem **memlist, **nextmem;
  424. unsigned int numBuckets = hash->numBuckets;
  425. for (i = 0; i < numBuckets; i++) {
  426. bucket = hash->bucket[i];
  427. while (bucket != NULL) {
  428. Cudd_RecursiveDeref(dd, bucket->value);
  429. bucket = bucket->next;
  430. }
  431. }
  432. memlist = hash->memoryList;
  433. while (memlist != NULL) {
  434. nextmem = (DdHashItem **) memlist[0];
  435. FREE(memlist);
  436. memlist = nextmem;
  437. }
  438. FREE(hash->bucket);
  439. FREE(hash);
  440. return;
  441. } /* end of cuddHashTableQuit */
  442. /**
  443. @brief Shuts down a hash table.
  444. @details Shuts down a hash table, when the values are not DdNode
  445. pointers.
  446. @sideeffect None
  447. @see cuddHashTableInit
  448. */
  449. void
  450. cuddHashTableGenericQuit(
  451. DdHashTable * hash)
  452. {
  453. DdHashItem **memlist, **nextmem;
  454. memlist = hash->memoryList;
  455. while (memlist != NULL) {
  456. nextmem = (DdHashItem **) memlist[0];
  457. FREE(memlist);
  458. memlist = nextmem;
  459. }
  460. FREE(hash->bucket);
  461. FREE(hash);
  462. return;
  463. } /* end of cuddHashTableGenericQuit */
  464. /**
  465. @brief Inserts an item in a hash table.
  466. @details Inserts an item in a hash table when the key has more than
  467. three pointers.
  468. @return 1 if successful; 0 otherwise.
  469. @sideeffect None
  470. @see [cuddHashTableInsert1 cuddHashTableInsert2 cuddHashTableInsert3
  471. cuddHashTableLookup
  472. */
  473. int
  474. cuddHashTableInsert(
  475. DdHashTable * hash,
  476. DdNodePtr * key,
  477. DdNode * value,
  478. ptrint count)
  479. {
  480. int result;
  481. unsigned int posn;
  482. DdHashItem *item;
  483. unsigned int i;
  484. #ifdef DD_DEBUG
  485. assert(hash->keysize > 3);
  486. #endif
  487. if (hash->size > hash->maxsize) {
  488. result = cuddHashTableResize(hash);
  489. if (result == 0) return(0);
  490. }
  491. item = cuddHashTableAlloc(hash);
  492. if (item == NULL) return(0);
  493. hash->size++;
  494. item->value = value;
  495. cuddRef(value);
  496. item->count = count;
  497. for (i = 0; i < hash->keysize; i++) {
  498. item->key[i] = key[i];
  499. }
  500. posn = ddLCHash(key,hash->keysize,hash->shift);
  501. item->next = hash->bucket[posn];
  502. hash->bucket[posn] = item;
  503. return(1);
  504. } /* end of cuddHashTableInsert */
  505. /**
  506. @brief Looks up a key in a hash table.
  507. @details Looks up a key consisting of more than three pointers in a
  508. hash table. If the entry is present, its reference counter is
  509. decremented if not saturated. If the counter reaches 0, the value of
  510. the entry is dereferenced, and the entry is returned to the free
  511. list.
  512. @return the value associated to the key if there is an entry for the
  513. given key in the table; NULL otherwise.
  514. @sideeffect None
  515. @see cuddHashTableLookup1 cuddHashTableLookup2 cuddHashTableLookup3
  516. cuddHashTableInsert
  517. */
  518. DdNode *
  519. cuddHashTableLookup(
  520. DdHashTable * hash,
  521. DdNodePtr * key)
  522. {
  523. unsigned int posn;
  524. DdHashItem *item, *prev;
  525. unsigned int i, keysize;
  526. #ifdef DD_DEBUG
  527. assert(hash->keysize > 3);
  528. #endif
  529. posn = ddLCHash(key,hash->keysize,hash->shift);
  530. item = hash->bucket[posn];
  531. prev = NULL;
  532. keysize = hash->keysize;
  533. while (item != NULL) {
  534. DdNodePtr *key2 = item->key;
  535. int equal = 1;
  536. for (i = 0; i < keysize; i++) {
  537. if (key[i] != key2[i]) {
  538. equal = 0;
  539. break;
  540. }
  541. }
  542. if (equal) {
  543. DdNode *value = item->value;
  544. cuddSatDec(item->count);
  545. if (item->count == 0) {
  546. cuddDeref(value);
  547. if (prev == NULL) {
  548. hash->bucket[posn] = item->next;
  549. } else {
  550. prev->next = item->next;
  551. }
  552. item->next = hash->nextFree;
  553. hash->nextFree = item;
  554. hash->size--;
  555. }
  556. return(value);
  557. }
  558. prev = item;
  559. item = item->next;
  560. }
  561. return(NULL);
  562. } /* end of cuddHashTableLookup */
  563. /**
  564. @brief Inserts an item in a hash table.
  565. @details Inserts an item in a hash table when the key is one pointer.
  566. @return 1 if successful; 0 otherwise.
  567. @sideeffect None
  568. @see cuddHashTableInsert cuddHashTableInsert2 cuddHashTableInsert3
  569. cuddHashTableLookup1
  570. */
  571. int
  572. cuddHashTableInsert1(
  573. DdHashTable * hash,
  574. DdNode * f,
  575. DdNode * value,
  576. ptrint count)
  577. {
  578. int result;
  579. unsigned int posn;
  580. DdHashItem *item;
  581. #ifdef DD_DEBUG
  582. assert(hash->keysize == 1);
  583. #endif
  584. if (hash->size > hash->maxsize) {
  585. result = cuddHashTableResize(hash);
  586. if (result == 0) return(0);
  587. }
  588. item = cuddHashTableAlloc(hash);
  589. if (item == NULL) return(0);
  590. hash->size++;
  591. item->value = value;
  592. cuddRef(value);
  593. item->count = count;
  594. item->key[0] = f;
  595. posn = ddLCHash1(f,hash->shift);
  596. item->next = hash->bucket[posn];
  597. hash->bucket[posn] = item;
  598. return(1);
  599. } /* end of cuddHashTableInsert1 */
  600. /**
  601. @brief Looks up a key consisting of one pointer in a hash table.
  602. @details If the entry is present, its reference count is
  603. decremented if not saturated. If the counter reaches 0, the value of
  604. the entry is dereferenced, and the entry is returned to the free
  605. list.
  606. @return the value associated to the key if there is an entry for the
  607. given key in the table; NULL otherwise.
  608. @sideeffect None
  609. @see cuddHashTableLookup cuddHashTableLookup2 cuddHashTableLookup3
  610. cuddHashTableInsert1
  611. */
  612. DdNode *
  613. cuddHashTableLookup1(
  614. DdHashTable * hash,
  615. DdNode * f)
  616. {
  617. unsigned int posn;
  618. DdHashItem *item, *prev;
  619. #ifdef DD_DEBUG
  620. assert(hash->keysize == 1);
  621. #endif
  622. posn = ddLCHash1(f,hash->shift);
  623. item = hash->bucket[posn];
  624. prev = NULL;
  625. while (item != NULL) {
  626. DdNodePtr *key = item->key;
  627. if (f == key[0]) {
  628. DdNode *value = item->value;
  629. cuddSatDec(item->count);
  630. if (item->count == 0) {
  631. cuddDeref(value);
  632. if (prev == NULL) {
  633. hash->bucket[posn] = item->next;
  634. } else {
  635. prev->next = item->next;
  636. }
  637. item->next = hash->nextFree;
  638. hash->nextFree = item;
  639. hash->size--;
  640. }
  641. return(value);
  642. }
  643. prev = item;
  644. item = item->next;
  645. }
  646. return(NULL);
  647. } /* end of cuddHashTableLookup1 */
  648. /**
  649. @brief Inserts a generic item in a hash table.
  650. @details Inserts an item in a hash table when the key is one
  651. pointer and the value is not a DdNode pointer. The main difference w.r.t.
  652. cuddHashTableInsert1 is that the reference count of the value is not
  653. incremented.
  654. @return 1 if successful; 0 otherwise.
  655. @sideeffect None
  656. @see cuddHashTableInsert1 cuddHashTableGenericLookup
  657. */
  658. int
  659. cuddHashTableGenericInsert(
  660. DdHashTable * hash,
  661. DdNode * f,
  662. void * value)
  663. {
  664. int result;
  665. unsigned int posn;
  666. DdHashItem *item;
  667. #ifdef DD_DEBUG
  668. assert(hash->keysize == 1);
  669. #endif
  670. if (hash->size > hash->maxsize) {
  671. result = cuddHashTableResize(hash);
  672. if (result == 0) return(0);
  673. }
  674. item = cuddHashTableAlloc(hash);
  675. if (item == NULL) return(0);
  676. hash->size++;
  677. item->value = (DdNode *) value;
  678. item->count = 0;
  679. item->key[0] = f;
  680. posn = ddLCHash1(f,hash->shift);
  681. item->next = hash->bucket[posn];
  682. hash->bucket[posn] = item;
  683. return(1);
  684. } /* end of cuddHashTableGenericInsert */
  685. /**
  686. @brief Looks up a key consisting of one pointer in a hash table.
  687. @details Looks up a key consisting of one pointer in a hash
  688. table when the value is not a DdNode pointer.
  689. @return the value associated to the key if there is an entry for the
  690. given key in the table; NULL otherwise.
  691. @sideeffect None
  692. @see cuddHashTableLookup1 cuddHashTableGenericInsert
  693. */
  694. void *
  695. cuddHashTableGenericLookup(
  696. DdHashTable * hash,
  697. DdNode * f)
  698. {
  699. unsigned int posn;
  700. DdHashItem *item;
  701. #ifdef DD_DEBUG
  702. assert(hash->keysize == 1);
  703. #endif
  704. posn = ddLCHash1(f,hash->shift);
  705. item = hash->bucket[posn];
  706. while (item != NULL) {
  707. if (f == item->key[0]) {
  708. return ((void *) item->value);
  709. }
  710. item = item->next;
  711. }
  712. return(NULL);
  713. } /* end of cuddHashTableGenericLookup */
  714. /**
  715. @brief Inserts an item in a hash table.
  716. @details Inserts an item in a hash table when the key is
  717. composed of two pointers.
  718. @return 1 if successful; 0 otherwise.
  719. @sideeffect None
  720. @see cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert3
  721. cuddHashTableLookup2
  722. */
  723. int
  724. cuddHashTableInsert2(
  725. DdHashTable * hash,
  726. DdNode * f,
  727. DdNode * g,
  728. DdNode * value,
  729. ptrint count)
  730. {
  731. int result;
  732. unsigned int posn;
  733. DdHashItem *item;
  734. #ifdef DD_DEBUG
  735. assert(hash->keysize == 2);
  736. #endif
  737. if (hash->size > hash->maxsize) {
  738. result = cuddHashTableResize(hash);
  739. if (result == 0) return(0);
  740. }
  741. item = cuddHashTableAlloc(hash);
  742. if (item == NULL) return(0);
  743. hash->size++;
  744. item->value = value;
  745. cuddRef(value);
  746. item->count = count;
  747. item->key[0] = f;
  748. item->key[1] = g;
  749. posn = ddLCHash2(f,g,hash->shift);
  750. item->next = hash->bucket[posn];
  751. hash->bucket[posn] = item;
  752. return(1);
  753. } /* end of cuddHashTableInsert2 */
  754. /**
  755. @brief Looks up a key consisting of two pointers in a hash table.
  756. @details If the entry is present, its reference counter is
  757. decremented if not saturated. If the counter reaches 0, the value of
  758. the entry is dereferenced, and the entry is returned to the free
  759. list.
  760. @return the value associated to the key if there is an entry for the
  761. given key in the table; NULL otherwise.
  762. @sideeffect None
  763. @see cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup3
  764. cuddHashTableInsert2
  765. */
  766. DdNode *
  767. cuddHashTableLookup2(
  768. DdHashTable * hash,
  769. DdNode * f,
  770. DdNode * g)
  771. {
  772. unsigned int posn;
  773. DdHashItem *item, *prev;
  774. #ifdef DD_DEBUG
  775. assert(hash->keysize == 2);
  776. #endif
  777. posn = ddLCHash2(f,g,hash->shift);
  778. item = hash->bucket[posn];
  779. prev = NULL;
  780. while (item != NULL) {
  781. DdNodePtr *key = item->key;
  782. if ((f == key[0]) && (g == key[1])) {
  783. DdNode *value = item->value;
  784. cuddSatDec(item->count);
  785. if (item->count == 0) {
  786. cuddDeref(value);
  787. if (prev == NULL) {
  788. hash->bucket[posn] = item->next;
  789. } else {
  790. prev->next = item->next;
  791. }
  792. item->next = hash->nextFree;
  793. hash->nextFree = item;
  794. hash->size--;
  795. }
  796. return(value);
  797. }
  798. prev = item;
  799. item = item->next;
  800. }
  801. return(NULL);
  802. } /* end of cuddHashTableLookup2 */
  803. /**
  804. @brief Inserts an item in a hash table.
  805. @details Inserts an item in a hash table when the key is
  806. composed of three pointers.
  807. @return 1 if successful; 0 otherwise.
  808. @sideeffect None
  809. @see cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert2
  810. cuddHashTableLookup3
  811. */
  812. int
  813. cuddHashTableInsert3(
  814. DdHashTable * hash,
  815. DdNode * f,
  816. DdNode * g,
  817. DdNode * h,
  818. DdNode * value,
  819. ptrint count)
  820. {
  821. int result;
  822. unsigned int posn;
  823. DdHashItem *item;
  824. #ifdef DD_DEBUG
  825. assert(hash->keysize == 3);
  826. #endif
  827. if (hash->size > hash->maxsize) {
  828. result = cuddHashTableResize(hash);
  829. if (result == 0) return(0);
  830. }
  831. item = cuddHashTableAlloc(hash);
  832. if (item == NULL) return(0);
  833. hash->size++;
  834. item->value = value;
  835. cuddRef(value);
  836. item->count = count;
  837. item->key[0] = f;
  838. item->key[1] = g;
  839. item->key[2] = h;
  840. posn = ddLCHash3(f,g,h,hash->shift);
  841. item->next = hash->bucket[posn];
  842. hash->bucket[posn] = item;
  843. return(1);
  844. } /* end of cuddHashTableInsert3 */
  845. /**
  846. @brief Looks up a key consisting of three pointers in a hash table.
  847. @details If the entry is present, its reference counter is
  848. decremented if not saturated. If the counter reaches 0, the value of
  849. the entry is dereferenced, and the entry is returned to the free
  850. list.
  851. @return the value associated to the key if there is an entry for the
  852. given key in the table; NULL otherwise.
  853. @sideeffect None
  854. @see cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup2
  855. cuddHashTableInsert3
  856. */
  857. DdNode *
  858. cuddHashTableLookup3(
  859. DdHashTable * hash,
  860. DdNode * f,
  861. DdNode * g,
  862. DdNode * h)
  863. {
  864. unsigned int posn;
  865. DdHashItem *item, *prev;
  866. #ifdef DD_DEBUG
  867. assert(hash->keysize == 3);
  868. #endif
  869. posn = ddLCHash3(f,g,h,hash->shift);
  870. item = hash->bucket[posn];
  871. prev = NULL;
  872. while (item != NULL) {
  873. DdNodePtr *key = item->key;
  874. if ((f == key[0]) && (g == key[1]) && (h == key[2])) {
  875. DdNode *value = item->value;
  876. cuddSatDec(item->count);
  877. if (item->count == 0) {
  878. cuddDeref(value);
  879. if (prev == NULL) {
  880. hash->bucket[posn] = item->next;
  881. } else {
  882. prev->next = item->next;
  883. }
  884. item->next = hash->nextFree;
  885. hash->nextFree = item;
  886. hash->size--;
  887. }
  888. return(value);
  889. }
  890. prev = item;
  891. item = item->next;
  892. }
  893. return(NULL);
  894. } /* end of cuddHashTableLookup3 */
  895. /*---------------------------------------------------------------------------*/
  896. /* Definition of static functions */
  897. /*---------------------------------------------------------------------------*/
  898. /**
  899. @brief Resizes a local cache.
  900. @sideeffect None
  901. */
  902. static void
  903. cuddLocalCacheResize(
  904. DdLocalCache * cache)
  905. {
  906. DdLocalCacheItem *item, *olditem, *entry, *old;
  907. int i, shift;
  908. unsigned int posn;
  909. unsigned int slots, oldslots;
  910. extern DD_OOMFP MMoutOfMemory;
  911. DD_OOMFP saveHandler;
  912. olditem = cache->item;
  913. oldslots = cache->slots;
  914. slots = cache->slots = oldslots << 1;
  915. #ifdef DD_VERBOSE
  916. (void) fprintf(cache->manager->err,
  917. "Resizing local cache from %d to %d entries\n",
  918. oldslots, slots);
  919. (void) fprintf(cache->manager->err,
  920. "\thits = %.0f\tlookups = %.0f\thit ratio = %5.3f\n",
  921. cache->hits, cache->lookUps, cache->hits / cache->lookUps);
  922. #endif
  923. saveHandler = MMoutOfMemory;
  924. MMoutOfMemory = cache->manager->outOfMemCallback;
  925. cache->item = item =
  926. (DdLocalCacheItem *) ALLOC(char, slots * cache->itemsize);
  927. MMoutOfMemory = saveHandler;
  928. /* If we fail to allocate the new table we just give up. */
  929. if (item == NULL) {
  930. #ifdef DD_VERBOSE
  931. (void) fprintf(cache->manager->err,"Resizing failed. Giving up.\n");
  932. #endif
  933. cache->slots = oldslots;
  934. cache->item = olditem;
  935. /* Do not try to resize again. */
  936. cache->maxslots = oldslots - 1;
  937. return;
  938. }
  939. shift = --(cache->shift);
  940. cache->manager->memused += (slots - oldslots) * cache->itemsize;
  941. /* Clear new cache. */
  942. memset(item, 0, slots * cache->itemsize);
  943. /* Copy from old cache to new one. */
  944. for (i = 0; (unsigned) i < oldslots; i++) {
  945. old = (DdLocalCacheItem *) ((char *) olditem + i * cache->itemsize);
  946. if (old->value != NULL) {
  947. posn = ddLCHash(old->key,cache->keysize,shift);
  948. entry = (DdLocalCacheItem *) ((char *) item +
  949. posn * cache->itemsize);
  950. memcpy(entry->key,old->key,cache->keysize*sizeof(DdNode *));
  951. entry->value = old->value;
  952. }
  953. }
  954. FREE(olditem);
  955. /* Reinitialize measurements so as to avoid division by 0 and
  956. ** immediate resizing.
  957. */
  958. cache->lookUps = (double) (int) (slots * cache->minHit + 1);
  959. cache->hits = 0;
  960. } /* end of cuddLocalCacheResize */
  961. /**
  962. @brief Computes the hash value for a local cache.
  963. @return the bucket index.
  964. @sideeffect None
  965. */
  966. static unsigned int
  967. ddLCHash(
  968. DdNodePtr * key,
  969. unsigned int keysize,
  970. int shift)
  971. {
  972. unsigned int val = (unsigned int) (ptrint) key[0] * DD_P2;
  973. unsigned int i;
  974. for (i = 1; i < keysize; i++) {
  975. val = val * DD_P1 + (int) (ptrint) key[i];
  976. }
  977. return(val >> shift);
  978. } /* end of ddLCHash */
  979. /**
  980. @brief Inserts a local cache in the manager list.
  981. @sideeffect None
  982. */
  983. static void
  984. cuddLocalCacheAddToList(
  985. DdLocalCache * cache)
  986. {
  987. DdManager *manager = cache->manager;
  988. cache->next = manager->localCaches;
  989. manager->localCaches = cache;
  990. return;
  991. } /* end of cuddLocalCacheAddToList */
  992. /**
  993. @brief Removes a local cache from the manager list.
  994. @sideeffect None
  995. */
  996. static void
  997. cuddLocalCacheRemoveFromList(
  998. DdLocalCache * cache)
  999. {
  1000. DdManager *manager = cache->manager;
  1001. DdLocalCache **prevCache, *nextCache;
  1002. prevCache = &(manager->localCaches);
  1003. nextCache = manager->localCaches;
  1004. while (nextCache != NULL) {
  1005. if (nextCache == cache) {
  1006. *prevCache = nextCache->next;
  1007. return;
  1008. }
  1009. prevCache = &(nextCache->next);
  1010. nextCache = nextCache->next;
  1011. }
  1012. return; /* should never get here */
  1013. } /* end of cuddLocalCacheRemoveFromList */
  1014. /**
  1015. @brief Resizes a hash table.
  1016. @return 1 if successful; 0 otherwise.
  1017. @sideeffect None
  1018. @see cuddHashTableInsert
  1019. */
  1020. static int
  1021. cuddHashTableResize(
  1022. DdHashTable * hash)
  1023. {
  1024. int j;
  1025. unsigned int posn;
  1026. DdHashItem *item;
  1027. DdHashItem *next;
  1028. DdNode **key;
  1029. int numBuckets;
  1030. DdHashItem **buckets;
  1031. DdHashItem **oldBuckets = hash->bucket;
  1032. int shift;
  1033. int oldNumBuckets = hash->numBuckets;
  1034. extern DD_OOMFP MMoutOfMemory;
  1035. DD_OOMFP saveHandler;
  1036. /* Compute the new size of the table. */
  1037. numBuckets = oldNumBuckets << 1;
  1038. saveHandler = MMoutOfMemory;
  1039. MMoutOfMemory = hash->manager->outOfMemCallback;
  1040. buckets = ALLOC(DdHashItem *, numBuckets);
  1041. MMoutOfMemory = saveHandler;
  1042. if (buckets == NULL) {
  1043. hash->maxsize <<= 1;
  1044. return(1);
  1045. }
  1046. hash->bucket = buckets;
  1047. hash->numBuckets = numBuckets;
  1048. shift = --(hash->shift);
  1049. hash->maxsize <<= 1;
  1050. memset(buckets, 0, numBuckets * sizeof(DdHashItem *));
  1051. if (hash->keysize == 1) {
  1052. for (j = 0; j < oldNumBuckets; j++) {
  1053. item = oldBuckets[j];
  1054. while (item != NULL) {
  1055. next = item->next;
  1056. key = item->key;
  1057. posn = ddLCHash1(key[0], shift);
  1058. item->next = buckets[posn];
  1059. buckets[posn] = item;
  1060. item = next;
  1061. }
  1062. }
  1063. } else if (hash->keysize == 2) {
  1064. for (j = 0; j < oldNumBuckets; j++) {
  1065. item = oldBuckets[j];
  1066. while (item != NULL) {
  1067. next = item->next;
  1068. key = item->key;
  1069. posn = ddLCHash2(key[0], key[1], shift);
  1070. item->next = buckets[posn];
  1071. buckets[posn] = item;
  1072. item = next;
  1073. }
  1074. }
  1075. } else if (hash->keysize == 3) {
  1076. for (j = 0; j < oldNumBuckets; j++) {
  1077. item = oldBuckets[j];
  1078. while (item != NULL) {
  1079. next = item->next;
  1080. key = item->key;
  1081. posn = ddLCHash3(key[0], key[1], key[2], shift);
  1082. item->next = buckets[posn];
  1083. buckets[posn] = item;
  1084. item = next;
  1085. }
  1086. }
  1087. } else {
  1088. for (j = 0; j < oldNumBuckets; j++) {
  1089. item = oldBuckets[j];
  1090. while (item != NULL) {
  1091. next = item->next;
  1092. posn = ddLCHash(item->key, hash->keysize, shift);
  1093. item->next = buckets[posn];
  1094. buckets[posn] = item;
  1095. item = next;
  1096. }
  1097. }
  1098. }
  1099. FREE(oldBuckets);
  1100. return(1);
  1101. } /* end of cuddHashTableResize */
  1102. /**
  1103. @brief Fast storage allocation for items in a hash table.
  1104. @details The first sizeof(void *) bytes of a chunk contain a pointer to the
  1105. next block; the rest contains DD_MEM_CHUNK spaces for hash items.
  1106. @return a pointer to a new item if successful; NULL is memory is full.
  1107. @sideeffect None
  1108. @see cuddAllocNode cuddDynamicAllocNode
  1109. */
  1110. static DdHashItem *
  1111. cuddHashTableAlloc(
  1112. DdHashTable * hash)
  1113. {
  1114. int i;
  1115. unsigned int itemsize = hash->itemsize;
  1116. extern DD_OOMFP MMoutOfMemory;
  1117. DD_OOMFP saveHandler;
  1118. DdHashItem **mem, *thisOne, *next, *item;
  1119. if (hash->nextFree == NULL) {
  1120. saveHandler = MMoutOfMemory;
  1121. MMoutOfMemory = hash->manager->outOfMemCallback;
  1122. mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize);
  1123. MMoutOfMemory = saveHandler;
  1124. if (mem == NULL) {
  1125. if (hash->manager->stash != NULL) {
  1126. FREE(hash->manager->stash);
  1127. hash->manager->stash = NULL;
  1128. /* Inhibit resizing of tables. */
  1129. hash->manager->maxCacheHard = hash->manager->cacheSlots - 1;
  1130. hash->manager->cacheSlack = - (int) (hash->manager->cacheSlots + 1);
  1131. for (i = 0; i < hash->manager->size; i++) {
  1132. hash->manager->subtables[i].maxKeys <<= 2;
  1133. }
  1134. hash->manager->gcFrac = 0.2;
  1135. hash->manager->minDead =
  1136. (unsigned) (0.2 * (double) hash->manager->slots);
  1137. mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize);
  1138. }
  1139. if (mem == NULL) {
  1140. (*MMoutOfMemory)((size_t)((DD_MEM_CHUNK + 1) * itemsize));
  1141. hash->manager->errorCode = CUDD_MEMORY_OUT;
  1142. return(NULL);
  1143. }
  1144. }
  1145. mem[0] = (DdHashItem *) hash->memoryList;
  1146. hash->memoryList = mem;
  1147. thisOne = (DdHashItem *) ((char *) mem + itemsize);
  1148. hash->nextFree = thisOne;
  1149. for (i = 1; i < DD_MEM_CHUNK; i++) {
  1150. next = (DdHashItem *) ((char *) thisOne + itemsize);
  1151. thisOne->next = next;
  1152. thisOne = next;
  1153. }
  1154. thisOne->next = NULL;
  1155. }
  1156. item = hash->nextFree;
  1157. hash->nextFree = item->next;
  1158. return(item);
  1159. } /* end of cuddHashTableAlloc */