File: mem.ml

package info (click to toggle)
herdtools7 7.58-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,732 kB
  • sloc: ml: 128,583; ansic: 3,827; makefile: 670; python: 407; sh: 212; awk: 14
file content (2136 lines) | stat: -rw-r--r-- 77,025 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
(****************************************************************************)
(*                           the diy toolsuite                              *)
(*                                                                          *)
(* Jade Alglave, University College London, UK.                             *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France.                          *)
(*                                                                          *)
(* Copyright 2010-present Institut National de Recherche en Informatique et *)
(* en Automatique, ARM Ltd and the authors. All rights reserved.            *)
(*                                                                          *)
(* This software is governed by the CeCILL-B license under French law and   *)
(* abiding by the rules of distribution of free software. You can use,      *)
(* modify and/ or redistribute the software under the terms of the CeCILL-B *)
(* license as circulated by CEA, CNRS and INRIA at the following URL        *)
(* "http://www.cecill.info". We also give a copy in LICENSE.txt.            *)
(****************************************************************************)
(* Authors:                                                                 *)
(* Jade Alglave, University College London, UK.                             *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France.                          *)
(* Hadrien Renaud, University College London, UK.                           *)
(****************************************************************************)

(** Produce event structures (which include variables) + constraints,
   using instruction semantics *)

module type CommonConfig = sig
  val verbose : int
  val optace : OptAce.t
  val unroll : int option
  val speedcheck : Speed.t
  val debug : Debug_herd.t
  val observed_finals_only : bool
  val initwrites : bool
  val check_filter : bool
  val maxphantom : int option
  val variant : Variant.t -> bool
  val fault_handling : Fault.Handling.t
  val mte_precision : Precision.t
end

module type Config = sig
  include CommonConfig
  val byte : MachSize.sz
  val dirty : DirtyBit.t option
end

module type S = sig

  module S : Sem.Semantics

  type result =
     {
       event_structures : (int * S.M.VC.cnstrnts * S.event_structure) list ;
       overwritable_labels : Label.Set.t ;
     }

(** [glommed_event_structures t] performs "instruction semantics".
 *  Argument [t] is a test.
 *  The function returns a pair whose first component is a record.
 *
 *  It includes a set (list) of "abstract"  event structures. In such
 *  structures, most values (and locations) are not resolved yet. Hence the
 *  [S.M.VC.cnstrnts] second component. Those are equations to be solved
 *  once some read-from relation on memory is selected by the function
 *  [calculate_rf_with_cnstrnts] (see below). The record also includes
 *  additional processing information to pass on to calculate_rf_with_cnstrnts,
 *  which is the set of labels that are allowed to be addressed by stores.
 *
 *  Second component of the returned pair is the test itself,
 *  which can be slightly modified (noticeably in the case of
 *  `-variant self`, initial values of overwitable code locations
 *  are added.
 *
 *  This modified test *must* be used in the following. *)

  val glommed_event_structures : S.test -> result * S.test



(* A first generator,
   calculate_rf_with_cnstrnts test es constraints kont res,

   - test and es are test description and (abstract) event structure.
     By abstract, we here mean that some values and even
     memory locations in them are variables.

   - constraints is a set of constraint, which
     * Is solvable: ie resolution results
       in either an assigment of all variables in es, or
       in failure.
     * expresses the constraints generated by semantics.

   - kont : S.concrete -> VC.cnstrnts -> 'a  -> 'a
     will be called on all generated concrete event structures, resulting
     in  computation:
     kont (esN (... (kont es1 res)))
     where esK is the
        + abstract event structure with variables replaced by constants
        + [Failed] of [Warn] constraint from equations, if any
        + rfmap
        + final state (included in rfmap in fact)
 *)


  val calculate_rf_with_cnstrnts :
      S.test -> Label.Set.t -> S.event_structure -> S.M.VC.cnstrnts ->
        (S.concrete -> S.M.VC.cnstrnt option -> 'a -> 'a ) -> (* kont *)
          'a -> 'a

  val solve_regs :
      S.test -> S.E.event_structure -> S.M.VC.cnstrnt list ->
        (S.E.event_structure * S.read_from S.RFMap.t * S.M.VC.cnstrnt list) option

  val solve_mem :
      S.test ->S.E.event_structure -> S.read_from S.RFMap.t -> S.M.VC.cnstrnt list ->
        (S.E.event_structure ->
          S.read_from S.RFMap.t -> S.M.VC.cnstrnt list -> 'a -> 'a) ->
            'a -> 'a

  val check_sizes : S.test -> S.event_structure -> unit

  val check_rfmap :
      S.E.event_structure -> S.read_from S.RFMap.t -> bool

  val when_unsolved :
      S.test -> S.E.event_structure -> S.read_from S.RFMap.t -> S.M.VC.cnstrnt list -> 'a -> 'a

  val compute_final_state :
    S.test -> S.read_from S.RFMap.t -> S.E.EventSet.t -> S.A.state * S.A.FaultSet.t

  val check_filter : S.test -> S.A.state * S.A.FaultSet.t -> bool

  val get_loc :
    S.E.event -> S.E.A.location

  val make_atomic_load_store :
    S.E.event_structure -> S.E.EventRel.t

end

open Printf

module Make(C:Config) (S:Sem.Semantics) : S with module S = S	=
  struct
    module S = S

    module A = S.A
    module B = S.B
    module AM = A.Mixed(C)
    module V = A.V
    module E = S.E
    module EM = S.M
    module VC = EM.VC
    module U = MemUtils.Make(S)
    module W = Warn.Make(C)

    let dbg = C.debug.Debug_herd.mem
    let morello = C.variant Variant.Morello
    let mixed = C.variant Variant.Mixed || morello
    let unaligned = C.variant Variant.Unaligned
    let memtag = C.variant Variant.MemTag
        (* default is checking *)
    let check_mixed =  not (C.variant Variant.DontCheckMixed)
    let do_deps = C.variant Variant.Deps
    let kvm = C.variant Variant.VMSA
    let self = C.variant Variant.Ifetch
    let asl = C.variant Variant.ASL
    let oota = C.variant Variant.OOTA
    let unroll =
      match C.unroll with
      | None -> Opts.unroll_default A.arch
      | Some u -> u

(*****************************)
(* Event structure generator *)
(*****************************)

(* Relabeling (eiid) events so as to get memory events labeled by 0,1, etc *)
    module IMap =
      Map.Make
        (struct
          type t = E.eiid
          let compare = Misc.int_compare
        end)

    let count_mem evts =
      E.EventSet.fold
        (fun e k ->
          if E.is_mem e then k+1
          else k)
        evts 0

    let build_map n_mem evts =
      let build_bd e (next_mem,next_other,k) =
        let key = e.E.eiid in
        if E.is_mem e then
          (next_mem+1,next_other,IMap.add key next_mem k)
        else
          (next_mem,next_other+1,IMap.add key next_other k) in
      let _,_,r = E.EventSet.fold build_bd evts (0,n_mem,IMap.empty) in
      r

(* Note: events from mem_accesses are not relabeled, as they are not part of final events *)
    let relabel es =
      let n_mem = count_mem es.E.events in
      let map = build_map n_mem es.E.events in
      let relabel_event e =
        let id =  e.E.eiid in
        try { e with E.eiid = IMap.find id map }
        with Not_found -> assert (E.EventSet.mem e es.E.mem_accesses) ; e in
       E.map_event_structure relabel_event es


    let (|*|) = EM.(|*|)
    let (>>>) = EM.(>>>)

    let is_back_jump addr_jmp addr_tgt = Misc.int_compare addr_jmp addr_tgt >= 0

    type result =
        {
         event_structures : (int * S.M.VC.cnstrnts * S.event_structure) list ;
         overwritable_labels : Label.Set.t ;
        }

(* All (virtual) locations from init state *)

    let get_all_locs_init init =
      let locs =
        A.state_fold
          (fun loc v locs ->
            let locs =
              match loc with
              | A.Location_global _
                -> loc::locs
              | A.Location_reg _ -> locs in
            try
              match A.V.as_virtual v with
              | Some s ->
                 let sym = Constant.mk_sym_virtual s in
                 A.Location_global (A.V.Val sym)::locs
              | None -> locs
            with V.Undetermined -> locs)
          init [] in
      A.LocSet.of_list locs

(* All (virtual) memory locations reachable by a test *)

    let get_all_mem_locs test =
      let locs_final =
        A.LocSet.filter
          (function
           | A.Location_global _ -> true
           | A.Location_reg _ -> false)
          (S.observed_locations test)
      and locs_init = get_all_locs_init test.Test_herd.init_state in
      let () =
        if dbg then begin
          let pp_locs locs =
            A.LocSet.pp_str "," A.dump_location locs in
          Printf.eprintf
            "locs_init={%s}, locs_final={%s}\n%!"
            (pp_locs locs_init)
            (pp_locs locs_final)
        end in
      let locs = A.LocSet.union locs_final locs_init in
      let locs =
        List.fold_left
          (fun locs (_,code,fh_code) ->
            let code = match fh_code with
              | Some fh_code -> code@fh_code
              | None -> code in
            List.fold_left
              (fun locs (_,ins) ->
                A.fold_addrs
                  (fun x ->
                    let loc = A.maybev_to_location x in
                    match loc with
                    | A.Location_global _ -> A.LocSet.add loc
                    | _ -> fun locs -> locs)
                  locs ins)
              locs code)
          locs
          test.Test_herd.start_points in
      let env =
        A.LocSet.fold
          (fun loc env ->
            try
              let v = A.look_address_in_state test.Test_herd.init_state loc in
              (loc,v)::env
            with A.LocUndetermined -> assert false)
          locs [] in
      env

    module SM = S.Mixed(C)

    type ('a,'b,'c) fetch_r = Ok of 'a * 'b | No of 'a | Segfault of 'c

    let segfault =
      Warn.user_error
        "Segmentation fault (kidding, label %s not found)"

    let glommed_event_structures (test:S.test) =
      let prog = test.Test_herd.program in
      let starts = test.Test_herd.start_points in
      let code_segment = test.Test_herd.code_segment in
      let procs = List.map (fun (p,_,_) -> p) starts in
      let labels_of_instr = test.Test_herd.entry_points in
      let exported_labels = S.get_exported_labels test in
      let is_exported_label lbl =
        Label.Full.Set.exists
          (fun (_,lbl0) -> Misc.string_eq lbl lbl0)
           exported_labels in

(**********************************************************)
(* In mode `-variant self` test init_state is changed:    *)
(*  1. Normalize labels in values                         *)
(*  2. Add initialisation of overwritable instructions    *)
(* Labels have to be normalized because only one memory   *)
(* location holds the instruction that can be pointed to  *)
(* by several labels. Read and write events *must* use a  *)
(* canonical location (label).                            *)
(**********************************************************)

      (* lbls2i -- overwritable instructions, with labels          *)
      (* overwritable_labels -- the set of labels of instructions  *)
      (*                        that are allowed to be overwritten *)
      let lbls2i, overwritable_labels =
        if self then
          List.fold_left
            (fun (ps, ls)  (proc,code,fh_code) ->
              let code = match fh_code with
                | Some fh_code -> code@fh_code
                | None -> code in
              List.fold_left
                (fun (ps, ls) (addr,i) ->
                  let lbls = labels_of_instr addr in
                  if Label.Set.exists is_exported_label lbls
                  then
                      (lbls,(proc,i))::ps,
                      Label.Set.union lbls ls
                  else ps,ls)
                (ps,ls) code)
            ([], Label.Set.empty) starts
        else (* For Adr to work *)
          let lbl2code lbl =
            IntMap.find (Label.Map.find lbl prog) code_segment in
          let ps =
            Label.Full.Set.fold
              (fun (_,lab) ps ->
                try begin
                  match lbl2code lab with
                  | (p,(_,i)::_) ->
                      (Label.Set.singleton lab,(p,i))::ps
                  | (_,[]) -> ps
                end with Not_found -> ps)
              exported_labels [] in
          ps,Label.Set.empty in
      let norm_lbl = (* Normalize labels, useful in `-variant self` *)
        if self then
          let m =
            List.fold_left
              (fun m (lbls,_) ->
                match Label.norm lbls with
                | None -> assert false (* as lbls is non-empty *)
                | Some lbl0 ->
                    Label.Set.fold
                      (fun lbl m -> Label.Map.add lbl lbl0 m)
                      lbls m)
              Label.Map.empty lbls2i in
          fun lbl ->
            try Label.Map.find lbl m
            with
            | Not_found ->
                if Label.Map.mem lbl prog then
                  lbl
                else
                  Warn.user_error
                    "Label %s is undefined, yet it is used as constant"
                    (Label.pp lbl)
        else
          (* Normalisation reduced to label existence check *)
          fun lbl -> ignore (Label.Map.find lbl prog) ; lbl in

      let norm_val = (* Normalize labels in values *)
        if self then
          fun v ->
            match v with
            | V.Val c -> V.Val (Constant.map_label norm_lbl c)
            | _ -> v
        else fun v -> v in

      let test =
        match lbls2i with
        | [] -> test
        | _::_ ->
            let open Test_herd in
            let init_state =
              (* Change labels into their canonical representants *)
              A.map_state norm_val test.init_state in
            let init_state =
              (* Add initialisation of overwritable instructions *)
              List.fold_left
                (fun env (lbls,(proc,i)) ->
                  match Label.norm lbls with
                  | None -> assert false (* as lbls is non-empty *)
                  | Some lbl ->
                      let loc =
                        A.Location_global
                          (A.V.cstToV (Constant.Label (proc, lbl)))
                      and v = A.V.instructionToV i in
                      A.state_add env loc v)
                init_state lbls2i in
            { test with init_state; } in

(*****************************************************)
(* Build events monad, _i.e._ run code in some sense *)
(*****************************************************)

(* Manage labels *)
      let see seen lbl =
        let x = try IntMap.find lbl seen with Not_found -> 0 in
        let x = x+1 in
        let seen = IntMap.add lbl x seen in
        x,seen in

      let get_label lbl addr = match lbl with
        | Some lbl -> lbl
        | None ->
           let lbls = labels_of_instr addr in
           match Label.norm lbls with
           | Some lbl -> lbl
           | None -> sprintf "##%d" addr in

      let tgt2lbl = function
        | B.Lbl lbl -> lbl
        | B.Addr addr -> get_label None addr in

    let fetch_addr check_back seen proc_jmp addr_jmp lbl addr =
        try
          let (proc_tgt,_) as tgt = IntMap.find addr code_segment in
          if (* Limit jump threshold to non determined jumps ? *)
            Misc.int_eq proc_jmp proc_tgt
            && check_back
            && is_back_jump addr_jmp addr
          then
            let x,seen = see seen addr in
            if x > unroll then begin
                W.warn "loop unrolling limit reached: %s" (get_label lbl addr);
                No tgt
              end else
              Ok (tgt,seen)
          else
            Ok (tgt,seen)
        with Not_found ->
            Segfault addr in

      let fetch_code check_back seen proc_jmp addr_jmp = function
        | B.Lbl lbl ->
           begin try
             let addr = Label.Map.find  lbl prog in
             fetch_addr check_back seen proc_jmp addr_jmp (Some lbl) addr
           with Not_found -> segfault lbl
           end
        | B.Addr addr ->
           fetch_addr check_back seen proc_jmp addr_jmp None addr in

      (* All memory locations in a test, with initial values *)
      let env0 = get_all_mem_locs test in

        let addr2v proc s =
          try (* Look for label to overwritable instruction *)
            V.Val (Constant.Label (proc,norm_lbl s))
          with
            e -> (* No, look for data location *)
              let v =  A.V.nameToV s in
              if
                List.exists
                  (fun (a,_) ->
                    match a with
                    | A.Location_global a -> A.V.compare v a=0
                    | _ -> false)
                  env0
              then v else (* No code nor data, check error *)
                match e with
                | Not_found ->
                    Warn.user_error
                      " Symbol %s is not a code label nor a location"
                      s
                | e -> raise e in

(* Call instruction semantics proper *)
        let wrap re_exec fetch_proc proc inst addr env m poi =
        let ii =
           { A.program_order_index = poi;
             proc = proc; fetch_proc; inst = inst;
             labels = labels_of_instr addr;
             lbl2addr = prog;
             addr = addr;
             addr2v=addr2v proc;
             env = env;
             in_handler = re_exec;
           } in
        if dbg then
          Printf.eprintf "%s env=%s\n"
            (A.dump_instruction ii.A.inst)
            (A.pp_reg_state ii.A.env.A.regs) ;
        if dbg && not (Label.Set.is_empty ii.A.labels) then
          eprintf "Instruction %s has labels {%s}\n"
            (A.dump_instruction inst)
            (Label.Set.pp_str ","  Label.pp ii.A.labels) ;
        m ii in

      let  sem_instr =  SM.build_semantics test in
      let rec add_next_instr re_exec fetch_proc proc env seen addr inst nexts =
        wrap re_exec fetch_proc proc inst addr env sem_instr >>> fun branch ->
          let { A.regs=env; lx_sz=szo; fh_code } = env in
          let env = A.kill_regs (A.killed inst) env
          and szo =
            let open MachSize in
            match A.get_lx_sz inst with
            | No -> szo
            | St -> None
            | Ld sz -> Some sz in
          let env =
            match branch with
            | S.B.Next bds|S.B.Jump (_,bds)|S.B.IndirectJump (_,_,bds)
            | B.Fault bds ->
                List.fold_right
                  (fun (r,v) -> A.set_reg r v)
                  bds env
            | _ -> env in
          next_instr
            re_exec inst fetch_proc proc { A.regs=env; lx_sz=szo; fh_code}
            seen addr nexts branch

      and add_code re_exec fetch_proc proc env seen nexts = match nexts with
      | [] -> EM.unitcodeT true
      | (addr,inst)::nexts ->
          add_next_instr re_exec fetch_proc proc env seen addr inst nexts

      and add_lbl re_exec check_back proc env seen addr_jmp lbl =
        add_tgt re_exec check_back proc env seen addr_jmp (B.Lbl lbl)

      and add_tgt re_exec check_back proc env seen addr_jmp tgt =
        match fetch_code check_back seen proc addr_jmp tgt with
        | No (tgt_proc,(addr,inst)::_) ->
            let m ii =
              EM.addT
                (A.next_po_index ii.A.program_order_index)
                (EM.cutoffT (tgt2lbl tgt) ii (S.B.Next [])) in
            wrap
              re_exec tgt_proc proc inst addr env m >>> fun _ -> EM.unitcodeT true
        | No (_,[]) -> assert false (* Backward jump cannot be to end of code *)
        | Ok ((tgt_proc,code),seen) -> add_code re_exec tgt_proc proc env seen code
        | Segfault addr ->
          let msg = Printf.sprintf
            "Segmentation fault (kidding, address 0x%x does not point to code)"
            addr in
          EM.failcodeT (Misc.UserError msg) true

      and add_fault re_exec inst fetch_proc proc env seen addr nexts =
        match env.A.fh_code,re_exec with
        | Some _, true ->
           let e = "Fault inside a fault handler" in
           EM.warncodeT e true
        | Some fh_code, false ->
           add_code true fetch_proc proc env seen fh_code
        | None, true ->
           EM.unitcodeT false
        | None, false ->
           let open Fault.Handling in
           match C.fault_handling with
           | Fatal -> EM.unitcodeT true
           | Skip -> add_code false fetch_proc proc env seen nexts
           | Handled ->
              add_next_instr true fetch_proc proc env seen addr inst nexts

      and next_instr re_exec inst fetch_proc proc env seen addr nexts b =
        match b with
      | S.B.Exit -> EM.unitcodeT true
      | S.B.Next _ ->
          add_code re_exec fetch_proc proc env seen nexts
      | S.B.Jump (tgt,_) ->
          add_tgt re_exec true proc env seen addr tgt
      | S.B.Fault _ ->
          add_fault re_exec inst fetch_proc proc env seen addr nexts
      | S.B.FaultRet tgt ->
          add_tgt false true proc env seen addr tgt
      | S.B.CondJump (v,tgt) ->
          EM.condJumpT v
            (add_tgt re_exec (not (V.is_var_determined v))
               proc env seen addr tgt)
            (add_code re_exec fetch_proc proc env seen nexts)
      | S.B.IndirectJump (v,lbls,_) ->
         EM.indirectJumpT v lbls
           (add_lbl re_exec true proc env seen addr)
      in

(* Code monad returns a boolean. When false the code must be discarded.
   See also add_instr in eventsMonad.ml *)
      let jump_start proc env code =
        add_code false proc proc env IntMap.empty code in

(* As name suggests, add events of one thread *)
      let add_events_for_a_processor env (proc,code,fh_code) evts =
        let env =
          if A.opt_env then A.build_reg_state proc A.reg_defaults env
          else A.reg_state_empty in
        let () =
          if dbg then
            Printf.eprintf  "Init reg state for proc %s: %s\n%!"
              (Proc.pp proc) (A.pp_reg_state env) in
        let evts_proc =
          jump_start proc { A.regs=env; lx_sz=None; fh_code } code in
        evts_proc |*| evts
      in

(* Initial events, some additional events from caller in madd *)
      let make_inits madd env size_env =
        let module MI = EM.Mixed(C) in
        if C.initwrites then
          MI.initwrites madd env size_env
        else
          EM.zerocodeT
      in

(* Build code monad for one given set of events to add *)
      let set_of_all_instr_events madd =
        List.fold_right
          (add_events_for_a_processor test.Test_herd.init_state)
          starts
          (make_inits madd env0 test.Test_herd.size_env) in

      let transitive_po es =
        let r,e = es.E.po in
        (r,E.EventRel.transitive_closure e) in

      let af0 = (* locations with initial spurious update *)
        if
          match C.dirty with
          | None -> false
          | Some t -> t.DirtyBit.some_ha || t.DirtyBit.some_hd
        then begin (* One spurious update per observed pte (final load) *)
            if C.variant Variant.PhantomOnLoad then
              let look_pt rloc k = match rloc with
                | ConstrGen.Loc (A.Location_global (V.Val c as vloc))
                when Constant.is_pt c -> vloc::k
                | _ -> k in
              A.RLocSet.fold look_pt test.Test_herd.observed []
            else (* One spurious update per variable not accessed initially *)
              let add_setaf0 k (loc,v) =
                match loc with
                | A.Location_global (V.Val c as vloc) ->
                   if Constant.is_pt c then
                     match v with
                     | V.Val (Constant.PteVal p)
                           when not (V.Cst.PteVal.is_af p) ->
                        vloc::k
                     | _ -> k
                   else k
                | _ -> k in
              List.fold_left add_setaf0 [] env0
        end else [] in

      let rec index xs i = match xs with
      | [] ->
          W.warn "%i abstract event structures\n%!" i ;
          []
      | (vcl,es)::xs ->
          let es = if C.debug.Debug_herd.monad then es else relabel es in
          let es =
            { es with E.procs = procs; E.po = if do_deps then transitive_po es else es.E.po } in
          (i,vcl,es)::index xs (i+1) in
      let r =
        Misc.fold_subsets_gen
          (fun vloc -> EM.(|||) (SM.spurious_setaf vloc))
          (EM.unitT ()) af0
          (fun maf0 ->
            EM.get_output (set_of_all_instr_events (EM.(|||) maf0)))
          [] in
      let r = match C.maxphantom with
        | None -> r
        | Some max ->
           let count_spurious es =
             E.EventSet.fold
               (fun e k ->
                 if E.is_load e && E.is_spurious e then k+1 else k)
               es 0 in
           List.filter
             (fun (_,es) -> count_spurious es.E.events <= max)
             r in
      { event_structures=index r 0; overwritable_labels; },test


(*******************)
(* Rfmap generator *)
(*******************)


(* Step 1. make rfmap for registers and reservations *)


let get_loc e = match E.location_of e with
| Some loc -> loc
| None -> assert false

and get_read e = match E.read_of e  with
| Some v -> v
| None -> assert false

and get_written e = match E.written_of e with
| Some v -> v
| None -> assert false




(* Add (local) final edges in rfm, ie for all (register) location, find the last (po+iico) store to it *)

let add_finals es =
    U.LocEnv.fold
      (fun loc stores k ->
        let stores =
          List.filter
            (fun x -> not (E.EventSet.mem x (es.E.speculated))) stores in
      match stores with
      | [] -> k
      | ew::stores ->
          let last =
            List.fold_right
              (fun ew0 ew ->
                if U.is_before_strict es ew0 ew then ew
                else begin
                  (* If writes to a given register by a given thread
                     are not totally ordered, it gets weird to define
                     the last or 'final'register write *)
                  if not (U.is_before_strict es ew ew0) then
                    Warn.fatal
                      "Ambiguous po for register %s" (A.pp_location loc) ;
                  ew0
                end)
              stores ew in
          S.RFMap.add (S.Final loc) (S.Store last) k)

(*******************************)
(* Compute rfmap for registers *)
(*******************************)

let map_loc_find loc m =
  try U.LocEnv.find loc m
  with Not_found -> []

let match_reg_events es =
  let loc_loads = U.collect_reg_loads es
  and loc_stores = U.collect_reg_stores es
  (* Share computation of the iico relation *)
  and is_before_strict =  U.is_before_strict es in

(* For all loads find the right store, the one "just before" the load *)
  let rfm =
    U.LocEnv.fold
      (fun loc loads k ->
        let stores = map_loc_find loc loc_stores in
        List.fold_right
          (fun er k ->
            let rf =
              List.fold_left
                (fun rf ew ->
                  if is_before_strict ew er then
                    match rf with
                    | S.Init -> S.Store ew
                    | S.Store ew0 ->
                        if U.is_before_strict es ew0 ew then
                          S.Store ew
                        else begin
                          (* store order is total *)
                            if not (is_before_strict ew ew0) then begin
                              Printf.eprintf "Not ordered stores %a and %a\n"
                                E.debug_event ew0
                                E.debug_event ew ;
                              assert false
                            end ;
                          rf
                        end
                  else rf)
                S.Init stores in
            S.RFMap.add (S.Load er) rf k)
          loads k)
      loc_loads S.RFMap.empty in
(* Complete with stores to final state *)
  add_finals es loc_stores rfm



    let get_rf_value test read rf = match rf with
    | S.Init ->
        let loc = get_loc read in
        let look_address =
          A.look_address_in_state test.Test_herd.init_state in
        begin
          try look_address loc with A.LocUndetermined -> assert false
        end
    | S.Store e -> get_written e

(* Add a constraint for two values *)

(* Optimization: adding constraint v1 := v2 should always work *)

    exception Contradiction

    let add_eq v1 v2 eqs =
      if V.is_var_determined v1 then
        if V.is_var_determined v2 then
          if V.equal v1 v2 then eqs
          else raise Contradiction
        else (* Here, v1 and v2 necessarily differ *)
          VC.Assign (v2, VC.Atom v1)::eqs
      else if V.equal v1 v2 then eqs
      else VC.Assign (v1, VC.Atom v2)::eqs

    let pp_nosol lvl test es rfm =
      let module PP = Pretty.Make(S) in
      eprintf "No solution at %s level\n%!" lvl;
      PP.show_es_rfm test es rfm ;
      ()

    let solve_regs test es csn =
      let rfm = match_reg_events es in
      let csn =
        S.RFMap.fold
          (fun wt rf csn -> match wt with
          | S.Final _ -> csn
          | S.Load load ->
              let v_loaded = get_read load in
              let v_stored = get_rf_value test load rf in
              try add_eq v_loaded v_stored csn
              with Contradiction ->
                let loc = Misc.as_some (E.location_of load) in
                Printf.eprintf
                  "Contradiction on reg %s: loaded %s vs. stored %s\n"
                  (A.pp_location loc)
                  (A.V.pp_v v_loaded)
                  (A.V.pp_v v_stored) ;
                assert false)
          rfm csn in
      if  C.debug.Debug_herd.solver then
        prerr_endline "++ Solve  registers" ;
      match VC.solve csn with
      | VC.NoSolns ->
         if C.debug.Debug_herd.solver then
           pp_nosol "register" test es rfm ;
         None
      | VC.Maybe (sol,csn) ->
          Some
            (E.simplify_vars_in_event_structure sol es,
             S.simplify_vars_in_rfmap sol rfm,
             csn)

(**************************************)
(* Step 2. Generate rfmap for memory  *)
(**************************************)

    let get_loc_as_value e = match  E.global_loc_of e with
    | None -> eprintf "%a\n" E.debug_event e ; assert false
    | Some v -> v

(* Compatible location are:
   - either both determined and equal,
   - or at least one location is undetermined. *)
    let compatible_locs_mem e1 e2 =
      E.event_compare e1 e2 <> 0 && (* C RMWs cannot feed themselves *)
      E.compatible_accesses e1 e2 &&
      begin
        let loc1 = get_loc e1
        and loc2 = get_loc e2 in
        let ov1 =  A.undetermined_vars_in_loc_opt loc1
        and ov2 =  A.undetermined_vars_in_loc_opt loc2 in
        match ov1,ov2 with
        | None,None -> E.same_location e1 e2
        | (Some _,None)|(None,Some _)
        | (Some _,Some _) -> true
      end

(* Add a constraint for a store/load match *)
    let pp_init load =
      if dbg then
        eprintf "Add eq load=%a, store=Init\n%!"
          E.debug_event load

    let pp_add load store =
      if dbg then
        eprintf "Add eq load=%a, store=%a\n%!"
          E.debug_event load
          E.debug_event store

    let add_mem_eqs test rf load eqs =
      let v_loaded = get_read load in
      match rf with
      | S.Init -> (* Tricky, if location (of load) is
                     not know yet, emit a specific constraint *)
          let state = test.Test_herd.init_state
          and loc_load = get_loc load in
          pp_init load  ;
          begin try
            let v_stored = A.look_address_in_state state loc_load in
            add_eq v_stored  v_loaded eqs
          with A.LocUndetermined ->
            VC.Assign
              (v_loaded, VC.ReadInit (loc_load,state))::eqs
          end
      | S.Store store ->
          pp_add load store ;
          add_eq v_loaded (get_written store)
            (add_eq
               (get_loc_as_value store)
               (get_loc_as_value load) eqs)

(* Our rather loose rfmaps can induce a cycle in
   causality. Check this. *)
    let rfmap_is_cyclic es rfm =
      let iico = E.iico es in
      let causality =
        S.RFMap.fold
          (fun load store k -> match load,store with
          | S.Load er,S.Store ew -> E.EventRel.add (ew,er) k
          | _,_ -> k)
          rfm iico in
      match E.EventRel.get_cycle causality with
      | None -> prerr_endline "no cycle"; false
      | Some cy ->
          if C.debug.Debug_herd.rfm then begin
            let debug_event chan e = fprintf chan "%i" e.E.eiid in
            eprintf "cycle = %a\n" debug_event
              (match cy with e::_ -> e | [] -> assert false)
          end; true

(* solve_mem proper *)


(* refrain from being subtle: match a load with all compatible
   stores, and there may be many *)

(* First consider loads from init, in the initwrite case
   nothing to consider, as the initial stores should present
   as events *)
    let init = if C.initwrites then [] else [S.Init]
    let map_load_init loads =
      E.EventSet.fold
        (fun load k -> (load,init)::k)
        loads []

(*condition:
  soit le load est specule, auquel cas il peut lire de partout;
  soit le load n'est pas specule, auquel cas il ne peut pas lire de stores specules*)

    let check_speculation es store load =
      let spec = es.E.speculated in
      E.EventSet.mem load spec ||
      not (E.EventSet.mem store spec)

    let is_spec es e = E.EventSet.mem e es.E.speculated

(* Consider all stores that may feed a load
   - Compatible location.
   - Not after in program order
    (suppressed when uniproc is not optmised early) *)

    let map_load_possible_stores test es rfm loads stores compat_locs =
      let ok = match C.optace with
        | OptAce.False -> fun _ _ -> true
        | OptAce.True ->
           let pred = U.is_before_strict es
           and iico = U.iico es in
           fun er ew ->
           not
             (E.is_explicit er && E.is_explicit ew && pred er ew
              || E.EventRel.mem (er,ew) iico)
        | OptAce.Iico ->
           let iico = U.iico es in
           fun load store -> not (E.EventRel.mem (load,store) iico) in
      let m =
        E.EventSet.fold
          (fun store map_load ->
            List.map
              (fun ((load,stores) as c) ->
                if
                  compat_locs store load &&
                  check_speculation es store load &&
                  ok load store
                then
                  load,S.Store store::stores
                else c)
              map_load)
          stores (map_load_init loads) in
      if dbg then begin
        let pp_read_froms chan rfs =
          List.iter
            (fun rf -> match rf with
            | S.Init -> fprintf chan "Init"
            | S.Store e -> E.debug_event chan e ; fprintf chan ",")
            rfs in
        List.iter
          (fun (load,stores) ->
            eprintf "Pairing {%a} with {%a}\n"
              E.debug_event load
              pp_read_froms stores)
          m
      end ;
(* Check for loads that cannot feed on some write *)
      if not do_deps && not asl then begin
        List.iter
          (fun (load,stores) ->
            match stores with
            | [] ->
                begin match E.location_of load with
                | Some loc ->
                    begin match A.symbol loc with
                    | Some sym ->
                        if S.is_non_mixed_symbol test sym then
                          Warn.fatal
                            "read on location %s does not match any write"
                        (A.pp_location loc)
                        else if check_mixed then
                          Warn.user_error
                            "mixed-size test rejected (symbol %s), consider option -variant mixed"
                            (A.pp_location loc)
                    | None ->
                       if dbg then begin
                        let module PP = Pretty.Make(S) in
                        eprintf
                          "Failed to find at least one write for load %a\n%!"
                          E.debug_event load ;
                        PP.show_es_rfm test es rfm
                       end ;
                       Warn.fatal
                         "Non symbolic location with no initial write: '%s'\n"
                         (A.pp_location loc)
                    end
                | _ -> assert false
                end
            | _::_ -> ())
          m
        end ;
      m

(* Add memory events to rfmap *)
    let add_mem =
      List.fold_right2
        (fun er rf -> S.RFMap.add (S.Load er) rf)

    let add_some_mem =
      List.fold_right2
        (fun er rf ->  match rf with
        | None -> fun rfm -> rfm
        | Some rf -> S.RFMap.add (S.Load er) rf)


    let add_mems =
      List.fold_right2
        (List.fold_right2 (fun r w ->  S.RFMap.add (S.Load r) (S.Store w)))

    let solve_mem_or_res test es rfm cns kont res loads stores compat_locs add_eqs =
      let possible =
        map_load_possible_stores test es rfm loads stores compat_locs in
      let possible =
        List.map
          (fun (er,ws) ->
            let ws = List.map (fun w -> Some w) ws in
            (* Add reading from nowhere for speculated reads *)
            let ws = if is_spec es er then None::ws else ws in
            er,ws)
          possible in
      let loads,possible_stores =  List.split possible in
      (* Cross product fold. Probably an overkill here *)
      Misc.fold_cross possible_stores
        (fun stores res ->
          (* stores is a list of stores that may match the loads list.
             Both lists in same order [by List.split above]. *)
          try
            (* Add constraints now *)
            if dbg then eprintf "Add equations\n" ;
            let cns =
              List.fold_right2
                (fun load rf k -> match rf with
                | None -> k (* No write, no equation *)
                | Some rf -> add_eqs test rf load k)
                loads stores cns in
            if dbg then eprintf "\n%!" ;
            (* And solve *)
            if C.debug.Debug_herd.solver then
              prerr_endline "++ Solve memory" ;
            match VC.solve cns with
            | VC.NoSolns ->
               if C.debug.Debug_herd.solver then begin
                 let rfm = add_some_mem loads stores rfm in
                 pp_nosol "memory" test es rfm
               end ;
               res
            | VC.Maybe (sol,cs) ->
                (* Time to complete rfmap *)
                let rfm = add_some_mem loads stores rfm in
                (* And to make everything concrete *)
                let es = E.simplify_vars_in_event_structure sol es
                and rfm = S.simplify_vars_in_rfmap sol rfm in
                kont es rfm cs res
          with
          | Contradiction ->  (* May  be raised by add_mem_eqs *)
             if C.debug.Debug_herd.solver then
               begin
                 let rfm = add_some_mem loads stores rfm in
                 pp_nosol "memory" test es rfm
               end ;
             res
          | e ->
              if C.debug.Debug_herd.top then begin
                eprintf "Exception: %s\n%!" (Printexc.to_string e) ;
                let module PP = Pretty.Make(S) in
                let rfm = add_some_mem loads stores rfm in
                PP.show_es_rfm test es rfm
              end ;
              raise e
        )
        res

    let when_unsolved test es rfm _cs res =
      (* This system in fact has no solution.
         In other words, it is not possible to make
         such event structures concrete.
         This occurs with cyclic rfmaps *)
      if C.debug.Debug_herd.solver then begin
          let module PP = Pretty.Make(S) in
          prerr_endline "Unsolvable system" ;
          PP.show_es_rfm test es rfm ;
        end ;
      assert (true || rfmap_is_cyclic es rfm);
      res

    let solve_mem_non_mixed test es rfm cns kont res =
      let compat_locs = compatible_locs_mem in
      if self then
        let code_store e =
        E.is_store e &&
        match
          Misc.seq_opt A.global (E.location_of e)
        with
        | Some (V.Val (Constant.Label _)|V.Var _) -> true
        | Some _|None -> false in
        (* Select code accesses *)
        let code_loads =
          E.EventSet.filter E.is_ifetch es.E.events
        and code_stores =
          E.EventSet.filter code_store es.E.events in
        let kont es rfm cns res =
          (* We get here once code accesses are solved *)
          let loads =  E.EventSet.filter E.is_mem_load es.E.events
          and stores = E.EventSet.filter E.is_mem_store es.E.events in
          let loads =
            (* Remove code loads that are now solved *)
            E.EventSet.diff loads code_loads in
          if dbg then begin
            eprintf "Left loads : %a\n"E.debug_events loads ;
            eprintf "All stores: %a\n"E.debug_events stores
          end ;
          solve_mem_or_res test es rfm cns kont res
            loads stores compat_locs add_mem_eqs in
        if dbg then begin
            eprintf "Code loads : %a\n"E.debug_events code_loads ;
            eprintf "Code stores: %a\n"E.debug_events code_stores
          end ;
        solve_mem_or_res test es rfm cns kont res
          code_loads code_stores compat_locs add_mem_eqs
      else
        let loads = E.EventSet.filter E.is_mem_load es.E.events
        and stores = E.EventSet.filter E.is_mem_store es.E.events in
        if dbg then begin
          eprintf "Loads : %a\n"E.debug_events loads ;
          eprintf "Stores: %a\n"E.debug_events stores
          end ;
        solve_mem_or_res test es rfm cns kont res
          loads stores compat_locs add_mem_eqs

(*************************************)
(* Mixed-size write-to-load matching *)
(*************************************)

    exception CannotSca

(* Various utilities on symbolic addresses as locations *)
    (* Absolute base of indexed symbol (i.e. array address) *)
    let get_base a =
      let open Constant in
      match A.symbolic_data a with
      | Some ({name=s;_} as sym) ->
          let s = if Misc.check_ctag s then Misc.tr_ctag s else s in
          A.of_symbolic_data {sym with name=s; offset=0;}
      | _ -> raise CannotSca

(* Sort same_base *)
    let compare_index e1 e2 =
      let open Constant in
      let loc1 = E.location_of e1 and loc2 = E.location_of e2 in
      match Misc.seq_opt A.symbolic_data loc1,
            Misc.seq_opt A.symbolic_data loc2
      with
      | Some {name=s1;offset=i1;_},Some {name=s2;offset=i2;_}
            when  Misc.string_eq s1 s2 ->
          Misc.int_compare i1 i2
      | Some {name=s1;_},Some {name=s2;_} when morello ->
          if Misc.check_ctag s1 && Misc.string_eq (Misc.tr_ctag s1) s2 then 1
          else if Misc.check_ctag s2 && Misc.string_eq s1 (Misc.tr_ctag s2) then -1
          else if Misc.check_ctag s1 && Misc.check_ctag s2 && Misc.string_eq s1 s2 then 0
          else raise CannotSca
      | _,_ -> raise CannotSca

    let sort_same_base es = List.sort compare_index es

    let debug_events out es = Misc.pp_list out " " E.debug_event es

    module Match
        (R:sig
          type read
          val compare_index : read -> S.event -> int
          val debug_read : out_channel -> read -> unit
        end) = struct

          let debug_reads out es = Misc.pp_list out " " R.debug_read es

          let rec inter rs0 ws0 = match rs0 with
          | [] -> [],[]
          | r::rs -> begin match ws0 with
            | [] -> rs0,[]
            | w::ws ->
                let c = R.compare_index r w in
                if c < 0 then
                  let rs,ws = inter rs ws0 in
                  r::rs,ws
                else if c > 0 then
                  let rs,ws = inter rs0 ws in
                  rs,ws
                else
                  let rs,ws = inter rs ws in
                  rs,w::ws
          end

          let rec all_ws rs ws wss =
            if dbg then
              eprintf "Trying [%a] on [%a]\n"
                debug_reads rs debug_events ws ;
            let rs_diff,ws_inter = inter rs ws in
            if dbg then
              eprintf "Found [%a] (remains [%a])\n"
                debug_events ws_inter debug_reads rs_diff ;
            match ws_inter with
            | [] -> next_all_ws rs wss
            | _  ->
                List.fold_right
                  (fun ws k -> (ws_inter@ws)::k)
                  (next_all_ws rs_diff wss)
                  (next_all_ws rs wss)

          and next_all_ws rs wss = match rs with
          | [] -> [[]]
          | _  ->
              begin match wss with
              | [] -> []
              | ws::wss ->  all_ws rs ws wss
              end

          let find_rfs_sca s rs wss = match next_all_ws rs wss with
          | _::_ as wss -> List.map sort_same_base wss
          | [] -> begin match rs with
            | [] -> assert false
            | _::_ -> Warn.user_error "out-of-bound access on %s" s
          end
        end

    module MatchRf =
      Match
        (struct
          type read = S.event
          let compare_index = compare_index
          let debug_read = E.debug_event
        end)

    let byte_sz = MachSize.nbytes C.byte

    let expose_sca es sca =
      let open Constant in
      let sca = E.EventSet.elements sca in
      let sca = sort_same_base sca in
      let fst = match sca with
      | e::_ -> e
      | [] -> assert false in
      let s,idx= match  E.global_loc_of fst with
      |  Some (V.Val (Symbolic (Virtual {name=s; offset=i;_})))
         ->
           (if morello && Misc.check_ctag s then Misc.tr_ctag s else s),i
      | _ -> raise CannotSca in
      let sz = List.length sca*byte_sz in
      is_spec es fst,E.get_mem_dir fst,s,idx,sz,sca

    let expose_scas es =
      let scas = es.E.sca in
      let ms =
        E.EventSetSet.fold
          (fun sca k -> expose_sca es sca::k)
          scas [] in
      let rs,ws =
        List.fold_left
          (fun (rs,ws) (g,d,x,idx,sz,es) -> match d with
          | Dir.W ->
              let old = StringMap.safe_find [] x ws in
              rs,StringMap.add x ((g,idx,sz,es)::old) ws
          | Dir.R -> (g,x,idx,sz,es)::rs,ws)
          ([],StringMap.empty) ms in
      let ws =
        StringMap.map
          (List.sort (fun (_,_,sz1,_) (_,_,sz2,_) -> Misc.int_compare sz1 sz2))
          ws in
      let ms =
        List.map
          (fun (gr,x,i,sz,rs) ->
            let ws = try StringMap.find x ws with Not_found -> assert false in (* Because of init writes *)
            let ws =
              List.filter
                (fun (gw,i_w,sz_w,_) ->
                  not (not gr && gw) &&
                  (* non-ghost reads cannot read from ghost writes *)
                  i+sz >= i_w && i < i_w+sz_w
                    (* write and read intersects *))
                ws in
            x,rs,List.map (fun (_,_,_,ws) -> ws) ws)
          rs in
      let ms =
        List.map
          (fun (x,rs,wss) -> rs,MatchRf.find_rfs_sca x rs wss)
          ms in
      if C.debug.Debug_herd.solver || C.debug.Debug_herd.mem then begin
        eprintf "+++++++++++++++++++++++\n" ;
        List.iter
          (fun (rs,wss) -> eprintf "[%a] ->\n" debug_events rs ;
            List.iter
              (fun ws ->  eprintf "    [%a]\n" debug_events ws)
              wss)
          ms ;
        flush stderr
      end ;
      ms

(* Non-mixed pairing for tags, if any *)
    let pair_tags test es rfm =
      let tags = E.EventSet.filter E.is_tag es.E.events in
      let loads = E.EventSet.filter E.is_load tags
      and stores = E.EventSet.filter E.is_store tags in
      let m =
        map_load_possible_stores test es rfm loads stores compatible_locs_mem in
      m

    let solve_mem_mixed test es rfm cns kont res =
      let match_tags = if morello then []
        else pair_tags test es rfm in
      let tag_loads,tag_possible_stores = List.split match_tags in
      let ms = expose_scas es in
      let rss,wsss = List.split ms in
      (* Cross product fold. Probably an overkill here *)
      Misc.fold_cross wsss
        (fun wss res ->
          (* Add memory constraints now *)
          try
            let cns =
              List.fold_right2
                (fun rs ws eqs ->
                  List.fold_right2
                    (fun r w eqs ->
                      assert (E.same_location r w) ;
                      add_eq (get_read r) (get_written w) eqs)
                    rs ws eqs)
                rss wss cns in
            Misc.fold_cross tag_possible_stores
              (fun tag_stores res ->
                (* Add tag memory constraints *)
                try
                  let cns =
                    List.fold_right2
                      (fun load store k -> add_mem_eqs test store load k)
                      tag_loads tag_stores cns in
                  (* And solve *)
                  match VC.solve cns with
                  | VC.NoSolns -> res
                  | VC.Maybe (sol,cs) ->
                      (* Time to complete rfmap *)
                      let rfm = add_mems rss wss rfm in
                      let rfm = add_mem tag_loads tag_stores rfm in
                      (* And to make everything concrete *)
                      let es = E.simplify_vars_in_event_structure sol es
                      and rfm = S.simplify_vars_in_rfmap sol rfm in
                      kont es rfm cs res
                with
                | Contradiction -> res  (* can be raised by add_mem_eqs *)
                | e ->
                    if C.debug.Debug_herd.top then begin
                      eprintf "Exception: %s\n%!" (Printexc.to_string e) ;
                      let module PP = Pretty.Make(S) in
                      let rfm = add_mems rss wss rfm in
                      PP.show_es_rfm test es rfm
                    end ;
                    raise e)
              res
          with Contradiction -> res)   (* can be raised by add_eq *)
        res

    let solve_mem test es rfm cns kont res =
      try
        if mixed && not C.debug.Debug_herd.mixed then solve_mem_mixed test es rfm cns kont res
        else solve_mem_non_mixed  test es rfm cns kont res
      with
      | CannotSca ->
         solve_mem_non_mixed test es rfm cns kont res


(*************************************)
(* Final condition invalidation mode *)
(*************************************)

    module CM = S.Cons.Mixed(C)

(* Internal filter *)
    let check_filter test fsc = match test.Test_herd.filter with
    | None -> true
    | Some p ->
        not C.check_filter ||
          CM.check_prop p (S.type_env test) (S.size_env test) fsc

(*************************************)
(* Final condition invalidation mode *)
(*************************************)

(*
  A little optimisation: we check whether the existence/non-existence
  of some vo would help in validation/invalidating the constraint
  of the test.

  If no, not need to go on
 *)

    module T = Test_herd.Make(S.A)

    let final_is_relevant test fsc =
      let open ConstrGen in
      let cnstr = T.find_our_constraint test in
      let senv = S.size_env test
      and tenv = S.type_env test in
      let check_prop p = CM.check_prop p tenv senv fsc in
      match cnstr with
        (* Looking for 'Allow' witness *)
      | NotExistsState p | ExistsState p -> check_prop p
            (* Looking for witness that invalidates 'Require' *)
      | ForallStates p -> not (check_prop p)
            (* Looking for witness that invalidates 'Forbid' *)


    let worth_going test fsc = match C.speedcheck with
    | Speed.True|Speed.Fast -> final_is_relevant test fsc
    | Speed.False -> true

(***************************)
(* Rfmap full exploitation *)
(***************************)

(* final state *)
    let tr_physical =
      let open Constant in
      if kvm then
        (function
         | A.Location_global (V.Val (Symbolic (Physical (s,idx)))) ->
             let sym = { default_symbolic_data with name=s; offset=idx; } in
             A.of_symbolic_data sym
         | A.Location_global (V.Val (Symbolic (TagAddr (PHY,s,o)))) ->
             A.Location_global (V.Val (Symbolic (TagAddr (VIR,s,o))))
         | loc -> loc)
      else
        Misc.identity

    let compute_final_state test rfm es =
      let st =
        S.RFMap.fold
          (fun wt rf k -> match wt,rf with
          | S.Final loc,S.Store ew ->
              A.state_add k (tr_physical loc) (get_written ew)
          | _,_ -> k)
          rfm test.Test_herd.init_state in
      st,
      if A.FaultAtomSet.is_empty test.Test_herd.ffaults && not !Opts.dumpallfaults then
        A.FaultSet.empty
      else
        E.EventSet.fold
          (fun e k ->
            match E.to_fault e with
            | Some f -> A.FaultSet.add f k
            | None -> k)
          es A.FaultSet.empty


(* View before relations easily available, from po_iico and rfmap *)


(* Preserved Program Order, per memory location - same processor *)
    let make_ppoloc po_iico_data es =
      let mem_evts = E.mem_of es in
      E.EventRel.of_pred mem_evts mem_evts
        (fun e1 e2 ->
          E.same_location e1 e2 &&
          E.EventRel.mem (e1,e2) po_iico_data)

(* Store is before rfm load successor *)
    let store_load rfm =
      S.RFMap.fold
        (fun wt rf k -> match wt,rf with
        | S.Load er,S.Store ew -> E.EventRel.add (ew,er) k
        | _,_ -> k)
        rfm E.EventRel.empty

(* Load from init is before all stores *)
    let init_load es rfm =
      let loc_stores = U.collect_stores es in
      S.RFMap.fold
        (fun wt rf k -> match wt,rf with
        | S.Load er,S.Init ->
            List.fold_left
              (fun k ew ->
                E.EventRel.add (er,ew) k)
              k (map_loc_find (get_loc er) loc_stores)
        | _,_ -> k)
        rfm E.EventRel.empty

(* Reconstruct load/store atomic pairs *)

    let make_atomic_load_store es =
      let all = E.atomics_of es.E.events in
      let atms = U.collect_atomics es in
      U.LocEnv.fold
        (fun _loc atms k ->
          let atms =
            List.filter
              (fun e -> not (E.is_load e && E.is_store e))
              atms in (* get rid of C RMW *)
          let rs,ws = List.partition E.is_load atms in
          List.fold_left
            (fun k r ->
              let exp = E.is_explicit r in
              List.fold_left
                (fun k w ->
                  if
                    S.atomic_pair_allowed r w &&
                    U.is_before_strict es r w &&
                    E.is_explicit w = exp &&
                    not
                      (E.EventSet.exists
                         (fun e ->
                           E.is_explicit e = exp &&
                           U.is_before_strict es r e &&
                           U.is_before_strict es e w)
                         all)
                  then E.EventRel.add (r,w) k
                  else k)
                k ws)
            k rs)
        atms E.EventRel.empty


(* Retrieve last store from rfmap *)
    let get_max_store _test _es rfm loc =
      try match S.RFMap.find (S.Final loc) rfm with
      | S.Store ew -> Some ew
      | S.Init -> None       (* means no store to loc *)
      with Not_found -> None
(*
  let module PP = Pretty.Make(S) in
  eprintf "Uncomplete rfmap: %s\n%!" (A.pp_location loc) ;
  PP.show_es_rfm test es rfm ;
  assert false
 *)
(* Store to final state comes last *)
    let last_store test es rfm =
      let loc_stores = U.collect_stores_non_spec es
      and loc_loads = U.collect_loads_non_spec es in
      U.LocEnv.fold
        (fun loc ws k ->
          match get_max_store test es rfm loc with
          | None -> k
          | Some max ->
              let loads = map_loc_find loc loc_loads in
              let k =
                List.fold_left
                  (fun k er ->
                    if E.event_equal er max then k (* possible with RMW *)
                    else match S.RFMap.find (S.Load er) rfm with
                    | S.Init -> E.EventRel.add (er,max) k
                    | S.Store my_ew ->
                        if E.event_equal my_ew max then k
                        else E.EventRel.add (er,max) k)
                  k loads in
              List.fold_left
                (fun k ew ->
                  if E.event_equal ew max then k
                  else E.EventRel.add (ew,max) k)
                k ws)
        loc_stores E.EventRel.empty

    let keep_observed_loc =
      if kvm then
        let open Constant in
        fun loc -> match loc with
        | A.Location_global (V.Val (Symbolic (Physical _|TagAddr (PHY, _, _) as sym1))) ->
            let p oloc = match oloc with
            | A.Location_global (V.Val (Symbolic sym2)) ->
                Constant.virt_match_phy sym2 sym1
            | _ -> false in
            A.LocSet.exists p
        | _ -> A.LocSet.mem loc
      else A.LocSet.mem

    let pp_locations = A.LocSet.pp_str " " A.pp_location

    let all_finals_non_mixed test es =
      let loc_stores = U.remove_spec_from_map es (U.collect_mem_stores es) in
      let loc_stores =
        if C.observed_finals_only then
          let observed_locs =
            let locs = S.observed_locations test in
            if mixed then
              let senv = S.size_env test in
              A.LocSet.map_union
                (fun loc ->
                  let open Constant in
                  match loc with
                  | A.Location_global
                    (V.Val (Symbolic (Virtual {name=s;_})) as a)
                    ->
                      let eas = AM.byte_eas (A.look_size senv s) a in
                      A.LocSet.of_list
                        (List.map (fun a -> A.Location_global a) eas)
                  | _ -> A.LocSet.singleton loc)
                locs
            else if morello then
              A.LocSet.map_union
                (fun loc ->
                  let open Constant in
                  match loc with
                  | A.Location_global
                    (A.V.Val
                       (Symbolic
                          (Virtual ({name=s; offset=0; _} as sym))))
                        ->
                        A.LocSet.add
                          (A.of_symbolic_data {sym with name=Misc.add_ctag s})
                          (A.LocSet.singleton loc)
                    | _ -> A.LocSet.singleton loc)
                locs
            else locs in
          if C.debug.Debug_herd.mem then begin
            eprintf "Observed locs: {%s}\n" (pp_locations observed_locs)
          end ;
          U.LocEnv.fold
            (fun loc ws k ->
              if keep_observed_loc loc observed_locs then
                U.LocEnv.add loc ws k
              else k)
            loc_stores U.LocEnv.empty
        else loc_stores in

      let possible_finals =
        match C.optace with
        | OptAce.True ->
           U.LocEnv.fold
             (fun _loc ws k ->
               List.filter
                 (fun w ->
                   not (E.is_explicit w) ||
                   not
                     (List.exists
                        (fun w' ->
                          E.is_explicit w' && U.is_before_strict es w w') ws))
                 ws::k)
             loc_stores []
        | OptAce.False|OptAce.Iico ->
           U.LocEnv.fold (fun _loc ws k -> ws::k) loc_stores [] in
      if C.debug.Debug_herd.solver && Misc.consp possible_finals then begin
        eprintf "+++++++++ possible finals ++++++++++++++\n" ;
        List.iter
          (fun ws ->  eprintf "[%a]\n" debug_events ws)
          possible_finals ;
        flush stderr
      end ;
      List.map (fun ws -> List.map (fun w -> [w]) ws) (possible_finals)


    let rec compare_len xs ys = match xs,ys with
    | [],[] -> 0
    | [],_::_ -> -1
    | _::_,[] -> 1
    | _::xs,_::ys -> compare_len xs ys

    module MatchFinal =
      Match
        (struct

          type read = int

          let compare_index idx e =
            let open Constant in
            match Misc.seq_opt A.symbolic_data (E.location_of e) with
            | Some {name=s; offset=i; _} ->
                if Misc.check_ctag s then  Misc.int_compare idx max_int (* always -1 ??? *)
                else  Misc.int_compare idx i
            | _ -> assert false

          let debug_read out = fprintf out "%i"
        end)


    let all_finals_mixed test es =
      assert  C.observed_finals_only ;
      let locs = S.observed_locations test in
      let locs =  A.LocSet.filter A.is_global locs in
      let loc_wss =
        E.EventSetSet.fold
          (fun sca k ->
            let e = E.EventSet.choose sca in
            if E.is_store e && not (E.EventSet.mem e es.E.speculated) then match E.location_of e with
            | Some a ->
                let a0 = get_base a in
                let t = A.look_type (S.type_env test) a0 in
                let a =
                  match t with
                  | TestType.TyArray _ -> raise CannotSca
                  | _ -> a0 in
                if keep_observed_loc a locs then
                  let old = A.LocMap.safe_find [] a k in
                  A.LocMap.add a (sort_same_base (E.EventSet.elements sca)::old) k
                else k
            | _ -> assert false (* Only globals in sca *)
            else k)
          es.E.sca A.LocMap.empty in
      let wsss =
        A.LocMap.fold
          (fun loc wss k ->
            let wss = List.sort compare_len (List.map sort_same_base wss)
            and rs =
              let senv = S.size_env test
              and o = Misc.as_some (A.offset loc) in
              AM.byte_indices o (A.look_size_location senv loc) in
            let rs = if morello then rs@[max_int] else rs in
            MatchFinal.find_rfs_sca (A.pp_location loc) rs wss::k)
          loc_wss [] in

      if C.debug.Debug_herd.solver && Misc.consp wsss then begin
        eprintf "+++++++++ possible finals ++++++++++++++\n" ;
        List.iter
          (fun wss ->
            List.iter
              (fun ws ->  eprintf "[%a]\n" debug_events ws)
              wss ;
            eprintf "--------------\n")
          wsss ;
        flush stderr
      end ;
      wsss

    let fold_left_left f = List.fold_left (List.fold_left f)

    let all_finals test es =
      try
        if mixed && not C.debug.Debug_herd.mixed then
          all_finals_mixed test es
        else
          all_finals_non_mixed test es
      with CannotSca ->
        all_finals_non_mixed test es

    let some_same_rf_rmw rfm rmw =
      let loads = U.partition_events (E.EventRel.domain rmw) in
      List.exists
        (fun ers ->
          let rfs =
            E.EventSet.fold
              (fun er k ->
                try S.RFMap.find (S.Load er) rfm::k
                with Not_found -> assert false)
            ers [] in
          Misc.exists_pair S.read_from_equal rfs)
        loads

    let fold_mem_finals test es rfm ofail atomic_load_store kont res =
      (* We can build those now *)
      let evts = es.E.events in
      let po_iico = U.po_iico es in
      let partial_po = E.EventTransRel.to_implicitely_transitive_rel es.E.partial_po in
      let ppoloc = make_ppoloc po_iico evts in
      let store_load_vbf = store_load rfm
      and init_load_vbf = init_load es rfm in
(* Now generate final stores *)
      let possible_finals = all_finals test es in
      if C.debug.Debug_herd.mem then begin
        eprintf "Possible finals:\n" ;
        List.iter
          (fun wss ->
            (List.iter
               (fun ws ->
                 List.iter (eprintf " %a" E.debug_event) ws)
               wss ;
             eprintf "\n"))
          possible_finals
      end ;
(* Add final loads from init for all locations, cleaner *)
      let loc_stores = U.collect_stores es
      and loc_loads = U.collect_loads es in
      let rfm =
        U.LocEnv.fold
          (fun loc _rs k ->
            try
              ignore (U.LocEnv.find loc loc_stores) ;
              k
            with Not_found -> S.RFMap.add (S.Final loc) S.Init k)
          loc_loads rfm in
      try
        let pco0 =
          if C.initwrites then U.compute_pco_init es
          else E.EventRel.empty in
        (*jade: looks ok even in specul case: writes from init are before all
          other writes, including speculated writes*)
        let pco =
          match C.optace with
          | OptAce.False|OptAce.Iico -> pco0
          | OptAce.True ->
            (*jade: looks compatible with speculation, but there might be some
              unforeseen subtlety here so flagging it to be sure*)
             let ppoloc =
               E.EventRel.restrict_rel
                 (fun e1 e2 -> E.is_explicit e1 && E.is_explicit e2)
                 ppoloc in
               match U.compute_pco rfm ppoloc with
               | None -> raise Exit
               | Some pco -> E.EventRel.union pco0 pco in
(* Cross product *)
        Misc.fold_cross
          possible_finals
          (fun ws res ->
            if C.debug.Debug_herd.mem then begin
              eprintf "Finals:" ;
              List.iter
                (fun ws ->
                  List.iter
                    (fun e -> eprintf " %a"  E.debug_event e) ws)
                ws ;
              eprintf "\n";
            end ;

            let rfm =
              fold_left_left
                (fun k w ->
                  S.RFMap.add (S.Final (get_loc w)) (S.Store w) k)
                rfm ws in
            let fsc = compute_final_state test rfm es.E.events in
            if check_filter test fsc && worth_going test fsc then begin
              if C.debug.Debug_herd.solver then begin
                let module PP = Pretty.Make(S) in
                let fsc,_ = fsc in eprintf "Final rfmap, final state=%s\n%!" (S.A.dump_state fsc);
                PP.show_es_rfm test es rfm ;
              end ;
              let last_store_vbf = last_store test es rfm in
              let pco =
                E.EventRel.union pco
                  (U.restrict_to_mem_stores last_store_vbf) in
              if E.EventRel.is_acyclic pco then
                let conc =
                  {
                   S.str = es ;
                   rfmap = rfm ;
                   fs = fsc ;
                   po = po_iico ;
                   partial_po = partial_po;
                   pos = ppoloc ;
                   pco = pco ;

                   store_load_vbf = store_load_vbf ;
                   init_load_vbf = init_load_vbf ;
                   last_store_vbf = last_store_vbf ;
                   atomic_load_store = atomic_load_store ;
                 } in
                kont conc ofail res
              else begin
                if C.debug.Debug_herd.solver then begin
                  let conc =
                    {
                     S.str = es ;
                     rfmap = rfm ;
                     fs = fsc ;
                     po = po_iico ;
                     partial_po = partial_po ;
                     pos = ppoloc ;
                     pco = pco ;

                     store_load_vbf = store_load_vbf ;
                     init_load_vbf = init_load_vbf ;
                     last_store_vbf = last_store_vbf ;
                     atomic_load_store = atomic_load_store ;
                   } in
                  let module PP = Pretty.Make(S) in
                  eprintf "PCO is cyclic, discarding candidate\n%!" ;
                  PP.show_legend test "PCO is cyclic" conc [("last_store_vbf",last_store_vbf); ("pco",pco);];
                end ;
                res
              end
            end else res)
          res
      with Exit -> res


(* Initial check of rfmap validity: no intervening writes.
   Limited to memory, since  generated rfmaps are correct for registers *)
(* NOTE: this is more like an optimization,
   models should rule out those anyway *)

    let check_rfmap es rfm =
      let po_iico = U.is_before_strict es in
      S.for_all_in_rfmap
        (fun wt rf -> match wt with
        | S.Load er when E.is_mem_load er && E.is_explicit er ->
            begin match rf with
            | S.Store ew ->
                not (E.is_explicit ew) ||
                begin
                  assert (not (po_iico er ew)) ;
                (* ok by construction, in theory *)
                  not
                    (E.EventSet.exists
                       (fun e ->
                         E.is_store e && E.same_location e ew &&
                         E.is_explicit e && po_iico ew e &&
                         po_iico e er)
                       es.E.events)
                end
            | S.Init ->
                not
                  (E.EventSet.exists
                     (fun e ->
                       E.is_store e && E.same_location e er &&
                       E.is_explicit e && po_iico e er)
                     es.E.events)
            end
        | _ -> true)
        rfm

    let check_sizes test es =
      if check_mixed then begin
        (* No need to check initial writes, correct by construction. *)
        let loc_mems = U.collect_mem_non_init es in
        U.LocEnv.iter
          (fun loc evts ->
            let open Constant in
            begin match loc with
            | A.Location_global (V.Val (Symbolic sym))
                  when not (S.is_non_mixed_symbol test sym) ->
                Warn.user_error "mixed-size test rejected (symbol %s), consider option -variant mixed"
                  (A.pp_location loc)
            | _ -> ()
            end ;
            begin match evts with
            | [] -> ()
            | e0::es ->
                let sz0 = E.get_mem_size e0 in
                List.iter
                  (fun e ->
                    let sz =  E.get_mem_size e in
                    if sz0 <> sz then begin
                      Printf.eprintf
                        "Size mismatch %a vs. %a, ie %s vs. %s\n"
                        E.debug_event e0
                        E.debug_event e
                        (MachSize.pp sz0) (MachSize.pp sz);
                      Warn.user_error "Illegal mixed-size test"
                    end)
                  es
            end)
          loc_mems
        end

     let check_event_aligned test e =
       let a = Misc.as_some (E.global_loc_of e) in
       if not (U.is_aligned (S.type_env test) (S.size_env test) e) then begin
         if dbg then eprintf "UNALIGNED: %s\n" (E.pp_action e);
         Warn.user_error "Unaligned or out-of-bound access: %s, %d bytes"
           (A.V.pp_v a) (E.get_mem_size e |> MachSize.nbytes)
       end

(* Check alignement in the mixed-size case.
   Check is performed on original memory accesses,
   not on splitted sub-events. Checking sub-events would
   be too permissive, as easily shown by splitting accesses
   into byte accesses. *)
    let check_aligned test es =
      E.EventSet.iter
        (fun e -> check_event_aligned test e)
        es.E.mem_accesses

    let check_symbolic_locations _test es =
      E.EventSet.iter
        (fun e -> match E.location_of e with
        | Some (A.Location_global (V.Val cst)) when
            Constant.is_symbol cst ||
            Constant.is_label cst
            -> ()
        | Some (A.Location_global (V.Var _))
            -> ()
        | Some loc ->
            Warn.user_error "Non-symbolic memory access found on '%s'"
              (A.pp_location loc)
        | None -> assert false)
        (E.mem_of es.E.events)

      let check_noifetch_limitations es =
        let non_init_stores = E.EventSet.filter
          (fun e -> E.is_mem_store e && not (E.is_mem_store_init e))
          es.E.events in
        E.EventSet.iter (fun e ->
          match E.location_of e with
          | Some (A.Location_global (V.Val(Constant.Label(p, lbl)))) ->
             Warn.user_error
               "Store to %s:%s requires instruction fetch functionality.\n\
Please use `-variant self` as an argument to herd7 to enable it."
              (Proc.pp p) (Label.pp lbl)
          | _ -> ()
        ) non_init_stores

    let check_ifetch_limitations test es owls =
      let stores = E.EventSet.filter E.is_mem_store es.E.events in
      let program = test.Test_herd.program
      and code_segment = test.Test_herd.code_segment in
      E.EventSet.iter (fun e ->
        match E.location_of e with
        | Some (A.Location_global (V.Val(Constant.Label(p, lbl))) as loc) ->
          if Label.Set.mem lbl owls then begin
            if false then (* insert a proper test here *)
              Warn.user_error "Illegal store to '%s'; overwrite with the given argument not supported"
              (A.pp_location loc)
          end else begin
            if not (E.is_mem_store_init e) then begin
              try
                match IntMap.find (Label.Map.find lbl program) code_segment with
                | (_,[]) ->
                   Warn.user_error
                     "Final label %s cannot be overwritten"
                     (Label.pp lbl)
                | (_,(_,i)::_) ->
                   Warn.user_error
                     "Instruction %s:%s cannot be overwritten"
                     (Label.pp lbl)
                     (A.dump_instruction i)
              with
              | Not_found ->
                 Warn.user_error
                   "Label %s not found on %s, yet it is used as constant"
                   (Label.pp lbl) (Proc.pp p)
              end
            end
        | _ ->
          ()
      ) stores

    let calculate_rf_with_cnstrnts test owls es cs kont res =
      match solve_regs test es cs with
      | None -> res
      | Some (es,rfm,cs) ->
          if C.debug.Debug_herd.solver && C.verbose > 0 then begin
            let module PP = Pretty.Make(S) in
            prerr_endline "Reg solved" ;
            PP.show_es_rfm test es rfm ;
          end ;
          solve_mem test es rfm cs
            (fun es rfm cs res ->
              let ofail = VC.get_failed cs in
              match cs with
              | _::_
                   when
                     (not oota)
                     && (not C.initwrites || not do_deps)
                     && not asl
                     && Misc.is_none ofail
                ->
(*
 Jade:
  on tolere qu'il reste des equations dans le cas d'evts specules -
  mais il faudrait sans doute le preciser dans la clause when ci-dessus.
 Luc:
   Done, or at least avoid accepting such candidates in non-deps mode.
   Namely, having  non-sensical candidates rejected later by model
   entails a tremendous runtime penalty. *)
                  when_unsolved test es rfm cs res
              | _ ->
                  check_symbolic_locations test es ;
                  if self then check_ifetch_limitations test es owls
                  else check_noifetch_limitations es;
                  if (mixed && not unaligned) then check_aligned test es ;
                  if A.reject_mixed
                     && not (mixed || memtag || morello)
                  then
                    check_sizes test es ;
                  if C.debug.Debug_herd.solver && C.verbose > 0 then begin
                    let module PP = Pretty.Make(S) in
                    prerr_endline "Mem solved" ;
                    PP.show_es_rfm test es rfm
                  end ;
                  if
                    match C.optace with
                    | OptAce.False|OptAce.Iico -> true
                    | OptAce.True -> check_rfmap es rfm
                  then
                    (* Atomic load/store pairs *)
                    let atomic_load_store = make_atomic_load_store es in
                    if
                      C.variant Variant.OptRfRMW
                      && some_same_rf_rmw rfm atomic_load_store
                    then begin
                      if C.debug.Debug_herd.mem then begin
                        let module PP = Pretty.Make(S) in
                        eprintf
                          "Atomicity violation anticipated from rf map%!" ;
                        PP.show_es_rfm test es rfm
                      end ;
                      res
                      end else
                      fold_mem_finals test es
                        rfm ofail atomic_load_store kont res
                  else  res)
            res
  end