123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356(**************************************************************************)(* 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). *)(* *)(**************************************************************************)(* Note that we do not combine the information coming from both
domains; there is no generic way to do it. Combination should be
done in the caller of this functor.
XXX: This is because we are not really making a product of domains,
but merely calling two domains with the same input. Hmm, still
we should be able to intersect the queries; weird.
*)(* Make every operation twice. We need a pair of boolean identifiers,
a pair of binary identifiers, etc. Only truth_value benefits from
the interaction.
TODO: call eval + inject on all boolean operations, then on all operations. *)moduleMake(P1:Sig.BASE)(P2:Sig.BASE):Sig.BASEwithtypeContext.t=P1.Context.t*P2.Context.tandtypebinary=P1.binary*P2.binaryandtypeboolean=P1.boolean*P2.booleanandtypeQuery.Binary_Lattice.t=P1.Query.Binary_Lattice.t*P2.Query.Binary_Lattice.t=structletname()=(P1.name())^"*"^(P2.name())letunique_id()=Sig.Fresh_id.fresh@@name();;moduleTypes=structtypeboolean=P1.boolean*P2.booleantypebinary=P1.binary*P2.binarytypeenum=P1.enum*P2.enumendincludeTypes(**************** Context ****************)moduleContext=structtypet=P1.Context.t*P2.Context.tletcopy(x,y)=P1.Context.copyx,P2.Context.copyyletassign(ctx1,ctx2)(newctx1,newctx2)=P1.Context.assignctx1newctx1;P2.Context.assignctx2newctx2letlevel(x1,x2)=letres=P1.Context.levelx1inassert(res==P2.Context.levelx2);restype'ain_tuple=In:'aP1.Context.in_tuple*'bP2.Context.in_tuple->('a*'b)in_tupletype'ain_acc=bool*'ain_tupletype'bout_tuple=Out:'aP1.Context.out_tuple*'bP2.Context.out_tuple->('a*'b)out_tupletypeempty_tuple=(P1.Context.empty_tuple*P2.Context.empty_tuple)letempty_tuple()=In(P1.Context.empty_tuple(),P2.Context.empty_tuple())type('a,'b)result=Result:bool*'somein_tuple*(t->'someout_tuple->'a*'bout_tuple)->('a,'b)resultendopenContextletroot_context()=(P1.root_context(),P2.root_context())letcontext_prettyfmt(ctx1,ctx2)=Format.fprintffmt"{P1=%a|P2=%a}"P1.context_prettyctx1P2.context_prettyctx2letmu_context_open(ctx1,ctx2)=(P1.mu_context_openctx1,P2.mu_context_openctx2)letassume(ctx1,ctx2)(cond1,cond2)=matchP1.assumectx1cond1,P2.assumectx2cond2with|None,_|_,None->None|Somectx1,Somectx2->Some(ctx1,ctx2)(**************** Serialisation ****************)type'elthigher1={subf1:'tl.P1.Context.t->'elt->P1.Context.t->'elt->'tlP1.Context.in_acc->('elt,'tl)P1.Context.result}[@@unboxed]type'elthigher2={subf2:'tl.P2.Context.t->'elt->P2.Context.t->'elt->'tlP2.Context.in_acc->('elt,'tl)P2.Context.result}[@@unboxed]letserialize(typec){subf1}{subf2}(ctxa1,ctxa2)(a1,a2)(ctxb1,ctxb2)(b1,b2)(acc:cContext.in_acc):(_,c)Context.result=let(included,In(acc1,acc2))=accinletP1.Context.Result(inc1,acc1,d1)=subf1ctxa1a1ctxb1b1(included,acc1)inletP2.Context.Result(inc2,acc2,d2)=subf2ctxa2a2ctxb2b2(included,acc2)inResult(inc1&&inc2,In(acc1,acc2),fun(ctx1,ctx2)(Out(tup1,tup2))->letr1,tup1=d1ctx1tup1inletr2,tup2=d2ctx2tup2inlettup=Context.Out(tup1,tup2)in(r1,r2),tup);;letserialize_booleanctxaactxbbacc=serialize{subf1=P1.serialize_boolean}{subf2=P2.serialize_boolean}ctxaactxbbaccletserialize_binary~widens~sizectxaactxbbacc=serialize{subf1=functxaactxbbacc->P1.serialize_binary~widens~sizectxaactxbbacc}{subf2=functxaactxbbacc->P2.serialize_binary~widens~sizectxaactxbbacc}ctxaactxbbaccletserialize_enumctxaactxbbacc=serialize{subf1=P1.serialize_enum}{subf2=P2.serialize_enum}ctxaactxbbacclettyped_nondet2(typec)(ctxa1,ctxa2)(ctxb1,ctxb2)(In(tup1,tup2):cin_tuple):Context.t*cout_tuple=letctx1,res1=P1.typed_nondet2ctxa1ctxb1tup1inletctx2,res2=P2.typed_nondet2ctxa2ctxb2tup2in(ctx1,ctx2),Out(res1,res2);;letnondet_same_context(typec)(ctx1,ctx2)(In(tup1,tup2):cin_tuple):cout_tuple=letres1=P1.nondet_same_contextctx1tup1inletres2=P2.nondet_same_contextctx2tup2inOut(res1,res2);;lettyped_fixpoint_step(typec)~iteration~init:(init1,init2)~arg:(arg1,arg2)~body:(body1,body2)(included,In(tup1,tup2):cin_acc):bool*(close:bool->cout_tuple*Context.t)=letbool1,k1=P1.typed_fixpoint_step~iteration~init:init1~arg:arg1~body:body1(included,tup1)inletbool2,k2=P2.typed_fixpoint_step~iteration~init:init2~arg:arg2~body:body2(bool1,tup2)inbool2,(fun~close->lettup1,ctx1=k1~closeinlettup2,ctx2=k2~closeinOut(tup1,tup2),(ctx1,ctx2));;letwidened_fixpoint_step(typec)~widening_id~previous:(prev1,prev2)~next:(next1,next2)(included,In(tup1,tup2):cin_acc):Context.t*bool*cout_tuple=letctx1,bool1,out1=P1.widened_fixpoint_step~widening_id~previous:prev1~next:next1(included,tup1)inletctx2,bool2,out2=P2.widened_fixpoint_step~widening_id~previous:prev2~next:next2((included&&bool1),tup2)in(ctx1,ctx2),bool2,Out(out1,out2);;moduleBinary=Datatype_sig.Prod2(P1.Binary)(P2.Binary)moduleBoolean=Datatype_sig.Prod2(P1.Boolean)(P2.Boolean)moduleEnum=Datatype_sig.Prod2(P1.Enum)(P2.Enum)(**************** Transfer functions ****************)(* TODO: We could probably factor using arity. *)moduleBoolean_Forward=structletnot(ctx1,ctx2)(v1,v2)=(P1.Boolean_Forward.notctx1v1,P2.Boolean_Forward.notctx2v2)let(&&)(ctx1,ctx2)(a1,a2)(b1,b2)=(P1.Boolean_Forward.(&&)ctx1a1b1,P2.Boolean_Forward.(&&)ctx2a2b2);;let(||)(ctx1,ctx2)(a1,a2)(b1,b2)=(P1.Boolean_Forward.(||)ctx1a1b1,P2.Boolean_Forward.(||)ctx2a2b2);;lettrue_(ctx1,ctx2)=P1.Boolean_Forward.true_ctx1,P2.Boolean_Forward.true_ctx2;;letfalse_(ctx1,ctx2)=P1.Boolean_Forward.false_ctx1,P2.Boolean_Forward.false_ctx2;;endmoduleBinary_Forward=structmoduleBF1=P1.Binary_ForwardmoduleBF2=P2.Binary_Forwardletbuninit~size(ctx1,ctx2)=BF1.buninit~sizectx1,BF2.buninit~sizectx2;;letbiconst~sizek(ctx1,ctx2)=BF1.biconst~sizekctx1,BF2.biconst~sizekctx2;;letbop1~sizeop1op2(ctx1,ctx2)(v1,v2)=op1~sizectx1v1,op2~sizectx2v2;;letvalid~sizeacc_typ=bop1(BF1.validacc_typ)(BF2.validacc_typ)~sizeletbsext~size~oldsize=bop1(BF1.bsext~oldsize)(BF2.bsext~oldsize)~sizeletbuext~size~oldsize=bop1(BF1.buext~oldsize)(BF2.buext~oldsize)~sizeletbofbool~size=bop1(BF1.bofbool)(BF2.bofbool)~sizeletbchoose~sizecond=bop1(BF1.bchoosecond)(BF2.bchoosecond)~sizeletbextract~size~index~oldsize(ctx1,ctx2)(v1,v2)=BF1.bextract~size~index~oldsizectx1v1,BF2.bextract~size~index~oldsizectx2v2;;letbop2_no_sizeop1op2(ctx1,ctx2)(v1,v2)(w1,w2)=op1ctx1v1w1,op2ctx2v2w2letbpred=bop2_no_size(* Note: We cannot cross the results with inject_boolean in
general: if domains abstracts unrelated pieces of information,
the fact that one is true does not imply that the other
is. E.g. if one domain abstracts the set of memory regions to
which a pointer may points, and the other the possible offsets
of the pointers, we cannot deduce from a[0] != b[0] that 0 != 0.
So, crossing informations like this should take place in the
caller of this functor. *)letbisle~size=bpred(BF1.bisle~size)(BF2.bisle~size);;letbiule~size=bpred(BF1.biule~size)(BF2.biule~size);;letbeq~size=bpred(BF1.beq~size)(BF2.beq~size);;letbop2_flags~size~flagsbop1bop2=bop2_no_size(bop1~size~flags)(bop2~size~flags)letbop2_all_flags~size~nsw~nuw~nuswbop1bop2=bop2_no_size(bop1~size~nsw~nuw~nusw)(bop2~size~nsw~nuw~nusw)letbop2~sizebop1bop2=bop2_no_size(bop1~size)(bop2~size)letvalid_ptr_arith~sizearith_typ(ctx1,ctx2)(v1,v2)(w1,w2)=(BF1.valid_ptr_arith~sizearith_typctx1v1w1),(BF2.valid_ptr_arith~sizearith_typctx2v2w2)letbiadd=bop2_flagsBF1.biaddBF2.biaddletbisub=bop2_flagsBF1.bisubBF2.bisubletbimul=bop2_flagsBF1.bimulBF2.bimulletbshl=bop2_flagsBF1.bshlBF2.bshlletblshr=bop2BF1.blshrBF2.blshrletbashr=bop2BF1.bashrBF2.bashrletbiumod=bop2BF1.biumodBF2.biumodletbiudiv=bop2BF1.biudivBF2.biudivletbismod=bop2BF1.bismodBF2.bismodletbisdiv=bop2BF1.bisdivBF2.bisdivletbxor=bop2BF1.bxorBF2.bxorletbor=bop2BF1.borBF2.borletband=bop2BF1.bandBF2.bandletbconcat~size1~size2=bop2_no_size(BF1.bconcat~size1~size2)(BF2.bconcat~size1~size2)letbshift~size~offset~max_=assertfalseletbindex~size_=assertfalseendmoduleEnum_Forward=structletcaseof~case(ctx1,ctx2)(v1,v2)=P1.Enum_Forward.caseof~casectx1v1,P2.Enum_Forward.caseof~casectx2v2letenum_const~case(ctx1,ctx2)=P1.Enum_Forward.enum_const~casectx1,P2.Enum_Forward.enum_const~casectx2end(**************** Tuple ****************)(* include Operator.Builtin.Make(Types)(Context) *)(**************** Requests ****************)letboolean_pretty(ctx1,ctx2)fmt(mem1,mem2)=Format.fprintffmt"(%a,%a)"(P1.boolean_prettyctx1)mem1(P2.boolean_prettyctx2)mem2;;letboolean_is_empty(mem1,mem2)=assertfalseletbinary_pretty~size(ctx1,ctx2)fmt(mem1,mem2)=Format.fprintffmt"(%a,%a)"(P1.binary_pretty~sizectx1)mem1(P2.binary_pretty~sizectx2)mem2;;letbinary_is_empty~size(mem1,mem2)=assertfalseletenum_pretty(ctx1,ctx2)fmt(mem1,mem2)=Format.fprintffmt"(%a,%a)"(P1.enum_prettyctx1)mem1(P2.enum_prettyctx2)mem2;;moduleQuery=structmoduleBoolean_Lattice=structincludeLattices.Quadrivalentend(* module Binary = Lattices.Prod.Prod2_With_Inter_Bottom(P1.Query.Binary)(P2.Query.Binary) *)moduleBinary_Lattice=structmoduleA=P1.QuerymoduleB=P2.QueryincludeLattices.Unimplemented.Bitvector_Lattice(structtypenonrect=A.Binary_Lattice.t*B.Binary_Lattice.tletloc=__LOC__end)letequal(a1,b1)(a2,b2)=A.Binary_Lattice.equala1a2&&B.Binary_Lattice.equalb1b2letcompare(a1,b1)(a2,b2)=letra=A.Binary_Lattice.comparea1a2inifra!=0thenraelseB.Binary_Lattice.compareb1b2lethash(a,b)=Hashing.hash2(A.Binary_Lattice.hasha)(B.Binary_Lattice.hashb)letpretty~sizefmt(a,b)=Format.fprintffmt"(%a,%a)"(A.Binary_Lattice.pretty~size)a(B.Binary_Lattice.pretty~size)blettop~size=P1.Query.Binary_Lattice.top~size,P2.Query.Binary_Lattice.top~sizeletinter~size(a1,a2)(b1,b2)=P1.Query.Binary_Lattice.inter~sizea1b1,P2.Query.Binary_Lattice.inter~sizea2b2letis_bottom~size_=assertfalseletincludes~size_=assertfalseletjoin~size_=assertfalseletbottom~size=P1.Query.Binary_Lattice.bottom~size,P2.Query.Binary_Lattice.bottom~sizeletwiden~size~previous_=assertfalseletincludes_or_widen~size~previous_=assertfalseletsingleton~sizek=P1.Query.Binary_Lattice.singleton~sizek,P2.Query.Binary_Lattice.singleton~sizekletis_empty~size(x1,x2)=A.Binary_Lattice.is_empty~sizex1||B.Binary_Lattice.is_empty~sizex2end(* To be filled by the caller of this functor. *)letconvert_to_quadrivalent_=assertfalseletbinary_to_ival~signed~size_=assertfalseletbinary_fold_crop~sizebin~inf~supfacc=assertfalseletbinary~size(ctx1,ctx2)(id1,id2)=(P1.Query.binary~sizectx1id1,P2.Query.binary~sizectx2id2)letbinary_is_singleton~size(a,b)=matchP1.Query.Binary_Lattice.is_singleton~sizeawith|None->P2.Query.Binary_Lattice.is_singleton~sizeb|x->x;;moduleEnum_Lattice=Lattices.Unimplemented.Enum_Lattice(structletloc=__LOC__end)letenum_=assertfalseletis_singleton_enum_=assertfalseletenum_to_values_=assertfalseendletsatisfiable(ctx1,ctx2)(b1,b2)=matchP1.satisfiablectx1b1with|(Smtbackend.Smtlib_sig.Unsat|Smtbackend.Smtlib_sig.Sat_)asr->r|_->P2.satisfiablectx2b2;;letbinary_empty~size(ctx1,ctx2)=P1.binary_emptyctx1~size,P2.binary_emptyctx2~sizeletboolean_empty(ctx1,ctx2)=P1.boolean_emptyctx1,P2.boolean_emptyctx2letenum_empty(ctx1,ctx2)=P1.enum_emptyctx1,P2.enum_emptyctx2letbinary_unknown~size(ctx1,ctx2)=P1.binary_unknownctx1~size,P2.binary_unknownctx2~sizeletboolean_unknown(ctx1,ctx2)=P1.boolean_unknownctx1,P2.boolean_unknownctx2letenum_unknown~enumsize(ctx1,ctx2)=P1.enum_unknown~enumsizectx1,P2.enum_unknown~enumsizectx2letunion_=assertfalseletbinary_unknown_typed~size:__=assertfalseletquery_boolean(ctx1,ctx2)(id1,id2)=letl1=(P1.query_booleanctx1id1)inletl2=(P2.query_booleanctx2id2)inLattices.Quadrivalent.interl1l2end