File: cptr.ml

package info (click to toggle)
why 2.13-2
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 12,608 kB
  • ctags: 16,817
  • sloc: ml: 102,672; java: 7,173; ansic: 4,439; makefile: 1,409; sh: 585
file content (2367 lines) | stat: -rw-r--r-- 85,866 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
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
(**************************************************************************)
(*                                                                        *)
(*  The Why platform for program certification                            *)
(*  Copyright (C) 2002-2008                                               *)
(*    Romain BARDOU                                                       *)
(*    Jean-Franois COUCHOT                                               *)
(*    Mehdi DOGGUY                                                        *)
(*    Jean-Christophe FILLITRE                                           *)
(*    Thierry HUBERT                                                      *)
(*    Claude MARCH                                                       *)
(*    Yannick MOY                                                         *)
(*    Christine PAULIN                                                    *)
(*    Yann RGIS-GIANAS                                                   *)
(*    Nicolas ROUSSET                                                     *)
(*    Xavier URBAIN                                                       *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU General Public                   *)
(*  License version 2, as published by the Free Software Foundation.      *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(*  See the GNU General Public License version 2 for more details         *)
(*  (enclosed in the file GPL).                                           *)
(*                                                                        *)
(**************************************************************************)

(* $Id: cptr.ml,v 1.24 2008/02/05 12:10:47 marche Exp $ *)

(* TO DO:

   - treat correctly variables whose address is taken (probably do not treat
   them at all). Problem even without option on code:
   void f(int* p) {
     int** q = &p;
     ( *q )++;
     *p = 0;
   }
   Definition of [q] printed in cnorm:
     int** q = p;

   - change the naming scheme for offset variables.
   Currently, offset variable for pointer [p] is either "p_offset" or
   "p_self_offset". Neither one is satisfactory, since a variable could
   already be called like this. The solution should use "__" in front of
   the name, to make it a reserved name by the compiler, e.g.
   "__offset_p" and "__self_offset_p". We must only check that such names
   are correctly understood by the provers, or translated appropriately.

   - add same transformation for integers, e.g. on [size] in
         while (size --) *p++ = *q++;
   --> DONE. document it.
   
   - take into account possible non-initialization

*)

open Info
open Clogic
open Cast
open Cutil
open Cabsint

let debug = Coptions.debug
let debug_more = false


(*****************************************************************************
 *                                                                           *
 * 		Pointer lattice used for local aliasing analysis             *
 *                                                                           *
 *****************************************************************************)

module type POINTER_SEMI_LATTICE = sig
  include SEMI_LATTICE
  type var_t
    (* in the following, an [alias] is the same as an [index] of 0.
       It is provided for the sake of simplicity. *)
    (* constructors *)
  val make_alias : var_t -> t
  val make_index : var_t -> int -> t
  val make_offset : var_t -> var_t option -> t
  val make_defined : var_t -> var_t option -> t
    (* query functions *)
  val is_alias : t -> bool
  val is_index : t -> bool
  val is_self_index : var_t -> t -> bool
  val is_offset : t -> bool
  val is_self_offset : var_t -> t -> bool
  val is_defined : t -> bool
  val is_top : t -> bool
    (* destructors *)
  val get_alias : t -> var_t
  val get_index : t -> var_t * int
  val get_offset_opt : t -> var_t * var_t option
  val get_offset : t -> var_t * var_t
  val get_defined_opt : t -> var_t * var_t option
  val has_variable : t -> bool
  val get_variable : t -> var_t
end

module Make_PtrSemiLattice(V : VARIABLE)
    : POINTER_SEMI_LATTICE with type var_t = V.t and type dim_t = unit =
struct

  type var_t = V.t

  (* different kinds of pointers, depending on their local aliasing,
     obtained through assignments. 
     Only relevant for local variables, i.e. parameters and locals, 
     because globals could be modified inside a function call. *)
  type t =
        (* result of join between incompatible pointers on different paths *)
    | PKcomplex
	(* not known offset from local/global variable or parameter.
	   The optional variable will be set during the analysis to be 
	   an integer variable containing the actual offset. *)
    | PKoffset of V.t * V.t option
	(* known index from local/global variable or parameter.
	   In the particular case of a 0 index, it represents an alias of 
	   a local/global variable or parameter. *)
    | PKindex of V.t * int 
	(* defined pointer, but definition was too complex to be classified
	   as an offset or an index pointer. 
	   The 1st variable allows for a common treatment with other kinds.
	   The optional variable will be possibly set during the analysis 
	   to be an integer variable containing an offset to reset to 0
	   when assigning the pointer variable.
	   Conceptually, [PKdefined] is not far from [PKindex] on the same 
	   variable with index 0. *)
    | PKdefined of V.t * V.t option
	(* undefined pointer *)
    | PKundefined

  type dim_t = unit
  let top () = PKcomplex
  let bottom () = PKundefined
  let init = bottom

  (* get underlying variable if any *)
  let get_var_opt p1 = match p1 with
    | PKundefined | PKcomplex -> None
    | PKindex (v,_) | PKoffset (v,_) | PKdefined (v,_) -> Some v

  let same_var p1 p2 = match get_var_opt p1,get_var_opt p2 with
    | Some v1,Some v2 -> V.compare v1 v2 = 0
    | _ -> false

  let is_not_self_index_offset p = match p with
    | PKindex _ | PKoffset _ -> true
    | _ -> false

  (* constructors *)
  let make_alias v = PKindex (v,0)
  let make_index v i = PKindex (v,i)
  let make_offset v vo = PKoffset (v,vo)
  let make_defined v vo = PKdefined (v,vo)

  (* query functions *)
  let is_alias t = match t with PKindex (_,0) -> true | _ -> false
  let is_index t = match t with PKindex _ -> true | _ -> false
  let is_self_index var t = 
    match t with PKindex (v,_) -> V.equal var v | _ -> false
  let is_offset t = match t with PKoffset _ -> true | _ -> false
  let is_self_offset var t = 
    match t with PKoffset (v,_) -> V.equal var v | _ -> false
  let is_defined t = match t with PKdefined _ -> true | _ -> false
  let is_top t = match t with PKcomplex -> true | _ -> false

  (* destructors *)
  let get_alias t = match t with 
    | PKindex (v,0) -> v 
    | _ -> assert false
  let get_index t = match t with 
    | PKindex (v,i) -> v,i 
    | _ -> assert false
  let get_offset_opt t = match t with 
    | PKoffset (v,vo) -> v,vo 
    | _ -> assert false
  let get_offset t = match t with 
    | PKoffset (v,o) ->
	begin match o with
	  | Some off -> v,off
	  | _ -> 
	      (* when querying an [offset] pointer kind, we expect 
		 the offset variable to be set *)
	      assert false
	end
    | _ -> assert false
  let get_defined_opt t = match t with 
    | PKdefined (v,vo) -> v,vo 
    | _ -> assert false
  let has_variable t = match t with
    | PKindex _ | PKoffset _ | PKdefined _ -> true
    | _ -> false
  let get_variable t = match t with
    | PKindex _ -> fst (get_index t)
    | PKoffset _ -> fst (get_offset_opt t)
    | PKdefined _ -> fst (get_defined_opt t)
    | _ -> assert false

  let equal p1 p2 = match p1,p2 with
    | PKundefined,PKundefined -> true
    | PKindex (v1,o1),PKindex (v2,o2) ->
	V.equal v1 v2 && o1 = o2
    | PKoffset (v1,o1),PKoffset (v2,o2)
    | PKdefined (v1,o1),PKdefined (v2,o2) ->
	V.equal v1 v2 && (Option.equal V.equal o1 o2)
    | PKcomplex,PKcomplex -> true
    | _ -> false

  let pretty fmt t = match t with
    | PKundefined -> Format.fprintf fmt "PKundefined"
    | PKindex (v,i) -> Format.fprintf fmt "PKindex(%a,%d)" V.pretty v i
    | PKoffset (v,o) -> 
	begin
	  match o with
	    | None -> Format.fprintf fmt "PKoffset(%a)" V.pretty v
	    | Some vo -> 
		Format.fprintf fmt "PKoffset(%a,%a)" V.pretty v V.pretty vo
	end
    | PKdefined (v,o) ->
	begin
	  match o with
	    | None -> Format.fprintf fmt "PKdefined(%a)" V.pretty v
	    | Some vo -> Format.fprintf 
		fmt "PKdefined(%a,%a)" V.pretty v V.pretty vo
	end
    | PKcomplex -> Format.fprintf fmt "PKcomplex"

  (* lattice associated to var [v] has the following form:

                              PKcomplex
                                   |
                              PKoffset(v,o)
                                   |
                              PKindex(v,i)
                                   |
                              PKdefined(v,o)
                                   |
                              PKundefined

     2 different lattices for [u] and [v] only connect at top and bottom.
  *)

  (* 2 pointer kinds are comparable only if they have the same underlying 
     variable, if any. The opposite is true too, except for pairs of the same
     pointer kind with different second argument. 
     A subtlety is that a PKdefined pointer is comparable to a PKindex one
     only if the index is 0. *)
  let comparable p1 p2 = match get_var_opt p1,get_var_opt p2 with
    | Some v1,Some v2 -> 
	let second_arg_same = 
	  if is_offset p1 && (is_offset p2) then
	    let o1 = snd (get_offset_opt p1) in
	    let o2 = snd (get_offset_opt p2) in
	    Option.equal V.equal o1 o2
	  else if is_index p1 && (is_index p2) then
	    let o1 = snd (get_index p1) in
	    let o2 = snd (get_index p2) in
	    o1 = o2
	  else if is_defined p1 && (is_defined p2) then
	    let o1 = snd (get_defined_opt p1) in
	    let o2 = snd (get_defined_opt p2) in
	    Option.equal V.equal o1 o2
	  else true
	in
	let defined_with_index0 =
	  if is_defined p1 || (is_defined p2) then
	    if is_index p1 then snd (get_index p1) = 0
	    else if is_index p2 then snd (get_index p2) = 0
	    else true
	  else true
	in
	V.compare v1 v2 = 0 && second_arg_same && defined_with_index0
    | _ -> true

  (* p1 <= p2 *)
  let inf p1 p2 = 
    comparable p1 p2 &&
      match p1,p2 with
            (* PKundefined is the bottom element *)
	| PKundefined,_ -> true
	| _,PKundefined -> false
	    (* PKdefined is the next lowest element *)
	| PKdefined _,_ -> true
	| _,PKdefined _ -> false
	    (* PKindex is the next lowest element *)
	| PKindex _,_ -> true
	| _,PKindex _ -> false
	    (* PKoffset is the next lowest element *)
	| PKoffset _,_ -> true
	| _,PKoffset _ -> false
	    (* PKcomplex is the top element *)
	| PKcomplex,PKcomplex -> true

  (* union *)
  let rec join ?(backward=false) p1 p2 = 
    if not (comparable p1 p2) then 
      if same_var p1 p2 then
	(* case of interest here is 2 PKindex with different indices,
	   or equivalently a PKdefined with a PKindex of index different 
	   from 0 *)
	let v = get_variable p1 in
	PKoffset (v,None)
      else
	PKcomplex
    else if inf p2 p1 && not (equal p2 p1) then join p2 p1 else
      (* only treat here the case where p1 <= p2 *)
      match p1,p2 with
          (* PKundefined is the bottom element *)
	| PKundefined,p -> p
	    (* PKdefined is the next lowest element *)
	| PKdefined _,p -> p
	    (* PKindex is the next lowest element *)
	| PKindex _,p -> p
	    (* PKoffset is the next lowest element *)
	| PKoffset _,p -> p
	    (* PKcomplex is the top element *)
	| PKcomplex,_ -> PKcomplex

    (* widening should not be used on this finite lattice *)
    let widening _ = assert false
end


(*****************************************************************************
 *                                                                           *
 * 		Concrete modules for local pointer analysis		     *
 *                                                                           *
 *****************************************************************************)

(* We make the following choices in the following:
   - variables are represented by an entity of type [Info.var_info]
   - the intermediate language is the normalized AST as described by
   Cast.ndecl/nstatement/nexpr
   
   Other choices are perfectly possible, e.g. the pre-normalized AST
   as described by Cast.tdecl/tstatement/texpr as intermediate language.
*)

(* it happens to be the same as ILVar *)
module Var : VARIABLE with type t = var_info = struct
  type t = var_info
  let pretty fmt v = Format.fprintf fmt "%s" v.var_name
  let to_string v = v.var_name
  let compare v1 v2 = Pervasives.compare v1.var_uniq_tag v2.var_uniq_tag
  let equal v1 v2 = compare v1 v2 = 0
  let hash v = v.var_uniq_tag
end

module VarMap = Map.Make (Var)
module VarSet = Set.Make (Var)

module PtrLattice 
    : POINTER_SEMI_LATTICE with type var_t = Var.t and type dim_t = unit
  = Make_PtrSemiLattice(Var)

module PointWisePtrLattice = 
  Make_PointWiseSemiLattice(Var)(PtrLattice)

(* specialized intermediate language for pointer analysis *)

module PtrLangFromNormalized : sig
  include CFG_LANG_EXTERNAL
  
  (* query functions *)
    (* if the right-hand side of this assignment is a variable, 
       return it, with an optional constant offset if known.
       Only when [assign_get_local_lhs_var] has been called before
       with success. *)
  val assign_get_rhs_var : Node.t -> ilvar_t option * int option
    (* takes a variable expression or a pointer/integer addition 
       of an expression and an offset.
       returns as 1st item the possible variable in the addition.
       returns as 2nd item the possible offset expression in the addition. *)
  val get_intptr_add_on_var_opt : Node.t -> ilvar_t option * Node.t option

  (* constructors.
     - [make_] functions operate directly on their arguments.
     - [change_] functions take a first node as context, and operate on 
     other arguments.

     Most of those functions are valid both for pointer and integer variables.
  *)
    (* create a new node integer/pointer expression + constant *)
  val make_intptr_expr_add_cst : Node.t -> int -> Node.t
    (* create a new node integer/pointer expression + expression *)
  val make_intptr_expr_add_expr : Node.t -> Node.t -> Node.t
    (* make this node be a term/expression over an integer/pointer variable *)
  val change_in_intptr_var : Node.t -> ilvar_t -> Node.t
    (* make this node be a term/expression of a sum variable + constant *)
  val change_in_intptr_var_add_cst : Node.t -> ilvar_t -> int -> Node.t
    (* make this node be a term/expression of a sum variable + variable *)
  val change_in_intptr_var_add_var : Node.t -> ilvar_t -> ilvar_t -> Node.t
    (* make this node be an increment/decrement of the 2nd node,
       materialized as a pointer addition.
       The boolean is [true] if the function is called on the result of
       the assignment, to get back a pointer expression for the evaluation
       of the increment/decrement expression.
       The boolean is [false] if the function is called before the assignment,
       to get the result of the increment/decrement evaluation. *)
  val change_in_intptr_incr : Node.t -> Node.t -> bool -> Node.t

    (* make this node be an increment/decrement of the variable,
       materialized as an integer addition, that is assigned to
       the 1st node *)
  val change_in_int_incr_assign : Node.t -> ilvar_t -> Node.t
    (* change the structural sub-components of this node.
       Supersede the same function from CFGLangFromNormalized. *)
  val change_sub_components : Node.t -> Node.t list -> Node.t 
    (* add new local variables to this declaration node.
       The boolean is [true] if the variables are zero-initialized. *)
  val introduce_new_vars : Node.t -> ilvar_t list -> bool -> Node.t

end = struct
  
  include CFGLangFromNormalized

  (* more elaborate query functions related to pointer usage *)

  let assign_get_rhs_var node =
    let rec get_rhs_var e = match e.nexpr_node with
      | NEvar (Var_info var) -> 
	  (* direct alias *)
	  Some var,Some 0
      | NEincr _ | NEassign _ | NEassign_op _ ->
	  (* right-hand side is itself an assignment *)
	  begin match sub_assign_get_lhs_var e with
	    | None -> None,None (* rhs variable not identified *)
	    | Some var ->
		(* rhs variable has been identified *)
		(* post- and pre- increment/decrement not treated uniformly
		   since we are interested in the -value- resulting from
		   this operation *)
		begin match e.nexpr_node with
		  | NEincr (Upostfix_inc,_) ->
		      Some var,Some (-1)
		  | NEincr (Upostfix_dec,_) ->
		      Some var,Some 1
		  | _ -> Some var,Some 0
		end
	  end
      | NEbinary (e1,(Badd_pointer_int | Badd_int _ | Bsub_int _ as op),e2) ->
	  (* right-hand side is an offset from a pointer/integer expression *)
	  begin match get_rhs_var e1 with
	    | Some var,Some off ->
		(* rhs is an known offset from some variable *)
		begin match e2.nexpr_node with
		  | NEconstant (IntConstant s) ->
		      (* rhs is constant offset from variable *)
		      begin
			try 
			  let new_off = match op with
			    | Badd_pointer_int | Badd_int _ -> 
				off + (int_of_string s)
			    | Bsub_int _ ->
				off - (int_of_string s)
			    | _ -> assert false
			  in
			  (* constant offset is representable *)
			  Some var,Some new_off
			with Failure "int_of_string" ->
			  (* constant offset not representable *)
			  Some var,None
		      end
		  | _ -> Some var,None (* offset not known *)
		end
	    | _ -> None,None (* rhs not offset from variable *)
	  end
      | _ -> None,None (* rhs not recognized *)
    in
    match get_expr node with
      | NEincr (op,e) ->
	  begin match e.nexpr_node with
	    | NEvar (Var_info var) ->
		(* post- and pre- increment/decrement treated uniformy,
		   since we are interested here in the -assignment- resulting
		   from this operation *)
		begin match op with
		  | Upostfix_inc | Uprefix_inc ->
		      Some var,Some 1
		  | Upostfix_dec | Uprefix_dec ->
		      Some var,Some (-1)
		end
	    | _ -> None,None (* rhs not recognized *)
	  end
      | NEassign_op (e1,(Badd_pointer_int | Badd_int _ | Bsub_int _ as op),
		     e2) ->
	  begin match e1.nexpr_node with
	    | NEvar (Var_info var) ->
		begin match e2.nexpr_node with
		  | NEconstant (IntConstant s) ->
		      (* rhs is constant offset from variable *)
		      begin
			try 
			  let new_off = match op with
			    | Badd_pointer_int | Badd_int _ -> 
			        (int_of_string s)
			    | Bsub_int _ ->
			        - (int_of_string s)
			    | _ -> assert false
			  in
			  (* constant offset is representable *)
			  Some var,Some new_off
			with Failure "int_of_string" ->
			  (* constant offset not representable *)
			  Some var,None
		      end
		  | _ -> Some var,None (* offset not known *)
		end
	    | _ -> None,None (* rhs not recognized *)
	  end
      | NEassign (_,e) ->
	  get_rhs_var e
      | _ -> failwith ("[assign_get_rhs_var] should be called on assignment")

  let rec get_intptr_add_on_var_opt node =
    if debug_more then Coptions.lprintf 
      "[get_intptr_add_on_var_opt] %a@." Node.pretty node;
    (* a cast may have been introduced for arrays used as pointers *)
    let node = skip_casts node in
    let e = get_e node in
    match e.nexpr_node with
      | NEvar _ -> None,None
      | NEbinary (e1,(Badd_pointer_int | Badd_int _ | Bsub_int _ as op),e2) ->
	  let e2 = match op with
	    | Bsub_int _ -> { e2 with nexpr_node = NEunary (Uminus,e2) }
	    | _ -> e2
	  in
	  let add_opt = Some (create_tmp_node (Nexpr e2)) in
	  let var_opt = match e1.nexpr_node with
	    | NEvar (Var_info v) -> Some v
	    | _ -> None
	  in
	  var_opt,add_opt
      | _ -> assert false

  (* constructors *)

  (* expr + neg_cst is coded as expr + (-abs(neg_cst))
     This format is expected by [Cconst] module. *)
  let make_intptr_expr_add_cst node cst =
    let e = get_e node in
    let cst_e = NEconstant (IntConstant (string_of_int (abs cst))) in
    let cst_e = { e with nexpr_node = cst_e; nexpr_type = int_offset_type } in
    let cst_e = if cst >= 0 then cst_e else
      { cst_e with nexpr_node = NEunary (Uminus,cst_e) } in
    let addop = 
      if expr_type_is_ptr node then Badd_pointer_int
      else if expr_type_is_int node then int_offset_addop
      else assert false in
    let new_e = NEbinary (e,addop,cst_e) in
    let new_e = { e with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  let make_intptr_expr_add_expr node1 node2 =
    let e1 = get_e node1 in
    let e2 = get_e node2 in
    let addop = 
      if expr_type_is_ptr node1 then Badd_pointer_int
      else if expr_type_is_int node1 then int_offset_addop
      else assert false in
    let new_e = NEbinary (e1,addop,e2) in
    let new_e = { e1 with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)

  (* deals with array address used as pointer *)
  let change_in_intptr_var node var =
    match get_node_kind node with
      | NKexpr | NKtest | NKlvalue -> 
	  let e = get_e node in
	  let var_e = NEvar (Var_info var) in
	  let var_e = { e with nexpr_node = var_e } in
	  let new_e =
	    match var.var_type.Ctypes.ctype_node with 
	      | Ctypes.Tarray (valid,ty,_) -> 
		  let cast_e = NEcast (Ctypes.c_pointer valid ty,var_e) in
		  { e with nexpr_node = cast_e }
	      | _ -> var_e
	  in
	  create_tmp_node (Nexpr new_e) 
      | NKterm ->
	  let t = get_t node in
	  let var_t = NTvar var in
	  let var_t = { t with nterm_node = var_t } in
	  let new_t = 
	    match var.var_type.Ctypes.ctype_node with 
	      | Ctypes.Tarray (valid,ty,_) -> 
		  let cast_t = NTcast (Ctypes.c_pointer valid ty,var_t) in
		  { t with nterm_node = cast_t }
	      | _ -> var_t
	  in
	  create_tmp_node (Nterm new_t)
      | _ -> assert false

  (* var + neg_cst is coded as var + (-abs(neg_cst))
     This format is expected by [Cconst] module. *)
  let change_in_intptr_var_add_cst node var offset =
    let var_te = change_in_intptr_var node var in
    match get_node_kind node with
      | NKexpr | NKtest | NKlvalue -> 
	  let e = get_e node in
	  let cst_e = NEconstant (IntConstant (string_of_int (abs offset))) in
	  let cst_e = { e with nexpr_node = cst_e; 
			  nexpr_type = int_offset_type } in
	  let cst_e = if offset >= 0 then cst_e else
	    { cst_e with nexpr_node = NEunary (Uminus,cst_e) } in
	  let addop = 
	    if var_is_pointer var then Badd_pointer_int
	    else if var_is_integer var then int_offset_addop
	    else assert false in
	  let new_e = NEbinary (get_e var_te, addop, cst_e) in
	  let new_e = { e with nexpr_node = new_e } in
	  create_tmp_node (Nexpr new_e)
      | NKterm ->
	  let t = get_t node in
	  let cst_t = NTconstant (IntConstant (string_of_int (abs offset))) in
	  let cst_t = { t with nterm_node = cst_t; 
			  nterm_type = int_offset_type } in
	  let cst_t = if offset >= 0 then cst_t else
	    { cst_t with nterm_node = NTunop (Clogic.Uminus,cst_t) } in
	  let new_t = NTbinop (get_t var_te, Clogic.Badd, cst_t) in
	  let new_t = { t with nterm_node = new_t } in
	  create_tmp_node (Nterm new_t)
      | _ -> assert false

  let change_in_intptr_var_add_var node var offset_var =
    let var_te = change_in_intptr_var node var in
    match get_node_kind node with
      | NKexpr | NKtest | NKlvalue -> 
	  let e = get_e node in
	  let cst_e = NEvar (Var_info offset_var) in
	  let cst_e = { e with nexpr_node = cst_e; 
			  nexpr_type = int_offset_type } in
	  let addop = 
	    if var_is_pointer var then Badd_pointer_int
	    else if var_is_integer var then int_offset_addop
	    else assert false in
	  let new_e = NEbinary (get_e var_te, addop, cst_e) in
	  let new_e = { e with nexpr_node = new_e } in
	  create_tmp_node (Nexpr new_e)
      | NKterm ->
	  let t = get_t node in
	  let cst_t = NTvar offset_var in
	  let cst_t = { t with nterm_node = cst_t; 
			  nterm_type = int_offset_type } in
	  let new_t = NTbinop (get_t var_te, Clogic.Badd, cst_t) in
	  let new_t = { t with nterm_node = new_t } in
	  create_tmp_node (Nterm new_t)
      | _ -> assert false

  let change_in_intptr_incr node new_rhs_node is_after_assign =
    let e = get_e node in
    let op = match e.nexpr_node with NEincr (op,_) -> op | _ -> assert false in
    let is_inop_op op =
      if is_after_assign then
	(* prefix operations must have occurred after assignment *)
	op = Uprefix_inc || op = Uprefix_dec
      else
	(* postfix operations must not have occurred before assignment *)
	op = Upostfix_inc || op = Upostfix_dec
    in
    let is_add1_op op =
      if is_after_assign then
	(* postfix decrement should be reversed after asssignment *)
	op = Upostfix_dec
      else
	(* prefix increment must be coded before assignment *)
	op = Uprefix_inc
    in
    let is_sub1_op op =
      if is_after_assign then
	(* postfix increment should be reversed after asssignment *)
	op = Upostfix_inc
      else
	(* prefix decrement must be coded before assignment *)
	op = Uprefix_dec
    in
    if is_inop_op op then
      new_rhs_node
    else if is_add1_op op then
      make_intptr_expr_add_cst new_rhs_node 1
    else if is_sub1_op op then
      make_intptr_expr_add_cst new_rhs_node (-1)
    else
      assert false

  let change_in_int_incr_assign node offset_var =
    let e = get_e node in
    let var_e = NEvar (Var_info offset_var) in
    let var_e = { e with nexpr_node = var_e; nexpr_type = int_offset_type } in
    let new_e =
      match e.nexpr_node with
	| NEincr (op,_) -> 
	    (* keep the original increment/decrement operator for ergonomy
	       purposes. Postfix/prefix choice should have no effect here
	       since this node is not supposed to be used as 
	       a sub-expression. *)
	    NEincr (op,var_e)
	| _ -> assert false
    in
    let new_e = { e with nexpr_node = new_e; nexpr_type = int_offset_type } in
    create_tmp_node (Nexpr new_e)

  (* exception used to report unexpected encoding in [change_sub_components]
     or its sub-functions. *)
  exception Bad_encoding

  (* exception used to report CFGLangFromNormalized treatment should be 
     applied, instead of a special treatment for pointer analysis *)
  exception Use_inherited

  let change_sub_components_in_stat node sub_nodes =
    let s = get_s node in
    (* in case an assignment was transformed into its right-hand side,
       do not keep the resulting expression if useless.
       In general this could be seen as an optimization.
       Currently it is necessary because [Cinterp] module might fail
       on such code that it considers as a "statement expression". *)
    let rec useless_expr e = match e.nexpr_node with
	(* only possible "statement expression" cases returned 
	   by the transformation *)
      | NEconstant _ | NEvar _ -> true
      | NEbinary (e1,_,e2) -> (useless_expr e1) && (useless_expr e2)
      | NEunary (_,e1) | NEcast (_,e1) -> useless_expr e1
      | _ -> false
    in
    let simplify_expr e = match e.nexpr_node with
      | NEseq (e1,e2) ->
	  (* [e2] may be the pointer evaluation, useless here *)
	  if useless_expr e2 then e1 else e
      | _ -> e
    in
    let simplify_expr_under_stat e = 
      let e = simplify_expr e in
      match e.nexpr_node with
	| NEbinary (e1,_,e2) ->
	    (* [e1] may be the base pointer evaluation, useless here *)
	    if useless_expr e1 then e2 else e
	| _ -> e
    in
    let statement_expr_or_nop e = 
      let e = simplify_expr_under_stat e in
      if useless_expr e then NSnop else NSexpr e
    in
    let filter_nop sl =
      List.filter (fun s -> not (s.nst_node = NSnop)) sl
    in
    try let new_s = match s.nst_node with
      | NSexpr e -> 
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  statement_expr_or_nop new_e
      | NSif (e,s1,s2) ->
	  assert (List.length sub_nodes = 3);
	  let new_e,new_s1,new_s2 = list3 sub_nodes in
	  let new_e,new_s1,new_s2 
	    = get_e new_e,get_s new_s1,get_s new_s2 in
	  let new_e = simplify_expr new_e in
	  NSif (new_e,new_s1,new_s2)
      | NSwhile (annot,e,s1) ->
	  assert (List.length sub_nodes = 3);
	  let new_e,new_s1,new_a = list3 sub_nodes in
	  let new_e,new_s1,new_a = get_e new_e,get_s new_s1,get_annot new_a in
	  let new_e = simplify_expr new_e in
	  NSwhile (new_a,new_e,new_s1)
      | NSdowhile (annot,s1,e) ->
	  assert (List.length sub_nodes = 3);
	  let new_s1,new_e,new_a = list3 sub_nodes in
	  let new_s1,new_e,new_a = get_s new_s1,get_e new_e,get_annot new_a in
	  let new_e = simplify_expr new_e in
	  NSdowhile (new_a,new_s1,new_e)
      | NSfor (annot,einit,etest,eincr,s1) ->
	  assert (List.length sub_nodes = 5);
	  let new_einit,new_etest,new_eincr,new_s1,new_a = list5 sub_nodes in
	  let new_einit,new_etest,new_eincr,new_s1,new_a
	    = get_e new_einit,get_e new_etest,
	    get_e new_eincr,get_s new_s1,get_annot new_a in
	  let new_einit = simplify_expr_under_stat new_einit in
	  let new_etest = simplify_expr new_etest in
	  let new_eincr = simplify_expr_under_stat new_eincr in
	  NSfor (new_a,new_einit,new_etest,new_eincr,new_s1)
      | NSblock sl ->
	  let new_sl = filter_nop (List.map get_s sub_nodes) in
	  NSblock new_sl
      | NSreturn (Some e) -> 
	  assert (List.length sub_nodes = 1);
	  let new_e = list1 sub_nodes in
	  let new_e = get_e new_e in
	  let new_e = simplify_expr new_e in
	  NSreturn (Some new_e)
      | NSdecl (typ,var,Some cinit,s1) ->
	  assert (List.length sub_nodes = 2);
	  let new_e,new_s1 = list2 sub_nodes in
	  let new_e = get_e new_e in
	  let new_e = simplify_expr new_e in
	  let new_s1 = get_s new_s1 in
	  begin try
	    let lhs_expr,rhs_expr = match new_e.nexpr_node with 
	      | NEassign (lhs_expr,rhs_expr) -> lhs_expr,rhs_expr
	      | _ -> raise Bad_encoding in
	    let new_var = match lhs_expr.nexpr_node with
	      | NEvar (Var_info new_var) -> new_var
	      | _ -> raise Bad_encoding
	    in
	    if ILVar.equal var new_var then
	      (* not an offset assignment *)
	      let new_cinit = decode_decl_list rhs_expr in
	      NSdecl (typ,var,Some new_cinit,new_s1)
	    else if var_is_pointer new_var then
	      (* neither the original variable nor an offset 
		 assignment. It must be of pointer type. *)
	      raise Bad_encoding
	    else
	      begin
		(* offset assignment.
		   Can only occur for [Iexpr] initializer, because 
		   [Ilist] initializer is encoded into a call, which
		   can only lead to a complex pointer kind for [var].
		   The variable [var] is not used anymore in the new
		   program. We can safely eliminate it. *)
		match cinit with
		  | Iexpr e ->
		      (* keep the offset initialization *)
		      let new_typ = new_var.var_type in
		      let new_cinit = decode_decl_list rhs_expr in
		      let offset_stat = 
			NSdecl (new_typ,new_var,Some new_cinit,new_s1) in
		      let offset_stat = { s with nst_node = offset_stat } in
		      (* always keep the pointer declaration *)
		      NSdecl (typ,var,None,offset_stat)
		  | _ -> assert false
	      end
	  with Bad_encoding ->
	    (* exception [Bad_encoding] was raised if the encoded
	       assignment was neither maintained nor transformed 
	       into an offset assignment. This can happen if
	       the assignment was detected as useless, and therefore
	       only the right-hand side was returned. Keep it. *)
	    let new_s = statement_expr_or_nop new_e in
	    match new_s with
	      | NSnop ->
		  (* always keep the pointer declaration *)
		  NSdecl (typ,var,None,new_s1)
	      | _ ->
		  let new_stat = { s with nst_node = new_s } in
		  let block_stat = NSblock [new_stat;new_s1] in
		  let block_stat = { s with nst_node = block_stat }
		  in
		  (* always keep the pointer declaration *)
		  NSdecl (typ,var,None,block_stat)
	  end
      | NSswitch (e,c,cases) -> 
	  let new_e = List.hd sub_nodes in
	  let new_cases = List.tl sub_nodes in
	  let new_e = get_e new_e in
	  let new_e = simplify_expr new_e in
	  (* remove [Nfwd] node introduced for each [case] *)
	  let new_cases = 
	    List.map (fun n -> code_children n) new_cases in
	  let new_cases = List.map (List.map get_s) new_cases in
	  let new_cases = 
	    List.map2 (fun (cmap,_) sl -> (cmap,sl)) cases new_cases in
	  NSswitch (new_e,c,new_cases)
      | NSnop | NSlogic_label _ | NSassert _ | NSassume _ | NSreturn None 
      | NSbreak | NScontinue | NSgoto _ | NSlabel _ | NSspec _
      | NSdecl (_,_,None,_) ->
	  raise Use_inherited
    in
    let new_s = { s with nst_node = new_s } in
    create_tmp_node (Nstat new_s)
    with Use_inherited ->
      CFGLangFromNormalized.change_sub_components_in_stat node sub_nodes

  let change_sub_components_in_expr node sub_nodes =
    let e = get_e node in
    try let new_e = match e.nexpr_node with
      | NEbinary (e1,op,e2) ->
	  assert (List.length sub_nodes = 2);
	  let new_e1,new_e2 = list2 sub_nodes in
	  let new_e1,new_e2 = get_e new_e1,get_e new_e2 in
	  begin match op with
	    | Bsub_pointer | Blt_pointer | Bgt_pointer | Ble_pointer 
	    | Bge_pointer | Beq_pointer | Bneq_pointer ->
		(* binary operation on pointers. If both arguments are
		   indices/offsets from the same pointer, translate
		   the pointer operation into an equivalent integer 
		   operation. *)
		let rec destr_ptr_off e = match e.nexpr_node with
		  | NEvar (Var_info v) -> Some (v,None)
		  | NEcast (_,e3) ->
		      (* deals with array address used as pointer *)
		      begin match e3.nexpr_node with
			| NEvar (Var_info v) -> Some (v,None)
			| _ -> None
		      end
		  | NEbinary(e1,Badd_pointer_int,e2) ->
		      (* recursive call *)
		      begin match destr_ptr_off e1 with
			| Some (v,None) ->
			    Some (v,Some e2)
			| Some (v,Some e3) ->
			    let e2 = create_tmp_node (Nexpr e2) in
			    let e3 = create_tmp_node (Nexpr e3) in
			    let e4 = make_int_termexpr_add_termexpr e2 e3
			    in
			    Some (v,Some (get_e e4))
			| None -> None
		      end
		  | _ -> None
		in
		let pointer_op_to_int_op op = match op with
		  | Bsub_pointer -> Bsub_int int_offset_kind
		  | Blt_pointer -> Blt_int
		  | Bgt_pointer -> Bgt_int
		  | Ble_pointer -> Ble_int
		  | Bge_pointer -> Bge_int
		  | Beq_pointer -> Beq_int
		  | Bneq_pointer -> Bneq_int
		  | _ -> assert false
		in
		begin match destr_ptr_off new_e1,destr_ptr_off new_e2 with
		  | Some (v1,e3),Some (v2,e4) ->
		      if ILVar.equal v1 v2 then 
			let op = pointer_op_to_int_op op in
			match e3,e4 with
			  | None,None -> 
			      let e3 = 
				NEconstant (IntConstant (string_of_int 0)) in
			      let e3 = { new_e1 with nexpr_node = e3;
					   nexpr_type = int_offset_type } in
			      let e4 = 
				NEconstant (IntConstant (string_of_int 0)) in
			      let e4 = { new_e2 with nexpr_node = e4;
					   nexpr_type = int_offset_type } in
			      NEbinary (e3,op,e4)
			  | None,Some e4 ->
			      let e3 = 
				NEconstant (IntConstant (string_of_int 0)) in
			      let e3 = { new_e1 with nexpr_node = e3;
					   nexpr_type = int_offset_type } in
			      NEbinary (e3,op,e4)
			  | Some e3,None ->
			      let e4 = 
				NEconstant (IntConstant (string_of_int 0)) in
			      let e4 = { new_e2 with nexpr_node = e4;
					   nexpr_type = int_offset_type } in
			      NEbinary (e3,op,e4)
			  | Some e3,Some e4 ->
			      NEbinary (e3,op,e4)
		      else NEbinary (new_e1,op,new_e2)
		  | _ -> NEbinary (new_e1,op,new_e2)
		end
	    | _ -> NEbinary (new_e1,op,new_e2)
	  end
      | NEnop | NEconstant _ | NEstring_literal _ | NEvar _ | NEarrow _ 
      | NEunary _ | NEincr _ | NEcast _ | NEmalloc _ | NEseq _ | NEassign _
      | NEassign_op _ | NEcall _ | NEcond _ ->
	  raise Use_inherited
    in		
    let new_e = { e with nexpr_node = new_e } in
    create_tmp_node (Nexpr new_e)
    with Use_inherited ->
      CFGLangFromNormalized.change_sub_components_in_expr node sub_nodes

  (* recognize offset from pointer *)
  let rec term_destr_ptr_off t = match t.nterm_node with
    | NTvar v -> Some (v,None)
    | NTcast (_,t3) -> (* deals with array address used as pointer *)
	begin match t3.nterm_node with
	  | NTvar v -> Some (v,None)
	  | _ -> None
	end
    | NTbinop(t1,Clogic.Badd,t2) ->
	(* recursive call *)
	begin match term_destr_ptr_off t1 with
	  | Some (v,None) ->
	      Some (v,Some t2)
	  | Some (v,Some t3) ->
	      let t2 = create_tmp_node (Nterm t2) in
	      let t3 = create_tmp_node (Nterm t3) in
	      let t4 = make_int_termexpr_add_termexpr t2 t3
	      in
	      Some (v,Some (get_t t4))
	  | None -> None
	end
    | _ -> None

  let change_sub_components_in_term node sub_nodes =
    let t = get_t node in
    try let new_t = match t.nterm_node with
      | NTbinop (t1,op,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  begin match op with
	    | Clogic.Bsub ->
		(* could be a binary operation on pointers. 
		   If both arguments are indices/offsets from the same pointer,
		   translate the pointer operation into an equivalent integer 
		   operation. *)
		begin match term_destr_ptr_off new_t1,
		  term_destr_ptr_off new_t2 with
		    | Some (v1,t3),Some (v2,t4) ->
			if ILVar.equal v1 v2 then 
			  match t3,t4 with
			    | None,None -> 
				NTconstant (IntConstant (string_of_int 0))
			    | Some t,None ->
				t.nterm_node
			    | None,Some t ->
				NTunop (Clogic.Uminus,t)
			    | Some t3,Some t4 ->
				NTbinop (t3,op,t4)
			else NTbinop (new_t1,op,new_t2)
		    | _ -> NTbinop (new_t1,op,new_t2)
		end
	    | _ -> NTbinop (new_t1,op,new_t2)
	  end
      | NTrange (t1,t2opt,t3opt,zone,info) ->
	  assert (List.length sub_nodes = 3);
	  let new_t1,new_t2,new_t3 = list3 sub_nodes in
	  let new_t1 = get_t new_t1 in
	  let new_t2 = match logic_children new_t2 with
	    | [new_t2] -> Some (get_t new_t2)
	    | [] -> None
	    | _ -> assert false (* bad encoding *)
	  in
	  let new_t3 = match logic_children new_t3 with
	    | [new_t3] -> Some (get_t new_t3)
	    | [] -> None
	    | _ -> assert false (* bad encoding *)
	  in
	  (* [new_t1] could be an offset from some pointer *)
	  begin match term_destr_ptr_off new_t1 with
	    | Some (v,Some t4) ->
		let new_t2 = match new_t2 with
		  | Some new_t2 -> 
		      let t2 = create_tmp_node (Nterm new_t2) in
		      let t4 = create_tmp_node (Nterm t4) in
		      Some (get_t (make_int_termexpr_add_termexpr t2 t4))
		  | None -> None
		in
		let new_t3 = match new_t3 with
		  | Some new_t3 -> 
		      let t3 = create_tmp_node (Nterm new_t3) in
		      let t4 = create_tmp_node (Nterm t4) in
		      Some (get_t (make_int_termexpr_add_termexpr t3 t4))
		  | None -> None
		in
		let new_t1 = { new_t1 with nterm_node = NTvar v } in
		NTrange (new_t1,new_t2,new_t3,zone,info)
	    | _ ->
		NTrange (new_t1,new_t2,new_t3,zone,info)
	  end
      | NTconstant _ | NTvar _ | NTapp _ | NTunop _ | NTarrow _ | NTold _
      | NTat _ | NTbase_addr _ | NToffset _ | NTblock_length _ | NTarrlen _
      | NTstrlen _ | NTcast _ | NTif _ | NTmin _ | NTmax _ 
      | NTminint _ | NTmaxint _
	  -> raise Use_inherited
    in		
    let new_t = { t with nterm_node = new_t } in
    create_tmp_node (Nterm new_t)
    with Use_inherited ->
      CFGLangFromNormalized.change_sub_components_in_term node sub_nodes

  let change_sub_components_in_pred node sub_nodes =
    let p = get_p node in
    try let new_p = match p.npred_node with
      | NPrel (t1,rel,t2) ->
	  assert (List.length sub_nodes = 2);
	  let new_t1,new_t2 = list2 sub_nodes in
	  let new_t1,new_t2 = get_t new_t1,get_t new_t2 in
	  (* could be a binary operation on pointers. 
	     If both arguments are indices/offsets from the same pointer,
	     translate the pointer operation into an equivalent integer 
	     operation. *)
	  begin match term_destr_ptr_off new_t1,term_destr_ptr_off new_t2 with
	    | Some (v1,t3),Some (v2,t4) ->
		if ILVar.equal v1 v2 then 
		  match t3,t4 with
		    | None,None -> 
			begin match rel with
			  | Le | Ge | Eq -> NPtrue
			  | Lt | Gt | Neq -> NPfalse
			end
		    | None,Some t4 ->
			let t3 = NTconstant (IntConstant (string_of_int 0)) in
			let t3 = { new_t1 with nterm_node = t3;
				     nterm_type = t4.nterm_type } in
			NPrel (t3,rel,t4)
		    | Some t3,None ->
			let t4 = NTconstant (IntConstant (string_of_int 0)) in
			let t4 = { new_t2 with nterm_node = t4;
				     nterm_type = t3.nterm_type } in
			NPrel (t3,rel,t4)
		    | Some t3,Some t4 ->
			NPrel (t3,rel,t4)
		else NPrel (new_t1,rel,new_t2)
	    | _ -> NPrel (new_t1,rel,new_t2)
	  end
      | NPfalse | NPtrue | NPapp _ | NPvalid_index _ | NPand _ | NPor _
      | NPimplies _ | NPiff _ | NPnot _ | NPforall _ | NPexists _ | NPold _
      | NPat _ | NPnamed _ | NPif _ | NPvalid _ | NPfresh _ 
      | NPvalid_range _ | NPseparated _ | NPfull_separated _ 
      | NPbound_separated _ ->
	  raise Use_inherited
    in		
    let new_p = { p with npred_node = new_p } in
    create_tmp_node (Npred new_p)
    with Use_inherited ->
      CFGLangFromNormalized.change_sub_components_in_pred node sub_nodes

  let change_sub_components node sub_nodes =
    match get_node_kind node with
      | NKstat ->
	  change_sub_components_in_stat node sub_nodes
	    
      | NKexpr | NKtest | NKlvalue ->
	  change_sub_components_in_expr node sub_nodes

      | NKterm ->
	  change_sub_components_in_term node sub_nodes
	    
      | NKpred | NKassume | NKassert -> 
	  change_sub_components_in_pred node sub_nodes
	    
      | NKnone | NKdecl | NKannot | NKspec ->
	  CFGLangFromNormalized.change_sub_components node sub_nodes

  let introduce_new_vars node var_list zero_init =
    let d = get_decl node in
    let new_d = match d.s with
      | Some s ->
	  let new_s = 
	    List.fold_left 
	      (fun next_s var -> 
		 let init = 
		   if zero_init then
		     let zero_cst = 
		       NEconstant (IntConstant (string_of_int 0)) in
		     let zero_expr = { nexpr_node = zero_cst; 
				       nexpr_type = int_offset_type;
				       nexpr_loc = Loc.dummy_position }
		     in
		     Some (Iexpr zero_expr)
		   else None
		 in
		 let decl_stat = 
		   NSdecl (var.var_type,var,init,next_s) in
		 { next_s with nst_node = decl_stat }
	      ) s var_list
	  in
	  { d with s = Some new_s }
      | None -> 
	  begin match var_list with
	    | [] -> d
	    | _ -> failwith ("[introduce_new_vars] should be called with"
			     ^ " empty [var_list] in this case")
	  end
    in
    create_tmp_node (Ndecl new_d)

end

module ConnectCFGtoPtr : sig
  include CONNECTION with type node_t = PtrLangFromNormalized.Node.t 
                and type 'a node_hash_t = 'a PtrLangFromNormalized.NodeHash.t 
		and type absval_t = PointWisePtrLattice.t
		and type transform_t = ILVar.t ILVarMap.t

    (* type of a map from variables to their offset variable *)
  type map_t = ILVar.t ILVarMap.t

    (* takes the result of the abstract interpretation.
       returns a formatted analysis easily exploited by [transform].
       The map returned is the map of all offset variables. *)
  val format : absint_analysis_t -> absint_analysis_t * map_t
    (* cleanup variable declarations and declare offset variables *)
  val cleanup : ILVarSet.t -> ILVarSet.t -> map_t -> node_t list -> node_t list
end = struct

  open PtrLangFromNormalized

  type node_t = Node.t
  type 'a node_hash_t = 'a NodeHash.t
  type absval_t = PointWisePtrLattice.t
  type 'a analysis_t = 'a pair_t node_hash_t
  type absint_analysis_t = absval_t analysis_t
  type map_t = ILVar.t ILVarMap.t
  type transform_t = map_t

  (* type used in [transfer] to compute the representant of a pointer *)
  type transfer_rep_t = 
      {
	orig_lhs_var : ILVar.t;
	orig_rhs_var : ILVar.t;
	is_index : bool;
	sum_index : int;
	is_offset : bool
      }

  (* no need for widening here as we are operating on a finite lattice *)
  let widening_threshold = None
  let widening_strategy = WidenFast (* any value would fit *)

  (* exception used in [representative] to end the search *)
  exception End_representative of int

  (* transfer function.
     Only interesting case is the assignment to a pointer variable,
     in which case we discriminate on the form of the right-hand side.
  *)
  let transfer ?(backward=false) ?(with_assert=false) ?(one_pass=false) 
      ?previous_value node cur_val =

    if debug_more then Coptions.lprintf 
      "[transfer] %a@." Node.pretty node;

    (* returns the representative integer/pointer kind for offset/index
       on [var], as described by [rep], with the invariant that [cur_val] 
       does not contain a loop between variables, except possible self-loops.
       [rep] is an accumulator that tells if on the current path of 
       representative integers/pointers, we found index or offset pointer
       kinds. [rep.orig_lhs_var] is the original variable for which we compute 
       a representant.
    *)
    let rec representative rep cur_val var =
      try
	let pval = PointWisePtrLattice.find var cur_val in
	(* [var] has itself a representant *)
	if PtrLattice.is_index pval then
	  let new_var,index = PtrLattice.get_index pval in
	  if ILVar.equal var new_var then
	    (* [var] is a self-index *)
	    raise (End_representative index)
	  else
	    let new_rep = { rep with is_index = true; 
			      sum_index = rep.sum_index + index } in
	    representative new_rep cur_val new_var
	else if PtrLattice.is_offset pval then
	  let new_var,_ = PtrLattice.get_offset_opt pval in
	  if ILVar.equal var new_var && rep.is_offset then
	    (* [var] is a self-offset *)
	    raise (End_representative 0)
	  else
	    let new_rep = { rep with is_offset = true } in
	    representative new_rep cur_val new_var
	else 
	  (* [var] has no representant *)
	  raise (End_representative 0)
      with End_representative last_index ->
	if rep.is_offset then
	  begin
	    if debug_more then Coptions.lprintf 
	      "[transfer] %a is represented by an offset of %a@."
	      ILVar.pretty rep.orig_lhs_var ILVar.pretty var;
	    PtrLattice.make_offset var None
	  end
	else if rep.is_index then
	  begin
	    (* In an assignment
	           q = p;
	       if [p] is a self-index, then count this self-index for [q].
	       But if [p] is an index on [r], which is itself a self-index, 
	       then only count [p]'s index for [q]. *)
	    let idx = 
	      if ILVar.equal rep.orig_rhs_var var then
		rep.sum_index + last_index
	      else
		rep.sum_index 
	    in
	    if debug_more then Coptions.lprintf 
	      "[transfer] %a is represented by an index of %i from %a@."
	      ILVar.pretty rep.orig_lhs_var idx ILVar.pretty var;
	    PtrLattice.make_index var idx
	  end
	else assert false
    in
    match get_node_kind node with
      | NKnone | NKstat | NKassume | NKassert -> cur_val

      | NKdecl ->
	  (* initially define an abstract value for parameters *)
	  let param_list = decl_get_params node in
	  List.fold_left 
	    (fun pw param ->
	       let init_val = PtrLattice.make_defined param None in
	       PointWisePtrLattice.replace param init_val pw
	    ) cur_val param_list

      | NKexpr | NKtest | NKlvalue ->
	  if expr_is_ptr_assign node || expr_is_int_assign node then
	    match assign_get_local_lhs_var node with
	      | None -> cur_val
	      | Some lhs_var ->
		  begin
		    (* compute new value for [lhs_var] *)
		    let rhs_val =
		      match assign_get_rhs_var node with
			| None,None -> 
			    if expr_is_int_assign node then
			      let rhs_node = 
				skip_casts (assign_get_rhs_operand node) 
			      in
			      if expr_is_int_constant rhs_node then
				(* avoid creating a self-offset variable for
				   an integer variable that is initialized
				   with some integer constant *)
				PtrLattice.top ()
			      else 
				PtrLattice.make_defined lhs_var None
			    else
			      PtrLattice.make_defined lhs_var None
			| Some rhs_var,None -> 
			    let rep = { orig_lhs_var = lhs_var; 
					orig_rhs_var = rhs_var; 
					is_index = false; 
					sum_index = 0; is_offset = true } in
			    representative rep cur_val rhs_var
			| Some rhs_var,Some index ->
			    let rep = { orig_lhs_var = lhs_var; 
					orig_rhs_var = rhs_var; 
					is_index = true;
					sum_index = index; is_offset = false }
			    in
			    representative rep cur_val rhs_var
			| _ -> assert false
		    in
		    let representative_or_var var rep_val = 
		      if PtrLattice.has_variable rep_val then
			PtrLattice.get_variable rep_val 
		      else var
		    in
		    let remove_ref_to_var rem_var var abs_val =
		      let rep_var = representative_or_var var abs_val in
		      if ILVar.equal rem_var rep_var then
			PtrLattice.top ()
		      else
			abs_val
		    in
		    (* remove information on variables previously represented
		       by [lhs_var] if this last variable representant 
		       changes *)
		    let old_val = PointWisePtrLattice.find lhs_var cur_val in
		    let old_rep_var = representative_or_var lhs_var old_val in
		    let new_rep_var = representative_or_var lhs_var rhs_val in
		    let cur_val =
		      if ILVar.equal old_rep_var new_rep_var
			&& not (PtrLattice.is_defined rhs_val) then
			cur_val
		      else
			PointWisePtrLattice.mapi 
			  (remove_ref_to_var lhs_var) cur_val
		    in
		    PointWisePtrLattice.replace lhs_var rhs_val cur_val
		  end
	  else cur_val
	    
      | NKspec | NKannot | NKterm | NKpred -> cur_val

  (* format function.
     Only the post-part of the analysis is relevant here.
     - If a variable is always referenced as an (un)defined or an index 
     pointer, leave it as such.
     - If a variable is always references as an (un)defined, index or offset
     pointer, make it always an offset pointer, with a fixed offset variable.
     - If a variable is sometimes references as a complex pointer, make it
     a complex pointer everywhere.

     Self-index/offset pointers are those that only reference themselves.

     This allows to exploit the pointer kind information locally to transform 
     the program.
  *)
  let format analysis =

    (* sets of variables of interest *)
    let index_vars = ref VarSet.empty in
    let self_index_vars = ref VarSet.empty in
    let offset_vars = ref VarSet.empty in
    let self_offset_vars = ref VarSet.empty in
    let defined_vars = ref VarSet.empty in
    let complex_vars = ref VarSet.empty in

    (* correspondance between representative variable and the variables 
       it represents at some point *)
    let rep_to_based = Hashtbl.create 0 in

    (* basic operations on sets of variables *)
    let add_to_set var set =
      set := VarSet.add var (!set) in
    let print_set set msg =
      Coptions.lprintf 
	"[format] detected %i %s integer/pointer(s)@."
	(VarSet.cardinal set) msg;
      if not (VarSet.is_empty set) then 
	Coptions.lprintf "[format] %a@." 
	  (fun fmt -> (VarSet.iter (Coptions.lprintf "%a " ILVar.pretty))) set
    in
    let add_to_table rep_var var =
      let var_set =
	try 
	  VarSet.add var (Hashtbl.find rep_to_based rep_var)
	with Not_found ->
	  VarSet.add var VarSet.empty 
      in
      Hashtbl.replace rep_to_based rep_var var_set
    in

    (* only keep variables where read/written *)
    let analysis = 
      NodeHash.fold_post
	(fun node pwval new_analysis ->
	   match get_node_kind node with
	     | NKexpr | NKtest | NKlvalue | NKterm -> 
		 let new_pwval =
		   if termexpr_is_local_var node then
		     let var = termexpr_var_get node in
		     let pval = PointWisePtrLattice.find var pwval in 
		     PointWisePtrLattice.singleton var pval
		   else if (expr_is_ptr_assign node 
			    || expr_is_int_assign node) then
		     match assign_get_local_lhs_var node with
		       | None -> PointWisePtrLattice.bottom ()
		       | Some lhs_var ->
			   let pval = PointWisePtrLattice.find lhs_var pwval in
			   PointWisePtrLattice.singleton lhs_var pval
		   else PointWisePtrLattice.bottom ()
		 in
		 (* also store abstract value for rhs variable in assignment *)
		 let new_pwval =
		   match get_node_kind node with
		     | NKexpr | NKtest | NKlvalue ->
			 if expr_is_assign node then
			   match assign_get_rhs_var node with
			     | Some rhs_var,_ -> 
				 let pval = 
				   PointWisePtrLattice.find rhs_var pwval in 
				 PointWisePtrLattice.replace 
				   rhs_var pval new_pwval
			     | _ -> new_pwval
			 else new_pwval
		     | _ -> new_pwval
		 in
		 NodeHash.replace_post new_analysis node new_pwval;
		 new_analysis
	     | NKstat | NKpred | NKassume | NKassert
	     | NKnone | NKdecl | NKannot | NKspec ->
		 if is_logic_scope node then
		   NodeHash.replace_post new_analysis node pwval
		 else 
                   (* do not include abstract values for these constructs *)
		   (); 
		 new_analysis
	) analysis (NodeHash.create 0)
    in

    (* collect variables in the sets they match *)
    NodeHash.iter_post
	(fun _ pwval ->
	   PointWisePtrLattice.iter
	     (fun var pval ->
		(* if [pval] uses a representative variable, 
		   store this correspondance *)
		if PtrLattice.has_variable pval then
		  begin
		    let rep_var = PtrLattice.get_variable pval in
		    add_to_table rep_var var
		  end;
		(* dispatch [var] in sets according to [pval] *)
		if PtrLattice.is_index pval then
		  if PtrLattice.is_self_index var pval then
		    add_to_set var self_index_vars
		  else
		    add_to_set var index_vars
		else if PtrLattice.is_offset pval then
		  if PtrLattice.is_self_offset var pval then
		    add_to_set var self_offset_vars
		  else
		    add_to_set var offset_vars
		else if PtrLattice.is_defined pval then
		  add_to_set var defined_vars
		else if PtrLattice.is_top pval then
		  add_to_set var complex_vars
		else (* undefined integer/pointer value *)
		  ()
	     ) pwval
	) analysis;

    (* remove references *)
    let index_vars = !index_vars in
    let self_index_vars = !self_index_vars in
    let offset_vars = !offset_vars in
    let self_offset_vars = !self_offset_vars in
    let defined_vars = !defined_vars in
    let complex_vars = !complex_vars in
    if debug_more then print_set index_vars "basic index";
    if debug_more then print_set self_index_vars "basic self-index";
    if debug_more then print_set offset_vars "basic offset";
    if debug_more then print_set self_offset_vars "basic self-offset";
    if debug_more then print_set defined_vars "basic defined";
    if debug_more then print_set complex_vars "basic complex";

    (* all variables represented by a complex one are complex *)
    let rec close_set corresp set =
      let new_set =
	VarSet.fold (fun v vs -> 
		       try
			 let ns = Hashtbl.find corresp v in
			 VarSet.fold VarSet.add ns vs
		       with Not_found -> vs
		    ) set set
      in
      if VarSet.equal new_set set then
	set
      else
	close_set corresp new_set
    in
    let complex_vars = close_set rep_to_based complex_vars in

    (* compute cross-referencing integers/pointers *)
    let cross_vars = VarSet.union offset_vars index_vars in
    (* cross-referencing integers/pointers (index/offset) cannot be self-one *)
    let cross_rest = VarSet.inter self_offset_vars cross_vars in
    let self_offset_vars = VarSet.diff self_offset_vars cross_rest in
    let offset_vars = VarSet.union offset_vars cross_rest in
    (* offset integers/pointers must never be complex *)
    let offset_vars = VarSet.diff offset_vars complex_vars in
    if debug_more then print_set offset_vars "offset"; 
    (* self-offset integers/pointers must never be complex *)
    let self_offset_vars = VarSet.diff self_offset_vars complex_vars in
    if debug_more then print_set self_offset_vars "self-offset";
    (* compute sum of offset and self-offset integers/pointers *)
    let all_offset_vars = VarSet.union offset_vars self_offset_vars in

    (* index integers/pointers must never be offset *)
    let index_vars = VarSet.diff index_vars all_offset_vars in
    (* self-index integers/pointers must never be offset *)
    let self_index_vars = VarSet.diff self_index_vars all_offset_vars in
    (* index integers/pointers must never be complex *)
    let index_vars = VarSet.diff index_vars complex_vars in
    if debug_more then print_set index_vars "index";
    (* self_index integers/pointers must never be complex *)
    let self_index_vars = VarSet.diff self_index_vars complex_vars in
    if debug_more then print_set self_index_vars "self-index";

    (* defined integers/pointers must never be complex *)
    let defined_vars = VarSet.diff defined_vars complex_vars in

    (* associate a unique offset variable to every offset integer/pointer *)
    let offset_map =
      VarSet.fold (fun var m ->
		     let offset_var = 
		       Info.default_var_info (var.var_unique_name ^ "_offset")
		     in
		     Info.set_assigned offset_var;
		     Cenv.set_var_type 
		       (Var_info offset_var) int_offset_type false;
		     ILVarMap.add var offset_var m
		  ) offset_vars ILVarMap.empty 
    in
    let offset_map =
      VarSet.fold (fun var m ->
		     let offset_var = 
		       Info.default_var_info 
			 (var.var_unique_name ^ "_self_offset") in
		     Info.set_assigned offset_var;
		     Cenv.set_var_type 
		       (Var_info offset_var) int_offset_type false;
		     ILVarMap.add var offset_var m
		  ) self_offset_vars offset_map
    in

    (* compute the formatted analysis from the information just gathered *)
    let formatted_analysis = 
      NodeHash.fold_post
	(fun node pwval new_analysis ->
	   let new_pwval =
	     PointWisePtrLattice.mapi
	       (fun var pval ->
		  if VarSet.mem var index_vars then
		    (* pval = undefined/self-index/index/defined *)
		    pval 
		  else if VarSet.mem var self_index_vars then
		    (* pval = undefined/self-index/defined *)
		    pval
		  else if VarSet.mem var offset_vars then
		    (* pval = all except complex *)
		    if PtrLattice.is_defined pval then
		      (* explicit the offset variable for initialization *)
		      let offset_var = ILVarMap.find var offset_map in
		      PtrLattice.make_defined var (Some offset_var)
		    else if PtrLattice.is_index pval 
		      || PtrLattice.is_offset pval then
			(* explicit the offset variable *)
			let avar = PtrLattice.get_variable pval in
			let offset_var = ILVarMap.find var offset_map in
			PtrLattice.make_offset avar (Some offset_var)
		    else (* undefined *)
		      pval
		  else if VarSet.mem var self_offset_vars then
		    (* pval = undefined/self-index/self-offset/defined *)
		    if PtrLattice.is_defined pval then
		      (* explicit the offset variable for initialization *)
		      let offset_var = ILVarMap.find var offset_map in
		      PtrLattice.make_defined var (Some offset_var)
		    else if PtrLattice.is_index pval 
		      || PtrLattice.is_offset pval then
			(* explicit the offset variable *)
			let avar = PtrLattice.get_variable pval in
			assert (ILVar.equal avar var);
			let offset_var = ILVarMap.find var offset_map in
			PtrLattice.make_offset var (Some offset_var)
		    else (* undefined *)
		      pval
		  else if VarSet.mem var defined_vars then
		    (* pval = all except complex *)
		    PtrLattice.make_defined var None
		  else if VarSet.mem var complex_vars then
		    (* pval = any one is possible
		       Destroy any information associated to [var]. *)
		    PtrLattice.top ()
		  else
		    (* pval = undefined *)
		    pval
	       ) pwval
	   in
	   NodeHash.replace_post new_analysis node new_pwval;
	   new_analysis
	) analysis (NodeHash.create 0)
    in
    formatted_analysis,offset_map

  (* exception used to share the default treatment in [sub_transform] *)
  exception Rec_transform

  (* type used to propagate information in [sub_transform] and
     its sub-functions *)
  type inter_transform_t =
      {
        offset_nodes : NodeSet.t ref;
	offset_vars : VarSet.t ref;
	offset_map : ILVar.t ILVarMap.t;
	label_corresp : Node.t StringMap.t;
	block_begin : Node.t;
	block_end : Node.t;
	has_old : bool;
	has_at : String.t option
      }

  (* types used to pass information from [sub_transform] to 
     its sub-functions *)
  type addon_t = 
      {
	is_replacement : bool;
	addon_node : Node.t
      }
  type local_nodes_t =
      {
	node : Node.t;
	sub_nodes : Node.t list;
	new_sub_nodes : (Node.t * addon_t option) list
      }

  (* transformation on an individual node.
     takes as input a node [node] in the graph(s) previously created.
     returns a pair consisting of:
     - a main part, i.e. a potentially new node that is not connected 
     to the graph(s), that corresponds to the transformed node.
     - an addon part, i.e. a potentially new node that corresponds to
     the resulting pointer.

     On an assignment 
          [p = q], 
     the main part could be e.g. 
          [p_offset = q_offset] 
     and the addon part could be 
          [r + p_offset].
     The statement 
          [p = q;] 
     would be transformed into 
          [p_offset = q_offset;]
     and the statement 
          [return (p = q);] 
     into the more complex:
          [return (p_offset = q_offset,r + p_offset);]

     The results of the data-flow analysis [analysis] contain only 
     the relevant information for code transformation. Other intermediate
     results of the analysis have been erased after the analysis phase.
     Only the post-part of the analysis is relevant here.
  *)

  (* 4 sub-functions that share code between cases below *)

  (* every offset variable that is used in the code must be stored in 
     the repository [offset_vars], to be declared later on by a call to
     [introduce_new_vars]. *)
  let store_offset_var params offset_var =
    params.offset_vars := VarSet.add offset_var (!(params.offset_vars))

  (* returns an integer/pointer read that corresponds to the abstract value
     [aval], with other elements taken in [node] 
  *)
  let make_integer_pointer_read params node pval =
    if PtrLattice.is_alias pval then
      (* rewrite alias of v as v *)
      let new_var = PtrLattice.get_alias pval in
      change_in_intptr_var node new_var
    else if PtrLattice.is_index pval then
      (* rewrite constant offset i of v as v+i *)
      let new_var,index = PtrLattice.get_index pval in
      change_in_intptr_var_add_cst node new_var index
    else if PtrLattice.is_offset pval then
      (* rewrite offset o of v as v+o *)
      let new_var,offset_var = PtrLattice.get_offset pval in
      store_offset_var params offset_var;
      change_in_intptr_var_add_var node new_var offset_var
    else if PtrLattice.is_defined pval then
      (* rewrite possible assignment to v as v *)
      let new_var,_ = PtrLattice.get_defined_opt pval in
      change_in_intptr_var node new_var
    else node

  (* returns an integer/pointer read that corresponds to the base 
     integer/pointer for the abstract value [aval], with other elements
     taken in [node] 
  *)
  let make_base_integer_pointer_read node pval =
    if PtrLattice.is_alias pval then
      (* rewrite alias of v as v *)
      let new_var = PtrLattice.get_alias pval in
      change_in_intptr_var node new_var
    else if PtrLattice.is_index pval then
      (* rewrite constant offset i of v as v *)
      let new_var,_ = PtrLattice.get_index pval in
      change_in_intptr_var node new_var
    else if PtrLattice.is_offset pval then
      (* rewrite offset o of v as v *)
      let new_var,_ = PtrLattice.get_offset pval in
      change_in_intptr_var node new_var
    else if PtrLattice.is_defined pval then
      (* rewrite possible assignment to v as v *)
      let new_var,_ = PtrLattice.get_defined_opt pval in
      change_in_intptr_var node new_var
    else node

  (* returns an offset assignment from the abstract value [rhs_val]
     to the variable [offset_lhs_var], with the possible addition
     of an expression [add_offset_opt]. Other elements are taken
     in [node]. 
  *)
  let make_offset_assign params 
      node offset_lhs_var rhs_val add_offset_opt is_incr_decr =
    if PtrLattice.is_index rhs_val then
      begin
	(* it cannot be an increment/decrement *)
	assert (not is_incr_decr);
	(* [offset_lhs_var] is assigned a constant *)
	let (_,index) = PtrLattice.get_index rhs_val in
	match add_offset_opt with
	  | None -> 
	      change_in_int_var_assign_cst node offset_lhs_var index
	  | Some offset_expr ->
	      let add_expr = make_int_expr_add_cst offset_expr index
	      in
	      change_in_int_var_assign_expr node offset_lhs_var add_expr
      end
    else if PtrLattice.is_offset rhs_val then
      (* [offset_lhs_var] is assigned another offset *)
      let (_,offset_rhs_var) = 
	PtrLattice.get_offset rhs_val in
      store_offset_var params offset_rhs_var;
      match add_offset_opt with
	| None -> 
	    (* only possible case for an increment/decrement *)
	    if is_incr_decr then
	      change_in_int_incr_assign node offset_lhs_var
	    else
	      change_in_int_var_assign_var 
		node offset_lhs_var offset_rhs_var
	| Some offset_expr ->
	    begin
	      (* it cannot be an increment/decrement *)
	      assert (not is_incr_decr);
	      let add_expr = 
		make_int_expr_add_var offset_expr offset_rhs_var in
	      change_in_int_var_assign_expr node offset_lhs_var add_expr
	    end
    else
      begin
	(* it cannot be an increment/decrement *)
	assert (not is_incr_decr);
	match add_offset_opt with
	  | None -> (* [offset_lhs_var] is reset *)
	      change_in_int_var_assign_cst node offset_lhs_var 0
	  | Some offset_expr ->
	      change_in_int_var_assign_expr 
		node offset_lhs_var offset_expr
      end

  (* returns an integer/pointer assignment from [new_rhs_node] to [lhs_node],
     with [new_rhs_not_offset] telling if the new right-hand side
     is the translation for the original expression, or an offset expression.
     Other elements are taken in [node]. 
  *)
  let keep_integer_pointer_assignment params 
      node aval new_rhs_not_offset lhs_node new_rhs_node is_incr_decr =
    (* integer/pointer assignment must be kept.
       The only possible problem is that the right-hand side might be 
       an integer/pointer assignment too, that could have been transformed
       into an offset assignment. *)
    if new_rhs_not_offset then
      if is_incr_decr then
	(* nothing to change *)
	node
      else
	(* keep old lhs and new rhs *)
	change_sub_components node [lhs_node;new_rhs_node]
    else
      begin
	(* it cannot be an increment/decrement *)
	assert (not is_incr_decr);
	(* The rhs is now an offset assignment.
	   In all cases, the analysis must have computed 
	   an index or an offset for the right-hand side.
	   Create an equivalent sequence of assignments. *)
	let rhs_val =
	  match assign_get_rhs_var node with
	    | Some rhs_var,_ -> 
		PointWisePtrLattice.find rhs_var aval
	    | _ -> assert false
	in
	let ptr_node = make_integer_pointer_read params node rhs_val in
	let assign_node = 
	  change_sub_components node [lhs_node;ptr_node]
	in
	make_seq_expr new_rhs_node assign_node
      end

  (* part of the transformation that deals with expressions.
     Can raise exception Rec_transform for a common treatment with
     [sub_transform]. *)
  let sub_transform_on_expr analysis params local_nodes =
    let node = local_nodes.node in
    let sub_nodes = local_nodes.sub_nodes in
    let new_sub_nodes = local_nodes.new_sub_nodes in

    if debug_more then Coptions.lprintf
      "[sub_transform_on_expr] node %a@." Node.pretty node;

    (* transformation is possible only if analysis provides some information. 
       Otherwise raise Not_found. *)
    let aval = match NodeHash.find_post analysis node with
      | None -> raise Rec_transform
      | Some v -> v
    in

    (* beginning of transformation for expressions *)
    
    (* reading some integer/pointer variable.
       This is called also on integer/pointer write, but the result
       of this rewrite is ignored by the calling [sub_transform]
       on integer/pointer assignment. *)
    if termexpr_is_local_var node then
      let var = termexpr_var_get node in
      if var_is_pointer var || var_is_integer var then
	let pval = PointWisePtrLattice.find var aval in 
	(make_integer_pointer_read params node pval,
	 None) (* nothing to add to make it a pointer *)
      else
	raise Rec_transform

    (* writing some integer/pointer variable *)
    else if (expr_is_ptr_assign node || expr_is_int_assign node)
	     && (match assign_get_local_lhs_var node with
	           | None -> false | Some _ -> true)
    then
      (* 2 possibilities: it may be a real assignment or 
	 an increment/decrement operation *)
      let is_incr_decr = List.length sub_nodes = 1 in
      assert (List.length sub_nodes = List.length new_sub_nodes);
      let lhs_node,new_rhs_node =
	if is_incr_decr then
	  (* increment/decrement *)
	  list1 sub_nodes,list1 new_sub_nodes
	else
	  (* assignment *)
	  fst (list2 sub_nodes),snd (list2 new_sub_nodes)
      in
      (* separate main part from addon part *)
      let new_rhs_node,new_rhs_node_addon = new_rhs_node in
      let new_rhs_is_assign = expr_is_assign new_rhs_node in
      let new_rhs_not_offset =
	not (NodeSet.mem new_rhs_node !(params.offset_nodes))
      in
      match assign_get_local_lhs_var node with
	| None -> 
	    (keep_integer_pointer_assignment params node aval
	       new_rhs_not_offset lhs_node new_rhs_node is_incr_decr,
	     None) (* nothing to add to make it an integer/pointer *)
	      
	| Some lhs_var ->
	    let lhs_val = PointWisePtrLattice.find lhs_var aval in 

	    (* share addon part used in offset/defined cases *)
	    let wrap_addon offset_node = 
	      let addon_part = 
		if is_incr_decr then
		  (* [new_rhs_node] must be a variable here, 
		     in the offset/defined cases *)
		  (* the original treatment here was 
		         { is_replacement = false;
		           addon_node = 
		             change_in_intptr_incr node new_rhs_node true }
		     but this led to some problems with Simplify trying
		     to prove the subsequent goal.
		     Therefore it was change to the following, which gives
		     Simplify an easier goal.
		     e.g. the C code
		         *p++ = 0;
		     was changed to
		         *(p_offset++, q+p_offset-1) = 0;
		     and is now translated into
		         *(q+(p_offset++)) = 0;
		  *)
		  let base_node = make_base_integer_pointer_read node lhs_val 
		  in
		  { is_replacement = true;
		    addon_node = 
		    make_intptr_expr_add_expr base_node offset_node }
		else
		  { is_replacement = false;
		    addon_node = 
		    make_integer_pointer_read params node lhs_val }
	      in
	      (* store the information that this node is an offset node *)
	      params.offset_nodes := 
		NodeSet.add offset_node !(params.offset_nodes);
	      offset_node,Some addon_part in

	    if PtrLattice.is_index lhs_val then
	      (* assignment to [lhs_var] not useful, since reading
		 [lhs_var] will be replaced by reading its alias
		 or its constant offset from some variable.
		 Contains the test [PtrLattice.is_alias lhs_val]. *)
	      if is_incr_decr then
		(change_in_intptr_incr node new_rhs_node false,
		 None) (* nothing to add to make it a pointer *)
	      else
		(* just propagate the right-hand side *)
		(new_rhs_node,new_rhs_node_addon)

	    else if PtrLattice.is_offset lhs_val then
	      let (new_lhs_var,offset_lhs_var) =
		PtrLattice.get_offset lhs_val in
	      store_offset_var params offset_lhs_var;
	      (* right-hand side can only be another variable or
		 another integer/pointer assignment.
		 In either case, the analysis must have computed 
		 an index or an offset for the right-hand side. *)
	      let rhs_val =
		match assign_get_rhs_var node with
		  | Some rhs_var,_ -> 
		      PointWisePtrLattice.find rhs_var aval
		  | _ -> assert false
	      in
	      (* The integer/pointer assignment must be changed into
		 an offset assignment. 4 cases are possible depending
		 on the new right-hand side computed :
		 - the rhs is still an integer/pointer assignment, e.g. in
		 p = q = malloc(...);
		 Create an equivalent sequence of assignments, e.g.:
		 q = malloc(...), p_offset = 0;
		 - the rhs is itself an offset assignment. Use it.
		 - the rhs is the sum of a integer/pointer variable 
		 and an integer expression. Ignore the integer/pointer 
		 and keep the expression.
		 - the rhs is another integer/pointer expression. Ignore it. *)
	      if new_rhs_is_assign then
		begin
		  (* it cannot be an increment/decrement *)
		  assert (not is_incr_decr);
		  if new_rhs_not_offset then	
		    (* The rhs is still an integer/pointer assignment.
		       Create an equivalent sequence of assignments. *)
		    let offset_node = 
		      make_offset_assign params 
			node offset_lhs_var rhs_val None false
		    in
		    wrap_addon (make_seq_expr new_rhs_node offset_node)
		  else
		    (* The rhs is itself an offset assignment. Use it. *)
		    wrap_addon (change_in_int_var_assign_expr 
				  node offset_lhs_var new_rhs_node)
		end
	      else
		(* The rhs can be:
		   - the sum of an integer/pointer variable 
		   and an integer expression
		   - or any other integer/pointer expression.
		   The former case can be the original source code or 
		   due to the transformation of an offset pointer 
		   or an index pointer.
		*)
		if is_incr_decr then
		  (* do not compute [off_opt] here, it would be equal
		     to the offset variable, because the rhs has been
		     translated to the sum var + offset_var *)
		  wrap_addon (make_offset_assign params 
				node offset_lhs_var rhs_val None true)
		else
		  let var_opt,off_opt = 
		    get_intptr_add_on_var_opt new_rhs_node in
		  let off_opt = match off_opt with
		    | None -> None
		    | Some off_node ->
			if expr_is_local_var off_node then
			  (* rule out transformation of offset *)
			  let off_var = termexpr_var_get off_node in
			  if VarSet.mem off_var (!(params.offset_vars)) then
			    (* here, offset is only the transformation
			       on the sub-node. Same as the increment/
			       decrement case. Ignore it. *)
			    None
			  else off_opt
			else 
			  match var_opt with
			    | Some var ->
				(* rule out transformation of index *)
				let var_val = PointWisePtrLattice.find 
				  var aval in 
				if PtrLattice.is_index var_val
				  && not (PtrLattice.is_alias var_val)
				then None
				else off_opt
			    | None -> off_opt
		  in    
		  wrap_addon 
		    (make_offset_assign params 
		       node offset_lhs_var rhs_val off_opt false)

	    else if PtrLattice.is_defined lhs_val then
	      let assign_node = 
		keep_integer_pointer_assignment params node aval 
		  new_rhs_not_offset lhs_node new_rhs_node is_incr_decr
	      in
	      match PtrLattice.get_defined_opt lhs_val with
		| _,Some offset_var ->
		    store_offset_var params offset_var;
		    let reset_node = 
		      change_in_int_var_assign_cst node offset_var 0 in
		    (* sequence in this order due to possible effects
		       in [assign_node]. Use [wrap_addon] to return 
		       pointer. *) 
		    wrap_addon (make_seq_expr assign_node reset_node)
		| _,None -> (* like in the default assignment case *)
		    (assign_node,
		     None) (* nothing to add to make it a pointer *)

	    else (* default: not any of index/offset/defined pointer *)
	      (keep_integer_pointer_assignment params node aval 
		 new_rhs_not_offset lhs_node new_rhs_node is_incr_decr,
	       None) (* nothing to add to make it a pointer *)

    else raise Rec_transform

  let sub_transform_on_term analysis params local_nodes =
    let node = local_nodes.node in

    if termexpr_is_local_var node then
      let var = termexpr_var_get node in
      if var_is_pointer var || var_is_integer var then
	let pval = 
	  if params.has_old then
	    begin
	      assert (params.has_at = None);
	      if debug then Coptions.lprintf
		  "[sub_transform_on_term] has old@.";
	      let begin_val = 
		match NodeHash.find_post analysis params.block_begin with
		  | None -> PointWisePtrLattice.bottom ()
		  | Some v -> v
	      in
	      PointWisePtrLattice.find var begin_val
	    end
	  else match params.has_at with
	  | Some lab ->
	      begin
		assert (not params.has_old);
		if debug then Coptions.lprintf
		    "[sub_transform_on_term] has at@.";
		let at_node = StringMap.find lab params.label_corresp in
		let at_val = match NodeHash.find_post analysis at_node with
		  | None -> PointWisePtrLattice.bottom ()
		  | Some v -> v
		in
		PointWisePtrLattice.find var at_val
	      end
	  | None ->
	      let end_val = 
		match NodeHash.find_post analysis params.block_end with
		  | None -> PointWisePtrLattice.bottom ()
		  | Some v -> v
	      in
	      PointWisePtrLattice.find var end_val
	in
	let new_node = make_integer_pointer_read params node pval in
	if debug then Coptions.lprintf
	    "[sub_transform_on_term] term %a@.into %a@."
	    Node.pretty node Node.pretty new_node;
	(new_node,
	 None) (* addon part useless here *)
      else raise Rec_transform
    else raise Rec_transform

  let rec sub_transform analysis params node =

    let params = match get_node_kind node with
      | NKstat ->
	  if stat_is_assert node then
	    let beg_node = logic_begin node in
	    let end_node = logic_end node in
	    { params with block_begin = beg_node; block_end = end_node }
	  else if stat_is_label node then (* both kinds of labels ? *)
	    let lab = stat_get_label node in
	    { params with label_corresp = 
		StringMap.add lab node params.label_corresp }
	  else params 
      | NKpred | NKassume | NKassert | NKterm ->
	  if termpred_is_old node then
	    { params with has_old = true }
	  else if termpred_is_at node then
	    let lab = termpred_get_label node in
	    { params with has_at = Some lab }
	  else params
      | _ -> params
    in

    (* apply [sub_transform] recursively on sub-nodes *)
    let sub_nodes = (code_children node) @ (logic_children node) in
    let new_sub_nodes = match get_node_kind node with
      | NKannot ->
	  let beg_function = logic_begin node and beg_loop = logic_end node in
	  let inv_params = 
	    { params with block_begin = beg_function; block_end = beg_loop } in
	  let assinv_params = inv_params in
	  let ass_params = inv_params in
	  let var_params = inv_params in
	  let params = [inv_params; assinv_params; ass_params; var_params] in
	  List.map2 (sub_transform analysis) params sub_nodes
      | NKspec ->
	  let beg_block = logic_begin node and end_block = logic_end node in
	  let req_params = 
	    { params with block_begin = beg_block; block_end = beg_block } in
	  let ass_params = req_params in
	  let ens_params = 
	    { params with block_begin = beg_block; block_end = end_block } in
	  let dec_params = req_params in
	  let params = [req_params; ass_params; ens_params; dec_params] in
	  List.map2 (sub_transform analysis) params sub_nodes	  
      | _ ->
	  List.map (sub_transform analysis params) sub_nodes
    in
    let local_nodes =
      {
	node = node;
	sub_nodes = sub_nodes;
	new_sub_nodes = new_sub_nodes
      } 
    in

    (* treat declaration/statement/expression separately *)
    try match get_node_kind node with
      | NKnone | NKstat | NKpred 
      | NKassume | NKassert | NKannot | NKspec -> 
	  raise Rec_transform

      | NKdecl -> 
	  (* no type change needed here, keep only main part *)
	  let new_sub_nodes = List.map fst new_sub_nodes in
	  let new_node = change_sub_components node new_sub_nodes in
	  let param_list = decl_get_params node in
	  let param_offset_vars =
	    List.fold_left 
	      (fun set param ->
		 try 
		   let offset_var = ILVarMap.find param params.offset_map in
		   if VarSet.mem offset_var (!(params.offset_vars)) then
		     VarSet.add offset_var set
		   else set
		 with Not_found -> set
	      ) VarSet.empty param_list
	  in
	  (* only introduce offset variables for parameters here.
	     Other offset variables are already declared locally, or will
	     be declared in [cleanup]. *)
	  let new_node = introduce_new_vars new_node 
	    (VarSet.elements param_offset_vars) (* zero_init= *)true
	  in
	  (new_node,None) (* addon part has no meaning here *)

      | NKexpr | NKtest | NKlvalue ->
	  sub_transform_on_expr analysis params local_nodes

      | NKterm ->
	  sub_transform_on_term analysis params local_nodes
	  

    with Rec_transform -> 
      (* return same expression on new sub-nodes.
	 Only subtlety is to use the addon part of an expression sub-node when
	 the new sub-node is an offset node (which the original sub-node
	 obviously was not). *)
      let new_sub_nodes =
	List.map2 (fun sub_node (new_main,new_addon) ->
		     begin match get_node_kind sub_node with
		       | NKexpr | NKtest | NKlvalue ->
			   if NodeSet.mem new_main !(params.offset_nodes) then
			       match new_addon with
				 | Some new_addon ->
				     let addon_node = new_addon.addon_node in
				     if new_addon.is_replacement then
				       addon_node
				     else
				       make_seq_expr new_main addon_node
				 | None -> assert false
			   else
			     new_main
		       | _ -> new_main
		     end
		  ) sub_nodes new_sub_nodes
      in
      (change_sub_components node new_sub_nodes,
       None) (* addon part empty here *)

  let transform analysis offset_map decls =
    List.map (fun decl ->
		let params = 
		  { 
		    offset_nodes = ref NodeSet.empty;
		    offset_vars = ref VarSet.empty;
		    offset_map = offset_map;
		    label_corresp = StringMap.empty;
		    block_begin = decl; (* dummy value *)
		    block_end = decl; (* dummy value *)
		    has_old = false;
		    has_at = None
		  } 
		in
		let new_decl,_ = 
		  sub_transform analysis params decl in
		new_decl
	     ) decls

  let cleanup used_vars decl_vars offset_map decls =
    
    let rec sub_cleanup node = 

      let sub_nodes = (code_children node) @ (logic_children node) in
      let new_sub_nodes = List.map sub_cleanup sub_nodes in
      let node = change_sub_components node new_sub_nodes in

      match get_node_kind node with
	| NKstat ->
	    if stat_is_decl node then
	      let var = decl_stat_get_var node in
	      if debug_more then Coptions.lprintf
		"[sub_cleanup] declaration of variable %s used ? %B@."
		var.var_name (ILVarSet.mem var used_vars);
	      let node =
		if ILVarSet.mem var used_vars then 
		  node 
		else 
		  decl_stat_get_next node 
	      in
	      try
		let offset_var = ILVarMap.find var offset_map in
		if ILVarSet.mem offset_var decl_vars then
		  node
		else
		  make_var_decl node offset_var
	      with Not_found -> node
	    else node
	| _ -> node
    in
    List.map sub_cleanup decls
end

module LocalPtrAnalysis = Make_DataFlowAnalysis(Var)(PtrLangFromNormalized)
    (Make_LatticeFromSemiLattice(PointWisePtrLattice))(ConnectCFGtoPtr)


(*****************************************************************************
 *                                                                           *
 * 		External interface for local pointer analysis		     *
 *                                                                           *
 *****************************************************************************)

let local_aliasing fundecl =

  if debug_more then Coptions.lprintf 
    "[local_aliasing] treating function %s@." fundecl.f.fun_name; 

  (* build control-flow graph *)
  let decls = PtrLangFromNormalized.from_file [fundecl] in
  let decls = List.map fst decls in
  (* perform local pointer analysis *)
  let raw_analysis = LocalPtrAnalysis.compute decls in
  (* format results of the analysis *)
  let analysis,offset_map = ConnectCFGtoPtr.format raw_analysis in
  (* transform the program using the analysis *)
  let decls = ConnectCFGtoPtr.transform analysis offset_map decls in
  (* return the new program *)
  let file = PtrLangFromNormalized.to_file decls in

  (* rebuild control-flow graph *)
  let decls = PtrLangFromNormalized.from_file file in
  let decls = List.map fst decls in
  (* collect the local variables used/declared *)
  let used_vars,decl_vars = PtrLangFromNormalized.collect_vars () in
  if debug_more then Coptions.lprintf
    "[local_aliasing_transform] %i local variables used@." 
    (ILVarSet.cardinal used_vars);
  if debug_more && not (ILVarSet.is_empty used_vars) then 
    Coptions.lprintf "[local_aliasing_transform] %a@." 
      (fun _ -> (ILVarSet.iter (Coptions.lprintf "%a " ILVar.pretty))) 
      used_vars;
  (* add the necessary declarations *)
  let decls = ConnectCFGtoPtr.cleanup used_vars decl_vars offset_map decls in
  (* return the new program *)
  PtrLangFromNormalized.to_file decls

let local_aliasing_transform () =
  (* necessary prefix to translate the hash-table of functions in 
     the normalized code into a list of function representatives,
     as defined by type [func_t] in [Cabsint] *)
  let file = Hashtbl.fold 
    (fun name (spec,typ,f,s,loc) funcs ->
       { name = name; spec = spec; typ = typ; f = f; s = s; loc = loc } 
       :: funcs
    ) Cenv.c_functions []
  in

  if debug_more then Coptions.lprintf 
    "[local_aliasing_transform] %i functions to treat@." (List.length file); 

  let file = List.fold_right
    (fun fundecl acc -> (local_aliasing fundecl) @ acc) file [] in

  if debug_more then Coptions.lprintf 
    "[local_aliasing_transform] %i functions treated@." (List.length file);

  (* necessary suffix to translate the list of function representatives
     to the hash-table format *)
  List.iter (fun { name = name; spec = spec; typ = typ; 
		   f = f; s = s; loc = loc } ->
	       Cenv.add_c_fun name (spec,typ,f,s,loc)) file

(* Local Variables: *)
(* compile-command: "make -C .." *)
(* End: *)