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
|
(**************************************************************************)
(* *)
(* The Why platform for program certification *)
(* *)
(* Copyright (C) 2002-2011 *)
(* *)
(* Jean-Christophe FILLIATRE, CNRS & Univ. Paris-sud 11 *)
(* Claude MARCHE, INRIA & Univ. Paris-sud 11 *)
(* Yannick MOY, Univ. Paris-sud 11 *)
(* Romain BARDOU, Univ. Paris-sud 11 *)
(* *)
(* Secondary contributors: *)
(* *)
(* Thierry HUBERT, Univ. Paris-sud 11 (former Caduceus front-end) *)
(* Nicolas ROUSSET, Univ. Paris-sud 11 (on Jessie & Krakatoa) *)
(* Ali AYAD, CNRS & CEA Saclay (floating-point support) *)
(* Sylvie BOLDO, INRIA (floating-point support) *)
(* Jean-Francois COUCHOT, INRIA (sort encodings, hyps pruning) *)
(* Mehdi DOGGUY, Univ. Paris-sud 11 (Why GUI) *)
(* *)
(* This software is free software; you can redistribute it and/or *)
(* modify it under the terms of the GNU Lesser General Public *)
(* License version 2.1, with the special exception on linking *)
(* described in file LICENSE. *)
(* *)
(* 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. *)
(* *)
(**************************************************************************)
(* Import from Cil *)
open Cil_types
open Cil
open Cilutil
open Ast_info
open Extlib
open Visitor
(* Utility functions *)
open Format
let constant_expr ?(loc=Cil_datatype.Location.unknown) e =
Ast_info.constant_expr ~loc e
exception NotImplemented of string
exception Unsupported of string
let fatal fmt = Jessie_options.fatal ~current:true fmt
let notimplemented fmt =
Jessie_options.with_failure
(fun evt -> raise (NotImplemented evt.Log.evt_message)) ~current:true fmt
let unsupported fmt =
Jessie_options.with_failure
(fun evt ->
raise (Unsupported evt.Log.evt_message)
) ~current:true fmt
let warning fmt = Jessie_options.warning ~current:true fmt
let warn_general fmt = Jessie_options.warning ~current:false fmt
let warn_once =
let known_warns = Hashtbl.create 7 in
fun s ->
if not (Hashtbl.mem known_warns s) then begin
Hashtbl.add known_warns s ();
warn_general "%s" s
end
(*****************************************************************************)
(* Options *)
(*****************************************************************************)
let flatten_multi_dim_array = ref false
(*****************************************************************************)
(* Source locations *)
(*****************************************************************************)
let is_unknown_location loc =
(fst loc).Lexing.pos_lnum = 0
(*****************************************************************************)
(* Types *)
(*****************************************************************************)
(* type for ghost variables until integer is a valid type for ghosts *)
let almost_integer_type = TInt(ILongLong,[])
let struct_type_for_void = ref voidType
(* Query functions on types *)
let rec app_term_type f default = function
| Ctype typ -> f typ
| Ltype ({lt_name = "set"},[t]) -> app_term_type f default t
| Ltype _ | Lvar _ | Linteger | Lreal | Larrow _ -> default
let rec force_app_term_type f = function
| Ctype typ -> f typ
| Ltype ({ lt_name = "set"},[t]) -> force_app_term_type f t
| Ltype _ | Lvar _ | Linteger | Lreal | Larrow _ as ty ->
Jessie_options.fatal "Unexpected non-C type %a" !Ast_printer.d_logic_type ty
let get_unique_field ty = match unrollType ty with
| TComp(compinfo,_,_) ->
begin match compinfo.cfields with
| [content_fi] -> content_fi
| _ ->
Jessie_options.fatal "Type %a has no unique field" !Ast_printer.d_type ty
end
| _ -> Jessie_options.fatal "non-struct (unique field)"
let get_struct_name = function
| TComp(compinfo,_,_) -> compinfo.cname
| _ -> Jessie_options.fatal "non-struct (name)"
let get_struct_info = function
| TComp(compinfo,_,_) -> compinfo
| _ -> Jessie_options.fatal "non-struct (info)"
(* Integral types *)
let size_in_bytes ik =
let size = function
| IBool -> assert false
| IChar | ISChar | IUChar -> 1 (* Cil assumes char is one byte *)
| IInt | IUInt -> theMachine.theMachine.sizeof_int
| IShort | IUShort -> theMachine.theMachine.sizeof_short
| ILong | IULong -> theMachine.theMachine.sizeof_long
| ILongLong | IULongLong -> theMachine.theMachine.sizeof_longlong
in
size ik
let integral_type_size_in_bytes ty =
match unrollType ty with
| TInt(IBool,_attr) -> (* TODO *)
Extlib.not_yet_implemented "Common.integral_type_size_in_bytes IBool"
| TInt(ik,_attr) -> size_in_bytes ik
| TEnum ({ekind = IBool},_) ->
Extlib.not_yet_implemented "Common.integral_type_size_in_bytes IBool"
| TEnum (ei,_) -> size_in_bytes ei.ekind
| _ -> assert false
let integral_type_size_in_bits ty =
integral_type_size_in_bytes ty * 8
let min_value_of_integral_type ?bitsize ty =
let min_of signed size_in_bytes =
let numbits =
match bitsize with Some siz -> siz | None -> size_in_bytes * 8
in
if signed then
My_bigint.neg
(My_bigint.power_int_positive_int 2
(numbits - 1))
else My_bigint.zero
in
match unrollType ty with
| TInt(IBool,_attr) -> My_bigint.zero
| TInt(ik,_attr) ->
min_of (isSigned ik) (size_in_bytes ik)
| TEnum ({ ekind = IBool},_) -> My_bigint.zero
| TEnum ({ekind=ik},_) ->
min_of (isSigned ik) (size_in_bytes ik)
| _ -> assert false
let max_value_of_integral_type ?bitsize ty =
let max_of signed size_in_bytes =
let numbits =
match bitsize with Some siz -> siz | None -> size_in_bytes * 8
in
if signed then
My_bigint.pred
(My_bigint.power_int_positive_int 2
(numbits - 1))
else
My_bigint.pred
(My_bigint.power_int_positive_int 2
numbits)
in
match unrollType ty with
| TInt(IBool,_attr) -> My_bigint.one
| TInt(ik,_attr) ->
max_of (isSigned ik) (size_in_bytes ik)
| TEnum ({ekind=IBool},_) -> My_bigint.one
| TEnum ({ekind=ik},_) -> max_of (isSigned ik) (size_in_bytes ik)
| _ -> assert false
let all_integral_types = Hashtbl.create 5
let name_of_integral_type ?bitsize ty =
let name_it signed size_in_bytes =
let numbits =
match bitsize with Some siz -> siz | None -> size_in_bytes * 8
in
let name = (if signed then "" else "u") ^ "int" ^ (string_of_int numbits) in
Hashtbl.replace all_integral_types name (ty,numbits);
name
in
match unrollType ty with
| TInt(IBool,_attr) -> "_bool"
| TInt(ik,_attr) ->
name_it (isSigned ik) (size_in_bytes ik)
| TEnum ({ekind= IBool},_) -> "_bool"
| TEnum ({ekind = ik},_) -> name_it (isSigned ik) (size_in_bytes ik)
| _ -> assert false
(* Reference type *)
(* We introduce a reference type, that is different from the C pointer or
* array type. It is a direct translation in C of the Jessie bounded pointer
* type, where the lower/upper bounds that can be safely accessed are
* statically known. To avoid introducing a new type, we reuse the existing
* C pointer type, with an attribute "arrlen" to give the size.
* Then, we use it as a regular pointer type. E.g., we allow dynamic allocation
* of such references:
* r = (TRef(T)) (malloc (sizeof(T)));
* and usual dereference:
* T t = *r;
* Another advantage is it should be working fine with [typeOf], [typeOfLval],
* [pointed_type] and similar functions.
*
* As a result of this transformation, all allocation/releases of memory
* on a reference type do implicitly allocate/release the fields of reference
* type. It will be translated in Jessie in various alloc/free statements.
*)
let arraylen_attr_name = "arraylen"
let mkTRef elemty _msg =
(* Define the same arguments as for [mkTRefArray] *)
(*
Format.eprintf "mkTRef, coming from %s@." msg;
*)
let size = constant_expr My_bigint.one and attr = [] in
(* Do the same as in [mkTRefArray] *)
let siz = expToAttrParam size in
let attr = addAttribute (Attr(arraylen_attr_name,[siz])) attr in
(* Avoid creating an array for single pointed elements that do not
* originate in a C array, to avoid having to access to the first
* element everywhere.
*)
TPtr(elemty,attr)
let mkTRefArray (elemty,size,attr) =
(* Check the array size is of a correct form *)
ignore (lenOfArray64 (Some size));
let siz = expToAttrParam size in
let attr = addAttribute (Attr(arraylen_attr_name,[siz])) attr in
(* Make the underlying type an array so that indexing it is still valid C. *)
TPtr(TArray(elemty,Some size,empty_size_cache (),[]),attr)
let reference_size ty =
match findAttribute arraylen_attr_name (typeAttrs ty) with
| [AInt i] ->
(*
eprintf "AInt has argument %d@." i;
*)
Int64.of_int i
| _ -> assert false
let is_reference_type ty =
isPointerType ty && hasAttribute arraylen_attr_name (typeAttrs ty)
let is_array_reference_type ty =
is_reference_type ty && isArrayType (direct_pointed_type ty)
let reference_of_array ty =
let rec reftype ty =
if isArrayType ty then
let elty = reftype (direct_element_type ty) in
(* if array_size ty > 0L then *)
let size = constant_expr (direct_array_size ty) in
mkTRefArray(elty,size,[])
(* else *)
(* (\* Array of zero size, e.g. in struct array hack. *\) *)
(* TPtr(elty,[]) *)
else ty
in
assert (isArrayType ty);
reftype ty
(* Wrappers on [mkCompInfo] that update size/offset of fields *)
let mkStructEmpty stname =
mkCompInfo true stname (fun _ -> []) []
let mkStructSingleton ?(padding=0) stname finame fitype =
let compinfo =
mkCompInfo true stname
(fun _ -> [finame,fitype,None,[],CurrentLoc.get ()]) []
in
let fi = get_unique_field (TComp(compinfo,empty_size_cache (),[])) in
fi.fsize_in_bits <- Some (bitsSizeOf fitype);
fi.foffset_in_bits <- Some 0;
fi.fpadding_in_bits <- Some padding;
compinfo
(* Locally use 64 bits integers *)
open Integer
let bits_sizeof ty =
let rec rec_size ?(top_size=false) ty =
match unrollType ty with
| TPtr _ ->
if is_reference_type ty && not top_size then
rec_size (pointed_type ty) * (reference_size ty)
else
Int64.of_int (bitsSizeOf ty)
| TArray _ -> assert false (* Removed by translation *)
| TFun _ -> unsupported "Function pointer type %a not allowed" !Ast_printer.d_type ty
| TNamed _ -> assert false (* Removed by call to [unrollType] *)
| TComp(compinfo,_,_) ->
let size_from_field fi =
match
fi.foffset_in_bits, fi.fsize_in_bits, fi.fpadding_in_bits
with
| Some off, Some siz, Some padd ->
Int64.of_int off + Int64.of_int siz + Int64.of_int padd
| _ -> assert false
in
if compinfo.cstruct then
match List.rev compinfo.cfields with
| [] -> 0L
| fi :: _ -> size_from_field fi
else
List.fold_left max 0L (List.map size_from_field compinfo.cfields)
| TEnum _ | TVoid _ | TInt _ | TFloat _ | TBuiltin_va_list _ ->
Int64.of_int (bitsSizeOf ty)
in
rec_size ~top_size:true ty
(* Come back to normal 31 bits integers *)
open Pervasives
(*****************************************************************************)
(* Names *)
(*****************************************************************************)
(* Predefined entities *)
let name_of_valid_string = "valid_string"
let name_of_valid_wstring = "valid_wstring"
let name_of_strlen = "strlen"
let name_of_wcslen = "wcslen"
let name_of_assert = "assert"
let name_of_free = "free"
let name_of_malloc = "malloc"
let name_of_calloc = "calloc"
let name_of_realloc = "realloc"
let predefined_name =
[ (* coding functions *)
name_of_assert;
name_of_malloc;
name_of_calloc;
name_of_realloc;
name_of_free;
]
let is_predefined_name s = List.mem s predefined_name
let is_assert_function v = isFunctionType v.vtype && v.vname = name_of_assert
let is_free_function v = isFunctionType v.vtype && v.vname = name_of_free
let is_malloc_function v = isFunctionType v.vtype && v.vname = name_of_malloc
let is_calloc_function v = isFunctionType v.vtype && v.vname = name_of_calloc
let is_realloc_function v = isFunctionType v.vtype && v.vname = name_of_realloc
(* Name management *)
let unique_name_generator is_exception =
let unique_names = Hashtbl.create 127 in
let rec aux s =
if is_exception s then s else
try
let s = if s = "" then "unnamed" else s in
let count = Hashtbl.find unique_names s in
let s = s ^ "_" ^ (string_of_int !count) in
if Hashtbl.mem unique_names s then
aux s
else
(Hashtbl.add unique_names s (ref 0);
incr count; s)
with Not_found ->
Hashtbl.add unique_names s (ref 0); s
in
let add s = Hashtbl.add unique_names s (ref 0) in
aux, add
let unique_name, add_unique_name =
(* let u = *)unique_name_generator is_predefined_name
(* in (fun s -> let s' = u s in s') *)
let unique_logic_name, add_unique_logic_name =
(* let u = *) unique_name_generator (fun _ -> false)
(* in (fun s -> let s' = u s in s')*)
let unique_name_if_empty s =
if s = "" then unique_name "unnamed" else s
(* Jessie reserved names *)
let jessie_reserved_names =
[
(* a *) "and"; "as"; "assert"; "assigns"; "assumes"; "axiom";
(* b *) "behavior"; "boolean"; "break";
(* c *) "case"; "catch"; "continue";
(* d *) "default"; "do";
(* e *) "else"; "end"; "ensures"; "exception";
(* f *) "false"; "finally"; "for"; "free";
(* g *) "goto";
(* i *) "if"; "in"; "integer"; "invariant";
(* l *) "lemma"; "let"; "logic";
(* m *) "match";
(* n *) "new"; "null";
(* o *) "of";
(* p *) "pack";
(* r *) "reads"; "real"; "rep"; "requires"; "return";
(* s *) "switch";
(* t *) "tag"; "then"; "throw"; "throws"; "true"; "try"; "type";
(* u *) "unit"; "unpack";
(* v *) "var"; "variant";
(* w *) "while"; "with";
]
let () = List.iter add_unique_name jessie_reserved_names
let () = List.iter add_unique_logic_name jessie_reserved_names
(*
let reserved_name name =
if List.mem name jessie_reserved_names then name else unique_name name
let reserved_logic_name name =
if List.mem name jessie_reserved_names then name else unique_logic_name name
*)
(* Type name *)
let string_explode s =
let rec next acc i =
if i >= 0 then next (s.[i] :: acc) (i-1) else acc
in
next [] (String.length s - 1)
let string_implode ls =
let s = String.create (List.length ls) in
ignore (List.fold_left (fun i c -> s.[i] <- c; i + 1) 0 ls);
s
let filter_alphanumeric s assoc default =
let is_alphanum c =
String.contains "abcdefghijklmnopqrstuvwxyz" c
|| String.contains "ABCDEFGHIJKLMNOPQRSTUVWXYZ" c
|| String.contains "123456789" c
|| c = '_'
in
let alphanum_or_default c =
if is_alphanum c then c
else try List.assoc c assoc with Not_found -> default
in
string_implode (List.map alphanum_or_default (string_explode s))
let type_name ty =
ignore (flush_str_formatter ());
fprintf str_formatter "%a" !Ast_printer.d_type ty;
let name = flush_str_formatter () in
filter_alphanumeric name [('*','x')] '_'
let logic_type_name ty =
ignore (flush_str_formatter ());
let old_mode = Kernel.Unicode.get() in
Kernel.Unicode.set false;
fprintf str_formatter "%a" !Ast_printer.d_logic_type ty;
let name = flush_str_formatter () in
Kernel.Unicode.set old_mode;
filter_alphanumeric name [('*','x')] '_'
let name_of_padding_type = (*reserved_logic_name*) "padding"
let name_of_string_declspec = "valid_string"
let name_of_hint_assertion = "hint"
let name_of_safety_behavior = "safety"
let name_of_default_behavior = "default"
(*****************************************************************************)
(* Visitors *)
(*****************************************************************************)
(* Visitor for adding globals and global initializations (for global
* variables). It delays updates to the file until after the visitor has
* completed its work, to avoid visiting a newly created global or
* initialization.
*)
let attach_detach_mode = ref false
let globinits : stmt list ref = ref []
let globals : global list ref = ref []
let globactions : (unit -> unit) list ref = ref []
(*
let attach_globinit init =
assert (!attach_detach_mode);
globinits := init :: !globinits
*)
let attach_global g =
assert (!attach_detach_mode);
globals := g :: !globals
let attach_globaction action =
assert (!attach_detach_mode);
globactions := action :: !globactions
(*
let detach_globinits file =
let gif =
Kernel_function.get_definition (Globals.Functions.get_glob_init file)
in
Format.eprintf "Common.detach_globinits: len(globinits) = %d@."
(List.length !globinits);
gif.sbody.bstmts <- List.rev_append !globinits gif.sbody.bstmts;
globinits := []
*)
let detach_globals file =
file.globals <- !globals @ file.globals;
List.iter
(function GVar(v,init,_) -> Globals.Vars.add v init | _ -> ()) !globals;
globals := []
let detach_globactions () =
List.iter (fun f -> f ()) !globactions;
globactions := []
let do_and_update_globals action file =
attach_detach_mode := true;
assert (!globinits = [] && !globals = [] && !globactions = []);
action file;
(*
detach_globinits file;
*)
detach_globals file;
detach_globactions ();
attach_detach_mode := false
let visit_and_update_globals visitor file =
do_and_update_globals (visitFramacFile visitor) file
(* Visitor for adding statements in front of the body *)
let adding_statement_mode = ref false
let pending_statements_at_beginning : stmt list ref = ref []
let pending_statements_at_end : stmt list ref = ref []
let add_pending_statement ~beginning s =
assert (!adding_statement_mode);
if beginning then
pending_statements_at_beginning := s :: !pending_statements_at_beginning
else
pending_statements_at_end := s :: !pending_statements_at_end
let insert_pending_statements f =
f.sbody.bstmts <-
List.rev_append !pending_statements_at_beginning f.sbody.bstmts;
pending_statements_at_beginning := [];
match !pending_statements_at_end with [] -> () | slist ->
(* Insert pending statements before return statement *)
let return_stat =
Kernel_function.find_return (Globals.Functions.get f.svar)
in
(* Remove labels from the single return statement. Leave them on the most
* external block with cleanup code instead.
*)
let s = { return_stat with labels = []; } in
let block = mkBlock (List.rev_append (s :: slist) []) in
return_stat.skind <- Block block;
pending_statements_at_end := []
class proxy_frama_c_visitor (visitor : Visitor.frama_c_visitor) =
object
(* [VP 2011-08-24] Do not inherit from Visitor.frama_c_visitor: all methods
that are not overloaded have to come from visitor. Otherwise, proxy will
fail to delegate some of its actions. *)
method set_current_kf kf = visitor#set_current_kf kf
method set_current_func f = visitor#set_current_func f
method current_kf = visitor#current_kf
method current_func = visitor#current_func
method push_stmt s = visitor#push_stmt s
method pop_stmt s = visitor#pop_stmt s
method current_stmt = visitor#current_stmt
method current_kinstr = visitor#current_kinstr
method get_filling_actions = visitor#get_filling_actions
method fill_global_tables = visitor#fill_global_tables
method vlogic_label = visitor#vlogic_label
(* Modify visitor on functions so that it prepends/postpends statements *)
method vfunc f =
adding_statement_mode := true;
assert (!pending_statements_at_beginning = []);
assert (!pending_statements_at_end = []);
let change c = fun f -> adding_statement_mode:=false; c f in
match visitor#vfunc f with
| SkipChildren -> ChangeToPost(f, change (fun x -> x))
| JustCopy -> JustCopyPost (change (fun x -> x))
| JustCopyPost f -> JustCopyPost (change f)
| ChangeTo f' -> ChangeToPost (f', change (fun x -> x))
| ChangeToPost(f',action) -> ChangeToPost(f', change action)
| DoChildren ->
let postaction_func f =
insert_pending_statements f;
adding_statement_mode := false;
f
in
ChangeDoChildrenPost (f, postaction_func)
| ChangeDoChildrenPost (f', action) ->
let postaction_func f =
let f = action f in
insert_pending_statements f;
adding_statement_mode := false;
f
in
ChangeDoChildrenPost (f', postaction_func)
(* Inherit all other visitors *)
(* Methods introduced by the Frama-C visitor *)
method vfile = visitor#vfile
method vrooted_code_annotation = visitor#vrooted_code_annotation
method vglob_aux = visitor#vglob_aux
method vstmt_aux = visitor#vstmt_aux
(* Methods from Cil visitor for coding constructs *)
method vblock = visitor#vblock
method vvrbl = visitor#vvrbl
method vvdec = visitor#vvdec
method vexpr = visitor#vexpr
method vlval = visitor#vlval
method voffs = visitor#voffs
method vinitoffs = visitor#vinitoffs
method vinst = visitor#vinst
method vinit = visitor#vinit
method vtype = visitor#vtype
method vattr = visitor#vattr
method vattrparam = visitor#vattrparam
(* Methods from Cil visitor for logic constructs *)
method vlogic_type = visitor#vlogic_type
method vterm = visitor#vterm
method vterm_node = visitor#vterm_node
method vterm_lval = visitor#vterm_lval
method vterm_lhost = visitor#vterm_lhost
method vterm_offset = visitor#vterm_offset
method vlogic_info_decl = visitor#vlogic_info_decl
method vlogic_info_use = visitor#vlogic_info_use
method vlogic_var_decl = visitor#vlogic_var_decl
method vlogic_var_use = visitor#vlogic_var_use
method vquantifiers = visitor#vquantifiers
method vpredicate = visitor#vpredicate
method vpredicate_named = visitor#vpredicate_named
(*
method vpredicate_info_decl = visitor#vpredicate_info_decl
method vpredicate_info_use = visitor#vpredicate_info_use
*)
method vbehavior = visitor#vbehavior
method vspec = visitor#vspec
method vassigns = visitor#vassigns
method vloop_pragma = visitor#vloop_pragma
method vslice_pragma = visitor#vslice_pragma
method vdeps = visitor#vdeps
method vcode_annot = visitor#vcode_annot
method vannotation = visitor#vannotation
method behavior = visitor#behavior
method frama_c_plain_copy = visitor#frama_c_plain_copy
method is_annot_before = visitor#is_annot_before
method plain_copy_visitor = visitor#plain_copy_visitor
method queueInstr = visitor#queueInstr
method reset_current_func = visitor#reset_current_func
method reset_current_kf = visitor#reset_current_kf
method unqueueInstr = visitor#unqueueInstr
method vcompinfo = visitor#vcompinfo
method venuminfo = visitor#venuminfo
method venumitem = visitor#venumitem
method vfieldinfo = visitor#vfieldinfo
method vfrom = visitor#vfrom
method vglob = visitor#vglob
method vimpact_pragma = visitor#vimpact_pragma
method vlogic_ctor_info_decl = visitor#vlogic_ctor_info_decl
method vlogic_ctor_info_use = visitor#vlogic_ctor_info_use
method vlogic_type_def = visitor#vlogic_type_def
method vlogic_type_info_decl = visitor#vlogic_type_info_decl
method vlogic_type_info_use = visitor#vlogic_type_info_use
method vstmt = visitor#vstmt
end
let visit_and_push_statements_visitor visitor =
new proxy_frama_c_visitor visitor
let visit_and_push_statements visit visitor file =
let visitor = new proxy_frama_c_visitor visitor in
visit visitor file
(* Visitor for tracing computation *)
class trace_frama_c_visitor (visitor : Visitor.frama_c_visitor) =
object
inherit Visitor.generic_frama_c_visitor
(Project.current ()) (Cil.inplace_visit ()) as super
(* Inherit all visitors, printing visited item on the way *)
(* Methods introduced by the Frama-C visitor *)
method vfile = visitor#vfile
method vrooted_code_annotation = visitor#vrooted_code_annotation
method vglob_aux g =
Jessie_options.feedback "%a" !Ast_printer.d_global g;
visitor#vglob_aux g
method vstmt_aux s =
Jessie_options.feedback "%a" !Ast_printer.d_stmt s;
visitor#vstmt_aux s
(* Methods from Cil visitor for coding constructs *)
method vfunc = visitor#vfunc
method vblock b =
Jessie_options.feedback "%a" !Ast_printer.d_block b;
visitor#vblock b
method vvrbl v =
Jessie_options.feedback "%s" v.vname;
visitor#vvrbl v
method vvdec v =
Jessie_options.feedback "%s" v.vname;
visitor#vvdec v
method vexpr e =
Jessie_options.feedback "%a" !Ast_printer.d_exp e;
visitor#vexpr e
method vlval lv =
Jessie_options.feedback "%a" !Ast_printer.d_lval lv;
visitor#vlval lv
method voffs off =
Jessie_options.feedback "%a" !Ast_printer.d_offset off;
visitor#voffs off
method vinitoffs off =
Jessie_options.feedback "%a" !Ast_printer.d_offset off;
visitor#vinitoffs off
method vinst i =
Jessie_options.feedback "%a" !Ast_printer.d_instr i;
visitor#vinst i
method vinit = visitor#vinit
method vtype ty =
Jessie_options.feedback "%a" !Ast_printer.d_type ty;
visitor#vtype ty
method vattr attr =
Jessie_options.feedback "%a" !Ast_printer.d_attr attr;
visitor#vattr attr
method vattrparam pattr =
Jessie_options.feedback "%a" !Ast_printer.d_attrparam pattr;
visitor#vattrparam pattr
(* Methods from Cil visitor for logic constructs *)
method vlogic_type lty =
Jessie_options.feedback "%a" !Ast_printer.d_logic_type lty;
visitor#vlogic_type lty
method vterm t =
Jessie_options.feedback "%a" !Ast_printer.d_term t;
visitor#vterm t
method vterm_node = visitor#vterm_node
method vterm_lval tlv =
Jessie_options.feedback "%a" !Ast_printer.d_term_lval tlv;
visitor#vterm_lval tlv
method vterm_lhost = visitor#vterm_lhost
method vterm_offset = visitor#vterm_offset
method vlogic_info_decl = visitor#vlogic_info_decl
method vlogic_info_use = visitor#vlogic_info_use
method vlogic_var_decl lv =
Jessie_options.feedback "%a" !Ast_printer.d_logic_var lv;
visitor#vlogic_var_decl lv
method vlogic_var_use lv =
Jessie_options.feedback "%a" !Ast_printer.d_logic_var lv;
visitor#vlogic_var_use lv
method vquantifiers = visitor#vquantifiers
method vpredicate = visitor#vpredicate
method vpredicate_named p =
Jessie_options.feedback "%a" !Ast_printer.d_predicate_named p;
visitor#vpredicate_named p
(*
method vpredicate_info_decl = visitor#vpredicate_info_decl
method vpredicate_info_use = visitor#vpredicate_info_use
*)
method vbehavior = visitor#vbehavior
method vspec funspec =
Jessie_options.feedback "%a" !Ast_printer.d_funspec funspec;
visitor#vspec funspec
method vassigns = visitor#vassigns
method vloop_pragma = visitor#vloop_pragma
method vslice_pragma = visitor#vslice_pragma
method vdeps = visitor#vdeps
method vcode_annot annot =
Jessie_options.feedback "%a" !Ast_printer.d_code_annotation annot;
visitor#vcode_annot annot
method vannotation annot =
Jessie_options.feedback "%a" !Ast_printer.d_annotation annot;
visitor#vannotation annot
end
let visit_and_trace_framac visit visitor file =
let visitor = new trace_frama_c_visitor visitor in
visit visitor file
let visit_and_trace_cil visit visitor file =
let visitor = new trace_frama_c_visitor visitor in
visit (visitor :> cilVisitor) file
(* Visitor for fixpoint computation *)
let change = ref false
let signal_change () = change := true
let visit_until_convergence visitor file =
change := true;
while !change do
change := false;
visitFramacFile visitor file;
done
(* Visitor methods for sharing preaction/postaction between exp/term/tsets *)
let do_on_term_offset (preaction_offset,postaction_offset) tlv =
let preaction_toffset tlv =
match preaction_offset with None -> tlv | Some preaction_offset ->
let lv,env =
!Db.Properties.Interp.force_term_offset_to_offset tlv
in
let lv = preaction_offset lv in
!Db.Properties.Interp.force_back_offset_to_term_offset env lv
in
let postaction_toffset tlv =
match postaction_offset with None -> tlv | Some postaction_offset ->
let lv,env = !Db.Properties.Interp.force_term_offset_to_offset tlv in
let lv = postaction_offset lv in
!Db.Properties.Interp.force_back_offset_to_term_offset env lv
in
ChangeDoChildrenPost (preaction_toffset tlv, postaction_toffset)
let do_on_term_lval (preaction_lval,postaction_lval) tlv =
let preaction_tlval tlv =
match preaction_lval with None -> tlv | Some preaction_lval ->
let lv,env = !Db.Properties.Interp.force_term_lval_to_lval tlv in
let lv = preaction_lval lv in
!Db.Properties.Interp.force_back_lval_to_term_lval env lv
in
let postaction_tlval tlv =
match postaction_lval with None -> tlv | Some postaction_lval ->
let lv,env = !Db.Properties.Interp.force_term_lval_to_lval tlv in
let lv = postaction_lval lv in
!Db.Properties.Interp.force_back_lval_to_term_lval env lv
in
ChangeDoChildrenPost (preaction_tlval tlv, postaction_tlval)
let do_on_term (preaction_expr,postaction_expr) t =
let preaction_term t =
match preaction_expr with None -> t | Some preaction_expr ->
let e,env = !Db.Properties.Interp.force_term_to_exp t in
let e = map_under_info preaction_expr e in
!Db.Properties.Interp.force_back_exp_to_term env e
in
let postaction_term t =
match postaction_expr with None -> t | Some postaction_expr ->
let e,env = !Db.Properties.Interp.force_term_to_exp t in
let e = map_under_info postaction_expr e in
!Db.Properties.Interp.force_back_exp_to_term env e
in
ChangeDoChildrenPost (preaction_term t, postaction_term)
(*****************************************************************************)
(* Debugging *)
(*****************************************************************************)
let checking = true
let print_to_stdout file =
(* Printer takes into account annotations *)
let printer = new Printer.print () in
Log.print_on_output (Extlib.swap (Cil.d_file printer) file)
class checkTypes =
let preaction_expr e = ignore (typeOf e); e in
object
inherit Visitor.generic_frama_c_visitor
(Project.current ()) (Cil.inplace_visit ()) as super
method vexpr e =
ChangeDoChildrenPost (preaction_expr e, fun x -> x)
(* I comment on purpose additional verifications, because they cause some tests
to fail, see e.g. [copy_struct], because term-lhost TResult does not have
a type for a proper conversion to expression. *)
(* method vterm = *)
(* do_on_term (Some preaction_expr,None) *)
end
let check_types file =
(* check types *)
let visitor = new checkTypes in
visitFramacFile visitor file;
(* check general consistency *)
(* Cil.visitCilFile (new File.check_file :> Cil.cilVisitor) file *)
class check_file: Visitor.frama_c_visitor =
object(self)
inherit Visitor.generic_frama_c_visitor
(Project.current ()) (Cil.inplace_visit ()) as super
val known_fields = Cil_datatype.Fieldinfo.Hashtbl.create 7
method vterm t =
match t.term_node with
| TLval _ ->
begin match t.term_type with
| Ctype ty when isVoidType ty ->
Jessie_options.fatal ~current:true "Term %a has void type" !Ast_printer.d_term t
| _ -> DoChildren
end
| _ -> DoChildren
method voffs = function
NoOffset -> SkipChildren
| Index _ -> DoChildren
| Field(fi,_) ->
begin
try
if not (fi == Cil_datatype.Fieldinfo.Hashtbl.find known_fields fi)
then
Jessie_options.warning ~current:true
"Field %s of type %s is not shared between declaration and use"
fi.fname fi.fcomp.cname
with Not_found ->
Jessie_options.warning ~current:true
"Field %s of type %s is unbound in the AST"
fi.fname fi.fcomp.cname
end;
DoChildren
method vterm_offset = function
TNoOffset -> SkipChildren
| TIndex _ -> DoChildren
| TField(fi,_) ->
begin
try
if not (fi == Cil_datatype.Fieldinfo.Hashtbl.find known_fields fi)
then
Jessie_options.warning ~current:true
"Field %s of type %s is not shared between declaration and use"
fi.fname fi.fcomp.cname
with Not_found ->
Jessie_options.warning ~current:true
"Field %s of type %s is unbound in the AST"
fi.fname fi.fcomp.cname
end;
DoChildren
method vinitoffs = self#voffs
method vglob_aux = function
GCompTag(c,_) ->
List.iter
(fun x -> Cil_datatype.Fieldinfo.Hashtbl.add known_fields x x) c.cfields;
DoChildren
| _ -> DoChildren
end
(*****************************************************************************)
(* Miscellaneous *)
(*****************************************************************************)
(* Queries *)
let is_base_addr t = match (stripTermCasts t).term_node with
| Tbase_addr _ -> true
| _ -> false
(* Smart constructors *)
(* Redefine statement constructor of CIL to create them with valid sid *)
let mkStmt = mkStmt ~valid_sid:true
let mkterm tnode ty loc =
{
term_node = tnode;
term_loc = loc;
term_type = ty;
term_name = []
}
let term_of_var v =
let lv = cvar_to_lvar v in
if app_term_type isArrayType false lv.lv_type then
let ptrty =
TPtr(force_app_term_type element_type lv.lv_type,[])
in
mkterm (TStartOf(TVar lv,TNoOffset)) (Ctype ptrty) v.vdecl
else
mkterm (TLval(TVar lv,TNoOffset)) lv.lv_type v.vdecl
let mkpred pnode loc =
{
ip_name = [];
ip_loc = loc;
ip_id = Logic_const.fresh_predicate_id ();
ip_content = pnode;
}
let mkInfo e =
match e.enode with
Info _ -> e
| _ ->
let einfo = { exp_type = Ctype voidType; exp_name = [] } in
(* In many cases, the correct type may not be available, as
* the expression may result from a conversion from a term or a tset.
* Calling [typeOf] on such an expression may raise an error.
* Therefore, put here a dummy type until tsets correctly typed.
*)
new_exp ~loc:e.eloc (Info(e,einfo))
(* Manipulation of offsets *)
let rec offset_list = function
| NoOffset -> []
| Field (fi,off) -> (Field (fi, NoOffset)) :: offset_list off
| Index (e,off) -> (Index (e, NoOffset)) :: offset_list off
let is_last_offset = function
| NoOffset -> true
| Field (_fi,NoOffset) -> true
| Field (_fi,_) -> false
| Index (_e,NoOffset) -> true
| Index (_e,_) -> false
(* Transform an index on a multi-dimensional array into an index on the
* same array that would be flattened to a single dimensional array.
*)
let rec lift_offset ty = function
| Index(idx1,(Index(idx2, _off) as suboff)) ->
let subty = direct_element_type ty in
let siz = array_size subty in
begin match lift_offset subty suboff with
| Index(idx, off) ->
let mulidx =
new_exp ~loc:idx2.eloc
(BinOp(Mult,idx1,constant_expr siz,intType)) in
(* Keep info at top-level for visitors on terms that where
* translated to expressions. Those expect these info when
* translating back to term.
*)
let addidx =
map_under_info
(fun e ->
new_exp ~loc:e.eloc (BinOp(PlusA,mulidx,idx,intType))) idx1
in
Index(addidx,off)
| _ -> assert false
end
| Index(idx1,NoOffset) as off ->
let subty = direct_element_type ty in
if isArrayType subty then
let siz = array_size subty in
(* Keep info at top-level *)
let mulidx =
map_under_info
(fun e ->
new_exp ~loc:e.eloc
(BinOp(Mult,idx1,constant_expr siz,intType)))
idx1
in
Index(mulidx,NoOffset)
else off
| off -> off
let change_idx idx1 idx siz =
let boff =
Logic_utils.mk_dummy_term (TBinOp(Mult,idx1,constant_term Cil_datatype.Location.unknown siz))
intType
in
Logic_utils.mk_dummy_term (TBinOp(PlusA,boff,idx)) intType
let rec lift_toffset ty off =
match off with
TIndex(idx1,(TIndex _ as suboff)) ->
let subty = direct_element_type ty in
let siz = array_size subty in
begin match lift_toffset subty suboff with
| TIndex(idx,off) -> TIndex(change_idx idx1 idx siz,off)
| TField _ | TNoOffset -> assert false
end
| TIndex(idx1,TNoOffset) ->
let subty = direct_element_type ty in
if isArrayType subty then
let siz = array_size subty in
TIndex(change_idx
idx1
(constant_term Cil_datatype.Location.unknown My_bigint.zero)
siz,
TNoOffset)
else off
| TIndex _ | TField _ | TNoOffset -> off
(* Allocation/deallocation *)
let malloc_function () =
try
Kernel_function.get_vi (Globals.Functions.find_by_name "malloc")
with Not_found ->
let params = Some ["size",uintType,[]] in
let f =
findOrCreateFunc
(Ast.get ()) "malloc" (TFun(voidPtrType,params,false,[]))
in
let behav = {
b_name = Cil.default_behavior_name;
b_assumes = [];
b_requires = [];
b_extended = [];
b_assigns = Writes [];
b_post_cond = [];
} in
let spec = { (empty_funspec ()) with spec_behavior = [behav]; } in
Globals.Functions.replace_by_declaration spec f Cil_datatype.Location.unknown;
f
let free_function () =
try
Kernel_function.get_vi (Globals.Functions.find_by_name "free")
with Not_found ->
let params = Some ["ptr",voidPtrType,[]] in
let f =
findOrCreateFunc
(Ast.get ()) "free" (TFun(voidType,params,false,[]))
in
let behav = {
b_name = Cil.default_behavior_name;
b_assumes = [];
b_post_cond = [];
b_requires = [];
b_extended = [];
b_assigns = Writes [];
}
in
let spec = { (empty_funspec ()) with spec_behavior = [behav]; } in
Globals.Functions.replace_by_declaration spec f Cil_datatype.Location.unknown;
f
let mkalloc v ty loc =
let callee = new_exp ~loc (Lval(Var(malloc_function ()),NoOffset)) in
let arg = sizeOf ~loc ty in
Call(Some(Var v,NoOffset),callee,[arg],loc)
let mkalloc_statement v ty loc = mkStmt (Instr(mkalloc v ty loc))
let mkalloc_array v ty num loc =
let callee = new_exp ~loc (Lval(Var(malloc_function ()),NoOffset)) in
let arg = constant_expr
(My_bigint.of_int64 (Int64.mul num (Int64.of_int (sizeOf_int ty))))
in
Call(Some(Var v,NoOffset),callee,[arg],loc)
let mkalloc_array_statement v ty num loc =
mkStmt (Instr(mkalloc_array v ty num loc))
let mkfree v loc =
let callee = new_exp ~loc (Lval(Var(free_function ()),NoOffset)) in
let arg = new_exp ~loc (Lval(Var v,NoOffset)) in
Call(None,callee,[arg],loc)
let mkfree_statement v loc = mkStmt (Instr(mkfree v loc))
(*
Local Variables:
compile-command: "LC_ALL=C make -C .. -j byte"
End:
*)
|