123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Hook to collect widening thresholds.
The heuristic is simple: the hook tracks all comparisons between a
numeric variable and a constant. It then adds the constant to the
context of thresholds associated to the variable.
For better precision, a simple constant folding pass is performed on
expressions. It replaces variables evaluated to singleton intervals with
constants.
For better performances, only comparisons in loops are considered.
*)openMopsaopenHookopenAstopenNumeric.CommonmoduleHook=struct(** {2 Hook header} *)(** *************** *)letname="constant_widening_thresholds"letdebugfmt=Debug.debug~channel:namefmt(** {2 Utility functions} *)(** ********************* *)(** A condition is interesting if it is a comparison between numeric
expressions *)letis_interesting_conditione=is_numeric_typee.etyp&&matchekindewith|E_binop(op,e1,e2)->is_comparison_opop&&is_numeric_typee1.etyp&&is_numeric_typee2.etyp|_->false(** Simple constant folding transformation. Variables that evaluate to
singleton intervals are replaced with their value. Also, operations between
constants are simplified. *)letrecconstant_foldingemanflow=matchekindewith|E_var(v,_)whenis_int_typev.vtyp->letitv=ask_and_reduceman.ask(mk_int_interval_querye)flowin(matchitvwith|Bot.Nb(I.B.Finitelo,I.B.Finitehi)whenZ.(lo=hi)->(* Note that we return both the variable and its value. *)ExprSet.of_list[e;mk_zloe.erange]|_->ExprSet.singletone)|_->matchexpr_to_constewith|Somec->ExprSet.singleton{ewithekind=E_constantc}|None->matchekindewith|E_binop(O_plus,e1,e2)->lets1=constant_foldinge1manflowinlets2=constant_foldinge2manflowinExprSet.fold(fune1'acc->matchekinde1'with|E_constant(C_intn)whenZ.(n=zero)->ExprSet.unionaccs2|_->ExprSet.fold(fune2'acc->matchekinde2'with|E_constant(C_intn)whenZ.(n=zero)->ExprSet.adde1'acc|_->lete'={ewithekind=E_binop(O_plus,e1',e2')}inmatchexpr_to_conste'with|Somec->ExprSet.add{ewithekind=E_constantc}acc|_->ExprSet.adde'acc)s2acc)s1ExprSet.empty|E_binop(O_minus,e1,e2)->lets1=constant_foldinge1manflowinlets2=constant_foldinge2manflowinExprSet.fold(fune2'acc->matchekinde2'with|E_constant(C_intn)whenZ.(n=zero)->ExprSet.unionaccs1|_->ExprSet.fold(fune1'acc->matchekinde1'with|E_constant(C_intn)whenZ.(n=zero)->ExprSet.add{ewithekind=E_unop(O_minus,e2')}acc|_->lete'={ewithekind=E_binop(O_plus,e1',e2')}inmatchexpr_to_conste'with|Somec->ExprSet.add{ewithekind=E_constantc}acc|_->ExprSet.adde'acc)s1acc)s2ExprSet.empty|E_binop(op,e1,e2)whenis_comparison_opop->lets1=constant_foldinge1manflowinlets2=constant_foldinge2manflowinExprSet.fold(fune1'acc->ExprSet.fold(fune2'acc->ExprSet.add{ewithekind=E_binop(op,e1',e2')}acc)s2acc)s1ExprSet.empty|_->ExprSet.singletonemoduleThresholdSet=SetExt.Make(structtypet=var*Z.tletcomparex1x2=Compare.paircompare_varZ.comparex1x2end)letfind_vars_comparisons_in_expre=Visitor.fold_expr(funaccee->matchekindeewith|E_binop(op,{ekind=E_var(v,_);etyp=T_int},{ekind=E_constant(C_intn)})|E_binop(op,{ekind=E_constant(C_intn)},{ekind=E_var(v,_);etyp=T_int})whenis_comparison_opop->Keep(ThresholdSet.add(v,n)acc)|_->VisitPartsacc)(funaccs->VisitPartsacc)ThresholdSet.emptye(** {2 Events handlers} *)(** ******************* *)letinitctx=ctxletloops=ref0leton_before_execroutestmtmanflow=matchskindstmtwith|S_while(cond,body)->(incrloops;None)|_->Noneleton_after_execroutestmtmanflowpost=matchskindstmtwith|S_while(cond,body)->(decrloops;None)|S_assumeewhen!loops>0&&is_interesting_conditione->(* Simplify e by replacing performing a simple constant folding pass *)lets=constant_foldingemanflowin(* Search for comparisons between variables and constants in set [s] *)letcmps=ExprSet.fold(fune'acc->ThresholdSet.unionacc(find_vars_comparisons_in_expre'))sThresholdSet.emptyin(* Add compared constants to the context of widening thresholds *)letctx=ThresholdSet.fold(fun(v,n)acc->Numeric.Common.add_widening_thresholdvnacc)cmps(Cases.get_ctxpost)inSomectx|S_remove{ekind=E_var(v,_);etyp=T_int}->letctx=Cases.get_ctxpost|>Numeric.Common.remove_widening_thresholdsvinSomectx|_->Noneleton_before_evalroutesemanticexpmanflow=Noneleton_after_evalroutesemanticexpmanfloweval=Noneleton_finishmanflow=()endlet()=register_hook(moduleHook)