123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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). *)(* *)(**************************************************************************)openCil_typesopenCil_datatypeopenPdg_typesopenPdgIndexopenReason_graph(** Computation of the PDG nodes that are impacted by the "execution"
of some initial PDG nodes. This is implemented as a forward
inter-procedural analysis on top of the PDG plugin. *)moduleNS=Pdg_aux.NStypenodes=NS.tmoduleNM=PdgTypes.Node.MapmoduleKFS=Kernel_function.HptsetmoduleKFM=Kernel_function.Mapletkfmns_find_defaultkeym=tryKFM.findkeymwithNot_found->NS.empty(* Data associated to PDG nodes that are impacted, and that have not been
treated yet. *)typetodo={kf:kernel_function(* kernel_function in which the node can be found *);pdg:PdgTypes.Pdg.t(* pdg of this kernel_function *);zone:Locations.Zone.t(* fragment of the node that is impacted *);init:bool(* is this node in the worklist only because it is part of the
nodes initially selected as source? The initial nodes are not
in the final result, but must be present in intermediate
results for technical reasons *);}(* Nodes that are impacted, and that will have to be considered at some point.*)andtodolist=todoNM.t(* All nodes that have been found to be impacted. Presented as a map from
Kf, because this information cannot be recovered from the PDG nodes.
(Also, this speeds up some operations *)typeresult=nodesKFM.t(* Modelization of a call. The first function (the caller) calls the second
(the callee) at the given statement. *)moduleKfKfCall=Datatype.Triple_with_collections(Kernel_function)(Kernel_function)(Cil_datatype.Stmt)(structletmodule_name="Impact.Compute.KfKfCall"end)(** Worklist maintained by the plugin to build its results *)typeworklist={mutabletodo:todolist(** nodes that are impacted, but that have not been
propagated yet. *);mutableresult:result(** impacted nodes. This field only grows.
An invariant is that nodes in [todolist] are not already in [result],
except with differing [init] fields. *);mutabledownward_calls:Pdg_aux.call_interfaceKfKfCall.Map.t(** calls for which an input may be impacted. If so, we must compute the
impact within the called function. For each call, we associate to each
PDG input of the callee the nodes that define the input in the caller.
The contents of this field grow. *);mutablecallers:KFS.t(** all the callers of the functions in which the
initial nodes are located. Constant after initialization, used to
initialize [upward_calls] below. *);mutableupward_calls:Pdg_aux.call_interfaceLazy.tKfKfCall.Map.t(** calls for which an output may be impacted. If so, we must compute the
impact after the call in the caller (which is part of the [callers]
field by construction). For each output node at the call point in the
caller, associate all the nodes of the callee that define this output.
The field is lazy: if the impact "dies" before before reaching the call,
we may avoid a costly computation. Constant once initialized. *);mutablefun_changed_downward:KFS.t(** Functions in which a new pdg node has
been found since the last iteration. The impact on downward calls with
those callers will have to be computed again. *);mutablefun_changed_upward:KFS.t(** Functions in which a new pdg node has
been found. The impact on upward calls to those callees
will have to be computed again. *);skip:Locations.Zone.t(** Locations for which the impact is
dismissed. Nodes that involve only those zones
are skipped. Constant after initialization *);mutableinitial_nodes:nodesKFM.t(** Nodes that are part of the initial impact query, or directly
equivalent to those (corresponding nodes in a caller). *);mutableunimpacted_initial:nodesKFM.t(** Initial nodes (as defined above) that are not "self-impacting"
so far. Those nodes will not be part of the final results. *);mutablereason:reason_graph(** Reasons why nodes in [result] are marked as impacted. *);compute_reason:bool(** compute the field [reason]; may be costly *);}(** Extract the node of the kf that are only part of the initial impact *)letunimpacted_initial_by_kfwlkf=kfmns_find_defaultkfwl.unimpacted_initial(** Extract the current results for a given function *)letresult_by_kfwlkf=kfmns_find_defaultkfwl.resultletresult_to_node_origin(r:result):Reason_graph.nodes_origin=KFM.fold(funkfnsacc->NS.fold(fun(n,_)acc->PdgTypes.Node.Map.addnkfacc)nsacc)rPdgTypes.Node.Map.emptyletinitial_to_node_set(init:nodesKFM.t):NS.t=KFM.fold(fun_->NS.union)initNS.empty(* -------------------------------------------------------------------------- *)(* --- Adding nodes to the worklist, or to the results --- *)(* -------------------------------------------------------------------------- *)(** Mark that [n] comes from an indirect impact, ie. remove it from the
set of initial nodes that are not impacted. *)letremove_from_unimpacted_initialwlkf(n,z)=letunimpacted=unimpacted_initial_by_kfwlkfinifNS.mem'(n,z)unimpactedthenbeginOptions.debug~level:2"node of initial impact %a is indirectly impacted"PdgTypes.Node.prettyn;wl.unimpacted_initial<-KFM.addkf(NS.removenunimpacted)wl.unimpacted_initial;end;;(** Add a node to the sets of impacted nodes. Update the various fields
of the worklist that need it. [init] indicates that the node
is added only because it belongs to the set of initial nodes. *)letadd_to_resultwlnkfinit=ifinit=falsethenremove_from_unimpacted_initialwlkfn;(* if useful, mark that a new node was found in [kf] *)ifnot(KFS.memkfwl.fun_changed_downward)then(* wl.fun_changed_upward is not updated, because we merge
wl.fun_changed_downward with wl.fun_changed_upward when needed *)wl.fun_changed_downward<-KFS.addkfwl.fun_changed_downward;letset=result_by_kfwlkfinlets'=NS.add'nsetinwl.result<-KFM.addkfs'wl.result(** return [true] if the location in [n] is contained in [skip], in which
case the node should be skipped entirely *)letnode_to_skipskipn=matchPdg.Api.node_keynwith|Key.SigKey(Signature.In(Signature.InImplz))|Key.SigKey(Signature.Out(Signature.OutLocz))|Key.SigCallKey(_,Signature.In(Signature.InImplz))|Key.SigCallKey(_,Signature.Out(Signature.OutLocz))->Locations.Zone.equalLocations.Zone.bottom(Locations.Zone.diffzskip)|_->false(** Auxiliary function, used to refuse some nodes that should not go in
the results *)letfilterwl(n,z)=not(Locations.Zone.is_bottomz)&&matchPdg.Api.node_keynwith|Key.SigKey(Signature.InSignature.InCtrl)->false(* do not consider node [InCtrl]. YYY: find when this may happen *)|Key.VarDecl_->false(* do not consider variable declarations. This is probably impossible
in a forward analysis anyway. *)|_->ifnode_to_skipwl.skipnthen(Options.debug~once:true~level:2"skipping node %a as required"PdgTypes.Node.prettyn;false)elsetrue(** Add a new edge in the graph explaining the results *)letadd_to_reasonwl~nsrc~ndstrt=ifwl.compute_reason&&filterwlndstthenletreason=Reason.Set.add(fstnsrc,fstndst,rt)wl.reasoninOptions.debug~level:2"@[<hov 4>Adding %a@ because of@ %a/%a@]"Pdg_aux.pretty_nodendstReason_graph.ReasonType.prettyrtPdg_aux.pretty_nodensrc;wl.reason<-reason;;(** Add some nodes to the [todo] field of the worklist, while enforcing some
invariants. Some kind of pdg nodes must not appear in it, plus the nodes
must not be in result already. *)letadd_to_do_aux~initwlkfpdg(pn,zoneasn)=iffilterwlnthenletppfmt=Format.fprintffmt"node %a (in %a)"Pdg_aux.pretty_nodenKernel_function.prettykf;inletadd()=lettodo={kf;pdg;init;zone}inwl.todo<-NM.addpntodowl.todointryletcur=NM.findpnwl.todoin(* Node is already in the todo list. Check init field and zone *)if(cur.init=true&&init=false)||(not(Locations.Zone.is_includedzonecur.zone))thenbegin(* overwrite the existing binding in the todo list *)Options.debug~level:2"todo list node %t is now init=false"pp;add();endwithNot_found->(* Node is not in todo list. Check if it is already in results *)ifNS.mem'n(result_by_kfwlkf)thenbegin(* Already in results. Check if [init] flag matches. *)ifinit=false&&NS.mem'n(unimpacted_initial_by_kfwlkf)thenbegin(* TODO: check above *)(* Node was already there with [init=true] or with smaller .
Compute impact again
with [init=false] *)Options.debug~level:2"adding again node %t, with init=false"pp;add()endendelsebegin(* General case *)Options.debug~level:2"adding %t"pp;add()end;;(** Build the initial value of the [todo] field, from a list of initial nodes *)letinitial_to_do_listwlkfpdgnodes=List.iter(funn->add_to_do_aux~init:truewlkfpdgn)nodes(** Mark a new node as impacted, and simultaneously mark that it is equivalent
to nodes that are all initial nodes *)letadd_to_do_part_of_initialwlkfpdgn=add_to_do_aux~init:truewlkfpdgn;letinitial_nodes=kfmns_find_defaultkfwl.initial_nodesinifnot(NS.mem'ninitial_nodes)thenbegin(* n has never been marked as initial. Mark it in both initial and
unimpacted_initial fields (it may leave the second later) *)Options.debug~level:2"node %a is a part of the initial impact"Pdg_aux.pretty_noden;letunimpacted_kf=unimpacted_initial_by_kfwlkfinletnew_unimpacted=NS.add'nunimpacted_kfinletnew_initial=NS.add'ninitial_nodesinwl.unimpacted_initial<-KFM.addkfnew_unimpactedwl.unimpacted_initial;wl.initial_nodes<-KFM.addkfnew_initialwl.initial_nodes;end;;(** From now on, most functions will pass [init = false] to [add_to_do_aux]. We
define an alias instead *)letadd_to_do=add_to_do_aux~init:false(* -------------------------------------------------------------------------- *)(* --- Basic propagation --- *)(* -------------------------------------------------------------------------- *)(** Purely intra-procedural propagation from one impacted node. Just follow
the PDG once, for all kind of dependencies. *)letintraprocedural_one_nodewl(node,zasnsrc)kfpdg=Options.debug~level:3"intraprocedural part";PdgTypes.Pdg.fold_direct_codpdspdg(fun()(dpd,zopt)n->(* Filter edge according to the subzone of the node that is impacted *)letfollow=matchzoptwith|None->true|Somez'->Locations.Zone.intersectszz'iniffollowthenbegin(* YYY: is it possible to compute a refinement on this zone? *)letndst=(n,Locations.Zone.top)inadd_to_reasonwl~nsrc~ndst(Intraproceduraldpd);add_to_dowlkfpdgndst;end)()node;Options.debug~level:3"intraprocedural part done"(* -------------------------------------------------------------------------- *)(* --- Downward call propagation --- *)(* -------------------------------------------------------------------------- *)(** Add a downward call to the worklist the first time it is encountered. This
functions implicitly caches the mapping from the PDG nodes of the caller to
the ones of the callee, as this information is expensive to compute *)letadd_downward_callwl(caller_kf,pdg)(called_kf,called_pdg)stmt=Options.debug~level:3"downward part";ifnot(KfKfCall.Map.mem(caller_kf,called_kf,stmt)wl.downward_calls)thenletcallee=(called_kf,called_pdg)inletdeps=Pdg_aux.all_call_input_nodes~caller:pdg~calleestmtinwl.downward_calls<-KfKfCall.Map.add(caller_kf,called_kf,stmt)depswl.downward_calls;Options.debug~level:3"downard part done"elseOptions.debug~level:3"empty downward part";;(** Propagate impact from node [node] if it corresponds to a call statement.
This is a partially inter-procedural propagation: some nodes of the
callee are directly in the worklist, and the call is registered in the
field [downward_calls]. *)letdownward_one_call_nodewl(pnode,_asnode)caller_kfpdg=matchPdg.Api.node_keypnodewith|Key.SigKey(Signature.InSignature.InCtrl)(* never in the worklist *)|Key.VarDecl_(* never in the worklist *)|Key.CallStmt_(* pdg returns a SigCallKey instead *)->assertfalse|Key.SigKey_|Key.Stmt_|Key.Label_->(* Only intraprocedural part needed, done by
[intraprocedural_one_node] *)()|Key.SigCallKey(id,key)->letstmt=Key.call_from_ididinletcalled_kfs=Eva.Results.calleestmtinList.iter(funcalled_kf->letcalled_pdg=Pdg.Api.getcalled_kfinletnodes_callee,pdg_ok=Options.debug~level:3"%a: considering call to %a"Pdg_aux.pretty_nodenodeKernel_function.prettycalled_kf;try(matchkeywith|Signature.In(Signature.InNumn)->(try[Pdg.Api.find_input_nodecalled_pdgn,Locations.Zone.top]withNot_found->[])|Signature.InSignature.InCtrl->(try[Pdg.Api.find_entry_point_nodecalled_pdg,Locations.Zone.top]withNot_found->[])|Signature.In(Signature.InImpl_)->assertfalse|Signature.Out_->[]),truewith|Pdg.Api.Top->Options.warning"no precise pdg for function %s. \n\
Ignoring this function in the analysis (potentially incorrect results)."(Kernel_function.get_namecalled_kf);[],false|Pdg.Api.Bottom->(*Function that fails or never returns immediately *)[],false|Not_found->assertfalseinOptions.debug~level:4"Direct call nodes %a"(Pretty_utils.pp_list~sep:" "Pdg_aux.pretty_node)nodes_callee;List.iter(funn->add_to_reasonwl~nsrc:node~ndst:nInterproceduralDownward;add_to_dowlcalled_kfcalled_pdgn)nodes_callee;ifpdg_okthenadd_downward_callwl(caller_kf,pdg)(called_kf,called_pdg)stmt)called_kfs;Options.debug~level:3"propagation of call %a done"Pdg_aux.pretty_nodenode(* TODO: document *)letzone_restrictset_src_impact=letaux(_,z)acc=Locations.Zone.joinzaccinNS.foldauxset_src_impactLocations.Zone.bottom(** Propagate impact for one call registered in [downward_calls]. If the set
of impacted nodes in the caller intersect the nodes [deps] that define the
input [node] of the call, add [node] to the impacted nodes. *)letdownward_one_call_inputswlkf_callerkf_callee(node,deps)=letresults_for_kf_caller=result_by_kfwlkf_callerinifNS.intersectsdepsresults_for_kf_callerthenletinter=NS.interdepsresults_for_kf_callerinletz=zone_restrictinterinletnode'=(node,z)inNS.iter'(funnsrc->add_to_reasonwl~nsrc~ndst:node'InterproceduralDownward)inter;add_to_dowlkf_callee(Pdg.Api.getkf_callee)node';;;(** Propagate impact for all calls registered in [downward_calls]. For each
caller, if new impacted nodes have been found, try to propagate the call.
Then, zero out the list of functions that must be considered again. *)letdownward_calls_inputswl=letaux(kf_caller,kf_callee,_stmt)ldeps=ifKFS.memkf_callerwl.fun_changed_downwardthenbeginOptions.debug~level:3"Inputs from call %a -> %a"Kernel_function.prettykf_callerKernel_function.prettykf_callee;List.iter(downward_one_call_inputswlkf_callerkf_callee)ldeps;Options.debug~level:3"call done"endinKfKfCall.Map.iterauxwl.downward_calls;wl.fun_changed_downward<-KFS.empty(* -------------------------------------------------------------------------- *)(* --- Upward call propagation --- *)(* -------------------------------------------------------------------------- *)(** Fill out the field [upward_calls] of the worklist. This is done by
visiting (transitively) all the callers of functions in [kfs], and
registering all the calls found this way. The callers found are added
to the field [callers]. For each find, we find the nodes of the callee
that define a given output in the caller using [Pdg_aux.all_call_out_nodes].
[kfs] must be all the functions containing the initial nodes of the
analysis. *)letall_upward_callerswlkfs=letaux_call(caller,pdg_caller)(callee,pdg_callee)callsite=Options.debug~level:2~source:(fst(Cil_datatype.Stmt.loccallsite))"Found call %a -> %a"Kernel_function.prettycallerKernel_function.prettycallee;letnodes=lazy(Pdg_aux.all_call_out_nodes~callee:pdg_callee~caller:pdg_callercallsite)inwl.upward_calls<-KfKfCall.Map.add(caller,callee,callsite)nodeswl.upward_callsinletrecfixpointtodo=tryletkf=KFS.choosetodoinlettodo=KFS.removekftodoinlettodo=ifnot(KFS.memkfwl.callers)then(Options.debug"Found caller %a"Kernel_function.prettykf;letpdg_kf=Pdg.Api.getkfinList.fold_left(funtodo(caller,callsites)->letpdg_caller=Pdg.Api.getcallerinList.iter(aux_call(caller,pdg_caller)(kf,pdg_kf))callsites;KFS.addcallertodo)todo(Eva.Results.callsiteskf);)elsetodoinwl.callers<-KFS.addkfwl.callers;fixpointtodowithNot_found->()infixpointkfs(** Upward propagation in all the callers. For all upward-registered calls,
find if new impacted nodes have been found in the callee. If so, check if
they intersect with the nodes of the callee defining the output. Then, mark
the (caller) output node as impacted. At the end, zero out the list of
function that must be examined again. *)letupward_in_callerswl=letaux(caller,callee,_callsite)l=ifKFS.memcalleewl.fun_changed_upwardthenList.iter(fun(n,nodes)->letresults_for_callee=result_by_kfwlcalleeinifNS.intersectsnodesresults_for_calleethenletinter=NS.internodesresults_for_calleeinletunimpacted_callee=unimpacted_initial_by_kfwlcalleeinletinit=NS.for_all'(funn->NS.mem'nunimpacted_callee)interinletz=zone_restrictinterinletn=(n,z)inNS.iter'(funnsrc->add_to_reasonwl~nsrc~ndst:nInterproceduralUpward)inter;ifinitthenadd_to_do_part_of_initialwlcaller(Pdg.Api.getcaller)nelseadd_to_dowlcaller(Pdg.Api.getcaller)n)(Lazy.forcel)inKfKfCall.Map.iterauxwl.upward_calls;wl.fun_changed_upward<-KFS.empty(* -------------------------------------------------------------------------- *)(* --- Initialization --- *)(* -------------------------------------------------------------------------- *)(** Compute the initial state of the worklist. *)letinitial_worklist?(skip=Locations.Zone.bottom)?(reason=false)nodeskf=letinitial=KFM.addkf(List.fold_left(funsn->NS.add'ns)NS.emptynodes)KFM.empty;inletwl={todo=NM.empty;result=KFM.empty;downward_calls=KfKfCall.Map.empty;callers=KFS.empty;upward_calls=KfKfCall.Map.empty;initial_nodes=initial;unimpacted_initial=initial;fun_changed_downward=KFS.empty;fun_changed_upward=KFS.empty;skip=skip;reason=Reason.Set.empty;compute_reason=reason;}in(* Fill the [todo] field *)initial_to_do_listwlkf(Pdg.Api.getkf)nodes;letinitial_callers=ifOptions.Upward.get()thenKFS.singletonkfelseKFS.emptyin(* Fill the [callers] and [upward_calls] fields *)all_upward_callerswlinitial_callers;wl(** To compute the impact of a statement, find the initial PDG nodes that must
be put in the worklist. The only subtlety consists in skipping input nodes
on statements that are calls; otherwise, we would get an impact in the
callees of the call. *)letinitial_nodes~skipkfstmt=Options.debug~level:3"computing initial nodes for %d"stmt.sid;letpdg=Pdg.Api.getkfinifEva.Results.is_reachablestmtthentryletall=Pdg.Api.find_simple_stmt_nodespdgstmtinletfiltern=matchPdgTypes.Node.elem_keynwith|Key.SigCallKey(_,Signature.In_)->false|_->not(node_to_skipskipn)inList.filterfilterallwith|PdgTypes.Pdg.Top->Options.warning"analysis of %a is too imprecise, impact cannot be computed@."Kernel_function.prettykf;[]|Not_found->assertfalseelsebeginOptions.debug~level:3"stmt %d is dead. skipping."stmt.sid;[]end(* -------------------------------------------------------------------------- *)(* --- Fixpoint --- *)(* -------------------------------------------------------------------------- *)(** Choose one node to process in the todo list, if one remains *)letpickwl=trylet(n,_asr)=NM.choosewl.todoinwl.todo<-NM.removenwl.todo;SomerwithNot_found->None(** Empty the [todo] field of the worklist by applying as many basic steps as
possible: intra-procedural steps, plus basic inter-procedural steps on
downward calls. *)letrecintraproceduralwl=matchpickwlwith|None->()|Some(pnode,{kf;pdg;init;zone})->letnode=pnode,zoneinadd_to_resultwlnodekfinit;Db.yield();Options.debug~level:2"considering new node %a in %a:@ <%a>%t"PdgTypes.Node.prettypnodeKernel_function.prettykfPdg_aux.pretty_nodenode(funfmt->ifinitthenFormat.pp_print_stringfmt" (init)");intraprocedural_one_nodewlnodekfpdg;downward_one_call_nodewlnodekfpdg;intraproceduralwlletsomething_to_dowl=not(NM.is_emptywl.todo)(** Make the worklist reach a fixpoint, by propagating all possible source of
impact as much as possible. Due to the way calls are treated (by
intersecting new impacted nodes with constant sets of nodes), it is more
efficient to saturate the field [result] before calling
[downward_calls_inputs] and [upward_in_callers]. We also make sure all
downward propagation is done before starting upward propagation. *)letrecfixpointwl=ifsomething_to_dowlthenbeginintraproceduralwl;(* Save functions on which the results have changed, as
[downward_calls_inputs] clears the field [fun_changed_downward] *)wl.fun_changed_upward<-KFS.unionwl.fun_changed_downwardwl.fun_changed_upward;downward_calls_inputswl;ifsomething_to_dowlthenfixpointwlelse(upward_in_callerswl;fixpointwl)endletremove_unimpacted_kfimpactinitial=matchimpact,initialwith|None,None|Some_,None|None,Some_(* impossible *)->impact|Someimpact,Someinitial->Some(NS.diffimpactinitial)(** Impact of a set of nodes. Once the worklist has reached its fixpoint,
remove the initial nodes that are not self-impacting from the result,
and return this result. *)letimpact?skip?reasonnodeskf=letwl=initial_worklist?skip?reasonnodeskfinfixpointwl;letwithout_init=KFM.mergeremove_unimpactedwl.resultwl.unimpacted_initialinwithout_init,wl.unimpacted_initial,wl.initial_nodes,wl.reason(* -------------------------------------------------------------------------- *)(* --- High-level API --- *)(* -------------------------------------------------------------------------- *)(** Impact of a list of PDG nodes coming from the same function *)letnodes_impacted_by_nodes?(skip=Locations.Zone.bottom)?(restrict=Locations.Zone.top)?(reason=false)kfnodes=letnodes=List.map(funn->n,restrict)nodesinletr,unimpacted,initial,reason_graph=impact~skip~reasonnodeskfinletpp_kffmt(kf,ns)=Format.fprintffmt"@[%a: %a@]@ "Kernel_function.prettykf(Pretty_utils.pp_iter~sep:",@ "~pre:""~suf:""NS.iter'Pdg_aux.pretty_node)nsinletiterf=KFM.iter(funkfns->f(kf,ns))inOptions.debug~level:1"@[<v>Results:@ %a@]"(Pretty_utils.pp_iter~sep:"@ "~pre:""~suf:""iterpp_kf)r;letreason_full={Reason_graph.reason_graph;nodes_origin=result_to_node_originr;initial_nodes=initial_to_node_setinitial;}inifreasonthenReason_graph.print_dot_graphreason_full;r,unimpacted,reason_full(** Impact of a list stmts coming from the same function *)letnodes_impacted_by_stmts?(skip=Locations.Zone.bottom)?(restrict=Locations.Zone.top)?(reason=false)kfstmts=letnodes=List.map(initial_nodes~skipkf)stmtsinletnodes=List.concatnodesinOptions.debug"about to compute impact for stmt(s) %a, %d initial nodes"(Pretty_utils.pp_list~sep:",@ "Stmt.pretty_sid)stmts(List.lengthnodes);nodes_impacted_by_nodes~skip~restrict~reasonkfnodes(** Transform the result of an analysis into a set of PDG nodes *)letresult_to_nodes(res:result):nodes=KFM.fold(fun_sacc->NS.unionsacc)resNS.empty(** Transform a set of PDG nodes into a set of statements *)letnodes_to_stmtsns=letget_stmtnode=Key.stmt(Pdg.Api.node_keynode)inletset=(* Do not generate a list immediately, some nodes would be duplicated *)NS.fold(fun(n,_z)acc->Option.fold~none:acc~some:(funs->Stmt.Set.addsacc)(get_stmtn))nsStmt.Set.emptyinStmt.Set.elementsset(** Impact of a list of statements as a set of statements *)letstmts_impacted?(skip=Locations.Zone.bottom)~reasonkfstmts=letr,_,_=nodes_impacted_by_stmts~skip~reasonkfstmtsinnodes_to_stmts(result_to_nodesr)(** Impact of a list of PDG nodes as a set of nodes *)letnodes_impacted?(skip=Locations.Zone.bottom)~reasonkfnodes=letr,_,_=nodes_impacted_by_nodes~skip~reasonkfnodesinresult_to_nodesr(** Nodes impacted in a given function *)letimpact_in_kf(res:result)kf=kfmns_find_defaultkfres(** Computation of the [skip] field from a list of variables *)letskip_basesvars=letauxaccv=letz=Locations.Zone.injectvInt_Intervals.topinLocations.Zone.joinzaccinList.fold_leftauxLocations.Zone.bottomvars(** Computation of the [skip] field from the [-impact-skip] option *)letskip()=letbases=Options.Skip.fold(funnamel->letvi=tryBase.of_varinfo(Globals.Vars.find_from_astinfonameGlobal)withNot_found->ifname="NULL"thenBase.nullelseOptions.abort"cannot skip unknown variable %s"nameinvi::l)[]inskip_basesbases(* TODO: dynamically register more high-level functions *)(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)