Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file loops.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Loops iterator with widening *)openMopsaopenSig.Abstraction.StatelessopenAstletname="universal.iterators.loops"(*==========================================================================*)(** {2 Loops flow token} *)(*==========================================================================*)typetoken+=|T_break(** Control flows reaching a break statement *)|T_continue(** Control flows reaching a continue statement *)let()=register_token{compare=(funnexttk1tk2->nexttk1tk2);print=(funnextfmttk->matchtkwith|T_break->Format.pp_print_stringfmt"break"|T_continue->Format.pp_print_stringfmt"cont"|_->nextfmttk);}(*==========================================================================*)(** {2 Unrolling strategy} *)(*==========================================================================*)typeunrolling={mutableunroll_global_nb:intoption(* unrolling numbers *);mutableunroll_locals:local_unrollinglist;}andlocal_unrolling={unroll_local_file:stringoption;unroll_local_line:int;unroll_local_nb:intoption;}letopt_unrolling={unroll_global_nb=Some1;unroll_locals=[];}(** Parse local unrolling specification string *)letparse_unroll_local(spec:string):local_unrolling=ifnotStr.(string_match(regexp"^\\(\\([a-zA-Z][^:]*\\):\\)?\\([0-9]+\\):\\([0-9]+\\)$")spec0)thenpanic"incorrect argument '%s' for option -loop-unrolling-at"spec;letfile=trySome(Str.matched_group2spec)withNot_found->Noneinletline=Str.matched_group3spec|>int_of_stringinletnb=Some(Str.matched_group4spec|>int_of_string)in{unroll_local_file=file;unroll_local_line=line;unroll_local_nb=nb;}(** Parse local full unrolling specification string *)letparse_full_unroll_local(spec:string):local_unrolling=ifnotStr.(string_match(regexp"^\\(\\([a-zA-Z][^:]*\\):\\)?\\([0-9]+\\)$")spec0)thenpanic"incorrect argument '%s' for option -loop-full-unrolling-at"spec;letfile=trySome(Str.matched_group2spec)withNot_found->Noneinletline=Str.matched_group3spec|>int_of_stringin{unroll_local_file=file;unroll_local_line=line;unroll_local_nb=None;}(** Get the unrolling limit for a given loop location *)letget_range_unrollingrange:intoption=letrange=untag_rangerangeinletis_matchingu=match_range_lineu.unroll_local_linerange&&matchu.unroll_local_filewith|None->true|Somefile->match_range_filefilerangeinmatchList.find_optis_matchingopt_unrolling.unroll_localswith|Someu->u.unroll_local_nb|None->opt_unrolling.unroll_global_nb(*==========================================================================*)(** {2 Command line options} *)(*==========================================================================*)letopt_loop_widening_delay:intref=ref0(** Number of iterations before applying a widening. *)letopt_loop_use_cache:boolref=reffalseletopt_loop_decreasing_it:boolref=reffalselet()=register_domain_optionname{key="-widening-delay";category="Loops";doc=" number of iterations before applying a widening";spec=Set_int(opt_loop_widening_delay,ArgExt.empty);default="0";};register_domain_optionname{key="-loop-unrolling";category="Loops";doc=" number of unrolling iterations before joining the environments";spec=Int((funn->opt_unrolling.unroll_global_nb<-Somen),ArgExt.empty);default="1";};register_domain_optionname{key="-loop-unrolling-at";category="Loops";doc=" number of unrolling iterations at specific program location (syntax: [file.]line:unrolling)";spec=String(ArgExt.string_list_lifter(funspecs->opt_unrolling.unroll_locals<-List.mapparse_unroll_localspecs),ArgExt.empty);default="";};register_domain_optionname{key="-loop-full-unrolling";category="Loops";doc=" unroll loops without applying widening";spec=Bool(funb->opt_unrolling.unroll_global_nb<-(ifbthenNoneelseopt_unrolling.unroll_global_nb));default="false";};register_domain_optionname{key="-loop-full-unrolling-at";category="Loops";doc=" fully unroll loop at specific program location (syntax: [file.]line)";spec=String(ArgExt.string_list_lifter(funspecs->opt_unrolling.unroll_locals<-List.mapparse_full_unroll_localspecs),ArgExt.empty);default="";};register_domain_optionname{key="-loop-no-cache";category="Loops";doc=" do not use cache for loops";spec=Clearopt_loop_use_cache;default="use cache";};register_domain_optionname{key="-loop-decr-it";category="Loops";doc=" enable decreasing iteration";spec=Setopt_loop_decreasing_it;default="disabled";}(*==========================================================================*)(** {2 Domain} *)(*==========================================================================*)letnestedness=ref0moduleDomain=struct(** {3 Domain header} *)(** ***************** *)includeGenStatelessDomainId(structletname=nameend)letchecks=[](** {3 Cache of last fixpoint} *)(** ************************** *)(** Fixpoints are attached to the callstack and the location of the loop head *)moduleLoopHeadMap=MapExt.Make(structtypet=(callstack*range)letcompare(cs1,r1)(cs2,r2)=Compare.compose[(fun()->compare_ranger1r2);(fun()->compare_callstackcs1cs2);]letprintfmt(cs,r)=Format.fprintffmt"[%a, %a]"pp_rangerpp_callstackcsend)(** Cache of the last fixpoints at loop heads *)moduleLastFixpointCtx=GenContextKey(structtype'at='aflowLoopHeadMap.tletprintlfmtctx=Format.fprintffmt"Lfp cache context: %a"(LoopHeadMap.fprintMapExt.printer_default(funfmt(cs,r)->pp_callstackfmtcs;pp_rangefmtr)(funfmtflow->format(TokenMap.printl)fmt(Flow.get_token_mapflow)))ctxend)(** Search the last fixpoint attached to a loop *)letsearch_last_fixpoint(srange,scs)manflow=tryletm=find_ctxLastFixpointCtx.key(Flow.get_ctxflow)inletmf=LoopHeadMap.filter(fun(cs,range)_->compare_callstackcsscs=0&&compare_rangesrangerange=0)minSome(LoopHeadMap.choosemf|>snd|>Flow.joinman.latticeflow)withNot_found->(debug"no ctx found";None)(** Update the last fixpoint attached to a loop *)letstore_fixpointmanflow(range,cs)=letold_lfp_ctx=tryletr=find_ctxLastFixpointCtx.key(Flow.get_ctxflow)inifLoopHeadMap.cardinalr>5thenLoopHeadMap.remove_min_bindingrelserwithNot_found->LoopHeadMap.emptyinletstripped_flow=Flow.bottom(Flow.get_ctxflow)(Flow.get_reportflow)|>Flow.addT_cur(Flow.getT_curman.latticeflow)man.lattice|>Flow.addT_continue(Flow.getT_continueman.latticeflow)man.lattice|>Flow.addT_break(Flow.getT_breakman.latticeflow)man.latticeinDebug.debug~channel:(name^".cache")"@(%a, %a): adding %a"pp_rangerangepp_callstackcs(format(Flow.printman.lattice.print))stripped_flow;letlfp_ctx=LoopHeadMap.add(cs,range)stripped_flowold_lfp_ctxinFlow.set_ctx(add_ctxLastFixpointCtx.keylfp_ctx(Flow.get_ctxflow))flowletjoin_w_old_lfpmanflowrange=debug"searching in cache";matchsearch_last_fixpointrangemanflowwith|None->None|Someold_lfp->letres=Flow.joinman.latticeold_lfpflowinDebug.debug~channel:"universal.iterators.loops.cache""cache: %a join %a = %a@\n"(format(Flow.printman.lattice.print))old_lfp(format(Flow.printman.lattice.print))flow(format(Flow.printman.lattice.print))res;Someres(* flow *)(** Merge tokens T_cur and T_continue into T_cur *)letmerge_cur_and_continuemanflow=letcur=Flow.getT_curman.latticeflowinletcont=Flow.getT_continueman.latticeflowinletctx=Flow.get_ctxflowinFlow.setT_cur(man.lattice.joinctxcurcont)man.latticeflow|>Flow.removeT_continueletinitprogmanflow=Flow.map_ctx(add_ctxLastFixpointCtx.keyLoopHeadMap.empty)flow|>Post.return|>Option.someletdecr_iterationcondbodymanflow_initflow=Flow.removeT_continueflow|>Flow.removeT_break|>Flow.remove_report|>man.exec(mk_assumecondcond.erange)>>%man.execbody>>%funflow->merge_cur_and_continuemanflow|>Flow.joinman.latticeflow_init|>Post.returnletreclfpcountdelaycondbodymanflow_initflow=debug"lfp called, range = %a, count = %d"pp_rangebody.srangecount;(* Ignore continue and break flows of the previous iterations *)Flow.removeT_continueflow|>Flow.removeT_break|>(* Ignore report of the previous iterations *)Flow.set_report(Flow.get_reportflow_init)|>(* Apply condition and execute body *)man.exec(mk_assumecondcond.erange)>>%man.execbody>>%funf->(* Merge cur and continue flows *)letflow'=merge_cur_and_continuemanf|>Flow.joinman.latticeflow_initin(* Check convergence of cur flow *)letcur=Flow.getT_curman.latticeflowinletcur'=Flow.getT_curman.latticeflow'inletis_sub=man.lattice.subset(Flow.get_ctxflow')cur'curindebug"lfp range %a is_sub: %b"pp_rangebody.srangeis_sub;ifis_subthen(* Convergence *)Post.returnflow'elseifdelay=0then(* Widen *)letwflow=Flow.widenman.latticeflowflow'inlfp(count+1)!opt_loop_widening_delaycondbodymanflow_initwflowelse(* Delay *)lfp(count+1)(delay-1)condbodymanflow_initflow'letrecunrollicondbodymanflow=debug"unrolling iteration %a in %a"(OptionExt.print~none:""~some:""Format.pp_print_int)ipp_range(srangebody);ifi=Some0thenCases.singleton(false,Flow.bottom(Flow.get_ctxflow)(Flow.get_reportflow))flowelseletpost1=man.exec{skind=S_assumecond;srange=cond.erange}flow>>%man.execbody>>%funf->merge_cur_and_continuemanf|>Post.returninletpost2=man.exec(mk_assume(mk_notcondcond.erange)cond.erange)(Flow.set_ctx(Cases.get_ctxpost1)flow)inpost1>>%funflow1->post2>>%funflow2->ifFlow.subsetman.latticeflow1flowthenlet()=debug"stabilisation reached in unrolling!"inCases.singleton(true,flow2)flow1elseunroll(OptionExt.lift(funii->(ii-1))i)condbodyman(Flow.copy_ctxflow2flow1)>>$fun(flag,flow2')flow1'->Cases.singleton(flag,Flow.joinman.latticeflow2flow2')flow1'letrecexecstmtmanflow=matchskindstmtwith|S_while(cond,body)->incrnestedness;Debug.debug~channel:"nested""nestedness: %d"!nestedness;debug"while %a:"(* @\nflow = @[%a@] *)pp_rangestmt.srange(* (Flow.print man.lattice) flow *);letflow0=Flow.removeT_continueflow|>Flow.removeT_breakinbeginif!opt_loop_use_cachethenmatchjoin_w_old_lfpmanflow0(stmt.srange,Flow.get_callstackflow0)with|Someflow0->Cases.singleton(false,Flow.bottom(Flow.get_ctxflow0)(Flow.get_reportflow0))flow0|None->unroll(get_range_unrollingstmt.srange)condbodymanflow0elseunroll(get_range_unrollingstmt.srange)condbodymanflow0end>>$?fun(is_fp,flow_out)flow_init->beginifis_fpthenPost.returnflow_initelselfp0!opt_loop_widening_delaycondbodymanflow_initflow_init>>%funflow_lfp->beginif!opt_loop_decreasing_itthendecr_iterationcondbodymanflow_initflow_lfpelsePost.returnflow_lfpend>>%funflow_lfp->letflow_lfp=if!opt_loop_use_cachethenstore_fixpointmanflow_lfp(stmt.srange,Flow.get_callstackflow_lfp)elseflow_lfpinPost.returnflow_lfpend>>%?funflow_lfp->man.exec(mk_assume(mk_notcondcond.erange)cond.erange)flow_lfp>>%?funf->letres0=Flow.joinman.latticeflow_outfindebug"while post abs %a:"(* @\nres0 = @[%a@] *)pp_rangestmt.srange(* (Flow.print man.lattice) res0 *);letres1=Flow.addT_cur(Flow.getT_breakman.latticeres0)man.latticeres0|>Flow.setT_break(Flow.getT_breakman.latticeflow)man.lattice|>Flow.setT_continue(Flow.getT_continueman.latticeflow)man.latticeindecrnestedness;Some(Post.returnres1)|S_break->letcur=Flow.getT_curman.latticeflowinletflow'=Flow.addT_breakcurman.latticeflow|>Flow.removeT_curinSome(Post.returnflow')|S_continue->letcur=Flow.getT_curman.latticeflowinletflow'=Flow.addT_continuecurman.latticeflow|>Flow.removeT_curinSome(Post.returnflow')|_->Noneleteval___=Noneletask___=Noneletprint_exprmanflowprinterexp=()end(*==========================================================================*)(** {2 Setup} *)(*==========================================================================*)let()=register_stateless_domain(moduleDomain)