123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133(**************************************************************************)(* 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). *)(* *)(**************************************************************************)moduleLog=Tracelog.Make(structletcategory="Fixpoint.Fixpoint_Wto"end);;moduletypeAbstractDomain=sigtypestatetypetransitiontypeloop_idvalcopy:state->statevaljoin:state->state->statevalfixpoint_step:loop_id:loop_id->state->state->(state*bool)valtransfer:transition->state->stateoptionvalpp:Format.formatter->state->unitvalenter_loop:state->(state*loop_id)valleave_loop:state->stateendmoduletypeGraph=sigtypetransitionmoduleControlLocation:PatriciaTree.KEYvalpreds_with_transition:ControlLocation.t->(ControlLocation.t*transition)listendmoduleMake(G:Graph)(D:AbstractDomainwithtypetransition=G.transition)=structmoduleCLMap=PatriciaTree.MakeMap(G.ControlLocation);;(* Compute the prestate for a node. *)letprenodemap=letpreds=G.preds_with_transitionnodeinletprestates=preds|>List.filter_map(fun(pred,transition)->matchCLMap.findpredmapwith|exceptionNot_found->None|state->D.transfertransitionstate)inmatchprestateswith|[]->None|a::b->letstate=List.fold_leftD.joinabinSomestateletpp_optfmtx=matchxwith|None->Format.fprintffmt"None"|Somev->Format.fprintffmt"Some %a"D.ppvletpp_mapfmtmap=Format.fprintffmt"{ ";map|>CLMap.iter(funkey_value->Format.fprintffmt"%d "(G.ControlLocation.to_intkey));Format.fprintffmt"} ";;(* If node is a single node, update it in the map (remove the binding if the state is bottom. *)letupdatenodevaluemap=Log.debug(funp->p"Updating %d with %a %a"(G.ControlLocation.to_intnode)pp_optvaluepp_mapmap);matchvaluewith|None->CLMap.removenodemap|Somevalue->CLMap.addnodevaluemap;;letfixpoint_step~loop_idpreviousnext=matchprevious,nextwith|Someprevious,Somenext->letstate,bool=D.fixpoint_step~loop_idpreviousnextinLog.debug(funp->p"Fixpoint step@\n previous:@\n %a@\nnext:@\n %a@\nres:@\n%a"D.pppreviousD.ppnextD.ppstate);Somestate,bool|None,(Some_)->next,false|None,None->None,true|Some_,None->assertfalse(* Should not happen if transfer
functions are monotonous. *)letcopy=Option.mapD.copy(* Note: this assumes an initial map which is not changed after,
i.e. we want the entry point to have no predecessor. TODO: check
it. *)letrecfixpoint_componentacc=function|Wto.Noden->updaten(prenacc)acc|Wto.Component(head,part)->(* We start over from save_acc everytime, otherwise we have
spurious values when joining that come from the end of
previous iterations. This strategy is described in [Lemerre,
POPL 2023]. *)letsave_acc=accinlethead_prestate=preheadaccinmatchhead_prestatewith|None->acc(* Nothing to propagate on this component. *)|Somehead_prestate->beginlethead_prestate,loop_id=D.enter_loophead_prestateinletrecloophead_prestate=lethead_prestate_backup=copyhead_prestateinletacc=updateheadhead_prestateaccinletacc=fixpoint_partitionaccpartinletnew_head_prestate=preheadaccinletnewstate,fp_reached=fixpoint_step~loop_idhead_prestate_backupnew_head_prestateinLog.debug(funp->p"Fixpoint reached: %b"fp_reached);iffp_reachedthen(* We found an invariant on head. Perform one last,
possibly descending, iteration, to continue and to
register alarms in the loop. Note that this also
performs localized widening
[amato_scozzari2013localizing_widening_narrowing]. *)letnewstate=Option.map(funst->D.leave_loopst)newstateinletacc=updateheadnewstatesave_accinfixpoint_partitionaccpartelseloopnewstateinloop(Somehead_prestate)endandfixpoint_partitionacclist=List.fold_leftfixpoint_componentacclistend