123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652openGoblintCilopenEscapeopenPrettyopenFeatureopenLivenessmoduleE=ErrormsgmoduleH=HashtblmoduleIH=InthashmoduleM=MachdepmoduleU=UtilmoduleRD=ReachingdefsmoduleUD=UsedefmoduleA=CabsmoduleCH=CabshelpermoduleGA=GrowArraymoduleRCT=RmciltmpsmoduleDCE=DeadcodeelimmoduleEC=ExpcompareletdoElimTemps=reffalseletdebug=reffalseletprintComments=reffalseletenvWarnings=reffalse(* Stuff for Deputy support *)letdeputyAttrs=reffalseletthisKeyword="__this"typeparamkind=|PKNone|PKThis|PKOffsetofattrparamletreccheckParam(ap:attrparam):paramkind=matchapwith|ACons(name,[])whenname=thisKeyword->PKThis|ABinOp(PlusA,a1,a2)whencheckParama1=PKThis->ifa2=AInt0thenPKThiselsePKOffseta2|_->PKNone(* End stuff for Deputy support *)(* Some(-1) => l1 < l2
Some(0) => l1 = l2
Some(1) => l1 > l2
None => different files *)letloc_compl1l2=ifString.comparel1.A.filenamel2.A.filename!=0thenNoneelseifl1.A.lineno>l2.A.linenothenSome(1)elseifl2.A.lineno>l1.A.linenothenSome(-1)elseifl1.A.byteno>l2.A.bytenothenSome(1)elseifl2.A.byteno>l1.A.bytenothenSome(-1)elseifl1.A.columnno>l2.A.columnnothenSome(1)elseifl2.A.columnno>l1.A.columnnothenSome(-1)elseifl1.A.endLineno>l2.A.endLinenothenSome(1)elseifl2.A.endLineno>l1.A.endLinenothenSome(-1)elseifl1.A.endByteno>l2.A.endBytenothenSome(1)elseifl2.A.endByteno>l1.A.endBytenothenSome(-1)elseifl1.A.endColumnno>l2.A.endColumnnothenSome(1)elseifl2.A.endColumnno>l1.A.endColumnnothenSome(-1)elseSome(0)letsimpleGaSearchl=lethi=GA.max_init_indexCH.commentsGAinletrecloopi=ifi<0then-1elselet(l',_,_)=GA.getCH.commentsGAiinmatchloc_compll'withNone->loop(i-1)|Some(0)->i|Some(-1)->loop(i-1)|Some(1)->i|_->E.s(E.error"simpleGaSearch: unexpected return from loc_comp")inloophi(* location -> string list *)letget_commentsl=letcabsl={A.lineno=l.line;A.filename=l.file;A.byteno=l.byte;A.columnno=l.column;A.ident=0;A.endLineno=l.endLine;A.endByteno=l.endByte;A.endColumnno=l.endColumn;}inlets=simpleGaSearchcabslinletrecloopicl=ifi<0thenclelselet(l',c,b)=GA.getCH.commentsGAiinifString.comparecabsl.A.filenamel'.A.filename!=0thenloop(i-1)clelseifbthenclelselet_=GA.setCH.commentsGAi(l',c,true)inloop(i-1)(c::cl)inList.rev(loops[])(* clean up some of the mess made below *)letrecsimpl_conde=matchewith|UnOp(LNot,BinOp(LAnd,e1,e2,t1),t2)->lete1=simpl_cond(UnOp(LNot,e1,t1))inlete2=simpl_cond(UnOp(LNot,e2,t1))inBinOp(LOr,e1,e2,t2)|UnOp(LNot,BinOp(LOr,e1,e2,t1),t2)->lete1=simpl_cond(UnOp(LNot,e1,t1))inlete2=simpl_cond(UnOp(LNot,e2,t1))inBinOp(LAnd,e1,e2,t2)|UnOp(LNot,UnOp(LNot,e,_),_)->simpl_conde|_->e(* the argument b is the body of a Loop *)(* returns the loop termination condition *)(* block -> exp option *)letget_loop_conditionb=(* returns the first non-empty
statement of a statement list *)(* stm list -> stm list *)letrecskipEmpty=function|[]->[]|{skind=Instr[];labels=[];_}::rest->skipEmptyrest|x->xin(* stm -> exp option * instr list *)letrecget_cond_from_ifif_stm=matchif_stm.skindwithIf(e,tb,fb,_,_)->lete=EC.stripNopCastseinRCT.fold_blockstb;RCT.fold_blocksfb;lettsl=skipEmptytb.bstmtsinletfsl=skipEmptyfb.bstmtsin(matchtsl,fslwith{skind=Break_;_}::_,[]->Somee|[],{skind=Break_;_}::_->Some(UnOp(LNot,e,intType))|({skind=If(_,_,_,_,_);_}ass)::_,[]->letteo=get_cond_from_ifsin(matchteowithNone->None|Somete->Some(BinOp(LAnd,e,EC.stripNopCastste,intType)))|[],({skind=If(_,_,_,_,_);_}ass)::_->letfeo=get_cond_from_ifsin(matchfeowithNone->None|Somefe->Some(BinOp(LAnd,UnOp(LNot,e,intType),EC.stripNopCastsfe,intType)))|{skind=Break_;_}::_,({skind=If(_,_,_,_,_);_}ass)::_->letfeo=get_cond_from_ifsin(matchfeowithNone->None|Somefe->Some(BinOp(LOr,e,EC.stripNopCastsfe,intType)))|({skind=If(_,_,_,_,_);_}ass)::_,{skind=Break_;_}::_->letteo=get_cond_from_ifsin(matchteowithNone->None|Somete->Some(BinOp(LOr,UnOp(LNot,e,intType),EC.stripNopCastste,intType)))|({skind=If(_,_,_,_,_);_}asts)::_,({skind=If(_,_,_,_,_);_}asfs)::_->letteo=get_cond_from_iftsinletfeo=get_cond_from_iffsin(matchteo,feowithSomete,Somefe->Some(BinOp(LOr,BinOp(LAnd,e,EC.stripNopCastste,intType),BinOp(LAnd,UnOp(LNot,e,intType),EC.stripNopCastsfe,intType),intType))|_,_->None)|_,_->(if!debugthenignore(E.log"cond_finder: branches of %a not good\n"d_stmtif_stm);None))|_->(if!debugthenignore(E.log"cond_finder: %a not an if\n"d_stmtif_stm);None)inletsl=skipEmptyb.bstmtsinmatchslwith({skind=If(_,_,_,_,_);labels=[];_}ass)::rest->get_cond_from_ifs,rest|s::_->(if!debugthenignore(E.log"checkMover: %a is first, not an if\n"d_stmts);None,sl)|[]->(if!debugthenignore(E.log"checkMover: no statements in loop block?\n");None,sl)classzraCilPrinterClass:cilPrinter=object(self)inheritdefaultCilPrinterClassassupervalgenvHtbl:(string,varinfo)H.t=H.create128vallenvHtbl:(string,varinfo)H.t=H.create128(*** VARIABLES ***)(* give the varinfo for the variable to be printed,
returns the varinfo for the varinfo with that name
in the current environment.
Returns argument and prints a warning if the variable
isn't in the environment *)methodprivategetEnvVi(v:varinfo):varinfo=tryifH.memlenvHtblv.vnamethenH.findlenvHtblv.vnameelseH.findgenvHtblv.vnamewithNot_found->if!envWarningsthenignore(warn"variable %s not in pp environment"v.vname);v(* True when v agrees with the entry in the environment for the name of v.
False otherwise *)methodprivatecheckVi(v:varinfo):bool=letv'=self#getEnvVivinv.vid=v'.vidmethodprivatecheckViAndWarn(v:varinfo)=ifnot(self#checkViv)thenignore(warn"mentioned variable %s and its entry in the current environment have different varinfo."v.vname)(** Get the comment out of a location if there is one *)method!pLineDirective?(forcefile=false)l=letld=super#pLineDirectivelinif!printCommentsthenletc=String.concat"\n"(get_commentsl)inmatchcwith""->ld|_->ld++line++text"/*"++textc++text"*/"++lineelseld(* variable use *)method!pVar(v:varinfo)=(* warn about instances where a possibly unintentionally
conflicting name is used *)ifIH.memRCT.iiohv.vidthenletrhso=IH.findRCT.iiohv.vidinmatchrhsowithSome(Call(_,e,el,l,eloc))->(* print a call instead of a temp variable *)letoldpit=super#getPrintInstrTerminator()inlet_=super#setPrintInstrTerminator""inletopc=!printCommentsinlet_=printComments:=falseinletc=matchunrollType(typeOfe)withTFun(rt,_,_,_)whennot(Util.equals(typeSigrt)(typeSigv.vtype))->text"("++self#pTypeNone()v.vtype++text")"|_->nilinletd=self#pInstr()(Call(None,e,el,l,eloc))inlet_=super#setPrintInstrTerminatoroldpitinlet_=printComments:=opcinc++d|_->ifIH.memRCT.incdecHashv.vidthen(* print an post-inc/dec instead of a temp variable *)letredefid,rhsvi,b=IH.findRCT.incdecHashv.vidinmatchbwithPlusA|PlusPI|IndexPI->textrhsvi.vname++text"++"|MinusA|MinusPI->textrhsvi.vname++text"--"|_->E.s(E.error"zraCilPrinterClass.pVar: unexpected op for inc/dec")else(self#checkViAndWarnv;textv.vname)elseifIH.memRCT.incdecHashv.vidthen(* print an post-inc/dec instead of a temp variable *)letredefid,rhsvi,b=IH.findRCT.incdecHashv.vidinmatchbwithPlusA|PlusPI|IndexPI->textrhsvi.vname++text"++"|MinusA|MinusPI->textrhsvi.vname++text"--"|_->E.s(E.error"zraCilPrinterClass.pVar: unexpected op for inc/dec")else(self#checkViAndWarnv;textv.vname)(* variable declaration *)method!pVDecl()(v:varinfo)=(* See if the name is already in the environment with a
different varinfo. If so, give a warning.
If not, add the name to the environment *)let_=if(H.memlenvHtblv.vname)&¬(self#checkViv)thenignore(warn"name %s has already been declared locally with different varinfo"v.vname)elseif(H.memgenvHtblv.vname)&¬(self#checkViv)thenignore(warn"name %s has already been declared globally with different varinfo"v.vname)elseifnotv.vglobthen(if!debugthenignore(E.log"zrapp: adding %s to local pp environment\n"v.vname);H.addlenvHtblv.vnamev)else(if!debugthenignore(E.log"zrapp: adding %s to global pp envirnoment\n"v.vname);H.addgenvHtblv.vnamev)in(* First the storage modifiers *)self#pLineDirectivev.vdecl++text(ifv.vinlinethen"__inline "else"")++d_storage()v.vstorage++(self#pType(Some(textv.vname))()v.vtype)++text" "++self#pAttrs()v.vattr(* For printing deputy annotations *)method!pAttr(Attr(an,args):attribute):doc*bool=ifnot(!deputyAttrs)thensuper#pAttr(Attr(an,args))elsematchan,argswith|"fancybounds",[AInti1;AInti2]->nil,false(*if !showBounds then
dprintf "BND(%a, %a)" self#pExp (getBoundsExp i1)
self#pExp (getBoundsExp i2), false
else
text "BND(...)", false*)|"bounds",[a1;a2]->beginmatchcheckParama1,checkParama2with|PKThis,PKThis->text"COUNT(0)",false|PKThis,PKOffset(AInt1)->text"SAFE",false|PKThis,PKOffseta->nil,false(*if !showBounds then
dprintf "COUNT(%a)" self#pAttrParam a, false
else
text "COUNT(...)", false*)|_->nil,false(* if !showBounds then
dprintf "BND(%a, %a)" self#pAttrParam a1
self#pAttrParam a2, false
else
text "BND(...)", false*)end|"fancysize",[AInti]->nil,false(*dprintf "SIZE(%a)" self#pExp (getBoundsExp i), false*)|"size",[a]->dprintf"SIZE(%a)"self#pAttrParama,false|"fancywhen",[AInti]->nil,false(*dprintf "WHEN(%a)" self#pExp (getBoundsExp i), false*)|"when",[a]->dprintf"WHEN(%a)"self#pAttrParama,false|"nullterm",[]->text"NT",false|"assumeconst",[]->text"ASSUMECONST",false|"trusted",[]->text"TRUSTED",false|"poly",[a]->dprintf"POLY(%a)"self#pAttrParama,false|"poly",[]->text"POLY",false|"sentinel",[]->text"SNT",false|"nonnull",[]->text"NONNULL",false|"_ptrnode",[AIntn]->nil,false(*if !Doptions.emitGraphDetailLevel >= 3 then
dprintf "NODE(%d)" n, false
else
nil, false*)|"missing_annot",_->(* Don't bother printing thess *)nil,false|_->super#pAttr(Attr(an,args))(*** GLOBALS ***)method!pGlobal()(g:global):doc=(* global (vars, types, etc.) *)matchgwith|GFun(fundec,l)->(* If the function has attributes then print a prototype because
GCC cannot accept function attributes in a definition *)letoldattr=fundec.svar.vattrin(* Always pring the file name before function declarations *)letproto=ifoldattr<>[]then(self#pLineDirectivel)++(self#pVDecl()fundec.svar)++chr';'++lineelsenilin(* Temporarily remove the function attributes *)fundec.svar.vattr<-[];letbody=(self#pLineDirective~forcefile:truel)++(self#pFunDecl()fundec)infundec.svar.vattr<-oldattr;proto++body++line|GType(typ,l)->self#pLineDirective~forcefile:truel++text"typedef "++(self#pType(Some(texttyp.tname))()typ.ttype)++text";\n"|GEnumTag(enum,l)->self#pLineDirectivel++text"enum"++align++text(" "^enum.ename)++self#pAttrs()enum.eattr++text" {"++line++(docList~sep:(chr','++line)(fun(n,attrs,i,loc)->textn++self#pAttrs()attrs++text(n^" = ")++self#pExp()i)()enum.eitems)++unalign++line++text"};\n"|GEnumTagDecl(enum,l)->(* This is a declaration of a tag *)self#pLineDirectivel++text("enum "^enum.ename^";\n")|GCompTag(comp,l)->(* This is a definition of a tag *)letn=comp.cnameinletsu,su1,su2=ifcomp.cstructthen"struct","str","uct"else"union","uni","on"inself#pLineDirective~forcefile:truel++textsu1++(align++textsu2++chr' '++textn++text" {"++line++((docList~sep:line(self#pFieldDecl()))()comp.cfields)++unalign)++line++text"}"++(self#pAttrs()comp.cattr)++text";\n"|GCompTagDecl(comp,l)->(* This is a declaration of a tag *)self#pLineDirectivel++text(compFullNamecomp)++text";\n"|GVar(vi,io,l)->self#pLineDirective~forcefile:truel++self#pVDecl()vi++chr' '++(matchio.initwithNone->nil|Somei->text" = "++(letislong=matchiwithCompoundInit(_,il)whenList.lengthil>=8->true|_->falseinifislongthenline++self#pLineDirectivel++text" "elsenil)++(self#pInit()i))++text";\n"(* print global variable 'extern' declarations, and function prototypes *)|GVarDecl(vi,l)->letbuiltins=gccBuiltinsinifnot!printCilAsIs&&H.membuiltinsvi.vnamethenbegin(* Compiler builtins need no prototypes. Just print them in
comments. *)text"/* compiler builtin: \n "++(self#pVDecl()vi)++text"; */\n"endelseself#pLineDirectivel++(self#pVDecl()vi)++text";\n"|GAsm(s,l)->self#pLineDirectivel++text("__asm__(\""^escape_strings^"\");\n")|GPragma(Attr(an,args),l)->(* sm: suppress printing pragmas that gcc does not understand *)(* assume anything starting with "ccured" is ours *)(* also don't print the 'combiner' pragma *)(* nor 'cilnoremove' *)letsuppress=not!print_CIL_Input&&((startsWith"box"an)||(startsWith"ccured"an)||(an="merger")||(an="cilnoremove"))inletd=matchan,argswith|_,[]->textan|"weak",[ACons(symbol,[])]->text"weak "++textsymbol|_->text(an^"(")++docList~sep:(chr',')(self#pAttrParam())()args++text")"inself#pLineDirectivel++(ifsuppressthentext"/* "elsetext"")++(text"#pragma ")++d++(ifsuppressthentext" */\n"elsetext"\n")|GTexts->ifs<>"//"thentexts++text"\n"elsenilmethod!dGlobal(out:out_channel)(g:global):unit=(* For all except functions and variable with initializers, use the
pGlobal *)matchgwithGFun(fdec,l)->(* If the function has attributes then print a prototype because
GCC cannot accept function attributes in a definition *)letoldattr=fdec.svar.vattrinletproto=ifoldattr<>[]then(self#pLineDirectivel)++(self#pVDecl()fdec.svar)++chr';'++lineelsenilinfprintout~width:80(proto++(self#pLineDirective~forcefile:truel));(* Temporarily remove the function attributes *)fdec.svar.vattr<-[];fprintout~width:80(self#pFunDecl()fdec);fdec.svar.vattr<-oldattr;output_stringout"\n"|GVar(vi,{init=Somei},l)->beginfprintout~width:80(self#pLineDirective~forcefile:truel++self#pVDecl()vi++text" = "++(letislong=matchiwithCompoundInit(_,il)whenList.lengthil>=8->true|_->falseinifislongthenline++self#pLineDirectivel++text" "elsenil));self#dInitout3i;output_stringout";\n"end|g->fprintout~width:80(self#pGlobal()g)method!pFieldDecl()fi=self#pLineDirectivefi.floc++(self#pType(Some(text(iffi.fname=missingFieldNamethen""elsefi.fname)))()fi.ftype)++text" "++(matchfi.fbitfieldwithNone->nil|Somei->text": "++numi++text" ")++self#pAttrs()fi.fattr++text";"methodprivatepFunDecl()f=H.addgenvHtblf.svar.vnamef.svar;(* add function to global env *)H.clearlenvHtbl;(* new local environment *)(* add the arguments to the local environment *)List.iter(funvi->H.addlenvHtblvi.vnamevi)f.sformals;letnf=if!doElimTempsthenRCT.eliminate_tempsfelsefinletdecls=docList~sep:line(funvi->self#pVDecl()vi++text";")()nf.slocalsinself#pVDecl()nf.svar++line++text"{ "++(align(* locals. *)++decls++line++line(* the body *)++((* remember the declaration *)super#setCurrentFormalsnf.sformals;letbody=self#pBlock()nf.sbodyinsuper#setCurrentFormals[];body))++line++text"}"method!privatepStmtKind(next:stmt)()(sk:stmtkind)=matchskwith|Loop(b,l,el,_,_)->begin(* See if we can turn this into a while(e) {} *)(* TODO: See if we can turn this into a do { } while(e); *)letco,bodystmts=get_loop_conditionbinmatchcowith|None->super#pStmtKindnext()sk|Somec->beginself#pLineDirectivel++text"wh"++(align++text"ile ("++self#pExp()(simpl_cond(UnOp(LNot,c,intType)))++text") "++self#pBlock(){bstmts=bodystmts;battrs=b.battrs})endend|_->super#pStmtKindnext()skend(* class zraCilPrinterClass *)letzraCilPrinter=newzraCilPrinterClass(* pretty print an expression *)letpp_exp(fd:fundec)()(e:exp)=deputyAttrs:=true;ignore(RCT.eliminateTempsForExpPrintingfd);letd=zraCilPrinter#pExp()eindeputyAttrs:=false;dletfeature={fd_name="zrapp";fd_enabled=false;fd_description="pretty printing with checks for name conflicts and\n\t\t\t\ttemp variable elimination";fd_extraopt=["--zrapp_elim_temps",Arg.Unit(funn->doElimTemps:=true),"Try to eliminate temporary variables during pretty printing";"--zrapp_debug",Arg.Unit(funn->debug:=true;RD.debug:=true),"Lots of debugging info for pretty printing and reaching definitions";"--zrapp_debug_fn",Arg.String(funs->RD.debug_fn:=s),"Only output debugging info for one function";"--zrapp_comments",Arg.Unit(fun_->printComments:=true),"Print comments from source file in output";];fd_doit=(function(f:file)->lineDirectiveStyle:=None;printerForMaincil:=zraCilPrinter);fd_post_check=false}let()=Feature.registerfeature