123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2024 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It 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 Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openLocationsmoduleDeps=Eva.DepsmoduleDepsOrUnassigned=structincludeEva.Assigns.DepsOrUnassignedletsubstfd=matchdwith|DepsBottom->DepsBottom|Unassigned->Unassigned|AssignedFromfd->letfd'=ffdiniffd==fd'thendelseAssignedFromfd'|MaybeAssignedFromfd->letfd'=ffdiniffd==fd'thendelseMaybeAssignedFromfd'letcomposed1d2=matchd1,d2with|DepsBottom,_|_,DepsBottom->DepsBottom(* could indicate dead code. Not used in practice anyway *)|Unassigned,_->d2|AssignedFrom_,_->d1|MaybeAssignedFrom_,Unassigned->d1|MaybeAssignedFromd1,MaybeAssignedFromd2->MaybeAssignedFrom(Deps.joind1d2)|MaybeAssignedFromd1,AssignedFromd2->AssignedFrom(Deps.joind1d2)(* for backwards compatibility *)letprettyfmtfd=matchfdwith|DepsBottom->Format.pp_print_stringfmt"DEPS_BOTTOM"|Unassigned->Format.pp_print_stringfmt"(SELF)"|AssignedFromd->Zone.prettyfmt(Deps.to_zoned)|MaybeAssignedFromd->Format.fprintffmt"%a (and SELF)"Zone.pretty(Deps.to_zoned)letpretty_precisefmt=function|DepsBottom->Format.pp_print_stringfmt"DEPS_BOTTOM"|Unassigned->Format.pp_print_stringfmt"UNASSIGNED"|AssignedFromfd->Deps.pretty_precisefmtfd|MaybeAssignedFromfd->(* '(or UNASSIGNED)' would be a better pretty-printer, we use
'(and SELF)' only for compatibility reasons *)Format.fprintffmt"%a (and SELF)"Deps.pretty_precisefdendincludeEva.Assigns.Memoryletbind_varvivm=letz=Locations.zone_of_varinfoviinadd_binding~exact:truemzvletunbind_varvim=remove_base(Base.of_varinfovi)mletmapf=map(DepsOrUnassigned.substf)letis_unassignedm=LOffset.is_same_valuemDepsOrUnassigned.Unassigned(* Unassigned is a neutral value for compose, on both sides *)letdecide_composem1m2=ifm1==m2||is_unassignedm1thenLOffset.ReturnRightelseifis_unassignedm2thenLOffset.ReturnLeftelseLOffset.Recurseletcompose_map=letcache=Hptmap_sig.PersistentCache"From_memory.compose"in(* Partial application is important because of the cache. Idempotent,
because [compose x x] is always equal to [x]. *)map2~cache~symmetric:false~idempotent:true~empty_neutral:truedecide_composeDepsOrUnassigned.composeletcomposem1m2=matchm1,m2with|Top,_|_,Top->Top|Mapm1,Mapm2->Map(compose_mapm1m2)|Bottom,(Map_|Bottom)|Map_,Bottom->Bottom(* ----- Substitute --------------------------------------------------------- *)(** Auxiliary function that substitutes the data right-hand part of a
dependency by a pre-existing From state. The returned result is a Deps.t:
the data part will be the data part of the complete result, the indirect
part will be added to the indirect part of the final result. *)(* This function iterates simultaneously on a From memory, and on a zone.
It is cached. The definitions below are used to call the function that
does the recursive descent. *)letsubstitute_data_deps=(* Nothing left to substitute, return z unchanged *)letempty_rightz=Deps.datazin(* Zone to substitute is empty *)letempty_left_=Deps.bottomin(* [b] is in the zone and substituted. Rewrite appropriately *)letbothbitvsoffsm=find_precise_loffsetoffsmbitvsinletjoin=Deps.joininletempty=Deps.bottominletcache=Hptmap_sig.PersistentCache"From_memory.substitute_data"inletf_map=Zone.fold2_join_heterogeneous~cache~empty_left~empty_right~both~join~emptyinfuncall_site_fromsz->matchcall_site_fromswith|Bottom->Deps.bottom|Top->Deps.top|Mapm->tryf_mapz(shapem)withAbstract_interp.Error_Top->Deps.top(** Auxiliary function that substitutes the indirect right-hand part of a
dependency by a pre-existing From state. The returned result is a zone,
which will be added to the indirect part of the final result. *)letsubstitute_indirect_deps=(* Nothing left to substitute, z is directly an indirect dependency *)letempty_rightz=zin(* Zone to substitute is empty *)letempty_left_=Zone.bottominletbothbitvsoffsm=(* Both the found data and indirect dependencies are computed for indirect
dependencies: merge to a single zone *)Deps.to_zone(find_precise_loffsetoffsmbitvs)inletjoin=Zone.joininletempty=Zone.bottominletcache=Hptmap_sig.PersistentCache"From_memory.substitute_indirect"inletf_map=Zone.fold2_join_heterogeneous~cache~empty_left~empty_right~both~join~emptyinfuncall_site_fromsz->matchcall_site_fromswith|Bottom->Zone.bottom|Top->Zone.top|Mapm->tryf_mapz(shapem)withAbstract_interp.Error_Top->Zone.topletsubstitutecall_site_fromsdeps=letopenDepsinlet{data;indirect}=depsin(* depending directly on an indirect dependency -> indirect,
depending indirectly on a direct dependency -> indirect *)letdirdeps=substitute_data_depscall_site_fromsdatainletinddeps=substitute_indirect_depscall_site_fromsindirectinletdir=dirdeps.datainletind=Zone.(joindirdeps.indirectinddeps)in{data=dir;indirect=ind}(* ----- Dependency of returned values -------------------------------------- *)typereturn=Deps.tletdefault_return=Deps.bottomlettop_return=Deps.topletadd_to_return?start:(_start=0)~size:_size?(m=default_return)v=Deps.joinmv(*
let start = Ival.of_int start in
let itvs = Int_Intervals.from_ival_size start size in
LOffset.add_iset ~exact:true itvs (DepsOrUnassigned.AssignedFrom v) m
*)lettop_return_sizesize=add_to_return~sizeDeps.topletcollapse_returnx=x(* ----- Pretty-printing ---------------------------------------------------- *)let()=imprecise_write_msg:="dependencies to update"letpretty_skip=function|DepsOrUnassigned.DepsBottom->true|DepsOrUnassigned.Unassigned->true|DepsOrUnassigned.AssignedFrom_->false|DepsOrUnassigned.MaybeAssignedFrom_->falseletpretty=pretty_generic_printer~skip_v:pretty_skip~pretty_v:DepsOrUnassigned.pretty~sep:"FROM"()letpretty_ind_data=pretty_generic_printer~skip_v:pretty_skip~pretty_v:DepsOrUnassigned.pretty_precise~sep:"FROM"()(** same as pretty, but uses the type of the function to output more
precise information.
@raise Error if the given type is not a function type
*)letpretty_with_type~indirecttypfmtassigns=letEva.Assigns.{memory;return}=assignsinlet(rt_typ,_,_,_)=Cil.splitFunctionTypetypinifis_bottommemorythenFormat.fprintffmt"@[NON TERMINATING - NO EFFECTS@]"elseletmap_pretty=ifindirectthenpretty_ind_dataelseprettyinifCil.isVoidTypert_typthenbeginifis_emptymemorythenFormat.fprintffmt"@[NO EFFECTS@]"elsemap_prettyfmtmemoryendelseletpp_spacefmt=ifnot(is_emptymemory)thenFormat.fprintffmt"@ "inFormat.fprintffmt"@[<v>%a%t@[\\result FROM @[%a@]@]@]"map_prettymemorypp_space(ifindirectthenDeps.pretty_preciseelseDeps.pretty)returnletpretty_with_type_indirect=pretty_with_type~indirect:trueletpretty_with_type=pretty_with_type~indirect:false