123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)openPdg_typesmoduleR=Datascope.Rletdebug1fmt=R.debug~level:1fmtletdebug2fmt=R.debug~level:2fmtopenCil_datatypeopenCil_typestypet_zones=Locations.Zone.tStmt.Hashtbl.tmoduleData=structtypet=Locations.Zone.tletbottom=Locations.Zone.bottomletequal=Locations.Zone.equalletintersects=Locations.Zone.valid_intersectsletmerge=Locations.Zone.join(* over-approx *)letdiff=Locations.Zone.diff(* over-approx *)letprettyfmtz=Format.fprintffmt"@[<h 1>%a@]"Locations.Zone.prettyzletexp_zonestmtexp=Eva.Results.(beforestmt|>expr_depsexp)endmoduleCtx=structtypet=Data.tStmt.Hashtbl.tletcreate=Stmt.Hashtbl.createletfind=Stmt.Hashtbl.findletaddctxkd=letd=tryletold_d=findctxkinData.mergeold_ddwithNot_found->dinStmt.Hashtbl.replacectxkd(* let mem = Stmt.Hashtbl.mem : useless because Ctx has to be initialized to bot *)let_prettyfmtinfos=Stmt.Hashtbl.iter(funkd->Format.fprintffmt"Stmt:%d -> %a@\n"k.sidData.prettyd)infosendletcompute_new_dataold_zonel_zonel_dpdsexactr_dpds=if(Data.intersectsold_zonel_zone)thenletzone=ifexactthenData.diffold_zonel_zoneelseold_zoneinletzone=Data.mergezonel_dpdsinletzone=Data.mergezoner_dpdsin(true,zone)else(false,old_zone)letget_lval_zones~for_writingstmtlval=letrequest=Eva.Results.beforestmtinletaddress=Eva.Results.eval_address~for_writinglvalrequestinletzone=Eva.Results.as_zoneaddressinletexact=Eva.Results.is_singletonaddressinletdpds=Eva.Results.address_depslvalrequestindpds,exact,zone(* the call result can be processed like a normal assignment *)letprocess_call_resdatastmtlvaloptionfroms=letdata=matchlvaloptionwith|None->false,data|Somelval->letret_dpds=froms.Function_Froms.deps_returninletr_dpds=Function_Froms.Memory.collapse_returnret_dpdsinletr_dpds=Function_Froms.Deps.to_zoner_dpdsinletl_dpds,exact,l_zone=get_lval_zones~for_writing:truestmtlvalincompute_new_datadatal_zonel_dpdsexactr_dpdsindata(* we need [data_after] zone after the call, so we need to add the dpds
* of each output that intersects this zone.
* Moreover, we need to add the part of [data_after] that has not been
* modified for sure. *)letprocess_fromsdata_afterfroms=letfrom_table=froms.Function_Froms.deps_tableinletprocess_out_calloutdeps(to_prop,used,new_data)=letout_dpds=Function_Froms.DepsOrUnassigned.to_zonedepsinletdefault=Function_Froms.DepsOrUnassigned.may_be_unassigneddepsinletexact=notdefaultin(* be careful to compare out with data_after and not new_data *)if(Data.intersectsdata_afterout)thenletto_prop=ifexactthenData.diffto_propoutelseto_propinletnew_data=Data.mergenew_dataout_dpdsin(to_prop,true,new_data)else(to_prop,used,new_data)inletto_prop=(* part of data_after that we need to compute before call :
* = data_after minus all exact outputs.
* Don't use [data_after - (merge out)] to avoid approximation in merge *)data_afterinletnew_data=Data.bottomin(* add out_dpds when out intersects data_after*)letused=falsein(* is the call needed ? *)letto_prop,used,new_data=matchfrom_tablewith|Function_Froms.Memory.Bottom->to_prop,used,new_data|Function_Froms.Memory.Top->letv=Function_Froms.DepsOrUnassigned.topinprocess_out_callLocations.Zone.topv(to_prop,used,new_data)|Function_Froms.Memory.Mapm->Function_Froms.Memory.foldprocess_out_callm(to_prop,used,new_data)inletdata=Data.mergeto_propnew_datain(used,data)letprocess_call_argsdatacalled_kfstmtargs=letparam_list=Kernel_function.get_formalscalled_kfinletasgn_arg_to_paramdataparamarg=letparam_zone=Locations.zone_of_varinfoparaminletarg_dpds=Data.exp_zonestmtarginletexact=truein(* param is always a variable so asgn is exact *)let_used,data=compute_new_datadataparam_zoneData.bottomexactarg_dpdsin(* can ignore 'used' because if we need param, we already know that the
* call is needed *)datainletrecdo_param_argdataparam_listargs=matchparam_list,argswith|[],[]->data|p::param_list,a::args->letdata=asgn_arg_to_paramdatapaindo_param_argdataparam_listargs|[],_->(* call to a variadic function *)(* warning already sent during 'from' computation. *)(* TODO : merge the remaining args in data ?... *)data|_,[]->R.abort"call to a function with to few arguments"indo_param_argdataparam_listargsletprocess_one_calldatastmtlvaloptionfroms=letres_used,data=process_call_resdatastmtlvaloptionfromsinletout_used,data=process_fromsdatafromsinletused=res_used||out_usedinused,dataletprocess_calldata_afterstmtlvaloptionfuncexpargs_loc=letfuncexp_dpds=Eva.Results.(beforestmt|>expr_depsfuncexp)andcalled_functions=Eva.Results.(beforestmt|>eval_calleefuncexp|>default[])inletused,data=tryletfroms=From.Callwise.find(Kstmtstmt)inprocess_one_calldata_afterstmtlvaloptionfromswithNot_found->(* don't have callwise (-calldeps option) *)letdo_callacckf=(* notice that we use the same old data for each possible call *)(process_one_calldata_afterstmtlvaloption(From.getkf))::accinletl=List.fold_leftdo_call[]called_functionsin(* in l, we have one result for each possible function called *)List.fold_left(fun(acc_u,acc_d)(u,d)->(acc_u||u),Data.mergeacc_dd)(false,Data.bottom)linifusedthenletdata=(* no problem of order because parameters are disjoint for sure *)List.fold_left(fundatakf->process_call_argsdatakfstmtargs)datacalled_functionsinletdata=Data.mergefuncexp_dpdsdatainused,dataelsebeginassert(R.verify(Data.equaldatadata_after)"if statement not used, data doesn't change !");used,dataendmoduleComputer(Param:sigvalstates:Ctx.tend)=structletname="Zones"letdebug=falseletused_stmts=ref[]letadd_used_stmtstmt=used_stmts:=stmt::!used_stmtsletget_and_reset_used_stmts()=letstmts=!used_stmtsinused_stmts:=[];stmtstypet=Data.tletpretty=Data.prettymoduleStmtStartData=structtypedata=tletclear()=Stmt.Hashtbl.clearParam.statesletmem=Stmt.Hashtbl.memParam.statesletfind=Stmt.Hashtbl.findParam.statesletreplace=Stmt.Hashtbl.replaceParam.statesletadd=Stmt.Hashtbl.addParam.statesletiterf=Stmt.Hashtbl.iterfParam.statesletlength()=Stmt.Hashtbl.lengthParam.statesendletcombineStmtStartData_stmt~oldnew_=ifData.equalnew_oldthenNoneelseSomenew_letcombineSuccessors=Data.mergeletdoStmt_stmt=Dataflow2.Defaultletdo_assignstmtlvalexpdata=letl_dpds,exact,l_zone=get_lval_zones~for_writing:truestmtlvalinletr_dpds=Data.exp_zonestmtexpinletused,data=compute_new_datadatal_zonel_dpdsexactr_dpdsinlet_=ifusedthenadd_used_stmtstmtindataletdoInstrstmtinstrdata=matchinstrwith|Set(lval,exp,_)->Dataflow2.Done(do_assignstmtlvalexpdata)|Local_init(v,AssignIniti,_)->letrecauxlviacc=matchiwith|SingleInite->do_assignstmtlvedata|CompoundInit(ct,initl)->letimplicit=trueinletdoinitoi_data=aux(Cil.addOffsetLvalolv)idatainCil.foldLeftCompound~implicit~doinit~ct~initl~accinDataflow2.Done(aux(Cil.varv)idata)|Call(lvaloption,funcexp,args,loc)->letused,data=process_calldatastmtlvaloptionfuncexpargslocinlet_=ifusedthenadd_used_stmtstmtinDataflow2.Donedata|Local_init(v,ConsInit(f,args,k),l)->letused,data=Cil.treat_constructor_as_func(process_calldatastmt)vfargsklinifusedthenadd_used_stmtstmt;Dataflow2.Donedata|Skip_|Code_annot_|Asm_->Dataflow2.DefaultletfilterStmt_stmt_next=trueletfuncExitData=Data.bottomendletcompute_ctrl_infopdgctrl_partused_stmts=letmoduleCtrlComputer=Computer(structletstates=ctrl_partend)inletmoduleCtrlCompute=Dataflow2.Backwards(CtrlComputer)inletseen=Stmt.Hashtbl.create50inletrecadd_node_ctrl_nodesnew_stmtsnode=letctrl_nodes=Pdg.Api.direct_ctrl_dpdspdgnodeinList.fold_leftadd_ctrl_nodenew_stmtsctrl_nodesandadd_ctrl_nodenew_stmtsctrl_node=debug2"[zones] add ctrl node %a@."PdgTypes.Node.prettyctrl_node;matchPdgTypes.Node.stmtctrl_nodewith|None->(* node without stmt : add its ctrl_dpds *)add_node_ctrl_nodesnew_stmtsctrl_node|Somestmt->debug2"[zones] node %a is stmt %d@."PdgTypes.Node.prettyctrl_nodestmt.sid;ifStmt.Hashtbl.memseenstmtthennew_stmtselseletctrl_zone=matchstmt.skindwith|Switch(exp,_,_,_)|If(exp,_,_,_)->Data.exp_zonestmtexp|_->Data.bottominCtx.addctrl_partstmtctrl_zone;Stmt.Hashtbl.addseenstmt();debug2"[zones] add ctrl zone %a at stmt %d@."Data.prettyctrl_zonestmt.sid;stmt::new_stmtsandadd_stmt_ctrlnew_stmtsstmt=debug1"[zones] add ctrl of stmt %d@."stmt.sid;ifStmt.Hashtbl.memseenstmtthennew_stmtselsebeginStmt.Hashtbl.addseenstmt();matchPdg.Api.find_simple_stmt_nodespdgstmtwith|[]->[]|n::_->add_node_ctrl_nodesnew_stmtsnendinletrecadd_stmts_ctrlstmtsall_used_stmts=letall_used_stmts=stmts@all_used_stmtsinletnew_stmts=List.fold_leftadd_stmt_ctrl[]stmtsinletpreds=List.fold_left(funaccs->s.preds@acc)[]new_stmtsinifpreds<>[]thenCtrlCompute.computepreds;letused_stmts=CtrlComputer.get_and_reset_used_stmts()inifused_stmts=[]thenall_used_stmtselseadd_stmts_ctrlused_stmtsall_used_stmtsinadd_stmts_ctrlused_stmts[]letcomputekfstmtlval=letf=Kernel_function.get_definitionkfinletdpds,_exact,zone=get_lval_zones~for_writing:falsestmtlvalinletzone=Data.mergedpdszoneindebug1"[zones] build for %a before %d in %a@\n"Data.prettyzonestmt.sidKernel_function.prettykf;letdata_part=Ctx.create50inList.iter(funs->Ctx.adddata_partsData.bottom)f.sallstmts;let_=Ctx.adddata_partstmtzoneinletmoduleDataComputer=Computer(structletstates=data_partend)inletmoduleDataCompute=Dataflow2.Backwards(DataComputer)inlet_=DataCompute.computestmt.predsinletctrl_part=data_part(* Ctx.create 50 *)in(* it is confusing to have 2 part in the provided information,
* because in fact, it means nothing to separate them.
* So let's put everything in the same object *)letused_stmts=DataComputer.get_and_reset_used_stmts()inletall_used_stmts=ifused_stmts=[]then[]elsecompute_ctrl_info(Pdg.Api.getkf)ctrl_partused_stmtsinletall_used_stmts=List.fold_left(funeacc->Stmt.Hptset.addacce)Stmt.Hptset.emptyall_used_stmtsinall_used_stmts,data_partletgetstmt_zonesstmt=tryCtx.findstmt_zonesstmtwithNot_found->Data.bottomletprettyfmtstmt_zones=letppsd=Format.fprintffmt"Stmt:%d -> %a@."s.sidData.prettydinStmt.Hashtbl.iter_sortedppstmt_zones(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)letbuild_zoneskfstmtlval=(* TODO: Journal.register *)(* (Datatype.func Kernel_type.kernel_function
(Datatype.func Kernel_type.stmt
(Datatype.func Kernel_type.lval
(Datatype.couple Kernel_type.stmt_set zones_ty)))))
*)ifstmt.preds=[]thenStmt.Hptset.empty,Ctx.create0elsecomputekfstmtlvalletget_zones=(* TODO: Journal.register *)(*(Datatype.func zones_ty (Datatype.func Kernel_type.stmt data_ty)))*)getletpretty_zones=(* TODO: Journal.register *)(*( Datatype.func Datatype.formatter (Datatype.func zones_ty Datatype.unit)))*)pretty