123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2018-2019 The MOPSA Project. *)(* *)(* This program is free software: 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, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(**
Bitfields - Sequences of bits that can be set, cleared, or unknown
We rely on Zarith for arithmetic operations.
*)openBot(** {2 Types} *)typet=Z.t(** set *)*Z.t(** cleared *)(**
Bitfields: bit sequences with bit values in a 3-valued logic: set,
cleared, or unknown.
Alternatively, this can be seen representing a set of arbtirary
precision integers through a Cartesian (non-relational) abstraction:
each bit is abstracted independently.
The first component has a 1 for each bit that can be set.
The second component has a 1 for each bit that can be cleared.
A bit can be both in the set and the clear state, indicating a bit that
may be set of cleared (set & cleared gives the bits at top).
At least one of set or cleared must be true for each bit
(set | cleared = -1), i.e., an element of [t] is not empty.
set and cleared can be negative, indicating bit sequences starting with
infinitely many 1s in 2's complement representation.
*)typet_with_bot=twith_bot(** The type of possibly empty bitfields. *)moduleI=ItvUtils.IntItvmoduleB=ItvUtils.IntBoundmoduleC=CongUtils.IntCongletis_valid((set,clr):t):bool=Z.logorsetclr=Z.minus_one(** Every bit in a bitfield must be set, cleared, or both. *)(** {2 Constructors} *)letof_z(set:Z.t)(clr:Z.t):t=ifnot(is_valid(set,clr))theninvalid_arg(Printf.sprintf"Bitfields.of_z (set:%s,clear:%s)"(Z.to_stringset)(Z.to_stringclr));(set,clr)letof_z_bot(set:Z.t)(clr:Z.t):t_with_bot=ifZ.logorsetclr<>Z.minus_onethenBOTelseNb(set,clr)letcst(c:Z.t):t=(* all bits at 1 in c must be set, and all bits at 0 must be cleared *)c,Z.lognotc(** Constant. *)letcst_int(c:int):t=cst(Z.of_intc)letcst_int64(c:int64):t=cst(Z.of_int64c)letzero:t=cstZ.zero(** 0 *)letone:t=cstZ.one(** 1 *)letmone:t=cstZ.minus_one(** -1 *)letzero_one:t=(* bit 0 can be in set or cleared, other bits must be cleared *)Z.one,Z.minus_one(** [0,1] *)letminf_inf:t=(* all bits can be set and cleared *)Z.minus_one,Z.minus_one(** All integers. Indistiguishable from [0,+∞]. *)letunsigned(sz:int):t=(* bits 0 to sz can be set or cleared, other bits must be cleared *)Z.pred(Z.shift_leftZ.onesz),Z.minus_one(** Bitfields of unsigned integers with the specified bitsize. *)letunsigned8:t=unsigned8letunsigned16:t=unsigned16letunsigned32:t=unsigned32letunsigned64:t=unsigned64letof_range_bot(lo:Z.t)(hi:Z.t):twith_bot=iflo>hithen(* empty set *)BOTelseiflo<Z.zero&&hi>=Z.zerothen(* contains -1 and 0 -> every bit can be 0 and can be 1 *)Nbminf_infelse(* mask is all 1 after the highest bit != in lo and hi *)letto_mask=Z.numbits(Z.logxorlohi)inletmask=Z.pred(Z.shift_leftZ.oneto_mask)in(* the bits in the mask can be set or cleared,
other bits are fixed to lo (or equivalently to hi) *)Nb(Z.logorlomask,Z.logor(Z.lognotlo)mask)(** Bitfield enclosing the range [lo,hi]. *)letof_bound_bot(lo:B.t)(hi:B.t):twith_bot=matchlo,hiwith|B.Finitel,B.Finiteh->of_range_botlh|_->Nbminf_inf(** Bitfield enclosing the range [lo,hi]. *)letof_range(lo:Z.t)(hi:Z.t):t=matchof_range_botlohiwith|Nbx->x|BOT->invalid_arg(Printf.sprintf"IntBitfields.of_range [%s,%s]"(Z.to_stringlo)(Z.to_stringhi))(** Bitfield enclosing the range [lo,hi].
Fails with [invalid_arg] if the range is empty.
*)letof_bound(lo:B.t)(hi:B.t):t=matchof_bound_botlohiwith|Nbx->x|BOT->invalid_arg(Printf.sprintf"IntBitfields.of_bound [%s,%s]"(B.to_stringlo)(B.to_stringhi))(** Bitfield enclosing the range [lo,hi].
Fails with [invalid_arg] if the range is empty.
*)(** {2 Predicates} *)letequal((set1,clr1):t)((set2,clr2):t):bool=set1=set2&&clr1=clr2(** Equality. = also works. *)letequal_bot:t_with_bot->t_with_bot->bool=bot_equalequalletincluded((set1,clr1):t)((set2,clr2):t):bool=(* set1 (resp. clr1) has less bits at 1 than set2 (resp. clr2) *)Z.logorset1set2=set2&&Z.logorclr1clr2=clr2(** Set ordering. *)letincluded_bot:t_with_bot->t_with_bot->bool=bot_includedincludedletintersect((set1,clr1):t)((set2,clr2):t):bool=(* for each bit, it must be set in both arguments, or cleared in both *)Z.logor(Z.logandset1set2)(Z.logandclr1clr2)=Z.minus_one(** Whether the intersection is non-empty. *)letintersect_bot:t_with_bot->t_with_bot->bool=bot_dfl2falseintersectletcontains(x:Z.t)((set,clr):t):bool=(* each bit at 1 in x must be in set, and each bit at 0 must be in clr *)(x=Z.logandxset)&&(x=Z.logorx(Z.lognotclr))letcompare((set1,clr1):t)((set2,clr2):t):int=ifset1=set2thenZ.compareclr1clr2elseZ.compareset1set2(**
A total ordering on bitfields, returning -1, 0, or 1.
Can be used as compare for sets, maps, etc.
*)letcompare_bot(x:twith_bot)(y:twith_bot):int=Bot.bot_comparecomparexy(** Total ordering on possibly empty bitfields. *)letcontains_zero((set,clr):t):bool=(* all bits must be in clr *)clr=Z.minus_one(** Whether the bifield contains zero. *)letcontains_one(x:t):bool=containsZ.onex(** Whether the bifield contains one. *)letcontains_nonzero((set,clr):t):bool=(* at least one bit must be in set *)set<>Z.zero(** Whether the bifield contains a non-zero value. *)letis_zero(x:t):bool=x=zeroletis_positive((set,clr):t):bool=(* there are only finitely many bits that cn be 1 *)set>=Z.zeroletis_positive_strict(a:t):bool=is_positivea&¬(contains_zeroa)letis_negative_strict((set,clr):t):bool=(* there are only finitely many bits that can be 0 *)clr>=Z.zeroletis_negative(a:t):bool=(* any bitfield allowing 0 and a negative number also allows a strictly
positive number; the only way to be negative or nul is to either
contain only strictly negative numbers, or to contain only 0
*)is_negative_stricta||is_zeroaletis_nonzero((set,clr):t):bool=(* there is a position with a bit that cannot be 0 *)clr<>Z.minus_one(** Contains only non-zero elements. *)letis_minf_inf((a,b):t):bool=a=Z.minus_one&&b=Z.minus_one(** The bitfield represents [-∞,+∞]. *)letis_singleton((set,clr):t):bool=(* every bit can be either set of cleared, but not both *)set=Z.lognotclr(** Whether the bitfield contains a single element. *)letis_bounded((set,clr):t):bool=(* the set of bits that can be both set and cleared is finite *)Z.logandsetclr>=Z.zero(** Whether the bitfield contains a finite number of elements. *)letlower_bound((set,clr):t):B.t=ifset<Z.zero&&clr<Z.zerothenB.MINF(* can be arbitrarily negative *)elseB.Finite(Z.lognotclr)(* clear all the bits that can be cleared *)(** Get the lower bound (possibly MINF). *)letupper_bound((set,clr):t):B.t=ifset<Z.zero&&clr<Z.zerothenB.PINF(* can be arbitrarily positive *)elseB.Finiteset(* set all the bits that can be set *)(** Get the upper bound (possibly PINF). *)(** {2 Printing} *)letto_string((set,clr):t):string=letb=Buffer.create10inletboth=Z.logandsetclrin(matchset<Z.zero,clr<Z.zerowith|true,false->Buffer.add_stringb"…1"|true,true->Buffer.add_stringb"…⊤"|_->());fori=Z.numbitsboth-1downto0domatchZ.testbitseti,Z.testbitclriwith|true,false->Buffer.add_stringb"1"|false,true->Buffer.add_stringb"0"|true,true->Buffer.add_stringb"⊤"|_->invalid_arg(Printf.sprintf"IntBitfields.to_string (set:%s,clear:%s)"(Z.to_stringset)(Z.to_stringclr));done;Buffer.contentsbletprintch(x:t)=output_stringch(to_stringx)letfprintch(x:t)=Format.pp_print_stringch(to_stringx)letbprintch(x:t)=Buffer.add_stringch(to_stringx)letto_string_bot=bot_to_stringto_stringletprint_bot=bot_printprintletfprint_bot=bot_fprintfprintletbprint_bot=bot_bprintbprint(** {2 Enumeration} *)letsize((set,clr):t):int=letboth=Z.logandsetclrinifboth<Z.zerotheninvalid_arg(Printf.sprintf"Bitfields.size: unbounded set %s"(to_string(set,clr)));Z.popcountboth(** Number of elements. Raises an invalid argument if it is unbounded. *)letto_list((set,clr):t):Z.tlist=letboth=Z.logandsetclrinifboth<Z.zerotheninvalid_arg(Printf.sprintf"Bitfields.size: unbounded set %s"(to_string(set,clr)));letrecdoitaccvi=ifi<0thenv::accelse(* bit i can be 0 *)letacc=ifZ.testbitclrithendoitaccv(i-1)elseaccin(* bit i can be 1 *)ifZ.testbitsetithendoitacc(Z.logorv(Z.shift_leftZ.onei))(i-1)elseaccinletorg=Z.lognotclrindoit[]org(Z.numbitsboth-1)(** List of elements, in increasing order.
Raises an invalid argument if it is unbounded.
*)(** {2 Set operations} *)letjoin((set,clr):t)((set',clr'):t):t=Z.logorsetset',Z.logorclrclr'(** Abstract union. *)letjoin_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_neutral2joinabletjoin_list(l:tlist):t_with_bot=List.fold_left(funab->join_bota(Nbb))BOTlletmeet((set,clr):t)((set',clr'):t):t_with_bot=of_z_bot(Z.logandsetset')(Z.logandclrclr')(** Abstract intersection. *)letmeet_bot(a:t_with_bot)(b:t_with_bot):t_with_bot=bot_absorb2meetabletmeet_list(l:tlist):t_with_bot=List.fold_left(funab->meet_bota(Nbb))(Nbminf_inf)l(** {2 Forward operations} *)letto_bool(can_be_zero:bool)(can_be_one:bool):t=matchcan_be_zero,can_be_onewith|true,false->zero|false,true->one|true,true->zero_one|_->failwith"unreachable case encountered in IntBitfields.to_bool"(* helper function for operators returning a boolean that can be zero and/or one *)letlog_cast(a:t):t=to_bool(contains_zeroa)(contains_nonzeroa)(** Conversion from integer to boolean in [0,1]: maps 0 to 0 (false) and non-zero to 1 (true). *)letlog_not(a:t):t=to_bool(contains_nonzeroa)(contains_zeroa)(** Logical negation.
Logical operation use the C semantics: they accept 0 and non-0 respectively as false and true, but they always return 0 and 1 respectively for false and true.
*)letlog_and(a:t)(b:t):t=to_bool(contains_zeroa||contains_zerob)(contains_nonzeroa&&contains_nonzerob)(** Logical and. *)letlog_or(a:t)(b:t):t=to_bool(contains_zeroa&&contains_zerob)(contains_nonzeroa||contains_nonzerob)(** Logical or. *)letlog_xor(a:t)(b:t):t=letf,f'=contains_zeroa,contains_zerobandt,t'=contains_nonzeroa,contains_nonzerobinto_bool((f&&f')||(t&&t'))((f&&t')||(t&&f'))(** Logical exclusive or. *)letlog_eq(a:t)(b:t):t=to_bool(not(equalab&&is_singletona))(intersectab)letlog_neq(a:t)(b:t):t=to_bool(intersectab)(not(equalab&&is_singletona))letlog_leq(a:t)(b:t):t=to_bool(B.gt(upper_bounda)(lower_boundb))(B.leq(lower_bounda)(upper_boundb))letlog_geq(a:t)(b:t):t=to_bool(B.lt(lower_bounda)(upper_boundb))(B.geq(upper_bounda)(lower_boundb))letlog_lt(a:t)(b:t):t=to_bool(B.geq(upper_bounda)(lower_boundb))(B.lt(lower_bounda)(upper_boundb))letlog_gt(a:t)(b:t):t=to_bool(B.leq(lower_bounda)(upper_boundb))(B.gt(upper_bounda)(lower_boundb))(** C comparison tests. Returns an interval included in [0,1] (a boolean) *)letis_log_eq(a:t)(b:t):bool=intersectabletis_log_neq(a:t)(b:t):bool=not(equalab&&is_singletona)letis_log_leq(a:t)(b:t):bool=B.leq(lower_bounda)(upper_boundb)letis_log_geq(a:t)(b:t):bool=B.geq(upper_bounda)(lower_boundb)letis_log_lt(a:t)(b:t):bool=B.lt(lower_bounda)(upper_boundb)letis_log_gt(a:t)(b:t):bool=B.gt(upper_bounda)(lower_boundb)(** C comparison tests. Returns a boolean if the test may succeed *)letshift_left((set,clr):t)((set',clr'):t):t=ifis_singleton(set',clr')&&set'>=Z.zerothen(* exact when the right argument is a positive singleton, top else *)tryletl=Z.to_intset'inZ.shift_leftsetl,Z.shift_leftclrlwithZ.Overflow->minf_infelseminf_inf(** Bitshift left: multiplication by a power of 2. *)letshift_right((set,clr):t)((set',clr'):t):t=ifis_singleton(set',clr')&&set'>=Z.zerothen(* exact when the right argument is a positive singleton, top else *)tryletl=Z.to_intset'inZ.shift_rightsetl,Z.shift_rightclrlwithZ.Overflow->minf_infelseminf_inf(** Bitshift right: division by a power of 2 rounding towards -∞. *)letshift_right_trunc((set,clr):t)((set',clr'):t):t=ifis_singleton(set',clr')&&set'>=Z.zerothen(* exact when the right argument is a positive singleton, top else *)tryletl=Z.to_intset'inZ.shift_right_truncsetl,Z.shift_right_truncclrlwithZ.Overflow->minf_infelseminf_inf(** Unsigned bitshift right: division by a power of 2 with truncation. *)letbit_not((set,clr):t):t=clr,set(** Bitwise negation. *)letbit_or((set,clr):t)((set',clr'):t):t=Z.logorsetset',Z.logandclrclr'(** Bitwise or. *)letbit_and((set,clr):t)((set',clr'):t):t=Z.logandsetset',Z.logorclrclr'(** Bitwise and. *)letbit_xor((set,clr):t)((set',clr'):t):t=Z.logor(Z.logandsetclr')(Z.logandset'clr),Z.logor(Z.logandclrclr')(Z.logandsetset')(** Bitwise exclusive or. *)(** {2 Filters} *)(** Given two interval aruments, return the arguments assuming that the predicate holds.
*)letfilter_eq(a:t)(b:t):(t*t)with_bot=matchmeetabwithBOT->BOT|Nbx->Nb(x,x)letfilter_sglop((set,clr)asa:t)((set',clr')asa':t):(t*t)with_bot=ifis_singleton(set,clr)&&is_singleton(set',clr')&¬(opsetset')thenBOTelseNb(a,a')(* utility function: we only handle the simple case of singletons *)letfilter_neq=filter_sgl(<>)letfilter_leq=filter_sgl(<=)letfilter_geq=filter_sgl(>=)letfilter_lt=filter_sgl(<)letfilter_gt=filter_sgl(>)(** {2 Reduction} *)letof_interval((lo,hi):I.t):t=of_boundlohiletto_interval((set,clr):t):I.t=lower_bound(set,clr),upper_bound(set,clr)letmeet_inter(b:t)(i:I.t):(t*I.t)with_bot=bot_merge2(meetb(of_intervali))(I.meeti(to_intervalb))(** Intersects a bitfield with an interval, and returns the set represented
both as a bitfield and as an interval.
Useful to implement reductions.
*)