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.

312 lines
9.2 KiB

  1. /*
  2. * Copyright 2011-2016 Formal Methods and Tools, University of Twente
  3. * Copyright 2016-2017 Tom van Dijk, Johannes Kepler University Linz
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #include <sylvan_int.h>
  18. #include <errno.h> // for errno
  19. #include <string.h> // for strerror
  20. #include <sys/mman.h> // for mmap
  21. #ifndef MAP_ANONYMOUS
  22. #define MAP_ANONYMOUS MAP_ANON
  23. #endif
  24. #ifndef CACHE_MASK
  25. #define CACHE_MASK 1
  26. #endif
  27. #ifndef compiler_barrier
  28. #define compiler_barrier() { asm volatile("" ::: "memory"); }
  29. #endif
  30. #ifndef cas
  31. #define cas(ptr, old, new) (__sync_bool_compare_and_swap((ptr),(old),(new)))
  32. #endif
  33. /**
  34. * This cache is designed to store a,b,c->res, with a,b,c,res 64-bit integers.
  35. *
  36. * Each cache bucket takes 32 bytes, 2 per cache line.
  37. * Each cache status bucket takes 4 bytes, 16 per cache line.
  38. * Therefore, size 2^N = 36*(2^N) bytes.
  39. */
  40. struct __attribute__((packed)) cache6_entry {
  41. uint64_t a;
  42. uint64_t b;
  43. uint64_t c;
  44. uint64_t res;
  45. uint64_t d;
  46. uint64_t e;
  47. uint64_t f;
  48. uint64_t res2;
  49. };
  50. typedef struct cache6_entry *cache6_entry_t;
  51. struct __attribute__((packed)) cache_entry {
  52. uint64_t a;
  53. uint64_t b;
  54. uint64_t c;
  55. uint64_t res;
  56. };
  57. static size_t cache_size; // power of 2
  58. static size_t cache_max; // power of 2
  59. #if CACHE_MASK
  60. static size_t cache_mask; // cache_size-1
  61. #endif
  62. static cache_entry_t cache_table;
  63. static uint32_t* cache_status;
  64. static uint64_t next_opid;
  65. uint64_t
  66. cache_next_opid()
  67. {
  68. return __sync_fetch_and_add(&next_opid, 1LL<<40);
  69. }
  70. // status: 0x80000000 - bitlock
  71. // 0x7fff0000 - hash (part of the 64-bit hash not used to position)
  72. // 0x0000ffff - tag (every put increases tag field)
  73. /* Rotating 64-bit FNV-1a hash */
  74. static uint64_t
  75. cache_hash(uint64_t a, uint64_t b, uint64_t c)
  76. {
  77. const uint64_t prime = 1099511628211;
  78. uint64_t hash = 14695981039346656037LLU;
  79. hash = (hash ^ (a>>32));
  80. hash = (hash ^ a) * prime;
  81. hash = (hash ^ b) * prime;
  82. hash = (hash ^ c) * prime;
  83. return hash;
  84. }
  85. static uint64_t
  86. cache_hash6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f)
  87. {
  88. const uint64_t prime = 1099511628211;
  89. uint64_t hash = 14695981039346656037LLU;
  90. hash = (hash ^ (a>>32));
  91. hash = (hash ^ a) * prime;
  92. hash = (hash ^ b) * prime;
  93. hash = (hash ^ c) * prime;
  94. hash = (hash ^ d) * prime;
  95. hash = (hash ^ e) * prime;
  96. hash = (hash ^ f) * prime;
  97. return hash;
  98. }
  99. int
  100. cache_get6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t *res1, uint64_t *res2)
  101. {
  102. const uint64_t hash = cache_hash6(a, b, c, d, e, f);
  103. #if CACHE_MASK
  104. volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash & cache_mask)/2;
  105. cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash & cache_mask)/2;
  106. #else
  107. volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash % cache_size)/2;
  108. cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash % cache_size)/2;
  109. #endif
  110. const uint64_t s = *s_bucket;
  111. compiler_barrier();
  112. // abort if locked or second part of 2-part entry or if different hash
  113. uint64_t x = ((hash>>32) & 0x7fff0000) | 0x04000000;
  114. x = x | (x<<32);
  115. if ((s & 0xffff0000ffff0000) != x) return 0;
  116. // abort if key different
  117. if (bucket->a != a || bucket->b != b || bucket->c != c) return 0;
  118. if (bucket->d != d || bucket->e != e || bucket->f != f) return 0;
  119. *res1 = bucket->res;
  120. if (res2) *res2 = bucket->res2;
  121. compiler_barrier();
  122. // abort if status field changed after compiler_barrier()
  123. return *s_bucket == s ? 1 : 0;
  124. }
  125. int
  126. cache_put6(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t res1, uint64_t res2)
  127. {
  128. const uint64_t hash = cache_hash6(a, b, c, d, e, f);
  129. #if CACHE_MASK
  130. volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash & cache_mask)/2;
  131. cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash & cache_mask)/2;
  132. #else
  133. volatile uint64_t *s_bucket = (uint64_t*)cache_status + (hash % cache_size)/2;
  134. cache6_entry_t bucket = (cache6_entry_t)cache_table + (hash % cache_size)/2;
  135. #endif
  136. const uint64_t s = *s_bucket;
  137. // abort if locked
  138. if (s & 0x8000000080000000LL) return 0;
  139. // create new
  140. uint64_t new_s = ((hash>>32) & 0x7fff0000) | 0x04000000;
  141. new_s |= (new_s<<32);
  142. new_s |= (((s>>32)+1)&0xffff)<<32;
  143. new_s |= (s+1)&0xffff;
  144. // use cas to claim bucket
  145. if (!cas(s_bucket, s, new_s | 0x8000000080000000LL)) return 0;
  146. // cas succesful: write data
  147. bucket->a = a;
  148. bucket->b = b;
  149. bucket->c = c;
  150. bucket->d = d;
  151. bucket->e = e;
  152. bucket->f = f;
  153. bucket->res = res1;
  154. bucket->res2 = res2;
  155. compiler_barrier();
  156. // after compiler_barrier(), unlock status field
  157. *s_bucket = new_s;
  158. return 1;
  159. }
  160. int
  161. cache_get(uint64_t a, uint64_t b, uint64_t c, uint64_t *res)
  162. {
  163. const uint64_t hash = cache_hash(a, b, c);
  164. #if CACHE_MASK
  165. volatile uint32_t *s_bucket = cache_status + (hash & cache_mask);
  166. cache_entry_t bucket = cache_table + (hash & cache_mask);
  167. #else
  168. volatile uint32_t *s_bucket = cache_status + (hash % cache_size);
  169. cache_entry_t bucket = cache_table + (hash % cache_size);
  170. #endif
  171. const uint32_t s = *s_bucket;
  172. compiler_barrier();
  173. // abort if locked or if part of a 2-part cache entry
  174. if (s & 0xc0000000) return 0;
  175. // abort if different hash
  176. if ((s ^ (hash>>32)) & 0x3fff0000) return 0;
  177. // abort if key different
  178. if (bucket->a != a || bucket->b != b || bucket->c != c) return 0;
  179. *res = bucket->res;
  180. compiler_barrier();
  181. // abort if status field changed after compiler_barrier()
  182. return *s_bucket == s ? 1 : 0;
  183. }
  184. int
  185. cache_put(uint64_t a, uint64_t b, uint64_t c, uint64_t res)
  186. {
  187. const uint64_t hash = cache_hash(a, b, c);
  188. #if CACHE_MASK
  189. volatile uint32_t *s_bucket = cache_status + (hash & cache_mask);
  190. cache_entry_t bucket = cache_table + (hash & cache_mask);
  191. #else
  192. volatile uint32_t *s_bucket = cache_status + (hash % cache_size);
  193. cache_entry_t bucket = cache_table + (hash % cache_size);
  194. #endif
  195. const uint32_t s = *s_bucket;
  196. // abort if locked
  197. if (s & 0x80000000) return 0;
  198. // abort if hash identical -> no: in iscasmc this occasionally causes timeouts?!
  199. const uint32_t hash_mask = (hash>>32) & 0x3fff0000;
  200. // if ((s & 0x7fff0000) == hash_mask) return 0;
  201. // use cas to claim bucket
  202. const uint32_t new_s = ((s+1) & 0x0000ffff) | hash_mask;
  203. if (!cas(s_bucket, s, new_s | 0x80000000)) return 0;
  204. // cas succesful: write data
  205. bucket->a = a;
  206. bucket->b = b;
  207. bucket->c = c;
  208. bucket->res = res;
  209. compiler_barrier();
  210. // after compiler_barrier(), unlock status field
  211. *s_bucket = new_s;
  212. return 1;
  213. }
  214. void
  215. cache_create(size_t _cache_size, size_t _max_size)
  216. {
  217. #if CACHE_MASK
  218. // Cache size must be a power of 2
  219. if (__builtin_popcountll(_cache_size) != 1 || __builtin_popcountll(_max_size) != 1) {
  220. fprintf(stderr, "cache_create: Table size must be a power of 2!\n");
  221. exit(1);
  222. }
  223. #endif
  224. cache_size = _cache_size;
  225. cache_max = _max_size;
  226. #if CACHE_MASK
  227. cache_mask = cache_size - 1;
  228. #endif
  229. if (cache_size > cache_max) {
  230. fprintf(stderr, "cache_create: Table size must be <= max size!\n");
  231. exit(1);
  232. }
  233. cache_table = (cache_entry_t)mmap(0, cache_max * sizeof(struct cache_entry), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  234. cache_status = (uint32_t*)mmap(0, cache_max * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  235. if (cache_table == (cache_entry_t)-1 || cache_status == (uint32_t*)-1) {
  236. fprintf(stderr, "cache_create: Unable to allocate memory: %s!\n", strerror(errno));
  237. exit(1);
  238. }
  239. next_opid = 512LL << 40;
  240. }
  241. void
  242. cache_free()
  243. {
  244. munmap(cache_table, cache_max * sizeof(struct cache_entry));
  245. munmap(cache_status, cache_max * sizeof(uint32_t));
  246. }
  247. void
  248. cache_clear()
  249. {
  250. // a bit silly, but this works just fine, and does not require writing 0 everywhere...
  251. cache_free();
  252. cache_create(cache_size, cache_max);
  253. }
  254. void
  255. cache_setsize(size_t size)
  256. {
  257. // easy solution
  258. cache_free();
  259. cache_create(size, cache_max);
  260. }
  261. size_t
  262. cache_getsize()
  263. {
  264. return cache_size;
  265. }
  266. size_t
  267. cache_getused()
  268. {
  269. size_t result = 0;
  270. for (size_t i=0;i<cache_size;i++) {
  271. uint32_t s = cache_status[i];
  272. if (s & 0x80000000) fprintf(stderr, "cache_getuser: cache in use during cache_getused()\n");
  273. if (s) result++;
  274. }
  275. return result;
  276. }
  277. size_t
  278. cache_getmaxsize()
  279. {
  280. return cache_max;
  281. }