123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569(* Calculate reaching definitions for each instruction.
Determine when it is okay to replace some variables with
expressions.
After calling computeRDs on a fundec,
ReachingDef.stmtStartData will contain a mapping from
statement ids to data about which definitions reach each
statement. ReachingDef.defIdStmtHash will contain a
mapping from definition ids to the statement in which
that definition takes place.
instrRDs takes a list of instructions, and the
definitions that reach the first instruction, and
for each instruction figures out which definitions
reach into or out of each instruction.
*)openGoblintCilopenPrettyopenLivenessmoduleE=ErrormsgmoduleDF=DataflowmoduleUD=UsedefmoduleL=LivenessmoduleIH=InthashmoduleU=UtilmoduleS=Statsletdebug_fn=ref""letdoTime=reffalselettimesfa=if!doTimethenS.timesfaelsefamoduleIOS=Set.Make(structtypet=intoptionletcompareio1io2=matchio1,io2withSomei1,Somei2->Stdlib.comparei1i2|Somei1,None->1|None,Somei2->-1|None,None->0end)letdebug=reffalse(* return the intersection of
Inthashes ih1 and ih2 *)letih_interih1ih2=letih'=IH.copyih1inIH.iter(funidvi->ifnot(IH.memih2id)thenIH.removeih'idelse())ih1;ih'letih_unionih1ih2=letih'=IH.copyih1inIH.iter(funidvi->ifnot(IH.memih'id)thenIH.addih'idvielse())ih2;ih'(* Lookup varinfo in iosh. If the set contains None
or is not a singleton, return None, otherwise
return Some of the singleton *)(* IOS.t IH.t -> varinfo -> int option *)letiosh_singleton_lookupioshvi=ifIH.memioshvi.vidthenletios=IH.findioshvi.vidinifnot(IOS.cardinalios=1)thenNoneelseIOS.chooseioselseNone(* IOS.t IH.t -> varinfo -> IOS.t *)letiosh_lookupioshvi=ifIH.memioshvi.vidthenSome(IH.findioshvi.vid)elseNone(* return Some(vid) if iosh contains defId.
return None otherwise *)(* IOS.t IH.t -> int -> int option *)letiosh_defId_findioshdefId=(* int -> IOS.t -> int option -> int option*)letget_vidvidiosio=matchiowithSome(i)->Some(i)|None->letthere=IOS.exists(functionNone->false|Some(i')->defId=i')iosiniftherethenSome(vid)elseNoneinIH.foldget_vidioshNone(* The resulting iosh will contain the
union of the same entries from iosh1 and
iosh2. If iosh1 has an entry that iosh2
does not, then the result will contain
None in addition to the things from the
entry in iosh1. *)(* XXX this function is a performance bottleneck *)letiosh_combineiosh1iosh2=letiosh'=IH.copyiosh1inIH.iter(funidios1->tryletios2=IH.findiosh2idinletnewset=IOS.unionios1ios2inIH.replaceiosh'idnewset;withNot_found->letnewset=IOS.addNoneios1inIH.replaceiosh'idnewset)iosh1;IH.iter(funidios2->tryignore(IH.findiosh1id)withNot_found->begin(*if not(IH.mem iosh1 id) then*)letnewset=IOS.addNoneios2inIH.addiosh'idnewsetend)iosh2;iosh'(* determine if two IOS.t IH.t s are the same *)letiosh_equalsiosh1iosh2=(* if IH.length iosh1 = 0 && not(IH.length iosh2 = 0) ||
IH.length iosh2 = 0 && not(IH.length iosh1 = 0)*)ifnot(IH.lengthiosh1=IH.lengthiosh2)then(if!debugthenignore(E.log"iosh_equals: length not same: %d %d\n"(IH.lengthiosh1)(IH.lengthiosh2));false)elseIH.fold(funvidiosb->ifnotbthenbelsetryletios2=IH.findiosh2vidinifnot(IOS.compareiosios2=0)then(if!debugthenignore(E.log"iosh_equals: sets for vid %d not equal\n"vid);false)elsetruewithNot_found->(if!debugthenignore(E.log"iosh_equals: vid %d not in iosh2\n"vid);false))iosh1true(* replace an entire set with a singleton.
if nothing was there just add the singleton *)(* IOS.t IH.t -> int -> varinfo -> unit *)letiosh_replaceioshivi=ifIH.memioshvi.vidthenletnewset=IOS.singleton(Somei)inIH.replaceioshvi.vidnewsetelseletnewset=IOS.singleton(Somei)inIH.addioshvi.vidnewsetletiosh_filter_deadioshvs=iosh(* IH.iter (fun vid _ ->
if not(UD.VS.exists (fun vi -> vid = vi.vid) vs)
then IH.remove iosh vid)
iosh;
iosh*)(* remove definitions that are killed.
add definitions that are gend *)(* Takes the defs, the data, and a function for
obtaining the next def id *)(* VS.t -> IOS.t IH.t -> (unit->int) -> unit *)letproc_defsvsioshf=letpdvi=letnewi=f()inif!debugthenignore(E.log"proc_defs: genning %d\n"newi);iosh_replaceioshnewiviinUD.VS.iterpdvsletidMaker()start=letcounter=refstartinfun()->letret=!counterincounter:=!counter+1;ret(* given reaching definitions into a list of
instructions, figure out the definitions that
reach in/out of each instruction *)(* if out is true then calculate the definitions that
go out of each instruction, if it is false then
calculate the definitions reaching into each instruction *)(* instr list -> int -> (varinfo IH.t * int) -> bool -> (varinfo IH.t * int) list *)letiRDsHtbl=Hashtbl.create128letinstrRDsilsid(ivih,s,iosh)out=ifHashtbl.memiRDsHtbl(sid,out)thenHashtbl.findiRDsHtbl(sid,out)else(* let print_instr i (_,s', iosh') = *)(* let d = d_instr () i ++ line in *)(* fprint stdout 80 d; *)(* flush stdout *)(* in *)letproc_onehili=matchhilwith|[]->let_,defd=UD.computeUseDefInstriinifUD.VS.is_emptydefdthen((*if !debug then print_instr i ((), s, iosh);*)[((),s,iosh)])elseletiosh'=IH.copyioshinproc_defsdefdiosh'(idMaker()s);(*if !debug then
print_instr i ((), s + UD.VS.cardinal defd, iosh');*)((),s+UD.VS.cardinaldefd,iosh')::hil|(_,s',iosh')::hrstasl->let_,defd=UD.computeUseDefInstriinifUD.VS.is_emptydefdthen((*if !debug then
print_instr i ((),s', iosh');*)((),s',iosh')::l)elseletiosh''=IH.copyiosh'inproc_defsdefdiosh''(idMaker()s');(*if !debug then
print_instr i ((), s' + UD.VS.cardinal defd, iosh'');*)((),s'+UD.VS.cardinaldefd,iosh'')::linletfolded=List.fold_leftproc_one[((),s,iosh)]ilinletfoldedout=List.tl(List.revfolded)inletfoldednotout=List.rev(List.tlfolded)inHashtbl.addiRDsHtbl(sid,true)foldedout;Hashtbl.addiRDsHtbl(sid,false)foldednotout;ifoutthenfoldedoutelsefoldednotout(* The right hand side of an assignment is either
a function call or an expression *)typerhs=RDExpofexp|RDCallofinstr(* take the id number of a definition and return
the rhs of the definition if there is one.
Returns None if, for example, the definition is
caused by an assembly instruction *)(* stmt IH.t -> (()*int*IOS.t IH.t) IH.t -> int -> (rhs * int * IOS.t IH.t) option *)letrhsHtbl=IH.create64(* to avoid recomputation *)letgetDefRhsdidstmhstmdatdefId=ifIH.memrhsHtbldefIdthenIH.findrhsHtbldefIdelseletstm=tryIH.finddidstmhdefIdwithNot_found->E.s(E.error"getDefRhs: defId %d not found"defId)inlet(_,s,iosh)=tryIH.findstmdatstm.sidwithNot_found->E.s(E.error"getDefRhs: sid %d not found"stm.sid)inmatchstm.skindwithInstril->letivihl=instrRDsilstm.sid((),s,iosh)truein(* defs that reach out of each instr *)letivihl_in=instrRDsilstm.sid((),s,iosh)falsein(* defs that reach into each instr *)begintryletiihl=List.combine(List.combineilivihl)ivihl_inin(trylet((i,(_,_,diosh)),(_,_,iosh_in))=List.find(fun((i,(_,_,iosh')),_)->matchtime"iosh_defId_find"(iosh_defId_findiosh')defIdwithSomevid->(matchiwithSet((Varvi',NoOffset),_,_,_)->vi'.vid=vid(* _ -> NoOffset *)|Call(Some(Varvi',NoOffset),_,_,_,_)->vi'.vid=vid(* _ -> NoOffset *)|Call(None,_,_,_,_)->false|Asm(_,_,sll,_,_,_)->List.exists(function(_,_,(Varvi',NoOffset))->vi'.vid=vid|_->false)sll|_->false)|None->false)iihlin(matchiwithSet((lh,_),e,_,_)->(matchlhwithVar(vi')->(IH.addrhsHtbldefId(Some(RDExp(e),stm.sid,iosh_in));Some(RDExp(e),stm.sid,iosh_in))|_->E.s(E.error"Reaching Defs getDefRhs: right vi not first"))|Call(lvo,e,el,_,_)->(IH.addrhsHtbldefId(Some(RDCall(i),stm.sid,iosh_in));Some(RDCall(i),stm.sid,iosh_in))|Asm(a,sl,slvl,sel,sl',_)->None|VarDecl_->None)(* ? *)withNot_found->(if!debugthenignore(E.log"getDefRhs: No instruction defines %d\n"defId);IH.addrhsHtbldefIdNone;None))withInvalid_argument_->Noneend|_->E.s(E.error"getDefRhs: defining statement not an instruction list %d"defId)(*None*)letprettyprintdidstmhstmdat()(_,s,iosh)=(*text ""*)seq~sep:line~doit:(fun(vid,ios)->numvid++text": "++IOS.fold(funiod->matchiowithNone->d++text"None "|Somei->(*let stm = IH.find didstmh i in*)matchgetDefRhsdidstmhstmdatiwithNone->d++numi|Some(RDExp(e),_,_)->d++numi++text" "++(d_exp()e)|Some(RDCall(c),_,_)->d++numi++text" "++(d_instr()c))iosnil)~elements:(IH.tolistiosh)moduleReachingDef=structletname="Reaching Definitions"letdebug=debug(* Should the analysis calculate may-reach
or must-reach *)letmayReach=reffalse(* An integer that tells the id number of
the first definition *)(* Also a hash from variable ids to a set of
definition ids that reach this statement.
None means there is a path to this point on which
there is no definition of the variable *)typet=(unit*int*IOS.tIH.t)letcopy(_,i,iosh)=((),i,IH.copyiosh)(* entries for starting statements must
be added before calling compute *)letstmtStartData=IH.create32(* a mapping from definition ids to
the statement corresponding to that id *)letdefIdStmtHash=IH.create32(* mapping from statement ids to statements
for better performance of ok_to_replace *)letsidStmtHash=IH.create64(* pretty printer *)letpretty=prettyprintdefIdStmtHashstmtStartData(* The first id to use when computeFirstPredecessor
is next called *)letnextDefId=ref0(* Count the number of variable definitions in
a statement *)letnum_defsstm=matchstm.skindwithInstr(il)->List.fold_left(funsi->let_,d=UD.computeUseDefInstriins+UD.VS.cardinald)0il|_->let_,d=UD.computeUseDefStmtKindstm.skindinUD.VS.cardinald(* the first predecessor is just the data in along with
the id of the first definition of the statement,
which we get from nextDefId *)letcomputeFirstPredecessorstm(_,s,iosh)=letstartDefId=max!nextDefIdsinletnumds=num_defsstminletrecloopn=ifn<0then()else(if!debugthenignore(E.log"RD: defId %d -> stm %d\n"(startDefId+n)stm.sid);IH.adddefIdStmtHash(startDefId+n)stm;loop(n-1))inloop(numds-1);nextDefId:=startDefId+numds;matchL.getLiveSetstm.sidwith|None->((),startDefId,IH.copyiosh)|Somevs->((),startDefId,iosh_filter_dead(IH.copyiosh)vs)letcombinePredecessors(stm:stmt)~(old:t)((_,s,iosh):t)=matcholdwith(_,os,oiosh)->beginiftime"iosh_equals"(iosh_equalsoiosh)ioshthenNoneelsebeginSome((),os,time"iosh_combine"(iosh_combineoiosh)iosh)endend(* return an action that removes things that
are redefinied and adds the generated defs *)letdoInstrinst(_,s,iosh)=if!debugthenE.log"RD: looking at %a\n"d_instrinst;lettransform(_,s',iosh')=let_,defd=UD.computeUseDefInstrinstinproc_defsdefdiosh'(idMaker()s');((),s'+UD.VS.cardinaldefd,iosh')inDF.Posttransform(* all the work gets done at the instruction level *)letdoStmtstm(_,s,iosh)=ifnot(IH.memsidStmtHashstm.sid)thenIH.addsidStmtHashstm.sidstm;if!debugthenignore(E.log"RD: looking at %a\n"d_stmtstm);matchL.getLiveSetstm.sidwith|None->DF.SDefault|Somevs->beginDF.SUse((),s,iosh_filter_deadioshvs)(*DF.SDefault*)endletdoGuardcondition_=DF.GDefaultletfilterStmtstm=trueendmoduleRD=DF.ForwardsDataFlow(ReachingDef)(* map all variables in vil to a set containing
None in iosh *)(* IOS.t IH.t -> varinfo list -> () *)letiosh_none_fillioshvil=List.iter(funvi->IH.addioshvi.vid(IOS.singletonNone))villetclearMemos()=IH.clearrhsHtbl;Hashtbl.cleariRDsHtbl(* Computes the reaching definitions for a
function. *)(* Cil.fundec -> unit *)letcomputeRDsfdec=tryifcomparefdec.svar.vname(!debug_fn)=0then(debug:=true;ignore(E.log"%s =\n%a\n"(!debug_fn)d_blockfdec.sbody));letbdy=fdec.sbodyinletslst=bdy.bstmtsinIH.clearReachingDef.stmtStartData;IH.clearReachingDef.defIdStmtHash;IH.clearrhsHtbl;Hashtbl.cleariRDsHtbl;ReachingDef.nextDefId:=0;letfst_stm=List.hdslstinletfst_iosh=IH.create32inUD.onlyNoOffsetsAreDefs:=true;IH.addReachingDef.stmtStartDatafst_stm.sid((),0,fst_iosh);time"liveness"L.computeLivenessfdec;UD.onlyNoOffsetsAreDefs:=true;ignore(ReachingDef.computeFirstPredecessorfst_stm((),0,fst_iosh));(matchL.getLiveSetfst_stm.sidwith|None->if!debugthenignore(E.log"Nothing live at fst_stm\n")|Somevs->ignore(iosh_filter_deadfst_ioshvs));if!debugthenignore(E.log"computeRDs: fst_stm.sid=%d\n"fst_stm.sid);RD.compute[fst_stm];ifcomparefdec.svar.vname(!debug_fn)=0thendebug:=false(* now ReachingDef.stmtStartData has the reaching def data in it *)withFailure_->ifcomparefdec.svar.vname(!debug_fn)=0thendebug:=false(* return the definitions that reach the statement
with statement id sid *)letgetRDssid=trySome(IH.findReachingDef.stmtStartDatasid)withNot_found->None(* E.s (E.error "getRDs: sid %d not found" sid) *)letgetDefIdStmtdefid=trySome(IH.findReachingDef.defIdStmtHashdefid)withNot_found->NoneletgetStmtsid=trySome(IH.findReachingDef.sidStmtHashsid)withNot_found->None(* returns the rhs for the definition *)letgetSimpRhsdefId=letrhso=getDefRhsReachingDef.defIdStmtHashReachingDef.stmtStartDatadefIdinmatchrhsowithNone->None|Some(r,_,_)->Some(r)(* check if i is responsible for defId *)(* instr -> int -> bool *)letisDefInstridefId=matchgetSimpRhsdefIdwithSome(RDCalli')->Util.equalsii'|_->false(* Pretty print the reaching definition data for
a function *)letppFdecfdec=seq~sep:line~doit:(funstm->letivih=IH.findReachingDef.stmtStartDatastm.sidinReachingDef.pretty()ivih)~elements:fdec.sbody.bstmts(* If this class is extended with a visitor on expressions,
then the current rd data is available at each expression *)classrdVisitorClass=object(self)inheritnopCilVisitor(* the statement being worked on *)valmutablesid=-1(* if a list of instructions is being processed,
then this is the corresponding list of
reaching definitions *)valmutablerd_dat_lst=[](* these are the reaching defs for the current
instruction if there is one *)valmutablecur_rd_dat=Nonemethod!vstmtstm=sid<-stm.sid;matchgetRDssidwithNone->if!debugthenignore(E.log"rdVis: stm %d had no data\n"sid);cur_rd_dat<-None;DoChildren|Some(_,s,iosh)->matchstm.skindwithInstril->if!debugthenignore(E.log"rdVis: visit il\n");rd_dat_lst<-instrRDsilstm.sid((),s,iosh)false;DoChildren|_->if!debugthenignore(E.log"rdVis: visit non-il\n");cur_rd_dat<-None;DoChildrenmethod!vinsti=if!debugthenignore(E.log"rdVis: before %a, rd_dat_lst is %d long\n"d_instri(List.lengthrd_dat_lst));trycur_rd_dat<-Some(List.hdrd_dat_lst);rd_dat_lst<-List.tlrd_dat_lst;DoChildrenwithFailure_->if!debugthenignore(E.log"rdVis: il rd_dat_lst mismatch\n");DoChildrenmethodget_cur_iosh()=matchcur_rd_datwithNone->(matchgetRDssidwithNone->None|Some(_,_,iosh)->Someiosh)|Some(_,_,iosh)->Someioshend