Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file dataflow.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508moduleIH=InthashmoduleE=ErrormsgopenCilopenPretty(** A framework for data flow analysis for CIL code. Before using
this framework, you must initialize the Control-flow Graph for your
program, e.g using {!Cfg.computeFileCFG} *)type'taction=Default(** The default action *)|Doneof't(** Do not do the default action. Use this result *)|Postof('t->'t)(** The default action, followed by the given
* transformer *)type'tstmtaction=SDefault(** The default action *)|SDone(** Do not visit this statement or its successors *)|SUseof't(** Visit the instructions and successors of this statement
as usual, but use the specified state instead of the
one that was passed to doStmt *)(* For if statements *)type'tguardaction=GDefault(** The default state *)|GUseof't(** Use this data for the branch *)|GUnreachable(** The branch will never be taken. *)(******************************************************************
**********
********** FORWARDS
**********
********************************************************************)moduletypeForwardsTransfer=sigvalname:string(** For debugging purposes, the name of the analysis *)valdebug:boolref(** Whether to turn on debugging *)typet(** The type of the data we compute for each block start. May be
* imperative. *)valcopy:t->t(** Make a deep copy of the data *)valstmtStartData:tInthash.t(** For each statement id, the data at the start. Not found in the hash
* table means nothing is known about the state at this point. At the end
* of the analysis this means that the block is not reachable. *)valpretty:unit->t->Pretty.doc(** Pretty-print the state *)valcomputeFirstPredecessor:Cil.stmt->t->t(** Give the first value for a predecessors, compute the value to be set
* for the block *)valcombinePredecessors:Cil.stmt->old:t->t->toption(** Take some old data for the start of a statement, and some new data for
* the same point. Return None if the combination is identical to the old
* data. Otherwise, compute the combination, and return it. *)valdoInstr:Cil.instr->t->taction(** The (forwards) transfer function for an instruction. The
* {!Cil.currentLoc} is set before calling this. The default action is to
* continue with the state unchanged. *)valdoStmt:Cil.stmt->t->tstmtaction(** The (forwards) transfer function for a statement. The {!Cil.currentLoc}
* is set before calling this. The default action is to do the instructions
* in this statement, if applicable, and continue with the successors. *)valdoGuard:Cil.exp->t->tguardaction(** Generate the successor to an If statement assuming the given expression
* is nonzero. Analyses that don't need guard information can return
* GDefault; this is equivalent to returning GUse of the input.
* A return value of GUnreachable indicates that this half of the branch
* will not be taken and should not be explored. This will be called
* twice per If, once for "then" and once for "else".
*)valfilterStmt:Cil.stmt->bool(** Whether to put this statement in the worklist. This is called when a
* block would normally be put in the worklist. *)endmoduleForwardsDataFlow=functor(T:ForwardsTransfer)->struct(** Keep a worklist of statements to process. It is best to keep a queue,
* because this way it is more likely that we are going to process all
* predecessors of a statement before the statement itself. *)letworklist:Cil.stmtQueue.t=Queue.create()(** We call this function when we have encountered a statement, with some
* state. *)letreachedStatement(s:stmt)(d:T.t):unit=letloc=get_stmtLocs.skindinifloc!=locUnknownthencurrentLoc:=get_stmtLocs.skind;(* see if we know about it already *)E.pushContext(fun_->dprintf"Reached statement %d with %a"s.sidT.prettyd);letnewdata:T.toption=tryletold=IH.findT.stmtStartDatas.sidinmatchT.combinePredecessorss~old:olddwithNone->(* We are done here *)if!T.debugthenignore(E.log"FF(%s): reached stmt %d with %a\n implies the old state %a\n"T.names.sidT.prettydT.prettyold);None|Somed'->begin(* We have changed the data *)if!T.debugthenignore(E.log"FF(%s): weaken data for block %d: %a\n"T.names.sidT.prettyd');Somed'endwithNot_found->(* was bottom before *)letd'=T.computeFirstPredecessorsdinif!T.debugthenignore(E.log"FF(%s): set data for block %d: %a\n"T.names.sidT.prettyd');Somed'inE.popContext();matchnewdatawithNone->()|Somed'->IH.replaceT.stmtStartDatas.sidd';ifT.filterStmts&¬(Queue.fold(funexistss'->exists||s'.sid=s.sid)falseworklist)thenQueue.addsworklist(** Get the two successors of an If statement *)letifSuccs(s:stmt):stmt*stmt=letfstStmtblk=matchblk.bstmtswith[]->Cil.dummyStmt|fst::_->fstinmatchs.skindwithIf(e,b1,b2,_)->letthenSucc=fstStmtb1inletelseSucc=fstStmtb2inletoneFallthrough()=letfallthrough=List.filter(funs'->thenSucc!=s'&&elseSucc!=s')s.succsinmatchfallthroughwith[]->E.s(bug"Bad CFG: missing fallthrough for If.")|[s']->s'|_->E.s(bug"Bad CFG: multiple fallthrough for If.")in(* If thenSucc or elseSucc is Cil.dummyStmt, it's an empty block.
So the successor is the statement after the if *)letstmtOrFallthroughs'=ifs'==Cil.dummyStmtthenoneFallthrough()elses'in(stmtOrFallthroughthenSucc,stmtOrFallthroughelseSucc)|_->E.s(bug"ifSuccs on a non-If Statement.")(** Process a statement *)letprocessStmt(s:stmt):unit=currentLoc:=get_stmtLocs.skind;if!T.debugthenignore(E.log"FF(%s).stmt %d at %t\n"T.names.sidd_thisloc);(* It must be the case that the block has some data *)letinit:T.t=tryT.copy(IH.findT.stmtStartDatas.sid)withNot_found->E.s(E.bug"FF(%s): processing block without data"T.name)in(* See what the custom says *)matchT.doStmtsinitwithSDone->()|(SDefault|SUse_)asact->beginletcurr=matchactwithSDefault->init|SUsed->d|SDone->E.s(bug"SDone")in(* Do the instructions in order *)lethandleInstruction(s:T.t)(i:instr):T.t=currentLoc:=get_instrLoci;(* Now handle the instruction itself *)lets'=letaction=T.doInstrisinmatchactionwith|Dones'->s'|Default->s(* do nothing *)|Postf->fsins'inletafter:T.t=matchs.skindwithInstril->(* Handle instructions starting with the first one *)List.fold_lefthandleInstructioncurril|Goto_|ComputedGoto_|Break_|Continue_|If_|TryExcept_|TryFinally_|Switch_|Loop_|Return_|Block_->currincurrentLoc:=get_stmtLocs.skind;(* Handle If guards *)letsuccsToReach=matchs.skindwithIf(e,_,_,_)->beginletnot_e=UnOp(LNot,e,intType)inletthenGuard=T.doGuardeafterinletelseGuard=T.doGuardnot_eafterinifthenGuard=GDefault&&elseGuard=GDefaultthen(* this is the common case *)s.succselsebeginletdoBranchsuccguard=matchguardwithGDefault->reachedStatementsuccafter|GUsed->reachedStatementsuccd|GUnreachable->if!T.debugthenignore(E.log"FF(%s): Not exploring branch to %d\n"T.namesucc.sid);()inletthenSucc,elseSucc=ifSuccssindoBranchthenSuccthenGuard;doBranchelseSuccelseGuard;[]endend|_->s.succsin(* Reach the successors *)List.iter(funs'->reachedStatements'after)succsToReach;end(** Compute the data flow. Must have the CFG initialized *)letcompute(sources:stmtlist)=Queue.clearworklist;List.iter(funs->Queue.addsworklist)sources;(* All initial stmts must have non-bottom data *)List.iter(funs->ifnot(IH.memT.stmtStartDatas.sid)thenE.s(E.error"FF(%s): initial stmt %d does not have data"T.names.sid))sources;if!T.debugthenignore(E.log"\nFF(%s): processing\n"T.name);letrecfixedpoint()=if!T.debug&¬(Queue.is_emptyworklist)thenignore(E.log"FF(%s): worklist= %a\n"T.name(docList(funs->nums.sid))(List.rev(Queue.fold(funaccs->s::acc)[]worklist)));letkeepgoing=trylets=Queue.takeworklistinprocessStmts;truewithQueue.Empty->if!T.debugthenignore(E.log"FF(%s): done\n\n"T.name);falseinifkeepgoingthenfixedpoint()infixedpoint()end(******************************************************************
**********
********** BACKWARDS
**********
********************************************************************)moduletypeBackwardsTransfer=sigvalname:string(* For debugging purposes, the name of the analysis *)valdebug:boolref(** Whether to turn on debugging *)typet(** The type of the data we compute for each block start. In many
* presentations of backwards data flow analysis we maintain the
* data at the block end. This is not easy to do with JVML because
* a block has many exceptional ends. So we maintain the data for
* the statement start. *)valpretty:unit->t->Pretty.doc(** Pretty-print the state *)valstmtStartData:tInthash.t(** For each block id, the data at the start. This data structure must be
* initialized with the initial data for each block *)valfuncExitData:t(** The data at function exit. Used for statements with no successors.
This is usually bottom, since we'll also use doStmt on Return
statements. *)valcombineStmtStartData:Cil.stmt->old:t->t->toption(** When the analysis reaches the start of a block, combine the old data
* with the one we have just computed. Return None if the combination is
* the same as the old data, otherwise return the combination. In the
* latter case, the predecessors of the statement are put on the working
* list. *)valcombineSuccessors:t->t->t(** Take the data from two successors and combine it *)valdoStmt:Cil.stmt->taction(** The (backwards) transfer function for a branch. The {!Cil.currentLoc} is
* set before calling this. If it returns None, then we have some default
* handling. Otherwise, the returned data is the data before the branch
* (not considering the exception handlers) *)valdoInstr:Cil.instr->t->taction(** The (backwards) transfer function for an instruction. The
* {!Cil.currentLoc} is set before calling this. If it returns None, then we
* have some default handling. Otherwise, the returned data is the data
* before the branch (not considering the exception handlers) *)valfilterStmt:Cil.stmt->Cil.stmt->bool(** Whether to put this predecessor block in the worklist. We give the
* predecessor and the block whose predecessor we are (and whose data has
* changed) *)endmoduleBackwardsDataFlow=functor(T:BackwardsTransfer)->structletgetStmtStartData(s:stmt):T.t=tryIH.findT.stmtStartDatas.sidwithNot_found->E.s(E.bug"BF(%s): stmtStartData is not initialized for %d: %a"T.names.sidd_stmts)(** Process a statement and return true if the set of live return
* addresses on its entry has changed. *)letprocessStmt(s:stmt):bool=if!T.debugthenignore(E.log"FF(%s).stmt %d\n"T.names.sid);(* Find the state before the branch *)currentLoc:=get_stmtLocs.skind;letd:T.t=matchT.doStmtswithDoned->d|(Default|Post_)asaction->begin(* Do the default one. Combine the successors *)letres=matchs.succswith[]->T.funcExitData|fst::rest->List.fold_left(funaccsucc->T.combineSuccessorsacc(getStmtStartDatasucc))(getStmtStartDatafst)restin(* Now do the instructions *)letres'=matchs.skindwithInstril->(* Now scan the instructions in reverse order. This may
* Stack_overflow on very long blocks ! *)lethandleInstruction(i:instr)(s:T.t):T.t=currentLoc:=get_instrLoci;(* First handle the instruction itself *)letaction=T.doInstrisinmatchactionwith|Dones'->s'|Default->s(* do nothing *)|Postf->fsin(* Handle instructions starting with the last one *)List.fold_righthandleInstructionilres|_->resinmatchactionwithPostf->fres'|_->res'endin(* See if the state has changed. The only changes are that it may grow.*)lets0=getStmtStartDatasinmatchT.combineStmtStartDatas~old:s0dwithNone->(* The old data is good enough *)false|Somed'->(* We have changed the data *)if!T.debugthenignore(E.log"BF(%s): set data for block %d: %a\n"T.names.sidT.prettyd');IH.replaceT.stmtStartDatas.sidd';true(** Compute the data flow. Must have the CFG initialized *)letcompute(sinks:stmtlist)=letworklist:Cil.stmtQueue.t=Queue.create()inList.iter(funs->Queue.addsworklist)sinks;if!T.debug&¬(Queue.is_emptyworklist)thenignore(E.log"\nBF(%s): processing\n"T.name);letrecfixedpoint()=if!T.debug&¬(Queue.is_emptyworklist)thenignore(E.log"BF(%s): worklist= %a\n"T.name(docList(funs->nums.sid))(List.rev(Queue.fold(funaccs->s::acc)[]worklist)));letkeepgoing=trylets=Queue.takeworklistinletchanges=processStmtsinifchangesthenbegin(* We must add all predecessors of block b, only if not already
* in and if the filter accepts them. *)List.iter(funp->ifnot(Queue.fold(funexistss'->exists||p.sid=s'.sid)falseworklist)&&T.filterStmtpsthenQueue.addpworklist)s.preds;end;truewithQueue.Empty->if!T.debugthenignore(E.log"BF(%s): done\n\n"T.name);falseinifkeepgoingthenfixedpoint();infixedpoint();end(** Helper utility that finds all of the statements of a function.
It also lists the return statments (including statements that
fall through the end of a void function). Useful when you need an
initial set of statements for BackwardsDataFlow.compute. *)letsink_stmts=ref[]letall_stmts=ref[]letsinkFinder=object(self)inheritnopCilVisitormethod!vstmts=all_stmts:=s::(!all_stmts);matchs.succswith[]->(sink_stmts:=s::(!sink_stmts);DoChildren)|_->DoChildrenend(* returns (all_stmts, return_stmts). *)letfind_stmts(fdec:fundec):(stmtlist*stmtlist)=ignore(visitCilFunction(sinkFinder)fdec);letall=!all_stmtsinletret=!sink_stmtsinall_stmts:=[];sink_stmts:=[];all,ret