12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013(**************************************************************************)(* 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"]includeSva_quadrivalent;;moduleQuadrivalent=Lattices.Quadrivalent;;moduleCollecting=Lattices.BVSet;;moduleConcrete=Operator.Concrete.Bitvector_InterpmoduleIn_bits=Units.In_bitsmoduleZSet=Set.Make(Z)(* Several representations for known bits can be considered:
must0_must1: a pair (x0,x1) with x0 = 0 when the bit must be 0, 1 otherwise
x1 = 1 when the bit must be 1, 0 otherwise
thus: (0,0) = 0 (1,1) = 1 (0,1) = bottom (1,0) = top
value_known: a pair (v,k) with k = 1 if the bit is known, 0 otherwise
v = value of the bit if known.
value_unknown: a pair (v,k) with k = 1 if the bit is unknown, 0 otherwise
v = value of the bit if known.
Except for xor and addition, the first representation allows much
simpler transfer functions, and is thus the one that we use.
(Moreover, it is difficult to represent bottom with the second one)*)(* Conversion between the above representations. *)let_must0_must1_to_value_known(x0,x1)=letopenZin(x0,~!x0lorx1)(* or xor, if we know there is no bottom *);;let_value_known_to_must0_must1(xv,xk)=letopenZin(xvlor~!xk,xvlandxk);;letname ="Known_bits";;moduleMust0_Must1=structmoduleBinary_Lattice=Lattices.Known_BitsmoduleBL=Binary_Lattice(* Conversions; should go inside Lattice. *)letbinary_to_known_bits~sizex=xletbinary_to_ival~signed~size_=assertfalseletbinary_is_singleton~sizex=assertfalseletbinary_is_empty~size=assertfalseletbinary_fold_crop~size_=assertfalseletis_bottom(x0,x1)=letopenZinnot@@Z.equalZ.zero@@~!x0landx1;;letchop~sizex=Z.extractx0size;;letis_singleton(x0,x1)=Z.equalx0x1;;letis_power_of_twox=Z.equalZ.zero(Z.(land)x(Z.predx))(* alternative: Z.popcount x == 1 *)letinter0=Z.(land)letinter1=Z.(lor)letinter(x0,x1)(y0,y1)=(inter0x0y0,inter1x1y1)letjoin0=Z.(lor)letjoin1=Z.(land)letjoin(x0,x1)(y0,y1)=(join0x0y0,join1x1y1)(* If not bottom, the unknown bits are those that differ between x0 and x1 *)letunknown(x0,x1)=Z.(lxor)x0x1;;(* True if x and y are known to be empty, false otherwise. This
means that at one index, one of the bit is known to be different. *)letdisjoint(x0,x1)(y0,y1)=letopenZin(not@@Z.equalzero(~!x0landy1))||(not@@Z.equalzero(~!y0landx1));;(* x1 has 0 whenever the bit may be 0, x0 has 1 whenever it may be 1.
So x1 has more zeroes than x1. *)letmin_max_unsigned~size(x0,x1)=(x1,x0)lettestbit=Lattices.Integer.Known_Bits.testbit;;letmin_max_signed~size(x0,x1)=matchtestbit(x0,x1)(size-1)with|Zero->(x1,x0)|One->(Z.signed_extractx10size,Z.signed_extractx00size)(* If the number is negative, the more one the higher, like when it is positive. *)|Unknown->(* Flip the bit before extraction, to ensure that min is negative and max positive. *)letflip=(Z.shift_leftZ.one@@size-1)in(Z.signed_extract(Z.(lxor)x1flip)0size,Z.extract(Z.(lxor)x0flip)0size);;letconcretize(x0,x1)=letopenZinifis_bottom(x0,x1)then(* (\* Collecting.bottom *\) failwith (Format.asprintf "bottom for (%s,%s)" (Z.format "%b" x0) (Z.format "%b" x1)) *)ZSet.emptyelse(* let max = x0 lor x1 in *)letnumbits=Stdlib.max(Z.numbitsx0)(Z.numbitsx1)in(* Format.printf "numbits = %d@." numbits; *)letmax=(Z.(lsl)Z.onenumbits)inletrecloopbitacc=(* Format.printf "loop@."; *)ifZ.geqbitmaxthenaccelseif(Z.equalzero@@x0landbit)thenloop(bitlsl1)accelseif(Z.equalbit@@x1landbit)thenloop(bitlsl1)(ZSet.map((lor)bit)acc)elseloop(bitlsl1)@@ZSet.unionacc(ZSet.map((lor)bit)acc)inloopZ.one(ZSet.singletonZ.zero);;(* Best abstraction for this set. *)letabstract~sizeset=matchZSet.elementssetwith|[]->BL.bottom~size|a::b->List.fold_left(funaccx->joinacc(BL.singleton~sizex))(BL.singleton~sizea)b;;moduleBinary_Forward=structletbiconst=BL.singletonletbofbool~size=function|Quadrivalent.Bottom->Binary_Lattice.bottom~size|Quadrivalent.True->biconst~sizeZ.one|Quadrivalent.False->biconst~sizeZ.zero|Quadrivalent.Top->join(biconst~sizeZ.zero)(biconst~sizeZ.one)(* And: keep 0 when one is known, 1 when both are known. *)letband0=Z.(land)letband1=Z.(land)letband~size(x0,x1)(y0,y1)=(band0x0y0,band1x1y1)(* Or: keep 1 when one is known, 0 when both are known. *)letbor0=Z.(lor)letbor1=Z.(lor)letbor~size(x0,x1)(y0,y1)=(bor0x0y0,bor1x1y1)(* Not: known 1 bits are known as 0, known 0 bits are known as 1. *)(* We use the size to avoid keeping negative numbers. *)letbnot~size(x0,x1)=(chop~size(Z.(~!)x1),chop~size(Z.(~!)x0))letbconcat~size1~size2(x0,x1)(y0,y1)=Concrete.bconcat~size1~size2x0y0,Concrete.bconcat~size1~size2x1y1;;letbextract~size~index~oldsize(x0,x1)=Concrete.bextract~size~index~oldsizex0,Concrete.bextract~size~index~oldsizex1;;(* 6 operations. *)letbxor~size(x0,x1)(y0,y1)=letopenZinletxorx0y0=x0lxory0inletxorx1y1=x1lxory1inletunknown=x0lxorx1in(* xorx0y0 lor xorx1y1 works for all but the double unknown case.
So we add an "unkown" check by loring with (x0 xor x1, i.e. when xbit is unknown) *)(xorx0y0lorxorx1y1lorunknown,xorx0y0landxorx1y1);;(* Fast and relatively precise, without requiring a
large number of iterations. *)letbiadd~size~nsw~nuw~nusw(x0,x1)(y0,y1)=(* Ideal case: no carry at all, if for all indices at least one
component is known to be 0. *)letcarry=Z.(land)x0y0inif(Z.equalZ.zerocarry)thenbxor~size(x0,x1)(y0,y1)else(* Fallback case: when initially there are two zeroes, the
carries will not propagate across this position. So we know
the value of the bit above. This is useful when both values
are known to hold a lot of zeroes. *)letnever_carry=Z.(lor)x0y0inlet(res0,res1)=bxor~size(x0,x1)(y0,y1)inletshifted_carry=(Z.shift_leftnever_carry1)inletnshifted_carry=Z.(~!)shifted_carryin(Z.(lor)res0shifted_carry,Z.(land)res1nshifted_carry);;(* More precise. *)let_biadd~size(x0,x1)(y0,y1)=(* let nocarry = Z.extract (Z.shift_left (Z.(land) x0 y0) 1) 0 size in *)(* Nocarry avoids infinitely propagating carries.
I think it ensures termination. *)letnocarry=Z.(land)x0y0inletrecloopab=lethalf_sum=bxor~sizeabinlet(carry0,carry1)=band~sizeabinletcarry0=Z.(land)carry0nocarryin(* If a carry is possible. *)ifZ.equalcarry0Z.zerothenhalf_sumelseletcarry0=Z.shift_leftcarry01inletcarry1=Z.(land)carry1nocarryinletcarry1=Z.shift_leftcarry11inloophalf_sum(carry0,carry1)inlet(res0,res1)=(loop(x0,x1)(y0,y1))inZ.extractres00size,Z.extractres10size;;(* This algorithm is symmetric to biadd's. *)letbisub~size~nsw~nuw~nusw(x0,x1)(y0,y1)=(* Borrow happens when x may be 0 and y may be 1. *)letres=bxor~size(x0,x1)(y0,y1)inletno_borrow=Z.(land)(Z.(~!)x1)y0inif(Z.equalZ.zerono_borrow)thenreselse(* Fallback: no borrow when x_i is known to be 1 and y_i known to be 0.
In this case, we use the half_sum computed by xor.
Otherwise, we set the bit to unknown. *)letnever_borrow=Z.(land)x1(Z.(~!)y0)inletshifted_borrow=(Z.shift_leftnever_borrow1)inletnshifted_borrow=Z.extract(Z.(~!)shifted_borrow)0sizeinlet(res0,res1)=resin(Z.(lor)res0nshifted_borrow,Z.(land)res1shifted_borrow);;letbimul_singleton~(size:In_bits.t)(x0,x1)k=if(is_power_of_twok)then(Z.extract(Z.(*)x0k)0(size:>int),Z.extract(Z.(*)x1k)0(size:>int))elseBL.top~size;;letbimul~size~nsw~nuwxy=matchis_singletonx,is_singletonywith|true,true->biconst~sizeZ.(fstx*fsty)|true,false->bimul_singleton~sizey(fstx)|false,true->bimul_singleton~sizex(fsty)|false,false->BL.top~size(* Ival will tell us what we need. *)letbiudiv~size(x0,x1)((y0,y1)asy)=if(is_singletony&&is_power_of_twoy0)thenifZ.equalZ.zeroy0thenBL.bottom~sizeelse(Concrete.biudiv~sizex0y0,Concrete.biudiv~sizex1y0)elseBL.top~size(* TODO: we can do better than this:
* - All the bottom bits are known according to number of trailing zeroes
* - All the top bits are known according to the number of leading zeroes *)letbiumod~size(x0,x1)((y0,y1)asy)=if(is_singletony&&is_power_of_twoy0)thenifZ.equalZ.zeroy0thenBL.bottom~sizeelse(Concrete.biumod~sizex0y0,Concrete.biumod~sizex1y0)elseBL.top~size(* If y is a multiple of 2^k, the last k bits of x and result are
the same.
Also, if y is smaller than some value, then the result must be
smaller than this value. *)letbiumod~size(x0,x1)(y0,y1)=ifZ.equalZ.zeroy0thenBL.bottom~sizeelseletk=Z.trailing_zerosy0(* Number of known zeros. *)in(* Put to top all the bits above k. *)letmask0=Z.sub(Z.shift_leftZ.onek)Z.oneinletmask1=Z.(~!)mask0inletk2=Z.log2upy0in(* Maximum value for y0. *)letnask=Z.sub(Z.shift_leftZ.onek2)Z.onein(Z.(land)nask@@Z.(lor)x0mask1,Z.(land)nask@@Z.(land)x1mask0);;(* Works well, despite the fact that the behaviour of positive and
negative numbers is quite different. *)letbismod_alt~size(x0,x1)((y0,y1)asy)=if(is_singletony&&is_power_of_twoy0)thenifZ.equalZ.zeroy0thenBL.bottom~sizeelse(Concrete.bismod~sizex0y0,Concrete.bismod~sizex1y0)elseBL.top~size;;(* Like biumod for the low bits, but more complex for the high ones *)letbismod~(size:In_bits.t)((x0,x1)asx)((y0,y1)asy)=matchtestbitx((size:>int)-1),testbity((size:>int)-1)with|Zero,Zero->biumod~sizexy|_->beginifZ.equalZ.zeroy0thenBL.bottom~sizeelseletk=Z.trailing_zerosy0(* Number of known zeros. *)in(* Put to top all the bits above k. *)letmask0=Z.sub(Z.shift_leftZ.onek)Z.oneinletmask1=Z.extract(Z.(~!)mask0)0(size:>int)inletlow0=Z.(lor)x0mask1inletlow1=Z.(land)x1mask0in(low0,low1)end;;letbismod~sizexy=inter(bismod~sizexy)(bismod_alt~sizexy);;letbisdiv~size(x0,x1)((y0,y1)asy)=if(is_singletony&&is_power_of_twoy0)thenifZ.equalZ.zeroy0thenBL.bottom~sizeelsematchtestbit(x0,x1)((size:>int)-1)with|Zero->(Concrete.bisdiv~sizex0y0,Concrete.bisdiv~sizex1y0)(* Signed division of negative numbers is different from a
shift. *)|One->BL.top~size|Unknown->BL.top~sizeelseBL.top~size;;letbuext~size~oldsize(x0,x1)=(x0,x1);;letbsext~size~oldsize((x0,x1)asx)=matchtestbitx(oldsize-1)with|Zero->x|One->(Z.extract(Z.signed_extractx00oldsize)0size,Z.extract(Z.signed_extractx10oldsize)0size)|Unknown->(Z.extract(Z.signed_extractx00oldsize)0size,x1);;letbshl_singleton~(size:In_bits.t)(x0,x1)y=(Z.extract(Z.shift_leftx0y)0(size:>int),Z.extract(Z.shift_leftx1y)0(size:>int));;letblshr_singleton~(size:In_bits.t)(x0,x1)y=(Z.extract(Z.shift_rightx0y)0(size:>int),Z.extract(Z.shift_rightx1y)0(size:>int));;letbashr_singleton~size(x0,x1)y=letsize=In_bits.to_intsizein(Z.extract(Z.shift_right(Z.signed_extractx00size)y)0size,Z.extract(Z.shift_right(Z.signed_extractx10size)y)0size);;(* Note: if we have an interval, then we can put a minimum of
zeros. In general: we could be more precise by using the
interval concretisation. *)(* We implement a very precise version; actually, if x or y are imprecise,
this may not be worth it. *)(* Note: we follow SMTLIB's semantics, which returns 0 when shifts are too large. *)letdo_shiftf~(size:In_bits.t)x((y0,y1)asy)=(* Check that size is a power of two. *)assert((size:>int)land((size:>int)-1)==0);assert((size:>int)<=64);(* We don't want to do this on huge values. *)(* Mask the last bits. *)letnzsizem1=Z.lognot@@Z.of_int((size:>int)-1)inletmust_be_larger_than_size=(not@@Z.equalZ.zero@@Z.(land)y1nzsizem1)inifmust_be_larger_than_sizethenBL.singleton~sizeZ.zeroelseletmay_be_larger_than_size=(not@@Z.equalZ.zero@@Z.(land)y0nzsizem1)inletzset=concretize(band~sizey(BL.singleton~size(Z.of_int@@(size:>int)-1)))inletlist=ZSet.fold(funkacc->List.cons(f~sizex@@Z.to_intk)acc)zset[]inletres=matchlistwith|[]->assertfalse(* Should not happen. *)|a::b->List.fold_leftjoinabinifmay_be_larger_than_sizethenjoinres(BL.singleton~sizeZ.zero)elseres;;(* TODO: Handle nsw and nuw flags. *)letbshl~size~nsw~nuw=do_shiftbshl_singleton~size;;letblshr=do_shiftblshr_singleton;;letbashr~(size:In_bits.t)x((y0,y1)asy)=(* Check that size is a power of two. *)assert((size:>int)land((size:>int)-1)==0);assert((size:>int)<=64);(* We don't want to do this on huge values. *)(* Mask the last bits. *)letnzsizem1=Z.lognot@@Z.of_int((size:>int)-1)inletmust_be_larger_than_size=(not@@Z.equalZ.zero@@Z.(land)y1nzsizem1)inletoverflow_value=matchtestbitx((size:>int)-1)with|Zero->BL.singleton~sizeZ.zero|One->BL.singleton~sizeZ.minus_one|Unknown->BL.top~sizeinifmust_be_larger_than_sizethenoverflow_valueelseletmay_be_larger_than_size=(not@@Z.equalZ.zero@@Z.(land)y0nzsizem1)inletzset=concretize(band~sizey(BL.singleton~size(Z.of_int@@(size:>int)-1)))inletlist=ZSet.fold(funkacc->List.cons(bashr_singleton~sizex@@Z.to_intk)acc)zset[]inletres=matchlistwith|[]->assertfalse(* Should not happen. *)|a::b->List.fold_leftjoinabinifmay_be_larger_than_sizethenjoinresoverflow_valueelseres;;letbeq~size(x0,x1)(y0,y1)=letopenZin(* Bits for which both x0 and x1 are different. Outside of
(bottom,top) that we do not consider, should happen only if
one bit is known to be 0 and the other known to be 1. *)if(not@@Z.equalZ.zero@@(x0lxory0)land(x1lxory1))thenQuadrivalent.Falseelseif(Z.equalx0x1)&&(Z.equalx0y0)&&(Z.equalx1y1)thenQuadrivalent.TrueelseQuadrivalent.Top(* This domain will be used with others that can check the singleton case,
so we do not bother. *)(* must be true if no unknown bit and equal. Not the best domain for that. *)(* must be false if a single bit is known to be different. *)(* Note that this probably duplicates what the interval will be doing. *)letcompmin_max_extract~sizexy=let(minx,maxx)=min_max_extract~sizexinlet(miny,maxy)=min_max_extract~sizeyinif(Z.leqmaxxminy)thenQuadrivalent.Trueelseif(Z.ltmaxyminx)thenQuadrivalent.FalseelseQuadrivalent.Topletbisle=compmin_max_signed;;letbiule=compmin_max_unsigned;;endmoduleBinary_Backward=structletinter_refines(old0,old1)(new0,new1)=letres0=inter0old0new0inletres1=inter1old1new1inif(Z.equalold0res0&&Z.equalold1new1)thenNoneelseSome(res0,res1);;letbofbool~sizearg(x0,x1)=matcharg,testbit(x0,x1)0with|Quadrivalent.Bottom,_->None|_whenis_bottom(x0,x1)->SomeQuadrivalent.Bottom|_,Unknown->None|Quadrivalent.Top,One->SomeQuadrivalent.True|Quadrivalent.Top,Zero->SomeQuadrivalent.False|Quadrivalent.True,One->None|Quadrivalent.False,Zero->None|Quadrivalent.True,Zero->SomeQuadrivalent.Bottom|Quadrivalent.False,One->SomeQuadrivalent.Bottom;;letband~size(x0,x1)(y0,y1)(res0,res1)=letopenZinletx1'=res1in(* x and y are one whenever res is 1 *)lety1'=res1inletx0'=(res0lor~!y1)in(* x is 0 when res is 0 and y is 1. *)lety0'=(res0lor~!x1)in(inter_refines(x0,x1)(x0',x1'),inter_refines(y0,y1)(y0',y1'));;letbor~size(x0,x1)(y0,y1)(res0,res1)=letopenZinletx0'=res0in(* x and y are 0 whenever res is 0 *)lety0'=res0inletx1'=(res1land~!y0)in(* x is 1 when res is 1 and y is 0. *)lety1'=(res1land~!x0)in(inter_refines(x0,x1)(x0',x1'),inter_refines(y0,y1)(y0',y1'));;letbxor~sizexyr=(* TODO: There are some redundant computations between the two. *)letx'=Binary_Forward.bxor~sizeyrinlety'=Binary_Forward.bxor~sizexrininter_refinesxx',inter_refinesyy';;letbnot~size(x0,x1)(r0,r1)=inter_refines(x0,x1)@@Binary_Forward.bnot~size(r0,r1);;letbconcat~size1~size2xyr=letoldsize=In_bits.(size1+size2)in(inter_refinesx@@Binary_Forward.bextract~oldsize~index:size2~size:size1r,inter_refinesy@@Binary_Forward.bextract~oldsize~index:In_bits.zero~size:size2r)letbextract~size~index~oldsizexr=(* Add top bits before and after if needed. *)letr=ifindex==In_bits.zerothenrelseBinary_Forward.bconcat~size1:size~size2:indexr(BL.top~size:index)inletr=ifIn_bits.(index+size)==oldsizethenrelseletmissing_size=In_bits.(oldsize-(index+size))inBinary_Forward.bconcat~size1:missing_size~size2:In_bits.(index+size)(BL.top~size:missing_size)rininter_refinesxr;;letbuext~(size:In_bits.t)~(oldsize:In_bits.t)xr=let(r0,r1)=rin(* If something in the upper bits of r is known to be 1,
the result is bottom. Maybe not worth to check. *)letupper1=Z.extractr1(oldsize:>int)((size:>int)-(oldsize:>int))inifnot@@Z.equalZ.zeroupper1theninter_refinesx@@BL.bottom~size:oldsizeelseinter_refinesx(Binary_Forward.bextract~size:oldsize~oldsize:size~index:In_bits.zeror);;(* Returns a pair (contains_known_one,contains_known_zero). *)lettest_high_bits~sizerk=letsize=In_bits.to_intsizein(* Printf.printf "Test high bits: size = %d k = %d%!" size k; *)let(r0,r1)=rinletupper0=Z.extractr0(size-k)kinletupper1=Z.extractr1(size-k)kinlethigh_bit_is_one=(* One bit is known to be 1. *)(not@@Z.equalZ.zeroupper1)inlethigh_bit_is_zero=(* One bit is known to be 0. *)(not@@Z.equal(Z.extractZ.minus_one0k)upper0)in(high_bit_is_one,high_bit_is_zero);;(* We are in a situation where r (or size bigsize) was derived
from some x (of size smallsize), such that the high bits in r
is a copy from the most significant bit of x. We update the
corresponding bit of r so that it can be used to refine x. *)letupdate_according_to_high_bits~bigsize~(smallsize:In_bits.t)((r0,r1)asr)=matchtest_high_bits~size:bigsizer(1+(bigsize:>int)-(smallsize:>int))with|true,true->BL.bottom~size:bigsize|true,false->letclear_bit=Z.(lognot)@@Z.shift_leftZ.one(smallsize:>int)inletr=(Z.(land)r0clear_bit,Z.(land)r1clear_bit)inr|false,true->letset_bit=Z.shift_leftZ.one(smallsize:>int)inletr=(Z.(lor)r0set_bit,Z.(lor)r1set_bit)inr|false,false->rletbsext~size~oldsizexr=letr=update_according_to_high_bits~bigsize:size~smallsize:oldsizerininter_refinesx(Binary_Forward.bextract~size:oldsize~oldsize:size~index:In_bits.zeror)letshift_right_introduce_top~sizexk=ifk==0thenxelseletk=In_bits.of_intkinBinary_Forward.bconcat~size1:k(BL.top~size:k)~size2:In_bits.(size-k)(Binary_Forward.bextract~index:k~size:In_bits.(size-k)~oldsize:sizex);;letshift_left_introduce_top~sizexk=ifk==0thenxelseletk=In_bits.of_intkinBinary_Forward.bconcat~size1:In_bits.(size-k)(Binary_Forward.bextract~index:In_bits.zero~size:In_bits.(size-k)~oldsize:sizex)~size2:k(BL.top~size:k);;letdo_shiftf~(size:In_bits.t)xyr=assert(is_power_of_two@@Z.of_int(size:>int));(* If zero belongs to the result, it may be that y is just very large;
in this case x and y can have any value, and we don't learn anything.
TODO: also check nuw and nsw. *)letsizem1=(Z.of_int@@(size:>int)-1)inletresult_may_be_zero=not@@(disjointr@@BL.singleton~sizeZ.zero)inlety_may_be_larger_than_size=let(y0,_)=yinnot@@Z.equalZ.zero@@Z.(land)y0@@Z.(~!)sizem1inifresult_may_be_zero&&y_may_be_larger_than_sizethenNone,Noneelsebeginletyset=concretize(Binary_Forward.band~sizey(BL.singleton~sizesizem1))inlet(newx,newy)=ZSet.fold(funy(newx,newy)->(* Format.printf "y is %s@." @@ Z.to_string y; *)letoldx=f~sizer(Z.to_inty)inifdisjointxoldxthen(newx,newy)else(joinnewx@@interxoldx,joinnewy@@BL.singleton~sizey))yset(BL.bottom~size,BL.bottom~size)in(inter_refinesxnewx,inter_refinesynewy)end;;letbshl~size~nsw~nuw=do_shiftshift_right_introduce_top~size;;letblshr=do_shiftshift_left_introduce_top;;letbashr~(size:In_bits.t)xyr=assert(is_power_of_two@@Z.of_int(size:>int));(* TODO: independently from everything, we can do like in bsext
* and try to find the top bit of x from the top bits of r. *)(* If zero belongs to the result (of -1 if x can be negative),
it may be that y is just very large; in this case x and y can
have any value, and we don't learn anything. TODO: also
check nuw and nsw. *)letsizem1=(Z.of_int@@(size:>int)-1)inletresult_ok_large_size=matchtestbitx((size:>int)-1)with|Zero->not(disjointr@@BL.singleton~sizeZ.zero)|One->not(disjointr@@BL.singleton~sizeZ.minus_one)|Unknown->not(disjointr@@BL.singleton~sizeZ.zero)||not(disjointr@@BL.singleton~sizeZ.minus_one)in(* Format.printf "HELLOS %a size %d@." pretty y size;
* Format.printf "OK large size %b@." result_ok_large_size; *)lety_may_be_larger_than_size=let(y0,_)=yinnot@@Z.equalZ.zero@@Z.(land)y0@@Z.(~!)sizem1in(* Format.printf "OK large size %b@." result_ok_large_size; *)ifresult_ok_large_size&&y_may_be_larger_than_sizethenNone,Noneelsebeginletyset=concretize(Binary_Forward.band~sizey(BL.singleton~sizesizem1))inlet(newx,newy)=ZSet.fold(funy(newx,newy)->(* Lookup the high bits of r to better estimate the high bit of x. *)letr=ifZ.equalyZ.zerothenrelseletsmallsize=(In_bits.of_int((size:>int)-Z.to_inty))inupdate_according_to_high_bits~bigsize:size~smallsizerinletoldx=shift_left_introduce_top~sizer(Z.to_inty)inifdisjointxoldxthen(newx,newy)else(joinnewx@@interxoldx,joinnewy@@BL.singleton~sizey))yset(BL.bottom~size,BL.bottom~size)in(inter_refinesxnewx,inter_refinesynewy)end;;letbeq~sizexyr=matchrwith|Quadrivalent.Bottom|Quadrivalent.Top->None,None|Quadrivalent.True->letxy=interxyininter_refinesxxy,inter_refinesyxy|Quadrivalent.False->(* We can learn only one bit, if it is the only one unknown
for x, that everything is known in y, and all the other
bits are the same in x and y.
Probably never triggers. *)if(is_bottomx||is_bottomy)then(None,None)elseletunknownx=unknownxinletunknowny=unknownyin(* Learn 1 bit of y from x. *)letlearn(x0,x1)(y0,y1)=if(Z.equalx0y0)then(y1,y1)else(y0,y0)inifZ.equalunknownxZ.zero&&is_power_of_twounknownythenNone,inter_refinesy@@learnxyelseifZ.equalunknownyZ.zero&&is_power_of_twounknownxthen(inter_refinesx@@learnyx),Noneelse(None,None);;(* Assume that (x0,x1) <=u maxu. *)letassume_unsigned_max~sizemaxu(x0,x1)=(* Format.printf "assume unsigned max %s %a@." (Z.to_string maxu) pretty (x0,x1); *)(* Naive algorithm: iterate from the most significant bit.
If max is 0: set to 0 and continue.
If max is 1: if x is known as 1, continue; if unknown, or known 0, stop
(we don't known about the next bits).
Clever algorithm : find the limit of all the bits that will be copied
from maxu, then do the copy. *)tryletfirst_uncopied_bit=Z.log2@@Z.(land)maxu@@Z.(~!)@@Z.(land)x0x1inletcopy_bits_up_to=first_uncopied_bit+1in(* Format.printf "copy_bits_up_to %d@." copy_bits_up_to; *)letmask=Z.sub(Z.shift_leftZ.onecopy_bits_up_to)Z.oneinletnmask=Z.(~!)maskinletmaxu_copied_bits=Z.(land)nmaskmaxuinletres0=Z.(lor)maxu_copied_bits(Z.(land)maskx0)inletres1=Z.(lor)maxu_copied_bits(Z.(land)maskx1)in(res0,res1)withInvalid_argument_->ifZ.equalZ.zeromaxuthen(Z.zero,Z.zero)else(x0,x1)(* Do something here? *);;(* Assume that minu <=u (x0,x1). *)letassume_unsigned_min~sizeminu(x0,x1)=(* Naive algorithm: iterate from the most significant bit.
If min is 1: set to 1 and continue.
If min is 0: if x is known as 0, continue; if unknown, or known 1, stop
(we don't known about the next bits).
Clever algorithm : find the limit of all the bits that will be copied
from maxu, then do the copy. *)letfirst_uncopied_bit=tryZ.log2@@Z.(land)(Z.(~!)minu)@@Z.(lor)x0x1withInvalid_argument_->-1inletcopy_bits_up_to=first_uncopied_bit+1in(* Format.printf "copy_bits_up_to %d@." copy_bits_up_to; *)letmask=Z.sub(Z.shift_leftZ.onecopy_bits_up_to)Z.oneinletnmask=Z.(~!)maskinletmaxu_copied_bits=Z.(land)nmaskminuinletres0=Z.(lor)maxu_copied_bits(Z.(land)maskx0)inletres1=Z.(lor)maxu_copied_bits(Z.(land)maskx1)in(res0,res1);;(* Here: by comparing the min and max, we can try to learn some
bits. Do we learn something more than intervals? Yet, because
we maintain the knowledge of the low bits. *)letbiule~sizexyr=matchrwith|Quadrivalent.Top|Quadrivalent.Bottom->None,None|Quadrivalent.True->let(_,maxy)=min_max_unsigned~sizeyinlet(minx,_)=min_max_unsigned~sizexinletnewx=assume_unsigned_max~sizemaxyxinletnewy=assume_unsigned_min~sizeminxyininter_refinesxnewx,inter_refinesynewy|Quadrivalent.False->(* ~(x <= y) <=> x > y <=> x >= y + 1 *)let(miny,_)=min_max_unsigned~sizeyinlet(_,maxx)=min_max_unsigned~sizexinletnewx=assume_unsigned_min~size(Z.addminyZ.one)xinletnewy=assume_unsigned_max~size(Z.submaxxZ.one)yininter_refinesxnewx,inter_refinesynewy;;(* For signed values, 1 is also higher than 0s, except for the first bit. *)letassume_signed_max~sizemaxu(x0,x1)=if(Z.ltmaxuZ.zero)thenletmask=Z.shift_leftZ.one(size-1)inlet(x0,x1)=(Z.(lor)x0mask,Z.(lor)x1mask)inassume_unsigned_max~sizemaxu(x0,x1)else(* If the first bit is 0: try to learn.
Otherwise: we cannot learn anything. *)matchtestbit(x0,x1)(size-1)with|Zero->assume_unsigned_max~sizemaxu(x0,x1)|One|Unknown->(x0,x1);;(* For signed values, 1 is also higher than 0s, except for the first bit. *)letassume_signed_min~sizeminu(x0,x1)=(* let mask = Z.shift_left Z.one (size - 1) in *)if(Z.geqminuZ.zero)thenassume_unsigned_min~sizeminu(x0,x1)else(* If the first bit is 1: try to learn.
Otherwise: we cannot learn anything. *)matchtestbit(x0,x1)(size-1)with(* The extract here is needed, otherwise we produce negative numbers. *)|One->assume_unsigned_min~size(Z.extractminu0size)(x0,x1)|Zero|Unknown->(x0,x1);;letbisle~sizexyr=matchrwith|Quadrivalent.Top|Quadrivalent.Bottom->None,None|Quadrivalent.True->(* we known that x <= y, so x <= max(y) *)let(_,maxy)=min_max_signed~sizeyinlet(minx,_)=min_max_signed~sizexin(* Format.printf "maxy is %s@." (Z.to_string maxy); *)letnewx=assume_signed_max~sizemaxyxinletnewy=assume_signed_min~sizeminxyin(* Format.printf "y is %a newy is %a inter is %a" pretty y pretty newy pretty (inter y newy); *)inter_refinesxnewx,inter_refinesynewy|Quadrivalent.False->let(miny,_)=min_max_signed~sizeyinlet(_,maxx)=min_max_signed~sizexinletnewx=assume_signed_min~size(Z.addminyZ.one)xinletnewy=assume_signed_max~size(Z.submaxxZ.one)yin(* Format.printf "old y %a newy %a maxx %s@." pretty y pretty newy (Z.to_string maxx); *)inter_refinesxnewx,inter_refinesynewy(* x + y = r <=> x = r - y <=> y = r - x *)letbiadd~size~nsw~nuw~nuswxyr=inter_refinesx(Binary_Forward.bisub~size~nsw~nuw~nuswry),inter_refinesy(Binary_Forward.bisub~size~nsw~nuw~nuswrx);;(* x - y = r <=> x = r + y <=> y = x - r *)letbisub~size~nsw~nuw~nuswxyr=inter_refinesx(Binary_Forward.biadd~size~nsw~nuw~nuswry),inter_refinesy(Binary_Forward.bisub~size~nsw~nuw~nuswxr);;letbitimes~size~nsw~nuwx((k0,k1)ask)r=(* k must be a singleton. *)assert(Z.equalk0k1);ifZ.equalZ.zerok0thenNoneelseifnuwtheninter_refinesx@@Binary_Forward.biudiv~sizerkelseifnswtheninter_refinesx@@Binary_Forward.bisdiv~sizerkelseNone;;letbimul~size~nsw~nuwxyr=ifis_singletonythen(bitimes~size~nsw~nuwxyr),Noneelseifis_singletonxthenNone,(bitimes~size~nsw~nuwyxr)elseNone,None;;(* Only handle the cases where the division is a shift. *)letbiudiv~sizexyr=let(y0,_)=yinifis_singletony&&(not@@Z.equalZ.zeroy0)&&is_power_of_twoy0thenletk=Z.log2y0inFormat.printf"k is %d %a@."k(Binary_Lattice.pretty~size)(shift_left_introduce_top~sizerk);inter_refinesx(shift_left_introduce_top~sizerk),NoneelseNone,None;;(* Negative values behave badly, so we rule them out. *)letbisdiv~(size:In_bits.t)xyr=iftestbitx((size:>int)-1)=Zero&&testbity((size:>int)-1)=Zerothenbiudiv~sizexyrelseNone,None;;(* if r = x mod (2^k * z)
then the k lastbits of r are the k last bits of x.
TODO: This should be done on the forward operation too. *)letbiumod~sizex(y0,y1)(r0,r1)=(* Find the number of trailing zeroes in y. *)ifZ.equalZ.zeroy0(* y must be zero. *)thenNone,Noneelseletk=Z.trailing_zerosy0(* Number of known zeros. *)in(* Put to top all the bits above k. *)letmask0=Z.sub(Z.shift_leftZ.onek)Z.oneinletmask1=Z.(~!)mask0ininter_refinesx(Z.(lor)r0mask1,Z.(land)r1mask0),None;;(* This also works for modulo. *)letbismod=biumod;;endendincludeMust0_Must1moduleValue_Known=struct(* This representation is often less iteresting, but here is an
implementation of some operations. *)(* Intersection: we know more about both. Note: could also raise bottom. *)letinter(xv,xk)(yv,yk)=letopenZin(* There is an inconsistency if known values differ. *)letinconsistent=(xklandyk)land(xvlxoryv)inif(not(Z.equalinconsistentZ.zero))thenfailwith"Bottom";letvalue=(xkloryv)land(yklorxv)in(value,xkloryk);;letbnot(xv,xk)=(Z.(~!)xv,xk)letband(xv,xk)(yv,yk)=letopenZin(xvlandyv,(xklandyk)lor((Z.(~!)xv)landxk)lor((Z.(~!)yv)landyk));;letbor(xv,xk)(yv,yk)=letopenZin(xvloryv,xklandyklor(xvlandxk)lor(yvlandyk));;(* Result of xor is result of the known variable, but only value
that are both known are known. *)letbxor(xv,xk)(yv,yk)=(Z.(lxor)xvyv,Z.(land)xkyk)endmoduleValue_Unknown=struct(* Inersection: we know more about both. Note: could also raise bottom. *)letinter(xv,xu)(yv,yu)=letopenZin(* There is an inconsistency if known values differ. *)letinconsistent=~!(xuloryu)land(xvlxoryv)inif(not(Z.equalinconsistentZ.zero))thenfailwith"Bottom";letvalue=(xulorxv)land(yuloryv)in(value,xulandyu);;letbnot(xv,xu)=(Z.(~!)xv,xu)letband(xv,xu)(yv,yu)=letopenZin(xvlandyv,(xuloryu)land(xvlorxu)land(yvloryu));;letbor(xv,xu)(yv,yu)=letopenZin(xvloryv,(xuloryu)land(~!xvlorxu)land(~!yvloryu));;(* Result of xor is result of the known variable, but only value
that are both known are known. *)letbxor(xv,xu)(yv,yu)=(Z.(lxor)xvyv,Z.(lor)xuyu)(* We transform this addition algorithm: into a n addition wih uncertainty.
let rec add a b =
let half_sum = a lxor b in
let carry = (a land b) in
if carry == 0 then half_sum
else add half_sum (carry lsl 1)
*)letrecbadd(av,au)(bv,bu)=letopenZinlethalf_sum=bxor(av,au)(bv,bu)inlet(carryv,carryu)=band(av,au)(bv,bu)in(* if carryv == 0 && carryu == 0 *)(* Might drop the carryu part. We could also do a or to avoid making two tests *)if(Z.equalzero@@carryvlorcarryu)(* all bits are known to be 0. *)thenhalf_sumelsebaddhalf_sum(carryv,carryu)end;;