123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970(**************************************************************************)(* 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). *)(* *)(**************************************************************************)[@@@warning"-32-34"]exceptionNever_refinedmoduletypeL=sigtypetvalequal:t->t->boolvalcompare:t->t->intvalhash:t->intendmoduletypeCONDITION=sigtypetvalpretty:Format.formatter->t->unitvalall:tvalequal:t->t->boolvalempty:tvalis_empty:t->boolvalinter:t->t->tval(&&~):t->t->tvalunion:t->t->tval(||~):t->t->tvaldisjoint:t->t->boolvalis_included:t->t->boolvalcomplement:t->tvalvar:unit->tvalhash:t->intendmoduletypeLConditionMapCommon=sigmoduleL:LmoduleCond:CONDITIONtypetvalpretty:(Format.formatter->L.t->unit)->Format.formatter->t->unitvalfind:join:(L.t->L.t->L.t)->bottom:L.t->t->Cond.t->L.tvalrefine:inter:(L.t->L.t->L.t)->t->cond:Cond.t->?notcond:Cond.t->L.t->tendmoduletypeLConditionMapNoPartial=sigincludeLConditionMapCommonendmoduletypeLConditionMap=sigincludeLConditionMapCommonvalcreate_partial:tendmoduletypeLConditionMapFold=sigincludeLConditionMapvalcreate_partial:tvalfold_with_cond:t->Cond.t->'a->(L.t->Cond.t->'a->'a)->'aendmoduleFakeTop(L:L)(* :L with type t = L.t option *)=structtypet=L.toptionletjoin~joinab=match(a,b)with|None,x|x,None->raiseNever_refined|Somea,Someb->Some(joinab)letinter~interab=match(a,b)with|None,x|x,None->x|Somea,Someb->Some(interab)letbottom~bottom=Somebottomletprettyppfmt=function|None->Format.fprintffmt"<none>"|Somex->ppfmtxletequalab=matcha,bwith|None,None->true|Somea,Someb->L.equalab|_,_->falseletcompareab=matcha,bwith|None,None->0|None,Some_->1|Some_,None->-1|Somea,Someb->L.compareablethash=function|None->0|Somex->1+L.hashxendmoduletypeTRANSFER_FUNCTIONS=sigmoduleCond:CONDITIONtype'atvalar0:(moduleLConditionMapwithtypet='atandtypeL.t='aandtypeCond.t=Cond.t)->interres:('a->'a->'a)->Cond.t->'a->'at->'atvalar1:(moduleLConditionMapwithtypet='atandtypeL.t='aandtypeCond.t=Cond.t)->joina:('a->'a->'a)->bottoma:'a->(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->interres:('res->'res->'res)->Cond.t->('a->'res)->'at->'rest->'restvalar2:(moduleLConditionMapwithtypet='atandtypeL.t='aandtypeCond.t=Cond.t)->joina:('a->'a->'a)->bottoma:'a->(moduleLConditionMapwithtypet='btandtypeL.t='bandtypeCond.t=Cond.t)->joinb:('b->'b->'b)->bottomb:'b->(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->interres:('res->'res->'res)->Cond.t->('a->'b->'res)->'at->'bt->'rest->'restvalar1_bwd:(moduleLConditionMapwithtypet='atandtypeL.t='aandtypeCond.t=Cond.t)->joina:('a->'a->'a)->bottoma:'a->intera:('a->'a->'a)->(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->joinres:('res->'res->'res)->bottomres:'res->Cond.t->('a->'res->'aoption)->'at->'rest->(Cond.t*'at)valar2_bwd:(moduleLConditionMapwithtypet='atandtypeL.t='aandtypeCond.t=Cond.t)->joina:('a->'a->'a)->bottoma:'a->intera:('a->'a->'a)->(moduleLConditionMapwithtypet='btandtypeL.t='bandtypeCond.t=Cond.t)->joinb:('b->'b->'b)->bottomb:'b->interb:('b->'b->'b)->(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->joinres:('res->'res->'res)->bottomres:'res->Cond.t->('a->'b->'res->'aoption*'boption)->'at->'bt->'rest->(Cond.t*'at)*(Cond.t*'bt)valnondet_disjoint:(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->conda:Cond.t->notconda:Cond.t->cma:'rest->condb:Cond.t->notcondb:Cond.t->cmb:'rest->join:('res->'res->'res)->bottom:'res->inter:('res->'res->'res)->old:'rest->'restvalnondet_non_disjoint:(moduleLConditionMapwithtypet='restandtypeL.t='resandtypeCond.t=Cond.t)->conda:Cond.t->cma:'rest->condb:Cond.t->cmb:'rest->condaorb:Cond.t->notcondaorb:Cond.t->join:('res->'res->'res)->bottom:'res->inter:('res->'res->'res)->old:'rest->'restendmoduleConditionMapTree(BDD:CONDITION)=structtype'anode_=|Interiorof'ai*'ai|Leafof'aand'ai={key:BDD.t;node:'anode_}type'at='aoptioni;;moduleMake(L:L)=struct(* To handle partial information, we add a top on the lattice. *)moduleFL=FakeTop(L)typet=L.toptionimoduleCond=BDDmoduleL=LmoduleOrig_(L:L)=structletrecprettyppfmt{key;node}=Format.fprintffmt"@[<hv>%a -> %a@]"BDD.prettykey(pretty_nodepp)nodeandpretty_nodeppfmt=function|Leafa->ppfmta|Interior(n1,n2)->Format.fprintffmt"@[<v>%a@\n%a@]"(prettypp)n1(prettypp)n2;;letcreatetop={key=BDD.all;node=Leaftop}letrecfind~join~bottom=fun{key;node}cond->ifBDD.disjointkeycondthenbottomelsematchnodewith|Leaf(v)->v|Interior(c1,c2)->join(find~join~bottomc1cond)(find~join~bottomc2cond);;letrefine~inter=letrecrefine_whole{key;node}value=letnewnode=matchnodewith|Leaf(v)->Leaf(intervvalue)|Interior(n1,n2)->Interior(refine_wholen1value,refine_wholen2value)in{key;node=newnode}inletrecf({key;node}asobj)~cond?(notcond=(BDD.complementcond))value=letbinter=BDD.intercondkeyinifBDD.is_emptybinterthenobjelseifBDD.equalbinterkeythenrefine_wholeobjvalueelseletnewnode=matchnodewith|Leafv->letn1={key=binter;node=Leaf(intervvalue)}inletn2={key=BDD.interkeynotcond;node=Leaf(v)}inInterior(n1,n2)|Interior(n1,n2)->Interior(fn1~cond~notcondvalue,fn2~cond~notcondvalue)in{key;node=newnode}inf;;end(**************** Handle partial. ****************)moduleOrig=Orig_(FL);;letprettyppfmtx=Orig.pretty(FL.prettypp)fmtx;;letcreate_partial=Orig.createNoneletfind~join~bottomnc=matchOrig.find~join:(FL.join~join)~bottom:(FL.bottom~bottom)ncwith|None->assertfalse|Somex->x;;letrefine~intern~cond?notcondv=Orig.refine~inter:(FL.inter~inter)n~cond?notcond(Somev);;endend(* Similar to ConditionMap below, but do not regroup partitions based
on their values. In some case this leads to a huge number of
cases. Merging partitions using lists is also not very
efficient. So this structure should be used only for testing
purpose, or expereminting changes in the interface. *)moduleConditionMapPartitionList(BDD:CONDITION)=structtype'ai=('a*BDD.t)list(* type 'a i = Cons of ('a * BDD.t * 'a i option) *)type'at='aoptionimoduleMake(L:L)=struct(* To handle partial information, we add a top on the lattice. *)moduleFL=FakeTop(L)typet=L.toptionimoduleCond=BDDmoduleL=LmoduleOrig_(L:L)=structletprettyfmt=assertfalseletcreatetop=[(top,BDD.all)]letfind~join~bottomncond=List.fold_left(funacc(v,c)->ifBDD.disjointccondthenaccelseFL.join~joinaccv)(FL.bottom~bottom)n;;(* One advantage of the alist is that we do not need to have
that two different partitions have different values. This
could allow to avoid computing a or if the BDD operation is
too costly (e.g. CUDD allows to limit the time spent doing
some operations). *)letrecremove_assocx(accbdd,acclist)=function|[]->(accbdd,acclist)|(v,c)::restwhenL.equalxv->remove_assocx(BDD.unionaccbddc,acclist)rest|a::rest->remove_assocx(accbdd,a::acclist)rest;;letrecmerge_partitions=function|[]->[]|(v1,c1)::rest->letrest=merge_partitionsrestinlet(newc,rest)=remove_assocv1(c1,[])restin(v1,newc)::rest;;(* We could join partitions with same BDD together. But it is also OK not to do it. *)letrefine~intern~cond?(notcond=BDD.complementcond)newv=letrecloop=function|[]->[]|(v,c)::rest->letc1=BDD.intercondcinletres=looprestinifBDD.is_emptyc1then(v,c)::reselseifBDD.equalc1cthen(internewvv,c)::reselseletc2=BDD.internotcondcin(v,c2)::(internewvv,c1)::resinletres=loopninletlength=List.lengthresin(* This is optional. *)letnewres=merge_partitionsresinletnewlength=List.lengthnewresiniflength>10thenCodex_log.warning"I have a list of length %d newlength %d"lengthnewlength;newres;;end(**************** Handle partial. ****************)(* Same as above *)(* module Orig = Orig_(FL);;
*
* let pretty fmt x = Orig.pretty fmt x;;
* let create _ = assert false
*
* let create_partial = Orig.create None
*
* let find n c =
* match Orig.find n c with
* | None -> assert false
* | Some x -> x
* ;;
*
* let refine n ~cond ?notcond v =
* Orig.refine n ~cond ?notcond (Some v)
* ;; *)endend(* Old version of ConditionMap where we use a fake top on the lattice. *)moduleConditionMapPartitionOld(BDD:CONDITION)=structmoduleMakeReal(L:L):LConditionMapFoldwithmoduleL=LandmoduleCond=BDD=struct(* To handle partial information, we add a top on the lattice. *)moduleFL=FakeTop(L)(* type 'a tmp = 'a t *)moduleCond=BDDmoduleL=LmoduleMap=Map.Make(FL)typet=BDD.tMap.tmoduleOrig=structletprettyppfmtx=letl=Map.bindingsxinletprint_nodefmt(v,c)=Format.fprintffmt"%a -> %a"(FL.prettypp)vBDD.prettycin(Format.pp_print_listprint_node)fmtl(* let create top = [(top,BDD.all)] *)letfind~join~bottomncond=(* Codex_log.feedback "find: %a %a" (pretty pp) n BDD.pretty cond; *)Map.fold(funvcacc->ifBDD.disjointccondthenaccelseFL.join~joinaccv)n(FL.bottom~bottom);;letrefine~intern~cond?(notcond=BDD.complementcond)newv=letaddvcmap=matchMap.findvmapwith|exceptionNot_found->Map.addvcmap|c2->Map.addv(BDD.unioncc2)mapinletfvcacc=letc1=BDD.intercondcinifBDD.is_emptyc1then(addvcacc)elseifBDD.equalc1cthen(add(FL.inter~internewvv)cacc)elseletc2=BDD.internotcondcinletacc=add(FL.inter~internewvv)c1accinletacc=addvc2accinaccinMap.foldfnMap.empty;;end(* Same as above *)letprettyppfmtx=Orig.prettyppfmtx;;letcreate_=assertfalseletcreate_partial=Map.singletonNoneBDD.allletfind~join~bottomnc=matchOrig.find~join~bottomncwith|None->assertfalse|Somex->x;;letrefine~intern~cond?notcondv=(* Kernel.feedback "refine %a %a with %a" L.pretty v pretty n BDD.pretty cond; *)letres=Orig.refine~intern~cond?notcond(Somev)in(* Kernel.feedback "refine %a %a with %a res %a" L.pretty v
pretty n BDD.pretty cond pretty res; *)res;;letfold_with_condxcondaccg=letfvcacc=matchvwith|None->ifnot(BDD.disjointcondc)thenraiseNever_refinedelseacc|Somev->letinter=BDD.intercondcinifBDD.is_emptyinterthenaccelsegvinter(* inter *)accinMap.foldfxacc;;end(* I think that this is still safe to use, but the implementation is
using Obj. *)moduleUnsafe=structtype'at=Obj.tmoduleMake(L:L)=structmoduleCM=MakeReal(L)moduleCond=BDDmoduleL=Ltypet=Obj.tletprettyppfmtx=CM.prettyppfmt(Obj.objx)letrefine~interx~cond?notcondv=Obj.repr@@CM.refine~inter(Obj.objx)~cond?notcondvletfind~join~bottomxcond=CM.find~join~bottom(Obj.objx)condletcreate_partial=Obj.repr@@CM.create_partialletfold_with_cond=Obj.magicCM.fold_with_condendend(* This is less performant, because we allocate an extra record, and
we need to retrieve the function pointer from an element in the
record (virtual call). *)moduleSafe=structmoduletypeT=sigtypekeymoduleCM:LConditionMapFoldwithtypeL.t=keyandmoduleCond=BDDvalvalue:CM.tendtype'at=(moduleTwithtypekey='a)moduleMake(L:L)=structmoduleCM=MakeReal(L)moduleCond=BDDmoduleL=Ltype'atmp='attypet=L.ttmpletprettyppfmt(moduleT:Twithtypekey=L.t)=T.CM.prettyppfmtT.valueletrefine~inter(moduleT:Twithtypekey=L.t)~cond?notcondv=letv=T.CM.refine~interT.value~cond?notcondvinletmoduleRes=structtypekey=T.keymoduleCM=T.CMletvalue=vendin(moduleRes:Twithtypekey=L.t);;letfind~join~bottom(moduleT:Twithtypekey=L.t)cond=T.CM.find~join~bottomT.valuecond;;letfold_with_cond(moduleT:Twithtypekey=L.t)condaccf=T.CM.fold_with_condT.valuecondaccf;;letcreate_partial=letmoduleRes=structtypekey=L.tmoduleCM=CMletvalue=CM.create_partialendin(moduleRes:Twithtypekey=L.t)endend(* include Safe *)includeUnsafeendmoduleConditionMapPartition(BDD:CONDITION)=structmoduleMakeReal(L:L):LConditionMapFoldwithmoduleL=LandmoduleCond=BDD=structmoduleCond=BDDmoduleL=LmoduleOrig=struct(* module Map = Map.Make(L) *)moduleMap=Smallmap.Make(L)typet=BDD.tMap.t(* Can be used only for explicit. *)letmap_fold_explicitfmapinit=Map.foldfmapinit,BDD.empty(* For implicit: also return the union of the keys, which is the
conditions for which the map is defined. *)letmap_fold_implicitfmapinit=letf'keyvalue(acc,bdds)=letacc=fkeyvalueaccinletbdds=BDD.unionbddsvalueinacc,bddsinMap.foldf'map(init,BDD.empty);;letmap_fold=map_fold_explicitletprettyppfmtx=letl=Map.bindingsxinletprint_nodefmt(v,c)=Format.fprintffmt"%a -> %a"ppvBDD.prettycin(Format.pp_print_listprint_node)fmtl(* let create top = [(top,BDD.all)] *)letfind~join~bottomncond=(* Codex_log.feedback "find: %a" (\* (pretty pp) n *\) BDD.pretty cond; *)map_fold(funvcacc->ifBDD.disjointccondthenaccelsejoinaccv)nbottom;;letaddvcmap=matchMap.findvmapwith|exceptionNot_found->Map.addvcmap|c2->Map.addv(BDD.unioncc2)map;;letrefine~intern~cond?(notcond=BDD.complementcond)newv=letfvcacc=letc1=BDD.intercondcinifBDD.is_emptyc1then(addvcacc)elseifBDD.equalc1cthen(add(internewvv)cacc)elseletc2=BDD.internotcondcinletacc=add(internewvv)c1accinletacc=addvc2accinaccinmap_foldfnMap.empty;;end(* Add the special case where the BDD is undefined on top. *)moduleExplicit(Arg:sigend)=structassert(Orig.map_fold==Orig.map_fold_explicit);(* Explicit the condition for which the BDD is not defined;
remove the need for the fake top. *)typet={orig:Orig.t;undefined:BDD.t}letprettyppfmtx=Orig.prettyppfmtx.orig;;letcreate_=assertfalseletcreate_partial={orig=Orig.Map.empty;undefined=BDD.all}letfind~join~bottomnc=if(not@@BDD.disjointn.undefinedc)thenraiseNever_refined;fst@@Orig.find~join~bottomn.origcletrefine~intern~cond?(notcond=BDD.complementcond)v=(* Codex_log.feedback "refining condition %a oldundef %a" BDD.pretty cond BDD.pretty n.undefined; *)letnewundefined=BDD.intern.undefinednotcondinifBDD.equalnewundefinedn.undefinedthen(* Refine already existing values. *){orig=fst@@Orig.refine~intern.orig~cond~notcondv;undefined=newundefined;}else(* We define new values whose values we previously not knew;
we make as if these old values were "top". *)letnewlydefined=BDD.intercondn.undefinedinassert(not@@BDD.is_emptynewlydefined);letorig=n.originletorig=fst@@Orig.refine~interorig~cond~notcondvinletorig=Orig.addvnewlydefinedorigin{orig;undefined=newundefined}letfold_with_condxcondaccg=assertfalse(* let fold_with_cond x cond acc g =
* let f v c acc = match v with
* | None -> if not (BDD.disjoint cond c) then raise Never_refined else acc
* | Some v ->
* let inter = BDD.inter cond c in
* if BDD.is_empty inter then acc
* else g v inter(\* inter *\) acc
* in
* Map.fold f x acc
* ;; *)endmoduleImplicit(Arg:sigend)=structassert(Orig.map_fold==Orig.map_fold_implicit);typet=Orig.tletprettyppfmtx=Orig.prettyppfmtx;;letcreate_partial=Orig.Map.empty;;letfind~join~bottomorigc=letres,defined=Orig.find~join~bottomorigcinif(not@@BDD.is_includedcdefined)thenraiseNever_refinedelseresletrefine~intern~cond?(notcond=BDD.complementcond)v=letorig,defined=Orig.refine~intern~cond~notcondvinifBDD.is_includedconddefined(* Refine already existing values only. *)thenorigelse(* We define new values whose values we previously not knew;
we make as if these old values were "top". *)letnewlydefined=BDD.intercond@@BDD.complementdefinedinassert(not@@BDD.is_emptynewlydefined);letorig=Orig.addvnewlydefinedoriginorigletfold_with_condxcondaccg=assertfalseend(* Explicit requires more storage, but implicit requires
additional BDD computation; so which is faster or more
efficient depends on the benchmark. Maybe a good solution
would be to store the value in the smallmap. *)includeExplicit(structend)(* include Implicit(struct end) *)end(* I think that this is still safe to use, but the implementation is
using Obj. *)moduleUnsafe=structtype'at=Obj.tmoduleMake(L:L)=structmoduleCM=MakeReal(L)moduleCond=BDDmoduleL=Ltypet=Obj.tletprettyppfmtx=CM.prettyppfmt(Obj.objx)letrefine~interx~cond?notcondv=Obj.repr@@CM.refine~inter(Obj.objx)~cond?notcondvletfind~join~bottomxcond=CM.find~join~bottom(Obj.objx)condletcreate_partial=Obj.repr@@CM.create_partialletfold_with_cond=Obj.magicCM.fold_with_condendend(* This is less performant, because we allocate an extra record, and
we need to retrieve the function pointer from an element in the
record (virtual call). *)moduleSafe=structmoduletypeT=sigtypekeymoduleCM:LConditionMapFoldwithtypeL.t=keyandmoduleCond=BDDvalvalue:CM.tendtype'at=(moduleTwithtypekey='a)moduleMake(L:L)=structmoduleCM=MakeReal(L)moduleCond=BDDmoduleL=Ltype'atmp='attypet=L.ttmpletprettyppfmt(moduleT:Twithtypekey=L.t)=T.CM.prettyppfmtT.valueletrefine~inter(moduleT:Twithtypekey=L.t)~cond?notcondv=letv=T.CM.refine~interT.value~cond?notcondvinletmoduleRes=structtypekey=T.keymoduleCM=T.CMletvalue=vendin(moduleRes:Twithtypekey=L.t);;letfind~join~bottom(moduleT:Twithtypekey=L.t)cond=T.CM.find~join~bottomT.valuecond;;letfold_with_cond(moduleT:Twithtypekey=L.t)condaccf=T.CM.fold_with_condT.valuecondaccf;;letcreate_partial=letmoduleRes=structtypekey=L.tmoduleCM=CMletvalue=CM.create_partialendin(moduleRes:Twithtypekey=L.t)endend(* include Safe *)includeUnsafeend(* Generic implementation. *)moduleMakePathInsensitive(BDD:CONDITION)(M:sigtype'atend):TRANSFER_FUNCTIONSwithmoduleCond=BDDandtype'at='aM.t=structmoduleCond=BDDtype'at='aM.tletar0(typeres)(moduleL:LConditionMapwithtypet=resM.tandtypeL.t=resandtypeCond.t=Cond.t)~interrescondvalueold=letnotcond=BDD.complementcondinL.refine~inter:interresold~cond~notcondvalueletar1(typea)(typema)(moduleLa:LConditionMapwithtypet=maandtypeL.t=aandtypeCond.t=Cond.t)~joina~bottoma(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~interrescondfaold=letav=La.find~join:joina~bottom:bottomaacondinletnotcond=BDD.complementcondinLres.refine~inter:interresold~cond~notcond(fav);;letar2(typea)(typema)(moduleLa:LConditionMapwithtypet=maandtypeL.t=aandtypeCond.t=Cond.t)~joina~bottoma(typeb)(typemb)(moduleLb:LConditionMapwithtypet=mbandtypeL.t=bandtypeCond.t=Cond.t)~joinb~bottomb(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~interrescondfabold=letav=La.find~join:joina~bottom:bottomaacondinletbv=Lb.find~join:joinb~bottom:bottombbcondinletnotcond=BDD.complementcondinLres.refine~inter:interresold~cond~notcond(favbv);;letchanged(refine:'a->cond:BDD.t->?notcond:BDD.t->'b->'a)condr=function|None->BDD.empty,r|Somex->cond,refiner~condx;;letar1_bwd(typea)(typema)(moduleLa:LConditionMapwithtypet=maandtypeL.t=aandtypeCond.t=Cond.t)~joina~bottoma~intera(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~joinres~bottomrescondfares=letva=La.find~join:joina~bottom:bottomaacondinletv=Lres.find~join:joinres~bottom:bottomresrescondinletnewva=fvavinchanged(La.refine~inter:intera)condanewva;;letar2_bwd(typea)(typema)(moduleLa:LConditionMapwithtypet=maandtypeL.t=aandtypeCond.t=Cond.t)~joina~bottoma~intera(typeb)(typemb)(moduleLb:LConditionMapwithtypet=mbandtypeL.t=bandtypeCond.t=Cond.t)~joinb~bottomb~interb(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~joinres~bottomrescondfabres=letva=La.find~join:joina~bottom:bottomaacondinletvb=Lb.find~join:joinb~bottom:bottombbcondinletv=Lres.find~join:joinres~bottom:bottomresrescondinletnewva,newvb=fvavbvin(changed(La.refine~inter:intera)condanewva,changed(Lb.refine~inter:interb)condbnewvb);;(* Note: a simple implementation of path-sensitive analysis would
get a list of (condition,value) leaves; and refine with the
cartesian product. This would work, but there would be a
quadratic, instead of linear behaviour when the elements have the
same partitionning. *)letnondet_disjoint(typeres)(typemres)(moduleL:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~conda~notconda~cma~condb~notcondb~cmb~join~bottom~inter~old=letav=L.find~join~bottomcmacondainletbv=L.find~join~bottomcmbcondbinletres=L.refine~interoldav~cond:conda~notcond:notcondainletres=L.refine~interresbv~cond:condb~notcond:notcondbinres;;letnondet_non_disjoint(typeres)(typemres)(moduleL:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~conda~cma~condb~cmb~condaorb~notcondaorb~join~bottom~inter~old=letav=L.find~join~bottomcmacondainletbv=L.find~join~bottomcmbcondbinletabv=joinavbvinletres=L.refine~interoldabv~cond:condaorb~notcond:notcondaorbinres;;endmoduleMakePathSensitive(BDD:CONDITION)(M:sigtype'atend)(* :TRANSFER_FUNCTIONS with module Cond = BDD and type 'a t = 'a M.t *)=structmoduleCond=BDDtype'at='aM.tletar0(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=BDD.t)condvalueold=Lres.refineold~condvalue;;letar1(typea)(typema)(moduleLa:LConditionMapFoldwithtypet=maandtypeL.t=aandtypeCond.t=BDD.t)(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=BDD.t)~intercondfaold=La.fold_with_condacondold(funvcacc->letv=fvinLres.refine~interacc~cond:cv);;letar2(typea)(typema)(moduleLa:LConditionMapFoldwithtypet=maandtypeL.t=aandtypeCond.t=BDD.t)(typeb)(typemb)(moduleLb:LConditionMapFoldwithtypet=mbandtypeL.t=bandtypeCond.t=BDD.t)(typeres)(typemres)(moduleLres:LConditionMapwithtypet=mresandtypeL.t=resandtypeCond.t=BDD.t)~intercondfabold=La.fold_with_condacondold(funvacaacc->Lb.fold_with_condbcaacc(funvbcabacc->Lres.refine~interacc~cond:cab(fvavb)));;letar1_bwd(typea)(typema)(moduleLa:LConditionMapFoldwithtypet=maandtypeL.t=aandtypeCond.t=BDD.t)~intera(typeres)(typemres)(moduleLres:LConditionMapFoldwithtypet=mresandtypeL.t=resandtypeCond.t=BDD.t)condfares=Lres.fold_with_condrescond(BDD.empty,a)(funvrescres((acccond,acca)asacc)->La.fold_with_condacresacc(funvacares(acccond,acca)->matchfvavreswith|None->(acccond,acca)|Somev->(BDD.unioncaresacccond,La.refine~inter:interaacca~cond:caresv)));;letar2_bwd(typea)(typema)(moduleLa:LConditionMapFoldwithtypet=maandtypeL.t=aandtypeCond.t=BDD.t)~intera(typeb)(typemb)(moduleLb:LConditionMapFoldwithtypet=mbandtypeL.t=bandtypeCond.t=BDD.t)~interb(typeres)(typemres)(moduleLres:LConditionMapFoldwithtypet=mresandtypeL.t=resandtypeCond.t=BDD.t)condfabres=Lres.fold_with_condrescond((BDD.empty,a),(BDD.empty,b))(funvrescresacc->La.fold_with_condacresacc(funvacaresacc->Lb.fold_with_condbcaresacc(funvbcabres((accca,acca),(acccb,accb))->letnewa,newb=fvavbvresinlet(accca,acca)=matchnewawith|None->accca,acca|Somev->(BDD.unioncabresaccca,La.refine~inter:interaacca~cond:cabresv)inlet(acccb,accb)=matchnewbwith|None->acccb,accb|Somev->(BDD.unioncabresacccb,Lb.refine~inter:interbaccb~cond:cabresv)in((accca,acca),(acccb,accb)))));;letnondet_disjoint(typeres)(typemres)(moduleL:LConditionMapFoldwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~conda~notconda~cma~condb~notcondb~cmb~inter~old=letres=oldinletres=L.fold_with_condcmacondares(funvcacc->L.refine~interacc~cond:c(* (BDD.inter c conda) *)v)inletres=L.fold_with_condcmbcondbres(funvcacc->L.refine~interacc~cond:c(* (BDD.inter c condb) *)v)inres;;letnondet_non_disjoint(typeres)(typemres)(moduleL:LConditionMapFoldwithtypet=mresandtypeL.t=resandtypeCond.t=Cond.t)~conda~cma~condb~cmb~condaorb~notcondaorb~inter~old=(* Special case. *)ifBDD.equalcondacondbthenar2(moduleL)(moduleL)(moduleL)conda(assertfalse)~intercmacmboldelse(* MAYBE: pass these as an argument. *)leta_and_b=BDD.intercondacondbinleta_minus_b=BDD.interconda@@BDD.complementcondbinletb_minus_a=BDD.intercondb@@BDD.complementcondain(* Add the common values. *)letacc=ar2(moduleL)(moduleL)(moduleL)a_and_b(* L.L.join *)(assertfalse)~intercmacmboldin(* Add the values in a only *)letacc=L.fold_with_condcmaa_minus_bacc(funvcacc->L.refine~interacc~cond:cv)in(* Add the values in b only. *)letacc=L.fold_with_condcmbb_minus_aacc(funvcacc->L.refine~interacc~cond:cv)inaccend