123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434(**************************************************************************)(* *)(* 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). *)(* *)(**************************************************************************)letdkey=Pdg_parameters.register_category"ctrl-dpds"openCil_typesopenCil_datatype(** {2 Lexical successors} *)(** Compute a graph which provide the lexical successor of each statement s,
ie. the statement which is the next one if 's' is replaced by Nop.
Notice that if 's' is an If, Loop, ...
the considered statement is the whole block.
Example : (1) x = 3;
(2) if (c) (3) y = 3; (4) goto L; else (5) z = 8;
(6) while (c--) (7) x++;
(8) L : return x;
(1) -> (2) -> (6) -> (8)
(3) -> (4) -> (6)
(5) -> (6)
(7) -> (6)
*)moduleLexical_successors:sigtypetvalcompute:Cil_types.kernel_function->t(** @return the lexical successor of stmt in graph.
@raise Not_found if 'stmt' has no successor in 'graph'
*)valfind:t->Cil_types.stmt->Cil_types.stmtend=structletdkey=Pdg_parameters.register_category"lex-succs"(** Type of the graph *)typet=Cil_types.stmtStmt.Hashtbl.tletpp_stmtfmts=Format.fprintffmt"@[sid:%d(%a)@]"s.sidStmt.prettys(** Add links from each [prev] in [prev_list] to [next]. *)letadd_linksgraphprev_listnext=matchprev_listwith|[]->()|_->letlinkprev=tryignore(Stmt.Hashtbl.findgraphprev)withNot_found->Pdg_parameters.debug~dkey"add @[%a@,-> %a@]"pp_stmtprevpp_stmtnext;Stmt.Hashtbl.addgraphprevnextinList.iterlinkprev_list(** Add links from [prev_list] to [stmt].
* (ie. [stmt] is the lexical successor of every statements in [prev_list])
* and build the links inside [stmt] (when it contains blocks)
* @return a list of the last statements in [stmt] to continue processing
* with the statement that follows.
*)letrecprocess_stmtgraph~prev_list~stmt=Pdg_parameters.debug~dkey"computing for statement %a@."pp_stmtstmt;matchstmt.skindwith|If(_,bthen,belse,_)->let_=add_linksgraphprev_liststmtinletlast_then=process_blockgraphbtheninletlast_else=process_blockgraphbelseinletprev_list=matchlast_then,last_elsewith|[],[]->[stmt]|last,[]|[],last->stmt::last|last_then,last_else->last_then@last_elseinprev_list|Switch(_,blk,_,_)|Blockblk->let_=add_linksgraphprev_liststmtinprocess_blockgraphblk|UnspecifiedSequenceseq->let_=add_linksgraphprev_liststmtinprocess_blockgraph(Cil.block_from_unspecified_sequenceseq)|Loop(_,body,_,_,_)->letprev_list=matchbody.bstmtswith|[]->let_=add_linksgraphprev_liststmtin[stmt]|head::_->let_=add_linksgraphprev_listheadinletlast_list=process_blockgraphbodyinlet_=add_linksgraphlast_liststmtinstmt::[]inprev_list|TryCatch_->Pdg_parameters.fatal"Try/Catch node in the AST"|Instr_|Return_|Goto_|Break_|Continue_|Throw_|TryFinally_|TryExcept_->let_=add_linksgraphprev_liststmtin[stmt](** Process each statement in blk with no previous statement to begin with.
* Then process each statement in the statement list
* knowing that the first element of 'tail'
* is the successor of every statement in prev_list.
* @return a list of the last statements in tail or prev_list if tail=[].
*)andprocess_blockgraphblk=letrecprocess_stmtsprev_liststmts=matchstmtswith|[]->prev_list|s::tail->lets_last_stmts=process_stmtgraph~prev_list~stmt:sinprocess_stmtss_last_stmtstailinprocess_stmts[]blk.bstmts(** Compute the lexical successor graph for function kf *)letcomputekf=Pdg_parameters.debug~dkey"computing for function %s@."(Kernel_function.get_namekf);ifEva.Analysis.use_spec_instead_of_definitionkfthenStmt.Hashtbl.create0elseletgraph=Stmt.Hashtbl.create17inletf=Kernel_function.get_definitionkfinlet_=process_blockgraphf.sbodyingraph(** @return the lexical successor of stmt in graph.
@raise Not_found if 'stmt' has no successor in 'graph' ie when it is [return].
*)letfindgraphstmt=tryStmt.Hashtbl.findgraphstmtwithNot_found->Pdg_parameters.debug~dkey~level:2"not found for stmt:%d@."stmt.sid;raiseNot_foundend(** {2 Postdominators (with infinite path extension)} *)(** This backward dataflow implements a variant of postdominators that verify
the property P enunciated in bts 963: a statement postdominates itself
if and only it is within the main path of a syntactically infinite loop.
The implementation is as follows:
- compute postdominators with an additional flag infinite loop/non-infinite
loop. Every path that may terminate does not have the "infinite loop" flag
- the implementation verifies property P only for Loop statements. To
obtain the property, the cfg is locally rewritten. For statements
--> p --> s:Loop --> h --> ... --> e
^ |
| |
--------------------------
the edges p --> s are transformed into p --> h, but _not_ the backward
edges e --> s. This way, s post-dominates itself if and only if s is
a syntactically infinite loop, but not if there is an outgoing edge. *)modulePdgPostdom:sigtypetvalcompute:kernel_function->t(** @param with_s tells if the statement has to be added to its postdom.
* The returned boolean tells if there is a path to [return] *)valget:t->with_s:bool->stmt->bool*Stmt.Hptset.tend=structmoduleState=structtypet=|ToReturnofStmt.Hptset.t|ToInfinityofStmt.Hptset.tletinterab=matcha,bwith|ToReturnv,ToReturnv'->ToReturn(Stmt.Hptset.intervv')|ToInfinityv,ToInfinityv'->ToInfinity(Stmt.Hptset.intervv')|ToReturnv,ToInfinity_|ToInfinity_,ToReturnv->ToReturnvletequalab=matcha,bwith|ToReturnv,ToReturnv'->Stmt.Hptset.equalvv'|ToInfinityv,ToInfinityv'->Stmt.Hptset.equalvv'|_->falseletaddstmtset=matchsetwith|ToReturnset->ToReturn(Stmt.Hptset.addstmtset)|ToInfinityset->ToInfinity(Stmt.Hptset.addstmtset)letprettyfmtd=matchdwith|ToReturnd->Format.fprintffmt"{%a}_ret"Stmt.Hptset.prettyd|ToInfinityd->Format.fprintffmt"{%a}_oo"Stmt.Hptset.prettydendtypet=State.tStmt.Hashtbl.tlet_prettyfmtinfos=Stmt.Hashtbl.iter(funkv->Format.fprintffmt"Stmt:%d\n%a\n======"k.sidState.prettyv)infosletis_in_stmtsitersstmts=tryiter(funs'->ifs.sid=s'.sidthenraiseExit)stmts;falsewithExit->true(** change [succs] so move the edges [entry -> loop] to [entry -> head] *)letsuccsstmt=letmodifaccs=matchs.skindwith|Loop_->lethead=matchs.succswith|[head]->head|_->assertfalseinletentry,_back_edges=Stmts_graph.loop_predssinifis_in_stmtsList.iterstmtentrythenhead::accelses::acc|_->s::accinList.fold_leftmodif[]stmt.succs(** change [preds] so remove the edges [entry <- loop]
* and to add the edges [entry <- head] *)letpredsstmt=matchstmt.skindwith|Loop_->(* remove edges from entry to loop *)let_entry,back_edges=Stmts_graph.loop_predsstmtinback_edges|_->letmodifaccs=matchs.skindwith|Loop_->letentry,_back_edges=Stmts_graph.loop_predssins::entry@acc|_->s::accinList.fold_leftmodif[]stmt.predsletadd_postdominfosstartinit=letgets=tryStmt.Hashtbl.findinfosswithNot_found->State.ToInfinityStmt.Hptset.emptyinletdo_stmtstmt=matchsuccsstmtwith|[]whenstmt.sid=start.sid->Some(State.ToReturn(Stmt.Hptset.empty))|[]->assertfalse|s::tl->letadd_gets=State.adds(gets)inletcombineSuccessorssts=State.interst(add_gets)inletst=List.fold_leftcombineSuccessors(add_gets)tlinletold=getstmtinletnew_st=(* don't need to State.inter old *)stinifState.equaloldnew_stthenNoneelseSomenew_stinlettodo=Queue.create()inletadd_todop=ifis_in_stmtsQueue.iterptodothen()elseQueue.addptodoinletrecdo_todo()=lets=Queue.taketodoinbeginmatchdo_stmtswith|None->(* finished with that one *)()|Somest->(* store state and add preds *)Stmt.Hashtbl.addinfossst;List.iteradd_todo(predss)end;do_todo()intrylet_=Stmt.Hashtbl.addinfosstartinitinlet_=List.iter(funp->Queue.addptodo)(predsstart)indo_todo()withQueue.Empty->()letcomputekf=letinfos=Stmt.Hashtbl.create50inletreturn=tryKernel_function.find_returnkfwithKernel_function.No_Statement->Pdg_parameters.fatal"No return statement for a function with body %a"Kernel_function.prettykfinlet_=add_postdominfosreturn(State.ToReturn(Stmt.Hptset.empty))inletstmts=ifEva.Analysis.use_spec_instead_of_definitionkftheninvalid_arg"[traces] cannot compute for a leaf function"elseletf=Kernel_function.get_definitionkfinf.sallstmtsinletremove_tops=tryignore(Stmt.Hashtbl.findinfoss)withNot_found->Pdg_parameters.debug~dkey"compute infinite path to sid:%d"s.sid;add_postdominfoss(State.ToInfinity(Stmt.Hptset.empty))inlet_=List.iterremove_topstmtsininfosletgetinfos~with_sstmt=tryletstmt_to_ret,postdoms=matchStmt.Hashtbl.findinfosstmtwith|State.ToInfinitypostdoms->false,postdoms|State.ToReturnpostdoms->true,postdomsinletpostdoms=ifwith_sthenStmt.Hptset.addstmtpostdomselsepostdomsinPdg_parameters.debug~dkey~level:2"get_postdoms for sid:%d (%s) = %a (%spath to ret)@."stmt.sid(ifwith_sthen"with"else"without")Stmt.Hptset.prettypostdoms(ifstmt_to_retthen""else"no ");stmt_to_ret,postdomswithNot_found->assertfalseend(*============================================================================*)(** Compute information needed for control dependencies *)(*============================================================================*)typet=Lexical_successors.t*PdgPostdom.tletcomputekf=letlex_succ_graph=Lexical_successors.computekfinletctrl_dpds_infos=PdgPostdom.computekfin(lex_succ_graph,ctrl_dpds_infos)(** Compute the PDB(A,B) set used in the control dependencies algorithm.
* Roughly speaking, it gives {v (\{B\} U postdom(B))-postdom(A) v}.
* It means that if S is in the result, it postdominates B but not A.
* As B is usually a successor of A, it means that S is reached if the B-branch
* is chosen, but not necessary for the other branches. Then, S should depend
* on A.
(see the document to know more about the applied algorithm)
*)letpd_b_but_not_ainfosstmt_astmt_b=ifstmt_a.sid=stmt_b.sidthenStmt.Hptset.emptyelsebeginleta_to_ret,postdom_a=PdgPostdom.getinfos~with_s:falsestmt_ainletb_to_ret,postdom_b=PdgPostdom.getinfos~with_s:truestmt_binletres=matcha_to_ret,b_to_retwith|true,true|false,false->Stmt.Hptset.diffpostdom_bpostdom_a|true,false->postdom_b|false,true->(* no path [a, ret] but path [b, ret]
* possible when a there is a jump, because then we have
* either (A=G, B=S) or (A=S, B=L) *)Stmt.Hptset.empty(* because we don't want b postdoms
to depend on the jump *)inPdg_parameters.debug~dkey~level:2"pd_b_but_not_a for a=sid:%d b=sid:%d = %a"stmt_a.sidstmt_b.sidStmt.Hptset.prettyres;resend(*============================================================================*)(** Control dependencies *)(*============================================================================*)(** @return the statements which are depending on the condition.
*
* {v = U (PDB (if, succs(if)) v}
* (see the document to know more about the applied algorithm).
*)letget_if_controlled_stmtsctrl_dpds_infosstmt=let_,infos=ctrl_dpds_infosinletadd_pdb_ssetsucc=Stmt.Hptset.unionset(pd_b_but_not_ainfosstmtsucc)inletcontrolled_stmts=List.fold_leftadd_pdb_sStmt.Hptset.emptystmt.succsinPdg_parameters.debug~dkey"controlled_stmt for cond sid:%d = %a"stmt.sidStmt.Hptset.prettycontrolled_stmts;controlled_stmtsletjump_controlled_stmtsinfosjumplabellex_suc=Pdg_parameters.debug~dkey~level:2"lex_succ sid:%d = sid:%d"jump.sidlex_suc.sid;Pdg_parameters.debug~dkey~level:2"jump succ sid:%d = sid:%d"jump.sidlabel.sid;letcontrolled_stmts=iflex_suc.sid=label.sidthenbegin(* the label is the jump lexical successor: no dpds *)Pdg_parameters.debug~dkey"useless jump sid:%d (label = lex_succ = %d)"jump.sidlex_suc.sid;Stmt.Hptset.emptyendelseletpdb_jump_lex_suc=pd_b_but_not_ainfosjumplex_sucinletpdb_lex_suc_label=pd_b_but_not_ainfoslex_suclabelinletpdb_lex_suc_label=Stmt.Hptset.removelex_sucpdb_lex_suc_labelinStmt.Hptset.unionpdb_jump_lex_sucpdb_lex_suc_labelincontrolled_stmts(** let's find the statements which are depending on
* the jump statement (goto, break, continue) =
{v PDB(jump,lex_suc) U (PDB(lex_suc,label) - lex_suc) v}
(see the document to know more about the applied algorithm).
*)letget_jump_controlled_stmtsctrl_dpds_infosjump=letlex_succ_graph,infos=ctrl_dpds_infosinletlex_suc=tryLexical_successors.findlex_succ_graphjumpwithNot_found->assertfalseinletlabel=matchjump.succswith|[label]->label|_->assertfalseinletcontrolled_stmts=jump_controlled_stmtsinfosjumplabellex_sucinPdg_parameters.debug~dkey"controlled_stmt for jump sid:%d = %a"jump.sidStmt.Hptset.prettycontrolled_stmts;controlled_stmts(** Try to process [while(1) S; LS: ] as [L: S; goto L; LS: ] *)letget_loop_controlled_stmtsctrl_dpds_infosloop=letlex_succ_graph,infos=ctrl_dpds_infosinletlex_suc=tryLexical_successors.findlex_succ_graphloopwithNot_found->(* must have at least a return *)assertfalseinletjump=loopinletlabel=matchloop.succswith[head]->head|_->assertfalseinletcontrolled_stmts=jump_controlled_stmtsinfosjumplabellex_sucinPdg_parameters.debug~dkey"controlled_stmt for loop sid:%d = %a"loop.sidStmt.Hptset.prettycontrolled_stmts;controlled_stmts(*============================================================================*)(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)