123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)moduleResulting_projects=State_builder.Hashtbl(Datatype.String.Hashtbl)(Project.Datatype)(structletname="E-ACSL resulting projects"letsize=7letdependencies=Ast.self::Options.parameter_statesend)letgenerate_code=Resulting_projects.memo(funname->Options.feedback"beginning translation.";Temporal.enable(Options.Temporal_validity.get());Options.setup();(* slightly more efficient to copy the project before computing the AST
if it is not yet computed *)letrtl_prj_name=Options.Project_name.get()^" RTL"inletrtl_prj=Project.create_by_copy~last:falsertl_prj_namein(* force AST computation before copying the project it belongs to *)Ast.compute();letcopied_prj=Project.create_by_copy~last:truenameinProject.oncopied_prj(fun()->(* preparation of the AST does not concern the E-ACSL RTL:
do it first *)Prepare_ast.prepare();Memory_tracking.SpecialPointers.initialize();Rtl.linkrtl_prj;(* the E-ACSL type system ensures the soundness of the generated
arithmetic operations. Therefore, deactivate the corresponding
options in order to prevent RTE to generate spurious alarms. *)letsigned=Kernel.SignedOverflow.get()inletunsigned=Kernel.UnsignedOverflow.get()in(* we do know that setting these flags does not modify the program's
semantics: using their unsafe variants is thus safe and preserve
all emitted property statuses. *)Kernel.SignedOverflow.unsafe_setfalse;Kernel.UnsignedOverflow.unsafe_setfalse;letfinally()=Kernel.SignedOverflow.unsafe_setsigned;Kernel.UnsignedOverflow.unsafe_setunsignedinFun.protect~finally(fun()->Gmp_types.init();Analyses.preprocess();Injector.inject());(* remove the RTE's results computed from E-ACSL: they are partial
and associated with the wrong kernel function (the one of the old
project). *)(* [TODO] what if RTE was already computed? To be fixed when
redoing the RTE management system *)letselection=State_selection.union(Rte.get_state_selection_with_dependencies())(State_selection.with_dependenciesOptions.Run.self)inProject.clear~selection~project:copied_prj();Resulting_projects.mark_as_computed())();ifnot(Options.debug_atleast1)thenProject.remove~project:rtl_prj();Options.feedback"translation done in project \"%s\"."(Options.Project_name.get());copied_prj)letgenerate_code=Dynamic.register~plugin:"E_ACSL""generate_code"(Datatype.funcDatatype.stringProject.ty)generate_code(* The Frama-C standard library contains specific built-in variables prefixed by
"__fc_" and declared as extern: they prevent the generated code to be
linked. Some are used to represent internal states of the specifications and
should not be printed. Others are used as targets of standard library
macros like stdout or errno and in that case the original macro should be
printed instead.
Moreover, the builtins for VLA allocation and deallocation are specific to
Frama-C. This printer reprint the original builtins (or nothing for the
deallocation).
TODO: could be done by the Frama-C default printer at some points, but since
the transformation is very specific to E-ACSL it should probably be
configurable. *)letchange_printer=(* not projectified on purpose: this printer change is common to each
project. *)letfirst=reftrueinfun()->if!firstthenbeginfirst:=false;letmodulePrinter_class(X:Printer.PrinterClass)=structclassprinter=objectinheritX.printerassupermethod!varinfofmtvi=ifFunctions.Libc.is_vla_alloc_namevi.Cil_types.vnamethen(* Replace VLA allocation with calls to [__builtin_alloca] *)Format.fprintffmt"%s"Functions.Libc.actual_allocaelseletreplacement=Ast_attributes.find_fc_stdlib_extern_replacementvi.vattrinmatchreplacementwith|Somereplacement->(* The varinfo is replacing a libc macro, print the replaced
name. *)Format.pp_print_stringfmtreplacement|None->(* Otherwise use the original printer *)super#varinfofmtvimethod!instrfmti=matchiwith|Call(_,fct,_,_)whenFunctions.Libc.is_vla_freefct->(* Nothing to print: VLA deallocation is done automatically when
leaving the scope *)Format.fprintffmt"/* ";super#instrfmti;Format.fprintffmt" */"|_->super#instrfmtimethod!globalfmtg=letis_vla_builtinvi=Functions.Libc.is_vla_alloc_namevi.Cil_types.vname||Functions.Libc.is_vla_free_namevi.Cil_types.vnameinletis_fc_internal(vi:Cil_types.varinfo)=vi.vstorage==Cil_types.Extern&&Ast_attributes.(containsfc_stdlib_internalvi.vattr)inmatchgwith|GFunDecl(_,vi,_)whenis_vla_builtinvi->(* Nothing to print: the VLA builtins don't have an original libc
version. *)()|GFunDecl(_,vi,_)|GVarDecl(vi,_)whenis_fc_internalvi->(* Do not print definitions internal to Frama-C's libc. *)()|_->super#globalfmtgendendinPrinter.update_printer(modulePrinter_class:Printer.PrinterExtension)endletmain()=ifOptions.Run.get()thenbeginchange_printer();ignore(generate_code(Options.Project_name.get()));endlet()=Boot.Main.extendmain