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: *)
|