12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100(* These are functions etc. for removing CIL generated
temporary variables. Some can be removed immediately,
others must wait until pretty printing *)openGoblintCilopenCilintopenLivenessmoduleE=ErrormsgmoduleRD=ReachingdefsmoduleAELV=AvailexpslvmoduleUD=UsedefmoduleIH=InthashmoduleS=StatsmoduleIS=Set.Make(structtypet=intletcompare=Stdlib.compareend)letdebug=RD.debugletdoTime=reffalselettimesfa=if!doTimethenS.timesfaelsefa(* Type for the form of temporary variable names *)typenameform=Suffixofstring|Prefixofstring|Exactofstring(* 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 *)(* int -> (rhs * int * IOS.t IH.t) option *)letgetDefRhs=RD.getDefRhsRD.ReachingDef.defIdStmtHashRD.ReachingDef.stmtStartData(* exp_is_ok_replacement -
Returns false if the argument contains a pointer dereference
or a variable whose address is taken anywhere *)letexp_ok=reftrueclassmemReadOrAddrOfFinderClass=object(self)inheritnopCilVisitormethod!vexpre=matchewithLval(Mem_,_)->exp_ok:=false;SkipChildren|_->DoChildrenmethod!vvrblvi=ifvi.vglobthen(if!debugthenignore(E.log"memReadOrAddrOfFinder: %s is a global\n"vi.vname);exp_ok:=false;SkipChildren)elseifvi.vaddrofthen(if!debugthenignore(E.log"memReadOrAddrOfFinder: %s has its address taken\n"vi.vname);exp_ok:=false;SkipChildren)else(if!debugthenignore(E.log"memReadOrAddrOfFinder: %s does not have its address taken\n"vi.vname);DoChildren)endletmemReadOrAddrOfFinder=newmemReadOrAddrOfFinderClass(* exp -> bool *)letexp_is_ok_replacemente=if!debugthenignore(E.log"exp_is_ok_replacement: in exp_is_ok_replacement with %a\n"d_expe);exp_ok:=true;ignore(visitCilExprmemReadOrAddrOfFindere);!exp_okletemptyStmt=mkEmptyStmt()letfsr=refemptyStmtclassstmtFinderClasssid=object(self)inheritnopCilVisitormethod!vstmtstm=ifstm.sid=sidthen(fsr:=stm;SkipChildren)elseDoChildrenendletfind_statementfsid=RD.getStmtsid(* Are there writes to memory in between
the two statements with the given ids *)(* fundec -> int -> int -> bool *)letwbHtbl=Hashtbl.create256letwrites_betweenfdsidsid=ifHashtbl.memwbHtbl(dsid,sid)thenHashtbl.findwbHtbl(dsid,sid)elseletdstmo=find_statementfdsidinletstmo=find_statementfsidinletfind_writes=matchs.skindwithInstril->List.exists(funi->matchiwithSet((Mem_,_),_,_,_)->true(* pointer write *)|Set((_,Index(_,_)),_,_,_)->true(* array write *)|Call(_,_,_,_,_)->true|_->false)il|_->falsein(* is there a path from start to goal that includes an
instruction that writes to memory? Do a dfs *)letvisited_sid_isr=refIS.emptyinletrecdfsgoalbstart=if!debugthenignore(E.log"writes_between: dfs visiting %a\n"d_stmtstart);ifstart.sid=goal.sidthenletwh=find_writestartin(if!debug&&bthenignore(E.log"writes_between: start=goal and found a write\n");if!debug&&(notb)thenignore(E.log"writes_between: start=goal and no write\n");if!debug&&whthenignore(E.log"writes_between: start=goal and write here\n");if!debug&&(notwh)thenignore(E.log"writes_between: start=goal and no write here\n");b||(find_writestart))else(* if time "List.mem1" (List.mem start.sid) (!visited_sid_lr) then false else *)ifIS.memstart.sid(!visited_sid_isr)thenfalseelseletw=find_writestartinif!debug&&wthenignore(E.log"writes_between: found write %a\n"d_stmtstart);visited_sid_isr:=IS.addstart.sid(!visited_sid_isr);letrecproc_succssl=matchslwith[]->false|s::rest->ifdfsgoal(w||b)sthentrueelseproc_succsrestinproc_succsstart.succsinmatchstmo,dstmowithNone,_|_,None->E.s(E.error"writes_between: defining stmt not an instr")|Somestm,Somedstm->let_=visited_sid_isr:=IS.singletonstm.sidinletfrom_stm=List.fold_left(dfsstm)falsestm.succsinlet_=visited_sid_isr:=IS.emptyinletfrom_dstm=dfsstmfalsedstmin(Hashtbl.addwbHtbl(dsid,sid)(from_stm||from_dstm);from_stm||from_dstm)(* returns true when the variables in uses
have the same definition ids in both curiosh
and defiosh or are global and not defined in
the current function *)letverify_unmodifiedusesfdefscurioshdefiosh=UD.VS.fold(funvib->letcurido=RD.iosh_singleton_lookupcurioshviinletdefido=RD.iosh_singleton_lookupdefioshviinmatchcurido,defidowithSome(curid),Some(defid)->(if!debugthenignore(E.log"verify_unmodified: curido: %d defido: %d\n"curiddefid);curid=defid&&b)|None,None->ifnot(UD.VS.memvifdefs)then(if!debugthenignore(E.log"verify_unmodified: %s not defined in function\n"vi.vname);b)else(* if the same set of definitions reaches, we can replace, also *)letcurios=tryIH.findcurioshvi.vidwithNot_found->RD.IOS.emptyinletdefios=tryIH.finddefioshvi.vidwithNot_found->RD.IOS.emptyinRD.IOS.comparecuriosdefios==0&&b|_,_->(if!debugthenignore(E.log"verify_unmodified: %s has conflicting definitions. cur: %a\n def: %a\n"vi.vnameRD.ReachingDef.pretty((),0,curiosh)RD.ReachingDef.pretty((),0,defiosh));false))usestrueletfdefs=refUD.VS.emptyletudDeepSkindHtbl=IH.create64classdefCollectorClass=object(self)inheritnopCilVisitormethod!vstmts=let_,d=ifIH.memudDeepSkindHtbls.sidthenIH.findudDeepSkindHtbls.sidelseletu',d'=UD.computeDeepUseDefStmtKinds.skindinIH.addudDeepSkindHtbls.sid(u',d');(u',d')infdefs:=UD.VS.union!fdefsd;DoChildrenendletdefCollector=newdefCollectorClassletcollect_fun_defsfd=fdefs:=UD.VS.empty;ignore(visitCilFunctiondefCollectorfd);!fdefs(* ok_to_replace *)(* is it alright to replace a variable use with the expression
that the variable was defined to be? *)(* Takes the definitions that reached the place where the
variable was defined and the definitions that reach the
place the variable is used. If the same definitions for
the variables used in the expression reach both places,
then it is okay to replace the variable with the expression. *)(* With regards to globals and parameters there are two
possibilities if the reverse lookup returns None for both
sets of reaching definitions:
1) The global or parameter is actually not redefined.
2) At both points no one definition *must* reach there.
For this reason, this function also takes the fundec,
so that it can be figured out which is the case *)(* varinfo -> varinfo IH.t -> sid -> varinfo IH.t -> fundec -> rhs -> bool *)(* sid is an int that is the statement id of the statement where
we are trying to do a replacement *)(* vi is the varinfo of the variable that we are trying to replace *)letok_to_replacevicurioshsiddefioshdsidfr=letuses,safe=matchrwithRD.RDExpe->(UD.computeUseExpe,exp_is_ok_replacemente)|RD.RDCall(Call(_,_,el,_,_)asi)->letsafe=List.fold_left(funbe->(exp_is_ok_replacemente)&&b)trueelinletu,d=UD.computeUseDefInstriinu,safe|_->E.s(E.bug"ok_to_replace: got non Call in RDCall.")inlettarget_addrof=ifvi.vaddrof||vi.vglobthen(if!debugthenignore(E.log"ok_to_replace: target %s had its address taken or is a global\n"vi.vname);true)else(if!debugthenignore(E.log"ok_to_replace: target %s does not have its address taken\n"vi.vname);false)inletwrites=ifsafe&¬(target_addrof)thenfalseelse(time"writes_between"(writes_betweenfdsid)sid)inif(notsafe||target_addrof)&&writesthen(if!debugthenignore(E.log"ok_to_replace: replacement not safe because of pointers or addrOf\n");false)elseletfdefs=collect_fun_defsfinlet_=if!debugthenignore(E.log"ok_to_replace: card fdefs = %d\n"(UD.VS.cardinalfdefs))inlet_=if!debugthenignore(E.log"ok_to_replace: card uses = %d\n"(UD.VS.cardinaluses))inverify_unmodifiedusesfdefscurioshdefioshletuseList=ref[](* Visitor for making a list of statements that use a definition *)classuseListerClass(defid:int)(vi:varinfo)=object(self)inheritRD.rdVisitorClassmethod!vexpre=matchewith|Lval(Varvi',off)->beginmatchself#get_cur_iosh()withSomeiosh->letvido=RD.iosh_defId_findioshdefidinletexists=matchvidowithSome_->true|None->falseinifUtil.equalsvivi'&&existsthen(useList:=sid::(!useList);DoChildren)elseDoChildren|_->DoChildren(*E.s (E.error "useLister: no data for statement")*)end|_->DoChildrenend(* ok_to_replace_with_incdec *)(* Find out if it is alright to replace the use of a variable
with a post-increment/decrement of the variable it is assigned to be *)(* Takes the definitions reaching the variable use, the definitions
reaching the place where the variable was defined, the fundec,
the varinfo for the variable being considered and the right
hand side of its definition. *)letok_to_replace_with_incdeccurioshdefioshfidvir=(* number of uses of vi where definition id reaches *)letnum_uses()=let_=useList:=[]inletulc=newuseListerClassidviinlet_=visitCilFunction(ulc:>cilVisitor)finList.length(!useList)in(* Is e the addition or subtraction of one to vi?
Return Some(PlusA) if it's an addition,
Some(MinusA) if it's a subtraction,
and None otherwise *)letinc_or_decevi=matchewithBinOp((PlusA|PlusPI|IndexPI),Lval(Varvi',NoOffset),Const(CInt(a,_,_)),_)->ifvi.vid=vi'.vid&&compare_cilintaone_cilint=0thenSome(PlusA)elseifvi.vid=vi'.vid&&compare_cilintamone_cilint=0thenSome(MinusA)elseNone|BinOp((MinusA|MinusPI),Lval(Varvi',NoOffset),Const(CInt(a,_,_)),_)->ifvi.vid=vi'.vid&&compare_cilintaone_cilint=0thenSome(MinusA)elseNone|_->NoneinmatchrwithRD.RDExp(Lval(Varrhsvi,NoOffset))->letcurido=RD.iosh_singleton_lookupcurioshrhsviinletdefido=RD.iosh_singleton_lookupdefioshrhsviin(matchcurido,defidowithSome(curid),_->letdefios=tryIH.finddefioshrhsvi.vidwithNot_found->RD.IOS.emptyinletredefrhso=getDefRhscuridin(matchredefrhsowithNone->(if!debugthenignore(E.log"ok_to_replace: couldn't get rhs for redef: %d\n"curid);None)|Some(redefrhs,_,redefiosh)->lettmprdido=RD.iosh_singleton_lookupredefioshviinmatchtmprdidowithNone->(if!debugthenignore(E.log"ok_to_replace: conflicting defs of %s reach redef of %s\n"vi.vnamerhsvi.vname);None)|Sometmprdid->ifnot(tmprdid=id)then(if!debugthenignore(E.log"ok_to_replace: initial def of %s doesn't reach redef of %s\n"vi.vnamerhsvi.vname);None)elseletredefios=tryIH.findredefioshrhsvi.vidwithNot_found->RD.IOS.emptyinletcurdef_stmt=tryIH.findRD.ReachingDef.defIdStmtHashcuridwithNot_found->E.s(E.error"ok_to_replace: couldn't find statement defining %d"curid)inifnot(RD.IOS.comparedefiosredefios=0)then(if!debugthenignore(E.log"ok_to_replace: different sets of definitions of %s reach the def of %s and the redef of %s\n"rhsvi.vnamevi.vnamerhsvi.vname);None)else(matchredefrhswithRD.RDExp(e)->(matchinc_or_decerhsviwithSome(PlusA)->ifnum_uses()=1thenSome(curdef_stmt.sid,curid,rhsvi,PlusA)else(if!debugthenignore(E.log"ok_to_replace: tmp used more than once\n");None)|Some(MinusA)->ifnum_uses()=1thenSome(curdef_stmt.sid,curid,rhsvi,MinusA)else(if!debugthenignore(E.log"ok_to_replace: tmp used more than once\n");None)|None->(if!debugthenignore(E.log"ok_to_replace: redef isn't adding or subtracting one from itself\n");None)|_->E.s(E.error"ok_to_replace: unexpected op in inc/dec info."))|_->(if!debugthenignore(E.log"ok_to_replace: redef a call\n");None)))|_->(if!debugthenignore(E.log"ok_to_replace: %s has conflicting definitions\n"rhsvi.vname);None))|_->(if!debugthenignore(E.log"ok_to_replace: rhs not of correct form\n");None)(* A hash from variable ids to Call instruction
options. If a variable id is in this table,
and it is mapped to Some(Call()), then the
function call can be printed instead of the
variable *)letiioh=IH.create16(* A hash from variable ids to information that
can be used to print a post increment/decrement
that can replace the variable *)letincdecHash=IH.create16(* A hash from variable ids to a list of statement ids.
Because a post-inc/dec will be printed elsewhere,
the assignments of the variable in these statements
don't need to be printed *)letidDefHash=IH.create16(* Add a pair to the list for vid and create a list if one
doesn't exist *)letid_dh_addvidp=ifIH.memidDefHashvidthenletoldlist=IH.findidDefHashvidinletnewlist=p::oldlistinIH.replaceidDefHashvidnewlistelseIH.addidDefHashvid[p](* check if a name matches a form *)(* string -> nameform -> bool *)letcheck_formsf=matchfwithSuffixsfx->letfrmlen=String.lengthsfxinletslen=String.lengthsinslen>=frmlen&&compare(String.subs(slen-frmlen)frmlen)sfx=0|Prefixpfx->letfrmlen=String.lengthpfxinString.lengths>=frmlen&&compare(String.subs0frmlen)pfx=0|Exactext->letfrmlen=String.lengthextinString.lengths=frmlen&&comparesext=0(* check a name against a list of forms
if it matches any then return true *)(* string -> nameform list -> bool *)letcheck_formssfl=List.fold_left(funbf->b||check_formsf)falseflletforms=[Exact"tmp";Prefix"tmp___";Prefix"__cil_tmp";Suffix"__e";Suffix"__b";](* action: 'a -> varinfo -> fundec -> bool -> exp option
iosh: 'a
fd: fundec
nofrm: bool
Replace Lval(Var vi, NoOffset) with
e where action iosh sid vi fd nofrm returns Some(e) *)letvarXformClassactiondatasidfdnofrm=object(self)inheritnopCilVisitormethod!vexpre=matchewithLval(Varvi,NoOffset)->(matchactiondatasidvifdnofrmwithNone->DoChildren|Somee'->(* Cast e' to the correct type. *)lete''=mkCast~e:e'~newt:vi.vtypeinChangeToe'')|Lval(Meme',off)->(* don't substitute constants in memory lvals *)letposte=matchewithLval(Mem(Const_),off')->Lval(Meme',off')|_->einChangeDoChildrenPost(Lval(Meme',off),post)|_->DoChildrenend(* action: 'a -> lval -> fundec -> bool -> exp option
lvh: 'a
fd: fundec
nofrm: bool
Replace Lval(lv) with
e where action lvh sid lv fd nofrm returns Some(e) *)letlvalXformClassactiondatasidfdnofrm=object(self)inheritnopCilVisitormethod!vexpre=letcastrme=e(*stripCastsForPtrArith e*)inmatchewith|Lval((Meme',off)aslv)->beginmatchactiondatasidlvfdnofrmwith|None->(* don't substitute constants in memory lvals *)letposte=matchewith|Lval(Mem(Const_),off')->Lval(Meme',off')|_->castrmeinChangeDoChildrenPost(Lval(Meme',off),post)|Somee'->lete''=mkCast~e:e'~newt:(typeOf(Lvallv))inChangeDoChildrenPost(e'',castrm)end|Lvallv->beginmatchactiondatasidlvfdnofrmwith|None->DoChildren|Somee'->begin(* Cast e' to the correct type. *)lete''=mkCast~e:e'~newt:(typeOf(Lvallv))inChangeDoChildrenPost(e'',castrm)endend|e->ChangeDoChildrenPost(castrme,castrm)end(* Returns the set of definitions of vi in iosh that
are not due to assignments of the form x = x *)(* IOS.t IH.t -> varinfo -> int option *)letiosh_get_useful_defioshvi=ifIH.memioshvi.vidthenletios=IH.findioshvi.vidinletios'=RD.IOS.filter(funido->matchidowithNone->true|Some(id)->matchtime"getDefRhs"getDefRhsidwithSome(RD.RDExp(Lval(Varvi',NoOffset)),_,_)|Some(RD.RDExp(CastE(_,Lval(Varvi',NoOffset))),_,_)->not(vi.vid=vi'.vid)(* false if they are the same *)|_->true)iosinifnot(RD.IOS.cardinalios'=1)then(if!debugthenignore(E.log"iosh_get_useful_def: multiple different defs of %d:%s(%d)\n"vi.vidvi.vname(RD.IOS.cardinalios'));None)elseRD.IOS.chooseios'else(if!debugthenignore(E.log"iosh_get_useful_def: no def of %s reaches here\n"vi.vname);None)letae_tmp_to_exp_change=reffalseletae_tmp_to_expehsidvifdnofrm=ifnofrm||(check_formsvi.vnameforms)thentrybeginlete=IH.findehvi.vidinif!debugthenignore(E.log"tmp_to_exp: changing %s to %a\n"vi.vnamed_plainexpe);matchewith|Const(CStr_)|Const(CWStr_)->None(* don't fwd subst str lits *)|_->beginae_tmp_to_exp_change:=true;SomeeendendwithNot_found->NoneelseNoneletae_lval_to_exp_change=reffalseletae_lval_to_exp?(propStrings:bool=false)lvhsidlvfdnofrm=matchlv,nofrmwith|(Varvi,NoOffset),false->(* If the var is not a temp, then don't replace *)ifcheck_formsvi.vnameformsthenbegintrylete=AELV.LvExpHash.findlvhlvinmatchewith|Const(CStr_)|Const(CWStr_)->ifpropStringsthen(Somee)elseNone|_->beginae_lval_to_exp_change:=true;if!debugthenignore(E.log"ae: replacing %a with %a\n"d_lvallvd_expe);SomeeendwithNot_found->NoneendelseNone|_,true->begin(* replace everything *)trylete=AELV.LvExpHash.findlvhlvinmatchewith|Const(CStr_)|Const(CWStr_)->ifpropStringsthen(Somee)elseNone|_->beginae_lval_to_exp_change:=true;if!debugthenignore(E.log"ae: replacing %a with %a\n"d_lvallvd_expe);SomeeendwithNot_found->Noneend|_,_->None(* if the temp with varinfo vi can be
replaced by an expression then return
Some of that expression. o/w None.
If b is true, then don't check the form *)(* IOS.t IH.t -> sid -> varinfo -> fundec -> bool -> exp option *)letrd_tmp_to_exp_change=reffalseletrd_tmp_to_expioshsidvifdnofrm=ifnofrm||(check_formsvi.vnameforms)thenletido=iosh_get_useful_defioshviinmatchidowithNone->if!debugthenignore(E.log"tmp_to_exp: non-single def: %s\n"vi.vname);None|Some(id)->letdefrhs=time"getDefRhs"getDefRhsidinmatchdefrhswithNone->if!debugthenignore(E.log"tmp_to_exp: no def of %s\n"vi.vname);None|Some(RD.RDExp(e)asr,dsid,defiosh)->iftime"ok_to_replace"(ok_to_replaceviioshsiddefioshdsidfd)rthen(if!debugthenignore(E.log"tmp_to_exp: changing %s to %a\n"vi.vnamed_plainexpe);matchewith|Const(CStr_)|Const(CWStr_)->None|_->beginrd_tmp_to_exp_change:=true;Someeend)else(if!debugthenignore(E.log"tmp_to_exp: not ok to replace %s\n"vi.vname);None)|_->if!debugthenignore(E.log"tmp_to_exp: rhs is call %s\n"vi.vname);Noneelse(if!debugthenignore(E.log"tmp_to_exp: %s didn't match form or nofrm\n"vi.vname);None)letrd_fwd_substdatasidefdnofrm=rd_tmp_to_exp_change:=false;lete'=visitCilExpr(varXformClassrd_tmp_to_expdatasidfdnofrm)ein(e',!rd_tmp_to_exp_change)letae_fwd_substdatasidefdnofrm=ae_tmp_to_exp_change:=false;lete'=visitCilExpr(varXformClassae_tmp_to_expdatasidfdnofrm)ein(e',!ae_tmp_to_exp_change)letae_lv_fwd_subst?(propStrings:bool=false)datasidefdnofrm=ae_lval_to_exp_change:=false;lete'=visitCilExpr(lvalXformClass(ae_lval_to_exp~propStrings:propStrings)datasidfdnofrm)ein(e',!ae_lval_to_exp_change)letae_simp_fwd_substdataenofrm=ae_fwd_substdata(-1)edummyFunDecnofrmletae_lv_simp_fwd_substdataenofrm=ae_lv_fwd_substdata(-1)edummyFunDecnofrmletae_tmp_to_const_change=reffalseletae_tmp_to_constehsidvifdnofrm=ifnofrm||check_formsvi.vnameformsthentrybeginlete=IH.findehvi.vidinmatchewithConstc->beginae_tmp_to_const_change:=true;Some(Constc)end|_->NoneendwithNot_found->NoneelseNone(* See if vi can be replaced by a constant
by checking all of the definitions reaching
this use of vi *)lettmp_to_const_change=reffalselettmp_to_constioshsidvifdnofrm=ifnofrm||check_formsvi.vnameformsthenmatchRD.iosh_lookupioshviwithNone->None|Some(ios)->letdefido=tryRD.IOS.chooseioswithNot_found->NoneinmatchdefidowithNone->None|Somedefid->matchtime"getDefRhs"getDefRhsdefidwithNone->None|Some(RD.RDExp(Constc),_,defiosh)->(matchRD.getDefIdStmtdefidwithNone->E.s(E.error"tmp_to_const: defid has no statement")|Some(stm)->ifok_to_replaceviioshsiddefioshstm.sidfd(RD.RDExp(Constc))thenletsame=RD.IOS.for_all(fundefido->matchdefidowithNone->false|Somedefid->matchtime"getDefRhs"getDefRhsdefidwithNone->false|Some(RD.RDExp(Constc'),_,defiosh)->ifUtil.equalscc'thenmatchRD.getDefIdStmtdefidwithNone->E.s(E.error"tmp_to_const: defid has no statement")|Some(stm)->ok_to_replaceviioshsiddefioshstm.sidfd(RD.RDExp(Constc'))elsefalse|_->false)iosinifsamethen(tmp_to_const_change:=true;Some(Constc))elseNoneelseNone)|_->NoneelseNoneletconst_propioshsidefdnofrm=tmp_to_const_change:=false;lete'=visitCilExpr(varXformClasstmp_to_constioshsidfdnofrm)ein(e',!tmp_to_const_change)letae_const_propehsidefdnofrm=ae_tmp_to_const_change:=false;lete'=visitCilExpr(varXformClassae_tmp_to_constehsidfdnofrm)ein(e',!ae_tmp_to_const_change)classexpTempElimClass(fd:fundec)=object(self)inheritRD.rdVisitorClassmethod!vexpre=letdo_changeioshvi=letido=RD.iosh_singleton_lookupioshviin(matchidowithSomeid->letriviho=getDefRhsidin(matchrivihowithSome(RD.RDExp(e)asr,dsid,defiosh)->if!debugthenignore(E.log"Can I replace %s with %a?\n"vi.vnamed_expe);ifok_to_replaceviioshsiddefioshdsidfdrthen(if!debugthenignore(E.log"Yes.\n");ChangeTo(e))else(if!debugthenignore(E.log"No.\n");DoChildren)|_->DoChildren)|_->DoChildren)inmatchewithLval(Varvi,NoOffset)->(ifcheck_formsvi.vnameformsthen(* only allowed to replace a tmp with a function call once *)(matchcur_rd_datwithSome(_,s,iosh)->do_changeioshvi|None->letiviho=RD.getRDssidinmatchivihowithSome(_,s,iosh)->(if!debugthenignore(E.log"Try to change %s outside of instruction.\n"vi.vname);do_changeioshvi)|None->(if!debugthenignore(E.log"%s in statement w/o RD info\n"vi.vname);DoChildren))elseDoChildren)|_->DoChildrenendclassexpLvTmpElimClass(fd:fundec)=object(self)inheritAELV.aeVisitorClassmethod!vexpre=matchself#get_cur_eh()with|None->DoChildren|Someeh->beginlete',_=ae_lv_fwd_subst~propStrings:trueehsidefdfalseinChangeToe'endendclassincdecTempElimClass(fd:fundec)=object(self)inheritRD.rdVisitorClassmethod!vexpre=letdo_changeioshvi=letido=RD.iosh_singleton_lookupioshviin(matchidowithSomeid->letriviho=getDefRhsidin(matchrivihowithSome(RD.RDExp(e)asr,_,defiosh)->(matchok_to_replace_with_incdecioshdefioshfdidvirwithSome(curdef_stmt_id,redefid,rhsvi,b)->(if!debugthenignore(E.log"No, but I can replace it with a post-inc/dec\n");if!debugthenignore(E.log"cdsi: %d redefid: %d name: %s\n"curdef_stmt_idredefidrhsvi.vname);IH.addincdecHashvi.vid(redefid,rhsvi,b);id_dh_addrhsvi.vid(curdef_stmt_id,redefid);DoChildren)|None->(if!debugthenignore(E.log"No.\n");DoChildren))|_->DoChildren)|_->DoChildren)inmatchewithLval(Varvi,NoOffset)->(ifcheck_formsvi.vnameformsthen(* only allowed to replace a tmp with an inc/dec if there is only one use *)(matchcur_rd_datwithSome(_,s,iosh)->do_changeioshvi|None->letiviho=RD.getRDssidinmatchivihowithSome(_,s,iosh)->(if!debugthenignore(E.log"Try to change %s outside of instruction.\n"vi.vname);do_changeioshvi)|None->(if!debugthenignore(E.log"%s in statement w/o RD info\n"vi.vname);DoChildren))elseDoChildren)|_->DoChildrenendclasscallTempElimClass(fd:fundec)=object(self)inheritRD.rdVisitorClassmethod!vexpre=letdo_changeioshvi=letido=RD.iosh_singleton_lookupioshviin(matchidowithSomeid->letriviho=getDefRhsidin(matchrivihowithSome(RD.RDCall(i)asr,dsid,defiosh)->if!debugthenignore(E.log"Can I replace %s with %a?\n"vi.vnamed_instri);ifok_to_replaceviioshsiddefioshdsidfdrthen(if!debugthenignore(E.log"Yes.\n");IH.addiiohvi.vid(Some(i));DoChildren)else(if!debugthenignore(E.log"No.\n");DoChildren)|_->DoChildren)|_->DoChildren)inmatchewithLval(Varvi,NoOffset)->(ifcheck_formsvi.vnameformsthen(* only allowed to replace a tmp with a function call if there is only one use *)ifIH.memiiohvi.vidthen(IH.replaceiiohvi.vidNone;DoChildren)else(matchcur_rd_datwithSome(_,s,iosh)->do_changeioshvi|None->letiviho=RD.getRDssidinmatchivihowithSome(_,s,iosh)->(if!debugthenignore(E.log"Try to change %s:%d outside of instruction.\n"vi.vnamevi.vid);do_changeioshvi)|None->(if!debugthenignore(E.log"%s in statement w/o RD info\n"vi.vname);DoChildren))elseDoChildren)|_->DoChildren(* Unused definitions cause multiple replacements
unless they are found and the replacement prevented.
It will be possible to replace more temps if dead
code elimination is performed before printing. *)method!vinsti=(* Need to copy this from rdVisitorClass because we are overriding *)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_lstwithFailure_->if!debugthenignore(E.log"rdVis: il rd_dat_lst mismatch\n"));matchiwithSet((Varvi,off),_,_,_)->ifIH.memiiohvi.vidthen(IH.replaceiiohvi.vidNone;DoChildren)else(IH.addiiohvi.vidNone;DoChildren)|_->DoChildrenend(* Remove local declarations that aren't set or used *)(* fundec -> unit *)letrm_unused_localsfd=letoldIgnoreSizeof=!UD.ignoreSizeofinUD.ignoreSizeof:=false;letused=List.fold_left(funus->letu',d'=UD.computeDeepUseDefStmtKinds.skindinUD.VS.unionu(UD.VS.unionu'd'))UD.VS.emptyfd.sbody.bstmtsinUD.ignoreSizeof:=oldIgnoreSizeof;letgood_varvi=UD.VS.memviusedinletgood_locals=List.filtergood_varfd.slocalsinfd.slocals<-good_locals(* see if a vi is volatile *)letis_volatilevi=letvi_vol=List.exists(function(Attr("volatile",_))->true|_->false)vi.vattrinlettyp_vol=List.exists(function(Attr("volatile",_))->true|_->false)(typeAttrsvi.vtype)inif!debug&&(vi_vol||typ_vol)thenignore(E.log"unusedRemover: %s is volatile\n"vi.vname);if!debug&¬(vi_vol||typ_vol)thenignore(E.log"unusedRemover: %s is not volatile\n"vi.vname);vi_vol||typ_vol(* Remove temp variables that are set but not used *)(* This is different from dead code elimination because
temps that can be eliminated during pretty printing
are also considered *)classunusedRemoverClass:cilVisitor=object(self)inheritnopCilVisitorvalmutableunused_set=UD.VS.emptyvalmutablecur_func=dummyFunDec(* figure out which locals aren't used *)method!vfuncf=cur_func<-f;(* the set of used variables *)letused=List.fold_left(funus->letu',_=UD.computeDeepUseDefStmtKinds.skindinUD.VS.unionuu')UD.VS.emptyf.sbody.bstmtsinletused=UD.computeUseLocalTypes~acc_used:usedfin(* the set of unused locals *)letunused=List.fold_left(fununvi->ifUD.VS.memviusedthenunelse(if!debugthenignore(E.log"unusedRemoverClass: %s is unused\n"vi.vname);UD.VS.addviun))UD.VS.emptyf.slocalsin(* a filter function for picking out
the local variables that need to be kept *)letgood_varvi=(is_volatilevi)||(* have to keep if it's volatile *)(not(UD.VS.memviunused)&&(* have to keep if it's used and if *)(not(IH.memiiohvi.vid)||(* it's not getting eliminated during pp *)(matchIH.findiiohvi.vidwith(* getting eliminated *)None->true|Some_->false))&¬(IH.memincdecHashvi.vid))inletgood_locals=List.filtergood_varf.slocalsinf.slocals<-good_locals;unused_set<-unused;DoChildren(* remove instructions that set variables
that aren't used. Also remove instructions
that set variables mentioned in iioh *)method!vstmtstm=(* return the list of pairs with fst = f *)letfindf_in_plfpl=List.filter(fun(fst,snd)->iffst=fthentrueelsefalse)plin(* Return true if the assignment of this
variable in this statement is going to be
replaced by a post-inc/dec *)letcheck_incdecvie=ifIH.memidDefHashvi.vidthenletpl=IH.findidDefHashvi.vidinmatchfindf_in_plstm.sidplwith(sid,redefid)::l->letrhso=getDefRhsredefidin(matchrhsowithNone->(if!debugthenignore(E.log"check_incdec: couldn't find rhs for def %d\n"redefid);false)|Some(rhs,_,indiosh)->(matchrhswithRD.RDCall_->(if!debugthenignore(E.log"check_incdec: rhs not an expression\n");false)|RD.RDExpe'->ifUtil.equalsee'thentrueelse(if!debugthenignore(E.log"check_incdec: rhs of %d: %a, and needed redef %a not equal\n"redefidd_plainexpe'd_plainexpe);false)))|[]->(if!debugthenignore(E.log"check_incdec: current statement not in list: %d. %s = %a\n"stm.sidvi.vnamed_expe);false)else(if!debugthenignore(E.log"check_incdec: %s not in idDefHash\n"vi.vname);false)in(* return true if the rhs will get
pretty printed as a function call *)letwill_be_calle=matchewithLval(Varvi,NoOffset)->ifnot(IH.memiiohvi.vid)thenfalseelse(matchIH.findiiohvi.vidwithNone->false|Some_->true)|_->falsein(* a filter function for picking out
the instructions that we want to keep *)(* instr -> bool *)letgood_instri=matchiwith|Set((Var(vi),_),e,_,_)->beginifwill_be_calle&¬(List.memvicur_func.slocals)&¬vi.vglobthencur_func.slocals<-vi::cur_func.slocals;is_volatilevi||(not(UD.VS.memviunused_set)&¬(IH.memincdecHashvi.vid)&¬(check_incdecvie))||will_be_calleend|Call(Some(Var(vi),_),_,_,_,_)->begin(* If not in the table or entry is None,
then it's good *)not(IH.memiiohvi.vid)||(matchIH.findiiohvi.vidwithNone->true|Some_->false)end|Asm(_,_,slvlst,_,_,_)->begin(* make sure the outputs are in the locals list *)List.iter(fun(_,s,lv)->matchlvwith(Varvi,_)->ifList.memvicur_func.slocalsthen()elsecur_func.slocals<-vi::cur_func.slocals|_->())slvlst;trueend|_->truein(* If the result of a function call isn't used,
then change to Call(None,...) *)letcall_fixeri=matchiwithCall(Some(Var(vi),_),e,el,l,eloc)asc->ifUD.VS.memviunused_setthenCall(None,e,el,l,eloc)elsec|_->iinmatchstm.skindwithInstril->letnewil=List.filtergood_instrilinletnewil'=Util.list_mapcall_fixernewilinstm.skind<-Instr(newil');SkipChildren|_->DoChildrenend(* from cleaner.ml *)(* Lifts child blocks into parents if the block has no attributes or labels *)letrecfold_blocksb=b.bstmts<-List.fold_right(funsacc->matchs.skindwithBlockib->fold_blocksib;if(List.lengthib.battrs=0&&List.lengths.labels=0)thenib.bstmts@accelses::acc|Instrilwhenil=[]&&s.labels=[]->acc|_->s::acc)b.bstmts[]classremoveBrackets=object(self)inheritnopCilVisitormethod!vblockb=fold_blocksb;DoChildrenend(* clean up the code and
eliminate some temporaries
for pretty printing a whole function *)(* Cil.fundec -> Cil.fundec *)leteliminate_tempsf=ignore(visitCilFunction(newremoveBrackets)f);Cfg.clearCFGinfof;ignore(Cfg.cfgFunf);UD.ignoreSizeof:=false;RD.computeRDsf;IH.cleariioh;IH.clearincdecHash;IH.clearidDefHash;letetec=newexpLvTmpElimClassfinletf'=visitCilFunction(etec:>cilVisitor)finRD.clearMemos();(* we changed instructions and invalidated the "cache" *)letidtec=newincdecTempElimClassf'inletf'=visitCilFunction(idtec:>cilVisitor)f'inletctec=newcallTempElimClassf'inletf'=visitCilFunction(ctec:>cilVisitor)f'inletf'=visitCilFunction(newunusedRemoverClass)f'inf'(* same as above, but doesn't remove the
obviated instructions and declarations.
Use this before using zrapp to print
expressions without temps *)leteliminateTempsForExpPrintingf=Cfg.clearCFGinfof;ignore(Cfg.cfgFunf);UD.ignoreSizeof:=false;RD.computeRDsf;IH.cleariioh;IH.clearincdecHash;IH.clearidDefHash;letetec=newexpLvTmpElimClassfinletf'=visitCilFunction(etec:>cilVisitor)finRD.clearMemos();(* we changed instructions and invalidated the "cache" *)letidtec=newincdecTempElimClassf'inletf'=visitCilFunction(idtec:>cilVisitor)f'inletctec=newcallTempElimClassf'inletf'=visitCilFunction(ctec:>cilVisitor)f'inf'