123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476(**************************************************************************)(* 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). *)(* *)(**************************************************************************)openOperator.Function_symbolmoduleIn_bits=Units.In_bitsmoduletypeGROUP_ACTION=sigtypebitvectortypeintegertypebooleantypeenumtype('a,'b)relationtype(_,_)mapping=|BitvectorMapping:(bitvector,Operator.Function_symbol.bitvector)mapping|IntegerMapping:(integer,Operator.Function_symbol.integer)mapping|BooleanMapping:(boolean,Operator.Function_symbol.boolean)mapping|EnumMapping:(enum,Operator.Function_symbol.enum)mappingtype'termwrapper=Wrap:('value*('value,'term)mapping)->'termwrappervalapply_relation:'value_parent->('term_child,'term_parent)relation->('value_parent,'term_parent)mapping->'term_childwrappervalrefine_relation:'value_parent->'value_child->('term_child,'term_parent)relation->('value_parent,'term_parent)mapping->('value_child,'term_child)mapping->'value_parentoptionendmoduleEquality=structtype('a,'b)t=Equal:('a,'a)tletcompose(typeabc)(Equal:(b,c)t)(Equal:(a,b)t):(a,c)t=Equalletinverse(typeab)(Equal:(a,b)t):(b,a)t=Equalletidentity=Equalletequal(typeab)(Equal:(a,b)t)(Equal:(a,b)t)=trueletpretty(typeab)fmt(Equal:(a,b)t)=Format.fprintffmt"Equal"letpretty_with_termspp_xxpp_yyfmt_=Format.fprintffmt"@[%a = %a@]"pp_xxpp_yymoduleAction(B:sigtypebitvectortypeintegertypebooleantypeenumend)=structincludeBtype('a,'b)relation=('a,'b)ttype(_,_)mapping=|BitvectorMapping:(bitvector,Operator.Function_symbol.bitvector)mapping|IntegerMapping:(integer,Operator.Function_symbol.integer)mapping|BooleanMapping:(boolean,Operator.Function_symbol.boolean)mapping|EnumMapping:(enum,Operator.Function_symbol.enum)mappingtype'termwrapper=Wrap:('value*('value,'term)mapping)->'termwrapperletapply_relation(typevalue_parentterm_parentterm_child)(value_parent:value_parent)(Equal:(term_child,term_parent)relation)(map:(value_parent,term_parent)mapping):term_childwrapper=matchmapwith|BitvectorMapping->Wrap(value_parent,map)|IntegerMapping->Wrap(value_parent,map)|BooleanMapping->Wrap(value_parent,map)|EnumMapping->Wrap(value_parent,map)letrefine_relation(typevalue_parentvalue_childterm_parentterm_child)(value_parent:value_parent)(value_child:value_child)(Equal:(term_child,term_parent)relation)(mp:(value_parent,term_parent)mapping)(mc:(value_child,term_child)mapping):value_parentoption=matchmp,mcwith|BitvectorMapping,BitvectorMapping->Somevalue_child|IntegerMapping,IntegerMapping->Somevalue_child|BooleanMapping,BooleanMapping->Somevalue_child|EnumMapping,EnumMapping->Somevalue_childendendmoduleAdditive=structtypedelta=PlusOne|MinusOnetype(_,_)t=|Identity:('a,'a)t|Add_Modulo:{factor:delta;size:In_bits.t;amount:Z.t}->(bitvector,bitvector)t|Add_Unbounded:delta*Z.t->(integer,integer)t|Bool_Not:(boolean,boolean)tletadditive_identity=Identityletadditive_bitvector~(size:In_bits.t)factorx=letx'=Z.signed_extractx0(size:>int)inifZ.equalx'Z.zero&&factor=PlusOnethenIdentityelseAdd_Modulo{factor;size;amount=x}letadditive_integerfactorx=ifZ.equalxZ.zero&&factor=PlusOnethenIdentityelseAdd_Unbounded(factor,x)letboolean_not=Bool_Notletpretty_signfmtfactor=iffactor=MinusOnethenFormat.fprintffmt"-"letidentity=additive_identityletflip=function|PlusOne->MinusOne|MinusOne->PlusOneletsign_composexy=matchxwith|PlusOne->y|MinusOne->flipy(** Only print "+" if number is positive *)letpretty_int_signfmtl=ifZ.geqlZ.zerothenFormat.fprintffmt"+"letpretty(typeab)fmt(x:(a,b)t)=matchxwith|Identity->Format.fprintffmt"id"|Add_Unbounded(z,l)->Format.fprintffmt"x->%ax%a%a"pretty_signzpretty_int_signlFramac_ival.Abstract_interp.Int.prettyl|Add_Modulo{factor;size;amount}->Format.fprintffmt"x->%ax%a%a[%d]"pretty_signfactorpretty_int_signamountFramac_ival.Abstract_interp.Int.prettyamount(size:>int)|Bool_Not->Format.fprintffmt"neg"letpretty_with_termspp_xxpp_yyfmt(typeab)(rel:(a,b)t)=matchrelwith|Identity->Format.fprintffmt"[%a = %a]"pp_xxpp_yy|Add_Unbounded(z,l)->Format.fprintffmt"[%a = %a %s %a]"pp_xxpp_yy(matchzwithPlusOne->"+"|MinusOne->"-")Z.pp_printl|Add_Modulo{factor;amount;size}->Format.fprintffmt"%a->%a%a%a%a[%d]"pp_xxpretty_signfactorpp_yypretty_int_signamountFramac_ival.Abstract_interp.Int.prettyamount(size:>int)|Bool_Not->Format.fprintffmt"[%a = bnot %a]"pp_xxpp_yy(* val --(y)--> val' --(x)-->
where y: delta * val + b
and x: delta'* val'+b'
is delta'*delta*val + b' + delta'*b *)letcompose(typeabc)(x:(b,c)t)(y:(a,b)t):(a,c)t=matchx,ywith|Identity,c->c|c,Identity->c|Add_Unbounded(zl,off_l),Add_Unbounded(zr,off_r)->additive_integer(sign_composezlzr)(ifzr=PlusOnethenZ.(off_l+off_r)elseZ.(off_r-off_l))|Add_Modulo{factor=zl;size=sl;amount=off_l},Add_Modulo{factor=zr;size=sr;amount=off_r}->assert(sl=sr);additive_bitvector~size:sl(sign_composezlzr)(ifzr=PlusOnethenZ.(off_l+off_r)elseZ.(off_r-off_l))|Bool_Not,Bool_Not->Identity(* For y = delta*x + b
Then x = delta*y - delta*b *)letinverse(typeab)(x:(a,b)t):(b,a)t=matchxwith|Identity->x|Bool_Not->x|Add_Unbounded(z,l)->Add_Unbounded(z,ifz=PlusOnethenZ.neglelsel)|Add_Modulo{factor;size;amount}->additive_bitvector~sizefactor(iffactor=PlusOnethenZ.negamountelseamount)letequalxy=x=ymoduleAction(B:Single_value_abstraction.Sig.NUMERIC_ENUM)=structtypebitvector=B.bitvectortypeinteger=B.integertypeboolean=B.booleantypeenum=B.enumtype('a,'b)relation=('a,'b)ttype(_,_)mapping=|BitvectorMapping:(bitvector,Operator.Function_symbol.bitvector)mapping|IntegerMapping:(integer,Operator.Function_symbol.integer)mapping|BooleanMapping:(boolean,Operator.Function_symbol.boolean)mapping|EnumMapping:(enum,Operator.Function_symbol.enum)mappingtype'termwrapper=Wrap:('value*('value,'term)mapping)->'termwrapperletapply_relation(typevalue_parentterm_parentterm_child)(value_parent:value_parent)(rel:(term_child,term_parent)relation)(map:(value_parent,term_parent)mapping):term_childwrapper=matchrel,mapwith|Identity,BitvectorMapping->Wrap(value_parent,map)|Identity,IntegerMapping->Wrap(value_parent,map)|Identity,BooleanMapping->Wrap(value_parent,map)|Identity,EnumMapping->Wrap(value_parent,map)|Bool_Not,BooleanMapping->Wrap(B.Boolean_Forward.notvalue_parent,map)|Add_Unbounded(is_pos,z),IntegerMapping->letconst=B.Integer_Forward.iconstzinWrap((ifis_pos=PlusOnethenB.Integer_Forward.iaddconstvalue_parentelseB.Integer_Forward.isubconstvalue_parent),map)|Add_Modulox,BitvectorMapping->letconst=B.Bitvector_Forward.biconst~size:x.sizex.amountinWrap((ifx.factor=PlusOnethenB.Bitvector_Forward.biaddelseB.Bitvector_Forward.bisub)~flags:(Operator.Flags.Biadd.pack~nsw:false~nuw:false~nusw:false)~size:x.sizeconstvalue_parent,map)letrefine_relation(typevalue_parentvalue_childterm_parentterm_child)(value_parent:value_parent)(value_child:value_child)(rel:(term_child,term_parent)relation)(mp:(value_parent,term_parent)mapping)(mc:(value_child,term_child)mapping):value_parentoption=matchrel,mp,mcwith|Identity,BitvectorMapping,BitvectorMapping->Somevalue_child|Identity,IntegerMapping,IntegerMapping->Somevalue_child|Identity,BooleanMapping,BooleanMapping->Somevalue_child|Identity,EnumMapping,EnumMapping->Somevalue_child|Bool_Not,BooleanMapping,BooleanMapping->B.Boolean_Backward.notvalue_parentvalue_child|Add_Unbounded(is_pos,z),IntegerMapping,IntegerMapping->letconst=B.Integer_Forward.iconstzinlet(_,new_value)=ifis_pos=PlusOnethenB.Integer_Backward.iaddconstvalue_parentvalue_childelseB.Integer_Backward.isubconstvalue_parentvalue_childinnew_value|Add_Modulox,BitvectorMapping,BitvectorMapping->letconst=B.Bitvector_Forward.biconst~size:x.sizex.amountinlet(_,new_value)=ifx.factor=PlusOnethenB.Bitvector_Backward.biadd~flags:(Operator.Flags.Biadd.pack~nsw:false~nuw:false~nusw:false)~size:x.sizeconstvalue_parentvalue_childelseB.Bitvector_Backward.bisub~flags:(Operator.Flags.Bisub.pack~nsw:false~nuw:false~nusw:false)~size:x.sizeconstvalue_parentvalue_childinnew_valueend(* let inverse x =
let y = inverse x in
Format.printf "Inverse %a = %a@."
pretty x pretty y;
y *)endmoduleXOR_Rotate=structtype(_,_)t=|XR_Identity:('a,'a)t|XR_BNot:(boolean,boolean)t|XR_XOR_rotate:{rotate:int;xor:Z.t;size:In_bits.t}->(bitvector,bitvector)tletxr_identity=XR_Identityletxr_bnot=XR_BNotletxr_xor_rotate~rotate~xor~(size:In_bits.t)=letxor=Z.extractxor0(size:>int)inletrotate=rotatemod(size:>int)inletrotate=ifrotate<0thenrotate+(size:>int)elserotateinifInt.equalrotate0&&Z.(equalxorzero)thenxr_identityelseXR_XOR_rotate{rotate;xor;size}letpretty(typeab)fmt:(a,b)t->unit=function|XR_Identity->Format.fprintffmt"XR_Identity"|XR_BNot->Format.fprintffmt"XR_BNot"|XR_XOR_rotatea->Format.fprintffmt"XR_XOR_Rotate@[{rot=%d; xor=%a; %d}@]"a.rotateFramac_ival.Abstract_interp.Int.prettya.xor(a.size:>int)letpretty_with_termspp_xxpp_yyfmt(typeab)(rel:(a,b)t)=matchrelwith|XR_Identity->Format.fprintffmt"[%a = %a]"pp_xxpp_yy|XR_BNot->Format.fprintffmt"[%a = bnot %a]"pp_xxpp_yy|XR_XOR_rotatea->Format.fprintffmt"@[%a = (%a xor %a) rot %d [%d]@]"pp_xxpp_yyFramac_ival.Abstract_interp.Int.prettya.xora.rotate(a.size:>int)letrotate~(size:In_bits.t)shiftvalue=ifshift==0thenvalueelselettop=Z.extractvalue((size:>int)-shift)(shift)inZ.logor(Z.shift_leftvalueshift)top(**{v
y = (x << s) lxor c
y lxor c = x << s
(y lxor c) << -s = x
(y << -s) lxor (c << -s) = x
v}*)letinverse(typeab):(a,b)t->(b,a)t=function|XR_Identity->XR_Identity|XR_BNot->XR_BNot|XR_XOR_rotatex->letrot=ifx.rotate=0then0else(x.size:>int)-x.rotateinxr_xor_rotate~size:x.size~rotate:rot~xor:(rotate~size:x.sizerotx.xor)(** {v
y = (x << s) lxor c AND z = (y << s') lxor c'
so z = ((x << s lxor c) << s' lxor c')
so z = x << (s + s') lxor ((c << s') lxor c')
v}*)letcompose(typeabc)(x:(b,c)t)(y:(a,b)t):(a,c)t=matchx,ywith|XR_Identity,c->c|c,XR_Identity->c|XR_BNot,XR_BNot->XR_Identity|XR_XOR_rotatex,XR_XOR_rotatey->assert(x.size=y.size);letrot=x.rotate+y.rotateinletrot=ifrot>(x.size:>int)thenrot-(x.size:>int)elserotinxr_xor_rotate~size:x.size~rotate:rot~xor:(Z.logxor(rotate~size:x.sizex.rotatey.xor)x.xor)letequalxy=x=yletidentity=XR_IdentityendmoduleLinearTwoVarEquality=structtype(_,_)t=|Identity:('a,'a)t|Linear_Equality:{size:In_bits.t;f1:Z.t;f2:Z.t;offset:Z.t}->(bitvector,bitvector)tletidentity=Identityletmake~size:sz~f1~f2offset=letopenZinassert(not(equalf1zero||equalf2zero));ifequaloffsetzero&&equalf1Z.(-f2)thenidentityelseletgcd=gcdf1(gcdf2offset)inletgcd=ifZ.leqf1Z.zerothen-gcdelsegcdin(* ensure invariant f1 > 0 *)ifequalgcdonethenLinear_Equality{size=sz;f1;f2;offset}else(* ensure factors are minimal *)Linear_Equality{size=sz;f1=f1/gcd;f2=f2/gcd;offset=offset/gcd}letf1:typeab.(a,b)t->Z.t=function|Identity->Z.one|Linear_Equality{f1;_}->f1letf2:typeab.(a,b)t->Z.t=function|Identity->Z.minus_one|Linear_Equality{f2;_}->f2letoffset:typeab.(a,b)t->Z.t=function|Identity->Z.zero|Linear_Equality{offset;_}->offsetletinverse(typeab)(x:(a,b)t):(b,a)t=matchxwith|Identity->Identity|Linear_Equalityr->letopenZinifltr.f2zerothenLinear_Equality{rwithf1=-r.f2;f2=-r.f1;offset=-r.offset}elseLinear_Equality{rwithf1=r.f2;f2=r.f1}(** Pretty printer for z factors:
{[
| 1 -> positive
| -1 -> "-"
| x when x >= 0 -> positive ^ x
| x -> x (* automatically preceded by a "-" *)
]} *)letpretty_factorpositivefmtz=ifZ.equalzZ.onethenFormat.fprintffmt"%s"positiveelseifZ.equalzZ.minus_onethenFormat.fprintffmt"-"elseifZ.geqzZ.zerothenFormat.fprintffmt"%s%a"positiveZ.pp_printzelseFormat.fprintffmt"%a"Z.pp_printzletpretty(typeab)fmt(x:(a,b)t)=matchxwith|Identity->Format.fprintffmt"id"|Linear_Equality{f1;f2;size;offset}->Format.fprintffmt"%ax%ay=%a[%d]"(pretty_factor"")f1(pretty_factor"+")f2Z.pp_printoffset(size:>int)letpretty_with_termspp_xxpp_yyfmt(typeab)(rel:(a,b)t)=matchrelwith|Identity->Format.fprintffmt"[%a = %a]"pp_xxpp_yy|Linear_Equality{f1;f2;size;offset}->Format.fprintffmt"@[%a%a %a %a = %a[%d]@]"(pretty_factor"")f1pp_xx(pretty_factor"+")f2pp_yyZ.pp_printoffset(size:>int)letequalxy=x=y(** Greatest common multiple *)letgcmab=Z.((a*b)/gcdab)letcompose(typeabc)(x:(b,c)t)(y:(a,b)t):(a,c)t=matchx,ywith|Identity,c->c|c,Identity->c|Linear_Equalityy,Linear_Equalityx->(* x.f1 A + x.f2 B = x.offset /\ y.f1 B + y.f2 C = y.offset
implies
mul_x x.f1 A + GCM B = mul_x x.offset (where mul_x = gcm x.f2 y.f1 / x.f2)
GCM B + mul_y y.f2 C = mul_y y.offset (where mul_y = gcm x.f2 y.f1 / x.f2)
Subbing both:
mul_x x.f1 A - mul_y y.f2 C = mul_x x.offset - mul_y y.offset *)assert(x.size=y.size);letgcm=gcmx.f2y.f1inletopenZinletmul_x=gcm/x.f2inletmul_y=-gcm/y.f1inmake~size:x.size~f1:(x.f1*mul_x)~f2:(y.f2*mul_y)(x.offset*mul_x+y.offset*mul_y)moduleAction(B:Single_value_abstraction.Sig.NUMERIC_ENUM)=structtypebitvector=B.bitvectortypeinteger=B.integertypeboolean=B.booleantypeenum=B.enumtype('a,'b)relation=('a,'b)ttype(_,_)mapping=|BitvectorMapping:(bitvector,Operator.Function_symbol.bitvector)mapping|IntegerMapping:(integer,Operator.Function_symbol.integer)mapping|BooleanMapping:(boolean,Operator.Function_symbol.boolean)mapping|EnumMapping:(enum,Operator.Function_symbol.enum)mappingtype'termwrapper=Wrap:('value*('value,'term)mapping)->'termwrapperletapply_relation(typevalue_parentterm_parentterm_child)(value:value_parent)(rel:(term_child,term_parent)relation)(map:(value_parent,term_parent)mapping):term_childwrapper=matchrel,mapwith|Identity,BitvectorMapping->Wrap(value,map)|Identity,IntegerMapping->Wrap(value,map)|Identity,BooleanMapping->Wrap(value,map)|Identity,EnumMapping->Wrap(value,map)|Linear_Equalityx,BitvectorMapping->letvalue=B.Bitvector_Forward.bimul_add~size:x.size~prod:(Z.negx.f2)~offset:x.offsetvaluein(* If f1 is not one, some division is required... *)letvalue=ifZ.equalx.f1Z.onethenvalueelseB.Bitvector_Forward.bisdiv~size:x.sizevalue(B.Bitvector_Forward.biconst~size:x.sizex.f1)inWrap(value,BitvectorMapping)letrefine_relation(typevalue_parentvalue_childterm_parentterm_child)(value_parent:value_parent)(value_child:value_child)(rel:(term_child,term_parent)relation)(mp:(value_parent,term_parent)mapping)(mc:(value_child,term_child)mapping):value_parentoption=matchrel,mp,mcwith|Identity,BitvectorMapping,BitvectorMapping->Somevalue_child|Identity,IntegerMapping,IntegerMapping->Somevalue_child|Identity,BooleanMapping,BooleanMapping->Somevalue_child|Identity,EnumMapping,EnumMapping->Somevalue_child|Linear_Equalityx,BitvectorMapping,BitvectorMapping->(* Start by doing forward propagation through all layers (but the last),
just like in apply_relation *)letvalue_mid=B.Bitvector_Forward.bimul_add~size:x.size~prod:(Z.negx.f2)~offset:x.offsetvalue_parentin(* Now backpropagate up each layer in turn *)matchifZ.equalx.f1Z.onethenSomevalue_childelsefst@@B.Bitvector_Backward.bisdiv~size:x.sizevalue_mid(B.Bitvector_Forward.biconst~size:x.sizex.f1)value_childwith|None->None|Somevalue_mid'->letvalue_mid=B.Bitvector_Lattice.inter~size:x.sizevalue_midvalue_mid'inB.Bitvector_Backward.bimul_add~size:x.size~prod:(Z.negx.f2)~offset:x.offsetvalue_parentvalue_midendend