123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407(**************************************************************************)(* 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). *)(* *)(**************************************************************************)moduleTC=Operator.Function_symbolmoduleIn_bits=Units.In_bitsletnb_binary_terms=ref0letnb_unions=ref0letsizes=Hashtbl.create1000moduletypeSUPER_TERMS=sigtype'asupertermsmoduleMake(Param:sigtypeeltvalget_superterms:elt->eltsupertermsvalset_superterms:elt->eltsuperterms->unitend):sig(** An empty set of superterms. *)valcreate:unit->Param.eltsuperterms(** Add one parent to one child. *)valadd_one_superterm:subterm:Param.elt->superterm:Param.elt->unit(** Iterate on all superterms of a child. *)valiter_on_superterms:Param.elt->(Param.elt->unit)->unitendendmoduleSUPER_TERMS_WeakArray:SUPER_TERMS=struct(** WeakSet takes too much memory, we use weak array instead. *)type'asuperterms='aWeak.tmoduleMake(P:sigtypeeltvalget_superterms:elt->eltsupertermsvalset_superterms:elt->eltsuperterms->unitend)=struct(* We assume that most constraints will have at least one parent. *)letcreate()=Weak.create1;;(* Linear search *)letadd_one_superterm~subterm~superterm=tryletparents=P.get_supertermssubterminletlength=Weak.lengthparentsinassert(length>0);fori=0tolength-1doifnot(Weak.checkparentsi)thenbeginWeak.setparentsi(Somesuperterm);raiseExitenddone;(* Complexity warning. Note that we could always drop parents
if we really need to. *)(* Note: I think this now happens many when I do weak updates:
the new value is in a nondet with all the cells in the array. *)iflength>=32then(Codex_log.performance_warning"Complexity warning: a constrain has more than %d parents"length;(* raise Exit *)(* Kernel.warning "subterm %a superterm %a" *)(* length Pretty.Pretty.pretty subterm Pretty.Pretty.pretty superterm *));letnewparents=Weak.create(2*length)inWeak.blitparents0newparents0length;P.set_supertermssubtermnewparents;Weak.setnewparentslength(Somesuperterm)withExit->()letiter_on_supertermssubtermf=letparents=P.get_supertermssubterminfori=0to(Weak.lengthparents)-1domatchWeak.getparentsiwith|None->()|Somex->f@@xdoneend(* MAYBE: also add the index of the last superterm added, and start
from here. This should allow amortized O(1) insertion if we
explore from the start when the array is full, and O(1) if
we do not try to reuse de-allocated array cells.
We could also have a bitmap with the free elements in an int,
and find the correct one with find_first_set. The bitmap would
be updated on iter_on_superterms. More complex, and probably not
as useful.
To have small bitmaps unboxed and large boxed, we could just
use Zarith; there are popcount and trailing_zeros functions.
For now, just warn when we have a large number of superterms,
which is the main problem anyway. The goal is to have a low
memory consumption, so an extra index is useless. *)end(** Does not store superterms *)moduleSUPER_TERMS_Unit:SUPER_TERMS=structtype'asuperterms=unitmoduleMake(P:sigtypeelt[@@@ocaml.warning"-32"]valget_superterms:elt->eltsupertermsvalset_superterms:elt->eltsuperterms->unit[@@@ocaml.warning"+32"]end)=structletcreate()=()letadd_one_superterm~subterm~superterm=()letiter_on_superterms__=()endendletsuperterm_maker:(moduleSUPER_TERMS)=ifCodex_config.terms_register_parents()then(moduleSUPER_TERMS_WeakArray)else(moduleSUPER_TERMS_Unit)moduleSupertermMaker=(valsuperterm_maker)moduleMake(Condition:Condition_map.CONDITION)(Relation:Union_Find.Parameters.GENERIC_GROUP)()=structtypeboolean=TC.booleantypeinteger=TC.integertypebinary=TC.binarytypeenum=TC.enummoduleLog=Tracelog.Make(structletcategory="Terms.Builder"end);;(* Install the buttons to be understood by tracelog. *)Log.info(funp->p"Defines clickable buttons for Emacs tracelog-mode: %s%s%s""<(define:B:search-before:let B%1 =)>""<(define:i:search-before:let i%1 =)>""<(define:b:search-before:let b%1 =)>");;moduleCondition=ConditionmoduleRelation=Relationtype('arg,'res)function_symbol=('arg,'res)TC.function_symbolmoduleId:sigtype'at=Id:int->'at[@@unboxed];;valfresh:unit->'atval_equal:'at->'bt->boolval_hash:'at->intvalto_int:'at->intend=structtype'at=Id:int->'at[@@unboxed];;letcount=ref0;;letfresh()=incrcount;Id!count;;let_equal(Ida)(Idb)=a==b;;let_hash(Ida)=a;;letto_int=_hashendtypewidening_id=inttypelevel=intmoduleT=struct(* MAYBE: Completely replace booleans by bdds. Alternatively: hide
the presence of bdd, we only manipulate boolean constraints. *)type'rescomplete_term=|Tuple_get:int*cfg_node->'rescomplete_term|Empty:'rescomplete_term|Unknown:level->'rescomplete_term|Mu_formal:{level:level;actual:('rest*TC.booleant)}->'rescomplete_term(* Note: maybe the level is a less fragile way to give name to inductive variables
(where desambiguation can be done by the scope). For now we store both. *)(* Note: we have to choose between getting rid of the tuples and
having [Inductive_var] here, or keeping them and having
[Inductive_vars] in tuple_. *)|Inductive_var:{widening_id:widening_id;level:level;mutabledefinition:'rest}->'rescomplete_term|T0:{tag:(TC.ar0,'res)function_symbol}->'rescomplete_term|T1:{tag:('aTC.ar1,'res)function_symbol;a:'at;level:level}->'rescomplete_term|T2:{tag:(('a,'b)TC.ar2,'res)function_symbol;a:'at;b:'bt;level:level}->'rescomplete_termand'at=|Bool:{mutablesuperterms:superterms;id:booleanId.t;mutableparent:booleanparent;term:booleancomplete_term;bdd:Condition.t}->booleant|Integer:{mutablesuperterms:superterms;id:integerId.t;mutableparent:integerparent;term:integercomplete_term;}->integert|Binary:{mutablesuperterms:superterms;id:binaryId.t;mutableparent:binaryparent;term:binarycomplete_term;size:In_bits.t}->binaryt|Enum:{mutablesuperterms:superterms;id:enumId.t;mutableparent:enumparent;term:enumcomplete_term}->enumtand'aparent=|Node:'bt*('a,'b)Relation.t->'aparent|Rootandany=Any:'rest->any[@@unboxed](** Note: Cannot use any (because the boxing clashes with using Weak),
and we need to make the type disappear: so we use Obj.t instead. *)andsuperterms=Obj.tSupertermMaker.superterms(** The tuple that we manipulate *)andtuple=anyImmutable_array.t(** Node in the SSA graph. *)andcfg_node=(* TODO:
- View tuples as a CFG node, as in Lemerre's HDR.
- Compute the domination tree, use this to replace the notion of level
- When joining: compute the condition that separates the two from the dominator.
Use this for the path-sensitive analysis. *)|Nondetof{id:tupleId.t;level:level;a:tuple;conda_bool:booleant;b:tuple;condb_bool:booleant}|Muof{id:tupleId.t;level:level;init:tuple;var:tuple;body:tuple;body_cond:booleant}(* Maybe: add a condition for the path predicate. *)|Inductive_varsof{id:tupleId.t;widening_id:widening_id;level:int;mutabledef:tuple}endincludeTtype'aconstraints='atletget_id:typea.at->aId.t=function|Bool{id}->id|Integer{id}->id|Binary{id}->id|Enum{id}->idletget_term:typea.at->acomplete_term=function|Bool{term}->term|Integer{term}->term|Binary{term}->term|Enum{term}->termletget_id_int:typea.at->int=function|Bool{id=Id.Idid}->id|Integer{id=Id.Idid}->id|Binary{id=Id.Idid}->id|Enum{id=Id.Idid}->idletpolyeq(typea)(typeb)(x:at)(y:bt):(a,b)PatriciaTree.cmp=matchx,ywith|Bool{id=Id.Idil;_},Bool{id=Id.Idir;_}->ifil==irthenEqelseDiff|Integer{id=Id.Idil;_},Integer{id=Id.Idir;_}->ifil==irthenEqelseDiff|Binary{id=Id.Idil;_},Binary{id=Id.Idir;_}->ifil==irthenEqelseDiff|_->Diffletcompare:typeab.at->bt->int=funab->Int.compare(get_id_inta)(get_id_intb)letequal:typeab.at->bt->bool=funab->(get_id_inta)==(get_id_intb)lethashx=get_id_intx;;letlevel_term:typea.acomplete_term->level=function|Tuple_get(_,Nondet{level})->level|Tuple_get(_,Inductive_vars{level})->level|Tuple_get(_,Mu{level})->level|Empty->-1|Unknownlevel->level|Mu_formal{level}->level|Inductive_var{level}->level|T0_->-1|T1{level}->level|T2{level}->level;;letlevelx=level_term@@get_termx;;letsize_of:binaryt->In_bits.t=functionBinary{size}->sizemoduleAny=structtypet=anylethash(Anyx)=get_id_intxletequal(Anya)(Anyb)=(get_id_inta)==(get_id_intb);;letget_id_int(Anya)=get_id_inta;;letcompareab=Int.compare(get_id_inta)(get_id_intb)endmoduleCFG_Node=structtypet=cfg_nodeletequalab=match(a,b)with|Mu{id=Id.Id(id1)},Mu{id=Id.Id(id2)}->id1==id2|Nondet{id=Id.Id(id1)},Nondet{id=Id.Id(id2)}->id1==id2|Inductive_vars{id=Id.Id(id1)},Inductive_vars{id=Id.Id(id2)}->id1==id2|_->falselethash=function|Mu{id=Id.Id(id)}->id|Nondet{id=Id.Id(id)}->id|Inductive_vars{id=Id.Id(id)}->idletcompareab=Int.compare(hasha)(hashb)endmodulePretty=structtype'apack=|Unit:TC.ar0pack|Single:'at->'aTC.ar1pack|Pair:'at*'bt->('a,'b)TC.ar2packmodulerecPrettyArg:TC.PRETTY_ARGwithtype'apack='apackandtype'at='at=structtypenonrec'at='attypenonrec'apack='apack(* The eta expansion is important to avoid having a recursive module exception. *)letprettyfmtx=Pretty.prettyfmtxletextract1(Singlex)=xletextract2(Pair(a,b))=a,bendandPrettyApply:TC.PRETTY_RESULTwithtype'apack='apackandtype'at='at=TC.Pretty(PrettyArg)andPretty:sigvalpretty:Format.formatter->'at->unitvalpretty_definition:Format.formatter->'at->unit(* val pretty_tuple_deps: Format.formatter -> tuple -> unit *)valpretty_any:Format.formatter->any->unitend=structletrecpretty_definition:typea.Format.formatter->at->unit=funfmtcons->matchconswith|Bool{id=Id.Idid;term}->pretty_definition_complete_term"b"idfmtterm;|Integer{id=Id.Idid;term}->pretty_definition_complete_term"i"idfmtterm;|Binary{id=Id.Idid;term}->pretty_definition_complete_term"B"idfmtterm;|Enum{id=Id.Idid;term}->pretty_definition_complete_term"e"idfmtterm;andpretty_definition_complete_term:typea.string->int->Format.formatter->acomplete_term->unit=funstridfmtcons->letprint_deftagx=Format.fprintffmt"let %s%d = "strid;PrettyApply.pretty_destructfmttagx;inmatchconswith|T0{tag=(TC.Biconst_)astag}->Format.fprintffmt"(";print_deftagUnit;Format.fprintffmt")"|T0{tag}->print_deftagUnit|T1{tag;a}->print_deftag(Singlea)|T2{tag;a;b}->print_deftag(Pair(a,b))|Tuple_get(i,tup)->Format.fprintffmt"let %s%d = tuple_get(%d,%a)@\n"stridipretty_tupletup|Empty->Format.fprintffmt"let %s%d = empty@\n"strid|Unknown_level->Format.fprintffmt"let %s%d = unknown(%d)@\n"strid_level|Mu_formal{level}->Format.fprintffmt"let %s%d = var(%d)@\n"stridlevel|Inductive_var{widening_id}->Format.fprintffmt"let %s%d = ivar(%d)@\n"stridwidening_idandpretty:typea.Format.formatter->at->unit=funfmtcons->matchconswith|Bool{id=Id.Idid;term;parent}->Format.fprintffmt"<(b:%d%a)>"idpretty_parentparent|Integer{id=Id.Idid;term;parent}->Format.fprintffmt"<(i:%d%a)>"idpretty_parentparent|Binary{term=T0{tag=(TC.Biconst(size,k))astag}}->PrettyApply.pretty_destructfmttagUnit|Binary{id=Id.Idid;term;parent}->Format.fprintffmt"<(B:%d%a)>"idpretty_parentparent|Enum{id=Id.Idid;term}->Format.fprintffmt"<(e:%d)>"idandpretty_parent:typea.Format.formatter->aparent->unit=funfmtparent->matchparentwith|Root->()|Node_->()andpretty_tuplefmttup=matchtupwith|Nondet({id=Id.Idid})->Format.fprintffmt"T%d"id|Inductive_vars({id=Id.Idid})->Format.fprintffmt"T%d"id|Mu{id=Id.Idid}->Format.fprintffmt"T%d"idandpretty_anyfmt=function|Anyx->prettyfmtxandpretty_definition_anyfmt=function|Anyx->pretty_definitionfmtxandpretty_tuple_fmtx=Immutable_array.pp_array~pre:"<"~suf:">"~sep:","pretty_anyfmtxandpretty_definition_tuple_fmtx=Immutable_array.iter(pretty_definition_anyfmt)x;;let_pretty_tuple_deps=pretty_definition_tuple_let_pretty_tuple=pretty_tuple_endendincludePretty.PrettymoduleSUPER_TERMS=structmoduleParam=structtypeelt=Obj.tletget_superterms:typea.aconstraints->superterms=function|Bool{superterms;_}->superterms|Integer{superterms;_}->superterms|Binary{superterms;_}->superterms|Enum{superterms;_}->supertermsletget_supertermsx=get_superterms@@Obj.objxletset_superterms:typea.aconstraints->superterms->unit=funxsuperterms->matchxwith|Boolr->r.superterms<-superterms|Integerr->r.superterms<-superterms|Binaryr->r.superterms<-superterms|Enumr->r.superterms<-superterms;;letset_supertermsxpar=set_superterms(Obj.objx)parendmoduleMade=SupertermMaker.Make(Param)letcreate=Made.createletadd_one_superterm~child~parent=(* Constants cannot be improved, and have lots of superterms. Do
not register superterms for constants. *)letis_constant:typea.aconstraints->'b=function|Bool{term=(T0_|Empty)}->true|Integer{term=(T0_|Empty)}->true|Binary{term=(T0_|Empty)}->true|_->falseinifis_constantchildthen()elseMade.add_one_superterm~subterm:(Obj.reprchild)~superterm:(Obj.reprparent);;letadd_to_childs:typea.aconstraints->unit=funparent->letadd_parent_term:typeres.resconstraints->rescomplete_term->unit=funparentterm->matchtermwith|Empty|Unknown_|Mu_formal_|T0_|Inductive_var_->()|Tuple_get(i,(Mu_|Inductive_vars_))->()(* Do not redo fixpoints. *)|Tuple_get(i,Nondet{a;b})->letAnyxa=Immutable_array.getaiinadd_one_superterm~child:xa~parent;letAnyxb=Immutable_array.getbiinadd_one_superterm~child:xb~parent|T1{a}->add_one_superterm~child:a~parent|T2{a;b}->add_one_superterm~child:a~parent;add_one_superterm~child:b~parentinletterm=get_termparentinadd_parent_termparentterm;;(* With this we can remove the set of superterms altogether (thereby
allowing for no forward constraint propagation), if it takes
too long (e.g. for now, on huge arrays which should be dealt
with.) *)(* let add_to_childs _ = ();; *)letiter_on_supertermschildf=letchild=Obj.reprchildinletfx=f(Any(Obj.objx))inMade.iter_on_supertermschildfend(* Note: my benchs showed that, at least for the way I generate
constraints, hashconsing is very important. *)moduleHashCons=struct(* Note: we use different hashtables according to the type of the
complete term. This should reduce the amount of collisions? *)moduleParam=structletequal:typea.acomplete_term->acomplete_term->bool=funab->(a==b)||match(a,b)with|Tuple_get(i,x),Tuple_get(j,y)->i==j&&(assertfalse)|Empty,_->assertfalse|Unknown_,_->assertfalse|Mu_formal_,_->assertfalse|Inductive_var_,_->assertfalse|T0{tag=tag1},T0{tag=tag2}->TC.equaltag1tag2|T1{tag=tag1;a=a1},T1{tag=tag2;a=a2}->TC.equaltag1tag2&&(get_id_inta1)==(get_id_inta2)|T2{tag=tag1;a=a1;b=b1},T2{tag=tag2;a=a2;b=b2}->TC.equaltag1tag2&&(get_id_inta1)==(get_id_inta2)&&(get_id_intb1)==(get_id_intb2)|_->falselethash:typea.acomplete_term->int=function|Tuple_get(i,_)->assertfalse(* TODO: create an id for tuples. *)|Empty->assertfalse(* Should not be hashed *)|Unknown_->assertfalse(* Should not be hashed: created every time. *)|Mu_formal_->assertfalse(* Should not be hashed: fresh every time. *)|Inductive_var_->assertfalse(* Should not be hashed: fresh every time. *)|T0{tag}->Hashing.hash_sum~nb:0~total:2(TC.hashtag)|T1{tag;a}->Hashing.hash_sum~nb:1~total:2(Hashing.hash2(TC.hashtag)(get_id_inta))|T2{tag;a;b}->Hashing.hash_sum~nb:2~total:2(Hashing.hash3(TC.hashtag)(get_id_inta)(get_id_intb))endmoduletypeEphemeronHashCommon=sigtypekeytype'atvalcreate:int->'atvalfind:'at->key->'avalreplace:'at->key->'a->unitend(* Note: If prefer_predictability_over_performance is false, the
variables numbers can vary according to garbage collection
cycles.
TODO We should probably hash-cons nothing, or just the
constants, this should be predictable and probably more
efficient.
Note: actually my benchmarks seem to run better
(more efficiently, and often taking less memory) when this is activated. *)moduleBoolParam=structtypet=booleancomplete_termincludeParamendmoduleHashBool=(valifCodex_config.hash_cons_terms_with_weak_table()then(moduleEphemeron.K1.Make(BoolParam):EphemeronHashCommonwithtypekey=booleancomplete_term)else(moduleHashtbl.Make(BoolParam):EphemeronHashCommonwithtypekey=booleancomplete_term));;moduleIntParam=structtypet=integercomplete_termincludeParamendmoduleHashInt=(valifCodex_config.hash_cons_terms_with_weak_table()then(moduleEphemeron.K1.Make(IntParam):EphemeronHashCommonwithtypekey=integercomplete_term)else(moduleHashtbl.Make(IntParam):EphemeronHashCommonwithtypekey=integercomplete_term))moduleEnumParam=structtypet=enumcomplete_termincludeParamendmoduleHashEnum=(valifCodex_config.hash_cons_terms_with_weak_table()then(moduleEphemeron.K1.Make(EnumParam):EphemeronHashCommonwithtypekey=enumcomplete_term)else(moduleHashtbl.Make(EnumParam):EphemeronHashCommonwithtypekey=enumcomplete_term))moduleBinParam=structtypet=binarycomplete_termincludeParamendmoduleHashBin=(valifCodex_config.hash_cons_terms_with_weak_table()then(moduleEphemeron.K1.Make(BinParam):EphemeronHashCommonwithtypekey=binarycomplete_term)else(moduleHashtbl.Make(BinParam):EphemeronHashCommonwithtypekey=binarycomplete_term));;letbool_hash=HashBool.create117;;letint_hash=HashInt.create117;;letenum_hash=HashEnum.create117;;letbin_hash=HashBin.create117;;moduleBDDHash=Ephemeron.K1.Make(Condition);;letbddhash=BDDHash.create117;;moduleX=Hashtbl.Make(Condition);;letbooleantermbdd=tryHashBool.findbool_hashtermwithNot_found->letbdd=bdd()intryletx=BDDHash.findbddhashbddinletnewer=(Bool{superterms=SUPER_TERMS.create();parent=Root;id=Id.fresh();term;bdd})inLog.info(funp->p"%a"pretty_definitionnewer);xwithNot_found->letres=Bool{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term;bdd}inLog.info(funp->p"%a"pretty_definitionres);(* Codex_log.feedback "Creating boolean %a" pretty res; *)HashBool.replacebool_hashtermres;BDDHash.replacebddhashbddres;(* Kernel.feedback "bdd: %a term %a" Condition.pretty bdd *)(* pretty res; *)SUPER_TERMS.add_to_childsres;resletintegerterm=tryHashInt.findint_hashtermwithNot_found->letres=Integer{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term}inLog.info(funp->p"%a"pretty_definitionres);HashInt.replaceint_hashtermres;SUPER_TERMS.add_to_childsres;resletenumterm=tryHashEnum.findenum_hashtermwithNot_found->letres=Enum{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term}inLog.info(funp->p"%a"pretty_definitionres);HashEnum.replaceenum_hashtermres;SUPER_TERMS.add_to_childsres;resletbinary~(size:In_bits.t)term=tryHashBin.findbin_hashtermwithNot_found->incrnb_binary_terms;letres=Binary{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term;size}inLog.info(funp->p"%a"pretty_definitionres);HashBin.replacebin_hashtermres;SUPER_TERMS.add_to_childsres;resend(** Similar to Hashcons, except that we do not need to hashcons because
what we create is always fresh. *)moduleFresh=struct(* Note: possibly no need to call add_to_childs here. *)letbooleantermbdd=letres=Bool{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term;bdd}inHashCons.BDDHash.replaceHashCons.bddhashbddres;(matchtermwithTuple_get_->()|_->Log.info(funp->p"%a"pretty_definitionres));SUPER_TERMS.add_to_childsres;resletintegerterm=letres=Integer{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term}in(matchtermwithTuple_get_->()|_->Log.info(funp->p"%a"pretty_definitionres));SUPER_TERMS.add_to_childsres;resletbinary~sizeterm=incrnb_binary_terms;letres=Binary{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term;size}in(matchtermwithTuple_get_->()|_->Log.info(funp->p"%a"pretty_definitionres));SUPER_TERMS.add_to_childsres;resletenumterm=letres=Enum{superterms=SUPER_TERMS.create();id=Id.fresh();parent=Root;term}in(matchtermwithTuple_get_->()|_->Log.info(funp->p"%a"pretty_definitionres));SUPER_TERMS.add_to_childsres;resendmoduleBuild=struct(* Note on smart constructors rewriting. Since we also store the
conditions for correct definition, we can perform all kind of
rewrites; it is OK to have 0 * x -> 0 (we don't loose the
condition on x, because it is already handled by the caller of
this module).
But we have to avoid creating new terms (which may not have
been evaluated by the domains); we can only reuse already
existing terms. The only exception to this is the creation of
new constant terms: we mandate that the domains are able to
'auto-evaluate' them. *)moduleBoolean=structletbunion(Boolbaasa)(Boolbbasb)=HashCons.boolean(T2{tag=TC.BoolUnion;a;b;level=max0@@max(levela)(levelb)})(fun()->Condition.unionba.bddbb.bdd);;let(&&)(Boolbaasa)(Boolbbasb)=HashCons.boolean(T2{tag=TC.And;a;b;level=max(levela)(levelb)})(fun()->Condition.interba.bddbb.bdd);;(* Optimized version. Maybe not worth it. *)let(&&)(Boolbaasa)(Boolbbasb)=matchba.term,bb.termwith|(T0{tag=TC.True}),_->b|(T0{tag=TC.False}),_->a|_,(T0{tag=TC.True})->a|_,(T0{tag=TC.False})->b|_,_whena==b->a|_->(&&)ab;;let(||)(Boolbaasa)(Boolbbasb)=HashCons.boolean(T2{tag=TC.Or;a;b;level=max(levela)(levelb)})(fun()->Condition.unionba.bddbb.bdd);;(* Optimized version. Maybe not worth it. *)let(||)(Boolbaasa)(Boolbbasb)=matchba.term,bb.termwith|(T0{tag=TC.True}),_->a|(T0{tag=TC.False}),_->b|_,(T0{tag=TC.True})->b|_,(T0{tag=TC.False})->a|_,_whena==b->a|_->(||)ab;;(* Note: we need to use boolean_with_id here, because the value
of the bdd field is special. *)lettrue_=Fresh.boolean(T0{tag=TC.True})Condition.all;;letfalse_=Fresh.boolean(T0{tag=TC.False})Condition.empty;;(* Optimisation: avoid creating terms when we can. *)letnot((Boolbaasa):booleant)=matchba.termwith|T0{tag=TC.True}->false_|T0{tag=TC.False}->true_|_->HashCons.boolean(T1{tag=TC.Not;a;level=levela})(fun()->Condition.complementba.bdd);;letunknown~level=assert(level>=0);Fresh.boolean(Unknownlevel)@@Condition.var()(* Both representations of empty are possible: a variable which
is initially empty, and a special empty constructor. We favor
the second which is semantically better grounded (and yields
better translations, etc.). But using the former sometimes
slightly improves performances. *)(* let empty = unknown ~level:(0);; *)letempty=Fresh.booleanEmpty@@Condition.var();;(* It is important to use these smart constructors to avoid
creating huge BDDs representing empty. *)let(&&)ab=ifa==emptythenemptyelseifb==emptythenemptyelse(&&)ab;;letnota=ifa==emptythenemptyelsenota;;let(||)ab=ifa==emptythenemptyelseifb==emptythenemptyelse(||)ab;;typeboolean=TC.booleantendmoduleInteger=structleticonstk=HashCons.integer@@T0{tag=TC.Iconstk};;letitimeska=HashCons.integer@@T1{tag=TC.Itimesk;a;level=levela};;letzero=iconstZ.zeroletone=iconstZ.oneletunknown~level=Fresh.integer(Unknownlevel)letempty=Fresh.integerEmptyletar2_integer_integer_integertagab=HashCons.integer@@T2{tag;a;b;level=max(levela)(levelb)};;letar2_integer_integer_integer_commutativetagab=(* This comparison ensures that constants are on the right. *)letgeab=letlvla=levelaandlvlb=levelbiniflvla=lvlbthenget_ida>=get_idbelselvla>=lvlbinlet(a,b)=ifgeabthen(a,b)else(b,a)inar2_integer_integer_integertagab;;letiadd=ar2_integer_integer_integer_commutativeTC.Iaddletimul=ar2_integer_integer_integer_commutativeTC.Imulletidiv=ar2_integer_integer_integerTC.Idivletimod=ar2_integer_integer_integerTC.Imodletishl=ar2_integer_integer_integerTC.Ishlletishr=ar2_integer_integer_integerTC.Ishrletiand=ar2_integer_integer_integer_commutativeTC.Iandletior=ar2_integer_integer_integer_commutativeTC.Iorletixor=ar2_integer_integer_integer_commutativeTC.Ixorletisub=ar2_integer_integer_integerTC.Isubletiadd:integert->integert->integert=funab->match(a,b)with|Integer{term=T0{tag=TC.Iconstk}},xwhenZ.equalkZ.zero->x|x,Integer{term=T0{tag=TC.Iconstk}}whenZ.equalkZ.zero->x|_,_->iaddab;;letiadd:integert->integert->integert=funab->ifa==bthenbeginimul(iconst@@Z.of_int2)aendelseiaddabletimul:integert->integert->integert=funab->match(a,b)with|Integer{term=T0{tag=TC.Iconstk}},xwhenZ.equalkZ.zero->a|x,Integer{term=T0{tag=TC.Iconstk}}whenZ.equalkZ.zero->b|Integer{term=T0{tag=TC.Iconstk}},xwhenZ.equalkZ.one->x|x,Integer{term=T0{tag=TC.Iconstk}}whenZ.equalkZ.one->x|_,_->imulab;;letold_ishr=ishrletold_iand=iand;;letold_imod=imod;;(* Note: this creates a new Constraint, the constant "k1 + k2",
which may have not been evaluated yet by the domain.
On the other hand, this is necessary to guarantee equality
between the same values in a structure (when there are
various extracts patterns that lead to the same value).
The solution is to require that abstract domain can evaluate
constants by themselves. *)letrecishr:integert->integert->integert=funab->match(a,b)with|Integer{term=T2{tag=TC.Ishr;a;b=Integer{term=T0{tag=TC.Iconstk1}}}},Integer{term=T0{tag=TC.Iconstk2}}->(* Codex_log.feedback "in ishr %s" @@ Printexc.get_backtrace(); *)leta:integert=ainishra(iconst@@Z.addk1k2)|Integer{term=T2{tag=TC.Iand;a;b=Integer{term=T0{tag=TC.Iconstk1}}}},Integer{term=T0{tag=TC.Iconstk2}}->leta:integert=ainiand(ishrab)(iconst@@Z.shift_rightk1@@Z.to_intk2)(* Note: modulo is not the best way to do this. *)(* MAYBE: just add an extract_bits operation to integers *)(* | Integer{term=T2{tag=TC.Imod; a; b=Integer{term=T0{tag=TC.Iconst k1}}}},
* Integer{term=T0{tag=TC.Iconst k2}} when Z.(equal zero @@ k1 land (one lsl Z.to_int k2)) ->
* let a:integer t = a in
* imod (ishr a b) (iconst @@ Z.(asr) k1 @@ Z.to_int k2) *)|_->old_ishrabandimod:integert->integert->integert=funab->match(a,b)with(* (a mod b) mod c = a mod c (if b divides c). *)|Integer{term=T2{tag=TC.Imod;a;b=Integer{term=T0{tag=TC.Iconstk1}}}},Integer{term=T0{tag=TC.Iconstk2}}whenZ.equalZ.zero@@Z.remk1k2->leta:integert=ainimodab|_->old_imodabandiand:integert->integert->integert=funab->match(a,b)with|Integer{term=T2{tag=TC.Iand;a;b=Integer{term=T0{tag=TC.Iconstk1}}}},Integer{term=T0{tag=TC.Iconstk2}}->letk12=Z.(land)k1k2inleta:integert=ainianda(iconstk12)|_->old_iandab;;letar2_integer_integer_booleantagab=HashCons.boolean(T2{tag;a;b;level=max(levela)(levelb)})Condition.var;;letar2_integer_integer_boolean_commutativetagab=let(a,b)=ifget_ida<=get_idbthen(a,b)else(b,a)inar2_integer_integer_booleantagab;;letieq=ar2_integer_integer_boolean_commutativeTC.Ieqletile=ar2_integer_integer_booleanTC.Ile(* Happens a lot due to validity constraints. *)letile:integert->integert->booleant=funab->match(a,b)with|Integer{term=T0{tag=TC.Iconstk1}},Integer{term=T0{tag=TC.Iconstk2}}->ifZ.leqk1k2thenBoolean.true_elseBoolean.false_|_,_->ileab;;(* Happens when a condition is stored in a temporary variable. *)let_ieq:integert->integert->booleant=funleftright->match(left,right)with|(Integer{term=Tuple_get(i,Nondet{conda_bool;a;condb_bool;b})},Integer{term=T0{tag=TC.Iconstk}}|Integer{term=T0{tag=TC.Iconstk}},Integer{term=Tuple_get(i,Nondet{conda_bool;a;condb_bool;b})})->(matchImmutable_array.getai,Immutable_array.getbiwith|Any(Integer{term=T0{tag=TC.Iconstka}}),Any(Integer{term=T0{tag=TC.Iconstkb}})whenZ.equalkka&&(not@@Z.equalkkb)->conda_bool|Any(Integer{term=T0{tag=TC.Iconstka}}),Any(Integer{term=T0{tag=TC.Iconstkb}})whenZ.equalkkb&&(not@@Z.equalkka)->condb_bool|_,_->ieqleftright)|_,_->ieqleftright;;typeboolean=TC.booleanttypeinteger=TC.integertendmoduleBinary=structtypeboolean=TC.booleanttypebinary=TC.binarytletunknown~size~level=Fresh.binary~size(Unknownlevel)letempty=letemptyHash=Hashtbl.create10infun~size->matchHashtbl.findemptyHashsizewith|exceptionNot_found->letres=Fresh.binary~sizeEmptyinHashtbl.addemptyHashsizeres;res|res->resletbuninit=emptyletar1_boolean_binary~sizetaga=HashCons.binary~size@@T1{tag;a;level=(levela)};;letar1_binary_binary~sizetaga=HashCons.binary~size@@T1{tag;a;level=(levela)};;letar2_binary_binary_binary~sizetagab=HashCons.binary~size@@T2{tag;a;b;level=max(levela)(levelb)};;letar2_binary_binary_binary_commutativetagab=let(a,b)=ifget_ida<=get_idbthen(a,b)else(b,a)inar2_binary_binary_binarytagab;;letar2_binary_binary_booleantagab=HashCons.boolean(T2{tag;a;b;level=max(levela)(levelb)})Condition.var;;letar2_binary_binary_boolean_commutativetagab=let(a,b)=ifget_ida<=get_idbthen(a,b)else(b,a)inar2_binary_binary_booleantagab;;moduleTCBB=TC.Build.Binary(* TODO: size is both in the constructor and in the constraint;
it is probably useless to store it twice. *)letbeq~size=ar2_binary_binary_boolean_commutative(TCBB.beq~size)letbiule~size=ar2_binary_binary_boolean(TCBB.biule~size)letbisle~size=ar2_binary_binary_boolean(TCBB.bisle~size)letbiadd~size~flags=ar2_binary_binary_binary_commutative~size(TCBB.biadd~size~flags)letbisub~size~flags=ar2_binary_binary_binary~size(TCBB.bisub~size~flags)letbimul~size~flags=ar2_binary_binary_binary_commutative~size(TCBB.bimul~size~flags)letbxor~size=ar2_binary_binary_binary_commutative~size(TCBB.bxor~size)letbshift~size~offset~max_=assertfalseletbindex~size_=assertfalseletold_bimul=bimul(* Optimized version; necessary for binary analysis of ARM. *)letbimul(* : size:int -> binary -> binary -> binary *)=fun~size~flags(Binarybxasx)(Binarybyasy)->matchbx.term,by.termwith|(T0{tag=TC.Biconst(_,k)}),_whenZ.equalkZ.zero->x|_,(T0{tag=TC.Biconst(_,k)})whenZ.equalkZ.zero->y|(T0{tag=TC.Biconst(_,k)}),_whenZ.equalkZ.one->y|_,(T0{tag=TC.Biconst(_,k)})whenZ.equalkZ.one->x|_->old_bimul~size~flagsxy(* Optimized version; necessary for binary analysis of ARM. *)letbxor:size:In_bits.t->binary->binary->binary=fun~size(Binarybxasx)(Binarybyasy)->(* Codex_log.feedback "bxor %a %a" pretty x pretty y; *)matchby.termwith|(T2{tag=TC.Bxor_;a;b})whena==x->b(* | (T2 {tag = TC.Bxor _;a;b}) when b == x -> y *)(* | (T0 {tag = TC.False}), _ -> a *)(* | _, (T0 {tag = TC.True}) -> a *)(* | _, (T0 {tag = TC.False}) -> b *)(* | _, _ when a == b -> a *)|_->(* Codex_log.feedback "no success"; *)bxor~sizexy;;(* Note: this does not work, because conda_bool in nondet is a condition since the beginning
of the program, it cannot be extracted to be evaluated.
I have added the "binofbool" term for a proper fix. *)let_beq:size:In_bits.t->binary->binary->boolean=fun~sizeleftright->match(left,right)with|(Binary{term=Tuple_get(i,Nondet{conda_bool;a;condb_bool;b})},Binary{term=T0{tag=TC.Biconst(_,k)}}|Binary{term=T0{tag=TC.Biconst(_,k)}},Binary{term=Tuple_get(i,Nondet{conda_bool;a;condb_bool;b})})->(matchImmutable_array.getai,Immutable_array.getbiwith|Any(Binary{term=T0{tag=TC.Biconst(_,ka)}}),Any(Binary{term=T0{tag=TC.Biconst(_,kb)}})whenZ.equalkka&&(not@@Z.equalkkb)->conda_bool|Any(Binary{term=T0{tag=TC.Biconst(_,ka)}}),Any(Binary{term=T0{tag=TC.Biconst(_,kb)}})whenZ.equalkkb&&(not@@Z.equalkka)->condb_bool|_,_->beq~sizeleftright)|_,_->beq~sizeleftright;;(* This optimization is very important for kcg, which creates a
lot of boolean variables. XXX: en fait, non. Le problème
vient du fait qu'on passe le temps dans cudd à lui faire
calculer des &&. Je pense qu'avec les assume, on n'avait pas
le problème (on appendait des conditions à la fin comme à
chaque fois), mais qu'il se produit là.
Peut-être que je n'ai juste pas besoin de faire correspondre
tous mes termes booléens à des trucs cudd, et de ne le faire
que dans le domaine? Ca pourrait marcher, et ca serait aussi
utile pour constraint_dominator.
=> Rendre optionelle la presence de bdds dans mes contraintes booléennes.
=> Un domaine devrait être représenté par deux choses: une contrainte, et une bdd
Oui, je pense que ca marcherait?
XXX: si l'autre est efficace, c'est parce que je passe toute la condition dans le "domaine"
du terme. Si d est la condition courante, on remplace "c ({0,1} -> d)" par
nondet ... "0 -> !c && d; 1 -> c && d".
Du coup si plus tard, je suis avec une condition courante e (qui inclus souvent d, donc e == d && f), et que je fais un assume de la condition calculée.
dans le premier cas je calcule c && d && e
dans le deuxième cas je calcule 1 && (c && d) && e
on doit donc aller plus vite, parce que d && c est déjà calculé.
Pas tout a fait clair
=>
*)letbeq:size:In_bits.t->binary->binary->boolean=fun~sizeleftright->letdoitka=ifZ.equalkZ.onethenaelseifZ.equalkZ.zerothen(* XXX: Cree un nouveau terme: c'est pas bien *)Boolean.notaelseassertfalseinmatch(left,right)with|(Binary{term=T1{tag=TC.Bofbool(_);a}},Binary{term=T0{tag=TC.Biconst(_,k)}})->doitka|(Binary{term=T0{tag=TC.Biconst(_,k)}},Binary{term=T1{tag=TC.Bofbool(_);a}})->doitka(* | Binary{term=T0{tag=TC.Biconst(_,k)}},
* Binary{term=Tuple_get(i,Nondet{conda_bool;a;condb_bool;b})}) ->
* (match Immutable_array.get a i, Immutable_array.get b i with
* | Any(Binary{term=T0{tag=TC.Biconst(_,ka)}}), Any(Binary{term=T0{tag=TC.Biconst(_,kb)}})
* when Z.equal k ka && (not @@ Z.equal k kb) -> conda_bool
* | Any(Binary{term=T0{tag=TC.Biconst(_,ka)}}), Any(Binary{term=T0{tag=TC.Biconst(_,kb)}})
* when Z.equal k kb && (not @@ Z.equal k ka) -> condb_bool
* | _,_ -> beq ~size left right
* ) *)|_,_->beq~sizeleftright;;letbextract~size~index~oldsize=ar1_binary_binary~size(TC.Bextract{size;index;oldsize});;letbextract~size~index~oldsize((Binaryx)asbx)=matchx.termwith|T1{tag=TC.Bextract{size=size';index=index';oldsize=oldsize'};a}->assert(oldsize==size');bextract~size~index:In_bits.(index'+index)~oldsize:oldsize'a|_->bextract~size~index~oldsizebx(* let beq_ ~size left right = *)(* let res = beq ~size left right in *)(* Codex_log.feedback "beq %a %a res %a" pretty left pretty right pretty res; *)(* res *)(* ;; *)letband~size=ar2_binary_binary_binary_commutative~size(TCBB.band~size)letbor~size=ar2_binary_binary_binary_commutative~size(TCBB.bor~size)letbsext~size~oldsize=ar1_binary_binary~size(TCBB.bsext~size~oldsize)letbuext~size~oldsize=ar1_binary_binary~size(TCBB.buext~size~oldsize)letbofbool~size=ar1_boolean_binary~size(TCBB.bofbool~size)letbchoose~sizecond=ar1_binary_binary~size(TCBB.bchoose~sizecond)letbshl~size~flags=ar2_binary_binary_binary~size(TCBB.bshl~size~flags)letblshr~size=ar2_binary_binary_binary~size(TCBB.blshr~size)letbashr~size=ar2_binary_binary_binary~size(TCBB.bashr~size)letbisdiv~size=ar2_binary_binary_binary~size(TCBB.bisdiv~size)letbiudiv~size=ar2_binary_binary_binary~size(TCBB.biudiv~size)letbismod~size=ar2_binary_binary_binary~size(TCBB.bismod~size)letbiumod~size=ar2_binary_binary_binary~size(TCBB.biumod~size)letbconcat~size1~size2=ar2_binary_binary_binary~size:In_bits.(size1+size2)(TCBB.bconcat~size1~size2);;letvalid~size_=assertfalseletvalid_ptr_arith~size_=assertfalseletbiconst~sizek=HashCons.binary~size@@T0{tag=TC.Biconst(size,k)};;(* We do not want union to be considered a constant, so the min level is 0 for it.
Because of this, we inline ar2_binary_binary_binary and ar2_binary_binary_binary_commutative.
*)letbunion~sizecondab=(* ar2_binary_binary_binary_commutative ~size (TCBB.bunion ~size cond) *)lettag=(TCBB.bunion~sizecond)inlet(a,b)=ifget_ida<=get_idbthen(a,b)else(b,a)inHashCons.binary~size@@T2{tag;a;b;level=max0@@max(levela)(levelb)};;endmoduleEnum=structtypeboolean=TC.booleanttypeenum=TC.enumtletunknown~level=Fresh.enum(Unknownlevel)letempty=Fresh.enumEmptyletar1_enum_booleantaga=HashCons.boolean(T1{tag;a;level=(levela)})Condition.varletcaseof~case=ar1_enum_boolean(TC.CaseOf(case));;letenum_const~case=HashCons.enum@@T0{tag=TC.EnumConstcase};;endmoduleMu_Formal=structletintro:typea.level:int->actual:at->actual_cond:TC.booleant->aTC.typ->at=fun~level~actual~actual_condtyp->(* Codex_log.feedback "Calling intro"; *)matchtypwith|TC.Integer->Fresh.integer(Mu_formal{level;actual=(actual,actual_cond)})|TC.Boolean->Fresh.boolean(Mu_formal{level;actual=(actual,actual_cond)})@@Condition.var()|TC.Binarysize->Fresh.binary~size(Mu_formal{level;actual=(actual,actual_cond)})|TC.Memory->assertfalse|TC.Enum->Fresh.enum(Mu_formal{level;actual=(actual,actual_cond)});;endmoduleTuple=struct(* The model is just to get the type. *)lettuple_from_tuple_modeltuple_=model|>Immutable_array.mapi(funia->letAnya=ainletf:typea.at->any=function|(Binary{size})->Any(Fresh.binary~size(Tuple_get(i,tuple_)))|(Integer_)->Any(Fresh.integer(Tuple_get(i,tuple_)))|(Bool_)->Any(Fresh.boolean(Tuple_get(i,tuple_))@@Condition.var())|(Enum_)->Any(Fresh.enum(Tuple_get(i,tuple_)))infa);;letlevel_tuplex=Immutable_array.fold_left(funa(Anyb)->maxa@@levelb)(-1)x;;(* Print a nondet constraint by separating each component. *)letpretty_nondet_tuplefmt((tuple:anyImmutable_array.t),conda_bool,a,condb_bool,b)=letlen=Immutable_array.lengthtupleinfori=0tolen-1doFormat.fprintffmt"let %a = phi_nondet(%a when %a,%a when %a)@\n"pretty_any(Immutable_array.gettuplei)pretty_any(Immutable_array.getai)prettyconda_boolpretty_any(Immutable_array.getbi)prettycondb_booldone;;(* Note: if two constraints are the same (especially when all
true), simplify. Else this creates huge BDDs. But this
should have been already done by the caller, so we do
nothing here any more. *)letnondet~level~conda_bool~a:tup1~condb_bool~b:tup2=(* Codex_log.feedback "nondet level %d %d %d@\n tup1 %a@\n tup2 %a" level (level_tuple tup1) (level_tuple tup2) pretty_tuple tup1 pretty_tuple tup2; *)assert(level>=(level_tupletup1)&&level>=(level_tupletup2));lettuple_=Nondet({id=Id.fresh();conda_bool;a=tup1;condb_bool;b=tup2;level})in(* Log.info (fun p -> p "tutu %a" Pretty.Pretty.pretty_tuple_ tuple_); *)letorig=tuple_from_tuple_tup1tuple_inLog.info(funp->p"%a"pretty_nondet_tuple(orig,conda_bool,tup1,condb_bool,tup2));orig;;letinductive_vars_individual~widening_id~level~def:left=(* We fill the definition arbitrarily with left so make the typer happy. *)Immutable_array.map(funleft->letAnyleft=leftinletf:typea.at->any=funleft->matchleftwith|(Binary{size})->Any(Fresh.binary~size(Inductive_var{widening_id;level;definition=left}))|(Integer_)->Any(Fresh.integer(Inductive_var{widening_id;level;definition=left}))|(Bool_)->Any(Fresh.boolean(Inductive_var{widening_id;level;definition=left})@@Condition.var())|(Enum_)->Any(Fresh.enum(Inductive_var{widening_id;level;definition=left}))infleft)leftletinductive_vars_grouped~widening_id~level~def=lettuple_:cfg_node=Inductive_vars({id=Id.fresh();widening_id;level;def})inletorig=tuple_from_tuple_deftuple_inorigletinductive_vars=ifCodex_config.term_group_inductive_variable_by_tupletheninductive_vars_groupedelseinductive_vars_individual(* Print a nondet constraint by separating each component. *)letpretty_mu_tuplefmt((tuple:anyImmutable_array.t),init,var,body,body_cond)=letlen=Immutable_array.lengthtupleinfori=0tolen-1doFormat.fprintffmt"let %a = phi_mu(init:%a,var:%a,body:%a with %a)@\n"pretty_any(Immutable_array.gettuplei)pretty_any(Immutable_array.getiniti)pretty_any(Immutable_array.getvari)pretty_any(Immutable_array.getbodyi)prettybody_conddone;;letmu~level~init~var~body~body_cond=(* Codex_log.feedback "%d %d %d %a %a" level (level_tuple init) (level_tuple body)
* Pretty.Pretty.pretty_tuple init Pretty.Pretty.pretty_tuple body; *)lettuple_=Mu({id=Id.fresh();init;var;body;level;body_cond})inletorig=tuple_from_tuple_inittuple_inLog.info(funp->p"%a"pretty_mu_tuple(orig,init,var,body,body_cond));if(Immutable_array.lengthinit!=0)thenbeginassert(level>=(level_tupleinit));assert((level_tuplebody)<=level+1)end;orig(* Optimize the creation of terms in the case where nothing changes. Yields
a small improvement in some benchmarks. *)letnondet~level~conda_bool~a:tup1~condb_bool~b:tup2=ifImmutable_array.lengthtup1==0thentup1elsenondet~level~conda_bool~a:tup1~condb_bool~b:tup2letmu~level~init~var~body~body_cond=ifImmutable_array.lengthinit==0theninitelsemu~level~init~var~body~body_cond;;(* Downcast with dynamic check. *)letget_boolean:int->tuple->booleant=funidxtup->letAnyx=Immutable_array.gettupidxinmatchxwith|Bool_->x|_->assertfalseletget_integer:int->tuple->integert=funidxtup->letAnyx=Immutable_array.gettupidxinmatchxwith|Integer_->x|_->assertfalseletget_binary:size:In_bits.t->int->tuple->binaryt=fun~sizeidxtup->letAnyx=Immutable_array.gettupidxinmatchxwith|Binary_->x|_->assertfalseletget_enum:int->tuple->enumt=funidxtup->letAnyx=Immutable_array.gettupidxinmatchxwith|Enum_->x|_->assertfalseendendmoduleUtils=structletget_term=get_termendletget_parent(typea):at->aparent=function|Bool{parent;_}->parent|Integer{parent;_}->parent|Binary{parent;_}->parent|Enum{parent;_}->parentmoduleUnionFind=Union_Find.Imperative.GenericRelational(structincludeTletpolyeq=polyeqletget_parent=get_parentletset_parent(typea)(child:at)(parent:aparent)=SUPER_TERMS.add_one_superterm~child~parent;matchchildwith|Boolx->x.parent<-parent;|Integerx->x.parent<-parent|Binaryx->x.parent<-parent|Enumx->x.parent<-parentmoduleRelation=Relationend)end