From dc45444e22c8e7fc3296f00924e4472257f96d79 Mon Sep 17 00:00:00 2001 From: compudj Date: Thu, 12 Feb 2009 07:38:58 +0000 Subject: [PATCH] nico and md model git-svn-id: http://ltt.polymtl.ca/svn@3318 04897980-b3bd-0310-b5e0-8ef037075253 --- trunk/verif/md/defines | 8 + trunk/verif/md/emacs.nico | 61 + trunk/verif/md/log.txt | 272 + trunk/verif/md/model.spin | 1 + trunk/verif/md/model.spin.trail | 172 + trunk/verif/md/model_01.spin | 303 + trunk/verif/md/model_02.spin | 298 + trunk/verif/md/model_03.spin | 289 + trunk/verif/md/model_03_events_lost.spin.ltl | 5 + .../verif/md/model_03_no_events_lost.spin.ltl | 5 + .../md/model_03_write_commit_sum.spin.ltl | 6 + .../verif/md/model_03_write_read_off.spin.ltl | 4 + trunk/verif/md/pan | Bin 0 -> 98028 bytes trunk/verif/md/pan.b | 427 + trunk/verif/md/pan.c | 11541 ++++++++++++++++ trunk/verif/md/pan.err | 0 trunk/verif/md/pan.h | 638 + trunk/verif/md/pan.ltl | 22 + trunk/verif/md/pan.m | 1010 ++ trunk/verif/md/pan.oin | 307 + trunk/verif/md/pan.otl | 32 + trunk/verif/md/pan.out | 50 + trunk/verif/md/pan.t | 924 ++ trunk/verif/md/pan.tmp | 0 trunk/verif/md/pan_in | 307 + trunk/verif/md/promela-mode.el | 985 ++ trunk/verif/md/run | 44 + trunk/verif/nico-md-merge/Makefile | 59 + trunk/verif/nico-md-merge/commit_sum.log | 65 + trunk/verif/nico-md-merge/commit_sum.ltl | 6 + trunk/verif/nico-md-merge/defines | 8 + trunk/verif/nico-md-merge/events_lost.log | 65 + trunk/verif/nico-md-merge/events_lost.ltl | 5 + trunk/verif/nico-md-merge/model.spin | 1 + trunk/verif/nico-md-merge/model.spin.trail | 116 + .../verif/nico-md-merge/models/model_01.spin | 303 + .../verif/nico-md-merge/models/model_02.spin | 298 + .../verif/nico-md-merge/models/model_03.spin | 291 + trunk/verif/nico-md-merge/no_events_lost.def | 1 + trunk/verif/nico-md-merge/no_events_lost.log | 65 + trunk/verif/nico-md-merge/no_events_lost.ltl | 5 + trunk/verif/nico-md-merge/pan | Bin 0 -> 97005 bytes trunk/verif/nico-md-merge/pan.b | 423 + trunk/verif/nico-md-merge/pan.c | 11541 ++++++++++++++++ trunk/verif/nico-md-merge/pan.h | 638 + trunk/verif/nico-md-merge/pan.ltl | 17 + trunk/verif/nico-md-merge/pan.m | 980 ++ trunk/verif/nico-md-merge/pan.spin | 292 + trunk/verif/nico-md-merge/pan.t | 916 ++ trunk/verif/nico-md-merge/read_write.log | 65 + trunk/verif/nico-md-merge/read_write.ltl | 4 + trunk/verif/nico/Makefile | 57 + trunk/verif/nico/commit_sum.ltl | 1 + trunk/verif/nico/defines | 8 + trunk/verif/nico/events_lost.ltl | 1 + trunk/verif/nico/model.spin | 1 + trunk/verif/nico/model.spin.trail | 174 + trunk/verif/nico/models/model_01.spin | 303 + trunk/verif/nico/models/model_02.spin | 298 + trunk/verif/nico/models/model_03.spin | 316 + trunk/verif/nico/no_events_lost.ltl | 1 + trunk/verif/nico/read_write.ltl | 1 + 62 files changed, 35036 insertions(+) create mode 100644 trunk/verif/md/defines create mode 100755 trunk/verif/md/emacs.nico create mode 100644 trunk/verif/md/log.txt create mode 120000 trunk/verif/md/model.spin create mode 100644 trunk/verif/md/model.spin.trail create mode 100755 trunk/verif/md/model_01.spin create mode 100755 trunk/verif/md/model_02.spin create mode 100755 trunk/verif/md/model_03.spin create mode 100644 trunk/verif/md/model_03_events_lost.spin.ltl create mode 100644 trunk/verif/md/model_03_no_events_lost.spin.ltl create mode 100644 trunk/verif/md/model_03_write_commit_sum.spin.ltl create mode 100644 trunk/verif/md/model_03_write_read_off.spin.ltl create mode 100755 trunk/verif/md/pan create mode 100644 trunk/verif/md/pan.b create mode 100644 trunk/verif/md/pan.c create mode 100644 trunk/verif/md/pan.err create mode 100644 trunk/verif/md/pan.h create mode 100644 trunk/verif/md/pan.ltl create mode 100644 trunk/verif/md/pan.m create mode 100644 trunk/verif/md/pan.oin create mode 100644 trunk/verif/md/pan.otl create mode 100644 trunk/verif/md/pan.out create mode 100644 trunk/verif/md/pan.t create mode 100644 trunk/verif/md/pan.tmp create mode 100644 trunk/verif/md/pan_in create mode 100755 trunk/verif/md/promela-mode.el create mode 100755 trunk/verif/md/run create mode 100755 trunk/verif/nico-md-merge/Makefile create mode 100644 trunk/verif/nico-md-merge/commit_sum.log create mode 100755 trunk/verif/nico-md-merge/commit_sum.ltl create mode 100755 trunk/verif/nico-md-merge/defines create mode 100644 trunk/verif/nico-md-merge/events_lost.log create mode 100755 trunk/verif/nico-md-merge/events_lost.ltl create mode 120000 trunk/verif/nico-md-merge/model.spin create mode 100755 trunk/verif/nico-md-merge/model.spin.trail create mode 100755 trunk/verif/nico-md-merge/models/model_01.spin create mode 100755 trunk/verif/nico-md-merge/models/model_02.spin create mode 100755 trunk/verif/nico-md-merge/models/model_03.spin create mode 100644 trunk/verif/nico-md-merge/no_events_lost.def create mode 100644 trunk/verif/nico-md-merge/no_events_lost.log create mode 100755 trunk/verif/nico-md-merge/no_events_lost.ltl create mode 100755 trunk/verif/nico-md-merge/pan create mode 100644 trunk/verif/nico-md-merge/pan.b create mode 100644 trunk/verif/nico-md-merge/pan.c create mode 100644 trunk/verif/nico-md-merge/pan.h create mode 100644 trunk/verif/nico-md-merge/pan.ltl create mode 100644 trunk/verif/nico-md-merge/pan.m create mode 100644 trunk/verif/nico-md-merge/pan.spin create mode 100644 trunk/verif/nico-md-merge/pan.t create mode 100644 trunk/verif/nico-md-merge/read_write.log create mode 100755 trunk/verif/nico-md-merge/read_write.ltl create mode 100755 trunk/verif/nico/Makefile create mode 100755 trunk/verif/nico/commit_sum.ltl create mode 100755 trunk/verif/nico/defines create mode 100755 trunk/verif/nico/events_lost.ltl create mode 120000 trunk/verif/nico/model.spin create mode 100755 trunk/verif/nico/model.spin.trail create mode 100755 trunk/verif/nico/models/model_01.spin create mode 100755 trunk/verif/nico/models/model_02.spin create mode 100755 trunk/verif/nico/models/model_03.spin create mode 100755 trunk/verif/nico/no_events_lost.ltl create mode 100755 trunk/verif/nico/read_write.ltl diff --git a/trunk/verif/md/defines b/trunk/verif/md/defines new file mode 100644 index 00000000..3751f336 --- /dev/null +++ b/trunk/verif/md/defines @@ -0,0 +1,8 @@ +#define rwoff1 (write_off - read_off >= 0) +#define rwoff2 (write_off - read_off < HALF_UCHAR) + +#define wcsum1 (write_off - _commit_sum >= 0) +#define wcsum2 (write_off - _commit_sum < HALF_UCHAR) + +#define buffer_large_enough (NUMPROCS + NUMSWITCH <= BUFSIZE) +#define have_events_lost (events_lost != 0) diff --git a/trunk/verif/md/emacs.nico b/trunk/verif/md/emacs.nico new file mode 100755 index 00000000..04a0afa0 --- /dev/null +++ b/trunk/verif/md/emacs.nico @@ -0,0 +1,61 @@ +;; Display +(set-default-font "6x13") +;;(standard-display-european t) ; affichage des caracte`res iso-latin +(require 'iso-insert) ; pour l'insertion de caracte`res + + +;; Auto font lock mode +(if (string-match "XEmacs" emacs-version 0) + (progn + (require 'font-lock) + (setq-default font-lock-auto-fontify t)) + (global-font-lock-mode t) +) + + +;; Custom variables and keys +(menu-bar-mode 0) +(tool-bar-mode 0) +(setq-default auto-save-mode nil) +(global-set-key "\M-g" 'goto-line) +(setq-default indent-tabs-mode nil) +(setq c-basic-offset 8) +(global-set-key [delete] 'delete-char) ; [Del] +(global-set-key [home] 'beginning-of-line) ; [Home] +(global-set-key [end] 'end-of-line) ; [End] + + +;; Promela mode +(autoload 'promela-mode "promela-mode" "PROMELA mode" nil t) +(setq auto-mode-alist + (append + (list (cons "\\.promela$" 'promela-mode) + (cons "\\.spin$" 'promela-mode) + (cons "\\.pml$" 'promela-mode) + ) + auto-mode-alist)) + +;; Mousewheel +(defun sd-mousewheel-scroll-up (event) + "Scroll window under mouse up by five lines." + (interactive "e") + (let ((current-window (selected-window))) + (unwind-protect + (progn + (select-window (posn-window (event-start event))) + (scroll-up 5)) + (select-window current-window)))) + +(defun sd-mousewheel-scroll-down (event) + "Scroll window under mouse down by five lines." + (interactive "e") + (let ((current-window (selected-window))) + (unwind-protect + (progn + (select-window (posn-window (event-start event))) + (scroll-down 5)) + (select-window current-window)))) + +(global-set-key (kbd "") 'sd-mousewheel-scroll-up) +(global-set-key (kbd "") 'sd-mousewheel-scroll-down) + diff --git a/trunk/verif/md/log.txt b/trunk/verif/md/log.txt new file mode 100644 index 00000000..4ebf457d --- /dev/null +++ b/trunk/verif/md/log.txt @@ -0,0 +1,272 @@ +Exit-Status 0 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 301) +Depth= 198 States= 1e+06 Transitions= 2.9e+06 Memory= 79.649 t= 7.71 R= 1e+05 + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 100 byte, depth reached 200, errors: 0 + 1295413 states, stored + 2540827 states, matched + 3836240 transitions (= stored+matched) + 4818193 atomic steps +hash conflicts: 1991528 (resolved) + +Stats on memory usage (in Megabytes): + 143.307 equivalent memory usage for states (stored*(State-vector + overhead)) + 100.700 actual memory usage for states (compression: 70.27%) + state-vector as stored = 66 byte + 16 byte overhead + 2.000 memory used for hash table (-w19) + 0.305 memory used for DFS stack (-m10000) + 102.891 total actual memory usage + +unreached in proctype switcher + (0 of 31 states) +unreached in proctype tracer + (0 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + (0 of 43 states) +unreached in proctype :never: + line 306, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 10.3 seconds +pan: rate 125768.25 states/second +pan: avg transition delay 2.6849e-06 usec +Exit-Status 0 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 301) +Depth= 198 States= 1e+06 Transitions= 2.9e+06 Memory= 79.649 t= 7.7 R= 1e+05 + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 100 byte, depth reached 200, errors: 0 + 1295413 states, stored + 2540827 states, matched + 3836240 transitions (= stored+matched) + 4818193 atomic steps +hash conflicts: 1991528 (resolved) + +Stats on memory usage (in Megabytes): + 143.307 equivalent memory usage for states (stored*(State-vector + overhead)) + 100.700 actual memory usage for states (compression: 70.27%) + state-vector as stored = 66 byte + 16 byte overhead + 2.000 memory used for hash table (-w19) + 0.305 memory used for DFS stack (-m10000) + 102.891 total actual memory usage + +unreached in proctype switcher + (0 of 31 states) +unreached in proctype tracer + (0 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + (0 of 43 states) +unreached in proctype :never: + line 306, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 10.3 seconds +pan: rate 125890.48 states/second +pan: avg transition delay 2.6823e-06 usec +Exit-Status 0 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 3 (line 302) +depth 25: Claim reached state 7 (line 307) +pan: acceptance cycle (at depth 167) +pan: wrote model.spin.trail + +(Spin Version 5.1.6 -- 9 May 2008) +Warning: Search not completed + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 100 byte, depth reached 168, errors: 1 + 43 states, stored + 0 states, matched + 43 transitions (= stored+matched) + 83 atomic steps +hash conflicts: 0 (resolved) + +Stats on memory usage (in Megabytes): + 0.005 equivalent memory usage for states (stored*(State-vector + overhead)) + 0.277 actual memory usage for states (unsuccessful compression: 5822.98%) + state-vector as stored = 6739 byte + 16 byte overhead + 2.000 memory used for hash table (-w19) + 0.305 memory used for DFS stack (-m10000) + 2.501 total actual memory usage + +unreached in proctype switcher + line 80, "pan.___", state 8, "(1)" + line 75, "pan.___", state 9, "(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))" + line 75, "pan.___", state 9, "else" + line 86, "pan.___", state 15, "write_off = new_off" + line 83, "pan.___", state 18, "((prev_off!=write_off))" + line 83, "pan.___", state 18, "else" + line 96, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 102, "pan.___", state 25, "(1)" + line 97, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 97, "pan.___", state 26, "else" + line 90, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + (8 of 31 states) +unreached in proctype tracer + line 153, "pan.___", state 34, "((i=size))" + line 151, "pan.___", state 46, "i = 0" + line 176, "pan.___", state 48, "events_lost = (events_lost+1)" + (3 of 51 states) +unreached in proctype reader + line 201, "pan.___", state 12, "i = 0" + line 215, "pan.___", state 23, "i = 0" + (2 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + line 253, "pan.___", state 7, "((i<2))" + line 253, "pan.___", state 7, "((i>=2))" + line 272, "pan.___", state 29, "((i<4))" + line 272, "pan.___", state 29, "((i>=4))" + (2 of 43 states) +unreached in proctype :never: + line 306, "pan.___", state 7, "(!((events_lost!=0)))" + line 309, "pan.___", state 9, "-end-" + (2 of 9 states) + +pan: elapsed time 0 seconds +Exit-Status 0 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 301) +depth 0: Claim reached state 5 (line 302) + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 32 byte, depth reached 0, errors: 0 + 1 states, stored + 0 states, matched + 1 transitions (= stored+matched) + 0 atomic steps +hash conflicts: 0 (resolved) + +Stats on memory usage (in Megabytes): + 0.000 equivalent memory usage for states (stored*(State-vector + overhead)) + 0.277 actual memory usage for states (unsuccessful compression: 604850.00%) + state-vector as stored = 290312 byte + 16 byte overhead + 2.000 memory used for hash table (-w19) + 0.305 memory used for DFS stack (-m10000) + 2.501 total actual memory usage + +unreached in proctype switcher + line 74, "pan.___", state 3, "new_off = (prev_off+size)" + line 80, "pan.___", state 8, "(1)" + line 75, "pan.___", state 9, "(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))" + line 75, "pan.___", state 9, "else" + line 71, "pan.___", state 11, "prev_off = write_off" + line 86, "pan.___", state 15, "write_off = new_off" + line 83, "pan.___", state 18, "((prev_off!=write_off))" + line 83, "pan.___", state 18, "else" + line 96, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 102, "pan.___", state 25, "(1)" + line 97, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 97, "pan.___", state 26, "else" + line 90, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + line 108, "pan.___", state 31, "-end-" + (11 of 31 states) +unreached in proctype tracer + line 122, "pan.___", state 3, "prev_off = write_off" + line 130, "pan.___", state 7, "(1)" + line 126, "pan.___", state 10, "((((new_off-read_off)>4)&&((new_off-read_off)<(255/2))))" + line 126, "pan.___", state 10, "else" + line 136, "pan.___", state 14, "write_off = new_off" + line 142, "pan.___", state 20, "buffer_use[((prev_off+i)%4)] = 1" + line 143, "pan.___", state 21, "i = (i+1)" + line 133, "pan.___", state 27, "((prev_off!=write_off))" + line 133, "pan.___", state 27, "else" + line 156, "pan.___", state 31, "i = (i+1)" + line 153, "pan.___", state 34, "((i=size))" + line 164, "pan.___", state 39, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 170, "pan.___", state 43, "(1)" + line 165, "pan.___", state 44, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 165, "pan.___", state 44, "else" + line 151, "pan.___", state 46, "i = 0" + line 176, "pan.___", state 48, "events_lost = (events_lost+1)" + line 178, "pan.___", state 49, "refcount = (refcount-1)" + line 173, "pan.___", state 50, "goto end" + line 180, "pan.___", state 51, "-end-" + (17 of 51 states) +unreached in proctype reader + line 206, "pan.___", state 5, "buffer_use[((read_off+i)%4)] = 1" + line 207, "pan.___", state 6, "i = (i+1)" + line 201, "pan.___", state 12, "i = 0" + line 220, "pan.___", state 16, "i = (i+1)" + line 223, "pan.___", state 22, "read_off = (read_off+(4/2))" + line 215, "pan.___", state 23, "i = 0" + line 195, "pan.___", state 26, "((((((write_off/(4/2))-(read_off/(4/2)))>0)&&(((write_off/(4/2))-(read_off/(4/2)))<(255/2)))&&(((commit_count[((read_off%4)/(4/2))]-(4/2))-(((read_off/4)*4)/2))==0)))" + line 195, "pan.___", state 26, "((read_off>=(4-events_lost)))" + line 227, "pan.___", state 29, "-end-" + (8 of 29 states) +unreached in proctype cleaner + line 239, "pan.___", state 3, "(run switcher())" + line 236, "pan.___", state 5, "((refcount==0))" + line 235, "pan.___", state 8, "((refcount==0))" + line 243, "pan.___", state 9, "-end-" + (4 of 9 states) +unreached in proctype :init: + line 256, "pan.___", state 4, "i = (i+1)" + line 253, "pan.___", state 7, "((i<2))" + line 253, "pan.___", state 7, "((i>=2))" + line 266, "pan.___", state 14, "i = (i+1)" + line 263, "pan.___", state 17, "((i<4))" + line 263, "pan.___", state 17, "((i>=4))" + line 269, "pan.___", state 20, "(run reader())" + line 270, "pan.___", state 21, "(run cleaner())" + line 275, "pan.___", state 25, "(run tracer())" + line 272, "pan.___", state 29, "((i<4))" + line 272, "pan.___", state 29, "((i>=4))" + line 283, "pan.___", state 35, "(run switcher())" + line 288, "pan.___", state 43, "-end-" + (10 of 43 states) +unreached in proctype :never: + line 305, "pan.___", state 11, "((events_lost!=0))" + line 305, "pan.___", state 11, "(1)" + line 311, "pan.___", state 14, "-end-" + (2 of 14 states) + +pan: elapsed time 0 seconds diff --git a/trunk/verif/md/model.spin b/trunk/verif/md/model.spin new file mode 120000 index 00000000..792412df --- /dev/null +++ b/trunk/verif/md/model.spin @@ -0,0 +1 @@ +model_03.spin \ No newline at end of file diff --git a/trunk/verif/md/model.spin.trail b/trunk/verif/md/model.spin.trail new file mode 100644 index 00000000..992c9aae --- /dev/null +++ b/trunk/verif/md/model.spin.trail @@ -0,0 +1,172 @@ +-2:5:-2 +-4:-4:-4 +1:0:163 +2:1:120 +3:1:121 +4:1:121 +5:1:124 +6:1:131 +7:1:131 +8:1:131 +9:1:131 +10:1:134 +11:1:139 +12:1:140 +13:1:142 +14:1:144 +15:1:142 +16:1:144 +17:1:142 +18:1:144 +19:1:142 +20:1:144 +21:1:146 +22:1:152 +23:1:154 +24:1:156 +25:1:160 +26:0:167 +27:8:0 +28:8:3 +29:0:167 +30:8:29 +31:0:167 +32:8:30 +33:0:167 +34:7:31 +35:0:167 +36:7:36 +37:7:37 +38:0:167 +39:7:43 +40:7:44 +41:7:48 +42:7:52 +43:7:56 +44:0:167 +45:7:58 +46:7:59 +47:7:62 +48:7:70 +49:0:167 +50:7:77 +51:7:79 +52:0:167 +53:7:81 +54:0:167 +55:6:31 +56:0:167 +57:6:36 +58:6:37 +59:0:167 +60:6:43 +61:6:44 +62:6:48 +63:6:52 +64:6:56 +65:0:167 +66:6:58 +67:6:59 +68:6:62 +69:6:72 +70:6:73 +71:0:167 +72:6:77 +73:6:79 +74:0:167 +75:6:81 +76:0:167 +77:5:31 +78:0:167 +79:5:36 +80:5:37 +81:0:167 +82:5:43 +83:5:44 +84:5:48 +85:5:52 +86:5:56 +87:0:167 +88:5:58 +89:5:59 +90:5:62 +91:5:70 +92:0:167 +93:5:77 +94:5:79 +95:0:167 +96:5:81 +97:0:167 +98:4:31 +99:0:167 +100:4:36 +101:4:37 +102:0:167 +103:4:43 +104:4:44 +105:4:48 +106:4:52 +107:4:56 +108:0:167 +109:4:58 +110:4:59 +111:4:62 +112:4:72 +113:4:73 +114:0:167 +115:4:77 +116:4:79 +117:0:167 +118:4:81 +119:0:167 +120:3:111 +121:3:113 +122:3:117 +123:0:167 +124:4:0 +125:4:3 +126:0:167 +127:4:29 +128:0:167 +129:4:30 +130:0:167 +131:3:119 +132:0:167 +133:2:82 +134:0:167 +135:2:83 +136:2:84 +137:2:84 +138:2:88 +139:2:92 +140:0:167 +141:2:94 +142:2:95 +143:2:95 +144:2:98 +145:2:102 +146:2:103 +147:0:167 +148:2:82 +149:0:167 +150:2:83 +151:2:84 +152:2:84 +153:2:88 +154:2:92 +155:0:167 +156:2:94 +157:2:95 +158:2:95 +159:2:98 +160:2:102 +161:2:103 +162:0:167 +163:2:105 +164:0:167 +165:2:110 +166:0:167 +167:1:162 +-1:-1:-1 +168:0:167 +169:0:167 diff --git a/trunk/verif/md/model_01.spin b/trunk/verif/md/model_01.spin new file mode 100755 index 00000000..edcd8fac --- /dev/null +++ b/trunk/verif/md/model_01.spin @@ -0,0 +1,303 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + /* The commit count of a particular subbuffer must always be higher + * or equal to the retrieve_count of this subbuffer. + * assert(commit_count[j] - retrieve_count[j] >= 0 && + * commit_count[j] - retrieve_count[j] < HALF_UCHAR); + */ + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not lose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/md/model_02.spin b/trunk/verif/md/model_02.spin new file mode 100755 index 00000000..6ec9e0aa --- /dev/null +++ b/trunk/verif/md/model_02.spin @@ -0,0 +1,298 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/md/model_03.spin b/trunk/verif/md/model_03.spin new file mode 100755 index 00000000..baa7d5cc --- /dev/null +++ b/trunk/verif/md/model_03.spin @@ -0,0 +1,289 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* */ +byte _commit_sum; +/* */ + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +//#ifdef RACE_TEST +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; +//#endif + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + /* */ + _commit_sum = 0; + /* */ + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } +} + diff --git a/trunk/verif/md/model_03_events_lost.spin.ltl b/trunk/verif/md/model_03_events_lost.spin.ltl new file mode 100644 index 00000000..00b4f1da --- /dev/null +++ b/trunk/verif/md/model_03_events_lost.spin.ltl @@ -0,0 +1,5 @@ +// If we have less writers than the buffer space available, we should +// not loose events. +// assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + +(!buffer_large_enough) -> (<>have_events_lost) diff --git a/trunk/verif/md/model_03_no_events_lost.spin.ltl b/trunk/verif/md/model_03_no_events_lost.spin.ltl new file mode 100644 index 00000000..253f75db --- /dev/null +++ b/trunk/verif/md/model_03_no_events_lost.spin.ltl @@ -0,0 +1,5 @@ +// If we have less writers than the buffer space available, we should +// not loose events. +// assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + +buffer_large_enough -> ([](!have_events_lost)) diff --git a/trunk/verif/md/model_03_write_commit_sum.spin.ltl b/trunk/verif/md/model_03_write_commit_sum.spin.ltl new file mode 100644 index 00000000..b43d2922 --- /dev/null +++ b/trunk/verif/md/model_03_write_commit_sum.spin.ltl @@ -0,0 +1,6 @@ +// The sum of all subbuffer commit counts must always be lower or equal +// to the writer head, because space must be reserved before it is +// written to and then committed. +// assert(write_off - _commit_sum >= 0 && write_off - _commit_sum < HALF_UCHAR); + +[] (wcsum1 && wcsum2) diff --git a/trunk/verif/md/model_03_write_read_off.spin.ltl b/trunk/verif/md/model_03_write_read_off.spin.ltl new file mode 100644 index 00000000..fbed3b87 --- /dev/null +++ b/trunk/verif/md/model_03_write_read_off.spin.ltl @@ -0,0 +1,4 @@ +// The writer head must always be superior or equal to the reader head. +// assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + +[] (rwoff1 && rwoff2) diff --git a/trunk/verif/md/pan b/trunk/verif/md/pan new file mode 100755 index 0000000000000000000000000000000000000000..ebbe6374b6a8b864394d1f1a9c8baea682e00c73 GIT binary patch literal 98028 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 z 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 O?Oh=}%cSAb@Bab!L|ZEW literal 0 HcmV?d00001 diff --git a/trunk/verif/md/pan.b b/trunk/verif/md/pan.b new file mode 100644 index 00000000..182def4a --- /dev/null +++ b/trunk/verif/md/pan.b @@ -0,0 +1,427 @@ + switch (t->back) { + default: Uerror("bad return move"); + case 0: goto R999; /* nothing to undo */ + + /* PROC :never: */ +; + ; + ; + ; + ; + ; + + case 6: /* STATE 14 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC :init: */ + + case 7: /* STATE 1 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 8: /* STATE 4 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now.commit_count[ Index(((P4 *)this)->i, 2) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 9: /* STATE 11 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 10: /* STATE 11 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now._commit_sum = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 11: /* STATE 14 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((P4 *)this)->i, 4) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 12: /* STATE 15 */ + ; + /* 0 */ ((P4 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 13: /* STATE 20 */ + ; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 14: /* STATE 22 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 15: /* STATE 24 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 16: /* STATE 26 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 17: /* STATE 32 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 18: /* STATE 32 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 19: /* STATE 34 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 20: /* STATE 36 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 21: /* STATE 37 */ + ; + /* 0 */ ((P4 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 22: /* STATE 43 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC cleaner */ + + case 23: /* STATE 2 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 24: /* STATE 3 */ + ; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 25: /* STATE 9 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC reader */ +; + ; + + case 27: /* STATE 2 */ + ; + ((P2 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 28: /* STATE 6 */ + ; + ((P2 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 29: /* STATE 7 */ + ; + /* 0 */ ((P2 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 30: /* STATE 16 */ + ; + ((P2 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 31: /* STATE 17 */ + ; + /* 0 */ ((P2 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 32: /* STATE 22 */ + ; + now.read_off = trpt->bup.oval; + ; + goto R999; +; + ; + + case 34: /* STATE 29 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC tracer */ + + case 35: /* STATE 2 */ + ; + ((P1 *)this)->new_off = trpt->bup.ovals[1]; + ((P1 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 36: /* STATE 4 */ + ; + /* 0 */ ((P1 *)this)->new_off = trpt->bup.oval; + ; + ; + goto R999; +; + + case 37: /* STATE 7 */ + goto R999; + + case 38: /* STATE 11 */ + ; + /* 0 */ ((P1 *)this)->prev_off = trpt->bup.oval; + ; + ; + goto R999; + + case 39: /* STATE 17 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.write_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 40: /* STATE 17 */ + ; + ((P1 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 41: /* STATE 21 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 42: /* STATE 22 */ + ; + /* 0 */ ((P1 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 43: /* STATE 28 */ + ; + ((P1 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 44: /* STATE 31 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 45: /* STATE 39 */ + ; + now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[3]; + now._commit_sum = trpt->bup.ovals[2]; + ((P1 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P1 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 4); + goto R999; + + case 46: /* STATE 39 */ + ; + now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + ((P1 *)this)->tmp_commit = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 47: /* STATE 41 */ + ; + deliver = trpt->bup.ovals[2]; + /* 1 */ ((P1 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P1 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; +; + + case 48: /* STATE 45 */ + goto R999; +; + + case 49: /* STATE 43 */ + goto R999; + + case 50: /* STATE 48 */ + ; + now.events_lost = trpt->bup.oval; + ; + goto R999; + + case 51: /* STATE 49 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 52: /* STATE 51 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC switcher */ + + case 53: /* STATE 3 */ + ; + ((P0 *)this)->new_off = trpt->bup.ovals[2]; + ((P0 *)this)->size = trpt->bup.ovals[1]; + ((P0 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 54: /* STATE 5 */ + ; + now.refcount = trpt->bup.ovals[2]; + /* 1 */ ((P0 *)this)->size = trpt->bup.ovals[1]; + /* 0 */ ((P0 *)this)->new_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; +; + + case 55: /* STATE 8 */ + goto R999; + + case 56: /* STATE 12 */ + ; + /* 0 */ ((P0 *)this)->prev_off = trpt->bup.oval; + ; + ; + goto R999; +; + + case 57: /* STATE 17 */ + goto R999; + + case 58: /* STATE 15 */ + ; + now.write_off = trpt->bup.oval; + ; + goto R999; + + case 59: /* STATE 21 */ + ; + now.commit_count[ Index(((((P0 *)this)->prev_off%4)/(4/2)), 2) ] = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + ((P0 *)this)->tmp_commit = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 60: /* STATE 28 */ + ; + now.refcount = trpt->bup.ovals[3]; + deliver = trpt->bup.ovals[2]; + /* 1 */ ((P0 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P0 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 4); + goto R999; + + case 61: /* STATE 28 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 62: /* STATE 28 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 63: /* STATE 31 */ + ; + p_restor(II); + ; + ; + goto R999; + } + diff --git a/trunk/verif/md/pan.c b/trunk/verif/md/pan.c new file mode 100644 index 00000000..34df6112 --- /dev/null +++ b/trunk/verif/md/pan.c @@ -0,0 +1,11541 @@ +/*** Generated by Spin Version 5.1.6 -- 9 May 2008 ***/ +/*** From source: model.spin ***/ + +#ifdef SC +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined(WIN32) || defined(WIN64) +#include +#else +#include +#include +#endif +#include +#include +#include +#define Offsetof(X, Y) ((unsigned long)(&(((X *)0)->Y))) +#ifndef max +#define max(a,b) (((a)<(b)) ? (b) : (a)) +#endif +#ifndef PRINTF +int Printf(const char *fmt, ...); /* prototype only */ +#endif +#include "pan.h" +#ifdef LOOPSTATE +double cnt_loops; +#endif +State A_Root; /* seed-state for cycles */ +State now; /* the full state-vector */ +#undef C_States +#if defined(C_States) && defined(HAS_TRACK) +void +c_update(uchar *p_t_r) +{ +#ifdef VERBOSE + printf("c_update %u\n", p_t_r); +#endif +} +void +c_revert(uchar *p_t_r) +{ +#ifdef VERBOSE + printf("c_revert %u\n", p_t_r); +#endif +} +#endif +void +globinit(void) +{ +} +void +locinit5(int h) +{ +} +void +locinit4(int h) +{ +} +void +locinit3(int h) +{ +} +void +locinit2(int h) +{ +} +void +locinit1(int h) +{ +} +void +locinit0(int h) +{ +} +#ifdef CNTRSTACK +#define onstack_now() (LL[trpt->j6] && LL[trpt->j7]) +#define onstack_put() LL[trpt->j6]++; LL[trpt->j7]++ +#define onstack_zap() LL[trpt->j6]--; LL[trpt->j7]-- +#endif +#if !defined(SAFETY) && !defined(NOCOMP) +#define V_A (((now._a_t&1)?2:1) << (now._a_t&2)) +#define A_V (((now._a_t&1)?1:2) << (now._a_t&2)) +int S_A = 0; +#else +#define V_A 0 +#define A_V 0 +#define S_A 0 +#endif +#ifdef MA +#undef onstack_now +#undef onstack_put +#undef onstack_zap +#define onstack_put() ; +#define onstack_zap() gstore((char *) &now, vsize, 4) +#else +#if defined(FULLSTACK) && !defined(BITSTATE) +#define onstack_put() trpt->ostate = Lstate +#define onstack_zap() { \ + if (trpt->ostate) \ + trpt->ostate->tagged = \ + (S_A)? (trpt->ostate->tagged&~V_A) : 0; \ + } +#endif +#endif +#ifndef NO_V_PROVISO +#define V_PROVISO +#endif +#if !defined(NO_RESIZE) && !defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(SPACE) && NCORE==1 + #define AUTO_RESIZE +#endif + +struct H_el { + struct H_el *nxt; +#ifdef FULLSTACK + unsigned int tagged; + #if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY) + unsigned int proviso; + #endif +#endif +#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK)) + unsigned long st_id; +#endif +#if !defined(SAFETY) || defined(REACH) + unsigned int D; +#endif +#if NCORE>1 + /* could cost 1 extra word: 4 bytes if 32-bit and 8 bytes if 64-bit */ + #ifdef V_PROVISO + uchar cpu_id; /* id of cpu that created the state */ + #endif +#endif +#ifdef COLLAPSE + #if VECTORSZ<65536 + unsigned short ln; + #else + unsigned long ln; + #endif +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + unsigned long m_K1; +#endif + unsigned long state; +} **H_tab, **S_Tab; + +typedef struct Trail { + int st; /* current state */ + uchar pr; /* process id */ + uchar tau; /* 8 bit-flags */ + uchar o_pm; /* 8 more bit-flags */ +#if 0 + Meaning of bit-flags: + tau&1 -> timeout enabled + tau&2 -> request to enable timeout 1 level up (in claim) + tau&4 -> current transition is a claim move + tau&8 -> current transition is an atomic move + tau&16 -> last move was truncated on stack + tau&32 -> current transition is a preselected move + tau&64 -> at least one next state is not on the stack + tau&128 -> current transition is a stutter move + o_pm&1 -> the current pid moved -- implements else + o_pm&2 -> this is an acceptance state + o_pm&4 -> this is a progress state + o_pm&8 -> fairness alg rule 1 undo mark + o_pm&16 -> fairness alg rule 3 undo mark + o_pm&32 -> fairness alg rule 2 undo mark + o_pm&64 -> the current proc applied rule2 + o_pm&128 -> a fairness, dummy move - all procs blocked +#endif +#ifdef NSUCC + uchar n_succ; /* nr of successor states */ +#endif +#if defined(FULLSTACK) && defined(MA) && !defined(BFS) + uchar proviso; +#endif +#ifndef BFS + uchar o_n, o_ot; /* to save locals */ +#endif + uchar o_m; +#ifdef EVENT_TRACE +#if nstates_event<256 + uchar o_event; +#else + unsigned short o_event; +#endif +#endif + int o_tt; +#ifndef BFS + short o_To; +#ifdef RANDOMIZE + short oo_i; +#endif +#endif +#if defined(HAS_UNLESS) && !defined(BFS) + int e_state; /* if escape trans - state of origin */ +#endif +#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS) || (NCORE>1) + struct H_el *ostate; /* pointer to stored state */ +#endif +#if defined(CNTRSTACK) && !defined(BFS) + long j6, j7; +#endif + Trans *o_t; +#ifdef SCHED + /* based on Qadeer&Rehof, Tacas 2005, LNCS 3440, pp. 93-107 */ + #if NCORE>1 + #error "-DSCHED cannot be combined with -DNCORE (yet)" + #endif + int sched_limit; +#endif +#ifdef HAS_SORTED + short ipt; +#endif + union { + int oval; + int *ovals; + } bup; +} Trail; +Trail *trail, *trpt; +FILE *efd; +uchar *this; +long maxdepth=10000; +long omaxdepth=10000; +#ifdef SCHED +int sched_max = 10; +#endif +#ifdef PERMUTED + uchar permuted = 1; +#else + uchar permuted = 0; +#endif +double quota; /* time limit */ +#if NCORE>1 +long z_handoff = -1; +#endif +#ifdef SC +char *stackfile; +#endif +uchar *SS, *LL; +uchar HASH_NR = 0; + +double memcnt = (double) 0; +double memlim = (double) (1<<30); /* 1 GB */ +#if NCORE>1 +double mem_reserved = (double) 0; +#endif + +/* for emalloc: */ +static char *have; +static long left = 0L; +static double fragment = (double) 0; +static unsigned long grow; + +unsigned int HASH_CONST[] = { + /* asuming 4 bytes per int */ + 0x88888EEF, 0x00400007, + 0x04c11db7, 0x100d4e63, + 0x0fc22f87, 0x3ff0c3ff, + 0x38e84cd7, 0x02b148e9, + 0x98b2e49d, 0xb616d379, + 0xa5247fd9, 0xbae92a15, + 0xb91c8bc5, 0x8e5880f3, + 0xacd7c069, 0xb4c44bb3, + 0x2ead1fb7, 0x8e428171, + 0xdbebd459, 0x828ae611, + 0x6cb25933, 0x86cdd651, + 0x9e8f5f21, 0xd5f8d8e7, + 0x9c4e956f, 0xb5cf2c71, + 0x2e805a6d, 0x33fc3a55, + 0xaf203ed1, 0xe31f5909, + 0x5276db35, 0x0c565ef7, + 0x273d1aa5, 0x8923b1dd, + 0 +}; +#if NCORE>1 +extern int core_id; +#endif +long mreached=0; +int done=0, errors=0, Nrun=1; +int c_init_done=0; +char *c_stack_start = (char *) 0; +double nstates=0, nlinks=0, truncs=0, truncs2=0; +double nlost=0, nShadow=0, hcmp=0, ngrabs=0; +#if defined(ZAPH) && defined(BITSTATE) +double zstates = 0; +#endif +int c_init_run; +#ifdef BFS +double midrv=0, failedrv=0, revrv=0; +#endif +unsigned long nr_states=0; /* nodes in DFA */ +long Fa=0, Fh=0, Zh=0, Zn=0; +long PUT=0, PROBE=0, ZAPS=0; +long Ccheck=0, Cholds=0; +int a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0; +#ifdef HAS_CODE +int gui = 0, coltrace = 0, readtrail = 0; +int whichtrail = 0, onlyproc = -1, silent = 0; +#endif +int state_tables=0, fairness=0, no_rck=0, Nr_Trails=0; +char simvals[128]; +#ifndef INLINE +int TstOnly=0; +#endif +unsigned long mask, nmask; +#ifdef BITSTATE +int ssize=23; /* 1 Mb */ +#else +int ssize=19; /* 512K slots */ +#endif +int hmax=0, svmax=0, smax=0; +int Maxbody=0, XX; +uchar *noptr; /* used by macro Pptr(x) */ +#ifdef VAR_RANGES +void logval(char *, int); +void dumpranges(void); +#endif +#ifdef MA +#define INLINE_REV +extern void dfa_init(unsigned short); +extern int dfa_member(unsigned long); +extern int dfa_store(uchar *); +unsigned int maxgs = 0; +#endif + +#ifdef ALIGNED + State comp_now __attribute__ ((aligned (8))); + /* gcc 64-bit aligned for Itanium2 systems */ + /* MAJOR runtime penalty if not used on those systems */ +#else + State comp_now; /* compressed state vector */ +#endif + +State comp_msk; +uchar *Mask = (uchar *) &comp_msk; +#ifdef COLLAPSE +State comp_tmp; +static char *scratch = (char *) &comp_tmp; +#endif +Stack *stack; /* for queues, processes */ +Svtack *svtack; /* for old state vectors */ +#ifdef BITSTATE +static unsigned int hfns = 3; /* new default */ +#endif +static unsigned long j1; +static unsigned long K1, K2; +static unsigned long j2, j3, j4; +#ifdef BITSTATE +static long udmem; +#endif +static long A_depth = 0; +long depth = 0; +#if NCORE>1 +long nr_handoffs = 0; +#endif +static uchar warned = 0, iterative = 0, exclusive = 0, like_java = 0, every_error = 0; +static uchar noasserts = 0, noends = 0, bounded = 0; +#if SYNC>0 && ASYNC==0 +void set_recvs(void); +int no_recvs(int); +#endif +#if SYNC +#define IfNotBlocked if (boq != -1) continue; +#define UnBlock boq = -1 +#else +#define IfNotBlocked /* cannot block */ +#define UnBlock /* don't bother */ +#endif + +#ifdef BITSTATE +int (*bstore)(char *, int); +int bstore_reg(char *, int); +int bstore_mod(char *, int); +#endif +void active_procs(void); +void cleanup(void); +void do_the_search(void); +void find_shorter(int); +void iniglobals(void); +void stopped(int); +void wrapup(void); +int *grab_ints(int); +void ungrab_ints(int *, int); +#ifndef NOBOUNDCHECK +#define Index(x, y) Boundcheck(x, y, II, tt, t) +#else +#define Index(x, y) x +#endif +short Air[] = { (short) Air0, (short) Air1, (short) Air2, (short) Air3, (short) Air4, (short) Air5, (short) Air6 }; +int +addproc(int n) +{ int j, h = now._nr_pr; +#ifndef NOCOMP + int k; +#endif + uchar *o_this = this; + +#ifndef INLINE + if (TstOnly) return (h < MAXPROC); +#endif +#ifndef NOBOUNDCHECK +/* redefine Index only within this procedure */ +#undef Index +#define Index(x, y) Boundcheck(x, y, 0, 0, 0) +#endif + if (h >= MAXPROC) + Uerror("too many processes"); + switch (n) { + case 0: j = sizeof(P0); break; + case 1: j = sizeof(P1); break; + case 2: j = sizeof(P2); break; + case 3: j = sizeof(P3); break; + case 4: j = sizeof(P4); break; + case 5: j = sizeof(P5); break; + case 6: j = sizeof(P6); break; + default: Uerror("bad proc - addproc"); + } + if (vsize%WS) + proc_skip[h] = WS-(vsize%WS); + else + proc_skip[h] = 0; +#ifndef NOCOMP + for (k = vsize + (int) proc_skip[h]; k > vsize; k--) + Mask[k-1] = 1; /* align */ +#endif + vsize += (int) proc_skip[h]; + proc_offset[h] = vsize; +#ifdef SVDUMP + if (vprefix > 0) + { int dummy = 0; + write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */ + write(svfd, (uchar *) &h, sizeof(int)); + write(svfd, (uchar *) &n, sizeof(int)); +#if VECTORSZ>32000 + write(svfd, (uchar *) &proc_offset[h], sizeof(int)); +#else + write(svfd, (uchar *) &proc_offset[h], sizeof(short)); +#endif + write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */ + } +#endif + now._nr_pr += 1; + if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2)) + { printf("pan: error: too many processes -- current"); + printf(" max is %d procs (-DNFAIR=%d)\n", + (8*NFAIR)/2 - 2, NFAIR); + printf("\trecompile with -DNFAIR=%d\n", + NFAIR+1); + pan_exit(1); + } + vsize += j; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (k = 1; k <= Air[n]; k++) + Mask[vsize - k] = 1; /* pad */ + Mask[vsize-j] = 1; /* _pid */ +#endif + hmax = max(hmax, vsize); + if (vsize >= VECTORSZ) + { printf("pan: error, VECTORSZ too small, recompile pan.c"); + printf(" with -DVECTORSZ=N with N>%d\n", (int) vsize); + Uerror("aborting"); + } + memset((char *)pptr(h), 0, j); + this = pptr(h); + if (BASE > 0 && h > 0) + ((P0 *)this)->_pid = h-BASE; + else + ((P0 *)this)->_pid = h; + switch (n) { + case 6: /* np_ */ + ((P6 *)pptr(h))->_t = 6; + ((P6 *)pptr(h))->_p = 0; + reached6[0] = 1; + accpstate[6][1] = 1; + break; + case 5: /* :never: */ + ((P5 *)pptr(h))->_t = 5; + ((P5 *)pptr(h))->_p = 5; reached5[5]=1; + /* params: */ + /* locals: */ +#ifdef VAR_RANGES +#endif +#ifdef HAS_CODE + locinit5(h); +#endif + break; + case 4: /* :init: */ + ((P4 *)pptr(h))->_t = 4; + ((P4 *)pptr(h))->_p = 42; reached4[42]=1; + /* params: */ + /* locals: */ + ((P4 *)pptr(h))->i = 0; + ((P4 *)pptr(h))->j = 0; + ((P4 *)pptr(h))->sum = 0; + ((P4 *)pptr(h))->commit_sum = 0; +#ifdef VAR_RANGES + logval(":init::i", ((P4 *)pptr(h))->i); + logval(":init::j", ((P4 *)pptr(h))->j); + logval(":init::sum", ((P4 *)pptr(h))->sum); + logval(":init::commit_sum", ((P4 *)pptr(h))->commit_sum); +#endif +#ifdef HAS_CODE + locinit4(h); +#endif + break; + case 3: /* cleaner */ + ((P3 *)pptr(h))->_t = 3; + ((P3 *)pptr(h))->_p = 8; reached3[8]=1; + /* params: */ + /* locals: */ +#ifdef VAR_RANGES +#endif +#ifdef HAS_CODE + locinit3(h); +#endif + break; + case 2: /* reader */ + ((P2 *)pptr(h))->_t = 2; + ((P2 *)pptr(h))->_p = 26; reached2[26]=1; + /* params: */ + /* locals: */ + ((P2 *)pptr(h))->i = 0; + ((P2 *)pptr(h))->j = 0; +#ifdef VAR_RANGES + logval("reader:i", ((P2 *)pptr(h))->i); + logval("reader:j", ((P2 *)pptr(h))->j); +#endif +#ifdef HAS_CODE + locinit2(h); +#endif + break; + case 1: /* tracer */ + ((P1 *)pptr(h))->_t = 1; + ((P1 *)pptr(h))->_p = 3; reached1[3]=1; + /* params: */ + /* locals: */ + ((P1 *)pptr(h))->size = 1; + ((P1 *)pptr(h))->prev_off = 0; + ((P1 *)pptr(h))->new_off = 0; + ((P1 *)pptr(h))->tmp_commit = 0; + ((P1 *)pptr(h))->i = 0; + ((P1 *)pptr(h))->j = 0; +#ifdef VAR_RANGES + logval("tracer:size", ((P1 *)pptr(h))->size); + logval("tracer:prev_off", ((P1 *)pptr(h))->prev_off); + logval("tracer:new_off", ((P1 *)pptr(h))->new_off); + logval("tracer:tmp_commit", ((P1 *)pptr(h))->tmp_commit); + logval("tracer:i", ((P1 *)pptr(h))->i); + logval("tracer:j", ((P1 *)pptr(h))->j); +#endif +#ifdef HAS_CODE + locinit1(h); +#endif + break; + case 0: /* switcher */ + ((P0 *)pptr(h))->_t = 0; + ((P0 *)pptr(h))->_p = 11; reached0[11]=1; + /* params: */ + /* locals: */ + ((P0 *)pptr(h))->prev_off = 0; + ((P0 *)pptr(h))->new_off = 0; + ((P0 *)pptr(h))->tmp_commit = 0; + ((P0 *)pptr(h))->size = 0; +#ifdef VAR_RANGES + logval("switcher:prev_off", ((P0 *)pptr(h))->prev_off); + logval("switcher:new_off", ((P0 *)pptr(h))->new_off); + logval("switcher:tmp_commit", ((P0 *)pptr(h))->tmp_commit); + logval("switcher:size", ((P0 *)pptr(h))->size); +#endif +#ifdef HAS_CODE + locinit0(h); +#endif + break; + } + this = o_this; + return h-BASE; +#ifndef NOBOUNDCHECK +#undef Index +#define Index(x, y) Boundcheck(x, y, II, tt, t) +#endif +} + +#if defined(BITSTATE) && defined(COLLAPSE) +/* just to allow compilation, to generate the error */ +long col_p(int i, char *z) { return 0; } +long col_q(int i, char *z) { return 0; } +#endif +#ifndef BITSTATE +#ifdef COLLAPSE +long +col_p(int i, char *z) +{ int j, k; unsigned long ordinal(char *, long, short); + char *x, *y; + P0 *ptr = (P0 *) pptr(i); + switch (ptr->_t) { + case 0: j = sizeof(P0); break; + case 1: j = sizeof(P1); break; + case 2: j = sizeof(P2); break; + case 3: j = sizeof(P3); break; + case 4: j = sizeof(P4); break; + case 5: j = sizeof(P5); break; + case 6: j = sizeof(P6); break; + default: Uerror("bad proctype - collapse"); + } + if (z) x = z; else x = scratch; + y = (char *) ptr; k = proc_offset[i]; + for ( ; j > 0; j--, y++) + if (!Mask[k++]) *x++ = *y; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; + if (z) return (long) (x - z); + return ordinal(scratch, x-scratch, (short) (2+ptr->_t)); +} +#endif +#endif +void +run(void) +{ /* int i; */ + memset((char *)&now, 0, sizeof(State)); + vsize = (unsigned long) (sizeof(State) - VECTORSZ); +#ifndef NOVSZ + now._vsz = vsize; +#endif +/* optional provisioning statements, e.g. to */ +/* set hidden variables, used as constants */ +#ifdef PROV +#include PROV +#endif + settable(); + Maxbody = max(Maxbody, ((int) sizeof(P0))); + Maxbody = max(Maxbody, ((int) sizeof(P1))); + Maxbody = max(Maxbody, ((int) sizeof(P2))); + Maxbody = max(Maxbody, ((int) sizeof(P3))); + Maxbody = max(Maxbody, ((int) sizeof(P4))); + Maxbody = max(Maxbody, ((int) sizeof(P5))); + Maxbody = max(Maxbody, ((int) sizeof(P6))); + reached[0] = reached0; + reached[1] = reached1; + reached[2] = reached2; + reached[3] = reached3; + reached[4] = reached4; + reached[5] = reached5; + reached[6] = reached6; + accpstate[0] = (uchar *) emalloc(nstates0); + accpstate[1] = (uchar *) emalloc(nstates1); + accpstate[2] = (uchar *) emalloc(nstates2); + accpstate[3] = (uchar *) emalloc(nstates3); + accpstate[4] = (uchar *) emalloc(nstates4); + accpstate[5] = (uchar *) emalloc(nstates5); + accpstate[6] = (uchar *) emalloc(nstates6); + progstate[0] = (uchar *) emalloc(nstates0); + progstate[1] = (uchar *) emalloc(nstates1); + progstate[2] = (uchar *) emalloc(nstates2); + progstate[3] = (uchar *) emalloc(nstates3); + progstate[4] = (uchar *) emalloc(nstates4); + progstate[5] = (uchar *) emalloc(nstates5); + progstate[6] = (uchar *) emalloc(nstates6); + loopstate0 = loopstate[0] = (uchar *) emalloc(nstates0); + loopstate1 = loopstate[1] = (uchar *) emalloc(nstates1); + loopstate2 = loopstate[2] = (uchar *) emalloc(nstates2); + loopstate3 = loopstate[3] = (uchar *) emalloc(nstates3); + loopstate4 = loopstate[4] = (uchar *) emalloc(nstates4); + loopstate5 = loopstate[5] = (uchar *) emalloc(nstates5); + loopstate6 = loopstate[6] = (uchar *) emalloc(nstates6); + stopstate[0] = (uchar *) emalloc(nstates0); + stopstate[1] = (uchar *) emalloc(nstates1); + stopstate[2] = (uchar *) emalloc(nstates2); + stopstate[3] = (uchar *) emalloc(nstates3); + stopstate[4] = (uchar *) emalloc(nstates4); + stopstate[5] = (uchar *) emalloc(nstates5); + stopstate[6] = (uchar *) emalloc(nstates6); + visstate[0] = (uchar *) emalloc(nstates0); + visstate[1] = (uchar *) emalloc(nstates1); + visstate[2] = (uchar *) emalloc(nstates2); + visstate[3] = (uchar *) emalloc(nstates3); + visstate[4] = (uchar *) emalloc(nstates4); + visstate[5] = (uchar *) emalloc(nstates5); + visstate[6] = (uchar *) emalloc(nstates6); + mapstate[0] = (short *) emalloc(nstates0 * sizeof(short)); + mapstate[1] = (short *) emalloc(nstates1 * sizeof(short)); + mapstate[2] = (short *) emalloc(nstates2 * sizeof(short)); + mapstate[3] = (short *) emalloc(nstates3 * sizeof(short)); + mapstate[4] = (short *) emalloc(nstates4 * sizeof(short)); + mapstate[5] = (short *) emalloc(nstates5 * sizeof(short)); + mapstate[6] = (short *) emalloc(nstates6 * sizeof(short)); +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE + NrStates[0] = nstates0; + NrStates[1] = nstates1; + NrStates[2] = nstates2; + NrStates[3] = nstates3; + NrStates[4] = nstates4; + NrStates[5] = nstates5; + NrStates[6] = nstates6; +#endif +#endif +#endif +#endif +#endif +#endif +#endif + stopstate[0][endstate0] = 1; + stopstate[1][endstate1] = 1; + stopstate[2][endstate2] = 1; + stopstate[3][endstate3] = 1; + stopstate[4][endstate4] = 1; + stopstate[5][endstate5] = 1; + stopstate[6][endstate6] = 1; + accpstate[5][13] = 1; + stopstate[1][49] = 1; + retrans(0, nstates0, start0, src_ln0, reached0, loopstate0); + retrans(1, nstates1, start1, src_ln1, reached1, loopstate1); + retrans(2, nstates2, start2, src_ln2, reached2, loopstate2); + retrans(3, nstates3, start3, src_ln3, reached3, loopstate3); + retrans(4, nstates4, start4, src_ln4, reached4, loopstate4); + retrans(5, nstates5, start5, src_ln5, reached5, loopstate5); + if (state_tables) + { printf("\nTransition Type: "); + printf("A=atomic; D=d_step; L=local; G=global\n"); + printf("Source-State Labels: "); + printf("p=progress; e=end; a=accept;\n"); +#ifdef MERGED + printf("Note: statement merging was used. Only the first\n"); + printf(" stmnt executed in each merge sequence is shown\n"); + printf(" (use spin -a -o3 to disable statement merging)\n"); +#endif + pan_exit(0); + } + iniglobals(); +#if defined(VERI) && !defined(NOREDUCE) && !defined(NP) + if (!state_tables +#ifdef HAS_CODE + && !readtrail +#endif +#if NCORE>1 + && core_id == 0 +#endif + ) + { printf("warning: for p.o. reduction to be valid "); + printf("the never claim must be stutter-invariant\n"); + printf("(never claims generated from LTL "); + printf("formulae are stutter-invariant)\n"); + } +#endif + UnBlock; /* disable rendez-vous */ +#ifdef BITSTATE + if (udmem) + { udmem *= 1024L*1024L; + #if NCORE>1 + if (!readtrail) + { void init_SS(unsigned long); + init_SS((unsigned long) udmem); + } else + #endif + SS = (uchar *) emalloc(udmem); + bstore = bstore_mod; + } else + #if NCORE>1 + { void init_SS(unsigned long); + init_SS(ONE_L<<(ssize-3)); + } + #else + SS = (uchar *) emalloc(ONE_L<<(ssize-3)); + #endif +#else + hinit(); +#endif +#if defined(FULLSTACK) && defined(BITSTATE) + onstack_init(); +#endif +#if defined(CNTRSTACK) && !defined(BFS) + LL = (uchar *) emalloc(ONE_L<<(ssize-3)); +#endif + stack = ( Stack *) emalloc(sizeof(Stack)); + svtack = (Svtack *) emalloc(sizeof(Svtack)); + /* a place to point for Pptr of non-running procs: */ + noptr = (uchar *) emalloc(Maxbody * sizeof(char)); +#ifdef SVDUMP + if (vprefix > 0) + write(svfd, (uchar *) &vprefix, sizeof(int)); +#endif +#ifdef VERI + Addproc(VERI); /* never - pid = 0 */ +#endif + active_procs(); /* started after never */ +#ifdef EVENT_TRACE + now._event = start_event; + reached[EVENT_TRACE][start_event] = 1; +#endif +#ifdef HAS_CODE + globinit(); +#endif +#ifdef BITSTATE +go_again: +#endif + do_the_search(); +#ifdef BITSTATE + if (--Nrun > 0 && HASH_CONST[++HASH_NR]) + { printf("Run %d:\n", HASH_NR); + wrap_stats(); + printf("\n"); + memset(SS, 0, ONE_L<<(ssize-3)); +#ifdef CNTRSTACK + memset(LL, 0, ONE_L<<(ssize-3)); +#endif +#ifdef FULLSTACK + memset((uchar *) S_Tab, 0, + maxdepth*sizeof(struct H_el *)); +#endif + nstates=nlinks=truncs=truncs2=ngrabs = 0; + nlost=nShadow=hcmp = 0; + Fa=Fh=Zh=Zn = 0; + PUT=PROBE=ZAPS=Ccheck=Cholds = 0; + goto go_again; + } +#endif +} +#ifdef HAS_PROVIDED +int provided(int, uchar, int, Trans *); +#endif +#if NCORE>1 +#define GLOBAL_LOCK (0) +#ifndef CS_N +#define CS_N (256*NCORE) +#endif +#ifdef NGQ +#define NR_QS (NCORE) +#define CS_NR (CS_N+1) /* 2^N + 1, nr critical sections */ +#define GQ_RD GLOBAL_LOCK +#define GQ_WR GLOBAL_LOCK +#define CS_ID (1 + (int) (j1 & (CS_N-1))) /* mask: 2^N - 1, zero reserved */ +#define QLOCK(n) (1+n) +#else +#define NR_QS (NCORE+1) +#define CS_NR (CS_N+3) +#define GQ_RD (1) +#define GQ_WR (2) +#define CS_ID (3 + (int) (j1 & (CS_N-1))) +#define QLOCK(n) (3+n) +#endif + +void e_critical(int); +void x_critical(int); + +#ifndef SEP_STATE + #define enter_critical(w) e_critical(w) + #define leave_critical(w) x_critical(w) +#else + #ifdef NGQ + #define enter_critical(w) { if (w < 1+NCORE) e_critical(w); } + #define leave_critical(w) { if (w < 1+NCORE) x_critical(w); } + #else + #define enter_critical(w) { if (w < 3+NCORE) e_critical(w); } + #define leave_critical(w) { if (w < 3+NCORE) x_critical(w); } + #endif +#endif + +int +cpu_printf(const char *fmt, ...) +{ va_list args; + enter_critical(GLOBAL_LOCK); /* printing */ + printf("cpu%d: ", core_id); + fflush(stdout); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + leave_critical(GLOBAL_LOCK); + return 1; +} +#else +int +cpu_printf(const char *fmt, ...) +{ va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + return 1; +} +#endif +int +Printf(const char *fmt, ...) +{ /* Make sure the args to Printf + * are always evaluated (e.g., they + * could contain a run stmnt) + * but do not generate the output + * during verification runs + * unless explicitly wanted + * If this fails on your system + * compile SPIN itself -DPRINTF + * and this code is not generated + */ +#ifdef HAS_CODE + if (readtrail) + { va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + return 1; + } +#endif +#ifdef PRINTF + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +#endif + return 1; +} +extern void printm(int); +#ifndef SC +#define getframe(i) &trail[i]; +#else +static long HHH, DDD, hiwater; +static long CNT1, CNT2; +static int stackwrite; +static int stackread; +static Trail frameptr; +Trail * +getframe(int d) +{ + if (CNT1 == CNT2) + return &trail[d]; + + if (d >= (CNT1-CNT2)*DDD) + return &trail[d - (CNT1-CNT2)*DDD]; + + if (!stackread + && (stackread = open(stackfile, 0)) < 0) + { printf("getframe: cannot open %s\n", stackfile); + wrapup(); + } + if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1 + || read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail)) + { printf("getframe: frame read error\n"); + wrapup(); + } + return &frameptr; +} +#endif +#if !defined(SAFETY) && !defined(BITSTATE) +#if !defined(FULLSTACK) || defined(MA) +#define depth_of(x) A_depth /* an estimate */ +#else +int +depth_of(struct H_el *s) +{ Trail *t; int d; + for (d = 0; d <= A_depth; d++) + { t = getframe(d); + if (s == t->ostate) + return d; + } + printf("pan: cannot happen, depth_of\n"); + return depthfound; +} +#endif +#endif +#if NCORE>1 +extern void cleanup_shm(int); +volatile unsigned int *search_terminated; /* to signal early termination */ +#endif +void +pan_exit(int val) +{ void stop_timer(void); + if (signoff) + { printf("--end of output--\n"); + } +#if NCORE>1 + if (search_terminated != NULL) + { *search_terminated |= 1; /* pan_exit */ + } +#ifdef USE_DISK + { void dsk_stats(void); + dsk_stats(); + } +#endif + if (!state_tables && !readtrail) + { cleanup_shm(1); + } +#endif + if (val == 2) + { val = 0; + } else + { stop_timer(); + } + exit(val); +} +#ifdef HAS_CODE +char * +transmognify(char *s) +{ char *v, *w; + static char buf[2][2048]; + int i, toggle = 0; + if (!s || strlen(s) > 2047) return s; + memset(buf[0], 0, 2048); + memset(buf[1], 0, 2048); + strcpy(buf[toggle], s); + while ((v = strstr(buf[toggle], "{c_code"))) + { *v = '\0'; v++; + strcpy(buf[1-toggle], buf[toggle]); + for (w = v; *w != '}' && *w != '\0'; w++) /* skip */; + if (*w != '}') return s; + *w = '\0'; w++; + for (i = 0; code_lookup[i].c; i++) + if (strcmp(v, code_lookup[i].c) == 0 + && strlen(v) == strlen(code_lookup[i].c)) + { if (strlen(buf[1-toggle]) + + strlen(code_lookup[i].t) + + strlen(w) > 2047) + return s; + strcat(buf[1-toggle], code_lookup[i].t); + break; + } + strcat(buf[1-toggle], w); + toggle = 1 - toggle; + } + buf[toggle][2047] = '\0'; + return buf[toggle]; +} +#else +char * transmognify(char *s) { return s; } +#endif +#ifdef HAS_CODE +void +add_src_txt(int ot, int tt) +{ Trans *t; + char *q; + + for (t = trans[ot][tt]; t; t = t->nxt) + { printf("\t\t"); + q = transmognify(t->tp); + for ( ; q && *q; q++) + if (*q == '\n') + printf("\\n"); + else + putchar(*q); + printf("\n"); + } +} +void +wrap_trail(void) +{ static int wrap_in_progress = 0; + int i; short II; + P0 *z; + + if (wrap_in_progress++) return; + + printf("spin: trail ends after %ld steps\n", depth); + if (onlyproc >= 0) + { if (onlyproc >= now._nr_pr) { pan_exit(0); } + II = onlyproc; + z = (P0 *)pptr(II); + printf("%3ld: proc %d (%s) ", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d", + src_all[i].src[z->_p]); + break; + } + printf(" (state %2d)", z->_p); + if (!stopstate[z->_t][z->_p]) + printf(" (invalid end state)"); + printf("\n"); + add_src_txt(z->_t, z->_p); + pan_exit(0); + } + printf("#processes %d:\n", now._nr_pr); + if (depth < 0) depth = 0; + for (II = 0; II < now._nr_pr; II++) + { z = (P0 *)pptr(II); + printf("%3ld: proc %d (%s) ", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d", + src_all[i].src[z->_p]); + break; + } + printf(" (state %2d)", z->_p); + if (!stopstate[z->_t][z->_p]) + printf(" (invalid end state)"); + printf("\n"); + add_src_txt(z->_t, z->_p); + } + c_globals(); + for (II = 0; II < now._nr_pr; II++) + { z = (P0 *)pptr(II); + c_locals(II, z->_t); + } +#ifdef ON_EXIT + ON_EXIT; +#endif + pan_exit(0); +} +FILE * +findtrail(void) +{ FILE *fd; + char fnm[512], *q; + char MyFile[512]; + char MySuffix[16]; + int try_core; + int candidate_files; + + if (trailfilename != NULL) + { fd = fopen(trailfilename, "r"); + if (fd == NULL) + { printf("pan: cannot find %s\n", trailfilename); + pan_exit(1); + } /* else */ + goto success; + } +talk: + try_core = 1; + candidate_files = 0; + tprefix = "trail"; + strcpy(MyFile, TrailFile); + do { /* see if there's more than one possible trailfile */ + if (whichtrail) + { sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } + if ((q = strchr(MyFile, '.')) != NULL) + { *q = '\0'; + sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } } + } else + { sprintf(fnm, "%s.%s", MyFile, tprefix); + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } + if ((q = strchr(MyFile, '.')) != NULL) + { *q = '\0'; + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } } } + tprefix = MySuffix; + sprintf(tprefix, "cpu%d_trail", try_core++); + } while (try_core <= NCORE); + + if (candidate_files != 1) + { if (verbose != 100) + { printf("error: there are %d trail files:\n", + candidate_files); + verbose = 100; + goto talk; + } else + { printf("pan: rm or mv all except one\n"); + exit(1); + } } + try_core = 1; + strcpy(MyFile, TrailFile); /* restore */ + tprefix = "trail"; +try_again: + if (whichtrail) + { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, tprefix); + fd = fopen(fnm, "r"); + if (fd == NULL && (q = strchr(MyFile, '.'))) + { *q = '\0'; + sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + } + } else + { sprintf(fnm, "%s.%s", MyFile, tprefix); + fd = fopen(fnm, "r"); + if (fd == NULL && (q = strchr(MyFile, '.'))) + { *q = '\0'; + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + } } + if (fd == NULL) + { if (try_core < NCORE) + { tprefix = MySuffix; + sprintf(tprefix, "cpu%d_trail", try_core++); + goto try_again; + } + printf("pan: cannot find trailfile %s\n", fnm); + pan_exit(1); + } +success: +#if NCORE>1 && defined(SEP_STATE) + { void set_root(void); /* for partial traces from local root */ + set_root(); + } +#endif + return fd; +} + +uchar do_transit(Trans *, short); + +void +getrail(void) +{ FILE *fd; + char *q; + int i, t_id, lastnever=-1; short II; + Trans *t; + P0 *z; + + fd = findtrail(); /* exits if unsuccessful */ + while (fscanf(fd, "%ld:%d:%d\n", &depth, &i, &t_id) == 3) + { if (depth == -1) + printf("<<<<>>>>\n"); + if (depth < 0) + continue; + if (i > now._nr_pr) + { printf("pan: Error, proc %d invalid pid ", i); + printf("transition %d\n", t_id); + break; + } + II = i; + z = (P0 *)pptr(II); + for (t = trans[z->_t][z->_p]; t; t = t->nxt) + if (t->t_id == (T_ID) t_id) + break; + if (!t) + { for (i = 0; i < NrStates[z->_t]; i++) + { t = trans[z->_t][i]; + if (t && t->t_id == (T_ID) t_id) + { printf("\tRecovered at state %d\n", i); + z->_p = i; + goto recovered; + } } + printf("pan: Error, proc %d type %d state %d: ", + II, z->_t, z->_p); + printf("transition %d not found\n", t_id); + printf("pan: list of possible transitions in this process:\n"); + if (z->_t >= 0 && z->_t <= _NP_) + for (t = trans[z->_t][z->_p]; t; t = t->nxt) + printf(" t_id %d -- case %d, [%s]\n", + t->t_id, t->forw, t->tp); + break; /* pan_exit(1); */ + } +recovered: + q = transmognify(t->tp); + if (gui) simvals[0] = '\0'; + this = pptr(II); + trpt->tau |= 1; + if (!do_transit(t, II)) + { if (onlyproc >= 0 && II != onlyproc) + goto moveon; + printf("pan: error, next transition UNEXECUTABLE on replay\n"); + printf(" most likely causes: missing c_track statements\n"); + printf(" or illegal side-effects in c_expr statements\n"); + } + if (onlyproc >= 0 && II != onlyproc) + goto moveon; + if (verbose) + { printf("%3ld: proc %2d (%s) ", depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) trans {%d,%d} [%s]\n", + z->_p, t_id, t->forw, q?q:""); + c_globals(); + for (i = 0; i < now._nr_pr; i++) + { c_locals(i, ((P0 *)pptr(i))->_t); + } + } else + if (strcmp(procname[z->_t], ":never:") == 0) + { if (lastnever != (int) z->_p) + { for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf("MSC: ~G %d\n", + src_all[i].src[z->_p]); + break; + } + if (!src_all[i].src) + printf("MSC: ~R %d\n", z->_p); + } + lastnever = z->_p; + goto sameas; + } else + if (strcmp(procname[z->_t], ":np_:") != 0) + { +sameas: if (no_rck) goto moveon; + if (coltrace) + { printf("%ld: ", depth); + for (i = 0; i < II; i++) + printf("\t\t"); + printf("%s(%d):", procname[z->_t], II); + printf("[%s]\n", q?q:""); + } else if (!silent) + { if (strlen(simvals) > 0) { + printf("%3ld: proc %2d (%s)", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) [values: %s]\n", z->_p, simvals); + } + printf("%3ld: proc %2d (%s)", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) [%s]\n", z->_p, q?q:""); + /* printf("\n"); */ + } } +moveon: z->_p = t->st; + } + wrap_trail(); +} +#endif +int +f_pid(int pt) +{ int i; + P0 *z; + for (i = 0; i < now._nr_pr; i++) + { z = (P0 *)pptr(i); + if (z->_t == (unsigned) pt) + return BASE+z->_pid; + } + return -1; +} +#ifdef VERI +void check_claim(int); +#endif + +#if !defined(HASH64) && !defined(HASH32) + #define HASH32 +#endif +#if defined(HASH32) && defined(SAFETY) && !defined(SFH) && !defined(SPACE) + #define SFH +#endif +#if defined(SFH) && (defined(BITSTATE) || defined(COLLAPSE) || defined(HC) || defined(HASH64)) + #undef SFH +#endif +#if defined(SFH) && !defined(NOCOMP) + #define NOCOMP /* go for speed */ +#endif +#if NCORE>1 && !defined(GLOB_HEAP) + #define SEP_HEAP /* version 5.1.2 */ +#endif + +#ifdef BITSTATE +int +bstore_mod(char *v, int n) /* hasharray size not a power of two */ +{ unsigned long x, y; + unsigned int i = 1; + + d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */ + x = K1; y = j3; + for (;;) + { if (!(SS[x%udmem]&(1< RANDSTOR) return 0; +#endif + for (;;) + { SS[x%udmem] |= (1< RANDSTOR) return 0; +#endif + for (;;) + { SS[x] |= (1< 0) + { sprintf(fnm, "%s%d.%s", + MyFile, Nr_Trails-1, tprefix); + } else + { +#ifdef PUTPID + sprintf(fnm, "%s%d.%s", MyFile, getpid(), tprefix); +#else + sprintf(fnm, "%s.%s", MyFile, tprefix); +#endif + } + if ((fd = open(fnm, w_flags, TMODE)) < 0) + { if ((q = strchr(MyFile, '.'))) + { *q = '\0'; + if (iterative == 0 && Nr_Trails-1 > 0) + sprintf(fnm, "%s%d.%s", + MyFile, Nr_Trails-1, tprefix); + else + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = open(fnm, w_flags, TMODE); + } } + if (fd < 0) + { printf("pan: cannot create %s\n", fnm); + perror("cause"); + } else + { +#if NCORE>1 && (defined(SEP_STATE) || !defined(FULL_TRAIL)) + void write_root(void); + write_root(); +#else + printf("pan: wrote %s\n", fnm); +#endif + } + return fd; +} + +#ifndef FREQ +#define FREQ (1000000) +#endif +#ifdef BFS +#define Q_PROVISO +#ifndef INLINE_REV +#define INLINE_REV +#endif + +typedef struct SV_Hold { + State *sv; + int sz; + struct SV_Hold *nxt; +} SV_Hold; + +typedef struct EV_Hold { + char *sv; + int sz; + int nrpr; + int nrqs; + char *po; + char *qo; + char *ps, *qs; + struct EV_Hold *nxt; +} EV_Hold; + +typedef struct BFS_Trail { + Trail *frame; + SV_Hold *onow; + EV_Hold *omask; +#ifdef Q_PROVISO + struct H_el *lstate; +#endif + short boq; + struct BFS_Trail *nxt; +} BFS_Trail; + +BFS_Trail *bfs_trail, *bfs_bot, *bfs_free; + +SV_Hold *svhold, *svfree; + +#ifdef BFS_DISK +#ifndef BFS_LIMIT + #define BFS_LIMIT 100000 +#endif +#ifndef BFS_DSK_LIMIT + #define BFS_DSK_LIMIT 1000000 +#endif +#if defined(WIN32) || defined(WIN64) + #define RFLAGS (O_RDONLY|O_BINARY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY) +#else + #define RFLAGS (O_RDONLY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC) +#endif +long bfs_size_limit; +int bfs_dsk_write = -1; +int bfs_dsk_read = -1; +long bfs_dsk_writes, bfs_dsk_reads; +int bfs_dsk_seqno_w, bfs_dsk_seqno_r; +#endif + +uchar do_reverse(Trans *, short, uchar); +void snapshot(void); + +SV_Hold * +getsv(int n) +{ SV_Hold *h = (SV_Hold *) 0, *oh; + + oh = (SV_Hold *) 0; + for (h = svfree; h; oh = h, h = h->nxt) + { if (n == h->sz) + { if (!oh) + svfree = h->nxt; + else + oh->nxt = h->nxt; + h->nxt = (SV_Hold *) 0; + break; + } + if (n < h->sz) + { h = (SV_Hold *) 0; + break; + } + /* else continue */ + } + + if (!h) + { h = (SV_Hold *) emalloc(sizeof(SV_Hold)); + h->sz = n; +#ifdef BFS_DISK + if (bfs_size_limit >= BFS_LIMIT) + { h->sv = (State *) 0; /* means: read disk */ + bfs_dsk_writes++; /* count */ + if (bfs_dsk_write < 0 /* file descriptor */ + || bfs_dsk_writes%BFS_DSK_LIMIT == 0) + { char dsk_nm[32]; + if (bfs_dsk_write >= 0) + { (void) close(bfs_dsk_write); + } + sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_w++); + bfs_dsk_write = open(dsk_nm, WFLAGS, 0644); + if (bfs_dsk_write < 0) + { Uerror("could not create tmp disk file"); + } + printf("pan: created disk file %s\n", dsk_nm); + } + if (write(bfs_dsk_write, (char *) &now, n) != n) + { Uerror("aborting -- disk write failed (disk full?)"); + } + return h; /* no memcpy */ + } + bfs_size_limit++; +#endif + h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n); + } + + memcpy((char *)h->sv, (char *)&now, n); + return h; +} + +EV_Hold * +getsv_mask(int n) +{ EV_Hold *h; + static EV_Hold *kept = (EV_Hold *) 0; + + for (h = kept; h; h = h->nxt) + if (n == h->sz + && (memcmp((char *) Mask, (char *) h->sv, n) == 0) + && (now._nr_pr == h->nrpr) + && (now._nr_qs == h->nrqs) +#if VECTORSZ>32000 + && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0) + && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0) +#else + && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0) + && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0) +#endif + && (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0) + && (memcmp((char *) q_skip, (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0)) + break; + if (!h) + { h = (EV_Hold *) emalloc(sizeof(EV_Hold)); + h->sz = n; + h->nrpr = now._nr_pr; + h->nrqs = now._nr_qs; + + h->sv = (char *) emalloc(n * sizeof(char)); + memcpy((char *) h->sv, (char *) Mask, n); + + if (now._nr_pr > 0) + { h->ps = (char *) emalloc(now._nr_pr * sizeof(int)); + memcpy((char *) h->ps, (char *) proc_skip, now._nr_pr * sizeof(uchar)); +#if VECTORSZ>32000 + h->po = (char *) emalloc(now._nr_pr * sizeof(int)); + memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int)); +#else + h->po = (char *) emalloc(now._nr_pr * sizeof(short)); + memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short)); +#endif + } + if (now._nr_qs > 0) + { h->qs = (char *) emalloc(now._nr_qs * sizeof(int)); + memcpy((char *) h->qs, (char *) q_skip, now._nr_qs * sizeof(uchar)); +#if VECTORSZ>32000 + h->qo = (char *) emalloc(now._nr_qs * sizeof(int)); + memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int)); +#else + h->qo = (char *) emalloc(now._nr_qs * sizeof(short)); + memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short)); +#endif + } + + h->nxt = kept; + kept = h; + } + return h; +} + +void +freesv(SV_Hold *p) +{ SV_Hold *h, *oh; + + oh = (SV_Hold *) 0; + for (h = svfree; h; oh = h, h = h->nxt) + if (h->sz >= p->sz) + break; + + if (!oh) + { p->nxt = svfree; + svfree = p; + } else + { p->nxt = h; + oh->nxt = p; + } +} + +BFS_Trail * +get_bfs_frame(void) +{ BFS_Trail *t; + + if (bfs_free) + { t = bfs_free; + bfs_free = bfs_free->nxt; + t->nxt = (BFS_Trail *) 0; + } else + { t = (BFS_Trail *) emalloc(sizeof(BFS_Trail)); + } + t->frame = (Trail *) emalloc(sizeof(Trail)); + return t; +} + +void +push_bfs(Trail *f, int d) +{ BFS_Trail *t; + + t = get_bfs_frame(); + memcpy((char *)t->frame, (char *)f, sizeof(Trail)); + t->frame->o_tt = d; /* depth */ + + t->boq = boq; + t->onow = getsv(vsize); + t->omask = getsv_mask(vsize); +#if defined(FULLSTACK) && defined(Q_PROVISO) + t->lstate = Lstate; +#endif + if (!bfs_bot) + { bfs_bot = bfs_trail = t; + } else + { bfs_bot->nxt = t; + bfs_bot = t; + } +#ifdef CHECK + printf("PUSH %u (%d)\n", t->frame, d); +#endif +} + +Trail * +pop_bfs(void) +{ BFS_Trail *t; + + if (!bfs_trail) + return (Trail *) 0; + + t = bfs_trail; + bfs_trail = t->nxt; + if (!bfs_trail) + bfs_bot = (BFS_Trail *) 0; +#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE) + if (t->lstate) t->lstate->tagged = 0; +#endif + + t->nxt = bfs_free; + bfs_free = t; + + vsize = t->onow->sz; + boq = t->boq; +#ifdef BFS_DISK + if (t->onow->sv == (State *) 0) + { char dsk_nm[32]; + bfs_dsk_reads++; /* count */ + if (bfs_dsk_read >= 0 /* file descriptor */ + && bfs_dsk_reads%BFS_DSK_LIMIT == 0) + { (void) close(bfs_dsk_read); + sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r-1); + (void) unlink(dsk_nm); + bfs_dsk_read = -1; + } + if (bfs_dsk_read < 0) + { sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r++); + bfs_dsk_read = open(dsk_nm, RFLAGS); + if (bfs_dsk_read < 0) + { Uerror("could not open temp disk file"); + } } + if (read(bfs_dsk_read, (char *) &now, vsize) != vsize) + { Uerror("bad bfs disk file read"); + } +#ifndef NOVSZ + if (now._vsz != vsize) + { Uerror("disk read vsz mismatch"); + } +#endif + } else +#endif + memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize); + memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize); + if (now._nr_pr > 0) +#if VECTORSZ>32000 + { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int)); +#else + { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short)); +#endif + memcpy((char *)proc_skip, (char *)t->omask->ps, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) +#if VECTORSZ>32000 + { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int)); +#else + { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short)); +#endif + memcpy((uchar *)q_skip, (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar)); + } +#ifdef BFS_DISK + if (t->onow->sv != (State *) 0) +#endif + freesv(t->onow); /* omask not freed */ +#ifdef CHECK + printf("POP %u (%d)\n", t->frame, t->frame->o_tt); +#endif + return t->frame; +} + +void +store_state(Trail *ntrpt, int shortcut, short oboq) +{ +#ifdef VERI + Trans *t2 = (Trans *) 0; + uchar ot; int tt, E_state; + uchar o_opm = trpt->o_pm, *othis = this; + + if (shortcut) + { +#ifdef VERBOSE + printf("claim: shortcut\n"); +#endif + goto store_it; /* no claim move */ + } + + this = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */ + trpt->o_pm = 0; + + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + +#ifdef HAS_UNLESS + E_state = 0; +#endif + for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0) + { +#ifdef HAS_UNLESS + if (E_state > 0 + && E_state != t2->e_trans) + break; +#endif + if (do_transit(t2, 0)) + { +#ifdef VERBOSE + if (!reached[ot][t2->st]) + printf("depth: %d -- claim move from %d -> %d\n", + trpt->o_tt, ((P0 *)this)->_p, t2->st); +#endif +#ifdef HAS_UNLESS + E_state = t2->e_trans; +#endif + if (t2->st > 0) + { ((P0 *)this)->_p = t2->st; + reached[ot][t2->st] = 1; +#ifndef NOCLAIM + check_claim(t2->st); +#endif + } + if (now._nr_pr == 0) /* claim terminated */ + uerror("end state in claim reached"); + +#ifdef PEG + peg[t2->forw]++; +#endif + trpt->o_pm |= 1; + if (t2->atom&2) + Uerror("atomic in claim not supported in BFS mode"); +store_it: + +#endif + +#ifdef BITSTATE + if (!bstore((char *)&now, vsize)) +#else +#ifdef MA + if (!gstore((char *)&now, vsize, 0)) +#else + if (!hstore((char *)&now, vsize)) +#endif +#endif + { static long sdone = (long) 0; long ndone; + nstates++; +#ifndef NOREDUCE + trpt->tau |= 64; +#endif + ndone = (unsigned long) (nstates/((double) FREQ)); + if (ndone != sdone && mreached%10 != 0) + { snapshot(); + sdone = ndone; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) + if (nstates > ((double)(1<<(ssize+1)))) + { void resize_hashtable(void); + resize_hashtable(); + } +#endif + } +#if SYNC + if (boq != -1) + midrv++; + else if (oboq != -1) + { Trail *x; + x = (Trail *) trpt->ostate; /* pre-rv state */ + if (x) x->o_pm |= 4; /* mark success */ + } +#endif + push_bfs(ntrpt, trpt->o_tt+1); + } else + { truncs++; +#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO) +#if !defined(BITSTATE) + if (Lstate && Lstate->tagged) trpt->tau |= 64; +#else + if (trpt->tau&32) + { BFS_Trail *tprov; + for (tprov = bfs_trail; tprov; tprov = tprov->nxt) + if (tprov->onow->sv != (State *) 0 + && memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize) == 0) + { trpt->tau |= 64; + break; /* state is in queue */ + } } +#endif +#endif + } +#ifdef VERI + ((P0 *)this)->_p = tt; /* reset claim */ + if (t2) + do_reverse(t2, 0, 0); + else + break; + } } + this = othis; + trpt->o_pm = o_opm; +#endif +} + +Trail *ntrpt; + +void +bfs(void) +{ Trans *t; Trail *otrpt, *x; + uchar _n, _m, ot, nps = 0; + int tt, E_state; + short II, From = (short) (now._nr_pr-1), To = BASE; + short oboq = boq; + + ntrpt = (Trail *) emalloc(sizeof(Trail)); + trpt->ostate = (struct H_el *) 0; + trpt->tau = 0; + + trpt->o_tt = -1; + store_state(ntrpt, 0, oboq); /* initial state */ + + while ((otrpt = pop_bfs())) /* also restores now */ + { memcpy((char *) trpt, (char *) otrpt, sizeof(Trail)); +#if defined(C_States) && (HAS_TRACK==1) + c_revert((uchar *) &(now.c_state[0])); +#endif + if (trpt->o_pm & 4) + { +#ifdef VERBOSE + printf("Revisit of atomic not needed (%d)\n", + trpt->o_pm); +#endif + continue; + } +#ifndef NOREDUCE + nps = 0; +#endif + if (trpt->o_pm == 8) + { revrv++; + if (trpt->tau&8) + { +#ifdef VERBOSE + printf("Break atomic (pm:%d,tau:%d)\n", + trpt->o_pm, trpt->tau); +#endif + trpt->tau &= ~8; + } +#ifndef NOREDUCE + else if (trpt->tau&32) + { +#ifdef VERBOSE + printf("Void preselection (pm:%d,tau:%d)\n", + trpt->o_pm, trpt->tau); +#endif + trpt->tau &= ~32; + nps = 1; /* no preselection in repeat */ + } +#endif + } + trpt->o_pm &= ~(4|8); + if (trpt->o_tt > mreached) + { mreached = trpt->o_tt; + if (mreached%10 == 0) + { snapshot(); + } } + depth = trpt->o_tt; + if (depth >= maxdepth) + { +#if SYNC + Trail *x; + if (boq != -1) + { x = (Trail *) trpt->ostate; + if (x) x->o_pm |= 4; /* not failing */ + } +#endif + truncs++; + if (!warned) + { warned = 1; + printf("error: max search depth too small\n"); + } + if (bounded) + uerror("depth limit reached"); + continue; + } +#ifndef NOREDUCE + if (boq == -1 && !(trpt->tau&8) && nps == 0) + for (II = now._nr_pr-1; II >= BASE; II -= 1) + { +Pickup: this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + if (trans[ot][tt]->atom & 8) + { t = trans[ot][tt]; + if (t->qu[0] != 0) + { Ccheck++; + if (!q_cond(II, t)) + continue; + Cholds++; + } + From = To = II; + trpt->tau |= 32; /* preselect marker */ +#ifdef DEBUG + printf("%3d: proc %d PreSelected (tau=%d)\n", + depth, II, trpt->tau); +#endif + goto MainLoop; + } } + trpt->tau &= ~32; +#endif +Repeat: + if (trpt->tau&8) /* atomic */ + { From = To = (short ) trpt->pr; + nlinks++; + } else + { From = now._nr_pr-1; + To = BASE; + } +MainLoop: + _n = _m = 0; + for (II = From; II >= To; II -= 1) + { + this = (((uchar *)&now)+proc_offset[II]); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; +#if SYNC + /* no rendezvous with same proc */ + if (boq != -1 && trpt->pr == II) continue; +#endif + ntrpt->pr = (uchar) II; + ntrpt->st = tt; + trpt->o_pm &= ~1; /* no move yet */ +#ifdef EVENT_TRACE + trpt->o_event = now._event; +#endif +#ifdef HAS_PROVIDED + if (!provided(II, ot, tt, t)) continue; +#endif +#ifdef HAS_UNLESS + E_state = 0; +#endif + for (t = trans[ot][tt]; t; t = t->nxt) + { +#ifdef HAS_UNLESS + if (E_state > 0 + && E_state != t->e_trans) + break; +#endif + ntrpt->o_t = t; + + oboq = boq; + + if (!(_m = do_transit(t, II))) + continue; + + trpt->o_pm |= 1; /* we moved */ + (trpt+1)->o_m = _m; /* for unsend */ +#ifdef PEG + peg[t->forw]++; +#endif +#ifdef CHECK + printf("%3d: proc %d exec %d, ", + depth, II, t->forw); + printf("%d to %d, %s %s %s", + tt, t->st, t->tp, + (t->atom&2)?"atomic":"", + (boq != -1)?"rendez-vous":""); +#ifdef HAS_UNLESS + if (t->e_trans) + printf(" (escapes to state %d)", t->st); +#endif + printf(" %saccepting [tau=%d]\n", + (trpt->o_pm&2)?"":"non-", trpt->tau); +#endif +#ifdef HAS_UNLESS + E_state = t->e_trans; +#if SYNC>0 + if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */)) + { fprintf(efd, "error: the use of rendezvous stmnt in the escape clause\n"); + fprintf(efd, " of an unless stmnt is not compatible with -DBFS\n"); + pan_exit(1); + } +#endif +#endif + if (t->st > 0) ((P0 *)this)->_p = t->st; + + /* ptr to pred: */ ntrpt->ostate = (struct H_el *) otrpt; + ntrpt->st = tt; + if (boq == -1 && (t->atom&2)) /* atomic */ + ntrpt->tau = 8; /* record for next move */ + else + ntrpt->tau = 0; + + store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq); +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif + + /* undo move and continue */ + trpt++; /* this is where ovals and ipt are set */ + do_reverse(t, II, _m); /* restore now. */ + trpt--; +#ifdef CHECK + #if NCORE>1 + enter_critical(GLOBAL_LOCK); /* in verbose mode only */ + printf("cpu%d: ", core_id); + #endif + printf("%3d: proc %d ", depth, II); + printf("reverses %d, %d to %d,", + t->forw, tt, t->st); + printf(" %s [abit=%d,adepth=%d,", + t->tp, now._a_t, A_depth); + printf("tau=%d,%d]\n", + trpt->tau, (trpt-1)->tau); + #if NCORE>1 + leave_critical(GLOBAL_LOCK); + #endif +#endif + reached[ot][t->st] = 1; + reached[ot][tt] = 1; + + ((P0 *)this)->_p = tt; + _n |= _m; + } } +#ifndef NOREDUCE + /* preselected - no succ definitely outside stack */ + if ((trpt->tau&32) && !(trpt->tau&64)) + { From = now._nr_pr-1; To = BASE; +#ifdef DEBUG + cpu_printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, (int) _n, trpt->tau); +#endif + _n = 0; trpt->tau &= ~32; + if (II >= BASE) + goto Pickup; + goto MainLoop; + } + trpt->tau &= ~(32|64); +#endif + if (_n != 0) + continue; +#ifdef DEBUG + printf("%3d: no move [II=%d, tau=%d, boq=%d, _nr_pr=%d]\n", + depth, II, trpt->tau, boq, now._nr_pr); +#endif + if (boq != -1) + { failedrv++; + x = (Trail *) trpt->ostate; /* pre-rv state */ + if (!x) continue; /* root state */ + if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */ + { x->o_pm |= 8; /* mark failure */ + this = (((uchar *)&now)+proc_offset[otrpt->pr]); +#ifdef VERBOSE + printf("\treset state of %d from %d to %d\n", + otrpt->pr, ((P0 *)this)->_p, otrpt->st); +#endif + ((P0 *)this)->_p = otrpt->st; + unsend(boq); /* retract rv offer */ + boq = -1; + push_bfs(x, x->o_tt); +#ifdef VERBOSE + printf("failed rv, repush with %d\n", x->o_pm); +#endif + } +#ifdef VERBOSE + else printf("failed rv, tau at parent: %d\n", x->tau); +#endif + } else if (now._nr_pr > 0) + { + if ((trpt->tau&8)) /* atomic */ + { trpt->tau &= ~(1|8); /* 1=timeout, 8=atomic */ +#ifdef DEBUG + printf("%3d: atomic step proc %d blocks\n", + depth, II+1); +#endif + goto Repeat; + } + + if (!(trpt->tau&1)) /* didn't try timeout yet */ + { trpt->tau |= 1; +#ifdef DEBUG + printf("%d: timeout\n", depth); +#endif + goto MainLoop; + } +#ifndef VERI + if (!noends && !a_cycles && !endstate()) + uerror("invalid end state"); +#endif + } } +} + +void +putter(Trail *trpt, int fd) +{ long j; + + if (!trpt) return; + + if (trpt != (Trail *) trpt->ostate) + putter((Trail *) trpt->ostate, fd); + + if (trpt->o_t) + { sprintf(snap, "%d:%d:%d\n", + trcnt++, trpt->pr, trpt->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing %s\n", fnm); + pan_exit(1); + } } +} + +void +nuerror(char *str) +{ int fd = make_trail(); + int j; + + if (fd < 0) return; +#ifdef VERI + sprintf(snap, "-2:%d:-2\n", VERI); + write(fd, snap, strlen(snap)); +#endif +#ifdef MERGED + sprintf(snap, "-4:-4:-4\n"); + write(fd, snap, strlen(snap)); +#endif + trcnt = 1; + putter(trpt, fd); + if (ntrpt->o_t) + { sprintf(snap, "%d:%d:%d\n", + trcnt++, ntrpt->pr, ntrpt->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing %s\n", fnm); + pan_exit(1); + } } + close(fd); + if (errors >= upto && upto != 0) + { wrapup(); + } +} +#endif +#if NCORE>1 +#if defined(WIN32) || defined(WIN64) +#ifndef _CONSOLE + #define _CONSOLE +#endif + #ifdef WIN64 +#undef long + #endif +#include + + #ifdef WIN64 + #define long long long + #endif +#else +#include +#include +#include +#endif + +/* code common to cygwin/linux and win32/win64: */ + +#ifdef VERBOSE + #define VVERBOSE (1) +#else + #define VVERBOSE (0) +#endif + +/* the following values must be larger than 256 and must fit in an int */ +#define QUIT 1024 /* terminate now command */ +#define QUERY 512 /* termination status query message */ +#define QUERY_F 513 /* query failed, cannot quit */ + +#define GN_FRAMES (int) (GWQ_SIZE / (double) sizeof(SM_frame)) +#define LN_FRAMES (int) (LWQ_SIZE / (double) sizeof(SM_frame)) + +#ifndef VMAX + #define VMAX VECTORSZ +#endif +#ifndef PMAX + #define PMAX 64 +#endif +#ifndef QMAX + #define QMAX 64 +#endif + +#if VECTORSZ>32000 + #define OFFT int +#else + #define OFFT short +#endif + +#ifdef SET_SEG_SIZE + /* no longer usefule -- being recomputed for local heap size anyway */ + double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.); +#else + double SEG_SIZE = (1048576.*1024.); /* 1GB default shared memory pool segments */ +#endif + +double LWQ_SIZE = 0.; /* initialized in main */ + +#ifdef SET_WQ_SIZE + #ifdef NGQ + #warning SET_WQ_SIZE applies to global queue -- ignored + double GWQ_SIZE = 0.; + #else + double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.); + /* must match the value in pan_proxy.c, if used */ + #endif +#else + #ifdef NGQ + double GWQ_SIZE = 0.; + #else + double GWQ_SIZE = (128.*1048576.); /* 128 MB default queue sizes */ + #endif +#endif + +/* Crash Detection Parameters */ +#ifndef ONESECOND + #define ONESECOND (1<<25) +#endif +#ifndef SHORT_T + #define SHORT_T (0.1) +#endif +#ifndef LONG_T + #define LONG_T (600) +#endif + +double OneSecond = (double) (ONESECOND); /* waiting for a free slot -- checks crash */ +double TenSeconds = 10. * (ONESECOND); /* waiting for a lock -- check for a crash */ + +/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */ +double Delay = ((double) SHORT_T) * (ONESECOND); /* termination detection trigger */ +double OneHour = ((double) LONG_T) * (ONESECOND); /* timeout termination detection */ + +typedef struct SM_frame SM_frame; +typedef struct SM_results SM_results; +typedef struct sh_Allocater sh_Allocater; + +struct SM_frame { /* about 6K per slot */ + volatile int m_vsize; /* 0 means free slot */ + volatile int m_boq; /* >500 is a control message */ +#ifdef FULL_TRAIL + volatile struct Stack_Tree *m_stack; /* ptr to previous state */ +#endif + volatile uchar m_tau; + volatile uchar m_o_pm; + volatile int nr_handoffs; /* to compute real_depth */ + volatile char m_now [VMAX]; + volatile char m_Mask [(VMAX + 7)/8]; + volatile OFFT m_p_offset[PMAX]; + volatile OFFT m_q_offset[QMAX]; + volatile uchar m_p_skip [PMAX]; + volatile uchar m_q_skip [QMAX]; +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + volatile uchar m_c_stack [StackSize]; +#endif +}; + +int proxy_pid; /* id of proxy if nonzero -- receive half */ +int store_proxy_pid; +short remote_party; +int proxy_pid_snd; /* id of proxy if nonzero -- send half */ +char o_cmdline[512]; /* to pass options to children */ + +int iamin[CS_NR+NCORE]; /* non-shared */ + +#if defined(WIN32) || defined(WIN64) +int tas(volatile LONG *); + +HANDLE proxy_handle_snd; /* for Windows Create and Terminate */ + +struct sh_Allocater { /* shared memory for states */ + volatile char *dc_arena; /* to allocate states from */ + volatile long pattern; /* to detect overruns */ + volatile long dc_size; /* nr of bytes left */ + volatile void *dc_start; /* where memory segment starts */ + volatile void *dc_id; /* to attach, detach, remove shared memory segments */ + volatile sh_Allocater *nxt; /* linked list of pools */ +}; +DWORD worker_pids[NCORE]; /* root mem of pids of all workers created */ +HANDLE worker_handles[NCORE]; /* for windows Create and Terminate */ +void * shmid [NR_QS]; /* return value from CreateFileMapping */ +void * shmid_M; /* shared mem for state allocation in hashtable */ + +#ifdef SEP_STATE + void *shmid_X; +#else + void *shmid_S; /* shared bitstate arena or hashtable */ +#endif +#else +int tas(volatile int *); + +struct sh_Allocater { /* shared memory for states */ + volatile char *dc_arena; /* to allocate states from */ + volatile long pattern; /* to detect overruns */ + volatile long dc_size; /* nr of bytes left */ + volatile char *dc_start; /* where memory segment starts */ + volatile int dc_id; /* to attach, detach, remove shared memory segments */ + volatile sh_Allocater *nxt; /* linked list of pools */ +}; + +int worker_pids[NCORE]; /* root mem of pids of all workers created */ +int shmid [NR_QS]; /* return value from shmget */ +int nibis = 0; /* set after shared mem has been released */ +int shmid_M; /* shared mem for state allocation in hashtable */ +#ifdef SEP_STATE + long shmid_X; +#else + int shmid_S; /* shared bitstate arena or hashtable */ + volatile sh_Allocater *first_pool; /* of shared state memory */ + volatile sh_Allocater *last_pool; +#endif +#endif + +struct SM_results { /* for shuttling back final stats */ + volatile int m_vsize; /* avoid conflicts with frames */ + volatile int m_boq; /* these 2 fields are not written in record_info */ + /* probably not all fields really need to be volatile */ + volatile double m_memcnt; + volatile double m_nstates; + volatile double m_truncs; + volatile double m_truncs2; + volatile double m_nShadow; + volatile double m_nlinks; + volatile double m_ngrabs; + volatile double m_nlost; + volatile double m_hcmp; + volatile double m_frame_wait; + volatile int m_hmax; + volatile int m_svmax; + volatile int m_smax; + volatile int m_mreached; + volatile int m_errors; + volatile int m_VMAX; + volatile short m_PMAX; + volatile short m_QMAX; + volatile uchar m_R; /* reached info for all proctypes */ +}; + +int core_id = 0; /* internal process nr, to know which q to use */ +unsigned long nstates_put = 0; /* statistics */ +unsigned long nstates_get = 0; +int query_in_progress = 0; /* termination detection */ + +double free_wait = 0.; /* waiting for a free frame */ +double frame_wait = 0.; /* waiting for a full frame */ +double lock_wait = 0.; /* waiting for access to cs */ +double glock_wait[3]; /* waiting for access to global lock */ + +char *sprefix = "rst"; +uchar was_interrupted, issued_kill, writing_trail; + +static SM_frame cur_Root; /* current root, to be safe with error trails */ + +SM_frame *m_workq [NR_QS]; /* per cpu work queues + global q */ +char *shared_mem[NR_QS]; /* return value from shmat */ +#ifdef SEP_HEAP +char *my_heap; +long my_size; +#endif +volatile sh_Allocater *dc_shared; /* assigned at initialization */ + +static int vmax_seen, pmax_seen, qmax_seen; +static double gq_tries, gq_hasroom, gq_hasnoroom; + +volatile int *prfree; +volatile int *prfull; +volatile int *prcnt; +volatile int *prmax; + +volatile int *sh_lock; /* mutual exclusion locks - in shared memory */ +volatile double *is_alive; /* to detect when processes crash */ +volatile int *grfree, *grfull, *grcnt, *grmax; /* access to shared global q */ +volatile double *gr_readmiss, *gr_writemiss; +static int lrfree; /* used for temporary recording of slot */ +static int dfs_phase2; + +void mem_put(int); /* handoff state to other cpu */ +void mem_put_acc(void); /* liveness mode */ +void mem_get(void); /* get state from work queue */ +void sudden_stop(char *); +#if 0 +void enter_critical(int); +void leave_critical(int); +#endif + +void +record_info(SM_results *r) +{ int i; + uchar *ptr; + +#ifdef SEP_STATE + if (0) + { cpu_printf("nstates %g nshadow %g -- memory %-6.3f Mb\n", + nstates, nShadow, memcnt/(1048576.)); + } + r->m_memcnt = 0; +#else + #ifdef BITSTATE + r->m_memcnt = 0; /* it's shared */ + #endif + r->m_memcnt = memcnt; +#endif + if (a_cycles && core_id == 1) + { r->m_nstates = nstates; + r->m_nShadow = nstates; + } else + { r->m_nstates = nstates; + r->m_nShadow = nShadow; + } + r->m_truncs = truncs; + r->m_truncs2 = truncs2; + r->m_nlinks = nlinks; + r->m_ngrabs = ngrabs; + r->m_nlost = nlost; + r->m_hcmp = hcmp; + r->m_frame_wait = frame_wait; + r->m_hmax = hmax; + r->m_svmax = svmax; + r->m_smax = smax; + r->m_mreached = mreached; + r->m_errors = errors; + r->m_VMAX = vmax_seen; + r->m_PMAX = (short) pmax_seen; + r->m_QMAX = (short) qmax_seen; + ptr = (uchar *) &(r->m_R); + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar)); + ptr += NrStates[i]*sizeof(uchar); + } + if (verbose>1) + { cpu_printf("Put Results nstates %g (sz %d)\n", nstates, ptr - &(r->m_R)); + } +} + +void snapshot(void); + +void +retrieve_info(SM_results *r) +{ int i, j; + volatile uchar *ptr; + + snapshot(); /* for a final report */ + + enter_critical(GLOBAL_LOCK); +#ifdef SEP_HEAP + if (verbose) + { printf("cpu%d: local heap-left %ld KB (%d MB)\n", + core_id, (int) (my_size/1024), (int) (my_size/1048576)); + } +#endif + if (verbose && core_id == 0) + { printf("qmax: "); + for (i = 0; i < NCORE; i++) + { printf("%d ", prmax[i]); + } +#ifndef NGQ + printf("G: %d", *grmax); +#endif + printf("\n"); + } + leave_critical(GLOBAL_LOCK); + + memcnt += r->m_memcnt; + nstates += r->m_nstates; + nShadow += r->m_nShadow; + truncs += r->m_truncs; + truncs2 += r->m_truncs2; + nlinks += r->m_nlinks; + ngrabs += r->m_ngrabs; + nlost += r->m_nlost; + hcmp += r->m_hcmp; + /* frame_wait += r->m_frame_wait; */ + errors += r->m_errors; + + if (hmax < r->m_hmax) hmax = r->m_hmax; + if (svmax < r->m_svmax) svmax = r->m_svmax; + if (smax < r->m_smax) smax = r->m_smax; + if (mreached < r->m_mreached) mreached = r->m_mreached; + + if (vmax_seen < r->m_VMAX) vmax_seen = r->m_VMAX; + if (pmax_seen < (int) r->m_PMAX) pmax_seen = (int) r->m_PMAX; + if (qmax_seen < (int) r->m_QMAX) qmax_seen = (int) r->m_QMAX; + + ptr = &(r->m_R); + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { for (j = 0; j < NrStates[i]; j++) + { if (*(ptr + j) != 0) + { reached[i][j] = 1; + } } + ptr += NrStates[i]*sizeof(uchar); + } + if (verbose>1) + { cpu_printf("Got Results (%d)\n", ptr - &(r->m_R)); + snapshot(); + } +} + +#if !defined(WIN32) && !defined(WIN64) +static void +rm_shared_segments(void) +{ int m; + volatile sh_Allocater *nxt_pool; + /* + * mark all shared memory segments for removal + * the actual removes wont happen intil last process dies or detaches + * the shmctl calls can return -1 if not all procs have detached yet + */ + for (m = 0; m < NR_QS; m++) /* +1 for global q */ + { if (shmid[m] != -1) + { (void) shmctl(shmid[m], IPC_RMID, NULL); + } } +#ifdef SEP_STATE + if (shmid_M != -1) + { (void) shmctl(shmid_M, IPC_RMID, NULL); + } +#else + if (shmid_S != -1) + { (void) shmctl(shmid_S, IPC_RMID, NULL); + } + for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool) + { shmid_M = (int) (last_pool->dc_id); + nxt_pool = last_pool->nxt; /* as a pre-caution only */ + if (shmid_M != -1) + { (void) shmctl(shmid_M, IPC_RMID, NULL); + } } +#endif +} +#endif + +void +sudden_stop(char *s) +{ char b[64]; + int i; + + printf("cpu%d: stop - %s\n", core_id, s); +#if !defined(WIN32) && !defined(WIN64) + if (proxy_pid != 0) + { rm_shared_segments(); + } +#endif + if (search_terminated != NULL) + { if (*search_terminated != 0) + { if (verbose) + { printf("cpu%d: termination initiated (%d)\n", + core_id, *search_terminated); + } + } else + { if (verbose) + { printf("cpu%d: initiated termination\n", core_id); + } + *search_terminated |= 8; /* sudden_stop */ + } + if (core_id == 0) + { if (((*search_terminated) & 4) /* uerror in one of the cpus */ + && !((*search_terminated) & (8|32|128|256))) /* abnormal stop */ + { if (errors == 0) errors++; /* we know there is at least 1 */ + } + wrapup(); /* incomplete stats, but at least something */ + } + return; + } /* else: should rarely happen, take more drastic measures */ + + if (core_id == 0) /* local root process */ + { for (i = 1; i < NCORE; i++) /* not for 0 of course */ + { +#if defined(WIN32) || defined(WIN64) + DWORD dwExitCode = 0; + GetExitCodeProcess(worker_handles[i], &dwExitCode); + if (dwExitCode == STILL_ACTIVE) + { TerminateProcess(worker_handles[i], 0); + } + printf("cpu0: terminate %d %d\n", + worker_pids[i], (dwExitCode == STILL_ACTIVE)); +#else + sprintf(b, "kill -%d %d", SIGKILL, worker_pids[i]); + system(b); /* if this is a proxy: receive half */ + printf("cpu0: %s\n", b); +#endif + } + issued_kill++; + } else + { /* on WIN32/WIN64 -- these merely kills the root process... */ + if (was_interrupted == 0) + { sprintf(b, "kill -%d %d", SIGINT, worker_pids[0]); + system(b); /* warn the root process */ + printf("cpu%d: %s\n", core_id, b); + issued_kill++; + } } +} + +#define iam_alive() is_alive[core_id]++ + +extern int crash_test(double); +extern void crash_reset(void); + +int +someone_crashed(int wait_type) +{ static double last_value = 0.0; + static int count = 0; + + if (search_terminated == NULL + || *search_terminated != 0) + { + if (!(*search_terminated & (8|32|128|256))) + { if (count++ < 100*NCORE) + { return 0; + } } + return 1; + } + /* check left neighbor only */ + if (last_value == is_alive[(core_id + NCORE - 1) % NCORE]) + { if (count++ >= 100) /* to avoid unnecessary checks */ + { return 1; + } + return 0; + } + last_value = is_alive[(core_id + NCORE - 1) % NCORE]; + count = 0; + crash_reset(); + return 0; +} + +void +sleep_report(void) +{ + enter_critical(GLOBAL_LOCK); + if (verbose) + { +#ifdef NGQ + printf("cpu%d: locks: global %g\tother %g\t", + core_id, glock_wait[0], lock_wait - glock_wait[0]); +#else + printf("cpu%d: locks: GL %g, RQ %g, WQ %g, HT %g\t", + core_id, glock_wait[0], glock_wait[1], glock_wait[2], + lock_wait - glock_wait[0] - glock_wait[1] - glock_wait[2]); +#endif + printf("waits: states %g slots %g\n", frame_wait, free_wait); +#ifndef NGQ + printf("cpu%d: gq [tries %g, room %g, noroom %g]\n", core_id, gq_tries, gq_hasroom, gq_hasnoroom); + if (core_id == 0 && (*gr_readmiss >= 1.0 || *gr_readmiss >= 1.0 || *grcnt != 0)) + printf("cpu0: gq [readmiss: %g, writemiss: %g cnt %d]\n", *gr_readmiss, *gr_writemiss, *grcnt); +#endif + } + if (free_wait > 1000000.) + #ifndef NGQ + if (!a_cycles) + { printf("hint: this search may be faster with a larger work-queue\n"); + printf(" (-DSET_WQ_SIZE=N with N>%g), and/or with -DUSE_DISK\n", + GWQ_SIZE/sizeof(SM_frame)); + printf(" or with a larger value for -zN (N>%d)\n", z_handoff); + #else + { printf("hint: this search may be faster if compiled without -DNGQ, with -DUSE_DISK, "); + printf("or with a larger -zN (N>%d)\n", z_handoff); + #endif + } + leave_critical(GLOBAL_LOCK); +} + +#ifndef MAX_DSK_FILE + #define MAX_DSK_FILE 1000000 /* default is max 1M states per file */ +#endif + +void +multi_usage(FILE *fd) +{ static int warned = 0; + if (warned > 0) { return; } else { warned++; } + fprintf(fd, "\n"); + fprintf(fd, "Defining multi-core mode:\n\n"); + fprintf(fd, " -DDUAL_CORE --> same as -DNCORE=2\n"); + fprintf(fd, " -DQUAD_CORE --> same as -DNCORE=4\n"); + fprintf(fd, " -DNCORE=N --> enables multi_core verification if N>1\n"); + fprintf(fd, "\n"); + fprintf(fd, "Additional directives supported in multi-core mode:\n\n"); + fprintf(fd, " -DSEP_STATE --> forces separate statespaces instead of a single shared state space\n"); + fprintf(fd, " -DNUSE_DISK --> use disk for storing states when a work queue overflows\n"); + fprintf(fd, " -DMAX_DSK_FILE --> max nr of states per diskfile (%d)\n", MAX_DSK_FILE); + fprintf(fd, " -DFULL_TRAIL --> support full error trails (increases memory use)\n"); + fprintf(fd, "\n"); + fprintf(fd, "More advanced use (should rarely need changing):\n\n"); + fprintf(fd, " To change the nr of states that can be stored in the global queue\n"); + fprintf(fd, " (lower numbers allow for more states to be stored, prefer multiples of 8):\n"); + fprintf(fd, " -DVMAX=N --> upperbound on statevector for handoffs (N=%d)\n", VMAX); + fprintf(fd, " -DPMAX=N --> upperbound on nr of procs (default: N=%d)\n", PMAX); + fprintf(fd, " -DQMAX=N --> upperbound on nr of channels (default: N=%d)\n", QMAX); + fprintf(fd, "\n"); + fprintf(fd, " To set the total amount of memory reserved for the global workqueue:\n"); + fprintf(fd, " -DSET_WQ_SIZE=N --> default: N=128 (defined in MBytes)\n\n"); + fprintf(fd, " To force the use of a single global heap, instead of separate heaps:\n"); + fprintf(fd, " -DGLOB_HEAP\n"); + fprintf(fd, "\n"); + fprintf(fd, " To define a fct to initialize data before spawning processes (use quotes):\n"); + fprintf(fd, " \"-DC_INIT=fct()\"\n"); + fprintf(fd, "\n"); + fprintf(fd, " Timer settings for termination and crash detection:\n"); + fprintf(fd, " -DSHORT_T=N --> timeout for termination detection trigger (N=%g)\n", (double) SHORT_T); + fprintf(fd, " -DLONG_T=N --> timeout for giving up on termination detection (N=%g)\n", (double) LONG_T); + fprintf(fd, " -DONESECOND --> (1<<29) --> timeout waiting for a free slot -- to check for crash\n"); + fprintf(fd, " -DT_ALERT --> collect stats on crash alert timeouts\n\n"); + fprintf(fd, "Help with Linux/Windows/Cygwin configuration for multi-core:\n"); + fprintf(fd, " http://spinroot.com/spin/multicore/V5_Readme.html\n"); + fprintf(fd, "\n"); +} +#if NCORE>1 && defined(FULL_TRAIL) +typedef struct Stack_Tree { + uchar pr; /* process that made transition */ + T_ID t_id; /* id of transition */ + volatile struct Stack_Tree *prv; /* backward link towards root */ +} Stack_Tree; + +struct H_el *grab_shared(int); +volatile Stack_Tree **stack_last; /* in shared memory */ +char *stack_cache = NULL; /* local */ +int nr_cached = 0; /* local */ + +#ifndef CACHE_NR + #define CACHE_NR 1024 +#endif + +volatile Stack_Tree * +stack_prefetch(void) +{ volatile Stack_Tree *st; + + if (nr_cached == 0) + { stack_cache = (char *) grab_shared(CACHE_NR * sizeof(Stack_Tree)); + nr_cached = CACHE_NR; + } + st = (volatile Stack_Tree *) stack_cache; + stack_cache += sizeof(Stack_Tree); + nr_cached--; + return st; +} + +void +Push_Stack_Tree(short II, T_ID t_id) +{ volatile Stack_Tree *st; + + st = (volatile Stack_Tree *) stack_prefetch(); + st->pr = II; + st->t_id = t_id; + st->prv = (Stack_Tree *) stack_last[core_id]; + stack_last[core_id] = st; +} + +void +Pop_Stack_Tree(void) +{ volatile Stack_Tree *cf = stack_last[core_id]; + + if (cf) + { stack_last[core_id] = cf->prv; + } else if (nr_handoffs * z_handoff + depth > 0) + { printf("cpu%d: error pop_stack_tree (depth %d)\n", + core_id, depth); + } +} +#endif + +void +e_critical(int which) +{ double cnt_start; + + if (readtrail || iamin[which] > 0) + { if (!readtrail && verbose) + { printf("cpu%d: Double Lock on %d (now %d)\n", + core_id, which, iamin[which]+1); + fflush(stdout); + } + iamin[which]++; /* local variable */ + return; + } + + cnt_start = lock_wait; + + while (sh_lock != NULL) /* as long as we have shared memory */ + { int r = tas(&sh_lock[which]); + if (r == 0) + { iamin[which] = 1; + return; /* locked */ + } + + lock_wait++; +#ifndef NGQ + if (which < 3) { glock_wait[which]++; } +#else + if (which == 0) { glock_wait[which]++; } +#endif + iam_alive(); + + if (lock_wait - cnt_start > TenSeconds) + { printf("cpu%d: lock timeout on %d\n", core_id, which); + cnt_start = lock_wait; + if (someone_crashed(1)) + { sudden_stop("lock timeout"); + pan_exit(1); + } } } +} + +void +x_critical(int which) +{ + if (iamin[which] != 1) + { if (iamin[which] > 1) + { iamin[which]--; /* this is thread-local - no races on this one */ + if (!readtrail && verbose) + { printf("cpu%d: Partial Unlock on %d (%d more needed)\n", + core_id, which, iamin[which]); + fflush(stdout); + } + return; + } else /* iamin[which] <= 0 */ + { if (!readtrail) + { printf("cpu%d: Invalid Unlock iamin[%d] = %d\n", + core_id, which, iamin[which]); + fflush(stdout); + } + return; + } } + + if (sh_lock != NULL) + { iamin[which] = 0; + sh_lock[which] = 0; /* unlock */ + } +} + +void +#if defined(WIN32) || defined(WIN64) +start_proxy(char *s, DWORD r_pid) +#else +start_proxy(char *s, int r_pid) +#endif +{ char Q_arg[16], Z_arg[16], Y_arg[16]; + char *args[32], *ptr; + int argcnt = 0; + + sprintf(Q_arg, "-Q%d", getpid()); + sprintf(Y_arg, "-Y%d", r_pid); + sprintf(Z_arg, "-Z%d", proxy_pid /* core_id */); + + args[argcnt++] = "proxy"; + args[argcnt++] = s; /* -r or -s */ + args[argcnt++] = Q_arg; + args[argcnt++] = Z_arg; + args[argcnt++] = Y_arg; + + if (strlen(o_cmdline) > 0) + { ptr = o_cmdline; /* assume args separated by spaces */ + do { args[argcnt++] = ptr++; + if ((ptr = strchr(ptr, ' ')) != NULL) + { while (*ptr == ' ') + { *ptr++ = '\0'; + } + } else + { break; + } + } while (argcnt < 31); + } + args[argcnt] = NULL; +#if defined(WIN32) || defined(WIN64) + execvp("pan_proxy", args); /* no return */ +#else + execvp("./pan_proxy", args); /* no return */ +#endif + Uerror("pan_proxy exec failed"); +} +/*** end of common code fragment ***/ + +#if !defined(WIN32) && !defined(WIN64) +void +init_shm(void) /* initialize shared work-queues - linux/cygwin */ +{ key_t key[NR_QS]; + int n, m; + int must_exit = 0; + + if (core_id == 0 && verbose) + { printf("cpu0: step 3: allocate shared workqueues %g MB\n", + ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.) ); + } + for (m = 0; m < NR_QS; m++) /* last q is the global q */ + { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE; + key[m] = ftok(PanSource, m+1); + if (key[m] == -1) + { perror("ftok shared queues"); must_exit = 1; break; + } + + if (core_id == 0) /* root creates */ + { /* check for stale copy */ + shmid[m] = shmget(key[m], (size_t) qsize, 0600); + if (shmid[m] != -1) /* yes there is one; remove it */ + { printf("cpu0: removing stale q%d, status: %d\n", + m, shmctl(shmid[m], IPC_RMID, NULL)); + } + shmid[m] = shmget(key[m], (size_t) qsize, 0600|IPC_CREAT|IPC_EXCL); + memcnt += qsize; + } else /* workers attach */ + { shmid[m] = shmget(key[m], (size_t) qsize, 0600); + /* never called, since we create shm *before* we fork */ + } + if (shmid[m] == -1) + { perror("shmget shared queues"); must_exit = 1; break; + } + + shared_mem[m] = (char *) shmat(shmid[m], (void *) 0, 0); /* attach */ + if (shared_mem[m] == (char *) -1) + { fprintf(stderr, "error: cannot attach shared wq %d (%d Mb)\n", + m+1, (int) (qsize/(1048576.))); + perror("shmat shared queues"); must_exit = 1; break; + } + + m_workq[m] = (SM_frame *) shared_mem[m]; + if (core_id == 0) + { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES; + for (n = 0; n < nframes; n++) + { m_workq[m][n].m_vsize = 0; + m_workq[m][n].m_boq = 0; + } } } + + if (must_exit) + { rm_shared_segments(); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); /* calls cleanup_shm */ + } +} + +static uchar * +prep_shmid_S(size_t n) /* either sets SS or H_tab, linux/cygwin */ +{ char *rval; +#ifndef SEP_STATE + key_t key; + + if (verbose && core_id == 0) + { + #ifdef BITSTATE + printf("cpu0: step 1: allocate shared bitstate %g Mb\n", + (double) n / (1048576.)); + #else + printf("cpu0: step 1: allocate shared hastable %g Mb\n", + (double) n / (1048576.)); + #endif + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu0: S %8g + %d Kb exceeds memory limit of %8g Mb\n", + memcnt/1024., n/1024, memlim/(1048576.)); + printf("cpu0: insufficient memory -- aborting\n"); + exit(1); + } + #endif + + key = ftok(PanSource, NCORE+2); /* different from queues */ + if (key == -1) + { perror("ftok shared bitstate or hashtable"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + if (core_id == 0) /* root */ + { shmid_S = shmget(key, n, 0600); + if (shmid_S != -1) + { printf("cpu0: removing stale segment, status: %d\n", + shmctl(shmid_S, IPC_RMID, NULL)); + } + shmid_S = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL); + memcnt += (double) n; + } else /* worker */ + { shmid_S = shmget(key, n, 0600); + } + if (shmid_S == -1) + { perror("shmget shared bitstate or hashtable too large?"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + rval = (char *) shmat(shmid_S, (void *) 0, 0); /* attach */ + if ((char *) rval == (char *) -1) + { perror("shmat shared bitstate or hashtable"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } +#else + rval = (char *) emalloc(n); +#endif + return (uchar *) rval; +} + +#define TRY_AGAIN 1 +#define NOT_AGAIN 0 + +static char shm_prep_result; + +static uchar * +prep_state_mem(size_t n) /* sets memory arena for states linux/cygwin */ +{ char *rval; + key_t key; + static int cnt = 3; /* start larger than earlier ftok calls */ + + shm_prep_result = NOT_AGAIN; /* default */ + if (verbose && core_id == 0) + { printf("cpu0: step 2+: pre-allocate memory arena %d of %6.2g Mb\n", + cnt-3, (double) n / (1048576.)); + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu0: error: M %.0f + %.0f Kb exceeds memory limit of %.0f Mb\n", + memcnt/1024.0, (double) n/1024.0, memlim/(1048576.)); + return NULL; + } + #endif + + key = ftok(PanSource, NCORE+cnt); cnt++; + if (key == -1) + { perror("ftok T"); + printf("pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + if (core_id == 0) + { shmid_M = shmget(key, n, 0600); + if (shmid_M != -1) + { printf("cpu0: removing stale memory segment %d, status: %d\n", + cnt-3, shmctl(shmid_M, IPC_RMID, NULL)); + } + shmid_M = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL); + /* memcnt += (double) n; -- only amount actually used is counted */ + } else + { shmid_M = shmget(key, n, 0600); + + } + if (shmid_M == -1) + { if (verbose) + { printf("error: failed to get pool of shared memory %d of %.0f Mb\n", + cnt-3, ((double)n)/(1048576.)); + perror("state mem"); + printf("pan: check './pan --' for usage details\n"); + } + shm_prep_result = TRY_AGAIN; + return NULL; + } + rval = (char *) shmat(shmid_M, (void *) 0, 0); /* attach */ + + if ((char *) rval == (char *) -1) + { printf("cpu%d error: failed to attach pool of shared memory %d of %.0f Mb\n", + core_id, cnt-3, ((double)n)/(1048576.)); + perror("state mem"); + return NULL; + } + return (uchar *) rval; +} + +void +init_HT(unsigned long n) /* cygwin/linux version */ +{ volatile char *x; + double get_mem; +#ifndef SEP_STATE + volatile char *dc_mem_start; + double need_mem, got_mem = 0.; +#endif + +#ifdef SEP_STATE + #ifndef MEMLIM + if (verbose) + { printf("cpu0: steps 0,1: no -DMEMLIM set\n"); + } + #else + if (verbose) + { printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n", + MEMLIM, ((double)n/(1048576.)), (((double) NCORE * LWQ_SIZE) + GWQ_SIZE) /(1048576.) ); + } + #endif + get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *) + 4*sizeof(void *) + 2*sizeof(double); + /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */ + get_mem += 4 * NCORE * sizeof(void *); /* prfree, prfull, prcnt, prmax */ + #ifdef FULL_TRAIL + get_mem += (NCORE) * sizeof(Stack_Tree *); /* NCORE * stack_last */ + #endif + x = (volatile char *) prep_state_mem((size_t) get_mem); /* work queues and basic structs */ + shmid_X = (long) x; + if (x == NULL) + { printf("cpu0: could not allocate shared memory, see ./pan --\n"); + exit(1); + } + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + + #ifndef BITSTATE + H_tab = (struct H_el **) emalloc(n); + #endif +#else + #ifndef MEMLIM + #warning MEMLIM not set + #define MEMLIM (2048) + #endif + + if (core_id == 0 && verbose) + { printf("cpu0: step 0: -DMEMLIM=%d Mb minus hashtable+workqs (%g + %g Mb) leaves %g Mb\n", + MEMLIM, ((double)n/(1048576.)), (NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.), + (memlim - memcnt - (double) n - (NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.)); + } + #ifndef BITSTATE + H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */ + #endif + need_mem = memlim - memcnt - ((double) NCORE * LWQ_SIZE) - GWQ_SIZE; + if (need_mem <= 0.) + { Uerror("internal error -- shared state memory"); + } + + if (core_id == 0 && verbose) + { printf("cpu0: step 2: pre-allocate shared state memory %g Mb\n", + need_mem/(1048576.)); + } +#ifdef SEP_HEAP + SEG_SIZE = need_mem / NCORE; + if (verbose && core_id == 0) + { printf("cpu0: setting segsize to %6g MB\n", + SEG_SIZE/(1048576.)); + } + #if defined(CYGWIN) || defined(__CYGWIN__) + if (SEG_SIZE > 512.*1024.*1024.) + { printf("warning: reducing SEG_SIZE of %g MB to 512MB (exceeds max for Cygwin)\n", + SEG_SIZE/(1024.*1024.)); + SEG_SIZE = 512.*1024.*1024.; + } + #endif +#endif + mem_reserved = need_mem; + while (need_mem > 1024.) + { get_mem = need_mem; +shm_more: + if (get_mem > (double) SEG_SIZE) + { get_mem = (double) SEG_SIZE; + } + if (get_mem <= 0.0) break; + + /* for allocating states: */ + x = dc_mem_start = (volatile char *) prep_state_mem((size_t) get_mem); + if (x == NULL) + { if (shm_prep_result == NOT_AGAIN + || first_pool != NULL + || SEG_SIZE < (16. * 1048576.)) + { break; + } + SEG_SIZE /= 2.; + if (verbose) + { printf("pan: lowered segsize to 0.000000\n", SEG_SIZE); + } + if (SEG_SIZE >= 1024.) + { goto shm_more; + } + break; + } + + need_mem -= get_mem; + got_mem += get_mem; + if (first_pool == NULL) + { search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + if (((long)x)&(sizeof(void *)-1)) /* 64-bit word alignment */ + { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); + } + + #ifdef COLLAPSE + ncomps = (unsigned long *) x; + x += (256+2) * sizeof(unsigned long); + #endif + } + + dc_shared = (sh_Allocater *) x; /* must be in shared memory */ + x += sizeof(sh_Allocater); + + if (core_id == 0) /* root only */ + { dc_shared->dc_id = shmid_M; + dc_shared->dc_start = dc_mem_start; + dc_shared->dc_arena = x; + dc_shared->pattern = 1234567; /* protection */ + dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start); + dc_shared->nxt = (long) 0; + + if (last_pool == NULL) + { first_pool = last_pool = dc_shared; + } else + { last_pool->nxt = dc_shared; + last_pool = dc_shared; + } + } else if (first_pool == NULL) + { first_pool = dc_shared; + } } + + if (need_mem > 1024.) + { printf("cpu0: could allocate only %g Mb of shared memory (wanted %g more)\n", + got_mem/(1048576.), need_mem/(1048576.)); + } + + if (!first_pool) + { printf("cpu0: insufficient memory -- aborting.\n"); + exit(1); + } + /* we are still single-threaded at this point, with core_id 0 */ + dc_shared = first_pool; + +#endif +} + + /* Test and Set assembly code */ + + #if defined(i386) || defined(__i386__) || defined(__x86_64__) + int + tas(volatile int *s) /* tested */ + { int r; + __asm__ __volatile__( + "xchgl %0, %1 \n\t" + : "=r"(r), "=m"(*s) + : "0"(1), "m"(*s) + : "memory"); + + return r; + } + #elif defined(__arm__) + int + tas(volatile int *s) /* not tested */ + { int r = 1; + __asm__ __volatile__( + "swpb %0, %0, [%3] \n" + : "=r"(r), "=m"(*s) + : "0"(r), "r"(s)); + + return r; + } + #elif defined(sparc) || defined(__sparc__) + int + tas(volatile int *s) /* not tested */ + { int r = 1; + __asm__ __volatile__( + " ldstub [%2], %0 \n" + : "=r"(r), "=m"(*s) + : "r"(s)); + + return r; + } + #elif defined(ia64) || defined(__ia64__) + /* Intel Itanium */ + int + tas(volatile int *s) /* tested */ + { long int r; + __asm__ __volatile__( + " xchg4 %0=%1,%2 \n" + : "=r"(r), "+m"(*s) + : "r"(1) + : "memory"); + return (int) r; + } + #else + #error missing definition of test and set operation for this platform + #endif + +void +cleanup_shm(int val) +{ volatile sh_Allocater *nxt_pool; + unsigned long cnt = 0; + int m; + + if (nibis != 0) + { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val); + return; + } else + { nibis = 1; + } + if (search_terminated != NULL) + { *search_terminated |= 16; /* cleanup_shm */ + } + + for (m = 0; m < NR_QS; m++) + { if (shmdt((void *) shared_mem[m]) > 0) + { perror("shmdt detaching from shared queues"); + } } + +#ifdef SEP_STATE + if (shmdt((void *) shmid_X) != 0) + { perror("shmdt detaching from shared state memory"); + } +#else + #ifdef BITSTATE + if (SS > 0 && shmdt((void *) SS) != 0) + { if (verbose) + { perror("shmdt detaching from shared bitstate arena"); + } } + #else + if (core_id == 0) + { /* before detaching: */ + for (nxt_pool = dc_shared; nxt_pool != NULL; nxt_pool = nxt_pool->nxt) + { cnt += nxt_pool->dc_size; + } + if (verbose) + { printf("cpu0: done, %ld Mb of shared state memory left\n", + cnt / (long)(1048576)); + } } + + if (shmdt((void *) H_tab) != 0) + { perror("shmdt detaching from shared hashtable"); + } + + for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool) + { nxt_pool = last_pool->nxt; + if (shmdt((void *) last_pool->dc_start) != 0) + { perror("shmdt detaching from shared state memory"); + } } + first_pool = last_pool = NULL; /* precaution */ + #endif +#endif + /* detached from shared memory - so cannot use cpu_printf */ + if (verbose) + { printf("cpu%d: done -- got %d states from queue\n", + core_id, nstates_get); + } +} + +extern void give_up(int); +extern void Read_Queue(int); + +void +mem_get(void) +{ SM_frame *f; + int is_parent; + +#if defined(MA) && !defined(SEP_STATE) + #error MA without SEP_STATE is not supported with multi-core +#endif +#ifdef BFS + #error BFS is not supported with multi-core +#endif +#ifdef SC + #error SC is not supported with multi-core +#endif + init_shm(); /* we are single threaded when this starts */ + + if (core_id == 0 && verbose) + { printf("cpu0: step 4: calling fork()\n"); + } + fflush(stdout); + +/* if NCORE > 1 the child or the parent should fork N-1 more times + * the parent is the only process with core_id == 0 and is_parent > 0 + * the workers have is_parent = 0 and core_id = 1..NCORE-1 + */ + if (core_id == 0) + { worker_pids[0] = getpid(); /* for completeness */ + while (++core_id < NCORE) /* first worker sees core_id = 1 */ + { is_parent = fork(); + if (is_parent == -1) + { Uerror("fork failed"); + } + if (is_parent == 0) /* this is a worker process */ + { if (proxy_pid == core_id) /* always non-zero */ + { start_proxy("-r", 0); /* no return */ + } + goto adapt; /* root process continues spawning */ + } + worker_pids[core_id] = is_parent; + } + /* note that core_id is now NCORE */ + if (proxy_pid > 0 && proxy_pid < NCORE) + { proxy_pid_snd = fork(); + if (proxy_pid_snd == -1) + { Uerror("proxy fork failed"); + } + if (proxy_pid_snd == 0) + { start_proxy("-s", worker_pids[proxy_pid]); /* no return */ + } } /* else continue */ + if (is_parent > 0) + { core_id = 0; /* reset core_id for root process */ + } + } else /* worker */ + { static char db0[16]; /* good for up to 10^6 cores */ + static char db1[16]; +adapt: tprefix = db0; sprefix = db1; + sprintf(tprefix, "cpu%d_trail", core_id); + sprintf(sprefix, "cpu%d_rst", core_id); + memcnt = 0; /* count only additionally allocated memory */ + } + signal(SIGINT, give_up); + + if (proxy_pid == 0) /* not in a cluster setup, pan_proxy must attach */ + { rm_shared_segments(); /* mark all shared segments for removal on exit */ + } + if (verbose) + { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid()); + } +#if defined(SEP_HEAP) && !defined(SEP_STATE) + { int i; + volatile sh_Allocater *ptr; + ptr = first_pool; + for (i = 0; i < NCORE && ptr != NULL; i++) + { if (i == core_id) + { my_heap = (char *) ptr->dc_arena; + my_size = (long) ptr->dc_size; + if (verbose) + cpu_printf("local heap %ld MB\n", my_size/(1048576)); + break; + } + ptr = ptr->nxt; /* local */ + } + if (my_heap == NULL) + { printf("cpu%d: no local heap\n", core_id); + pan_exit(1); + } /* else */ + #if defined(CYGWIN) || defined(__CYGWIN__) + ptr = first_pool; + for (i = 0; i < NCORE && ptr != NULL; i++) + { ptr = ptr->nxt; /* local */ + } + dc_shared = ptr; /* any remainder */ + #else + dc_shared = NULL; /* used all mem for local heaps */ + #endif + } +#endif + if (core_id == 0 && !remote_party) + { new_state(); /* cpu0 explores root */ + if (verbose) + cpu_printf("done with 1st dfs, nstates %g (put %d states), read q\n", + nstates, nstates_put); + dfs_phase2 = 1; + } + Read_Queue(core_id); /* all cores */ + + if (verbose) + { cpu_printf("put %6d states into queue -- got %6d\n", + nstates_put, nstates_get); + } + if (proxy_pid != 0) + { rm_shared_segments(); + } + done = 1; + wrapup(); + exit(0); +} + +#else +int unpack_state(SM_frame *, int); +#endif + +struct H_el * +grab_shared(int n) +{ +#ifndef SEP_STATE + char *rval = (char *) 0; + + if (n == 0) + { printf("cpu%d: grab shared zero\n", core_id); fflush(stdout); + return (struct H_el *) rval; + } else if (n&(sizeof(void *)-1)) + { n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */ + } + +#ifdef SEP_HEAP + /* no locking */ + if (my_heap != NULL && my_size > n) + { rval = my_heap; + my_heap += n; + my_size -= n; + goto done; + } +#endif + + if (!dc_shared) + { sudden_stop("pan: out of memory"); + } + + /* another lock is always already in effect when this is called */ + /* but not always the same lock -- i.e., on different parts of the hashtable */ + enter_critical(GLOBAL_LOCK); /* this must be independently mutex */ +#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64) + { static int noted = 0; + if (!noted) + { noted = 1; + printf("cpu%d: global heap has %ld bytes left, needed %d\n", + core_id, dc_shared?dc_shared->dc_size:0, n); + } } +#endif +#if 0 + if (dc_shared->pattern != 1234567) + { leave_critical(GLOBAL_LOCK); + Uerror("overrun -- memory corruption"); + } +#endif + if (dc_shared->dc_size < n) + { if (verbose) + { printf("Next Pool %g Mb + %d\n", memcnt/(1048576.), n); + } + if (dc_shared->nxt == NULL + || dc_shared->nxt->dc_arena == NULL + || dc_shared->nxt->dc_size < n) + { printf("cpu%d: memcnt %g Mb + wanted %d bytes more\n", + core_id, memcnt / (1048576.), n); + leave_critical(GLOBAL_LOCK); + sudden_stop("out of memory -- aborting"); + wrapup(); /* exits */ + } else + { dc_shared = (sh_Allocater *) dc_shared->nxt; + } } + + rval = (char *) dc_shared->dc_arena; + dc_shared->dc_arena += n; + dc_shared->dc_size -= (long) n; +#if 0 + if (VVERBOSE) + printf("cpu%d grab shared (%d bytes) -- %ld left\n", + core_id, n, dc_shared->dc_size); +#endif + leave_critical(GLOBAL_LOCK); +done: + memset(rval, 0, n); + memcnt += (double) n; + + return (struct H_el *) rval; +#else + return (struct H_el *) emalloc(n); +#endif +} + +SM_frame * +Get_Full_Frame(int n) +{ SM_frame *f; + double cnt_start = frame_wait; + + f = &m_workq[n][prfull[n]]; + while (f->m_vsize == 0) /* await full slot LOCK : full frame */ + { iam_alive(); +#ifndef NGQ + #ifndef SAFETY + if (!a_cycles || core_id != 0) + #endif + if (*grcnt > 0) /* accessed outside lock, but safe even if wrong */ + { enter_critical(GQ_RD); /* gq - read access */ + if (*grcnt > 0) /* could have changed */ + { f = &m_workq[NCORE][*grfull]; /* global q */ + if (f->m_vsize == 0) + { /* writer is still filling the slot */ + *gr_writemiss++; + f = &m_workq[n][prfull[n]]; /* reset */ + } else + { *grfull = (*grfull+1) % (GN_FRAMES); + enter_critical(GQ_WR); + *grcnt = *grcnt - 1; + leave_critical(GQ_WR); + leave_critical(GQ_RD); + return f; + } } + leave_critical(GQ_RD); + } +#endif + if (frame_wait++ - cnt_start > Delay) + { if (0) + { cpu_printf("timeout on q%d -- %u -- query %d\n", + n, f, query_in_progress); + } + return (SM_frame *) 0; /* timeout */ + } } + iam_alive(); + if (VVERBOSE) cpu_printf("got frame from q%d\n", n); + prfull[n] = (prfull[n] + 1) % (LN_FRAMES); + enter_critical(QLOCK(n)); + prcnt[n]--; /* lock out increments */ + leave_critical(QLOCK(n)); + return f; +} + +SM_frame * +Get_Free_Frame(int n) +{ SM_frame *f; + double cnt_start = free_wait; + + if (VVERBOSE) { cpu_printf("get free frame from q%d\n", n); } + + if (n == NCORE) /* global q */ + { f = &(m_workq[n][lrfree]); + } else + { f = &(m_workq[n][prfree[n]]); + } + while (f->m_vsize != 0) /* await free slot LOCK : free slot */ + { iam_alive(); + if (free_wait++ - cnt_start > OneSecond) + { if (verbose) + { cpu_printf("timeout waiting for free slot q%d\n", n); + } + cnt_start = free_wait; + if (someone_crashed(1)) + { printf("cpu%d: search terminated\n", core_id); + sudden_stop("get free frame"); + pan_exit(1); + } } } + if (n != NCORE) + { prfree[n] = (prfree[n] + 1) % (LN_FRAMES); + enter_critical(QLOCK(n)); + prcnt[n]++; /* lock out decrements */ + if (prmax[n] < prcnt[n]) + { prmax[n] = prcnt[n]; + } + leave_critical(QLOCK(n)); + } + return f; +} +#ifndef NGQ +int +GlobalQ_HasRoom(void) +{ int rval = 0; + + gq_tries++; + if (*grcnt < GN_FRAMES) /* there seems to be room */ + { enter_critical(GQ_WR); /* gq write access */ + if (*grcnt < GN_FRAMES) + { if (m_workq[NCORE][*grfree].m_vsize != 0) + { /* can happen if reader is slow emptying slot */ + *gr_readmiss++; + goto out; /* dont wait: release lock and return */ + } + lrfree = *grfree; /* Get_Free_Frame use lrfree in this mode */ + *grfree = (*grfree + 1) % GN_FRAMES; + *grcnt = *grcnt + 1; /* count nr of slots filled -- no additional lock needed */ + if (*grmax < *grcnt) *grmax = *grcnt; + leave_critical(GQ_WR); /* for short lock duration */ + gq_hasroom++; + mem_put(NCORE); /* copy state into reserved slot */ + rval = 1; /* successfull handoff */ + } else + { gq_hasnoroom++; +out: leave_critical(GQ_WR); + } } + return rval; +} +#endif + +int +unpack_state(SM_frame *f, int from_q) +{ int i, j; + static struct H_el D_State; + + if (f->m_vsize > 0) + { boq = f->m_boq; + if (boq > 256) + { cpu_printf("saw control %d, expected state\n", boq); + return 0; + } + vsize = f->m_vsize; +correct: + memcpy((uchar *) &now, (uchar *) f->m_now, vsize); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { Mask[i] = (f->m_Mask[i/8] & (1< 0) + { memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT)); + memcpy((uchar *) proc_skip, (uchar *) f->m_p_skip, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) q_offset, (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT)); + memcpy((uchar *) q_skip, (uchar *) f->m_q_skip, now._nr_qs * sizeof(uchar)); + } +#ifndef NOVSZ + if (vsize != now._vsz) + { cpu_printf("vsize %d != now._vsz %d (type %d) %d\n", + vsize, now._vsz, f->m_boq, f->m_vsize); + vsize = now._vsz; + goto correct; /* rare event: a race */ + } +#endif + hmax = max(hmax, vsize); + + if (f != &cur_Root) + { memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame)); + } + + if (((now._a_t) & 1) == 1) /* i.e., when starting nested DFS */ + { A_depth = depthfound = 0; + memcpy((uchar *)&A_Root, (uchar *)&now, vsize); + } + nr_handoffs = f->nr_handoffs; + } else + { cpu_printf("pan: state empty\n"); + } + + depth = 0; + trpt = &trail[1]; + trpt->tau = f->m_tau; + trpt->o_pm = f->m_o_pm; + + (trpt-1)->ostate = &D_State; /* stub */ + trpt->ostate = &D_State; + +#ifdef FULL_TRAIL + if (upto > 0) + { stack_last[core_id] = (Stack_Tree *) f->m_stack; + } + #if defined(VERBOSE) + if (stack_last[core_id]) + { cpu_printf("%d: UNPACK -- SET m_stack %u (%d,%d)\n", + depth, stack_last[core_id], stack_last[core_id]->pr, + stack_last[core_id]->t_id); + } + #endif +#endif + + if (!trpt->o_t) + { static Trans D_Trans; + trpt->o_t = &D_Trans; + } + + #ifdef VERI + if ((trpt->tau & 4) != 4) + { trpt->tau |= 4; /* the claim moves first */ + cpu_printf("warning: trpt was not up to date\n"); + } + #endif + + for (i = 0; i < (int) now._nr_pr; i++) + { P0 *ptr = (P0 *) pptr(i); + #ifndef NP + if (accpstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 2; + } + #else + if (progstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 4; + } + #endif + } + + #ifdef EVENT_TRACE + #ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 2; + } + #else + if (progstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 4; + } + #endif + #endif + + #if defined(C_States) && (HAS_TRACK==1) + /* restore state of tracked C objects */ + c_revert((uchar *) &(now.c_state[0])); + #if (HAS_STACK==1) + c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */ + #endif + #endif + return 1; +} + +void +write_root(void) /* for trail file */ +{ int fd; + + if (iterative == 0 && Nr_Trails > 1) + sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix); + else + sprintf(fnm, "%s.%s", TrailFile, sprefix); + + if (cur_Root.m_vsize == 0) + { (void) unlink(fnm); /* remove possible old copy */ + return; /* its the default initial state */ + } + + if ((fd = creat(fnm, TMODE)) < 0) + { char *q; + if ((q = strchr(TrailFile, '.'))) + { *q = '\0'; /* strip .pml */ + if (iterative == 0 && Nr_Trails-1 > 0) + sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix); + else + sprintf(fnm, "%s.%s", TrailFile, sprefix); + *q = '.'; + fd = creat(fnm, TMODE); + } + if (fd < 0) + { cpu_printf("pan: cannot create %s\n", fnm); + perror("cause"); + return; + } } + + if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame)) + { cpu_printf("pan: error writing %s\n", fnm); + } else + { cpu_printf("pan: wrote %s\n", fnm); + } + close(fd); +} + +void +set_root(void) +{ int fd; + char *q; + char MyFile[512]; + char MySuffix[16]; + char *ssuffix = "rst"; + int try_core = 1; + + strcpy(MyFile, TrailFile); +try_again: + if (whichtrail > 0) + { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix); + fd = open(fnm, O_RDONLY, 0); + if (fd < 0 && (q = strchr(MyFile, '.'))) + { *q = '\0'; /* strip .pml */ + sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix); + *q = '.'; + fd = open(fnm, O_RDONLY, 0); + } + } else + { sprintf(fnm, "%s.%s", MyFile, ssuffix); + fd = open(fnm, O_RDONLY, 0); + if (fd < 0 && (q = strchr(MyFile, '.'))) + { *q = '\0'; /* strip .pml */ + sprintf(fnm, "%s.%s", MyFile, ssuffix); + *q = '.'; + fd = open(fnm, O_RDONLY, 0); + } } + + if (fd < 0) + { if (try_core < NCORE) + { ssuffix = MySuffix; + sprintf(ssuffix, "cpu%d_rst", try_core++); + goto try_again; + } + cpu_printf("no file '%s.rst' or '%s' (not an error)\n", MyFile, fnm); + } else + { if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame)) + { cpu_printf("read error %s\n", fnm); + close(fd); + pan_exit(1); + } + close(fd); + (void) unpack_state(&cur_Root, -2); +#ifdef SEP_STATE + cpu_printf("partial trail -- last few steps only\n"); +#endif + cpu_printf("restored root from '%s'\n", fnm); + printf("=====State:=====\n"); + { int i, j; P0 *z; + for (i = 0; i < now._nr_pr; i++) + { z = (P0 *)pptr(i); + printf("proc %2d (%s) ", i, procname[z->_t]); + for (j = 0; src_all[j].src; j++) + if (src_all[j].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[j].src[z->_p], PanSource); + break; + } + printf("(state %d)\n", z->_p); + c_locals(i, z->_t); + } + c_globals(); + } + printf("================\n"); + } +} + +#ifdef USE_DISK +unsigned long dsk_written, dsk_drained; +void mem_drain(void); +#endif + +void +m_clear_frame(SM_frame *f) +{ int i, clr_sz = sizeof(SM_results); + + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { clr_sz += NrStates[i]*sizeof(uchar); + } + memset(f, 0, clr_sz); + /* caution if sizeof(SM_results) > sizeof(SM_frame) */ +} + +#define TargetQ_Full(n) (m_workq[n][prfree[n]].m_vsize != 0) +#define TargetQ_NotFull(n) (m_workq[n][prfree[n]].m_vsize == 0) + +int +AllQueuesEmpty(void) +{ int q; +#ifndef NGQ + if (*grcnt != 0) + { return 0; + } +#endif + for (q = 0; q < NCORE; q++) + { if (prcnt[q] != 0) + { return 0; + } } + return 1; +} + +void +Read_Queue(int q) +{ SM_frame *f, *of; + int remember, target_q; + SM_results *r; + double patience = 0.0; + + target_q = (q + 1) % NCORE; + + for (;;) + { f = Get_Full_Frame(q); + if (!f) /* 1 second timeout -- and trigger for Query */ + { if (someone_crashed(2)) + { printf("cpu%d: search terminated [code %d]\n", + core_id, search_terminated?*search_terminated:-1); + sudden_stop(""); + pan_exit(1); + } +#ifdef TESTING + /* to profile with cc -pg and gprof pan.exe -- set handoff depth beyond maxdepth */ + exit(0); +#endif + remember = *grfree; + if (core_id == 0 /* root can initiate termination */ + && remote_party == 0 /* and only the original root */ + && query_in_progress == 0 /* unless its already in progress */ + && AllQueuesEmpty()) + { f = Get_Free_Frame(target_q); + query_in_progress = 1; /* only root process can do this */ + if (!f) { Uerror("Fatal1: no free slot"); } + f->m_boq = QUERY; /* initiate Query */ + if (verbose) + { cpu_printf("snd QUERY to q%d (%d) into slot %d\n", + target_q, nstates_get + 1, prfree[target_q]-1); + } + f->m_vsize = remember + 1; + /* number will not change unless we receive more states */ + } else if (patience++ > OneHour) /* one hour watchdog timer */ + { cpu_printf("timeout -- giving up\n"); + sudden_stop("queue timeout"); + pan_exit(1); + } + if (0) cpu_printf("timed out -- try again\n"); + continue; + } + patience = 0.0; /* reset watchdog */ + + if (f->m_boq == QUERY) + { if (verbose) + { cpu_printf("got QUERY on q%d (%d <> %d) from slot %d\n", + q, f->m_vsize, nstates_put + 1, prfull[q]-1); + snapshot(); + } + remember = f->m_vsize; + f->m_vsize = 0; /* release slot */ + + if (core_id == 0 && remote_party == 0) /* original root cpu0 */ + { if (query_in_progress == 1 /* didn't send more states in the interim */ + && *grfree + 1 == remember) /* no action on global queue meanwhile */ + { if (verbose) cpu_printf("Termination detected\n"); + if (TargetQ_Full(target_q)) + { if (verbose) + cpu_printf("warning: target q is full\n"); + } + f = Get_Free_Frame(target_q); + if (!f) { Uerror("Fatal2: no free slot"); } + m_clear_frame(f); + f->m_boq = QUIT; /* send final Quit, collect stats */ + f->m_vsize = 111; /* anything non-zero will do */ + if (verbose) + cpu_printf("put QUIT on q%d\n", target_q); + } else + { if (verbose) cpu_printf("Stale Query\n"); +#ifdef USE_DISK + mem_drain(); +#endif + } + query_in_progress = 0; + } else + { if (TargetQ_Full(target_q)) + { if (verbose) + cpu_printf("warning: forward query - target q full\n"); + } + f = Get_Free_Frame(target_q); + if (verbose) + cpu_printf("snd QUERY response to q%d (%d <> %d) in slot %d\n", + target_q, remember, *grfree + 1, prfree[target_q]-1); + if (!f) { Uerror("Fatal4: no free slot"); } + + if (*grfree + 1 == remember) /* no action on global queue */ + { f->m_boq = QUERY; /* forward query, to root */ + f->m_vsize = remember; + } else + { f->m_boq = QUERY_F; /* no match -- busy */ + f->m_vsize = 112; /* anything non-zero */ +#ifdef USE_DISK + if (dsk_written != dsk_drained) + { mem_drain(); + } +#endif + } } + continue; + } + + if (f->m_boq == QUERY_F) + { if (verbose) + { cpu_printf("got QUERY_F on q%d from slot %d\n", q, prfull[q]-1); + } + f->m_vsize = 0; /* release slot */ + + if (core_id == 0 && remote_party == 0) /* original root cpu0 */ + { if (verbose) cpu_printf("No Match on Query\n"); + query_in_progress = 0; + } else + { if (TargetQ_Full(target_q)) + { if (verbose) cpu_printf("warning: forwarding query_f, target queue full\n"); + } + f = Get_Free_Frame(target_q); + if (verbose) cpu_printf("forward QUERY_F to q%d into slot %d\n", + target_q, prfree[target_q]-1); + if (!f) { Uerror("Fatal5: no free slot"); } + f->m_boq = QUERY_F; /* cannot terminate yet */ + f->m_vsize = 113; /* anything non-zero */ + } +#ifdef USE_DISK + if (dsk_written != dsk_drained) + { mem_drain(); + } +#endif + continue; + } + + if (f->m_boq == QUIT) + { if (0) cpu_printf("done -- local memcnt %g Mb\n", memcnt/(1048576.)); + retrieve_info((SM_results *) f); /* collect and combine stats */ + if (verbose) + { cpu_printf("received Quit\n"); + snapshot(); + } + f->m_vsize = 0; /* release incoming slot */ + if (core_id != 0) + { f = Get_Free_Frame(target_q); /* new outgoing slot */ + if (!f) { Uerror("Fatal6: no free slot"); } + m_clear_frame(f); /* start with zeroed stats */ + record_info((SM_results *) f); + f->m_boq = QUIT; /* forward combined results */ + f->m_vsize = 114; /* anything non-zero */ + if (verbose>1) + cpu_printf("fwd Results to q%d\n", target_q); + } + break; /* successful termination */ + } + + /* else: 0<= boq <= 255, means STATE transfer */ + if (unpack_state(f, q) != 0) + { nstates_get++; + f->m_vsize = 0; /* release slot */ + if (VVERBOSE) cpu_printf("Got state\n"); + + if (search_terminated != NULL + && *search_terminated == 0) + { new_state(); /* explore successors */ + memset((uchar *) &cur_Root, 0, sizeof(SM_frame)); /* avoid confusion */ + } else + { pan_exit(0); + } + } else + { pan_exit(0); + } } + if (verbose) cpu_printf("done got %d put %d\n", nstates_get, nstates_put); + sleep_report(); +} + +void +give_up(int unused_x) +{ + if (search_terminated != NULL) + { *search_terminated |= 32; /* give_up */ + } + if (!writing_trail) + { was_interrupted = 1; + snapshot(); + cpu_printf("Give Up\n"); + sleep_report(); + pan_exit(1); + } else /* we are already terminating */ + { cpu_printf("SIGINT\n"); + } +} + +void +check_overkill(void) +{ + vmax_seen = (vmax_seen + 7)/ 8; + vmax_seen *= 8; /* round up to a multiple of 8 */ + + if (core_id == 0 + && !remote_party + && nstates_put > 0 + && VMAX - vmax_seen > 8) + { +#ifdef BITSTATE + printf("cpu0: max VMAX value seen in this run: "); +#else + printf("cpu0: recommend recompiling with "); +#endif + printf("-DVMAX=%d\n", vmax_seen); + } +} + +void +mem_put(int q) /* handoff state to other cpu, workq q */ +{ SM_frame *f; + int i, j; + + if (vsize > VMAX) + { vsize = (vsize + 7)/8; vsize *= 8; /* round up */ + printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize); + Uerror("aborting"); + } + if (now._nr_pr > PMAX) + { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr); + Uerror("aborting"); + } + if (now._nr_qs > QMAX) + { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs); + Uerror("aborting"); + } + if (vsize > vmax_seen) vmax_seen = vsize; + if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr; + if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs; + + f = Get_Free_Frame(q); /* not called in likely deadlock states */ + if (!f) { Uerror("Fatal3: no free slot"); } + + if (VVERBOSE) cpu_printf("putting state into q%d\n", q); + + memcpy((uchar *) f->m_now, (uchar *) &now, vsize); + memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char)); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { if (Mask[i]) + { f->m_Mask[i/8] |= (1< 0) + { memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT)); + memcpy((uchar *) f->m_p_skip, (uchar *) proc_skip, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT)); + memcpy((uchar *) f->m_q_skip, (uchar *) q_skip, now._nr_qs * sizeof(uchar)); + } +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */ +#endif +#ifdef FULL_TRAIL + f->m_stack = stack_last[core_id]; +#endif + f->nr_handoffs = nr_handoffs+1; + f->m_tau = trpt->tau; + f->m_o_pm = trpt->o_pm; + f->m_boq = boq; + f->m_vsize = vsize; /* must come last - now the other cpu can see it */ + + if (query_in_progress == 1) + query_in_progress = 2; /* make sure we know, if a query makes the rounds */ + nstates_put++; +} + +#ifdef USE_DISK +int Dsk_W_Nr, Dsk_R_Nr; +int dsk_file = -1, dsk_read = -1; +unsigned long dsk_written, dsk_drained; +char dsk_name[512]; + +#ifndef BFS_DISK +#if defined(WIN32) || defined(WIN64) + #define RFLAGS (O_RDONLY|O_BINARY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY) +#else + #define RFLAGS (O_RDONLY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC) +#endif +#endif + +void +dsk_stats(void) +{ int i; + + if (dsk_written > 0) + { cpu_printf("dsk_written %d states in %d files\ncpu%d: dsk_drained %6d states\n", + dsk_written, Dsk_W_Nr, core_id, dsk_drained); + close(dsk_read); + close(dsk_file); + for (i = 0; i < Dsk_W_Nr; i++) + { sprintf(dsk_name, "Q%.3d_%.3d.tmp", i, core_id); + unlink(dsk_name); + } } +} + +void +mem_drain(void) +{ SM_frame *f, g; + int q = (core_id + 1) % NCORE; /* target q */ + int sz; + + if (dsk_read < 0 + || dsk_written <= dsk_drained) + { return; + } + + while (dsk_written > dsk_drained + && TargetQ_NotFull(q)) + { f = Get_Free_Frame(q); + if (!f) { Uerror("Fatal: unhandled condition"); } + + if ((dsk_drained+1)%MAX_DSK_FILE == 0) /* 100K states max per file */ + { (void) close(dsk_read); /* close current read handle */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr++, core_id); + (void) unlink(dsk_name); /* remove current file */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr, core_id); + cpu_printf("reading %s\n", dsk_name); + dsk_read = open(dsk_name, RFLAGS); /* open next file */ + if (dsk_read < 0) + { Uerror("could not open dsk file"); + } } + if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame)) + { Uerror("bad dsk file read"); + } + sz = g.m_vsize; + g.m_vsize = 0; + memcpy(f, &g, sizeof(SM_frame)); + f->m_vsize = sz; /* last */ + + dsk_drained++; + } +} + +void +mem_file(void) +{ SM_frame f; + int i, j, q = (core_id + 1) % NCORE; /* target q */ + + if (vsize > VMAX) + { printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize); + Uerror("aborting"); + } + if (now._nr_pr > PMAX) + { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr); + Uerror("aborting"); + } + if (now._nr_qs > QMAX) + { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs); + Uerror("aborting"); + } + + if (VVERBOSE) cpu_printf("filing state for q%d\n", q); + + memcpy((uchar *) f.m_now, (uchar *) &now, vsize); + memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char)); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { if (Mask[i]) + { f.m_Mask[i/8] |= (1< 0) + { memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT)); + memcpy((uchar *)f.m_p_skip, (uchar *)proc_skip, now._nr_pr*sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT)); + memcpy((uchar *) f.m_q_skip, (uchar *) q_skip, now._nr_qs*sizeof(uchar)); + } +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */ +#endif +#ifdef FULL_TRAIL + f.m_stack = stack_last[core_id]; +#endif + f.nr_handoffs = nr_handoffs+1; + f.m_tau = trpt->tau; + f.m_o_pm = trpt->o_pm; + f.m_boq = boq; + f.m_vsize = vsize; + + if (query_in_progress == 1) + { query_in_progress = 2; + } + if (dsk_file < 0) + { sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr, core_id); + dsk_file = open(dsk_name, WFLAGS, 0644); + dsk_read = open(dsk_name, RFLAGS); + if (dsk_file < 0 || dsk_read < 0) + { cpu_printf("File: <%s>\n", dsk_name); + Uerror("cannot open diskfile"); + } + Dsk_W_Nr++; /* nr of next file to open */ + cpu_printf("created temporary diskfile %s\n", dsk_name); + } else if ((dsk_written+1)%MAX_DSK_FILE == 0) + { close(dsk_file); /* close write handle */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr++, core_id); + dsk_file = open(dsk_name, WFLAGS, 0644); + if (dsk_file < 0) + { cpu_printf("File: <%s>\n", dsk_name); + Uerror("aborting: cannot open new diskfile"); + } + cpu_printf("created temporary diskfile %s\n", dsk_name); + } + if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame)) + { Uerror("aborting -- disk write failed (disk full?)"); + } + nstates_put++; + dsk_written++; +} +#endif + +int +mem_hand_off(void) +{ + if (search_terminated == NULL + || *search_terminated != 0) /* not a full crash check */ + { pan_exit(0); + } + iam_alive(); /* on every transition of Down */ +#ifdef USE_DISK + mem_drain(); /* maybe call this also on every Up */ +#endif + if (depth > z_handoff /* above handoff limit */ +#ifndef SAFETY + && !a_cycles /* not in liveness mode */ +#endif +#if SYNC + && boq == -1 /* not mid-rv */ +#endif +#ifdef VERI + && (trpt->tau&4) /* claim moves first */ + && !((trpt-1)->tau&128) /* not a stutter move */ +#endif + && !(trpt->tau&8)) /* not an atomic move */ + { int q = (core_id + 1) % NCORE; /* circular handoff */ + #ifdef GENEROUS + if (prcnt[q] < LN_FRAMES) + #else + if (TargetQ_NotFull(q) + && (dfs_phase2 == 0 || prcnt[core_id] > 0)) + #endif + { mem_put(q); + return 1; + } + { int rval; + #ifndef NGQ + rval = GlobalQ_HasRoom(); + #else + rval = 0; + #endif + #ifdef USE_DISK + if (rval == 0) + { void mem_file(void); + mem_file(); + rval = 1; + } + #endif + return rval; + } + } + return 0; /* i.e., no handoff */ +} + +void +mem_put_acc(void) /* liveness mode */ +{ int q = (core_id + 1) % NCORE; + + if (search_terminated == NULL + || *search_terminated != 0) + { pan_exit(0); + } +#ifdef USE_DISK + mem_drain(); +#endif + /* some tortured use of preprocessing: */ +#if !defined(NGQ) || defined(USE_DISK) + if (TargetQ_Full(q)) + { +#endif +#ifndef NGQ + if (GlobalQ_HasRoom()) + { return; + } +#endif +#ifdef USE_DISK + mem_file(); + } else +#else + #if !defined(NGQ) || defined(USE_DISK) + } + #endif +#endif + { mem_put(q); + } +} + +#if defined(WIN32) || defined(WIN64) +void +init_shm(void) /* initialize shared work-queues */ +{ char key[512]; + int n, m; + int must_exit = 0; + + if (core_id == 0 && verbose) + { printf("cpu0: step 3: allocate shared work-queues %g Mb\n", + ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.)); + } + for (m = 0; m < NR_QS; m++) /* last q is global 1 */ + { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE; + sprintf(key, "Global\\pan_%s_%.3d", PanSource, m); + if (core_id == 0) + { shmid[m] = CreateFileMapping( + INVALID_HANDLE_VALUE, /* use paging file */ + NULL, /* default security */ + PAGE_READWRITE, /* access permissions */ + 0, /* high-order 4 bytes */ + qsize, /* low-order bytes, size in bytes */ + key); /* name */ + } else /* worker nodes just open these segments */ + { shmid[m] = OpenFileMapping( + FILE_MAP_ALL_ACCESS, /* read/write access */ + FALSE, /* children do not inherit handle */ + key); + } + if (shmid[m] == NULL) + { fprintf(stderr, "cpu%d: could not create or open shared queues\n", + core_id); + must_exit = 1; + break; + } + /* attach: */ + shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (shared_mem[m] == NULL) + { fprintf(stderr, "cpu%d: cannot attach shared q%d (%d Mb)\n", + core_id, m+1, (int) (qsize/(1048576.))); + must_exit = 1; + break; + } + + memcnt += qsize; + + m_workq[m] = (SM_frame *) shared_mem[m]; + if (core_id == 0) + { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES; + for (n = 0; n < nframes; n++) + { m_workq[m][n].m_vsize = 0; + m_workq[m][n].m_boq = 0; + } } } + + if (must_exit) + { fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); /* calls cleanup_shm */ + } +} + +static uchar * +prep_shmid_S(size_t n) /* either sets SS or H_tab, WIN32/WIN64 */ +{ char *rval; +#ifndef SEP_STATE + char key[512]; + + if (verbose && core_id == 0) + { + #ifdef BITSTATE + printf("cpu0: step 1: allocate shared bitstate %g Mb\n", + (double) n / (1048576.)); + #else + printf("cpu0: step 1: allocate shared hastable %g Mb\n", + (double) n / (1048576.)); + #endif + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu%d: S %8g + %d Kb exceeds memory limit of %8g Mb\n", + core_id, memcnt/1024., n/1024, memlim/(1048576.)); + printf("cpu%d: insufficient memory -- aborting\n", core_id); + exit(1); + } + #endif + + /* make key different from queues: */ + sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+2); /* different from qs */ + + if (core_id == 0) /* root */ + { shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, +#ifdef WIN64 + PAGE_READWRITE, (n>>32), (n & 0xffffffff), key); +#else + PAGE_READWRITE, 0, n, key); +#endif + memcnt += (double) n; + } else /* worker */ + { shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key); + } + if (shmid_S == NULL) + { + #ifdef BITSTATE + fprintf(stderr, "cpu%d: cannot %s shared bitstate", + core_id, core_id?"open":"create"); + #else + fprintf(stderr, "cpu%d: cannot %s shared hashtable", + core_id, core_id?"open":"create"); + #endif + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */ + if ((char *) rval == NULL) + { fprintf(stderr, "cpu%d: cannot attach shared bitstate or hashtable\n", core_id); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } +#else + rval = (char *) emalloc(n); +#endif + return (uchar *) rval; +} + +static uchar * +prep_state_mem(size_t n) /* WIN32/WIN64 sets memory arena for states */ +{ char *rval; + char key[512]; + static int cnt = 3; /* start larger than earlier ftok calls */ + + if (verbose && core_id == 0) + { printf("cpu0: step 2+: pre-allocate memory arena %d of %g Mb\n", + cnt-3, (double) n / (1048576.)); + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu%d: error: M %.0f + %.0f exceeds memory limit of %.0f Kb\n", + core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0); + return NULL; + } + #endif + + sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+cnt); cnt++; + + if (core_id == 0) + { shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, +#ifdef WIN64 + PAGE_READWRITE, (n>>32), (n & 0xffffffff), key); +#else + PAGE_READWRITE, 0, n, key); +#endif + } else + { shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key); + } + if (shmid_M == NULL) + { printf("cpu%d: failed to get pool of shared memory nr %d of size %d\n", + core_id, cnt-3, n); + printf("pan: check './pan --' for usage details\n"); + return NULL; + } + rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */ + + if (rval == NULL) + { printf("cpu%d: failed to attach pool of shared memory nr %d of size %d\n", + core_id, cnt-3, n); + return NULL; + } + return (uchar *) rval; +} + +void +init_HT(unsigned long n) /* WIN32/WIN64 version */ +{ volatile char *x; + double get_mem; +#ifndef SEP_STATE + char *dc_mem_start; +#endif + if (verbose) printf("cpu%d: initialization for Windows\n", core_id); + +#ifdef SEP_STATE + #ifndef MEMLIM + if (verbose) + { printf("cpu0: steps 0,1: no -DMEMLIM set\n"); + } + #else + if (verbose) + printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n", + MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.)); +#endif + get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double); + /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */ + get_mem += 4 * NCORE * sizeof(void *); + #ifdef FULL_TRAIL + get_mem += (NCORE) * sizeof(Stack_Tree *); + /* NCORE * stack_last */ + #endif + x = (volatile char *) prep_state_mem((size_t) get_mem); + shmid_X = (void *) x; + if (x == NULL) + { printf("cpu0: could not allocate shared memory, see ./pan --\n"); + exit(1); + } + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); /* allow 1 word per entry */ + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + + #ifndef BITSTATE + H_tab = (struct H_el **) emalloc(n); + #endif +#else + #ifndef MEMLIM + #warning MEMLIM not set + #define MEMLIM (2048) + #endif + + if (core_id == 0 && verbose) + printf("cpu0: step 0: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb) = %g Mb for state storage\n", + MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.), + (memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.)); + #ifndef BITSTATE + H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */ + #endif + get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE; + if (get_mem <= 0) + { Uerror("internal error -- shared state memory"); + } + + if (core_id == 0 && verbose) + { printf("cpu0: step 2: shared state memory %g Mb\n", + get_mem/(1048576.)); + } + x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem); /* for states */ + if (x == NULL) + { printf("cpu%d: insufficient memory -- aborting\n", core_id); + exit(1); + } + + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(int); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + if (((long)x)&(sizeof(void *)-1)) /* word alignment */ + { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */ + } + + #ifdef COLLAPSE + ncomps = (unsigned long *) x; + x += (256+2) * sizeof(unsigned long); + #endif + + dc_shared = (sh_Allocater *) x; /* in shared memory */ + x += sizeof(sh_Allocater); + + if (core_id == 0) /* root only */ + { dc_shared->dc_id = shmid_M; + dc_shared->dc_start = (void *) dc_mem_start; + dc_shared->dc_arena = x; + dc_shared->pattern = 1234567; + dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start); + dc_shared->nxt = NULL; + } +#endif +} + +#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__) +extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit); +int +tas(volatile LONG *s) +{ return InterlockedBitTestAndSet(s, 1); +} +#else + #error missing definition of test and set operation for this platform +#endif + +void +cleanup_shm(int val) +{ int m; + static int nibis = 0; + + if (nibis != 0) + { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val); + return; + } else + { nibis = 1; + } + if (search_terminated != NULL) + { *search_terminated |= 16; /* cleanup_shm */ + } + + for (m = 0; m < NR_QS; m++) + { if (shmid[m] != NULL) + { UnmapViewOfFile((char *) shared_mem[m]); + CloseHandle(shmid[m]); + } } +#ifdef SEP_STATE + UnmapViewOfFile((void *) shmid_X); + CloseHandle((void *) shmid_M); +#else + #ifdef BITSTATE + if (shmid_S != NULL) + { UnmapViewOfFile(SS); + CloseHandle(shmid_S); + } + #else + if (core_id == 0 && verbose) + { printf("cpu0: done, %ld Mb of shared state memory left\n", + dc_shared->dc_size / (long)(1048576)); + } + if (shmid_S != NULL) + { UnmapViewOfFile(H_tab); + CloseHandle(shmid_S); + } + shmid_M = (void *) (dc_shared->dc_id); + UnmapViewOfFile((char *) dc_shared->dc_start); + CloseHandle(shmid_M); + #endif +#endif + /* detached from shared memory - so cannot use cpu_printf */ + if (verbose) + { printf("cpu%d: done -- got %d states from queue\n", + core_id, nstates_get); + } +} + +void +mem_get(void) +{ SM_frame *f; + int is_parent; + +#if defined(MA) && !defined(SEP_STATE) + #error MA requires SEP_STATE in multi-core mode +#endif +#ifdef BFS + #error BFS is not supported in multi-core mode +#endif +#ifdef SC + #error SC is not supported in multi-core mode +#endif + init_shm(); /* we are single threaded when this starts */ + signal(SIGINT, give_up); /* windows control-c interrupt */ + + if (core_id == 0 && verbose) + { printf("cpu0: step 4: creating additional workers (proxy %d)\n", + proxy_pid); + } +#if 0 + if NCORE > 1 the child or the parent should fork N-1 more times + the parent is the only process with core_id == 0 and is_parent > 0 + the others (workers) have is_parent = 0 and core_id = 1..NCORE-1 +#endif + if (core_id == 0) /* root starts up the workers */ + { worker_pids[0] = (DWORD) getpid(); /* for completeness */ + while (++core_id < NCORE) /* first worker sees core_id = 1 */ + { char cmdline[64]; + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + + if (proxy_pid == core_id) /* always non-zero */ + { sprintf(cmdline, "pan_proxy.exe -r %s-Q%d -Z%d", + o_cmdline, getpid(), core_id); + } else + { sprintf(cmdline, "pan.exe %s-Q%d -Z%d", + o_cmdline, getpid(), core_id); + } + if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline); + + is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi); + if (is_parent == 0) + { Uerror("fork failed"); + } + worker_pids[core_id] = pi.dwProcessId; + worker_handles[core_id] = pi.hProcess; + if (verbose) + { cpu_printf("created core %d, pid %d\n", + core_id, pi.dwProcessId); + } + if (proxy_pid == core_id) /* we just created the receive half */ + { /* add proxy send, store pid in proxy_pid_snd */ + sprintf(cmdline, "pan_proxy.exe -s %s-Q%d -Z%d -Y%d", + o_cmdline, getpid(), core_id, worker_pids[proxy_pid]); + if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline); + is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi); + if (is_parent == 0) + { Uerror("fork failed"); + } + proxy_pid_snd = pi.dwProcessId; + proxy_handle_snd = pi.hProcess; + if (verbose) + { cpu_printf("created core %d, pid %d (send proxy)\n", + core_id, pi.dwProcessId); + } } } + core_id = 0; /* reset core_id for root process */ + } else /* worker */ + { static char db0[16]; /* good for up to 10^6 cores */ + static char db1[16]; + tprefix = db0; sprefix = db1; + sprintf(tprefix, "cpu%d_trail", core_id); /* avoid conflicts on file access */ + sprintf(sprefix, "cpu%d_rst", core_id); + memcnt = 0; /* count only additionally allocated memory */ + } + if (verbose) + { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid()); + } + if (core_id == 0 && !remote_party) + { new_state(); /* root starts the search */ + if (verbose) + cpu_printf("done with 1st dfs, nstates %g (put %d states), start reading q\n", + nstates, nstates_put); + dfs_phase2 = 1; + } + Read_Queue(core_id); /* all cores */ + + if (verbose) + { cpu_printf("put %6d states into queue -- got %6d\n", + nstates_put, nstates_get); + } + done = 1; + wrapup(); + exit(0); +} +#endif + +#ifdef BITSTATE +void +init_SS(unsigned long n) +{ + SS = (uchar *) prep_shmid_S((size_t) n); + init_HT(0L); +} +#endif + +#endif +clock_t start_time; +#if NCORE>1 +clock_t crash_stamp; +#endif +#if !defined(WIN32) && !defined(WIN64) +struct tms start_tm; +#endif + +void +start_timer(void) +{ +#if defined(WIN32) || defined(WIN64) + start_time = clock(); +#else + start_time = times(&start_tm); +#endif +} + +void +stop_timer(void) +{ clock_t stop_time; + double delta_time; +#if !defined(WIN32) && !defined(WIN64) + struct tms stop_tm; + stop_time = times(&stop_tm); + delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK)); +#else + stop_time = clock(); + delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC); +#endif + if (readtrail || delta_time < 0.00) return; +#if NCORE>1 + if (core_id == 0 && nstates > (double) 0) + { printf("\ncpu%d: elapsed time %.3g seconds (%g states visited)\n", core_id, delta_time, nstates); + if (delta_time > 0.01) + { printf("cpu%d: rate %g states/second\n", core_id, nstates/delta_time); + } + { void check_overkill(void); + check_overkill(); + } } +#else + printf("\npan: elapsed time %.3g seconds\n", delta_time); + if (delta_time > 0.01) + { printf("pan: rate %9.8g states/second\n", nstates/delta_time); + if (verbose) + { printf("pan: avg transition delay %.5g usec\n", + delta_time/(nstates+truncs)); + } } +#endif +} + +#if NCORE>1 +#ifdef T_ALERT +double t_alerts[17]; + +void +crash_report(void) +{ int i; + printf("crash alert intervals:\n"); + for (i = 0; i < 17; i++) + { printf("%d\t%g\n", i, t_alerts[i]); +} } +#endif + +void +crash_reset(void) +{ /* false alarm */ + if (crash_stamp != (clock_t) 0) + { +#ifdef T_ALERT + double delta_time; + int i; +#if defined(WIN32) || defined(WIN64) + delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC); +#else + delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK)); +#endif + for (i = 0; i < 16; i++) + { if (delta_time <= (i*30)) + { t_alerts[i] = delta_time; + break; + } } + if (i == 16) t_alerts[i] = delta_time; +#endif + if (verbose) + printf("cpu%d: crash alert off\n", core_id); + } + crash_stamp = (clock_t) 0; +} + +int +crash_test(double maxtime) +{ double delta_time; + if (crash_stamp == (clock_t) 0) + { /* start timing */ +#if defined(WIN32) || defined(WIN64) + crash_stamp = clock(); +#else + crash_stamp = times(&start_tm); +#endif + if (verbose) + { printf("cpu%d: crash detection\n", core_id); + } + return 0; + } +#if defined(WIN32) || defined(WIN64) + delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC); +#else + delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK)); +#endif + return (delta_time >= maxtime); +} +#endif + +void +do_the_search(void) +{ int i; + depth = mreached = 0; + trpt = &trail[0]; +#ifdef VERI + trpt->tau |= 4; /* the claim moves first */ +#endif + for (i = 0; i < (int) now._nr_pr; i++) + { P0 *ptr = (P0 *) pptr(i); +#ifndef NP + if (!(trpt->o_pm&2) + && accpstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 2; + } +#else + if (!(trpt->o_pm&4) + && progstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 4; + } +#endif + } +#ifdef EVENT_TRACE +#ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 2; + } +#else + if (progstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 4; + } +#endif +#endif +#ifndef NOCOMP + Mask[0] = Mask[1] = 1; /* _nr_pr, _nr_qs */ + if (!a_cycles) + { i = &(now._a_t) - (uchar *) &now; + Mask[i] = 1; /* _a_t */ + } +#ifndef NOFAIR + if (!fairness) + { int j = 0; + i = &(now._cnt[0]) - (uchar *) &now; + while (j++ < NFAIR) + Mask[i++] = 1; /* _cnt[] */ + } +#endif +#endif +#ifndef NOFAIR + if (fairness + && (a_cycles && (trpt->o_pm&2))) + { now._a_t = 2; /* set the A-bit */ + now._cnt[0] = now._nr_pr + 1; +#ifdef VERBOSE + printf("%3d: fairness Rule 1, cnt=%d, _a_t=%d\n", + depth, now._cnt[now._a_t&1], now._a_t); +#endif + } +#endif + c_stack_start = (char *) &i; /* meant to be read-only */ +#if defined(HAS_CODE) && defined (C_INIT) + C_INIT; /* initialization of data that must precede fork() */ + c_init_done++; +#endif +#if defined(C_States) && (HAS_TRACK==1) + /* capture initial state of tracked C objects */ + c_update((uchar *) &(now.c_state[0])); +#endif +#ifdef HAS_CODE + if (readtrail) getrail(); /* no return */ +#endif + start_timer(); +#ifdef BFS + bfs(); +#else +#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1) + /* initial state of tracked & unmatched objects */ + c_stack((uchar *) &(svtack->c_stack[0])); +#endif +#ifdef RANDOMIZE + #if RANDOMIZE>0 + srand(RANDOMIZE); + #else + srand(123); + #endif +#endif +#if NCORE>1 + mem_get(); +#else + new_state(); /* start 1st DFS */ +#endif +#endif +} +#ifdef INLINE_REV +uchar +do_reverse(Trans *t, short II, uchar M) +{ uchar _m = M; + int tt = (int) ((P0 *)this)->_p; +#include REVERSE_MOVES +R999: return _m; +} +#endif +#ifndef INLINE +#ifdef EVENT_TRACE +static char _tp = 'n'; static int _qid = 0; +#endif +uchar +do_transit(Trans *t, short II) +{ uchar _m = 0; + int tt = (int) ((P0 *)this)->_p; +#ifdef M_LOSS + uchar delta_m = 0; +#endif +#ifdef EVENT_TRACE + short oboq = boq; + uchar ot = (uchar) ((P0 *)this)->_t; + if (ot == EVENT_TRACE) boq = -1; +#define continue { boq = oboq; return 0; } +#else +#define continue return 0 +#ifdef SEPARATE + uchar ot = (uchar) ((P0 *)this)->_t; +#endif +#endif +#include FORWARD_MOVES +P999: +#ifdef EVENT_TRACE + if (ot == EVENT_TRACE) boq = oboq; +#endif + return _m; +#undef continue +} +#ifdef EVENT_TRACE +void +require(char tp, int qid) +{ Trans *t; + _tp = tp; _qid = qid; + + if (now._event != endevent) + for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt) + { if (do_transit(t, EVENT_TRACE)) + { now._event = t->st; + reached[EVENT_TRACE][t->st] = 1; +#ifdef VERBOSE + printf(" event_trace move to -> %d\n", t->st); +#endif +#ifndef BFS +#ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + (trpt+1)->o_pm |= 2; +#else + if (progstate[EVENT_TRACE][now._event]) + (trpt+1)->o_pm |= 4; +#endif +#endif +#ifdef NEGATED_TRACE + if (now._event == endevent) + { +#ifndef BFS + depth++; trpt++; +#endif + uerror("event_trace error (all events matched)"); +#ifndef BFS + trpt--; depth--; +#endif + break; + } +#endif + for (t = t->nxt; t; t = t->nxt) + { if (do_transit(t, EVENT_TRACE)) + Uerror("non-determinism in event-trace"); + } + return; + } +#ifdef VERBOSE + else + printf(" event_trace miss '%c' -- %d, %d, %d\n", + tp, qid, now._event, t->forw); +#endif + } +#ifdef NEGATED_TRACE + now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */ +#else +#ifndef BFS + depth++; trpt++; +#endif + uerror("event_trace error (no matching event)"); +#ifndef BFS + trpt--; depth--; +#endif +#endif +} +#endif +int +enabled(int iam, int pid) +{ Trans *t; uchar *othis = this; + int res = 0; int tt; uchar ot; +#ifdef VERI + /* if (pid > 0) */ pid++; +#endif + if (pid == iam) + Uerror("used: enabled(pid=thisproc)"); + if (pid < 0 || pid >= (int) now._nr_pr) + return 0; + this = pptr(pid); + TstOnly = 1; + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + for (t = trans[ot][tt]; t; t = t->nxt) + if (do_transit(t, (short) pid)) + { res = 1; + break; + } + TstOnly = 0; + this = othis; + return res; +} +#endif +void +snap_time(void) +{ clock_t stop_time; + double delta_time; +#if !defined(WIN32) && !defined(WIN64) + struct tms stop_tm; + stop_time = times(&stop_tm); + delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK)); +#else + stop_time = clock(); + delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC); +#endif + if (delta_time > 0.01) + { printf("t= %6.3g ", delta_time); + printf("R= %7.0g", nstates/delta_time); + } + printf("\n"); + if (quota > 0.1 && delta_time > quota) + { printf("Time limit of %6.3g minutes exceeded\n", quota/60.0); +#if NCORE>1 + fflush(stdout); + leave_critical(GLOBAL_LOCK); + sudden_stop("time-limit"); + exit(1); +#endif + wrapup(); + } +} +void +snapshot(void) +{ +#if NCORE>1 + enter_critical(GLOBAL_LOCK); /* snapshot */ + printf("cpu%d: ", core_id); +#endif + printf("Depth= %7ld States= %8.3g ", +#if NCORE>1 + (long) (nr_handoffs * z_handoff) + +#endif + mreached, nstates); + printf("Transitions= %8.3g ", nstates+truncs); +#ifdef MA + printf("Nodes= %7d ", nr_states); +#endif + printf("Memory= %9.3f\t", memcnt/1048576.); + snap_time(); + fflush(stdout); +#if NCORE>1 + leave_critical(GLOBAL_LOCK); +#endif +} +#ifdef SC +void +stack2disk(void) +{ + if (!stackwrite + && (stackwrite = creat(stackfile, TMODE)) < 0) + Uerror("cannot create stackfile"); + + if (write(stackwrite, trail, DDD*sizeof(Trail)) + != DDD*sizeof(Trail)) + Uerror("stackfile write error -- disk is full?"); + + memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail)); + memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail)); + CNT1++; +} +void +disk2stack(void) +{ long have; + + CNT2++; + memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail)); + + if (!stackwrite + || lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1) + Uerror("disk2stack lseek error"); + + if (!stackread + && (stackread = open(stackfile, 0)) < 0) + Uerror("cannot open stackfile"); + + if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1) + Uerror("disk2stack lseek error"); + + have = read(stackread, trail, DDD*sizeof(Trail)); + if (have != DDD*sizeof(Trail)) + Uerror("stackfile read error"); +} +#endif +uchar * +Pptr(int x) +{ if (x < 0 || x >= MAXPROC || !proc_offset[x]) + return noptr; + else + return (uchar *) pptr(x); +} +int qs_empty(void); +/* + * new_state() is the main DFS search routine in the verifier + * it has a lot of code ifdef-ed together to support + * different search modes, which makes it quite unreadable. + * if you are studying the code, first use the C preprocessor + * to generate a specific version from the pan.c source, + * e.g. by saying: + * gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c + * and then study the resulting file, rather than this one + */ +#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA)) + +#ifdef NSUCC +int N_succ[512]; +void +tally_succ(int cnt) +{ if (cnt < 512) N_succ[cnt]++; + else printf("tally_succ: cnt %d exceeds range\n", cnt); +} + +void +dump_succ(void) +{ int i; double sum = 0.0; + double w_avg = 0.0; + printf("Successor counts:\n"); + for (i = 0; i < 512; i++) + { sum += (double) N_succ[i]; + } + for (i = 0; i < 512; i++) + { if (N_succ[i] > 0) + { printf("%3d %10d (%.4g %% of total)\n", + i, N_succ[i], (100.0 * (double) N_succ[i])/sum); + w_avg += (double) i * (double) N_succ[i]; + } } + if (sum > N_succ[0]) + printf("mean %.4g (without 0: %.4g)\n", w_avg / sum, w_avg / (sum - (double) N_succ[0])); +} +#endif + +void +new_state(void) +{ Trans *t; + uchar _n, _m, ot; +#ifdef RANDOMIZE + short ooi, eoi; +#endif +#ifdef M_LOSS + uchar delta_m = 0; +#endif + short II, JJ = 0, kk; + int tt; +#ifdef REVERSE + short From = BASE, To = now._nr_pr-1; +#else + short From = now._nr_pr-1, To = BASE; +#endif +Down: +#ifdef CHECK + cpu_printf("%d: Down - %s %saccepting [pids %d-%d]\n", + depth, (trpt->tau&4)?"claim":"program", + (trpt->o_pm&2)?"":"non-", From, To); +#endif +#ifdef SCHED + if (depth > 0) + { trpt->sched_limit = (trpt-1)->sched_limit; + } else + { trpt->sched_limit = 0; + } +#endif +#ifdef SC + if (depth > hiwater) + { stack2disk(); + maxdepth += DDD; + hiwater += DDD; + trpt -= DDD; + if(verbose) + printf("zap %d: %d (maxdepth now %d)\n", + CNT1, hiwater, maxdepth); + } +#endif + trpt->tau &= ~(16|32|64); /* make sure these are off */ +#if defined(FULLSTACK) && defined(MA) + trpt->proviso = 0; +#endif +#ifdef NSUCC + trpt->n_succ = 0; +#endif +#if NCORE>1 + if (mem_hand_off()) + { +#if SYNC + (trpt+1)->o_n = 1; /* not a deadlock: as below */ +#endif +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; /* worstcase guess: as below */ +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#endif + if (depth >= maxdepth) + { if (!warned) + { warned = 1; + printf("error: max search depth too small\n"); + } + if (bounded) + { uerror("depth limit reached"); + } + truncs++; +#if SYNC + (trpt+1)->o_n = 1; /* not a deadlock */ +#endif +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; /* worstcase guess */ +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +AllOver: +#if (defined(FULLSTACK) && !defined(MA)) || NCORE>1 + /* if atomic or rv move, carry forward previous state */ + trpt->ostate = (trpt-1)->ostate; +#endif +#ifdef VERI + if ((trpt->tau&4) || ((trpt-1)->tau&128)) +#endif + if (boq == -1) { /* if not mid-rv */ +#ifndef SAFETY + /* this check should now be redundant + * because the seed state also appears + * on the 1st dfs stack and would be + * matched in hstore below + */ + if ((now._a_t&1) && depth > A_depth) + { if (!memcmp((char *)&A_Root, + (char *)&now, vsize)) + { + depthfound = A_depth; +#ifdef CHECK + printf("matches seed\n"); +#endif +#ifdef NP + uerror("non-progress cycle"); +#else + uerror("acceptance cycle"); +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#ifdef CHECK + printf("not seed\n"); +#endif + } +#endif + if (!(trpt->tau&8)) /* if no atomic move */ + { +#ifdef BITSTATE +#ifdef CNTRSTACK + II = bstore((char *)&now, vsize); + trpt->j6 = j1; trpt->j7 = j2; + JJ = LL[j1] && LL[j2]; +#else +#ifdef FULLSTACK + JJ = onstack_now(); +#else +#ifndef NOREDUCE + JJ = II; /* worstcase guess for p.o. */ +#endif +#endif + II = bstore((char *)&now, vsize); +#endif +#else +#ifdef MA + II = gstore((char *)&now, vsize, 0); +#ifndef FULLSTACK + JJ = II; +#else + JJ = (II == 2)?1:0; +#endif +#else + II = hstore((char *)&now, vsize); +#ifdef FULLSTACK + JJ = (II == 2)?1:0; +#endif +#endif +#endif + kk = (II == 1 || II == 2); +#ifndef SAFETY +#if NCORE==1 || defined (SEP_STATE) + if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2))) + #ifndef NOFAIR +#if 0 + if (!fairness || ((now._a_t&1) && now._cnt[1] == 1)) /* 5.1.4 */ +#else + if (a_cycles && !fairness) /* 5.1.6 -- example by Hirofumi Watanabe */ +#endif + #endif + { + II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */ +#ifdef VERBOSE + printf("state match on dfs stack\n"); +#endif + goto same_case; + } +#endif +#if defined(FULLSTACK) && defined(BITSTATE) + if (!JJ && (now._a_t&1) && depth > A_depth) + { int oj1 = j1; + uchar o_a_t = now._a_t; + now._a_t &= ~(1|16|32); + if (onstack_now()) + { II = 3; +#ifdef VERBOSE + printf("state match on 1st dfs stack\n"); +#endif + } + now._a_t = o_a_t; + j1 = oj1; + } +#endif + if (II == 3 && a_cycles && (now._a_t&1)) + { +#ifndef NOFAIR + if (fairness && now._cnt[1] > 1) /* was != 0 */ + { +#ifdef VERBOSE + printf(" fairness count non-zero\n"); +#endif + II = 0; + } else +#endif + { +#ifndef BITSTATE + nShadow--; +#endif +same_case: if (Lstate) depthfound = Lstate->D; +#ifdef NP + uerror("non-progress cycle"); +#else + uerror("acceptance cycle"); +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } + } +#endif +#ifndef NOREDUCE +#ifndef SAFETY +#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO) + if (II != 0 && (!Lstate || Lstate->cpu_id < core_id)) + { (trpt-1)->tau |= 16; + } +#endif + if ((II && JJ) || (II == 3)) + { /* marker for liveness proviso */ +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; +#endif + truncs2++; + } +#else +#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO) + if (!(II != 0 && (!Lstate || Lstate->cpu_id < core_id))) + { /* treat as stack state */ + (trpt-1)->tau |= 16; + } else + { /* treat as non-stack state */ + (trpt-1)->tau |= 64; + } +#endif + if (!II || !JJ) + { /* successor outside stack */ + (trpt-1)->tau |= 64; + } +#endif +#endif + if (II) + { truncs++; +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + if (depth == 0) + { return; + } } +#endif + goto Up; + } + if (!kk) + { static long sdone = (long) 0; long ndone; + nstates++; +#if defined(ZAPH) && defined(BITSTATE) + zstates += (double) hfns; +#endif + ndone = (unsigned long) (nstates/((double) FREQ)); + if (ndone != sdone) + { snapshot(); + sdone = ndone; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) + if (nstates > ((double)(ONE_L<<(ssize+1)))) + { void resize_hashtable(void); + resize_hashtable(); + } +#endif +#if defined(ZAPH) && defined(BITSTATE) + if (zstates > ((double)(ONE_L<<(ssize-2)))) + { /* more than half the bits set */ + void zap_hashtable(void); + zap_hashtable(); + zstates = 0; + } +#endif + } +#ifdef SVDUMP + if (vprefix > 0) + if (write(svfd, (uchar *) &now, vprefix) != vprefix) + { fprintf(efd, "writing %s.svd failed\n", PanSource); + wrapup(); + } +#endif +#if defined(MA) && defined(W_XPT) + if ((unsigned long) nstates%W_XPT == 0) + { void w_xpoint(void); + w_xpoint(); + } +#endif + } +#if defined(FULLSTACK) || defined(CNTRSTACK) + onstack_put(); +#ifdef DEBUG2 +#if defined(FULLSTACK) && !defined(MA) + printf("%d: putting %u (%d)\n", depth, + trpt->ostate, + (trpt->ostate)?trpt->ostate->tagged:0); +#else + printf("%d: putting\n", depth); +#endif +#endif +#else + #if NCORE>1 + trpt->ostate = Lstate; + #endif +#endif + } } + if (depth > mreached) + mreached = depth; +#ifdef VERI + if (trpt->tau&4) +#endif + trpt->tau &= ~(1|2); /* timeout and -request off */ + _n = 0; +#if SYNC + (trpt+1)->o_n = 0; +#endif +#ifdef VERI + if (now._nr_pr == 0) /* claim terminated */ + uerror("end state in claim reached"); + check_claim(((P0 *)pptr(0))->_p); +Stutter: + if (trpt->tau&4) /* must make a claimmove */ + { +#ifndef NOFAIR + if ((now._a_t&2) /* A-bit set */ + && now._cnt[now._a_t&1] == 1) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm |= 16; +#ifdef DEBUG + printf("%3d: fairness Rule 3.: _a_t = %d\n", + depth, now._a_t); +#endif + } +#endif + II = 0; /* never */ + goto Veri0; + } +#endif +#ifndef NOREDUCE + /* Look for a process with only safe transitions */ + /* (special rules apply in the 2nd dfs) */ + if (boq == -1 && From != To + +#ifdef SAFETY + #if NCORE>1 + && (depth < z_handoff) + #endif + ) +#else + #if NCORE>1 + && ((a_cycles) || (!a_cycles && depth < z_handoff)) + #endif + && (!(now._a_t&1) + || (a_cycles && + #ifndef BITSTATE +#ifdef MA +#ifdef VERI + !((trpt-1)->proviso)) +#else + !(trpt->proviso)) +#endif +#else +#ifdef VERI + (trpt-1)->ostate && + !(((char *)&((trpt-1)->ostate->state))[0] & 128)) +#else + !(((char *)&(trpt->ostate->state))[0] & 128)) +#endif +#endif + #else +#ifdef VERI + (trpt-1)->ostate && + (trpt-1)->ostate->proviso == 0) +#else + trpt->ostate->proviso == 0) +#endif + #endif + )) +#endif + +#ifdef REVERSE + for (II = From; II <= To; II++) +#else + for (II = From; II >= To; II--) +#endif + { +Resume: /* pick up here if preselect fails */ + this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + if (trans[ot][tt]->atom & 8) + { t = trans[ot][tt]; + if (t->qu[0] != 0) + { Ccheck++; + if (!q_cond(II, t)) + continue; + Cholds++; + } + From = To = II; /* the process preselected */ +#ifdef NIBIS + t->om = 0; +#endif + trpt->tau |= 32; /* preselect marker */ +#ifdef DEBUG +#ifdef NIBIS + printf("%3d: proc %d Pre", depth, II); + printf("Selected (om=%d, tau=%d)\n", + t->om, trpt->tau); +#else + printf("%3d: proc %d PreSelected (tau=%d)\n", + depth, II, trpt->tau); +#endif +#endif + goto Again; + } + } + trpt->tau &= ~32; +#endif +#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI)) +Again: +#endif + /* The Main Expansion Loop over Processes */ + trpt->o_pm &= ~(8|16|32|64); /* fairness-marks */ +#ifndef NOFAIR + if (fairness && boq == -1 +#ifdef VERI + && (!(trpt->tau&4) && !((trpt-1)->tau&128)) +#endif + && !(trpt->tau&8)) + { /* A_bit = 1; Cnt = N in acc states with A_bit 0 */ + if (!(now._a_t&2)) + { + if (a_cycles && (trpt->o_pm&2)) + { /* Accepting state */ + now._a_t |= 2; + now._cnt[now._a_t&1] = now._nr_pr + 1; + trpt->o_pm |= 8; +#ifdef DEBUG + printf("%3d: fairness Rule 1: cnt=%d, _a_t=%d\n", + depth, now._cnt[now._a_t&1], now._a_t); +#endif + } + } else + { /* A_bit = 0 when Cnt 0 */ + if (now._cnt[now._a_t&1] == 1) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm |= 16; +#ifdef DEBUG + printf("%3d: fairness Rule 3: _a_t = %d\n", + depth, now._a_t); +#endif + } } } +#endif + +#ifdef REVERSE + for (II = From; II <= To; II++) +#else + for (II = From; II >= To; II--) +#endif + { +#if SYNC + /* no rendezvous with same proc */ + if (boq != -1 && trpt->pr == II) continue; +#endif +#ifdef SCHED + /* limit max nr of interleavings */ + if (From != To + && depth > 0 + #ifdef VERI + && II != 0 + #endif + && (trpt-1)->pr != II + && trpt->sched_limit >= sched_max) + { continue; + } +#endif +#ifdef VERI +Veri0: +#endif + this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; +#ifdef NIBIS + /* don't repeat a previous preselected expansion */ + /* could hit this if reduction proviso was false */ + t = trans[ot][tt]; + if (!(trpt->tau&4) + && !(trpt->tau&1) + && !(trpt->tau&32) + && (t->atom & 8) + && boq == -1 + && From != To) + { if (t->qu[0] == 0 + || q_cond(II, t)) + { _m = t->om; + if (_m>_n||(_n>3&&_m!=0)) _n=_m; + continue; /* did it before */ + } } +#endif + trpt->o_pm &= ~1; /* no move in this pid yet */ +#ifdef EVENT_TRACE + (trpt+1)->o_event = now._event; +#endif + /* Fairness: Cnt++ when Cnt == II */ +#ifndef NOFAIR + trpt->o_pm &= ~64; /* didn't apply rule 2 */ + if (fairness + && boq == -1 + && !(trpt->o_pm&32) + && (now._a_t&2) + && now._cnt[now._a_t&1] == II+2) + { now._cnt[now._a_t&1] -= 1; +#ifdef VERI + /* claim need not participate */ + if (II == 1) + now._cnt[now._a_t&1] = 1; +#endif +#ifdef DEBUG + printf("%3d: proc %d fairness ", depth, II); + printf("Rule 2: --cnt to %d (%d)\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm |= (32|64); + } +#endif +#ifdef HAS_PROVIDED + if (!provided(II, ot, tt, t)) continue; +#endif + /* check all trans of proc II - escapes first */ +#ifdef HAS_UNLESS + trpt->e_state = 0; +#endif + (trpt+1)->pr = (uchar) II; + (trpt+1)->st = tt; +#ifdef RANDOMIZE + for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++) + { if (strcmp(t->tp, "else") == 0) + { eoi++; + break; + } } + if (eoi > 0) + { t = trans[ot][tt]; + #ifdef VERBOSE + printf("randomizer: suppressed, saw else\n"); + #endif + } else + { eoi = rand()%ooi; + #ifdef VERBOSE + printf("randomizer: skip %d in %d\n", eoi, ooi); + #endif + for (t = trans[ot][tt]; t; t = t->nxt) + if (eoi-- <= 0) break; + } +domore: + for ( ; t && ooi > 0; t = t->nxt, ooi--) +#else + for (t = trans[ot][tt]; t; t = t->nxt) +#endif + { +#ifdef HAS_UNLESS + /* exploring all transitions from + * a single escape state suffices + */ + if (trpt->e_state > 0 + && trpt->e_state != t->e_trans) + { +#ifdef DEBUG + printf("skip 2nd escape %d (did %d before)\n", + t->e_trans, trpt->e_state); +#endif + break; + } +#endif + (trpt+1)->o_t = t; +#ifdef INLINE +#include FORWARD_MOVES +P999: /* jumps here when move succeeds */ +#else + if (!(_m = do_transit(t, II))) continue; +#endif +#ifdef SCHED + if (depth > 0 + #ifdef VERI + && II != 0 + #endif + && (trpt-1)->pr != II) + { trpt->sched_limit = 1 + (trpt-1)->sched_limit; + } +#endif + if (boq == -1) +#ifdef CTL + /* for branching-time, can accept reduction only if */ + /* the persistent set contains just 1 transition */ + { if ((trpt->tau&32) && (trpt->o_pm&1)) + trpt->tau |= 16; + trpt->o_pm |= 1; /* we moved */ + } +#else + trpt->o_pm |= 1; /* we moved */ +#endif +#ifdef LOOPSTATE + if (loopstate[ot][tt]) + { +#ifdef VERBOSE + printf("exiting from loopstate:\n"); +#endif + trpt->tau |= 16; + cnt_loops++; + } +#endif +#ifdef PEG + peg[t->forw]++; +#endif +#if defined(VERBOSE) || defined(CHECK) +#if defined(SVDUMP) + cpu_printf("%3d: proc %d exec %d \n", depth, II, t->t_id); +#else + cpu_printf("%3d: proc %d exec %d, %d to %d, %s %s %s %saccepting [tau=%d]\n", + depth, II, t->forw, tt, t->st, t->tp, + (t->atom&2)?"atomic":"", + (boq != -1)?"rendez-vous":"", + (trpt->o_pm&2)?"":"non-", trpt->tau); +#ifdef HAS_UNLESS + if (t->e_trans) + cpu_printf("\t(escape to state %d)\n", t->st); +#endif +#endif +#ifdef RANDOMIZE + cpu_printf("\t(randomizer %d)\n", ooi); +#endif +#endif +#ifdef HAS_LAST +#ifdef VERI + if (II != 0) +#endif + now._last = II - BASE; +#endif +#ifdef HAS_UNLESS + trpt->e_state = t->e_trans; +#endif + depth++; trpt++; + trpt->pr = (uchar) II; + trpt->st = tt; + trpt->o_pm &= ~(2|4); + if (t->st > 0) + { ((P0 *)this)->_p = t->st; +/* moved down reached[ot][t->st] = 1; */ + } +#ifndef SAFETY + if (a_cycles) + { +#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP)) + int ii; +#endif +#define P__Q ((P0 *)pptr(ii)) +#if ACCEPT_LAB>0 +#ifdef NP + /* state 1 of np_ claim is accepting */ + if (((P0 *)pptr(0))->_p == 1) + trpt->o_pm |= 2; +#else + for (ii = 0; ii < (int) now._nr_pr; ii++) + { if (accpstate[P__Q->_t][P__Q->_p]) + { trpt->o_pm |= 2; + break; + } } +#endif +#endif +#if defined(HAS_NP) && PROG_LAB>0 + for (ii = 0; ii < (int) now._nr_pr; ii++) + { if (progstate[P__Q->_t][P__Q->_p]) + { trpt->o_pm |= 4; + break; + } } +#endif +#undef P__Q + } +#endif + trpt->o_t = t; trpt->o_n = _n; + trpt->o_ot = ot; trpt->o_tt = tt; + trpt->o_To = To; trpt->o_m = _m; + trpt->tau = 0; +#ifdef RANDOMIZE + trpt->oo_i = ooi; +#endif + if (boq != -1 || (t->atom&2)) + { trpt->tau |= 8; +#ifdef VERI + /* atomic sequence in claim */ + if((trpt-1)->tau&4) + trpt->tau |= 4; + else + trpt->tau &= ~4; + } else + { if ((trpt-1)->tau&4) + trpt->tau &= ~4; + else + trpt->tau |= 4; + } + /* if claim allowed timeout, so */ + /* does the next program-step: */ + if (((trpt-1)->tau&1) && !(trpt->tau&4)) + trpt->tau |= 1; +#else + } else + trpt->tau &= ~8; +#endif + if (boq == -1 && (t->atom&2)) + { From = To = II; nlinks++; + } else +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif + } +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Push_Stack_Tree(II, t->t_id); + } +#endif + goto Down; /* pseudo-recursion */ +Up: +#ifdef CHECK + cpu_printf("%d: Up - %s\n", depth, + (trpt->tau&4)?"claim":"program"); +#endif +#if NCORE>1 + iam_alive(); + #ifdef USE_DISK + mem_drain(); + #endif +#endif +#if defined(MA) || NCORE>1 + if (depth <= 0) return; + /* e.g., if first state is old, after a restart */ +#endif +#ifdef SC + if (CNT1 > CNT2 + && depth < hiwater - (HHH-DDD) + 2) + { + trpt += DDD; + disk2stack(); + maxdepth -= DDD; + hiwater -= DDD; + if(verbose) + printf("unzap %d: %d\n", CNT2, hiwater); + } +#endif +#ifndef NOFAIR + if (trpt->o_pm&128) /* fairness alg */ + { now._cnt[now._a_t&1] = trpt->bup.oval; + _n = 1; trpt->o_pm &= ~128; + depth--; trpt--; +#if defined(VERBOSE) || defined(CHECK) + printf("%3d: reversed fairness default move\n", depth); +#endif + goto Q999; + } +#endif +#ifdef HAS_LAST +#ifdef VERI + { int d; Trail *trl; + now._last = 0; + for (d = 1; d < depth; d++) + { trl = getframe(depth-d); /* was (trpt-d) */ + if (trl->pr != 0) + { now._last = trl->pr - BASE; + break; + } } } +#else + now._last = (depth<1)?0:(trpt-1)->pr; +#endif +#endif +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif +#ifndef SAFETY + if ((now._a_t&1) && depth <= A_depth) + return; /* to checkcycles() */ +#endif + t = trpt->o_t; _n = trpt->o_n; + ot = trpt->o_ot; II = trpt->pr; + tt = trpt->o_tt; this = pptr(II); + To = trpt->o_To; _m = trpt->o_m; +#ifdef RANDOMIZE + ooi = trpt->oo_i; +#endif +#ifdef INLINE_REV + _m = do_reverse(t, II, _m); +#else +#include REVERSE_MOVES +R999: /* jumps here when done */ +#endif +#ifdef VERBOSE + cpu_printf("%3d: proc %d reverses %d, %d to %d\n", + depth, II, t->forw, tt, t->st); + cpu_printf("\t%s [abit=%d,adepth=%d,tau=%d,%d]\n", + t->tp, now._a_t, A_depth, trpt->tau, (trpt-1)->tau); +#endif +#ifndef NOREDUCE + /* pass the proviso tags */ + if ((trpt->tau&8) /* rv or atomic */ + && (trpt->tau&16)) + (trpt-1)->tau |= 16; +#ifdef SAFETY + if ((trpt->tau&8) /* rv or atomic */ + && (trpt->tau&64)) + (trpt-1)->tau |= 64; +#endif +#endif + depth--; trpt--; + +#ifdef NSUCC + trpt->n_succ++; +#endif +#ifdef NIBIS + (trans[ot][tt])->om = _m; /* head of list */ +#endif + /* i.e., not set if rv fails */ + if (_m) + { +#if defined(VERI) && !defined(NP) + if (II == 0 && verbose && !reached[ot][t->st]) + { + printf("depth %d: Claim reached state %d (line %d)\n", + depth, t->st, src_claim [t->st]); + fflush(stdout); + } +#endif + reached[ot][t->st] = 1; + reached[ot][tt] = 1; + } +#ifdef HAS_UNLESS + else trpt->e_state = 0; /* undo */ +#endif + if (_m>_n||(_n>3&&_m!=0)) _n=_m; + ((P0 *)this)->_p = tt; + } /* all options */ +#ifdef RANDOMIZE + if (!t && ooi > 0) + { t = trans[ot][tt]; + #ifdef VERBOSE + printf("randomizer: continue for %d more\n", ooi); + #endif + goto domore; + } + #ifdef VERBOSE + else + printf("randomizer: done\n"); + #endif +#endif +#ifndef NOFAIR + /* Fairness: undo Rule 2 */ + if ((trpt->o_pm&32) + && (trpt->o_pm&64)) + { if (trpt->o_pm&1) + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 1) + now._cnt[now._a_t&1] = 2; +#endif + now._cnt[now._a_t&1] += 1; +#ifdef VERBOSE + printf("%3d: proc %d fairness ", depth, II); + printf("undo Rule 2, cnt=%d, _a_t=%d\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm &= ~(32|64); + } else + { if (_n > 0) + { + trpt->o_pm &= ~64; +#ifdef REVERSE + II = From-1; +#else + II = From+1; +#endif + } } } +#endif +#ifdef VERI + if (II == 0) break; /* never claim */ +#endif + } /* all processes */ +#ifdef NSUCC + tally_succ(trpt->n_succ); +#endif +#ifdef SCHED + if (_n == 0 /* no process could move */ + #ifdef VERI + && II != 0 + #endif + && depth > 0 + && trpt->sched_limit >= sched_max) + { _n = 1; /* not a deadlock */ + } +#endif +#ifndef NOFAIR + /* Fairness: undo Rule 2 */ + if (trpt->o_pm&32) /* remains if proc blocked */ + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 1) + now._cnt[now._a_t&1] = 2; +#endif + now._cnt[now._a_t&1] += 1; +#ifdef VERBOSE + printf("%3d: proc -- fairness ", depth); + printf("undo Rule 2, cnt=%d, _a_t=%d\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm &= ~32; + } +#ifndef NP + if (fairness + && _n == 0 /* nobody moved */ +#ifdef VERI + && !(trpt->tau&4) /* in program move */ +#endif + && !(trpt->tau&8) /* not an atomic one */ +#ifdef OTIM + && ((trpt->tau&1) || endstate()) +#else +#ifdef ETIM + && (trpt->tau&1) /* already tried timeout */ +#endif +#endif +#ifndef NOREDUCE + /* see below */ + && !((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) +#endif + && now._cnt[now._a_t&1] > 0) /* needed more procs */ + { depth++; trpt++; + trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4)); + trpt->bup.oval = now._cnt[now._a_t&1]; + now._cnt[now._a_t&1] = 1; +#ifdef VERI + trpt->tau = 4; +#else + trpt->tau = 0; +#endif +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif +#if defined(VERBOSE) || defined(CHECK) + printf("%3d: fairness default move ", depth); + printf("(all procs block)\n"); +#endif + goto Down; + } +#endif +Q999: /* returns here with _n>0 when done */; + if (trpt->o_pm&8) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm &= ~8; +#ifdef VERBOSE + printf("%3d: fairness undo Rule 1, _a_t=%d\n", + depth, now._a_t); +#endif + } + if (trpt->o_pm&16) + { now._a_t |= 2; + now._cnt[now._a_t&1] = 1; + trpt->o_pm &= ~16; +#ifdef VERBOSE + printf("%3d: fairness undo Rule 3, _a_t=%d\n", + depth, now._a_t); +#endif + } +#endif +#ifndef NOREDUCE +#ifdef SAFETY +#ifdef LOOPSTATE + /* at least one move that was preselected at this */ + /* level, blocked or was a loop control flow point */ + if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) +#else + /* preselected move - no successors outside stack */ + if ((trpt->tau&32) && !(trpt->tau&64)) +#endif +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif +#ifdef DEBUG + printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, _n, trpt->tau); +#endif + _n = 0; trpt->tau &= ~(16|32|64); +#ifdef REVERSE + if (II <= To) /* II already decremented */ +#else + if (II >= BASE) /* II already decremented */ +#endif + goto Resume; + else + goto Again; + } +#else + /* at least one move that was preselected at this */ + /* level, blocked or truncated at the next level */ +/* implied: #ifdef FULLSTACK */ + if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) + { +#ifdef DEBUG + printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, (int) _n, trpt->tau); +#endif + if (a_cycles && (trpt->tau&16)) + { if (!(now._a_t&1)) + { +#ifdef DEBUG + printf("%3d: setting proviso bit\n", depth); +#endif +#ifndef BITSTATE +#ifdef MA +#ifdef VERI + (trpt-1)->proviso = 1; +#else + trpt->proviso = 1; +#endif +#else +#ifdef VERI + if ((trpt-1)->ostate) + ((char *)&((trpt-1)->ostate->state))[0] |= 128; +#else + ((char *)&(trpt->ostate->state))[0] |= 128; +#endif +#endif +#else +#ifdef VERI + if ((trpt-1)->ostate) + (trpt-1)->ostate->proviso = 1; +#else + trpt->ostate->proviso = 1; +#endif +#endif +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif + _n = 0; trpt->tau &= ~(16|32|64); + goto Again; /* do full search */ + } /* else accept reduction */ + } else +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif + _n = 0; trpt->tau &= ~(16|32|64); +#ifdef REVERSE + if (II <= To) /* already decremented */ +#else + if (II >= BASE) /* already decremented */ +#endif + goto Resume; + else + goto Again; + } } +/* #endif */ +#endif +#endif + if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2))) + { +#ifdef DEBUG + cpu_printf("%3d: no move [II=%d, tau=%d, boq=%d]\n", + depth, II, trpt->tau, boq); +#endif +#if SYNC + /* ok if a rendez-vous fails: */ + if (boq != -1) goto Done; +#endif + /* ok if no procs or we're at maxdepth */ + if ((now._nr_pr == 0 && (!strict || qs_empty())) +#ifdef OTIM + || endstate() +#endif + || depth >= maxdepth-1) goto Done; + if ((trpt->tau&8) && !(trpt->tau&4)) + { trpt->tau &= ~(1|8); + /* 1=timeout, 8=atomic */ +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif +#ifdef DEBUG + cpu_printf("%3d: atomic step proc %d unexecutable\n", depth, II+1); +#endif +#ifdef VERI + trpt->tau |= 4; /* switch to claim */ +#endif + goto AllOver; + } +#ifdef ETIM + if (!(trpt->tau&1)) /* didn't try timeout yet */ + { +#ifdef VERI + if (trpt->tau&4) + { +#ifndef NTIM + if (trpt->tau&2) /* requested */ +#endif + { trpt->tau |= 1; + trpt->tau &= ~2; +#ifdef DEBUG + cpu_printf("%d: timeout\n", depth); +#endif + goto Stutter; + } } + else + { /* only claim can enable timeout */ + if ((trpt->tau&8) + && !((trpt-1)->tau&4)) +/* blocks inside an atomic */ goto BreakOut; +#ifdef DEBUG + cpu_printf("%d: req timeout\n", + depth); +#endif + (trpt-1)->tau |= 2; /* request */ +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#else +#ifdef DEBUG + cpu_printf("%d: timeout\n", depth); +#endif + trpt->tau |= 1; + goto Again; +#endif + } +#endif +#ifdef VERI +BreakOut: +#ifndef NOSTUTTER + if (!(trpt->tau&4)) + { trpt->tau |= 4; /* claim stuttering */ + trpt->tau |= 128; /* stutter mark */ +#ifdef DEBUG + cpu_printf("%d: claim stutter\n", depth); +#endif + goto Stutter; + } +#else + ; +#endif +#else + if (!noends && !a_cycles && !endstate()) + { depth--; trpt--; /* new 4.2.3 */ + uerror("invalid end state"); + depth++; trpt++; + } +#ifndef NOSTUTTER + else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */ + { depth--; trpt--; + uerror("accept stutter"); + depth++; trpt++; + } +#endif +#endif + } +Done: + if (!(trpt->tau&8)) /* not in atomic seqs */ + { +#ifndef SAFETY + if (_n != 0 +#ifdef VERI + /* --after-- a program-step, i.e., */ + /* after backtracking a claim-step */ + && (trpt->tau&4) + /* with at least one running process */ + /* unless in a stuttered accept state */ + && ((now._nr_pr > 1) || (trpt->o_pm&2)) +#endif + && !(now._a_t&1)) + { +#ifndef NOFAIR + if (fairness) + { +#ifdef VERBOSE + cpu_printf("Consider check %d %d...\n", + now._a_t, now._cnt[0]); +#endif + if ((now._a_t&2) /* A-bit */ + && (now._cnt[0] == 1)) + checkcycles(); + } else +#endif + if (a_cycles && (trpt->o_pm&2)) + checkcycles(); + } +#endif +#ifndef MA +#if defined(FULLSTACK) || defined(CNTRSTACK) +#ifdef VERI + if (boq == -1 + && (((trpt->tau&4) && !(trpt->tau&128)) + || ( (trpt-1)->tau&128))) +#else + if (boq == -1) +#endif + { +#ifdef DEBUG2 +#if defined(FULLSTACK) + printf("%d: zapping %u (%d)\n", + depth, trpt->ostate, + (trpt->ostate)?trpt->ostate->tagged:0); +#endif +#endif + onstack_zap(); + } +#endif +#else +#ifdef VERI + if (boq == -1 + && (((trpt->tau&4) && !(trpt->tau&128)) + || ( (trpt-1)->tau&128))) +#else + if (boq == -1) +#endif + { +#ifdef DEBUG + printf("%d: zapping\n", depth); +#endif + onstack_zap(); +#ifndef NOREDUCE + if (trpt->proviso) + gstore((char *) &now, vsize, 1); +#endif + } +#endif + } + if (depth > 0) + { +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +} + +#else +void new_state(void) { /* place holder */ } +#endif + +void +assert(int a, char *s, int ii, int tt, Trans *t) +{ + if (!a && !noasserts) + { char bad[1024]; + strcpy(bad, "assertion violated "); + if (strlen(s) > 1000) + { strncpy(&bad[19], (const char *) s, 1000); + bad[1019] = '\0'; + } else + strcpy(&bad[19], s); + uerror(bad); + } +} +#ifndef NOBOUNDCHECK +int +Boundcheck(int x, int y, int a1, int a2, Trans *a3) +{ + assert((x >= 0 && x < y), "- invalid array index", + a1, a2, a3); + return x; +} +#endif +void +wrap_stats(void) +{ + if (nShadow>0) + printf("%9.8g states, stored (%g visited)\n", + nstates - nShadow, nstates); + else + printf("%9.8g states, stored\n", nstates); +#ifdef BFS +#if SYNC + printf(" %8g nominal states (- rv and atomic)\n", nstates-midrv-nlinks+revrv); + printf(" %8g rvs succeeded\n", midrv-failedrv); +#else + printf(" %8g nominal states (stored-atomic)\n", nstates-nlinks); +#endif +#ifdef DEBUG + printf(" %8g midrv\n", midrv); + printf(" %8g failedrv\n", failedrv); + printf(" %8g revrv\n", revrv); +#endif +#endif + printf("%9.8g states, matched\n", truncs); +#ifdef CHECK + printf("%9.8g matches within stack\n",truncs2); +#endif + if (nShadow>0) + printf("%9.8g transitions (= visited+matched)\n", + nstates+truncs); + else + printf("%9.8g transitions (= stored+matched)\n", + nstates+truncs); + printf("%9.8g atomic steps\n", nlinks); + if (nlost) printf("%g lost messages\n", (double) nlost); + +#ifndef BITSTATE + printf("hash conflicts: %9.8g (resolved)\n", hcmp); + #ifndef AUTO_RESIZE + if (hcmp > (double) (1< 100.)\n\n", + (double)(((double) udmem) * 8.0) / (double) nstates); + else + printf("\nhash factor: %4g (best if > 100.)\n\n", + (double)(1<<(ssize-8)) / (double) nstates * 256.0); + printf("bits set per state: %u (-k%u)\n", hfns, hfns); + #if 0 + if (udmem) + { printf("total bits available: %8g (-M%ld)\n", + ((double) udmem) * 8.0, udmem/(1024L*1024L)); + } else + printf("total bits available: %8g (-w%d)\n", + ((double) (ONE_L << (ssize-4)) * 16.0), ssize); + #endif +#endif +#ifdef BFS_DISK + printf("bfs disk reads: %ld writes %ld -- diff %ld\n", + bfs_dsk_reads, bfs_dsk_writes, bfs_dsk_writes-bfs_dsk_reads); + if (bfs_dsk_read >= 0) (void) close(bfs_dsk_read); + if (bfs_dsk_write >= 0) (void) close(bfs_dsk_write); + (void) unlink("pan_bfs_dsk.tmp"); +#endif +} + +void +wrapup(void) +{ +#if defined(BITSTATE) || !defined(NOCOMP) + double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0; + #if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM)) + int mverbose = 1; + #else + int mverbose = verbose; + #endif +#endif +#if NCORE>1 + if (verbose) cpu_printf("wrapup -- %d error(s)\n", errors); + if (core_id != 0) + { +#ifdef USE_DISK + void dsk_stats(void); + dsk_stats(); +#endif + if (search_terminated != NULL) + { *search_terminated |= 2; /* wrapup */ + } + exit(0); /* normal termination, not an error */ + } +#endif +#if !defined(WIN32) && !defined(WIN64) + signal(SIGINT, SIG_DFL); +#endif + printf("\n(%s)\n", SpinVersion); + if (!done) printf("Warning: Search not completed\n"); +#ifdef SC + (void) unlink((const char *)stackfile); +#endif +#if NCORE>1 + if (a_cycles) + { printf(" + Multi-Core (NCORE=%d)\n", NCORE); + } else + { printf(" + Multi-Core (NCORE=%d -z%d)\n", NCORE, z_handoff); + } +#endif +#ifdef BFS + printf(" + Using Breadth-First Search\n"); +#endif +#ifndef NOREDUCE + printf(" + Partial Order Reduction\n"); +#endif +#ifdef REVERSE + printf(" + Reverse Depth-First Search Order\n"); +#endif +#ifdef T_REVERSE + printf(" + Reverse Transition Ordering\n"); +#endif +#ifdef RANDOMIZE + printf(" + Randomized Transition Ordering\n"); +#endif +#ifdef SCHED + printf(" + Scheduling Restriction (-DSCHED=%d)\n", sched_max); +#endif +#ifdef COLLAPSE + printf(" + Compression\n"); +#endif +#ifdef MA + printf(" + Graph Encoding (-DMA=%d)\n", MA); + #ifdef R_XPT + printf(" Restarted from checkpoint %s.xpt\n", PanSource); + #endif +#endif +#ifdef CHECK + #ifdef FULLSTACK + printf(" + FullStack Matching\n"); + #endif + #ifdef CNTRSTACK + printf(" + CntrStack Matching\n"); + #endif +#endif +#ifdef BITSTATE + printf("\nBit statespace search for:\n"); +#else +#ifdef HC + printf("\nHash-Compact %d search for:\n", HC); +#else + printf("\nFull statespace search for:\n"); +#endif +#endif +#ifdef EVENT_TRACE +#ifdef NEGATED_TRACE + printf(" notrace assertion +\n"); +#else + printf(" trace assertion +\n"); +#endif +#endif +#ifdef VERI + printf(" never claim +\n"); + printf(" assertion violations "); + if (noasserts) + printf("- (disabled by -A flag)\n"); + else + printf("+ (if within scope of claim)\n"); +#else +#ifdef NOCLAIM + printf(" never claim - (not selected)\n"); +#else + printf(" never claim - (none specified)\n"); +#endif + printf(" assertion violations "); + if (noasserts) + printf("- (disabled by -A flag)\n"); + else + printf("+\n"); +#endif +#ifndef SAFETY +#ifdef NP + printf(" non-progress cycles "); +#else + printf(" acceptance cycles "); +#endif + if (a_cycles) + printf("+ (fairness %sabled)\n", + fairness?"en":"dis"); + else printf("- (not selected)\n"); +#else + printf(" cycle checks - (disabled by -DSAFETY)\n"); +#endif +#ifdef VERI + printf(" invalid end states - "); + printf("(disabled by "); + if (noends) + printf("-E flag)\n\n"); + else + printf("never claim)\n\n"); +#else + printf(" invalid end states "); + if (noends) + printf("- (disabled by -E flag)\n\n"); + else + printf("+\n\n"); +#endif + printf("State-vector %d byte, depth reached %ld", hmax, +#if NCORE>1 + (nr_handoffs * z_handoff) + +#endif + mreached); + printf(", errors: %d\n", errors); + fflush(stdout); +#ifdef MA + if (done) + { extern void dfa_stats(void); + if (maxgs+a_cycles+2 < MA) + printf("MA stats: -DMA=%d is sufficient\n", + maxgs+a_cycles+2); + dfa_stats(); + } +#endif + wrap_stats(); +#ifdef CHECK + printf("stackframes: %d/%d\n\n", smax, svmax); + printf("stats: fa %d, fh %d, zh %d, zn %d - ", + Fa, Fh, Zh, Zn); + printf("check %d holds %d\n", Ccheck, Cholds); + printf("stack stats: puts %d, probes %d, zaps %d\n", + PUT, PROBE, ZAPS); +#else + printf("\n"); +#endif + +#if defined(BITSTATE) || !defined(NOCOMP) + nr1 = (nstates-nShadow)* + (double)(hmax+sizeof(struct H_el)-sizeof(unsigned)); +#ifdef BFS + nr2 = 0.0; +#else + nr2 = (double) ((maxdepth+3)*sizeof(Trail)); +#endif +#ifndef BITSTATE +#if !defined(MA) || defined(COLLAPSE) + nr3 = (double) (ONE_L<1 && !defined(SEP_STATE) + tmp_nr -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE; +#endif + if (tmp_nr < 0.0) tmp_nr = 0.; + printf("Stats on memory usage (in Megabytes):\n"); + printf("%9.3f equivalent memory usage for states", + nr1/1048576.); /* 1024*1024=1048576 */ + printf(" (stored*(State-vector + overhead))\n"); + #if NCORE>1 && !defined(WIN32) && !defined(WIN64) + printf("%9.3f shared memory reserved for state storage\n", + mem_reserved/1048576.); + #ifdef SEP_HEAP + printf(" in %d local heaps of %7.3f MB each\n", + NCORE, mem_reserved/(NCORE*1048576.)); + #endif + printf("\n"); + #endif +#ifdef BITSTATE + if (udmem) + printf("%9.3f memory used for hash array (-M%ld)\n", + nr3/1048576., udmem/(1024L*1024L)); + else + printf("%9.3f memory used for hash array (-w%d)\n", + nr3/1048576., ssize); + if (nr5 > 0.0) + printf("%9.3f memory used for bit stack\n", + nr5/1048576.); + remainder = remainder - nr3 - nr5; +#else + printf("%9.3f actual memory usage for states", + tmp_nr/1048576.); + remainder -= tmp_nr; + printf(" ("); + if (tmp_nr > 0.) + { if (tmp_nr > nr1) printf("unsuccessful "); + printf("compression: %.2f%%)\n", + (100.0*tmp_nr)/nr1); + } else + printf("less than 1k)\n"); +#ifndef MA + if (tmp_nr > 0.) + { printf(" state-vector as stored = %.0f byte", + (tmp_nr)/(nstates-nShadow) - + (double) (sizeof(struct H_el) - sizeof(unsigned))); + printf(" + %ld byte overhead\n", + (long int) sizeof(struct H_el)-sizeof(unsigned)); + } +#endif +#if !defined(MA) || defined(COLLAPSE) + printf("%9.3f memory used for hash table (-w%d)\n", + nr3/1048576., ssize); + remainder -= nr3; +#endif +#endif +#ifndef BFS + printf("%9.3f memory used for DFS stack (-m%ld)\n", + nr2/1048576., maxdepth); + remainder -= nr2; +#endif +#if NCORE>1 + remainder -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE; + printf("%9.3f shared memory used for work-queues\n", + (GWQ_SIZE + (double) NCORE * LWQ_SIZE) /1048576.); + printf(" in %d queues of %7.3f MB each", + NCORE, (double) LWQ_SIZE /1048576.); + #ifndef NGQ + printf(" + a global q of %7.3f MB\n", + (double) GWQ_SIZE / 1048576.); + #else + printf("\n"); + #endif + #endif + if (remainder - fragment > 1048576.) + printf("%9.3f other (proc and chan stacks)\n", + (remainder-fragment)/1048576.); + if (fragment > 1048576.) + printf("%9.3f memory lost to fragmentation\n", + fragment/1048576.); + printf("%9.3f total actual memory usage\n\n", + memcnt/1048576.); + } +#ifndef MA + else +#endif +#endif +#ifndef MA + printf("%9.3f memory usage (Mbyte)\n\n", + memcnt/1048576.); +#endif +#ifdef COLLAPSE + printf("nr of templates: [ globals chans procs ]\n"); + printf("collapse counts: [ "); + { int i; for (i = 0; i < 256+2; i++) + if (ncomps[i] != 0) + printf("%d ", ncomps[i]); + printf("]\n"); + } +#endif + if ((done || verbose) && !no_rck) do_reach(); +#ifdef PEG + { int i; + printf("\nPeg Counts (transitions executed):\n"); + for (i = 1; i < NTRANS; i++) + { if (peg[i]) putpeg(i, peg[i]); + } } +#endif +#ifdef VAR_RANGES + dumpranges(); +#endif +#ifdef SVDUMP + if (vprefix > 0) close(svfd); +#endif +#ifdef LOOPSTATE + printf("%g loopstates hit\n", cnt_loops); +#endif +#ifdef NSUCC + dump_succ(); +#endif +#if NCORE>1 && defined(T_ALERT) + crash_report(); +#endif + pan_exit(0); +} + +void +stopped(int arg) +{ printf("Interrupted\n"); +#if NCORE>1 + was_interrupted = 1; +#endif + wrapup(); + pan_exit(0); +} + +#ifdef SFH +/* + * super fast hash, based on Paul Hsieh's function + * http://www.azillionmonkeys.com/qed/hash.html + */ +#include + #undef get16bits + #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) + #define get16bits(d) (*((const uint16_t *) (d))) + #endif + + #ifndef get16bits + #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) + #endif + +void +d_sfh(const char *s, int len) +{ uint32_t h = len, tmp; + int rem; + + rem = len & 3; + len >>= 2; + + for ( ; len > 0; len--) + { h += get16bits(s); + tmp = (get16bits(s+2) << 11) ^ h; + h = (h << 16) ^ tmp; + s += 2*sizeof(uint16_t); + h += h >> 11; + } + switch (rem) { + case 3: h += get16bits(s); + h ^= h << 16; + h ^= s[sizeof(uint16_t)] << 18; + h += h >> 11; + break; + case 2: h += get16bits(s); + h ^= h << 11; + h += h >> 17; + break; + case 1: h += *s; + h ^= h << 10; + h += h >> 1; + break; + } + h ^= h << 3; + h += h >> 5; + h ^= h << 4; + h += h >> 17; + h ^= h << 25; + h += h >> 6; + + K1 = h; +} +#endif + +#include +#if defined(HASH64) || defined(WIN64) +/* 64-bit Jenkins hash, 1997 + * http://burtleburtle.net/bob/c/lookup8.c + */ +#define mix(a,b,c) \ +{ a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} +#else +/* 32-bit Jenkins hash, 2006 + * http://burtleburtle.net/bob/c/lookup3.c + */ +#define rot(x,k) (((x)<<(k))|((x)>>(32-(k)))) + +#define mix(a,b,c) \ +{ a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +#define final(a,b,c) \ +{ c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} +#endif + +void +d_hash(uchar *kb, int nbytes) +{ uint8_t *bp; +#if defined(HASH64) || defined(WIN64) + uint64_t a = 0, b, c, n; + uint64_t *k = (uint64_t *) kb; +#else + uint32_t a, b, c, n; + uint32_t *k = (uint32_t *) kb; +#endif + /* extend to multiple of words, if needed */ + n = nbytes/WS; /* nr of words */ + a = nbytes - (n*WS); + if (a > 0) + { n++; + bp = kb + nbytes; + switch (a) { + case 3: *bp++ = 0; /* fall thru */ + case 2: *bp++ = 0; /* fall thru */ + case 1: *bp = 0; + case 0: break; + } } +#if defined(HASH64) || defined(WIN64) + b = HASH_CONST[HASH_NR]; + c = 0x9e3779b97f4a7c13LL; /* arbitrary value */ + while (n >= 3) + { a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + n -= 3; + k += 3; + } + c += (((uint64_t) nbytes)<<3); + switch (n) { + case 2: b += k[1]; + case 1: a += k[0]; + case 0: break; + } + mix(a,b,c); +#else + a = c = 0xdeadbeef + (n<<2); + b = HASH_CONST[HASH_NR]; + while (n > 3) + { a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + n -= 3; + k += 3; + } + switch (n) { + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + case 0: break; + } + final(a,b,c); +#endif + j1 = c&nmask; j3 = a&7; /* 1st bit */ + j2 = b&nmask; j4 = (a>>3)&7; /* 2nd bit */ + K1 = c; K2 = b; +} + +void +s_hash(uchar *cp, int om) +{ +#if defined(SFH) + d_sfh((const char *) cp, om); /* sets K1 */ +#else + d_hash(cp, om); /* sets K1 etc */ +#endif +#ifdef BITSTATE + if (S_Tab == H_tab) + j1 = K1 % omaxdepth; + else +#endif + if (ssize < 8*WS) + j1 = K1&mask; + else + j1 = K1; +} +#ifndef RANDSTOR +int *prerand; +void +inirand(void) +{ int i; + srand(123); /* fixed startpoint */ + prerand = (int *) emalloc((omaxdepth+3)*sizeof(int)); + for (i = 0; i < omaxdepth+3; i++) + prerand[i] = rand(); +} +int +pan_rand(void) +{ if (!prerand) inirand(); + return prerand[depth]; +} +#endif + +void +set_masks(void) /* 4.2.5 */ +{ + if (WS == 4 && ssize >= 32) + { mask = 0xffffffff; +#ifdef BITSTATE + switch (ssize) { + case 34: nmask = (mask>>1); break; + case 33: nmask = (mask>>2); break; + default: nmask = (mask>>3); break; + } +#else + nmask = mask; +#endif + } else if (WS == 8) + { mask = ((ONE_L<>3; +#else + nmask = mask; +#endif + } else if (WS != 4) + { fprintf(stderr, "pan: wordsize %ld not supported\n", (long int) WS); + exit(1); + } else /* WS == 4 and ssize < 32 */ + { mask = ((ONE_L<>3); + } +} + +static long reclaim_size; +static char *reclaim_mem; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) +#if NCORE>1 + #error cannot combine AUTO_RESIZE with NCORE>1 yet +#endif +static struct H_el **N_tab; +void +reverse_capture(struct H_el *p) +{ if (!p) return; + reverse_capture(p->nxt); + /* last element of list moves first */ + /* to preserve list-order */ + j2 = p->m_K1; + if (ssize < 8*WS) /* probably always true */ + { j2 &= mask; + } + p->nxt = N_tab[j2]; + N_tab[j2] = p; +} +void +resize_hashtable(void) +{ + if (WS == 4 && ssize >= 27 - 1) + { return; /* canot increase further */ + } + + ssize += 2; /* 4x size */ + + printf("pan: resizing hashtable to -w%d.. ", ssize); + + N_tab = (struct H_el **) + emalloc((ONE_L<1 + { int i, j; + strcpy(o_cmdline, ""); + for (j = 1; j < argc; j++) + { strcat(o_cmdline, argv[j]); + strcat(o_cmdline, " "); + } + /* printf("Command Line: %s\n", o_cmdline); */ + if (strlen(o_cmdline) >= sizeof(o_cmdline)) + { Uerror("option list too long"); + } } +#endif + while (argc > 1 && argv[1][0] == '-') + { switch (argv[1][1]) { +#ifndef SAFETY +#ifdef NP + case 'a': fprintf(efd, "error: -a disabled"); + usage(efd); break; +#else + case 'a': a_cycles = 1; break; +#endif +#endif + case 'A': noasserts = 1; break; + case 'b': bounded = 1; break; +#ifdef HAS_CODE + case 'C': coltrace = 1; goto samething; +#endif + case 'c': upto = atoi(&argv[1][2]); break; + case 'd': state_tables++; break; + case 'e': every_error = 1; Nr_Trails = 1; break; + case 'E': noends = 1; break; +#ifdef SC + case 'F': if (strlen(argv[1]) > 2) + stackfile = &argv[1][2]; + break; +#endif +#if !defined(SAFETY) && !defined(NOFAIR) + case 'f': fairness = 1; break; +#endif +#ifdef HAS_CODE + case 'g': gui = 1; goto samething; +#endif + case 'h': if (!argv[1][2]) usage(efd); else + HASH_NR = atoi(&argv[1][2])%33; break; + case 'I': iterative = 2; every_error = 1; break; + case 'i': iterative = 1; every_error = 1; break; + case 'J': like_java = 1; break; /* Klaus Havelund */ +#ifdef BITSTATE + case 'k': hfns = atoi(&argv[1][2]); break; +#endif +#ifdef SCHED + case 'L': sched_max = atoi(&argv[1][2]); break; +#endif +#ifndef SAFETY +#ifdef NP + case 'l': a_cycles = 1; break; +#else + case 'l': fprintf(efd, "error: -l disabled"); + usage(efd); break; +#endif +#endif +#ifdef BITSTATE + case 'M': udmem = atoi(&argv[1][2]); break; + case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break; +#else + case 'M': case 'G': + fprintf(stderr, "-M and -G affect only -DBITSTATE\n"); + break; +#endif + case 'm': maxdepth = atoi(&argv[1][2]); break; + case 'n': no_rck = 1; break; + case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]); + if (argv[2][0] != '-') /* check next arg */ + { trailfilename = argv[2]; + argc--; argv++; /* skip next arg */ + } + break; +#ifdef SVDUMP + case 'p': vprefix = atoi(&argv[1][2]); break; +#endif +#if NCORE==1 + case 'Q': quota = (double) 60.0 * (double) atoi(&argv[1][2]); break; +#endif + case 'q': strict = 1; break; + case 'R': Nrun = atoi(&argv[1][2]); break; +#ifdef HAS_CODE + case 'r': +samething: readtrail = 1; + if (isdigit(argv[1][2])) + whichtrail = atoi(&argv[1][2]); + else if (argc > 2 && argv[2][0] != '-') /* check next arg */ + { trailfilename = argv[2]; + argc--; argv++; /* skip next arg */ + } + break; + case 'S': silent = 1; goto samething; +#endif +#ifdef BITSTATE + case 's': hfns = 1; break; +#endif + case 'T': TMODE = 0444; break; + case 't': if (argv[1][2]) tprefix = &argv[1][2]; break; + case 'V': start_timer(); printf("Generated by %s\n", SpinVersion); + to_compile(); pan_exit(2); break; + case 'v': verbose++; break; + case 'w': ssize = atoi(&argv[1][2]); break; + case 'Y': signoff = 1; break; + case 'X': efd = stdout; break; + case 'x': exclusive = 1; break; +#if NCORE>1 + /* -B ip is passthru to proxy of remote ip address: */ + case 'B': argc--; argv++; break; + case 'Q': worker_pids[0] = atoi(&argv[1][2]); break; + /* -Un means that the nth worker should be instantiated as a proxy */ + case 'U': proxy_pid = atoi(&argv[1][2]); break; + /* -W means that this copy is started by a cluster-server as a remote */ + /* this flag is passed to ./pan_proxy, which interprets it */ + case 'W': remote_party++; break; + case 'Z': core_id = atoi(&argv[1][2]); + if (verbose) + { printf("cpu%d: pid %d parent %d\n", + core_id, getpid(), worker_pids[0]); + } + break; + case 'z': z_handoff = atoi(&argv[1][2]); break; +#else + case 'z': break; /* ignored for single-core */ +#endif + default : fprintf(efd, "saw option -%c\n", argv[1][1]); usage(efd); break; + } + argc--; argv++; + } + if (iterative && TMODE != 0666) + { TMODE = 0666; + fprintf(efd, "warning: -T ignored when -i or -I is used\n"); + } +#if defined(HASH32) && !defined(SFH) + if (WS > 4) + { fprintf(efd, "strong warning: compiling -DHASH32 on a 64-bit machine\n"); + fprintf(efd, " without -DSFH can slow down performance a lot\n"); + } +#endif +#if defined(WIN32) || defined(WIN64) + if (TMODE == 0666) + TMODE = _S_IWRITE | _S_IREAD; + else + TMODE = _S_IREAD; +#endif +#if NCORE>1 + store_proxy_pid = proxy_pid; /* for checks in mem_file() and someone_crashed() */ + if (core_id != 0) { proxy_pid = 0; } + #ifndef SEP_STATE + if (core_id == 0 && a_cycles) + { fprintf(efd, "hint: this search may be more efficient "); + fprintf(efd, "if pan.c is compiled -DSEP_STATE\n"); + } + #endif + if (z_handoff < 0) + { z_handoff = 20; /* conservative default - for non-liveness checks */ + } +#if defined(NGQ) || defined(LWQ_FIXED) + LWQ_SIZE = (double) (128.*1048576.); +#else + LWQ_SIZE = (double) ( z_handoff + 2.) * (double) sizeof(SM_frame); +#endif + #if NCORE>2 + if (a_cycles) + { fprintf(efd, "warning: the intended nr of cores to be used in liveness mode is 2\n"); + #ifndef SEP_STATE + fprintf(efd, "warning: without -DSEP_STATE there is no guarantee that all liveness violations are found\n"); + #endif + } + #endif + #ifdef HAS_HIDDEN + #error cannot use hidden variables when compiling multi-core + #endif +#endif +#ifdef BITSTATE + if (hfns <= 0) + { hfns = 1; + fprintf(efd, "warning: using -k%d as minimal usable value\n", hfns); + } +#endif + omaxdepth = maxdepth; +#ifdef BITSTATE + if (WS == 4 && ssize > 34) + { ssize = 34; + fprintf(efd, "warning: using -w%d as max usable value\n", ssize); +/* + * -w35 would not work: 35-3 = 32 but 1^31 is the largest + * power of 2 that can be represented in an unsigned long + */ + } +#else + if (WS == 4 && ssize > 27) + { ssize = 27; + fprintf(efd, "warning: using -w%d as max usable value\n", ssize); +/* + * for emalloc, the lookup table size multiplies by 4 for the pointers + * the largest power of 2 that can be represented in a ulong is 1^31 + * hence the largest number of lookup table slots is 31-4 = 27 + */ + } +#endif +#ifdef SC + hiwater = HHH = maxdepth-10; + DDD = HHH/2; + if (!stackfile) + { stackfile = (char *) emalloc(strlen(PanSource)+4+1); + sprintf(stackfile, "%s._s_", PanSource); + } + if (iterative) + { fprintf(efd, "error: cannot use -i or -I with -DSC\n"); + pan_exit(1); + } +#endif +#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA) + #warning -DR_XPT and -DW_XPT assume -DMA (ignored) +#endif + if (iterative && a_cycles) + fprintf(efd, "warning: -i or -I work for safety properties only\n"); +#ifdef BFS + #ifdef SC + #error -DBFS not compatible with -DSC + #endif + #ifdef HAS_LAST + #error -DBFS not compatible with _last + #endif + #ifdef HAS_STACK + #error cannot use c_track UnMatched with BFS + #endif + #ifdef REACH + #warning -DREACH is redundant when -DBFS is used + #endif +#endif +#if defined(MERGED) && defined(PEG) + #error to use -DPEG use: spin -o3 -a +#endif +#ifdef HC + #ifdef SFH + #error cannot combine -DHC and -DSFH + /* use of NOCOMP is the real reason */ + #else + #ifdef NOCOMP + #error cannot combine -DHC and -DNOCOMP + #endif + #endif + #ifdef BITSTATE + #error cannot combine -DHC and -DBITSTATE + #endif +#endif +#if defined(SAFETY) && defined(NP) + #error cannot combine -DNP and -DBFS or -DSAFETY +#endif +#ifdef MA + #ifdef BITSTATE + #error cannot combine -DMA and -DBITSTATE + #endif + #if MA <= 0 + #error usage: -DMA=N with N > 0 and N < VECTORSZ + #endif +#endif +#ifdef COLLAPSE + #ifdef BITSTATE + #error cannot combine -DBITSTATE and -DCOLLAPSE + #endif + #ifdef SFH + #error cannot combine -DCOLLAPSE and -DSFH + /* use of NOCOMP is the real reason */ + #else + #ifdef NOCOMP + #error cannot combine -DCOLLAPSE and -DNOCOMP + #endif + #endif +#endif + if (maxdepth <= 0 || ssize <= 1) usage(efd); +#if SYNC>0 && !defined(NOREDUCE) + if (a_cycles && fairness) + { fprintf(efd, "error: p.o. reduction not compatible with "); + fprintf(efd, "fairness (-f) in models\n"); + fprintf(efd, " with rendezvous operations: "); + fprintf(efd, "recompile with -DNOREDUCE\n"); + pan_exit(1); + } +#endif +#if defined(REM_VARS) && !defined(NOREDUCE) + #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE) +#endif +#if defined(NOCOMP) && !defined(BITSTATE) + if (a_cycles) + { fprintf(efd, "error: use of -DNOCOMP voids -l and -a\n"); + pan_exit(1); + } +#endif +#ifdef MEMLIM + memlim = ((double) MEMLIM) * (double) (1<<20); /* size in Mbyte */ +#endif +#ifndef BITSTATE + if (Nrun > 1) HASH_NR = Nrun - 1; +#endif + if (Nrun < 1 || Nrun > 32) + { fprintf(efd, "error: invalid arg for -R\n"); + usage(efd); + } +#ifndef SAFETY + if (fairness && !a_cycles) + { fprintf(efd, "error: -f requires -a or -l\n"); + usage(efd); + } + #if ACCEPT_LAB==0 + if (a_cycles) + { fprintf(efd, "error: no accept labels defined "); + fprintf(efd, "in model (for option -a)\n"); + usage(efd); + } + #endif +#endif +#ifndef NOREDUCE + #ifdef HAS_ENABLED + #error use of enabled() requires -DNOREDUCE + #endif + #ifdef HAS_PCVALUE + #error use of pcvalue() requires -DNOREDUCE + #endif + #ifdef HAS_BADELSE + #error use of 'else' combined with i/o stmnts requires -DNOREDUCE + #endif + #ifdef HAS_LAST + #error use of _last requires -DNOREDUCE + #endif +#endif +#if SYNC>0 && !defined(NOREDUCE) + #ifdef HAS_UNLESS + fprintf(efd, "warning: use of a rendezvous stmnts in the escape\n"); + fprintf(efd, " of an unless clause, if present, could make p.o. reduction\n"); + fprintf(efd, " invalid (use -DNOREDUCE to avoid this)\n"); + #ifdef BFS + fprintf(efd, " (this type of rv is also not compatible with -DBFS)\n"); + #endif + #endif +#endif +#if SYNC>0 && defined(BFS) + #warning use of rendezvous with BFS does not preserve all invalid endstates +#endif +#if !defined(REACH) && !defined(BITSTATE) + if (iterative != 0 && a_cycles == 0) + { fprintf(efd, "warning: -i and -I need -DREACH to work accurately\n"); + } +#endif +#if defined(BITSTATE) && defined(REACH) + #warning -DREACH is voided by -DBITSTATE +#endif +#if defined(MA) && defined(REACH) + #warning -DREACH is voided by -DMA +#endif +#if defined(FULLSTACK) && defined(CNTRSTACK) + #error cannot combine -DFULLSTACK and -DCNTRSTACK +#endif +#if defined(VERI) + #if ACCEPT_LAB>0 + #ifndef BFS + if (!a_cycles + #ifdef HAS_CODE + && !readtrail + #endif + #if NCORE>1 + && core_id == 0 + #endif + && !state_tables) + { fprintf(efd, "warning: never claim + accept labels "); + fprintf(efd, "requires -a flag to fully verify\n"); + } + #else + if (!state_tables + #ifdef HAS_CODE + && !readtrail + #endif + ) + { fprintf(efd, "warning: verification in BFS mode "); + fprintf(efd, "is restricted to safety properties\n"); + } + #endif + #endif +#endif +#ifndef SAFETY + if (!a_cycles + #ifdef HAS_CODE + && !readtrail + #endif + #if NCORE>1 + && core_id == 0 + #endif + && !state_tables) + { fprintf(efd, "hint: this search is more efficient "); + fprintf(efd, "if pan.c is compiled -DSAFETY\n"); + } + #ifndef NOCOMP + if (!a_cycles) + { S_A = 0; + } else + { if (!fairness) + { S_A = 1; /* _a_t */ + #ifndef NOFAIR + } else /* _a_t and _cnt[NFAIR] */ + { S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2; + /* -2 because first two uchars in now are masked */ + #endif + } } + #endif +#endif + signal(SIGINT, stopped); + set_masks(); +#ifdef BFS + trail = (Trail *) emalloc(6*sizeof(Trail)); + trail += 3; +#else + trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail)); + trail++; /* protect trpt-1 refs at depth 0 */ +#endif +#ifdef SVDUMP + if (vprefix > 0) + { char nm[64]; + sprintf(nm, "%s.svd", PanSource); + if ((svfd = creat(nm, TMODE)) < 0) + { fprintf(efd, "couldn't create %s\n", nm); + vprefix = 0; + } } +#endif +#ifdef RANDSTOR + srand(123); +#endif +#if SYNC>0 && ASYNC==0 + set_recvs(); +#endif + run(); + done = 1; + wrapup(); + return 0; +} + +void +usage(FILE *fd) +{ + fprintf(fd, "%s\n", SpinVersion); + fprintf(fd, "Valid Options are:\n"); +#ifndef SAFETY +#ifdef NP + fprintf(fd, " -a -> is disabled by -DNP "); + fprintf(fd, "(-DNP compiles for -l only)\n"); +#else + fprintf(fd, " -a find acceptance cycles\n"); +#endif +#else + fprintf(fd, " -a,-l,-f -> are disabled by -DSAFETY\n"); +#endif + fprintf(fd, " -A ignore assert() violations\n"); + fprintf(fd, " -b consider it an error to exceed the depth-limit\n"); + fprintf(fd, " -cN stop at Nth error "); + fprintf(fd, "(defaults to -c1)\n"); + fprintf(fd, " -d print state tables and stop\n"); + fprintf(fd, " -e create trails for all errors\n"); + fprintf(fd, " -E ignore invalid end states\n"); +#ifdef SC + fprintf(fd, " -Ffile use 'file' to store disk-stack\n"); +#endif +#ifndef NOFAIR + fprintf(fd, " -f add weak fairness (to -a or -l)\n"); +#endif + fprintf(fd, " -hN use different hash-seed N:1..32\n"); + fprintf(fd, " -i search for shortest path to error\n"); + fprintf(fd, " -I like -i, but approximate and faster\n"); + fprintf(fd, " -J reverse eval order of nested unlesses\n"); +#ifdef BITSTATE + fprintf(fd, " -kN set N bits per state (defaults to 3)\n"); +#endif +#ifdef SCHED + fprintf(fd, " -LN set scheduling restriction to N (default 10)\n"); +#endif +#ifndef SAFETY +#ifdef NP + fprintf(fd, " -l find non-progress cycles\n"); +#else + fprintf(fd, " -l find non-progress cycles -> "); + fprintf(fd, "disabled, requires "); + fprintf(fd, "compilation with -DNP\n"); +#endif +#endif +#ifdef BITSTATE + fprintf(fd, " -MN use N Megabytes for bitstate hash array\n"); + fprintf(fd, " -GN use N Gigabytes for bitstate hash array\n"); +#endif + fprintf(fd, " -mN max depth N steps (default=10k)\n"); + fprintf(fd, " -n no listing of unreached states\n"); +#ifdef SVDUMP + fprintf(fd, " -pN create svfile (save N bytes per state)\n"); +#endif + fprintf(fd, " -QN set time-limit on execution of N minutes\n"); + fprintf(fd, " -q require empty chans in valid end states\n"); +#ifdef HAS_CODE + fprintf(fd, " -r read and execute trail - can add -v,-n,-PN,-g,-C\n"); + fprintf(fd, " -rN read and execute N-th error trail\n"); + fprintf(fd, " -C read and execute trail - columnated output (can add -v,-n)\n"); + fprintf(fd, " -PN read and execute trail - restrict trail output to proc N\n"); + fprintf(fd, " -g read and execute trail + msc gui support\n"); + fprintf(fd, " -S silent replay: only user defined printfs show\n"); +#endif +#ifdef BITSTATE + fprintf(fd, " -RN repeat run Nx with N "); + fprintf(fd, "[1..32] independent hash functions\n"); + fprintf(fd, " -s same as -k1 (single bit per state)\n"); +#endif + fprintf(fd, " -T create trail files in read-only mode\n"); + fprintf(fd, " -tsuf replace .trail with .suf on trailfiles\n"); + fprintf(fd, " -V print SPIN version number\n"); + fprintf(fd, " -v verbose -- filenames in unreached state listing\n"); + fprintf(fd, " -wN hashtable of 2^N entries "); + fprintf(fd, "(defaults to -w%d)\n", ssize); + fprintf(fd, " -x do not overwrite an existing trail file\n"); +#if NCORE>1 + fprintf(fd, " -zN handoff states below depth N to 2nd cpu (multi_core)\n"); +#endif +#ifdef HAS_CODE + fprintf(fd, "\n options -r, -C, -PN, -g, and -S can optionally be followed by\n"); + fprintf(fd, " a filename argument, as in '-r filename', naming the trailfile\n"); +#endif +#if NCORE>1 + multi_usage(fd); +#endif + exit(1); +} + +char * +Malloc(unsigned long n) +{ char *tmp; +#ifdef MEMLIM + if (memcnt+ (double) n > memlim) goto err; +#endif +#if 1 + tmp = (char *) malloc(n); + if (!tmp) +#else + tmp = (char *) sbrk(n); + if (tmp == (char *) -ONE_L) +#endif + { +#ifdef MEMLIM +err: +#endif + printf("pan: out of memory\n"); +#ifdef MEMLIM + printf(" %g bytes used\n", memcnt); + printf(" %g bytes more needed\n", (double) n); + printf(" %g bytes limit\n", + memlim); +#endif +#ifdef COLLAPSE + printf("hint: to reduce memory, recompile with\n"); +#ifndef MA + printf(" -DMA=%d # better/slower compression, or\n", hmax); +#endif + printf(" -DBITSTATE # supertrace, approximation\n"); +#else +#ifndef BITSTATE + printf("hint: to reduce memory, recompile with\n"); +#ifndef HC + printf(" -DCOLLAPSE # good, fast compression, or\n"); +#ifndef MA + printf(" -DMA=%d # better/slower compression, or\n", hmax); +#endif + printf(" -DHC # hash-compaction, approximation\n"); +#endif + printf(" -DBITSTATE # supertrace, approximation\n"); +#endif +#endif +#if NCORE>1 + #ifdef FULL_TRAIL + printf(" omit -DFULL_TRAIL or use pan -c0 to reduce memory\n"); + #endif + #ifdef SEP_STATE + printf("hint: to reduce memory, recompile without\n"); + printf(" -DSEP_STATE # may be faster, but uses more memory\n"); + #endif +#endif + wrapup(); + } + memcnt += (double) n; + return tmp; +} + +#define CHUNK (100*VECTORSZ) + +char * +emalloc(unsigned long n) /* never released or reallocated */ +{ char *tmp; + if (n == 0) + return (char *) NULL; + if (n&(sizeof(void *)-1)) /* for proper alignment */ + n += sizeof(void *)-(n&(sizeof(void *)-1)); + if ((unsigned long) left < n) + { grow = (n < CHUNK) ? CHUNK : n; + have = Malloc(grow); + fragment += (double) left; + left = grow; + } + tmp = have; + have += (long) n; + left -= (long) n; + memset(tmp, 0, n); + return tmp; +} +void +Uerror(char *str) +{ /* always fatal */ + uerror(str); +#if NCORE>1 + sudden_stop("Uerror"); +#endif + wrapup(); +} + +#if defined(MA) && !defined(SAFETY) +int +Unwind(void) +{ Trans *t; uchar ot, _m; int tt; short II; +#ifdef VERBOSE + int i; +#endif + uchar oat = now._a_t; + now._a_t &= ~(1|16|32); + memcpy((char *) &comp_now, (char *) &now, vsize); + now._a_t = oat; +Up: +#ifdef SC + trpt = getframe(depth); +#endif +#ifdef VERBOSE + printf("%d State: ", depth); + for (i = 0; i < vsize; i++) printf("%d%s,", + ((char *)&now)[i], Mask[i]?"*":""); + printf("\n"); +#endif +#ifndef NOFAIR + if (trpt->o_pm&128) /* fairness alg */ + { now._cnt[now._a_t&1] = trpt->bup.oval; + depth--; +#ifdef SC + trpt = getframe(depth); +#else + trpt--; +#endif + goto Q999; + } +#endif +#ifdef HAS_LAST +#ifdef VERI + { int d; Trail *trl; + now._last = 0; + for (d = 1; d < depth; d++) + { trl = getframe(depth-d); /* was trl = (trpt-d); */ + if (trl->pr != 0) + { now._last = trl->pr - BASE; + break; + } } } +#else + now._last = (depth<1)?0:(trpt-1)->pr; +#endif +#endif +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif + if ((now._a_t&1) && depth <= A_depth) + { now._a_t &= ~(1|16|32); + if (fairness) now._a_t |= 2; /* ? */ + A_depth = 0; + goto CameFromHere; /* checkcycles() */ + } + t = trpt->o_t; + ot = trpt->o_ot; II = trpt->pr; + tt = trpt->o_tt; this = pptr(II); + _m = do_reverse(t, II, trpt->o_m); +#ifdef VERBOSE + printf("%3d: proc %d ", depth, II); + printf("reverses %d, %d to %d,", + t->forw, tt, t->st); + printf(" %s [abit=%d,adepth=%d,", + t->tp, now._a_t, A_depth); + printf("tau=%d,%d] \n", + trpt->tau, (trpt-1)->tau); +#endif + depth--; +#ifdef SC + trpt = getframe(depth); +#else + trpt--; +#endif + /* reached[ot][t->st] = 1; 3.4.13 */ + ((P0 *)this)->_p = tt; +#ifndef NOFAIR + if ((trpt->o_pm&32)) + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 0) + now._cnt[now._a_t&1] = 1; +#endif + now._cnt[now._a_t&1] += 1; + } +Q999: + if (trpt->o_pm&8) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + } + if (trpt->o_pm&16) + now._a_t |= 2; +#endif +CameFromHere: + if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0) + return depth; + if (depth > 0) goto Up; + return 0; +} +#endif +static char unwinding; +void +uerror(char *str) +{ static char laststr[256]; + int is_cycle; + + if (unwinding) return; /* 1.4.2 */ + if (strncmp(str, laststr, 254)) +#if NCORE>1 + cpu_printf("pan: %s (at depth %ld)\n", str, +#else + printf("pan: %s (at depth %ld)\n", str, +#endif +#if NCORE>1 + (nr_handoffs * z_handoff) + +#endif + ((depthfound==-1)?depth:depthfound)); + strncpy(laststr, str, 254); + errors++; +#ifdef HAS_CODE + if (readtrail) { wrap_trail(); return; } +#endif + is_cycle = (strstr(str, " cycle") != (char *) 0); + if (!is_cycle) + { depth++; trpt++; + } + if ((every_error != 0) + || errors == upto) + { +#if defined(MA) && !defined(SAFETY) + if (is_cycle) + { int od = depth; + unwinding = 1; + depthfound = Unwind(); + unwinding = 0; + depth = od; + } +#endif +#if NCORE>1 + writing_trail = 1; +#endif +#ifdef BFS + if (depth > 1) trpt--; + nuerror(str); + if (depth > 1) trpt++; +#else + putrail(); +#endif +#if defined(MA) && !defined(SAFETY) + if (strstr(str, " cycle")) + { if (every_error) + printf("sorry: MA writes 1 trail max\n"); + wrapup(); /* no recovery from unwind */ + } +#endif +#if NCORE>1 + if (search_terminated != NULL) + { *search_terminated |= 4; /* uerror */ + } + writing_trail = 0; +#endif + } + if (!is_cycle) + { depth--; trpt--; /* undo */ + } +#ifndef BFS + if (iterative != 0 && maxdepth > 0) + { maxdepth = (iterative == 1)?(depth-1):(depth/2); + warned = 1; + printf("pan: reducing search depth to %ld\n", + maxdepth); + } else +#endif + if (errors >= upto && upto != 0) + { +#if NCORE>1 + sudden_stop("uerror"); +#endif + wrapup(); + } + depthfound = -1; +} + +int +xrefsrc(int lno, S_F_MAP *mp, int M, int i) +{ Trans *T; int j, retval=1; + for (T = trans[M][i]; T; T = T->nxt) + if (T && T->tp) + { if (strcmp(T->tp, ".(goto)") == 0 + || strncmp(T->tp, "goto :", 6) == 0) + return 1; /* not reported */ + + printf("\tline %d", lno); + if (verbose) + for (j = 0; j < sizeof(mp); j++) + if (i >= mp[j].from && i <= mp[j].upto) + { printf(", \"%s\"", mp[j].fnm); + break; + } + printf(", state %d", i); + if (strcmp(T->tp, "") != 0) + { char *q; + q = transmognify(T->tp); + printf(", \"%s\"", q?q:""); + } else if (stopstate[M][i]) + printf(", -end state-"); + printf("\n"); + retval = 0; /* reported */ + } + return retval; +} + +void +r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp) +{ int i, m=0; + +#ifdef VERI + if (M == VERI && !verbose) return; +#endif + printf("unreached in proctype %s\n", procname[M]); + for (i = 1; i < N; i++) + if (which[i] == 0 + && (mapstate[M][i] == 0 + || which[mapstate[M][i]] == 0)) + m += xrefsrc((int) src[i], mp, M, i); + else + m++; + printf(" (%d of %d states)\n", N-1-m, N-1); +} +#if NCORE>1 && !defined(SEP_STATE) +static long rev_trail_cnt; + +#ifdef FULL_TRAIL +void +rev_trail(int fd, volatile Stack_Tree *st_tr) +{ long j; char snap[64]; + + if (!st_tr) + { return; + } + rev_trail(fd, st_tr->prv); +#ifdef VERBOSE + printf("%d (%d) LRT [%d,%d] -- %9u (root %9u)\n", + depth, rev_trail_cnt, st_tr->pr, st_tr->t_id, st_tr, stack_last[core_id]); +#endif + if (st_tr->pr != 255) + { sprintf(snap, "%ld:%d:%d\n", + rev_trail_cnt++, st_tr->pr, st_tr->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing trailfile\n"); + close(fd); + wrapup(); + return; + } + } else /* handoff point */ + { if (a_cycles) + { write(fd, "-1:-1:-1\n", 9); + } } +} +#endif +#endif + +void +putrail(void) +{ int fd; +#if defined VERI || defined(MERGED) + char snap[64]; +#endif +#if NCORE==1 || defined(SEP_STATE) || !defined(FULL_TRAIL) + long i, j; + Trail *trl; +#endif + fd = make_trail(); + if (fd < 0) return; +#ifdef VERI + sprintf(snap, "-2:%d:-2\n", VERI); + write(fd, snap, strlen(snap)); +#endif +#ifdef MERGED + sprintf(snap, "-4:-4:-4\n"); + write(fd, snap, strlen(snap)); +#endif +#if NCORE>1 && !defined(SEP_STATE) && defined(FULL_TRAIL) + rev_trail_cnt = 1; + enter_critical(GLOBAL_LOCK); + rev_trail(fd, stack_last[core_id]); + leave_critical(GLOBAL_LOCK); +#else + i = 1; /* trail starts at position 1 */ + #if NCORE>1 && defined(SEP_STATE) + if (cur_Root.m_vsize > 0) { i++; depth++; } + #endif + for ( ; i <= depth; i++) + { if (i == depthfound+1) + write(fd, "-1:-1:-1\n", 9); + trl = getframe(i); + if (!trl->o_t) continue; + if (trl->o_pm&128) continue; + sprintf(snap, "%ld:%d:%d\n", + i, trl->pr, trl->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing trailfile\n"); + close(fd); + wrapup(); + } } +#endif + close(fd); +#if NCORE>1 + cpu_printf("pan: wrote trailfile\n"); +#endif +} + +void +sv_save(void) /* push state vector onto save stack */ +{ if (!svtack->nxt) + { svtack->nxt = (Svtack *) emalloc(sizeof(Svtack)); + svtack->nxt->body = emalloc(vsize*sizeof(char)); + svtack->nxt->lst = svtack; + svtack->nxt->m_delta = vsize; + svmax++; + } else if (vsize > svtack->nxt->m_delta) + { svtack->nxt->body = emalloc(vsize*sizeof(char)); + svtack->nxt->lst = svtack; + svtack->nxt->m_delta = vsize; + svmax++; + } + svtack = svtack->nxt; +#if SYNC + svtack->o_boq = boq; +#endif + svtack->o_delta = vsize; /* don't compress */ + memcpy((char *)(svtack->body), (char *) &now, vsize); +#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1) + c_stack((uchar *) &(svtack->c_stack[0])); +#endif +#ifdef DEBUG + cpu_printf("%d: sv_save\n", depth); +#endif +} + +void +sv_restor(void) /* pop state vector from save stack */ +{ + memcpy((char *)&now, svtack->body, svtack->o_delta); +#if SYNC + boq = svtack->o_boq; +#endif +#if defined(C_States) && (HAS_TRACK==1) +#ifdef HAS_STACK + c_unstack((uchar *) &(svtack->c_stack[0])); +#endif + c_revert((uchar *) &(now.c_state[0])); +#endif + if (vsize != svtack->o_delta) + Uerror("sv_restor"); + if (!svtack->lst) + Uerror("error: v_restor"); + svtack = svtack->lst; +#ifdef DEBUG + cpu_printf(" sv_restor\n"); +#endif +} + +void +p_restor(int h) +{ int i; char *z = (char *) &now; + + proc_offset[h] = stack->o_offset; + proc_skip[h] = (uchar) stack->o_skip; +#ifndef XUSAFE + p_name[h] = stack->o_name; +#endif +#ifndef NOCOMP + for (i = vsize + stack->o_skip; i > vsize; i--) + Mask[i-1] = 1; /* align */ +#endif + vsize += stack->o_skip; + memcpy(z+vsize, stack->body, stack->o_delta); + vsize += stack->o_delta; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++) + Mask[vsize - i] = 1; /* pad */ + Mask[proc_offset[h]] = 1; /* _pid */ +#endif + if (BASE > 0 && h > 0) + ((P0 *)pptr(h))->_pid = h-BASE; + else + ((P0 *)pptr(h))->_pid = h; + i = stack->o_delqs; + now._nr_pr += 1; + if (!stack->lst) /* debugging */ + Uerror("error: p_restor"); + stack = stack->lst; + this = pptr(h); + while (i-- > 0) + q_restor(); +} + +void +q_restor(void) +{ char *z = (char *) &now; +#ifndef NOCOMP + int k, k_end; +#endif + q_offset[now._nr_qs] = stack->o_offset; + q_skip[now._nr_qs] = (uchar) stack->o_skip; +#ifndef XUSAFE + q_name[now._nr_qs] = stack->o_name; +#endif + vsize += stack->o_skip; + memcpy(z+vsize, stack->body, stack->o_delta); + vsize += stack->o_delta; +#ifndef NOVSZ + now._vsz = vsize; +#endif + now._nr_qs += 1; +#ifndef NOCOMP + k_end = stack->o_offset; + k = k_end - stack->o_skip; +#if SYNC +#ifndef BFS + if (q_zero(now._nr_qs)) k_end += stack->o_delta; +#endif +#endif + for ( ; k < k_end; k++) + Mask[k] = 1; +#endif + if (!stack->lst) /* debugging */ + Uerror("error: q_restor"); + stack = stack->lst; +} +typedef struct IntChunks { + int *ptr; + struct IntChunks *nxt; +} IntChunks; +IntChunks *filled_chunks[512]; +IntChunks *empty_chunks[512]; +int * +grab_ints(int nr) +{ IntChunks *z; + if (nr >= 512) Uerror("cannot happen grab_int"); + if (filled_chunks[nr]) + { z = filled_chunks[nr]; + filled_chunks[nr] = filled_chunks[nr]->nxt; + } else + { z = (IntChunks *) emalloc(sizeof(IntChunks)); + z->ptr = (int *) emalloc(nr * sizeof(int)); + } + z->nxt = empty_chunks[nr]; + empty_chunks[nr] = z; + return z->ptr; +} +void +ungrab_ints(int *p, int nr) +{ IntChunks *z; + if (!empty_chunks[nr]) Uerror("cannot happen ungrab_int"); + z = empty_chunks[nr]; + empty_chunks[nr] = empty_chunks[nr]->nxt; + z->ptr = p; + z->nxt = filled_chunks[nr]; + filled_chunks[nr] = z; +} +int +delproc(int sav, int h) +{ int d, i=0; +#ifndef NOCOMP + int o_vsize = vsize; +#endif + if (h+1 != (int) now._nr_pr) return 0; + + while (now._nr_qs + && q_offset[now._nr_qs-1] > proc_offset[h]) + { delq(sav); + i++; + } + d = vsize - proc_offset[h]; + if (sav) + { if (!stack->nxt) + { stack->nxt = (Stack *) + emalloc(sizeof(Stack)); + stack->nxt->body = + emalloc(Maxbody*sizeof(char)); + stack->nxt->lst = stack; + smax++; + } + stack = stack->nxt; + stack->o_offset = proc_offset[h]; +#if VECTORSZ>32000 + stack->o_skip = (int) proc_skip[h]; +#else + stack->o_skip = (short) proc_skip[h]; +#endif +#ifndef XUSAFE + stack->o_name = p_name[h]; +#endif + stack->o_delta = d; + stack->o_delqs = i; + memcpy(stack->body, (char *)pptr(h), d); + } + vsize = proc_offset[h]; + now._nr_pr = now._nr_pr - 1; + memset((char *)pptr(h), 0, d); + vsize -= (int) proc_skip[h]; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (i = vsize; i < o_vsize; i++) + Mask[i] = 0; /* reset */ +#endif + return 1; +} + +void +delq(int sav) +{ int h = now._nr_qs - 1; + int d = vsize - q_offset[now._nr_qs - 1]; +#ifndef NOCOMP + int k, o_vsize = vsize; +#endif + if (sav) + { if (!stack->nxt) + { stack->nxt = (Stack *) + emalloc(sizeof(Stack)); + stack->nxt->body = + emalloc(Maxbody*sizeof(char)); + stack->nxt->lst = stack; + smax++; + } + stack = stack->nxt; + stack->o_offset = q_offset[h]; +#if VECTORSZ>32000 + stack->o_skip = (int) q_skip[h]; +#else + stack->o_skip = (short) q_skip[h]; +#endif +#ifndef XUSAFE + stack->o_name = q_name[h]; +#endif + stack->o_delta = d; + memcpy(stack->body, (char *)qptr(h), d); + } + vsize = q_offset[h]; + now._nr_qs = now._nr_qs - 1; + memset((char *)qptr(h), 0, d); + vsize -= (int) q_skip[h]; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (k = vsize; k < o_vsize; k++) + Mask[k] = 0; /* reset */ +#endif +} + +int +qs_empty(void) +{ int i; + for (i = 0; i < (int) now._nr_qs; i++) + { if (q_sz(i) > 0) + return 0; + } + return 1; +} + +int +endstate(void) +{ int i; P0 *ptr; + for (i = BASE; i < (int) now._nr_pr; i++) + { ptr = (P0 *) pptr(i); + if (!stopstate[ptr->_t][ptr->_p]) + return 0; + } + if (strict) return qs_empty(); +#if defined(EVENT_TRACE) && !defined(OTIM) + if (!stopstate[EVENT_TRACE][now._event] && !a_cycles) + { printf("pan: event_trace not completed\n"); + return 0; + } +#endif + return 1; +} + +#ifndef SAFETY +void +checkcycles(void) +{ uchar o_a_t = now._a_t; +#ifdef SCHED + int o_limit; +#endif +#ifndef NOFAIR + uchar o_cnt = now._cnt[1]; +#endif +#ifdef FULLSTACK +#ifndef MA + struct H_el *sv = trpt->ostate; /* save */ +#else + uchar prov = trpt->proviso; /* save */ +#endif +#endif +#ifdef DEBUG + { int i; uchar *v = (uchar *) &now; + printf(" set Seed state "); +#ifndef NOFAIR + if (fairness) printf("(cnt = %d:%d, nrpr=%d) ", + now._cnt[0], now._cnt[1], now._nr_pr); +#endif + /* for (i = 0; i < n; i++) printf("%d,", v[i]); */ + printf("\n"); + } + printf("%d: cycle check starts\n", depth); +#endif + now._a_t |= (1|16|32); + /* 1 = 2nd DFS; (16|32) to help hasher */ +#ifndef NOFAIR + now._cnt[1] = now._cnt[0]; +#endif + memcpy((char *)&A_Root, (char *)&now, vsize); + A_depth = depthfound = depth; +#if NCORE>1 + mem_put_acc(); +#else + #ifdef SCHED + o_limit = trpt->sched_limit; + trpt->sched_limit = 0; + #endif + new_state(); /* start 2nd DFS */ + #ifdef SCHED + trpt->sched_limit = o_limit; + #endif +#endif + now._a_t = o_a_t; +#ifndef NOFAIR + now._cnt[1] = o_cnt; +#endif + A_depth = 0; depthfound = -1; +#ifdef DEBUG + printf("%d: cycle check returns\n", depth); +#endif +#ifdef FULLSTACK +#ifndef MA + trpt->ostate = sv; /* restore */ +#else + trpt->proviso = prov; +#endif +#endif +} +#endif + +#if defined(FULLSTACK) && defined(BITSTATE) +struct H_el *Free_list = (struct H_el *) 0; +void +onstack_init(void) /* to store stack states in a bitstate search */ +{ S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *)); +} +struct H_el * +grab_state(int n) +{ struct H_el *v, *last = 0; + if (H_tab == S_Tab) + { for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt) + { if ((int) v->tagged == n) + { if (last) + last->nxt = v->nxt; + else +gotcha: Free_list = v->nxt; + v->tagged = 0; + v->nxt = 0; +#ifdef COLLAPSE + v->ln = 0; +#endif + return v; + } + Fh++; last=v; + } + /* new: second try */ + v = Free_list; + if (v && ((int) v->tagged >= n)) + goto gotcha; + ngrabs++; + } + return (struct H_el *) + emalloc(sizeof(struct H_el)+n-sizeof(unsigned)); +} + +#else +#if NCORE>1 +struct H_el * +grab_state(int n) +{ struct H_el *grab_shared(int); + return grab_shared(sizeof(struct H_el)+n-sizeof(unsigned)); +} +#else + #ifndef AUTO_RESIZE + #define grab_state(n) (struct H_el *) \ + emalloc(sizeof(struct H_el)+n-sizeof(unsigned long)); + #else + struct H_el * + grab_state(int n) + { struct H_el *p; + int cnt = sizeof(struct H_el)+n-sizeof(unsigned long); + + if (reclaim_size >= cnt+WS) + { if ((cnt & (WS-1)) != 0) /* alignment */ + { cnt += WS - (cnt & (WS-1)); + } + p = (struct H_el *) reclaim_mem; + reclaim_mem += cnt; + reclaim_size -= cnt; + memset(p, 0, cnt); + } else + { p = (struct H_el *) emalloc(cnt); + } + return p; + } + #endif +#endif +#endif +#ifdef COLLAPSE +unsigned long +ordinal(char *v, long n, short tp) +{ struct H_el *tmp, *ntmp; long m; + struct H_el *olst = (struct H_el *) 0; + s_hash((uchar *)v, n); +#if NCORE>1 && !defined(SEP_STATE) + enter_critical(CS_ID); /* uses spinlock - 1..128 */ +#endif + tmp = H_tab[j1]; + if (!tmp) + { tmp = grab_state(n); + H_tab[j1] = tmp; + } else + for ( ;; olst = tmp, tmp = tmp->nxt) + { m = memcmp(((char *)&(tmp->state)), v, n); + if (n == tmp->ln) + { + if (m == 0) + goto done; + if (m < 0) + { +Insert: ntmp = grab_state(n); + ntmp->nxt = tmp; + if (!olst) + H_tab[j1] = ntmp; + else + olst->nxt = ntmp; + tmp = ntmp; + break; + } else if (!tmp->nxt) + { +Append: tmp->nxt = grab_state(n); + tmp = tmp->nxt; + break; + } + continue; + } + if (n < tmp->ln) + goto Insert; + else if (!tmp->nxt) + goto Append; + } + m = ++ncomps[tp]; +#ifdef FULLSTACK + tmp->tagged = m; +#else + tmp->st_id = m; +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + tmp->m_K1 = K1; +#endif + memcpy(((char *)&(tmp->state)), v, n); + tmp->ln = n; +done: +#if NCORE>1 && !defined(SEP_STATE) + leave_critical(CS_ID); /* uses spinlock */ +#endif +#ifdef FULLSTACK + return tmp->tagged; +#else + return tmp->st_id; +#endif +} + +int +compress(char *vin, int nin) /* collapse compression */ +{ char *w, *v = (char *) &comp_now; + int i, j; + unsigned long n; + static char *x; + static uchar nbytes[513]; /* 1 + 256 + 256 */ + static unsigned short nbytelen; + long col_q(int, char *); + long col_p(int, char *); +#ifndef SAFETY + if (a_cycles) + *v++ = now._a_t; +#ifndef NOFAIR + if (fairness) + for (i = 0; i < NFAIR; i++) + *v++ = now._cnt[i]; +#endif +#endif + nbytelen = 0; +#ifndef JOINPROCS + for (i = 0; i < (int) now._nr_pr; i++) + { n = col_p(i, (char *) 0); +#ifdef NOFIX + nbytes[nbytelen] = 0; +#else + nbytes[nbytelen] = 1; + *v++ = ((P0 *) pptr(i))->_t; +#endif + *v++ = n&255; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; + } +#else + x = scratch; + for (i = 0; i < (int) now._nr_pr; i++) + x += col_p(i, x); + n = ordinal(scratch, x-scratch, 2); /* procs */ + *v++ = n&255; + nbytes[nbytelen] = 0; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; +#endif +#ifdef SEPQS + for (i = 0; i < (int) now._nr_qs; i++) + { n = col_q(i, (char *) 0); + nbytes[nbytelen] = 0; + *v++ = n&255; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; + } +#endif +#ifdef NOVSZ + /* 3 = _a_t, _nr_pr, _nr_qs */ + w = (char *) &now + 3 * sizeof(uchar); +#ifndef NOFAIR + w += NFAIR; +#endif +#else +#if VECTORSZ<65536 + w = (char *) &(now._vsz) + sizeof(unsigned short); +#else + w = (char *) &(now._vsz) + sizeof(unsigned long); +#endif +#endif + x = scratch; + *x++ = now._nr_pr; + *x++ = now._nr_qs; + if (now._nr_qs > 0 && qptr(0) < pptr(0)) + n = qptr(0) - (uchar *) w; + else + n = pptr(0) - (uchar *) w; + j = w - (char *) &now; + for (i = 0; i < (int) n; i++, w++) + if (!Mask[j++]) *x++ = *w; +#ifndef SEPQS + for (i = 0; i < (int) now._nr_qs; i++) + x += col_q(i, x); +#endif + x--; + for (i = 0, j = 6; i < nbytelen; i++) + { if (j == 6) + { j = 0; + *(++x) = 0; + } else + j += 2; + *x |= (nbytes[i] << j); + } + x++; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; j = 0; + n = ordinal(scratch, x-scratch, 0); /* globals */ + *v++ = n&255; + if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; } + if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; } + if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; } + *v++ = j; /* add last count as a byte */ + for (i = 0; i < WS-1; i++) + *v++ = 0; + v -= i; +#if 0 + printf("collapse %d -> %d\n", + vsize, v - (char *)&comp_now); +#endif + return v - (char *)&comp_now; +} +#else +#if !defined(NOCOMP) +int +compress(char *vin, int n) /* default compression */ +{ +#ifdef HC + int delta = 0; + s_hash((uchar *)vin, n); /* sets K1 and K2 */ +#ifndef SAFETY + if (S_A) + { delta++; /* _a_t */ +#ifndef NOFAIR + if (S_A > NFAIR) + delta += NFAIR; /* _cnt[] */ +#endif + } +#endif + memcpy((char *) &comp_now + delta, (char *) &K1, WS); + delta += WS; +#if HC>0 + memcpy((char *) &comp_now + delta, (char *) &K2, HC); + delta += HC; +#endif + return delta; +#else + char *vv = vin; + char *v = (char *) &comp_now; + int i; + #ifndef NO_FAST_C + int r = 0, unroll = n/8; + if (unroll > 0) + { i = 0; + while (r++ < unroll) + { /* unroll 8 times, avoid ifs */ + /* 1 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 2 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 3 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 4 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 5 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 6 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 7 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 8 */ *v = *vv++; + v += 1 - Mask[i++]; + } + r = n - i; /* the rest, at most 7 */ + switch (r) { + case 7: *v = *vv++; v += 1 - Mask[i++]; + case 6: *v = *vv++; v += 1 - Mask[i++]; + case 5: *v = *vv++; v += 1 - Mask[i++]; + case 4: *v = *vv++; v += 1 - Mask[i++]; + case 3: *v = *vv++; v += 1 - Mask[i++]; + case 2: *v = *vv++; v += 1 - Mask[i++]; + case 1: *v = *vv++; v += 1 - Mask[i++]; + case 0: break; + } + r = (n+WS-1)/WS; /* words rounded up */ + r *= WS; /* bytes */ + i = r - i; /* remainder */ + switch (i) { + case 7: *v++ = 0; /* fall thru */ + case 6: *v++ = 0; + case 5: *v++ = 0; + case 4: *v++ = 0; + case 3: *v++ = 0; + case 2: *v++ = 0; + case 1: *v++ = 0; + case 0: break; + default: Uerror("unexpected wordsize"); + } + v -= i; + } else + #endif + { for (i = 0; i < n; i++, vv++) + if (!Mask[i]) *v++ = *vv; + for (i = 0; i < WS-1; i++) + *v++ = 0; + v -= i; + } +#if 0 + printf("compress %d -> %d\n", + n, v - (char *)&comp_now); +#endif + return v - (char *)&comp_now; +#endif +} +#endif +#endif +#if defined(FULLSTACK) && defined(BITSTATE) +#if defined(MA) +#if !defined(onstack_now) +int onstack_now(void) {} +#endif +#if !defined(onstack_put) +void onstack_put(void) {} +#endif +#if !defined(onstack_zap) +void onstack_zap(void) {} +#endif +#else +void +onstack_zap(void) +{ struct H_el *v, *w, *last = 0; + struct H_el **tmp = H_tab; + char *nv; int n, m; + + static char warned = 0; + + H_tab = S_Tab; +#ifndef NOCOMP + nv = (char *) &comp_now; + n = compress((char *)&now, vsize); +#else +#if defined(BITSTATE) && defined(LC) + nv = (char *) &comp_now; + n = compact_stack((char *)&now, vsize); +#else + nv = (char *) &now; + n = vsize; +#endif +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)nv, n); +#endif + H_tab = tmp; + for (v = S_Tab[j1]; v; Zh++, last=v, v=v->nxt) + { m = memcmp(&(v->state), nv, n); + if (m == 0) + goto Found; + if (m < 0) + break; + } +/* NotFound: */ +#ifndef ZAPH + #if defined(BITSTATE) && NCORE>1 + /* seen this happen, likely harmless, but not yet understood */ + if (warned == 0) + #endif + { /* Uerror("stack out of wack - zap"); */ + cpu_printf("pan: warning, stack incomplete\n"); + warned = 1; + } +#endif + return; +Found: + ZAPS++; + if (last) + last->nxt = v->nxt; + else + S_Tab[j1] = v->nxt; + v->tagged = (unsigned) n; +#if !defined(NOREDUCE) && !defined(SAFETY) + v->proviso = 0; +#endif + v->nxt = last = (struct H_el *) 0; + for (w = Free_list; w; Fa++, last=w, w = w->nxt) + { if ((int) w->tagged <= n) + { if (last) + { v->nxt = w; + last->nxt = v; + } else + { v->nxt = Free_list; + Free_list = v; + } + return; + } + if (!w->nxt) + { w->nxt = v; + return; + } } + Free_list = v; +} +void +onstack_put(void) +{ struct H_el **tmp = H_tab; + H_tab = S_Tab; + if (hstore((char *)&now, vsize) != 0) +#if defined(BITSTATE) && defined(LC) + printf("pan: warning, double stack entry\n"); +#else + #ifndef ZAPH + Uerror("cannot happen - unstack_put"); + #endif +#endif + H_tab = tmp; + trpt->ostate = Lstate; + PUT++; +} +int +onstack_now(void) +{ struct H_el *tmp; + struct H_el **tmp2 = H_tab; + char *v; int n, m = 1; + + H_tab = S_Tab; +#ifdef NOCOMP +#if defined(BITSTATE) && defined(LC) + v = (char *) &comp_now; + n = compact_stack((char *)&now, vsize); +#else + v = (char *) &now; + n = vsize; +#endif +#else + v = (char *) &comp_now; + n = compress((char *)&now, vsize); +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)v, n); +#endif + H_tab = tmp2; + for (tmp = S_Tab[j1]; tmp; Zn++, tmp = tmp->nxt) + { m = memcmp(((char *)&(tmp->state)),v,n); + if (m <= 0) + { Lstate = (struct H_el *) tmp; + break; + } } + PROBE++; + return (m == 0); +} +#endif +#endif +#ifndef BITSTATE +void +hinit(void) +{ + #ifdef MA +#ifdef R_XPT + { void r_xpoint(void); + r_xpoint(); + } +#else + dfa_init((unsigned short) (MA+a_cycles)); +#if NCORE>1 && !defined(COLLAPSE) + if (!readtrail) + { void init_HT(unsigned long); + init_HT(0L); + } +#endif +#endif + #endif + #if !defined(MA) || defined(COLLAPSE) +#if NCORE>1 + if (!readtrail) + { void init_HT(unsigned long); + init_HT((unsigned long) (ONE_L<= MA) + { printf("pan: error, MA too small, recompile pan.c"); + printf(" with -DMA=N with N>%d\n", n); + Uerror("aborting"); + } + if (n > (int) maxgs) + { maxgs = (unsigned int) n; + } + for (i = 0; i < n; i++) + { Info[i] = v[i]; + } + for ( ; i < MA-1; i++) + { Info[i] = 0; + } + Info[MA-1] = pbit; + if (a_cycles) /* place _a_t at the end */ + { Info[MA] = Info[0]; + Info[0] = 0; + } + +#if NCORE>1 && !defined(SEP_STATE) + enter_critical(GLOBAL_LOCK); /* crude, but necessary */ + /* to make this mode work, also replace emalloc with grab_shared inside store MA routines */ +#endif + + if (!dfa_store(Info)) + { if (pbit == 0 + && (now._a_t&1) + && depth > A_depth) + { Info[MA] &= ~(1|16|32); /* _a_t */ + if (dfa_member(MA)) + { Info[MA-1] = 4; /* off-stack bit */ + nShadow++; + if (!dfa_member(MA-1)) + { ret_val = 3; + #ifdef VERBOSE + printf("intersected 1st dfs stack\n"); + #endif + goto done; + } } } + ret_val = 0; + #ifdef VERBOSE + printf("new state\n"); + #endif + goto done; + } +#ifdef FULLSTACK + if (pbit == 0) + { Info[MA-1] = 1; /* proviso bit */ +#ifndef BFS + trpt->proviso = dfa_member(MA-1); +#endif + Info[MA-1] = 4; /* off-stack bit */ + if (dfa_member(MA-1)) + { ret_val = 1; /* off-stack */ + #ifdef VERBOSE + printf("old state\n"); + #endif + } else + { ret_val = 2; /* on-stack */ + #ifdef VERBOSE + printf("on-stack\n"); + #endif + } + goto done; + } +#endif + ret_val = 1; +#ifdef VERBOSE + printf("old state\n"); +#endif +done: +#if NCORE>1 && !defined(SEP_STATE) + leave_critical(GLOBAL_LOCK); +#endif + return ret_val; /* old state */ +} +#endif +#if defined(BITSTATE) && defined(LC) +int +compact_stack(char *vin, int n) +{ int delta = 0; + s_hash((uchar *)vin, n); /* sets K1 and K2 */ +#ifndef SAFETY + delta++; /* room for state[0] |= 128 */ +#endif + memcpy((char *) &comp_now + delta, (char *) &K1, WS); + delta += WS; + memcpy((char *) &comp_now + delta, (char *) &K2, WS); + delta += WS; /* use all available bits */ + return delta; +} +#endif +int +hstore(char *vin, int nin) /* hash table storage */ +{ struct H_el *ntmp; + struct H_el *tmp, *olst = (struct H_el *) 0; + char *v; int n, m=0; +#ifdef HC + uchar rem_a; +#endif +#ifdef NOCOMP +#if defined(BITSTATE) && defined(LC) + if (S_Tab == H_tab) + { v = (char *) &comp_now; + n = compact_stack(vin, nin); + } else + { v = vin; n = nin; + } +#else + v = vin; n = nin; +#endif +#else + v = (char *) &comp_now; + #ifdef HC + rem_a = now._a_t; + now._a_t = 0; + #endif + n = compress(vin, nin); + #ifdef HC + now._a_t = rem_a; + #endif +#ifndef SAFETY + if (S_A) + { v[0] = 0; /* _a_t */ +#ifndef NOFAIR + if (S_A > NFAIR) + for (m = 0; m < NFAIR; m++) + v[m+1] = 0; /* _cnt[] */ +#endif + m = 0; + } + #endif +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)v, n); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + enter_critical(CS_ID); /* uses spinlock */ +#endif + tmp = H_tab[j1]; + if (!tmp) + { tmp = grab_state(n); +#if NCORE>1 + if (!tmp) + { /* if we get here -- we've already issued a warning */ + /* but we want to allow the normal distributed termination */ + /* to collect the stats on all cpus in the wrapup */ + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + H_tab[j1] = tmp; + } else + { for (;; hcmp++, olst = tmp, tmp = tmp->nxt) + { /* skip the _a_t and the _cnt bytes */ +#ifdef COLLAPSE + if (tmp->ln != 0) + { if (!tmp->nxt) goto Append; + continue; + } +#endif + m = memcmp(((char *)&(tmp->state)) + S_A, + v + S_A, n - S_A); + if (m == 0) { +#ifdef SAFETY +#define wasnew 0 +#else + int wasnew = 0; +#endif +#ifndef SAFETY +#ifndef NOCOMP + if (S_A) + { if ((((char *)&(tmp->state))[0] & V_A) != V_A) + { wasnew = 1; nShadow++; + ((char *)&(tmp->state))[0] |= V_A; + } +#ifndef NOFAIR + if (S_A > NFAIR) + { /* 0 <= now._cnt[now._a_t&1] < MAXPROC */ + unsigned ci, bp; /* index, bit pos */ + ci = (now._cnt[now._a_t&1] / 8); + bp = (now._cnt[now._a_t&1] - 8*ci); + if (now._a_t&1) /* use tail-bits in _cnt */ + { ci = (NFAIR - 1) - ci; + bp = 7 - bp; /* bp = 0..7 */ + } + ci++; /* skip over _a_t */ + bp = 1 << bp; /* the bit mask */ + if ((((char *)&(tmp->state))[ci] & bp)==0) + { if (!wasnew) + { wasnew = 1; + nShadow++; + } + ((char *)&(tmp->state))[ci] |= bp; + } + } + /* else: wasnew == 0, i.e., old state */ +#endif + } +#endif +#endif +#if NCORE>1 + Lstate = (struct H_el *) tmp; +#endif +#ifdef FULLSTACK +#ifndef SAFETY + if (wasnew) + { Lstate = (struct H_el *) tmp; + tmp->tagged |= V_A; + if ((now._a_t&1) + && (tmp->tagged&A_V) + && depth > A_depth) + { +intersect: +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf("1st dfs-stack intersected on state %d+\n", + (int) tmp->st_id); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 3; + } +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" New state %d+\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(1, (char *)&(tmp->state),n,tmp->tagged); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } else +#endif + if ((S_A)?(tmp->tagged&V_A):tmp->tagged) + { Lstate = (struct H_el *) tmp; +#ifndef SAFETY + /* already on current dfs stack */ + /* but may also be on 1st dfs stack */ + if ((now._a_t&1) + && (tmp->tagged&A_V) + && depth > A_depth +#ifndef NOFAIR + && (!fairness || now._cnt[1] <= 1) +#endif + ) + goto intersect; +#endif +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" Stack state %d\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(0, (char *)&(tmp->state),n,tmp->tagged); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 2; /* match on stack */ + } +#else + if (wasnew) + { +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" New state %d+\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(1, (char *)&(tmp->state), n, 0); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } +#endif +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" Old state %d\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(0, (char *)&(tmp->state), n, 0); +#endif +#ifdef REACH + if (tmp->D > depth) + { tmp->D = depth; +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" ReVisiting (from smaller depth)\n"); +#endif + nstates--; +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } +#endif +#if (defined(BFS) && defined(Q_PROVISO)) || NCORE>1 + Lstate = (struct H_el *) tmp; +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 1; /* match outside stack */ + } else if (m < 0) + { /* insert state before tmp */ + ntmp = grab_state(n); +#if NCORE>1 + if (!ntmp) + { + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + ntmp->nxt = tmp; + if (!olst) + H_tab[j1] = ntmp; + else + olst->nxt = ntmp; + tmp = ntmp; + break; + } else if (!tmp->nxt) + { /* append after tmp */ +#ifdef COLLAPSE +Append: +#endif + tmp->nxt = grab_state(n); +#if NCORE>1 + if (!tmp->nxt) + { + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + tmp = tmp->nxt; + break; + } } + } +#ifdef CHECK + tmp->st_id = (unsigned) nstates; +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif +#ifdef BITSTATE + printf(" Push state %d\n", ((int) nstates) - 1); +#else + printf(" New state %d\n", (int) nstates); +#endif +#endif +#if !defined(SAFETY) || defined(REACH) + tmp->D = depth; +#endif +#ifndef SAFETY +#ifndef NOCOMP + if (S_A) + { v[0] = V_A; +#ifndef NOFAIR + if (S_A > NFAIR) + { unsigned ci, bp; /* as above */ + ci = (now._cnt[now._a_t&1] / 8); + bp = (now._cnt[now._a_t&1] - 8*ci); + if (now._a_t&1) + { ci = (NFAIR - 1) - ci; + bp = 7 - bp; /* bp = 0..7 */ + } + v[1+ci] = 1 << bp; + } +#endif + } +#endif +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + tmp->m_K1 = K1; +#endif + memcpy(((char *)&(tmp->state)), v, n); +#ifdef FULLSTACK + tmp->tagged = (S_A)?V_A:(depth+1); +#ifdef DEBUG + dumpstate(-1, v, n, tmp->tagged); +#endif + Lstate = (struct H_el *) tmp; +#else + #ifdef DEBUG + dumpstate(-1, v, n, 0); + #endif + #if NCORE>1 + Lstate = (struct H_el *) tmp; + #endif +#endif +/* #if NCORE>1 && !defined(SEP_STATE) */ +#if NCORE>1 + #ifdef V_PROVISO + tmp->cpu_id = core_id; + #endif + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif +#endif + return 0; +} +#endif +#include TRANSITIONS +void +do_reach(void) +{ + r_ck(reached0, nstates0, 0, src_ln0, src_file0); + r_ck(reached1, nstates1, 1, src_ln1, src_file1); + r_ck(reached2, nstates2, 2, src_ln2, src_file2); + r_ck(reached3, nstates3, 3, src_ln3, src_file3); + r_ck(reached4, nstates4, 4, src_ln4, src_file4); + r_ck(reached5, nstates5, 5, src_ln5, src_file5); +} + +void +iniglobals(void) +{ + deliver = 0; + { int l_in; + for (l_in = 0; l_in < 4; l_in++) + { + now.buffer_use[l_in] = 0; + } + } + now.write_off = 0; + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + now.commit_count[l_in] = 0; + } + } + now._commit_sum = 0; + now.read_off = 0; + now.events_lost = 0; + now.refcount = 0; +#ifdef VAR_RANGES + { int l_in; + for (l_in = 0; l_in < 4; l_in++) + { + logval("buffer_use[l_in]", now.buffer_use[l_in]); + } + } + logval("write_off", now.write_off); + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + logval("commit_count[l_in]", now.commit_count[l_in]); + } + } + logval("_commit_sum", now._commit_sum); + logval("read_off", now.read_off); + logval("events_lost", now.events_lost); + logval("refcount", now.refcount); +#endif + Maxbody = max(Maxbody, sizeof(State)-VECTORSZ); +} + +int +addqueue(int n, int is_rv) +{ int j=0, i = now._nr_qs; +#ifndef NOCOMP + int k; +#endif + if (i >= MAXQ) + Uerror("too many queues"); + switch (n) { + default: Uerror("bad queue - addqueue"); + } + if (vsize%WS) + q_skip[i] = WS-(vsize%WS); + else + q_skip[i] = 0; +#ifndef NOCOMP + k = vsize; +#ifndef BFS + if (is_rv) k += j; +#endif + for (k += (int) q_skip[i]; k > vsize; k--) + Mask[k-1] = 1; +#endif + vsize += (int) q_skip[i]; + q_offset[i] = vsize; + now._nr_qs += 1; + vsize += j; +#ifndef NOVSZ + now._vsz = vsize; +#endif + hmax = max(hmax, vsize); + if (vsize >= VECTORSZ) + Uerror("VECTORSZ is too small, edit pan.h"); + memset((char *)qptr(i), 0, j); + ((Q0 *)qptr(i))->_t = n; + return i+1; +} + +#if NQS>0 +void +qsend(int into, int sorted, int args_given) +{ int j; uchar *z; + +#ifdef HAS_SORTED + int k; +#endif + if (!into--) + uerror("ref to uninitialized chan name (sending)"); + if (into >= (int) now._nr_qs || into < 0) + Uerror("qsend bad queue#"); + z = qptr(into); + j = ((Q0 *)qptr(into))->Qlen; + switch (((Q0 *)qptr(into))->_t) { + case 0: printf("queue %d was deleted\n", into+1); + default: Uerror("bad queue - qsend"); + } +#ifdef EVENT_TRACE + if (in_s_scope(into+1)) + require('s', into); +#endif +} +#endif + +#if SYNC +int +q_zero(int from) +{ if (!from--) + { uerror("ref to uninitialized chan name (q_zero)"); + return 0; + } + switch(((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + } + Uerror("bad queue q-zero"); + return -1; +} +int +not_RV(int from) +{ if (q_zero(from)) + { printf("==>> a test of the contents of a rv "); + printf("channel always returns FALSE\n"); + uerror("error to poll rendezvous channel"); + } + return 1; +} +#endif +#ifndef XUSAFE +void +setq_claim(int x, int m, char *s, int y, char *p) +{ if (x == 0) + uerror("x[rs] claim on uninitialized channel"); + if (x < 0 || x > MAXQ) + Uerror("cannot happen setq_claim"); + q_claim[x] |= m; + p_name[y] = p; + q_name[x] = s; + if (m&2) q_S_check(x, y); + if (m&1) q_R_check(x, y); +} +short q_sender[MAXQ+1]; +int +q_S_check(int x, int who) +{ if (!q_sender[x]) + { q_sender[x] = who+1; +#if SYNC + if (q_zero(x)) + { printf("chan %s (%d), ", + q_name[x], x-1); + printf("sndr proc %s (%d)\n", + p_name[who], who); + uerror("xs chans cannot be used for rv"); + } +#endif + } else + if (q_sender[x] != who+1) + { printf("pan: xs assertion violated: "); + printf("access to chan <%s> (%d)\npan: by ", + q_name[x], x-1); + if (q_sender[x] > 0 && p_name[q_sender[x]-1]) + printf("%s (proc %d) and by ", + p_name[q_sender[x]-1], q_sender[x]-1); + printf("%s (proc %d)\n", + p_name[who], who); + uerror("error, partial order reduction invalid"); + } + return 1; +} +short q_recver[MAXQ+1]; +int +q_R_check(int x, int who) +{ if (!q_recver[x]) + { q_recver[x] = who+1; +#if SYNC + if (q_zero(x)) + { printf("chan %s (%d), ", + q_name[x], x-1); + printf("recv proc %s (%d)\n", + p_name[who], who); + uerror("xr chans cannot be used for rv"); + } +#endif + } else + if (q_recver[x] != who+1) + { printf("pan: xr assertion violated: "); + printf("access to chan %s (%d)\npan: ", + q_name[x], x-1); + if (q_recver[x] > 0 && p_name[q_recver[x]-1]) + printf("by %s (proc %d) and ", + p_name[q_recver[x]-1], q_recver[x]-1); + printf("by %s (proc %d)\n", + p_name[who], who); + uerror("error, partial order reduction invalid"); + } + return 1; +} +#endif +int +q_len(int x) +{ if (!x--) + uerror("ref to uninitialized chan name (len)"); + return ((Q0 *)qptr(x))->Qlen; +} + +int +q_full(int from) +{ if (!from--) + uerror("ref to uninitialized chan name (qfull)"); + switch(((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + } + Uerror("bad queue - q_full"); + return 0; +} + +#ifdef HAS_UNLESS +int +q_e_f(int from) +{ /* empty or full */ + return !q_len(from) || q_full(from); +} +#endif +#if NQS>0 +int +qrecv(int from, int slot, int fld, int done) +{ uchar *z; + int j, k, r=0; + + if (!from--) + uerror("ref to uninitialized chan name (receiving)"); + if (from >= (int) now._nr_qs || from < 0) + Uerror("qrecv bad queue#"); + z = qptr(from); +#ifdef EVENT_TRACE + if (done && (in_r_scope(from+1))) + require('r', from); +#endif + switch (((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + default: Uerror("bad queue - qrecv"); + } + return r; +} +#endif + +#ifndef BITSTATE +#ifdef COLLAPSE +long +col_q(int i, char *z) +{ int j=0, k; + char *x, *y; + Q0 *ptr = (Q0 *) qptr(i); + switch (ptr->_t) { + default: Uerror("bad qtype - collapse"); + } + if (z) x = z; else x = scratch; + y = (char *) ptr; k = q_offset[i]; + /* no need to store the empty slots at the end */ + j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]); + for ( ; j > 0; j--, y++) + if (!Mask[k++]) *x++ = *y; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; + if (z) return (long) (x - z); + return ordinal(scratch, x-scratch, 1); /* chan */ +} +#endif +#endif +int +unsend(int into) +{ int _m=0, j; uchar *z; + +#ifdef HAS_SORTED + int k; +#endif + if (!into--) + uerror("ref to uninitialized chan (unsend)"); + z = qptr(into); + j = ((Q0 *)z)->Qlen; + ((Q0 *)z)->Qlen = --j; + switch (((Q0 *)qptr(into))->_t) { + default: Uerror("bad queue - unsend"); + } + return _m; +} + +void +unrecv(int from, int slot, int fld, int fldvar, int strt) +{ int j; uchar *z; + + if (!from--) + uerror("ref to uninitialized chan (unrecv)"); + z = qptr(from); + j = ((Q0 *)z)->Qlen; + if (strt) ((Q0 *)z)->Qlen = j+1; + switch (((Q0 *)qptr(from))->_t) { + default: Uerror("bad queue - qrecv"); + } +} +int +q_cond(short II, Trans *t) +{ int i = 0; + for (i = 0; i < 6; i++) + { if (t->ty[i] == TIMEOUT_F) return 1; + if (t->ty[i] == ALPHA_F) +#ifdef GLOB_ALPHA + return 0; +#else + return (II+1 == (short) now._nr_pr && II+1 < MAXPROC); +#endif + switch (t->qu[i]) { + case 0: break; + default: Uerror("unknown qid - q_cond"); + return 0; + } + } + return 1; +} +void +to_compile(void) +{ char ctd[1024], carg[64]; +#ifdef BITSTATE + strcpy(ctd, "-DBITSTATE "); +#else + strcpy(ctd, ""); +#endif +#ifdef NOVSZ + strcat(ctd, "-DNOVSZ "); +#endif +#ifdef REVERSE + strcat(ctd, "-DREVERSE "); +#endif +#ifdef T_REVERSE + strcat(ctd, "-DT_REVERSE "); +#endif +#ifdef RANDOMIZE + #if RANDOMIZE>0 + sprintf(carg, "-DRANDOMIZE=%d ", RANDOMIZE); + strcat(ctd, carg); + #else + strcat(ctd, "-DRANDOMIZE "); + #endif +#endif +#ifdef SCHED + sprintf(carg, "-DSCHED=%d ", SCHED); + strcat(ctd, carg); +#endif +#ifdef BFS + strcat(ctd, "-DBFS "); +#endif +#ifdef MEMLIM + sprintf(carg, "-DMEMLIM=%d ", MEMLIM); + strcat(ctd, carg); +#else +#ifdef MEMCNT + sprintf(carg, "-DMEMCNT=%d ", MEMCNT); + strcat(ctd, carg); +#endif +#endif +#ifdef NOCLAIM + strcat(ctd, "-DNOCLAIM "); +#endif +#ifdef SAFETY + strcat(ctd, "-DSAFETY "); +#else +#ifdef NOFAIR + strcat(ctd, "-DNOFAIR "); +#else +#ifdef NFAIR + if (NFAIR != 2) + { sprintf(carg, "-DNFAIR=%d ", NFAIR); + strcat(ctd, carg); + } +#endif +#endif +#endif +#ifdef NOREDUCE + strcat(ctd, "-DNOREDUCE "); +#else +#ifdef XUSAFE + strcat(ctd, "-DXUSAFE "); +#endif +#endif +#ifdef NP + strcat(ctd, "-DNP "); +#endif +#ifdef PEG + strcat(ctd, "-DPEG "); +#endif +#ifdef VAR_RANGES + strcat(ctd, "-DVAR_RANGES "); +#endif +#ifdef HC0 + strcat(ctd, "-DHC0 "); +#endif +#ifdef HC1 + strcat(ctd, "-DHC1 "); +#endif +#ifdef HC2 + strcat(ctd, "-DHC2 "); +#endif +#ifdef HC3 + strcat(ctd, "-DHC3 "); +#endif +#ifdef HC4 + strcat(ctd, "-DHC4 "); +#endif +#ifdef CHECK + strcat(ctd, "-DCHECK "); +#endif +#ifdef CTL + strcat(ctd, "-DCTL "); +#endif +#ifdef NIBIS + strcat(ctd, "-DNIBIS "); +#endif +#ifdef NOBOUNDCHECK + strcat(ctd, "-DNOBOUNDCHECK "); +#endif +#ifdef NOSTUTTER + strcat(ctd, "-DNOSTUTTER "); +#endif +#ifdef REACH + strcat(ctd, "-DREACH "); +#endif +#ifdef PRINTF + strcat(ctd, "-DPRINTF "); +#endif +#ifdef OTIM + strcat(ctd, "-DOTIM "); +#endif +#ifdef COLLAPSE + strcat(ctd, "-DCOLLAPSE "); +#endif +#ifdef MA + sprintf(carg, "-DMA=%d ", MA); + strcat(ctd, carg); +#endif +#ifdef SVDUMP + strcat(ctd, "-DSVDUMP "); +#endif +#ifdef VECTORSZ + if (VECTORSZ != 1024) + { sprintf(carg, "-DVECTORSZ=%d ", VECTORSZ); + strcat(ctd, carg); + } +#endif +#ifdef VERBOSE + strcat(ctd, "-DVERBOSE "); +#endif +#ifdef CHECK + strcat(ctd, "-DCHECK "); +#endif +#ifdef SDUMP + strcat(ctd, "-DSDUMP "); +#endif +#if NCORE>1 + sprintf(carg, "-DNCORE=%d ", NCORE); + strcat(ctd, carg); +#endif +#ifdef SFH + sprintf(carg, "-DSFH "); + strcat(ctd, carg); +#endif +#ifdef VMAX + if (VMAX != 256) + { sprintf(carg, "-DVMAX=%d ", VMAX); + strcat(ctd, carg); + } +#endif +#ifdef PMAX + if (PMAX != 16) + { sprintf(carg, "-DPMAX=%d ", PMAX); + strcat(ctd, carg); + } +#endif +#ifdef QMAX + if (QMAX != 16) + { sprintf(carg, "-DQMAX=%d ", QMAX); + strcat(ctd, carg); + } +#endif +#ifdef SET_WQ_SIZE + sprintf(carg, "-DSET_WQ_SIZE=%d ", SET_WQ_SIZE); + strcat(ctd, carg); +#endif + printf("Compiled as: cc -o pan %span.c\n", ctd); +} +void +active_procs(void) +{ + if (!permuted) { + Addproc(4); + } else { + Addproc(4); + } +} +#ifdef MA +/* +#include +#define uchar unsigned char +*/ +#define ulong unsigned long +#define ushort unsigned short + +#define TWIDTH 256 +#define HASH(y,n) (n)*(((long)y)) +#define INRANGE(e,h) ((h>=e->From && h<=e->To)||(e->s==1 && e->S==h)) + +extern char *emalloc(unsigned long); /* imported routine */ +extern void dfa_init(ushort); /* 4 exported routines */ +extern int dfa_member(ulong); +extern int dfa_store(uchar *); +extern void dfa_stats(void); + +typedef struct Edge { + uchar From, To; /* max range 0..255 */ + uchar s, S; /* if s=1, S is singleton */ + struct Vertex *Dst; + struct Edge *Nxt; +} Edge; + +typedef struct Vertex { + ulong key, num; /* key for splay tree, nr incoming edges */ + uchar from[2], to[2]; /* in-node predefined edge info */ + struct Vertex *dst[2];/* most nodes have 2 or more edges */ + struct Edge *Succ; /* in case there are more edges */ + struct Vertex *lnk, *left, *right; /* splay tree plumbing */ +} Vertex; + +static Edge *free_edges; +static Vertex *free_vertices; +static Vertex **layers; /* one splay tree of nodes per layer */ +static Vertex **path; /* run of word in the DFA */ +static Vertex *R, *F, *NF; /* Root, Final, Not-Final */ +static uchar *word, *lastword;/* string, and last string inserted */ +static int dfa_depth, iv=0, nv=0, pfrst=0, Tally; + +static void insert_it(Vertex *, int); /* splay-tree code */ +static void delete_it(Vertex *, int); +static Vertex *find_it(Vertex *, Vertex *, uchar, int); + +static void +recyc_edges(Edge *e) +{ + if (!e) return; + recyc_edges(e->Nxt); + e->Nxt = free_edges; + free_edges = e; +} + +static Edge * +new_edge(Vertex *dst) +{ Edge *e; + + if (free_edges) + { e = free_edges; + free_edges = e->Nxt; + e->From = e->To = e->s = e->S = 0; + e->Nxt = (Edge *) 0; + } else + e = (Edge *) emalloc(sizeof(Edge)); + e->Dst = dst; + + return e; +} + +static void +recyc_vertex(Vertex *v) +{ + recyc_edges(v->Succ); + v->Succ = (Edge *) free_vertices; + free_vertices = v; + nr_states--; +} + +static Vertex * +new_vertex(void) +{ Vertex *v; + + if (free_vertices) + { v = free_vertices; + free_vertices = (Vertex *) v->Succ; + v->Succ = (Edge *) 0; + v->num = 0; + } else + v = (Vertex *) emalloc(sizeof(Vertex)); + + nr_states++; + return v; +} + +static Vertex * +allDelta(Vertex *v, int n) +{ Vertex *dst = new_vertex(); + + v->from[0] = 0; + v->to[0] = 255; + v->dst[0] = dst; + dst->num = 256; + insert_it(v, n); + return dst; +} + +static void +insert_edge(Vertex *v, Edge *e) +{ /* put new edge first */ + if (!v->dst[0]) + { v->dst[0] = e->Dst; + v->from[0] = e->From; + v->to[0] = e->To; + recyc_edges(e); + return; + } + if (!v->dst[1]) + { v->from[1] = v->from[0]; v->from[0] = e->From; + v->to[1] = v->to[0]; v->to[0] = e->To; + v->dst[1] = v->dst[0]; v->dst[0] = e->Dst; + recyc_edges(e); + return; + } /* shift */ + { int f = v->from[1]; + int t = v->to[1]; + Vertex *d = v->dst[1]; + v->from[1] = v->from[0]; v->from[0] = e->From; + v->to[1] = v->to[0]; v->to[0] = e->To; + v->dst[1] = v->dst[0]; v->dst[0] = e->Dst; + e->From = f; + e->To = t; + e->Dst = d; + } + e->Nxt = v->Succ; + v->Succ = e; +} + +static void +copyRecursive(Vertex *v, Edge *e) +{ Edge *f; + if (e->Nxt) copyRecursive(v, e->Nxt); + f = new_edge(e->Dst); + f->From = e->From; + f->To = e->To; + f->s = e->s; + f->S = e->S; + f->Nxt = v->Succ; + v->Succ = f; +} + +static void +copyEdges(Vertex *to, Vertex *from) +{ int i; + for (i = 0; i < 2; i++) + { to->from[i] = from->from[i]; + to->to[i] = from->to[i]; + to->dst[i] = from->dst[i]; + } + if (from->Succ) copyRecursive(to, from->Succ); +} + +static Edge * +cacheDelta(Vertex *v, int h, int first) +{ static Edge *ov, tmp; int i; + + if (!first && INRANGE(ov,h)) + return ov; /* intercepts about 10% */ + for (i = 0; i < 2; i++) + if (v->dst[i] && h >= v->from[i] && h <= v->to[i]) + { tmp.From = v->from[i]; + tmp.To = v->to[i]; + tmp.Dst = v->dst[i]; + tmp.s = tmp.S = 0; + ov = &tmp; + return ov; + } + for (ov = v->Succ; ov; ov = ov->Nxt) + if (INRANGE(ov,h)) return ov; + + Uerror("cannot get here, cacheDelta"); + return (Edge *) 0; +} + +static Vertex * +Delta(Vertex *v, int h) /* v->delta[h] */ +{ Edge *e; + + if (v->dst[0] && h >= v->from[0] && h <= v->to[0]) + return v->dst[0]; /* oldest edge */ + if (v->dst[1] && h >= v->from[1] && h <= v->to[1]) + return v->dst[1]; + for (e = v->Succ; e; e = e->Nxt) + if (INRANGE(e,h)) + return e->Dst; + Uerror("cannot happen Delta"); + return (Vertex *) 0; +} + +static void +numDelta(Vertex *v, int d) +{ Edge *e; + ulong cnt; + int i; + + for (i = 0; i < 2; i++) + if (v->dst[i]) + { cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]); + if (d == 1 && cnt < v->dst[i]->num) goto bad; + v->dst[i]->num = cnt; + } + for (e = v->Succ; e; e = e->Nxt) + { cnt = e->Dst->num + d*(1 + e->To - e->From + e->s); + if (d == 1 && cnt < e->Dst->num) +bad: Uerror("too many incoming edges"); + e->Dst->num = cnt; + } +} + +static void +setDelta(Vertex *v, int h, Vertex *newdst) /* v->delta[h] = newdst; */ +{ Edge *e, *f = (Edge *) 0, *g; + int i; + + /* remove the old entry, if there */ + for (i = 0; i < 2; i++) + if (v->dst[i] && h >= v->from[i] && h <= v->to[i]) + { if (h == v->from[i]) + { if (h == v->to[i]) + { v->dst[i] = (Vertex *) 0; + v->from[i] = v->to[i] = 0; + } else + v->from[i]++; + } else if (h == v->to[i]) + { v->to[i]--; + } else + { g = new_edge(v->dst[i]);/* same dst */ + g->From = v->from[i]; + g->To = h-1; /* left half */ + v->from[i] = h+1; /* right half */ + insert_edge(v, g); + } + goto part2; + } + for (e = v->Succ; e; f = e, e = e->Nxt) + { if (e->s == 1 && e->S == h) + { e->s = e->S = 0; + goto rem_tst; + } + if (h >= e->From && h <= e->To) + { if (h == e->From) + { if (h == e->To) + { if (e->s) + { e->From = e->To = e->S; + e->s = 0; + break; + } else + goto rem_do; + } else + e->From++; + } else if (h == e->To) + { e->To--; + } else /* split */ + { g = new_edge(e->Dst); /* same dst */ + g->From = e->From; + g->To = h-1; /* g=left half */ + e->From = h+1; /* e=right half */ + g->Nxt = e->Nxt; /* insert g */ + e->Nxt = g; /* behind e */ + break; /* done */ + } + +rem_tst: if (e->From > e->To) + { if (e->s == 0) { +rem_do: if (f) + f->Nxt = e->Nxt; + else + v->Succ = e->Nxt; + e->Nxt = (Edge *) 0; + recyc_edges(e); + } else + { e->From = e->To = e->S; + e->s = 0; + } } + break; + } } +part2: + /* check if newdst is already there */ + for (i = 0; i < 2; i++) + if (v->dst[i] == newdst) + { if (h+1 == (int) v->from[i]) + { v->from[i] = h; + return; + } + if (h == (int) v->to[i]+1) + { v->to[i] = h; + return; + } } + for (e = v->Succ; e; e = e->Nxt) + { if (e->Dst == newdst) + { if (h+1 == (int) e->From) + { e->From = h; + if (e->s == 1 && e->S+1 == e->From) + { e->From = e->S; + e->s = e->S = 0; + } + return; + } + if (h == (int) e->To+1) + { e->To = h; + if (e->s == 1 && e->S == e->To+1) + { e->To = e->S; + e->s = e->S = 0; + } + return; + } + if (e->s == 0) + { e->s = 1; + e->S = h; + return; + } } } + /* add as a new edge */ + e = new_edge(newdst); + e->From = e->To = h; + insert_edge(v, e); +} + +static ulong +cheap_key(Vertex *v) +{ ulong vk2 = 0; + + if (v->dst[0]) + { vk2 = (ulong) v->dst[0]; + if ((ulong) v->dst[1] > vk2) + vk2 = (ulong) v->dst[1]; + } else if (v->dst[1]) + vk2 = (ulong) v->dst[1]; + if (v->Succ) + { Edge *e; + for (e = v->Succ; e; e = e->Nxt) + if ((ulong) e->Dst > vk2) + vk2 = (ulong) e->Dst; + } + Tally = (vk2>>2)&(TWIDTH-1); + return v->key; +} + +static ulong +mk_key(Vertex *v) /* not sensitive to order */ +{ ulong m = 0, vk2 = 0; + Edge *e; + + if (v->dst[0]) + { m += HASH(v->dst[0], v->to[0] - v->from[0] + 1); + vk2 = (ulong) v->dst[0]; + } + if (v->dst[1]) + { m += HASH(v->dst[1], v->to[1] - v->from[1] + 1); + if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1]; + } + for (e = v->Succ; e; e = e->Nxt) + { m += HASH(e->Dst, e->To - e->From + 1 + e->s); + if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst; + } + Tally = (vk2>>2)&(TWIDTH-1); + return m; +} + +static ulong +mk_special(int sigma, Vertex *n, Vertex *v) +{ ulong m = 0, vk2 = 0; + Edge *f; + int i; + + for (i = 0; i < 2; i++) + if (v->dst[i]) + { if (sigma >= v->from[i] && sigma <= v->to[i]) + { m += HASH(v->dst[i], v->to[i]-v->from[i]); + if ((ulong) v->dst[i] > vk2 + && v->to[i] > v->from[i]) + vk2 = (ulong) v->dst[i]; + } else + { m += HASH(v->dst[i], v->to[i]-v->from[i]+1); + if ((ulong) v->dst[i] > vk2) + vk2 = (ulong) v->dst[i]; + } } + for (f = v->Succ; f; f = f->Nxt) + { if (sigma >= f->From && sigma <= f->To) + { m += HASH(f->Dst, f->To - f->From + f->s); + if ((ulong) f->Dst > vk2 + && f->To - f->From + f->s > 0) + vk2 = (ulong) f->Dst; + } else if (f->s == 1 && sigma == f->S) + { m += HASH(f->Dst, f->To - f->From + 1); + if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; + } else + { m += HASH(f->Dst, f->To - f->From + 1 + f->s); + if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; + } } + + if ((ulong) n > vk2) vk2 = (ulong) n; + Tally = (vk2>>2)&(TWIDTH-1); + m += HASH(n, 1); + return m; +} + +void +dfa_init(ushort nr_layers) +{ int i; Vertex *r, *t; + + dfa_depth = nr_layers; /* one byte per layer */ + path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *)); + layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *)); + lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar)); + lastword[dfa_depth] = lastword[0] = 255; + path[0] = R = new_vertex(); F = new_vertex(); + + for (i = 1, r = R; i < dfa_depth; i++, r = t) + t = allDelta(r, i-1); + NF = allDelta(r, i-1); +} + +#if 0 +static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; } +#endif + +double +tree_stats(Vertex *t) +{ Edge *e; double cnt=0.0; + if (!t) return 0; + if (!t->key) return 0; + t->key = 0; /* precaution */ + if (t->dst[0]) cnt++; + if (t->dst[1]) cnt++; + for (e = t->Succ; e; e = e->Nxt) + cnt++; + cnt += tree_stats(t->lnk); + cnt += tree_stats(t->left); + cnt += tree_stats(t->right); + return cnt; +} + +void +dfa_stats(void) +{ int i, j; double cnt = 0.0; + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + cnt += tree_stats(layers[i*TWIDTH+j]); + printf("Minimized Automaton: %6d nodes and %6g edges\n", + nr_states, cnt); +} + +int +dfa_member(ulong n) +{ Vertex **p, **q; + uchar *w = &word[n]; + int i; + + p = &path[n]; q = (p+1); + for (i = n; i < dfa_depth; i++) + *q++ = Delta(*p++, *w++); + return (*p == F); +} + +int +dfa_store(uchar *sv) +{ Vertex **p, **q, *s, *y, *old, *new = F; + uchar *w, *u = lastword; + int i, j, k; + + w = word = sv; + while (*w++ == *u++) /* find first byte that differs */ + ; + pfrst = (int) (u - lastword) - 1; + memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst); + if (pfrst > iv) pfrst = iv; + if (pfrst > nv) pfrst = nv; +/* phase1: */ + p = &path[pfrst]; q = (p+1); w = &word[pfrst]; + for (i = pfrst; i < dfa_depth; i++) + *q++ = Delta(*p++, *w++); /* (*p)->delta[*w++]; */ + + if (*p == F) return 1; /* it's already there */ +/* phase2: */ + iv = dfa_depth; + do { iv--; + old = new; + new = find_it(path[iv], old, word[iv], iv); + } while (new && iv > 0); + +/* phase3: */ + nv = k = 0; s = path[0]; + for (j = 1; j <= iv; ++j) + if (path[j]->num > 1) + { y = new_vertex(); + copyEdges(y, path[j]); + insert_it(y, j); + numDelta(y, 1); + delete_it(s, j-1); + setDelta(s, word[j-1], y); + insert_it(s, j-1); + y->num = 1; /* initial value 1 */ + s = y; + path[j]->num--; /* only 1 moved from j to y */ + k = 1; + } else + { s = path[j]; + if (!k) nv = j; + } + y = Delta(s, word[iv]); + y->num--; + delete_it(s, iv); + setDelta(s, word[iv], old); + insert_it(s, iv); + old->num++; + + for (j = iv+1; j < dfa_depth; j++) + if (path[j]->num == 0) + { numDelta(path[j], -1); + delete_it(path[j], j); + recyc_vertex(path[j]); + } else + break; + return 0; +} + +static Vertex * +splay(ulong i, Vertex *t) +{ Vertex N, *l, *r, *y; + + if (!t) return t; + N.left = N.right = (Vertex *) 0; + l = r = &N; + for (;;) + { if (i < t->key) + { if (!t->left) break; + if (i < t->left->key) + { y = t->left; + t->left = y->right; + y->right = t; + t = y; + if (!t->left) break; + } + r->left = t; + r = t; + t = t->left; + } else if (i > t->key) + { if (!t->right) break; + if (i > t->right->key) + { y = t->right; + t->right = y->left; + y->left = t; + t = y; + if (!t->right) break; + } + l->right = t; + l = t; + t = t->right; + } else + break; + } + l->right = t->left; + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + +static void +insert_it(Vertex *v, int L) +{ Vertex *new, *t; + ulong i; int nr; + + i = mk_key(v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + + v->key = i; + if (!t) + { layers[nr] = v; + return; + } + t = splay(i, t); + if (i < t->key) + { new = v; + new->left = t->left; + new->right = t; + t->left = (Vertex *) 0; + } else if (i > t->key) + { new = v; + new->right = t->right; + new->left = t; + t->right = (Vertex *) 0; + } else /* it's already there */ + { v->lnk = t->lnk; /* put in linked list off v */ + t->lnk = v; + new = t; + } + layers[nr] = new; +} + +static int +checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma) +{ Edge *g, *f; + int i, k, j = 1; + + for (k = 0; k < 2; k++) + if (h->dst[k]) + { if (sigma >= h->from[k] && sigma <= h->to[k]) + { if (h->dst[k] != n) goto no_match; + } + for (i = h->from[k]; i <= h->to[k]; i++) + { if (i == sigma) continue; + g = cacheDelta(v, i, j); j = 0; + if (h->dst[k] != g->Dst) + goto no_match; + if (g->s == 0 || g->S != i) + i = g->To; + } } + for (f = h->Succ; f; f = f->Nxt) + { if (INRANGE(f,sigma)) + { if (f->Dst != n) goto no_match; + } + for (i = f->From; i <= f->To; i++) + { if (i == sigma) continue; + g = cacheDelta(v, i, j); j = 0; + if (f->Dst != g->Dst) + goto no_match; + if (g->s == 1 && i == g->S) + continue; + i = g->To; + } + if (f->s && f->S != sigma) + { g = cacheDelta(v, f->S, 1); + if (f->Dst != g->Dst) + goto no_match; + } + } + if (h->Succ || h->dst[0] || h->dst[1]) return 1; +no_match: + return 0; +} + +static Vertex * +find_it(Vertex *v, Vertex *n, uchar sigma, int L) +{ Vertex *z, *t; + ulong i; int nr; + + i = mk_special(sigma,n,v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + + if (!t) return (Vertex *) 0; + layers[nr] = t = splay(i, t); + if (i == t->key) + for (z = t; z; z = z->lnk) + if (checkit(z, v, n, sigma)) + return z; + + return (Vertex *) 0; +} + +static void +delete_it(Vertex *v, int L) +{ Vertex *x, *t; + ulong i; int nr; + + i = cheap_key(v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + if (!t) return; + + t = splay(i, t); + if (i == t->key) + { Vertex *z, *y = (Vertex *) 0; + for (z = t; z && z != v; y = z, z = z->lnk) + ; + if (z != v) goto bad; + if (y) + { y->lnk = z->lnk; + z->lnk = (Vertex *) 0; + layers[nr] = t; + return; + } else if (z->lnk) /* z == t == v */ + { y = z->lnk; + y->left = t->left; + y->right = t->right; + t->left = t->right = t->lnk = (Vertex *) 0; + layers[nr] = y; + return; + } + /* delete the node itself */ + if (!t->left) + { x = t->right; + } else + { x = splay(i, t->left); + x->right = t->right; + } + t->left = t->right = t->lnk = (Vertex *) 0; + layers[nr] = x; + return; + } +bad: Uerror("cannot happen delete"); +} +#endif +#if defined(MA) && (defined(W_XPT) || defined(R_XPT)) +static Vertex **temptree; +static char wbuf[4096]; +static int WCNT = 4096, wcnt=0; +static uchar stacker[MA+1]; +static ulong stackcnt = 0; +extern double nstates, nlinks, truncs, truncs2; + +static void +xwrite(int fd, char *b, int n) +{ + if (wcnt+n >= 4096) + { write(fd, wbuf, wcnt); + wcnt = 0; + } + memcpy(&wbuf[wcnt], b, n); + wcnt += n; +} + +static void +wclose(fd) +{ + if (wcnt > 0) + write(fd, wbuf, wcnt); + wcnt = 0; + close(fd); +} + +static void +w_vertex(int fd, Vertex *v) +{ char t[3]; int i; Edge *e; + + xwrite(fd, (char *) &v, sizeof(Vertex *)); + t[0] = 0; + for (i = 0; i < 2; i++) + if (v->dst[i]) + { t[1] = v->from[i], t[2] = v->to[i]; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *)); + } + for (e = v->Succ; e; e = e->Nxt) + { t[1] = e->From, t[2] = e->To; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *)); + + if (e->s) + { t[1] = t[2] = e->S; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *)); + } } +} + +static void +w_layer(int fd, Vertex *v) +{ uchar c=1; + + if (!v) return; + xwrite(fd, (char *) &c, 1); + w_vertex(fd, v); + w_layer(fd, v->lnk); + w_layer(fd, v->left); + w_layer(fd, v->right); +} + +void +w_xpoint(void) +{ int fd; char nm[64]; + int i, j; uchar c; + static uchar xwarned = 0; + + sprintf(nm, "%s.xpt", PanSource); + if ((fd = creat(nm, 0666)) <= 0) + if (!xwarned) + { xwarned = 1; + printf("cannot creat checkpoint file\n"); + return; + } + xwrite(fd, (char *) &nstates, sizeof(double)); + xwrite(fd, (char *) &truncs, sizeof(double)); + xwrite(fd, (char *) &truncs2, sizeof(double)); + xwrite(fd, (char *) &nlinks, sizeof(double)); + xwrite(fd, (char *) &dfa_depth, sizeof(int)); + xwrite(fd, (char *) &R, sizeof(Vertex *)); + xwrite(fd, (char *) &F, sizeof(Vertex *)); + xwrite(fd, (char *) &NF, sizeof(Vertex *)); + + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + { w_layer(fd, layers[i*TWIDTH+j]); + c = 2; xwrite(fd, (char *) &c, 1); + } + wclose(fd); +} + +static void +xread(int fd, char *b, int n) +{ int m = wcnt; int delta = 0; + if (m < n) + { if (m > 0) memcpy(b, &wbuf[WCNT-m], m); + delta = m; + WCNT = wcnt = read(fd, wbuf, 4096); + if (wcnt < n-m) + Uerror("xread failed -- insufficient data"); + n -= m; + } + memcpy(&b[delta], &wbuf[WCNT-wcnt], n); + wcnt -= n; +} + +static void +x_cleanup(Vertex *c) +{ Edge *e; /* remove the tree and edges from c */ + if (!c) return; + for (e = c->Succ; e; e = e->Nxt) + x_cleanup(e->Dst); + recyc_vertex(c); +} + +static void +x_remove(void) +{ Vertex *tmp; int i, s; + int r, j; + /* double-check: */ + stacker[dfa_depth-1] = 0; r = dfa_store(stacker); + stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1); + if (r != 1 || j != 0) + { printf("%d: ", stackcnt); + for (i = 0; i < dfa_depth; i++) + printf("%d,", stacker[i]); + printf(" -- not a stackstate \n", r, j); + return; + } + stacker[dfa_depth-1] = 1; + s = dfa_member(dfa_depth-1); + + { tmp = F; F = NF; NF = tmp; } /* complement */ + if (s) dfa_store(stacker); + stacker[dfa_depth-1] = 0; + dfa_store(stacker); + stackcnt++; + { tmp = F; F = NF; NF = tmp; } +} + +static void +x_rm_stack(Vertex *t, int k) +{ int j; Edge *e; + + if (k == 0) + { x_remove(); + return; + } + if (t) + for (e = t->Succ; e; e = e->Nxt) + { for (j = e->From; j <= (int) e->To; j++) + { stacker[k] = (uchar) j; + x_rm_stack(e->Dst, k-1); + } + if (e->s) + { stacker[k] = e->S; + x_rm_stack(e->Dst, k-1); + } } +} + +static Vertex * +insert_withkey(Vertex *v, int L) +{ Vertex *new, *t = temptree[L]; + + if (!t) { temptree[L] = v; return v; } + t = splay(v->key, t); + if (v->key < t->key) + { new = v; + new->left = t->left; + new->right = t; + t->left = (Vertex *) 0; + } else if (v->key > t->key) + { new = v; + new->right = t->right; + new->left = t; + t->right = (Vertex *) 0; + } else + { if (t != R && t != F && t != NF) + Uerror("double insert, bad checkpoint data"); + else + { recyc_vertex(v); + new = t; + } } + temptree[L] = new; + + return new; +} + +static Vertex * +find_withkey(Vertex *v, int L) +{ Vertex *t = temptree[L]; + if (t) + { temptree[L] = t = splay((ulong) v, t); + if (t->key == (ulong) v) + return t; + } + Uerror("not found error, bad checkpoint data"); + return (Vertex *) 0; +} + +void +r_layer(int fd, int n) +{ Vertex *v; + Edge *e; + char c, t[2]; + + for (;;) + { xread(fd, &c, 1); + if (c == 2) break; + if (c == 1) + { v = new_vertex(); + xread(fd, (char *) &(v->key), sizeof(Vertex *)); + v = insert_withkey(v, n); + } else /* c == 0 */ + { e = new_edge((Vertex *) 0); + xread(fd, t, 2); + e->From = t[0]; + e->To = t[1]; + xread(fd, (char *) &(e->Dst), sizeof(Vertex *)); + insert_edge(v, e); + } } +} + +static void +v_fix(Vertex *t, int nr) +{ int i; Edge *e; + + if (!t) return; + + for (i = 0; i < 2; i++) + if (t->dst[i]) + t->dst[i] = find_withkey(t->dst[i], nr); + + for (e = t->Succ; e; e = e->Nxt) + e->Dst = find_withkey(e->Dst, nr); + + v_fix(t->left, nr); + v_fix(t->right, nr); +} + +static void +v_insert(Vertex *t, int nr) +{ Edge *e; int i; + + if (!t) return; + v_insert(t->left, nr); + v_insert(t->right, nr); + + /* remove only leafs from temptree */ + t->left = t->right = t->lnk = (Vertex *) 0; + insert_it(t, nr); /* into layers */ + for (i = 0; i < 2; i++) + if (t->dst[i]) + t->dst[i]->num += (t->to[i] - t->from[i] + 1); + for (e = t->Succ; e; e = e->Nxt) + e->Dst->num += (e->To - e->From + 1 + e->s); +} + +static void +x_fixup(void) +{ int i; + + for (i = 0; i < dfa_depth; i++) + v_fix(temptree[i], (i+1)); + + for (i = dfa_depth; i >= 0; i--) + v_insert(temptree[i], i); +} + +static Vertex * +x_tail(Vertex *t, ulong want) +{ int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0; + + if (!t) return v; + + yes = no = 0; + for (i = 0; i < 2; i++) + if ((ulong) t->dst[i] == want) + { /* was t->from[i] <= 0 && t->to[i] >= 0 */ + /* but from and to are uchar */ + if (t->from[i] == 0) + yes = 1; + else + if (t->from[i] <= 4 && t->to[i] >= 4) + no = 1; + } + + for (e = t->Succ; e; e = e->Nxt) + if ((ulong) e->Dst == want) + { /* was INRANGE(e,0) but From and To are uchar */ + if ((e->From == 0) || (e->s==1 && e->S==0)) + yes = 1; + else if (INRANGE(e, 4)) + no = 1; + } + if (yes && !no) return t; + v = x_tail(t->left, want); if (v) return v; + v = x_tail(t->right, want); if (v) return v; + return (Vertex *) 0; +} + +static void +x_anytail(Vertex *t, Vertex *c, int nr) +{ int i; Edge *e, *f; Vertex *v; + + if (!t) return; + + for (i = 0; i < 2; i++) + if ((ulong) t->dst[i] == c->key) + { v = new_vertex(); v->key = t->key; + f = new_edge(v); + f->From = t->from[i]; + f->To = t->to[i]; + f->Nxt = c->Succ; + c->Succ = f; + if (nr > 0) + x_anytail(temptree[nr-1], v, nr-1); + } + + for (e = t->Succ; e; e = e->Nxt) + if ((ulong) e->Dst == c->key) + { v = new_vertex(); v->key = t->key; + f = new_edge(v); + f->From = e->From; + f->To = e->To; + f->s = e->s; + f->S = e->S; + f->Nxt = c->Succ; + c->Succ = f; + x_anytail(temptree[nr-1], v, nr-1); + } + + x_anytail(t->left, c, nr); + x_anytail(t->right, c, nr); +} + +static Vertex * +x_cpy_rev(void) +{ Vertex *c, *v; /* find 0 and !4 predecessor of F */ + + v = x_tail(temptree[dfa_depth-1], F->key); + if (!v) return (Vertex *) 0; + + c = new_vertex(); c->key = v->key; + + /* every node on dfa_depth-2 that has v->key as succ */ + /* make copy and let c point to these (reversing ptrs) */ + + x_anytail(temptree[dfa_depth-2], c, dfa_depth-2); + + return c; +} + +void +r_xpoint(void) +{ int fd; char nm[64]; Vertex *d; + int i, j; + + wcnt = 0; + sprintf(nm, "%s.xpt", PanSource); + if ((fd = open(nm, 0)) < 0) /* O_RDONLY */ + Uerror("cannot open checkpoint file"); + + xread(fd, (char *) &nstates, sizeof(double)); + xread(fd, (char *) &truncs, sizeof(double)); + xread(fd, (char *) &truncs2, sizeof(double)); + xread(fd, (char *) &nlinks, sizeof(double)); + xread(fd, (char *) &dfa_depth, sizeof(int)); + + if (dfa_depth != MA+a_cycles) + Uerror("bad dfa_depth in checkpoint file"); + + path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *)); + layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *)); + temptree = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *)); + lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar)); + lastword[dfa_depth] = lastword[0] = 255; + + path[0] = R = new_vertex(); + xread(fd, (char *) &R->key, sizeof(Vertex *)); + R = insert_withkey(R, 0); + + F = new_vertex(); + xread(fd, (char *) &F->key, sizeof(Vertex *)); + F = insert_withkey(F, dfa_depth); + + NF = new_vertex(); + xread(fd, (char *) &NF->key, sizeof(Vertex *)); + NF = insert_withkey(NF, dfa_depth); + + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + r_layer(fd, i); + + if (wcnt != 0) Uerror("bad count in checkpoint file"); + + d = x_cpy_rev(); + x_fixup(); + stacker[dfa_depth-1] = 0; + x_rm_stack(d, dfa_depth-2); + x_cleanup(d); + close(fd); + + printf("pan: removed %d stackstates\n", stackcnt); + nstates -= (double) stackcnt; +} +#endif +#ifdef VERI +void +check_claim(int st) +{ + if (st == endclaim) + uerror("claim violated!"); + if (stopstate[VERI][st]) + uerror("end state in claim reached"); +} +#endif +void +c_globals(void) +{ /* int i; */ + printf("global vars:\n"); + printf(" byte write_off: %d\n", now.write_off); + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + printf(" byte commit_count[%d]: %d\n", l_in, now.commit_count[l_in]); + } + } + printf(" byte _commit_sum: %d\n", now._commit_sum); + printf(" byte read_off: %d\n", now.read_off); + printf(" byte events_lost: %d\n", now.events_lost); + printf(" byte refcount: %d\n", now.refcount); + { int l_in; + for (l_in = 0; l_in < 4; l_in++) + { + printf(" bit buffer_use[%d]: %d\n", l_in, now.buffer_use[l_in]); + } + } +} +void +c_locals(int pid, int tp) +{ /* int i; */ + switch(tp) { + case 5: + /* none */ + break; + case 4: + printf("local vars proc %d (:init:):\n", pid); + printf(" byte i: %d\n", ((P4 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P4 *)pptr(pid))->j); + printf(" byte sum: %d\n", ((P4 *)pptr(pid))->sum); + printf(" byte commit_sum: %d\n", ((P4 *)pptr(pid))->commit_sum); + break; + case 3: + /* none */ + break; + case 2: + printf("local vars proc %d (reader):\n", pid); + printf(" byte i: %d\n", ((P2 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P2 *)pptr(pid))->j); + break; + case 1: + printf("local vars proc %d (tracer):\n", pid); + printf(" byte size: %d\n", ((P1 *)pptr(pid))->size); + printf(" byte prev_off: %d\n", ((P1 *)pptr(pid))->prev_off); + printf(" byte new_off: %d\n", ((P1 *)pptr(pid))->new_off); + printf(" byte tmp_commit: %d\n", ((P1 *)pptr(pid))->tmp_commit); + printf(" byte i: %d\n", ((P1 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P1 *)pptr(pid))->j); + break; + case 0: + printf("local vars proc %d (switcher):\n", pid); + printf(" byte prev_off: %d\n", ((P0 *)pptr(pid))->prev_off); + printf(" byte new_off: %d\n", ((P0 *)pptr(pid))->new_off); + printf(" byte tmp_commit: %d\n", ((P0 *)pptr(pid))->tmp_commit); + printf(" byte size: %d\n", ((P0 *)pptr(pid))->size); + break; + } +} +void +printm(int x) +{ + switch (x) { + default: Printf("%d", x); + } +} +void +c_chandump(int unused) { unused++; /* avoid complaints */ } diff --git a/trunk/verif/md/pan.err b/trunk/verif/md/pan.err new file mode 100644 index 00000000..e69de29b diff --git a/trunk/verif/md/pan.h b/trunk/verif/md/pan.h new file mode 100644 index 00000000..75446627 --- /dev/null +++ b/trunk/verif/md/pan.h @@ -0,0 +1,638 @@ +#define SpinVersion "Spin Version 5.1.6 -- 9 May 2008" +#define PanSource "model.spin" + +#ifdef WIN64 +#define ONE_L ((unsigned long) 1) +#define long long long +#else +#define ONE_L (1L) +#endif +char *TrailFile = PanSource; /* default */ +char *trailfilename; +#if defined(BFS) +#ifndef SAFETY +#define SAFETY +#endif +#ifndef XUSAFE +#define XUSAFE +#endif +#endif +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef uint +#define uint unsigned int +#endif +#define DELTA 500 +#ifdef MA + #if NCORE>1 && !defined(SEP_STATE) + #define SEP_STATE + #endif +#if MA==1 +#undef MA +#define MA 100 +#endif +#endif +#ifdef W_XPT +#if W_XPT==1 +#undef W_XPT +#define W_XPT 1000000 +#endif +#endif +#ifndef NFAIR +#define NFAIR 2 /* must be >= 2 */ +#endif +#define HAS_CODE +#define MERGED 1 +#ifdef NP /* includes np_ demon */ +#define HAS_NP 2 +#define VERI 6 +#define endclaim 3 /* none */ +#endif +#if !defined(NOCLAIM) && !defined NP +#define VERI 5 +#define endclaim endstate5 +#endif +typedef struct S_F_MAP { + char *fnm; int from; int upto; +} S_F_MAP; + +#define nstates5 15 /* :never: */ +#define nstates_claim nstates5 +#define endstate5 14 +short src_ln5 [] = { + 0, 301, 301, 302, 302, 300, 304, 306, + 306, 307, 307, 305, 309, 310, 311, 0, }; +S_F_MAP src_file5 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 14 }, + { "-", 15, 16 } +}; +#define src_claim src_ln5 +uchar reached5 [] = { + 0, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 0, 0, }; +uchar *loopstate5; +#define reached_claim reached5 + +#define nstates4 44 /* :init: */ +#define endstate4 43 +short src_ln4 [] = { + 0, 252, 254, 255, 256, 257, 257, 253, + 260, 253, 260, 262, 264, 265, 266, 267, + 267, 263, 269, 263, 269, 270, 271, 273, + 274, 275, 276, 277, 277, 272, 279, 272, + 279, 281, 282, 283, 284, 285, 285, 280, + 287, 280, 251, 288, 0, }; +S_F_MAP src_file4 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 43 }, + { "-", 44, 45 } +}; +uchar reached4 [] = { + 0, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 1, 0, 0, 1, + 1, 0, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, }; +uchar *loopstate4; + +#define nstates3 10 /* cleaner */ +#define endstate3 9 +short src_ln3 [] = { + 0, 237, 238, 239, 240, 236, 242, 236, + 235, 243, 0, }; +S_F_MAP src_file3 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 9 }, + { "-", 10, 11 } +}; +uchar reached3 [] = { + 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 0, }; +uchar *loopstate3; + +#define nstates2 30 /* reader */ +#define endstate2 29 +short src_ln2 [] = { + 0, 200, 202, 204, 205, 206, 207, 208, + 208, 203, 210, 203, 201, 216, 218, 219, + 220, 221, 221, 217, 223, 217, 223, 215, + 225, 225, 195, 227, 195, 227, 0, }; +S_F_MAP src_file2 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 29 }, + { "-", 30, 31 } +}; +uchar reached2 [] = { + 0, 1, 1, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 0, 1, 1, 0, 0, }; +uchar *loopstate2; + +#define nstates1 52 /* tracer */ +#define endstate1 51 +short src_ln1 [] = { + 0, 123, 124, 122, 128, 129, 130, 130, + 127, 132, 126, 135, 135, 136, 136, 134, + 138, 138, 140, 141, 142, 143, 144, 144, + 139, 146, 139, 133, 152, 154, 155, 156, + 157, 157, 153, 159, 153, 159, 161, 164, + 167, 168, 169, 170, 165, 172, 151, 174, + 176, 178, 173, 180, 0, }; +S_F_MAP src_file1 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 51 }, + { "-", 52, 53 } +}; +uchar reached1 [] = { + 0, 1, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 0, 1, 1, 0, 0, 0, + 1, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, }; +uchar *loopstate1; + +#define nstates0 32 /* switcher */ +#define endstate0 31 +short src_ln0 [] = { + 0, 72, 73, 74, 77, 78, 79, 80, + 80, 75, 82, 71, 85, 85, 86, 86, + 84, 88, 83, 91, 93, 96, 99, 100, + 101, 102, 97, 104, 104, 90, 107, 108, + 0, }; +S_F_MAP src_file0 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 31 }, + { "-", 32, 33 } +}; +uchar reached0 [] = { + 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 0, + 0, }; +uchar *loopstate0; +struct { + int tp; short *src; +} src_all[] = { + { 5, &src_ln5[0] }, + { 4, &src_ln4[0] }, + { 3, &src_ln3[0] }, + { 2, &src_ln2[0] }, + { 1, &src_ln1[0] }, + { 0, &src_ln0[0] }, + { 0, (short *) 0 } +}; +short *frm_st0; +struct { + char *c; char *t; +} code_lookup[] = { + { (char *) 0, "" } +}; +#define _T5 64 +#define _T2 65 +#define T_ID unsigned char +#define SYNC 0 +#define ASYNC 0 + +#ifndef NCORE + #ifdef DUAL_CORE + #define NCORE 2 + #elif QUAD_CORE + #define NCORE 4 + #else + #define NCORE 1 + #endif +#endif +char *procname[] = { + "switcher", + "tracer", + "reader", + "cleaner", + ":init:", + ":never:", + ":np_:", +}; + +typedef struct P5 { /* :never: */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P5; +#define Air5 (sizeof(P5) - 3) +#define Pinit ((P4 *)this) +typedef struct P4 { /* :init: */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar i; + uchar j; + uchar sum; + uchar commit_sum; +} P4; +#define Air4 (sizeof(P4) - Offsetof(P4, commit_sum) - 1*sizeof(uchar)) +#define Pcleaner ((P3 *)this) +typedef struct P3 { /* cleaner */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P3; +#define Air3 (sizeof(P3) - 3) +#define Preader ((P2 *)this) +typedef struct P2 { /* reader */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar i; + uchar j; +} P2; +#define Air2 (sizeof(P2) - Offsetof(P2, j) - 1*sizeof(uchar)) +#define Ptracer ((P1 *)this) +typedef struct P1 { /* tracer */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar size; + uchar prev_off; + uchar new_off; + uchar tmp_commit; + uchar i; + uchar j; +} P1; +#define Air1 (sizeof(P1) - Offsetof(P1, j) - 1*sizeof(uchar)) +#define Pswitcher ((P0 *)this) +typedef struct P0 { /* switcher */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar prev_off; + uchar new_off; + uchar tmp_commit; + uchar size; +} P0; +#define Air0 (sizeof(P0) - Offsetof(P0, size) - 1*sizeof(uchar)) +typedef struct P6 { /* np_ */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P6; +#define Air6 (sizeof(P6) - 3) +#if defined(BFS) && defined(REACH) +#undef REACH +#endif +#ifdef VERI +#define BASE 1 +#else +#define BASE 0 +#endif +typedef struct Trans { + short atom; /* if &2 = atomic trans; if &8 local */ +#ifdef HAS_UNLESS + short escp[HAS_UNLESS]; /* lists the escape states */ + short e_trans; /* if set, this is an escp-trans */ +#endif + short tpe[2]; /* class of operation (for reduction) */ + short qu[6]; /* for conditional selections: qid's */ + uchar ty[6]; /* ditto: type's */ +#ifdef NIBIS + short om; /* completion status of preselects */ +#endif + char *tp; /* src txt of statement */ + int st; /* the nextstate */ + int t_id; /* transition id, unique within proc */ + int forw; /* index forward transition */ + int back; /* index return transition */ + struct Trans *nxt; +} Trans; + +#define qptr(x) (((uchar *)&now)+(int)q_offset[x]) +#define pptr(x) (((uchar *)&now)+(int)proc_offset[x]) +extern uchar *Pptr(int); +#define q_sz(x) (((Q0 *)qptr(x))->Qlen) + +#ifndef VECTORSZ +#define VECTORSZ 1024 /* sv size in bytes */ +#endif + +#ifdef VERBOSE +#ifndef CHECK +#define CHECK +#endif +#ifndef DEBUG +#define DEBUG +#endif +#endif +#ifdef SAFETY +#ifndef NOFAIR +#define NOFAIR +#endif +#endif +#ifdef NOREDUCE +#ifndef XUSAFE +#define XUSAFE +#endif +#if !defined(SAFETY) && !defined(MA) +#define FULLSTACK +#endif +#else +#ifdef BITSTATE +#if defined(SAFETY) && !defined(HASH64) +#define CNTRSTACK +#else +#define FULLSTACK +#endif +#else +#define FULLSTACK +#endif +#endif +#ifdef BITSTATE +#ifndef NOCOMP +#define NOCOMP +#endif +#if !defined(LC) && defined(SC) +#define LC +#endif +#endif +#if defined(COLLAPSE2) || defined(COLLAPSE3) || defined(COLLAPSE4) +/* accept the above for backward compatibility */ +#define COLLAPSE +#endif +#ifdef HC +#undef HC +#define HC4 +#endif +#ifdef HC0 +#define HC 0 +#endif +#ifdef HC1 +#define HC 1 +#endif +#ifdef HC2 +#define HC 2 +#endif +#ifdef HC3 +#define HC 3 +#endif +#ifdef HC4 +#define HC 4 +#endif +#ifdef COLLAPSE +#if NCORE>1 && !defined(SEP_STATE) +unsigned long *ncomps; /* in shared memory */ +#else +unsigned long ncomps[256+2]; +#endif +#endif +#define MAXQ 255 +#define MAXPROC 255 +#define WS sizeof(void *) /* word size in bytes */ +typedef struct Stack { /* for queues and processes */ +#if VECTORSZ>32000 + int o_delta; + int o_offset; + int o_skip; + int o_delqs; +#else + short o_delta; + short o_offset; + short o_skip; + short o_delqs; +#endif + short o_boq; +#ifndef XUSAFE + char *o_name; +#endif + char *body; + struct Stack *nxt; + struct Stack *lst; +} Stack; + +typedef struct Svtack { /* for complete state vector */ +#if VECTORSZ>32000 + int o_delta; + int m_delta; +#else + short o_delta; /* current size of frame */ + short m_delta; /* maximum size of frame */ +#endif +#if SYNC + short o_boq; +#endif +#define StackSize (WS) + char *body; + struct Svtack *nxt; + struct Svtack *lst; +} Svtack; + +Trans ***trans; /* 1 ptr per state per proctype */ + +struct H_el *Lstate; +int depthfound = -1; /* loop detection */ +#if VECTORSZ>32000 +int proc_offset[MAXPROC]; +int q_offset[MAXQ]; +#else +short proc_offset[MAXPROC]; +short q_offset[MAXQ]; +#endif +uchar proc_skip[MAXPROC]; +uchar q_skip[MAXQ]; +unsigned long vsize; /* vector size in bytes */ +#ifdef SVDUMP +int vprefix=0, svfd; /* runtime option -pN */ +#endif +char *tprefix = "trail"; /* runtime option -tsuffix */ +short boq = -1; /* blocked_on_queue status */ +typedef struct State { + uchar _nr_pr; + uchar _nr_qs; + uchar _a_t; /* cycle detection */ +#ifndef NOFAIR + uchar _cnt[NFAIR]; /* counters, weak fairness */ +#endif +#ifndef NOVSZ +#if VECTORSZ<65536 + unsigned short _vsz; +#else + unsigned long _vsz; +#endif +#endif +#ifdef HAS_LAST + uchar _last; /* pid executed in last step */ +#endif +#ifdef EVENT_TRACE +#if nstates_event<256 + uchar _event; +#else + unsigned short _event; +#endif +#endif + uchar buffer_use[4]; + uchar write_off; + uchar commit_count[2]; + uchar _commit_sum; + uchar read_off; + uchar events_lost; + uchar refcount; + uchar sv[VECTORSZ]; +} State; + +#define HAS_TRACK 0 +/* hidden variable: */ uchar deliver; +int _; /* a predefined write-only variable */ + +#define FORWARD_MOVES "pan.m" +#define REVERSE_MOVES "pan.b" +#define TRANSITIONS "pan.t" +#define _NP_ 6 +uchar reached6[3]; /* np_ */ +uchar *loopstate6; /* np_ */ +#define nstates6 3 /* np_ */ +#define endstate6 2 /* np_ */ + +#define start6 0 /* np_ */ +#define start5 5 +#define start_claim 5 +#define start4 42 +#define start3 8 +#define start2 26 +#define start1 3 +#define start0 11 +#ifdef NP + #define ACCEPT_LAB 1 /* at least 1 in np_ */ +#else + #define ACCEPT_LAB 1 /* user-defined accept labels */ +#endif +#ifdef MEMCNT + #ifdef MEMLIM + #warning -DMEMLIM takes precedence over -DMEMCNT + #undef MEMCNT + #else + #if MEMCNT<20 + #warning using minimal value -DMEMCNT=20 (=1MB) + #define MEMLIM (1) + #undef MEMCNT + #else + #if MEMCNT==20 + #define MEMLIM (1) + #undef MEMCNT + #else + #if MEMCNT>=50 + #error excessive value for MEMCNT + #else + #define MEMLIM (1<<(MEMCNT-20)) + #endif + #endif + #endif + #endif +#endif +#if NCORE>1 && !defined(MEMLIM) + #define MEMLIM (2048) /* need a default, using 2 GB */ +#endif +#define PROG_LAB 0 /* progress labels */ +uchar *accpstate[7]; +uchar *progstate[7]; +uchar *loopstate[7]; +uchar *reached[7]; +uchar *stopstate[7]; +uchar *visstate[7]; +short *mapstate[7]; +#ifdef HAS_CODE +int NrStates[7]; +#endif +#define NQS 0 +short q_flds[1]; +short q_max[1]; +typedef struct Q0 { /* generic q */ + uchar Qlen; /* q_size */ + uchar _t; +} Q0; + +/** function prototypes **/ +char *emalloc(unsigned long); +char *Malloc(unsigned long); +int Boundcheck(int, int, int, int, Trans *); +int addqueue(int, int); +/* int atoi(char *); */ +/* int abort(void); */ +int close(int); +int delproc(int, int); +int endstate(void); +int hstore(char *, int); +#ifdef MA +int gstore(char *, int, uchar); +#endif +int q_cond(short, Trans *); +int q_full(int); +int q_len(int); +int q_zero(int); +int qrecv(int, int, int, int); +int unsend(int); +/* void *sbrk(int); */ +void Uerror(char *); +void assert(int, char *, int, int, Trans *); +void c_chandump(int); +void c_globals(void); +void c_locals(int, int); +void checkcycles(void); +void crack(int, int, Trans *, short *); +void d_sfh(const char *, int); +void sfh(const char *, int); +void d_hash(uchar *, int); +void s_hash(uchar *, int); +void r_hash(uchar *, int); +void delq(int); +void do_reach(void); +void pan_exit(int); +void exit(int); +void hinit(void); +void imed(Trans *, int, int, int); +void new_state(void); +void p_restor(int); +void putpeg(int, int); +void putrail(void); +void q_restor(void); +void retrans(int, int, int, short *, uchar *, uchar *); +void settable(void); +void setq_claim(int, int, char *, int, char *); +void sv_restor(void); +void sv_save(void); +void tagtable(int, int, int, short *, uchar *); +void do_dfs(int, int, int, short *, uchar *, uchar *); +void uerror(char *); +void unrecv(int, int, int, int, int); +void usage(FILE *); +void wrap_stats(void); +#if defined(FULLSTACK) && defined(BITSTATE) +int onstack_now(void); +void onstack_init(void); +void onstack_put(void); +void onstack_zap(void); +#endif +#ifndef XUSAFE +int q_S_check(int, int); +int q_R_check(int, int); +uchar q_claim[MAXQ+1]; +char *q_name[MAXQ+1]; +char *p_name[MAXPROC+1]; +#endif +void qsend(int, int, int); +#define Addproc(x) addproc(x) +#define LOCAL 1 +#define Q_FULL_F 2 +#define Q_EMPT_F 3 +#define Q_EMPT_T 4 +#define Q_FULL_T 5 +#define TIMEOUT_F 6 +#define GLOBAL 7 +#define BAD 8 +#define ALPHA_F 9 +#define NTRANS 66 +#ifdef PEG +long peg[NTRANS]; +#endif diff --git a/trunk/verif/md/pan.ltl b/trunk/verif/md/pan.ltl new file mode 100644 index 00000000..09d943e8 --- /dev/null +++ b/trunk/verif/md/pan.ltl @@ -0,0 +1,22 @@ +#define rwoff1 (write_off - read_off >= 0) +#define rwoff2 (write_off - read_off < HALF_UCHAR) + +#define wcsum1 (write_off - _commit_sum >= 0) +#define wcsum2 (write_off - _commit_sum < HALF_UCHAR) + +#define buffer_large_enough (NUMPROCS + NUMSWITCH <= BUFSIZE) +#define have_events_lost (events_lost != 0) +never { /* !( buffer_large_enough -> ([](!have_events_lost))) */ +T0_init: + if + :: ((buffer_large_enough) && (have_events_lost)) -> goto accept_all + :: ((buffer_large_enough)) -> goto T0_S3 + fi; +T0_S3: + if + :: ((have_events_lost)) -> goto accept_all + :: (1) -> goto T0_S3 + fi; +accept_all: + skip +} diff --git a/trunk/verif/md/pan.m b/trunk/verif/md/pan.m new file mode 100644 index 00000000..e96178ba --- /dev/null +++ b/trunk/verif/md/pan.m @@ -0,0 +1,1010 @@ +#define rand pan_rand +#if defined(HAS_CODE) && defined(VERBOSE) + cpu_printf("Pr: %d Tr: %d\n", II, t->forw); +#endif + switch (t->forw) { + default: Uerror("bad forward move"); + case 0: /* if without executable clauses */ + continue; + case 1: /* generic 'goto' or 'skip' */ + IfNotBlocked + _m = 3; goto P999; + case 2: /* generic 'else' */ + IfNotBlocked + if (trpt->o_pm&1) continue; + _m = 3; goto P999; + + /* PROC :never: */ + case 3: /* STATE 1 - line 301 "pan.___" - [((((4+1)<=4)&&(events_lost!=0)))] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported1 = 0; + if (verbose && !reported1) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[1]); + reported1 = 1; + fflush(stdout); + } } +#endif + reached[5][1] = 1; + if (!((((4+1)<=4)&&(((int)now.events_lost)!=0)))) + continue; + _m = 3; goto P999; /* 0 */ + case 4: /* STATE 3 - line 302 "pan.___" - [(((4+1)<=4))] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported3 = 0; + if (verbose && !reported3) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[3]); + reported3 = 1; + fflush(stdout); + } } +#endif + reached[5][3] = 1; + if (!(((4+1)<=4))) + continue; + _m = 3; goto P999; /* 0 */ + case 5: /* STATE 7 - line 306 "pan.___" - [((events_lost!=0))] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported7 = 0; + if (verbose && !reported7) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[7]); + reported7 = 1; + fflush(stdout); + } } +#endif + reached[5][7] = 1; + if (!((((int)now.events_lost)!=0))) + continue; + _m = 3; goto P999; /* 0 */ + case 6: /* STATE 14 - line 311 "pan.___" - [-end-] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported14 = 0; + if (verbose && !reported14) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[14]); + reported14 = 1; + fflush(stdout); + } } +#endif + reached[5][14] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC :init: */ + case 7: /* STATE 1 - line 252 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[4][1] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 8: /* STATE 2 - line 254 "pan.___" - [((i<2))] (7:0:2 - 1) */ + IfNotBlocked + reached[4][2] = 1; + if (!((((int)((P4 *)this)->i)<2))) + continue; + /* merge: commit_count[i] = 0(7, 3, 7) */ + reached[4][3] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ]); + now.commit_count[ Index(((P4 *)this)->i, 2) ] = 0; +#ifdef VAR_RANGES + logval("commit_count[:init::i]", ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ])); +#endif + ; + /* merge: i = (i+1)(7, 4, 7) */ + reached[4][4] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 8, 7) */ + reached[4][8] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 9: /* STATE 5 - line 257 "pan.___" - [((i>=2))] (17:0:3 - 1) */ + IfNotBlocked + reached[4][5] = 1; + if (!((((int)((P4 *)this)->i)>=2))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b6(17, 6, 17) */ + reached[4][6] = 1; + ; + /* merge: _commit_sum = 0(17, 10, 17) */ + reached[4][10] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = 0; +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: i = 0(17, 11, 17) */ + reached[4][11] = 1; + (trpt+1)->bup.ovals[2] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 10: /* STATE 10 - line 260 "pan.___" - [_commit_sum = 0] (0:17:2 - 3) */ + IfNotBlocked + reached[4][10] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now._commit_sum); + now._commit_sum = 0; +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: i = 0(17, 11, 17) */ + reached[4][11] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 11: /* STATE 12 - line 264 "pan.___" - [((i<4))] (17:0:2 - 1) */ + IfNotBlocked + reached[4][12] = 1; + if (!((((int)((P4 *)this)->i)<4))) + continue; + /* merge: buffer_use[i] = 0(17, 13, 17) */ + reached[4][13] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 4) ]); + now.buffer_use[ Index(((P4 *)this)->i, 4) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[:init::i]", ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 4) ])); +#endif + ; + /* merge: i = (i+1)(17, 14, 17) */ + reached[4][14] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 12: /* STATE 15 - line 267 "pan.___" - [((i>=4))] (0:0:1 - 1) */ + IfNotBlocked + reached[4][15] = 1; + if (!((((int)((P4 *)this)->i)>=4))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + _m = 3; goto P999; /* 0 */ + case 13: /* STATE 20 - line 269 "pan.___" - [(run reader())] (0:0:0 - 3) */ + IfNotBlocked + reached[4][20] = 1; + if (!(addproc(2))) + continue; + _m = 3; goto P999; /* 0 */ + case 14: /* STATE 21 - line 270 "pan.___" - [(run cleaner())] (29:0:1 - 1) */ + IfNotBlocked + reached[4][21] = 1; + if (!(addproc(3))) + continue; + /* merge: i = 0(0, 22, 29) */ + reached[4][22] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 30, 29) */ + reached[4][30] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 15: /* STATE 23 - line 273 "pan.___" - [((i<4))] (25:0:1 - 1) */ + IfNotBlocked + reached[4][23] = 1; + if (!((((int)((P4 *)this)->i)<4))) + continue; + /* merge: refcount = (refcount+1)(0, 24, 25) */ + reached[4][24] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 16: /* STATE 25 - line 275 "pan.___" - [(run tracer())] (29:0:1 - 1) */ + IfNotBlocked + reached[4][25] = 1; + if (!(addproc(1))) + continue; + /* merge: i = (i+1)(0, 26, 29) */ + reached[4][26] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 30, 29) */ + reached[4][30] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 17: /* STATE 27 - line 277 "pan.___" - [((i>=4))] (39:0:2 - 1) */ + IfNotBlocked + reached[4][27] = 1; + if (!((((int)((P4 *)this)->i)>=4))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b8(39, 28, 39) */ + reached[4][28] = 1; + ; + /* merge: i = 0(39, 32, 39) */ + reached[4][32] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 18: /* STATE 32 - line 279 "pan.___" - [i = 0] (0:39:1 - 3) */ + IfNotBlocked + reached[4][32] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 19: /* STATE 33 - line 281 "pan.___" - [((i<1))] (35:0:1 - 1) */ + IfNotBlocked + reached[4][33] = 1; + if (!((((int)((P4 *)this)->i)<1))) + continue; + /* merge: refcount = (refcount+1)(0, 34, 35) */ + reached[4][34] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 20: /* STATE 35 - line 283 "pan.___" - [(run switcher())] (39:0:1 - 1) */ + IfNotBlocked + reached[4][35] = 1; + if (!(addproc(0))) + continue; + /* merge: i = (i+1)(0, 36, 39) */ + reached[4][36] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 21: /* STATE 37 - line 285 "pan.___" - [((i>=1))] (41:0:1 - 1) */ + IfNotBlocked + reached[4][37] = 1; + if (!((((int)((P4 *)this)->i)>=1))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b9(0, 38, 41) */ + reached[4][38] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 22: /* STATE 43 - line 288 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[4][43] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC cleaner */ + case 23: /* STATE 1 - line 237 "pan.___" - [((refcount==0))] (3:0:1 - 1) */ + IfNotBlocked + reached[3][1] = 1; + if (!((((int)now.refcount)==0))) + continue; + /* merge: refcount = (refcount+1)(0, 2, 3) */ + reached[3][2] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 24: /* STATE 3 - line 239 "pan.___" - [(run switcher())] (7:0:0 - 1) */ + IfNotBlocked + reached[3][3] = 1; + if (!(addproc(0))) + continue; + /* merge: goto :b5(0, 4, 7) */ + reached[3][4] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 25: /* STATE 9 - line 243 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[3][9] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC reader */ + case 26: /* STATE 1 - line 200 "pan.___" - [((((((write_off/(4/2))-(read_off/(4/2)))>0)&&(((write_off/(4/2))-(read_off/(4/2)))<(255/2)))&&(((commit_count[((read_off%4)/(4/2))]-(4/2))-(((read_off/4)*4)/2))==0)))] (0:0:0 - 1) */ + IfNotBlocked + reached[2][1] = 1; + if (!((((((((int)now.write_off)/(4/2))-(((int)now.read_off)/(4/2)))>0)&&(((((int)now.write_off)/(4/2))-(((int)now.read_off)/(4/2)))<(255/2)))&&(((((int)now.commit_count[ Index(((((int)now.read_off)%4)/(4/2)), 2) ])-(4/2))-(((((int)now.read_off)/4)*4)/2))==0)))) + continue; + _m = 3; goto P999; /* 0 */ + case 27: /* STATE 2 - line 202 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[2][2] = 1; + (trpt+1)->bup.oval = ((int)((P2 *)this)->i); + ((P2 *)this)->i = 0; +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 28: /* STATE 3 - line 204 "pan.___" - [((i<(4/2)))] (9:0:2 - 1) */ + IfNotBlocked + reached[2][3] = 1; + if (!((((int)((P2 *)this)->i)<(4/2)))) + continue; + /* merge: assert((buffer_use[((read_off+i)%4)]==0))(9, 4, 9) */ + reached[2][4] = 1; + assert((((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ])==0), "(buffer_use[((read_off+i)%4)]==0)", II, tt, t); + /* merge: buffer_use[((read_off+i)%4)] = 1(9, 5, 9) */ + reached[2][5] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]); + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = 1; +#ifdef VAR_RANGES + logval("buffer_use[((read_off+reader:i)%4)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ])); +#endif + ; + /* merge: i = (i+1)(9, 6, 9) */ + reached[2][6] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i); + ((P2 *)this)->i = (((int)((P2 *)this)->i)+1); +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 10, 9) */ + reached[2][10] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 29: /* STATE 7 - line 208 "pan.___" - [((i>=(4/2)))] (11:0:1 - 1) */ + IfNotBlocked + reached[2][7] = 1; + if (!((((int)((P2 *)this)->i)>=(4/2)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P2 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P2 *)this)->i = 0; + /* merge: goto :b3(0, 8, 11) */ + reached[2][8] = 1; + ; + _m = 3; goto P999; /* 1 */ +/* STATE 13 - line 216 "pan.___" - [i = 0] (0:0 - 1) same as 27 (0:0 - 1) */ + case 30: /* STATE 14 - line 218 "pan.___" - [((i<(4/2)))] (19:0:2 - 1) */ + IfNotBlocked + reached[2][14] = 1; + if (!((((int)((P2 *)this)->i)<(4/2)))) + continue; + /* merge: buffer_use[((read_off+i)%4)] = 0(19, 15, 19) */ + reached[2][15] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ]); + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%4), 4) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[((read_off+reader:i)%4)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%4), 4) ])); +#endif + ; + /* merge: i = (i+1)(19, 16, 19) */ + reached[2][16] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i); + ((P2 *)this)->i = (((int)((P2 *)this)->i)+1); +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 20, 19) */ + reached[2][20] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 31: /* STATE 17 - line 221 "pan.___" - [((i>=(4/2)))] (0:0:1 - 1) */ + IfNotBlocked + reached[2][17] = 1; + if (!((((int)((P2 *)this)->i)>=(4/2)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P2 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P2 *)this)->i = 0; + _m = 3; goto P999; /* 0 */ + case 32: /* STATE 22 - line 223 "pan.___" - [read_off = (read_off+(4/2))] (0:0:1 - 1) */ + IfNotBlocked + reached[2][22] = 1; + (trpt+1)->bup.oval = ((int)now.read_off); + now.read_off = (((int)now.read_off)+(4/2)); +#ifdef VAR_RANGES + logval("read_off", ((int)now.read_off)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 33: /* STATE 24 - line 225 "pan.___" - [((read_off>=(4-events_lost)))] (0:0:0 - 1) */ + IfNotBlocked + reached[2][24] = 1; + if (!((((int)now.read_off)>=(4-((int)now.events_lost))))) + continue; + _m = 3; goto P999; /* 0 */ + case 34: /* STATE 29 - line 227 "pan.___" - [-end-] (0:0:0 - 3) */ + IfNotBlocked + reached[2][29] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC tracer */ + case 35: /* STATE 1 - line 123 "pan.___" - [prev_off = write_off] (0:10:2 - 1) */ + IfNotBlocked + reached[1][1] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->prev_off); + ((P1 *)this)->prev_off = ((int)now.write_off); +#ifdef VAR_RANGES + logval("tracer:prev_off", ((int)((P1 *)this)->prev_off)); +#endif + ; + /* merge: new_off = (prev_off+size)(10, 2, 10) */ + reached[1][2] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->new_off); + ((P1 *)this)->new_off = (((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:new_off", ((int)((P1 *)this)->new_off)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 36: /* STATE 4 - line 128 "pan.___" - [((((new_off-read_off)>4)&&((new_off-read_off)<(255/2))))] (0:0:1 - 1) */ + IfNotBlocked + reached[1][4] = 1; + if (!((((((int)((P1 *)this)->new_off)-((int)now.read_off))>4)&&((((int)((P1 *)this)->new_off)-((int)now.read_off))<(255/2))))) + continue; + /* dead 1: new_off */ (trpt+1)->bup.oval = ((P1 *)this)->new_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->new_off = 0; + _m = 3; goto P999; /* 0 */ + case 37: /* STATE 7 - line 130 "pan.___" - [(1)] (27:0:0 - 1) */ + IfNotBlocked + reached[1][7] = 1; + if (!(1)) + continue; + /* merge: .(goto)(0, 9, 27) */ + reached[1][9] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 38: /* STATE 11 - line 135 "pan.___" - [((prev_off!=write_off))] (3:0:1 - 1) */ + IfNotBlocked + reached[1][11] = 1; + if (!((((int)((P1 *)this)->prev_off)!=((int)now.write_off)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P1 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->prev_off = 0; + /* merge: goto cmpxchg_loop(0, 12, 3) */ + reached[1][12] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 39: /* STATE 14 - line 136 "pan.___" - [write_off = new_off] (0:24:2 - 1) */ + IfNotBlocked + reached[1][14] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.write_off); + now.write_off = ((int)((P1 *)this)->new_off); +#ifdef VAR_RANGES + logval("write_off", ((int)now.write_off)); +#endif + ; + /* merge: .(goto)(24, 16, 24) */ + reached[1][16] = 1; + ; + /* merge: i = 0(24, 17, 24) */ + reached[1][17] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 40: /* STATE 17 - line 138 "pan.___" - [i = 0] (0:24:1 - 2) */ + IfNotBlocked + reached[1][17] = 1; + (trpt+1)->bup.oval = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 41: /* STATE 18 - line 140 "pan.___" - [((ii)<((int)((P1 *)this)->size)))) + continue; + /* merge: assert((buffer_use[((prev_off+i)%4)]==0))(24, 19, 24) */ + reached[1][19] = 1; + assert((((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ])==0), "(buffer_use[((prev_off+i)%4)]==0)", II, tt, t); + /* merge: buffer_use[((prev_off+i)%4)] = 1(24, 20, 24) */ + reached[1][20] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]); + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = 1; +#ifdef VAR_RANGES + logval("buffer_use[((tracer:prev_off+tracer:i)%4)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ])); +#endif + ; + /* merge: i = (i+1)(24, 21, 24) */ + reached[1][21] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = (((int)((P1 *)this)->i)+1); +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 42: /* STATE 22 - line 144 "pan.___" - [((i>=size))] (26:0:1 - 1) */ + IfNotBlocked + reached[1][22] = 1; + if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P1 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->i = 0; + /* merge: goto :b0(0, 23, 26) */ + reached[1][23] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 43: /* STATE 28 - line 152 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[1][28] = 1; + (trpt+1)->bup.oval = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 44: /* STATE 29 - line 154 "pan.___" - [((ii)<((int)((P1 *)this)->size)))) + continue; + /* merge: buffer_use[((prev_off+i)%4)] = 0(34, 30, 34) */ + reached[1][30] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ]); + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%4), 4) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[((tracer:prev_off+tracer:i)%4)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%4), 4) ])); +#endif + ; + /* merge: i = (i+1)(34, 31, 34) */ + reached[1][31] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = (((int)((P1 *)this)->i)+1); +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 35, 34) */ + reached[1][35] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 45: /* STATE 32 - line 157 "pan.___" - [((i>=size))] (44:0:4 - 1) */ + IfNotBlocked + reached[1][32] = 1; + if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size)))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(4); + (trpt+1)->bup.ovals[0] = ((P1 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->i = 0; + /* merge: goto :b1(44, 33, 44) */ + reached[1][33] = 1; + ; + /* merge: tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)(44, 37, 44) */ + reached[1][37] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->tmp_commit); + ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%4)/(4/2))])+tmp_commit)(44, 38, 44) */ + reached[1][38] = 1; + (trpt+1)->bup.ovals[2] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]))+((int)((P1 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(44, 39, 44) */ + reached[1][39] = 1; + (trpt+1)->bup.ovals[3] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]); + now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P1 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((tracer:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 4 */ + case 46: /* STATE 37 - line 159 "pan.___" - [tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)] (0:44:3 - 3) */ + IfNotBlocked + reached[1][37] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->tmp_commit); + ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%4)/(4/2))])+tmp_commit)(44, 38, 44) */ + reached[1][38] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]))+((int)((P1 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(44, 39, 44) */ + reached[1][39] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ]); + now.commit_count[ Index(((((P1 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P1 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((tracer:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%4)/(4/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 47: /* STATE 40 - line 167 "pan.___" - [((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))] (50:0:3 - 1) */ + IfNotBlocked + reached[1][40] = 1; + if (!((((((((int)((P1 *)this)->prev_off)/4)*4)/2)+(4/2))-((int)((P1 *)this)->tmp_commit)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P1 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->prev_off = 0; + /* dead 1: tmp_commit */ (trpt+1)->bup.ovals[1] = ((P1 *)this)->tmp_commit; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->tmp_commit = 0; + /* merge: deliver = 1(50, 41, 50) */ + reached[1][41] = 1; + (trpt+1)->bup.ovals[2] = ((int)deliver); + deliver = 1; +#ifdef VAR_RANGES + logval("deliver", ((int)deliver)); +#endif + ; + /* merge: .(goto)(50, 45, 50) */ + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 48: /* STATE 45 - line 172 "pan.___" - [.(goto)] (0:50:0 - 2) */ + IfNotBlocked + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 0 */ + case 49: /* STATE 43 - line 170 "pan.___" - [(1)] (50:0:0 - 1) */ + IfNotBlocked + reached[1][43] = 1; + if (!(1)) + continue; + /* merge: .(goto)(50, 45, 50) */ + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 50: /* STATE 48 - line 176 "pan.___" - [events_lost = (events_lost+1)] (0:0:1 - 2) */ + IfNotBlocked + reached[1][48] = 1; + (trpt+1)->bup.oval = ((int)now.events_lost); + now.events_lost = (((int)now.events_lost)+1); +#ifdef VAR_RANGES + logval("events_lost", ((int)now.events_lost)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 51: /* STATE 49 - line 178 "pan.___" - [refcount = (refcount-1)] (0:0:1 - 2) */ + IfNotBlocked + reached[1][49] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 52: /* STATE 51 - line 180 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[1][51] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC switcher */ + case 53: /* STATE 1 - line 72 "pan.___" - [prev_off = write_off] (0:9:3 - 1) */ + IfNotBlocked + reached[0][1] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->prev_off); + ((P0 *)this)->prev_off = ((int)now.write_off); +#ifdef VAR_RANGES + logval("switcher:prev_off", ((int)((P0 *)this)->prev_off)); +#endif + ; + /* merge: size = ((4/2)-(prev_off%(4/2)))(9, 2, 9) */ + reached[0][2] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P0 *)this)->size); + ((P0 *)this)->size = ((4/2)-(((int)((P0 *)this)->prev_off)%(4/2))); +#ifdef VAR_RANGES + logval("switcher:size", ((int)((P0 *)this)->size)); +#endif + ; + /* merge: new_off = (prev_off+size)(9, 3, 9) */ + reached[0][3] = 1; + (trpt+1)->bup.ovals[2] = ((int)((P0 *)this)->new_off); + ((P0 *)this)->new_off = (((int)((P0 *)this)->prev_off)+((int)((P0 *)this)->size)); +#ifdef VAR_RANGES + logval("switcher:new_off", ((int)((P0 *)this)->new_off)); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 54: /* STATE 4 - line 77 "pan.___" - [(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))] (30:0:3 - 1) */ + IfNotBlocked + reached[0][4] = 1; + if (!(((((((int)((P0 *)this)->new_off)-((int)now.read_off))>4)&&((((int)((P0 *)this)->new_off)-((int)now.read_off))<(255/2)))||(((int)((P0 *)this)->size)==(4/2))))) + continue; + /* dead 1: new_off */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P0 *)this)->new_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->new_off = 0; + /* dead 1: size */ (trpt+1)->bup.ovals[1] = ((P0 *)this)->size; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->size = 0; + /* merge: refcount = (refcount-1)(30, 5, 30) */ + reached[0][5] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + /* merge: goto not_needed(30, 6, 30) */ + reached[0][6] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 55: /* STATE 8 - line 80 "pan.___" - [(1)] (18:0:0 - 1) */ + IfNotBlocked + reached[0][8] = 1; + if (!(1)) + continue; + /* merge: .(goto)(0, 10, 18) */ + reached[0][10] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 56: /* STATE 12 - line 85 "pan.___" - [((prev_off!=write_off))] (11:0:1 - 1) */ + IfNotBlocked + reached[0][12] = 1; + if (!((((int)((P0 *)this)->prev_off)!=((int)now.write_off)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P0 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->prev_off = 0; + /* merge: goto cmpxchg_loop(0, 13, 11) */ + reached[0][13] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 57: /* STATE 17 - line 88 "pan.___" - [.(goto)] (0:29:0 - 1) */ + IfNotBlocked + reached[0][17] = 1; + ; + _m = 3; goto P999; /* 0 */ + case 58: /* STATE 15 - line 86 "pan.___" - [write_off = new_off] (0:29:1 - 1) */ + IfNotBlocked + reached[0][15] = 1; + (trpt+1)->bup.oval = ((int)now.write_off); + now.write_off = ((int)((P0 *)this)->new_off); +#ifdef VAR_RANGES + logval("write_off", ((int)now.write_off)); +#endif + ; + /* merge: .(goto)(29, 17, 29) */ + reached[0][17] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 59: /* STATE 19 - line 91 "pan.___" - [tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)] (0:26:3 - 1) */ + IfNotBlocked + reached[0][19] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->tmp_commit); + ((P0 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ])+((int)((P0 *)this)->size)); +#ifdef VAR_RANGES + logval("switcher:tmp_commit", ((int)((P0 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%4)/(4/2))])+tmp_commit)(26, 20, 26) */ + reached[0][20] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ]))+((int)((P0 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%4)/(4/2))] = tmp_commit(26, 21, 26) */ + reached[0][21] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ]); + now.commit_count[ Index(((((P0 *)this)->prev_off%4)/(4/2)), 2) ] = ((int)((P0 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((switcher:prev_off%4)/(4/2))]", ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%4)/(4/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 60: /* STATE 22 - line 99 "pan.___" - [((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))] (30:0:4 - 1) */ + IfNotBlocked + reached[0][22] = 1; + if (!((((((((int)((P0 *)this)->prev_off)/4)*4)/2)+(4/2))-((int)((P0 *)this)->tmp_commit)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.ovals = grab_ints(4); + (trpt+1)->bup.ovals[0] = ((P0 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->prev_off = 0; + /* dead 1: tmp_commit */ (trpt+1)->bup.ovals[1] = ((P0 *)this)->tmp_commit; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->tmp_commit = 0; + /* merge: deliver = 1(30, 23, 30) */ + reached[0][23] = 1; + (trpt+1)->bup.ovals[2] = ((int)deliver); + deliver = 1; +#ifdef VAR_RANGES + logval("deliver", ((int)deliver)); +#endif + ; + /* merge: .(goto)(30, 27, 30) */ + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.ovals[3] = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 3 */ + case 61: /* STATE 27 - line 104 "pan.___" - [.(goto)] (0:30:1 - 2) */ + IfNotBlocked + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 62: /* STATE 25 - line 102 "pan.___" - [(1)] (30:0:1 - 1) */ + IfNotBlocked + reached[0][25] = 1; + if (!(1)) + continue; + /* merge: .(goto)(30, 27, 30) */ + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 63: /* STATE 31 - line 108 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[0][31] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + case _T5: /* np_ */ + if (!((!(trpt->o_pm&4) && !(trpt->tau&128)))) + continue; + /* else fall through */ + case _T2: /* true */ + _m = 3; goto P999; +#undef rand + } + diff --git a/trunk/verif/md/pan.oin b/trunk/verif/md/pan.oin new file mode 100644 index 00000000..f079cb17 --- /dev/null +++ b/trunk/verif/md/pan.oin @@ -0,0 +1,307 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* */ +byte _commit_sum; +/* */ + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + /* */ + _commit_sum = 0; + /* */ + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + _commit_sum = 0; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + //assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - _commit_sum >= 0 && write_off - _commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + + diff --git a/trunk/verif/md/pan.otl b/trunk/verif/md/pan.otl new file mode 100644 index 00000000..37e2edf4 --- /dev/null +++ b/trunk/verif/md/pan.otl @@ -0,0 +1,32 @@ +#define p (write_off - read_off >= 0) +#define q (write_off - read_off < HALF_UCHAR) + + /* + * Formula As Typed: [] (p && q) + * The Never Claim Below Corresponds + * To The Negated Formula !([] (p && q)) + * (formalizing violations of the original) + */ + +never { /* !([] (p && q)) */ +T0_init: + if + :: (((! ((p))) || (! ((q))))) -> goto accept_all + :: (1) -> goto T0_init + fi; +accept_all: + skip +} + +#ifdef NOTES +Use Load to open a file or a template. +#endif +#ifdef RESULT +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 321) +Running verification -- waiting for output + (kill background process 'pan' to abort run) + (use /bin/ps to find pid; then: kill -2 pid) + +#endif diff --git a/trunk/verif/md/pan.out b/trunk/verif/md/pan.out new file mode 100644 index 00000000..cd59a065 --- /dev/null +++ b/trunk/verif/md/pan.out @@ -0,0 +1,50 @@ +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 321) +Depth= 201 States= 1e+06 Transitions= 3.11e+06 Memory= 79.063 t= 25.4 R= 4e+04 +Depth= 201 States= 2e+06 Transitions= 6.44e+06 Memory= 156.895 t= 46.1 R= 4e+04 +pan: resizing hashtable to -w21.. done + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 100 byte, depth reached 203, errors: 0 + 2590821 states, stored + 5924281 states, matched + 8515102 transitions (= stored+matched) + 9636364 atomic steps +hash conflicts: 6260639 (resolved) + +Stats on memory usage (in Megabytes): + 286.613 equivalent memory usage for states (stored*(State-vector + overhead)) + 202.768 actual memory usage for states (compression: 70.75%) + state-vector as stored = 66 byte + 16 byte overhead + 8.000 memory used for hash table (-w21) + 0.305 memory used for DFS stack (-m10000) + 210.891 total actual memory usage + +unreached in proctype switcher + (0 of 31 states) +unreached in proctype tracer + (0 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + (0 of 47 states) +unreached in proctype :never: + line 326, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 56.4 seconds +pan: rate 45960.99 states/second +pan: avg transition delay 6.62e-06 usec +23.05user 0.18system 0:56.38elapsed 41%CPU (0avgtext+0avgdata 0maxresident)k +0inputs+0outputs (0major+54128minor)pagefaults 0swaps diff --git a/trunk/verif/md/pan.t b/trunk/verif/md/pan.t new file mode 100644 index 00000000..0e25e9bf --- /dev/null +++ b/trunk/verif/md/pan.t @@ -0,0 +1,924 @@ +#ifdef PEG +struct T_SRC { + char *fl; int ln; +} T_SRC[NTRANS]; + +void +tr_2_src(int m, char *file, int ln) +{ T_SRC[m].fl = file; + T_SRC[m].ln = ln; +} + +void +putpeg(int n, int m) +{ printf("%5d trans %4d ", m, n); + printf("file %s line %3d\n", + T_SRC[n].fl, T_SRC[n].ln); +} +#endif + +void +settable(void) +{ Trans *T; + Trans *settr(int, int, int, int, int, char *, int, int, int); + + trans = (Trans ***) emalloc(7*sizeof(Trans **)); + + /* proctype 5: :never: */ + + trans[5] = (Trans **) emalloc(15*sizeof(Trans *)); + + T = trans[5][5] = settr(167,0,0,0,0,"IF", 0, 2, 0); + T = T->nxt = settr(167,0,1,0,0,"IF", 0, 2, 0); + T->nxt = settr(167,0,3,0,0,"IF", 0, 2, 0); + trans[5][1] = settr(163,0,13,3,0,"((((4+1)<=4)&&(events_lost!=0)))", 1, 2, 0); + trans[5][2] = settr(164,0,13,1,0,"goto accept_all", 0, 2, 0); + trans[5][6] = settr(168,0,11,1,0,".(goto)", 0, 2, 0); + trans[5][3] = settr(165,0,11,4,0,"(((4+1)<=4))", 0, 2, 0); + trans[5][4] = settr(166,0,11,1,0,"goto T0_S3", 0, 2, 0); + T = trans[5][11] = settr(173,0,0,0,0,"IF", 0, 2, 0); + T = T->nxt = settr(173,0,7,0,0,"IF", 0, 2, 0); + T->nxt = settr(173,0,9,0,0,"IF", 0, 2, 0); + trans[5][7] = settr(169,0,13,5,0,"((events_lost!=0))", 1, 2, 0); + trans[5][8] = settr(170,0,13,1,0,"goto accept_all", 0, 2, 0); + trans[5][12] = settr(174,0,13,1,0,".(goto)", 0, 2, 0); + trans[5][9] = settr(171,0,11,1,0,"(1)", 0, 2, 0); + trans[5][10] = settr(172,0,11,1,0,"goto T0_S3", 0, 2, 0); + trans[5][13] = settr(175,0,14,1,0,"(1)", 0, 2, 0); + trans[5][14] = settr(176,0,0,6,6,"-end-", 0, 3500, 0); + + /* proctype 4: :init: */ + + trans[4] = (Trans **) emalloc(44*sizeof(Trans *)); + + T = trans[ 4][42] = settr(161,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(161,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[4][1] = settr(120,2,7,7,7,"i = 0", 1, 2, 0); + trans[4][8] = settr(127,2,7,1,0,".(goto)", 1, 2, 0); + T = trans[4][7] = settr(126,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(126,2,2,0,0,"DO", 1, 2, 0); + T->nxt = settr(126,2,5,0,0,"DO", 1, 2, 0); + trans[4][2] = settr(121,2,7,8,8,"((i<2))", 1, 2, 0); /* m: 3 -> 7,0 */ + reached4[3] = 1; + trans[4][3] = settr(0,0,0,0,0,"commit_count[i] = 0",0,0,0); + trans[4][4] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][5] = settr(124,2,17,9,9,"((i>=2))", 1, 2, 0); /* m: 10 -> 17,0 */ + reached4[10] = 1; + trans[4][6] = settr(125,2,10,1,0,"goto :b6", 1, 2, 0); /* m: 10 -> 0,17 */ + reached4[10] = 1; + trans[4][9] = settr(128,2,10,1,0,"break", 1, 2, 0); + trans[4][10] = settr(129,2,17,10,10,"_commit_sum = 0", 1, 2, 0); /* m: 11 -> 0,17 */ + reached4[11] = 1; + trans[4][11] = settr(0,0,0,0,0,"i = 0",0,0,0); + trans[4][18] = settr(137,2,17,1,0,".(goto)", 1, 2, 0); + T = trans[4][17] = settr(136,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(136,2,12,0,0,"DO", 1, 2, 0); + T->nxt = settr(136,2,15,0,0,"DO", 1, 2, 0); + trans[4][12] = settr(131,2,17,11,11,"((i<4))", 1, 2, 0); /* m: 13 -> 17,0 */ + reached4[13] = 1; + trans[4][13] = settr(0,0,0,0,0,"buffer_use[i] = 0",0,0,0); + trans[4][14] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][15] = settr(134,2,20,12,12,"((i>=4))", 1, 2, 0); + trans[4][16] = settr(135,2,20,1,0,"goto :b7", 1, 2, 0); + trans[4][19] = settr(138,2,20,1,0,"break", 1, 2, 0); + trans[4][20] = settr(139,2,21,13,13,"(run reader())", 1, 2, 0); + trans[4][21] = settr(140,2,29,14,14,"(run cleaner())", 1, 2, 0); /* m: 22 -> 29,0 */ + reached4[22] = 1; + trans[4][22] = settr(0,0,0,0,0,"i = 0",0,0,0); + trans[4][30] = settr(149,2,29,1,0,".(goto)", 1, 2, 0); + T = trans[4][29] = settr(148,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(148,2,23,0,0,"DO", 1, 2, 0); + T->nxt = settr(148,2,27,0,0,"DO", 1, 2, 0); + trans[4][23] = settr(142,2,25,15,15,"((i<4))", 1, 2, 0); /* m: 24 -> 25,0 */ + reached4[24] = 1; + trans[4][24] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[4][25] = settr(144,2,29,16,16,"(run tracer())", 1, 2, 0); /* m: 26 -> 29,0 */ + reached4[26] = 1; + trans[4][26] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][27] = settr(146,2,39,17,17,"((i>=4))", 1, 2, 0); /* m: 32 -> 39,0 */ + reached4[32] = 1; + trans[4][28] = settr(147,2,32,1,0,"goto :b8", 1, 2, 0); /* m: 32 -> 0,39 */ + reached4[32] = 1; + trans[4][31] = settr(150,2,32,1,0,"break", 1, 2, 0); + trans[4][32] = settr(151,2,39,18,18,"i = 0", 1, 2, 0); + trans[4][40] = settr(159,2,39,1,0,".(goto)", 1, 2, 0); + T = trans[4][39] = settr(158,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(158,2,33,0,0,"DO", 1, 2, 0); + T->nxt = settr(158,2,37,0,0,"DO", 1, 2, 0); + trans[4][33] = settr(152,2,35,19,19,"((i<1))", 1, 2, 0); /* m: 34 -> 35,0 */ + reached4[34] = 1; + trans[4][34] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[4][35] = settr(154,2,39,20,20,"(run switcher())", 1, 2, 0); /* m: 36 -> 39,0 */ + reached4[36] = 1; + trans[4][36] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][37] = settr(156,2,41,21,21,"((i>=1))", 1, 2, 0); /* m: 38 -> 41,0 */ + reached4[38] = 1; + trans[4][38] = settr(157,2,41,1,0,"goto :b9", 1, 2, 0); + trans[4][41] = settr(160,0,43,1,0,"break", 1, 2, 0); + trans[4][43] = settr(162,0,0,22,22,"-end-", 0, 3500, 0); + + /* proctype 3: cleaner */ + + trans[3] = (Trans **) emalloc(10*sizeof(Trans *)); + + T = trans[ 3][8] = settr(118,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(118,2,5,0,0,"ATOMIC", 1, 2, 0); + trans[3][6] = settr(116,2,5,1,0,".(goto)", 1, 2, 0); + T = trans[3][5] = settr(115,2,0,0,0,"DO", 1, 2, 0); + T->nxt = settr(115,2,1,0,0,"DO", 1, 2, 0); + trans[3][1] = settr(111,2,3,23,23,"((refcount==0))", 1, 2, 0); /* m: 2 -> 3,0 */ + reached3[2] = 1; + trans[3][2] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[3][3] = settr(113,2,7,24,24,"(run switcher())", 1, 2, 0); /* m: 4 -> 7,0 */ + reached3[4] = 1; + trans[3][4] = settr(114,2,7,1,0,"goto :b5", 1, 2, 0); + trans[3][7] = settr(117,0,9,1,0,"break", 1, 2, 0); + trans[3][9] = settr(119,0,0,25,25,"-end-", 0, 3500, 0); + + /* proctype 2: reader */ + + trans[2] = (Trans **) emalloc(30*sizeof(Trans *)); + + trans[2][27] = settr(108,0,26,1,0,".(goto)", 0, 2, 0); + T = trans[2][26] = settr(107,0,0,0,0,"DO", 0, 2, 0); + T = T->nxt = settr(107,0,1,0,0,"DO", 0, 2, 0); + T->nxt = settr(107,0,24,0,0,"DO", 0, 2, 0); + trans[2][1] = settr(82,0,12,26,0,"((((((write_off/(4/2))-(read_off/(4/2)))>0)&&(((write_off/(4/2))-(read_off/(4/2)))<(255/2)))&&(((commit_count[((read_off%4)/(4/2))]-(4/2))-(((read_off/4)*4)/2))==0)))", 1, 2, 0); + T = trans[ 2][12] = settr(93,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(93,2,2,0,0,"ATOMIC", 1, 2, 0); + trans[2][2] = settr(83,2,9,27,27,"i = 0", 1, 2, 0); + trans[2][10] = settr(91,2,9,1,0,".(goto)", 1, 2, 0); + T = trans[2][9] = settr(90,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(90,2,3,0,0,"DO", 1, 2, 0); + T->nxt = settr(90,2,7,0,0,"DO", 1, 2, 0); + trans[2][3] = settr(84,2,9,28,28,"((i<(4/2)))", 1, 2, 0); /* m: 4 -> 9,0 */ + reached2[4] = 1; + trans[2][4] = settr(0,0,0,0,0,"assert((buffer_use[((read_off+i)%4)]==0))",0,0,0); + trans[2][5] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%4)] = 1",0,0,0); + trans[2][6] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[2][7] = settr(88,2,11,29,29,"((i>=(4/2)))", 1, 2, 0); /* m: 8 -> 11,0 */ + reached2[8] = 1; + trans[2][8] = settr(89,2,11,1,0,"goto :b3", 1, 2, 0); + trans[2][11] = settr(92,0,23,1,0,"break", 1, 2, 0); + T = trans[ 2][23] = settr(104,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(104,2,13,0,0,"ATOMIC", 1, 2, 0); + trans[2][13] = /* c */ settr(94,2,19,27,27,"i = 0", 1, 2, 0); + trans[2][20] = settr(101,2,19,1,0,".(goto)", 1, 2, 0); + T = trans[2][19] = settr(100,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(100,2,14,0,0,"DO", 1, 2, 0); + T->nxt = settr(100,2,17,0,0,"DO", 1, 2, 0); + trans[2][14] = settr(95,2,19,30,30,"((i<(4/2)))", 1, 2, 0); /* m: 15 -> 19,0 */ + reached2[15] = 1; + trans[2][15] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%4)] = 0",0,0,0); + trans[2][16] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[2][17] = settr(98,2,21,31,31,"((i>=(4/2)))", 1, 2, 0); + trans[2][18] = settr(99,2,21,1,0,"goto :b4", 1, 2, 0); + trans[2][21] = settr(102,2,22,1,0,"break", 1, 2, 0); + trans[2][22] = settr(103,0,26,32,32,"read_off = (read_off+(4/2))", 1, 2, 0); + trans[2][24] = settr(105,0,29,33,0,"((read_off>=(4-events_lost)))", 1, 2, 0); + trans[2][25] = settr(106,0,29,1,0,"goto :b2", 0, 2, 0); + trans[2][28] = settr(109,0,29,1,0,"break", 0, 2, 0); + trans[2][29] = settr(110,0,0,34,34,"-end-", 0, 3500, 0); + + /* proctype 1: tracer */ + + trans[1] = (Trans **) emalloc(52*sizeof(Trans *)); + + T = trans[ 1][3] = settr(33,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(33,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[1][1] = settr(31,4,10,35,35,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,10 */ + reached1[2] = 1; + trans[1][2] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0); + T = trans[ 1][10] = settr(40,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(40,2,8,0,0,"ATOMIC", 1, 2, 0); + T = trans[1][8] = settr(38,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(38,2,4,0,0,"IF", 1, 2, 0); + T->nxt = settr(38,2,6,0,0,"IF", 1, 2, 0); + trans[1][4] = settr(34,2,48,36,36,"((((new_off-read_off)>4)&&((new_off-read_off)<(255/2))))", 1, 2, 0); + trans[1][5] = settr(35,2,48,1,0,"goto lost", 1, 2, 0); + trans[1][9] = settr(39,0,27,1,0,".(goto)", 1, 2, 0); + trans[1][6] = settr(36,2,7,2,0,"else", 1, 2, 0); + trans[1][7] = settr(37,4,27,37,37,"(1)", 1, 2, 0); /* m: 9 -> 27,0 */ + reached1[9] = 1; + T = trans[ 1][27] = settr(57,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(57,2,15,0,0,"ATOMIC", 1, 2, 0); + T = trans[1][15] = settr(45,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(45,2,11,0,0,"IF", 1, 2, 0); + T->nxt = settr(45,2,13,0,0,"IF", 1, 2, 0); + trans[1][11] = settr(41,4,3,38,38,"((prev_off!=write_off))", 1, 2, 0); /* m: 12 -> 3,0 */ + reached1[12] = 1; + trans[1][12] = settr(42,0,3,1,0,"goto cmpxchg_loop", 1, 2, 0); + trans[1][16] = settr(46,2,17,1,0,".(goto)", 1, 2, 0); /* m: 17 -> 0,24 */ + reached1[17] = 1; + trans[1][13] = settr(43,2,14,2,0,"else", 1, 2, 0); + trans[1][14] = settr(44,2,24,39,39,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,24 */ + reached1[17] = 1; + trans[1][17] = settr(47,2,24,40,40,"i = 0", 1, 2, 0); + trans[1][25] = settr(55,2,24,1,0,".(goto)", 1, 2, 0); + T = trans[1][24] = settr(54,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(54,2,18,0,0,"DO", 1, 2, 0); + T->nxt = settr(54,2,22,0,0,"DO", 1, 2, 0); + trans[1][18] = settr(48,2,24,41,41,"((i 24,0 */ + reached1[19] = 1; + trans[1][19] = settr(0,0,0,0,0,"assert((buffer_use[((prev_off+i)%4)]==0))",0,0,0); + trans[1][20] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%4)] = 1",0,0,0); + trans[1][21] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[1][22] = settr(52,2,26,42,42,"((i>=size))", 1, 2, 0); /* m: 23 -> 26,0 */ + reached1[23] = 1; + trans[1][23] = settr(53,2,26,1,0,"goto :b0", 1, 2, 0); + trans[1][26] = settr(56,0,46,1,0,"break", 1, 2, 0); + T = trans[ 1][46] = settr(76,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(76,2,28,0,0,"ATOMIC", 1, 2, 0); + trans[1][28] = settr(58,2,34,43,43,"i = 0", 1, 2, 0); + trans[1][35] = settr(65,2,34,1,0,".(goto)", 1, 2, 0); + T = trans[1][34] = settr(64,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(64,2,29,0,0,"DO", 1, 2, 0); + T->nxt = settr(64,2,32,0,0,"DO", 1, 2, 0); + trans[1][29] = settr(59,2,34,44,44,"((i 34,0 */ + reached1[30] = 1; + trans[1][30] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%4)] = 0",0,0,0); + trans[1][31] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[1][32] = settr(62,2,44,45,45,"((i>=size))", 1, 2, 0); /* m: 37 -> 44,0 */ + reached1[37] = 1; + trans[1][33] = settr(63,2,37,1,0,"goto :b1", 1, 2, 0); /* m: 37 -> 0,44 */ + reached1[37] = 1; + trans[1][36] = settr(66,2,37,1,0,"break", 1, 2, 0); + trans[1][37] = settr(67,2,44,46,46,"tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)", 1, 2, 0); /* m: 38 -> 0,44 */ + reached1[38] = 1; + trans[1][38] = settr(0,0,0,0,0,"_commit_sum = ((_commit_sum-commit_count[((prev_off%4)/(4/2))])+tmp_commit)",0,0,0); + trans[1][39] = settr(0,0,0,0,0,"commit_count[((prev_off%4)/(4/2))] = tmp_commit",0,0,0); + T = trans[1][44] = settr(74,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(74,2,40,0,0,"IF", 1, 2, 0); + T->nxt = settr(74,2,42,0,0,"IF", 1, 2, 0); + trans[1][40] = settr(70,4,50,47,47,"((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))", 1, 2, 0); /* m: 41 -> 50,0 */ + reached1[41] = 1; + trans[1][41] = settr(0,0,0,0,0,"deliver = 1",0,0,0); + trans[1][45] = settr(75,0,50,48,48,".(goto)", 1, 2, 0); + trans[1][42] = settr(72,2,43,2,0,"else", 1, 2, 0); + trans[1][43] = settr(73,4,50,49,49,"(1)", 1, 2, 0); /* m: 45 -> 50,0 */ + reached1[45] = 1; + T = trans[ 1][50] = settr(80,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(80,2,47,0,0,"ATOMIC", 1, 2, 0); + trans[1][47] = settr(77,2,49,1,0,"goto end", 1, 2, 0); + trans[1][48] = settr(78,2,49,50,50,"events_lost = (events_lost+1)", 1, 2, 0); + trans[1][49] = settr(79,0,51,51,51,"refcount = (refcount-1)", 1, 2, 0); + trans[1][51] = settr(81,0,0,52,52,"-end-", 0, 3500, 0); + + /* proctype 0: switcher */ + + trans[0] = (Trans **) emalloc(32*sizeof(Trans *)); + + T = trans[ 0][11] = settr(10,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(10,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[0][1] = settr(0,2,9,53,53,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,9 */ + reached0[2] = 1; + trans[0][2] = settr(0,0,0,0,0,"size = ((4/2)-(prev_off%(4/2)))",0,0,0); + trans[0][3] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0); + T = trans[0][9] = settr(8,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(8,2,4,0,0,"IF", 1, 2, 0); + T->nxt = settr(8,2,7,0,0,"IF", 1, 2, 0); + trans[0][4] = settr(3,4,30,54,54,"(((((new_off-read_off)>4)&&((new_off-read_off)<(255/2)))||(size==(4/2))))", 1, 2, 0); /* m: 5 -> 30,0 */ + reached0[5] = 1; + trans[0][5] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0); + trans[0][6] = settr(5,0,30,1,0,"goto not_needed", 1, 2, 0); + trans[0][10] = settr(9,0,18,1,0,".(goto)", 1, 2, 0); + trans[0][7] = settr(6,2,8,2,0,"else", 1, 2, 0); + trans[0][8] = settr(7,4,18,55,55,"(1)", 1, 2, 0); /* m: 10 -> 18,0 */ + reached0[10] = 1; + T = trans[ 0][18] = settr(17,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(17,2,16,0,0,"ATOMIC", 1, 2, 0); + T = trans[0][16] = settr(15,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(15,2,12,0,0,"IF", 1, 2, 0); + T->nxt = settr(15,2,14,0,0,"IF", 1, 2, 0); + trans[0][12] = settr(11,4,11,56,56,"((prev_off!=write_off))", 1, 2, 0); /* m: 13 -> 11,0 */ + reached0[13] = 1; + trans[0][13] = settr(12,0,11,1,0,"goto cmpxchg_loop", 1, 2, 0); + trans[0][17] = settr(16,0,29,57,57,".(goto)", 1, 2, 0); + trans[0][14] = settr(13,2,15,2,0,"else", 1, 2, 0); + trans[0][15] = settr(14,4,29,58,58,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,29 */ + reached0[17] = 1; + T = trans[ 0][29] = settr(28,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(28,2,19,0,0,"ATOMIC", 1, 2, 0); + trans[0][19] = settr(18,2,26,59,59,"tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)", 1, 2, 0); /* m: 20 -> 0,26 */ + reached0[20] = 1; + trans[0][20] = settr(0,0,0,0,0,"_commit_sum = ((_commit_sum-commit_count[((prev_off%4)/(4/2))])+tmp_commit)",0,0,0); + trans[0][21] = settr(0,0,0,0,0,"commit_count[((prev_off%4)/(4/2))] = tmp_commit",0,0,0); + T = trans[0][26] = settr(25,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(25,2,22,0,0,"IF", 1, 2, 0); + T->nxt = settr(25,2,24,0,0,"IF", 1, 2, 0); + trans[0][22] = settr(21,4,30,60,60,"((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))", 1, 2, 0); /* m: 23 -> 30,0 */ + reached0[23] = 1; + trans[0][23] = settr(0,0,0,0,0,"deliver = 1",0,0,0); + trans[0][27] = settr(26,4,30,61,61,".(goto)", 1, 2, 0); /* m: 28 -> 0,30 */ + reached0[28] = 1; + trans[0][24] = settr(23,2,25,2,0,"else", 1, 2, 0); + trans[0][25] = settr(24,4,30,62,62,"(1)", 1, 2, 0); /* m: 27 -> 30,0 */ + reached0[27] = 1; + trans[0][28] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0); + trans[0][30] = settr(29,0,31,1,0,"(1)", 0, 2, 0); + trans[0][31] = settr(30,0,0,63,63,"-end-", 0, 3500, 0); + /* np_ demon: */ + trans[_NP_] = (Trans **) emalloc(2*sizeof(Trans *)); + T = trans[_NP_][0] = settr(9997,0,1,_T5,0,"(np_)", 1,2,0); + T->nxt = settr(9998,0,0,_T2,0,"(1)", 0,2,0); + T = trans[_NP_][1] = settr(9999,0,1,_T5,0,"(np_)", 1,2,0); +} + +Trans * +settr( int t_id, int a, int b, int c, int d, + char *t, int g, int tpe0, int tpe1) +{ Trans *tmp = (Trans *) emalloc(sizeof(Trans)); + + tmp->atom = a&(6|32); /* only (2|8|32) have meaning */ + if (!g) tmp->atom |= 8; /* no global references */ + tmp->st = b; + tmp->tpe[0] = tpe0; + tmp->tpe[1] = tpe1; + tmp->tp = t; + tmp->t_id = t_id; + tmp->forw = c; + tmp->back = d; + return tmp; +} + +Trans * +cpytr(Trans *a) +{ Trans *tmp = (Trans *) emalloc(sizeof(Trans)); + + int i; + tmp->atom = a->atom; + tmp->st = a->st; +#ifdef HAS_UNLESS + tmp->e_trans = a->e_trans; + for (i = 0; i < HAS_UNLESS; i++) + tmp->escp[i] = a->escp[i]; +#endif + tmp->tpe[0] = a->tpe[0]; + tmp->tpe[1] = a->tpe[1]; + for (i = 0; i < 6; i++) + { tmp->qu[i] = a->qu[i]; + tmp->ty[i] = a->ty[i]; + } + tmp->tp = (char *) emalloc(strlen(a->tp)+1); + strcpy(tmp->tp, a->tp); + tmp->t_id = a->t_id; + tmp->forw = a->forw; + tmp->back = a->back; + return tmp; +} + +#ifndef NOREDUCE +int +srinc_set(int n) +{ if (n <= 2) return LOCAL; + if (n <= 2+ DELTA) return Q_FULL_F; /* 's' or nfull */ + if (n <= 2+2*DELTA) return Q_EMPT_F; /* 'r' or nempty */ + if (n <= 2+3*DELTA) return Q_EMPT_T; /* empty */ + if (n <= 2+4*DELTA) return Q_FULL_T; /* full */ + if (n == 5*DELTA) return GLOBAL; + if (n == 6*DELTA) return TIMEOUT_F; + if (n == 7*DELTA) return ALPHA_F; + Uerror("cannot happen srinc_class"); + return BAD; +} +int +srunc(int n, int m) +{ switch(m) { + case Q_FULL_F: return n-2; + case Q_EMPT_F: return n-2-DELTA; + case Q_EMPT_T: return n-2-2*DELTA; + case Q_FULL_T: return n-2-3*DELTA; + case ALPHA_F: + case TIMEOUT_F: return 257; /* non-zero, and > MAXQ */ + } + Uerror("cannot happen srunc"); + return 0; +} +#endif +int cnt; +#ifdef HAS_UNLESS +int +isthere(Trans *a, int b) +{ Trans *t; + for (t = a; t; t = t->nxt) + if (t->t_id == b) + return 1; + return 0; +} +#endif +#ifndef NOREDUCE +int +mark_safety(Trans *t) /* for conditional safety */ +{ int g = 0, i, j, k; + + if (!t) return 0; + if (t->qu[0]) + return (t->qu[1])?2:1; /* marked */ + + for (i = 0; i < 2; i++) + { j = srinc_set(t->tpe[i]); + if (j >= GLOBAL && j != ALPHA_F) + return -1; + if (j != LOCAL) + { k = srunc(t->tpe[i], j); + if (g == 0 + || t->qu[0] != k + || t->ty[0] != j) + { t->qu[g] = k; + t->ty[g] = j; + g++; + } } } + return g; +} +#endif +void +retrans(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) + /* process n, with m states, is=initial state */ +{ Trans *T0, *T1, *T2, *T3; + int i, k; +#ifndef NOREDUCE + int g, h, j, aa; +#endif +#ifdef HAS_UNLESS + int p; +#endif + if (state_tables >= 4) + { printf("STEP 1 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } + do { + for (i = 1, cnt = 0; i < m; i++) + { T2 = trans[n][i]; + T1 = T2?T2->nxt:(Trans *)0; +/* prescan: */ for (T0 = T1; T0; T0 = T0->nxt) +/* choice in choice */ { if (T0->st && trans[n][T0->st] + && trans[n][T0->st]->nxt) + break; + } +#if 0 + if (T0) + printf("\tstate %d / %d: choice in choice\n", + i, T0->st); +#endif + if (T0) + for (T0 = T1; T0; T0 = T0->nxt) + { T3 = trans[n][T0->st]; + if (!T3->nxt) + { T2->nxt = cpytr(T0); + T2 = T2->nxt; + imed(T2, T0->st, n, i); + continue; + } + do { T3 = T3->nxt; + T2->nxt = cpytr(T3); + T2 = T2->nxt; + imed(T2, T0->st, n, i); + } while (T3->nxt); + cnt++; + } + } + } while (cnt); + if (state_tables >= 3) + { printf("STEP 2 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } + for (i = 1; i < m; i++) + { if (trans[n][i] && trans[n][i]->nxt) /* optimize */ + { T1 = trans[n][i]->nxt; +#if 0 + printf("\t\tpull %d (%d) to %d\n", + T1->st, T1->forw, i); +#endif + if (!trans[n][T1->st]) continue; + T0 = cpytr(trans[n][T1->st]); + trans[n][i] = T0; + reach[T1->st] = 1; + imed(T0, T1->st, n, i); + for (T1 = T1->nxt; T1; T1 = T1->nxt) + { +#if 0 + printf("\t\tpull %d (%d) to %d\n", + T1->st, T1->forw, i); +#endif + if (!trans[n][T1->st]) continue; + T0->nxt = cpytr(trans[n][T1->st]); + T0 = T0->nxt; + reach[T1->st] = 1; + imed(T0, T1->st, n, i); + } } } + if (state_tables >= 2) + { printf("STEP 3 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } +#ifdef HAS_UNLESS + for (i = 1; i < m; i++) + { if (!trans[n][i]) continue; + /* check for each state i if an + * escape to some state p is defined + * if so, copy and mark p's transitions + * and prepend them to the transition- + * list of state i + */ + if (!like_java) /* the default */ + { for (T0 = trans[n][i]; T0; T0 = T0->nxt) + for (k = HAS_UNLESS-1; k >= 0; k--) + { if (p = T0->escp[k]) + for (T1 = trans[n][p]; T1; T1 = T1->nxt) + { if (isthere(trans[n][i], T1->t_id)) + continue; + T2 = cpytr(T1); + T2->e_trans = p; + T2->nxt = trans[n][i]; + trans[n][i] = T2; + } } + } else /* outermost unless checked first */ + { Trans *T4; + T4 = T3 = (Trans *) 0; + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + for (k = HAS_UNLESS-1; k >= 0; k--) + { if (p = T0->escp[k]) + for (T1 = trans[n][p]; T1; T1 = T1->nxt) + { if (isthere(trans[n][i], T1->t_id)) + continue; + T2 = cpytr(T1); + T2->nxt = (Trans *) 0; + T2->e_trans = p; + if (T3) T3->nxt = T2; + else T4 = T2; + T3 = T2; + } } + if (T4) + { T3->nxt = trans[n][i]; + trans[n][i] = T4; + } + } + } +#endif +#ifndef NOREDUCE + for (i = 1; i < m; i++) + { if (a_cycles) + { /* moves through these states are visible */ + #if PROG_LAB>0 && defined(HAS_NP) + if (progstate[n][i]) + goto degrade; + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (progstate[n][T1->st]) + goto degrade; + #endif + if (accpstate[n][i] || visstate[n][i]) + goto degrade; + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (accpstate[n][T1->st]) + goto degrade; + } + T1 = trans[n][i]; + if (!T1) continue; + g = mark_safety(T1); /* V3.3.1 */ + if (g < 0) goto degrade; /* global */ + /* check if mixing of guards preserves reduction */ + if (T1->nxt) + { k = 0; + for (T0 = T1; T0; T0 = T0->nxt) + { if (!(T0->atom&8)) + goto degrade; + for (aa = 0; aa < 2; aa++) + { j = srinc_set(T0->tpe[aa]); + if (j >= GLOBAL && j != ALPHA_F) + goto degrade; + if (T0->tpe[aa] + && T0->tpe[aa] + != T1->tpe[0]) + k = 1; + } } + /* g = 0; V3.3.1 */ + if (k) /* non-uniform selection */ + for (T0 = T1; T0; T0 = T0->nxt) + for (aa = 0; aa < 2; aa++) + { j = srinc_set(T0->tpe[aa]); + if (j != LOCAL) + { k = srunc(T0->tpe[aa], j); + for (h = 0; h < 6; h++) + if (T1->qu[h] == k + && T1->ty[h] == j) + break; + if (h >= 6) + { T1->qu[g%6] = k; + T1->ty[g%6] = j; + g++; + } } } + if (g > 6) + { T1->qu[0] = 0; /* turn it off */ + printf("pan: warning, line %d, ", + srcln[i]); + printf("too many stmnt types (%d)", + g); + printf(" in selection\n"); + goto degrade; + } + } + /* mark all options global if >=1 is global */ + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (!(T1->atom&8)) break; + if (T1) +degrade: for (T1 = trans[n][i]; T1; T1 = T1->nxt) + T1->atom &= ~8; /* mark as unsafe */ + /* can only mix 'r's or 's's if on same chan */ + /* and not mixed with other local operations */ + T1 = trans[n][i]; + if (!T1 || T1->qu[0]) continue; + j = T1->tpe[0]; + if (T1->nxt && T1->atom&8) + { if (j == 5*DELTA) + { printf("warning: line %d ", srcln[i]); + printf("mixed condition "); + printf("(defeats reduction)\n"); + goto degrade; + } + for (T0 = T1; T0; T0 = T0->nxt) + for (aa = 0; aa < 2; aa++) + if (T0->tpe[aa] && T0->tpe[aa] != j) + { printf("warning: line %d ", srcln[i]); + printf("[%d-%d] mixed %stion ", + T0->tpe[aa], j, + (j==5*DELTA)?"condi":"selec"); + printf("(defeats reduction)\n"); + printf(" '%s' <-> '%s'\n", + T1->tp, T0->tp); + goto degrade; + } } + } +#endif + for (i = 1; i < m; i++) + { T2 = trans[n][i]; + if (!T2 + || T2->nxt + || strncmp(T2->tp, ".(goto)", 7) + || !stopstate[n][i]) + continue; + stopstate[n][T2->st] = 1; + } + if (state_tables) + { printf("proctype "); + if (!strcmp(procname[n], ":init:")) + printf("init\n"); + else + printf("%s\n", procname[n]); + for (i = 1; i < m; i++) + reach[i] = 1; + tagtable(n, m, is, srcln, reach); + } else + for (i = 1; i < m; i++) + { int nrelse; + if (strcmp(procname[n], ":never:") != 0) + { for (T0 = trans[n][i]; T0; T0 = T0->nxt) + { if (T0->st == i + && strcmp(T0->tp, "(1)") == 0) + { printf("error: proctype '%s' ", + procname[n]); + printf("line %d, state %d: has un", + srcln[i], i); + printf("conditional self-loop\n"); + pan_exit(1); + } } } + nrelse = 0; + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + { if (strcmp(T0->tp, "else") == 0) + nrelse++; + } + if (nrelse > 1) + { printf("error: proctype '%s' state", + procname[n]); + printf(" %d, inherits %d", i, nrelse); + printf(" 'else' stmnts\n"); + pan_exit(1); + } } + if (!state_tables && strcmp(procname[n], ":never:") == 0) + { int h = 0; + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + if (T0->forw > h) h = T0->forw; + h++; + frm_st0 = (short *) emalloc(h * sizeof(short)); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + frm_st0[T0->forw] = i; + } +#ifndef LOOPSTATE + if (state_tables) +#endif + do_dfs(n, m, is, srcln, reach, lstate); +#ifdef T_REVERSE + /* process n, with m states, is=initial state -- reverse list */ + if (!state_tables && strcmp(procname[n], ":never:") != 0) + { for (i = 1; i < m; i++) + { Trans *T4 = (Trans *) 0; + T1 = (Trans *) 0; + T2 = (Trans *) 0; + T3 = (Trans *) 0; + for (T0 = trans[n][i]; T0; T0 = T4) + { T4 = T0->nxt; + if (strcmp(T0->tp, "else") == 0) + { T3 = T0; + T0->nxt = (Trans *) 0; + } else + { T0->nxt = T1; + if (!T1) { T2 = T0; } + T1 = T0; + } } + if (T2 && T3) { T2->nxt = T3; } + trans[n][i] = T1; /* reversed -- else at end */ + } } +#endif +} +void +imed(Trans *T, int v, int n, int j) /* set intermediate state */ +{ progstate[n][T->st] |= progstate[n][v]; + accpstate[n][T->st] |= accpstate[n][v]; + stopstate[n][T->st] |= stopstate[n][v]; + mapstate[n][j] = T->st; +} +void +tagtable(int n, int m, int is, short srcln[], uchar reach[]) +{ Trans *z; + + if (is >= m || !trans[n][is] + || is <= 0 || reach[is] == 0) + return; + reach[is] = 0; + if (state_tables) + for (z = trans[n][is]; z; z = z->nxt) + crack(n, is, z, srcln); + for (z = trans[n][is]; z; z = z->nxt) + { +#ifdef HAS_UNLESS + int i, j; +#endif + tagtable(n, m, z->st, srcln, reach); +#ifdef HAS_UNLESS + for (i = 0; i < HAS_UNLESS; i++) + { j = trans[n][is]->escp[i]; + if (!j) break; + tagtable(n, m, j, srcln, reach); + } +#endif + } +} +void +dfs_table(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) +{ Trans *z; + + if (is >= m || is <= 0 || !trans[n][is]) + return; + if ((reach[is] & (4|8|16)) != 0) + { if ((reach[is] & (8|16)) == 16) /* on stack, not yet recorded */ + { lstate[is] = 1; + reach[is] |= 8; /* recorded */ + if (state_tables) + { printf("state %d line %d is a loopstate\n", is, srcln[is]); + } } + return; + } + reach[is] |= (4|16); /* visited | onstack */ + for (z = trans[n][is]; z; z = z->nxt) + { +#ifdef HAS_UNLESS + int i, j; +#endif + dfs_table(n, m, z->st, srcln, reach, lstate); +#ifdef HAS_UNLESS + for (i = 0; i < HAS_UNLESS; i++) + { j = trans[n][is]->escp[i]; + if (!j) break; + dfs_table(n, m, j, srcln, reach, lstate); + } +#endif + } + reach[is] &= ~16; /* no longer on stack */ +} +void +do_dfs(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) +{ int i; + dfs_table(n, m, is, srcln, reach, lstate); + for (i = 0; i < m; i++) + reach[i] &= ~(4|8|16); +} +void +crack(int n, int j, Trans *z, short srcln[]) +{ int i; + + if (!z) return; + printf(" state %3d -(tr %3d)-> state %3d ", + j, z->forw, z->st); + printf("[id %3d tp %3d", z->t_id, z->tpe[0]); + if (z->tpe[1]) printf(",%d", z->tpe[1]); +#ifdef HAS_UNLESS + if (z->e_trans) + printf(" org %3d", z->e_trans); + else if (state_tables >= 2) + for (i = 0; i < HAS_UNLESS; i++) + { if (!z->escp[i]) break; + printf(" esc %d", z->escp[i]); + } +#endif + printf("]"); + printf(" [%s%s%s%s%s] line %d => ", + z->atom&6?"A":z->atom&32?"D":"-", + accpstate[n][j]?"a" :"-", + stopstate[n][j]?"e" : "-", + progstate[n][j]?"p" : "-", + z->atom & 8 ?"L":"G", + srcln[j]); + for (i = 0; z->tp[i]; i++) + if (z->tp[i] == '\n') + printf("\\n"); + else + putchar(z->tp[i]); + if (z->qu[0]) + { printf("\t["); + for (i = 0; i < 6; i++) + if (z->qu[i]) + printf("(%d,%d)", + z->qu[i], z->ty[i]); + printf("]"); + } + printf("\n"); + fflush(stdout); +} + +#ifdef VAR_RANGES +#define BYTESIZE 32 /* 2^8 : 2^3 = 256:8 = 32 */ + +typedef struct Vr_Ptr { + char *nm; + uchar vals[BYTESIZE]; + struct Vr_Ptr *nxt; +} Vr_Ptr; +Vr_Ptr *ranges = (Vr_Ptr *) 0; + +void +logval(char *s, int v) +{ Vr_Ptr *tmp; + + if (v<0 || v > 255) return; + for (tmp = ranges; tmp; tmp = tmp->nxt) + if (!strcmp(tmp->nm, s)) + goto found; + tmp = (Vr_Ptr *) emalloc(sizeof(Vr_Ptr)); + tmp->nxt = ranges; + ranges = tmp; + tmp->nm = s; +found: + tmp->vals[(v)/8] |= 1<<((v)%8); +} + +void +dumpval(uchar X[], int range) +{ int w, x, i, j = -1; + + for (w = i = 0; w < range; w++) + for (x = 0; x < 8; x++, i++) + { +from: if ((X[w] & (1<= 0 && j != 255) + printf("-255"); +} + +void +dumpranges(void) +{ Vr_Ptr *tmp; + printf("\nValues assigned within "); + printf("interval [0..255]:\n"); + for (tmp = ranges; tmp; tmp = tmp->nxt) + { printf("\t%s\t: ", tmp->nm); + dumpval(tmp->vals, BYTESIZE); + printf("\n"); + } +} +#endif diff --git a/trunk/verif/md/pan.tmp b/trunk/verif/md/pan.tmp new file mode 100644 index 00000000..e69de29b diff --git a/trunk/verif/md/pan_in b/trunk/verif/md/pan_in new file mode 100644 index 00000000..f079cb17 --- /dev/null +++ b/trunk/verif/md/pan_in @@ -0,0 +1,307 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* */ +byte _commit_sum; +/* */ + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + /* */ + _commit_sum = 0; + /* */ + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + _commit_sum = 0; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + //assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - _commit_sum >= 0 && write_off - _commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + + diff --git a/trunk/verif/md/promela-mode.el b/trunk/verif/md/promela-mode.el new file mode 100755 index 00000000..10cac3fe --- /dev/null +++ b/trunk/verif/md/promela-mode.el @@ -0,0 +1,985 @@ +;; promela-mode.el --- major mode for editing PROMELA program files +;; $Revision: 1.11 $ $Date: 2001/07/09 18:36:45 $ $Author: engstrom $ + +;; Author: Eric Engstrom +;; Maintainer: Eric Engstrom +;; Keywords: spin, promela, tools + +;; Copyright (C) 1998-2003 Eric Engstrom / Honeywell Laboratories + +;; ... Possibly insert GPL here someday ... + +;;; Commentary: + +;; This file contains code for a GNU Emacs major mode for editing +;; PROMELA (SPIN) program files. + +;; Type "C-h m" in Emacs (while in a buffer in promela-mode) for +;; information on how to configure indentation and fontification, +;; or look at the configuration variables below. + +;; To use, place promela-mode.el in a directory in your load-path. +;; Then, put the following lines into your .emacs and promela-mode +;; will be automatically loaded when editing a PROMELA program. + +;; (autoload 'promela-mode "promela-mode" "PROMELA mode" nil t) +;; (setq auto-mode-alist +;; (append +;; (list (cons "\\.promela$" 'promela-mode) +;; (cons "\\.spin$" 'promela-mode) +;; (cons "\\.pml$" 'promela-mode) +;; ;; (cons "\\.other-extensions$" 'promela-mode) +;; ) +;; auto-mode-alist)) + +;; If you wish for promela-mode to be used for files with other +;; extensions you add your own patterned after the code above. + +;; Note that promela-mode adhears to the font-lock "standards" and +;; defines several "levels" of fontification or colorization. The +;; default is fairly gaudy, so I can imagine that some folks would +;; like a bit less. FMI: see `font-lock-maximum-decoration' + +;; This mode is known to work under the following versions of emacs: +;; - XEmacs: 19.16, 20.x, 21.x +;; - FSF/GNU Emacs: 19.34 +;; - NTEmacs (FSF): 20.[67] +;; That is not to say there are no bugs specific to one of those versions :-) + +;; Please send any comments, bugs, patches or other requests to +;; Eric Engstrom at engstrom@htc.honeywell.com + +;; To-Do: +;; - compile/syntax-check/verify? (suggested by R.Goldman) +;; - indentation - splitting lines at logical operators (M. Rangarajan) +;; [ This might "devolve" to indentation after "->" or ";" +;; being as is, but anything else indent even more? ] +;; :: SomeReallyLongArrayRef[this].typedefField != SomeReallyLongConstant -> /* some-comment */ +;; [ Suggestion would be to break the first line after the !=, therefore: ] +;; :: SomeReallyLongArrayRef[this].typedefField +;; != SomeReallyLongConstant -> /* some-comment */ +;; [ at this point I'm not so sure about this change... EE: 2001/05/19 ] + +;;; ------------------------------------------------------------------------- +;;; Code: + +;; NOTE: same as CVS revision: +(defconst promela-mode-version "$Revision: 1.11 $" + "Promela-mode version number.") + +;; ------------------------------------------------------------------------- +;; The following constant values can be modified by the user in a .emacs file + +(defconst promela-block-indent 2 + "*Controls indentation of lines within a block (`{') construct") + +(defconst promela-selection-indent 2 + "*Controls indentation of options within a selection (`if') +or iteration (`do') construct") + +(defconst promela-selection-option-indent 3 + "*Controls indentation of lines after options within selection or +iteration construct (`::')") + +(defconst promela-comment-col 32 + "*Defines the desired comment column for comments to the right of text.") + +(defconst promela-tab-always-indent t + "*Non-nil means TAB in Promela mode should always reindent the current line, +regardless of where in the line point is when the TAB command is used.") + +(defconst promela-auto-match-delimiter t + "*Non-nil means typing an open-delimiter (i.e. parentheses, brace, quote, etc) +should also insert the matching closing delmiter character.") + +;; That should be about it for most users... +;; unless you wanna hack elisp, the rest of this is probably uninteresting + + +;; ------------------------------------------------------------------------- +;; help determine what emacs we have here... + +(defconst promela-xemacsp (string-match "XEmacs" (emacs-version)) + "Non-nil if we are running in the XEmacs environment.") + +;;;(defconst promela-xemacs20p (and promela-xemacsp (>= emacs-major-version 20)) +;; "Non-nil if we are running in an XEmacs environment version 20 or greater.") + +;; ------------------------------------------------------------------------- +;; promela-mode font faces/definitions + +;; make use of font-lock stuff, so say that explicitly +(require 'font-lock) + +;; BLECH! YUCK! I just wish these guys could agree to something.... +;; Faces available in: ntemacs emacs xemacs xemacs xemacs +;; font-lock- xxx -face 20.6 19.34 19.16 20.x 21.x +;; -builtin- X +;; -constant- X +;; -comment- X X X X X +;; -doc-string- X X X +;; -function-name- X X X X X +;; -keyword- X X X X X +;; -preprocessor- X X X +;; -reference- X X X X +;; -signal-name- X X!20.0 +;; -string- X X X X X +;; -type- X X X X X +;; -variable-name- X X X X X +;; -warning- X X + +;;; Compatibility on faces between versions of emacs-en +(unless promela-xemacsp + + (defvar font-lock-preprocessor-face 'font-lock-preprocessor-face + "Face name to use for preprocessor statements.") + ;; For consistency try to define the preprocessor face == builtin face + (condition-case nil + (copy-face 'font-lock-builtin-face 'font-lock-preprocessor-face) + (error + (defface font-lock-preprocessor-face + '((t (:foreground "blue" :italic nil :underline t))) + "Face Lock mode face used to highlight preprocessor statements." + :group 'font-lock-highlighting-faces))) + + (defvar font-lock-reference-face 'font-lock-reference-face + "Face name to use for constants and reference and label names.") + ;; For consistency try to define the reference face == constant face + (condition-case nil + (copy-face 'font-lock-constant-face 'font-lock-reference-face) + (error + (defface font-lock-reference-face + '((((class grayscale) (background light)) + (:foreground "LightGray" :bold t :underline t)) + (((class grayscale) (background dark)) + (:foreground "Gray50" :bold t :underline t)) + (((class color) (background light)) (:foreground "CadetBlue")) + (((class color) (background dark)) (:foreground "Aquamarine")) + (t (:bold t :underline t))) + "Font Lock mode face used to highlight constancs, references and labels." + :group 'font-lock-highlighting-faces))) + + ) + +;; send-poll "symbol" face is custom to promela-mode +;; but check for existence to allow someone to override it +(defvar promela-fl-send-poll-face 'promela-fl-send-poll-face + "Face name to use for Promela Send or Poll symbols: `!' or `?'") +(copy-face (if promela-xemacsp 'modeline 'region) + 'promela-fl-send-poll-face) + +;; some emacs-en don't define or have regexp-opt available. +(unless (functionp 'regexp-opt) + (defmacro regexp-opt (strings) + "Cheap imitation of `regexp-opt' since it's not availble in this emacs" + `(mapconcat 'identity ,strings "\\|"))) + + +;; ------------------------------------------------------------------------- +;; promela-mode font lock specifications/regular-expressions +;; - for help, look at definition of variable 'font-lock-keywords +;; - some fontification ideas from -- [engstrom:20010309.1435CST] +;; Pat Tullman (tullmann@cs.utah.edu) and +;; Ny Aina Razermera Mamy (ainarazr@cs.uoregon.edu) +;; both had promela-mode's that I discovered after starting this one... +;; (but neither did any sort of indentation ;-) + +(defconst promela-font-lock-keywords-1 nil + "Subdued level highlighting for Promela mode.") + +(defconst promela-font-lock-keywords-2 nil + "Medium level highlighting for Promela mode.") + +(defconst promela-font-lock-keywords-3 nil + "Gaudy level highlighting for Promela mode.") + +;; set each of those three variables now.. +(let ((promela-keywords + (eval-when-compile + (regexp-opt + '("active" "assert" "atomic" "break" "d_step" + "do" "dproctype" "else" "empty" "enabled" + "eval" "fi" "full" "goto" "hidden" "if" "init" + "inline" "len" "local" "mtype" "nempty" "never" + "nfull" "od" "of" "pcvalue" "printf" "priority" + "proctype" "provided" "run" "show" "skip" + "timeout" "trace" "typedef" "unless" "xr" "xs")))) + (promela-types + (eval-when-compile + (regexp-opt '("bit" "bool" "byte" "short" + "int" "unsigned" "chan"))))) + + ;; really simple fontification (strings and comments come for "free") + (setq promela-font-lock-keywords-1 + (list + ;; Keywords: + (cons (concat "\\<\\(" promela-keywords "\\)\\>") + 'font-lock-keyword-face) + ;; Types: + (cons (concat "\\<\\(" promela-types "\\)\\>") + 'font-lock-type-face) + ;; Special constants: + '("\\<_\\(np\\|pid\\|last\\)\\>" . font-lock-reference-face))) + + ;; more complex fontification + ;; add function (proctype) names, lables and goto statements + ;; also add send/receive/poll fontification + (setq promela-font-lock-keywords-2 + (append promela-font-lock-keywords-1 + (list + ;; ANY Pre-Processor directive (lazy method: any line beginning with "#[a-z]+") + '("^\\(#[ \t]*[a-z]+\\)" 1 'font-lock-preprocessor-face t) + + ;; "Functions" (proctype-s and inline-s) + (list (concat "\\<\\(" + (eval-when-compile + (regexp-opt '("run" "dproctype" "proctype" "inline"))) + "\\)\\>[ \t]*\\(\\sw+\\)?") + ;;'(1 'font-lock-keyword-face nil) + '(2 'font-lock-function-name-face nil t)) + + ;; Labels and GOTO labels + '("^\\(\\sw+\\):" 1 'font-lock-reference-face) + '("\\<\\(goto\\)\\>[ \t]+\\(\\sw+\\)" + ;;(1 'font-lock-keyword-face nil) + (2 'font-lock-reference-face nil t)) + + ;; Send, Receive and Poll + '("\\(\\sw+\\)\\(\\[[^\\?!]+\\]\\)?\\(\\??\\?\\|!?!\\)\\(\\sw+\\)" + (1 'font-lock-variable-name-face nil t) + (3 'promela-fl-send-poll-face nil t) + (4 'font-lock-reference-face nil t) + ) + ))) + + ;; most complex fontification + ;; add pre-processor directives, typed variables and hidden/typedef decls. + (setq promela-font-lock-keywords-3 + (append promela-font-lock-keywords-2 + (list + ;; ANY Pre-Processor directive (lazy method: any line beginning with "#[a-z]+") + ;;'("^\\(#[ \t]*[a-z]+\\)" 1 'font-lock-preprocessor-face t) + ;; "defined" in an #if or #elif and associated macro names + '("^#[ \t]*\\(el\\)?if\\>" + ("\\<\\(defined\\)\\>[ \t]*(?\\(\\sw+\\)" nil nil + (1 'font-lock-preprocessor-face nil t) + (2 'font-lock-reference-face nil t))) + '("^#[ \t]*ifn?def\\>" + ("[ \t]*\\(\\sw+\\)" nil nil + (1 'font-lock-reference-face nil t))) + ;; Filenames in #include <...> directives + '("^#[ \t]*include[ \t]+<\\([^>\"\n]+\\)>" 1 'font-lock-string-face nil t) + ;; Defined functions and constants/types (non-functions) + '("^#[ \t]*define[ \t]+" + ("\\(\\sw+\\)(" nil nil (1 'font-lock-function-name-face nil t)) + ("\\(\\sw+\\)[ \t]+\\(\\sw+\\)" nil nil (1 'font-lock-variable-name-face) + (2 'font-lock-reference-face nil t)) + ("\\(\\sw+\\)[^(]?" nil nil (1 'font-lock-reference-face nil t))) + + ;; Types AND variables + ;; - room for improvement: (i.e. don't currently): + ;; highlight user-defined types and asociated variable declarations + (list (concat "\\<\\(" promela-types "\\)\\>") + ;;'(1 'font-lock-type-face) + ;; now match the variables after the type definition, if any + '(promela-match-variable-or-declaration + nil nil + (1 'font-lock-variable-name-face) ;; nil t) + (2 font-lock-reference-face nil t))) + + ;; Typedef/hidden types and declarations + '("\\<\\(typedef\\|hidden\\)\\>[ \t]*\\(\\sw+\\)?" + ;;(1 'font-lock-keyword-face nil) + (2 'font-lock-type-face nil t) + ;; now match the variables after the type definition, if any + (promela-match-variable-or-declaration + nil nil + (1 'font-lock-variable-name-face nil t) + (2 'font-lock-reference-face nil t))) + ))) + ) + +(defvar promela-font-lock-keywords promela-font-lock-keywords-1 + "Default expressions to highlight in Promela mode.") + +;; Font-lock matcher functions: +(defun promela-match-variable-or-declaration (limit) + "Match, and move over, any declaration/definition item after point. +Matches after point, but ignores leading whitespace characters. +Does not move further than LIMIT. + +The expected syntax of a declaration/definition item is `word' (preceded +by optional whitespace) optionally followed by a `= value' (preceded and +followed by more optional whitespace) + +Thus the regexp matches after point: word [ = value ] + ^^^^ ^^^^^ +Where the match subexpressions are: 1 2 + +The item is delimited by (match-beginning 1) and (match-end 1). +If (match-beginning 2) is non-nil, the item is followed by a `value'." + (when (looking-at "[ \t]*\\(\\sw+\\)[ \t]*=?[ \t]*\\(\\sw+\\)?[ \t]*,?") + (goto-char (min limit (match-end 0))))) + + +;; ------------------------------------------------------------------------- +;; "install" promela-mode font lock specifications + +;; FMI: look up 'font-lock-defaults +(defconst promela-font-lock-defaults + '( + (promela-font-lock-keywords + promela-font-lock-keywords-1 + promela-font-lock-keywords-2 + promela-font-lock-keywords-3) ;; font-lock stuff (keywords) + nil ;; keywords-only flag + nil ;; case-fold keyword searching + ;;((?_ . "w") (?$ . ".")) ;; mods to syntax table + nil ;; mods to syntax table (see below) + nil ;; syntax-begin + (font-lock-mark-block-function . mark-defun)) +) + +;; "install" the font-lock-defaults based upon version of emacs we have +(cond (promela-xemacsp + (put 'promela-mode 'font-lock-defaults promela-font-lock-defaults)) + ((not (assq 'promela-mode font-lock-defaults-alist)) + (setq font-lock-defaults-alist + (cons + (cons 'promela-mode promela-font-lock-defaults) + font-lock-defaults-alist)))) + + +;; ------------------------------------------------------------------------- +;; other promela-mode specific definitions + +(defconst promela-defun-prompt-regexp + "^[ \t]*\\(d?proctype\\|init\\|inline\\|never\\|trace\\|typedef\\|mtype\\s-+=\\)[^{]*" + "Regexp describing the beginning of a Promela top-level definition.") + +(defvar promela-mode-syntax-table nil + "Syntax table in use in PROMELA-mode buffers.") +(if promela-mode-syntax-table + () + (setq promela-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\\ "\\" promela-mode-syntax-table) + (modify-syntax-entry ?/ ". 14" promela-mode-syntax-table) + (modify-syntax-entry ?* ". 23" promela-mode-syntax-table) + (modify-syntax-entry ?+ "." promela-mode-syntax-table) + (modify-syntax-entry ?- "." promela-mode-syntax-table) + (modify-syntax-entry ?= "." promela-mode-syntax-table) + (modify-syntax-entry ?% "." promela-mode-syntax-table) + (modify-syntax-entry ?< "." promela-mode-syntax-table) + (modify-syntax-entry ?> "." promela-mode-syntax-table) + (modify-syntax-entry ?& "." promela-mode-syntax-table) + (modify-syntax-entry ?| "." promela-mode-syntax-table) + (modify-syntax-entry ?. "_" promela-mode-syntax-table) + (modify-syntax-entry ?_ "w" promela-mode-syntax-table) + (modify-syntax-entry ?\' "\"" promela-mode-syntax-table) + ) + +(defvar promela-mode-abbrev-table nil + "*Abbrev table in use in promela-mode buffers.") +(if promela-mode-abbrev-table + nil + (define-abbrev-table 'promela-mode-abbrev-table + '( +;; Commented out for now - need to think about what abbrevs make sense +;; ("assert" "ASSERT" promela-check-expansion 0) +;; ("d_step" "D_STEP" promela-check-expansion 0) +;; ("break" "BREAK" promela-check-expansion 0) +;; ("do" "DO" promela-check-expansion 0) +;; ("proctype" "PROCTYPE" promela-check-expansion 0) + ))) + +(defvar promela-mode-map nil + "Keymap for promela-mode.") +(if promela-mode-map + nil + (setq promela-mode-map (make-sparse-keymap)) + (define-key promela-mode-map "\t" 'promela-indent-command) + (define-key promela-mode-map "\C-m" 'promela-newline-and-indent) + ;(define-key promela-mode-map 'backspace 'backward-delete-char-untabify) + (define-key promela-mode-map "\C-c\C-p" 'promela-beginning-of-block) + ;(define-key promela-mode-map "\C-c\C-n" 'promela-end-of-block) + (define-key promela-mode-map "\M-\C-a" 'promela-beginning-of-defun) + ;(define-key promela-mode-map "\M-\C-e" 'promela-end-of-defun) + (define-key promela-mode-map "\C-c(" 'promela-toggle-auto-match-delimiter) + (define-key promela-mode-map "{" 'promela-open-delimiter) + (define-key promela-mode-map "}" 'promela-close-delimiter) + (define-key promela-mode-map "(" 'promela-open-delimiter) + (define-key promela-mode-map ")" 'promela-close-delimiter) + (define-key promela-mode-map "[" 'promela-open-delimiter) + (define-key promela-mode-map "]" 'promela-close-delimiter) + (define-key promela-mode-map ";" 'promela-insert-and-indent) + (define-key promela-mode-map ":" 'promela-insert-and-indent) + ;; + ;; this is preliminary at best - use at your own risk: + (define-key promela-mode-map "\C-c\C-s" 'promela-syntax-check) + ;; + ;;(define-key promela-mode-map "\C-c\C-d" 'promela-mode-toggle-debug) + ;;(define-key promela-mode-map "\C-c\C-r" 'promela-mode-revert-buffer) + ) + +(defvar promela-matching-delimiter-alist + '( (?( . ?)) + (?[ . ?]) + (?{ . "\n}") + ;(?< . ?>) + (?\' . ?\') + (?\` . ?\`) + (?\" . ?\") ) + "List of pairs of matching open/close delimiters - for auto-insert") + + +;; ------------------------------------------------------------------------- +;; Promela-mode itself + +(defun promela-mode () + "Major mode for editing PROMELA code. +\\{promela-mode-map} + +Variables controlling indentation style: + promela-block-indent + Relative offset of lines within a block (`{') construct. + + promela-selection-indent + Relative offset of option lines within a selection (`if') + or iteration (`do') construct. + + promela-selection-option-indent + Relative offset of lines after/within options (`::') within + selection or iteration constructs. + + promela-comment-col + Defines the desired comment column for comments to the right of text. + + promela-tab-always-indent + Non-nil means TAB in PROMELA mode should always reindent the current + line, regardless of where in the line the point is when the TAB + command is used. + + promela-auto-match-delimiter + Non-nil means typing an open-delimiter (i.e. parentheses, brace, + quote, etc) should also insert the matching closing delmiter + character. + +Turning on PROMELA mode calls the value of the variable promela-mode-hook with +no args, if that value is non-nil. + +For example: ' + (setq promela-mode-hook '(lambda () + (setq promela-block-indent 2) + (setq promela-selection-indent 0) + (setq promela-selection-option-indent 2) + (local-set-key \"\\C-m\" 'promela-indent-newline-indent) + ))' + +will indent block two steps, will make selection options aligned with DO/IF +and sub-option lines indent to a column after the `::'. Also, lines will +be reindented when you hit RETURN. + +Note that promela-mode adhears to the font-lock \"standards\" and +defines several \"levels\" of fontification or colorization. The +default is fairly gaudy, so if you would prefer a bit less, please see +the documentation for the variable: `font-lock-maximum-decoration'. +" + (interactive) + (kill-all-local-variables) + (setq mode-name "Promela") + (setq major-mode 'promela-mode) + (use-local-map promela-mode-map) + (set-syntax-table promela-mode-syntax-table) + (setq local-abbrev-table promela-mode-abbrev-table) + + ;; Make local variables + (make-local-variable 'case-fold-search) + (make-local-variable 'paragraph-start) + (make-local-variable 'paragraph-separate) + (make-local-variable 'paragraph-ignore-fill-prefix) + (make-local-variable 'indent-line-function) + (make-local-variable 'indent-region-function) + (make-local-variable 'parse-sexp-ignore-comments) + (make-local-variable 'comment-start) + (make-local-variable 'comment-end) + (make-local-variable 'comment-column) + (make-local-variable 'comment-start-skip) + (make-local-variable 'comment-indent-hook) + (make-local-variable 'defun-prompt-regexp) + (make-local-variable 'compile-command) + ;; Now set their values + (setq case-fold-search t + paragraph-start (concat "^$\\|" page-delimiter) + paragraph-separate paragraph-start + paragraph-ignore-fill-prefix t + indent-line-function 'promela-indent-command + ;;indent-region-function 'promela-indent-region + parse-sexp-ignore-comments t + comment-start "/* " + comment-end " */" + comment-column 32 + comment-start-skip "/\\*+ *" + ;;comment-start-skip "/\\*+ *\\|// *" + ;;comment-indent-hook 'promela-comment-indent + defun-prompt-regexp promela-defun-prompt-regexp + ) + + ;; Turn on font-lock mode + ;; (and promela-font-lock-mode (font-lock-mode)) + (font-lock-mode) + + ;; Finally, run the hooks and be done. + (run-hooks 'promela-mode-hook)) + + +;; ------------------------------------------------------------------------- +;; Interactive functions +;; + +(defun promela-mode-version () + "Print the current version of promela-mode in the minibuffer" + (interactive) + (message (concat "Promela-Mode: " promela-mode-version))) + +(defun promela-beginning-of-block () + "Move backward to start of containing block. +Containing block may be `{', `do' or `if' construct, or comment." + (interactive) + (goto-char (promela-find-start-of-containing-block-or-comment))) + +(defun promela-beginning-of-defun (&optional arg) + "Move backward to the beginning of a defun. +With argument, do it that many times. +Negative arg -N means move forward to Nth following beginning of defun. +Returns t unless search stops due to beginning or end of buffer. + +See also 'beginning-of-defun. + +This is a Promela-mode specific version since default (in xemacs 19.16 and +NT-Emacs 20) don't seem to skip comments - they will stop inside them. + +Also, this makes sure that the beginning of the defun is actually the +line which starts the proctype/init/etc., not just the open-brace." + (interactive "p") + (beginning-of-defun arg) + (if (not (looking-at promela-defun-prompt-regexp)) + (re-search-backward promela-defun-prompt-regexp nil t)) + (if (promela-inside-comment-p) + (goto-char (promela-find-start-of-containing-comment)))) + +(defun promela-indent-command () + "Indent the current line as PROMELA code." + (interactive) + (if (and (not promela-tab-always-indent) + (save-excursion + (skip-chars-backward " \t") + (not (bolp)))) + (tab-to-tab-stop) + (promela-indent-line))) + +(defun promela-newline-and-indent () + "Promela-mode specific newline-and-indent which expands abbrevs before +running a regular newline-and-indent." + (interactive) + (if abbrev-mode + (expand-abbrev)) + (newline-and-indent)) + +(defun promela-indent-newline-indent () + "Promela-mode specific newline-and-indent which expands abbrevs and +indents the current line before running a regular newline-and-indent." + (interactive) + (save-excursion (promela-indent-command)) + (if abbrev-mode + (expand-abbrev)) + (newline-and-indent)) + +(defun promela-insert-and-indent () + "Insert the last character typed and re-indent the current line" + (interactive) + (insert last-command-char) + (save-excursion (promela-indent-command))) + +(defun promela-open-delimiter () + "Inserts the open and matching close delimiters, indenting as appropriate." + (interactive) + (insert last-command-char) + (if (and promela-auto-match-delimiter (not (promela-inside-comment-p))) + (save-excursion + (insert (cdr (assq last-command-char promela-matching-delimiter-alist))) + (promela-indent-command)))) + +(defun promela-close-delimiter () + "Inserts and indents a close delimiter." + (interactive) + (insert last-command-char) + (if (not (promela-inside-comment-p)) + (save-excursion (promela-indent-command)))) + +(defun promela-toggle-auto-match-delimiter () + "Toggle auto-insertion of parens and other delimiters. +See variable `promela-auto-insert-matching-delimiter'" + (interactive) + (setq promela-auto-match-delimiter + (not promela-auto-match-delimiter)) + (message (concat "Promela auto-insert matching delimiters " + (if promela-auto-match-delimiter + "enabled" "disabled")))) + + +;; ------------------------------------------------------------------------- +;; Compilation/Verification functions + +;; all of this is in serious "beta" mode - don't trust it ;-) +(setq + promela-compile-command "spin " + promela-syntax-check-args "-a -v " +) + +;;(setq compilation-error-regexp-alist +;; (append compilation-error-regexp-alist +;; '(("spin: +line +\\([0-9]+\\) +\"\\([^\"]+\\)\"" 2 1)))) + +(defun promela-syntax-check () + (interactive) + (compile (concat promela-compile-command + promela-syntax-check-args + (buffer-name)))) + + +;; ------------------------------------------------------------------------- +;; Indentation support functions + +(defun promela-indent-around-label () + "Indent the current line as PROMELA code, +but make sure to consider the label at the beginning of the line." + (beginning-of-line) + (delete-horizontal-space) ; delete any leading whitespace + (if (not (looking-at "\\sw+:\\([ \t]*\\)")) + (error "promela-indent-around-label: no label on this line") + (goto-char (match-beginning 1)) + (let* ((space (length (match-string 1))) + (indent (promela-calc-indent)) + (wanted (max 0 (- indent (current-column))))) + (if (>= space wanted) + (delete-region (point) (+ (point) (- space wanted))) + (goto-char (+ (point) space)) + (indent-to-column indent))))) + +;; Note that indentation is based ENTIRELY upon the indentation of the +;; previous line(s), esp. the previous non-blank line and the line +;; starting the current containgng block... +(defun promela-indent-line () + "Indent the current line as PROMELA code. +Return the amount the by which the indentation changed." + (beginning-of-line) + (if (looking-at "[ \t]*\\sw+:") + (promela-indent-around-label) + (let ((indent (promela-calc-indent)) + beg + shift-amt + (pos (- (point-max) (point)))) + (setq beg (point)) + (skip-chars-forward " \t") + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + (delete-region beg (point)) + (indent-to indent) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))) + shift-amt))) + +(defun promela-calc-indent () + "Return the appropriate indentation for this line as an int." + (save-excursion + (beginning-of-line) + (let* ((orig-point (point)) + (state (promela-parse-partial-sexp)) + (paren-depth (nth 0 state)) + (paren-point (or (nth 1 state) 1)) + (paren-char (char-after paren-point))) + ;;(what-cursor-position) + (cond + ;; Indent not-at-all - inside a string + ((nth 3 state) + (current-indentation)) + ;; Indent inside a comment + ((nth 4 state) + (promela-calc-indent-within-comment)) + ;; looking at a pre-processor directive - indent=0 + ((looking-at "[ \t]*#\\(define\\|if\\(n?def\\)?\\|else\\|endif\\)") + 0) + ;; If we're not inside a "true" block (i.e. "{}"), then indent=0 + ;; I think this is fair, since no (indentable) code in promela + ;; exists outside of a proctype or similar "{ .. }" structure. + ((zerop paren-depth) + 0) + ;; Indent relative to non curly-brace "paren" + ;; [ NOTE: I'm saving this, but don't use it any more. + ;; Now, we let parens be indented like curly braces + ;;((and (>= paren-depth 1) (not (char-equal ?\{ paren-char))) + ;; (goto-char paren-point) + ;; (1+ (current-column))) + ;; + ;; Last option: indent relative to contaning block(s) + (t + (goto-char orig-point) + (promela-calc-indent-within-block paren-point)))))) + +(defun promela-calc-indent-within-block (&optional limit) + "Return the appropriate indentation for this line, assume within block. +with optional arg, limit search back to `limit'" + (save-excursion + (let* ((stop (or limit 1)) + (block-point (promela-find-start-of-containing-block stop)) + (block-type (promela-block-type-after block-point)) + (indent-point (point)) + (indent-type (promela-block-type-after indent-point))) + (if (not block-type) 0 + ;;(message "paren: %d (%d); block: %s (%d); indent: %s (%d); stop: %d" + ;; paren-depth paren-point block-type block-point + ;; indent-type indent-point stop) + (goto-char block-point) + (cond + ;; Indent (options) inside "if" or "do" + ((memq block-type '(selection iteration)) + (if (re-search-forward "\\(do\\|if\\)[ \t]*::" indent-point t) + (- (current-column) 2) + (+ (current-column) promela-selection-indent))) + ;; indent (generic code) inside "::" option + ((eq 'option block-type) + (if (and (not indent-type) + (re-search-forward "::.*->[ \t]*\\sw" + (save-excursion (end-of-line) (point)) + t)) + (1- (current-column)) + (+ (current-column) promela-selection-option-indent)) + ) + ;; indent code inside "{" + ((eq 'block block-type) + (cond + ;; if we are indenting the end of a block, + ;; use indentation of start-of-block + ((equal 'block-end indent-type) + (current-indentation)) + ;; if the start of the code inside the block is not at eol + ;; then indent to the same column as the block start +some + ;; [ but ignore comments after "{" ] + ((and (not (promela-effective-eolp (1+ (point)))) + (not (looking-at "{[ \t]*/\\*"))) + (forward-char) ; skip block-start + (skip-chars-forward "{ \t") ; skip whitespace, if any + (current-column)) + ;; anything else we indent +promela-block-indent from + ;; the indentation of the start of block (where we are now) + (t + (+ (current-indentation) + promela-block-indent)))) + ;; dunno what kind of block this is - sound an error + (t + (error "promela-calc-indent-within-block: unknown block type: %s" block-type) + (current-indentation))))))) + +(defun promela-calc-indent-within-comment () + "Return the indentation amount for line, assuming that the +current line is to be regarded as part of a block comment." + (save-excursion + (beginning-of-line) + (skip-chars-forward " \t") + (let ((indenting-end-of-comment (looking-at "\\*/")) + (indenting-blank-line (eolp))) + ;; if line is NOT blank and next char is NOT a "*' + (if (not (or indenting-blank-line (= (following-char) ?\*))) + ;; leave indent alone + (current-column) + ;; otherwise look back for _PREVIOUS_ possible nested comment start + (let ((comment-start (save-excursion + (re-search-backward comment-start-skip)))) + ;; and see if there is an appropriate middle-comment "*" + (if (re-search-backward "^[ \t]+\\*" comment-start t) + (current-indentation) + ;; guess not, so indent relative to comment start + (goto-char comment-start) + (if indenting-end-of-comment + (current-column) + (1+ (current-column))))))))) + + +;; ------------------------------------------------------------------------- +;; Misc other support functions + +(defun promela-parse-partial-sexp (&optional start limit) + "Return the partial parse state of current defun or from optional start +to end limit" + (save-excursion + (let ((end (or limit (point)))) + (if start + (goto-char start) + (promela-beginning-of-defun)) + (parse-partial-sexp (point) end)))) + +;;(defun promela-at-end-of-block-p () +;; "Return t if cursor is at the end of a promela block" +;; (save-excursion +;; (let ((eol (progn (end-of-line) (point)))) +;; (beginning-of-line) +;; (skip-chars-forward " \t") +;; ;;(re-search-forward "\\(}\\|\\b\\(od\\|fi\\)\\b\\)" eol t)))) +;; (looking-at "[ \t]*\\(od\\|fi\\)\\b")))) + +(defun promela-inside-comment-p () + "Check if the point is inside a comment block." + (save-excursion + (let ((origpoint (point)) + state) + (goto-char 1) + (while (> origpoint (point)) + (setq state (parse-partial-sexp (point) origpoint 0))) + (nth 4 state)))) + +(defun promela-inside-comment-or-string-p () + "Check if the point is inside a comment or a string." + (save-excursion + (let ((origpoint (point)) + state) + (goto-char 1) + (while (> origpoint (point)) + (setq state (parse-partial-sexp (point) origpoint 0))) + (or (nth 3 state) (nth 4 state))))) + + +(defun promela-effective-eolp (&optional point) + "Check if we are at the effective end-of-line, ignoring whitespace" + (save-excursion + (if point (goto-char point)) + (skip-chars-forward " \t") + (eolp))) + +(defun promela-check-expansion () + "If abbrev was made within a comment or a string, de-abbrev!" + (if promela-inside-comment-or-string-p + (unexpand-abbrev))) + +(defun promela-block-type-after (&optional point) + "Return the type of block after current point or parameter as a symbol. +Return one of 'iteration `do', 'selection `if', 'option `::', +'block `{' or `}' or nil if none of the above match." + (save-excursion + (goto-char (or point (point))) + (skip-chars-forward " \t") + (cond + ((looking-at "do\\b") 'iteration) + ;;((looking-at "od\\b") 'iteration-end) + ((looking-at "if\\b") 'selection) + ;;((looking-at "fi\\b") 'selection-end) + ((looking-at "::") 'option) + ((looking-at "[{(]") 'block) + ((looking-at "[})]") 'block-end) + (t nil)))) + +(defun promela-find-start-of-containing-comment (&optional limit) + "Return the start point of the containing comment block. +Stop at `limit' or beginning of buffer." + (let ((stop (or limit 1))) + (save-excursion + (while (and (>= (point) stop) + (nth 4 (promela-parse-partial-sexp))) + (re-search-backward comment-start-skip stop t)) + (point)))) + +(defun promela-find-start-of-containing-block (&optional limit) + "Return the start point of the containing `do', `if', `::' or +`{' block or containing comment. +Stop at `limit' or beginning of buffer." + (save-excursion + (skip-chars-forward " \t") + (let* ((type (promela-block-type-after)) + (stop (or limit + (save-excursion (promela-beginning-of-defun) (point)))) + (state (promela-parse-partial-sexp stop)) + (level (if (looking-at "\\(od\\|fi\\)\\b") + 2 + (if (zerop (nth 0 state)) 0 1)))) + ;;(message "find-start-of-containing-block: type: %s; level %d; stop %d" + ;; type level stop) + (while (and (> (point) stop) (not (zerop level))) + (re-search-backward + "\\({\\|}\\|::\\|\\b\\(do\\|od\\|if\\|fi\\)\\b\\)" + stop 'move) + ;;(message "looking from %d back-to %d" (point) stop) + (setq state (promela-parse-partial-sexp stop)) + (setq level (+ level + (cond ((or (nth 3 state) (nth 4 state)) 0) + ((and (= 1 level) (looking-at "::") + (not (equal type 'option))) -1) + ((looking-at "\\({\\|\\(do\\|if\\)\\b\\)") -1) + ((looking-at "\\(}\\|\\(od\\|fi\\)\\b\\)") +1) + (t 0))))) + (point)))) + +(defun promela-find-start-of-containing-block-or-comment (&optional limit) + "Return the start point of the containing comment or +the start of the containing `do', `if', `::' or `{' block. +Stop at limit or beginning of buffer." + (if (promela-inside-comment-p) + (promela-find-start-of-containing-comment limit) + (promela-find-start-of-containing-block limit))) + +;; ------------------------------------------------------------------------- +;; Debugging/testing + +;; (defun promela-mode-toggle-debug () +;; (interactive) +;; (make-local-variable 'debug-on-error) +;; (setq debug-on-error (not debug-on-error))) + +;;(defun promela-mode-revert-buffer () +;; (interactive) +;; (revert-buffer t t)) + +;; ------------------------------------------------------------------------- +;;###autoload + +(provide 'promela-mode) + + +;;---------------------------------------------------------------------- +;; Change History: +;; +;; $Log: promela-mode.el,v $ +;; Revision 1.11 2001/07/09 18:36:45 engstrom +;; - added comments on use of font-lock-maximum-decoration +;; - moved basic preprocess directive fontification to "level 2" +;; +;; Revision 1.10 2001/05/22 16:29:59 engstrom +;; - fixed error introduced in fontification levels stuff (xemacs only) +;; +;; Revision 1.9 2001/05/22 16:21:29 engstrom +;; - commented out the compilation / syntax check stuff for now +;; +;; Revision 1.8 2001/05/22 16:18:49 engstrom +;; - Munched history in preparation for first non-Honeywell release +;; - Added "levels" of fontification to be controlled by the std. variable: +;; 'font-lock-maximum-decoration' +;; +;; Revision 1.7 2001/04/20 01:41:46 engstrom +;; Revision 1.6 2001/04/06 23:57:18 engstrom +;; Revision 1.5 2001/04/04 20:04:15 engstrom +;; Revision 1.4 2001/03/15 02:22:18 engstrom +;; Revision 1.3 2001/03/09 19:39:51 engstrom +;; Revision 1.2 2001/03/01 18:07:47 engstrom +;; Revision 1.1 2001/02/01 xx:xx:xx engstrom +;; migrated to CVS versioning... +;; Pre-CVS-History: +;; 99-10-04 V0.4 EDE Fixed bug in end-of-block indentation +;; Simplified indentation code significantly +;; 99-09-2x V0.3 EDE Hacked on indentation more while at FM'99 +;; 99-09-16 V0.2 EDE Hacked, hacked, hacked on indentation +;; 99-04-01 V0.1 EDE Introduced (less-than) half-baked indentation +;; 98-11-05 V0.0 EDE Created - much code stolen from rexx-mode.el +;; Mostly just a fontification mode - +;; (indentation is HARD ;-) +;; +;; EOF promela-mode.el diff --git a/trunk/verif/md/run b/trunk/verif/md/run new file mode 100755 index 00000000..ccb6458f --- /dev/null +++ b/trunk/verif/md/run @@ -0,0 +1,44 @@ +#!/bin/bash + +#avail. mem +MEM=15360 + +#spin -a model.spin +#cc -DMEMLIM=${MEM} -DSAFETY -o pan pan.c +#./pan + +#first LTL formula +cat defines > pan.ltl +/usr/local/bin/spin -f "!($(cat model_03_write_read_off.spin.ltl | grep -v ^//))" >> pan.ltl +/usr/local/bin/spin -a -X -N pan.ltl model.spin + +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=1850 -DXUSAFE -DNOFAIR pan.c +time ./pan -v -X -m10000 -w19 -a -c1 + +#second LTL formula +cat defines > pan.ltl +/usr/local/bin/spin -f "!($(cat model_03_write_commit_sum.spin.ltl | grep -v ^//))" >> pan.ltl +/usr/local/bin/spin -a -X -N pan.ltl model.spin + +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=1850 -DXUSAFE -DNOFAIR pan.c +time ./pan -v -X -m10000 -w19 -a -c1 + +#3rd +cat defines > pan.ltl +/usr/local/bin/spin -f "!($(cat model_03_events_lost.spin.ltl | grep -v ^//))" >> pan.ltl +/usr/local/bin/spin -a -X -N pan.ltl model.spin + +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=1850 -DXUSAFE -DNOFAIR pan.c +time ./pan -v -X -m10000 -w19 -a -c1 + +#4th +cat defines > pan.ltl +/usr/local/bin/spin -f "!($(cat model_03_no_events_lost.spin.ltl | grep -v ^//))" >> pan.ltl +/usr/local/bin/spin -a -X -N pan.ltl model.spin + +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=1850 -DXUSAFE -DNOFAIR pan.c +time ./pan -v -X -m10000 -w19 -a -c1 + + + + diff --git a/trunk/verif/nico-md-merge/Makefile b/trunk/verif/nico-md-merge/Makefile new file mode 100755 index 00000000..88de87ee --- /dev/null +++ b/trunk/verif/nico-md-merge/Makefile @@ -0,0 +1,59 @@ +# makefile + +COPTIONS=-DSAFETY + + +default: + make commit_sum | tee commit_sum.log + make read_write | tee read_write.log + make events_lost | tee events_lost.log + make no_events_lost | tee no_events_lost.log + + +no_events_lost: clean no_events_lost_ltl run + +no_events_lost_ltl: + cat defines > pan.ltl + cat no_events_lost.def > pan.spin + spin -f "!(`cat no_events_lost.ltl | grep -v ^//`)" >> pan.ltl + + + +events_lost: clean events_lost_ltl run + +events_lost_ltl: + cat defines > pan.ltl + spin -f "!(`cat events_lost.ltl | grep -v ^//`)" >> pan.ltl + + + +read_write: clean read_write_ltl run + +read_write_ltl: + cat defines > pan.ltl + spin -f "!(`cat read_write.ltl | grep -v ^//`)" >> pan.ltl + + + +commit_sum: clean commit_sum_ltl run + +commit_sum_ltl: + cat defines > pan.ltl + spin -f "!(`cat commit_sum.ltl | grep -v ^//`)" >> pan.ltl + + + +run: pan + ./pan -v -X -m100000 -w21 -a -c1 + +pan: pan.c + gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c + +pan.c: pan.ltl model.spin + cat model.spin >> pan.spin + spin -a -X -N pan.ltl pan.spin + + + +clean: + rm -f pan* trail.out diff --git a/trunk/verif/nico-md-merge/commit_sum.log b/trunk/verif/nico-md-merge/commit_sum.log new file mode 100644 index 00000000..0c4a8c78 --- /dev/null +++ b/trunk/verif/nico-md-merge/commit_sum.log @@ -0,0 +1,65 @@ +make[1]: Entering directory `/home/compudj/repository/trunk/verif/nico-md-merge' +rm -f pan* trail.out +cat defines > pan.ltl +spin -f "!(`cat commit_sum.ltl | grep -v ^//`)" >> pan.ltl +spin -a -X -N pan.ltl model.spin +Exit-Status 0 +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c +./pan -v -X -m100000 -w21 -a -c1 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 302) + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 92 byte, depth reached 178, errors: 0 + 117886 states, stored + 210653 states, matched + 328539 transitions (= stored+matched) + 440774 atomic steps +hash conflicts: 3201 (resolved) + +Stats on memory usage (in Megabytes): + 12.142 equivalent memory usage for states (stored*(State-vector + overhead)) + 8.971 actual memory usage for states (compression: 73.88%) + state-vector as stored = 64 byte + 16 byte overhead + 8.000 memory used for hash table (-w21) + 3.052 memory used for DFS stack (-m100000) + 19.939 total actual memory usage + +unreached in proctype switcher + line 81, "pan.___", state 8, "(1)" + line 87, "pan.___", state 15, "write_off = new_off" + line 84, "pan.___", state 18, "((prev_off!=write_off))" + line 84, "pan.___", state 18, "else" + line 97, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 103, "pan.___", state 25, "(1)" + line 98, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 98, "pan.___", state 26, "else" + line 91, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + (7 of 31 states) +unreached in proctype tracer + line 177, "pan.___", state 48, "events_lost = (events_lost+1)" + (1 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + line 284, "pan.___", state 35, "(run switcher())" + (1 of 43 states) +unreached in proctype :never: + line 307, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 0.91 seconds +pan: rate 129545.05 states/second +pan: avg transition delay 2.7698e-06 usec +make[1]: Leaving directory `/home/compudj/repository/trunk/verif/nico-md-merge' diff --git a/trunk/verif/nico-md-merge/commit_sum.ltl b/trunk/verif/nico-md-merge/commit_sum.ltl new file mode 100755 index 00000000..b43d2922 --- /dev/null +++ b/trunk/verif/nico-md-merge/commit_sum.ltl @@ -0,0 +1,6 @@ +// The sum of all subbuffer commit counts must always be lower or equal +// to the writer head, because space must be reserved before it is +// written to and then committed. +// assert(write_off - _commit_sum >= 0 && write_off - _commit_sum < HALF_UCHAR); + +[] (wcsum1 && wcsum2) diff --git a/trunk/verif/nico-md-merge/defines b/trunk/verif/nico-md-merge/defines new file mode 100755 index 00000000..3751f336 --- /dev/null +++ b/trunk/verif/nico-md-merge/defines @@ -0,0 +1,8 @@ +#define rwoff1 (write_off - read_off >= 0) +#define rwoff2 (write_off - read_off < HALF_UCHAR) + +#define wcsum1 (write_off - _commit_sum >= 0) +#define wcsum2 (write_off - _commit_sum < HALF_UCHAR) + +#define buffer_large_enough (NUMPROCS + NUMSWITCH <= BUFSIZE) +#define have_events_lost (events_lost != 0) diff --git a/trunk/verif/nico-md-merge/events_lost.log b/trunk/verif/nico-md-merge/events_lost.log new file mode 100644 index 00000000..c9afd1a3 --- /dev/null +++ b/trunk/verif/nico-md-merge/events_lost.log @@ -0,0 +1,65 @@ +make[1]: Entering directory `/home/compudj/repository/trunk/verif/nico-md-merge' +rm -f pan* trail.out +cat defines > pan.ltl +spin -f "!(`cat events_lost.ltl | grep -v ^//`)" >> pan.ltl +spin -a -X -N pan.ltl model.spin +Exit-Status 0 +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c +./pan -v -X -m100000 -w21 -a -c1 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 302) + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 92 byte, depth reached 178, errors: 0 + 117886 states, stored + 210653 states, matched + 328539 transitions (= stored+matched) + 440774 atomic steps +hash conflicts: 3201 (resolved) + +Stats on memory usage (in Megabytes): + 12.142 equivalent memory usage for states (stored*(State-vector + overhead)) + 8.971 actual memory usage for states (compression: 73.88%) + state-vector as stored = 64 byte + 16 byte overhead + 8.000 memory used for hash table (-w21) + 3.052 memory used for DFS stack (-m100000) + 19.939 total actual memory usage + +unreached in proctype switcher + line 81, "pan.___", state 8, "(1)" + line 87, "pan.___", state 15, "write_off = new_off" + line 84, "pan.___", state 18, "((prev_off!=write_off))" + line 84, "pan.___", state 18, "else" + line 97, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 103, "pan.___", state 25, "(1)" + line 98, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 98, "pan.___", state 26, "else" + line 91, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + (7 of 31 states) +unreached in proctype tracer + line 177, "pan.___", state 48, "events_lost = (events_lost+1)" + (1 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + line 284, "pan.___", state 35, "(run switcher())" + (1 of 43 states) +unreached in proctype :never: + line 307, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 0.86 seconds +pan: rate 137076.74 states/second +pan: avg transition delay 2.6176e-06 usec +make[1]: Leaving directory `/home/compudj/repository/trunk/verif/nico-md-merge' diff --git a/trunk/verif/nico-md-merge/events_lost.ltl b/trunk/verif/nico-md-merge/events_lost.ltl new file mode 100755 index 00000000..cc0f360b --- /dev/null +++ b/trunk/verif/nico-md-merge/events_lost.ltl @@ -0,0 +1,5 @@ +// If we have less writers than the buffer space available, we should +// not loose events. +// assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + +[]((have_events_lost) -> (!buffer_large_enough)) diff --git a/trunk/verif/nico-md-merge/model.spin b/trunk/verif/nico-md-merge/model.spin new file mode 120000 index 00000000..b0ba3702 --- /dev/null +++ b/trunk/verif/nico-md-merge/model.spin @@ -0,0 +1 @@ +models/model_03.spin \ No newline at end of file diff --git a/trunk/verif/nico-md-merge/model.spin.trail b/trunk/verif/nico-md-merge/model.spin.trail new file mode 100755 index 00000000..f86d9759 --- /dev/null +++ b/trunk/verif/nico-md-merge/model.spin.trail @@ -0,0 +1,116 @@ +-2:5:-2 +-4:-4:-4 +1:0:165 +2:1:120 +3:1:121 +4:1:121 +5:1:124 +6:1:131 +7:1:131 +8:1:131 +9:1:131 +10:1:134 +11:1:139 +12:1:140 +13:1:142 +14:1:144 +15:1:142 +16:1:144 +17:1:142 +18:1:144 +19:1:142 +20:1:144 +21:1:146 +22:1:152 +23:1:154 +24:1:156 +25:1:160 +26:0:165 +27:7:31 +28:0:165 +29:7:36 +30:7:37 +31:0:165 +32:7:43 +33:7:44 +34:7:48 +35:7:52 +36:7:56 +37:0:165 +38:8:0 +39:8:6 +40:8:7 +41:0:165 +42:8:13 +43:8:14 +44:0:165 +45:8:18 +46:8:21 +47:0:165 +48:8:29 +49:0:165 +50:8:30 +51:0:165 +52:7:58 +53:7:59 +54:7:62 +55:7:72 +56:7:73 +57:0:165 +58:7:77 +59:7:79 +60:0:165 +61:7:81 +62:0:165 +63:6:31 +64:0:165 +65:6:36 +66:6:37 +67:0:165 +68:6:43 +69:6:44 +70:6:48 +71:6:52 +72:6:56 +73:0:165 +74:6:58 +75:6:59 +76:6:62 +77:6:70 +78:0:165 +79:6:77 +80:6:79 +81:0:165 +82:6:81 +83:0:165 +84:5:31 +85:0:165 +86:5:36 +87:5:37 +88:0:165 +89:5:43 +90:5:44 +91:5:48 +92:5:52 +93:5:56 +94:0:165 +95:5:58 +96:5:59 +97:5:62 +98:5:72 +99:5:73 +100:0:165 +101:5:77 +102:5:79 +103:0:165 +104:5:81 +105:0:165 +106:4:31 +107:0:165 +108:4:34 +109:4:78 +110:4:79 +111:0:163 +112:4:81 +113:0:169 +114:6:52 diff --git a/trunk/verif/nico-md-merge/models/model_01.spin b/trunk/verif/nico-md-merge/models/model_01.spin new file mode 100755 index 00000000..edcd8fac --- /dev/null +++ b/trunk/verif/nico-md-merge/models/model_01.spin @@ -0,0 +1,303 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + /* The commit count of a particular subbuffer must always be higher + * or equal to the retrieve_count of this subbuffer. + * assert(commit_count[j] - retrieve_count[j] >= 0 && + * commit_count[j] - retrieve_count[j] < HALF_UCHAR); + */ + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not lose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/nico-md-merge/models/model_02.spin b/trunk/verif/nico-md-merge/models/model_02.spin new file mode 100755 index 00000000..671e9f2e --- /dev/null +++ b/trunk/verif/nico-md-merge/models/model_02.spin @@ -0,0 +1,298 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + /*assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR);*/ + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/nico-md-merge/models/model_03.spin b/trunk/verif/nico-md-merge/models/model_03.spin new file mode 100755 index 00000000..a21ab1fc --- /dev/null +++ b/trunk/verif/nico-md-merge/models/model_03.spin @@ -0,0 +1,291 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#ifndef BUFSIZE +#define BUFSIZE 4 +#endif +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* */ +byte _commit_sum; +/* */ + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +//#ifdef RACE_TEST +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; +//#endif + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + /* */ + _commit_sum = 0; + /* */ + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } +} + diff --git a/trunk/verif/nico-md-merge/no_events_lost.def b/trunk/verif/nico-md-merge/no_events_lost.def new file mode 100644 index 00000000..080d5713 --- /dev/null +++ b/trunk/verif/nico-md-merge/no_events_lost.def @@ -0,0 +1 @@ +#define BUFSIZE 8 diff --git a/trunk/verif/nico-md-merge/no_events_lost.log b/trunk/verif/nico-md-merge/no_events_lost.log new file mode 100644 index 00000000..ad379f57 --- /dev/null +++ b/trunk/verif/nico-md-merge/no_events_lost.log @@ -0,0 +1,65 @@ +make[1]: Entering directory `/home/compudj/repository/trunk/verif/nico-md-merge' +rm -f pan* trail.out +cat defines > pan.ltl +spin -f "!(`cat no_events_lost.ltl | grep -v ^//`)" >> pan.ltl +spin -a -X -N pan.ltl model.spin +Exit-Status 0 +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c +./pan -v -X -m100000 -w21 -a -c1 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 302) + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 92 byte, depth reached 178, errors: 0 + 117886 states, stored + 210653 states, matched + 328539 transitions (= stored+matched) + 440774 atomic steps +hash conflicts: 3201 (resolved) + +Stats on memory usage (in Megabytes): + 12.142 equivalent memory usage for states (stored*(State-vector + overhead)) + 8.971 actual memory usage for states (compression: 73.88%) + state-vector as stored = 64 byte + 16 byte overhead + 8.000 memory used for hash table (-w21) + 3.052 memory used for DFS stack (-m100000) + 19.939 total actual memory usage + +unreached in proctype switcher + line 81, "pan.___", state 8, "(1)" + line 87, "pan.___", state 15, "write_off = new_off" + line 84, "pan.___", state 18, "((prev_off!=write_off))" + line 84, "pan.___", state 18, "else" + line 97, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 103, "pan.___", state 25, "(1)" + line 98, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 98, "pan.___", state 26, "else" + line 91, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + (7 of 31 states) +unreached in proctype tracer + line 177, "pan.___", state 48, "events_lost = (events_lost+1)" + (1 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + line 284, "pan.___", state 35, "(run switcher())" + (1 of 43 states) +unreached in proctype :never: + line 307, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 0.85 seconds +pan: rate 138689.41 states/second +pan: avg transition delay 2.5872e-06 usec +make[1]: Leaving directory `/home/compudj/repository/trunk/verif/nico-md-merge' diff --git a/trunk/verif/nico-md-merge/no_events_lost.ltl b/trunk/verif/nico-md-merge/no_events_lost.ltl new file mode 100755 index 00000000..3b34f26f --- /dev/null +++ b/trunk/verif/nico-md-merge/no_events_lost.ltl @@ -0,0 +1,5 @@ +// If we have less writers than the buffer space available, we should +// not loose events. +// assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + +[]((buffer_large_enough) -> (!have_events_lost)) diff --git a/trunk/verif/nico-md-merge/pan b/trunk/verif/nico-md-merge/pan new file mode 100755 index 0000000000000000000000000000000000000000..c2084684c16f338104568b625102857d320d2398 GIT binary patch literal 97005 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%0Dt~h(b&%>-!QXLAnREXY_$kUGQ%w1J89WurPlSf} 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 { + default: Uerror("bad return move"); + case 0: goto R999; /* nothing to undo */ + + /* PROC :never: */ +; + ; + + case 4: /* STATE 8 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC :init: */ + + case 5: /* STATE 1 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 6: /* STATE 4 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now.commit_count[ Index(((P4 *)this)->i, 2) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 7: /* STATE 11 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 8: /* STATE 11 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now._commit_sum = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 9: /* STATE 14 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((P4 *)this)->i, 8) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 10: /* STATE 15 */ + ; + /* 0 */ ((P4 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 11: /* STATE 20 */ + ; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 12: /* STATE 22 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 13: /* STATE 24 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 14: /* STATE 26 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 15: /* STATE 32 */ + ; + ((P4 *)this)->i = trpt->bup.ovals[1]; + /* 0 */ ((P4 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 16: /* STATE 32 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 17: /* STATE 34 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 18: /* STATE 36 */ + ; + ((P4 *)this)->i = trpt->bup.oval; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 19: /* STATE 37 */ + ; + /* 0 */ ((P4 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 20: /* STATE 43 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC cleaner */ + + case 21: /* STATE 2 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 22: /* STATE 3 */ + ; + ; + delproc(0, now._nr_pr-1); + ; + goto R999; + + case 23: /* STATE 9 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC reader */ +; + ; + + case 25: /* STATE 2 */ + ; + ((P2 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 26: /* STATE 6 */ + ; + ((P2 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%8), 8) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 27: /* STATE 7 */ + ; + /* 0 */ ((P2 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 28: /* STATE 16 */ + ; + ((P2 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%8), 8) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 29: /* STATE 17 */ + ; + /* 0 */ ((P2 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 30: /* STATE 22 */ + ; + now.read_off = trpt->bup.oval; + ; + goto R999; +; + ; + + case 32: /* STATE 29 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC tracer */ + + case 33: /* STATE 2 */ + ; + ((P1 *)this)->new_off = trpt->bup.ovals[1]; + ((P1 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 34: /* STATE 4 */ + ; + /* 0 */ ((P1 *)this)->new_off = trpt->bup.oval; + ; + ; + goto R999; +; + + case 35: /* STATE 7 */ + goto R999; + + case 36: /* STATE 11 */ + ; + /* 0 */ ((P1 *)this)->prev_off = trpt->bup.oval; + ; + ; + goto R999; + + case 37: /* STATE 17 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.write_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 38: /* STATE 17 */ + ; + ((P1 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 39: /* STATE 21 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%8), 8) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 40: /* STATE 22 */ + ; + /* 0 */ ((P1 *)this)->i = trpt->bup.oval; + ; + ; + goto R999; + + case 41: /* STATE 28 */ + ; + ((P1 *)this)->i = trpt->bup.oval; + ; + goto R999; + + case 42: /* STATE 31 */ + ; + ((P1 *)this)->i = trpt->bup.ovals[1]; + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%8), 8) ] = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 2); + goto R999; + + case 43: /* STATE 39 */ + ; + now.commit_count[ Index(((((P1 *)this)->prev_off%8)/(8/2)), 2) ] = trpt->bup.ovals[3]; + now._commit_sum = trpt->bup.ovals[2]; + ((P1 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P1 *)this)->i = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 4); + goto R999; + + case 44: /* STATE 39 */ + ; + now.commit_count[ Index(((((P1 *)this)->prev_off%8)/(8/2)), 2) ] = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + ((P1 *)this)->tmp_commit = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 45: /* STATE 41 */ + ; + deliver = trpt->bup.ovals[2]; + /* 1 */ ((P1 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P1 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; +; + + case 46: /* STATE 45 */ + goto R999; +; + + case 47: /* STATE 43 */ + goto R999; + + case 48: /* STATE 48 */ + ; + now.events_lost = trpt->bup.oval; + ; + goto R999; + + case 49: /* STATE 49 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 50: /* STATE 51 */ + ; + p_restor(II); + ; + ; + goto R999; + + /* PROC switcher */ + + case 51: /* STATE 3 */ + ; + ((P0 *)this)->new_off = trpt->bup.ovals[2]; + ((P0 *)this)->size = trpt->bup.ovals[1]; + ((P0 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 52: /* STATE 5 */ + ; + now.refcount = trpt->bup.ovals[2]; + /* 1 */ ((P0 *)this)->size = trpt->bup.ovals[1]; + /* 0 */ ((P0 *)this)->new_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; +; + + case 53: /* STATE 8 */ + goto R999; + + case 54: /* STATE 12 */ + ; + /* 0 */ ((P0 *)this)->prev_off = trpt->bup.oval; + ; + ; + goto R999; +; + + case 55: /* STATE 17 */ + goto R999; + + case 56: /* STATE 15 */ + ; + now.write_off = trpt->bup.oval; + ; + goto R999; + + case 57: /* STATE 21 */ + ; + now.commit_count[ Index(((((P0 *)this)->prev_off%8)/(8/2)), 2) ] = trpt->bup.ovals[2]; + now._commit_sum = trpt->bup.ovals[1]; + ((P0 *)this)->tmp_commit = trpt->bup.ovals[0]; + ; + ungrab_ints(trpt->bup.ovals, 3); + goto R999; + + case 58: /* STATE 28 */ + ; + now.refcount = trpt->bup.ovals[3]; + deliver = trpt->bup.ovals[2]; + /* 1 */ ((P0 *)this)->tmp_commit = trpt->bup.ovals[1]; + /* 0 */ ((P0 *)this)->prev_off = trpt->bup.ovals[0]; + ; + ; + ungrab_ints(trpt->bup.ovals, 4); + goto R999; + + case 59: /* STATE 28 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 60: /* STATE 28 */ + ; + now.refcount = trpt->bup.oval; + ; + goto R999; + + case 61: /* STATE 31 */ + ; + p_restor(II); + ; + ; + goto R999; + } + diff --git a/trunk/verif/nico-md-merge/pan.c b/trunk/verif/nico-md-merge/pan.c new file mode 100644 index 00000000..24947671 --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.c @@ -0,0 +1,11541 @@ +/*** Generated by Spin Version 5.1.6 -- 9 May 2008 ***/ +/*** From source: pan.spin ***/ + +#ifdef SC +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined(WIN32) || defined(WIN64) +#include +#else +#include +#include +#endif +#include +#include +#include +#define Offsetof(X, Y) ((unsigned long)(&(((X *)0)->Y))) +#ifndef max +#define max(a,b) (((a)<(b)) ? (b) : (a)) +#endif +#ifndef PRINTF +int Printf(const char *fmt, ...); /* prototype only */ +#endif +#include "pan.h" +#ifdef LOOPSTATE +double cnt_loops; +#endif +State A_Root; /* seed-state for cycles */ +State now; /* the full state-vector */ +#undef C_States +#if defined(C_States) && defined(HAS_TRACK) +void +c_update(uchar *p_t_r) +{ +#ifdef VERBOSE + printf("c_update %u\n", p_t_r); +#endif +} +void +c_revert(uchar *p_t_r) +{ +#ifdef VERBOSE + printf("c_revert %u\n", p_t_r); +#endif +} +#endif +void +globinit(void) +{ +} +void +locinit5(int h) +{ +} +void +locinit4(int h) +{ +} +void +locinit3(int h) +{ +} +void +locinit2(int h) +{ +} +void +locinit1(int h) +{ +} +void +locinit0(int h) +{ +} +#ifdef CNTRSTACK +#define onstack_now() (LL[trpt->j6] && LL[trpt->j7]) +#define onstack_put() LL[trpt->j6]++; LL[trpt->j7]++ +#define onstack_zap() LL[trpt->j6]--; LL[trpt->j7]-- +#endif +#if !defined(SAFETY) && !defined(NOCOMP) +#define V_A (((now._a_t&1)?2:1) << (now._a_t&2)) +#define A_V (((now._a_t&1)?1:2) << (now._a_t&2)) +int S_A = 0; +#else +#define V_A 0 +#define A_V 0 +#define S_A 0 +#endif +#ifdef MA +#undef onstack_now +#undef onstack_put +#undef onstack_zap +#define onstack_put() ; +#define onstack_zap() gstore((char *) &now, vsize, 4) +#else +#if defined(FULLSTACK) && !defined(BITSTATE) +#define onstack_put() trpt->ostate = Lstate +#define onstack_zap() { \ + if (trpt->ostate) \ + trpt->ostate->tagged = \ + (S_A)? (trpt->ostate->tagged&~V_A) : 0; \ + } +#endif +#endif +#ifndef NO_V_PROVISO +#define V_PROVISO +#endif +#if !defined(NO_RESIZE) && !defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(SPACE) && NCORE==1 + #define AUTO_RESIZE +#endif + +struct H_el { + struct H_el *nxt; +#ifdef FULLSTACK + unsigned int tagged; + #if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY) + unsigned int proviso; + #endif +#endif +#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK)) + unsigned long st_id; +#endif +#if !defined(SAFETY) || defined(REACH) + unsigned int D; +#endif +#if NCORE>1 + /* could cost 1 extra word: 4 bytes if 32-bit and 8 bytes if 64-bit */ + #ifdef V_PROVISO + uchar cpu_id; /* id of cpu that created the state */ + #endif +#endif +#ifdef COLLAPSE + #if VECTORSZ<65536 + unsigned short ln; + #else + unsigned long ln; + #endif +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + unsigned long m_K1; +#endif + unsigned long state; +} **H_tab, **S_Tab; + +typedef struct Trail { + int st; /* current state */ + uchar pr; /* process id */ + uchar tau; /* 8 bit-flags */ + uchar o_pm; /* 8 more bit-flags */ +#if 0 + Meaning of bit-flags: + tau&1 -> timeout enabled + tau&2 -> request to enable timeout 1 level up (in claim) + tau&4 -> current transition is a claim move + tau&8 -> current transition is an atomic move + tau&16 -> last move was truncated on stack + tau&32 -> current transition is a preselected move + tau&64 -> at least one next state is not on the stack + tau&128 -> current transition is a stutter move + o_pm&1 -> the current pid moved -- implements else + o_pm&2 -> this is an acceptance state + o_pm&4 -> this is a progress state + o_pm&8 -> fairness alg rule 1 undo mark + o_pm&16 -> fairness alg rule 3 undo mark + o_pm&32 -> fairness alg rule 2 undo mark + o_pm&64 -> the current proc applied rule2 + o_pm&128 -> a fairness, dummy move - all procs blocked +#endif +#ifdef NSUCC + uchar n_succ; /* nr of successor states */ +#endif +#if defined(FULLSTACK) && defined(MA) && !defined(BFS) + uchar proviso; +#endif +#ifndef BFS + uchar o_n, o_ot; /* to save locals */ +#endif + uchar o_m; +#ifdef EVENT_TRACE +#if nstates_event<256 + uchar o_event; +#else + unsigned short o_event; +#endif +#endif + int o_tt; +#ifndef BFS + short o_To; +#ifdef RANDOMIZE + short oo_i; +#endif +#endif +#if defined(HAS_UNLESS) && !defined(BFS) + int e_state; /* if escape trans - state of origin */ +#endif +#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS) || (NCORE>1) + struct H_el *ostate; /* pointer to stored state */ +#endif +#if defined(CNTRSTACK) && !defined(BFS) + long j6, j7; +#endif + Trans *o_t; +#ifdef SCHED + /* based on Qadeer&Rehof, Tacas 2005, LNCS 3440, pp. 93-107 */ + #if NCORE>1 + #error "-DSCHED cannot be combined with -DNCORE (yet)" + #endif + int sched_limit; +#endif +#ifdef HAS_SORTED + short ipt; +#endif + union { + int oval; + int *ovals; + } bup; +} Trail; +Trail *trail, *trpt; +FILE *efd; +uchar *this; +long maxdepth=10000; +long omaxdepth=10000; +#ifdef SCHED +int sched_max = 10; +#endif +#ifdef PERMUTED + uchar permuted = 1; +#else + uchar permuted = 0; +#endif +double quota; /* time limit */ +#if NCORE>1 +long z_handoff = -1; +#endif +#ifdef SC +char *stackfile; +#endif +uchar *SS, *LL; +uchar HASH_NR = 0; + +double memcnt = (double) 0; +double memlim = (double) (1<<30); /* 1 GB */ +#if NCORE>1 +double mem_reserved = (double) 0; +#endif + +/* for emalloc: */ +static char *have; +static long left = 0L; +static double fragment = (double) 0; +static unsigned long grow; + +unsigned int HASH_CONST[] = { + /* asuming 4 bytes per int */ + 0x88888EEF, 0x00400007, + 0x04c11db7, 0x100d4e63, + 0x0fc22f87, 0x3ff0c3ff, + 0x38e84cd7, 0x02b148e9, + 0x98b2e49d, 0xb616d379, + 0xa5247fd9, 0xbae92a15, + 0xb91c8bc5, 0x8e5880f3, + 0xacd7c069, 0xb4c44bb3, + 0x2ead1fb7, 0x8e428171, + 0xdbebd459, 0x828ae611, + 0x6cb25933, 0x86cdd651, + 0x9e8f5f21, 0xd5f8d8e7, + 0x9c4e956f, 0xb5cf2c71, + 0x2e805a6d, 0x33fc3a55, + 0xaf203ed1, 0xe31f5909, + 0x5276db35, 0x0c565ef7, + 0x273d1aa5, 0x8923b1dd, + 0 +}; +#if NCORE>1 +extern int core_id; +#endif +long mreached=0; +int done=0, errors=0, Nrun=1; +int c_init_done=0; +char *c_stack_start = (char *) 0; +double nstates=0, nlinks=0, truncs=0, truncs2=0; +double nlost=0, nShadow=0, hcmp=0, ngrabs=0; +#if defined(ZAPH) && defined(BITSTATE) +double zstates = 0; +#endif +int c_init_run; +#ifdef BFS +double midrv=0, failedrv=0, revrv=0; +#endif +unsigned long nr_states=0; /* nodes in DFA */ +long Fa=0, Fh=0, Zh=0, Zn=0; +long PUT=0, PROBE=0, ZAPS=0; +long Ccheck=0, Cholds=0; +int a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0; +#ifdef HAS_CODE +int gui = 0, coltrace = 0, readtrail = 0; +int whichtrail = 0, onlyproc = -1, silent = 0; +#endif +int state_tables=0, fairness=0, no_rck=0, Nr_Trails=0; +char simvals[128]; +#ifndef INLINE +int TstOnly=0; +#endif +unsigned long mask, nmask; +#ifdef BITSTATE +int ssize=23; /* 1 Mb */ +#else +int ssize=19; /* 512K slots */ +#endif +int hmax=0, svmax=0, smax=0; +int Maxbody=0, XX; +uchar *noptr; /* used by macro Pptr(x) */ +#ifdef VAR_RANGES +void logval(char *, int); +void dumpranges(void); +#endif +#ifdef MA +#define INLINE_REV +extern void dfa_init(unsigned short); +extern int dfa_member(unsigned long); +extern int dfa_store(uchar *); +unsigned int maxgs = 0; +#endif + +#ifdef ALIGNED + State comp_now __attribute__ ((aligned (8))); + /* gcc 64-bit aligned for Itanium2 systems */ + /* MAJOR runtime penalty if not used on those systems */ +#else + State comp_now; /* compressed state vector */ +#endif + +State comp_msk; +uchar *Mask = (uchar *) &comp_msk; +#ifdef COLLAPSE +State comp_tmp; +static char *scratch = (char *) &comp_tmp; +#endif +Stack *stack; /* for queues, processes */ +Svtack *svtack; /* for old state vectors */ +#ifdef BITSTATE +static unsigned int hfns = 3; /* new default */ +#endif +static unsigned long j1; +static unsigned long K1, K2; +static unsigned long j2, j3, j4; +#ifdef BITSTATE +static long udmem; +#endif +static long A_depth = 0; +long depth = 0; +#if NCORE>1 +long nr_handoffs = 0; +#endif +static uchar warned = 0, iterative = 0, exclusive = 0, like_java = 0, every_error = 0; +static uchar noasserts = 0, noends = 0, bounded = 0; +#if SYNC>0 && ASYNC==0 +void set_recvs(void); +int no_recvs(int); +#endif +#if SYNC +#define IfNotBlocked if (boq != -1) continue; +#define UnBlock boq = -1 +#else +#define IfNotBlocked /* cannot block */ +#define UnBlock /* don't bother */ +#endif + +#ifdef BITSTATE +int (*bstore)(char *, int); +int bstore_reg(char *, int); +int bstore_mod(char *, int); +#endif +void active_procs(void); +void cleanup(void); +void do_the_search(void); +void find_shorter(int); +void iniglobals(void); +void stopped(int); +void wrapup(void); +int *grab_ints(int); +void ungrab_ints(int *, int); +#ifndef NOBOUNDCHECK +#define Index(x, y) Boundcheck(x, y, II, tt, t) +#else +#define Index(x, y) x +#endif +short Air[] = { (short) Air0, (short) Air1, (short) Air2, (short) Air3, (short) Air4, (short) Air5, (short) Air6 }; +int +addproc(int n) +{ int j, h = now._nr_pr; +#ifndef NOCOMP + int k; +#endif + uchar *o_this = this; + +#ifndef INLINE + if (TstOnly) return (h < MAXPROC); +#endif +#ifndef NOBOUNDCHECK +/* redefine Index only within this procedure */ +#undef Index +#define Index(x, y) Boundcheck(x, y, 0, 0, 0) +#endif + if (h >= MAXPROC) + Uerror("too many processes"); + switch (n) { + case 0: j = sizeof(P0); break; + case 1: j = sizeof(P1); break; + case 2: j = sizeof(P2); break; + case 3: j = sizeof(P3); break; + case 4: j = sizeof(P4); break; + case 5: j = sizeof(P5); break; + case 6: j = sizeof(P6); break; + default: Uerror("bad proc - addproc"); + } + if (vsize%WS) + proc_skip[h] = WS-(vsize%WS); + else + proc_skip[h] = 0; +#ifndef NOCOMP + for (k = vsize + (int) proc_skip[h]; k > vsize; k--) + Mask[k-1] = 1; /* align */ +#endif + vsize += (int) proc_skip[h]; + proc_offset[h] = vsize; +#ifdef SVDUMP + if (vprefix > 0) + { int dummy = 0; + write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */ + write(svfd, (uchar *) &h, sizeof(int)); + write(svfd, (uchar *) &n, sizeof(int)); +#if VECTORSZ>32000 + write(svfd, (uchar *) &proc_offset[h], sizeof(int)); +#else + write(svfd, (uchar *) &proc_offset[h], sizeof(short)); +#endif + write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */ + } +#endif + now._nr_pr += 1; + if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2)) + { printf("pan: error: too many processes -- current"); + printf(" max is %d procs (-DNFAIR=%d)\n", + (8*NFAIR)/2 - 2, NFAIR); + printf("\trecompile with -DNFAIR=%d\n", + NFAIR+1); + pan_exit(1); + } + vsize += j; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (k = 1; k <= Air[n]; k++) + Mask[vsize - k] = 1; /* pad */ + Mask[vsize-j] = 1; /* _pid */ +#endif + hmax = max(hmax, vsize); + if (vsize >= VECTORSZ) + { printf("pan: error, VECTORSZ too small, recompile pan.c"); + printf(" with -DVECTORSZ=N with N>%d\n", (int) vsize); + Uerror("aborting"); + } + memset((char *)pptr(h), 0, j); + this = pptr(h); + if (BASE > 0 && h > 0) + ((P0 *)this)->_pid = h-BASE; + else + ((P0 *)this)->_pid = h; + switch (n) { + case 6: /* np_ */ + ((P6 *)pptr(h))->_t = 6; + ((P6 *)pptr(h))->_p = 0; + reached6[0] = 1; + accpstate[6][1] = 1; + break; + case 5: /* :never: */ + ((P5 *)pptr(h))->_t = 5; + ((P5 *)pptr(h))->_p = 5; reached5[5]=1; + /* params: */ + /* locals: */ +#ifdef VAR_RANGES +#endif +#ifdef HAS_CODE + locinit5(h); +#endif + break; + case 4: /* :init: */ + ((P4 *)pptr(h))->_t = 4; + ((P4 *)pptr(h))->_p = 42; reached4[42]=1; + /* params: */ + /* locals: */ + ((P4 *)pptr(h))->i = 0; + ((P4 *)pptr(h))->j = 0; + ((P4 *)pptr(h))->sum = 0; + ((P4 *)pptr(h))->commit_sum = 0; +#ifdef VAR_RANGES + logval(":init::i", ((P4 *)pptr(h))->i); + logval(":init::j", ((P4 *)pptr(h))->j); + logval(":init::sum", ((P4 *)pptr(h))->sum); + logval(":init::commit_sum", ((P4 *)pptr(h))->commit_sum); +#endif +#ifdef HAS_CODE + locinit4(h); +#endif + break; + case 3: /* cleaner */ + ((P3 *)pptr(h))->_t = 3; + ((P3 *)pptr(h))->_p = 8; reached3[8]=1; + /* params: */ + /* locals: */ +#ifdef VAR_RANGES +#endif +#ifdef HAS_CODE + locinit3(h); +#endif + break; + case 2: /* reader */ + ((P2 *)pptr(h))->_t = 2; + ((P2 *)pptr(h))->_p = 26; reached2[26]=1; + /* params: */ + /* locals: */ + ((P2 *)pptr(h))->i = 0; + ((P2 *)pptr(h))->j = 0; +#ifdef VAR_RANGES + logval("reader:i", ((P2 *)pptr(h))->i); + logval("reader:j", ((P2 *)pptr(h))->j); +#endif +#ifdef HAS_CODE + locinit2(h); +#endif + break; + case 1: /* tracer */ + ((P1 *)pptr(h))->_t = 1; + ((P1 *)pptr(h))->_p = 3; reached1[3]=1; + /* params: */ + /* locals: */ + ((P1 *)pptr(h))->size = 1; + ((P1 *)pptr(h))->prev_off = 0; + ((P1 *)pptr(h))->new_off = 0; + ((P1 *)pptr(h))->tmp_commit = 0; + ((P1 *)pptr(h))->i = 0; + ((P1 *)pptr(h))->j = 0; +#ifdef VAR_RANGES + logval("tracer:size", ((P1 *)pptr(h))->size); + logval("tracer:prev_off", ((P1 *)pptr(h))->prev_off); + logval("tracer:new_off", ((P1 *)pptr(h))->new_off); + logval("tracer:tmp_commit", ((P1 *)pptr(h))->tmp_commit); + logval("tracer:i", ((P1 *)pptr(h))->i); + logval("tracer:j", ((P1 *)pptr(h))->j); +#endif +#ifdef HAS_CODE + locinit1(h); +#endif + break; + case 0: /* switcher */ + ((P0 *)pptr(h))->_t = 0; + ((P0 *)pptr(h))->_p = 11; reached0[11]=1; + /* params: */ + /* locals: */ + ((P0 *)pptr(h))->prev_off = 0; + ((P0 *)pptr(h))->new_off = 0; + ((P0 *)pptr(h))->tmp_commit = 0; + ((P0 *)pptr(h))->size = 0; +#ifdef VAR_RANGES + logval("switcher:prev_off", ((P0 *)pptr(h))->prev_off); + logval("switcher:new_off", ((P0 *)pptr(h))->new_off); + logval("switcher:tmp_commit", ((P0 *)pptr(h))->tmp_commit); + logval("switcher:size", ((P0 *)pptr(h))->size); +#endif +#ifdef HAS_CODE + locinit0(h); +#endif + break; + } + this = o_this; + return h-BASE; +#ifndef NOBOUNDCHECK +#undef Index +#define Index(x, y) Boundcheck(x, y, II, tt, t) +#endif +} + +#if defined(BITSTATE) && defined(COLLAPSE) +/* just to allow compilation, to generate the error */ +long col_p(int i, char *z) { return 0; } +long col_q(int i, char *z) { return 0; } +#endif +#ifndef BITSTATE +#ifdef COLLAPSE +long +col_p(int i, char *z) +{ int j, k; unsigned long ordinal(char *, long, short); + char *x, *y; + P0 *ptr = (P0 *) pptr(i); + switch (ptr->_t) { + case 0: j = sizeof(P0); break; + case 1: j = sizeof(P1); break; + case 2: j = sizeof(P2); break; + case 3: j = sizeof(P3); break; + case 4: j = sizeof(P4); break; + case 5: j = sizeof(P5); break; + case 6: j = sizeof(P6); break; + default: Uerror("bad proctype - collapse"); + } + if (z) x = z; else x = scratch; + y = (char *) ptr; k = proc_offset[i]; + for ( ; j > 0; j--, y++) + if (!Mask[k++]) *x++ = *y; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; + if (z) return (long) (x - z); + return ordinal(scratch, x-scratch, (short) (2+ptr->_t)); +} +#endif +#endif +void +run(void) +{ /* int i; */ + memset((char *)&now, 0, sizeof(State)); + vsize = (unsigned long) (sizeof(State) - VECTORSZ); +#ifndef NOVSZ + now._vsz = vsize; +#endif +/* optional provisioning statements, e.g. to */ +/* set hidden variables, used as constants */ +#ifdef PROV +#include PROV +#endif + settable(); + Maxbody = max(Maxbody, ((int) sizeof(P0))); + Maxbody = max(Maxbody, ((int) sizeof(P1))); + Maxbody = max(Maxbody, ((int) sizeof(P2))); + Maxbody = max(Maxbody, ((int) sizeof(P3))); + Maxbody = max(Maxbody, ((int) sizeof(P4))); + Maxbody = max(Maxbody, ((int) sizeof(P5))); + Maxbody = max(Maxbody, ((int) sizeof(P6))); + reached[0] = reached0; + reached[1] = reached1; + reached[2] = reached2; + reached[3] = reached3; + reached[4] = reached4; + reached[5] = reached5; + reached[6] = reached6; + accpstate[0] = (uchar *) emalloc(nstates0); + accpstate[1] = (uchar *) emalloc(nstates1); + accpstate[2] = (uchar *) emalloc(nstates2); + accpstate[3] = (uchar *) emalloc(nstates3); + accpstate[4] = (uchar *) emalloc(nstates4); + accpstate[5] = (uchar *) emalloc(nstates5); + accpstate[6] = (uchar *) emalloc(nstates6); + progstate[0] = (uchar *) emalloc(nstates0); + progstate[1] = (uchar *) emalloc(nstates1); + progstate[2] = (uchar *) emalloc(nstates2); + progstate[3] = (uchar *) emalloc(nstates3); + progstate[4] = (uchar *) emalloc(nstates4); + progstate[5] = (uchar *) emalloc(nstates5); + progstate[6] = (uchar *) emalloc(nstates6); + loopstate0 = loopstate[0] = (uchar *) emalloc(nstates0); + loopstate1 = loopstate[1] = (uchar *) emalloc(nstates1); + loopstate2 = loopstate[2] = (uchar *) emalloc(nstates2); + loopstate3 = loopstate[3] = (uchar *) emalloc(nstates3); + loopstate4 = loopstate[4] = (uchar *) emalloc(nstates4); + loopstate5 = loopstate[5] = (uchar *) emalloc(nstates5); + loopstate6 = loopstate[6] = (uchar *) emalloc(nstates6); + stopstate[0] = (uchar *) emalloc(nstates0); + stopstate[1] = (uchar *) emalloc(nstates1); + stopstate[2] = (uchar *) emalloc(nstates2); + stopstate[3] = (uchar *) emalloc(nstates3); + stopstate[4] = (uchar *) emalloc(nstates4); + stopstate[5] = (uchar *) emalloc(nstates5); + stopstate[6] = (uchar *) emalloc(nstates6); + visstate[0] = (uchar *) emalloc(nstates0); + visstate[1] = (uchar *) emalloc(nstates1); + visstate[2] = (uchar *) emalloc(nstates2); + visstate[3] = (uchar *) emalloc(nstates3); + visstate[4] = (uchar *) emalloc(nstates4); + visstate[5] = (uchar *) emalloc(nstates5); + visstate[6] = (uchar *) emalloc(nstates6); + mapstate[0] = (short *) emalloc(nstates0 * sizeof(short)); + mapstate[1] = (short *) emalloc(nstates1 * sizeof(short)); + mapstate[2] = (short *) emalloc(nstates2 * sizeof(short)); + mapstate[3] = (short *) emalloc(nstates3 * sizeof(short)); + mapstate[4] = (short *) emalloc(nstates4 * sizeof(short)); + mapstate[5] = (short *) emalloc(nstates5 * sizeof(short)); + mapstate[6] = (short *) emalloc(nstates6 * sizeof(short)); +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE +#ifdef HAS_CODE + NrStates[0] = nstates0; + NrStates[1] = nstates1; + NrStates[2] = nstates2; + NrStates[3] = nstates3; + NrStates[4] = nstates4; + NrStates[5] = nstates5; + NrStates[6] = nstates6; +#endif +#endif +#endif +#endif +#endif +#endif +#endif + stopstate[0][endstate0] = 1; + stopstate[1][endstate1] = 1; + stopstate[2][endstate2] = 1; + stopstate[3][endstate3] = 1; + stopstate[4][endstate4] = 1; + stopstate[5][endstate5] = 1; + stopstate[6][endstate6] = 1; + accpstate[5][7] = 1; + stopstate[1][49] = 1; + retrans(0, nstates0, start0, src_ln0, reached0, loopstate0); + retrans(1, nstates1, start1, src_ln1, reached1, loopstate1); + retrans(2, nstates2, start2, src_ln2, reached2, loopstate2); + retrans(3, nstates3, start3, src_ln3, reached3, loopstate3); + retrans(4, nstates4, start4, src_ln4, reached4, loopstate4); + retrans(5, nstates5, start5, src_ln5, reached5, loopstate5); + if (state_tables) + { printf("\nTransition Type: "); + printf("A=atomic; D=d_step; L=local; G=global\n"); + printf("Source-State Labels: "); + printf("p=progress; e=end; a=accept;\n"); +#ifdef MERGED + printf("Note: statement merging was used. Only the first\n"); + printf(" stmnt executed in each merge sequence is shown\n"); + printf(" (use spin -a -o3 to disable statement merging)\n"); +#endif + pan_exit(0); + } + iniglobals(); +#if defined(VERI) && !defined(NOREDUCE) && !defined(NP) + if (!state_tables +#ifdef HAS_CODE + && !readtrail +#endif +#if NCORE>1 + && core_id == 0 +#endif + ) + { printf("warning: for p.o. reduction to be valid "); + printf("the never claim must be stutter-invariant\n"); + printf("(never claims generated from LTL "); + printf("formulae are stutter-invariant)\n"); + } +#endif + UnBlock; /* disable rendez-vous */ +#ifdef BITSTATE + if (udmem) + { udmem *= 1024L*1024L; + #if NCORE>1 + if (!readtrail) + { void init_SS(unsigned long); + init_SS((unsigned long) udmem); + } else + #endif + SS = (uchar *) emalloc(udmem); + bstore = bstore_mod; + } else + #if NCORE>1 + { void init_SS(unsigned long); + init_SS(ONE_L<<(ssize-3)); + } + #else + SS = (uchar *) emalloc(ONE_L<<(ssize-3)); + #endif +#else + hinit(); +#endif +#if defined(FULLSTACK) && defined(BITSTATE) + onstack_init(); +#endif +#if defined(CNTRSTACK) && !defined(BFS) + LL = (uchar *) emalloc(ONE_L<<(ssize-3)); +#endif + stack = ( Stack *) emalloc(sizeof(Stack)); + svtack = (Svtack *) emalloc(sizeof(Svtack)); + /* a place to point for Pptr of non-running procs: */ + noptr = (uchar *) emalloc(Maxbody * sizeof(char)); +#ifdef SVDUMP + if (vprefix > 0) + write(svfd, (uchar *) &vprefix, sizeof(int)); +#endif +#ifdef VERI + Addproc(VERI); /* never - pid = 0 */ +#endif + active_procs(); /* started after never */ +#ifdef EVENT_TRACE + now._event = start_event; + reached[EVENT_TRACE][start_event] = 1; +#endif +#ifdef HAS_CODE + globinit(); +#endif +#ifdef BITSTATE +go_again: +#endif + do_the_search(); +#ifdef BITSTATE + if (--Nrun > 0 && HASH_CONST[++HASH_NR]) + { printf("Run %d:\n", HASH_NR); + wrap_stats(); + printf("\n"); + memset(SS, 0, ONE_L<<(ssize-3)); +#ifdef CNTRSTACK + memset(LL, 0, ONE_L<<(ssize-3)); +#endif +#ifdef FULLSTACK + memset((uchar *) S_Tab, 0, + maxdepth*sizeof(struct H_el *)); +#endif + nstates=nlinks=truncs=truncs2=ngrabs = 0; + nlost=nShadow=hcmp = 0; + Fa=Fh=Zh=Zn = 0; + PUT=PROBE=ZAPS=Ccheck=Cholds = 0; + goto go_again; + } +#endif +} +#ifdef HAS_PROVIDED +int provided(int, uchar, int, Trans *); +#endif +#if NCORE>1 +#define GLOBAL_LOCK (0) +#ifndef CS_N +#define CS_N (256*NCORE) +#endif +#ifdef NGQ +#define NR_QS (NCORE) +#define CS_NR (CS_N+1) /* 2^N + 1, nr critical sections */ +#define GQ_RD GLOBAL_LOCK +#define GQ_WR GLOBAL_LOCK +#define CS_ID (1 + (int) (j1 & (CS_N-1))) /* mask: 2^N - 1, zero reserved */ +#define QLOCK(n) (1+n) +#else +#define NR_QS (NCORE+1) +#define CS_NR (CS_N+3) +#define GQ_RD (1) +#define GQ_WR (2) +#define CS_ID (3 + (int) (j1 & (CS_N-1))) +#define QLOCK(n) (3+n) +#endif + +void e_critical(int); +void x_critical(int); + +#ifndef SEP_STATE + #define enter_critical(w) e_critical(w) + #define leave_critical(w) x_critical(w) +#else + #ifdef NGQ + #define enter_critical(w) { if (w < 1+NCORE) e_critical(w); } + #define leave_critical(w) { if (w < 1+NCORE) x_critical(w); } + #else + #define enter_critical(w) { if (w < 3+NCORE) e_critical(w); } + #define leave_critical(w) { if (w < 3+NCORE) x_critical(w); } + #endif +#endif + +int +cpu_printf(const char *fmt, ...) +{ va_list args; + enter_critical(GLOBAL_LOCK); /* printing */ + printf("cpu%d: ", core_id); + fflush(stdout); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + leave_critical(GLOBAL_LOCK); + return 1; +} +#else +int +cpu_printf(const char *fmt, ...) +{ va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + return 1; +} +#endif +int +Printf(const char *fmt, ...) +{ /* Make sure the args to Printf + * are always evaluated (e.g., they + * could contain a run stmnt) + * but do not generate the output + * during verification runs + * unless explicitly wanted + * If this fails on your system + * compile SPIN itself -DPRINTF + * and this code is not generated + */ +#ifdef HAS_CODE + if (readtrail) + { va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + return 1; + } +#endif +#ifdef PRINTF + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +#endif + return 1; +} +extern void printm(int); +#ifndef SC +#define getframe(i) &trail[i]; +#else +static long HHH, DDD, hiwater; +static long CNT1, CNT2; +static int stackwrite; +static int stackread; +static Trail frameptr; +Trail * +getframe(int d) +{ + if (CNT1 == CNT2) + return &trail[d]; + + if (d >= (CNT1-CNT2)*DDD) + return &trail[d - (CNT1-CNT2)*DDD]; + + if (!stackread + && (stackread = open(stackfile, 0)) < 0) + { printf("getframe: cannot open %s\n", stackfile); + wrapup(); + } + if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1 + || read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail)) + { printf("getframe: frame read error\n"); + wrapup(); + } + return &frameptr; +} +#endif +#if !defined(SAFETY) && !defined(BITSTATE) +#if !defined(FULLSTACK) || defined(MA) +#define depth_of(x) A_depth /* an estimate */ +#else +int +depth_of(struct H_el *s) +{ Trail *t; int d; + for (d = 0; d <= A_depth; d++) + { t = getframe(d); + if (s == t->ostate) + return d; + } + printf("pan: cannot happen, depth_of\n"); + return depthfound; +} +#endif +#endif +#if NCORE>1 +extern void cleanup_shm(int); +volatile unsigned int *search_terminated; /* to signal early termination */ +#endif +void +pan_exit(int val) +{ void stop_timer(void); + if (signoff) + { printf("--end of output--\n"); + } +#if NCORE>1 + if (search_terminated != NULL) + { *search_terminated |= 1; /* pan_exit */ + } +#ifdef USE_DISK + { void dsk_stats(void); + dsk_stats(); + } +#endif + if (!state_tables && !readtrail) + { cleanup_shm(1); + } +#endif + if (val == 2) + { val = 0; + } else + { stop_timer(); + } + exit(val); +} +#ifdef HAS_CODE +char * +transmognify(char *s) +{ char *v, *w; + static char buf[2][2048]; + int i, toggle = 0; + if (!s || strlen(s) > 2047) return s; + memset(buf[0], 0, 2048); + memset(buf[1], 0, 2048); + strcpy(buf[toggle], s); + while ((v = strstr(buf[toggle], "{c_code"))) + { *v = '\0'; v++; + strcpy(buf[1-toggle], buf[toggle]); + for (w = v; *w != '}' && *w != '\0'; w++) /* skip */; + if (*w != '}') return s; + *w = '\0'; w++; + for (i = 0; code_lookup[i].c; i++) + if (strcmp(v, code_lookup[i].c) == 0 + && strlen(v) == strlen(code_lookup[i].c)) + { if (strlen(buf[1-toggle]) + + strlen(code_lookup[i].t) + + strlen(w) > 2047) + return s; + strcat(buf[1-toggle], code_lookup[i].t); + break; + } + strcat(buf[1-toggle], w); + toggle = 1 - toggle; + } + buf[toggle][2047] = '\0'; + return buf[toggle]; +} +#else +char * transmognify(char *s) { return s; } +#endif +#ifdef HAS_CODE +void +add_src_txt(int ot, int tt) +{ Trans *t; + char *q; + + for (t = trans[ot][tt]; t; t = t->nxt) + { printf("\t\t"); + q = transmognify(t->tp); + for ( ; q && *q; q++) + if (*q == '\n') + printf("\\n"); + else + putchar(*q); + printf("\n"); + } +} +void +wrap_trail(void) +{ static int wrap_in_progress = 0; + int i; short II; + P0 *z; + + if (wrap_in_progress++) return; + + printf("spin: trail ends after %ld steps\n", depth); + if (onlyproc >= 0) + { if (onlyproc >= now._nr_pr) { pan_exit(0); } + II = onlyproc; + z = (P0 *)pptr(II); + printf("%3ld: proc %d (%s) ", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d", + src_all[i].src[z->_p]); + break; + } + printf(" (state %2d)", z->_p); + if (!stopstate[z->_t][z->_p]) + printf(" (invalid end state)"); + printf("\n"); + add_src_txt(z->_t, z->_p); + pan_exit(0); + } + printf("#processes %d:\n", now._nr_pr); + if (depth < 0) depth = 0; + for (II = 0; II < now._nr_pr; II++) + { z = (P0 *)pptr(II); + printf("%3ld: proc %d (%s) ", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d", + src_all[i].src[z->_p]); + break; + } + printf(" (state %2d)", z->_p); + if (!stopstate[z->_t][z->_p]) + printf(" (invalid end state)"); + printf("\n"); + add_src_txt(z->_t, z->_p); + } + c_globals(); + for (II = 0; II < now._nr_pr; II++) + { z = (P0 *)pptr(II); + c_locals(II, z->_t); + } +#ifdef ON_EXIT + ON_EXIT; +#endif + pan_exit(0); +} +FILE * +findtrail(void) +{ FILE *fd; + char fnm[512], *q; + char MyFile[512]; + char MySuffix[16]; + int try_core; + int candidate_files; + + if (trailfilename != NULL) + { fd = fopen(trailfilename, "r"); + if (fd == NULL) + { printf("pan: cannot find %s\n", trailfilename); + pan_exit(1); + } /* else */ + goto success; + } +talk: + try_core = 1; + candidate_files = 0; + tprefix = "trail"; + strcpy(MyFile, TrailFile); + do { /* see if there's more than one possible trailfile */ + if (whichtrail) + { sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } + if ((q = strchr(MyFile, '.')) != NULL) + { *q = '\0'; + sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } } + } else + { sprintf(fnm, "%s.%s", MyFile, tprefix); + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } + if ((q = strchr(MyFile, '.')) != NULL) + { *q = '\0'; + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + if (fd != NULL) + { candidate_files++; + if (verbose==100) + printf("trail%d: %s\n", + candidate_files, fnm); + fclose(fd); + } } } + tprefix = MySuffix; + sprintf(tprefix, "cpu%d_trail", try_core++); + } while (try_core <= NCORE); + + if (candidate_files != 1) + { if (verbose != 100) + { printf("error: there are %d trail files:\n", + candidate_files); + verbose = 100; + goto talk; + } else + { printf("pan: rm or mv all except one\n"); + exit(1); + } } + try_core = 1; + strcpy(MyFile, TrailFile); /* restore */ + tprefix = "trail"; +try_again: + if (whichtrail) + { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, tprefix); + fd = fopen(fnm, "r"); + if (fd == NULL && (q = strchr(MyFile, '.'))) + { *q = '\0'; + sprintf(fnm, "%s%d.%s", + MyFile, whichtrail, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + } + } else + { sprintf(fnm, "%s.%s", MyFile, tprefix); + fd = fopen(fnm, "r"); + if (fd == NULL && (q = strchr(MyFile, '.'))) + { *q = '\0'; + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = fopen(fnm, "r"); + } } + if (fd == NULL) + { if (try_core < NCORE) + { tprefix = MySuffix; + sprintf(tprefix, "cpu%d_trail", try_core++); + goto try_again; + } + printf("pan: cannot find trailfile %s\n", fnm); + pan_exit(1); + } +success: +#if NCORE>1 && defined(SEP_STATE) + { void set_root(void); /* for partial traces from local root */ + set_root(); + } +#endif + return fd; +} + +uchar do_transit(Trans *, short); + +void +getrail(void) +{ FILE *fd; + char *q; + int i, t_id, lastnever=-1; short II; + Trans *t; + P0 *z; + + fd = findtrail(); /* exits if unsuccessful */ + while (fscanf(fd, "%ld:%d:%d\n", &depth, &i, &t_id) == 3) + { if (depth == -1) + printf("<<<<>>>>\n"); + if (depth < 0) + continue; + if (i > now._nr_pr) + { printf("pan: Error, proc %d invalid pid ", i); + printf("transition %d\n", t_id); + break; + } + II = i; + z = (P0 *)pptr(II); + for (t = trans[z->_t][z->_p]; t; t = t->nxt) + if (t->t_id == (T_ID) t_id) + break; + if (!t) + { for (i = 0; i < NrStates[z->_t]; i++) + { t = trans[z->_t][i]; + if (t && t->t_id == (T_ID) t_id) + { printf("\tRecovered at state %d\n", i); + z->_p = i; + goto recovered; + } } + printf("pan: Error, proc %d type %d state %d: ", + II, z->_t, z->_p); + printf("transition %d not found\n", t_id); + printf("pan: list of possible transitions in this process:\n"); + if (z->_t >= 0 && z->_t <= _NP_) + for (t = trans[z->_t][z->_p]; t; t = t->nxt) + printf(" t_id %d -- case %d, [%s]\n", + t->t_id, t->forw, t->tp); + break; /* pan_exit(1); */ + } +recovered: + q = transmognify(t->tp); + if (gui) simvals[0] = '\0'; + this = pptr(II); + trpt->tau |= 1; + if (!do_transit(t, II)) + { if (onlyproc >= 0 && II != onlyproc) + goto moveon; + printf("pan: error, next transition UNEXECUTABLE on replay\n"); + printf(" most likely causes: missing c_track statements\n"); + printf(" or illegal side-effects in c_expr statements\n"); + } + if (onlyproc >= 0 && II != onlyproc) + goto moveon; + if (verbose) + { printf("%3ld: proc %2d (%s) ", depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) trans {%d,%d} [%s]\n", + z->_p, t_id, t->forw, q?q:""); + c_globals(); + for (i = 0; i < now._nr_pr; i++) + { c_locals(i, ((P0 *)pptr(i))->_t); + } + } else + if (strcmp(procname[z->_t], ":never:") == 0) + { if (lastnever != (int) z->_p) + { for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf("MSC: ~G %d\n", + src_all[i].src[z->_p]); + break; + } + if (!src_all[i].src) + printf("MSC: ~R %d\n", z->_p); + } + lastnever = z->_p; + goto sameas; + } else + if (strcmp(procname[z->_t], ":np_:") != 0) + { +sameas: if (no_rck) goto moveon; + if (coltrace) + { printf("%ld: ", depth); + for (i = 0; i < II; i++) + printf("\t\t"); + printf("%s(%d):", procname[z->_t], II); + printf("[%s]\n", q?q:""); + } else if (!silent) + { if (strlen(simvals) > 0) { + printf("%3ld: proc %2d (%s)", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) [values: %s]\n", z->_p, simvals); + } + printf("%3ld: proc %2d (%s)", + depth, II, procname[z->_t]); + for (i = 0; src_all[i].src; i++) + if (src_all[i].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[i].src[z->_p], PanSource); + break; + } + printf("(state %d) [%s]\n", z->_p, q?q:""); + /* printf("\n"); */ + } } +moveon: z->_p = t->st; + } + wrap_trail(); +} +#endif +int +f_pid(int pt) +{ int i; + P0 *z; + for (i = 0; i < now._nr_pr; i++) + { z = (P0 *)pptr(i); + if (z->_t == (unsigned) pt) + return BASE+z->_pid; + } + return -1; +} +#ifdef VERI +void check_claim(int); +#endif + +#if !defined(HASH64) && !defined(HASH32) + #define HASH32 +#endif +#if defined(HASH32) && defined(SAFETY) && !defined(SFH) && !defined(SPACE) + #define SFH +#endif +#if defined(SFH) && (defined(BITSTATE) || defined(COLLAPSE) || defined(HC) || defined(HASH64)) + #undef SFH +#endif +#if defined(SFH) && !defined(NOCOMP) + #define NOCOMP /* go for speed */ +#endif +#if NCORE>1 && !defined(GLOB_HEAP) + #define SEP_HEAP /* version 5.1.2 */ +#endif + +#ifdef BITSTATE +int +bstore_mod(char *v, int n) /* hasharray size not a power of two */ +{ unsigned long x, y; + unsigned int i = 1; + + d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */ + x = K1; y = j3; + for (;;) + { if (!(SS[x%udmem]&(1< RANDSTOR) return 0; +#endif + for (;;) + { SS[x%udmem] |= (1< RANDSTOR) return 0; +#endif + for (;;) + { SS[x] |= (1< 0) + { sprintf(fnm, "%s%d.%s", + MyFile, Nr_Trails-1, tprefix); + } else + { +#ifdef PUTPID + sprintf(fnm, "%s%d.%s", MyFile, getpid(), tprefix); +#else + sprintf(fnm, "%s.%s", MyFile, tprefix); +#endif + } + if ((fd = open(fnm, w_flags, TMODE)) < 0) + { if ((q = strchr(MyFile, '.'))) + { *q = '\0'; + if (iterative == 0 && Nr_Trails-1 > 0) + sprintf(fnm, "%s%d.%s", + MyFile, Nr_Trails-1, tprefix); + else + sprintf(fnm, "%s.%s", MyFile, tprefix); + *q = '.'; + fd = open(fnm, w_flags, TMODE); + } } + if (fd < 0) + { printf("pan: cannot create %s\n", fnm); + perror("cause"); + } else + { +#if NCORE>1 && (defined(SEP_STATE) || !defined(FULL_TRAIL)) + void write_root(void); + write_root(); +#else + printf("pan: wrote %s\n", fnm); +#endif + } + return fd; +} + +#ifndef FREQ +#define FREQ (1000000) +#endif +#ifdef BFS +#define Q_PROVISO +#ifndef INLINE_REV +#define INLINE_REV +#endif + +typedef struct SV_Hold { + State *sv; + int sz; + struct SV_Hold *nxt; +} SV_Hold; + +typedef struct EV_Hold { + char *sv; + int sz; + int nrpr; + int nrqs; + char *po; + char *qo; + char *ps, *qs; + struct EV_Hold *nxt; +} EV_Hold; + +typedef struct BFS_Trail { + Trail *frame; + SV_Hold *onow; + EV_Hold *omask; +#ifdef Q_PROVISO + struct H_el *lstate; +#endif + short boq; + struct BFS_Trail *nxt; +} BFS_Trail; + +BFS_Trail *bfs_trail, *bfs_bot, *bfs_free; + +SV_Hold *svhold, *svfree; + +#ifdef BFS_DISK +#ifndef BFS_LIMIT + #define BFS_LIMIT 100000 +#endif +#ifndef BFS_DSK_LIMIT + #define BFS_DSK_LIMIT 1000000 +#endif +#if defined(WIN32) || defined(WIN64) + #define RFLAGS (O_RDONLY|O_BINARY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY) +#else + #define RFLAGS (O_RDONLY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC) +#endif +long bfs_size_limit; +int bfs_dsk_write = -1; +int bfs_dsk_read = -1; +long bfs_dsk_writes, bfs_dsk_reads; +int bfs_dsk_seqno_w, bfs_dsk_seqno_r; +#endif + +uchar do_reverse(Trans *, short, uchar); +void snapshot(void); + +SV_Hold * +getsv(int n) +{ SV_Hold *h = (SV_Hold *) 0, *oh; + + oh = (SV_Hold *) 0; + for (h = svfree; h; oh = h, h = h->nxt) + { if (n == h->sz) + { if (!oh) + svfree = h->nxt; + else + oh->nxt = h->nxt; + h->nxt = (SV_Hold *) 0; + break; + } + if (n < h->sz) + { h = (SV_Hold *) 0; + break; + } + /* else continue */ + } + + if (!h) + { h = (SV_Hold *) emalloc(sizeof(SV_Hold)); + h->sz = n; +#ifdef BFS_DISK + if (bfs_size_limit >= BFS_LIMIT) + { h->sv = (State *) 0; /* means: read disk */ + bfs_dsk_writes++; /* count */ + if (bfs_dsk_write < 0 /* file descriptor */ + || bfs_dsk_writes%BFS_DSK_LIMIT == 0) + { char dsk_nm[32]; + if (bfs_dsk_write >= 0) + { (void) close(bfs_dsk_write); + } + sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_w++); + bfs_dsk_write = open(dsk_nm, WFLAGS, 0644); + if (bfs_dsk_write < 0) + { Uerror("could not create tmp disk file"); + } + printf("pan: created disk file %s\n", dsk_nm); + } + if (write(bfs_dsk_write, (char *) &now, n) != n) + { Uerror("aborting -- disk write failed (disk full?)"); + } + return h; /* no memcpy */ + } + bfs_size_limit++; +#endif + h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n); + } + + memcpy((char *)h->sv, (char *)&now, n); + return h; +} + +EV_Hold * +getsv_mask(int n) +{ EV_Hold *h; + static EV_Hold *kept = (EV_Hold *) 0; + + for (h = kept; h; h = h->nxt) + if (n == h->sz + && (memcmp((char *) Mask, (char *) h->sv, n) == 0) + && (now._nr_pr == h->nrpr) + && (now._nr_qs == h->nrqs) +#if VECTORSZ>32000 + && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0) + && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0) +#else + && (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0) + && (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0) +#endif + && (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0) + && (memcmp((char *) q_skip, (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0)) + break; + if (!h) + { h = (EV_Hold *) emalloc(sizeof(EV_Hold)); + h->sz = n; + h->nrpr = now._nr_pr; + h->nrqs = now._nr_qs; + + h->sv = (char *) emalloc(n * sizeof(char)); + memcpy((char *) h->sv, (char *) Mask, n); + + if (now._nr_pr > 0) + { h->ps = (char *) emalloc(now._nr_pr * sizeof(int)); + memcpy((char *) h->ps, (char *) proc_skip, now._nr_pr * sizeof(uchar)); +#if VECTORSZ>32000 + h->po = (char *) emalloc(now._nr_pr * sizeof(int)); + memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int)); +#else + h->po = (char *) emalloc(now._nr_pr * sizeof(short)); + memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short)); +#endif + } + if (now._nr_qs > 0) + { h->qs = (char *) emalloc(now._nr_qs * sizeof(int)); + memcpy((char *) h->qs, (char *) q_skip, now._nr_qs * sizeof(uchar)); +#if VECTORSZ>32000 + h->qo = (char *) emalloc(now._nr_qs * sizeof(int)); + memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int)); +#else + h->qo = (char *) emalloc(now._nr_qs * sizeof(short)); + memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short)); +#endif + } + + h->nxt = kept; + kept = h; + } + return h; +} + +void +freesv(SV_Hold *p) +{ SV_Hold *h, *oh; + + oh = (SV_Hold *) 0; + for (h = svfree; h; oh = h, h = h->nxt) + if (h->sz >= p->sz) + break; + + if (!oh) + { p->nxt = svfree; + svfree = p; + } else + { p->nxt = h; + oh->nxt = p; + } +} + +BFS_Trail * +get_bfs_frame(void) +{ BFS_Trail *t; + + if (bfs_free) + { t = bfs_free; + bfs_free = bfs_free->nxt; + t->nxt = (BFS_Trail *) 0; + } else + { t = (BFS_Trail *) emalloc(sizeof(BFS_Trail)); + } + t->frame = (Trail *) emalloc(sizeof(Trail)); + return t; +} + +void +push_bfs(Trail *f, int d) +{ BFS_Trail *t; + + t = get_bfs_frame(); + memcpy((char *)t->frame, (char *)f, sizeof(Trail)); + t->frame->o_tt = d; /* depth */ + + t->boq = boq; + t->onow = getsv(vsize); + t->omask = getsv_mask(vsize); +#if defined(FULLSTACK) && defined(Q_PROVISO) + t->lstate = Lstate; +#endif + if (!bfs_bot) + { bfs_bot = bfs_trail = t; + } else + { bfs_bot->nxt = t; + bfs_bot = t; + } +#ifdef CHECK + printf("PUSH %u (%d)\n", t->frame, d); +#endif +} + +Trail * +pop_bfs(void) +{ BFS_Trail *t; + + if (!bfs_trail) + return (Trail *) 0; + + t = bfs_trail; + bfs_trail = t->nxt; + if (!bfs_trail) + bfs_bot = (BFS_Trail *) 0; +#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE) + if (t->lstate) t->lstate->tagged = 0; +#endif + + t->nxt = bfs_free; + bfs_free = t; + + vsize = t->onow->sz; + boq = t->boq; +#ifdef BFS_DISK + if (t->onow->sv == (State *) 0) + { char dsk_nm[32]; + bfs_dsk_reads++; /* count */ + if (bfs_dsk_read >= 0 /* file descriptor */ + && bfs_dsk_reads%BFS_DSK_LIMIT == 0) + { (void) close(bfs_dsk_read); + sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r-1); + (void) unlink(dsk_nm); + bfs_dsk_read = -1; + } + if (bfs_dsk_read < 0) + { sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r++); + bfs_dsk_read = open(dsk_nm, RFLAGS); + if (bfs_dsk_read < 0) + { Uerror("could not open temp disk file"); + } } + if (read(bfs_dsk_read, (char *) &now, vsize) != vsize) + { Uerror("bad bfs disk file read"); + } +#ifndef NOVSZ + if (now._vsz != vsize) + { Uerror("disk read vsz mismatch"); + } +#endif + } else +#endif + memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize); + memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize); + if (now._nr_pr > 0) +#if VECTORSZ>32000 + { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int)); +#else + { memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short)); +#endif + memcpy((char *)proc_skip, (char *)t->omask->ps, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) +#if VECTORSZ>32000 + { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int)); +#else + { memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short)); +#endif + memcpy((uchar *)q_skip, (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar)); + } +#ifdef BFS_DISK + if (t->onow->sv != (State *) 0) +#endif + freesv(t->onow); /* omask not freed */ +#ifdef CHECK + printf("POP %u (%d)\n", t->frame, t->frame->o_tt); +#endif + return t->frame; +} + +void +store_state(Trail *ntrpt, int shortcut, short oboq) +{ +#ifdef VERI + Trans *t2 = (Trans *) 0; + uchar ot; int tt, E_state; + uchar o_opm = trpt->o_pm, *othis = this; + + if (shortcut) + { +#ifdef VERBOSE + printf("claim: shortcut\n"); +#endif + goto store_it; /* no claim move */ + } + + this = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */ + trpt->o_pm = 0; + + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + +#ifdef HAS_UNLESS + E_state = 0; +#endif + for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0) + { +#ifdef HAS_UNLESS + if (E_state > 0 + && E_state != t2->e_trans) + break; +#endif + if (do_transit(t2, 0)) + { +#ifdef VERBOSE + if (!reached[ot][t2->st]) + printf("depth: %d -- claim move from %d -> %d\n", + trpt->o_tt, ((P0 *)this)->_p, t2->st); +#endif +#ifdef HAS_UNLESS + E_state = t2->e_trans; +#endif + if (t2->st > 0) + { ((P0 *)this)->_p = t2->st; + reached[ot][t2->st] = 1; +#ifndef NOCLAIM + check_claim(t2->st); +#endif + } + if (now._nr_pr == 0) /* claim terminated */ + uerror("end state in claim reached"); + +#ifdef PEG + peg[t2->forw]++; +#endif + trpt->o_pm |= 1; + if (t2->atom&2) + Uerror("atomic in claim not supported in BFS mode"); +store_it: + +#endif + +#ifdef BITSTATE + if (!bstore((char *)&now, vsize)) +#else +#ifdef MA + if (!gstore((char *)&now, vsize, 0)) +#else + if (!hstore((char *)&now, vsize)) +#endif +#endif + { static long sdone = (long) 0; long ndone; + nstates++; +#ifndef NOREDUCE + trpt->tau |= 64; +#endif + ndone = (unsigned long) (nstates/((double) FREQ)); + if (ndone != sdone && mreached%10 != 0) + { snapshot(); + sdone = ndone; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) + if (nstates > ((double)(1<<(ssize+1)))) + { void resize_hashtable(void); + resize_hashtable(); + } +#endif + } +#if SYNC + if (boq != -1) + midrv++; + else if (oboq != -1) + { Trail *x; + x = (Trail *) trpt->ostate; /* pre-rv state */ + if (x) x->o_pm |= 4; /* mark success */ + } +#endif + push_bfs(ntrpt, trpt->o_tt+1); + } else + { truncs++; +#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO) +#if !defined(BITSTATE) + if (Lstate && Lstate->tagged) trpt->tau |= 64; +#else + if (trpt->tau&32) + { BFS_Trail *tprov; + for (tprov = bfs_trail; tprov; tprov = tprov->nxt) + if (tprov->onow->sv != (State *) 0 + && memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize) == 0) + { trpt->tau |= 64; + break; /* state is in queue */ + } } +#endif +#endif + } +#ifdef VERI + ((P0 *)this)->_p = tt; /* reset claim */ + if (t2) + do_reverse(t2, 0, 0); + else + break; + } } + this = othis; + trpt->o_pm = o_opm; +#endif +} + +Trail *ntrpt; + +void +bfs(void) +{ Trans *t; Trail *otrpt, *x; + uchar _n, _m, ot, nps = 0; + int tt, E_state; + short II, From = (short) (now._nr_pr-1), To = BASE; + short oboq = boq; + + ntrpt = (Trail *) emalloc(sizeof(Trail)); + trpt->ostate = (struct H_el *) 0; + trpt->tau = 0; + + trpt->o_tt = -1; + store_state(ntrpt, 0, oboq); /* initial state */ + + while ((otrpt = pop_bfs())) /* also restores now */ + { memcpy((char *) trpt, (char *) otrpt, sizeof(Trail)); +#if defined(C_States) && (HAS_TRACK==1) + c_revert((uchar *) &(now.c_state[0])); +#endif + if (trpt->o_pm & 4) + { +#ifdef VERBOSE + printf("Revisit of atomic not needed (%d)\n", + trpt->o_pm); +#endif + continue; + } +#ifndef NOREDUCE + nps = 0; +#endif + if (trpt->o_pm == 8) + { revrv++; + if (trpt->tau&8) + { +#ifdef VERBOSE + printf("Break atomic (pm:%d,tau:%d)\n", + trpt->o_pm, trpt->tau); +#endif + trpt->tau &= ~8; + } +#ifndef NOREDUCE + else if (trpt->tau&32) + { +#ifdef VERBOSE + printf("Void preselection (pm:%d,tau:%d)\n", + trpt->o_pm, trpt->tau); +#endif + trpt->tau &= ~32; + nps = 1; /* no preselection in repeat */ + } +#endif + } + trpt->o_pm &= ~(4|8); + if (trpt->o_tt > mreached) + { mreached = trpt->o_tt; + if (mreached%10 == 0) + { snapshot(); + } } + depth = trpt->o_tt; + if (depth >= maxdepth) + { +#if SYNC + Trail *x; + if (boq != -1) + { x = (Trail *) trpt->ostate; + if (x) x->o_pm |= 4; /* not failing */ + } +#endif + truncs++; + if (!warned) + { warned = 1; + printf("error: max search depth too small\n"); + } + if (bounded) + uerror("depth limit reached"); + continue; + } +#ifndef NOREDUCE + if (boq == -1 && !(trpt->tau&8) && nps == 0) + for (II = now._nr_pr-1; II >= BASE; II -= 1) + { +Pickup: this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + if (trans[ot][tt]->atom & 8) + { t = trans[ot][tt]; + if (t->qu[0] != 0) + { Ccheck++; + if (!q_cond(II, t)) + continue; + Cholds++; + } + From = To = II; + trpt->tau |= 32; /* preselect marker */ +#ifdef DEBUG + printf("%3d: proc %d PreSelected (tau=%d)\n", + depth, II, trpt->tau); +#endif + goto MainLoop; + } } + trpt->tau &= ~32; +#endif +Repeat: + if (trpt->tau&8) /* atomic */ + { From = To = (short ) trpt->pr; + nlinks++; + } else + { From = now._nr_pr-1; + To = BASE; + } +MainLoop: + _n = _m = 0; + for (II = From; II >= To; II -= 1) + { + this = (((uchar *)&now)+proc_offset[II]); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; +#if SYNC + /* no rendezvous with same proc */ + if (boq != -1 && trpt->pr == II) continue; +#endif + ntrpt->pr = (uchar) II; + ntrpt->st = tt; + trpt->o_pm &= ~1; /* no move yet */ +#ifdef EVENT_TRACE + trpt->o_event = now._event; +#endif +#ifdef HAS_PROVIDED + if (!provided(II, ot, tt, t)) continue; +#endif +#ifdef HAS_UNLESS + E_state = 0; +#endif + for (t = trans[ot][tt]; t; t = t->nxt) + { +#ifdef HAS_UNLESS + if (E_state > 0 + && E_state != t->e_trans) + break; +#endif + ntrpt->o_t = t; + + oboq = boq; + + if (!(_m = do_transit(t, II))) + continue; + + trpt->o_pm |= 1; /* we moved */ + (trpt+1)->o_m = _m; /* for unsend */ +#ifdef PEG + peg[t->forw]++; +#endif +#ifdef CHECK + printf("%3d: proc %d exec %d, ", + depth, II, t->forw); + printf("%d to %d, %s %s %s", + tt, t->st, t->tp, + (t->atom&2)?"atomic":"", + (boq != -1)?"rendez-vous":""); +#ifdef HAS_UNLESS + if (t->e_trans) + printf(" (escapes to state %d)", t->st); +#endif + printf(" %saccepting [tau=%d]\n", + (trpt->o_pm&2)?"":"non-", trpt->tau); +#endif +#ifdef HAS_UNLESS + E_state = t->e_trans; +#if SYNC>0 + if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */)) + { fprintf(efd, "error: the use of rendezvous stmnt in the escape clause\n"); + fprintf(efd, " of an unless stmnt is not compatible with -DBFS\n"); + pan_exit(1); + } +#endif +#endif + if (t->st > 0) ((P0 *)this)->_p = t->st; + + /* ptr to pred: */ ntrpt->ostate = (struct H_el *) otrpt; + ntrpt->st = tt; + if (boq == -1 && (t->atom&2)) /* atomic */ + ntrpt->tau = 8; /* record for next move */ + else + ntrpt->tau = 0; + + store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq); +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif + + /* undo move and continue */ + trpt++; /* this is where ovals and ipt are set */ + do_reverse(t, II, _m); /* restore now. */ + trpt--; +#ifdef CHECK + #if NCORE>1 + enter_critical(GLOBAL_LOCK); /* in verbose mode only */ + printf("cpu%d: ", core_id); + #endif + printf("%3d: proc %d ", depth, II); + printf("reverses %d, %d to %d,", + t->forw, tt, t->st); + printf(" %s [abit=%d,adepth=%d,", + t->tp, now._a_t, A_depth); + printf("tau=%d,%d]\n", + trpt->tau, (trpt-1)->tau); + #if NCORE>1 + leave_critical(GLOBAL_LOCK); + #endif +#endif + reached[ot][t->st] = 1; + reached[ot][tt] = 1; + + ((P0 *)this)->_p = tt; + _n |= _m; + } } +#ifndef NOREDUCE + /* preselected - no succ definitely outside stack */ + if ((trpt->tau&32) && !(trpt->tau&64)) + { From = now._nr_pr-1; To = BASE; +#ifdef DEBUG + cpu_printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, (int) _n, trpt->tau); +#endif + _n = 0; trpt->tau &= ~32; + if (II >= BASE) + goto Pickup; + goto MainLoop; + } + trpt->tau &= ~(32|64); +#endif + if (_n != 0) + continue; +#ifdef DEBUG + printf("%3d: no move [II=%d, tau=%d, boq=%d, _nr_pr=%d]\n", + depth, II, trpt->tau, boq, now._nr_pr); +#endif + if (boq != -1) + { failedrv++; + x = (Trail *) trpt->ostate; /* pre-rv state */ + if (!x) continue; /* root state */ + if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */ + { x->o_pm |= 8; /* mark failure */ + this = (((uchar *)&now)+proc_offset[otrpt->pr]); +#ifdef VERBOSE + printf("\treset state of %d from %d to %d\n", + otrpt->pr, ((P0 *)this)->_p, otrpt->st); +#endif + ((P0 *)this)->_p = otrpt->st; + unsend(boq); /* retract rv offer */ + boq = -1; + push_bfs(x, x->o_tt); +#ifdef VERBOSE + printf("failed rv, repush with %d\n", x->o_pm); +#endif + } +#ifdef VERBOSE + else printf("failed rv, tau at parent: %d\n", x->tau); +#endif + } else if (now._nr_pr > 0) + { + if ((trpt->tau&8)) /* atomic */ + { trpt->tau &= ~(1|8); /* 1=timeout, 8=atomic */ +#ifdef DEBUG + printf("%3d: atomic step proc %d blocks\n", + depth, II+1); +#endif + goto Repeat; + } + + if (!(trpt->tau&1)) /* didn't try timeout yet */ + { trpt->tau |= 1; +#ifdef DEBUG + printf("%d: timeout\n", depth); +#endif + goto MainLoop; + } +#ifndef VERI + if (!noends && !a_cycles && !endstate()) + uerror("invalid end state"); +#endif + } } +} + +void +putter(Trail *trpt, int fd) +{ long j; + + if (!trpt) return; + + if (trpt != (Trail *) trpt->ostate) + putter((Trail *) trpt->ostate, fd); + + if (trpt->o_t) + { sprintf(snap, "%d:%d:%d\n", + trcnt++, trpt->pr, trpt->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing %s\n", fnm); + pan_exit(1); + } } +} + +void +nuerror(char *str) +{ int fd = make_trail(); + int j; + + if (fd < 0) return; +#ifdef VERI + sprintf(snap, "-2:%d:-2\n", VERI); + write(fd, snap, strlen(snap)); +#endif +#ifdef MERGED + sprintf(snap, "-4:-4:-4\n"); + write(fd, snap, strlen(snap)); +#endif + trcnt = 1; + putter(trpt, fd); + if (ntrpt->o_t) + { sprintf(snap, "%d:%d:%d\n", + trcnt++, ntrpt->pr, ntrpt->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing %s\n", fnm); + pan_exit(1); + } } + close(fd); + if (errors >= upto && upto != 0) + { wrapup(); + } +} +#endif +#if NCORE>1 +#if defined(WIN32) || defined(WIN64) +#ifndef _CONSOLE + #define _CONSOLE +#endif + #ifdef WIN64 +#undef long + #endif +#include + + #ifdef WIN64 + #define long long long + #endif +#else +#include +#include +#include +#endif + +/* code common to cygwin/linux and win32/win64: */ + +#ifdef VERBOSE + #define VVERBOSE (1) +#else + #define VVERBOSE (0) +#endif + +/* the following values must be larger than 256 and must fit in an int */ +#define QUIT 1024 /* terminate now command */ +#define QUERY 512 /* termination status query message */ +#define QUERY_F 513 /* query failed, cannot quit */ + +#define GN_FRAMES (int) (GWQ_SIZE / (double) sizeof(SM_frame)) +#define LN_FRAMES (int) (LWQ_SIZE / (double) sizeof(SM_frame)) + +#ifndef VMAX + #define VMAX VECTORSZ +#endif +#ifndef PMAX + #define PMAX 64 +#endif +#ifndef QMAX + #define QMAX 64 +#endif + +#if VECTORSZ>32000 + #define OFFT int +#else + #define OFFT short +#endif + +#ifdef SET_SEG_SIZE + /* no longer usefule -- being recomputed for local heap size anyway */ + double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.); +#else + double SEG_SIZE = (1048576.*1024.); /* 1GB default shared memory pool segments */ +#endif + +double LWQ_SIZE = 0.; /* initialized in main */ + +#ifdef SET_WQ_SIZE + #ifdef NGQ + #warning SET_WQ_SIZE applies to global queue -- ignored + double GWQ_SIZE = 0.; + #else + double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.); + /* must match the value in pan_proxy.c, if used */ + #endif +#else + #ifdef NGQ + double GWQ_SIZE = 0.; + #else + double GWQ_SIZE = (128.*1048576.); /* 128 MB default queue sizes */ + #endif +#endif + +/* Crash Detection Parameters */ +#ifndef ONESECOND + #define ONESECOND (1<<25) +#endif +#ifndef SHORT_T + #define SHORT_T (0.1) +#endif +#ifndef LONG_T + #define LONG_T (600) +#endif + +double OneSecond = (double) (ONESECOND); /* waiting for a free slot -- checks crash */ +double TenSeconds = 10. * (ONESECOND); /* waiting for a lock -- check for a crash */ + +/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */ +double Delay = ((double) SHORT_T) * (ONESECOND); /* termination detection trigger */ +double OneHour = ((double) LONG_T) * (ONESECOND); /* timeout termination detection */ + +typedef struct SM_frame SM_frame; +typedef struct SM_results SM_results; +typedef struct sh_Allocater sh_Allocater; + +struct SM_frame { /* about 6K per slot */ + volatile int m_vsize; /* 0 means free slot */ + volatile int m_boq; /* >500 is a control message */ +#ifdef FULL_TRAIL + volatile struct Stack_Tree *m_stack; /* ptr to previous state */ +#endif + volatile uchar m_tau; + volatile uchar m_o_pm; + volatile int nr_handoffs; /* to compute real_depth */ + volatile char m_now [VMAX]; + volatile char m_Mask [(VMAX + 7)/8]; + volatile OFFT m_p_offset[PMAX]; + volatile OFFT m_q_offset[QMAX]; + volatile uchar m_p_skip [PMAX]; + volatile uchar m_q_skip [QMAX]; +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + volatile uchar m_c_stack [StackSize]; +#endif +}; + +int proxy_pid; /* id of proxy if nonzero -- receive half */ +int store_proxy_pid; +short remote_party; +int proxy_pid_snd; /* id of proxy if nonzero -- send half */ +char o_cmdline[512]; /* to pass options to children */ + +int iamin[CS_NR+NCORE]; /* non-shared */ + +#if defined(WIN32) || defined(WIN64) +int tas(volatile LONG *); + +HANDLE proxy_handle_snd; /* for Windows Create and Terminate */ + +struct sh_Allocater { /* shared memory for states */ + volatile char *dc_arena; /* to allocate states from */ + volatile long pattern; /* to detect overruns */ + volatile long dc_size; /* nr of bytes left */ + volatile void *dc_start; /* where memory segment starts */ + volatile void *dc_id; /* to attach, detach, remove shared memory segments */ + volatile sh_Allocater *nxt; /* linked list of pools */ +}; +DWORD worker_pids[NCORE]; /* root mem of pids of all workers created */ +HANDLE worker_handles[NCORE]; /* for windows Create and Terminate */ +void * shmid [NR_QS]; /* return value from CreateFileMapping */ +void * shmid_M; /* shared mem for state allocation in hashtable */ + +#ifdef SEP_STATE + void *shmid_X; +#else + void *shmid_S; /* shared bitstate arena or hashtable */ +#endif +#else +int tas(volatile int *); + +struct sh_Allocater { /* shared memory for states */ + volatile char *dc_arena; /* to allocate states from */ + volatile long pattern; /* to detect overruns */ + volatile long dc_size; /* nr of bytes left */ + volatile char *dc_start; /* where memory segment starts */ + volatile int dc_id; /* to attach, detach, remove shared memory segments */ + volatile sh_Allocater *nxt; /* linked list of pools */ +}; + +int worker_pids[NCORE]; /* root mem of pids of all workers created */ +int shmid [NR_QS]; /* return value from shmget */ +int nibis = 0; /* set after shared mem has been released */ +int shmid_M; /* shared mem for state allocation in hashtable */ +#ifdef SEP_STATE + long shmid_X; +#else + int shmid_S; /* shared bitstate arena or hashtable */ + volatile sh_Allocater *first_pool; /* of shared state memory */ + volatile sh_Allocater *last_pool; +#endif +#endif + +struct SM_results { /* for shuttling back final stats */ + volatile int m_vsize; /* avoid conflicts with frames */ + volatile int m_boq; /* these 2 fields are not written in record_info */ + /* probably not all fields really need to be volatile */ + volatile double m_memcnt; + volatile double m_nstates; + volatile double m_truncs; + volatile double m_truncs2; + volatile double m_nShadow; + volatile double m_nlinks; + volatile double m_ngrabs; + volatile double m_nlost; + volatile double m_hcmp; + volatile double m_frame_wait; + volatile int m_hmax; + volatile int m_svmax; + volatile int m_smax; + volatile int m_mreached; + volatile int m_errors; + volatile int m_VMAX; + volatile short m_PMAX; + volatile short m_QMAX; + volatile uchar m_R; /* reached info for all proctypes */ +}; + +int core_id = 0; /* internal process nr, to know which q to use */ +unsigned long nstates_put = 0; /* statistics */ +unsigned long nstates_get = 0; +int query_in_progress = 0; /* termination detection */ + +double free_wait = 0.; /* waiting for a free frame */ +double frame_wait = 0.; /* waiting for a full frame */ +double lock_wait = 0.; /* waiting for access to cs */ +double glock_wait[3]; /* waiting for access to global lock */ + +char *sprefix = "rst"; +uchar was_interrupted, issued_kill, writing_trail; + +static SM_frame cur_Root; /* current root, to be safe with error trails */ + +SM_frame *m_workq [NR_QS]; /* per cpu work queues + global q */ +char *shared_mem[NR_QS]; /* return value from shmat */ +#ifdef SEP_HEAP +char *my_heap; +long my_size; +#endif +volatile sh_Allocater *dc_shared; /* assigned at initialization */ + +static int vmax_seen, pmax_seen, qmax_seen; +static double gq_tries, gq_hasroom, gq_hasnoroom; + +volatile int *prfree; +volatile int *prfull; +volatile int *prcnt; +volatile int *prmax; + +volatile int *sh_lock; /* mutual exclusion locks - in shared memory */ +volatile double *is_alive; /* to detect when processes crash */ +volatile int *grfree, *grfull, *grcnt, *grmax; /* access to shared global q */ +volatile double *gr_readmiss, *gr_writemiss; +static int lrfree; /* used for temporary recording of slot */ +static int dfs_phase2; + +void mem_put(int); /* handoff state to other cpu */ +void mem_put_acc(void); /* liveness mode */ +void mem_get(void); /* get state from work queue */ +void sudden_stop(char *); +#if 0 +void enter_critical(int); +void leave_critical(int); +#endif + +void +record_info(SM_results *r) +{ int i; + uchar *ptr; + +#ifdef SEP_STATE + if (0) + { cpu_printf("nstates %g nshadow %g -- memory %-6.3f Mb\n", + nstates, nShadow, memcnt/(1048576.)); + } + r->m_memcnt = 0; +#else + #ifdef BITSTATE + r->m_memcnt = 0; /* it's shared */ + #endif + r->m_memcnt = memcnt; +#endif + if (a_cycles && core_id == 1) + { r->m_nstates = nstates; + r->m_nShadow = nstates; + } else + { r->m_nstates = nstates; + r->m_nShadow = nShadow; + } + r->m_truncs = truncs; + r->m_truncs2 = truncs2; + r->m_nlinks = nlinks; + r->m_ngrabs = ngrabs; + r->m_nlost = nlost; + r->m_hcmp = hcmp; + r->m_frame_wait = frame_wait; + r->m_hmax = hmax; + r->m_svmax = svmax; + r->m_smax = smax; + r->m_mreached = mreached; + r->m_errors = errors; + r->m_VMAX = vmax_seen; + r->m_PMAX = (short) pmax_seen; + r->m_QMAX = (short) qmax_seen; + ptr = (uchar *) &(r->m_R); + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar)); + ptr += NrStates[i]*sizeof(uchar); + } + if (verbose>1) + { cpu_printf("Put Results nstates %g (sz %d)\n", nstates, ptr - &(r->m_R)); + } +} + +void snapshot(void); + +void +retrieve_info(SM_results *r) +{ int i, j; + volatile uchar *ptr; + + snapshot(); /* for a final report */ + + enter_critical(GLOBAL_LOCK); +#ifdef SEP_HEAP + if (verbose) + { printf("cpu%d: local heap-left %ld KB (%d MB)\n", + core_id, (int) (my_size/1024), (int) (my_size/1048576)); + } +#endif + if (verbose && core_id == 0) + { printf("qmax: "); + for (i = 0; i < NCORE; i++) + { printf("%d ", prmax[i]); + } +#ifndef NGQ + printf("G: %d", *grmax); +#endif + printf("\n"); + } + leave_critical(GLOBAL_LOCK); + + memcnt += r->m_memcnt; + nstates += r->m_nstates; + nShadow += r->m_nShadow; + truncs += r->m_truncs; + truncs2 += r->m_truncs2; + nlinks += r->m_nlinks; + ngrabs += r->m_ngrabs; + nlost += r->m_nlost; + hcmp += r->m_hcmp; + /* frame_wait += r->m_frame_wait; */ + errors += r->m_errors; + + if (hmax < r->m_hmax) hmax = r->m_hmax; + if (svmax < r->m_svmax) svmax = r->m_svmax; + if (smax < r->m_smax) smax = r->m_smax; + if (mreached < r->m_mreached) mreached = r->m_mreached; + + if (vmax_seen < r->m_VMAX) vmax_seen = r->m_VMAX; + if (pmax_seen < (int) r->m_PMAX) pmax_seen = (int) r->m_PMAX; + if (qmax_seen < (int) r->m_QMAX) qmax_seen = (int) r->m_QMAX; + + ptr = &(r->m_R); + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { for (j = 0; j < NrStates[i]; j++) + { if (*(ptr + j) != 0) + { reached[i][j] = 1; + } } + ptr += NrStates[i]*sizeof(uchar); + } + if (verbose>1) + { cpu_printf("Got Results (%d)\n", ptr - &(r->m_R)); + snapshot(); + } +} + +#if !defined(WIN32) && !defined(WIN64) +static void +rm_shared_segments(void) +{ int m; + volatile sh_Allocater *nxt_pool; + /* + * mark all shared memory segments for removal + * the actual removes wont happen intil last process dies or detaches + * the shmctl calls can return -1 if not all procs have detached yet + */ + for (m = 0; m < NR_QS; m++) /* +1 for global q */ + { if (shmid[m] != -1) + { (void) shmctl(shmid[m], IPC_RMID, NULL); + } } +#ifdef SEP_STATE + if (shmid_M != -1) + { (void) shmctl(shmid_M, IPC_RMID, NULL); + } +#else + if (shmid_S != -1) + { (void) shmctl(shmid_S, IPC_RMID, NULL); + } + for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool) + { shmid_M = (int) (last_pool->dc_id); + nxt_pool = last_pool->nxt; /* as a pre-caution only */ + if (shmid_M != -1) + { (void) shmctl(shmid_M, IPC_RMID, NULL); + } } +#endif +} +#endif + +void +sudden_stop(char *s) +{ char b[64]; + int i; + + printf("cpu%d: stop - %s\n", core_id, s); +#if !defined(WIN32) && !defined(WIN64) + if (proxy_pid != 0) + { rm_shared_segments(); + } +#endif + if (search_terminated != NULL) + { if (*search_terminated != 0) + { if (verbose) + { printf("cpu%d: termination initiated (%d)\n", + core_id, *search_terminated); + } + } else + { if (verbose) + { printf("cpu%d: initiated termination\n", core_id); + } + *search_terminated |= 8; /* sudden_stop */ + } + if (core_id == 0) + { if (((*search_terminated) & 4) /* uerror in one of the cpus */ + && !((*search_terminated) & (8|32|128|256))) /* abnormal stop */ + { if (errors == 0) errors++; /* we know there is at least 1 */ + } + wrapup(); /* incomplete stats, but at least something */ + } + return; + } /* else: should rarely happen, take more drastic measures */ + + if (core_id == 0) /* local root process */ + { for (i = 1; i < NCORE; i++) /* not for 0 of course */ + { +#if defined(WIN32) || defined(WIN64) + DWORD dwExitCode = 0; + GetExitCodeProcess(worker_handles[i], &dwExitCode); + if (dwExitCode == STILL_ACTIVE) + { TerminateProcess(worker_handles[i], 0); + } + printf("cpu0: terminate %d %d\n", + worker_pids[i], (dwExitCode == STILL_ACTIVE)); +#else + sprintf(b, "kill -%d %d", SIGKILL, worker_pids[i]); + system(b); /* if this is a proxy: receive half */ + printf("cpu0: %s\n", b); +#endif + } + issued_kill++; + } else + { /* on WIN32/WIN64 -- these merely kills the root process... */ + if (was_interrupted == 0) + { sprintf(b, "kill -%d %d", SIGINT, worker_pids[0]); + system(b); /* warn the root process */ + printf("cpu%d: %s\n", core_id, b); + issued_kill++; + } } +} + +#define iam_alive() is_alive[core_id]++ + +extern int crash_test(double); +extern void crash_reset(void); + +int +someone_crashed(int wait_type) +{ static double last_value = 0.0; + static int count = 0; + + if (search_terminated == NULL + || *search_terminated != 0) + { + if (!(*search_terminated & (8|32|128|256))) + { if (count++ < 100*NCORE) + { return 0; + } } + return 1; + } + /* check left neighbor only */ + if (last_value == is_alive[(core_id + NCORE - 1) % NCORE]) + { if (count++ >= 100) /* to avoid unnecessary checks */ + { return 1; + } + return 0; + } + last_value = is_alive[(core_id + NCORE - 1) % NCORE]; + count = 0; + crash_reset(); + return 0; +} + +void +sleep_report(void) +{ + enter_critical(GLOBAL_LOCK); + if (verbose) + { +#ifdef NGQ + printf("cpu%d: locks: global %g\tother %g\t", + core_id, glock_wait[0], lock_wait - glock_wait[0]); +#else + printf("cpu%d: locks: GL %g, RQ %g, WQ %g, HT %g\t", + core_id, glock_wait[0], glock_wait[1], glock_wait[2], + lock_wait - glock_wait[0] - glock_wait[1] - glock_wait[2]); +#endif + printf("waits: states %g slots %g\n", frame_wait, free_wait); +#ifndef NGQ + printf("cpu%d: gq [tries %g, room %g, noroom %g]\n", core_id, gq_tries, gq_hasroom, gq_hasnoroom); + if (core_id == 0 && (*gr_readmiss >= 1.0 || *gr_readmiss >= 1.0 || *grcnt != 0)) + printf("cpu0: gq [readmiss: %g, writemiss: %g cnt %d]\n", *gr_readmiss, *gr_writemiss, *grcnt); +#endif + } + if (free_wait > 1000000.) + #ifndef NGQ + if (!a_cycles) + { printf("hint: this search may be faster with a larger work-queue\n"); + printf(" (-DSET_WQ_SIZE=N with N>%g), and/or with -DUSE_DISK\n", + GWQ_SIZE/sizeof(SM_frame)); + printf(" or with a larger value for -zN (N>%d)\n", z_handoff); + #else + { printf("hint: this search may be faster if compiled without -DNGQ, with -DUSE_DISK, "); + printf("or with a larger -zN (N>%d)\n", z_handoff); + #endif + } + leave_critical(GLOBAL_LOCK); +} + +#ifndef MAX_DSK_FILE + #define MAX_DSK_FILE 1000000 /* default is max 1M states per file */ +#endif + +void +multi_usage(FILE *fd) +{ static int warned = 0; + if (warned > 0) { return; } else { warned++; } + fprintf(fd, "\n"); + fprintf(fd, "Defining multi-core mode:\n\n"); + fprintf(fd, " -DDUAL_CORE --> same as -DNCORE=2\n"); + fprintf(fd, " -DQUAD_CORE --> same as -DNCORE=4\n"); + fprintf(fd, " -DNCORE=N --> enables multi_core verification if N>1\n"); + fprintf(fd, "\n"); + fprintf(fd, "Additional directives supported in multi-core mode:\n\n"); + fprintf(fd, " -DSEP_STATE --> forces separate statespaces instead of a single shared state space\n"); + fprintf(fd, " -DNUSE_DISK --> use disk for storing states when a work queue overflows\n"); + fprintf(fd, " -DMAX_DSK_FILE --> max nr of states per diskfile (%d)\n", MAX_DSK_FILE); + fprintf(fd, " -DFULL_TRAIL --> support full error trails (increases memory use)\n"); + fprintf(fd, "\n"); + fprintf(fd, "More advanced use (should rarely need changing):\n\n"); + fprintf(fd, " To change the nr of states that can be stored in the global queue\n"); + fprintf(fd, " (lower numbers allow for more states to be stored, prefer multiples of 8):\n"); + fprintf(fd, " -DVMAX=N --> upperbound on statevector for handoffs (N=%d)\n", VMAX); + fprintf(fd, " -DPMAX=N --> upperbound on nr of procs (default: N=%d)\n", PMAX); + fprintf(fd, " -DQMAX=N --> upperbound on nr of channels (default: N=%d)\n", QMAX); + fprintf(fd, "\n"); + fprintf(fd, " To set the total amount of memory reserved for the global workqueue:\n"); + fprintf(fd, " -DSET_WQ_SIZE=N --> default: N=128 (defined in MBytes)\n\n"); + fprintf(fd, " To force the use of a single global heap, instead of separate heaps:\n"); + fprintf(fd, " -DGLOB_HEAP\n"); + fprintf(fd, "\n"); + fprintf(fd, " To define a fct to initialize data before spawning processes (use quotes):\n"); + fprintf(fd, " \"-DC_INIT=fct()\"\n"); + fprintf(fd, "\n"); + fprintf(fd, " Timer settings for termination and crash detection:\n"); + fprintf(fd, " -DSHORT_T=N --> timeout for termination detection trigger (N=%g)\n", (double) SHORT_T); + fprintf(fd, " -DLONG_T=N --> timeout for giving up on termination detection (N=%g)\n", (double) LONG_T); + fprintf(fd, " -DONESECOND --> (1<<29) --> timeout waiting for a free slot -- to check for crash\n"); + fprintf(fd, " -DT_ALERT --> collect stats on crash alert timeouts\n\n"); + fprintf(fd, "Help with Linux/Windows/Cygwin configuration for multi-core:\n"); + fprintf(fd, " http://spinroot.com/spin/multicore/V5_Readme.html\n"); + fprintf(fd, "\n"); +} +#if NCORE>1 && defined(FULL_TRAIL) +typedef struct Stack_Tree { + uchar pr; /* process that made transition */ + T_ID t_id; /* id of transition */ + volatile struct Stack_Tree *prv; /* backward link towards root */ +} Stack_Tree; + +struct H_el *grab_shared(int); +volatile Stack_Tree **stack_last; /* in shared memory */ +char *stack_cache = NULL; /* local */ +int nr_cached = 0; /* local */ + +#ifndef CACHE_NR + #define CACHE_NR 1024 +#endif + +volatile Stack_Tree * +stack_prefetch(void) +{ volatile Stack_Tree *st; + + if (nr_cached == 0) + { stack_cache = (char *) grab_shared(CACHE_NR * sizeof(Stack_Tree)); + nr_cached = CACHE_NR; + } + st = (volatile Stack_Tree *) stack_cache; + stack_cache += sizeof(Stack_Tree); + nr_cached--; + return st; +} + +void +Push_Stack_Tree(short II, T_ID t_id) +{ volatile Stack_Tree *st; + + st = (volatile Stack_Tree *) stack_prefetch(); + st->pr = II; + st->t_id = t_id; + st->prv = (Stack_Tree *) stack_last[core_id]; + stack_last[core_id] = st; +} + +void +Pop_Stack_Tree(void) +{ volatile Stack_Tree *cf = stack_last[core_id]; + + if (cf) + { stack_last[core_id] = cf->prv; + } else if (nr_handoffs * z_handoff + depth > 0) + { printf("cpu%d: error pop_stack_tree (depth %d)\n", + core_id, depth); + } +} +#endif + +void +e_critical(int which) +{ double cnt_start; + + if (readtrail || iamin[which] > 0) + { if (!readtrail && verbose) + { printf("cpu%d: Double Lock on %d (now %d)\n", + core_id, which, iamin[which]+1); + fflush(stdout); + } + iamin[which]++; /* local variable */ + return; + } + + cnt_start = lock_wait; + + while (sh_lock != NULL) /* as long as we have shared memory */ + { int r = tas(&sh_lock[which]); + if (r == 0) + { iamin[which] = 1; + return; /* locked */ + } + + lock_wait++; +#ifndef NGQ + if (which < 3) { glock_wait[which]++; } +#else + if (which == 0) { glock_wait[which]++; } +#endif + iam_alive(); + + if (lock_wait - cnt_start > TenSeconds) + { printf("cpu%d: lock timeout on %d\n", core_id, which); + cnt_start = lock_wait; + if (someone_crashed(1)) + { sudden_stop("lock timeout"); + pan_exit(1); + } } } +} + +void +x_critical(int which) +{ + if (iamin[which] != 1) + { if (iamin[which] > 1) + { iamin[which]--; /* this is thread-local - no races on this one */ + if (!readtrail && verbose) + { printf("cpu%d: Partial Unlock on %d (%d more needed)\n", + core_id, which, iamin[which]); + fflush(stdout); + } + return; + } else /* iamin[which] <= 0 */ + { if (!readtrail) + { printf("cpu%d: Invalid Unlock iamin[%d] = %d\n", + core_id, which, iamin[which]); + fflush(stdout); + } + return; + } } + + if (sh_lock != NULL) + { iamin[which] = 0; + sh_lock[which] = 0; /* unlock */ + } +} + +void +#if defined(WIN32) || defined(WIN64) +start_proxy(char *s, DWORD r_pid) +#else +start_proxy(char *s, int r_pid) +#endif +{ char Q_arg[16], Z_arg[16], Y_arg[16]; + char *args[32], *ptr; + int argcnt = 0; + + sprintf(Q_arg, "-Q%d", getpid()); + sprintf(Y_arg, "-Y%d", r_pid); + sprintf(Z_arg, "-Z%d", proxy_pid /* core_id */); + + args[argcnt++] = "proxy"; + args[argcnt++] = s; /* -r or -s */ + args[argcnt++] = Q_arg; + args[argcnt++] = Z_arg; + args[argcnt++] = Y_arg; + + if (strlen(o_cmdline) > 0) + { ptr = o_cmdline; /* assume args separated by spaces */ + do { args[argcnt++] = ptr++; + if ((ptr = strchr(ptr, ' ')) != NULL) + { while (*ptr == ' ') + { *ptr++ = '\0'; + } + } else + { break; + } + } while (argcnt < 31); + } + args[argcnt] = NULL; +#if defined(WIN32) || defined(WIN64) + execvp("pan_proxy", args); /* no return */ +#else + execvp("./pan_proxy", args); /* no return */ +#endif + Uerror("pan_proxy exec failed"); +} +/*** end of common code fragment ***/ + +#if !defined(WIN32) && !defined(WIN64) +void +init_shm(void) /* initialize shared work-queues - linux/cygwin */ +{ key_t key[NR_QS]; + int n, m; + int must_exit = 0; + + if (core_id == 0 && verbose) + { printf("cpu0: step 3: allocate shared workqueues %g MB\n", + ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.) ); + } + for (m = 0; m < NR_QS; m++) /* last q is the global q */ + { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE; + key[m] = ftok(PanSource, m+1); + if (key[m] == -1) + { perror("ftok shared queues"); must_exit = 1; break; + } + + if (core_id == 0) /* root creates */ + { /* check for stale copy */ + shmid[m] = shmget(key[m], (size_t) qsize, 0600); + if (shmid[m] != -1) /* yes there is one; remove it */ + { printf("cpu0: removing stale q%d, status: %d\n", + m, shmctl(shmid[m], IPC_RMID, NULL)); + } + shmid[m] = shmget(key[m], (size_t) qsize, 0600|IPC_CREAT|IPC_EXCL); + memcnt += qsize; + } else /* workers attach */ + { shmid[m] = shmget(key[m], (size_t) qsize, 0600); + /* never called, since we create shm *before* we fork */ + } + if (shmid[m] == -1) + { perror("shmget shared queues"); must_exit = 1; break; + } + + shared_mem[m] = (char *) shmat(shmid[m], (void *) 0, 0); /* attach */ + if (shared_mem[m] == (char *) -1) + { fprintf(stderr, "error: cannot attach shared wq %d (%d Mb)\n", + m+1, (int) (qsize/(1048576.))); + perror("shmat shared queues"); must_exit = 1; break; + } + + m_workq[m] = (SM_frame *) shared_mem[m]; + if (core_id == 0) + { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES; + for (n = 0; n < nframes; n++) + { m_workq[m][n].m_vsize = 0; + m_workq[m][n].m_boq = 0; + } } } + + if (must_exit) + { rm_shared_segments(); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); /* calls cleanup_shm */ + } +} + +static uchar * +prep_shmid_S(size_t n) /* either sets SS or H_tab, linux/cygwin */ +{ char *rval; +#ifndef SEP_STATE + key_t key; + + if (verbose && core_id == 0) + { + #ifdef BITSTATE + printf("cpu0: step 1: allocate shared bitstate %g Mb\n", + (double) n / (1048576.)); + #else + printf("cpu0: step 1: allocate shared hastable %g Mb\n", + (double) n / (1048576.)); + #endif + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu0: S %8g + %d Kb exceeds memory limit of %8g Mb\n", + memcnt/1024., n/1024, memlim/(1048576.)); + printf("cpu0: insufficient memory -- aborting\n"); + exit(1); + } + #endif + + key = ftok(PanSource, NCORE+2); /* different from queues */ + if (key == -1) + { perror("ftok shared bitstate or hashtable"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + if (core_id == 0) /* root */ + { shmid_S = shmget(key, n, 0600); + if (shmid_S != -1) + { printf("cpu0: removing stale segment, status: %d\n", + shmctl(shmid_S, IPC_RMID, NULL)); + } + shmid_S = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL); + memcnt += (double) n; + } else /* worker */ + { shmid_S = shmget(key, n, 0600); + } + if (shmid_S == -1) + { perror("shmget shared bitstate or hashtable too large?"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + rval = (char *) shmat(shmid_S, (void *) 0, 0); /* attach */ + if ((char *) rval == (char *) -1) + { perror("shmat shared bitstate or hashtable"); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } +#else + rval = (char *) emalloc(n); +#endif + return (uchar *) rval; +} + +#define TRY_AGAIN 1 +#define NOT_AGAIN 0 + +static char shm_prep_result; + +static uchar * +prep_state_mem(size_t n) /* sets memory arena for states linux/cygwin */ +{ char *rval; + key_t key; + static int cnt = 3; /* start larger than earlier ftok calls */ + + shm_prep_result = NOT_AGAIN; /* default */ + if (verbose && core_id == 0) + { printf("cpu0: step 2+: pre-allocate memory arena %d of %6.2g Mb\n", + cnt-3, (double) n / (1048576.)); + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu0: error: M %.0f + %.0f Kb exceeds memory limit of %.0f Mb\n", + memcnt/1024.0, (double) n/1024.0, memlim/(1048576.)); + return NULL; + } + #endif + + key = ftok(PanSource, NCORE+cnt); cnt++; + if (key == -1) + { perror("ftok T"); + printf("pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + if (core_id == 0) + { shmid_M = shmget(key, n, 0600); + if (shmid_M != -1) + { printf("cpu0: removing stale memory segment %d, status: %d\n", + cnt-3, shmctl(shmid_M, IPC_RMID, NULL)); + } + shmid_M = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL); + /* memcnt += (double) n; -- only amount actually used is counted */ + } else + { shmid_M = shmget(key, n, 0600); + + } + if (shmid_M == -1) + { if (verbose) + { printf("error: failed to get pool of shared memory %d of %.0f Mb\n", + cnt-3, ((double)n)/(1048576.)); + perror("state mem"); + printf("pan: check './pan --' for usage details\n"); + } + shm_prep_result = TRY_AGAIN; + return NULL; + } + rval = (char *) shmat(shmid_M, (void *) 0, 0); /* attach */ + + if ((char *) rval == (char *) -1) + { printf("cpu%d error: failed to attach pool of shared memory %d of %.0f Mb\n", + core_id, cnt-3, ((double)n)/(1048576.)); + perror("state mem"); + return NULL; + } + return (uchar *) rval; +} + +void +init_HT(unsigned long n) /* cygwin/linux version */ +{ volatile char *x; + double get_mem; +#ifndef SEP_STATE + volatile char *dc_mem_start; + double need_mem, got_mem = 0.; +#endif + +#ifdef SEP_STATE + #ifndef MEMLIM + if (verbose) + { printf("cpu0: steps 0,1: no -DMEMLIM set\n"); + } + #else + if (verbose) + { printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n", + MEMLIM, ((double)n/(1048576.)), (((double) NCORE * LWQ_SIZE) + GWQ_SIZE) /(1048576.) ); + } + #endif + get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *) + 4*sizeof(void *) + 2*sizeof(double); + /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */ + get_mem += 4 * NCORE * sizeof(void *); /* prfree, prfull, prcnt, prmax */ + #ifdef FULL_TRAIL + get_mem += (NCORE) * sizeof(Stack_Tree *); /* NCORE * stack_last */ + #endif + x = (volatile char *) prep_state_mem((size_t) get_mem); /* work queues and basic structs */ + shmid_X = (long) x; + if (x == NULL) + { printf("cpu0: could not allocate shared memory, see ./pan --\n"); + exit(1); + } + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + + #ifndef BITSTATE + H_tab = (struct H_el **) emalloc(n); + #endif +#else + #ifndef MEMLIM + #warning MEMLIM not set + #define MEMLIM (2048) + #endif + + if (core_id == 0 && verbose) + { printf("cpu0: step 0: -DMEMLIM=%d Mb minus hashtable+workqs (%g + %g Mb) leaves %g Mb\n", + MEMLIM, ((double)n/(1048576.)), (NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.), + (memlim - memcnt - (double) n - (NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.)); + } + #ifndef BITSTATE + H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */ + #endif + need_mem = memlim - memcnt - ((double) NCORE * LWQ_SIZE) - GWQ_SIZE; + if (need_mem <= 0.) + { Uerror("internal error -- shared state memory"); + } + + if (core_id == 0 && verbose) + { printf("cpu0: step 2: pre-allocate shared state memory %g Mb\n", + need_mem/(1048576.)); + } +#ifdef SEP_HEAP + SEG_SIZE = need_mem / NCORE; + if (verbose && core_id == 0) + { printf("cpu0: setting segsize to %6g MB\n", + SEG_SIZE/(1048576.)); + } + #if defined(CYGWIN) || defined(__CYGWIN__) + if (SEG_SIZE > 512.*1024.*1024.) + { printf("warning: reducing SEG_SIZE of %g MB to 512MB (exceeds max for Cygwin)\n", + SEG_SIZE/(1024.*1024.)); + SEG_SIZE = 512.*1024.*1024.; + } + #endif +#endif + mem_reserved = need_mem; + while (need_mem > 1024.) + { get_mem = need_mem; +shm_more: + if (get_mem > (double) SEG_SIZE) + { get_mem = (double) SEG_SIZE; + } + if (get_mem <= 0.0) break; + + /* for allocating states: */ + x = dc_mem_start = (volatile char *) prep_state_mem((size_t) get_mem); + if (x == NULL) + { if (shm_prep_result == NOT_AGAIN + || first_pool != NULL + || SEG_SIZE < (16. * 1048576.)) + { break; + } + SEG_SIZE /= 2.; + if (verbose) + { printf("pan: lowered segsize to 0.000000\n", SEG_SIZE); + } + if (SEG_SIZE >= 1024.) + { goto shm_more; + } + break; + } + + need_mem -= get_mem; + got_mem += get_mem; + if (first_pool == NULL) + { search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + if (((long)x)&(sizeof(void *)-1)) /* 64-bit word alignment */ + { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); + } + + #ifdef COLLAPSE + ncomps = (unsigned long *) x; + x += (256+2) * sizeof(unsigned long); + #endif + } + + dc_shared = (sh_Allocater *) x; /* must be in shared memory */ + x += sizeof(sh_Allocater); + + if (core_id == 0) /* root only */ + { dc_shared->dc_id = shmid_M; + dc_shared->dc_start = dc_mem_start; + dc_shared->dc_arena = x; + dc_shared->pattern = 1234567; /* protection */ + dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start); + dc_shared->nxt = (long) 0; + + if (last_pool == NULL) + { first_pool = last_pool = dc_shared; + } else + { last_pool->nxt = dc_shared; + last_pool = dc_shared; + } + } else if (first_pool == NULL) + { first_pool = dc_shared; + } } + + if (need_mem > 1024.) + { printf("cpu0: could allocate only %g Mb of shared memory (wanted %g more)\n", + got_mem/(1048576.), need_mem/(1048576.)); + } + + if (!first_pool) + { printf("cpu0: insufficient memory -- aborting.\n"); + exit(1); + } + /* we are still single-threaded at this point, with core_id 0 */ + dc_shared = first_pool; + +#endif +} + + /* Test and Set assembly code */ + + #if defined(i386) || defined(__i386__) || defined(__x86_64__) + int + tas(volatile int *s) /* tested */ + { int r; + __asm__ __volatile__( + "xchgl %0, %1 \n\t" + : "=r"(r), "=m"(*s) + : "0"(1), "m"(*s) + : "memory"); + + return r; + } + #elif defined(__arm__) + int + tas(volatile int *s) /* not tested */ + { int r = 1; + __asm__ __volatile__( + "swpb %0, %0, [%3] \n" + : "=r"(r), "=m"(*s) + : "0"(r), "r"(s)); + + return r; + } + #elif defined(sparc) || defined(__sparc__) + int + tas(volatile int *s) /* not tested */ + { int r = 1; + __asm__ __volatile__( + " ldstub [%2], %0 \n" + : "=r"(r), "=m"(*s) + : "r"(s)); + + return r; + } + #elif defined(ia64) || defined(__ia64__) + /* Intel Itanium */ + int + tas(volatile int *s) /* tested */ + { long int r; + __asm__ __volatile__( + " xchg4 %0=%1,%2 \n" + : "=r"(r), "+m"(*s) + : "r"(1) + : "memory"); + return (int) r; + } + #else + #error missing definition of test and set operation for this platform + #endif + +void +cleanup_shm(int val) +{ volatile sh_Allocater *nxt_pool; + unsigned long cnt = 0; + int m; + + if (nibis != 0) + { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val); + return; + } else + { nibis = 1; + } + if (search_terminated != NULL) + { *search_terminated |= 16; /* cleanup_shm */ + } + + for (m = 0; m < NR_QS; m++) + { if (shmdt((void *) shared_mem[m]) > 0) + { perror("shmdt detaching from shared queues"); + } } + +#ifdef SEP_STATE + if (shmdt((void *) shmid_X) != 0) + { perror("shmdt detaching from shared state memory"); + } +#else + #ifdef BITSTATE + if (SS > 0 && shmdt((void *) SS) != 0) + { if (verbose) + { perror("shmdt detaching from shared bitstate arena"); + } } + #else + if (core_id == 0) + { /* before detaching: */ + for (nxt_pool = dc_shared; nxt_pool != NULL; nxt_pool = nxt_pool->nxt) + { cnt += nxt_pool->dc_size; + } + if (verbose) + { printf("cpu0: done, %ld Mb of shared state memory left\n", + cnt / (long)(1048576)); + } } + + if (shmdt((void *) H_tab) != 0) + { perror("shmdt detaching from shared hashtable"); + } + + for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool) + { nxt_pool = last_pool->nxt; + if (shmdt((void *) last_pool->dc_start) != 0) + { perror("shmdt detaching from shared state memory"); + } } + first_pool = last_pool = NULL; /* precaution */ + #endif +#endif + /* detached from shared memory - so cannot use cpu_printf */ + if (verbose) + { printf("cpu%d: done -- got %d states from queue\n", + core_id, nstates_get); + } +} + +extern void give_up(int); +extern void Read_Queue(int); + +void +mem_get(void) +{ SM_frame *f; + int is_parent; + +#if defined(MA) && !defined(SEP_STATE) + #error MA without SEP_STATE is not supported with multi-core +#endif +#ifdef BFS + #error BFS is not supported with multi-core +#endif +#ifdef SC + #error SC is not supported with multi-core +#endif + init_shm(); /* we are single threaded when this starts */ + + if (core_id == 0 && verbose) + { printf("cpu0: step 4: calling fork()\n"); + } + fflush(stdout); + +/* if NCORE > 1 the child or the parent should fork N-1 more times + * the parent is the only process with core_id == 0 and is_parent > 0 + * the workers have is_parent = 0 and core_id = 1..NCORE-1 + */ + if (core_id == 0) + { worker_pids[0] = getpid(); /* for completeness */ + while (++core_id < NCORE) /* first worker sees core_id = 1 */ + { is_parent = fork(); + if (is_parent == -1) + { Uerror("fork failed"); + } + if (is_parent == 0) /* this is a worker process */ + { if (proxy_pid == core_id) /* always non-zero */ + { start_proxy("-r", 0); /* no return */ + } + goto adapt; /* root process continues spawning */ + } + worker_pids[core_id] = is_parent; + } + /* note that core_id is now NCORE */ + if (proxy_pid > 0 && proxy_pid < NCORE) + { proxy_pid_snd = fork(); + if (proxy_pid_snd == -1) + { Uerror("proxy fork failed"); + } + if (proxy_pid_snd == 0) + { start_proxy("-s", worker_pids[proxy_pid]); /* no return */ + } } /* else continue */ + if (is_parent > 0) + { core_id = 0; /* reset core_id for root process */ + } + } else /* worker */ + { static char db0[16]; /* good for up to 10^6 cores */ + static char db1[16]; +adapt: tprefix = db0; sprefix = db1; + sprintf(tprefix, "cpu%d_trail", core_id); + sprintf(sprefix, "cpu%d_rst", core_id); + memcnt = 0; /* count only additionally allocated memory */ + } + signal(SIGINT, give_up); + + if (proxy_pid == 0) /* not in a cluster setup, pan_proxy must attach */ + { rm_shared_segments(); /* mark all shared segments for removal on exit */ + } + if (verbose) + { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid()); + } +#if defined(SEP_HEAP) && !defined(SEP_STATE) + { int i; + volatile sh_Allocater *ptr; + ptr = first_pool; + for (i = 0; i < NCORE && ptr != NULL; i++) + { if (i == core_id) + { my_heap = (char *) ptr->dc_arena; + my_size = (long) ptr->dc_size; + if (verbose) + cpu_printf("local heap %ld MB\n", my_size/(1048576)); + break; + } + ptr = ptr->nxt; /* local */ + } + if (my_heap == NULL) + { printf("cpu%d: no local heap\n", core_id); + pan_exit(1); + } /* else */ + #if defined(CYGWIN) || defined(__CYGWIN__) + ptr = first_pool; + for (i = 0; i < NCORE && ptr != NULL; i++) + { ptr = ptr->nxt; /* local */ + } + dc_shared = ptr; /* any remainder */ + #else + dc_shared = NULL; /* used all mem for local heaps */ + #endif + } +#endif + if (core_id == 0 && !remote_party) + { new_state(); /* cpu0 explores root */ + if (verbose) + cpu_printf("done with 1st dfs, nstates %g (put %d states), read q\n", + nstates, nstates_put); + dfs_phase2 = 1; + } + Read_Queue(core_id); /* all cores */ + + if (verbose) + { cpu_printf("put %6d states into queue -- got %6d\n", + nstates_put, nstates_get); + } + if (proxy_pid != 0) + { rm_shared_segments(); + } + done = 1; + wrapup(); + exit(0); +} + +#else +int unpack_state(SM_frame *, int); +#endif + +struct H_el * +grab_shared(int n) +{ +#ifndef SEP_STATE + char *rval = (char *) 0; + + if (n == 0) + { printf("cpu%d: grab shared zero\n", core_id); fflush(stdout); + return (struct H_el *) rval; + } else if (n&(sizeof(void *)-1)) + { n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */ + } + +#ifdef SEP_HEAP + /* no locking */ + if (my_heap != NULL && my_size > n) + { rval = my_heap; + my_heap += n; + my_size -= n; + goto done; + } +#endif + + if (!dc_shared) + { sudden_stop("pan: out of memory"); + } + + /* another lock is always already in effect when this is called */ + /* but not always the same lock -- i.e., on different parts of the hashtable */ + enter_critical(GLOBAL_LOCK); /* this must be independently mutex */ +#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64) + { static int noted = 0; + if (!noted) + { noted = 1; + printf("cpu%d: global heap has %ld bytes left, needed %d\n", + core_id, dc_shared?dc_shared->dc_size:0, n); + } } +#endif +#if 0 + if (dc_shared->pattern != 1234567) + { leave_critical(GLOBAL_LOCK); + Uerror("overrun -- memory corruption"); + } +#endif + if (dc_shared->dc_size < n) + { if (verbose) + { printf("Next Pool %g Mb + %d\n", memcnt/(1048576.), n); + } + if (dc_shared->nxt == NULL + || dc_shared->nxt->dc_arena == NULL + || dc_shared->nxt->dc_size < n) + { printf("cpu%d: memcnt %g Mb + wanted %d bytes more\n", + core_id, memcnt / (1048576.), n); + leave_critical(GLOBAL_LOCK); + sudden_stop("out of memory -- aborting"); + wrapup(); /* exits */ + } else + { dc_shared = (sh_Allocater *) dc_shared->nxt; + } } + + rval = (char *) dc_shared->dc_arena; + dc_shared->dc_arena += n; + dc_shared->dc_size -= (long) n; +#if 0 + if (VVERBOSE) + printf("cpu%d grab shared (%d bytes) -- %ld left\n", + core_id, n, dc_shared->dc_size); +#endif + leave_critical(GLOBAL_LOCK); +done: + memset(rval, 0, n); + memcnt += (double) n; + + return (struct H_el *) rval; +#else + return (struct H_el *) emalloc(n); +#endif +} + +SM_frame * +Get_Full_Frame(int n) +{ SM_frame *f; + double cnt_start = frame_wait; + + f = &m_workq[n][prfull[n]]; + while (f->m_vsize == 0) /* await full slot LOCK : full frame */ + { iam_alive(); +#ifndef NGQ + #ifndef SAFETY + if (!a_cycles || core_id != 0) + #endif + if (*grcnt > 0) /* accessed outside lock, but safe even if wrong */ + { enter_critical(GQ_RD); /* gq - read access */ + if (*grcnt > 0) /* could have changed */ + { f = &m_workq[NCORE][*grfull]; /* global q */ + if (f->m_vsize == 0) + { /* writer is still filling the slot */ + *gr_writemiss++; + f = &m_workq[n][prfull[n]]; /* reset */ + } else + { *grfull = (*grfull+1) % (GN_FRAMES); + enter_critical(GQ_WR); + *grcnt = *grcnt - 1; + leave_critical(GQ_WR); + leave_critical(GQ_RD); + return f; + } } + leave_critical(GQ_RD); + } +#endif + if (frame_wait++ - cnt_start > Delay) + { if (0) + { cpu_printf("timeout on q%d -- %u -- query %d\n", + n, f, query_in_progress); + } + return (SM_frame *) 0; /* timeout */ + } } + iam_alive(); + if (VVERBOSE) cpu_printf("got frame from q%d\n", n); + prfull[n] = (prfull[n] + 1) % (LN_FRAMES); + enter_critical(QLOCK(n)); + prcnt[n]--; /* lock out increments */ + leave_critical(QLOCK(n)); + return f; +} + +SM_frame * +Get_Free_Frame(int n) +{ SM_frame *f; + double cnt_start = free_wait; + + if (VVERBOSE) { cpu_printf("get free frame from q%d\n", n); } + + if (n == NCORE) /* global q */ + { f = &(m_workq[n][lrfree]); + } else + { f = &(m_workq[n][prfree[n]]); + } + while (f->m_vsize != 0) /* await free slot LOCK : free slot */ + { iam_alive(); + if (free_wait++ - cnt_start > OneSecond) + { if (verbose) + { cpu_printf("timeout waiting for free slot q%d\n", n); + } + cnt_start = free_wait; + if (someone_crashed(1)) + { printf("cpu%d: search terminated\n", core_id); + sudden_stop("get free frame"); + pan_exit(1); + } } } + if (n != NCORE) + { prfree[n] = (prfree[n] + 1) % (LN_FRAMES); + enter_critical(QLOCK(n)); + prcnt[n]++; /* lock out decrements */ + if (prmax[n] < prcnt[n]) + { prmax[n] = prcnt[n]; + } + leave_critical(QLOCK(n)); + } + return f; +} +#ifndef NGQ +int +GlobalQ_HasRoom(void) +{ int rval = 0; + + gq_tries++; + if (*grcnt < GN_FRAMES) /* there seems to be room */ + { enter_critical(GQ_WR); /* gq write access */ + if (*grcnt < GN_FRAMES) + { if (m_workq[NCORE][*grfree].m_vsize != 0) + { /* can happen if reader is slow emptying slot */ + *gr_readmiss++; + goto out; /* dont wait: release lock and return */ + } + lrfree = *grfree; /* Get_Free_Frame use lrfree in this mode */ + *grfree = (*grfree + 1) % GN_FRAMES; + *grcnt = *grcnt + 1; /* count nr of slots filled -- no additional lock needed */ + if (*grmax < *grcnt) *grmax = *grcnt; + leave_critical(GQ_WR); /* for short lock duration */ + gq_hasroom++; + mem_put(NCORE); /* copy state into reserved slot */ + rval = 1; /* successfull handoff */ + } else + { gq_hasnoroom++; +out: leave_critical(GQ_WR); + } } + return rval; +} +#endif + +int +unpack_state(SM_frame *f, int from_q) +{ int i, j; + static struct H_el D_State; + + if (f->m_vsize > 0) + { boq = f->m_boq; + if (boq > 256) + { cpu_printf("saw control %d, expected state\n", boq); + return 0; + } + vsize = f->m_vsize; +correct: + memcpy((uchar *) &now, (uchar *) f->m_now, vsize); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { Mask[i] = (f->m_Mask[i/8] & (1< 0) + { memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT)); + memcpy((uchar *) proc_skip, (uchar *) f->m_p_skip, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) q_offset, (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT)); + memcpy((uchar *) q_skip, (uchar *) f->m_q_skip, now._nr_qs * sizeof(uchar)); + } +#ifndef NOVSZ + if (vsize != now._vsz) + { cpu_printf("vsize %d != now._vsz %d (type %d) %d\n", + vsize, now._vsz, f->m_boq, f->m_vsize); + vsize = now._vsz; + goto correct; /* rare event: a race */ + } +#endif + hmax = max(hmax, vsize); + + if (f != &cur_Root) + { memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame)); + } + + if (((now._a_t) & 1) == 1) /* i.e., when starting nested DFS */ + { A_depth = depthfound = 0; + memcpy((uchar *)&A_Root, (uchar *)&now, vsize); + } + nr_handoffs = f->nr_handoffs; + } else + { cpu_printf("pan: state empty\n"); + } + + depth = 0; + trpt = &trail[1]; + trpt->tau = f->m_tau; + trpt->o_pm = f->m_o_pm; + + (trpt-1)->ostate = &D_State; /* stub */ + trpt->ostate = &D_State; + +#ifdef FULL_TRAIL + if (upto > 0) + { stack_last[core_id] = (Stack_Tree *) f->m_stack; + } + #if defined(VERBOSE) + if (stack_last[core_id]) + { cpu_printf("%d: UNPACK -- SET m_stack %u (%d,%d)\n", + depth, stack_last[core_id], stack_last[core_id]->pr, + stack_last[core_id]->t_id); + } + #endif +#endif + + if (!trpt->o_t) + { static Trans D_Trans; + trpt->o_t = &D_Trans; + } + + #ifdef VERI + if ((trpt->tau & 4) != 4) + { trpt->tau |= 4; /* the claim moves first */ + cpu_printf("warning: trpt was not up to date\n"); + } + #endif + + for (i = 0; i < (int) now._nr_pr; i++) + { P0 *ptr = (P0 *) pptr(i); + #ifndef NP + if (accpstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 2; + } + #else + if (progstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 4; + } + #endif + } + + #ifdef EVENT_TRACE + #ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 2; + } + #else + if (progstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 4; + } + #endif + #endif + + #if defined(C_States) && (HAS_TRACK==1) + /* restore state of tracked C objects */ + c_revert((uchar *) &(now.c_state[0])); + #if (HAS_STACK==1) + c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */ + #endif + #endif + return 1; +} + +void +write_root(void) /* for trail file */ +{ int fd; + + if (iterative == 0 && Nr_Trails > 1) + sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix); + else + sprintf(fnm, "%s.%s", TrailFile, sprefix); + + if (cur_Root.m_vsize == 0) + { (void) unlink(fnm); /* remove possible old copy */ + return; /* its the default initial state */ + } + + if ((fd = creat(fnm, TMODE)) < 0) + { char *q; + if ((q = strchr(TrailFile, '.'))) + { *q = '\0'; /* strip .pml */ + if (iterative == 0 && Nr_Trails-1 > 0) + sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix); + else + sprintf(fnm, "%s.%s", TrailFile, sprefix); + *q = '.'; + fd = creat(fnm, TMODE); + } + if (fd < 0) + { cpu_printf("pan: cannot create %s\n", fnm); + perror("cause"); + return; + } } + + if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame)) + { cpu_printf("pan: error writing %s\n", fnm); + } else + { cpu_printf("pan: wrote %s\n", fnm); + } + close(fd); +} + +void +set_root(void) +{ int fd; + char *q; + char MyFile[512]; + char MySuffix[16]; + char *ssuffix = "rst"; + int try_core = 1; + + strcpy(MyFile, TrailFile); +try_again: + if (whichtrail > 0) + { sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix); + fd = open(fnm, O_RDONLY, 0); + if (fd < 0 && (q = strchr(MyFile, '.'))) + { *q = '\0'; /* strip .pml */ + sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix); + *q = '.'; + fd = open(fnm, O_RDONLY, 0); + } + } else + { sprintf(fnm, "%s.%s", MyFile, ssuffix); + fd = open(fnm, O_RDONLY, 0); + if (fd < 0 && (q = strchr(MyFile, '.'))) + { *q = '\0'; /* strip .pml */ + sprintf(fnm, "%s.%s", MyFile, ssuffix); + *q = '.'; + fd = open(fnm, O_RDONLY, 0); + } } + + if (fd < 0) + { if (try_core < NCORE) + { ssuffix = MySuffix; + sprintf(ssuffix, "cpu%d_rst", try_core++); + goto try_again; + } + cpu_printf("no file '%s.rst' or '%s' (not an error)\n", MyFile, fnm); + } else + { if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame)) + { cpu_printf("read error %s\n", fnm); + close(fd); + pan_exit(1); + } + close(fd); + (void) unpack_state(&cur_Root, -2); +#ifdef SEP_STATE + cpu_printf("partial trail -- last few steps only\n"); +#endif + cpu_printf("restored root from '%s'\n", fnm); + printf("=====State:=====\n"); + { int i, j; P0 *z; + for (i = 0; i < now._nr_pr; i++) + { z = (P0 *)pptr(i); + printf("proc %2d (%s) ", i, procname[z->_t]); + for (j = 0; src_all[j].src; j++) + if (src_all[j].tp == (int) z->_t) + { printf(" line %3d \"%s\" ", + src_all[j].src[z->_p], PanSource); + break; + } + printf("(state %d)\n", z->_p); + c_locals(i, z->_t); + } + c_globals(); + } + printf("================\n"); + } +} + +#ifdef USE_DISK +unsigned long dsk_written, dsk_drained; +void mem_drain(void); +#endif + +void +m_clear_frame(SM_frame *f) +{ int i, clr_sz = sizeof(SM_results); + + for (i = 0; i <= _NP_; i++) /* all proctypes */ + { clr_sz += NrStates[i]*sizeof(uchar); + } + memset(f, 0, clr_sz); + /* caution if sizeof(SM_results) > sizeof(SM_frame) */ +} + +#define TargetQ_Full(n) (m_workq[n][prfree[n]].m_vsize != 0) +#define TargetQ_NotFull(n) (m_workq[n][prfree[n]].m_vsize == 0) + +int +AllQueuesEmpty(void) +{ int q; +#ifndef NGQ + if (*grcnt != 0) + { return 0; + } +#endif + for (q = 0; q < NCORE; q++) + { if (prcnt[q] != 0) + { return 0; + } } + return 1; +} + +void +Read_Queue(int q) +{ SM_frame *f, *of; + int remember, target_q; + SM_results *r; + double patience = 0.0; + + target_q = (q + 1) % NCORE; + + for (;;) + { f = Get_Full_Frame(q); + if (!f) /* 1 second timeout -- and trigger for Query */ + { if (someone_crashed(2)) + { printf("cpu%d: search terminated [code %d]\n", + core_id, search_terminated?*search_terminated:-1); + sudden_stop(""); + pan_exit(1); + } +#ifdef TESTING + /* to profile with cc -pg and gprof pan.exe -- set handoff depth beyond maxdepth */ + exit(0); +#endif + remember = *grfree; + if (core_id == 0 /* root can initiate termination */ + && remote_party == 0 /* and only the original root */ + && query_in_progress == 0 /* unless its already in progress */ + && AllQueuesEmpty()) + { f = Get_Free_Frame(target_q); + query_in_progress = 1; /* only root process can do this */ + if (!f) { Uerror("Fatal1: no free slot"); } + f->m_boq = QUERY; /* initiate Query */ + if (verbose) + { cpu_printf("snd QUERY to q%d (%d) into slot %d\n", + target_q, nstates_get + 1, prfree[target_q]-1); + } + f->m_vsize = remember + 1; + /* number will not change unless we receive more states */ + } else if (patience++ > OneHour) /* one hour watchdog timer */ + { cpu_printf("timeout -- giving up\n"); + sudden_stop("queue timeout"); + pan_exit(1); + } + if (0) cpu_printf("timed out -- try again\n"); + continue; + } + patience = 0.0; /* reset watchdog */ + + if (f->m_boq == QUERY) + { if (verbose) + { cpu_printf("got QUERY on q%d (%d <> %d) from slot %d\n", + q, f->m_vsize, nstates_put + 1, prfull[q]-1); + snapshot(); + } + remember = f->m_vsize; + f->m_vsize = 0; /* release slot */ + + if (core_id == 0 && remote_party == 0) /* original root cpu0 */ + { if (query_in_progress == 1 /* didn't send more states in the interim */ + && *grfree + 1 == remember) /* no action on global queue meanwhile */ + { if (verbose) cpu_printf("Termination detected\n"); + if (TargetQ_Full(target_q)) + { if (verbose) + cpu_printf("warning: target q is full\n"); + } + f = Get_Free_Frame(target_q); + if (!f) { Uerror("Fatal2: no free slot"); } + m_clear_frame(f); + f->m_boq = QUIT; /* send final Quit, collect stats */ + f->m_vsize = 111; /* anything non-zero will do */ + if (verbose) + cpu_printf("put QUIT on q%d\n", target_q); + } else + { if (verbose) cpu_printf("Stale Query\n"); +#ifdef USE_DISK + mem_drain(); +#endif + } + query_in_progress = 0; + } else + { if (TargetQ_Full(target_q)) + { if (verbose) + cpu_printf("warning: forward query - target q full\n"); + } + f = Get_Free_Frame(target_q); + if (verbose) + cpu_printf("snd QUERY response to q%d (%d <> %d) in slot %d\n", + target_q, remember, *grfree + 1, prfree[target_q]-1); + if (!f) { Uerror("Fatal4: no free slot"); } + + if (*grfree + 1 == remember) /* no action on global queue */ + { f->m_boq = QUERY; /* forward query, to root */ + f->m_vsize = remember; + } else + { f->m_boq = QUERY_F; /* no match -- busy */ + f->m_vsize = 112; /* anything non-zero */ +#ifdef USE_DISK + if (dsk_written != dsk_drained) + { mem_drain(); + } +#endif + } } + continue; + } + + if (f->m_boq == QUERY_F) + { if (verbose) + { cpu_printf("got QUERY_F on q%d from slot %d\n", q, prfull[q]-1); + } + f->m_vsize = 0; /* release slot */ + + if (core_id == 0 && remote_party == 0) /* original root cpu0 */ + { if (verbose) cpu_printf("No Match on Query\n"); + query_in_progress = 0; + } else + { if (TargetQ_Full(target_q)) + { if (verbose) cpu_printf("warning: forwarding query_f, target queue full\n"); + } + f = Get_Free_Frame(target_q); + if (verbose) cpu_printf("forward QUERY_F to q%d into slot %d\n", + target_q, prfree[target_q]-1); + if (!f) { Uerror("Fatal5: no free slot"); } + f->m_boq = QUERY_F; /* cannot terminate yet */ + f->m_vsize = 113; /* anything non-zero */ + } +#ifdef USE_DISK + if (dsk_written != dsk_drained) + { mem_drain(); + } +#endif + continue; + } + + if (f->m_boq == QUIT) + { if (0) cpu_printf("done -- local memcnt %g Mb\n", memcnt/(1048576.)); + retrieve_info((SM_results *) f); /* collect and combine stats */ + if (verbose) + { cpu_printf("received Quit\n"); + snapshot(); + } + f->m_vsize = 0; /* release incoming slot */ + if (core_id != 0) + { f = Get_Free_Frame(target_q); /* new outgoing slot */ + if (!f) { Uerror("Fatal6: no free slot"); } + m_clear_frame(f); /* start with zeroed stats */ + record_info((SM_results *) f); + f->m_boq = QUIT; /* forward combined results */ + f->m_vsize = 114; /* anything non-zero */ + if (verbose>1) + cpu_printf("fwd Results to q%d\n", target_q); + } + break; /* successful termination */ + } + + /* else: 0<= boq <= 255, means STATE transfer */ + if (unpack_state(f, q) != 0) + { nstates_get++; + f->m_vsize = 0; /* release slot */ + if (VVERBOSE) cpu_printf("Got state\n"); + + if (search_terminated != NULL + && *search_terminated == 0) + { new_state(); /* explore successors */ + memset((uchar *) &cur_Root, 0, sizeof(SM_frame)); /* avoid confusion */ + } else + { pan_exit(0); + } + } else + { pan_exit(0); + } } + if (verbose) cpu_printf("done got %d put %d\n", nstates_get, nstates_put); + sleep_report(); +} + +void +give_up(int unused_x) +{ + if (search_terminated != NULL) + { *search_terminated |= 32; /* give_up */ + } + if (!writing_trail) + { was_interrupted = 1; + snapshot(); + cpu_printf("Give Up\n"); + sleep_report(); + pan_exit(1); + } else /* we are already terminating */ + { cpu_printf("SIGINT\n"); + } +} + +void +check_overkill(void) +{ + vmax_seen = (vmax_seen + 7)/ 8; + vmax_seen *= 8; /* round up to a multiple of 8 */ + + if (core_id == 0 + && !remote_party + && nstates_put > 0 + && VMAX - vmax_seen > 8) + { +#ifdef BITSTATE + printf("cpu0: max VMAX value seen in this run: "); +#else + printf("cpu0: recommend recompiling with "); +#endif + printf("-DVMAX=%d\n", vmax_seen); + } +} + +void +mem_put(int q) /* handoff state to other cpu, workq q */ +{ SM_frame *f; + int i, j; + + if (vsize > VMAX) + { vsize = (vsize + 7)/8; vsize *= 8; /* round up */ + printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize); + Uerror("aborting"); + } + if (now._nr_pr > PMAX) + { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr); + Uerror("aborting"); + } + if (now._nr_qs > QMAX) + { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs); + Uerror("aborting"); + } + if (vsize > vmax_seen) vmax_seen = vsize; + if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr; + if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs; + + f = Get_Free_Frame(q); /* not called in likely deadlock states */ + if (!f) { Uerror("Fatal3: no free slot"); } + + if (VVERBOSE) cpu_printf("putting state into q%d\n", q); + + memcpy((uchar *) f->m_now, (uchar *) &now, vsize); + memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char)); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { if (Mask[i]) + { f->m_Mask[i/8] |= (1< 0) + { memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT)); + memcpy((uchar *) f->m_p_skip, (uchar *) proc_skip, now._nr_pr * sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT)); + memcpy((uchar *) f->m_q_skip, (uchar *) q_skip, now._nr_qs * sizeof(uchar)); + } +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */ +#endif +#ifdef FULL_TRAIL + f->m_stack = stack_last[core_id]; +#endif + f->nr_handoffs = nr_handoffs+1; + f->m_tau = trpt->tau; + f->m_o_pm = trpt->o_pm; + f->m_boq = boq; + f->m_vsize = vsize; /* must come last - now the other cpu can see it */ + + if (query_in_progress == 1) + query_in_progress = 2; /* make sure we know, if a query makes the rounds */ + nstates_put++; +} + +#ifdef USE_DISK +int Dsk_W_Nr, Dsk_R_Nr; +int dsk_file = -1, dsk_read = -1; +unsigned long dsk_written, dsk_drained; +char dsk_name[512]; + +#ifndef BFS_DISK +#if defined(WIN32) || defined(WIN64) + #define RFLAGS (O_RDONLY|O_BINARY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC|O_BINARY) +#else + #define RFLAGS (O_RDONLY) + #define WFLAGS (O_CREAT|O_WRONLY|O_TRUNC) +#endif +#endif + +void +dsk_stats(void) +{ int i; + + if (dsk_written > 0) + { cpu_printf("dsk_written %d states in %d files\ncpu%d: dsk_drained %6d states\n", + dsk_written, Dsk_W_Nr, core_id, dsk_drained); + close(dsk_read); + close(dsk_file); + for (i = 0; i < Dsk_W_Nr; i++) + { sprintf(dsk_name, "Q%.3d_%.3d.tmp", i, core_id); + unlink(dsk_name); + } } +} + +void +mem_drain(void) +{ SM_frame *f, g; + int q = (core_id + 1) % NCORE; /* target q */ + int sz; + + if (dsk_read < 0 + || dsk_written <= dsk_drained) + { return; + } + + while (dsk_written > dsk_drained + && TargetQ_NotFull(q)) + { f = Get_Free_Frame(q); + if (!f) { Uerror("Fatal: unhandled condition"); } + + if ((dsk_drained+1)%MAX_DSK_FILE == 0) /* 100K states max per file */ + { (void) close(dsk_read); /* close current read handle */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr++, core_id); + (void) unlink(dsk_name); /* remove current file */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr, core_id); + cpu_printf("reading %s\n", dsk_name); + dsk_read = open(dsk_name, RFLAGS); /* open next file */ + if (dsk_read < 0) + { Uerror("could not open dsk file"); + } } + if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame)) + { Uerror("bad dsk file read"); + } + sz = g.m_vsize; + g.m_vsize = 0; + memcpy(f, &g, sizeof(SM_frame)); + f->m_vsize = sz; /* last */ + + dsk_drained++; + } +} + +void +mem_file(void) +{ SM_frame f; + int i, j, q = (core_id + 1) % NCORE; /* target q */ + + if (vsize > VMAX) + { printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize); + Uerror("aborting"); + } + if (now._nr_pr > PMAX) + { printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr); + Uerror("aborting"); + } + if (now._nr_qs > QMAX) + { printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs); + Uerror("aborting"); + } + + if (VVERBOSE) cpu_printf("filing state for q%d\n", q); + + memcpy((uchar *) f.m_now, (uchar *) &now, vsize); + memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char)); + for (i = j = 0; i < VMAX; i++, j = (j+1)%8) + { if (Mask[i]) + { f.m_Mask[i/8] |= (1< 0) + { memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT)); + memcpy((uchar *)f.m_p_skip, (uchar *)proc_skip, now._nr_pr*sizeof(uchar)); + } + if (now._nr_qs > 0) + { memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT)); + memcpy((uchar *) f.m_q_skip, (uchar *) q_skip, now._nr_qs*sizeof(uchar)); + } +#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1) + c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */ +#endif +#ifdef FULL_TRAIL + f.m_stack = stack_last[core_id]; +#endif + f.nr_handoffs = nr_handoffs+1; + f.m_tau = trpt->tau; + f.m_o_pm = trpt->o_pm; + f.m_boq = boq; + f.m_vsize = vsize; + + if (query_in_progress == 1) + { query_in_progress = 2; + } + if (dsk_file < 0) + { sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr, core_id); + dsk_file = open(dsk_name, WFLAGS, 0644); + dsk_read = open(dsk_name, RFLAGS); + if (dsk_file < 0 || dsk_read < 0) + { cpu_printf("File: <%s>\n", dsk_name); + Uerror("cannot open diskfile"); + } + Dsk_W_Nr++; /* nr of next file to open */ + cpu_printf("created temporary diskfile %s\n", dsk_name); + } else if ((dsk_written+1)%MAX_DSK_FILE == 0) + { close(dsk_file); /* close write handle */ + sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr++, core_id); + dsk_file = open(dsk_name, WFLAGS, 0644); + if (dsk_file < 0) + { cpu_printf("File: <%s>\n", dsk_name); + Uerror("aborting: cannot open new diskfile"); + } + cpu_printf("created temporary diskfile %s\n", dsk_name); + } + if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame)) + { Uerror("aborting -- disk write failed (disk full?)"); + } + nstates_put++; + dsk_written++; +} +#endif + +int +mem_hand_off(void) +{ + if (search_terminated == NULL + || *search_terminated != 0) /* not a full crash check */ + { pan_exit(0); + } + iam_alive(); /* on every transition of Down */ +#ifdef USE_DISK + mem_drain(); /* maybe call this also on every Up */ +#endif + if (depth > z_handoff /* above handoff limit */ +#ifndef SAFETY + && !a_cycles /* not in liveness mode */ +#endif +#if SYNC + && boq == -1 /* not mid-rv */ +#endif +#ifdef VERI + && (trpt->tau&4) /* claim moves first */ + && !((trpt-1)->tau&128) /* not a stutter move */ +#endif + && !(trpt->tau&8)) /* not an atomic move */ + { int q = (core_id + 1) % NCORE; /* circular handoff */ + #ifdef GENEROUS + if (prcnt[q] < LN_FRAMES) + #else + if (TargetQ_NotFull(q) + && (dfs_phase2 == 0 || prcnt[core_id] > 0)) + #endif + { mem_put(q); + return 1; + } + { int rval; + #ifndef NGQ + rval = GlobalQ_HasRoom(); + #else + rval = 0; + #endif + #ifdef USE_DISK + if (rval == 0) + { void mem_file(void); + mem_file(); + rval = 1; + } + #endif + return rval; + } + } + return 0; /* i.e., no handoff */ +} + +void +mem_put_acc(void) /* liveness mode */ +{ int q = (core_id + 1) % NCORE; + + if (search_terminated == NULL + || *search_terminated != 0) + { pan_exit(0); + } +#ifdef USE_DISK + mem_drain(); +#endif + /* some tortured use of preprocessing: */ +#if !defined(NGQ) || defined(USE_DISK) + if (TargetQ_Full(q)) + { +#endif +#ifndef NGQ + if (GlobalQ_HasRoom()) + { return; + } +#endif +#ifdef USE_DISK + mem_file(); + } else +#else + #if !defined(NGQ) || defined(USE_DISK) + } + #endif +#endif + { mem_put(q); + } +} + +#if defined(WIN32) || defined(WIN64) +void +init_shm(void) /* initialize shared work-queues */ +{ char key[512]; + int n, m; + int must_exit = 0; + + if (core_id == 0 && verbose) + { printf("cpu0: step 3: allocate shared work-queues %g Mb\n", + ((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.)); + } + for (m = 0; m < NR_QS; m++) /* last q is global 1 */ + { double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE; + sprintf(key, "Global\\pan_%s_%.3d", PanSource, m); + if (core_id == 0) + { shmid[m] = CreateFileMapping( + INVALID_HANDLE_VALUE, /* use paging file */ + NULL, /* default security */ + PAGE_READWRITE, /* access permissions */ + 0, /* high-order 4 bytes */ + qsize, /* low-order bytes, size in bytes */ + key); /* name */ + } else /* worker nodes just open these segments */ + { shmid[m] = OpenFileMapping( + FILE_MAP_ALL_ACCESS, /* read/write access */ + FALSE, /* children do not inherit handle */ + key); + } + if (shmid[m] == NULL) + { fprintf(stderr, "cpu%d: could not create or open shared queues\n", + core_id); + must_exit = 1; + break; + } + /* attach: */ + shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (shared_mem[m] == NULL) + { fprintf(stderr, "cpu%d: cannot attach shared q%d (%d Mb)\n", + core_id, m+1, (int) (qsize/(1048576.))); + must_exit = 1; + break; + } + + memcnt += qsize; + + m_workq[m] = (SM_frame *) shared_mem[m]; + if (core_id == 0) + { int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES; + for (n = 0; n < nframes; n++) + { m_workq[m][n].m_vsize = 0; + m_workq[m][n].m_boq = 0; + } } } + + if (must_exit) + { fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); /* calls cleanup_shm */ + } +} + +static uchar * +prep_shmid_S(size_t n) /* either sets SS or H_tab, WIN32/WIN64 */ +{ char *rval; +#ifndef SEP_STATE + char key[512]; + + if (verbose && core_id == 0) + { + #ifdef BITSTATE + printf("cpu0: step 1: allocate shared bitstate %g Mb\n", + (double) n / (1048576.)); + #else + printf("cpu0: step 1: allocate shared hastable %g Mb\n", + (double) n / (1048576.)); + #endif + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu%d: S %8g + %d Kb exceeds memory limit of %8g Mb\n", + core_id, memcnt/1024., n/1024, memlim/(1048576.)); + printf("cpu%d: insufficient memory -- aborting\n", core_id); + exit(1); + } + #endif + + /* make key different from queues: */ + sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+2); /* different from qs */ + + if (core_id == 0) /* root */ + { shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, +#ifdef WIN64 + PAGE_READWRITE, (n>>32), (n & 0xffffffff), key); +#else + PAGE_READWRITE, 0, n, key); +#endif + memcnt += (double) n; + } else /* worker */ + { shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key); + } + if (shmid_S == NULL) + { + #ifdef BITSTATE + fprintf(stderr, "cpu%d: cannot %s shared bitstate", + core_id, core_id?"open":"create"); + #else + fprintf(stderr, "cpu%d: cannot %s shared hashtable", + core_id, core_id?"open":"create"); + #endif + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } + + rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */ + if ((char *) rval == NULL) + { fprintf(stderr, "cpu%d: cannot attach shared bitstate or hashtable\n", core_id); + fprintf(stderr, "pan: check './pan --' for usage details\n"); + pan_exit(1); + } +#else + rval = (char *) emalloc(n); +#endif + return (uchar *) rval; +} + +static uchar * +prep_state_mem(size_t n) /* WIN32/WIN64 sets memory arena for states */ +{ char *rval; + char key[512]; + static int cnt = 3; /* start larger than earlier ftok calls */ + + if (verbose && core_id == 0) + { printf("cpu0: step 2+: pre-allocate memory arena %d of %g Mb\n", + cnt-3, (double) n / (1048576.)); + } + #ifdef MEMLIM + if (memcnt + (double) n > memlim) + { printf("cpu%d: error: M %.0f + %.0f exceeds memory limit of %.0f Kb\n", + core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0); + return NULL; + } + #endif + + sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+cnt); cnt++; + + if (core_id == 0) + { shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, +#ifdef WIN64 + PAGE_READWRITE, (n>>32), (n & 0xffffffff), key); +#else + PAGE_READWRITE, 0, n, key); +#endif + } else + { shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key); + } + if (shmid_M == NULL) + { printf("cpu%d: failed to get pool of shared memory nr %d of size %d\n", + core_id, cnt-3, n); + printf("pan: check './pan --' for usage details\n"); + return NULL; + } + rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0); /* attach */ + + if (rval == NULL) + { printf("cpu%d: failed to attach pool of shared memory nr %d of size %d\n", + core_id, cnt-3, n); + return NULL; + } + return (uchar *) rval; +} + +void +init_HT(unsigned long n) /* WIN32/WIN64 version */ +{ volatile char *x; + double get_mem; +#ifndef SEP_STATE + char *dc_mem_start; +#endif + if (verbose) printf("cpu%d: initialization for Windows\n", core_id); + +#ifdef SEP_STATE + #ifndef MEMLIM + if (verbose) + { printf("cpu0: steps 0,1: no -DMEMLIM set\n"); + } + #else + if (verbose) + printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n", + MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.)); +#endif + get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double); + /* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */ + get_mem += 4 * NCORE * sizeof(void *); + #ifdef FULL_TRAIL + get_mem += (NCORE) * sizeof(Stack_Tree *); + /* NCORE * stack_last */ + #endif + x = (volatile char *) prep_state_mem((size_t) get_mem); + shmid_X = (void *) x; + if (x == NULL) + { printf("cpu0: could not allocate shared memory, see ./pan --\n"); + exit(1); + } + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(void *); /* allow 1 word per entry */ + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + + #ifndef BITSTATE + H_tab = (struct H_el **) emalloc(n); + #endif +#else + #ifndef MEMLIM + #warning MEMLIM not set + #define MEMLIM (2048) + #endif + + if (core_id == 0 && verbose) + printf("cpu0: step 0: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb) = %g Mb for state storage\n", + MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.), + (memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.)); + #ifndef BITSTATE + H_tab = (struct H_el **) prep_shmid_S((size_t) n); /* hash_table */ + #endif + get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE; + if (get_mem <= 0) + { Uerror("internal error -- shared state memory"); + } + + if (core_id == 0 && verbose) + { printf("cpu0: step 2: shared state memory %g Mb\n", + get_mem/(1048576.)); + } + x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem); /* for states */ + if (x == NULL) + { printf("cpu%d: insufficient memory -- aborting\n", core_id); + exit(1); + } + + search_terminated = (volatile unsigned int *) x; /* comes first */ + x += sizeof(void *); /* maintain alignment */ + + is_alive = (volatile double *) x; + x += NCORE * sizeof(double); + + sh_lock = (volatile int *) x; + x += CS_NR * sizeof(int); + + grfree = (volatile int *) x; + x += sizeof(void *); + grfull = (volatile int *) x; + x += sizeof(void *); + grcnt = (volatile int *) x; + x += sizeof(void *); + grmax = (volatile int *) x; + x += sizeof(void *); + prfree = (volatile int *) x; + x += NCORE * sizeof(void *); + prfull = (volatile int *) x; + x += NCORE * sizeof(void *); + prcnt = (volatile int *) x; + x += NCORE * sizeof(void *); + prmax = (volatile int *) x; + x += NCORE * sizeof(void *); + gr_readmiss = (volatile double *) x; + x += sizeof(double); + gr_writemiss = (volatile double *) x; + x += sizeof(double); + + #ifdef FULL_TRAIL + stack_last = (volatile Stack_Tree **) x; + x += NCORE * sizeof(Stack_Tree *); + #endif + if (((long)x)&(sizeof(void *)-1)) /* word alignment */ + { x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */ + } + + #ifdef COLLAPSE + ncomps = (unsigned long *) x; + x += (256+2) * sizeof(unsigned long); + #endif + + dc_shared = (sh_Allocater *) x; /* in shared memory */ + x += sizeof(sh_Allocater); + + if (core_id == 0) /* root only */ + { dc_shared->dc_id = shmid_M; + dc_shared->dc_start = (void *) dc_mem_start; + dc_shared->dc_arena = x; + dc_shared->pattern = 1234567; + dc_shared->dc_size = (long) get_mem - (long) (x - dc_mem_start); + dc_shared->nxt = NULL; + } +#endif +} + +#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__) +extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit); +int +tas(volatile LONG *s) +{ return InterlockedBitTestAndSet(s, 1); +} +#else + #error missing definition of test and set operation for this platform +#endif + +void +cleanup_shm(int val) +{ int m; + static int nibis = 0; + + if (nibis != 0) + { printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val); + return; + } else + { nibis = 1; + } + if (search_terminated != NULL) + { *search_terminated |= 16; /* cleanup_shm */ + } + + for (m = 0; m < NR_QS; m++) + { if (shmid[m] != NULL) + { UnmapViewOfFile((char *) shared_mem[m]); + CloseHandle(shmid[m]); + } } +#ifdef SEP_STATE + UnmapViewOfFile((void *) shmid_X); + CloseHandle((void *) shmid_M); +#else + #ifdef BITSTATE + if (shmid_S != NULL) + { UnmapViewOfFile(SS); + CloseHandle(shmid_S); + } + #else + if (core_id == 0 && verbose) + { printf("cpu0: done, %ld Mb of shared state memory left\n", + dc_shared->dc_size / (long)(1048576)); + } + if (shmid_S != NULL) + { UnmapViewOfFile(H_tab); + CloseHandle(shmid_S); + } + shmid_M = (void *) (dc_shared->dc_id); + UnmapViewOfFile((char *) dc_shared->dc_start); + CloseHandle(shmid_M); + #endif +#endif + /* detached from shared memory - so cannot use cpu_printf */ + if (verbose) + { printf("cpu%d: done -- got %d states from queue\n", + core_id, nstates_get); + } +} + +void +mem_get(void) +{ SM_frame *f; + int is_parent; + +#if defined(MA) && !defined(SEP_STATE) + #error MA requires SEP_STATE in multi-core mode +#endif +#ifdef BFS + #error BFS is not supported in multi-core mode +#endif +#ifdef SC + #error SC is not supported in multi-core mode +#endif + init_shm(); /* we are single threaded when this starts */ + signal(SIGINT, give_up); /* windows control-c interrupt */ + + if (core_id == 0 && verbose) + { printf("cpu0: step 4: creating additional workers (proxy %d)\n", + proxy_pid); + } +#if 0 + if NCORE > 1 the child or the parent should fork N-1 more times + the parent is the only process with core_id == 0 and is_parent > 0 + the others (workers) have is_parent = 0 and core_id = 1..NCORE-1 +#endif + if (core_id == 0) /* root starts up the workers */ + { worker_pids[0] = (DWORD) getpid(); /* for completeness */ + while (++core_id < NCORE) /* first worker sees core_id = 1 */ + { char cmdline[64]; + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + + if (proxy_pid == core_id) /* always non-zero */ + { sprintf(cmdline, "pan_proxy.exe -r %s-Q%d -Z%d", + o_cmdline, getpid(), core_id); + } else + { sprintf(cmdline, "pan.exe %s-Q%d -Z%d", + o_cmdline, getpid(), core_id); + } + if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline); + + is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi); + if (is_parent == 0) + { Uerror("fork failed"); + } + worker_pids[core_id] = pi.dwProcessId; + worker_handles[core_id] = pi.hProcess; + if (verbose) + { cpu_printf("created core %d, pid %d\n", + core_id, pi.dwProcessId); + } + if (proxy_pid == core_id) /* we just created the receive half */ + { /* add proxy send, store pid in proxy_pid_snd */ + sprintf(cmdline, "pan_proxy.exe -s %s-Q%d -Z%d -Y%d", + o_cmdline, getpid(), core_id, worker_pids[proxy_pid]); + if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline); + is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi); + if (is_parent == 0) + { Uerror("fork failed"); + } + proxy_pid_snd = pi.dwProcessId; + proxy_handle_snd = pi.hProcess; + if (verbose) + { cpu_printf("created core %d, pid %d (send proxy)\n", + core_id, pi.dwProcessId); + } } } + core_id = 0; /* reset core_id for root process */ + } else /* worker */ + { static char db0[16]; /* good for up to 10^6 cores */ + static char db1[16]; + tprefix = db0; sprefix = db1; + sprintf(tprefix, "cpu%d_trail", core_id); /* avoid conflicts on file access */ + sprintf(sprefix, "cpu%d_rst", core_id); + memcnt = 0; /* count only additionally allocated memory */ + } + if (verbose) + { cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid()); + } + if (core_id == 0 && !remote_party) + { new_state(); /* root starts the search */ + if (verbose) + cpu_printf("done with 1st dfs, nstates %g (put %d states), start reading q\n", + nstates, nstates_put); + dfs_phase2 = 1; + } + Read_Queue(core_id); /* all cores */ + + if (verbose) + { cpu_printf("put %6d states into queue -- got %6d\n", + nstates_put, nstates_get); + } + done = 1; + wrapup(); + exit(0); +} +#endif + +#ifdef BITSTATE +void +init_SS(unsigned long n) +{ + SS = (uchar *) prep_shmid_S((size_t) n); + init_HT(0L); +} +#endif + +#endif +clock_t start_time; +#if NCORE>1 +clock_t crash_stamp; +#endif +#if !defined(WIN32) && !defined(WIN64) +struct tms start_tm; +#endif + +void +start_timer(void) +{ +#if defined(WIN32) || defined(WIN64) + start_time = clock(); +#else + start_time = times(&start_tm); +#endif +} + +void +stop_timer(void) +{ clock_t stop_time; + double delta_time; +#if !defined(WIN32) && !defined(WIN64) + struct tms stop_tm; + stop_time = times(&stop_tm); + delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK)); +#else + stop_time = clock(); + delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC); +#endif + if (readtrail || delta_time < 0.00) return; +#if NCORE>1 + if (core_id == 0 && nstates > (double) 0) + { printf("\ncpu%d: elapsed time %.3g seconds (%g states visited)\n", core_id, delta_time, nstates); + if (delta_time > 0.01) + { printf("cpu%d: rate %g states/second\n", core_id, nstates/delta_time); + } + { void check_overkill(void); + check_overkill(); + } } +#else + printf("\npan: elapsed time %.3g seconds\n", delta_time); + if (delta_time > 0.01) + { printf("pan: rate %9.8g states/second\n", nstates/delta_time); + if (verbose) + { printf("pan: avg transition delay %.5g usec\n", + delta_time/(nstates+truncs)); + } } +#endif +} + +#if NCORE>1 +#ifdef T_ALERT +double t_alerts[17]; + +void +crash_report(void) +{ int i; + printf("crash alert intervals:\n"); + for (i = 0; i < 17; i++) + { printf("%d\t%g\n", i, t_alerts[i]); +} } +#endif + +void +crash_reset(void) +{ /* false alarm */ + if (crash_stamp != (clock_t) 0) + { +#ifdef T_ALERT + double delta_time; + int i; +#if defined(WIN32) || defined(WIN64) + delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC); +#else + delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK)); +#endif + for (i = 0; i < 16; i++) + { if (delta_time <= (i*30)) + { t_alerts[i] = delta_time; + break; + } } + if (i == 16) t_alerts[i] = delta_time; +#endif + if (verbose) + printf("cpu%d: crash alert off\n", core_id); + } + crash_stamp = (clock_t) 0; +} + +int +crash_test(double maxtime) +{ double delta_time; + if (crash_stamp == (clock_t) 0) + { /* start timing */ +#if defined(WIN32) || defined(WIN64) + crash_stamp = clock(); +#else + crash_stamp = times(&start_tm); +#endif + if (verbose) + { printf("cpu%d: crash detection\n", core_id); + } + return 0; + } +#if defined(WIN32) || defined(WIN64) + delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC); +#else + delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK)); +#endif + return (delta_time >= maxtime); +} +#endif + +void +do_the_search(void) +{ int i; + depth = mreached = 0; + trpt = &trail[0]; +#ifdef VERI + trpt->tau |= 4; /* the claim moves first */ +#endif + for (i = 0; i < (int) now._nr_pr; i++) + { P0 *ptr = (P0 *) pptr(i); +#ifndef NP + if (!(trpt->o_pm&2) + && accpstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 2; + } +#else + if (!(trpt->o_pm&4) + && progstate[ptr->_t][ptr->_p]) + { trpt->o_pm |= 4; + } +#endif + } +#ifdef EVENT_TRACE +#ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 2; + } +#else + if (progstate[EVENT_TRACE][now._event]) + { trpt->o_pm |= 4; + } +#endif +#endif +#ifndef NOCOMP + Mask[0] = Mask[1] = 1; /* _nr_pr, _nr_qs */ + if (!a_cycles) + { i = &(now._a_t) - (uchar *) &now; + Mask[i] = 1; /* _a_t */ + } +#ifndef NOFAIR + if (!fairness) + { int j = 0; + i = &(now._cnt[0]) - (uchar *) &now; + while (j++ < NFAIR) + Mask[i++] = 1; /* _cnt[] */ + } +#endif +#endif +#ifndef NOFAIR + if (fairness + && (a_cycles && (trpt->o_pm&2))) + { now._a_t = 2; /* set the A-bit */ + now._cnt[0] = now._nr_pr + 1; +#ifdef VERBOSE + printf("%3d: fairness Rule 1, cnt=%d, _a_t=%d\n", + depth, now._cnt[now._a_t&1], now._a_t); +#endif + } +#endif + c_stack_start = (char *) &i; /* meant to be read-only */ +#if defined(HAS_CODE) && defined (C_INIT) + C_INIT; /* initialization of data that must precede fork() */ + c_init_done++; +#endif +#if defined(C_States) && (HAS_TRACK==1) + /* capture initial state of tracked C objects */ + c_update((uchar *) &(now.c_state[0])); +#endif +#ifdef HAS_CODE + if (readtrail) getrail(); /* no return */ +#endif + start_timer(); +#ifdef BFS + bfs(); +#else +#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1) + /* initial state of tracked & unmatched objects */ + c_stack((uchar *) &(svtack->c_stack[0])); +#endif +#ifdef RANDOMIZE + #if RANDOMIZE>0 + srand(RANDOMIZE); + #else + srand(123); + #endif +#endif +#if NCORE>1 + mem_get(); +#else + new_state(); /* start 1st DFS */ +#endif +#endif +} +#ifdef INLINE_REV +uchar +do_reverse(Trans *t, short II, uchar M) +{ uchar _m = M; + int tt = (int) ((P0 *)this)->_p; +#include REVERSE_MOVES +R999: return _m; +} +#endif +#ifndef INLINE +#ifdef EVENT_TRACE +static char _tp = 'n'; static int _qid = 0; +#endif +uchar +do_transit(Trans *t, short II) +{ uchar _m = 0; + int tt = (int) ((P0 *)this)->_p; +#ifdef M_LOSS + uchar delta_m = 0; +#endif +#ifdef EVENT_TRACE + short oboq = boq; + uchar ot = (uchar) ((P0 *)this)->_t; + if (ot == EVENT_TRACE) boq = -1; +#define continue { boq = oboq; return 0; } +#else +#define continue return 0 +#ifdef SEPARATE + uchar ot = (uchar) ((P0 *)this)->_t; +#endif +#endif +#include FORWARD_MOVES +P999: +#ifdef EVENT_TRACE + if (ot == EVENT_TRACE) boq = oboq; +#endif + return _m; +#undef continue +} +#ifdef EVENT_TRACE +void +require(char tp, int qid) +{ Trans *t; + _tp = tp; _qid = qid; + + if (now._event != endevent) + for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt) + { if (do_transit(t, EVENT_TRACE)) + { now._event = t->st; + reached[EVENT_TRACE][t->st] = 1; +#ifdef VERBOSE + printf(" event_trace move to -> %d\n", t->st); +#endif +#ifndef BFS +#ifndef NP + if (accpstate[EVENT_TRACE][now._event]) + (trpt+1)->o_pm |= 2; +#else + if (progstate[EVENT_TRACE][now._event]) + (trpt+1)->o_pm |= 4; +#endif +#endif +#ifdef NEGATED_TRACE + if (now._event == endevent) + { +#ifndef BFS + depth++; trpt++; +#endif + uerror("event_trace error (all events matched)"); +#ifndef BFS + trpt--; depth--; +#endif + break; + } +#endif + for (t = t->nxt; t; t = t->nxt) + { if (do_transit(t, EVENT_TRACE)) + Uerror("non-determinism in event-trace"); + } + return; + } +#ifdef VERBOSE + else + printf(" event_trace miss '%c' -- %d, %d, %d\n", + tp, qid, now._event, t->forw); +#endif + } +#ifdef NEGATED_TRACE + now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */ +#else +#ifndef BFS + depth++; trpt++; +#endif + uerror("event_trace error (no matching event)"); +#ifndef BFS + trpt--; depth--; +#endif +#endif +} +#endif +int +enabled(int iam, int pid) +{ Trans *t; uchar *othis = this; + int res = 0; int tt; uchar ot; +#ifdef VERI + /* if (pid > 0) */ pid++; +#endif + if (pid == iam) + Uerror("used: enabled(pid=thisproc)"); + if (pid < 0 || pid >= (int) now._nr_pr) + return 0; + this = pptr(pid); + TstOnly = 1; + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + for (t = trans[ot][tt]; t; t = t->nxt) + if (do_transit(t, (short) pid)) + { res = 1; + break; + } + TstOnly = 0; + this = othis; + return res; +} +#endif +void +snap_time(void) +{ clock_t stop_time; + double delta_time; +#if !defined(WIN32) && !defined(WIN64) + struct tms stop_tm; + stop_time = times(&stop_tm); + delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK)); +#else + stop_time = clock(); + delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC); +#endif + if (delta_time > 0.01) + { printf("t= %6.3g ", delta_time); + printf("R= %7.0g", nstates/delta_time); + } + printf("\n"); + if (quota > 0.1 && delta_time > quota) + { printf("Time limit of %6.3g minutes exceeded\n", quota/60.0); +#if NCORE>1 + fflush(stdout); + leave_critical(GLOBAL_LOCK); + sudden_stop("time-limit"); + exit(1); +#endif + wrapup(); + } +} +void +snapshot(void) +{ +#if NCORE>1 + enter_critical(GLOBAL_LOCK); /* snapshot */ + printf("cpu%d: ", core_id); +#endif + printf("Depth= %7ld States= %8.3g ", +#if NCORE>1 + (long) (nr_handoffs * z_handoff) + +#endif + mreached, nstates); + printf("Transitions= %8.3g ", nstates+truncs); +#ifdef MA + printf("Nodes= %7d ", nr_states); +#endif + printf("Memory= %9.3f\t", memcnt/1048576.); + snap_time(); + fflush(stdout); +#if NCORE>1 + leave_critical(GLOBAL_LOCK); +#endif +} +#ifdef SC +void +stack2disk(void) +{ + if (!stackwrite + && (stackwrite = creat(stackfile, TMODE)) < 0) + Uerror("cannot create stackfile"); + + if (write(stackwrite, trail, DDD*sizeof(Trail)) + != DDD*sizeof(Trail)) + Uerror("stackfile write error -- disk is full?"); + + memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail)); + memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail)); + CNT1++; +} +void +disk2stack(void) +{ long have; + + CNT2++; + memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail)); + + if (!stackwrite + || lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1) + Uerror("disk2stack lseek error"); + + if (!stackread + && (stackread = open(stackfile, 0)) < 0) + Uerror("cannot open stackfile"); + + if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1) + Uerror("disk2stack lseek error"); + + have = read(stackread, trail, DDD*sizeof(Trail)); + if (have != DDD*sizeof(Trail)) + Uerror("stackfile read error"); +} +#endif +uchar * +Pptr(int x) +{ if (x < 0 || x >= MAXPROC || !proc_offset[x]) + return noptr; + else + return (uchar *) pptr(x); +} +int qs_empty(void); +/* + * new_state() is the main DFS search routine in the verifier + * it has a lot of code ifdef-ed together to support + * different search modes, which makes it quite unreadable. + * if you are studying the code, first use the C preprocessor + * to generate a specific version from the pan.c source, + * e.g. by saying: + * gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c + * and then study the resulting file, rather than this one + */ +#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA)) + +#ifdef NSUCC +int N_succ[512]; +void +tally_succ(int cnt) +{ if (cnt < 512) N_succ[cnt]++; + else printf("tally_succ: cnt %d exceeds range\n", cnt); +} + +void +dump_succ(void) +{ int i; double sum = 0.0; + double w_avg = 0.0; + printf("Successor counts:\n"); + for (i = 0; i < 512; i++) + { sum += (double) N_succ[i]; + } + for (i = 0; i < 512; i++) + { if (N_succ[i] > 0) + { printf("%3d %10d (%.4g %% of total)\n", + i, N_succ[i], (100.0 * (double) N_succ[i])/sum); + w_avg += (double) i * (double) N_succ[i]; + } } + if (sum > N_succ[0]) + printf("mean %.4g (without 0: %.4g)\n", w_avg / sum, w_avg / (sum - (double) N_succ[0])); +} +#endif + +void +new_state(void) +{ Trans *t; + uchar _n, _m, ot; +#ifdef RANDOMIZE + short ooi, eoi; +#endif +#ifdef M_LOSS + uchar delta_m = 0; +#endif + short II, JJ = 0, kk; + int tt; +#ifdef REVERSE + short From = BASE, To = now._nr_pr-1; +#else + short From = now._nr_pr-1, To = BASE; +#endif +Down: +#ifdef CHECK + cpu_printf("%d: Down - %s %saccepting [pids %d-%d]\n", + depth, (trpt->tau&4)?"claim":"program", + (trpt->o_pm&2)?"":"non-", From, To); +#endif +#ifdef SCHED + if (depth > 0) + { trpt->sched_limit = (trpt-1)->sched_limit; + } else + { trpt->sched_limit = 0; + } +#endif +#ifdef SC + if (depth > hiwater) + { stack2disk(); + maxdepth += DDD; + hiwater += DDD; + trpt -= DDD; + if(verbose) + printf("zap %d: %d (maxdepth now %d)\n", + CNT1, hiwater, maxdepth); + } +#endif + trpt->tau &= ~(16|32|64); /* make sure these are off */ +#if defined(FULLSTACK) && defined(MA) + trpt->proviso = 0; +#endif +#ifdef NSUCC + trpt->n_succ = 0; +#endif +#if NCORE>1 + if (mem_hand_off()) + { +#if SYNC + (trpt+1)->o_n = 1; /* not a deadlock: as below */ +#endif +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; /* worstcase guess: as below */ +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#endif + if (depth >= maxdepth) + { if (!warned) + { warned = 1; + printf("error: max search depth too small\n"); + } + if (bounded) + { uerror("depth limit reached"); + } + truncs++; +#if SYNC + (trpt+1)->o_n = 1; /* not a deadlock */ +#endif +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; /* worstcase guess */ +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +AllOver: +#if (defined(FULLSTACK) && !defined(MA)) || NCORE>1 + /* if atomic or rv move, carry forward previous state */ + trpt->ostate = (trpt-1)->ostate; +#endif +#ifdef VERI + if ((trpt->tau&4) || ((trpt-1)->tau&128)) +#endif + if (boq == -1) { /* if not mid-rv */ +#ifndef SAFETY + /* this check should now be redundant + * because the seed state also appears + * on the 1st dfs stack and would be + * matched in hstore below + */ + if ((now._a_t&1) && depth > A_depth) + { if (!memcmp((char *)&A_Root, + (char *)&now, vsize)) + { + depthfound = A_depth; +#ifdef CHECK + printf("matches seed\n"); +#endif +#ifdef NP + uerror("non-progress cycle"); +#else + uerror("acceptance cycle"); +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#ifdef CHECK + printf("not seed\n"); +#endif + } +#endif + if (!(trpt->tau&8)) /* if no atomic move */ + { +#ifdef BITSTATE +#ifdef CNTRSTACK + II = bstore((char *)&now, vsize); + trpt->j6 = j1; trpt->j7 = j2; + JJ = LL[j1] && LL[j2]; +#else +#ifdef FULLSTACK + JJ = onstack_now(); +#else +#ifndef NOREDUCE + JJ = II; /* worstcase guess for p.o. */ +#endif +#endif + II = bstore((char *)&now, vsize); +#endif +#else +#ifdef MA + II = gstore((char *)&now, vsize, 0); +#ifndef FULLSTACK + JJ = II; +#else + JJ = (II == 2)?1:0; +#endif +#else + II = hstore((char *)&now, vsize); +#ifdef FULLSTACK + JJ = (II == 2)?1:0; +#endif +#endif +#endif + kk = (II == 1 || II == 2); +#ifndef SAFETY +#if NCORE==1 || defined (SEP_STATE) + if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2))) + #ifndef NOFAIR +#if 0 + if (!fairness || ((now._a_t&1) && now._cnt[1] == 1)) /* 5.1.4 */ +#else + if (a_cycles && !fairness) /* 5.1.6 -- example by Hirofumi Watanabe */ +#endif + #endif + { + II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */ +#ifdef VERBOSE + printf("state match on dfs stack\n"); +#endif + goto same_case; + } +#endif +#if defined(FULLSTACK) && defined(BITSTATE) + if (!JJ && (now._a_t&1) && depth > A_depth) + { int oj1 = j1; + uchar o_a_t = now._a_t; + now._a_t &= ~(1|16|32); + if (onstack_now()) + { II = 3; +#ifdef VERBOSE + printf("state match on 1st dfs stack\n"); +#endif + } + now._a_t = o_a_t; + j1 = oj1; + } +#endif + if (II == 3 && a_cycles && (now._a_t&1)) + { +#ifndef NOFAIR + if (fairness && now._cnt[1] > 1) /* was != 0 */ + { +#ifdef VERBOSE + printf(" fairness count non-zero\n"); +#endif + II = 0; + } else +#endif + { +#ifndef BITSTATE + nShadow--; +#endif +same_case: if (Lstate) depthfound = Lstate->D; +#ifdef NP + uerror("non-progress cycle"); +#else + uerror("acceptance cycle"); +#endif +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } + } +#endif +#ifndef NOREDUCE +#ifndef SAFETY +#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO) + if (II != 0 && (!Lstate || Lstate->cpu_id < core_id)) + { (trpt-1)->tau |= 16; + } +#endif + if ((II && JJ) || (II == 3)) + { /* marker for liveness proviso */ +#ifndef LOOPSTATE + (trpt-1)->tau |= 16; +#endif + truncs2++; + } +#else +#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO) + if (!(II != 0 && (!Lstate || Lstate->cpu_id < core_id))) + { /* treat as stack state */ + (trpt-1)->tau |= 16; + } else + { /* treat as non-stack state */ + (trpt-1)->tau |= 64; + } +#endif + if (!II || !JJ) + { /* successor outside stack */ + (trpt-1)->tau |= 64; + } +#endif +#endif + if (II) + { truncs++; +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + if (depth == 0) + { return; + } } +#endif + goto Up; + } + if (!kk) + { static long sdone = (long) 0; long ndone; + nstates++; +#if defined(ZAPH) && defined(BITSTATE) + zstates += (double) hfns; +#endif + ndone = (unsigned long) (nstates/((double) FREQ)); + if (ndone != sdone) + { snapshot(); + sdone = ndone; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) + if (nstates > ((double)(ONE_L<<(ssize+1)))) + { void resize_hashtable(void); + resize_hashtable(); + } +#endif +#if defined(ZAPH) && defined(BITSTATE) + if (zstates > ((double)(ONE_L<<(ssize-2)))) + { /* more than half the bits set */ + void zap_hashtable(void); + zap_hashtable(); + zstates = 0; + } +#endif + } +#ifdef SVDUMP + if (vprefix > 0) + if (write(svfd, (uchar *) &now, vprefix) != vprefix) + { fprintf(efd, "writing %s.svd failed\n", PanSource); + wrapup(); + } +#endif +#if defined(MA) && defined(W_XPT) + if ((unsigned long) nstates%W_XPT == 0) + { void w_xpoint(void); + w_xpoint(); + } +#endif + } +#if defined(FULLSTACK) || defined(CNTRSTACK) + onstack_put(); +#ifdef DEBUG2 +#if defined(FULLSTACK) && !defined(MA) + printf("%d: putting %u (%d)\n", depth, + trpt->ostate, + (trpt->ostate)?trpt->ostate->tagged:0); +#else + printf("%d: putting\n", depth); +#endif +#endif +#else + #if NCORE>1 + trpt->ostate = Lstate; + #endif +#endif + } } + if (depth > mreached) + mreached = depth; +#ifdef VERI + if (trpt->tau&4) +#endif + trpt->tau &= ~(1|2); /* timeout and -request off */ + _n = 0; +#if SYNC + (trpt+1)->o_n = 0; +#endif +#ifdef VERI + if (now._nr_pr == 0) /* claim terminated */ + uerror("end state in claim reached"); + check_claim(((P0 *)pptr(0))->_p); +Stutter: + if (trpt->tau&4) /* must make a claimmove */ + { +#ifndef NOFAIR + if ((now._a_t&2) /* A-bit set */ + && now._cnt[now._a_t&1] == 1) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm |= 16; +#ifdef DEBUG + printf("%3d: fairness Rule 3.: _a_t = %d\n", + depth, now._a_t); +#endif + } +#endif + II = 0; /* never */ + goto Veri0; + } +#endif +#ifndef NOREDUCE + /* Look for a process with only safe transitions */ + /* (special rules apply in the 2nd dfs) */ + if (boq == -1 && From != To + +#ifdef SAFETY + #if NCORE>1 + && (depth < z_handoff) + #endif + ) +#else + #if NCORE>1 + && ((a_cycles) || (!a_cycles && depth < z_handoff)) + #endif + && (!(now._a_t&1) + || (a_cycles && + #ifndef BITSTATE +#ifdef MA +#ifdef VERI + !((trpt-1)->proviso)) +#else + !(trpt->proviso)) +#endif +#else +#ifdef VERI + (trpt-1)->ostate && + !(((char *)&((trpt-1)->ostate->state))[0] & 128)) +#else + !(((char *)&(trpt->ostate->state))[0] & 128)) +#endif +#endif + #else +#ifdef VERI + (trpt-1)->ostate && + (trpt-1)->ostate->proviso == 0) +#else + trpt->ostate->proviso == 0) +#endif + #endif + )) +#endif + +#ifdef REVERSE + for (II = From; II <= To; II++) +#else + for (II = From; II >= To; II--) +#endif + { +Resume: /* pick up here if preselect fails */ + this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; + if (trans[ot][tt]->atom & 8) + { t = trans[ot][tt]; + if (t->qu[0] != 0) + { Ccheck++; + if (!q_cond(II, t)) + continue; + Cholds++; + } + From = To = II; /* the process preselected */ +#ifdef NIBIS + t->om = 0; +#endif + trpt->tau |= 32; /* preselect marker */ +#ifdef DEBUG +#ifdef NIBIS + printf("%3d: proc %d Pre", depth, II); + printf("Selected (om=%d, tau=%d)\n", + t->om, trpt->tau); +#else + printf("%3d: proc %d PreSelected (tau=%d)\n", + depth, II, trpt->tau); +#endif +#endif + goto Again; + } + } + trpt->tau &= ~32; +#endif +#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI)) +Again: +#endif + /* The Main Expansion Loop over Processes */ + trpt->o_pm &= ~(8|16|32|64); /* fairness-marks */ +#ifndef NOFAIR + if (fairness && boq == -1 +#ifdef VERI + && (!(trpt->tau&4) && !((trpt-1)->tau&128)) +#endif + && !(trpt->tau&8)) + { /* A_bit = 1; Cnt = N in acc states with A_bit 0 */ + if (!(now._a_t&2)) + { + if (a_cycles && (trpt->o_pm&2)) + { /* Accepting state */ + now._a_t |= 2; + now._cnt[now._a_t&1] = now._nr_pr + 1; + trpt->o_pm |= 8; +#ifdef DEBUG + printf("%3d: fairness Rule 1: cnt=%d, _a_t=%d\n", + depth, now._cnt[now._a_t&1], now._a_t); +#endif + } + } else + { /* A_bit = 0 when Cnt 0 */ + if (now._cnt[now._a_t&1] == 1) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm |= 16; +#ifdef DEBUG + printf("%3d: fairness Rule 3: _a_t = %d\n", + depth, now._a_t); +#endif + } } } +#endif + +#ifdef REVERSE + for (II = From; II <= To; II++) +#else + for (II = From; II >= To; II--) +#endif + { +#if SYNC + /* no rendezvous with same proc */ + if (boq != -1 && trpt->pr == II) continue; +#endif +#ifdef SCHED + /* limit max nr of interleavings */ + if (From != To + && depth > 0 + #ifdef VERI + && II != 0 + #endif + && (trpt-1)->pr != II + && trpt->sched_limit >= sched_max) + { continue; + } +#endif +#ifdef VERI +Veri0: +#endif + this = pptr(II); + tt = (int) ((P0 *)this)->_p; + ot = (uchar) ((P0 *)this)->_t; +#ifdef NIBIS + /* don't repeat a previous preselected expansion */ + /* could hit this if reduction proviso was false */ + t = trans[ot][tt]; + if (!(trpt->tau&4) + && !(trpt->tau&1) + && !(trpt->tau&32) + && (t->atom & 8) + && boq == -1 + && From != To) + { if (t->qu[0] == 0 + || q_cond(II, t)) + { _m = t->om; + if (_m>_n||(_n>3&&_m!=0)) _n=_m; + continue; /* did it before */ + } } +#endif + trpt->o_pm &= ~1; /* no move in this pid yet */ +#ifdef EVENT_TRACE + (trpt+1)->o_event = now._event; +#endif + /* Fairness: Cnt++ when Cnt == II */ +#ifndef NOFAIR + trpt->o_pm &= ~64; /* didn't apply rule 2 */ + if (fairness + && boq == -1 + && !(trpt->o_pm&32) + && (now._a_t&2) + && now._cnt[now._a_t&1] == II+2) + { now._cnt[now._a_t&1] -= 1; +#ifdef VERI + /* claim need not participate */ + if (II == 1) + now._cnt[now._a_t&1] = 1; +#endif +#ifdef DEBUG + printf("%3d: proc %d fairness ", depth, II); + printf("Rule 2: --cnt to %d (%d)\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm |= (32|64); + } +#endif +#ifdef HAS_PROVIDED + if (!provided(II, ot, tt, t)) continue; +#endif + /* check all trans of proc II - escapes first */ +#ifdef HAS_UNLESS + trpt->e_state = 0; +#endif + (trpt+1)->pr = (uchar) II; + (trpt+1)->st = tt; +#ifdef RANDOMIZE + for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++) + { if (strcmp(t->tp, "else") == 0) + { eoi++; + break; + } } + if (eoi > 0) + { t = trans[ot][tt]; + #ifdef VERBOSE + printf("randomizer: suppressed, saw else\n"); + #endif + } else + { eoi = rand()%ooi; + #ifdef VERBOSE + printf("randomizer: skip %d in %d\n", eoi, ooi); + #endif + for (t = trans[ot][tt]; t; t = t->nxt) + if (eoi-- <= 0) break; + } +domore: + for ( ; t && ooi > 0; t = t->nxt, ooi--) +#else + for (t = trans[ot][tt]; t; t = t->nxt) +#endif + { +#ifdef HAS_UNLESS + /* exploring all transitions from + * a single escape state suffices + */ + if (trpt->e_state > 0 + && trpt->e_state != t->e_trans) + { +#ifdef DEBUG + printf("skip 2nd escape %d (did %d before)\n", + t->e_trans, trpt->e_state); +#endif + break; + } +#endif + (trpt+1)->o_t = t; +#ifdef INLINE +#include FORWARD_MOVES +P999: /* jumps here when move succeeds */ +#else + if (!(_m = do_transit(t, II))) continue; +#endif +#ifdef SCHED + if (depth > 0 + #ifdef VERI + && II != 0 + #endif + && (trpt-1)->pr != II) + { trpt->sched_limit = 1 + (trpt-1)->sched_limit; + } +#endif + if (boq == -1) +#ifdef CTL + /* for branching-time, can accept reduction only if */ + /* the persistent set contains just 1 transition */ + { if ((trpt->tau&32) && (trpt->o_pm&1)) + trpt->tau |= 16; + trpt->o_pm |= 1; /* we moved */ + } +#else + trpt->o_pm |= 1; /* we moved */ +#endif +#ifdef LOOPSTATE + if (loopstate[ot][tt]) + { +#ifdef VERBOSE + printf("exiting from loopstate:\n"); +#endif + trpt->tau |= 16; + cnt_loops++; + } +#endif +#ifdef PEG + peg[t->forw]++; +#endif +#if defined(VERBOSE) || defined(CHECK) +#if defined(SVDUMP) + cpu_printf("%3d: proc %d exec %d \n", depth, II, t->t_id); +#else + cpu_printf("%3d: proc %d exec %d, %d to %d, %s %s %s %saccepting [tau=%d]\n", + depth, II, t->forw, tt, t->st, t->tp, + (t->atom&2)?"atomic":"", + (boq != -1)?"rendez-vous":"", + (trpt->o_pm&2)?"":"non-", trpt->tau); +#ifdef HAS_UNLESS + if (t->e_trans) + cpu_printf("\t(escape to state %d)\n", t->st); +#endif +#endif +#ifdef RANDOMIZE + cpu_printf("\t(randomizer %d)\n", ooi); +#endif +#endif +#ifdef HAS_LAST +#ifdef VERI + if (II != 0) +#endif + now._last = II - BASE; +#endif +#ifdef HAS_UNLESS + trpt->e_state = t->e_trans; +#endif + depth++; trpt++; + trpt->pr = (uchar) II; + trpt->st = tt; + trpt->o_pm &= ~(2|4); + if (t->st > 0) + { ((P0 *)this)->_p = t->st; +/* moved down reached[ot][t->st] = 1; */ + } +#ifndef SAFETY + if (a_cycles) + { +#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP)) + int ii; +#endif +#define P__Q ((P0 *)pptr(ii)) +#if ACCEPT_LAB>0 +#ifdef NP + /* state 1 of np_ claim is accepting */ + if (((P0 *)pptr(0))->_p == 1) + trpt->o_pm |= 2; +#else + for (ii = 0; ii < (int) now._nr_pr; ii++) + { if (accpstate[P__Q->_t][P__Q->_p]) + { trpt->o_pm |= 2; + break; + } } +#endif +#endif +#if defined(HAS_NP) && PROG_LAB>0 + for (ii = 0; ii < (int) now._nr_pr; ii++) + { if (progstate[P__Q->_t][P__Q->_p]) + { trpt->o_pm |= 4; + break; + } } +#endif +#undef P__Q + } +#endif + trpt->o_t = t; trpt->o_n = _n; + trpt->o_ot = ot; trpt->o_tt = tt; + trpt->o_To = To; trpt->o_m = _m; + trpt->tau = 0; +#ifdef RANDOMIZE + trpt->oo_i = ooi; +#endif + if (boq != -1 || (t->atom&2)) + { trpt->tau |= 8; +#ifdef VERI + /* atomic sequence in claim */ + if((trpt-1)->tau&4) + trpt->tau |= 4; + else + trpt->tau &= ~4; + } else + { if ((trpt-1)->tau&4) + trpt->tau &= ~4; + else + trpt->tau |= 4; + } + /* if claim allowed timeout, so */ + /* does the next program-step: */ + if (((trpt-1)->tau&1) && !(trpt->tau&4)) + trpt->tau |= 1; +#else + } else + trpt->tau &= ~8; +#endif + if (boq == -1 && (t->atom&2)) + { From = To = II; nlinks++; + } else +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif + } +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Push_Stack_Tree(II, t->t_id); + } +#endif + goto Down; /* pseudo-recursion */ +Up: +#ifdef CHECK + cpu_printf("%d: Up - %s\n", depth, + (trpt->tau&4)?"claim":"program"); +#endif +#if NCORE>1 + iam_alive(); + #ifdef USE_DISK + mem_drain(); + #endif +#endif +#if defined(MA) || NCORE>1 + if (depth <= 0) return; + /* e.g., if first state is old, after a restart */ +#endif +#ifdef SC + if (CNT1 > CNT2 + && depth < hiwater - (HHH-DDD) + 2) + { + trpt += DDD; + disk2stack(); + maxdepth -= DDD; + hiwater -= DDD; + if(verbose) + printf("unzap %d: %d\n", CNT2, hiwater); + } +#endif +#ifndef NOFAIR + if (trpt->o_pm&128) /* fairness alg */ + { now._cnt[now._a_t&1] = trpt->bup.oval; + _n = 1; trpt->o_pm &= ~128; + depth--; trpt--; +#if defined(VERBOSE) || defined(CHECK) + printf("%3d: reversed fairness default move\n", depth); +#endif + goto Q999; + } +#endif +#ifdef HAS_LAST +#ifdef VERI + { int d; Trail *trl; + now._last = 0; + for (d = 1; d < depth; d++) + { trl = getframe(depth-d); /* was (trpt-d) */ + if (trl->pr != 0) + { now._last = trl->pr - BASE; + break; + } } } +#else + now._last = (depth<1)?0:(trpt-1)->pr; +#endif +#endif +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif +#ifndef SAFETY + if ((now._a_t&1) && depth <= A_depth) + return; /* to checkcycles() */ +#endif + t = trpt->o_t; _n = trpt->o_n; + ot = trpt->o_ot; II = trpt->pr; + tt = trpt->o_tt; this = pptr(II); + To = trpt->o_To; _m = trpt->o_m; +#ifdef RANDOMIZE + ooi = trpt->oo_i; +#endif +#ifdef INLINE_REV + _m = do_reverse(t, II, _m); +#else +#include REVERSE_MOVES +R999: /* jumps here when done */ +#endif +#ifdef VERBOSE + cpu_printf("%3d: proc %d reverses %d, %d to %d\n", + depth, II, t->forw, tt, t->st); + cpu_printf("\t%s [abit=%d,adepth=%d,tau=%d,%d]\n", + t->tp, now._a_t, A_depth, trpt->tau, (trpt-1)->tau); +#endif +#ifndef NOREDUCE + /* pass the proviso tags */ + if ((trpt->tau&8) /* rv or atomic */ + && (trpt->tau&16)) + (trpt-1)->tau |= 16; +#ifdef SAFETY + if ((trpt->tau&8) /* rv or atomic */ + && (trpt->tau&64)) + (trpt-1)->tau |= 64; +#endif +#endif + depth--; trpt--; + +#ifdef NSUCC + trpt->n_succ++; +#endif +#ifdef NIBIS + (trans[ot][tt])->om = _m; /* head of list */ +#endif + /* i.e., not set if rv fails */ + if (_m) + { +#if defined(VERI) && !defined(NP) + if (II == 0 && verbose && !reached[ot][t->st]) + { + printf("depth %d: Claim reached state %d (line %d)\n", + depth, t->st, src_claim [t->st]); + fflush(stdout); + } +#endif + reached[ot][t->st] = 1; + reached[ot][tt] = 1; + } +#ifdef HAS_UNLESS + else trpt->e_state = 0; /* undo */ +#endif + if (_m>_n||(_n>3&&_m!=0)) _n=_m; + ((P0 *)this)->_p = tt; + } /* all options */ +#ifdef RANDOMIZE + if (!t && ooi > 0) + { t = trans[ot][tt]; + #ifdef VERBOSE + printf("randomizer: continue for %d more\n", ooi); + #endif + goto domore; + } + #ifdef VERBOSE + else + printf("randomizer: done\n"); + #endif +#endif +#ifndef NOFAIR + /* Fairness: undo Rule 2 */ + if ((trpt->o_pm&32) + && (trpt->o_pm&64)) + { if (trpt->o_pm&1) + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 1) + now._cnt[now._a_t&1] = 2; +#endif + now._cnt[now._a_t&1] += 1; +#ifdef VERBOSE + printf("%3d: proc %d fairness ", depth, II); + printf("undo Rule 2, cnt=%d, _a_t=%d\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm &= ~(32|64); + } else + { if (_n > 0) + { + trpt->o_pm &= ~64; +#ifdef REVERSE + II = From-1; +#else + II = From+1; +#endif + } } } +#endif +#ifdef VERI + if (II == 0) break; /* never claim */ +#endif + } /* all processes */ +#ifdef NSUCC + tally_succ(trpt->n_succ); +#endif +#ifdef SCHED + if (_n == 0 /* no process could move */ + #ifdef VERI + && II != 0 + #endif + && depth > 0 + && trpt->sched_limit >= sched_max) + { _n = 1; /* not a deadlock */ + } +#endif +#ifndef NOFAIR + /* Fairness: undo Rule 2 */ + if (trpt->o_pm&32) /* remains if proc blocked */ + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 1) + now._cnt[now._a_t&1] = 2; +#endif + now._cnt[now._a_t&1] += 1; +#ifdef VERBOSE + printf("%3d: proc -- fairness ", depth); + printf("undo Rule 2, cnt=%d, _a_t=%d\n", + now._cnt[now._a_t&1], now._a_t); +#endif + trpt->o_pm &= ~32; + } +#ifndef NP + if (fairness + && _n == 0 /* nobody moved */ +#ifdef VERI + && !(trpt->tau&4) /* in program move */ +#endif + && !(trpt->tau&8) /* not an atomic one */ +#ifdef OTIM + && ((trpt->tau&1) || endstate()) +#else +#ifdef ETIM + && (trpt->tau&1) /* already tried timeout */ +#endif +#endif +#ifndef NOREDUCE + /* see below */ + && !((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) +#endif + && now._cnt[now._a_t&1] > 0) /* needed more procs */ + { depth++; trpt++; + trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4)); + trpt->bup.oval = now._cnt[now._a_t&1]; + now._cnt[now._a_t&1] = 1; +#ifdef VERI + trpt->tau = 4; +#else + trpt->tau = 0; +#endif +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif +#if defined(VERBOSE) || defined(CHECK) + printf("%3d: fairness default move ", depth); + printf("(all procs block)\n"); +#endif + goto Down; + } +#endif +Q999: /* returns here with _n>0 when done */; + if (trpt->o_pm&8) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + trpt->o_pm &= ~8; +#ifdef VERBOSE + printf("%3d: fairness undo Rule 1, _a_t=%d\n", + depth, now._a_t); +#endif + } + if (trpt->o_pm&16) + { now._a_t |= 2; + now._cnt[now._a_t&1] = 1; + trpt->o_pm &= ~16; +#ifdef VERBOSE + printf("%3d: fairness undo Rule 3, _a_t=%d\n", + depth, now._a_t); +#endif + } +#endif +#ifndef NOREDUCE +#ifdef SAFETY +#ifdef LOOPSTATE + /* at least one move that was preselected at this */ + /* level, blocked or was a loop control flow point */ + if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) +#else + /* preselected move - no successors outside stack */ + if ((trpt->tau&32) && !(trpt->tau&64)) +#endif +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif +#ifdef DEBUG + printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, _n, trpt->tau); +#endif + _n = 0; trpt->tau &= ~(16|32|64); +#ifdef REVERSE + if (II <= To) /* II already decremented */ +#else + if (II >= BASE) /* II already decremented */ +#endif + goto Resume; + else + goto Again; + } +#else + /* at least one move that was preselected at this */ + /* level, blocked or truncated at the next level */ +/* implied: #ifdef FULLSTACK */ + if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16))) + { +#ifdef DEBUG + printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", + depth, II+1, (int) _n, trpt->tau); +#endif + if (a_cycles && (trpt->tau&16)) + { if (!(now._a_t&1)) + { +#ifdef DEBUG + printf("%3d: setting proviso bit\n", depth); +#endif +#ifndef BITSTATE +#ifdef MA +#ifdef VERI + (trpt-1)->proviso = 1; +#else + trpt->proviso = 1; +#endif +#else +#ifdef VERI + if ((trpt-1)->ostate) + ((char *)&((trpt-1)->ostate->state))[0] |= 128; +#else + ((char *)&(trpt->ostate->state))[0] |= 128; +#endif +#endif +#else +#ifdef VERI + if ((trpt-1)->ostate) + (trpt-1)->ostate->proviso = 1; +#else + trpt->ostate->proviso = 1; +#endif +#endif +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif + _n = 0; trpt->tau &= ~(16|32|64); + goto Again; /* do full search */ + } /* else accept reduction */ + } else +#ifdef REVERSE + { From = BASE; To = now._nr_pr-1; +#else + { From = now._nr_pr-1; To = BASE; +#endif + _n = 0; trpt->tau &= ~(16|32|64); +#ifdef REVERSE + if (II <= To) /* already decremented */ +#else + if (II >= BASE) /* already decremented */ +#endif + goto Resume; + else + goto Again; + } } +/* #endif */ +#endif +#endif + if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2))) + { +#ifdef DEBUG + cpu_printf("%3d: no move [II=%d, tau=%d, boq=%d]\n", + depth, II, trpt->tau, boq); +#endif +#if SYNC + /* ok if a rendez-vous fails: */ + if (boq != -1) goto Done; +#endif + /* ok if no procs or we're at maxdepth */ + if ((now._nr_pr == 0 && (!strict || qs_empty())) +#ifdef OTIM + || endstate() +#endif + || depth >= maxdepth-1) goto Done; + if ((trpt->tau&8) && !(trpt->tau&4)) + { trpt->tau &= ~(1|8); + /* 1=timeout, 8=atomic */ +#ifdef REVERSE + From = BASE; To = now._nr_pr-1; +#else + From = now._nr_pr-1; To = BASE; +#endif +#ifdef DEBUG + cpu_printf("%3d: atomic step proc %d unexecutable\n", depth, II+1); +#endif +#ifdef VERI + trpt->tau |= 4; /* switch to claim */ +#endif + goto AllOver; + } +#ifdef ETIM + if (!(trpt->tau&1)) /* didn't try timeout yet */ + { +#ifdef VERI + if (trpt->tau&4) + { +#ifndef NTIM + if (trpt->tau&2) /* requested */ +#endif + { trpt->tau |= 1; + trpt->tau &= ~2; +#ifdef DEBUG + cpu_printf("%d: timeout\n", depth); +#endif + goto Stutter; + } } + else + { /* only claim can enable timeout */ + if ((trpt->tau&8) + && !((trpt-1)->tau&4)) +/* blocks inside an atomic */ goto BreakOut; +#ifdef DEBUG + cpu_printf("%d: req timeout\n", + depth); +#endif + (trpt-1)->tau |= 2; /* request */ +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +#else +#ifdef DEBUG + cpu_printf("%d: timeout\n", depth); +#endif + trpt->tau |= 1; + goto Again; +#endif + } +#endif +#ifdef VERI +BreakOut: +#ifndef NOSTUTTER + if (!(trpt->tau&4)) + { trpt->tau |= 4; /* claim stuttering */ + trpt->tau |= 128; /* stutter mark */ +#ifdef DEBUG + cpu_printf("%d: claim stutter\n", depth); +#endif + goto Stutter; + } +#else + ; +#endif +#else + if (!noends && !a_cycles && !endstate()) + { depth--; trpt--; /* new 4.2.3 */ + uerror("invalid end state"); + depth++; trpt++; + } +#ifndef NOSTUTTER + else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */ + { depth--; trpt--; + uerror("accept stutter"); + depth++; trpt++; + } +#endif +#endif + } +Done: + if (!(trpt->tau&8)) /* not in atomic seqs */ + { +#ifndef SAFETY + if (_n != 0 +#ifdef VERI + /* --after-- a program-step, i.e., */ + /* after backtracking a claim-step */ + && (trpt->tau&4) + /* with at least one running process */ + /* unless in a stuttered accept state */ + && ((now._nr_pr > 1) || (trpt->o_pm&2)) +#endif + && !(now._a_t&1)) + { +#ifndef NOFAIR + if (fairness) + { +#ifdef VERBOSE + cpu_printf("Consider check %d %d...\n", + now._a_t, now._cnt[0]); +#endif + if ((now._a_t&2) /* A-bit */ + && (now._cnt[0] == 1)) + checkcycles(); + } else +#endif + if (a_cycles && (trpt->o_pm&2)) + checkcycles(); + } +#endif +#ifndef MA +#if defined(FULLSTACK) || defined(CNTRSTACK) +#ifdef VERI + if (boq == -1 + && (((trpt->tau&4) && !(trpt->tau&128)) + || ( (trpt-1)->tau&128))) +#else + if (boq == -1) +#endif + { +#ifdef DEBUG2 +#if defined(FULLSTACK) + printf("%d: zapping %u (%d)\n", + depth, trpt->ostate, + (trpt->ostate)?trpt->ostate->tagged:0); +#endif +#endif + onstack_zap(); + } +#endif +#else +#ifdef VERI + if (boq == -1 + && (((trpt->tau&4) && !(trpt->tau&128)) + || ( (trpt-1)->tau&128))) +#else + if (boq == -1) +#endif + { +#ifdef DEBUG + printf("%d: zapping\n", depth); +#endif + onstack_zap(); +#ifndef NOREDUCE + if (trpt->proviso) + gstore((char *) &now, vsize, 1); +#endif + } +#endif + } + if (depth > 0) + { +#if NCORE>1 && defined(FULL_TRAIL) + if (upto > 0) + { Pop_Stack_Tree(); + } +#endif + goto Up; + } +} + +#else +void new_state(void) { /* place holder */ } +#endif + +void +assert(int a, char *s, int ii, int tt, Trans *t) +{ + if (!a && !noasserts) + { char bad[1024]; + strcpy(bad, "assertion violated "); + if (strlen(s) > 1000) + { strncpy(&bad[19], (const char *) s, 1000); + bad[1019] = '\0'; + } else + strcpy(&bad[19], s); + uerror(bad); + } +} +#ifndef NOBOUNDCHECK +int +Boundcheck(int x, int y, int a1, int a2, Trans *a3) +{ + assert((x >= 0 && x < y), "- invalid array index", + a1, a2, a3); + return x; +} +#endif +void +wrap_stats(void) +{ + if (nShadow>0) + printf("%9.8g states, stored (%g visited)\n", + nstates - nShadow, nstates); + else + printf("%9.8g states, stored\n", nstates); +#ifdef BFS +#if SYNC + printf(" %8g nominal states (- rv and atomic)\n", nstates-midrv-nlinks+revrv); + printf(" %8g rvs succeeded\n", midrv-failedrv); +#else + printf(" %8g nominal states (stored-atomic)\n", nstates-nlinks); +#endif +#ifdef DEBUG + printf(" %8g midrv\n", midrv); + printf(" %8g failedrv\n", failedrv); + printf(" %8g revrv\n", revrv); +#endif +#endif + printf("%9.8g states, matched\n", truncs); +#ifdef CHECK + printf("%9.8g matches within stack\n",truncs2); +#endif + if (nShadow>0) + printf("%9.8g transitions (= visited+matched)\n", + nstates+truncs); + else + printf("%9.8g transitions (= stored+matched)\n", + nstates+truncs); + printf("%9.8g atomic steps\n", nlinks); + if (nlost) printf("%g lost messages\n", (double) nlost); + +#ifndef BITSTATE + printf("hash conflicts: %9.8g (resolved)\n", hcmp); + #ifndef AUTO_RESIZE + if (hcmp > (double) (1< 100.)\n\n", + (double)(((double) udmem) * 8.0) / (double) nstates); + else + printf("\nhash factor: %4g (best if > 100.)\n\n", + (double)(1<<(ssize-8)) / (double) nstates * 256.0); + printf("bits set per state: %u (-k%u)\n", hfns, hfns); + #if 0 + if (udmem) + { printf("total bits available: %8g (-M%ld)\n", + ((double) udmem) * 8.0, udmem/(1024L*1024L)); + } else + printf("total bits available: %8g (-w%d)\n", + ((double) (ONE_L << (ssize-4)) * 16.0), ssize); + #endif +#endif +#ifdef BFS_DISK + printf("bfs disk reads: %ld writes %ld -- diff %ld\n", + bfs_dsk_reads, bfs_dsk_writes, bfs_dsk_writes-bfs_dsk_reads); + if (bfs_dsk_read >= 0) (void) close(bfs_dsk_read); + if (bfs_dsk_write >= 0) (void) close(bfs_dsk_write); + (void) unlink("pan_bfs_dsk.tmp"); +#endif +} + +void +wrapup(void) +{ +#if defined(BITSTATE) || !defined(NOCOMP) + double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0; + #if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM)) + int mverbose = 1; + #else + int mverbose = verbose; + #endif +#endif +#if NCORE>1 + if (verbose) cpu_printf("wrapup -- %d error(s)\n", errors); + if (core_id != 0) + { +#ifdef USE_DISK + void dsk_stats(void); + dsk_stats(); +#endif + if (search_terminated != NULL) + { *search_terminated |= 2; /* wrapup */ + } + exit(0); /* normal termination, not an error */ + } +#endif +#if !defined(WIN32) && !defined(WIN64) + signal(SIGINT, SIG_DFL); +#endif + printf("\n(%s)\n", SpinVersion); + if (!done) printf("Warning: Search not completed\n"); +#ifdef SC + (void) unlink((const char *)stackfile); +#endif +#if NCORE>1 + if (a_cycles) + { printf(" + Multi-Core (NCORE=%d)\n", NCORE); + } else + { printf(" + Multi-Core (NCORE=%d -z%d)\n", NCORE, z_handoff); + } +#endif +#ifdef BFS + printf(" + Using Breadth-First Search\n"); +#endif +#ifndef NOREDUCE + printf(" + Partial Order Reduction\n"); +#endif +#ifdef REVERSE + printf(" + Reverse Depth-First Search Order\n"); +#endif +#ifdef T_REVERSE + printf(" + Reverse Transition Ordering\n"); +#endif +#ifdef RANDOMIZE + printf(" + Randomized Transition Ordering\n"); +#endif +#ifdef SCHED + printf(" + Scheduling Restriction (-DSCHED=%d)\n", sched_max); +#endif +#ifdef COLLAPSE + printf(" + Compression\n"); +#endif +#ifdef MA + printf(" + Graph Encoding (-DMA=%d)\n", MA); + #ifdef R_XPT + printf(" Restarted from checkpoint %s.xpt\n", PanSource); + #endif +#endif +#ifdef CHECK + #ifdef FULLSTACK + printf(" + FullStack Matching\n"); + #endif + #ifdef CNTRSTACK + printf(" + CntrStack Matching\n"); + #endif +#endif +#ifdef BITSTATE + printf("\nBit statespace search for:\n"); +#else +#ifdef HC + printf("\nHash-Compact %d search for:\n", HC); +#else + printf("\nFull statespace search for:\n"); +#endif +#endif +#ifdef EVENT_TRACE +#ifdef NEGATED_TRACE + printf(" notrace assertion +\n"); +#else + printf(" trace assertion +\n"); +#endif +#endif +#ifdef VERI + printf(" never claim +\n"); + printf(" assertion violations "); + if (noasserts) + printf("- (disabled by -A flag)\n"); + else + printf("+ (if within scope of claim)\n"); +#else +#ifdef NOCLAIM + printf(" never claim - (not selected)\n"); +#else + printf(" never claim - (none specified)\n"); +#endif + printf(" assertion violations "); + if (noasserts) + printf("- (disabled by -A flag)\n"); + else + printf("+\n"); +#endif +#ifndef SAFETY +#ifdef NP + printf(" non-progress cycles "); +#else + printf(" acceptance cycles "); +#endif + if (a_cycles) + printf("+ (fairness %sabled)\n", + fairness?"en":"dis"); + else printf("- (not selected)\n"); +#else + printf(" cycle checks - (disabled by -DSAFETY)\n"); +#endif +#ifdef VERI + printf(" invalid end states - "); + printf("(disabled by "); + if (noends) + printf("-E flag)\n\n"); + else + printf("never claim)\n\n"); +#else + printf(" invalid end states "); + if (noends) + printf("- (disabled by -E flag)\n\n"); + else + printf("+\n\n"); +#endif + printf("State-vector %d byte, depth reached %ld", hmax, +#if NCORE>1 + (nr_handoffs * z_handoff) + +#endif + mreached); + printf(", errors: %d\n", errors); + fflush(stdout); +#ifdef MA + if (done) + { extern void dfa_stats(void); + if (maxgs+a_cycles+2 < MA) + printf("MA stats: -DMA=%d is sufficient\n", + maxgs+a_cycles+2); + dfa_stats(); + } +#endif + wrap_stats(); +#ifdef CHECK + printf("stackframes: %d/%d\n\n", smax, svmax); + printf("stats: fa %d, fh %d, zh %d, zn %d - ", + Fa, Fh, Zh, Zn); + printf("check %d holds %d\n", Ccheck, Cholds); + printf("stack stats: puts %d, probes %d, zaps %d\n", + PUT, PROBE, ZAPS); +#else + printf("\n"); +#endif + +#if defined(BITSTATE) || !defined(NOCOMP) + nr1 = (nstates-nShadow)* + (double)(hmax+sizeof(struct H_el)-sizeof(unsigned)); +#ifdef BFS + nr2 = 0.0; +#else + nr2 = (double) ((maxdepth+3)*sizeof(Trail)); +#endif +#ifndef BITSTATE +#if !defined(MA) || defined(COLLAPSE) + nr3 = (double) (ONE_L<1 && !defined(SEP_STATE) + tmp_nr -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE; +#endif + if (tmp_nr < 0.0) tmp_nr = 0.; + printf("Stats on memory usage (in Megabytes):\n"); + printf("%9.3f equivalent memory usage for states", + nr1/1048576.); /* 1024*1024=1048576 */ + printf(" (stored*(State-vector + overhead))\n"); + #if NCORE>1 && !defined(WIN32) && !defined(WIN64) + printf("%9.3f shared memory reserved for state storage\n", + mem_reserved/1048576.); + #ifdef SEP_HEAP + printf(" in %d local heaps of %7.3f MB each\n", + NCORE, mem_reserved/(NCORE*1048576.)); + #endif + printf("\n"); + #endif +#ifdef BITSTATE + if (udmem) + printf("%9.3f memory used for hash array (-M%ld)\n", + nr3/1048576., udmem/(1024L*1024L)); + else + printf("%9.3f memory used for hash array (-w%d)\n", + nr3/1048576., ssize); + if (nr5 > 0.0) + printf("%9.3f memory used for bit stack\n", + nr5/1048576.); + remainder = remainder - nr3 - nr5; +#else + printf("%9.3f actual memory usage for states", + tmp_nr/1048576.); + remainder -= tmp_nr; + printf(" ("); + if (tmp_nr > 0.) + { if (tmp_nr > nr1) printf("unsuccessful "); + printf("compression: %.2f%%)\n", + (100.0*tmp_nr)/nr1); + } else + printf("less than 1k)\n"); +#ifndef MA + if (tmp_nr > 0.) + { printf(" state-vector as stored = %.0f byte", + (tmp_nr)/(nstates-nShadow) - + (double) (sizeof(struct H_el) - sizeof(unsigned))); + printf(" + %ld byte overhead\n", + (long int) sizeof(struct H_el)-sizeof(unsigned)); + } +#endif +#if !defined(MA) || defined(COLLAPSE) + printf("%9.3f memory used for hash table (-w%d)\n", + nr3/1048576., ssize); + remainder -= nr3; +#endif +#endif +#ifndef BFS + printf("%9.3f memory used for DFS stack (-m%ld)\n", + nr2/1048576., maxdepth); + remainder -= nr2; +#endif +#if NCORE>1 + remainder -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE; + printf("%9.3f shared memory used for work-queues\n", + (GWQ_SIZE + (double) NCORE * LWQ_SIZE) /1048576.); + printf(" in %d queues of %7.3f MB each", + NCORE, (double) LWQ_SIZE /1048576.); + #ifndef NGQ + printf(" + a global q of %7.3f MB\n", + (double) GWQ_SIZE / 1048576.); + #else + printf("\n"); + #endif + #endif + if (remainder - fragment > 1048576.) + printf("%9.3f other (proc and chan stacks)\n", + (remainder-fragment)/1048576.); + if (fragment > 1048576.) + printf("%9.3f memory lost to fragmentation\n", + fragment/1048576.); + printf("%9.3f total actual memory usage\n\n", + memcnt/1048576.); + } +#ifndef MA + else +#endif +#endif +#ifndef MA + printf("%9.3f memory usage (Mbyte)\n\n", + memcnt/1048576.); +#endif +#ifdef COLLAPSE + printf("nr of templates: [ globals chans procs ]\n"); + printf("collapse counts: [ "); + { int i; for (i = 0; i < 256+2; i++) + if (ncomps[i] != 0) + printf("%d ", ncomps[i]); + printf("]\n"); + } +#endif + if ((done || verbose) && !no_rck) do_reach(); +#ifdef PEG + { int i; + printf("\nPeg Counts (transitions executed):\n"); + for (i = 1; i < NTRANS; i++) + { if (peg[i]) putpeg(i, peg[i]); + } } +#endif +#ifdef VAR_RANGES + dumpranges(); +#endif +#ifdef SVDUMP + if (vprefix > 0) close(svfd); +#endif +#ifdef LOOPSTATE + printf("%g loopstates hit\n", cnt_loops); +#endif +#ifdef NSUCC + dump_succ(); +#endif +#if NCORE>1 && defined(T_ALERT) + crash_report(); +#endif + pan_exit(0); +} + +void +stopped(int arg) +{ printf("Interrupted\n"); +#if NCORE>1 + was_interrupted = 1; +#endif + wrapup(); + pan_exit(0); +} + +#ifdef SFH +/* + * super fast hash, based on Paul Hsieh's function + * http://www.azillionmonkeys.com/qed/hash.html + */ +#include + #undef get16bits + #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) + #define get16bits(d) (*((const uint16_t *) (d))) + #endif + + #ifndef get16bits + #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) + #endif + +void +d_sfh(const char *s, int len) +{ uint32_t h = len, tmp; + int rem; + + rem = len & 3; + len >>= 2; + + for ( ; len > 0; len--) + { h += get16bits(s); + tmp = (get16bits(s+2) << 11) ^ h; + h = (h << 16) ^ tmp; + s += 2*sizeof(uint16_t); + h += h >> 11; + } + switch (rem) { + case 3: h += get16bits(s); + h ^= h << 16; + h ^= s[sizeof(uint16_t)] << 18; + h += h >> 11; + break; + case 2: h += get16bits(s); + h ^= h << 11; + h += h >> 17; + break; + case 1: h += *s; + h ^= h << 10; + h += h >> 1; + break; + } + h ^= h << 3; + h += h >> 5; + h ^= h << 4; + h += h >> 17; + h ^= h << 25; + h += h >> 6; + + K1 = h; +} +#endif + +#include +#if defined(HASH64) || defined(WIN64) +/* 64-bit Jenkins hash, 1997 + * http://burtleburtle.net/bob/c/lookup8.c + */ +#define mix(a,b,c) \ +{ a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} +#else +/* 32-bit Jenkins hash, 2006 + * http://burtleburtle.net/bob/c/lookup3.c + */ +#define rot(x,k) (((x)<<(k))|((x)>>(32-(k)))) + +#define mix(a,b,c) \ +{ a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +#define final(a,b,c) \ +{ c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} +#endif + +void +d_hash(uchar *kb, int nbytes) +{ uint8_t *bp; +#if defined(HASH64) || defined(WIN64) + uint64_t a = 0, b, c, n; + uint64_t *k = (uint64_t *) kb; +#else + uint32_t a, b, c, n; + uint32_t *k = (uint32_t *) kb; +#endif + /* extend to multiple of words, if needed */ + n = nbytes/WS; /* nr of words */ + a = nbytes - (n*WS); + if (a > 0) + { n++; + bp = kb + nbytes; + switch (a) { + case 3: *bp++ = 0; /* fall thru */ + case 2: *bp++ = 0; /* fall thru */ + case 1: *bp = 0; + case 0: break; + } } +#if defined(HASH64) || defined(WIN64) + b = HASH_CONST[HASH_NR]; + c = 0x9e3779b97f4a7c13LL; /* arbitrary value */ + while (n >= 3) + { a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + n -= 3; + k += 3; + } + c += (((uint64_t) nbytes)<<3); + switch (n) { + case 2: b += k[1]; + case 1: a += k[0]; + case 0: break; + } + mix(a,b,c); +#else + a = c = 0xdeadbeef + (n<<2); + b = HASH_CONST[HASH_NR]; + while (n > 3) + { a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + n -= 3; + k += 3; + } + switch (n) { + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + case 0: break; + } + final(a,b,c); +#endif + j1 = c&nmask; j3 = a&7; /* 1st bit */ + j2 = b&nmask; j4 = (a>>3)&7; /* 2nd bit */ + K1 = c; K2 = b; +} + +void +s_hash(uchar *cp, int om) +{ +#if defined(SFH) + d_sfh((const char *) cp, om); /* sets K1 */ +#else + d_hash(cp, om); /* sets K1 etc */ +#endif +#ifdef BITSTATE + if (S_Tab == H_tab) + j1 = K1 % omaxdepth; + else +#endif + if (ssize < 8*WS) + j1 = K1&mask; + else + j1 = K1; +} +#ifndef RANDSTOR +int *prerand; +void +inirand(void) +{ int i; + srand(123); /* fixed startpoint */ + prerand = (int *) emalloc((omaxdepth+3)*sizeof(int)); + for (i = 0; i < omaxdepth+3; i++) + prerand[i] = rand(); +} +int +pan_rand(void) +{ if (!prerand) inirand(); + return prerand[depth]; +} +#endif + +void +set_masks(void) /* 4.2.5 */ +{ + if (WS == 4 && ssize >= 32) + { mask = 0xffffffff; +#ifdef BITSTATE + switch (ssize) { + case 34: nmask = (mask>>1); break; + case 33: nmask = (mask>>2); break; + default: nmask = (mask>>3); break; + } +#else + nmask = mask; +#endif + } else if (WS == 8) + { mask = ((ONE_L<>3; +#else + nmask = mask; +#endif + } else if (WS != 4) + { fprintf(stderr, "pan: wordsize %ld not supported\n", (long int) WS); + exit(1); + } else /* WS == 4 and ssize < 32 */ + { mask = ((ONE_L<>3); + } +} + +static long reclaim_size; +static char *reclaim_mem; +#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA) +#if NCORE>1 + #error cannot combine AUTO_RESIZE with NCORE>1 yet +#endif +static struct H_el **N_tab; +void +reverse_capture(struct H_el *p) +{ if (!p) return; + reverse_capture(p->nxt); + /* last element of list moves first */ + /* to preserve list-order */ + j2 = p->m_K1; + if (ssize < 8*WS) /* probably always true */ + { j2 &= mask; + } + p->nxt = N_tab[j2]; + N_tab[j2] = p; +} +void +resize_hashtable(void) +{ + if (WS == 4 && ssize >= 27 - 1) + { return; /* canot increase further */ + } + + ssize += 2; /* 4x size */ + + printf("pan: resizing hashtable to -w%d.. ", ssize); + + N_tab = (struct H_el **) + emalloc((ONE_L<1 + { int i, j; + strcpy(o_cmdline, ""); + for (j = 1; j < argc; j++) + { strcat(o_cmdline, argv[j]); + strcat(o_cmdline, " "); + } + /* printf("Command Line: %s\n", o_cmdline); */ + if (strlen(o_cmdline) >= sizeof(o_cmdline)) + { Uerror("option list too long"); + } } +#endif + while (argc > 1 && argv[1][0] == '-') + { switch (argv[1][1]) { +#ifndef SAFETY +#ifdef NP + case 'a': fprintf(efd, "error: -a disabled"); + usage(efd); break; +#else + case 'a': a_cycles = 1; break; +#endif +#endif + case 'A': noasserts = 1; break; + case 'b': bounded = 1; break; +#ifdef HAS_CODE + case 'C': coltrace = 1; goto samething; +#endif + case 'c': upto = atoi(&argv[1][2]); break; + case 'd': state_tables++; break; + case 'e': every_error = 1; Nr_Trails = 1; break; + case 'E': noends = 1; break; +#ifdef SC + case 'F': if (strlen(argv[1]) > 2) + stackfile = &argv[1][2]; + break; +#endif +#if !defined(SAFETY) && !defined(NOFAIR) + case 'f': fairness = 1; break; +#endif +#ifdef HAS_CODE + case 'g': gui = 1; goto samething; +#endif + case 'h': if (!argv[1][2]) usage(efd); else + HASH_NR = atoi(&argv[1][2])%33; break; + case 'I': iterative = 2; every_error = 1; break; + case 'i': iterative = 1; every_error = 1; break; + case 'J': like_java = 1; break; /* Klaus Havelund */ +#ifdef BITSTATE + case 'k': hfns = atoi(&argv[1][2]); break; +#endif +#ifdef SCHED + case 'L': sched_max = atoi(&argv[1][2]); break; +#endif +#ifndef SAFETY +#ifdef NP + case 'l': a_cycles = 1; break; +#else + case 'l': fprintf(efd, "error: -l disabled"); + usage(efd); break; +#endif +#endif +#ifdef BITSTATE + case 'M': udmem = atoi(&argv[1][2]); break; + case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break; +#else + case 'M': case 'G': + fprintf(stderr, "-M and -G affect only -DBITSTATE\n"); + break; +#endif + case 'm': maxdepth = atoi(&argv[1][2]); break; + case 'n': no_rck = 1; break; + case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]); + if (argv[2][0] != '-') /* check next arg */ + { trailfilename = argv[2]; + argc--; argv++; /* skip next arg */ + } + break; +#ifdef SVDUMP + case 'p': vprefix = atoi(&argv[1][2]); break; +#endif +#if NCORE==1 + case 'Q': quota = (double) 60.0 * (double) atoi(&argv[1][2]); break; +#endif + case 'q': strict = 1; break; + case 'R': Nrun = atoi(&argv[1][2]); break; +#ifdef HAS_CODE + case 'r': +samething: readtrail = 1; + if (isdigit(argv[1][2])) + whichtrail = atoi(&argv[1][2]); + else if (argc > 2 && argv[2][0] != '-') /* check next arg */ + { trailfilename = argv[2]; + argc--; argv++; /* skip next arg */ + } + break; + case 'S': silent = 1; goto samething; +#endif +#ifdef BITSTATE + case 's': hfns = 1; break; +#endif + case 'T': TMODE = 0444; break; + case 't': if (argv[1][2]) tprefix = &argv[1][2]; break; + case 'V': start_timer(); printf("Generated by %s\n", SpinVersion); + to_compile(); pan_exit(2); break; + case 'v': verbose++; break; + case 'w': ssize = atoi(&argv[1][2]); break; + case 'Y': signoff = 1; break; + case 'X': efd = stdout; break; + case 'x': exclusive = 1; break; +#if NCORE>1 + /* -B ip is passthru to proxy of remote ip address: */ + case 'B': argc--; argv++; break; + case 'Q': worker_pids[0] = atoi(&argv[1][2]); break; + /* -Un means that the nth worker should be instantiated as a proxy */ + case 'U': proxy_pid = atoi(&argv[1][2]); break; + /* -W means that this copy is started by a cluster-server as a remote */ + /* this flag is passed to ./pan_proxy, which interprets it */ + case 'W': remote_party++; break; + case 'Z': core_id = atoi(&argv[1][2]); + if (verbose) + { printf("cpu%d: pid %d parent %d\n", + core_id, getpid(), worker_pids[0]); + } + break; + case 'z': z_handoff = atoi(&argv[1][2]); break; +#else + case 'z': break; /* ignored for single-core */ +#endif + default : fprintf(efd, "saw option -%c\n", argv[1][1]); usage(efd); break; + } + argc--; argv++; + } + if (iterative && TMODE != 0666) + { TMODE = 0666; + fprintf(efd, "warning: -T ignored when -i or -I is used\n"); + } +#if defined(HASH32) && !defined(SFH) + if (WS > 4) + { fprintf(efd, "strong warning: compiling -DHASH32 on a 64-bit machine\n"); + fprintf(efd, " without -DSFH can slow down performance a lot\n"); + } +#endif +#if defined(WIN32) || defined(WIN64) + if (TMODE == 0666) + TMODE = _S_IWRITE | _S_IREAD; + else + TMODE = _S_IREAD; +#endif +#if NCORE>1 + store_proxy_pid = proxy_pid; /* for checks in mem_file() and someone_crashed() */ + if (core_id != 0) { proxy_pid = 0; } + #ifndef SEP_STATE + if (core_id == 0 && a_cycles) + { fprintf(efd, "hint: this search may be more efficient "); + fprintf(efd, "if pan.c is compiled -DSEP_STATE\n"); + } + #endif + if (z_handoff < 0) + { z_handoff = 20; /* conservative default - for non-liveness checks */ + } +#if defined(NGQ) || defined(LWQ_FIXED) + LWQ_SIZE = (double) (128.*1048576.); +#else + LWQ_SIZE = (double) ( z_handoff + 2.) * (double) sizeof(SM_frame); +#endif + #if NCORE>2 + if (a_cycles) + { fprintf(efd, "warning: the intended nr of cores to be used in liveness mode is 2\n"); + #ifndef SEP_STATE + fprintf(efd, "warning: without -DSEP_STATE there is no guarantee that all liveness violations are found\n"); + #endif + } + #endif + #ifdef HAS_HIDDEN + #error cannot use hidden variables when compiling multi-core + #endif +#endif +#ifdef BITSTATE + if (hfns <= 0) + { hfns = 1; + fprintf(efd, "warning: using -k%d as minimal usable value\n", hfns); + } +#endif + omaxdepth = maxdepth; +#ifdef BITSTATE + if (WS == 4 && ssize > 34) + { ssize = 34; + fprintf(efd, "warning: using -w%d as max usable value\n", ssize); +/* + * -w35 would not work: 35-3 = 32 but 1^31 is the largest + * power of 2 that can be represented in an unsigned long + */ + } +#else + if (WS == 4 && ssize > 27) + { ssize = 27; + fprintf(efd, "warning: using -w%d as max usable value\n", ssize); +/* + * for emalloc, the lookup table size multiplies by 4 for the pointers + * the largest power of 2 that can be represented in a ulong is 1^31 + * hence the largest number of lookup table slots is 31-4 = 27 + */ + } +#endif +#ifdef SC + hiwater = HHH = maxdepth-10; + DDD = HHH/2; + if (!stackfile) + { stackfile = (char *) emalloc(strlen(PanSource)+4+1); + sprintf(stackfile, "%s._s_", PanSource); + } + if (iterative) + { fprintf(efd, "error: cannot use -i or -I with -DSC\n"); + pan_exit(1); + } +#endif +#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA) + #warning -DR_XPT and -DW_XPT assume -DMA (ignored) +#endif + if (iterative && a_cycles) + fprintf(efd, "warning: -i or -I work for safety properties only\n"); +#ifdef BFS + #ifdef SC + #error -DBFS not compatible with -DSC + #endif + #ifdef HAS_LAST + #error -DBFS not compatible with _last + #endif + #ifdef HAS_STACK + #error cannot use c_track UnMatched with BFS + #endif + #ifdef REACH + #warning -DREACH is redundant when -DBFS is used + #endif +#endif +#if defined(MERGED) && defined(PEG) + #error to use -DPEG use: spin -o3 -a +#endif +#ifdef HC + #ifdef SFH + #error cannot combine -DHC and -DSFH + /* use of NOCOMP is the real reason */ + #else + #ifdef NOCOMP + #error cannot combine -DHC and -DNOCOMP + #endif + #endif + #ifdef BITSTATE + #error cannot combine -DHC and -DBITSTATE + #endif +#endif +#if defined(SAFETY) && defined(NP) + #error cannot combine -DNP and -DBFS or -DSAFETY +#endif +#ifdef MA + #ifdef BITSTATE + #error cannot combine -DMA and -DBITSTATE + #endif + #if MA <= 0 + #error usage: -DMA=N with N > 0 and N < VECTORSZ + #endif +#endif +#ifdef COLLAPSE + #ifdef BITSTATE + #error cannot combine -DBITSTATE and -DCOLLAPSE + #endif + #ifdef SFH + #error cannot combine -DCOLLAPSE and -DSFH + /* use of NOCOMP is the real reason */ + #else + #ifdef NOCOMP + #error cannot combine -DCOLLAPSE and -DNOCOMP + #endif + #endif +#endif + if (maxdepth <= 0 || ssize <= 1) usage(efd); +#if SYNC>0 && !defined(NOREDUCE) + if (a_cycles && fairness) + { fprintf(efd, "error: p.o. reduction not compatible with "); + fprintf(efd, "fairness (-f) in models\n"); + fprintf(efd, " with rendezvous operations: "); + fprintf(efd, "recompile with -DNOREDUCE\n"); + pan_exit(1); + } +#endif +#if defined(REM_VARS) && !defined(NOREDUCE) + #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE) +#endif +#if defined(NOCOMP) && !defined(BITSTATE) + if (a_cycles) + { fprintf(efd, "error: use of -DNOCOMP voids -l and -a\n"); + pan_exit(1); + } +#endif +#ifdef MEMLIM + memlim = ((double) MEMLIM) * (double) (1<<20); /* size in Mbyte */ +#endif +#ifndef BITSTATE + if (Nrun > 1) HASH_NR = Nrun - 1; +#endif + if (Nrun < 1 || Nrun > 32) + { fprintf(efd, "error: invalid arg for -R\n"); + usage(efd); + } +#ifndef SAFETY + if (fairness && !a_cycles) + { fprintf(efd, "error: -f requires -a or -l\n"); + usage(efd); + } + #if ACCEPT_LAB==0 + if (a_cycles) + { fprintf(efd, "error: no accept labels defined "); + fprintf(efd, "in model (for option -a)\n"); + usage(efd); + } + #endif +#endif +#ifndef NOREDUCE + #ifdef HAS_ENABLED + #error use of enabled() requires -DNOREDUCE + #endif + #ifdef HAS_PCVALUE + #error use of pcvalue() requires -DNOREDUCE + #endif + #ifdef HAS_BADELSE + #error use of 'else' combined with i/o stmnts requires -DNOREDUCE + #endif + #ifdef HAS_LAST + #error use of _last requires -DNOREDUCE + #endif +#endif +#if SYNC>0 && !defined(NOREDUCE) + #ifdef HAS_UNLESS + fprintf(efd, "warning: use of a rendezvous stmnts in the escape\n"); + fprintf(efd, " of an unless clause, if present, could make p.o. reduction\n"); + fprintf(efd, " invalid (use -DNOREDUCE to avoid this)\n"); + #ifdef BFS + fprintf(efd, " (this type of rv is also not compatible with -DBFS)\n"); + #endif + #endif +#endif +#if SYNC>0 && defined(BFS) + #warning use of rendezvous with BFS does not preserve all invalid endstates +#endif +#if !defined(REACH) && !defined(BITSTATE) + if (iterative != 0 && a_cycles == 0) + { fprintf(efd, "warning: -i and -I need -DREACH to work accurately\n"); + } +#endif +#if defined(BITSTATE) && defined(REACH) + #warning -DREACH is voided by -DBITSTATE +#endif +#if defined(MA) && defined(REACH) + #warning -DREACH is voided by -DMA +#endif +#if defined(FULLSTACK) && defined(CNTRSTACK) + #error cannot combine -DFULLSTACK and -DCNTRSTACK +#endif +#if defined(VERI) + #if ACCEPT_LAB>0 + #ifndef BFS + if (!a_cycles + #ifdef HAS_CODE + && !readtrail + #endif + #if NCORE>1 + && core_id == 0 + #endif + && !state_tables) + { fprintf(efd, "warning: never claim + accept labels "); + fprintf(efd, "requires -a flag to fully verify\n"); + } + #else + if (!state_tables + #ifdef HAS_CODE + && !readtrail + #endif + ) + { fprintf(efd, "warning: verification in BFS mode "); + fprintf(efd, "is restricted to safety properties\n"); + } + #endif + #endif +#endif +#ifndef SAFETY + if (!a_cycles + #ifdef HAS_CODE + && !readtrail + #endif + #if NCORE>1 + && core_id == 0 + #endif + && !state_tables) + { fprintf(efd, "hint: this search is more efficient "); + fprintf(efd, "if pan.c is compiled -DSAFETY\n"); + } + #ifndef NOCOMP + if (!a_cycles) + { S_A = 0; + } else + { if (!fairness) + { S_A = 1; /* _a_t */ + #ifndef NOFAIR + } else /* _a_t and _cnt[NFAIR] */ + { S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2; + /* -2 because first two uchars in now are masked */ + #endif + } } + #endif +#endif + signal(SIGINT, stopped); + set_masks(); +#ifdef BFS + trail = (Trail *) emalloc(6*sizeof(Trail)); + trail += 3; +#else + trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail)); + trail++; /* protect trpt-1 refs at depth 0 */ +#endif +#ifdef SVDUMP + if (vprefix > 0) + { char nm[64]; + sprintf(nm, "%s.svd", PanSource); + if ((svfd = creat(nm, TMODE)) < 0) + { fprintf(efd, "couldn't create %s\n", nm); + vprefix = 0; + } } +#endif +#ifdef RANDSTOR + srand(123); +#endif +#if SYNC>0 && ASYNC==0 + set_recvs(); +#endif + run(); + done = 1; + wrapup(); + return 0; +} + +void +usage(FILE *fd) +{ + fprintf(fd, "%s\n", SpinVersion); + fprintf(fd, "Valid Options are:\n"); +#ifndef SAFETY +#ifdef NP + fprintf(fd, " -a -> is disabled by -DNP "); + fprintf(fd, "(-DNP compiles for -l only)\n"); +#else + fprintf(fd, " -a find acceptance cycles\n"); +#endif +#else + fprintf(fd, " -a,-l,-f -> are disabled by -DSAFETY\n"); +#endif + fprintf(fd, " -A ignore assert() violations\n"); + fprintf(fd, " -b consider it an error to exceed the depth-limit\n"); + fprintf(fd, " -cN stop at Nth error "); + fprintf(fd, "(defaults to -c1)\n"); + fprintf(fd, " -d print state tables and stop\n"); + fprintf(fd, " -e create trails for all errors\n"); + fprintf(fd, " -E ignore invalid end states\n"); +#ifdef SC + fprintf(fd, " -Ffile use 'file' to store disk-stack\n"); +#endif +#ifndef NOFAIR + fprintf(fd, " -f add weak fairness (to -a or -l)\n"); +#endif + fprintf(fd, " -hN use different hash-seed N:1..32\n"); + fprintf(fd, " -i search for shortest path to error\n"); + fprintf(fd, " -I like -i, but approximate and faster\n"); + fprintf(fd, " -J reverse eval order of nested unlesses\n"); +#ifdef BITSTATE + fprintf(fd, " -kN set N bits per state (defaults to 3)\n"); +#endif +#ifdef SCHED + fprintf(fd, " -LN set scheduling restriction to N (default 10)\n"); +#endif +#ifndef SAFETY +#ifdef NP + fprintf(fd, " -l find non-progress cycles\n"); +#else + fprintf(fd, " -l find non-progress cycles -> "); + fprintf(fd, "disabled, requires "); + fprintf(fd, "compilation with -DNP\n"); +#endif +#endif +#ifdef BITSTATE + fprintf(fd, " -MN use N Megabytes for bitstate hash array\n"); + fprintf(fd, " -GN use N Gigabytes for bitstate hash array\n"); +#endif + fprintf(fd, " -mN max depth N steps (default=10k)\n"); + fprintf(fd, " -n no listing of unreached states\n"); +#ifdef SVDUMP + fprintf(fd, " -pN create svfile (save N bytes per state)\n"); +#endif + fprintf(fd, " -QN set time-limit on execution of N minutes\n"); + fprintf(fd, " -q require empty chans in valid end states\n"); +#ifdef HAS_CODE + fprintf(fd, " -r read and execute trail - can add -v,-n,-PN,-g,-C\n"); + fprintf(fd, " -rN read and execute N-th error trail\n"); + fprintf(fd, " -C read and execute trail - columnated output (can add -v,-n)\n"); + fprintf(fd, " -PN read and execute trail - restrict trail output to proc N\n"); + fprintf(fd, " -g read and execute trail + msc gui support\n"); + fprintf(fd, " -S silent replay: only user defined printfs show\n"); +#endif +#ifdef BITSTATE + fprintf(fd, " -RN repeat run Nx with N "); + fprintf(fd, "[1..32] independent hash functions\n"); + fprintf(fd, " -s same as -k1 (single bit per state)\n"); +#endif + fprintf(fd, " -T create trail files in read-only mode\n"); + fprintf(fd, " -tsuf replace .trail with .suf on trailfiles\n"); + fprintf(fd, " -V print SPIN version number\n"); + fprintf(fd, " -v verbose -- filenames in unreached state listing\n"); + fprintf(fd, " -wN hashtable of 2^N entries "); + fprintf(fd, "(defaults to -w%d)\n", ssize); + fprintf(fd, " -x do not overwrite an existing trail file\n"); +#if NCORE>1 + fprintf(fd, " -zN handoff states below depth N to 2nd cpu (multi_core)\n"); +#endif +#ifdef HAS_CODE + fprintf(fd, "\n options -r, -C, -PN, -g, and -S can optionally be followed by\n"); + fprintf(fd, " a filename argument, as in '-r filename', naming the trailfile\n"); +#endif +#if NCORE>1 + multi_usage(fd); +#endif + exit(1); +} + +char * +Malloc(unsigned long n) +{ char *tmp; +#ifdef MEMLIM + if (memcnt+ (double) n > memlim) goto err; +#endif +#if 1 + tmp = (char *) malloc(n); + if (!tmp) +#else + tmp = (char *) sbrk(n); + if (tmp == (char *) -ONE_L) +#endif + { +#ifdef MEMLIM +err: +#endif + printf("pan: out of memory\n"); +#ifdef MEMLIM + printf(" %g bytes used\n", memcnt); + printf(" %g bytes more needed\n", (double) n); + printf(" %g bytes limit\n", + memlim); +#endif +#ifdef COLLAPSE + printf("hint: to reduce memory, recompile with\n"); +#ifndef MA + printf(" -DMA=%d # better/slower compression, or\n", hmax); +#endif + printf(" -DBITSTATE # supertrace, approximation\n"); +#else +#ifndef BITSTATE + printf("hint: to reduce memory, recompile with\n"); +#ifndef HC + printf(" -DCOLLAPSE # good, fast compression, or\n"); +#ifndef MA + printf(" -DMA=%d # better/slower compression, or\n", hmax); +#endif + printf(" -DHC # hash-compaction, approximation\n"); +#endif + printf(" -DBITSTATE # supertrace, approximation\n"); +#endif +#endif +#if NCORE>1 + #ifdef FULL_TRAIL + printf(" omit -DFULL_TRAIL or use pan -c0 to reduce memory\n"); + #endif + #ifdef SEP_STATE + printf("hint: to reduce memory, recompile without\n"); + printf(" -DSEP_STATE # may be faster, but uses more memory\n"); + #endif +#endif + wrapup(); + } + memcnt += (double) n; + return tmp; +} + +#define CHUNK (100*VECTORSZ) + +char * +emalloc(unsigned long n) /* never released or reallocated */ +{ char *tmp; + if (n == 0) + return (char *) NULL; + if (n&(sizeof(void *)-1)) /* for proper alignment */ + n += sizeof(void *)-(n&(sizeof(void *)-1)); + if ((unsigned long) left < n) + { grow = (n < CHUNK) ? CHUNK : n; + have = Malloc(grow); + fragment += (double) left; + left = grow; + } + tmp = have; + have += (long) n; + left -= (long) n; + memset(tmp, 0, n); + return tmp; +} +void +Uerror(char *str) +{ /* always fatal */ + uerror(str); +#if NCORE>1 + sudden_stop("Uerror"); +#endif + wrapup(); +} + +#if defined(MA) && !defined(SAFETY) +int +Unwind(void) +{ Trans *t; uchar ot, _m; int tt; short II; +#ifdef VERBOSE + int i; +#endif + uchar oat = now._a_t; + now._a_t &= ~(1|16|32); + memcpy((char *) &comp_now, (char *) &now, vsize); + now._a_t = oat; +Up: +#ifdef SC + trpt = getframe(depth); +#endif +#ifdef VERBOSE + printf("%d State: ", depth); + for (i = 0; i < vsize; i++) printf("%d%s,", + ((char *)&now)[i], Mask[i]?"*":""); + printf("\n"); +#endif +#ifndef NOFAIR + if (trpt->o_pm&128) /* fairness alg */ + { now._cnt[now._a_t&1] = trpt->bup.oval; + depth--; +#ifdef SC + trpt = getframe(depth); +#else + trpt--; +#endif + goto Q999; + } +#endif +#ifdef HAS_LAST +#ifdef VERI + { int d; Trail *trl; + now._last = 0; + for (d = 1; d < depth; d++) + { trl = getframe(depth-d); /* was trl = (trpt-d); */ + if (trl->pr != 0) + { now._last = trl->pr - BASE; + break; + } } } +#else + now._last = (depth<1)?0:(trpt-1)->pr; +#endif +#endif +#ifdef EVENT_TRACE + now._event = trpt->o_event; +#endif + if ((now._a_t&1) && depth <= A_depth) + { now._a_t &= ~(1|16|32); + if (fairness) now._a_t |= 2; /* ? */ + A_depth = 0; + goto CameFromHere; /* checkcycles() */ + } + t = trpt->o_t; + ot = trpt->o_ot; II = trpt->pr; + tt = trpt->o_tt; this = pptr(II); + _m = do_reverse(t, II, trpt->o_m); +#ifdef VERBOSE + printf("%3d: proc %d ", depth, II); + printf("reverses %d, %d to %d,", + t->forw, tt, t->st); + printf(" %s [abit=%d,adepth=%d,", + t->tp, now._a_t, A_depth); + printf("tau=%d,%d] \n", + trpt->tau, (trpt-1)->tau); +#endif + depth--; +#ifdef SC + trpt = getframe(depth); +#else + trpt--; +#endif + /* reached[ot][t->st] = 1; 3.4.13 */ + ((P0 *)this)->_p = tt; +#ifndef NOFAIR + if ((trpt->o_pm&32)) + { +#ifdef VERI + if (now._cnt[now._a_t&1] == 0) + now._cnt[now._a_t&1] = 1; +#endif + now._cnt[now._a_t&1] += 1; + } +Q999: + if (trpt->o_pm&8) + { now._a_t &= ~2; + now._cnt[now._a_t&1] = 0; + } + if (trpt->o_pm&16) + now._a_t |= 2; +#endif +CameFromHere: + if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0) + return depth; + if (depth > 0) goto Up; + return 0; +} +#endif +static char unwinding; +void +uerror(char *str) +{ static char laststr[256]; + int is_cycle; + + if (unwinding) return; /* 1.4.2 */ + if (strncmp(str, laststr, 254)) +#if NCORE>1 + cpu_printf("pan: %s (at depth %ld)\n", str, +#else + printf("pan: %s (at depth %ld)\n", str, +#endif +#if NCORE>1 + (nr_handoffs * z_handoff) + +#endif + ((depthfound==-1)?depth:depthfound)); + strncpy(laststr, str, 254); + errors++; +#ifdef HAS_CODE + if (readtrail) { wrap_trail(); return; } +#endif + is_cycle = (strstr(str, " cycle") != (char *) 0); + if (!is_cycle) + { depth++; trpt++; + } + if ((every_error != 0) + || errors == upto) + { +#if defined(MA) && !defined(SAFETY) + if (is_cycle) + { int od = depth; + unwinding = 1; + depthfound = Unwind(); + unwinding = 0; + depth = od; + } +#endif +#if NCORE>1 + writing_trail = 1; +#endif +#ifdef BFS + if (depth > 1) trpt--; + nuerror(str); + if (depth > 1) trpt++; +#else + putrail(); +#endif +#if defined(MA) && !defined(SAFETY) + if (strstr(str, " cycle")) + { if (every_error) + printf("sorry: MA writes 1 trail max\n"); + wrapup(); /* no recovery from unwind */ + } +#endif +#if NCORE>1 + if (search_terminated != NULL) + { *search_terminated |= 4; /* uerror */ + } + writing_trail = 0; +#endif + } + if (!is_cycle) + { depth--; trpt--; /* undo */ + } +#ifndef BFS + if (iterative != 0 && maxdepth > 0) + { maxdepth = (iterative == 1)?(depth-1):(depth/2); + warned = 1; + printf("pan: reducing search depth to %ld\n", + maxdepth); + } else +#endif + if (errors >= upto && upto != 0) + { +#if NCORE>1 + sudden_stop("uerror"); +#endif + wrapup(); + } + depthfound = -1; +} + +int +xrefsrc(int lno, S_F_MAP *mp, int M, int i) +{ Trans *T; int j, retval=1; + for (T = trans[M][i]; T; T = T->nxt) + if (T && T->tp) + { if (strcmp(T->tp, ".(goto)") == 0 + || strncmp(T->tp, "goto :", 6) == 0) + return 1; /* not reported */ + + printf("\tline %d", lno); + if (verbose) + for (j = 0; j < sizeof(mp); j++) + if (i >= mp[j].from && i <= mp[j].upto) + { printf(", \"%s\"", mp[j].fnm); + break; + } + printf(", state %d", i); + if (strcmp(T->tp, "") != 0) + { char *q; + q = transmognify(T->tp); + printf(", \"%s\"", q?q:""); + } else if (stopstate[M][i]) + printf(", -end state-"); + printf("\n"); + retval = 0; /* reported */ + } + return retval; +} + +void +r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp) +{ int i, m=0; + +#ifdef VERI + if (M == VERI && !verbose) return; +#endif + printf("unreached in proctype %s\n", procname[M]); + for (i = 1; i < N; i++) + if (which[i] == 0 + && (mapstate[M][i] == 0 + || which[mapstate[M][i]] == 0)) + m += xrefsrc((int) src[i], mp, M, i); + else + m++; + printf(" (%d of %d states)\n", N-1-m, N-1); +} +#if NCORE>1 && !defined(SEP_STATE) +static long rev_trail_cnt; + +#ifdef FULL_TRAIL +void +rev_trail(int fd, volatile Stack_Tree *st_tr) +{ long j; char snap[64]; + + if (!st_tr) + { return; + } + rev_trail(fd, st_tr->prv); +#ifdef VERBOSE + printf("%d (%d) LRT [%d,%d] -- %9u (root %9u)\n", + depth, rev_trail_cnt, st_tr->pr, st_tr->t_id, st_tr, stack_last[core_id]); +#endif + if (st_tr->pr != 255) + { sprintf(snap, "%ld:%d:%d\n", + rev_trail_cnt++, st_tr->pr, st_tr->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing trailfile\n"); + close(fd); + wrapup(); + return; + } + } else /* handoff point */ + { if (a_cycles) + { write(fd, "-1:-1:-1\n", 9); + } } +} +#endif +#endif + +void +putrail(void) +{ int fd; +#if defined VERI || defined(MERGED) + char snap[64]; +#endif +#if NCORE==1 || defined(SEP_STATE) || !defined(FULL_TRAIL) + long i, j; + Trail *trl; +#endif + fd = make_trail(); + if (fd < 0) return; +#ifdef VERI + sprintf(snap, "-2:%d:-2\n", VERI); + write(fd, snap, strlen(snap)); +#endif +#ifdef MERGED + sprintf(snap, "-4:-4:-4\n"); + write(fd, snap, strlen(snap)); +#endif +#if NCORE>1 && !defined(SEP_STATE) && defined(FULL_TRAIL) + rev_trail_cnt = 1; + enter_critical(GLOBAL_LOCK); + rev_trail(fd, stack_last[core_id]); + leave_critical(GLOBAL_LOCK); +#else + i = 1; /* trail starts at position 1 */ + #if NCORE>1 && defined(SEP_STATE) + if (cur_Root.m_vsize > 0) { i++; depth++; } + #endif + for ( ; i <= depth; i++) + { if (i == depthfound+1) + write(fd, "-1:-1:-1\n", 9); + trl = getframe(i); + if (!trl->o_t) continue; + if (trl->o_pm&128) continue; + sprintf(snap, "%ld:%d:%d\n", + i, trl->pr, trl->o_t->t_id); + j = strlen(snap); + if (write(fd, snap, j) != j) + { printf("pan: error writing trailfile\n"); + close(fd); + wrapup(); + } } +#endif + close(fd); +#if NCORE>1 + cpu_printf("pan: wrote trailfile\n"); +#endif +} + +void +sv_save(void) /* push state vector onto save stack */ +{ if (!svtack->nxt) + { svtack->nxt = (Svtack *) emalloc(sizeof(Svtack)); + svtack->nxt->body = emalloc(vsize*sizeof(char)); + svtack->nxt->lst = svtack; + svtack->nxt->m_delta = vsize; + svmax++; + } else if (vsize > svtack->nxt->m_delta) + { svtack->nxt->body = emalloc(vsize*sizeof(char)); + svtack->nxt->lst = svtack; + svtack->nxt->m_delta = vsize; + svmax++; + } + svtack = svtack->nxt; +#if SYNC + svtack->o_boq = boq; +#endif + svtack->o_delta = vsize; /* don't compress */ + memcpy((char *)(svtack->body), (char *) &now, vsize); +#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1) + c_stack((uchar *) &(svtack->c_stack[0])); +#endif +#ifdef DEBUG + cpu_printf("%d: sv_save\n", depth); +#endif +} + +void +sv_restor(void) /* pop state vector from save stack */ +{ + memcpy((char *)&now, svtack->body, svtack->o_delta); +#if SYNC + boq = svtack->o_boq; +#endif +#if defined(C_States) && (HAS_TRACK==1) +#ifdef HAS_STACK + c_unstack((uchar *) &(svtack->c_stack[0])); +#endif + c_revert((uchar *) &(now.c_state[0])); +#endif + if (vsize != svtack->o_delta) + Uerror("sv_restor"); + if (!svtack->lst) + Uerror("error: v_restor"); + svtack = svtack->lst; +#ifdef DEBUG + cpu_printf(" sv_restor\n"); +#endif +} + +void +p_restor(int h) +{ int i; char *z = (char *) &now; + + proc_offset[h] = stack->o_offset; + proc_skip[h] = (uchar) stack->o_skip; +#ifndef XUSAFE + p_name[h] = stack->o_name; +#endif +#ifndef NOCOMP + for (i = vsize + stack->o_skip; i > vsize; i--) + Mask[i-1] = 1; /* align */ +#endif + vsize += stack->o_skip; + memcpy(z+vsize, stack->body, stack->o_delta); + vsize += stack->o_delta; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++) + Mask[vsize - i] = 1; /* pad */ + Mask[proc_offset[h]] = 1; /* _pid */ +#endif + if (BASE > 0 && h > 0) + ((P0 *)pptr(h))->_pid = h-BASE; + else + ((P0 *)pptr(h))->_pid = h; + i = stack->o_delqs; + now._nr_pr += 1; + if (!stack->lst) /* debugging */ + Uerror("error: p_restor"); + stack = stack->lst; + this = pptr(h); + while (i-- > 0) + q_restor(); +} + +void +q_restor(void) +{ char *z = (char *) &now; +#ifndef NOCOMP + int k, k_end; +#endif + q_offset[now._nr_qs] = stack->o_offset; + q_skip[now._nr_qs] = (uchar) stack->o_skip; +#ifndef XUSAFE + q_name[now._nr_qs] = stack->o_name; +#endif + vsize += stack->o_skip; + memcpy(z+vsize, stack->body, stack->o_delta); + vsize += stack->o_delta; +#ifndef NOVSZ + now._vsz = vsize; +#endif + now._nr_qs += 1; +#ifndef NOCOMP + k_end = stack->o_offset; + k = k_end - stack->o_skip; +#if SYNC +#ifndef BFS + if (q_zero(now._nr_qs)) k_end += stack->o_delta; +#endif +#endif + for ( ; k < k_end; k++) + Mask[k] = 1; +#endif + if (!stack->lst) /* debugging */ + Uerror("error: q_restor"); + stack = stack->lst; +} +typedef struct IntChunks { + int *ptr; + struct IntChunks *nxt; +} IntChunks; +IntChunks *filled_chunks[512]; +IntChunks *empty_chunks[512]; +int * +grab_ints(int nr) +{ IntChunks *z; + if (nr >= 512) Uerror("cannot happen grab_int"); + if (filled_chunks[nr]) + { z = filled_chunks[nr]; + filled_chunks[nr] = filled_chunks[nr]->nxt; + } else + { z = (IntChunks *) emalloc(sizeof(IntChunks)); + z->ptr = (int *) emalloc(nr * sizeof(int)); + } + z->nxt = empty_chunks[nr]; + empty_chunks[nr] = z; + return z->ptr; +} +void +ungrab_ints(int *p, int nr) +{ IntChunks *z; + if (!empty_chunks[nr]) Uerror("cannot happen ungrab_int"); + z = empty_chunks[nr]; + empty_chunks[nr] = empty_chunks[nr]->nxt; + z->ptr = p; + z->nxt = filled_chunks[nr]; + filled_chunks[nr] = z; +} +int +delproc(int sav, int h) +{ int d, i=0; +#ifndef NOCOMP + int o_vsize = vsize; +#endif + if (h+1 != (int) now._nr_pr) return 0; + + while (now._nr_qs + && q_offset[now._nr_qs-1] > proc_offset[h]) + { delq(sav); + i++; + } + d = vsize - proc_offset[h]; + if (sav) + { if (!stack->nxt) + { stack->nxt = (Stack *) + emalloc(sizeof(Stack)); + stack->nxt->body = + emalloc(Maxbody*sizeof(char)); + stack->nxt->lst = stack; + smax++; + } + stack = stack->nxt; + stack->o_offset = proc_offset[h]; +#if VECTORSZ>32000 + stack->o_skip = (int) proc_skip[h]; +#else + stack->o_skip = (short) proc_skip[h]; +#endif +#ifndef XUSAFE + stack->o_name = p_name[h]; +#endif + stack->o_delta = d; + stack->o_delqs = i; + memcpy(stack->body, (char *)pptr(h), d); + } + vsize = proc_offset[h]; + now._nr_pr = now._nr_pr - 1; + memset((char *)pptr(h), 0, d); + vsize -= (int) proc_skip[h]; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (i = vsize; i < o_vsize; i++) + Mask[i] = 0; /* reset */ +#endif + return 1; +} + +void +delq(int sav) +{ int h = now._nr_qs - 1; + int d = vsize - q_offset[now._nr_qs - 1]; +#ifndef NOCOMP + int k, o_vsize = vsize; +#endif + if (sav) + { if (!stack->nxt) + { stack->nxt = (Stack *) + emalloc(sizeof(Stack)); + stack->nxt->body = + emalloc(Maxbody*sizeof(char)); + stack->nxt->lst = stack; + smax++; + } + stack = stack->nxt; + stack->o_offset = q_offset[h]; +#if VECTORSZ>32000 + stack->o_skip = (int) q_skip[h]; +#else + stack->o_skip = (short) q_skip[h]; +#endif +#ifndef XUSAFE + stack->o_name = q_name[h]; +#endif + stack->o_delta = d; + memcpy(stack->body, (char *)qptr(h), d); + } + vsize = q_offset[h]; + now._nr_qs = now._nr_qs - 1; + memset((char *)qptr(h), 0, d); + vsize -= (int) q_skip[h]; +#ifndef NOVSZ + now._vsz = vsize; +#endif +#ifndef NOCOMP + for (k = vsize; k < o_vsize; k++) + Mask[k] = 0; /* reset */ +#endif +} + +int +qs_empty(void) +{ int i; + for (i = 0; i < (int) now._nr_qs; i++) + { if (q_sz(i) > 0) + return 0; + } + return 1; +} + +int +endstate(void) +{ int i; P0 *ptr; + for (i = BASE; i < (int) now._nr_pr; i++) + { ptr = (P0 *) pptr(i); + if (!stopstate[ptr->_t][ptr->_p]) + return 0; + } + if (strict) return qs_empty(); +#if defined(EVENT_TRACE) && !defined(OTIM) + if (!stopstate[EVENT_TRACE][now._event] && !a_cycles) + { printf("pan: event_trace not completed\n"); + return 0; + } +#endif + return 1; +} + +#ifndef SAFETY +void +checkcycles(void) +{ uchar o_a_t = now._a_t; +#ifdef SCHED + int o_limit; +#endif +#ifndef NOFAIR + uchar o_cnt = now._cnt[1]; +#endif +#ifdef FULLSTACK +#ifndef MA + struct H_el *sv = trpt->ostate; /* save */ +#else + uchar prov = trpt->proviso; /* save */ +#endif +#endif +#ifdef DEBUG + { int i; uchar *v = (uchar *) &now; + printf(" set Seed state "); +#ifndef NOFAIR + if (fairness) printf("(cnt = %d:%d, nrpr=%d) ", + now._cnt[0], now._cnt[1], now._nr_pr); +#endif + /* for (i = 0; i < n; i++) printf("%d,", v[i]); */ + printf("\n"); + } + printf("%d: cycle check starts\n", depth); +#endif + now._a_t |= (1|16|32); + /* 1 = 2nd DFS; (16|32) to help hasher */ +#ifndef NOFAIR + now._cnt[1] = now._cnt[0]; +#endif + memcpy((char *)&A_Root, (char *)&now, vsize); + A_depth = depthfound = depth; +#if NCORE>1 + mem_put_acc(); +#else + #ifdef SCHED + o_limit = trpt->sched_limit; + trpt->sched_limit = 0; + #endif + new_state(); /* start 2nd DFS */ + #ifdef SCHED + trpt->sched_limit = o_limit; + #endif +#endif + now._a_t = o_a_t; +#ifndef NOFAIR + now._cnt[1] = o_cnt; +#endif + A_depth = 0; depthfound = -1; +#ifdef DEBUG + printf("%d: cycle check returns\n", depth); +#endif +#ifdef FULLSTACK +#ifndef MA + trpt->ostate = sv; /* restore */ +#else + trpt->proviso = prov; +#endif +#endif +} +#endif + +#if defined(FULLSTACK) && defined(BITSTATE) +struct H_el *Free_list = (struct H_el *) 0; +void +onstack_init(void) /* to store stack states in a bitstate search */ +{ S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *)); +} +struct H_el * +grab_state(int n) +{ struct H_el *v, *last = 0; + if (H_tab == S_Tab) + { for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt) + { if ((int) v->tagged == n) + { if (last) + last->nxt = v->nxt; + else +gotcha: Free_list = v->nxt; + v->tagged = 0; + v->nxt = 0; +#ifdef COLLAPSE + v->ln = 0; +#endif + return v; + } + Fh++; last=v; + } + /* new: second try */ + v = Free_list; + if (v && ((int) v->tagged >= n)) + goto gotcha; + ngrabs++; + } + return (struct H_el *) + emalloc(sizeof(struct H_el)+n-sizeof(unsigned)); +} + +#else +#if NCORE>1 +struct H_el * +grab_state(int n) +{ struct H_el *grab_shared(int); + return grab_shared(sizeof(struct H_el)+n-sizeof(unsigned)); +} +#else + #ifndef AUTO_RESIZE + #define grab_state(n) (struct H_el *) \ + emalloc(sizeof(struct H_el)+n-sizeof(unsigned long)); + #else + struct H_el * + grab_state(int n) + { struct H_el *p; + int cnt = sizeof(struct H_el)+n-sizeof(unsigned long); + + if (reclaim_size >= cnt+WS) + { if ((cnt & (WS-1)) != 0) /* alignment */ + { cnt += WS - (cnt & (WS-1)); + } + p = (struct H_el *) reclaim_mem; + reclaim_mem += cnt; + reclaim_size -= cnt; + memset(p, 0, cnt); + } else + { p = (struct H_el *) emalloc(cnt); + } + return p; + } + #endif +#endif +#endif +#ifdef COLLAPSE +unsigned long +ordinal(char *v, long n, short tp) +{ struct H_el *tmp, *ntmp; long m; + struct H_el *olst = (struct H_el *) 0; + s_hash((uchar *)v, n); +#if NCORE>1 && !defined(SEP_STATE) + enter_critical(CS_ID); /* uses spinlock - 1..128 */ +#endif + tmp = H_tab[j1]; + if (!tmp) + { tmp = grab_state(n); + H_tab[j1] = tmp; + } else + for ( ;; olst = tmp, tmp = tmp->nxt) + { m = memcmp(((char *)&(tmp->state)), v, n); + if (n == tmp->ln) + { + if (m == 0) + goto done; + if (m < 0) + { +Insert: ntmp = grab_state(n); + ntmp->nxt = tmp; + if (!olst) + H_tab[j1] = ntmp; + else + olst->nxt = ntmp; + tmp = ntmp; + break; + } else if (!tmp->nxt) + { +Append: tmp->nxt = grab_state(n); + tmp = tmp->nxt; + break; + } + continue; + } + if (n < tmp->ln) + goto Insert; + else if (!tmp->nxt) + goto Append; + } + m = ++ncomps[tp]; +#ifdef FULLSTACK + tmp->tagged = m; +#else + tmp->st_id = m; +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + tmp->m_K1 = K1; +#endif + memcpy(((char *)&(tmp->state)), v, n); + tmp->ln = n; +done: +#if NCORE>1 && !defined(SEP_STATE) + leave_critical(CS_ID); /* uses spinlock */ +#endif +#ifdef FULLSTACK + return tmp->tagged; +#else + return tmp->st_id; +#endif +} + +int +compress(char *vin, int nin) /* collapse compression */ +{ char *w, *v = (char *) &comp_now; + int i, j; + unsigned long n; + static char *x; + static uchar nbytes[513]; /* 1 + 256 + 256 */ + static unsigned short nbytelen; + long col_q(int, char *); + long col_p(int, char *); +#ifndef SAFETY + if (a_cycles) + *v++ = now._a_t; +#ifndef NOFAIR + if (fairness) + for (i = 0; i < NFAIR; i++) + *v++ = now._cnt[i]; +#endif +#endif + nbytelen = 0; +#ifndef JOINPROCS + for (i = 0; i < (int) now._nr_pr; i++) + { n = col_p(i, (char *) 0); +#ifdef NOFIX + nbytes[nbytelen] = 0; +#else + nbytes[nbytelen] = 1; + *v++ = ((P0 *) pptr(i))->_t; +#endif + *v++ = n&255; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; + } +#else + x = scratch; + for (i = 0; i < (int) now._nr_pr; i++) + x += col_p(i, x); + n = ordinal(scratch, x-scratch, 2); /* procs */ + *v++ = n&255; + nbytes[nbytelen] = 0; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; +#endif +#ifdef SEPQS + for (i = 0; i < (int) now._nr_qs; i++) + { n = col_q(i, (char *) 0); + nbytes[nbytelen] = 0; + *v++ = n&255; + if (n >= (1<<8)) + { nbytes[nbytelen]++; + *v++ = (n>>8)&255; + } + if (n >= (1<<16)) + { nbytes[nbytelen]++; + *v++ = (n>>16)&255; + } + if (n >= (1<<24)) + { nbytes[nbytelen]++; + *v++ = (n>>24)&255; + } + nbytelen++; + } +#endif +#ifdef NOVSZ + /* 3 = _a_t, _nr_pr, _nr_qs */ + w = (char *) &now + 3 * sizeof(uchar); +#ifndef NOFAIR + w += NFAIR; +#endif +#else +#if VECTORSZ<65536 + w = (char *) &(now._vsz) + sizeof(unsigned short); +#else + w = (char *) &(now._vsz) + sizeof(unsigned long); +#endif +#endif + x = scratch; + *x++ = now._nr_pr; + *x++ = now._nr_qs; + if (now._nr_qs > 0 && qptr(0) < pptr(0)) + n = qptr(0) - (uchar *) w; + else + n = pptr(0) - (uchar *) w; + j = w - (char *) &now; + for (i = 0; i < (int) n; i++, w++) + if (!Mask[j++]) *x++ = *w; +#ifndef SEPQS + for (i = 0; i < (int) now._nr_qs; i++) + x += col_q(i, x); +#endif + x--; + for (i = 0, j = 6; i < nbytelen; i++) + { if (j == 6) + { j = 0; + *(++x) = 0; + } else + j += 2; + *x |= (nbytes[i] << j); + } + x++; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; j = 0; + n = ordinal(scratch, x-scratch, 0); /* globals */ + *v++ = n&255; + if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; } + if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; } + if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; } + *v++ = j; /* add last count as a byte */ + for (i = 0; i < WS-1; i++) + *v++ = 0; + v -= i; +#if 0 + printf("collapse %d -> %d\n", + vsize, v - (char *)&comp_now); +#endif + return v - (char *)&comp_now; +} +#else +#if !defined(NOCOMP) +int +compress(char *vin, int n) /* default compression */ +{ +#ifdef HC + int delta = 0; + s_hash((uchar *)vin, n); /* sets K1 and K2 */ +#ifndef SAFETY + if (S_A) + { delta++; /* _a_t */ +#ifndef NOFAIR + if (S_A > NFAIR) + delta += NFAIR; /* _cnt[] */ +#endif + } +#endif + memcpy((char *) &comp_now + delta, (char *) &K1, WS); + delta += WS; +#if HC>0 + memcpy((char *) &comp_now + delta, (char *) &K2, HC); + delta += HC; +#endif + return delta; +#else + char *vv = vin; + char *v = (char *) &comp_now; + int i; + #ifndef NO_FAST_C + int r = 0, unroll = n/8; + if (unroll > 0) + { i = 0; + while (r++ < unroll) + { /* unroll 8 times, avoid ifs */ + /* 1 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 2 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 3 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 4 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 5 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 6 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 7 */ *v = *vv++; + v += 1 - Mask[i++]; + /* 8 */ *v = *vv++; + v += 1 - Mask[i++]; + } + r = n - i; /* the rest, at most 7 */ + switch (r) { + case 7: *v = *vv++; v += 1 - Mask[i++]; + case 6: *v = *vv++; v += 1 - Mask[i++]; + case 5: *v = *vv++; v += 1 - Mask[i++]; + case 4: *v = *vv++; v += 1 - Mask[i++]; + case 3: *v = *vv++; v += 1 - Mask[i++]; + case 2: *v = *vv++; v += 1 - Mask[i++]; + case 1: *v = *vv++; v += 1 - Mask[i++]; + case 0: break; + } + r = (n+WS-1)/WS; /* words rounded up */ + r *= WS; /* bytes */ + i = r - i; /* remainder */ + switch (i) { + case 7: *v++ = 0; /* fall thru */ + case 6: *v++ = 0; + case 5: *v++ = 0; + case 4: *v++ = 0; + case 3: *v++ = 0; + case 2: *v++ = 0; + case 1: *v++ = 0; + case 0: break; + default: Uerror("unexpected wordsize"); + } + v -= i; + } else + #endif + { for (i = 0; i < n; i++, vv++) + if (!Mask[i]) *v++ = *vv; + for (i = 0; i < WS-1; i++) + *v++ = 0; + v -= i; + } +#if 0 + printf("compress %d -> %d\n", + n, v - (char *)&comp_now); +#endif + return v - (char *)&comp_now; +#endif +} +#endif +#endif +#if defined(FULLSTACK) && defined(BITSTATE) +#if defined(MA) +#if !defined(onstack_now) +int onstack_now(void) {} +#endif +#if !defined(onstack_put) +void onstack_put(void) {} +#endif +#if !defined(onstack_zap) +void onstack_zap(void) {} +#endif +#else +void +onstack_zap(void) +{ struct H_el *v, *w, *last = 0; + struct H_el **tmp = H_tab; + char *nv; int n, m; + + static char warned = 0; + + H_tab = S_Tab; +#ifndef NOCOMP + nv = (char *) &comp_now; + n = compress((char *)&now, vsize); +#else +#if defined(BITSTATE) && defined(LC) + nv = (char *) &comp_now; + n = compact_stack((char *)&now, vsize); +#else + nv = (char *) &now; + n = vsize; +#endif +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)nv, n); +#endif + H_tab = tmp; + for (v = S_Tab[j1]; v; Zh++, last=v, v=v->nxt) + { m = memcmp(&(v->state), nv, n); + if (m == 0) + goto Found; + if (m < 0) + break; + } +/* NotFound: */ +#ifndef ZAPH + #if defined(BITSTATE) && NCORE>1 + /* seen this happen, likely harmless, but not yet understood */ + if (warned == 0) + #endif + { /* Uerror("stack out of wack - zap"); */ + cpu_printf("pan: warning, stack incomplete\n"); + warned = 1; + } +#endif + return; +Found: + ZAPS++; + if (last) + last->nxt = v->nxt; + else + S_Tab[j1] = v->nxt; + v->tagged = (unsigned) n; +#if !defined(NOREDUCE) && !defined(SAFETY) + v->proviso = 0; +#endif + v->nxt = last = (struct H_el *) 0; + for (w = Free_list; w; Fa++, last=w, w = w->nxt) + { if ((int) w->tagged <= n) + { if (last) + { v->nxt = w; + last->nxt = v; + } else + { v->nxt = Free_list; + Free_list = v; + } + return; + } + if (!w->nxt) + { w->nxt = v; + return; + } } + Free_list = v; +} +void +onstack_put(void) +{ struct H_el **tmp = H_tab; + H_tab = S_Tab; + if (hstore((char *)&now, vsize) != 0) +#if defined(BITSTATE) && defined(LC) + printf("pan: warning, double stack entry\n"); +#else + #ifndef ZAPH + Uerror("cannot happen - unstack_put"); + #endif +#endif + H_tab = tmp; + trpt->ostate = Lstate; + PUT++; +} +int +onstack_now(void) +{ struct H_el *tmp; + struct H_el **tmp2 = H_tab; + char *v; int n, m = 1; + + H_tab = S_Tab; +#ifdef NOCOMP +#if defined(BITSTATE) && defined(LC) + v = (char *) &comp_now; + n = compact_stack((char *)&now, vsize); +#else + v = (char *) &now; + n = vsize; +#endif +#else + v = (char *) &comp_now; + n = compress((char *)&now, vsize); +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)v, n); +#endif + H_tab = tmp2; + for (tmp = S_Tab[j1]; tmp; Zn++, tmp = tmp->nxt) + { m = memcmp(((char *)&(tmp->state)),v,n); + if (m <= 0) + { Lstate = (struct H_el *) tmp; + break; + } } + PROBE++; + return (m == 0); +} +#endif +#endif +#ifndef BITSTATE +void +hinit(void) +{ + #ifdef MA +#ifdef R_XPT + { void r_xpoint(void); + r_xpoint(); + } +#else + dfa_init((unsigned short) (MA+a_cycles)); +#if NCORE>1 && !defined(COLLAPSE) + if (!readtrail) + { void init_HT(unsigned long); + init_HT(0L); + } +#endif +#endif + #endif + #if !defined(MA) || defined(COLLAPSE) +#if NCORE>1 + if (!readtrail) + { void init_HT(unsigned long); + init_HT((unsigned long) (ONE_L<= MA) + { printf("pan: error, MA too small, recompile pan.c"); + printf(" with -DMA=N with N>%d\n", n); + Uerror("aborting"); + } + if (n > (int) maxgs) + { maxgs = (unsigned int) n; + } + for (i = 0; i < n; i++) + { Info[i] = v[i]; + } + for ( ; i < MA-1; i++) + { Info[i] = 0; + } + Info[MA-1] = pbit; + if (a_cycles) /* place _a_t at the end */ + { Info[MA] = Info[0]; + Info[0] = 0; + } + +#if NCORE>1 && !defined(SEP_STATE) + enter_critical(GLOBAL_LOCK); /* crude, but necessary */ + /* to make this mode work, also replace emalloc with grab_shared inside store MA routines */ +#endif + + if (!dfa_store(Info)) + { if (pbit == 0 + && (now._a_t&1) + && depth > A_depth) + { Info[MA] &= ~(1|16|32); /* _a_t */ + if (dfa_member(MA)) + { Info[MA-1] = 4; /* off-stack bit */ + nShadow++; + if (!dfa_member(MA-1)) + { ret_val = 3; + #ifdef VERBOSE + printf("intersected 1st dfs stack\n"); + #endif + goto done; + } } } + ret_val = 0; + #ifdef VERBOSE + printf("new state\n"); + #endif + goto done; + } +#ifdef FULLSTACK + if (pbit == 0) + { Info[MA-1] = 1; /* proviso bit */ +#ifndef BFS + trpt->proviso = dfa_member(MA-1); +#endif + Info[MA-1] = 4; /* off-stack bit */ + if (dfa_member(MA-1)) + { ret_val = 1; /* off-stack */ + #ifdef VERBOSE + printf("old state\n"); + #endif + } else + { ret_val = 2; /* on-stack */ + #ifdef VERBOSE + printf("on-stack\n"); + #endif + } + goto done; + } +#endif + ret_val = 1; +#ifdef VERBOSE + printf("old state\n"); +#endif +done: +#if NCORE>1 && !defined(SEP_STATE) + leave_critical(GLOBAL_LOCK); +#endif + return ret_val; /* old state */ +} +#endif +#if defined(BITSTATE) && defined(LC) +int +compact_stack(char *vin, int n) +{ int delta = 0; + s_hash((uchar *)vin, n); /* sets K1 and K2 */ +#ifndef SAFETY + delta++; /* room for state[0] |= 128 */ +#endif + memcpy((char *) &comp_now + delta, (char *) &K1, WS); + delta += WS; + memcpy((char *) &comp_now + delta, (char *) &K2, WS); + delta += WS; /* use all available bits */ + return delta; +} +#endif +int +hstore(char *vin, int nin) /* hash table storage */ +{ struct H_el *ntmp; + struct H_el *tmp, *olst = (struct H_el *) 0; + char *v; int n, m=0; +#ifdef HC + uchar rem_a; +#endif +#ifdef NOCOMP +#if defined(BITSTATE) && defined(LC) + if (S_Tab == H_tab) + { v = (char *) &comp_now; + n = compact_stack(vin, nin); + } else + { v = vin; n = nin; + } +#else + v = vin; n = nin; +#endif +#else + v = (char *) &comp_now; + #ifdef HC + rem_a = now._a_t; + now._a_t = 0; + #endif + n = compress(vin, nin); + #ifdef HC + now._a_t = rem_a; + #endif +#ifndef SAFETY + if (S_A) + { v[0] = 0; /* _a_t */ +#ifndef NOFAIR + if (S_A > NFAIR) + for (m = 0; m < NFAIR; m++) + v[m+1] = 0; /* _cnt[] */ +#endif + m = 0; + } + #endif +#endif +#if !defined(HC) && !(defined(BITSTATE) && defined(LC)) + s_hash((uchar *)v, n); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + enter_critical(CS_ID); /* uses spinlock */ +#endif + tmp = H_tab[j1]; + if (!tmp) + { tmp = grab_state(n); +#if NCORE>1 + if (!tmp) + { /* if we get here -- we've already issued a warning */ + /* but we want to allow the normal distributed termination */ + /* to collect the stats on all cpus in the wrapup */ + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + H_tab[j1] = tmp; + } else + { for (;; hcmp++, olst = tmp, tmp = tmp->nxt) + { /* skip the _a_t and the _cnt bytes */ +#ifdef COLLAPSE + if (tmp->ln != 0) + { if (!tmp->nxt) goto Append; + continue; + } +#endif + m = memcmp(((char *)&(tmp->state)) + S_A, + v + S_A, n - S_A); + if (m == 0) { +#ifdef SAFETY +#define wasnew 0 +#else + int wasnew = 0; +#endif +#ifndef SAFETY +#ifndef NOCOMP + if (S_A) + { if ((((char *)&(tmp->state))[0] & V_A) != V_A) + { wasnew = 1; nShadow++; + ((char *)&(tmp->state))[0] |= V_A; + } +#ifndef NOFAIR + if (S_A > NFAIR) + { /* 0 <= now._cnt[now._a_t&1] < MAXPROC */ + unsigned ci, bp; /* index, bit pos */ + ci = (now._cnt[now._a_t&1] / 8); + bp = (now._cnt[now._a_t&1] - 8*ci); + if (now._a_t&1) /* use tail-bits in _cnt */ + { ci = (NFAIR - 1) - ci; + bp = 7 - bp; /* bp = 0..7 */ + } + ci++; /* skip over _a_t */ + bp = 1 << bp; /* the bit mask */ + if ((((char *)&(tmp->state))[ci] & bp)==0) + { if (!wasnew) + { wasnew = 1; + nShadow++; + } + ((char *)&(tmp->state))[ci] |= bp; + } + } + /* else: wasnew == 0, i.e., old state */ +#endif + } +#endif +#endif +#if NCORE>1 + Lstate = (struct H_el *) tmp; +#endif +#ifdef FULLSTACK +#ifndef SAFETY + if (wasnew) + { Lstate = (struct H_el *) tmp; + tmp->tagged |= V_A; + if ((now._a_t&1) + && (tmp->tagged&A_V) + && depth > A_depth) + { +intersect: +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf("1st dfs-stack intersected on state %d+\n", + (int) tmp->st_id); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 3; + } +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" New state %d+\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(1, (char *)&(tmp->state),n,tmp->tagged); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } else +#endif + if ((S_A)?(tmp->tagged&V_A):tmp->tagged) + { Lstate = (struct H_el *) tmp; +#ifndef SAFETY + /* already on current dfs stack */ + /* but may also be on 1st dfs stack */ + if ((now._a_t&1) + && (tmp->tagged&A_V) + && depth > A_depth +#ifndef NOFAIR + && (!fairness || now._cnt[1] <= 1) +#endif + ) + goto intersect; +#endif +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" Stack state %d\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(0, (char *)&(tmp->state),n,tmp->tagged); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 2; /* match on stack */ + } +#else + if (wasnew) + { +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" New state %d+\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(1, (char *)&(tmp->state), n, 0); +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } +#endif +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" Old state %d\n", (int) tmp->st_id); +#endif +#ifdef DEBUG + dumpstate(0, (char *)&(tmp->state), n, 0); +#endif +#ifdef REACH + if (tmp->D > depth) + { tmp->D = depth; +#ifdef CHECK +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif + printf(" ReVisiting (from smaller depth)\n"); +#endif + nstates--; +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 0; + } +#endif +#if (defined(BFS) && defined(Q_PROVISO)) || NCORE>1 + Lstate = (struct H_el *) tmp; +#endif +#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); +#endif + return 1; /* match outside stack */ + } else if (m < 0) + { /* insert state before tmp */ + ntmp = grab_state(n); +#if NCORE>1 + if (!ntmp) + { + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + ntmp->nxt = tmp; + if (!olst) + H_tab[j1] = ntmp; + else + olst->nxt = ntmp; + tmp = ntmp; + break; + } else if (!tmp->nxt) + { /* append after tmp */ +#ifdef COLLAPSE +Append: +#endif + tmp->nxt = grab_state(n); +#if NCORE>1 + if (!tmp->nxt) + { + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif + return 1; /* allow normal termination */ + } +#endif + tmp = tmp->nxt; + break; + } } + } +#ifdef CHECK + tmp->st_id = (unsigned) nstates; +#if NCORE>1 + printf("cpu%d: ", core_id); +#endif +#ifdef BITSTATE + printf(" Push state %d\n", ((int) nstates) - 1); +#else + printf(" New state %d\n", (int) nstates); +#endif +#endif +#if !defined(SAFETY) || defined(REACH) + tmp->D = depth; +#endif +#ifndef SAFETY +#ifndef NOCOMP + if (S_A) + { v[0] = V_A; +#ifndef NOFAIR + if (S_A > NFAIR) + { unsigned ci, bp; /* as above */ + ci = (now._cnt[now._a_t&1] / 8); + bp = (now._cnt[now._a_t&1] - 8*ci); + if (now._a_t&1) + { ci = (NFAIR - 1) - ci; + bp = 7 - bp; /* bp = 0..7 */ + } + v[1+ci] = 1 << bp; + } +#endif + } +#endif +#endif +#if defined(AUTO_RESIZE) && !defined(BITSTATE) + tmp->m_K1 = K1; +#endif + memcpy(((char *)&(tmp->state)), v, n); +#ifdef FULLSTACK + tmp->tagged = (S_A)?V_A:(depth+1); +#ifdef DEBUG + dumpstate(-1, v, n, tmp->tagged); +#endif + Lstate = (struct H_el *) tmp; +#else + #ifdef DEBUG + dumpstate(-1, v, n, 0); + #endif + #if NCORE>1 + Lstate = (struct H_el *) tmp; + #endif +#endif +/* #if NCORE>1 && !defined(SEP_STATE) */ +#if NCORE>1 + #ifdef V_PROVISO + tmp->cpu_id = core_id; + #endif + #if !defined(SEP_STATE) && !defined(BITSTATE) + leave_critical(CS_ID); + #endif +#endif + return 0; +} +#endif +#include TRANSITIONS +void +do_reach(void) +{ + r_ck(reached0, nstates0, 0, src_ln0, src_file0); + r_ck(reached1, nstates1, 1, src_ln1, src_file1); + r_ck(reached2, nstates2, 2, src_ln2, src_file2); + r_ck(reached3, nstates3, 3, src_ln3, src_file3); + r_ck(reached4, nstates4, 4, src_ln4, src_file4); + r_ck(reached5, nstates5, 5, src_ln5, src_file5); +} + +void +iniglobals(void) +{ + deliver = 0; + { int l_in; + for (l_in = 0; l_in < 8; l_in++) + { + now.buffer_use[l_in] = 0; + } + } + now.write_off = 0; + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + now.commit_count[l_in] = 0; + } + } + now._commit_sum = 0; + now.read_off = 0; + now.events_lost = 0; + now.refcount = 0; +#ifdef VAR_RANGES + { int l_in; + for (l_in = 0; l_in < 8; l_in++) + { + logval("buffer_use[l_in]", now.buffer_use[l_in]); + } + } + logval("write_off", now.write_off); + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + logval("commit_count[l_in]", now.commit_count[l_in]); + } + } + logval("_commit_sum", now._commit_sum); + logval("read_off", now.read_off); + logval("events_lost", now.events_lost); + logval("refcount", now.refcount); +#endif + Maxbody = max(Maxbody, sizeof(State)-VECTORSZ); +} + +int +addqueue(int n, int is_rv) +{ int j=0, i = now._nr_qs; +#ifndef NOCOMP + int k; +#endif + if (i >= MAXQ) + Uerror("too many queues"); + switch (n) { + default: Uerror("bad queue - addqueue"); + } + if (vsize%WS) + q_skip[i] = WS-(vsize%WS); + else + q_skip[i] = 0; +#ifndef NOCOMP + k = vsize; +#ifndef BFS + if (is_rv) k += j; +#endif + for (k += (int) q_skip[i]; k > vsize; k--) + Mask[k-1] = 1; +#endif + vsize += (int) q_skip[i]; + q_offset[i] = vsize; + now._nr_qs += 1; + vsize += j; +#ifndef NOVSZ + now._vsz = vsize; +#endif + hmax = max(hmax, vsize); + if (vsize >= VECTORSZ) + Uerror("VECTORSZ is too small, edit pan.h"); + memset((char *)qptr(i), 0, j); + ((Q0 *)qptr(i))->_t = n; + return i+1; +} + +#if NQS>0 +void +qsend(int into, int sorted, int args_given) +{ int j; uchar *z; + +#ifdef HAS_SORTED + int k; +#endif + if (!into--) + uerror("ref to uninitialized chan name (sending)"); + if (into >= (int) now._nr_qs || into < 0) + Uerror("qsend bad queue#"); + z = qptr(into); + j = ((Q0 *)qptr(into))->Qlen; + switch (((Q0 *)qptr(into))->_t) { + case 0: printf("queue %d was deleted\n", into+1); + default: Uerror("bad queue - qsend"); + } +#ifdef EVENT_TRACE + if (in_s_scope(into+1)) + require('s', into); +#endif +} +#endif + +#if SYNC +int +q_zero(int from) +{ if (!from--) + { uerror("ref to uninitialized chan name (q_zero)"); + return 0; + } + switch(((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + } + Uerror("bad queue q-zero"); + return -1; +} +int +not_RV(int from) +{ if (q_zero(from)) + { printf("==>> a test of the contents of a rv "); + printf("channel always returns FALSE\n"); + uerror("error to poll rendezvous channel"); + } + return 1; +} +#endif +#ifndef XUSAFE +void +setq_claim(int x, int m, char *s, int y, char *p) +{ if (x == 0) + uerror("x[rs] claim on uninitialized channel"); + if (x < 0 || x > MAXQ) + Uerror("cannot happen setq_claim"); + q_claim[x] |= m; + p_name[y] = p; + q_name[x] = s; + if (m&2) q_S_check(x, y); + if (m&1) q_R_check(x, y); +} +short q_sender[MAXQ+1]; +int +q_S_check(int x, int who) +{ if (!q_sender[x]) + { q_sender[x] = who+1; +#if SYNC + if (q_zero(x)) + { printf("chan %s (%d), ", + q_name[x], x-1); + printf("sndr proc %s (%d)\n", + p_name[who], who); + uerror("xs chans cannot be used for rv"); + } +#endif + } else + if (q_sender[x] != who+1) + { printf("pan: xs assertion violated: "); + printf("access to chan <%s> (%d)\npan: by ", + q_name[x], x-1); + if (q_sender[x] > 0 && p_name[q_sender[x]-1]) + printf("%s (proc %d) and by ", + p_name[q_sender[x]-1], q_sender[x]-1); + printf("%s (proc %d)\n", + p_name[who], who); + uerror("error, partial order reduction invalid"); + } + return 1; +} +short q_recver[MAXQ+1]; +int +q_R_check(int x, int who) +{ if (!q_recver[x]) + { q_recver[x] = who+1; +#if SYNC + if (q_zero(x)) + { printf("chan %s (%d), ", + q_name[x], x-1); + printf("recv proc %s (%d)\n", + p_name[who], who); + uerror("xr chans cannot be used for rv"); + } +#endif + } else + if (q_recver[x] != who+1) + { printf("pan: xr assertion violated: "); + printf("access to chan %s (%d)\npan: ", + q_name[x], x-1); + if (q_recver[x] > 0 && p_name[q_recver[x]-1]) + printf("by %s (proc %d) and ", + p_name[q_recver[x]-1], q_recver[x]-1); + printf("by %s (proc %d)\n", + p_name[who], who); + uerror("error, partial order reduction invalid"); + } + return 1; +} +#endif +int +q_len(int x) +{ if (!x--) + uerror("ref to uninitialized chan name (len)"); + return ((Q0 *)qptr(x))->Qlen; +} + +int +q_full(int from) +{ if (!from--) + uerror("ref to uninitialized chan name (qfull)"); + switch(((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + } + Uerror("bad queue - q_full"); + return 0; +} + +#ifdef HAS_UNLESS +int +q_e_f(int from) +{ /* empty or full */ + return !q_len(from) || q_full(from); +} +#endif +#if NQS>0 +int +qrecv(int from, int slot, int fld, int done) +{ uchar *z; + int j, k, r=0; + + if (!from--) + uerror("ref to uninitialized chan name (receiving)"); + if (from >= (int) now._nr_qs || from < 0) + Uerror("qrecv bad queue#"); + z = qptr(from); +#ifdef EVENT_TRACE + if (done && (in_r_scope(from+1))) + require('r', from); +#endif + switch (((Q0 *)qptr(from))->_t) { + case 0: printf("queue %d was deleted\n", from+1); + default: Uerror("bad queue - qrecv"); + } + return r; +} +#endif + +#ifndef BITSTATE +#ifdef COLLAPSE +long +col_q(int i, char *z) +{ int j=0, k; + char *x, *y; + Q0 *ptr = (Q0 *) qptr(i); + switch (ptr->_t) { + default: Uerror("bad qtype - collapse"); + } + if (z) x = z; else x = scratch; + y = (char *) ptr; k = q_offset[i]; + /* no need to store the empty slots at the end */ + j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]); + for ( ; j > 0; j--, y++) + if (!Mask[k++]) *x++ = *y; + for (j = 0; j < WS-1; j++) + *x++ = 0; + x -= j; + if (z) return (long) (x - z); + return ordinal(scratch, x-scratch, 1); /* chan */ +} +#endif +#endif +int +unsend(int into) +{ int _m=0, j; uchar *z; + +#ifdef HAS_SORTED + int k; +#endif + if (!into--) + uerror("ref to uninitialized chan (unsend)"); + z = qptr(into); + j = ((Q0 *)z)->Qlen; + ((Q0 *)z)->Qlen = --j; + switch (((Q0 *)qptr(into))->_t) { + default: Uerror("bad queue - unsend"); + } + return _m; +} + +void +unrecv(int from, int slot, int fld, int fldvar, int strt) +{ int j; uchar *z; + + if (!from--) + uerror("ref to uninitialized chan (unrecv)"); + z = qptr(from); + j = ((Q0 *)z)->Qlen; + if (strt) ((Q0 *)z)->Qlen = j+1; + switch (((Q0 *)qptr(from))->_t) { + default: Uerror("bad queue - qrecv"); + } +} +int +q_cond(short II, Trans *t) +{ int i = 0; + for (i = 0; i < 6; i++) + { if (t->ty[i] == TIMEOUT_F) return 1; + if (t->ty[i] == ALPHA_F) +#ifdef GLOB_ALPHA + return 0; +#else + return (II+1 == (short) now._nr_pr && II+1 < MAXPROC); +#endif + switch (t->qu[i]) { + case 0: break; + default: Uerror("unknown qid - q_cond"); + return 0; + } + } + return 1; +} +void +to_compile(void) +{ char ctd[1024], carg[64]; +#ifdef BITSTATE + strcpy(ctd, "-DBITSTATE "); +#else + strcpy(ctd, ""); +#endif +#ifdef NOVSZ + strcat(ctd, "-DNOVSZ "); +#endif +#ifdef REVERSE + strcat(ctd, "-DREVERSE "); +#endif +#ifdef T_REVERSE + strcat(ctd, "-DT_REVERSE "); +#endif +#ifdef RANDOMIZE + #if RANDOMIZE>0 + sprintf(carg, "-DRANDOMIZE=%d ", RANDOMIZE); + strcat(ctd, carg); + #else + strcat(ctd, "-DRANDOMIZE "); + #endif +#endif +#ifdef SCHED + sprintf(carg, "-DSCHED=%d ", SCHED); + strcat(ctd, carg); +#endif +#ifdef BFS + strcat(ctd, "-DBFS "); +#endif +#ifdef MEMLIM + sprintf(carg, "-DMEMLIM=%d ", MEMLIM); + strcat(ctd, carg); +#else +#ifdef MEMCNT + sprintf(carg, "-DMEMCNT=%d ", MEMCNT); + strcat(ctd, carg); +#endif +#endif +#ifdef NOCLAIM + strcat(ctd, "-DNOCLAIM "); +#endif +#ifdef SAFETY + strcat(ctd, "-DSAFETY "); +#else +#ifdef NOFAIR + strcat(ctd, "-DNOFAIR "); +#else +#ifdef NFAIR + if (NFAIR != 2) + { sprintf(carg, "-DNFAIR=%d ", NFAIR); + strcat(ctd, carg); + } +#endif +#endif +#endif +#ifdef NOREDUCE + strcat(ctd, "-DNOREDUCE "); +#else +#ifdef XUSAFE + strcat(ctd, "-DXUSAFE "); +#endif +#endif +#ifdef NP + strcat(ctd, "-DNP "); +#endif +#ifdef PEG + strcat(ctd, "-DPEG "); +#endif +#ifdef VAR_RANGES + strcat(ctd, "-DVAR_RANGES "); +#endif +#ifdef HC0 + strcat(ctd, "-DHC0 "); +#endif +#ifdef HC1 + strcat(ctd, "-DHC1 "); +#endif +#ifdef HC2 + strcat(ctd, "-DHC2 "); +#endif +#ifdef HC3 + strcat(ctd, "-DHC3 "); +#endif +#ifdef HC4 + strcat(ctd, "-DHC4 "); +#endif +#ifdef CHECK + strcat(ctd, "-DCHECK "); +#endif +#ifdef CTL + strcat(ctd, "-DCTL "); +#endif +#ifdef NIBIS + strcat(ctd, "-DNIBIS "); +#endif +#ifdef NOBOUNDCHECK + strcat(ctd, "-DNOBOUNDCHECK "); +#endif +#ifdef NOSTUTTER + strcat(ctd, "-DNOSTUTTER "); +#endif +#ifdef REACH + strcat(ctd, "-DREACH "); +#endif +#ifdef PRINTF + strcat(ctd, "-DPRINTF "); +#endif +#ifdef OTIM + strcat(ctd, "-DOTIM "); +#endif +#ifdef COLLAPSE + strcat(ctd, "-DCOLLAPSE "); +#endif +#ifdef MA + sprintf(carg, "-DMA=%d ", MA); + strcat(ctd, carg); +#endif +#ifdef SVDUMP + strcat(ctd, "-DSVDUMP "); +#endif +#ifdef VECTORSZ + if (VECTORSZ != 1024) + { sprintf(carg, "-DVECTORSZ=%d ", VECTORSZ); + strcat(ctd, carg); + } +#endif +#ifdef VERBOSE + strcat(ctd, "-DVERBOSE "); +#endif +#ifdef CHECK + strcat(ctd, "-DCHECK "); +#endif +#ifdef SDUMP + strcat(ctd, "-DSDUMP "); +#endif +#if NCORE>1 + sprintf(carg, "-DNCORE=%d ", NCORE); + strcat(ctd, carg); +#endif +#ifdef SFH + sprintf(carg, "-DSFH "); + strcat(ctd, carg); +#endif +#ifdef VMAX + if (VMAX != 256) + { sprintf(carg, "-DVMAX=%d ", VMAX); + strcat(ctd, carg); + } +#endif +#ifdef PMAX + if (PMAX != 16) + { sprintf(carg, "-DPMAX=%d ", PMAX); + strcat(ctd, carg); + } +#endif +#ifdef QMAX + if (QMAX != 16) + { sprintf(carg, "-DQMAX=%d ", QMAX); + strcat(ctd, carg); + } +#endif +#ifdef SET_WQ_SIZE + sprintf(carg, "-DSET_WQ_SIZE=%d ", SET_WQ_SIZE); + strcat(ctd, carg); +#endif + printf("Compiled as: cc -o pan %span.c\n", ctd); +} +void +active_procs(void) +{ + if (!permuted) { + Addproc(4); + } else { + Addproc(4); + } +} +#ifdef MA +/* +#include +#define uchar unsigned char +*/ +#define ulong unsigned long +#define ushort unsigned short + +#define TWIDTH 256 +#define HASH(y,n) (n)*(((long)y)) +#define INRANGE(e,h) ((h>=e->From && h<=e->To)||(e->s==1 && e->S==h)) + +extern char *emalloc(unsigned long); /* imported routine */ +extern void dfa_init(ushort); /* 4 exported routines */ +extern int dfa_member(ulong); +extern int dfa_store(uchar *); +extern void dfa_stats(void); + +typedef struct Edge { + uchar From, To; /* max range 0..255 */ + uchar s, S; /* if s=1, S is singleton */ + struct Vertex *Dst; + struct Edge *Nxt; +} Edge; + +typedef struct Vertex { + ulong key, num; /* key for splay tree, nr incoming edges */ + uchar from[2], to[2]; /* in-node predefined edge info */ + struct Vertex *dst[2];/* most nodes have 2 or more edges */ + struct Edge *Succ; /* in case there are more edges */ + struct Vertex *lnk, *left, *right; /* splay tree plumbing */ +} Vertex; + +static Edge *free_edges; +static Vertex *free_vertices; +static Vertex **layers; /* one splay tree of nodes per layer */ +static Vertex **path; /* run of word in the DFA */ +static Vertex *R, *F, *NF; /* Root, Final, Not-Final */ +static uchar *word, *lastword;/* string, and last string inserted */ +static int dfa_depth, iv=0, nv=0, pfrst=0, Tally; + +static void insert_it(Vertex *, int); /* splay-tree code */ +static void delete_it(Vertex *, int); +static Vertex *find_it(Vertex *, Vertex *, uchar, int); + +static void +recyc_edges(Edge *e) +{ + if (!e) return; + recyc_edges(e->Nxt); + e->Nxt = free_edges; + free_edges = e; +} + +static Edge * +new_edge(Vertex *dst) +{ Edge *e; + + if (free_edges) + { e = free_edges; + free_edges = e->Nxt; + e->From = e->To = e->s = e->S = 0; + e->Nxt = (Edge *) 0; + } else + e = (Edge *) emalloc(sizeof(Edge)); + e->Dst = dst; + + return e; +} + +static void +recyc_vertex(Vertex *v) +{ + recyc_edges(v->Succ); + v->Succ = (Edge *) free_vertices; + free_vertices = v; + nr_states--; +} + +static Vertex * +new_vertex(void) +{ Vertex *v; + + if (free_vertices) + { v = free_vertices; + free_vertices = (Vertex *) v->Succ; + v->Succ = (Edge *) 0; + v->num = 0; + } else + v = (Vertex *) emalloc(sizeof(Vertex)); + + nr_states++; + return v; +} + +static Vertex * +allDelta(Vertex *v, int n) +{ Vertex *dst = new_vertex(); + + v->from[0] = 0; + v->to[0] = 255; + v->dst[0] = dst; + dst->num = 256; + insert_it(v, n); + return dst; +} + +static void +insert_edge(Vertex *v, Edge *e) +{ /* put new edge first */ + if (!v->dst[0]) + { v->dst[0] = e->Dst; + v->from[0] = e->From; + v->to[0] = e->To; + recyc_edges(e); + return; + } + if (!v->dst[1]) + { v->from[1] = v->from[0]; v->from[0] = e->From; + v->to[1] = v->to[0]; v->to[0] = e->To; + v->dst[1] = v->dst[0]; v->dst[0] = e->Dst; + recyc_edges(e); + return; + } /* shift */ + { int f = v->from[1]; + int t = v->to[1]; + Vertex *d = v->dst[1]; + v->from[1] = v->from[0]; v->from[0] = e->From; + v->to[1] = v->to[0]; v->to[0] = e->To; + v->dst[1] = v->dst[0]; v->dst[0] = e->Dst; + e->From = f; + e->To = t; + e->Dst = d; + } + e->Nxt = v->Succ; + v->Succ = e; +} + +static void +copyRecursive(Vertex *v, Edge *e) +{ Edge *f; + if (e->Nxt) copyRecursive(v, e->Nxt); + f = new_edge(e->Dst); + f->From = e->From; + f->To = e->To; + f->s = e->s; + f->S = e->S; + f->Nxt = v->Succ; + v->Succ = f; +} + +static void +copyEdges(Vertex *to, Vertex *from) +{ int i; + for (i = 0; i < 2; i++) + { to->from[i] = from->from[i]; + to->to[i] = from->to[i]; + to->dst[i] = from->dst[i]; + } + if (from->Succ) copyRecursive(to, from->Succ); +} + +static Edge * +cacheDelta(Vertex *v, int h, int first) +{ static Edge *ov, tmp; int i; + + if (!first && INRANGE(ov,h)) + return ov; /* intercepts about 10% */ + for (i = 0; i < 2; i++) + if (v->dst[i] && h >= v->from[i] && h <= v->to[i]) + { tmp.From = v->from[i]; + tmp.To = v->to[i]; + tmp.Dst = v->dst[i]; + tmp.s = tmp.S = 0; + ov = &tmp; + return ov; + } + for (ov = v->Succ; ov; ov = ov->Nxt) + if (INRANGE(ov,h)) return ov; + + Uerror("cannot get here, cacheDelta"); + return (Edge *) 0; +} + +static Vertex * +Delta(Vertex *v, int h) /* v->delta[h] */ +{ Edge *e; + + if (v->dst[0] && h >= v->from[0] && h <= v->to[0]) + return v->dst[0]; /* oldest edge */ + if (v->dst[1] && h >= v->from[1] && h <= v->to[1]) + return v->dst[1]; + for (e = v->Succ; e; e = e->Nxt) + if (INRANGE(e,h)) + return e->Dst; + Uerror("cannot happen Delta"); + return (Vertex *) 0; +} + +static void +numDelta(Vertex *v, int d) +{ Edge *e; + ulong cnt; + int i; + + for (i = 0; i < 2; i++) + if (v->dst[i]) + { cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]); + if (d == 1 && cnt < v->dst[i]->num) goto bad; + v->dst[i]->num = cnt; + } + for (e = v->Succ; e; e = e->Nxt) + { cnt = e->Dst->num + d*(1 + e->To - e->From + e->s); + if (d == 1 && cnt < e->Dst->num) +bad: Uerror("too many incoming edges"); + e->Dst->num = cnt; + } +} + +static void +setDelta(Vertex *v, int h, Vertex *newdst) /* v->delta[h] = newdst; */ +{ Edge *e, *f = (Edge *) 0, *g; + int i; + + /* remove the old entry, if there */ + for (i = 0; i < 2; i++) + if (v->dst[i] && h >= v->from[i] && h <= v->to[i]) + { if (h == v->from[i]) + { if (h == v->to[i]) + { v->dst[i] = (Vertex *) 0; + v->from[i] = v->to[i] = 0; + } else + v->from[i]++; + } else if (h == v->to[i]) + { v->to[i]--; + } else + { g = new_edge(v->dst[i]);/* same dst */ + g->From = v->from[i]; + g->To = h-1; /* left half */ + v->from[i] = h+1; /* right half */ + insert_edge(v, g); + } + goto part2; + } + for (e = v->Succ; e; f = e, e = e->Nxt) + { if (e->s == 1 && e->S == h) + { e->s = e->S = 0; + goto rem_tst; + } + if (h >= e->From && h <= e->To) + { if (h == e->From) + { if (h == e->To) + { if (e->s) + { e->From = e->To = e->S; + e->s = 0; + break; + } else + goto rem_do; + } else + e->From++; + } else if (h == e->To) + { e->To--; + } else /* split */ + { g = new_edge(e->Dst); /* same dst */ + g->From = e->From; + g->To = h-1; /* g=left half */ + e->From = h+1; /* e=right half */ + g->Nxt = e->Nxt; /* insert g */ + e->Nxt = g; /* behind e */ + break; /* done */ + } + +rem_tst: if (e->From > e->To) + { if (e->s == 0) { +rem_do: if (f) + f->Nxt = e->Nxt; + else + v->Succ = e->Nxt; + e->Nxt = (Edge *) 0; + recyc_edges(e); + } else + { e->From = e->To = e->S; + e->s = 0; + } } + break; + } } +part2: + /* check if newdst is already there */ + for (i = 0; i < 2; i++) + if (v->dst[i] == newdst) + { if (h+1 == (int) v->from[i]) + { v->from[i] = h; + return; + } + if (h == (int) v->to[i]+1) + { v->to[i] = h; + return; + } } + for (e = v->Succ; e; e = e->Nxt) + { if (e->Dst == newdst) + { if (h+1 == (int) e->From) + { e->From = h; + if (e->s == 1 && e->S+1 == e->From) + { e->From = e->S; + e->s = e->S = 0; + } + return; + } + if (h == (int) e->To+1) + { e->To = h; + if (e->s == 1 && e->S == e->To+1) + { e->To = e->S; + e->s = e->S = 0; + } + return; + } + if (e->s == 0) + { e->s = 1; + e->S = h; + return; + } } } + /* add as a new edge */ + e = new_edge(newdst); + e->From = e->To = h; + insert_edge(v, e); +} + +static ulong +cheap_key(Vertex *v) +{ ulong vk2 = 0; + + if (v->dst[0]) + { vk2 = (ulong) v->dst[0]; + if ((ulong) v->dst[1] > vk2) + vk2 = (ulong) v->dst[1]; + } else if (v->dst[1]) + vk2 = (ulong) v->dst[1]; + if (v->Succ) + { Edge *e; + for (e = v->Succ; e; e = e->Nxt) + if ((ulong) e->Dst > vk2) + vk2 = (ulong) e->Dst; + } + Tally = (vk2>>2)&(TWIDTH-1); + return v->key; +} + +static ulong +mk_key(Vertex *v) /* not sensitive to order */ +{ ulong m = 0, vk2 = 0; + Edge *e; + + if (v->dst[0]) + { m += HASH(v->dst[0], v->to[0] - v->from[0] + 1); + vk2 = (ulong) v->dst[0]; + } + if (v->dst[1]) + { m += HASH(v->dst[1], v->to[1] - v->from[1] + 1); + if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1]; + } + for (e = v->Succ; e; e = e->Nxt) + { m += HASH(e->Dst, e->To - e->From + 1 + e->s); + if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst; + } + Tally = (vk2>>2)&(TWIDTH-1); + return m; +} + +static ulong +mk_special(int sigma, Vertex *n, Vertex *v) +{ ulong m = 0, vk2 = 0; + Edge *f; + int i; + + for (i = 0; i < 2; i++) + if (v->dst[i]) + { if (sigma >= v->from[i] && sigma <= v->to[i]) + { m += HASH(v->dst[i], v->to[i]-v->from[i]); + if ((ulong) v->dst[i] > vk2 + && v->to[i] > v->from[i]) + vk2 = (ulong) v->dst[i]; + } else + { m += HASH(v->dst[i], v->to[i]-v->from[i]+1); + if ((ulong) v->dst[i] > vk2) + vk2 = (ulong) v->dst[i]; + } } + for (f = v->Succ; f; f = f->Nxt) + { if (sigma >= f->From && sigma <= f->To) + { m += HASH(f->Dst, f->To - f->From + f->s); + if ((ulong) f->Dst > vk2 + && f->To - f->From + f->s > 0) + vk2 = (ulong) f->Dst; + } else if (f->s == 1 && sigma == f->S) + { m += HASH(f->Dst, f->To - f->From + 1); + if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; + } else + { m += HASH(f->Dst, f->To - f->From + 1 + f->s); + if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; + } } + + if ((ulong) n > vk2) vk2 = (ulong) n; + Tally = (vk2>>2)&(TWIDTH-1); + m += HASH(n, 1); + return m; +} + +void +dfa_init(ushort nr_layers) +{ int i; Vertex *r, *t; + + dfa_depth = nr_layers; /* one byte per layer */ + path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *)); + layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *)); + lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar)); + lastword[dfa_depth] = lastword[0] = 255; + path[0] = R = new_vertex(); F = new_vertex(); + + for (i = 1, r = R; i < dfa_depth; i++, r = t) + t = allDelta(r, i-1); + NF = allDelta(r, i-1); +} + +#if 0 +static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; } +#endif + +double +tree_stats(Vertex *t) +{ Edge *e; double cnt=0.0; + if (!t) return 0; + if (!t->key) return 0; + t->key = 0; /* precaution */ + if (t->dst[0]) cnt++; + if (t->dst[1]) cnt++; + for (e = t->Succ; e; e = e->Nxt) + cnt++; + cnt += tree_stats(t->lnk); + cnt += tree_stats(t->left); + cnt += tree_stats(t->right); + return cnt; +} + +void +dfa_stats(void) +{ int i, j; double cnt = 0.0; + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + cnt += tree_stats(layers[i*TWIDTH+j]); + printf("Minimized Automaton: %6d nodes and %6g edges\n", + nr_states, cnt); +} + +int +dfa_member(ulong n) +{ Vertex **p, **q; + uchar *w = &word[n]; + int i; + + p = &path[n]; q = (p+1); + for (i = n; i < dfa_depth; i++) + *q++ = Delta(*p++, *w++); + return (*p == F); +} + +int +dfa_store(uchar *sv) +{ Vertex **p, **q, *s, *y, *old, *new = F; + uchar *w, *u = lastword; + int i, j, k; + + w = word = sv; + while (*w++ == *u++) /* find first byte that differs */ + ; + pfrst = (int) (u - lastword) - 1; + memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst); + if (pfrst > iv) pfrst = iv; + if (pfrst > nv) pfrst = nv; +/* phase1: */ + p = &path[pfrst]; q = (p+1); w = &word[pfrst]; + for (i = pfrst; i < dfa_depth; i++) + *q++ = Delta(*p++, *w++); /* (*p)->delta[*w++]; */ + + if (*p == F) return 1; /* it's already there */ +/* phase2: */ + iv = dfa_depth; + do { iv--; + old = new; + new = find_it(path[iv], old, word[iv], iv); + } while (new && iv > 0); + +/* phase3: */ + nv = k = 0; s = path[0]; + for (j = 1; j <= iv; ++j) + if (path[j]->num > 1) + { y = new_vertex(); + copyEdges(y, path[j]); + insert_it(y, j); + numDelta(y, 1); + delete_it(s, j-1); + setDelta(s, word[j-1], y); + insert_it(s, j-1); + y->num = 1; /* initial value 1 */ + s = y; + path[j]->num--; /* only 1 moved from j to y */ + k = 1; + } else + { s = path[j]; + if (!k) nv = j; + } + y = Delta(s, word[iv]); + y->num--; + delete_it(s, iv); + setDelta(s, word[iv], old); + insert_it(s, iv); + old->num++; + + for (j = iv+1; j < dfa_depth; j++) + if (path[j]->num == 0) + { numDelta(path[j], -1); + delete_it(path[j], j); + recyc_vertex(path[j]); + } else + break; + return 0; +} + +static Vertex * +splay(ulong i, Vertex *t) +{ Vertex N, *l, *r, *y; + + if (!t) return t; + N.left = N.right = (Vertex *) 0; + l = r = &N; + for (;;) + { if (i < t->key) + { if (!t->left) break; + if (i < t->left->key) + { y = t->left; + t->left = y->right; + y->right = t; + t = y; + if (!t->left) break; + } + r->left = t; + r = t; + t = t->left; + } else if (i > t->key) + { if (!t->right) break; + if (i > t->right->key) + { y = t->right; + t->right = y->left; + y->left = t; + t = y; + if (!t->right) break; + } + l->right = t; + l = t; + t = t->right; + } else + break; + } + l->right = t->left; + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + +static void +insert_it(Vertex *v, int L) +{ Vertex *new, *t; + ulong i; int nr; + + i = mk_key(v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + + v->key = i; + if (!t) + { layers[nr] = v; + return; + } + t = splay(i, t); + if (i < t->key) + { new = v; + new->left = t->left; + new->right = t; + t->left = (Vertex *) 0; + } else if (i > t->key) + { new = v; + new->right = t->right; + new->left = t; + t->right = (Vertex *) 0; + } else /* it's already there */ + { v->lnk = t->lnk; /* put in linked list off v */ + t->lnk = v; + new = t; + } + layers[nr] = new; +} + +static int +checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma) +{ Edge *g, *f; + int i, k, j = 1; + + for (k = 0; k < 2; k++) + if (h->dst[k]) + { if (sigma >= h->from[k] && sigma <= h->to[k]) + { if (h->dst[k] != n) goto no_match; + } + for (i = h->from[k]; i <= h->to[k]; i++) + { if (i == sigma) continue; + g = cacheDelta(v, i, j); j = 0; + if (h->dst[k] != g->Dst) + goto no_match; + if (g->s == 0 || g->S != i) + i = g->To; + } } + for (f = h->Succ; f; f = f->Nxt) + { if (INRANGE(f,sigma)) + { if (f->Dst != n) goto no_match; + } + for (i = f->From; i <= f->To; i++) + { if (i == sigma) continue; + g = cacheDelta(v, i, j); j = 0; + if (f->Dst != g->Dst) + goto no_match; + if (g->s == 1 && i == g->S) + continue; + i = g->To; + } + if (f->s && f->S != sigma) + { g = cacheDelta(v, f->S, 1); + if (f->Dst != g->Dst) + goto no_match; + } + } + if (h->Succ || h->dst[0] || h->dst[1]) return 1; +no_match: + return 0; +} + +static Vertex * +find_it(Vertex *v, Vertex *n, uchar sigma, int L) +{ Vertex *z, *t; + ulong i; int nr; + + i = mk_special(sigma,n,v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + + if (!t) return (Vertex *) 0; + layers[nr] = t = splay(i, t); + if (i == t->key) + for (z = t; z; z = z->lnk) + if (checkit(z, v, n, sigma)) + return z; + + return (Vertex *) 0; +} + +static void +delete_it(Vertex *v, int L) +{ Vertex *x, *t; + ulong i; int nr; + + i = cheap_key(v); + nr = ((L*TWIDTH)+Tally); + t = layers[nr]; + if (!t) return; + + t = splay(i, t); + if (i == t->key) + { Vertex *z, *y = (Vertex *) 0; + for (z = t; z && z != v; y = z, z = z->lnk) + ; + if (z != v) goto bad; + if (y) + { y->lnk = z->lnk; + z->lnk = (Vertex *) 0; + layers[nr] = t; + return; + } else if (z->lnk) /* z == t == v */ + { y = z->lnk; + y->left = t->left; + y->right = t->right; + t->left = t->right = t->lnk = (Vertex *) 0; + layers[nr] = y; + return; + } + /* delete the node itself */ + if (!t->left) + { x = t->right; + } else + { x = splay(i, t->left); + x->right = t->right; + } + t->left = t->right = t->lnk = (Vertex *) 0; + layers[nr] = x; + return; + } +bad: Uerror("cannot happen delete"); +} +#endif +#if defined(MA) && (defined(W_XPT) || defined(R_XPT)) +static Vertex **temptree; +static char wbuf[4096]; +static int WCNT = 4096, wcnt=0; +static uchar stacker[MA+1]; +static ulong stackcnt = 0; +extern double nstates, nlinks, truncs, truncs2; + +static void +xwrite(int fd, char *b, int n) +{ + if (wcnt+n >= 4096) + { write(fd, wbuf, wcnt); + wcnt = 0; + } + memcpy(&wbuf[wcnt], b, n); + wcnt += n; +} + +static void +wclose(fd) +{ + if (wcnt > 0) + write(fd, wbuf, wcnt); + wcnt = 0; + close(fd); +} + +static void +w_vertex(int fd, Vertex *v) +{ char t[3]; int i; Edge *e; + + xwrite(fd, (char *) &v, sizeof(Vertex *)); + t[0] = 0; + for (i = 0; i < 2; i++) + if (v->dst[i]) + { t[1] = v->from[i], t[2] = v->to[i]; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *)); + } + for (e = v->Succ; e; e = e->Nxt) + { t[1] = e->From, t[2] = e->To; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *)); + + if (e->s) + { t[1] = t[2] = e->S; + xwrite(fd, t, 3); + xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *)); + } } +} + +static void +w_layer(int fd, Vertex *v) +{ uchar c=1; + + if (!v) return; + xwrite(fd, (char *) &c, 1); + w_vertex(fd, v); + w_layer(fd, v->lnk); + w_layer(fd, v->left); + w_layer(fd, v->right); +} + +void +w_xpoint(void) +{ int fd; char nm[64]; + int i, j; uchar c; + static uchar xwarned = 0; + + sprintf(nm, "%s.xpt", PanSource); + if ((fd = creat(nm, 0666)) <= 0) + if (!xwarned) + { xwarned = 1; + printf("cannot creat checkpoint file\n"); + return; + } + xwrite(fd, (char *) &nstates, sizeof(double)); + xwrite(fd, (char *) &truncs, sizeof(double)); + xwrite(fd, (char *) &truncs2, sizeof(double)); + xwrite(fd, (char *) &nlinks, sizeof(double)); + xwrite(fd, (char *) &dfa_depth, sizeof(int)); + xwrite(fd, (char *) &R, sizeof(Vertex *)); + xwrite(fd, (char *) &F, sizeof(Vertex *)); + xwrite(fd, (char *) &NF, sizeof(Vertex *)); + + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + { w_layer(fd, layers[i*TWIDTH+j]); + c = 2; xwrite(fd, (char *) &c, 1); + } + wclose(fd); +} + +static void +xread(int fd, char *b, int n) +{ int m = wcnt; int delta = 0; + if (m < n) + { if (m > 0) memcpy(b, &wbuf[WCNT-m], m); + delta = m; + WCNT = wcnt = read(fd, wbuf, 4096); + if (wcnt < n-m) + Uerror("xread failed -- insufficient data"); + n -= m; + } + memcpy(&b[delta], &wbuf[WCNT-wcnt], n); + wcnt -= n; +} + +static void +x_cleanup(Vertex *c) +{ Edge *e; /* remove the tree and edges from c */ + if (!c) return; + for (e = c->Succ; e; e = e->Nxt) + x_cleanup(e->Dst); + recyc_vertex(c); +} + +static void +x_remove(void) +{ Vertex *tmp; int i, s; + int r, j; + /* double-check: */ + stacker[dfa_depth-1] = 0; r = dfa_store(stacker); + stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1); + if (r != 1 || j != 0) + { printf("%d: ", stackcnt); + for (i = 0; i < dfa_depth; i++) + printf("%d,", stacker[i]); + printf(" -- not a stackstate \n", r, j); + return; + } + stacker[dfa_depth-1] = 1; + s = dfa_member(dfa_depth-1); + + { tmp = F; F = NF; NF = tmp; } /* complement */ + if (s) dfa_store(stacker); + stacker[dfa_depth-1] = 0; + dfa_store(stacker); + stackcnt++; + { tmp = F; F = NF; NF = tmp; } +} + +static void +x_rm_stack(Vertex *t, int k) +{ int j; Edge *e; + + if (k == 0) + { x_remove(); + return; + } + if (t) + for (e = t->Succ; e; e = e->Nxt) + { for (j = e->From; j <= (int) e->To; j++) + { stacker[k] = (uchar) j; + x_rm_stack(e->Dst, k-1); + } + if (e->s) + { stacker[k] = e->S; + x_rm_stack(e->Dst, k-1); + } } +} + +static Vertex * +insert_withkey(Vertex *v, int L) +{ Vertex *new, *t = temptree[L]; + + if (!t) { temptree[L] = v; return v; } + t = splay(v->key, t); + if (v->key < t->key) + { new = v; + new->left = t->left; + new->right = t; + t->left = (Vertex *) 0; + } else if (v->key > t->key) + { new = v; + new->right = t->right; + new->left = t; + t->right = (Vertex *) 0; + } else + { if (t != R && t != F && t != NF) + Uerror("double insert, bad checkpoint data"); + else + { recyc_vertex(v); + new = t; + } } + temptree[L] = new; + + return new; +} + +static Vertex * +find_withkey(Vertex *v, int L) +{ Vertex *t = temptree[L]; + if (t) + { temptree[L] = t = splay((ulong) v, t); + if (t->key == (ulong) v) + return t; + } + Uerror("not found error, bad checkpoint data"); + return (Vertex *) 0; +} + +void +r_layer(int fd, int n) +{ Vertex *v; + Edge *e; + char c, t[2]; + + for (;;) + { xread(fd, &c, 1); + if (c == 2) break; + if (c == 1) + { v = new_vertex(); + xread(fd, (char *) &(v->key), sizeof(Vertex *)); + v = insert_withkey(v, n); + } else /* c == 0 */ + { e = new_edge((Vertex *) 0); + xread(fd, t, 2); + e->From = t[0]; + e->To = t[1]; + xread(fd, (char *) &(e->Dst), sizeof(Vertex *)); + insert_edge(v, e); + } } +} + +static void +v_fix(Vertex *t, int nr) +{ int i; Edge *e; + + if (!t) return; + + for (i = 0; i < 2; i++) + if (t->dst[i]) + t->dst[i] = find_withkey(t->dst[i], nr); + + for (e = t->Succ; e; e = e->Nxt) + e->Dst = find_withkey(e->Dst, nr); + + v_fix(t->left, nr); + v_fix(t->right, nr); +} + +static void +v_insert(Vertex *t, int nr) +{ Edge *e; int i; + + if (!t) return; + v_insert(t->left, nr); + v_insert(t->right, nr); + + /* remove only leafs from temptree */ + t->left = t->right = t->lnk = (Vertex *) 0; + insert_it(t, nr); /* into layers */ + for (i = 0; i < 2; i++) + if (t->dst[i]) + t->dst[i]->num += (t->to[i] - t->from[i] + 1); + for (e = t->Succ; e; e = e->Nxt) + e->Dst->num += (e->To - e->From + 1 + e->s); +} + +static void +x_fixup(void) +{ int i; + + for (i = 0; i < dfa_depth; i++) + v_fix(temptree[i], (i+1)); + + for (i = dfa_depth; i >= 0; i--) + v_insert(temptree[i], i); +} + +static Vertex * +x_tail(Vertex *t, ulong want) +{ int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0; + + if (!t) return v; + + yes = no = 0; + for (i = 0; i < 2; i++) + if ((ulong) t->dst[i] == want) + { /* was t->from[i] <= 0 && t->to[i] >= 0 */ + /* but from and to are uchar */ + if (t->from[i] == 0) + yes = 1; + else + if (t->from[i] <= 4 && t->to[i] >= 4) + no = 1; + } + + for (e = t->Succ; e; e = e->Nxt) + if ((ulong) e->Dst == want) + { /* was INRANGE(e,0) but From and To are uchar */ + if ((e->From == 0) || (e->s==1 && e->S==0)) + yes = 1; + else if (INRANGE(e, 4)) + no = 1; + } + if (yes && !no) return t; + v = x_tail(t->left, want); if (v) return v; + v = x_tail(t->right, want); if (v) return v; + return (Vertex *) 0; +} + +static void +x_anytail(Vertex *t, Vertex *c, int nr) +{ int i; Edge *e, *f; Vertex *v; + + if (!t) return; + + for (i = 0; i < 2; i++) + if ((ulong) t->dst[i] == c->key) + { v = new_vertex(); v->key = t->key; + f = new_edge(v); + f->From = t->from[i]; + f->To = t->to[i]; + f->Nxt = c->Succ; + c->Succ = f; + if (nr > 0) + x_anytail(temptree[nr-1], v, nr-1); + } + + for (e = t->Succ; e; e = e->Nxt) + if ((ulong) e->Dst == c->key) + { v = new_vertex(); v->key = t->key; + f = new_edge(v); + f->From = e->From; + f->To = e->To; + f->s = e->s; + f->S = e->S; + f->Nxt = c->Succ; + c->Succ = f; + x_anytail(temptree[nr-1], v, nr-1); + } + + x_anytail(t->left, c, nr); + x_anytail(t->right, c, nr); +} + +static Vertex * +x_cpy_rev(void) +{ Vertex *c, *v; /* find 0 and !4 predecessor of F */ + + v = x_tail(temptree[dfa_depth-1], F->key); + if (!v) return (Vertex *) 0; + + c = new_vertex(); c->key = v->key; + + /* every node on dfa_depth-2 that has v->key as succ */ + /* make copy and let c point to these (reversing ptrs) */ + + x_anytail(temptree[dfa_depth-2], c, dfa_depth-2); + + return c; +} + +void +r_xpoint(void) +{ int fd; char nm[64]; Vertex *d; + int i, j; + + wcnt = 0; + sprintf(nm, "%s.xpt", PanSource); + if ((fd = open(nm, 0)) < 0) /* O_RDONLY */ + Uerror("cannot open checkpoint file"); + + xread(fd, (char *) &nstates, sizeof(double)); + xread(fd, (char *) &truncs, sizeof(double)); + xread(fd, (char *) &truncs2, sizeof(double)); + xread(fd, (char *) &nlinks, sizeof(double)); + xread(fd, (char *) &dfa_depth, sizeof(int)); + + if (dfa_depth != MA+a_cycles) + Uerror("bad dfa_depth in checkpoint file"); + + path = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *)); + layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *)); + temptree = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *)); + lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar)); + lastword[dfa_depth] = lastword[0] = 255; + + path[0] = R = new_vertex(); + xread(fd, (char *) &R->key, sizeof(Vertex *)); + R = insert_withkey(R, 0); + + F = new_vertex(); + xread(fd, (char *) &F->key, sizeof(Vertex *)); + F = insert_withkey(F, dfa_depth); + + NF = new_vertex(); + xread(fd, (char *) &NF->key, sizeof(Vertex *)); + NF = insert_withkey(NF, dfa_depth); + + for (j = 0; j < TWIDTH; j++) + for (i = 0; i < dfa_depth+1; i++) + r_layer(fd, i); + + if (wcnt != 0) Uerror("bad count in checkpoint file"); + + d = x_cpy_rev(); + x_fixup(); + stacker[dfa_depth-1] = 0; + x_rm_stack(d, dfa_depth-2); + x_cleanup(d); + close(fd); + + printf("pan: removed %d stackstates\n", stackcnt); + nstates -= (double) stackcnt; +} +#endif +#ifdef VERI +void +check_claim(int st) +{ + if (st == endclaim) + uerror("claim violated!"); + if (stopstate[VERI][st]) + uerror("end state in claim reached"); +} +#endif +void +c_globals(void) +{ /* int i; */ + printf("global vars:\n"); + printf(" byte write_off: %d\n", now.write_off); + { int l_in; + for (l_in = 0; l_in < 2; l_in++) + { + printf(" byte commit_count[%d]: %d\n", l_in, now.commit_count[l_in]); + } + } + printf(" byte _commit_sum: %d\n", now._commit_sum); + printf(" byte read_off: %d\n", now.read_off); + printf(" byte events_lost: %d\n", now.events_lost); + printf(" byte refcount: %d\n", now.refcount); + { int l_in; + for (l_in = 0; l_in < 8; l_in++) + { + printf(" bit buffer_use[%d]: %d\n", l_in, now.buffer_use[l_in]); + } + } +} +void +c_locals(int pid, int tp) +{ /* int i; */ + switch(tp) { + case 5: + /* none */ + break; + case 4: + printf("local vars proc %d (:init:):\n", pid); + printf(" byte i: %d\n", ((P4 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P4 *)pptr(pid))->j); + printf(" byte sum: %d\n", ((P4 *)pptr(pid))->sum); + printf(" byte commit_sum: %d\n", ((P4 *)pptr(pid))->commit_sum); + break; + case 3: + /* none */ + break; + case 2: + printf("local vars proc %d (reader):\n", pid); + printf(" byte i: %d\n", ((P2 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P2 *)pptr(pid))->j); + break; + case 1: + printf("local vars proc %d (tracer):\n", pid); + printf(" byte size: %d\n", ((P1 *)pptr(pid))->size); + printf(" byte prev_off: %d\n", ((P1 *)pptr(pid))->prev_off); + printf(" byte new_off: %d\n", ((P1 *)pptr(pid))->new_off); + printf(" byte tmp_commit: %d\n", ((P1 *)pptr(pid))->tmp_commit); + printf(" byte i: %d\n", ((P1 *)pptr(pid))->i); + printf(" byte j: %d\n", ((P1 *)pptr(pid))->j); + break; + case 0: + printf("local vars proc %d (switcher):\n", pid); + printf(" byte prev_off: %d\n", ((P0 *)pptr(pid))->prev_off); + printf(" byte new_off: %d\n", ((P0 *)pptr(pid))->new_off); + printf(" byte tmp_commit: %d\n", ((P0 *)pptr(pid))->tmp_commit); + printf(" byte size: %d\n", ((P0 *)pptr(pid))->size); + break; + } +} +void +printm(int x) +{ + switch (x) { + default: Printf("%d", x); + } +} +void +c_chandump(int unused) { unused++; /* avoid complaints */ } diff --git a/trunk/verif/nico-md-merge/pan.h b/trunk/verif/nico-md-merge/pan.h new file mode 100644 index 00000000..bd685009 --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.h @@ -0,0 +1,638 @@ +#define SpinVersion "Spin Version 5.1.6 -- 9 May 2008" +#define PanSource "pan.spin" + +#ifdef WIN64 +#define ONE_L ((unsigned long) 1) +#define long long long +#else +#define ONE_L (1L) +#endif +char *TrailFile = PanSource; /* default */ +char *trailfilename; +#if defined(BFS) +#ifndef SAFETY +#define SAFETY +#endif +#ifndef XUSAFE +#define XUSAFE +#endif +#endif +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef uint +#define uint unsigned int +#endif +#define DELTA 500 +#ifdef MA + #if NCORE>1 && !defined(SEP_STATE) + #define SEP_STATE + #endif +#if MA==1 +#undef MA +#define MA 100 +#endif +#endif +#ifdef W_XPT +#if W_XPT==1 +#undef W_XPT +#define W_XPT 1000000 +#endif +#endif +#ifndef NFAIR +#define NFAIR 2 /* must be >= 2 */ +#endif +#define HAS_CODE +#define MERGED 1 +#ifdef NP /* includes np_ demon */ +#define HAS_NP 2 +#define VERI 6 +#define endclaim 3 /* none */ +#endif +#if !defined(NOCLAIM) && !defined NP +#define VERI 5 +#define endclaim endstate5 +#endif +typedef struct S_F_MAP { + char *fnm; int from; int upto; +} S_F_MAP; + +#define nstates5 9 /* :never: */ +#define nstates_claim nstates5 +#define endstate5 8 +short src_ln5 [] = { + 0, 304, 304, 305, 305, 303, 307, 308, + 309, 0, }; +S_F_MAP src_file5 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 8 }, + { "-", 9, 10 } +}; +#define src_claim src_ln5 +uchar reached5 [] = { + 0, 1, 1, 1, 1, 0, 1, 1, + 0, 0, }; +uchar *loopstate5; +#define reached_claim reached5 + +#define nstates4 44 /* :init: */ +#define endstate4 43 +short src_ln4 [] = { + 0, 255, 257, 258, 259, 260, 260, 256, + 263, 256, 263, 265, 267, 268, 269, 270, + 270, 266, 272, 266, 272, 273, 274, 276, + 277, 278, 279, 280, 280, 275, 282, 275, + 282, 284, 285, 286, 287, 288, 288, 283, + 290, 283, 254, 291, 0, }; +S_F_MAP src_file4 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 43 }, + { "-", 44, 45 } +}; +uchar reached4 [] = { + 0, 1, 1, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 1, 0, 0, 1, + 1, 0, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 1, 1, + 0, 1, 0, 0, 0, 1, 1, 0, + 1, 1, 0, 0, 0, }; +uchar *loopstate4; + +#define nstates3 10 /* cleaner */ +#define endstate3 9 +short src_ln3 [] = { + 0, 240, 241, 242, 243, 239, 245, 239, + 238, 246, 0, }; +S_F_MAP src_file3 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 9 }, + { "-", 10, 11 } +}; +uchar reached3 [] = { + 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 0, }; +uchar *loopstate3; + +#define nstates2 30 /* reader */ +#define endstate2 29 +short src_ln2 [] = { + 0, 203, 205, 207, 208, 209, 210, 211, + 211, 206, 213, 206, 204, 219, 221, 222, + 223, 224, 224, 220, 226, 220, 226, 218, + 228, 228, 198, 230, 198, 230, 0, }; +S_F_MAP src_file2 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 29 }, + { "-", 30, 31 } +}; +uchar reached2 [] = { + 0, 1, 1, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 0, 1, 1, 0, 0, }; +uchar *loopstate2; + +#define nstates1 52 /* tracer */ +#define endstate1 51 +short src_ln1 [] = { + 0, 126, 127, 125, 131, 132, 133, 133, + 130, 135, 129, 138, 138, 139, 139, 137, + 141, 141, 143, 144, 145, 146, 147, 147, + 142, 149, 142, 136, 155, 157, 158, 159, + 160, 160, 156, 162, 156, 162, 164, 167, + 170, 171, 172, 173, 168, 175, 154, 177, + 179, 181, 176, 183, 0, }; +S_F_MAP src_file1 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 51 }, + { "-", 52, 53 } +}; +uchar reached1 [] = { + 0, 1, 0, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 0, 1, 1, 0, 0, 0, + 1, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, }; +uchar *loopstate1; + +#define nstates0 32 /* switcher */ +#define endstate0 31 +short src_ln0 [] = { + 0, 75, 76, 77, 80, 81, 82, 83, + 83, 78, 85, 74, 88, 88, 89, 89, + 87, 91, 86, 94, 96, 99, 102, 103, + 104, 105, 100, 107, 107, 93, 110, 111, + 0, }; +S_F_MAP src_file0 [] = { + { "-", 0, 0 }, + { "pan.___", 1, 31 }, + { "-", 32, 33 } +}; +uchar reached0 [] = { + 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 0, + 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, 0, 1, 0, + 0, }; +uchar *loopstate0; +struct { + int tp; short *src; +} src_all[] = { + { 5, &src_ln5[0] }, + { 4, &src_ln4[0] }, + { 3, &src_ln3[0] }, + { 2, &src_ln2[0] }, + { 1, &src_ln1[0] }, + { 0, &src_ln0[0] }, + { 0, (short *) 0 } +}; +short *frm_st0; +struct { + char *c; char *t; +} code_lookup[] = { + { (char *) 0, "" } +}; +#define _T5 62 +#define _T2 63 +#define T_ID unsigned char +#define SYNC 0 +#define ASYNC 0 + +#ifndef NCORE + #ifdef DUAL_CORE + #define NCORE 2 + #elif QUAD_CORE + #define NCORE 4 + #else + #define NCORE 1 + #endif +#endif +char *procname[] = { + "switcher", + "tracer", + "reader", + "cleaner", + ":init:", + ":never:", + ":np_:", +}; + +typedef struct P5 { /* :never: */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P5; +#define Air5 (sizeof(P5) - 3) +#define Pinit ((P4 *)this) +typedef struct P4 { /* :init: */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar i; + uchar j; + uchar sum; + uchar commit_sum; +} P4; +#define Air4 (sizeof(P4) - Offsetof(P4, commit_sum) - 1*sizeof(uchar)) +#define Pcleaner ((P3 *)this) +typedef struct P3 { /* cleaner */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P3; +#define Air3 (sizeof(P3) - 3) +#define Preader ((P2 *)this) +typedef struct P2 { /* reader */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar i; + uchar j; +} P2; +#define Air2 (sizeof(P2) - Offsetof(P2, j) - 1*sizeof(uchar)) +#define Ptracer ((P1 *)this) +typedef struct P1 { /* tracer */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar size; + uchar prev_off; + uchar new_off; + uchar tmp_commit; + uchar i; + uchar j; +} P1; +#define Air1 (sizeof(P1) - Offsetof(P1, j) - 1*sizeof(uchar)) +#define Pswitcher ((P0 *)this) +typedef struct P0 { /* switcher */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ + uchar prev_off; + uchar new_off; + uchar tmp_commit; + uchar size; +} P0; +#define Air0 (sizeof(P0) - Offsetof(P0, size) - 1*sizeof(uchar)) +typedef struct P6 { /* np_ */ + unsigned _pid : 8; /* 0..255 */ + unsigned _t : 4; /* proctype */ + unsigned _p : 7; /* state */ +} P6; +#define Air6 (sizeof(P6) - 3) +#if defined(BFS) && defined(REACH) +#undef REACH +#endif +#ifdef VERI +#define BASE 1 +#else +#define BASE 0 +#endif +typedef struct Trans { + short atom; /* if &2 = atomic trans; if &8 local */ +#ifdef HAS_UNLESS + short escp[HAS_UNLESS]; /* lists the escape states */ + short e_trans; /* if set, this is an escp-trans */ +#endif + short tpe[2]; /* class of operation (for reduction) */ + short qu[6]; /* for conditional selections: qid's */ + uchar ty[6]; /* ditto: type's */ +#ifdef NIBIS + short om; /* completion status of preselects */ +#endif + char *tp; /* src txt of statement */ + int st; /* the nextstate */ + int t_id; /* transition id, unique within proc */ + int forw; /* index forward transition */ + int back; /* index return transition */ + struct Trans *nxt; +} Trans; + +#define qptr(x) (((uchar *)&now)+(int)q_offset[x]) +#define pptr(x) (((uchar *)&now)+(int)proc_offset[x]) +extern uchar *Pptr(int); +#define q_sz(x) (((Q0 *)qptr(x))->Qlen) + +#ifndef VECTORSZ +#define VECTORSZ 1024 /* sv size in bytes */ +#endif + +#ifdef VERBOSE +#ifndef CHECK +#define CHECK +#endif +#ifndef DEBUG +#define DEBUG +#endif +#endif +#ifdef SAFETY +#ifndef NOFAIR +#define NOFAIR +#endif +#endif +#ifdef NOREDUCE +#ifndef XUSAFE +#define XUSAFE +#endif +#if !defined(SAFETY) && !defined(MA) +#define FULLSTACK +#endif +#else +#ifdef BITSTATE +#if defined(SAFETY) && !defined(HASH64) +#define CNTRSTACK +#else +#define FULLSTACK +#endif +#else +#define FULLSTACK +#endif +#endif +#ifdef BITSTATE +#ifndef NOCOMP +#define NOCOMP +#endif +#if !defined(LC) && defined(SC) +#define LC +#endif +#endif +#if defined(COLLAPSE2) || defined(COLLAPSE3) || defined(COLLAPSE4) +/* accept the above for backward compatibility */ +#define COLLAPSE +#endif +#ifdef HC +#undef HC +#define HC4 +#endif +#ifdef HC0 +#define HC 0 +#endif +#ifdef HC1 +#define HC 1 +#endif +#ifdef HC2 +#define HC 2 +#endif +#ifdef HC3 +#define HC 3 +#endif +#ifdef HC4 +#define HC 4 +#endif +#ifdef COLLAPSE +#if NCORE>1 && !defined(SEP_STATE) +unsigned long *ncomps; /* in shared memory */ +#else +unsigned long ncomps[256+2]; +#endif +#endif +#define MAXQ 255 +#define MAXPROC 255 +#define WS sizeof(void *) /* word size in bytes */ +typedef struct Stack { /* for queues and processes */ +#if VECTORSZ>32000 + int o_delta; + int o_offset; + int o_skip; + int o_delqs; +#else + short o_delta; + short o_offset; + short o_skip; + short o_delqs; +#endif + short o_boq; +#ifndef XUSAFE + char *o_name; +#endif + char *body; + struct Stack *nxt; + struct Stack *lst; +} Stack; + +typedef struct Svtack { /* for complete state vector */ +#if VECTORSZ>32000 + int o_delta; + int m_delta; +#else + short o_delta; /* current size of frame */ + short m_delta; /* maximum size of frame */ +#endif +#if SYNC + short o_boq; +#endif +#define StackSize (WS) + char *body; + struct Svtack *nxt; + struct Svtack *lst; +} Svtack; + +Trans ***trans; /* 1 ptr per state per proctype */ + +struct H_el *Lstate; +int depthfound = -1; /* loop detection */ +#if VECTORSZ>32000 +int proc_offset[MAXPROC]; +int q_offset[MAXQ]; +#else +short proc_offset[MAXPROC]; +short q_offset[MAXQ]; +#endif +uchar proc_skip[MAXPROC]; +uchar q_skip[MAXQ]; +unsigned long vsize; /* vector size in bytes */ +#ifdef SVDUMP +int vprefix=0, svfd; /* runtime option -pN */ +#endif +char *tprefix = "trail"; /* runtime option -tsuffix */ +short boq = -1; /* blocked_on_queue status */ +typedef struct State { + uchar _nr_pr; + uchar _nr_qs; + uchar _a_t; /* cycle detection */ +#ifndef NOFAIR + uchar _cnt[NFAIR]; /* counters, weak fairness */ +#endif +#ifndef NOVSZ +#if VECTORSZ<65536 + unsigned short _vsz; +#else + unsigned long _vsz; +#endif +#endif +#ifdef HAS_LAST + uchar _last; /* pid executed in last step */ +#endif +#ifdef EVENT_TRACE +#if nstates_event<256 + uchar _event; +#else + unsigned short _event; +#endif +#endif + uchar buffer_use[8]; + uchar write_off; + uchar commit_count[2]; + uchar _commit_sum; + uchar read_off; + uchar events_lost; + uchar refcount; + uchar sv[VECTORSZ]; +} State; + +#define HAS_TRACK 0 +/* hidden variable: */ uchar deliver; +int _; /* a predefined write-only variable */ + +#define FORWARD_MOVES "pan.m" +#define REVERSE_MOVES "pan.b" +#define TRANSITIONS "pan.t" +#define _NP_ 6 +uchar reached6[3]; /* np_ */ +uchar *loopstate6; /* np_ */ +#define nstates6 3 /* np_ */ +#define endstate6 2 /* np_ */ + +#define start6 0 /* np_ */ +#define start5 5 +#define start_claim 5 +#define start4 42 +#define start3 8 +#define start2 26 +#define start1 3 +#define start0 11 +#ifdef NP + #define ACCEPT_LAB 1 /* at least 1 in np_ */ +#else + #define ACCEPT_LAB 1 /* user-defined accept labels */ +#endif +#ifdef MEMCNT + #ifdef MEMLIM + #warning -DMEMLIM takes precedence over -DMEMCNT + #undef MEMCNT + #else + #if MEMCNT<20 + #warning using minimal value -DMEMCNT=20 (=1MB) + #define MEMLIM (1) + #undef MEMCNT + #else + #if MEMCNT==20 + #define MEMLIM (1) + #undef MEMCNT + #else + #if MEMCNT>=50 + #error excessive value for MEMCNT + #else + #define MEMLIM (1<<(MEMCNT-20)) + #endif + #endif + #endif + #endif +#endif +#if NCORE>1 && !defined(MEMLIM) + #define MEMLIM (2048) /* need a default, using 2 GB */ +#endif +#define PROG_LAB 0 /* progress labels */ +uchar *accpstate[7]; +uchar *progstate[7]; +uchar *loopstate[7]; +uchar *reached[7]; +uchar *stopstate[7]; +uchar *visstate[7]; +short *mapstate[7]; +#ifdef HAS_CODE +int NrStates[7]; +#endif +#define NQS 0 +short q_flds[1]; +short q_max[1]; +typedef struct Q0 { /* generic q */ + uchar Qlen; /* q_size */ + uchar _t; +} Q0; + +/** function prototypes **/ +char *emalloc(unsigned long); +char *Malloc(unsigned long); +int Boundcheck(int, int, int, int, Trans *); +int addqueue(int, int); +/* int atoi(char *); */ +/* int abort(void); */ +int close(int); +int delproc(int, int); +int endstate(void); +int hstore(char *, int); +#ifdef MA +int gstore(char *, int, uchar); +#endif +int q_cond(short, Trans *); +int q_full(int); +int q_len(int); +int q_zero(int); +int qrecv(int, int, int, int); +int unsend(int); +/* void *sbrk(int); */ +void Uerror(char *); +void assert(int, char *, int, int, Trans *); +void c_chandump(int); +void c_globals(void); +void c_locals(int, int); +void checkcycles(void); +void crack(int, int, Trans *, short *); +void d_sfh(const char *, int); +void sfh(const char *, int); +void d_hash(uchar *, int); +void s_hash(uchar *, int); +void r_hash(uchar *, int); +void delq(int); +void do_reach(void); +void pan_exit(int); +void exit(int); +void hinit(void); +void imed(Trans *, int, int, int); +void new_state(void); +void p_restor(int); +void putpeg(int, int); +void putrail(void); +void q_restor(void); +void retrans(int, int, int, short *, uchar *, uchar *); +void settable(void); +void setq_claim(int, int, char *, int, char *); +void sv_restor(void); +void sv_save(void); +void tagtable(int, int, int, short *, uchar *); +void do_dfs(int, int, int, short *, uchar *, uchar *); +void uerror(char *); +void unrecv(int, int, int, int, int); +void usage(FILE *); +void wrap_stats(void); +#if defined(FULLSTACK) && defined(BITSTATE) +int onstack_now(void); +void onstack_init(void); +void onstack_put(void); +void onstack_zap(void); +#endif +#ifndef XUSAFE +int q_S_check(int, int); +int q_R_check(int, int); +uchar q_claim[MAXQ+1]; +char *q_name[MAXQ+1]; +char *p_name[MAXPROC+1]; +#endif +void qsend(int, int, int); +#define Addproc(x) addproc(x) +#define LOCAL 1 +#define Q_FULL_F 2 +#define Q_EMPT_F 3 +#define Q_EMPT_T 4 +#define Q_FULL_T 5 +#define TIMEOUT_F 6 +#define GLOBAL 7 +#define BAD 8 +#define ALPHA_F 9 +#define NTRANS 64 +#ifdef PEG +long peg[NTRANS]; +#endif diff --git a/trunk/verif/nico-md-merge/pan.ltl b/trunk/verif/nico-md-merge/pan.ltl new file mode 100644 index 00000000..f36ef779 --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.ltl @@ -0,0 +1,17 @@ +#define rwoff1 (write_off - read_off >= 0) +#define rwoff2 (write_off - read_off < HALF_UCHAR) + +#define wcsum1 (write_off - _commit_sum >= 0) +#define wcsum2 (write_off - _commit_sum < HALF_UCHAR) + +#define buffer_large_enough (NUMPROCS + NUMSWITCH <= BUFSIZE) +#define have_events_lost (events_lost != 0) +never { /* !( []((buffer_large_enough) -> (!have_events_lost))) */ +T0_init: + if + :: ((buffer_large_enough) && (have_events_lost)) -> goto accept_all + :: (1) -> goto T0_init + fi; +accept_all: + skip +} diff --git a/trunk/verif/nico-md-merge/pan.m b/trunk/verif/nico-md-merge/pan.m new file mode 100644 index 00000000..e6dd5a35 --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.m @@ -0,0 +1,980 @@ +#define rand pan_rand +#if defined(HAS_CODE) && defined(VERBOSE) + cpu_printf("Pr: %d Tr: %d\n", II, t->forw); +#endif + switch (t->forw) { + default: Uerror("bad forward move"); + case 0: /* if without executable clauses */ + continue; + case 1: /* generic 'goto' or 'skip' */ + IfNotBlocked + _m = 3; goto P999; + case 2: /* generic 'else' */ + IfNotBlocked + if (trpt->o_pm&1) continue; + _m = 3; goto P999; + + /* PROC :never: */ + case 3: /* STATE 1 - line 304 "pan.___" - [((((4+1)<=8)&&(events_lost!=0)))] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported1 = 0; + if (verbose && !reported1) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[1]); + reported1 = 1; + fflush(stdout); + } } +#endif + reached[5][1] = 1; + if (!((((4+1)<=8)&&(((int)now.events_lost)!=0)))) + continue; + _m = 3; goto P999; /* 0 */ + case 4: /* STATE 8 - line 309 "pan.___" - [-end-] (0:0:0 - 1) */ + +#if defined(VERI) && !defined(NP) + { static int reported8 = 0; + if (verbose && !reported8) + { printf("depth %d: Claim reached state %d (line %d)\n", + depth, frm_st0[t->forw], src_claim[8]); + reported8 = 1; + fflush(stdout); + } } +#endif + reached[5][8] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC :init: */ + case 5: /* STATE 1 - line 255 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[4][1] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 6: /* STATE 2 - line 257 "pan.___" - [((i<2))] (7:0:2 - 1) */ + IfNotBlocked + reached[4][2] = 1; + if (!((((int)((P4 *)this)->i)<2))) + continue; + /* merge: commit_count[i] = 0(7, 3, 7) */ + reached[4][3] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ]); + now.commit_count[ Index(((P4 *)this)->i, 2) ] = 0; +#ifdef VAR_RANGES + logval("commit_count[:init::i]", ((int)now.commit_count[ Index(((int)((P4 *)this)->i), 2) ])); +#endif + ; + /* merge: i = (i+1)(7, 4, 7) */ + reached[4][4] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 8, 7) */ + reached[4][8] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 7: /* STATE 5 - line 260 "pan.___" - [((i>=2))] (17:0:3 - 1) */ + IfNotBlocked + reached[4][5] = 1; + if (!((((int)((P4 *)this)->i)>=2))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b6(17, 6, 17) */ + reached[4][6] = 1; + ; + /* merge: _commit_sum = 0(17, 10, 17) */ + reached[4][10] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = 0; +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: i = 0(17, 11, 17) */ + reached[4][11] = 1; + (trpt+1)->bup.ovals[2] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 8: /* STATE 10 - line 263 "pan.___" - [_commit_sum = 0] (0:17:2 - 3) */ + IfNotBlocked + reached[4][10] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now._commit_sum); + now._commit_sum = 0; +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: i = 0(17, 11, 17) */ + reached[4][11] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 9: /* STATE 12 - line 267 "pan.___" - [((i<8))] (17:0:2 - 1) */ + IfNotBlocked + reached[4][12] = 1; + if (!((((int)((P4 *)this)->i)<8))) + continue; + /* merge: buffer_use[i] = 0(17, 13, 17) */ + reached[4][13] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 8) ]); + now.buffer_use[ Index(((P4 *)this)->i, 8) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[:init::i]", ((int)now.buffer_use[ Index(((int)((P4 *)this)->i), 8) ])); +#endif + ; + /* merge: i = (i+1)(17, 14, 17) */ + reached[4][14] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 18, 17) */ + reached[4][18] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 10: /* STATE 15 - line 270 "pan.___" - [((i>=8))] (0:0:1 - 1) */ + IfNotBlocked + reached[4][15] = 1; + if (!((((int)((P4 *)this)->i)>=8))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + _m = 3; goto P999; /* 0 */ + case 11: /* STATE 20 - line 272 "pan.___" - [(run reader())] (0:0:0 - 3) */ + IfNotBlocked + reached[4][20] = 1; + if (!(addproc(2))) + continue; + _m = 3; goto P999; /* 0 */ + case 12: /* STATE 21 - line 273 "pan.___" - [(run cleaner())] (29:0:1 - 1) */ + IfNotBlocked + reached[4][21] = 1; + if (!(addproc(3))) + continue; + /* merge: i = 0(0, 22, 29) */ + reached[4][22] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 30, 29) */ + reached[4][30] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 13: /* STATE 23 - line 276 "pan.___" - [((i<4))] (25:0:1 - 1) */ + IfNotBlocked + reached[4][23] = 1; + if (!((((int)((P4 *)this)->i)<4))) + continue; + /* merge: refcount = (refcount+1)(0, 24, 25) */ + reached[4][24] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 14: /* STATE 25 - line 278 "pan.___" - [(run tracer())] (29:0:1 - 1) */ + IfNotBlocked + reached[4][25] = 1; + if (!(addproc(1))) + continue; + /* merge: i = (i+1)(0, 26, 29) */ + reached[4][26] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 30, 29) */ + reached[4][30] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 15: /* STATE 27 - line 280 "pan.___" - [((i>=4))] (39:0:2 - 1) */ + IfNotBlocked + reached[4][27] = 1; + if (!((((int)((P4 *)this)->i)>=4))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b8(39, 28, 39) */ + reached[4][28] = 1; + ; + /* merge: i = 0(39, 32, 39) */ + reached[4][32] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 16: /* STATE 32 - line 282 "pan.___" - [i = 0] (0:39:1 - 3) */ + IfNotBlocked + reached[4][32] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = 0; +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 17: /* STATE 33 - line 284 "pan.___" - [((i<1))] (35:0:1 - 1) */ + IfNotBlocked + reached[4][33] = 1; + if (!((((int)((P4 *)this)->i)<1))) + continue; + /* merge: refcount = (refcount+1)(0, 34, 35) */ + reached[4][34] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 18: /* STATE 35 - line 286 "pan.___" - [(run switcher())] (39:0:1 - 1) */ + IfNotBlocked + reached[4][35] = 1; + if (!(addproc(0))) + continue; + /* merge: i = (i+1)(0, 36, 39) */ + reached[4][36] = 1; + (trpt+1)->bup.oval = ((int)((P4 *)this)->i); + ((P4 *)this)->i = (((int)((P4 *)this)->i)+1); +#ifdef VAR_RANGES + logval(":init::i", ((int)((P4 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 40, 39) */ + reached[4][40] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 19: /* STATE 37 - line 288 "pan.___" - [((i>=1))] (41:0:1 - 1) */ + IfNotBlocked + reached[4][37] = 1; + if (!((((int)((P4 *)this)->i)>=1))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P4 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P4 *)this)->i = 0; + /* merge: goto :b9(0, 38, 41) */ + reached[4][38] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 20: /* STATE 43 - line 291 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[4][43] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC cleaner */ + case 21: /* STATE 1 - line 240 "pan.___" - [((refcount==0))] (3:0:1 - 1) */ + IfNotBlocked + reached[3][1] = 1; + if (!((((int)now.refcount)==0))) + continue; + /* merge: refcount = (refcount+1)(0, 2, 3) */ + reached[3][2] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)+1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 22: /* STATE 3 - line 242 "pan.___" - [(run switcher())] (7:0:0 - 1) */ + IfNotBlocked + reached[3][3] = 1; + if (!(addproc(0))) + continue; + /* merge: goto :b5(0, 4, 7) */ + reached[3][4] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 23: /* STATE 9 - line 246 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[3][9] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC reader */ + case 24: /* STATE 1 - line 203 "pan.___" - [((((((write_off/(8/2))-(read_off/(8/2)))>0)&&(((write_off/(8/2))-(read_off/(8/2)))<(255/2)))&&(((commit_count[((read_off%8)/(8/2))]-(8/2))-(((read_off/8)*8)/2))==0)))] (0:0:0 - 1) */ + IfNotBlocked + reached[2][1] = 1; + if (!((((((((int)now.write_off)/(8/2))-(((int)now.read_off)/(8/2)))>0)&&(((((int)now.write_off)/(8/2))-(((int)now.read_off)/(8/2)))<(255/2)))&&(((((int)now.commit_count[ Index(((((int)now.read_off)%8)/(8/2)), 2) ])-(8/2))-(((((int)now.read_off)/8)*8)/2))==0)))) + continue; + _m = 3; goto P999; /* 0 */ + case 25: /* STATE 2 - line 205 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[2][2] = 1; + (trpt+1)->bup.oval = ((int)((P2 *)this)->i); + ((P2 *)this)->i = 0; +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 26: /* STATE 3 - line 207 "pan.___" - [((i<(8/2)))] (9:0:2 - 1) */ + IfNotBlocked + reached[2][3] = 1; + if (!((((int)((P2 *)this)->i)<(8/2)))) + continue; + /* merge: assert((buffer_use[((read_off+i)%8)]==0))(9, 4, 9) */ + reached[2][4] = 1; + assert((((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%8), 8) ])==0), "(buffer_use[((read_off+i)%8)]==0)", II, tt, t); + /* merge: buffer_use[((read_off+i)%8)] = 1(9, 5, 9) */ + reached[2][5] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%8), 8) ]); + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%8), 8) ] = 1; +#ifdef VAR_RANGES + logval("buffer_use[((read_off+reader:i)%8)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%8), 8) ])); +#endif + ; + /* merge: i = (i+1)(9, 6, 9) */ + reached[2][6] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i); + ((P2 *)this)->i = (((int)((P2 *)this)->i)+1); +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 10, 9) */ + reached[2][10] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 27: /* STATE 7 - line 211 "pan.___" - [((i>=(8/2)))] (11:0:1 - 1) */ + IfNotBlocked + reached[2][7] = 1; + if (!((((int)((P2 *)this)->i)>=(8/2)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P2 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P2 *)this)->i = 0; + /* merge: goto :b3(0, 8, 11) */ + reached[2][8] = 1; + ; + _m = 3; goto P999; /* 1 */ +/* STATE 13 - line 219 "pan.___" - [i = 0] (0:0 - 1) same as 25 (0:0 - 1) */ + case 28: /* STATE 14 - line 221 "pan.___" - [((i<(8/2)))] (19:0:2 - 1) */ + IfNotBlocked + reached[2][14] = 1; + if (!((((int)((P2 *)this)->i)<(8/2)))) + continue; + /* merge: buffer_use[((read_off+i)%8)] = 0(19, 15, 19) */ + reached[2][15] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%8), 8) ]); + now.buffer_use[ Index(((now.read_off+((P2 *)this)->i)%8), 8) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[((read_off+reader:i)%8)]", ((int)now.buffer_use[ Index(((((int)now.read_off)+((int)((P2 *)this)->i))%8), 8) ])); +#endif + ; + /* merge: i = (i+1)(19, 16, 19) */ + reached[2][16] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P2 *)this)->i); + ((P2 *)this)->i = (((int)((P2 *)this)->i)+1); +#ifdef VAR_RANGES + logval("reader:i", ((int)((P2 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 20, 19) */ + reached[2][20] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 29: /* STATE 17 - line 224 "pan.___" - [((i>=(8/2)))] (0:0:1 - 1) */ + IfNotBlocked + reached[2][17] = 1; + if (!((((int)((P2 *)this)->i)>=(8/2)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P2 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P2 *)this)->i = 0; + _m = 3; goto P999; /* 0 */ + case 30: /* STATE 22 - line 226 "pan.___" - [read_off = (read_off+(8/2))] (0:0:1 - 1) */ + IfNotBlocked + reached[2][22] = 1; + (trpt+1)->bup.oval = ((int)now.read_off); + now.read_off = (((int)now.read_off)+(8/2)); +#ifdef VAR_RANGES + logval("read_off", ((int)now.read_off)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 31: /* STATE 24 - line 228 "pan.___" - [((read_off>=(4-events_lost)))] (0:0:0 - 1) */ + IfNotBlocked + reached[2][24] = 1; + if (!((((int)now.read_off)>=(4-((int)now.events_lost))))) + continue; + _m = 3; goto P999; /* 0 */ + case 32: /* STATE 29 - line 230 "pan.___" - [-end-] (0:0:0 - 3) */ + IfNotBlocked + reached[2][29] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC tracer */ + case 33: /* STATE 1 - line 126 "pan.___" - [prev_off = write_off] (0:10:2 - 1) */ + IfNotBlocked + reached[1][1] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->prev_off); + ((P1 *)this)->prev_off = ((int)now.write_off); +#ifdef VAR_RANGES + logval("tracer:prev_off", ((int)((P1 *)this)->prev_off)); +#endif + ; + /* merge: new_off = (prev_off+size)(10, 2, 10) */ + reached[1][2] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->new_off); + ((P1 *)this)->new_off = (((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:new_off", ((int)((P1 *)this)->new_off)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 34: /* STATE 4 - line 131 "pan.___" - [((((new_off-read_off)>8)&&((new_off-read_off)<(255/2))))] (0:0:1 - 1) */ + IfNotBlocked + reached[1][4] = 1; + if (!((((((int)((P1 *)this)->new_off)-((int)now.read_off))>8)&&((((int)((P1 *)this)->new_off)-((int)now.read_off))<(255/2))))) + continue; + /* dead 1: new_off */ (trpt+1)->bup.oval = ((P1 *)this)->new_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->new_off = 0; + _m = 3; goto P999; /* 0 */ + case 35: /* STATE 7 - line 133 "pan.___" - [(1)] (27:0:0 - 1) */ + IfNotBlocked + reached[1][7] = 1; + if (!(1)) + continue; + /* merge: .(goto)(0, 9, 27) */ + reached[1][9] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 36: /* STATE 11 - line 138 "pan.___" - [((prev_off!=write_off))] (3:0:1 - 1) */ + IfNotBlocked + reached[1][11] = 1; + if (!((((int)((P1 *)this)->prev_off)!=((int)now.write_off)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P1 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->prev_off = 0; + /* merge: goto cmpxchg_loop(0, 12, 3) */ + reached[1][12] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 37: /* STATE 14 - line 139 "pan.___" - [write_off = new_off] (0:24:2 - 1) */ + IfNotBlocked + reached[1][14] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.write_off); + now.write_off = ((int)((P1 *)this)->new_off); +#ifdef VAR_RANGES + logval("write_off", ((int)now.write_off)); +#endif + ; + /* merge: .(goto)(24, 16, 24) */ + reached[1][16] = 1; + ; + /* merge: i = 0(24, 17, 24) */ + reached[1][17] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 38: /* STATE 17 - line 141 "pan.___" - [i = 0] (0:24:1 - 2) */ + IfNotBlocked + reached[1][17] = 1; + (trpt+1)->bup.oval = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 39: /* STATE 18 - line 143 "pan.___" - [((ii)<((int)((P1 *)this)->size)))) + continue; + /* merge: assert((buffer_use[((prev_off+i)%8)]==0))(24, 19, 24) */ + reached[1][19] = 1; + assert((((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%8), 8) ])==0), "(buffer_use[((prev_off+i)%8)]==0)", II, tt, t); + /* merge: buffer_use[((prev_off+i)%8)] = 1(24, 20, 24) */ + reached[1][20] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%8), 8) ]); + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%8), 8) ] = 1; +#ifdef VAR_RANGES + logval("buffer_use[((tracer:prev_off+tracer:i)%8)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%8), 8) ])); +#endif + ; + /* merge: i = (i+1)(24, 21, 24) */ + reached[1][21] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = (((int)((P1 *)this)->i)+1); +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 25, 24) */ + reached[1][25] = 1; + ; + _m = 3; goto P999; /* 4 */ + case 40: /* STATE 22 - line 147 "pan.___" - [((i>=size))] (26:0:1 - 1) */ + IfNotBlocked + reached[1][22] = 1; + if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size)))) + continue; + /* dead 1: i */ (trpt+1)->bup.oval = ((P1 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->i = 0; + /* merge: goto :b0(0, 23, 26) */ + reached[1][23] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 41: /* STATE 28 - line 155 "pan.___" - [i = 0] (0:0:1 - 1) */ + IfNotBlocked + reached[1][28] = 1; + (trpt+1)->bup.oval = ((int)((P1 *)this)->i); + ((P1 *)this)->i = 0; +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 42: /* STATE 29 - line 157 "pan.___" - [((ii)<((int)((P1 *)this)->size)))) + continue; + /* merge: buffer_use[((prev_off+i)%8)] = 0(34, 30, 34) */ + reached[1][30] = 1; + (trpt+1)->bup.ovals = grab_ints(2); + (trpt+1)->bup.ovals[0] = ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%8), 8) ]); + now.buffer_use[ Index(((((P1 *)this)->prev_off+((P1 *)this)->i)%8), 8) ] = 0; +#ifdef VAR_RANGES + logval("buffer_use[((tracer:prev_off+tracer:i)%8)]", ((int)now.buffer_use[ Index(((((int)((P1 *)this)->prev_off)+((int)((P1 *)this)->i))%8), 8) ])); +#endif + ; + /* merge: i = (i+1)(34, 31, 34) */ + reached[1][31] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->i); + ((P1 *)this)->i = (((int)((P1 *)this)->i)+1); +#ifdef VAR_RANGES + logval("tracer:i", ((int)((P1 *)this)->i)); +#endif + ; + /* merge: .(goto)(0, 35, 34) */ + reached[1][35] = 1; + ; + _m = 3; goto P999; /* 3 */ + case 43: /* STATE 32 - line 160 "pan.___" - [((i>=size))] (44:0:4 - 1) */ + IfNotBlocked + reached[1][32] = 1; + if (!((((int)((P1 *)this)->i)>=((int)((P1 *)this)->size)))) + continue; + /* dead 1: i */ (trpt+1)->bup.ovals = grab_ints(4); + (trpt+1)->bup.ovals[0] = ((P1 *)this)->i; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->i = 0; + /* merge: goto :b1(44, 33, 44) */ + reached[1][33] = 1; + ; + /* merge: tmp_commit = (commit_count[((prev_off%8)/(8/2))]+size)(44, 37, 44) */ + reached[1][37] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P1 *)this)->tmp_commit); + ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ])+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%8)/(8/2))])+tmp_commit)(44, 38, 44) */ + reached[1][38] = 1; + (trpt+1)->bup.ovals[2] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ]))+((int)((P1 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%8)/(8/2))] = tmp_commit(44, 39, 44) */ + reached[1][39] = 1; + (trpt+1)->bup.ovals[3] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ]); + now.commit_count[ Index(((((P1 *)this)->prev_off%8)/(8/2)), 2) ] = ((int)((P1 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((tracer:prev_off%8)/(8/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 4 */ + case 44: /* STATE 37 - line 162 "pan.___" - [tmp_commit = (commit_count[((prev_off%8)/(8/2))]+size)] (0:44:3 - 3) */ + IfNotBlocked + reached[1][37] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P1 *)this)->tmp_commit); + ((P1 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ])+((int)((P1 *)this)->size)); +#ifdef VAR_RANGES + logval("tracer:tmp_commit", ((int)((P1 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%8)/(8/2))])+tmp_commit)(44, 38, 44) */ + reached[1][38] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ]))+((int)((P1 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%8)/(8/2))] = tmp_commit(44, 39, 44) */ + reached[1][39] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ]); + now.commit_count[ Index(((((P1 *)this)->prev_off%8)/(8/2)), 2) ] = ((int)((P1 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((tracer:prev_off%8)/(8/2))]", ((int)now.commit_count[ Index(((((int)((P1 *)this)->prev_off)%8)/(8/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 45: /* STATE 40 - line 170 "pan.___" - [((((((prev_off/8)*8)/2)+(8/2))-tmp_commit))] (50:0:3 - 1) */ + IfNotBlocked + reached[1][40] = 1; + if (!((((((((int)((P1 *)this)->prev_off)/8)*8)/2)+(8/2))-((int)((P1 *)this)->tmp_commit)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P1 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->prev_off = 0; + /* dead 1: tmp_commit */ (trpt+1)->bup.ovals[1] = ((P1 *)this)->tmp_commit; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P1 *)this)->tmp_commit = 0; + /* merge: deliver = 1(50, 41, 50) */ + reached[1][41] = 1; + (trpt+1)->bup.ovals[2] = ((int)deliver); + deliver = 1; +#ifdef VAR_RANGES + logval("deliver", ((int)deliver)); +#endif + ; + /* merge: .(goto)(50, 45, 50) */ + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 46: /* STATE 45 - line 175 "pan.___" - [.(goto)] (0:50:0 - 2) */ + IfNotBlocked + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 0 */ + case 47: /* STATE 43 - line 173 "pan.___" - [(1)] (50:0:0 - 1) */ + IfNotBlocked + reached[1][43] = 1; + if (!(1)) + continue; + /* merge: .(goto)(50, 45, 50) */ + reached[1][45] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 48: /* STATE 48 - line 179 "pan.___" - [events_lost = (events_lost+1)] (0:0:1 - 2) */ + IfNotBlocked + reached[1][48] = 1; + (trpt+1)->bup.oval = ((int)now.events_lost); + now.events_lost = (((int)now.events_lost)+1); +#ifdef VAR_RANGES + logval("events_lost", ((int)now.events_lost)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 49: /* STATE 49 - line 181 "pan.___" - [refcount = (refcount-1)] (0:0:1 - 2) */ + IfNotBlocked + reached[1][49] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 0 */ + case 50: /* STATE 51 - line 183 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[1][51] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + + /* PROC switcher */ + case 51: /* STATE 1 - line 75 "pan.___" - [prev_off = write_off] (0:9:3 - 1) */ + IfNotBlocked + reached[0][1] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->prev_off); + ((P0 *)this)->prev_off = ((int)now.write_off); +#ifdef VAR_RANGES + logval("switcher:prev_off", ((int)((P0 *)this)->prev_off)); +#endif + ; + /* merge: size = ((8/2)-(prev_off%(8/2)))(9, 2, 9) */ + reached[0][2] = 1; + (trpt+1)->bup.ovals[1] = ((int)((P0 *)this)->size); + ((P0 *)this)->size = ((8/2)-(((int)((P0 *)this)->prev_off)%(8/2))); +#ifdef VAR_RANGES + logval("switcher:size", ((int)((P0 *)this)->size)); +#endif + ; + /* merge: new_off = (prev_off+size)(9, 3, 9) */ + reached[0][3] = 1; + (trpt+1)->bup.ovals[2] = ((int)((P0 *)this)->new_off); + ((P0 *)this)->new_off = (((int)((P0 *)this)->prev_off)+((int)((P0 *)this)->size)); +#ifdef VAR_RANGES + logval("switcher:new_off", ((int)((P0 *)this)->new_off)); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 52: /* STATE 4 - line 80 "pan.___" - [(((((new_off-read_off)>8)&&((new_off-read_off)<(255/2)))||(size==(8/2))))] (30:0:3 - 1) */ + IfNotBlocked + reached[0][4] = 1; + if (!(((((((int)((P0 *)this)->new_off)-((int)now.read_off))>8)&&((((int)((P0 *)this)->new_off)-((int)now.read_off))<(255/2)))||(((int)((P0 *)this)->size)==(8/2))))) + continue; + /* dead 1: new_off */ (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((P0 *)this)->new_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->new_off = 0; + /* dead 1: size */ (trpt+1)->bup.ovals[1] = ((P0 *)this)->size; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->size = 0; + /* merge: refcount = (refcount-1)(30, 5, 30) */ + reached[0][5] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + /* merge: goto not_needed(30, 6, 30) */ + reached[0][6] = 1; + ; + _m = 3; goto P999; /* 2 */ + case 53: /* STATE 8 - line 83 "pan.___" - [(1)] (18:0:0 - 1) */ + IfNotBlocked + reached[0][8] = 1; + if (!(1)) + continue; + /* merge: .(goto)(0, 10, 18) */ + reached[0][10] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 54: /* STATE 12 - line 88 "pan.___" - [((prev_off!=write_off))] (11:0:1 - 1) */ + IfNotBlocked + reached[0][12] = 1; + if (!((((int)((P0 *)this)->prev_off)!=((int)now.write_off)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.oval = ((P0 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->prev_off = 0; + /* merge: goto cmpxchg_loop(0, 13, 11) */ + reached[0][13] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 55: /* STATE 17 - line 91 "pan.___" - [.(goto)] (0:29:0 - 1) */ + IfNotBlocked + reached[0][17] = 1; + ; + _m = 3; goto P999; /* 0 */ + case 56: /* STATE 15 - line 89 "pan.___" - [write_off = new_off] (0:29:1 - 1) */ + IfNotBlocked + reached[0][15] = 1; + (trpt+1)->bup.oval = ((int)now.write_off); + now.write_off = ((int)((P0 *)this)->new_off); +#ifdef VAR_RANGES + logval("write_off", ((int)now.write_off)); +#endif + ; + /* merge: .(goto)(29, 17, 29) */ + reached[0][17] = 1; + ; + _m = 3; goto P999; /* 1 */ + case 57: /* STATE 19 - line 94 "pan.___" - [tmp_commit = (commit_count[((prev_off%8)/(8/2))]+size)] (0:26:3 - 1) */ + IfNotBlocked + reached[0][19] = 1; + (trpt+1)->bup.ovals = grab_ints(3); + (trpt+1)->bup.ovals[0] = ((int)((P0 *)this)->tmp_commit); + ((P0 *)this)->tmp_commit = (((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%8)/(8/2)), 2) ])+((int)((P0 *)this)->size)); +#ifdef VAR_RANGES + logval("switcher:tmp_commit", ((int)((P0 *)this)->tmp_commit)); +#endif + ; + /* merge: _commit_sum = ((_commit_sum-commit_count[((prev_off%8)/(8/2))])+tmp_commit)(26, 20, 26) */ + reached[0][20] = 1; + (trpt+1)->bup.ovals[1] = ((int)now._commit_sum); + now._commit_sum = ((((int)now._commit_sum)-((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%8)/(8/2)), 2) ]))+((int)((P0 *)this)->tmp_commit)); +#ifdef VAR_RANGES + logval("_commit_sum", ((int)now._commit_sum)); +#endif + ; + /* merge: commit_count[((prev_off%8)/(8/2))] = tmp_commit(26, 21, 26) */ + reached[0][21] = 1; + (trpt+1)->bup.ovals[2] = ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%8)/(8/2)), 2) ]); + now.commit_count[ Index(((((P0 *)this)->prev_off%8)/(8/2)), 2) ] = ((int)((P0 *)this)->tmp_commit); +#ifdef VAR_RANGES + logval("commit_count[((switcher:prev_off%8)/(8/2))]", ((int)now.commit_count[ Index(((((int)((P0 *)this)->prev_off)%8)/(8/2)), 2) ])); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 58: /* STATE 22 - line 102 "pan.___" - [((((((prev_off/8)*8)/2)+(8/2))-tmp_commit))] (30:0:4 - 1) */ + IfNotBlocked + reached[0][22] = 1; + if (!((((((((int)((P0 *)this)->prev_off)/8)*8)/2)+(8/2))-((int)((P0 *)this)->tmp_commit)))) + continue; + /* dead 1: prev_off */ (trpt+1)->bup.ovals = grab_ints(4); + (trpt+1)->bup.ovals[0] = ((P0 *)this)->prev_off; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->prev_off = 0; + /* dead 1: tmp_commit */ (trpt+1)->bup.ovals[1] = ((P0 *)this)->tmp_commit; +#ifdef HAS_CODE + if (!readtrail) +#endif + ((P0 *)this)->tmp_commit = 0; + /* merge: deliver = 1(30, 23, 30) */ + reached[0][23] = 1; + (trpt+1)->bup.ovals[2] = ((int)deliver); + deliver = 1; +#ifdef VAR_RANGES + logval("deliver", ((int)deliver)); +#endif + ; + /* merge: .(goto)(30, 27, 30) */ + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.ovals[3] = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 3 */ + case 59: /* STATE 27 - line 107 "pan.___" - [.(goto)] (0:30:1 - 2) */ + IfNotBlocked + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 1 */ + case 60: /* STATE 25 - line 105 "pan.___" - [(1)] (30:0:1 - 1) */ + IfNotBlocked + reached[0][25] = 1; + if (!(1)) + continue; + /* merge: .(goto)(30, 27, 30) */ + reached[0][27] = 1; + ; + /* merge: refcount = (refcount-1)(30, 28, 30) */ + reached[0][28] = 1; + (trpt+1)->bup.oval = ((int)now.refcount); + now.refcount = (((int)now.refcount)-1); +#ifdef VAR_RANGES + logval("refcount", ((int)now.refcount)); +#endif + ; + _m = 3; goto P999; /* 2 */ + case 61: /* STATE 31 - line 111 "pan.___" - [-end-] (0:0:0 - 1) */ + IfNotBlocked + reached[0][31] = 1; + if (!delproc(1, II)) continue; + _m = 3; goto P999; /* 0 */ + case _T5: /* np_ */ + if (!((!(trpt->o_pm&4) && !(trpt->tau&128)))) + continue; + /* else fall through */ + case _T2: /* true */ + _m = 3; goto P999; +#undef rand + } + diff --git a/trunk/verif/nico-md-merge/pan.spin b/trunk/verif/nico-md-merge/pan.spin new file mode 100644 index 00000000..1567500c --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.spin @@ -0,0 +1,292 @@ +#define BUFSIZE 8 +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#ifndef BUFSIZE +#define BUFSIZE 4 +#endif +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* */ +byte _commit_sum; +/* */ + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +//#ifdef RACE_TEST +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; +//#endif + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + /* */ + _commit_sum = _commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + /* */ + _commit_sum = 0; + /* */ + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } +} + diff --git a/trunk/verif/nico-md-merge/pan.t b/trunk/verif/nico-md-merge/pan.t new file mode 100644 index 00000000..5c49e93e --- /dev/null +++ b/trunk/verif/nico-md-merge/pan.t @@ -0,0 +1,916 @@ +#ifdef PEG +struct T_SRC { + char *fl; int ln; +} T_SRC[NTRANS]; + +void +tr_2_src(int m, char *file, int ln) +{ T_SRC[m].fl = file; + T_SRC[m].ln = ln; +} + +void +putpeg(int n, int m) +{ printf("%5d trans %4d ", m, n); + printf("file %s line %3d\n", + T_SRC[n].fl, T_SRC[n].ln); +} +#endif + +void +settable(void) +{ Trans *T; + Trans *settr(int, int, int, int, int, char *, int, int, int); + + trans = (Trans ***) emalloc(7*sizeof(Trans **)); + + /* proctype 5: :never: */ + + trans[5] = (Trans **) emalloc(9*sizeof(Trans *)); + + T = trans[5][5] = settr(167,0,0,0,0,"IF", 0, 2, 0); + T = T->nxt = settr(167,0,1,0,0,"IF", 0, 2, 0); + T->nxt = settr(167,0,3,0,0,"IF", 0, 2, 0); + trans[5][1] = settr(163,0,7,3,0,"((((4+1)<=8)&&(events_lost!=0)))", 1, 2, 0); + trans[5][2] = settr(164,0,7,1,0,"goto accept_all", 0, 2, 0); + trans[5][6] = settr(168,0,7,1,0,".(goto)", 0, 2, 0); + trans[5][3] = settr(165,0,5,1,0,"(1)", 0, 2, 0); + trans[5][4] = settr(166,0,5,1,0,"goto T0_init", 0, 2, 0); + trans[5][7] = settr(169,0,8,1,0,"(1)", 0, 2, 0); + trans[5][8] = settr(170,0,0,4,4,"-end-", 0, 3500, 0); + + /* proctype 4: :init: */ + + trans[4] = (Trans **) emalloc(44*sizeof(Trans *)); + + T = trans[ 4][42] = settr(161,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(161,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[4][1] = settr(120,2,7,5,5,"i = 0", 1, 2, 0); + trans[4][8] = settr(127,2,7,1,0,".(goto)", 1, 2, 0); + T = trans[4][7] = settr(126,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(126,2,2,0,0,"DO", 1, 2, 0); + T->nxt = settr(126,2,5,0,0,"DO", 1, 2, 0); + trans[4][2] = settr(121,2,7,6,6,"((i<2))", 1, 2, 0); /* m: 3 -> 7,0 */ + reached4[3] = 1; + trans[4][3] = settr(0,0,0,0,0,"commit_count[i] = 0",0,0,0); + trans[4][4] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][5] = settr(124,2,17,7,7,"((i>=2))", 1, 2, 0); /* m: 10 -> 17,0 */ + reached4[10] = 1; + trans[4][6] = settr(125,2,10,1,0,"goto :b6", 1, 2, 0); /* m: 10 -> 0,17 */ + reached4[10] = 1; + trans[4][9] = settr(128,2,10,1,0,"break", 1, 2, 0); + trans[4][10] = settr(129,2,17,8,8,"_commit_sum = 0", 1, 2, 0); /* m: 11 -> 0,17 */ + reached4[11] = 1; + trans[4][11] = settr(0,0,0,0,0,"i = 0",0,0,0); + trans[4][18] = settr(137,2,17,1,0,".(goto)", 1, 2, 0); + T = trans[4][17] = settr(136,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(136,2,12,0,0,"DO", 1, 2, 0); + T->nxt = settr(136,2,15,0,0,"DO", 1, 2, 0); + trans[4][12] = settr(131,2,17,9,9,"((i<8))", 1, 2, 0); /* m: 13 -> 17,0 */ + reached4[13] = 1; + trans[4][13] = settr(0,0,0,0,0,"buffer_use[i] = 0",0,0,0); + trans[4][14] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][15] = settr(134,2,20,10,10,"((i>=8))", 1, 2, 0); + trans[4][16] = settr(135,2,20,1,0,"goto :b7", 1, 2, 0); + trans[4][19] = settr(138,2,20,1,0,"break", 1, 2, 0); + trans[4][20] = settr(139,2,21,11,11,"(run reader())", 1, 2, 0); + trans[4][21] = settr(140,2,29,12,12,"(run cleaner())", 1, 2, 0); /* m: 22 -> 29,0 */ + reached4[22] = 1; + trans[4][22] = settr(0,0,0,0,0,"i = 0",0,0,0); + trans[4][30] = settr(149,2,29,1,0,".(goto)", 1, 2, 0); + T = trans[4][29] = settr(148,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(148,2,23,0,0,"DO", 1, 2, 0); + T->nxt = settr(148,2,27,0,0,"DO", 1, 2, 0); + trans[4][23] = settr(142,2,25,13,13,"((i<4))", 1, 2, 0); /* m: 24 -> 25,0 */ + reached4[24] = 1; + trans[4][24] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[4][25] = settr(144,2,29,14,14,"(run tracer())", 1, 2, 0); /* m: 26 -> 29,0 */ + reached4[26] = 1; + trans[4][26] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][27] = settr(146,2,39,15,15,"((i>=4))", 1, 2, 0); /* m: 32 -> 39,0 */ + reached4[32] = 1; + trans[4][28] = settr(147,2,32,1,0,"goto :b8", 1, 2, 0); /* m: 32 -> 0,39 */ + reached4[32] = 1; + trans[4][31] = settr(150,2,32,1,0,"break", 1, 2, 0); + trans[4][32] = settr(151,2,39,16,16,"i = 0", 1, 2, 0); + trans[4][40] = settr(159,2,39,1,0,".(goto)", 1, 2, 0); + T = trans[4][39] = settr(158,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(158,2,33,0,0,"DO", 1, 2, 0); + T->nxt = settr(158,2,37,0,0,"DO", 1, 2, 0); + trans[4][33] = settr(152,2,35,17,17,"((i<1))", 1, 2, 0); /* m: 34 -> 35,0 */ + reached4[34] = 1; + trans[4][34] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[4][35] = settr(154,2,39,18,18,"(run switcher())", 1, 2, 0); /* m: 36 -> 39,0 */ + reached4[36] = 1; + trans[4][36] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[4][37] = settr(156,2,41,19,19,"((i>=1))", 1, 2, 0); /* m: 38 -> 41,0 */ + reached4[38] = 1; + trans[4][38] = settr(157,2,41,1,0,"goto :b9", 1, 2, 0); + trans[4][41] = settr(160,0,43,1,0,"break", 1, 2, 0); + trans[4][43] = settr(162,0,0,20,20,"-end-", 0, 3500, 0); + + /* proctype 3: cleaner */ + + trans[3] = (Trans **) emalloc(10*sizeof(Trans *)); + + T = trans[ 3][8] = settr(118,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(118,2,5,0,0,"ATOMIC", 1, 2, 0); + trans[3][6] = settr(116,2,5,1,0,".(goto)", 1, 2, 0); + T = trans[3][5] = settr(115,2,0,0,0,"DO", 1, 2, 0); + T->nxt = settr(115,2,1,0,0,"DO", 1, 2, 0); + trans[3][1] = settr(111,2,3,21,21,"((refcount==0))", 1, 2, 0); /* m: 2 -> 3,0 */ + reached3[2] = 1; + trans[3][2] = settr(0,0,0,0,0,"refcount = (refcount+1)",0,0,0); + trans[3][3] = settr(113,2,7,22,22,"(run switcher())", 1, 2, 0); /* m: 4 -> 7,0 */ + reached3[4] = 1; + trans[3][4] = settr(114,2,7,1,0,"goto :b5", 1, 2, 0); + trans[3][7] = settr(117,0,9,1,0,"break", 1, 2, 0); + trans[3][9] = settr(119,0,0,23,23,"-end-", 0, 3500, 0); + + /* proctype 2: reader */ + + trans[2] = (Trans **) emalloc(30*sizeof(Trans *)); + + trans[2][27] = settr(108,0,26,1,0,".(goto)", 0, 2, 0); + T = trans[2][26] = settr(107,0,0,0,0,"DO", 0, 2, 0); + T = T->nxt = settr(107,0,1,0,0,"DO", 0, 2, 0); + T->nxt = settr(107,0,24,0,0,"DO", 0, 2, 0); + trans[2][1] = settr(82,0,12,24,0,"((((((write_off/(8/2))-(read_off/(8/2)))>0)&&(((write_off/(8/2))-(read_off/(8/2)))<(255/2)))&&(((commit_count[((read_off%8)/(8/2))]-(8/2))-(((read_off/8)*8)/2))==0)))", 1, 2, 0); + T = trans[ 2][12] = settr(93,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(93,2,2,0,0,"ATOMIC", 1, 2, 0); + trans[2][2] = settr(83,2,9,25,25,"i = 0", 1, 2, 0); + trans[2][10] = settr(91,2,9,1,0,".(goto)", 1, 2, 0); + T = trans[2][9] = settr(90,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(90,2,3,0,0,"DO", 1, 2, 0); + T->nxt = settr(90,2,7,0,0,"DO", 1, 2, 0); + trans[2][3] = settr(84,2,9,26,26,"((i<(8/2)))", 1, 2, 0); /* m: 4 -> 9,0 */ + reached2[4] = 1; + trans[2][4] = settr(0,0,0,0,0,"assert((buffer_use[((read_off+i)%8)]==0))",0,0,0); + trans[2][5] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%8)] = 1",0,0,0); + trans[2][6] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[2][7] = settr(88,2,11,27,27,"((i>=(8/2)))", 1, 2, 0); /* m: 8 -> 11,0 */ + reached2[8] = 1; + trans[2][8] = settr(89,2,11,1,0,"goto :b3", 1, 2, 0); + trans[2][11] = settr(92,0,23,1,0,"break", 1, 2, 0); + T = trans[ 2][23] = settr(104,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(104,2,13,0,0,"ATOMIC", 1, 2, 0); + trans[2][13] = /* c */ settr(94,2,19,25,25,"i = 0", 1, 2, 0); + trans[2][20] = settr(101,2,19,1,0,".(goto)", 1, 2, 0); + T = trans[2][19] = settr(100,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(100,2,14,0,0,"DO", 1, 2, 0); + T->nxt = settr(100,2,17,0,0,"DO", 1, 2, 0); + trans[2][14] = settr(95,2,19,28,28,"((i<(8/2)))", 1, 2, 0); /* m: 15 -> 19,0 */ + reached2[15] = 1; + trans[2][15] = settr(0,0,0,0,0,"buffer_use[((read_off+i)%8)] = 0",0,0,0); + trans[2][16] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[2][17] = settr(98,2,21,29,29,"((i>=(8/2)))", 1, 2, 0); + trans[2][18] = settr(99,2,21,1,0,"goto :b4", 1, 2, 0); + trans[2][21] = settr(102,2,22,1,0,"break", 1, 2, 0); + trans[2][22] = settr(103,0,26,30,30,"read_off = (read_off+(8/2))", 1, 2, 0); + trans[2][24] = settr(105,0,29,31,0,"((read_off>=(4-events_lost)))", 1, 2, 0); + trans[2][25] = settr(106,0,29,1,0,"goto :b2", 0, 2, 0); + trans[2][28] = settr(109,0,29,1,0,"break", 0, 2, 0); + trans[2][29] = settr(110,0,0,32,32,"-end-", 0, 3500, 0); + + /* proctype 1: tracer */ + + trans[1] = (Trans **) emalloc(52*sizeof(Trans *)); + + T = trans[ 1][3] = settr(33,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(33,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[1][1] = settr(31,4,10,33,33,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,10 */ + reached1[2] = 1; + trans[1][2] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0); + T = trans[ 1][10] = settr(40,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(40,2,8,0,0,"ATOMIC", 1, 2, 0); + T = trans[1][8] = settr(38,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(38,2,4,0,0,"IF", 1, 2, 0); + T->nxt = settr(38,2,6,0,0,"IF", 1, 2, 0); + trans[1][4] = settr(34,2,48,34,34,"((((new_off-read_off)>8)&&((new_off-read_off)<(255/2))))", 1, 2, 0); + trans[1][5] = settr(35,2,48,1,0,"goto lost", 1, 2, 0); + trans[1][9] = settr(39,0,27,1,0,".(goto)", 1, 2, 0); + trans[1][6] = settr(36,2,7,2,0,"else", 1, 2, 0); + trans[1][7] = settr(37,4,27,35,35,"(1)", 1, 2, 0); /* m: 9 -> 27,0 */ + reached1[9] = 1; + T = trans[ 1][27] = settr(57,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(57,2,15,0,0,"ATOMIC", 1, 2, 0); + T = trans[1][15] = settr(45,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(45,2,11,0,0,"IF", 1, 2, 0); + T->nxt = settr(45,2,13,0,0,"IF", 1, 2, 0); + trans[1][11] = settr(41,4,3,36,36,"((prev_off!=write_off))", 1, 2, 0); /* m: 12 -> 3,0 */ + reached1[12] = 1; + trans[1][12] = settr(42,0,3,1,0,"goto cmpxchg_loop", 1, 2, 0); + trans[1][16] = settr(46,2,17,1,0,".(goto)", 1, 2, 0); /* m: 17 -> 0,24 */ + reached1[17] = 1; + trans[1][13] = settr(43,2,14,2,0,"else", 1, 2, 0); + trans[1][14] = settr(44,2,24,37,37,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,24 */ + reached1[17] = 1; + trans[1][17] = settr(47,2,24,38,38,"i = 0", 1, 2, 0); + trans[1][25] = settr(55,2,24,1,0,".(goto)", 1, 2, 0); + T = trans[1][24] = settr(54,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(54,2,18,0,0,"DO", 1, 2, 0); + T->nxt = settr(54,2,22,0,0,"DO", 1, 2, 0); + trans[1][18] = settr(48,2,24,39,39,"((i 24,0 */ + reached1[19] = 1; + trans[1][19] = settr(0,0,0,0,0,"assert((buffer_use[((prev_off+i)%8)]==0))",0,0,0); + trans[1][20] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%8)] = 1",0,0,0); + trans[1][21] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[1][22] = settr(52,2,26,40,40,"((i>=size))", 1, 2, 0); /* m: 23 -> 26,0 */ + reached1[23] = 1; + trans[1][23] = settr(53,2,26,1,0,"goto :b0", 1, 2, 0); + trans[1][26] = settr(56,0,46,1,0,"break", 1, 2, 0); + T = trans[ 1][46] = settr(76,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(76,2,28,0,0,"ATOMIC", 1, 2, 0); + trans[1][28] = settr(58,2,34,41,41,"i = 0", 1, 2, 0); + trans[1][35] = settr(65,2,34,1,0,".(goto)", 1, 2, 0); + T = trans[1][34] = settr(64,2,0,0,0,"DO", 1, 2, 0); + T = T->nxt = settr(64,2,29,0,0,"DO", 1, 2, 0); + T->nxt = settr(64,2,32,0,0,"DO", 1, 2, 0); + trans[1][29] = settr(59,2,34,42,42,"((i 34,0 */ + reached1[30] = 1; + trans[1][30] = settr(0,0,0,0,0,"buffer_use[((prev_off+i)%8)] = 0",0,0,0); + trans[1][31] = settr(0,0,0,0,0,"i = (i+1)",0,0,0); + trans[1][32] = settr(62,2,44,43,43,"((i>=size))", 1, 2, 0); /* m: 37 -> 44,0 */ + reached1[37] = 1; + trans[1][33] = settr(63,2,37,1,0,"goto :b1", 1, 2, 0); /* m: 37 -> 0,44 */ + reached1[37] = 1; + trans[1][36] = settr(66,2,37,1,0,"break", 1, 2, 0); + trans[1][37] = settr(67,2,44,44,44,"tmp_commit = (commit_count[((prev_off%8)/(8/2))]+size)", 1, 2, 0); /* m: 38 -> 0,44 */ + reached1[38] = 1; + trans[1][38] = settr(0,0,0,0,0,"_commit_sum = ((_commit_sum-commit_count[((prev_off%8)/(8/2))])+tmp_commit)",0,0,0); + trans[1][39] = settr(0,0,0,0,0,"commit_count[((prev_off%8)/(8/2))] = tmp_commit",0,0,0); + T = trans[1][44] = settr(74,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(74,2,40,0,0,"IF", 1, 2, 0); + T->nxt = settr(74,2,42,0,0,"IF", 1, 2, 0); + trans[1][40] = settr(70,4,50,45,45,"((((((prev_off/8)*8)/2)+(8/2))-tmp_commit))", 1, 2, 0); /* m: 41 -> 50,0 */ + reached1[41] = 1; + trans[1][41] = settr(0,0,0,0,0,"deliver = 1",0,0,0); + trans[1][45] = settr(75,0,50,46,46,".(goto)", 1, 2, 0); + trans[1][42] = settr(72,2,43,2,0,"else", 1, 2, 0); + trans[1][43] = settr(73,4,50,47,47,"(1)", 1, 2, 0); /* m: 45 -> 50,0 */ + reached1[45] = 1; + T = trans[ 1][50] = settr(80,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(80,2,47,0,0,"ATOMIC", 1, 2, 0); + trans[1][47] = settr(77,2,49,1,0,"goto end", 1, 2, 0); + trans[1][48] = settr(78,2,49,48,48,"events_lost = (events_lost+1)", 1, 2, 0); + trans[1][49] = settr(79,0,51,49,49,"refcount = (refcount-1)", 1, 2, 0); + trans[1][51] = settr(81,0,0,50,50,"-end-", 0, 3500, 0); + + /* proctype 0: switcher */ + + trans[0] = (Trans **) emalloc(32*sizeof(Trans *)); + + T = trans[ 0][11] = settr(10,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(10,2,1,0,0,"ATOMIC", 1, 2, 0); + trans[0][1] = settr(0,2,9,51,51,"prev_off = write_off", 1, 2, 0); /* m: 2 -> 0,9 */ + reached0[2] = 1; + trans[0][2] = settr(0,0,0,0,0,"size = ((8/2)-(prev_off%(8/2)))",0,0,0); + trans[0][3] = settr(0,0,0,0,0,"new_off = (prev_off+size)",0,0,0); + T = trans[0][9] = settr(8,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(8,2,4,0,0,"IF", 1, 2, 0); + T->nxt = settr(8,2,7,0,0,"IF", 1, 2, 0); + trans[0][4] = settr(3,4,30,52,52,"(((((new_off-read_off)>8)&&((new_off-read_off)<(255/2)))||(size==(8/2))))", 1, 2, 0); /* m: 5 -> 30,0 */ + reached0[5] = 1; + trans[0][5] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0); + trans[0][6] = settr(5,0,30,1,0,"goto not_needed", 1, 2, 0); + trans[0][10] = settr(9,0,18,1,0,".(goto)", 1, 2, 0); + trans[0][7] = settr(6,2,8,2,0,"else", 1, 2, 0); + trans[0][8] = settr(7,4,18,53,53,"(1)", 1, 2, 0); /* m: 10 -> 18,0 */ + reached0[10] = 1; + T = trans[ 0][18] = settr(17,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(17,2,16,0,0,"ATOMIC", 1, 2, 0); + T = trans[0][16] = settr(15,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(15,2,12,0,0,"IF", 1, 2, 0); + T->nxt = settr(15,2,14,0,0,"IF", 1, 2, 0); + trans[0][12] = settr(11,4,11,54,54,"((prev_off!=write_off))", 1, 2, 0); /* m: 13 -> 11,0 */ + reached0[13] = 1; + trans[0][13] = settr(12,0,11,1,0,"goto cmpxchg_loop", 1, 2, 0); + trans[0][17] = settr(16,0,29,55,55,".(goto)", 1, 2, 0); + trans[0][14] = settr(13,2,15,2,0,"else", 1, 2, 0); + trans[0][15] = settr(14,4,29,56,56,"write_off = new_off", 1, 2, 0); /* m: 17 -> 0,29 */ + reached0[17] = 1; + T = trans[ 0][29] = settr(28,2,0,0,0,"ATOMIC", 1, 2, 0); + T->nxt = settr(28,2,19,0,0,"ATOMIC", 1, 2, 0); + trans[0][19] = settr(18,2,26,57,57,"tmp_commit = (commit_count[((prev_off%8)/(8/2))]+size)", 1, 2, 0); /* m: 20 -> 0,26 */ + reached0[20] = 1; + trans[0][20] = settr(0,0,0,0,0,"_commit_sum = ((_commit_sum-commit_count[((prev_off%8)/(8/2))])+tmp_commit)",0,0,0); + trans[0][21] = settr(0,0,0,0,0,"commit_count[((prev_off%8)/(8/2))] = tmp_commit",0,0,0); + T = trans[0][26] = settr(25,2,0,0,0,"IF", 1, 2, 0); + T = T->nxt = settr(25,2,22,0,0,"IF", 1, 2, 0); + T->nxt = settr(25,2,24,0,0,"IF", 1, 2, 0); + trans[0][22] = settr(21,4,30,58,58,"((((((prev_off/8)*8)/2)+(8/2))-tmp_commit))", 1, 2, 0); /* m: 23 -> 30,0 */ + reached0[23] = 1; + trans[0][23] = settr(0,0,0,0,0,"deliver = 1",0,0,0); + trans[0][27] = settr(26,4,30,59,59,".(goto)", 1, 2, 0); /* m: 28 -> 0,30 */ + reached0[28] = 1; + trans[0][24] = settr(23,2,25,2,0,"else", 1, 2, 0); + trans[0][25] = settr(24,4,30,60,60,"(1)", 1, 2, 0); /* m: 27 -> 30,0 */ + reached0[27] = 1; + trans[0][28] = settr(0,0,0,0,0,"refcount = (refcount-1)",0,0,0); + trans[0][30] = settr(29,0,31,1,0,"(1)", 0, 2, 0); + trans[0][31] = settr(30,0,0,61,61,"-end-", 0, 3500, 0); + /* np_ demon: */ + trans[_NP_] = (Trans **) emalloc(2*sizeof(Trans *)); + T = trans[_NP_][0] = settr(9997,0,1,_T5,0,"(np_)", 1,2,0); + T->nxt = settr(9998,0,0,_T2,0,"(1)", 0,2,0); + T = trans[_NP_][1] = settr(9999,0,1,_T5,0,"(np_)", 1,2,0); +} + +Trans * +settr( int t_id, int a, int b, int c, int d, + char *t, int g, int tpe0, int tpe1) +{ Trans *tmp = (Trans *) emalloc(sizeof(Trans)); + + tmp->atom = a&(6|32); /* only (2|8|32) have meaning */ + if (!g) tmp->atom |= 8; /* no global references */ + tmp->st = b; + tmp->tpe[0] = tpe0; + tmp->tpe[1] = tpe1; + tmp->tp = t; + tmp->t_id = t_id; + tmp->forw = c; + tmp->back = d; + return tmp; +} + +Trans * +cpytr(Trans *a) +{ Trans *tmp = (Trans *) emalloc(sizeof(Trans)); + + int i; + tmp->atom = a->atom; + tmp->st = a->st; +#ifdef HAS_UNLESS + tmp->e_trans = a->e_trans; + for (i = 0; i < HAS_UNLESS; i++) + tmp->escp[i] = a->escp[i]; +#endif + tmp->tpe[0] = a->tpe[0]; + tmp->tpe[1] = a->tpe[1]; + for (i = 0; i < 6; i++) + { tmp->qu[i] = a->qu[i]; + tmp->ty[i] = a->ty[i]; + } + tmp->tp = (char *) emalloc(strlen(a->tp)+1); + strcpy(tmp->tp, a->tp); + tmp->t_id = a->t_id; + tmp->forw = a->forw; + tmp->back = a->back; + return tmp; +} + +#ifndef NOREDUCE +int +srinc_set(int n) +{ if (n <= 2) return LOCAL; + if (n <= 2+ DELTA) return Q_FULL_F; /* 's' or nfull */ + if (n <= 2+2*DELTA) return Q_EMPT_F; /* 'r' or nempty */ + if (n <= 2+3*DELTA) return Q_EMPT_T; /* empty */ + if (n <= 2+4*DELTA) return Q_FULL_T; /* full */ + if (n == 5*DELTA) return GLOBAL; + if (n == 6*DELTA) return TIMEOUT_F; + if (n == 7*DELTA) return ALPHA_F; + Uerror("cannot happen srinc_class"); + return BAD; +} +int +srunc(int n, int m) +{ switch(m) { + case Q_FULL_F: return n-2; + case Q_EMPT_F: return n-2-DELTA; + case Q_EMPT_T: return n-2-2*DELTA; + case Q_FULL_T: return n-2-3*DELTA; + case ALPHA_F: + case TIMEOUT_F: return 257; /* non-zero, and > MAXQ */ + } + Uerror("cannot happen srunc"); + return 0; +} +#endif +int cnt; +#ifdef HAS_UNLESS +int +isthere(Trans *a, int b) +{ Trans *t; + for (t = a; t; t = t->nxt) + if (t->t_id == b) + return 1; + return 0; +} +#endif +#ifndef NOREDUCE +int +mark_safety(Trans *t) /* for conditional safety */ +{ int g = 0, i, j, k; + + if (!t) return 0; + if (t->qu[0]) + return (t->qu[1])?2:1; /* marked */ + + for (i = 0; i < 2; i++) + { j = srinc_set(t->tpe[i]); + if (j >= GLOBAL && j != ALPHA_F) + return -1; + if (j != LOCAL) + { k = srunc(t->tpe[i], j); + if (g == 0 + || t->qu[0] != k + || t->ty[0] != j) + { t->qu[g] = k; + t->ty[g] = j; + g++; + } } } + return g; +} +#endif +void +retrans(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) + /* process n, with m states, is=initial state */ +{ Trans *T0, *T1, *T2, *T3; + int i, k; +#ifndef NOREDUCE + int g, h, j, aa; +#endif +#ifdef HAS_UNLESS + int p; +#endif + if (state_tables >= 4) + { printf("STEP 1 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } + do { + for (i = 1, cnt = 0; i < m; i++) + { T2 = trans[n][i]; + T1 = T2?T2->nxt:(Trans *)0; +/* prescan: */ for (T0 = T1; T0; T0 = T0->nxt) +/* choice in choice */ { if (T0->st && trans[n][T0->st] + && trans[n][T0->st]->nxt) + break; + } +#if 0 + if (T0) + printf("\tstate %d / %d: choice in choice\n", + i, T0->st); +#endif + if (T0) + for (T0 = T1; T0; T0 = T0->nxt) + { T3 = trans[n][T0->st]; + if (!T3->nxt) + { T2->nxt = cpytr(T0); + T2 = T2->nxt; + imed(T2, T0->st, n, i); + continue; + } + do { T3 = T3->nxt; + T2->nxt = cpytr(T3); + T2 = T2->nxt; + imed(T2, T0->st, n, i); + } while (T3->nxt); + cnt++; + } + } + } while (cnt); + if (state_tables >= 3) + { printf("STEP 2 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } + for (i = 1; i < m; i++) + { if (trans[n][i] && trans[n][i]->nxt) /* optimize */ + { T1 = trans[n][i]->nxt; +#if 0 + printf("\t\tpull %d (%d) to %d\n", + T1->st, T1->forw, i); +#endif + if (!trans[n][T1->st]) continue; + T0 = cpytr(trans[n][T1->st]); + trans[n][i] = T0; + reach[T1->st] = 1; + imed(T0, T1->st, n, i); + for (T1 = T1->nxt; T1; T1 = T1->nxt) + { +#if 0 + printf("\t\tpull %d (%d) to %d\n", + T1->st, T1->forw, i); +#endif + if (!trans[n][T1->st]) continue; + T0->nxt = cpytr(trans[n][T1->st]); + T0 = T0->nxt; + reach[T1->st] = 1; + imed(T0, T1->st, n, i); + } } } + if (state_tables >= 2) + { printf("STEP 3 proctype %s\n", + procname[n]); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + crack(n, i, T0, srcln); + return; + } +#ifdef HAS_UNLESS + for (i = 1; i < m; i++) + { if (!trans[n][i]) continue; + /* check for each state i if an + * escape to some state p is defined + * if so, copy and mark p's transitions + * and prepend them to the transition- + * list of state i + */ + if (!like_java) /* the default */ + { for (T0 = trans[n][i]; T0; T0 = T0->nxt) + for (k = HAS_UNLESS-1; k >= 0; k--) + { if (p = T0->escp[k]) + for (T1 = trans[n][p]; T1; T1 = T1->nxt) + { if (isthere(trans[n][i], T1->t_id)) + continue; + T2 = cpytr(T1); + T2->e_trans = p; + T2->nxt = trans[n][i]; + trans[n][i] = T2; + } } + } else /* outermost unless checked first */ + { Trans *T4; + T4 = T3 = (Trans *) 0; + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + for (k = HAS_UNLESS-1; k >= 0; k--) + { if (p = T0->escp[k]) + for (T1 = trans[n][p]; T1; T1 = T1->nxt) + { if (isthere(trans[n][i], T1->t_id)) + continue; + T2 = cpytr(T1); + T2->nxt = (Trans *) 0; + T2->e_trans = p; + if (T3) T3->nxt = T2; + else T4 = T2; + T3 = T2; + } } + if (T4) + { T3->nxt = trans[n][i]; + trans[n][i] = T4; + } + } + } +#endif +#ifndef NOREDUCE + for (i = 1; i < m; i++) + { if (a_cycles) + { /* moves through these states are visible */ + #if PROG_LAB>0 && defined(HAS_NP) + if (progstate[n][i]) + goto degrade; + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (progstate[n][T1->st]) + goto degrade; + #endif + if (accpstate[n][i] || visstate[n][i]) + goto degrade; + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (accpstate[n][T1->st]) + goto degrade; + } + T1 = trans[n][i]; + if (!T1) continue; + g = mark_safety(T1); /* V3.3.1 */ + if (g < 0) goto degrade; /* global */ + /* check if mixing of guards preserves reduction */ + if (T1->nxt) + { k = 0; + for (T0 = T1; T0; T0 = T0->nxt) + { if (!(T0->atom&8)) + goto degrade; + for (aa = 0; aa < 2; aa++) + { j = srinc_set(T0->tpe[aa]); + if (j >= GLOBAL && j != ALPHA_F) + goto degrade; + if (T0->tpe[aa] + && T0->tpe[aa] + != T1->tpe[0]) + k = 1; + } } + /* g = 0; V3.3.1 */ + if (k) /* non-uniform selection */ + for (T0 = T1; T0; T0 = T0->nxt) + for (aa = 0; aa < 2; aa++) + { j = srinc_set(T0->tpe[aa]); + if (j != LOCAL) + { k = srunc(T0->tpe[aa], j); + for (h = 0; h < 6; h++) + if (T1->qu[h] == k + && T1->ty[h] == j) + break; + if (h >= 6) + { T1->qu[g%6] = k; + T1->ty[g%6] = j; + g++; + } } } + if (g > 6) + { T1->qu[0] = 0; /* turn it off */ + printf("pan: warning, line %d, ", + srcln[i]); + printf("too many stmnt types (%d)", + g); + printf(" in selection\n"); + goto degrade; + } + } + /* mark all options global if >=1 is global */ + for (T1 = trans[n][i]; T1; T1 = T1->nxt) + if (!(T1->atom&8)) break; + if (T1) +degrade: for (T1 = trans[n][i]; T1; T1 = T1->nxt) + T1->atom &= ~8; /* mark as unsafe */ + /* can only mix 'r's or 's's if on same chan */ + /* and not mixed with other local operations */ + T1 = trans[n][i]; + if (!T1 || T1->qu[0]) continue; + j = T1->tpe[0]; + if (T1->nxt && T1->atom&8) + { if (j == 5*DELTA) + { printf("warning: line %d ", srcln[i]); + printf("mixed condition "); + printf("(defeats reduction)\n"); + goto degrade; + } + for (T0 = T1; T0; T0 = T0->nxt) + for (aa = 0; aa < 2; aa++) + if (T0->tpe[aa] && T0->tpe[aa] != j) + { printf("warning: line %d ", srcln[i]); + printf("[%d-%d] mixed %stion ", + T0->tpe[aa], j, + (j==5*DELTA)?"condi":"selec"); + printf("(defeats reduction)\n"); + printf(" '%s' <-> '%s'\n", + T1->tp, T0->tp); + goto degrade; + } } + } +#endif + for (i = 1; i < m; i++) + { T2 = trans[n][i]; + if (!T2 + || T2->nxt + || strncmp(T2->tp, ".(goto)", 7) + || !stopstate[n][i]) + continue; + stopstate[n][T2->st] = 1; + } + if (state_tables) + { printf("proctype "); + if (!strcmp(procname[n], ":init:")) + printf("init\n"); + else + printf("%s\n", procname[n]); + for (i = 1; i < m; i++) + reach[i] = 1; + tagtable(n, m, is, srcln, reach); + } else + for (i = 1; i < m; i++) + { int nrelse; + if (strcmp(procname[n], ":never:") != 0) + { for (T0 = trans[n][i]; T0; T0 = T0->nxt) + { if (T0->st == i + && strcmp(T0->tp, "(1)") == 0) + { printf("error: proctype '%s' ", + procname[n]); + printf("line %d, state %d: has un", + srcln[i], i); + printf("conditional self-loop\n"); + pan_exit(1); + } } } + nrelse = 0; + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + { if (strcmp(T0->tp, "else") == 0) + nrelse++; + } + if (nrelse > 1) + { printf("error: proctype '%s' state", + procname[n]); + printf(" %d, inherits %d", i, nrelse); + printf(" 'else' stmnts\n"); + pan_exit(1); + } } + if (!state_tables && strcmp(procname[n], ":never:") == 0) + { int h = 0; + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + if (T0->forw > h) h = T0->forw; + h++; + frm_st0 = (short *) emalloc(h * sizeof(short)); + for (i = 1; i < m; i++) + for (T0 = trans[n][i]; T0; T0 = T0->nxt) + frm_st0[T0->forw] = i; + } +#ifndef LOOPSTATE + if (state_tables) +#endif + do_dfs(n, m, is, srcln, reach, lstate); +#ifdef T_REVERSE + /* process n, with m states, is=initial state -- reverse list */ + if (!state_tables && strcmp(procname[n], ":never:") != 0) + { for (i = 1; i < m; i++) + { Trans *T4 = (Trans *) 0; + T1 = (Trans *) 0; + T2 = (Trans *) 0; + T3 = (Trans *) 0; + for (T0 = trans[n][i]; T0; T0 = T4) + { T4 = T0->nxt; + if (strcmp(T0->tp, "else") == 0) + { T3 = T0; + T0->nxt = (Trans *) 0; + } else + { T0->nxt = T1; + if (!T1) { T2 = T0; } + T1 = T0; + } } + if (T2 && T3) { T2->nxt = T3; } + trans[n][i] = T1; /* reversed -- else at end */ + } } +#endif +} +void +imed(Trans *T, int v, int n, int j) /* set intermediate state */ +{ progstate[n][T->st] |= progstate[n][v]; + accpstate[n][T->st] |= accpstate[n][v]; + stopstate[n][T->st] |= stopstate[n][v]; + mapstate[n][j] = T->st; +} +void +tagtable(int n, int m, int is, short srcln[], uchar reach[]) +{ Trans *z; + + if (is >= m || !trans[n][is] + || is <= 0 || reach[is] == 0) + return; + reach[is] = 0; + if (state_tables) + for (z = trans[n][is]; z; z = z->nxt) + crack(n, is, z, srcln); + for (z = trans[n][is]; z; z = z->nxt) + { +#ifdef HAS_UNLESS + int i, j; +#endif + tagtable(n, m, z->st, srcln, reach); +#ifdef HAS_UNLESS + for (i = 0; i < HAS_UNLESS; i++) + { j = trans[n][is]->escp[i]; + if (!j) break; + tagtable(n, m, j, srcln, reach); + } +#endif + } +} +void +dfs_table(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) +{ Trans *z; + + if (is >= m || is <= 0 || !trans[n][is]) + return; + if ((reach[is] & (4|8|16)) != 0) + { if ((reach[is] & (8|16)) == 16) /* on stack, not yet recorded */ + { lstate[is] = 1; + reach[is] |= 8; /* recorded */ + if (state_tables) + { printf("state %d line %d is a loopstate\n", is, srcln[is]); + } } + return; + } + reach[is] |= (4|16); /* visited | onstack */ + for (z = trans[n][is]; z; z = z->nxt) + { +#ifdef HAS_UNLESS + int i, j; +#endif + dfs_table(n, m, z->st, srcln, reach, lstate); +#ifdef HAS_UNLESS + for (i = 0; i < HAS_UNLESS; i++) + { j = trans[n][is]->escp[i]; + if (!j) break; + dfs_table(n, m, j, srcln, reach, lstate); + } +#endif + } + reach[is] &= ~16; /* no longer on stack */ +} +void +do_dfs(int n, int m, int is, short srcln[], uchar reach[], uchar lstate[]) +{ int i; + dfs_table(n, m, is, srcln, reach, lstate); + for (i = 0; i < m; i++) + reach[i] &= ~(4|8|16); +} +void +crack(int n, int j, Trans *z, short srcln[]) +{ int i; + + if (!z) return; + printf(" state %3d -(tr %3d)-> state %3d ", + j, z->forw, z->st); + printf("[id %3d tp %3d", z->t_id, z->tpe[0]); + if (z->tpe[1]) printf(",%d", z->tpe[1]); +#ifdef HAS_UNLESS + if (z->e_trans) + printf(" org %3d", z->e_trans); + else if (state_tables >= 2) + for (i = 0; i < HAS_UNLESS; i++) + { if (!z->escp[i]) break; + printf(" esc %d", z->escp[i]); + } +#endif + printf("]"); + printf(" [%s%s%s%s%s] line %d => ", + z->atom&6?"A":z->atom&32?"D":"-", + accpstate[n][j]?"a" :"-", + stopstate[n][j]?"e" : "-", + progstate[n][j]?"p" : "-", + z->atom & 8 ?"L":"G", + srcln[j]); + for (i = 0; z->tp[i]; i++) + if (z->tp[i] == '\n') + printf("\\n"); + else + putchar(z->tp[i]); + if (z->qu[0]) + { printf("\t["); + for (i = 0; i < 6; i++) + if (z->qu[i]) + printf("(%d,%d)", + z->qu[i], z->ty[i]); + printf("]"); + } + printf("\n"); + fflush(stdout); +} + +#ifdef VAR_RANGES +#define BYTESIZE 32 /* 2^8 : 2^3 = 256:8 = 32 */ + +typedef struct Vr_Ptr { + char *nm; + uchar vals[BYTESIZE]; + struct Vr_Ptr *nxt; +} Vr_Ptr; +Vr_Ptr *ranges = (Vr_Ptr *) 0; + +void +logval(char *s, int v) +{ Vr_Ptr *tmp; + + if (v<0 || v > 255) return; + for (tmp = ranges; tmp; tmp = tmp->nxt) + if (!strcmp(tmp->nm, s)) + goto found; + tmp = (Vr_Ptr *) emalloc(sizeof(Vr_Ptr)); + tmp->nxt = ranges; + ranges = tmp; + tmp->nm = s; +found: + tmp->vals[(v)/8] |= 1<<((v)%8); +} + +void +dumpval(uchar X[], int range) +{ int w, x, i, j = -1; + + for (w = i = 0; w < range; w++) + for (x = 0; x < 8; x++, i++) + { +from: if ((X[w] & (1<= 0 && j != 255) + printf("-255"); +} + +void +dumpranges(void) +{ Vr_Ptr *tmp; + printf("\nValues assigned within "); + printf("interval [0..255]:\n"); + for (tmp = ranges; tmp; tmp = tmp->nxt) + { printf("\t%s\t: ", tmp->nm); + dumpval(tmp->vals, BYTESIZE); + printf("\n"); + } +} +#endif diff --git a/trunk/verif/nico-md-merge/read_write.log b/trunk/verif/nico-md-merge/read_write.log new file mode 100644 index 00000000..3a61b484 --- /dev/null +++ b/trunk/verif/nico-md-merge/read_write.log @@ -0,0 +1,65 @@ +make[1]: Entering directory `/home/compudj/repository/trunk/verif/nico-md-merge' +rm -f pan* trail.out +cat defines > pan.ltl +spin -f "!(`cat read_write.ltl | grep -v ^//`)" >> pan.ltl +spin -a -X -N pan.ltl model.spin +Exit-Status 0 +gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c +./pan -v -X -m100000 -w21 -a -c1 +warning: for p.o. reduction to be valid the never claim must be stutter-invariant +(never claims generated from LTL formulae are stutter-invariant) +depth 0: Claim reached state 5 (line 302) + +(Spin Version 5.1.6 -- 9 May 2008) + + Partial Order Reduction + +Full statespace search for: + never claim + + assertion violations + (if within scope of claim) + acceptance cycles + (fairness disabled) + invalid end states - (disabled by never claim) + +State-vector 92 byte, depth reached 178, errors: 0 + 117886 states, stored + 210653 states, matched + 328539 transitions (= stored+matched) + 440774 atomic steps +hash conflicts: 3201 (resolved) + +Stats on memory usage (in Megabytes): + 12.142 equivalent memory usage for states (stored*(State-vector + overhead)) + 8.971 actual memory usage for states (compression: 73.88%) + state-vector as stored = 64 byte + 16 byte overhead + 8.000 memory used for hash table (-w21) + 3.052 memory used for DFS stack (-m100000) + 19.939 total actual memory usage + +unreached in proctype switcher + line 81, "pan.___", state 8, "(1)" + line 87, "pan.___", state 15, "write_off = new_off" + line 84, "pan.___", state 18, "((prev_off!=write_off))" + line 84, "pan.___", state 18, "else" + line 97, "pan.___", state 21, "commit_count[((prev_off%4)/(4/2))] = tmp_commit" + line 103, "pan.___", state 25, "(1)" + line 98, "pan.___", state 26, "((((((prev_off/4)*4)/2)+(4/2))-tmp_commit))" + line 98, "pan.___", state 26, "else" + line 91, "pan.___", state 29, "tmp_commit = (commit_count[((prev_off%4)/(4/2))]+size)" + (7 of 31 states) +unreached in proctype tracer + line 177, "pan.___", state 48, "events_lost = (events_lost+1)" + (1 of 51 states) +unreached in proctype reader + (0 of 29 states) +unreached in proctype cleaner + (0 of 9 states) +unreached in proctype :init: + line 284, "pan.___", state 35, "(run switcher())" + (1 of 43 states) +unreached in proctype :never: + line 307, "pan.___", state 8, "-end-" + (1 of 8 states) + +pan: elapsed time 0.85 seconds +pan: rate 138689.41 states/second +pan: avg transition delay 2.5872e-06 usec +make[1]: Leaving directory `/home/compudj/repository/trunk/verif/nico-md-merge' diff --git a/trunk/verif/nico-md-merge/read_write.ltl b/trunk/verif/nico-md-merge/read_write.ltl new file mode 100755 index 00000000..fbed3b87 --- /dev/null +++ b/trunk/verif/nico-md-merge/read_write.ltl @@ -0,0 +1,4 @@ +// The writer head must always be superior or equal to the reader head. +// assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + +[] (rwoff1 && rwoff2) diff --git a/trunk/verif/nico/Makefile b/trunk/verif/nico/Makefile new file mode 100755 index 00000000..0b96bfec --- /dev/null +++ b/trunk/verif/nico/Makefile @@ -0,0 +1,57 @@ +# makefile + +COPTIONS=-DSAFETY + + +default: + make commit_sum | tee commit_sum.log + make read_write | tee read_write.log + make events_lost | tee events_lost.log + make no_events_lost | tee no_events_lost.log + + +no_events_lost: clean no_events_lost_ltl run + +no_events_lost_ltl: + cat DEFINES > pan.ltl + spin -f "!(`cat no_events_lost.ltl | grep -v ^//`)" >> pan.ltl + + + +events_lost: clean events_lost_ltl run + +events_lost_ltl: + cat DEFINES > pan.ltl + spin -f "!(`cat events_lost.ltl`)" >> pan.ltl + + + +read_write: clean read_write_ltl run + +read_write_ltl: + cat DEFINES > pan.ltl + spin -f "!(`cat read_write.ltl`)" >> pan.ltl + + + +commit_sum: clean commit_sum_ltl run + +commit_sum_ltl: + cat DEFINES > pan.ltl + spin -f "!(`cat commit_sum.ltl`)" >> pan.ltl + + + +run: pan + ./pan -v -X -m100000 -w21 -a -c1 + +pan: pan.c + gcc -w -o pan -D_POSIX_SOURCE -DMEMLIM=750 -DXUSAFE -DNOFAIR pan.c + +pan.c: pan.ltl model.spin + spin -a -X -N pan.ltl model.spin + + + +clean: + rm -f pan* trail.out diff --git a/trunk/verif/nico/commit_sum.ltl b/trunk/verif/nico/commit_sum.ltl new file mode 100755 index 00000000..265a592a --- /dev/null +++ b/trunk/verif/nico/commit_sum.ltl @@ -0,0 +1 @@ +[] (commit_sum_wr_off1 && commit_sum_wr_off2) diff --git a/trunk/verif/nico/defines b/trunk/verif/nico/defines new file mode 100755 index 00000000..45dc7ea2 --- /dev/null +++ b/trunk/verif/nico/defines @@ -0,0 +1,8 @@ +#define rd_wr_wr_off1 (write_off - read_off >= 0) +#define rd_wr_wr_off2 (write_off - read_off < HALF_UCHAR) + +#define commit_sum_wr_off1 (write_off - commit_sum >= 0) +#define commit_sum_wr_off2 (write_off - commit_sum < HALF_UCHAR) + +#define buffer_large_enough (NUMPROCS + NUMSWITCH <= BUFSIZE) +#define have_events_lost (events_lost != 0) diff --git a/trunk/verif/nico/events_lost.ltl b/trunk/verif/nico/events_lost.ltl new file mode 100755 index 00000000..ce0592f7 --- /dev/null +++ b/trunk/verif/nico/events_lost.ltl @@ -0,0 +1 @@ +(!buffer_large_enough) -> (<> have_events_lost) diff --git a/trunk/verif/nico/model.spin b/trunk/verif/nico/model.spin new file mode 120000 index 00000000..b0ba3702 --- /dev/null +++ b/trunk/verif/nico/model.spin @@ -0,0 +1 @@ +models/model_03.spin \ No newline at end of file diff --git a/trunk/verif/nico/model.spin.trail b/trunk/verif/nico/model.spin.trail new file mode 100755 index 00000000..a930eafd --- /dev/null +++ b/trunk/verif/nico/model.spin.trail @@ -0,0 +1,174 @@ +-2:5:-2 +-4:-4:-4 +1:0:164 +2:1:120 +3:1:121 +4:1:121 +5:1:124 +6:1:130 +7:1:130 +8:1:130 +9:1:130 +10:1:133 +11:1:138 +12:1:139 +13:1:141 +14:1:143 +15:1:141 +16:1:143 +17:1:141 +18:1:143 +19:1:141 +20:1:143 +21:1:145 +22:1:151 +23:1:153 +24:1:155 +25:1:159 +26:0:168 +27:8:0 +28:8:3 +29:0:168 +30:8:29 +31:0:168 +32:8:30 +33:0:168 +34:7:31 +35:0:168 +36:7:36 +37:7:37 +38:0:168 +39:7:43 +40:7:44 +41:7:48 +42:7:52 +43:7:56 +44:0:168 +45:7:58 +46:7:59 +47:7:62 +48:7:70 +49:0:168 +50:7:77 +51:7:79 +52:0:168 +53:7:81 +54:0:168 +55:6:31 +56:0:168 +57:6:36 +58:6:37 +59:0:168 +60:6:43 +61:6:44 +62:6:48 +63:6:52 +64:6:56 +65:0:168 +66:6:58 +67:6:59 +68:6:62 +69:6:72 +70:6:73 +71:0:168 +72:6:77 +73:6:79 +74:0:168 +75:6:81 +76:0:168 +77:5:31 +78:0:168 +79:5:36 +80:5:37 +81:0:168 +82:5:43 +83:5:44 +84:5:48 +85:5:52 +86:5:56 +87:0:168 +88:5:58 +89:5:59 +90:5:62 +91:5:70 +92:0:168 +93:5:77 +94:5:79 +95:0:168 +96:5:81 +97:0:168 +98:4:31 +99:0:168 +100:4:36 +101:4:37 +102:0:168 +103:4:43 +104:4:44 +105:4:48 +106:4:52 +107:4:56 +108:0:168 +109:4:58 +110:4:59 +111:4:62 +112:4:72 +113:4:73 +114:0:168 +115:4:77 +116:4:79 +117:0:168 +118:4:81 +119:0:168 +120:3:111 +121:3:113 +122:3:117 +123:0:168 +124:4:0 +125:4:3 +126:0:168 +127:4:29 +128:0:168 +129:4:30 +130:0:168 +131:3:119 +132:0:168 +133:2:82 +134:0:168 +135:2:83 +136:2:84 +137:2:84 +138:2:88 +139:2:92 +140:0:168 +141:2:94 +142:2:95 +143:2:95 +144:2:98 +145:2:102 +146:2:103 +147:0:168 +148:2:82 +149:0:168 +150:2:83 +151:2:84 +152:2:84 +153:2:88 +154:2:92 +155:0:168 +156:2:94 +157:2:95 +158:2:95 +159:2:98 +160:2:102 +161:2:103 +162:0:168 +163:2:105 +164:0:168 +165:2:110 +166:0:168 +167:1:161 +168:0:168 +169:1:163 +-1:-1:-1 +170:0:168 +171:0:168 diff --git a/trunk/verif/nico/models/model_01.spin b/trunk/verif/nico/models/model_01.spin new file mode 100755 index 00000000..edcd8fac --- /dev/null +++ b/trunk/verif/nico/models/model_01.spin @@ -0,0 +1,303 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + /* The commit count of a particular subbuffer must always be higher + * or equal to the retrieve_count of this subbuffer. + * assert(commit_count[j] - retrieve_count[j] >= 0 && + * commit_count[j] - retrieve_count[j] < HALF_UCHAR); + */ + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not lose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/nico/models/model_02.spin b/trunk/verif/nico/models/model_02.spin new file mode 100755 index 00000000..671e9f2e --- /dev/null +++ b/trunk/verif/nico/models/model_02.spin @@ -0,0 +1,298 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + byte commit_sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + /*assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR);*/ + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + j++ + :: j >= NR_SUBBUFS -> break + od; + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/nico/models/model_03.spin b/trunk/verif/nico/models/model_03.spin new file mode 100755 index 00000000..ac947c8f --- /dev/null +++ b/trunk/verif/nico/models/model_03.spin @@ -0,0 +1,316 @@ +/* LTTng ltt-tracer.c atomic lockless buffering scheme Promela model v2 + * Created for the Spin validator. + * Mathieu Desnoyers + * October 2008 + + * TODO : create test cases that will generate an overflow on the offset and + * counter type. Counter types smaller than a byte should be used. + + * Promela only has unsigned char, no signed char. + * Because detection of difference < 0 depends on a signed type, but we want + * compactness, check also for the values being higher than half of the unsigned + * char range (and consider them negative). The model, by design, does not use + * offsets or counts higher than 127 because we would then have to use a larger + * type (short or int). + */ +#define HALF_UCHAR (255/2) + +/* NUMPROCS 4 : causes event loss with some reader timings. + * e.g. 3 events, 1 switch, 1 event (lost, buffer full), read 1 subbuffer + */ +#define NUMPROCS 4 + +/* NUMPROCS 3 : does not cause event loss because buffers are big enough. + * #define NUMPROCS 3 + * e.g. 3 events, 1 switch, read 1 subbuffer + */ + +#define NUMSWITCH 1 +#define BUFSIZE 4 +#define NR_SUBBUFS 2 +#define SUBBUF_SIZE (BUFSIZE / NR_SUBBUFS) + +/* */ +byte commit_sum = 0; +/* */ + +/* Writer counters +*/ +byte write_off = 0; +byte commit_count[NR_SUBBUFS]; + +/* Reader counters +*/ +byte read_off = 0; + +byte events_lost = 0; +byte refcount = 0; + +bool deliver = 0; + +/* buffer slot in-use bit. Detects racy use (more than a single process + * accessing a slot at any given step). + */ +bool buffer_use[BUFSIZE]; + +/* Proceed to a sub-subber switch is needed. + * Used in a periodical timer interrupt to fill and ship the current subbuffer + * to the reader so we can guarantee a steady flow. If a subbuffer is + * completely empty, do not switch. + * Also used as "finalize" operation to complete the last subbuffer after + * all writers have finished so the last subbuffer can be read by the reader. + */ +proctype switcher() +{ + byte prev_off, new_off, tmp_commit; + byte size; + +cmpxchg_loop: + atomic { + prev_off = write_off; + size = SUBBUF_SIZE - (prev_off % SUBBUF_SIZE); + new_off = prev_off + size; + if + :: (new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR) + || size == SUBBUF_SIZE -> + refcount = refcount - 1; + goto not_needed; + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + } + + atomic { + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + /* */ + commit_sum = commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + refcount = refcount - 1; + } +not_needed: + skip; +} + +/* tracer + * Writes 1 byte of information in the buffer at the current + * "write_off" position and then increment the commit_count of the sub-buffer + * the information has been written to. + */ +proctype tracer() +{ + byte size = 1; + byte prev_off, new_off, tmp_commit; + byte i, j; + +cmpxchg_loop: + atomic { + prev_off = write_off; + new_off = prev_off + size; + } + atomic { + if + :: new_off - read_off > BUFSIZE && new_off - read_off < HALF_UCHAR -> + goto lost + :: else -> skip + fi; + } + atomic { + if + :: prev_off != write_off -> goto cmpxchg_loop + :: else -> write_off = new_off; + fi; + i = 0; + do + :: i < size -> + assert(buffer_use[(prev_off + i) % BUFSIZE] == 0); + buffer_use[(prev_off + i) % BUFSIZE] = 1; + i++ + :: i >= size -> break + od; + } + + /* writing to buffer... + */ + + atomic { + i = 0; + do + :: i < size -> + buffer_use[(prev_off + i) % BUFSIZE] = 0; + i++ + :: i >= size -> break + od; + tmp_commit = commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + size; + commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] = tmp_commit; + /* */ + commit_sum = commit_sum - commit_count[(prev_off % BUFSIZE) / SUBBUF_SIZE] + + tmp_commit; + /* */ + if + :: (((prev_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + SUBBUF_SIZE - + tmp_commit + -> deliver = 1 + :: else + -> skip + fi; + } + atomic { + goto end; +lost: + events_lost++; +end: + refcount = refcount - 1; + } +} + +/* reader + * Read the information sub-buffer per sub-buffer when available. + * + * Reads the information as soon as it is ready, or may be delayed by + * an asynchronous delivery. Being modeled as a process insures all cases + * (scheduled very quickly or very late, causing event loss) are covered. + * Only one reader per buffer (normally ensured by a mutex). This is modeled + * by using a single reader process. + */ +proctype reader() +{ + byte i, j; + + do + :: (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) > 0 + && (write_off / SUBBUF_SIZE) - (read_off / SUBBUF_SIZE) < HALF_UCHAR + && (commit_count[(read_off % BUFSIZE) / SUBBUF_SIZE] + - SUBBUF_SIZE - (((read_off / BUFSIZE) * BUFSIZE) / NR_SUBBUFS) + == 0) -> + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + assert(buffer_use[(read_off + i) % BUFSIZE] == 0); + buffer_use[(read_off + i) % BUFSIZE] = 1; + i++ + :: i >= SUBBUF_SIZE -> break + od; + } + + /* reading from buffer... + */ + + atomic { + i = 0; + do + :: i < SUBBUF_SIZE -> + buffer_use[(read_off + i) % BUFSIZE] = 0; + i++ + :: i >= SUBBUF_SIZE -> break + od; + read_off = read_off + SUBBUF_SIZE; + } + :: read_off >= (NUMPROCS - events_lost) -> break; + od; +} + +/* Waits for all tracer and switcher processes to finish before finalizing + * the buffer. Only after that will the reader be allowed to read the + * last subbuffer. + */ +proctype cleaner() +{ + atomic { + do + :: refcount == 0 -> + refcount = refcount + 1; + run switcher(); /* Finalize the last sub-buffer so it can be read. */ + break; + od; + } +} + +init { + byte i = 0; + byte j = 0; + byte sum = 0; + + atomic { + i = 0; + do + :: i < NR_SUBBUFS -> + commit_count[i] = 0; + i++ + :: i >= NR_SUBBUFS -> break + od; + i = 0; + do + :: i < BUFSIZE -> + buffer_use[i] = 0; + i++ + :: i >= BUFSIZE -> break + od; + run reader(); + run cleaner(); + i = 0; + do + :: i < NUMPROCS -> + refcount = refcount + 1; + run tracer(); + i++ + :: i >= NUMPROCS -> break + od; + i = 0; + do + :: i < NUMSWITCH -> + refcount = refcount + 1; + run switcher(); + i++ + :: i >= NUMSWITCH -> break + od; + } + + /* Assertions. + */ + atomic { + /* The writer head must always be superior or equal to the reader head. + */ + + /* assert(write_off - read_off >= 0 && write_off - read_off < HALF_UCHAR); + */ + + /* + j = 0; + commit_sum = 0; + do + :: j < NR_SUBBUFS -> + commit_sum = commit_sum + commit_count[j]; + j++ + :: j >= NR_SUBBUFS -> break + od; + */ + + /* The sum of all subbuffer commit counts must always be lower or equal + * to the writer head, because space must be reserved before it is + * written to and then committed. + */ + /* + assert(write_off - commit_sum >= 0 && write_off - commit_sum < HALF_UCHAR); + */ + /* If we have less writers than the buffer space available, we should + * not loose events + */ + assert(NUMPROCS + NUMSWITCH > BUFSIZE || events_lost == 0); + } +} + diff --git a/trunk/verif/nico/no_events_lost.ltl b/trunk/verif/nico/no_events_lost.ltl new file mode 100755 index 00000000..d61a20f2 --- /dev/null +++ b/trunk/verif/nico/no_events_lost.ltl @@ -0,0 +1 @@ +buffer_large_enough -> ([](!have_events_lost)) diff --git a/trunk/verif/nico/read_write.ltl b/trunk/verif/nico/read_write.ltl new file mode 100755 index 00000000..f78149ec --- /dev/null +++ b/trunk/verif/nico/read_write.ltl @@ -0,0 +1 @@ +[] (rd_wr_wr_off1 && rd_wr_wr_off2) -- 2.34.1