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.

864 lines
24 KiB

3 months ago
  1. #include <argp.h>
  2. #include <inttypes.h>
  3. #include <locale.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #ifdef HAVE_PROFILER
  9. #include <gperftools/profiler.h>
  10. #endif
  11. #include <getrss.h>
  12. #include <sylvan_int.h>
  13. /* Configuration (via argp) */
  14. static int report_levels = 0; // report states at start of every level
  15. static int report_table = 0; // report table size at end of every level
  16. static int report_nodes = 0; // report number of nodes of LDDs
  17. static int strategy = 2; // 0 = BFS, 1 = PAR, 2 = SAT, 3 = CHAINING
  18. static int check_deadlocks = 0; // set to 1 to check for deadlocks on-the-fly
  19. static int print_transition_matrix = 0; // print transition relation matrix
  20. static int workers = 0; // autodetect
  21. static char* model_filename = NULL; // filename of model
  22. static char* out_filename = NULL; // filename of output
  23. #ifdef HAVE_PROFILER
  24. static char* profile_filename = NULL; // filename for profiling
  25. #endif
  26. /* argp configuration */
  27. static struct argp_option options[] =
  28. {
  29. {"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0},
  30. {"strategy", 's', "<bfs|par|sat|chaining>", 0, "Strategy for reachability (default=par)", 0},
  31. #ifdef HAVE_PROFILER
  32. {"profiler", 'p', "<filename>", 0, "Filename for profiling", 0},
  33. #endif
  34. {"deadlocks", 3, 0, 0, "Check for deadlocks", 1},
  35. {"count-nodes", 5, 0, 0, "Report #nodes for LDDs", 1},
  36. {"count-states", 1, 0, 0, "Report #states at each level", 1},
  37. {"count-table", 2, 0, 0, "Report table usage at each level", 1},
  38. {"print-matrix", 4, 0, 0, "Print transition matrix", 1},
  39. {0, 0, 0, 0, 0, 0}
  40. };
  41. static error_t
  42. parse_opt(int key, char *arg, struct argp_state *state)
  43. {
  44. switch (key) {
  45. case 'w':
  46. workers = atoi(arg);
  47. break;
  48. case 's':
  49. if (strcmp(arg, "bfs")==0) strategy = 0;
  50. else if (strcmp(arg, "par")==0) strategy = 1;
  51. else if (strcmp(arg, "sat")==0) strategy = 2;
  52. else if (strcmp(arg, "chaining")==0) strategy = 3;
  53. else argp_usage(state);
  54. break;
  55. case 4:
  56. print_transition_matrix = 1;
  57. break;
  58. case 3:
  59. check_deadlocks = 1;
  60. break;
  61. case 1:
  62. report_levels = 1;
  63. break;
  64. case 2:
  65. report_table = 1;
  66. break;
  67. case 5:
  68. report_nodes = 1;
  69. break;
  70. #ifdef HAVE_PROFILER
  71. case 'p':
  72. profile_filename = arg;
  73. break;
  74. #endif
  75. case ARGP_KEY_ARG:
  76. if (state->arg_num == 0) model_filename = arg;
  77. if (state->arg_num == 1) out_filename = arg;
  78. if (state->arg_num >= 2) argp_usage(state);
  79. break;
  80. case ARGP_KEY_END:
  81. if (state->arg_num < 1) argp_usage(state);
  82. break;
  83. default:
  84. return ARGP_ERR_UNKNOWN;
  85. }
  86. return 0;
  87. }
  88. static struct argp argp = { options, parse_opt, "<model> [<output-bdd>]", 0, 0, 0, 0 };
  89. /**
  90. * Types (set and relation)
  91. */
  92. typedef struct set
  93. {
  94. MDD dd;
  95. } *set_t;
  96. typedef struct relation
  97. {
  98. MDD dd;
  99. MDD meta; // for relprod
  100. int r_k, w_k, *r_proj, *w_proj;
  101. int firstvar; // for saturation/chaining
  102. MDD topmeta; // for saturation
  103. } *rel_t;
  104. static int vector_size; // size of vector in integers
  105. static int next_count; // number of partitions of the transition relation
  106. static rel_t *next; // each partition of the transition relation
  107. /**
  108. * Obtain current wallclock time
  109. */
  110. static double
  111. wctime()
  112. {
  113. struct timeval tv;
  114. gettimeofday(&tv, NULL);
  115. return (tv.tv_sec + 1E-6 * tv.tv_usec);
  116. }
  117. static double t_start;
  118. #define INFO(s, ...) fprintf(stdout, "[% 8.2f] " s, wctime()-t_start, ##__VA_ARGS__)
  119. #define Abort(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "Abort at line %d!\n", __LINE__); exit(-1); }
  120. /**
  121. * Load a set from file
  122. */
  123. static set_t
  124. set_load(FILE* f)
  125. {
  126. set_t set = (set_t)malloc(sizeof(struct set));
  127. /* read projection (actually we don't support projection) */
  128. int k;
  129. if (fread(&k, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  130. if (k != -1) Abort("Invalid input file!\n"); // only support full vector
  131. /* read dd */
  132. lddmc_serialize_fromfile(f);
  133. size_t dd;
  134. if (fread(&dd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  135. set->dd = lddmc_serialize_get_reversed(dd);
  136. lddmc_protect(&set->dd);
  137. return set;
  138. }
  139. /**
  140. * Save a set to file
  141. */
  142. static void
  143. set_save(FILE* f, set_t set)
  144. {
  145. int k = -1;
  146. fwrite(&k, sizeof(int), 1, f);
  147. size_t dd = lddmc_serialize_add(set->dd);
  148. lddmc_serialize_tofile(f);
  149. fwrite(&dd, sizeof(size_t), 1, f);
  150. }
  151. /**
  152. * Load a relation from file
  153. */
  154. #define rel_load_proj(f) CALL(rel_load_proj, f)
  155. TASK_1(rel_t, rel_load_proj, FILE*, f)
  156. {
  157. int r_k, w_k;
  158. if (fread(&r_k, sizeof(int), 1, f) != 1) Abort("Invalid file format.");
  159. if (fread(&w_k, sizeof(int), 1, f) != 1) Abort("Invalid file format.");
  160. rel_t rel = (rel_t)malloc(sizeof(struct relation));
  161. rel->r_k = r_k;
  162. rel->w_k = w_k;
  163. rel->r_proj = (int*)malloc(sizeof(int[rel->r_k]));
  164. rel->w_proj = (int*)malloc(sizeof(int[rel->w_k]));
  165. if (fread(rel->r_proj, sizeof(int), rel->r_k, f) != (size_t)rel->r_k) Abort("Invalid file format.");
  166. if (fread(rel->w_proj, sizeof(int), rel->w_k, f) != (size_t)rel->w_k) Abort("Invalid file format.");
  167. int *r_proj = rel->r_proj;
  168. int *w_proj = rel->w_proj;
  169. rel->firstvar = -1;
  170. /* Compute the meta */
  171. uint32_t meta[vector_size*2+2];
  172. memset(meta, 0, sizeof(uint32_t[vector_size*2+2]));
  173. int r_i=0, w_i=0, i=0, j=0;
  174. for (;;) {
  175. int type = 0;
  176. if (r_i < r_k && r_proj[r_i] == i) {
  177. r_i++;
  178. type += 1; // read
  179. }
  180. if (w_i < w_k && w_proj[w_i] == i) {
  181. w_i++;
  182. type += 2; // write
  183. }
  184. if (type == 0) meta[j++] = 0;
  185. else if (type == 1) { meta[j++] = 3; }
  186. else if (type == 2) { meta[j++] = 4; }
  187. else if (type == 3) { meta[j++] = 1; meta[j++] = 2; }
  188. if (type != 0 && rel->firstvar == -1) rel->firstvar = i;
  189. if (r_i == r_k && w_i == w_k) {
  190. meta[j++] = 5; // action label
  191. meta[j++] = (uint32_t)-1;
  192. break;
  193. }
  194. i++;
  195. }
  196. rel->meta = lddmc_cube((uint32_t*)meta, j);
  197. lddmc_protect(&rel->meta);
  198. if (rel->firstvar != -1) {
  199. rel->topmeta = lddmc_cube((uint32_t*)meta+rel->firstvar, j-rel->firstvar);
  200. lddmc_protect(&rel->topmeta);
  201. }
  202. rel->dd = lddmc_false;
  203. lddmc_protect(&rel->dd);
  204. return rel;
  205. }
  206. #define rel_load(f, rel) CALL(rel_load, f, rel)
  207. VOID_TASK_2(rel_load, FILE*, f, rel_t, rel)
  208. {
  209. lddmc_serialize_fromfile(f);
  210. size_t dd;
  211. if (fread(&dd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!");
  212. rel->dd = lddmc_serialize_get_reversed(dd);
  213. }
  214. /**
  215. * Save a relation to file
  216. */
  217. static void
  218. rel_save_proj(FILE* f, rel_t rel)
  219. {
  220. fwrite(&rel->r_k, sizeof(int), 1, f);
  221. fwrite(&rel->w_k, sizeof(int), 1, f);
  222. fwrite(rel->r_proj, sizeof(int), rel->r_k, f);
  223. fwrite(rel->w_proj, sizeof(int), rel->w_k, f);
  224. }
  225. static void
  226. rel_save(FILE* f, rel_t rel)
  227. {
  228. size_t dd = lddmc_serialize_add(rel->dd);
  229. lddmc_serialize_tofile(f);
  230. fwrite(&dd, sizeof(size_t), 1, f);
  231. }
  232. /**
  233. * Clone a set
  234. */
  235. static set_t
  236. set_clone(set_t source)
  237. {
  238. set_t set = (set_t)malloc(sizeof(struct set));
  239. set->dd = source->dd;
  240. lddmc_protect(&set->dd);
  241. return set;
  242. }
  243. static char*
  244. to_h(double size, char *buf)
  245. {
  246. const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
  247. int i = 0;
  248. for (;size>1024;size/=1024) i++;
  249. sprintf(buf, "%.*f %s", i, size, units[i]);
  250. return buf;
  251. }
  252. static void
  253. print_memory_usage(void)
  254. {
  255. char buf[32];
  256. to_h(getCurrentRSS(), buf);
  257. INFO("Memory usage: %s\n", buf);
  258. }
  259. /**
  260. * Get the first variable of the transition relation
  261. */
  262. static int
  263. get_first(MDD meta)
  264. {
  265. uint32_t val = lddmc_getvalue(meta);
  266. if (val != 0) return 0;
  267. return 1+get_first(lddmc_follow(meta, val));
  268. }
  269. /**
  270. * Print a single example of a set to stdout
  271. */
  272. static void
  273. print_example(MDD example)
  274. {
  275. if (example != lddmc_false) {
  276. LACE_ME;
  277. uint32_t vec[vector_size];
  278. lddmc_sat_one(example, vec, vector_size);
  279. printf("[");
  280. for (int i=0; i<vector_size; i++) {
  281. if (i>0) printf(",");
  282. printf("%" PRIu32, vec[i]);
  283. }
  284. printf("]");
  285. }
  286. }
  287. static void
  288. print_matrix(size_t size, MDD meta)
  289. {
  290. if (size == 0) return;
  291. uint32_t val = lddmc_getvalue(meta);
  292. if (val == 1) {
  293. printf("+");
  294. print_matrix(size-1, lddmc_follow(lddmc_follow(meta, 1), 2));
  295. } else {
  296. if (val == (uint32_t)-1) printf("-");
  297. else if (val == 0) printf("-");
  298. else if (val == 3) printf("r");
  299. else if (val == 4) printf("w");
  300. print_matrix(size-1, lddmc_follow(meta, val));
  301. }
  302. }
  303. /**
  304. * Implement parallel strategy (that performs the relprod operations in parallel)
  305. */
  306. TASK_5(MDD, go_par, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  307. {
  308. if (len == 1) {
  309. // Calculate NEW successors (not in visited)
  310. MDD succ = lddmc_relprod(cur, next[from]->dd, next[from]->meta);
  311. lddmc_refs_push(succ);
  312. if (deadlocks) {
  313. // check which MDDs in deadlocks do not have a successor in this relation
  314. MDD anc = lddmc_relprev(succ, next[from]->dd, next[from]->meta, cur);
  315. lddmc_refs_push(anc);
  316. *deadlocks = lddmc_minus(*deadlocks, anc);
  317. lddmc_refs_pop(1);
  318. }
  319. MDD result = lddmc_minus(succ, visited);
  320. lddmc_refs_pop(1);
  321. return result;
  322. } else if (deadlocks != NULL) {
  323. MDD deadlocks_left = *deadlocks;
  324. MDD deadlocks_right = *deadlocks;
  325. lddmc_refs_pushptr(&deadlocks_left);
  326. lddmc_refs_pushptr(&deadlocks_right);
  327. // Recursively compute left+right
  328. lddmc_refs_spawn(SPAWN(go_par, cur, visited, from, len/2, &deadlocks_left));
  329. MDD right = CALL(go_par, cur, visited, from+len/2, len-len/2, &deadlocks_right);
  330. lddmc_refs_push(right);
  331. MDD left = lddmc_refs_sync(SYNC(go_par));
  332. lddmc_refs_push(left);
  333. // Merge results of left+right
  334. MDD result = lddmc_union(left, right);
  335. lddmc_refs_pop(2);
  336. // Intersect deadlock sets
  337. lddmc_refs_push(result);
  338. *deadlocks = lddmc_intersect(deadlocks_left, deadlocks_right);
  339. lddmc_refs_pop(1);
  340. lddmc_refs_popptr(2);
  341. // Return result
  342. return result;
  343. } else {
  344. // Recursively compute left+right
  345. lddmc_refs_spawn(SPAWN(go_par, cur, visited, from, len/2, NULL));
  346. MDD right = CALL(go_par, cur, visited, from+len/2, len-len/2, NULL);
  347. lddmc_refs_push(right);
  348. MDD left = lddmc_refs_sync(SYNC(go_par));
  349. lddmc_refs_push(left);
  350. // Merge results of left+right
  351. MDD result = lddmc_union(left, right);
  352. lddmc_refs_pop(2);
  353. // Return result
  354. return result;
  355. }
  356. }
  357. /**
  358. * Implementation of the PAR strategy
  359. */
  360. VOID_TASK_1(par, set_t, set)
  361. {
  362. /* Prepare variables */
  363. MDD visited = set->dd;
  364. MDD front = visited;
  365. lddmc_refs_pushptr(&visited);
  366. lddmc_refs_pushptr(&front);
  367. int iteration = 1;
  368. do {
  369. if (check_deadlocks) {
  370. // compute successors in parallel
  371. MDD deadlocks = front;
  372. lddmc_refs_pushptr(&deadlocks);
  373. front = CALL(go_par, front, visited, 0, next_count, &deadlocks);
  374. lddmc_refs_popptr(1);
  375. if (deadlocks != lddmc_false) {
  376. INFO("Found %'0.0f deadlock states... ", lddmc_satcount_cached(deadlocks));
  377. printf("example: ");
  378. print_example(deadlocks);
  379. printf("\n");
  380. check_deadlocks = 0;
  381. }
  382. } else {
  383. // compute successors in parallel
  384. front = CALL(go_par, front, visited, 0, next_count, NULL);
  385. }
  386. // visited = visited + front
  387. visited = lddmc_union(visited, front);
  388. INFO("Level %d done", iteration);
  389. if (report_levels) {
  390. printf(", %'0.0f states explored", lddmc_satcount_cached(visited));
  391. }
  392. if (report_table) {
  393. size_t filled, total;
  394. sylvan_table_usage(&filled, &total);
  395. printf(", table: %0.1f%% full (%'zu nodes)", 100.0*(double)filled/total, filled);
  396. }
  397. char buf[32];
  398. to_h(getCurrentRSS(), buf);
  399. printf(", rss=%s.\n", buf);
  400. iteration++;
  401. } while (front != lddmc_false);
  402. set->dd = visited;
  403. lddmc_refs_popptr(2);
  404. }
  405. /**
  406. * Implement sequential strategy (that performs the relprod operations one by one)
  407. */
  408. TASK_5(MDD, go_bfs, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  409. {
  410. if (len == 1) {
  411. // Calculate NEW successors (not in visited)
  412. MDD succ = lddmc_relprod(cur, next[from]->dd, next[from]->meta);
  413. lddmc_refs_push(succ);
  414. if (deadlocks) {
  415. // check which MDDs in deadlocks do not have a successor in this relation
  416. MDD anc = lddmc_relprev(succ, next[from]->dd, next[from]->meta, cur);
  417. lddmc_refs_push(anc);
  418. *deadlocks = lddmc_minus(*deadlocks, anc);
  419. lddmc_refs_pop(1);
  420. }
  421. MDD result = lddmc_minus(succ, visited);
  422. lddmc_refs_pop(1);
  423. return result;
  424. } else if (deadlocks != NULL) {
  425. MDD deadlocks_left = *deadlocks;
  426. MDD deadlocks_right = *deadlocks;
  427. lddmc_refs_pushptr(&deadlocks_left);
  428. lddmc_refs_pushptr(&deadlocks_right);
  429. // Recursively compute left+right
  430. MDD left = CALL(go_par, cur, visited, from, len/2, &deadlocks_left);
  431. lddmc_refs_push(left);
  432. MDD right = CALL(go_par, cur, visited, from+len/2, len-len/2, &deadlocks_right);
  433. lddmc_refs_push(right);
  434. // Merge results of left+right
  435. MDD result = lddmc_union(left, right);
  436. lddmc_refs_pop(2);
  437. // Intersect deadlock sets
  438. lddmc_refs_push(result);
  439. *deadlocks = lddmc_intersect(deadlocks_left, deadlocks_right);
  440. lddmc_refs_pop(1);
  441. lddmc_refs_popptr(2);
  442. // Return result
  443. return result;
  444. } else {
  445. // Recursively compute left+right
  446. MDD left = CALL(go_par, cur, visited, from, len/2, NULL);
  447. lddmc_refs_push(left);
  448. MDD right = CALL(go_par, cur, visited, from+len/2, len-len/2, NULL);
  449. lddmc_refs_push(right);
  450. // Merge results of left+right
  451. MDD result = lddmc_union(left, right);
  452. lddmc_refs_pop(2);
  453. // Return result
  454. return result;
  455. }
  456. }
  457. /* BFS strategy, sequential strategy (but operations are parallelized by Sylvan) */
  458. VOID_TASK_1(bfs, set_t, set)
  459. {
  460. /* Prepare variables */
  461. MDD visited = set->dd;
  462. MDD front = visited;
  463. lddmc_refs_pushptr(&visited);
  464. lddmc_refs_pushptr(&front);
  465. int iteration = 1;
  466. do {
  467. if (check_deadlocks) {
  468. // compute successors
  469. MDD deadlocks = front;
  470. lddmc_refs_pushptr(&deadlocks);
  471. front = CALL(go_bfs, front, visited, 0, next_count, &deadlocks);
  472. lddmc_refs_popptr(1);
  473. if (deadlocks != lddmc_false) {
  474. INFO("Found %'0.0f deadlock states... ", lddmc_satcount_cached(deadlocks));
  475. printf("example: ");
  476. print_example(deadlocks);
  477. printf("\n");
  478. check_deadlocks = 0;
  479. }
  480. } else {
  481. // compute successors
  482. front = CALL(go_bfs, front, visited, 0, next_count, NULL);
  483. }
  484. // visited = visited + front
  485. visited = lddmc_union(visited, front);
  486. INFO("Level %d done", iteration);
  487. if (report_levels) {
  488. printf(", %'0.0f states explored", lddmc_satcount_cached(visited));
  489. }
  490. if (report_table) {
  491. size_t filled, total;
  492. sylvan_table_usage(&filled, &total);
  493. printf(", table: %0.1f%% full (%'zu nodes)", 100.0*(double)filled/total, filled);
  494. }
  495. char buf[32];
  496. to_h(getCurrentRSS(), buf);
  497. printf(", rss=%s.\n", buf);
  498. iteration++;
  499. } while (front != lddmc_false);
  500. set->dd = visited;
  501. lddmc_refs_popptr(2);
  502. }
  503. /**
  504. * Implementation of (parallel) saturation
  505. * (assumes relations are ordered on first variable)
  506. */
  507. TASK_3(MDD, go_sat, MDD, set, int, idx, int, depth)
  508. {
  509. /* Terminal cases */
  510. if (set == lddmc_false) return lddmc_false;
  511. if (idx == next_count) return set;
  512. /* Consult the cache */
  513. MDD result;
  514. const MDD _set = set;
  515. if (cache_get3(201LL<<40, _set, idx, 0, &result)) return result;
  516. lddmc_refs_pushptr(&_set);
  517. /**
  518. * Possible improvement: cache more things (like intermediate results?)
  519. * and chain-apply more of the current level before going deeper?
  520. */
  521. /* Check if the relation should be applied */
  522. const int var = next[idx]->firstvar;
  523. assert(depth <= var);
  524. if (depth == var) {
  525. /* Count the number of relations starting here */
  526. int n = 1;
  527. while ((idx + n) < next_count && var == next[idx + n]->firstvar) n++;
  528. /*
  529. * Compute until fixpoint:
  530. * - SAT deeper
  531. * - chain-apply all current level once
  532. */
  533. MDD prev = lddmc_false;
  534. lddmc_refs_pushptr(&set);
  535. lddmc_refs_pushptr(&prev);
  536. while (prev != set) {
  537. prev = set;
  538. // SAT deeper
  539. set = CALL(go_sat, set, idx + n, depth);
  540. // chain-apply all current level once
  541. for (int i=0; i<n; i++) {
  542. set = lddmc_relprod_union(set, next[idx+i]->dd, next[idx+i]->topmeta, set);
  543. }
  544. }
  545. lddmc_refs_popptr(2);
  546. result = set;
  547. } else {
  548. /* Recursive computation */
  549. lddmc_refs_spawn(SPAWN(go_sat, lddmc_getright(set), idx, depth));
  550. MDD down = lddmc_refs_push(CALL(go_sat, lddmc_getdown(set), idx, depth+1));
  551. MDD right = lddmc_refs_sync(SYNC(go_sat));
  552. lddmc_refs_pop(1);
  553. result = lddmc_makenode(lddmc_getvalue(set), down, right);
  554. }
  555. /* Store in cache */
  556. cache_put3(201LL<<40, _set, idx, 0, result);
  557. lddmc_refs_popptr(1);
  558. return result;
  559. }
  560. /**
  561. * Wrapper for the Saturation strategy
  562. */
  563. VOID_TASK_1(sat, set_t, set)
  564. {
  565. set->dd = CALL(go_sat, set->dd, 0, 0);
  566. }
  567. /**
  568. * Implementation of the Chaining strategy (does not support deadlock detection)
  569. */
  570. VOID_TASK_1(chaining, set_t, set)
  571. {
  572. MDD visited = set->dd;
  573. MDD front = visited;
  574. MDD succ = sylvan_false;
  575. lddmc_refs_pushptr(&visited);
  576. lddmc_refs_pushptr(&front);
  577. lddmc_refs_pushptr(&succ);
  578. int iteration = 1;
  579. do {
  580. // calculate successors in parallel
  581. for (int i=0; i<next_count; i++) {
  582. succ = lddmc_relprod(front, next[i]->dd, next[i]->meta);
  583. front = lddmc_union(front, succ);
  584. succ = lddmc_false; // reset, for gc
  585. }
  586. // front = front - visited
  587. // visited = visited + front
  588. front = lddmc_minus(front, visited);
  589. visited = lddmc_union(visited, front);
  590. INFO("Level %d done", iteration);
  591. if (report_levels) {
  592. printf(", %'0.0f states explored", lddmc_satcount_cached(visited));
  593. }
  594. if (report_table) {
  595. size_t filled, total;
  596. sylvan_table_usage(&filled, &total);
  597. printf(", table: %0.1f%% full (%'zu nodes)", 100.0*(double)filled/total, filled);
  598. }
  599. char buf[32];
  600. to_h(getCurrentRSS(), buf);
  601. printf(", rss=%s.\n", buf);
  602. iteration++;
  603. } while (front != lddmc_false);
  604. set->dd = visited;
  605. lddmc_refs_popptr(3);
  606. }
  607. VOID_TASK_0(gc_start)
  608. {
  609. char buf[32];
  610. to_h(getCurrentRSS(), buf);
  611. INFO("(GC) Starting garbage collection... (rss: %s)\n", buf);
  612. }
  613. VOID_TASK_0(gc_end)
  614. {
  615. char buf[32];
  616. to_h(getCurrentRSS(), buf);
  617. INFO("(GC) Garbage collection done. (rss: %s)\n", buf);
  618. }
  619. int
  620. main(int argc, char **argv)
  621. {
  622. /**
  623. * Parse command line, set locale, set startup time for INFO messages.
  624. */
  625. argp_parse(&argp, argc, argv, 0, 0, 0);
  626. setlocale(LC_NUMERIC, "en_US.utf-8");
  627. t_start = wctime();
  628. /**
  629. * Initialize Lace.
  630. *
  631. * First: setup with given number of workers (0 for autodetect) and some large size task queue.
  632. * Second: start all worker threads with default settings.
  633. * Third: setup local variables using the LACE_ME macro.
  634. */
  635. lace_init(workers, 1000000);
  636. lace_startup(0, NULL, NULL);
  637. LACE_ME;
  638. /**
  639. * Initialize Sylvan.
  640. *
  641. * First: set memory limits
  642. * - 2 GB memory, nodes table twice as big as cache, initial size halved 6x
  643. * (that means it takes 6 garbage collections to get to the maximum nodes&cache size)
  644. * Second: initialize package and subpackages
  645. * Third: add hooks to report garbage collection
  646. */
  647. sylvan_set_limits(2LL<<30, 1, 6);
  648. sylvan_init_package();
  649. sylvan_init_ldd();
  650. sylvan_gc_hook_pregc(TASK(gc_start));
  651. sylvan_gc_hook_postgc(TASK(gc_end));
  652. /**
  653. * Read the model from file
  654. */
  655. FILE *f = fopen(model_filename, "r");
  656. if (f == NULL) {
  657. Abort("Cannot open file '%s'!\n", model_filename);
  658. return -1;
  659. }
  660. /* Read domain data */
  661. if (fread(&vector_size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  662. /* Read initial state */
  663. set_t initial = set_load(f);
  664. /* Read number of transition relations */
  665. if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  666. next = (rel_t*)malloc(sizeof(rel_t) * next_count);
  667. /* Read transition relations */
  668. for (int i=0; i<next_count; i++) next[i] = rel_load_proj(f);
  669. for (int i=0; i<next_count; i++) rel_load(f, next[i]);
  670. /* We ignore the reachable states and action labels that are stored after the relations */
  671. /* Close the file */
  672. fclose(f);
  673. /**
  674. * Pre-processing and some statistics reporting
  675. */
  676. if (strategy == 2 || strategy == 3) {
  677. // for SAT and CHAINING, sort the transition relations (gnome sort because I like gnomes)
  678. int i = 1, j = 2;
  679. rel_t t;
  680. while (i < next_count) {
  681. rel_t *p = &next[i], *q = p-1;
  682. if ((*q)->firstvar > (*p)->firstvar) {
  683. t = *q;
  684. *q = *p;
  685. *p = t;
  686. if (--i) continue;
  687. }
  688. i = j++;
  689. }
  690. }
  691. INFO("Read file '%s'\n", model_filename);
  692. INFO("%d integers per state, %d transition groups\n", vector_size, next_count);
  693. if (print_transition_matrix) {
  694. for (int i=0; i<next_count; i++) {
  695. INFO("");
  696. print_matrix(vector_size, next[i]->meta);
  697. printf(" (%d)\n", get_first(next[i]->meta));
  698. }
  699. }
  700. set_t states = set_clone(initial);
  701. #ifdef HAVE_PROFILER
  702. if (profile_filename != NULL) ProfilerStart(profile_filename);
  703. #endif
  704. if (strategy == 0) {
  705. double t1 = wctime();
  706. CALL(bfs, states);
  707. double t2 = wctime();
  708. INFO("BFS Time: %f\n", t2-t1);
  709. } else if (strategy == 1) {
  710. double t1 = wctime();
  711. CALL(par, states);
  712. double t2 = wctime();
  713. INFO("PAR Time: %f\n", t2-t1);
  714. } else if (strategy == 2) {
  715. double t1 = wctime();
  716. CALL(sat, states);
  717. double t2 = wctime();
  718. INFO("SAT Time: %f\n", t2-t1);
  719. } else if (strategy == 3) {
  720. double t1 = wctime();
  721. CALL(chaining, states);
  722. double t2 = wctime();
  723. INFO("CHAINING Time: %f\n", t2-t1);
  724. } else {
  725. Abort("Invalid strategy set?!\n");
  726. }
  727. #ifdef HAVE_PROFILER
  728. if (profile_filename != NULL) ProfilerStop();
  729. #endif
  730. // Now we just have states
  731. INFO("Final states: %'0.0f states\n", lddmc_satcount_cached(states->dd));
  732. if (report_nodes) {
  733. INFO("Final states: %'zu MDD nodes\n", lddmc_nodecount(states->dd));
  734. }
  735. if (out_filename != NULL) {
  736. INFO("Writing to %s.\n", out_filename);
  737. // Create LDD file
  738. FILE *f = fopen(out_filename, "w");
  739. lddmc_serialize_reset();
  740. // Write domain...
  741. fwrite(&vector_size, sizeof(int), 1, f);
  742. // Write initial state...
  743. set_save(f, initial);
  744. // Write number of transitions
  745. fwrite(&next_count, sizeof(int), 1, f);
  746. // Write transitions
  747. for (int i=0; i<next_count; i++) rel_save_proj(f, next[i]);
  748. for (int i=0; i<next_count; i++) rel_save(f, next[i]);
  749. // Write reachable states
  750. int has_reachable = 1;
  751. fwrite(&has_reachable, sizeof(int), 1, f);
  752. set_save(f, states);
  753. // Write action labels
  754. fclose(f);
  755. }
  756. print_memory_usage();
  757. sylvan_stats_report(stdout);
  758. return 0;
  759. }