123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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 licenses/LGPLv2.1). *)(* *)(**************************************************************************)(** The aim here is to select the statements where a data D
* has the same value then a given starting program point L. *)openCil_typesletname="scope"moduleR=Plugin.Register(structletname=nameletshortname=namelethelp="data dependencies higher level functions"end)letcat_rm_asserts=R.register_category"rm_asserts"let()=R.add_debug_keyscat_rm_asserts(** {2 Computing a mapping between zones and modifying statements}
We first go through all the function statements in other to build
a mapping between each zone and the statements that are modifying it.
**)(** Statement identifier *)moduleStmtDefault=structincludeCil_datatype.Stmtletids=s.sidend(** set of values to store for each data *)moduleStmtSetLattice=structincludeAbstract_interp.Make_Hashconsed_Lattice_Set(StmtDefault)(Cil_datatype.Stmt.Hptset)letdefault:t=emptyletsingles=inject_singletonsend(** A place to map each data to the state of statements that modify it. *)moduleInitSid=structmoduleLM=Lmap_bitwise.Make_bitwise(StmtSetLattice)(* Clear the (non-project compliant) internal caches each time the ast
changes, which includes every time we switch project. *)let()=Ast.add_hook_on_updateLM.clear_cachesletempty=LM.emptyletfind=LM.findletadd_zonelmapzonesid=letnew_val=StmtSetLattice.singlesidinLM.add_binding~exact:falselmapzonenew_valletprettyfmtlmap=Format.fprintffmt"Lmap = %a@\n"LM.prettylmapendletget_writesstmtlval=Eva.Results.(beforestmt|>eval_address~for_writing:truelval|>as_zone)(** Add to [stmt] to [lmap] for all the locations modified by the statement.
* Something to do only for calls and assignments.
* *)letregister_modified_zoneslmapstmt=letregisterlmapzone=InitSid.add_zonelmapzonestmtinletaux_outoutkf=letinout=!Db.Operational_inputs.get_internal_precise~stmtkfinLocations.Zone.joinoutinout.Inout_type.over_outputsinmatchstmt.skindwith|Instr(Set(lval,_,_))->letzone=get_writesstmtlvalinregisterlmapzone|Instr(Local_init(v,i,_))->letzone=get_writesstmt(Cil.varv)inletlmap_init=registerlmapzonein(matchiwith|AssignInit_->lmap_init|ConsInit(f,_,_)->letkf=Globals.Functions.getfinletout=aux_outLocations.Zone.bottomkfinregisterlmap_initout)|Instr(Call(dst,funcexp,_args,_))->beginletlmap=matchdstwith|None->lmap|Somelval->letzone=get_writesstmtlvalinregisterlmapzoneinletkfs=Eva.Results.(beforestmt|>eval_calleefuncexp|>default[])inletout=List.fold_leftaux_outLocations.Zone.bottomkfsinregisterlmapoutend|_->lmap(** compute the mapping for the function
* @raise Kernel_function.No_Definition if [kf] has no definition
*)letcomputekf=R.debug~level:1"computing for function %a"Kernel_function.prettykf;letf=Kernel_function.get_definitionkfinletdo_stmtlmaps=Cil.CurrentLoc.set(Cil_datatype.Stmt.locs);ifEva.Results.is_reachablesthenregister_modified_zoneslmapselselmapinletf_datas=List.fold_leftdo_stmtInitSid.emptyf.sallstmtsinR.debug~level:2"data init stmts : %a"InitSid.prettyf_datas;f.sallstmts,f_datas(* TODO : store it ! *)(** {2 Computing Scopes} *)moduleState=struct(* The algorithm starts by defining the "modified" function, that
tells for each statement if it changes the lvalue under
consideration. We want to add a "temporal" information on top of
modified, i.e. we want to know for each statement s', whether for
each path from the starting statement s to s', the lvalue has
been modified. To make this computable, we overapproximate, and
the dataflow computes if the statement may have been modified
(Modif) or has not been modified in any case (SameVal).
The simple boolean lattice with Modif and SameVal does not
suffice: if we initialized the dataflow with "SameVal" for all
statements, "join_and_is_included" would return true and the
dataflow could stop before having visited all statements. This
explains why a value of Bottom is needed, to distinguish
statements not yet visited (or unreachable) from the others.
Now another problem in the dataflow is the representation of
loop. In a program such has:
while(1) {
s1;
s2;
s3;
s4;
}
Where "modified" is false except for s4. We start the forward
dataflow on s2. We would compute that s2 is not modified, then s3
is not modified, then s4 is modified, then s1 is modified... but
then we would compute that s3 and s4 are modified (and indeed,
they are in further iterations of the loop). To cope with this
problem, s2 is initialized to the Start state. The Start state is
not propagated (transfer Start = SameVal), and cannot be removed
from s2 (Start = Top). Thus the Hasse diagram of the lattice is simply:
: Start = Top
: |
: Modif
: |
: SameVal
: |
: NotSeen = Bottom
*)typet=Start|NotSeen|Modif|SameValletprettyfmtb=Format.fprintffmt"%s"(matchbwith|Start->"Start"|NotSeen->"NotSeen"|Modif->"Modif"|SameVal->"SameVal")letbottom=NotSeen(* Just compute the "max" between elements of the lattice. *)letmergeb1b2=letb=matchb1,b2with|Start,_|_,Start->Start|NotSeen,b|b,NotSeen->b|Modif,_|_,Modif->Modif|SameVal,SameVal->SameValinbletjoin=merge;;letequal(b1:t)(b2:t)=(b1=b2)letjoin_and_is_includedab=letj=joinabin(j,equaljb)letis_includedab=snd(join_and_is_includedab)(* Note: the transfer function "if m = Start then SameVal else if
modif then Modif else m" suits better visualisation by scope,
since it does not consider the "current statement" as
"modifying". But this gives incorrect results for
remove-redundant-alarms. *)lettransfermodifm=ifmodifthenModifelseifm=StartthenSameValelsemendmoduleBackwardScope(X:sigvalmodified:stmt->boolend)=structlettransfer_stmtstmtstate=matchstmt.skindwith|Instr_->State.transfer(X.modifiedstmt)state|_->stateincludeStateendletbackward_data_scopemodif_stmtsskf=letmodifieds=StmtSetLattice.memsmodif_stmtsinletmoduleFenv=(valDataflows.function_envkf:Dataflows.FUNCTION_ENV)inletmoduleArg=structincludeBackwardScope(structletmodified=modifiedend)letinit=[(s,State.Start)];;endinletmoduleCompute=Dataflows.Simple_backward(Fenv)(Arg)inCompute.pre_state;;moduleForwardScope(X:sig(* Effects of the statement itself *)valmodified:stmt->bool(* Effects of scope change *)valmodified_by_edge:stmt->stmt->boolend)=structincludeState;;lettransfer_stmtsstate=letmap_on_all_succsnew_state=letdo_succs'=(s',State.transfer(X.modified_by_edgess')new_state)inList.mapdo_succs.succsinmatchs.skindwith|Instr_->map_on_all_succs(State.transfer(X.modifieds)state)|If_|Switch_->map_on_all_succs(State.transferfalsestate)|Return_|Throw_->[]|UnspecifiedSequence_|Loop_|Block_|Goto_|Break_|Continue_|TryExcept_|TryFinally_|TryCatch_->map_on_all_succsstate;;endletforward_data_scopemodif_stmtsmodif_edgeskf=letmodifieds=StmtSetLattice.memsmodif_stmtsinletmoduleFenv=(valDataflows.function_envkf:Dataflows.FUNCTION_ENV)inletmoduleArg=structincludeForwardScope(structletmodified=modifiedletmodified_by_edge=modif_edgeend)letinit=[(s,State.Start)];;endinletmoduleCompute=Dataflows.Simple_forward(Fenv)(Arg)inCompute.pre_state,Compute.post_state;;(* Add only 'simple' statements. *)letadd_ssacc=matchs.skindwith|Instr_|Return_|Continue_|Break_|Goto_|Throw_->Cil_datatype.Stmt.Hptset.addsacc|Block_|Switch_|If_|UnspecifiedSequence_|Loop_|TryExcept_|TryFinally_|TryCatch_->acc(** Do backward and then forward propagations and compute the 3 statement sets :
* - forward only,
* - forward and backward,
* - backward only.
*)letfind_scopeallstmtsmodif_stmtsmodif_edgeskf=(* Add only statements for which the lvalue certainly did not change. *)letaddget_stateaccs=matchget_stateswith|State.Start|State.SameVal->add_ssacc|_->accinlet_,fw_post=forward_data_scopemodif_stmtsmodif_edgeskfinletfw=List.fold_left(addfw_post)Cil_datatype.Stmt.Hptset.emptyallstmtsinletbw_pre=backward_data_scopemodif_stmtsskfinletbw=List.fold_left(addbw_pre)Cil_datatype.Stmt.Hptset.emptyallstmtsinletfb=Cil_datatype.Stmt.Hptset.interbwfwinletfw=Cil_datatype.Stmt.Hptset.difffwfbinletbw=Cil_datatype.Stmt.Hptset.diffbwfbinfw,fb,bw(* Computes the memory zones that points to a base in [escaping] in a state. *)letgather_escaping_zonesescaping=function|Cvalue.Model.Top->Locations.Zone.top|Cvalue.Model.Bottom->Locations.Zone.bottom|Cvalue.Model.Mapm->letauxbaseoffsmzone=lettestb=Base.Hptset.membescapinginletgather(_,_asitv)(v,_,_)acc=letv=Cvalue.V_Or_Uninitialized.get_vvinifCvalue.V.contains_addresses_of_localstestvthenletz=Locations.Zone.injectbase(Int_Intervals.inject_itvitv)inLocations.Zone.joinacczelseaccinCvalue.V_Offsetmap.foldgatheroffsmzoneinCvalue.Model.foldauxmLocations.Zone.bottom(* compute the memory zones that are changed into ESCAPING ADDRESS
when taking the cfg edge s1->s2 *)letcompute_escaping_zoness1s2=letclosed_blocks=Kernel_function.blocks_closed_by_edges1s2inletlocals=List.flatten(List.map(funb->b.blocals)closed_blocks)inletfilteraccv=ifv.vtemp||notv.vreferencedthenaccelseBase.Hptset.add(Base.of_varinfov)accinletbases=List.fold_leftfilterBase.Hptset.emptylocalsinifBase.Hptset.is_emptybasesthenLocations.Zone.bottomelseletcvalue_state=Eva.Results.(befores1|>get_cvalue_model)ingather_escaping_zonesbasescvalue_state(* type pair_stmts = stmt * stmt *)modulePairStmts=Datatype.Pair_with_collections(Cil_datatype.Stmt)(Cil_datatype.Stmt)(structletmodule_name="Scope.Datascope.PairStmts"end)(* Hashtbl from pairs of stmts to zone. Used as maps from Cfg edges to the
memory zones that are 'modified' by thescope change. *)moduleHashPairStmtsZone=PairStmts.Hashtbl.Make(Locations.Zone)typemodified_by_edge=HashPairStmtsZone.t(* compute the {!modified_by_edge} hashtbl for the fundec [fdec] *)letcompute_modif_edgefdec:modified_by_edge=letmodifs_edge=PairStmts.Hashtbl.create17inletdo_stmtstmt=letdo_succstmt'=letz=compute_escaping_zonesstmtstmt'inPairStmts.Hashtbl.addmodifs_edge(stmt,stmt')zinList.iterdo_succstmt.succsinList.iterdo_stmtfdec.sallstmts;modifs_edgemoduleModifEdge=Cil_state_builder.Kernel_function_hashtbl(HashPairStmtsZone)(structletname="Scope.Datatscope.ModifsEdge"letdependencies=[Eva.Analysis.self]letsize=16end)letmodified_by_edge_kf=ModifEdge.memo(funkf->compute_modif_edge(Kernel_function.get_definitionkf))(* Does the Cfg edge [s1->s2] has an effect on [z]? *)letis_modified_by_edgekfzs1s2=letmodifs_edge=modified_by_edge_kfkfinLocations.Zone.intersectsz(PairStmts.Hashtbl.findmodifs_edge(s1,s2))(** Try to find the statement set where [data] has the same value than
* before [stmt].
* @raise Kernel_function.No_Definition if [kf] has no definition
*)letget_data_scope_at_stmtkfstmtlval=letzone=Eva.Results.(beforestmt|>lval_depslval)inletallstmts,info=computekfinletmodif_stmts=InitSid.findinfozoneinletmodifs_edge=is_modified_by_edgekfzoneinlet(f_scope,fb_scope,b_scope)=find_scopeallstmtsmodif_stmtsmodifs_edgestmtkfinR.debug"@[<hv 4>get_data_scope_at_stmt %a at %d @\n\
modified by = %a@\n\
f = %a@\nfb = %a@\nb = %a@]"(* stmt at *)Locations.Zone.prettyzonestmt.sid(* modified by *)(Pretty_utils.pp_iterStmtSetLattice.iter~sep:",@ "Cil_datatype.Stmt.pretty_sid)modif_stmts(* scope *)Cil_datatype.Stmt.Hptset.prettyf_scopeCil_datatype.Stmt.Hptset.prettyfb_scopeCil_datatype.Stmt.Hptset.prettyb_scope;(f_scope,(fb_scope,b_scope))exceptionToDoletget_annot_zonekfstmtannot=letadd_zonezinfo=lets=info.Logic_deps.kiinletbefore=info.Logic_deps.beforeinletzone=info.Logic_deps.zoneinR.debug~level:2"[forward_prop_scope] need %a %s stmt %d@."Locations.Zone.prettyzone(ifbeforethen"before"else"after")s.sid;ifbefore&&stmt.sid=s.sidthenLocations.Zone.joinzonezelse(* TODO *)raiseToDoinlet(info,_),_=Logic_deps.from_stmt_annotannot(stmt,kf)inmatchinfowith|None->raiseToDo|Someinfo->letzone=List.fold_leftadd_zoneLocations.Zone.bottominfoinR.debug"[get_annot_zone] need %a"Locations.Zone.prettyzone;zonemoduleCA_Map=Cil_datatype.Code_annotation.Maptypeproven=(stmt*code_annotation*stmt)CA_Map.t(** Type of the properties proven so far. A binding
[ca -> (stmt_ca, ca_because, stmt_because)] must be read as "[ca] at
statement [stmt_ca] is a logical consequence of [ca_because] at statement
[stmt_because]".
Currently, [ca] and [ca_because] are always exactly the same ACSL assertion,
although this may be extended in the future. *)(** Assertions proven so far, as a list *)letlist_proven(m:proven)=CA_Map.fold(funca_acc->ca::acc)m[](** [add_proven_annot proven because] add the fact that [proven] is proven
thanks to [because]. This function also returns a boolean indicating
that [proven] was not already proven. *)letadd_proven_annot(ca,stmt_ca)(ca_because,stmt_because)acc=ifCA_Map.memcaaccthen(* already proven *)acc,falseelseCA_Map.addca(stmt_ca,ca_because,stmt_because)acc,true(** Check if an assertion at [stmt] is identical to [ca] (itself emitted
at [stmt_ca]). Add them to acc if any *)letcheck_stmt_annots(ca,stmt_ca)stmtacc=letcheck_annotacc=matchca.annot_content,annot.annot_contentwith|AAssert(_,p'),AAssert(_,p)whenp'.tp_kind<>Check&&p.tp_kind<>Admit->letp=p.tp_statement.pred_contentinletp'=p'.tp_statement.pred_contentinifLogic_utils.is_same_predicate_nodepp'thenletacc,added=add_proven_annot(annot,stmt)(ca,stmt_ca)accinifaddedthenR.debug"annot at stmt %d could be removed: %a"stmt.sidPrinter.pp_code_annotationannot;accelseacc|_->accinAnnotations.fold_code_annotcheckstmtaccexceptionVolatileFound(* This visitor detects the presence of a volatile logic l-value. Such a
l-value may evaluate differently at different program point. *)classcontainsVolatile=objectinheritVisitor.frama_c_inplacemethod!vtermt=matcht.term_nodewith|TLvaltlv->ifCil.isVolatileTermLvaltlvthenraiseVolatileFound;Cil.DoChildren|_->Cil.DoChildrenendletcode_annot_is_volatileca=letvis=newcontainsVolatileintryignore(Visitor.visitFramacCodeAnnotationvisca);falsewithVolatileFound->true(** Return the set of stmts ([scope]) where [annot] has the same value
as at [stmt], and adds to [proven] the annotations that are identical to
[annot] at statements that are both in [scope] and dominated by [stmt].
[stmt] is not added to the set, and [annot] is not added to [proven]. *)letget_prop_scope_at_stmt~warnkfstmt?(proven=CA_Map.empty)annot=R.debug"[get_prop_scope_at_stmt] at stmt %d in %a : %a"stmt.sidKernel_function.prettykfPrinter.pp_code_annotationannot;letacc=(Cil_datatype.Stmt.Hptset.empty,proven)inifcode_annot_is_volatileannotthenaccelsetryletzone=get_annot_zonekfstmtannotinletallstmts,info=computekfinletmodif_stmts=InitSid.findinfozoneinletmodifs_edge=is_modified_by_edgekfzoneinletpre_state,_=forward_data_scopemodif_stmtsmodifs_edgestmtkfinbeginmatchannot.annot_contentwith|AAssert_->()|_->R.abort"only 'assert' are handled by get_prop_scope_at_stmt"end;letadd((acc_scope,acc_to_be_rm)asacc)s=matchpre_stateswith|State.SameVal->ifDominators.dominatesstmts&¬(Cil_datatype.Stmt.equalstmts)thenletacc_scope=add_ssacc_scopeinletacc_to_be_rm=check_stmt_annots(annot,stmt)sacc_to_be_rmin(acc_scope,acc_to_be_rm)elseacc|_->accinList.fold_leftaddaccallstmtswithToDo->ifwarnthenR.warning~current:true~once:true"[get_annot_zone] don't know how to compute zone: skip this annotation";acc(** Collect the annotations that can be removed because they are redundant. *)classcheck_annot_visitor=object(self)inheritVisitor.frama_c_inplacevalmutableproven=CA_Map.emptymethodproven()=provenmethod!vcode_annotannot=letkf=Option.getself#current_kfinletstmt=Visitor_behavior.Get_orig.stmtself#behavior(Option.getself#current_stmt)inbeginmatchannot.annot_contentwith|AAssert_->R.debug~level:2"[check] annot %d at stmt %d in %a : %a@."annot.annot_idstmt.sidKernel_function.prettykfPrinter.pp_code_annotationannot;let_scope,proven'=get_prop_scope_at_stmt~warn:falsekfstmt~provenannotinproven<-proven'|_->()end;Cil.SkipChildrenmethod!vglob_auxg=matchgwith|GFun_whenEva.Results.are_available(Option.getself#current_kf)->Cil.DoChildren|_->Cil.SkipChildrenmethod!vexpr_=Cil.SkipChildrenend(* class check_annot_visitor *)letredundant_assertions()=letvisitor=newcheck_annot_visitorinignore(Visitor.visitFramacFile(visitor:>Visitor.frama_c_visitor)(Ast.get()));visitor#proven()letcheck_asserts()=R.feedback"check if there are some redundant assertions...";letto_be_removed=redundant_assertions()inletn=CA_Map.cardinalto_be_removedinR.result"[check_asserts] %d assertion(s) could be removed@."n;(list_provento_be_removed)(* erasing optional arguments, plus return a list*)letget_prop_scope_at_stmtkfstmtannot=lets,m=get_prop_scope_at_stmt~warn:truekfstmtannotins,list_provenm(* Currently lazy, because we need to define it after Value has been registered
in Db *)letemitter=lazy(letconv=List.mapTyped_parameter.getinletcorrectness=conv(Emitter.correctness_parametersEva.Analysis.emitter)inlettuning=conv(Emitter.tuning_parametersEva.Analysis.emitter)inEmitter.create"RedundantAlarms"[Emitter.Property_status]~correctness~tuning)(** Mark as proved the annotations collected by [check_asserts]. *)letrm_asserts()=letto_be_removed=redundant_assertions()inletn=CA_Map.cardinalto_be_removedinifn>0thenbeginR.feedback~dkey:cat_rm_asserts"removing %d assertion(s)@."n;letauxca(stmt_ca,ca_because,stmt_because)=letloc=Cil_datatype.Stmt.locstmt_cainR.result~source:(fstloc)~dkey:cat_rm_asserts~level:2"@[removing redundant@ %a@]"Printer.pp_code_annotationca;letkf=Kernel_function.find_englobing_kfstmt_cainletip_ca=Property.ip_of_code_annot_singlekfstmt_cacainletip_because=Property.ip_of_code_annot_singlekfstmt_becauseca_becauseinlete=Lazy.forceemitterinProperty_status.emite~hyps:[ip_because]ip_caProperty_status.TrueinCA_Map.iterauxto_be_removedendletrm_asserts=Dynamic.register~comment:"Remove redundant alarms. Used by the Eva plugin."~plugin:name"rm_asserts"Datatype.(funcunitunit)rm_asserts(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)