123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* 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 LICENSE). *)(* *)(**************************************************************************)moduletypeABSTRACTDOMAIN=sig(* An abstract domain is a lattice... *)typetvaljoin:t->t->tvalis_included:t->t->boolvalbottom:tmoduleControlLocation:sigincludeDatatype_sig.Svalto_int:t->int(* Unique integer identifier. *)valpreds:t->tlist(* Predecessors to the block. *)end(* And a transfer function. Given a control location (cl) and an
abstract state, returns the successor control locations and
abstract states. *)valtransfer:ControlLocation.t->t->(ControlLocation.t*t)list(* Pretty printer used for debugging. *)valpp:Format.formatter->t->unitend(* This module defines functions for fixpoint computation over Bourdoncle's weak topological ordering.
The recursive and iterative strategies are defined by Bourdoncle (we only have a small trick
in the iterative strategy, to avoid recomputations at the nodes at the beginning of a component
if things are already propagated.) *)moduleWTOFixpoint(L:ABSTRACTDOMAIN)=structmoduleCLMap=PatriciaTree.MakeMap(L.ControlLocation)(* We store the values on the edges between cls. *)moduleCLPair=Datatype_sig.Prod2(L.ControlLocation)(L.ControlLocation);;(* Note: maps are faster than hashtbls here. Okasakimaps and regular maps are almost equally fast, with a small advantage to Okasaki maps. *)(* module CLPairMap = Map.Make(CLPair);; *)moduleCLPairMap=PatriciaTree.MakeMap(structincludeCLPairletto_int(a,b)=assert(L.ControlLocation.to_inta<1lsl32);assert(L.ControlLocation.to_intb<1lsl32);((L.ControlLocation.to_inta)lsl32)lor(L.ControlLocation.to_intb)end);;moduleCLPairHash=Hashtbl.Make(CLPair);;letreduce=function|[]->L.bottom|(a::b)->List.fold_leftL.joinab;;(* Get the prestate. *)letpreinitcl(map,_)=(* Printf.printf "Pre cl %d\n" cl.Cil_types.sid; *)letpreds=L.ControlLocation.predsclinlettentative=reduce@@List.map(funpred->tryCLPairMap.find(pred,cl)map(* CLPairHash.find hashtbl (pred,cl) *)withNot_found->L.bottom)predsin(* Printf.printf "Preds size is %d\n" @@ List.length preds; *)(* Printf.printf "Init is %b\n" @@ CLMap.mem cl init; *)tryL.jointentative@@CLMap.findclinitwithNot_found->tentative;;letupdateprestatenode(edgemap,clmap)=letclmap=CLMap.addnodeprestateclmapin(* Self.feedback "Update cl %d %a %a\n" node.Cil_types.sid Cil_datatype.Cl.pretty node L.pp prestate; *)(* let l = pre init node acc in *)letresult=L.transfernodeprestateinletedgemap=List.fold_left(funedgemap(cl,state)->(* CLPairHash.replace hashtbl (node,cl) state; acc *)CLPairMap.add(node,cl)stateedgemap)edgemapresultin(edgemap,clmap);;(* A counter to count the maximum number of iterations. *)letmax_iterations=ref0;;moduleRecursiveStrategy=structletrecfixpoint_componentcountinitacc=function|Wto.Noden->letprestate=preinitnaccinupdateprestatenacc|Wto.Component(head,part)->lethead_prestate=preinitheadaccin(* Note: if we don't pass acc here and start from the old acc for every loop,
it works but is much slower. Check why and see the algorithm in the paper. *)letrecloopacccounthead_prestate=(* Printf.printf "loop head %d %d\n" head.Cil_types.sid count; *)letacc=updatehead_prestateheadaccinletacc=fixpoint_partitioninitaccpartinletnew_head_prestate=preinitheadaccinifL.is_includednew_head_prestatehead_prestatethen(max_iterations:=max!max_iterationscount;acc)elseloopacc(count+1)new_head_prestateinloopacc1head_prestateandfixpoint_partitioninitacclist=List.fold_left(fixpoint_component0init)acclistendmoduleIterativeStrategy=structletfixpoint_componentcountinitaccc=(* We know that we will have to redo one iteration, so we just update. *)letrecloop_update_componentcacc=(* Printf.printf "Loop update component\n"; *)matchcwith|Wto.Noden->letprestate=preinitnaccinupdateprestatenacc|Wto.Component(head,part)->letprestate=preinitheadaccinletacc=updateprestateheadaccinloop_update_partitionpartaccandloop_update_partitionpartacc=matchpartwith|[]->acc|hd::tl->letacc=loop_update_componenthdaccinloop_update_partitiontlaccin(* We don't know if we have to redo one iteration: we check and do not update
as long as we can. We return true while the fixpoint is reached. *)letrecloop_check_componentc((edgemap,clmap)asacc)=(* Printf.printf "Loop check component\n"; *)matchcwith|Wto.Node_->acc,true|Wto.Component(head,part)->letold_prestate=CLMap.findheadclmapinletnew_prestate=preinitheadaccin(* Self.feedback "inclusion test %a %a %a" Cil_datatype.Cl.pretty head *)(* L.pp new_prestate L.pp old_prestate *)(* ; *)ifL.is_includednew_prestateold_prestatethenloop_check_partitionpartaccelsebegin(* Self.feedback "not included test %a %a %a" Cil_datatype.Cl.pretty head *)(* L.pp new_prestate L.pp old_prestate *)(* ; *)letclmap=CLMap.addheadnew_prestateclmapinletacc=updatenew_prestatehead(edgemap,clmap)inloop_update_partitionpartacc,falseendandloop_check_partitionpartacc=matchpartwith|[]->acc,true|hd::tl->letacc,cond=loop_check_componenthdaccinifcondthenloop_check_partitiontlaccelseloop_update_partitionpartacc,falsein(* Top level iteration. *)matchcwith|Wto.Noden->letprestate=preinitnaccinupdateprestatenacc|Wto.Component(head,part)asc->(* We need at least two iterations, the second one to check. *)letacc=loop_update_componentcaccinletrecloopnumiteracc=(* Self.feedback "Doing the iteration %d %a\n" numiter Cil_datatype.Cl.pretty head; *)letacc,fp_reached=loop_check_componentcacciniffp_reachedthenaccelsebegin(* We only count the iterations that updated the analysis,
not the ones that do only checks. So we update numiter
here (fp_reached false => we did some updates) *)letnumiter=numiter+1inmax_iterations:=maxnumiter!max_iterations;loopnumiteraccendinloop1acc;;letfixpoint_partitioninitacclist=List.fold_left(fixpoint_component0init)acclist;;endletiteration_strategy=`iterativeletfixpoint_partition=matchiteration_strategywith|`iterative->IterativeStrategy.fixpoint_partition|`recursive->RecursiveStrategy.fixpoint_partition;;letfixpoint_partitioninitc=let(edgemap,clmap)=fixpoint_partitioninit(CLPairMap.empty,CLMap.empty)cinclmapend