123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-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/>. *)(* *)(****************************************************************************)(** Common constructs for numeric abstractions. *)openMopsaopenAstmoduleI=ItvUtils.IntItvmoduleC=CongUtils.IntCong(** {2 Integer intervals} *)(** ********************* *)(** Integer intervals *)typeint_itv=I.t_with_bottype_avalue_kind+=V_int_interval:int_itvavalue_kind(** Query to evaluate the integer interval of an expression *)type_avalue_kind+=V_int_interval_fast:int_itvavalue_kind(** Same as [V_int_interval] but should be handled by optimized domains, such Boxes *)letmk_int_interval_query?(fast=true)e=iffastthenQ_avalue(e,V_int_interval_fast)elseQ_avalue(e,V_int_interval)letpp_int_intervalfmtitv=I.fprint_botfmtitvletcompare_int_intervalitv1itv2=I.compare_botitv1itv2(** Creates var \in itv constraint *)letconstraints_of_itvvar(itv:int_itv)range:expr=matchitvwith|Nb(ItvUtils.IntBound.Finitelo,ItvUtils.IntBound.Finitehi)->mk_invar(mk_zlorange)(mk_zhirange)range|Nb(ItvUtils.IntBound.MINF,ItvUtils.IntBound.Finitehi)->mk_levar(mk_zhirange)range|Nb(ItvUtils.IntBound.Finitelo,ItvUtils.IntBound.PINF)->mk_gevar(mk_zlorange)range|Nb_->mk_truerange|BOT->assertfalse(** {2 Integer intervals with congruence} *)(** ************************************* *)(** Integer step intervals *)typeint_congr_itv=int_itv*C.t_with_bot(** Query to evaluate the integer interval of an expression *)type_avalue_kind+=V_int_congr_interval:int_congr_itvavalue_kindletmk_int_congr_interval_querye=Q_avalue(e,V_int_congr_interval)(** {2 Rounding mode of floats} *)(** *************************** *)letopt_float_rounding=refApron.Texpr1.Nearletrounding_option_name="universal.numeric.rounding"let()=register_shared_optionrounding_option_name{key="-float-rounding-mode";category="Numeric";spec=Symbol(["near";"zero";"up";"down";"rnd"],(function|"near"->opt_float_rounding:=Apron.Texpr1.Near|"zero"->opt_float_rounding:=Apron.Texpr1.Zero|"up"->opt_float_rounding:=Apron.Texpr1.Up|"down"->opt_float_rounding:=Apron.Texpr1.Down|"rnd"->opt_float_rounding:=Apron.Texpr1.Rnd|x->Exceptions.panic"unknown rounding mode %s"x));doc="rounding mode of floating-point computations.";default="near";}(** {2 Float intervals} *)(** ******************* *)moduleF=ItvUtils.FloatItvNan(** Float intervals *)typefloat_itv=F.ttype_avalue_kind+=|V_float_interval:float_prec->float_itvavalue_kind(** Query to evaluate the float interval of an expression, with infinities and NaN *)|V_float_maybenan:float_prec->boolavalue_kindletmk_float_interval_query?(prec=F_DOUBLE)e=Q_avalue(e,V_float_intervalprec)letmk_float_maybenan_query?(prec=F_DOUBLE)e=Q_avalue(e,V_float_maybenanprec)letpp_float_intervalfmtitv=F.fprintF.dfl_fmtfmtitvletcompare_float_intervalitv1itv2=F.compareitv1itv2letprec:Ast.float_prec->ItvUtils.FloatItvNan.prec=function|F_SINGLE->`SINGLE|F_DOUBLE->`DOUBLE|F_LONG_DOUBLE->`EXTRA|F_FLOAT128->`EXTRA|F_REAL->`REALletround():ItvUtils.FloatItvNan.round=match!opt_float_roundingwith|Apron.Texpr1.Near->`NEAR|Apron.Texpr1.Zero->`ZERO|Apron.Texpr1.Up->`UP|Apron.Texpr1.Down->`DOWN|Apron.Texpr1.Rnd->`ANY(** {2 Fast assume on numeric conditions} *)(** ************************************* *)(** Get the intervals of a numeric expression *)letinterval_of_num_expremanflow:int_itv=matchexpr_to_zewith|Somen->I.of_range_botnn|None->ask_and_reduceman.ask(mk_int_interval_query~fast:truee)flow(** Evaluate a numeric condition using intervals *)leteval_num_condcondmanflow:booloption=(* Skip expressions that contain non-universal sub-expressions or statements *)ifexists_expr(fune->not(is_universal_typee.etyp))(funs->true)condthenNoneelse(* Evaluate the interval of the condition *)matchinterval_of_num_exprcondmanflowwith|Bot.Nbitv->beginmatchI.contains_zeroitv,I.contains_nonzeroitvwith|true,false->Somefalse|false,true->Sometrue|_->Noneend|_->None(** Optimized assume function that uses intervals to check a
condition or falls back to classic assume *)letassume_numcond~fthen~felse?(route=toplevel)manflow=letr1=(* Use [eval_num_cond] directly without evaluation if the expression
contains only pure universal sub-expressions (no statement) *)iffor_all_expr(fune->is_universal_typee.etyp)(funs->false)condthenmatcheval_num_condcondmanflowwith|Sometrue->Some(fthenflow)|Somefalse->Some(felseflow)|None->NoneelseNoneinmatchr1with|Somer->r|None->(* Evaluate the expression if it is not a pure universal expression, or when
[eval_num_cond] failed *)man.evalcondflow~translate:"Universal">>$funncondflow->matcheval_num_condncondmanflowwith|Sometrue->fthenflow|Somefalse->felseflow|None->assumencond~fthen~felse~eval:falsemanflow(** {2 Widening thresholds} *)(** *********************** *)openFramework.Combiners.Value.NonrelmoduleK=GenContextKey(structtype'at=SetExt.ZSet.tletprintppfmts=Format.fprintffmt"widening thresholds: @[%a@]"(SetExt.ZSet.fprintSetExtSig.{print_empty="∅";print_begin="{";print_sep=", ";print_end="}";}Z.pp_print)send)(** Key for accessing widening thresholds *)letwidening_thresholds_ctx_key=K.key(** Add a constant to the widening thresholds of a variable *)letadd_widening_thresholdvarnctx=letthresholds=tryfind_var_ctxvarwidening_thresholds_ctx_keyctxwithNot_found->SetExt.ZSet.emptyinadd_var_ctxvarwidening_thresholds_ctx_key(SetExt.ZSet.addnthresholds)ctx(** Remove all widening thresholds of a variable *)letremove_widening_thresholdsvarctx=remove_var_ctxvarwidening_thresholds_ctx_keyctxlet()=register_avalue{typ=(fun(typea)next(avk:aavalue_kind)->matchavkwith|V_int_interval->T_int|V_int_interval_fast->T_int|V_int_congr_interval->T_int|V_float_intervalp->T_floatp|_->next.pool_typavk);bottom=(letf:typea.avalue_pool->aavalue_kind->a=funnextavk->matchavkwith|V_int_interval->(Bot.BOT:int_itv)|V_int_interval_fast->(Bot.BOT:int_itv)|V_int_congr_interval->(Bot.BOT,Bot.BOT)|V_float_intervalp->F.bot|_->next.pool_bottomavkinf);top=(letf:typea.avalue_pool->aavalue_kind->a=funnextavk->matchavkwith|V_int_interval->Bot.NbI.minf_inf|V_int_interval_fast->Bot.NbI.minf_inf|V_int_congr_interval->(Bot.NbI.minf_inf,Bot.NbC.minf_inf)|V_float_intervalp->beginmatchpwith|F_SINGLE->F.single_special|F_DOUBLE->F.double_special|F_LONG_DOUBLE->F.extra|F_FLOAT128->F.extra|F_REAL->F.realend|_->next.pool_topavkinf);join=(letf:typea.avalue_pool->aavalue_kind->a->a->a=funnextavkav1av2->matchavkwith|V_int_interval->I.join_botav1av2|V_int_interval_fast->I.join_botav1av2|V_int_congr_interval->I.join_bot(fstav1)(fstav2),C.join_bot(sndav1)(sndav2)|V_float_intervalp->F.joinav1av2|_->next.pool_joinavkav1av2inf);meet=(letf:typea.avalue_pool->aavalue_kind->a->a->a=funnextavkav1av2->matchavkwith|V_int_interval->I.meet_botav1av2|V_int_interval_fast->I.meet_botav1av2|V_int_congr_interval->I.meet_bot(fstav1)(fstav2),C.meet_bot(sndav1)(sndav2)|V_float_intervalp->F.meetav1av2|_->next.pool_meetavkav1av2inf);print=(letf:typea.avalue_pool->aavalue_kind->Format.formatter->a->unit=funnextavkfmtav->matchavkwith|V_int_interval->I.fprint_botfmtav|V_int_interval_fast->I.fprint_botfmtav|V_int_congr_interval->Format.fprintffmt"%a:%a"I.fprint_bot(fstav)C.fprint_bot(sndav)|V_float_intervalp->F.fprintF.dfl_fmtfmtav|_->next.pool_printavkfmtavinf);compare=(letf:typeab.avalue_pool->aavalue_kind->a->bavalue_kind->b->int=funnextavk1av1avk2av2->matchavk1,avk2with|V_int_interval,V_int_interval->I.compare_botav1av2|V_int_interval_fast,V_int_interval_fast->I.compare_botav1av2|V_int_congr_interval,V_int_congr_interval->Compare.pairI.compare_botC.compare_botav1av2|V_float_intervalp1,V_float_intervalp2->Compare.paircompareF.compare(p1,av1)(p2,av2)|_->next.pool_compareavk1av1avk2av2inf)}