1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003(*
Copyright (c) 2001-2003,
George C. Necula <necula@cs.berkeley.edu>
Scott McPeak <smcpeak@cs.berkeley.edu>
Wes Weimer <weimer@cs.berkeley.edu>
Ben Liblit <liblit@cs.berkeley.edu>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)openEscapeopenPrettyopenCilint(* open Trace (\* sm: 'trace' function *\) *)moduleE=ErrormsgmoduleH=HashtblmoduleIH=Inthash(*
CIL: An intermediate language for analyzing C progams.
Scott McPeak, George Necula, Wes Weimer
*)(* The module Cilversion is generated automatically by Makefile from
information in configure.in *)letcilVersion=Cilversion.cilVersiontypecstd=C90|C99|C11letcstd_of_string=function|"c90"->C90|"c99"->C99|"c11"->C11|_->failwith"Not a valid c standard argument."letcstd=refC99letgnu89inline=reffalseletc99Mode()=!cstd<>C90(* True to handle ISO C 99 vs 90 changes.
c99mode only affects parsing of decimal integer constants without suffix
a) on machines where long and long long do not have the same size
(e.g. 32 Bit machines, 64 Bit Windows, not 64 Bit MacOS or (most? all?) 64 Bit Linux):
giving constants that are bigger than max long type long long in c99mode vs. unsigned long
if c99mode is off.
b) for constants bigger than long long producing a "Unimplemented: Cannot represent the integer"
warning in C99 mode vs. unsigned long long if c99mode is off. *)(* Set this to true to get old-style handling of gcc's extern inline C extension:
old-style: the extern inline definition is used until the actual definition is
seen (as long as optimization is enabled)
new-style: the extern inline definition is used only if there is no actual
definition (as long as optimization is enabled)
Note that CIL assumes that optimization is always enabled ;-) *)letoldstyleExternInline=reffalseletmakeStaticGlobal=reftrueletuseLogicalOperators=reffalseletuseComputedGoto=reffalseletuseCaseRange=reffalseletaddReturnOnNoreturnFallthrough=reffalsemoduleM=Machdep(* Cil.initCil will set this to the current machine description.
Makefile.cil generates the file src/machdep.ml,
which contains the descriptions of gcc and msvc. *)letenvMachine:M.machoptionref=refNoneletlowerConstants:boolref=reftrue(** Do lower constants (default true) *)letremoveBranchingOnConstants:boolref=reftrue(** Remove branches of the form if(const) ... else ... (default true) *)letinsertImplicitCasts:boolref=reftrue(** Do insert implicit casts (default true) *)letlittle_endian=reftrueletchar_is_unsigned=reffalseletunderscore_name=reffalsetypelineDirectiveStyle=|LineComment(** Before every element, print the line
number in comments. This is ignored by
processing tools (thus errors are reproted
in the CIL output), but useful for
visual inspection *)|LineCommentSparse(** Like LineComment but only print a line
directive for a new source line *)|LinePreprocessorInput(** Use #line directives *)|LinePreprocessorOutput(** Use # nnn directives (in gcc mode) *)letlineDirectiveStyle=ref(SomeLinePreprocessorInput)letprint_CIL_Input=reffalseletprintCilAsIs=reffalseletlineLength=ref80letwarnTruncate=reftrue(* sm: return the string 's' if we're printing output for gcc, suppres
it if we're printing for CIL to parse back in. the purpose is to
hide things from gcc that it complains about, but still be able
to do lossless transformations when CIL is the consumer *)letforgcc(s:string):string=if(!print_CIL_Input)then""elsesletdebugConstFold=false(** The Abstract Syntax of CIL *)(** The top-level representation of a CIL source file. Its main contents is
the list of global declarations and definitions. *)typefile={mutablefileName:string;(** The complete file name *)mutableglobals:globallist;(** List of globals as they will appear
in the printed file *)mutableglobinit:fundecoption;(** An optional global initializer function. This is a function where
you can put stuff that must be executed before the program is
started. This function, is conceptually at the end of the file,
although it is not part of the globals list. Use {!getGlobInit}
to create/get one. *)mutableglobinitcalled:bool;(** Whether the global initialization function is called in main. This
should always be false if there is no global initializer. When
you create a global initialization CIL will try to insert code in
main to call it. *)}andcomment=location*string(** The main type for representing global declarations and definitions. A list
of these form a CIL file. The order of globals in the file is generally
important. *)andglobal=|GTypeoftypeinfo*location(** A typedef. All uses of type names (through the [TNamed] constructor)
must be preceded in the file by a definition of the name. The string
is the defined name and always not-empty. *)|GCompTagofcompinfo*location(** Defines a struct/union tag with some fields. There must be one of
these for each struct/union tag that you use (through the [TComp]
constructor) since this is the only context in which the fields are
printed. Consequently nested structure tag definitions must be
broken into individual definitions with the innermost structure
defined first. *)|GCompTagDeclofcompinfo*location(** Declares a struct/union tag. Use as a forward declaration. This is
printed without the fields. *)|GEnumTagofenuminfo*location(** Declares an enumeration tag with some fields. There must be one of
these for each enumeration tag that you use (through the [TEnum]
constructor) since this is the only context in which the items are
printed. *)|GEnumTagDeclofenuminfo*location(** Declares an enumeration tag. Use as a forward declaration. This is
printed without the items. *)|GVarDeclofvarinfo*location(** A variable declaration (not a definition). If the variable has a
function type then this is a prototype. There can be several
declarations and at most one definition for a given variable in C, but
in CIL there is also only at most one declaration per variable. If both
forms appear then they must share the same varinfo structure. A
prototype shares the varinfo with the fundec of the definition. Either
has storage Extern or there must be a definition in this file *)|GVarofvarinfo*initinfo*location(** A variable definition. Can have an initializer. The initializer is
updateable so that you can change it without requiring to recreate
the list of globals. There can be at most one definition for a
variable in an entire program. Cannot have storage Extern or function
type. *)|GFunoffundec*location(** A function definition. *)|GAsmofstring*location(** Global asm statement. These ones
can contain only a template *)|GPragmaofattribute*location(** Pragmas at top level. Use the same
syntax as attributes *)|GTextofstring(** Some text (printed verbatim) at
top level. E.g., this way you can
put comments in the output. *)(** The various types available. Every type is associated with a list of
attributes, which are always kept in sorted order. Use {!addAttribute}
and {!addAttributes} to construct list of attributes. If you want to
inspect a type, you should use {!unrollType} to see through the uses
of named types. *)andtyp=TVoidofattributes(** Void type *)|TIntofikind*attributes(** An integer type. The kind specifies
the sign and width. *)|TFloatoffkind*attributes(** A floating-point type. The kind
specifies the precision. *)|TPtroftyp*attributes(** Pointer type. *)|TArrayoftyp*expoption*attributes(** Array type. It indicates the base type and the array length. *)|TFunoftyp*(string*typ*attributes)listoption*bool*attributes(** Function type. Indicates the type of the result, the name, type
and name attributes of the formal arguments ([None] if no
arguments were specified, as in a function whose definition or
prototype we have not seen; [Some \[\]] means void). Use
{!argsToList} to obtain a list of arguments. The boolean
indicates if it is a variable-argument function. If this is the
type of a varinfo for which we have a function declaration then
the information for the formals must match that in the
function's sformals. *)|TNamedoftypeinfo*attributes(** The use of a named type. All uses of the same type name must
share the typeinfo. Each such type name must be preceded
in the file by a [GType] global. This is printed as just the
type name. The actual referred type is not printed here and is
carried only to simplify processing. To see through a sequence
of named type references, use {!unrollType}. The attributes
are in addition to those given when the type name was defined. *)|TCompofcompinfo*attributes(** A reference to a struct or a union type. All references to the
same struct or union must share the same compinfo among them and
with a [GCompTag] global that precedes all uses (except maybe
those that are pointers to the composite type). The attributes
given are those pertaining to this use of the type and are in
addition to the attributes that were given at the definition of
the type and which are stored in the compinfo. *)|TEnumofenuminfo*attributes(** A reference to an enumeration type. All such references must
share the enuminfo among them and with a [GEnumTag] global that
precedes all uses. The attributes refer to this use of the
enumeration and are in addition to the attributes of the
enumeration itself, which are stored inside the enuminfo *)|TBuiltin_va_listofattributes(** This is the same as the gcc's type with the same name *)(** Various kinds of integers *)andikind=IChar(** [char] *)|ISChar(** [signed char] *)|IUChar(** [unsigned char] *)|IBool(** [_Bool (C99)] *)|IInt(** [int] *)|IUInt(** [unsigned int] *)|IShort(** [short] *)|IUShort(** [unsigned short] *)|ILong(** [long] *)|IULong(** [unsigned long] *)|ILongLong(** [long long] (or [_int64] on Microsoft Visual C) *)|IULongLong(** [unsigned long long] (or [unsigned _int64] on Microsoft
Visual C) *)|IInt128(** [__int128] *)|IUInt128(** [unsigned __int128] *)(** Various kinds of floating-point numbers*)andfkind=FFloat(** [float] *)|FDouble(** [double] *)|FLongDouble(** [long double] *)|FFloat128(** [float128] *)|FFloat16(** [_Float16] *)|FComplexFloat(** [float _Complex] *)|FComplexDouble(** [double _Complex] *)|FComplexLongDouble(** [long double _Complex]*)|FComplexFloat128(** [_float128 _Complex]*)|FComplexFloat16(** [_Float16 _Complex]*)(** An attribute has a name and some optional parameters *)andattribute=Attrofstring*attrparamlist(** Attributes are lists sorted by the attribute name *)andattributes=attributelist(** The type of parameters in attributes *)andattrparam=|AIntofint(** An integer constant *)|AStrofstring(** A string constant *)|AConsofstring*attrparamlist(** Constructed attributes. These
are printed [foo(a1,a2,...,an)].
The list of parameters can be
empty and in that case the
parentheses are not printed. *)|ASizeOfoftyp(** A way to talk about types *)|ASizeOfEofattrparam|ASizeOfSoftypsig(** Replacement for ASizeOf in type
signatures. Only used for
attributes inside typsigs.*)|AAlignOfoftyp|AAlignOfEofattrparam|AAlignOfSoftypsig|AUnOpofunop*attrparam|ABinOpofbinop*attrparam*attrparam|ADotofattrparam*string(** a.foo **)|AStarofattrparam(** * a *)|AAddrOfofattrparam(** & a **)|AIndexofattrparam*attrparam(** a1[a2] *)|AQuestionofattrparam*attrparam*attrparam(** a1 ? a2 : a3 **)|AAssignofattrparam*attrparam(** a1 = a2 *)(** Information about a composite type (a struct or a union). Use
{!mkCompInfo}
to create non-recursive or (potentially) recursive versions of this. Make
sure you have a [GCompTag] for each one of these. *)andcompinfo={mutablecstruct:bool;(** True if struct, False if union *)mutablecname:string;(** The name. Always non-empty. Use
{!compFullName} to get the
full name of a comp (along with
the struct or union) *)mutableckey:int;(** A unique integer constructed from
the name. Use {!Hashtbl.hash} on
the string returned by
{!compFullName}. All compinfo
for a given key are shared. *)mutablecfields:fieldinfolist;(** Information about the fields *)mutablecattr:attributes;(** The attributes that are defined at
the same time as the composite
type *)mutablecdefined:bool;(** Whether this is a defined
compinfo. *)mutablecreferenced:bool;(** True if used. Initially set to
false *)}(** Information about a struct/union field *)andfieldinfo={mutablefcomp:compinfo;(** The compinfo of the host. Note
that this must be shared with the
host since there can be only one
compinfo for a given id *)mutablefname:string;(** The name of the field. Might be
the value of
{!missingFieldName} in which
case it must be a bitfield and is
not printed and it does not
participate in initialization *)mutableftype:typ;(** The type *)mutablefbitfield:intoption;(** If a bitfield then ftype should be
an integer type *)mutablefattr:attributes;(** The attributes for this field
(not for its type) *)mutablefloc:location;(** The location where this field
is defined *)}(** Information about an enumeration. This is shared by all references to an
enumeration. Make sure you have a [GEnumTag] for each of of these. *)andenuminfo={mutableename:string;(** The name. Always non-empty *)mutableeitems:(string*attributes*exp*location)list;(** Items with names
and values. This list
should be
non-empty. The item
values must be
compile-time
constants. *)mutableeattr:attributes;(** Attributes *)mutableereferenced:bool;(** True if used. Initially set to false*)mutableekind:ikind;(** The integer kind used to represent this enum. Per ANSI-C, this
should always be IInt, but gcc allows other integer kinds *)}(** Information about a defined type *)andtypeinfo={mutabletname:string;(** The name. Can be empty only in a [GType] when introducing a composite
or enumeration tag. If empty cannot be referred to from the file *)mutablettype:typ;(** The actual type. *)mutabletreferenced:bool;(** True if used. Initially set to false*)}(** Information about a variable. These structures are shared by all
references to the variable. So, you can change the name easily, for
example. Use one of the {!makeLocalVar}, {!makeTempVar} or
{!makeGlobalVar} to create instances of this data structure. *)andvarinfo={mutablevname:string;(** The name of the variable. Cannot
be empty. *)mutablevtype:typ;(** The declared type of the
variable. *)mutablevattr:attributes;(** A list of attributes associated
with the variable. *)mutablevstorage:storage;(** The storage-class *)(* The other fields are not used in varinfo when they appear in the formal
argument list in a [TFun] type *)mutablevglob:bool;(** True if this is a global variable*)mutablevinline:bool;(** Whether this varinfo is for an inline function. *)mutablevdecl:location;(** Location of variable declaration *)vinit:initinfo;(** Optional initializer. Only used for static and global variables.
Initializers for other types of local variables are turned into
assignments. *)mutablevid:int;(** A unique integer identifier. *)mutablevaddrof:bool;(** True if the address of this
variable is taken. CIL will set
these flags when it parses C, but
you should make sure to set the
flag whenever your transformation
create [AddrOf] expression. *)mutablevreferenced:bool;(** True if this variable is ever
referenced. This is computed by
[removeUnusedVars]. It is safe to
just initialize this to False *)mutablevdescr:doc;(** For most temporary variables, a
description of what the var holds.
(e.g. for temporaries used for
function call results, this string
is a representation of the function
call.) *)mutablevdescrpure:bool;(** Indicates whether the vdescr above
is a pure expression or call.
True for all CIL expressions and
Lvals, but false for e.g. function
calls.
Printing a non-pure vdescr more
than once may yield incorrect
results. *)mutablevhasdeclinstruction:bool;(** Indicates whether a VarDecl instruction
was generated for this variable.
Only applies to local variables.
Currently, this is relevant for when to
print the declaration. If this is
true, it might be incorrect to print the
declaration at the beginning of the
function, rather than were the VarDecl
instruction is. This was originally
introduced to handle VLAs. *)}(** Storage-class information *)andstorage=NoStorage(** The default storage. Nothing is
printed *)|Static|Register|Extern(** Expressions (Side-effect free)*)andexp=Constofconstant(** Constant *)|Lvaloflval(** Lvalue *)|SizeOfoftyp(** sizeof(<type>). Has [unsigned
int] type (ISO 6.5.3.4). This is
not turned into a constant because
some transformations might want to
change types *)|Realofexp(** __real__(<expression>) *)|Imagofexp(** __imag__(<expression>) *)|SizeOfEofexp(** sizeof(<expression>) *)|SizeOfStrofstring(** sizeof(string_literal). We separate this case out because this is the
only instance in which a string literal should not be treated as
having type pointer to character. *)|AlignOfoftyp(** Has [unsigned int] type *)|AlignOfEofexp|UnOpofunop*exp*typ(** Unary operation. Includes
the type of the result *)|BinOpofbinop*exp*exp*typ(** Binary operation. Includes the
type of the result. The arithmetic
conversions are made explicit
for the arguments *)|Questionofexp*exp*exp*typ(** (a ? b : c) operation. Includes
the type of the result *)|CastEoftyp*exp(** Use {!mkCast} to make casts *)|AddrOfoflval(** Always use {!mkAddrOf} to
construct one of these. Apply to an
lvalue of type [T] yields an
expression of type [TPtr(T)] *)|AddrOfLabelofstmtref|StartOfoflval(** There is no C correspondent for this. C has
implicit coercions from an array to the address
of the first element. [StartOf] is used in CIL to
simplify type checking and is just an explicit
form of the above mentioned implicit conversion.
It is not printed. Given an lval of type
[TArray(T)] produces an expression of type
[TPtr(T)]. *)andwstring_type=|Wchar_t|Char16_t|Char32_tandencoding=No_encoding|Utf8(** Literal constants *)andconstant=|CIntofcilint*ikind*stringoption(** Integer constant. Give the ikind (see ISO9899 6.1.3.2)
and the textual representation, if available. Use
{!integer} or {!kinteger} to create these. *)|CStrofstring*encoding(** String constant (of pointer type) *)|CWStrofint64list*wstring_type(** Wide string constant (of type "wchar_t *") *)|CChrofchar(** Character constant. This has type int, so use
charConstToInt to read the value in case
sign-extension is needed. *)|CRealoffloat*fkind*stringoption(** Floating point constant. Give
the fkind (see ISO 6.4.4.2) and
also the textual representation,
if available *)|CEnumofexp*string*enuminfo(** An enumeration constant with the given value, name, from the given
enuminfo. This is not used if {!lowerEnum} is false (default).
Use {!Cillower.lowerEnumVisitor} to replace these with integer
constants. *)(** Unary operators *)andunop=Neg(** Unary minus *)|BNot(** Bitwise complement (~) *)|LNot(** Logical Not (!) *)(** Binary operations *)andbinop=PlusA(** arithmetic + *)|PlusPI(** pointer + integer *)|IndexPI(** pointer + integer but only when
it arises from an expression
[e\[i\]] when [e] is a pointer and
not an array. This is semantically
the same as PlusPI but CCured uses
this as a hint that the integer is
probably positive. *)|MinusA(** arithmetic - *)|MinusPI(** pointer - integer *)|MinusPP(** pointer - pointer *)|Mult(** * *)|Div(** / *)|Mod(** % *)|Shiftlt(** shift left *)|Shiftrt(** shift right *)|Lt(** < (arithmetic comparison) *)|Gt(** > (arithmetic comparison) *)|Le(** <= (arithmetic comparison) *)|Ge(** > (arithmetic comparison) *)|Eq(** == (arithmetic comparison) *)|Ne(** != (arithmetic comparison) *)|BAnd(** bitwise and *)|BXor(** exclusive-or *)|BOr(** inclusive-or *)|LAnd(** logical and *)|LOr(** logical or *)(** An lvalue denotes the contents of a range of memory addresses. This range
is denoted as a host object along with an offset within the object. The
host object can be of two kinds: a local or global variable, or an object
whose address is in a pointer expression. We distinguish the two cases so
that we can tell quickly whether we are accessing some component of a
variable directly or we are accessing a memory location through a pointer.*)andlval=lhost*offset(** The host part of an {!lval}. *)andlhost=|Varofvarinfo(** The host is a variable. *)|Memofexp(** The host is an object of type [T] when the expression has pointer
[TPtr(T)]. *)(** The offset part of an {!lval}. Each offset can be applied to certain
kinds of lvalues and its effect is that it advances the starting address
of the lvalue and changes the denoted type, essentially focussing to some
smaller lvalue that is contained in the original one. *)andoffset=|NoOffset(** No offset. Can be applied to any lvalue and does
not change either the starting address or the type.
This is used when the lval consists of just a host
or as a terminator in a list of other kinds of
offsets. *)|Fieldoffieldinfo*offset(** A field offset. Can be applied only to an lvalue
that denotes a structure or a union that contains
the mentioned field. This advances the offset to the
beginning of the mentioned field and changes the
type to the type of the mentioned field. *)|Indexofexp*offset(** An array index offset. Can be applied only to an
lvalue that denotes an array. This advances the
starting address of the lval to the beginning of the
mentioned array element and changes the denoted type
to be the type of the array element *)(* The following equivalences hold *)(* Mem(AddrOf(Mem a, aoff)), off = Mem a, aoff + off *)(* Mem(AddrOf(Var v, aoff)), off = Var v, aoff + off *)(* AddrOf (Mem a, NoOffset) = a *)(** Initializers for global variables. You can create an initializer with
{!makeZeroInit}. *)andinit=|SingleInitofexp(** A single initializer *)|CompoundInitoftyp*(offset*init)list(** Used only for initializers of structures, unions and arrays.
The offsets are all of the form [Field(f, NoOffset)] or
[Index(i, NoOffset)] and specify the field or the index being
initialized. For structures all fields
must have an initializer (except the unnamed bitfields), in
the proper order. This is necessary since the offsets are not
printed. For arrays the list must contain a prefix of the
initializers; the rest are 0-initialized.
For unions there must be exactly one initializer. If
the initializer is not for the first field then a field
designator is printed. You can scan an initializer list with
{!foldLeftCompound}. *)(** We want to be able to update an initializer in a global variable, so we
define it as a mutable field *)andinitinfo={mutableinit:initoption;}(** Function definitions. *)andfundec={mutablesvar:varinfo;(** Holds the name and type as a variable, so we can refer to it
easily from the program. All references to this function either
in a function call or in a prototype must point to the same
varinfo. *)mutablesformals:varinfolist;(** Formals. These must be shared with the formals that appear in the
type of the function. Use {!setFormals} or
{!setFunctionType} to set these
formals and ensure that they are reflected in the function type.
Do not make copies of these because the body refers to them. *)mutableslocals:varinfolist;(** Locals. Does not include the sformals. Do not make copies of
these because the body refers to them. *)mutablesmaxid:int;(** Max local id. Starts at 0. *)mutablesbody:block;(** The function body. *)mutablesmaxstmtid:intoption;(** max id of a (reachable) statement
in this function, if we have
computed it. range = 0 ...
(smaxstmtid-1). This is computed by
{!computeCFGInfo}. *)mutablesallstmts:stmtlist;(** After you call {!computeCFGInfo}
this field is set to contain all
statements in the function *)}(** A block is a sequence of statements with the control falling through from
one element to the next *)andblock={mutablebattrs:attributes;(** Attributes for the block *)mutablebstmts:stmtlist;(** The statements comprising the block*)}(** Statements.
The statement is the structural unit in the control flow graph. Use mkStmt
to make a statement and then fill in the fields. *)andstmt={mutablelabels:labellist;(** Whether the statement starts with
some labels, case statements or
default statement *)mutableskind:stmtkind;(** The kind of statement *)(* Now some additional control flow information. Initially this is not
filled in. *)mutablesid:int;(** A number (>= 0) that is unique
in a function. *)mutablesuccs:stmtlist;(** The successor statements. They can
always be computed from the skind
and the context in which this
statement appears *)mutablepreds:stmtlist;(** The inverse of the succs function*)mutablefallthrough:stmtoption;(** The fallthrough successor statement computed from the context of this statement in {!computeCFGInto}. Useful for the syntactic successor of Goto and Loop. *)}(** Labels *)andlabel=Labelofstring*location*bool(** A real label. If the bool is "true", the label is from the
input source program. If the bool is "false", the label was
created by CIL or some other transformation *)|Caseofexp*location*location(** A case statement. Second location is just for label. *)|CaseRangeofexp*exp*location*location(** A case statement corresponding to a
range of values. Second location is just for label. *)|Defaultoflocation*location(** A default statement. Second location is just for label. *)(* The various kinds of statements *)andstmtkind=|Instrofinstrlist(** A group of instructions that do not
contain control flow. Control
implicitly falls through. *)|Returnofexpoption*location*location(** The return statement. This is a
leaf in the CFG.
Second location is just for expression. *)|Gotoofstmtref*location(** A goto statement. Appears from
actual goto's in the code. *)|ComputedGotoofexp*location|Breakoflocation(** A break to the end of the nearest
enclosing Loop or Switch *)|Continueoflocation(** A continue to the start of the
nearest enclosing [Loop] *)|Ifofexp*block*block*location*location(** A conditional.
Two successors, the "then" and
the "else" branches. Both
branches fall-through to the
successor of the If statement.
Second location is just for expression. *)|Switchofexp*block*(stmtlist)*location*location(** A switch statement. The block
contains within all of the cases.
We also have direct pointers to the
statements that implement the
cases. Which cases they implement
you can get from the labels of the
statement.
Second location is just for expression. *)|Loopofblock*location*location*(stmtoption)*(stmtoption)(** A [while(1)] loop. The
termination test is implemented
in the body of a loop using a
[Break] statement. If
prepareCFG has been called, the
first stmt option will point to
the stmt containing the
continue label for this loop
and the second will point to
the stmt containing the break
label for this loop.
Second location is just for expression. *)|Blockofblock(** Just a block of statements. Use it
as a way to keep some attributes
local *)(** Instructions. They may cause effects directly but may not have control
flow.*)andinstr=Setoflval*exp*location*location(** An assignment. A cast is present
if the exp has different type
from lval.
Second location is just for expression when inside condition. *)|VarDeclofvarinfo*location(** "Instruction" in the location where a varinfo was declared.
All varinfos for which such a VarDecl instruction exists have
vhasdeclinstruction set to true.
The motivation for the addition of this instruction was to
support VLAs for which declarations can not be pulled up like
CIL used to do. *)|Calloflvaloption*exp*explist*location*location(** optional: result is an lval. A cast might be
necessary if the declared result type of the
function is not the same as that of the
destination. If the function is declared then
casts are inserted for those arguments that
correspond to declared formals. (The actual
number of arguments might be smaller or larger
than the declared number of arguments. C allows
this.) If the type of the result variable is not
the same as the declared type of the function
result then an implicit cast exists.
Second location is just for expression when inside condition. *)(* See the GCC specification for the meaning of ASM.
If the source is MS VC then only the templates
are used *)(* sm: I've added a notes.txt file which contains more
information on interpreting Asm instructions *)|Asmofattributes*(* Really only const and volatile can appear
here *)stringlist*(* templates (CR-separated) *)(stringoption*string*lval)list*(* outputs must be lvals with
optional names and constraints.
I would like these
to be actually variables, but I
run into some trouble with ASMs
in the Linux sources *)(stringoption*string*exp)list*(* inputs with optional names and constraints *)stringlist*(* register clobbers *)location(** An inline assembly instruction. The arguments are (1) a list of
attributes (only const and volatile can appear here and only for
GCC), (2) templates (CR-separated), (3) a list of
outputs, each of which is an lvalue with a constraint, (4) a list
of input expressions along with constraints, (5) clobbered
registers, and (5) location information *)(** Describes a location in a source file *)andlocation={line:int;(** The line number. -1 means "do not know" *)file:string;(** The name of the source file*)byte:int;(** The byte position in the source file *)column:int;(** The column number *)endLine:int;(** End line number. Negative means unknown. *)endByte:int;(** End byte position. Negative means unknown. *)endColumn:int;(** End column number. Negative means unknown. *)synthetic:bool;(** Synthetic location, doesn't necessarily precisely correspond to a location in original source code, e.g. due to CIL transformations.
@see <https://github.com/goblint/cil/pull/98> for some examples. *)}(* Type signatures. Two types are identical iff they have identical
signatures *)andtypsig=TSArrayoftypsig*cilintoption*attributelist|TSPtroftypsig*attributelist|TSCompofbool*string*attributelist|TSFunoftypsig*typsiglistoption*bool*attributelist|TSEnumofstring*attributelist|TSBaseoftypletlocUnknown={line=-1;file="";byte=-1;column=-1;endLine=-1;endByte=-1;endColumn=-1;synthetic=true;}(* A reference to the current location *)letcurrentLoc:locationref=reflocUnknown(* A reference to the current expression location *)letcurrentExpLoc:locationref=reflocUnknown(* A reference to the current global being visited *)letcurrentGlobal:globalref=ref(GText"dummy")letcompareLoc(a:location)(b:location):int=letnamecmp=comparea.fileb.fileinifnamecmp!=0thennamecmpelseletlinecmp=a.line-b.lineiniflinecmp!=0thenlinecmpelseletcolumncmp=a.column-b.columninifcolumncmp!=0thencolumncmpelseletbytecmp=a.byte-b.byteinifbytecmp!=0thenbytecmpelseletendLinecmp=a.endLine-b.endLineinifendLinecmp!=0thenendLinecmpelseletendColumncmp=a.endColumn-b.endColumninifendColumncmp!=0thenendColumncmpelsea.endByte-b.endByteletargsToList:(string*typ*attributes)listoption->(string*typ*attributes)list=functionNone->[]|Someal->al(* A hack to allow forward reference of d_exp *)letpd_exp:(unit->exp->doc)ref=ref(fun_->E.s(E.bug"pd_exp not initialized"))letpd_type:(unit->typ->doc)ref=ref(fun_->E.s(E.bug"pd_type not initialized"))letpd_attr:(unit->attribute->doc)ref=ref(fun_->E.s(E.bug"pd_attr not initialized"))(** Different visiting actions. 'a will be instantiated with [exp], [instr],
etc. *)type'avisitAction=SkipChildren(** Do not visit the children. Return
the node as it is. *)|DoChildren(** Continue with the children of this
node. Rebuild the node on return
if any of the children changes
(use == test) *)|ChangeToof'a(** Replace the expression with the
given one *)|ChangeDoChildrenPostof'a*('a->'a)(** First consider that the entire
exp is replaced by the first
parameter. Then continue with
the children. On return rebuild
the node if any of the children
has changed and then apply the
function on the node *)(* sm/gn: cil visitor interface for traversing Cil trees. *)(* Use visitCilStmt and/or visitCilFile to use this. *)(* Some of the nodes are changed in place if the children are changed. Use
one of Change... actions if you want to copy the node *)(** A visitor interface for traversing CIL trees. Create instantiations of
this type by specializing the class {!nopCilVisitor}. *)classtypecilVisitor=objectmethodvvdec:varinfo->varinfovisitAction(** Invoked for each variable declaration. The subtrees to be traversed
are those corresponding to the type and attributes of the variable.
Note that variable declarations are all the [GVar], [GVarDecl], [GFun],
all the [varinfo] in formals of function types, and the formals and
locals for function definitions. This means that the list of formals
in a function definition will be traversed twice, once as part of the
function type and second as part of the formals in a function
definition. *)methodvvrbl:varinfo->varinfovisitAction(** Invoked on each variable use. Here only the [SkipChildren] and
[ChangeTo] actions make sense since there are no subtrees. Note that
the type and attributes of the variable are not traversed for a
variable use *)methodvexpr:exp->expvisitAction(** Invoked on each expression occurrence. The subtrees are the
subexpressions, the types (for a [Cast] or [SizeOf] expression) or the
variable use. *)methodvlval:lval->lvalvisitAction(** Invoked on each lvalue occurrence *)methodvoffs:offset->offsetvisitAction(** Invoked on each offset occurrence that is *not* as part
of an initializer list specification, i.e. in an lval or
recursively inside an offset. *)methodvinitoffs:offset->offsetvisitAction(** Invoked on each offset appearing in the list of a
CompoundInit initializer. *)methodvinst:instr->instrlistvisitAction(** Invoked on each instruction occurrence. The [ChangeTo] action can
replace this instruction with a list of instructions *)methodvstmt:stmt->stmtvisitAction(** Control-flow statement. *)methodvblock:block->blockvisitAction(** Block. Replaced in
place. *)methodvfunc:fundec->fundecvisitAction(** Function definition.
Replaced in place. *)methodvglob:global->globallistvisitAction(** Global (vars, types,
etc.) *)methodvinit:varinfo->offset->init->initvisitAction(** Initializers for globals,
pass the global where this
occurs, and the offset *)methodvtype:typ->typvisitAction(** Use of some type. Note
that for structure/union
and enumeration types the
definition of the
composite type is not
visited. Use [vglob] to
visit it. *)methodvattr:attribute->attributelistvisitAction(** Attribute. Each attribute can be replaced by a list *)methodvattrparam:attrparam->attrparamvisitAction(** Attribute parameters. *)(** Add here instructions while visiting to queue them to
precede the current statement or instruction being processed *)methodqueueInstr:instrlist->unit(** Gets the queue of instructions and resets the queue *)methodunqueueInstr:unit->instrlistend(* the default visitor does nothing at each node, but does *)(* not stop; hence they return true *)classnopCilVisitor:cilVisitor=objectmethodvvrbl(v:varinfo)=DoChildren(* variable *)methodvvdec(v:varinfo)=DoChildren(* variable
declaration *)methodvexpr(e:exp)=DoChildren(* expression *)methodvlval(l:lval)=DoChildren(* lval (base is 1st
field) *)methodvoffs(o:offset)=DoChildren(* lval or recursive offset *)methodvinitoffs(o:offset)=DoChildren(* initializer offset *)methodvinst(i:instr)=DoChildren(* imperative instruction *)methodvstmt(s:stmt)=DoChildren(* control-flow statement *)methodvblock(b:block)=DoChildrenmethodvfunc(f:fundec)=DoChildren(* function definition *)methodvglob(g:global)=DoChildren(* global (vars, types, etc.) *)methodvinit(forg:varinfo)(off:offset)(i:init)=DoChildren(* global initializers *)methodvtype(t:typ)=DoChildren(* use of some type *)methodvattr(a:attribute)=DoChildrenmethodvattrparam(a:attrparam)=DoChildrenvalmutableinstrQueue=[]methodqueueInstr(il:instrlist)=List.iter(funi->instrQueue<-i::instrQueue)ilmethodunqueueInstr()=letres=List.revinstrQueueininstrQueue<-[];resendletassertEmptyQueuevis=ifvis#unqueueInstr()<>[]then(* Either a visitor inserted an instruction somewhere that it shouldn't
have (i.e. at the top level rather than inside of a statement), or
there's a bug in the visitor engine. *)E.s(E.bug"Visitor's instruction queue is not empty.\n You should only use queueInstr inside a function body!");()letlu=locUnknown(* sm: utility *)letstartsWith(prefix:string)(s:string):bool=(letprefixLen=(String.lengthprefix)in(String.lengths)>=prefixLen&&(String.subs0prefixLen)=prefix)letendsWith(suffix:string)(s:string):bool=letsuffixLen=String.lengthsuffixinletsLen=String.lengthsinsLen>=suffixLen&&(String.subs(sLen-suffixLen)suffixLen)=suffixletstripUnderscores(s:string):string=if(startsWith"__"s)&&(endsWith"__"s)thenString.subs2((String.lengths)-4)elsesletget_instrLoc(inst:instr)=matchinstwithSet(_,_,loc,_)->loc|Call(_,_,_,loc,_)->loc|Asm(_,_,_,_,_,loc)->loc|VarDecl(_,loc)->locletget_globalLoc(g:global)=matchgwith|GFun(_,l)->(l)|GType(_,l)->(l)|GEnumTag(_,l)->(l)|GEnumTagDecl(_,l)->(l)|GCompTag(_,l)->(l)|GCompTagDecl(_,l)->(l)|GVarDecl(_,l)->(l)|GVar(_,_,l)->(l)|GAsm(_,l)->(l)|GPragma(_,l)->(l)|GText(_)->locUnknownletrecget_stmtLoc(statement:stmtkind)=matchstatementwithInstr([])->lu|Instr(hd::tl)->get_instrLoc(hd)|Return(_,loc,_)->loc|Goto(_,loc)->loc|ComputedGoto(_,loc)->loc|Break(loc)->loc|Continue(loc)->loc|If(_,_,_,loc,_)->loc|Switch(_,_,_,loc,_)->loc|Loop(_,loc,_,_,_)->loc|Blockb->ifb.bstmts==[]thenluelseget_stmtLoc((List.hdb.bstmts).skind)(* The next variable identifier to use. Counts up *)letnextGlobalVID=ref1(* The next compinfo identifier to use. Counts up. *)letnextCompinfoKey=ref1(* Some error reporting functions *)letd_loc(_:unit)(loc:location):doc=textloc.file++chr':'++numloc.lineletd_thisloc(_:unit):doc=d_loc()!currentLocleterror(fmt:('a,unit,doc)format):'a=letfd=E.hadErrors:=true;ignore(eprintf"%t: Error: %a@!"d_thislocinsertd);nilinPretty.gprintfffmtletunimp(fmt:('a,unit,doc)format):'a=letfd=E.hadErrors:=true;ignore(eprintf"%t: Unimplemented: %a@!"d_thislocinsertd);nilinPretty.gprintfffmtletbug(fmt:('a,unit,doc)format):'a=letfd=E.hadErrors:=true;ignore(eprintf"%t: Bug: %a@!"d_thislocinsertd);E.showContext();nilinPretty.gprintfffmtleterrorLoc(loc:location)(fmt:('a,unit,doc)format):'a=letfd=E.hadErrors:=true;ignore(eprintf"%a: Error: %a@!"d_loclocinsertd);E.showContext();nilinPretty.gprintfffmtletwarn(fmt:('a,unit,doc)format):'a=letfd=ignore(eprintf"%t: Warning: %a@!"d_thislocinsertd);nilinPretty.gprintfffmtletwarnOpt(fmt:('a,unit,doc)format):'a=letfd=if!E.warnFlagthenignore(eprintf"%t: Warning: %a@!"d_thislocinsertd);nilinPretty.gprintfffmtletwarnContext(fmt:('a,unit,doc)format):'a=letfd=ignore(eprintf"%t: Warning: %a@!"d_thislocinsertd);E.showContext();nilinPretty.gprintfffmtletwarnContextOpt(fmt:('a,unit,doc)format):'a=letfd=if!E.warnFlagthenignore(eprintf"%t: Warning: %a@!"d_thislocinsertd);E.showContext();nilinPretty.gprintfffmtletwarnLoc(loc:location)(fmt:('a,unit,doc)format):'a=letfd=ignore(eprintf"%a: Warning: %a@!"d_loclocinsertd);E.showContext();nilinPretty.gprintfffmtletzero=Const(CInt(zero_cilint,IInt,None))(** Given the character c in a (CChr c), sign-extend it to 32 bits.
(This is the official way of interpreting character constants, according to
ISO C 6.4.4.4.10, which says that character constants are chars cast to ints)
Returns CInt(sign-extended c, IInt, None) *)letcharConstToInt(c:char):constant=letc'=Char.codecinletvalue=ifc'<128thencilint_of_intc'elsecilint_of_int(c'-256)inCInt(value,IInt,None)(** Convert a 64-bit int to an OCaml int, or raise an exception if that
can't be done. *)leti64_to_int(i:int64):int=leti':int=Int64.to_intiin(* i.e. i' = i mod 2^31 *)ifi=Int64.of_inti'theni'elseE.s(E.unimp"%a: Int constant too large: %Ld\n"d_loc!currentLoci)letcilint_to_int(i:cilint):int=tryint_of_cilintiwith_->E.s(E.unimp"%a: Int constant too large: %s\n"d_loc!currentLoc(string_of_cilinti))letvoidType=TVoid([])letintType=TInt(IInt,[])letuintType=TInt(IUInt,[])letlongType=TInt(ILong,[])letulongType=TInt(IULong,[])letcharType=TInt(IChar,[])letboolType=TInt(IBool,[])letcharPtrType=TPtr(charType,[])letcharConstPtrType=TPtr(TInt(IChar,[Attr("const",[]);Attr("pconst",[])]),[])letstringLiteralType=charPtrTypeletvoidPtrType=TPtr(voidType,[])letintPtrType=TPtr(intType,[])letuintPtrType=TPtr(uintType,[])letboolPtrType=TPtr(boolType,[])letdoubleType=TFloat(FDouble,[])(* An integer type that fits pointers. Initialized by initCIL *)letupointType=refvoidType(* An integer type that fits a pointer difference. Initialized by initCIL *)letptrdiffType=refvoidType(* Integer types that fit wchar_t, char16_t, and char32_t. Initialized by initCIL *)letwcharKind=refICharletwcharType=refvoidTypeletchar16Kind=refICharletchar16Type=refvoidTypeletchar32Kind=refICharletchar32Type=refvoidType(* An integer type that is the type of sizeof. Initialized by initCIL *)lettypeOfSizeOf=refvoidTypeletkindOfSizeOf=refIUIntletinitCIL_called=reffalse(** Returns true if and only if the given integer type is signed. *)letisSigned=function|IBool|IUChar|IUShort|IUInt|IULong|IULongLong|IUInt128->false|ISChar|IShort|IInt|ILong|ILongLong|IInt128->true|IChar->not!M.theMachine.M.char_is_unsignedletmkStmt(sk:stmtkind):stmt={skind=sk;labels=[];sid=-1;succs=[];preds=[];fallthrough=None}letmkBlock(slst:stmtlist):block={battrs=[];bstmts=slst;}letmkEmptyStmt()=mkStmt(Instr[])letmkStmtOneInstr(i:instr)=mkStmt(Instr[i])letdummyInstr=(Asm([],["dummy statement!!"],[],[],[],lu))letdummyStmt=mkStmt(Instr[dummyInstr])letcompactStmts(b:stmtlist):stmtlist=(* Try to compress statements. Scan the list of statements and remember
the last instrunction statement encountered, along with a Clist of
instructions in it. *)letreccompress(lastinstrstmt:stmt)(* Might be dummStmt *)(lastinstrs:instrClist.clist)(body:stmtlist)=letfinishLast(tail:stmtlist):stmtlist=iflastinstrstmt==dummyStmtthentailelsebeginlastinstrstmt.skind<-Instr(Clist.toListlastinstrs);lastinstrstmt::tailendinmatchbodywith[]->finishLast[]|({skind=Instril;_}ass)::rest->letils=Clist.fromListiliniflastinstrstmt!=dummyStmt&&s.labels==[]thencompresslastinstrstmt(Clist.appendlastinstrsils)restelsefinishLast(compresssilsrest)|{skind=Blockb;labels=[];_}::restwhenb.battrs=[]->compresslastinstrstmtlastinstrs(b.bstmts@rest)|s::rest->letres=s::compressdummyStmtClist.emptyrestinfinishLastresincompressdummyStmtClist.emptyb(** Construct sorted lists of attributes ***)letrecaddAttribute(Attr(an,_)asa:attribute)(al:attributes)=letrecinsertSorted=function[]->[a]|((Attr(an0,_)asa0)::rest)asl->ifan<an0thena::lelseifUtil.equalsaa0thenl(* Do not add if already in there *)elsea0::insertSortedrest(* Make sure we see all attributes with
this name *)ininsertSortedal(** The second attribute list is sorted *)andaddAttributesal0(al:attributes):attributes=ifal0==[]thenalelseList.fold_left(funacca->addAttributeaacc)alal0anddropAttribute(an:string)(al:attributes)=List.filter(fun(Attr(an',_))->an<>an')alanddropAttributes(anl:stringlist)(al:attributes)=List.fold_left(funaccan->dropAttributeanacc)alanlandfilterAttributes(s:string)(al:attributelist):attributelist=List.filter(fun(Attr(an,_))->an=s)al(* sm: *)lethasAttributesal=(filterAttributessal<>[])typeattributeClass=AttrName(* Attribute of a name. *)|AttrFunType(* Attribute of a function type. *)|AttrType(* Attribute of a type *)(* This table contains the mapping of predefined attributes to classes.
Extend this table with more attributes as you need. This table is used to
determine how to associate attributes with names or type during cabs2cil
conversion *)letattributeHash:(string,attributeClass)H.t=lettable=H.create13inList.iter(funa->H.addtableaAttrName)["section";"constructor";"destructor";"unused";"used";"weak";"no_instrument_function";"alias";"no_check_memory_usage";"exception";"model";(* "restrict"; *)"aconst";"__asm__"(* Gcc uses this to specify the name to be used in
assembly for a global *)];(* MSVC declspec attributes that are also supported by GCC *)List.iter(funa->H.addtableaAttrName)["thread";"naked";"dllimport";"dllexport";"selectany";"nothrow";"property";"noreturn";"align"];List.iter(funa->H.addtableaAttrFunType)["format";"regparm";"longcall";"noinline";"always_inline";"gnu_inline";"leaf";"artificial";"warn_unused_result";"nonnull";];List.iter(funa->H.addtableaAttrFunType)["stdcall";"cdecl";"fastcall"];List.iter(funa->H.addtableaAttrType)["const";"volatile";"restrict";"mode"];table(* Partition the attributes into classes *)letpartitionAttributes~(default:attributeClass)(attrs:attributelist):attributelist*attributelist*attributelist=letrecloop(n,f,t)=function[]->n,f,t|(Attr(an,_)asa)::rest->match(tryH.findattributeHashanwithNot_found->default)withAttrName->loop(addAttributean,f,t)rest|AttrFunType->loop(n,addAttributeaf,t)rest|AttrType->loop(n,f,addAttributeat)restinloop([],[],[])attrs(* Get the full name of a comp *)letcompFullNamecomp=(ifcomp.cstructthen"struct "else"union ")^comp.cnameletmissingFieldName="___missing_field_name"(** Creates a a (potentially recursive) composite type. Make sure you add a
GTag for it to the file! **)letmkCompInfo(isstruct:bool)(n:string)(* fspec is a function that when given a forward
representation of the structure type constructs the type of
the fields. The function can ignore this argument if not
constructing a recursive type. *)(mkfspec:compinfo->(string*typ*intoption*attributelist*location)list)(a:attributelist):compinfo=(* make a new name for anonymous structs *)ifn=""thenE.s(E.bug"mkCompInfo: missing structure name\n");(* Make a new self cell and a forward reference *)letcomp={cstruct=isstruct;cname="";ckey=0;cfields=[];cattr=a;creferenced=false;(* Make this compinfo undefined by default *)cdefined=false;}incomp.cname<-n;comp.ckey<-!nextCompinfoKey;incrnextCompinfoKey;letflds=Util.list_map(fun(fn,ft,fb,fa,fl)->{fcomp=comp;ftype=ft;fname=fn;fbitfield=fb;fattr=fa;floc=fl})(mkfspeccomp)incomp.cfields<-flds;ifflds<>[]thencomp.cdefined<-true;comp(** Make a copy of a compinfo, changing the name and the key *)letcopyCompInfo(ci:compinfo)(n:string):compinfo=letci'={ciwithcname=n;ckey=!nextCompinfoKey;}inincrnextCompinfoKey;(* Copy the fields and set the new pointers to parents *)ci'.cfields<-Util.list_map(funf->{fwithfcomp=ci'})ci'.cfields;ci'(**** Utility functions ******)letrectypeAttrs=functionTVoida->a|TInt(_,a)->a|TFloat(_,a)->a|TNamed(t,a)->addAttributesa(typeAttrst.ttype)|TPtr(_,a)->a|TArray(_,_,a)->a|TComp(comp,a)->addAttributescomp.cattra|TEnum(enum,a)->addAttributesenum.eattra|TFun(_,_,_,a)->a|TBuiltin_va_lista->a(** [typeAttrs], which doesn't add inner attributes. *)lettypeAttrsOuter=function|TVoida->a|TInt(_,a)->a|TFloat(_,a)->a|TNamed(_,a)->a|TPtr(_,a)->a|TArray(_,_,a)->a|TComp(_,a)->a|TEnum(_,a)->a|TFun(_,_,_,a)->a|TBuiltin_va_lista->aletsetTypeAttrsta=matchtwithTVoid_->TVoida|TInt(i,_)->TInt(i,a)|TFloat(f,_)->TFloat(f,a)|TNamed(t,_)->TNamed(t,a)|TPtr(t',_)->TPtr(t',a)|TArray(t',l,_)->TArray(t',l,a)|TComp(comp,_)->TComp(comp,a)|TEnum(enum,_)->TEnum(enum,a)|TFun(r,args,v,_)->TFun(r,args,v,a)|TBuiltin_va_list_->TBuiltin_va_listalettypeAddAttributesa0t=beginmatcha0with|[]->(* no attributes, keep same type *)t|_->(* anything else: add a0 to existing attributes *)letadd(a:attributes)=addAttributesa0ainmatchtwithTVoida->TVoid(adda)|TInt(ik,a)->TInt(ik,adda)|TFloat(fk,a)->TFloat(fk,adda)|TEnum(enum,a)->TEnum(enum,adda)|TPtr(t,a)->TPtr(t,adda)|TArray(t,l,a)->TArray(t,l,adda)|TFun(t,args,isva,a)->TFun(t,args,isva,adda)|TComp(comp,a)->TComp(comp,adda)|TNamed(t,a)->TNamed(t,adda)|TBuiltin_va_lista->TBuiltin_va_list(adda)endlettypeRemoveAttributes(anl:stringlist)t=letdrop(al:attributes)=dropAttributesanlalinmatchtwithTVoida->TVoid(dropa)|TInt(ik,a)->TInt(ik,dropa)|TFloat(fk,a)->TFloat(fk,dropa)|TEnum(enum,a)->TEnum(enum,dropa)|TPtr(t,a)->TPtr(t,dropa)|TArray(t,l,a)->TArray(t,l,dropa)|TFun(t,args,isva,a)->TFun(t,args,isva,dropa)|TComp(comp,a)->TComp(comp,dropa)|TNamed(t,a)->TNamed(t,dropa)|TBuiltin_va_lista->TBuiltin_va_list(dropa)(** Partition attributes into type qualifiers and non type qualifiers. *)letpartitionQualifierAttributesal=List.partition(function|Attr(("const"|"volatile"|"restrict"),[])->true|_->false)al(** Remove top-level type qualifiers from type. *)letremoveOuterQualifierAttributest=leta=typeAttrsOutertinlet(_,a')=partitionQualifierAttributesainsetTypeAttrsta'letunrollType(t:typ):typ=letrecwithAttrs(al:attributes)(t:typ):typ=matchtwithTNamed(r,a')->withAttrs(addAttributesala')r.ttype|x->typeAddAttributesalxinwithAttrs[]tletrecunrollTypeDeep(t:typ):typ=letrecwithAttrs(al:attributes)(t:typ):typ=matchtwithTNamed(r,a')->withAttrs(addAttributesala')r.ttype|TPtr(t,a')->TPtr(unrollTypeDeept,addAttributesala')|TArray(t,l,a')->TArray(unrollTypeDeept,l,addAttributesala')|TFun(rt,args,isva,a')->TFun(unrollTypeDeeprt,(matchargswithNone->None|Someargl->Some(Util.list_map(fun(an,at,aa)->(an,unrollTypeDeepat,aa))argl)),isva,addAttributesala')|x->typeAddAttributesalxinwithAttrs[]tletisVoidTypet=matchunrollTypetwithTVoid_->true|_->falseletisVoidPtrTypet=matchunrollTypetwithTPtr(tau,_)whenisVoidTypetau->true|_->false(* get the typ of __real__(e) or __imag__(e) for e of typ t*)lettypeOfRealAndImagComponentst=matchunrollTypetwith|TInt_->t|TFloat(fkind,attrs)->letnewfkind=function|FFloat->FFloat(* [float] *)|FDouble->FDouble(* [double] *)|FLongDouble->FLongDouble(* [long double] *)|FFloat128->FFloat128|FFloat16->FFloat16|FComplexFloat->FFloat|FComplexDouble->FDouble|FComplexLongDouble->FLongDouble|FComplexFloat128->FFloat128|FComplexFloat16->FFloat16inTFloat(newfkindfkind,attrs)|_->E.s(E.bug"unexpected non-numerical type for argument to __real__/__imag__ ")(** for an fkind, return the corresponding complex fkind *)letgetComplexFkind=function|FFloat->FComplexFloat|FDouble->FComplexDouble|FLongDouble->FComplexLongDouble|FFloat128->FComplexFloat128|FFloat16->FComplexFloat16|FComplexFloat->FComplexFloat|FComplexDouble->FComplexDouble|FComplexLongDouble->FComplexLongDouble|FComplexFloat128->FComplexFloat128|FComplexFloat16->FComplexFloat16letvarvi:lval=(Varvi,NoOffset)(* let assign vi e = Instrs(Set (var vi, e), lu) *)letmkStrings=Const(CStr(s,No_encoding))letmkWhile~(guard:exp)~(body:stmtlist):stmtlist=(* Do it like this so that the pretty printer recognizes it *)[mkStmt(Loop(mkBlock(mkStmt(If(guard,mkBlock[mkEmptyStmt()],mkBlock[mkStmt(Breaklu)],lu,lu))::body),lu,lu,None,None))]letmkFor~(start:stmtlist)~(guard:exp)~(next:stmtlist)~(body:stmtlist):stmtlist=(start@(mkWhile~guard:guard~body:(body@next)))letmkForIncr~(iter:varinfo)~(first:exp)~stopat:(past:exp)~(incr:exp)~(body:stmtlist):stmtlist=(* See what kind of operator we need *)letcompop,nextop=matchunrollTypeiter.vtypewithTPtr_->Lt,PlusPI|_->Lt,PlusAinmkFor~start:[mkStmt(Instr[(Set(variter,first,lu,lu))])]~guard:(BinOp(compop,Lval(variter),past,intType))~next:[mkStmt(Instr[(Set(variter,(BinOp(nextop,Lval(variter),incr,iter.vtype)),lu,lu))])]~body:bodyletrecstripCasts(e:exp)=matchewithCastE(_,e')->stripCastse'|_->e(* the name of the C function we call to get ccgr ASTs
external parse : string -> file = "cil_main"
*)(*
Pretty Printing
*)letd_ikind()=functionIChar->text"char"|ISChar->text"signed char"|IUChar->text"unsigned char"|IBool->text"_Bool"|IInt->text"int"|IUInt->text"unsigned int"|IShort->text"short"|IUShort->text"unsigned short"|ILong->text"long"|IULong->text"unsigned long"|ILongLong->text"long long"|IULongLong->text"unsigned long long"|IInt128->text"__int128"|IUInt128->text"unsigned __int128"letd_fkind()=functionFFloat->text"float"|FDouble->text"double"|FLongDouble->text"long double"|FFloat128->text"_Float128"|FFloat16->text"_Float16"|FComplexFloat->text"_Complex float"|FComplexDouble->text"_Complex double"|FComplexLongDouble->text"_Complex long double"|FComplexFloat128->text"_Complex _Float128"|FComplexFloat16->text"_Complex _Float16"letd_storage()=functionNoStorage->nil|Static->text"static "|Extern->text"extern "|Register->text"register "(* sm: need this value below *)letmostNeg32BitInt:cilint=cilint_of_string"-0x80000000"letmostNeg64BitInt:cilint=cilint_of_string"-0x8000000000000000"letbytesSizeOfInt(ik:ikind):int=matchikwith|IChar|ISChar|IUChar->1|IBool->!M.theMachine.M.sizeof_bool|IInt|IUInt->!M.theMachine.M.sizeof_int|IShort|IUShort->!M.theMachine.M.sizeof_short|ILong|IULong->!M.theMachine.M.sizeof_long|ILongLong|IULongLong->!M.theMachine.M.sizeof_longlong|IInt128|IUInt128->16(* constant *)letd_const()c=matchcwithCInt(_,_,Somes)->texts(* Always print the text if there is one *)|CInt(i,ik,None)->(* We must make sure to capture the type of the constant. For some
constants this is done with a suffix, for others with a cast prefix.*)letsuffix:string=matchikwithIUInt->"U"|ILong->"L"|IULong->"UL"|ILongLong->"LL"|IULongLong->"ULL"(* if long long is 128 bit we can use its suffix, otherwise unsupported by GCC, see https://github.com/goblint/cil/issues/41#issuecomment-893291878 *)|IInt128when!M.theMachine.M.sizeof_longlong=16->"LL"|IUInt128when!M.theMachine.M.sizeof_longlong=16->"ULL"|_->""(* TODO warn/fail? *)(* E.s (E.bug "unknown/unsupported suffix") *)inletprefix:string=ifsuffix<>""then""elseifik=IIntthen""else"("^(sprint~width:!lineLength(d_ikind()ik))^")"in(* Watch out here for negative integers that we should be printing as
large positive ones *)ifcompare_cilintizero_cilint<0&&(not(isSignedik))thenifbytesSizeOfIntik<>8then(* I am convinced that we shall never store smaller than 64-bits
integers in negative form. -- Gabriel *)E.s(E.bug"unexpected negative unsigned integer (please report this bug)")elsetext(prefix^"0x"^Z.format"%x"i^suffix)else(if(compare_cilintimostNeg32BitInt=0)then(* sm: quirk here: if you print -2147483648 then this is two tokens *)(* in C, and the second one is too large to represent in a signed *)(* int.. so we do what's done in limits.h, and print (-2147483467-1); *)(* in gcc this avoids a warning, but it might avoid a real problem *)(* on another compiler or a 64-bit architecture *)text(prefix^"(-0x7FFFFFFF-1)")elseif(compare_cilintimostNeg64BitInt=0)then(* The same is true of the largest 64-bit negative. *)text(prefix^"(-0x7FFFFFFFFFFFFFFF-1)")elsetext(prefix^(string_of_cilinti^suffix)))|CStr(s,enc)->letprefix=matchencwithNo_encoding->""|Utf8->"u8"intext(prefix^"\""^escape_strings^"\"")|CWStr(s,st)->(* text ("L\"" ^ escape_string s ^ "\"") *)letprefix=matchstwithWchar_t->"L"|Char16_t->"u"|Char32_t->"U"in(List.fold_left(funaccelt->acc++if(elt>=Int64.zero&&elt<=(Int64.of_int255))thentext(escape_char(Char.chr(Int64.to_intelt)))else(text(Printf.sprintf"\\x%LX\""elt)++break++(text"\"")))(text(prefix^"\""))s)++text"\""(* we cannot print L"\xabcd" "feedme" as L"\xabcdfeedme" --
the former has 7 wide characters and the later has 3. *)|CChr(c)->text("'"^escape_charc^"'")|CReal(_,_,Somes)->texts|CReal(f,fsize,None)->text(string_of_floatf)++(matchfsizewithFFloat->chr'f'|FDouble->nil|FLongDouble->chr'L'|FFloat128->text"F128"|FFloat16->text"F16"|FComplexFloat->text"iF"|FComplexDouble->chr'i'|FComplexLongDouble->text"iL"|FComplexFloat128->text"iF128"|FComplexFloat16->text"iF16")|CEnum(_,s,ei)->texts(* Parentheses/precedence level. An expression "a op b" is printed
parenthesized if its parentheses level is >= that that of its context.
Identifiers have the lowest level and weakly binding operators (e.g. |)
have the largest level. The correctness criterion is that a smaller level
MUST correspond to a stronger precedence! *)letderefStarLevel=20letindexLevel=20letarrowLevel=20letaddrOfLevel=30letadditiveLevel=60letcomparativeLevel=70letbitwiseLevel=75letquestionLevel=100letassignLevel=110letgetParenthLevel(e:exp)=matchewith|Question_->questionLevel|BinOp((LAnd|LOr),_,_,_)->80(* Bit operations. *)|BinOp((BOr|BXor|BAnd),_,_,_)->bitwiseLevel(* 75 *)(* Comparisons *)|BinOp((Eq|Ne|Gt|Lt|Ge|Le),_,_,_)->comparativeLevel(* 70 *)(* Additive. Shifts can have higher
level than + or - but I want
parentheses around them *)|BinOp((MinusA|MinusPP|MinusPI|PlusA|PlusPI|IndexPI|Shiftlt|Shiftrt),_,_,_)->additiveLevel(* 60 *)(* Multiplicative *)|BinOp((Div|Mod|Mult),_,_,_)->40(* Unary *)|Real_->30|Imag_->30|CastE(_,_)->30|AddrOf(_)->30|AddrOfLabel(_)->30|StartOf(_)->30|UnOp((Neg|BNot|LNot),_,_)->30(* Lvals *)|Lval(Mem_,_)->derefStarLevel(* 20 *)|Lval(Var_,(Field_|Index_))->indexLevel(* 20 *)|SizeOf_|SizeOfE_|SizeOfStr_->20|AlignOf_|AlignOfE_->20|Lval(Var_,NoOffset)->0(* Plain variables *)|Const_->0(* Constants *)letgetParenthLevelAttrParam(a:attrparam)=(* Create an expression of the same shape, and use {!getParenthLevel} *)matchawithAInt_|AStr_|ACons_->0|ASizeOf_|ASizeOfE_|ASizeOfS_->20|AAlignOf_|AAlignOfE_|AAlignOfS_->20|AUnOp(uo,_)->getParenthLevel(UnOp(uo,zero,intType))|ABinOp(bo,_,_)->getParenthLevel(BinOp(bo,zero,zero,intType))|AAddrOf_->30|ADot_|AIndex_|AStar_->20|AQuestion_->questionLevel|AAssign_->assignLevelletisIntegralTypet=matchunrollTypetwith(TInt_|TEnum_)->true|_->falseletisArithmeticTypet=matchunrollTypetwith(TInt_|TEnum_|TFloat_)->true|_->falseletisPointerTypet=matchunrollTypetwithTPtr_->true|_->falseletisScalarTypet=isArithmeticTypet||isPointerTypetletisFunctionTypet=matchunrollTypetwithTFun_->true|_->false(**** Compute the type of an expression ****)letrectypeOf(e:exp):typ=matchewith|Const(CInt(_,ik,_))->TInt(ik,[])(* Character constants have type int. ISO/IEC 9899:1999 (E),
section 6.4.4.4 [Character constants], paragraph 10, if you
don't believe me. *)|Const(CChr_)->intType(* The type of a string is a pointer to characters ! The only case when
you would want it to be an array is as an argument to sizeof, but we
have SizeOfStr for that *)|Const(CStr(_,_))->stringLiteralType|Const(CWStr(s,st))->TPtr((matchstwithWchar_t->!wcharType|Char16_t->!char16Type|Char32_t->!char32Type),[])|Const(CReal(_,fk,_))->TFloat(fk,[])|Const(CEnum(tag,_,ei))->typeOftag|Reale->typeOfRealAndImagComponents@@typeOfe|Image->typeOfRealAndImagComponents@@typeOfe|Lval(lv)->typeOfLvallv|SizeOf_|SizeOfE_|SizeOfStr_->!typeOfSizeOf|AlignOf_|AlignOfE_->!typeOfSizeOf|UnOp(_,_,t)|BinOp(_,_,_,t)|Question(_,_,_,t)|CastE(t,_)->t|AddrOf(lv)->TPtr(typeOfLvallv,[])|AddrOfLabel(lv)->voidPtrType|StartOf(lv)->beginmatchunrollType(typeOfLvallv)withTArray(t,_,a)->TPtr(t,a)|_->E.s(E.bug"typeOf: StartOf on a non-array")endandtypeOfLval=functionVarvi,off->typeOffsetvi.vtypeoff|Memaddr,off->beginmatchunrollType(typeOfaddr)withTPtr(t,_)->typeOffsettoff|_->E.s(bug"typeOfLval: Mem on a non-pointer (%a)"!pd_expaddr)endandtypeOffsetbasetyp=letblendAttributesbaseAttrs=let(_,_,contageous)=partitionAttributes~default:AttrNamebaseAttrsintypeAddAttributescontageousinfunctionNoOffset->basetyp|Index(_,o)->beginmatchunrollTypebasetypwithTArray(t,_,baseAttrs)->letelementType=typeOffsettoinblendAttributesbaseAttrselementType|t->E.s(E.bug"typeOffset: Index on a non-array")end|Field(fi,o)->matchunrollTypebasetypwithTComp(_,baseAttrs)->letfieldType=typeOffsetfi.ftypeoinblendAttributesbaseAttrsfieldType|_->E.s(bug"typeOffset: Field on a non-compound")(**
**
** MACHINE DEPENDENT PART
**
**)exceptionSizeOfErrorofstring*typletunsignedVersionOf(ik:ikind):ikind=matchikwith|ISChar|IChar->IUChar|IShort->IUShort|IInt->IUInt|ILong->IULong|ILongLong->IULongLong|IInt128->IUInt128|_->ikletsignedVersionOf(ik:ikind):ikind=matchikwith|IUChar|IChar->ISChar|IUShort->IShort|IUInt->IInt|IULong->ILong|IULongLong->ILongLong|IUInt128->IInt128|_->ik(* Return the integer conversion rank of an integer kind *)letintRank(ik:ikind):int=matchikwith|IBool->0|IChar|ISChar|IUChar->1|IShort|IUShort->2|IInt|IUInt->3|ILong|IULong->4|ILongLong|IULongLong->5|IInt128|IUInt128->6(* Return the common integer kind of the two integer arguments, as
defined in ISO C 6.3.1.8 ("Usual arithmetic conversions") *)letcommonIntKind(ik1:ikind)(ik2:ikind):ikind=letr1=intRankik1inletr2=intRankik2inif(isSignedik1)=(isSignedik2)thenbegin(* Both signed or both unsigned. *)ifr1>r2thenik1elseik2endelsebeginletsignedKind,unsignedKind,signedRank,unsignedRank=ifisSignedik1thenik1,ik2,r1,r2elseik2,ik1,r2,r1in(* The rules for signed + unsigned get hairy.
(unsigned short + long) is converted to signed long,
but (unsigned int + long) is converted to unsigned long.*)ifunsignedRank>=signedRankthenunsignedKindelseif(bytesSizeOfIntsignedKind)>(bytesSizeOfIntunsignedKind)thensignedKindelseunsignedVersionOfsignedKindendletintKindForSize(s:int)(unsigned:bool):ikind=ifunsignedthen(* Test the most common sizes first *)ifs=1thenIUCharelseifs=!M.theMachine.M.sizeof_intthenIUIntelseifs=!M.theMachine.M.sizeof_longthenIULongelseifs=!M.theMachine.M.sizeof_shortthenIUShortelseifs=!M.theMachine.M.sizeof_longlongthenIULongLongelseifs=16thenIUInt128elseraiseNot_foundelse(* Test the most common sizes first *)ifs=1thenISCharelseifs=!M.theMachine.M.sizeof_intthenIIntelseifs=!M.theMachine.M.sizeof_longthenILongelseifs=!M.theMachine.M.sizeof_shortthenIShortelseifs=!M.theMachine.M.sizeof_longlongthenILongLongelseifs=16thenIInt128elseraiseNot_foundletfloatKindForSize(s:int)=ifs=!M.theMachine.M.sizeof_doublethenFDoubleelseifs=!M.theMachine.M.sizeof_floatthenFFloatelseifs=!M.theMachine.M.sizeof_longdoublethenFLongDoubleelseifs=!M.theMachine.M.sizeof_float128thenFFloat128elseifs=!M.theMachine.M.sizeof_float16thenFFloat16elseraiseNot_found(* Represents an integer as for a given kind. Returns a flag saying
whether any "interesting" bits were lost during truncation. By
"interesting", we mean that the lost bits were not all-0 or all-1. *)lettruncateCilint(k:ikind)(i:cilint):cilint*truncation=(* TODO: What is this "truncation"? The standard defines no such notion
and the _Bool reference is about conversions (casts). *)(* Truncations to _Bool are special: they behave like "!= 0"
ISO C99 6.3.1.2 *)ifk=IBoolthenifis_zero_cilintithenzero_cilint,NoTruncationelseone_cilint,NoTruncationelseletnrBits=8*(bytesSizeOfIntk)inifisSignedkthentruncate_signed_cilintinrBitselsetruncate_unsigned_cilintinrBitsletmkCilintIk(ik:ikind)(i:cilint):cilint=fst(truncateCilintiki)letmkCilint(ik:ikind)(i:int64):cilint=mkCilintIkik(cilint_of_int64i)(* Construct an integer constant with possible truncation *)letkintegerCilintString(k:ikind)(i:cilint)(s:stringoption):exp=leti',truncated=truncateCilintkiiniftruncated=BitTruncation&&!warnTruncatethenignore(warnOpt"Truncating integer %s to %s"(string_of_cilinti)(string_of_cilinti'));Const(CInt(i',k,s))letkintegerCilint(k:ikind)(i:cilint):exp=kintegerCilintStringkiNone(* Construct an integer constant with possible truncation *)letkinteger64(k:ikind)(i:int64):exp=kintegerCilintk(cilint_of_int64i)(* Construct an integer of a given kind. *)letkinteger(k:ikind)(i:int)=kintegerCilintk(cilint_of_inti)(** Construct an integer of kind IInt. On targets where C's 'int' is 16-bits,
the integer may get truncated. *)letinteger(i:int)=kintegerIIntiletone=integer1letmone=integer(-1)(* True if the integer fits within the kind's range *)letfitsInInt(k:ikind)(i:cilint):bool=ifk=IBoolthen((* truncateCilint is weirdly defined for IBool, like it always fits *)is_zero_cilinti||compare_cilintione_cilint=0(* only 0 and 1 fit *))else(let_,truncated=truncateCilintkiintruncated=NoTruncation)(* Return the smallest kind that will hold the integer's value. The
kind will be unsigned if the 2nd argument is true, signed
otherwise. Note that if the value doesn't fit in any of the
available types, you will get ILongLong (2nd argument false) or
IULongLong (2nd argument true). *)letintKindForValue(i:cilint)(unsigned:bool)=ifunsignedtheniffitsInIntIBoolithenIBoolelseiffitsInIntIUCharithenIUCharelseiffitsInIntIUShortithenIUShortelseiffitsInIntIUIntithenIUIntelseiffitsInIntIULongithenIULongelseiffitsInIntIUInt128ithenIUInt128elseIULongLong(* warn, IUInt128? *)elseiffitsInIntISCharithenISCharelseiffitsInIntIShortithenIShortelseiffitsInIntIIntithenIIntelseiffitsInIntILongithenILongelseiffitsInIntIInt128ithenIInt128elseILongLong(* warn, IInt128? *)(** If the given expression is an integer constant or a CastE'd
integer constant, return that constant's value as an ikind, int64 pair.
Otherwise return None. *)letrecgetInteger(e:exp):cilintoption=matchewith|Const(CInt(n,ik,_))->Some(mkCilintIkikn)|Const(CChrc)->getInteger(Const(charConstToIntc))|Const(CEnum(v,_,_))->getIntegerv|CastE(t,e)->begin(* Handle any truncation due to cast. We optimistically ignore
loss-of-precision due to floating-point casts. *)letmkIntikn=Some(fst(truncateCilintikn))inmatchunrollTypet,getIntegerewith|TInt(ik,_),Somen->mkIntikn(* "integer constant expressions" may not cast to ptr *)|TEnum(ei,_),Somen->mkIntei.ekindn|TFloat_,v->v|_,_->Noneend|_->None(** Return the (wrapped) constant i if it fits into ik without any signed overflow,
otherwise return fallback *)letconst_if_not_overflowfallbackiki=ifnot(isSignedik)thenkintegerCilintikielseleti',trunc=truncateCilintikiiniftrunc=NoTruncationthenkintegerCilintikielsefallbackletisZero(e:exp):bool=matchgetIntegerewith|Somen->is_zero_cilintn|_->falsetypeoffsetAcc={oaFirstFree:int;(* The first free bit *)oaLastFieldStart:int;(* Where the previous field started *)oaLastFieldWidth:int;(* The width of the previous field. Might not
be same as FirstFree - FieldStart because
of internal padding *)oaPrevBitPack:(int*ikind*int)option;(* If the previous fields
were packed bitfields,
the bit where packing
has started, the ikind
of the bitfield and the
width of the ikind *)}(* Hack to prevent infinite recursion in alignments *)letignoreAlignmentAttrs=reffalse(* Get the minimum alignment in bytes for a given type *)letrecalignOf_intt=letalignOfType()=matchtwith|TInt((IChar|ISChar|IUChar),_)->1|TInt(IBool,_)->!M.theMachine.M.alignof_bool|TInt((IShort|IUShort),_)->!M.theMachine.M.alignof_short|TInt((IInt|IUInt),_)->!M.theMachine.M.alignof_int|TInt((ILong|IULong),_)->!M.theMachine.M.alignof_long|TInt((ILongLong|IULongLong),_)->!M.theMachine.M.alignof_longlong|TInt((IInt128|IUInt128),_)->16(* not generated since not all architectures support 128bit ints and the value should be the same for those that do *)|TEnum(ei,_)->alignOf_int(TInt(ei.ekind,[]))|TFloat(FFloat,_)->!M.theMachine.M.alignof_float|TFloat(FDouble,_)->!M.theMachine.M.alignof_double|TFloat(FLongDouble,_)->!M.theMachine.M.alignof_longdouble|TFloat(FFloat128,_)->!M.theMachine.M.alignof_float128|TFloat(FFloat16,_)->!M.theMachine.M.alignof_float16|TFloat(FComplexFloat,_)->!M.theMachine.M.alignof_floatcomplex|TFloat(FComplexDouble,_)->!M.theMachine.M.alignof_doublecomplex|TFloat(FComplexLongDouble,_)->!M.theMachine.M.alignof_longdoublecomplex|TFloat(FComplexFloat128,_)->!M.theMachine.M.alignof_float128complex|TFloat(FComplexFloat16,_)->!M.theMachine.M.alignof_float16complex|TNamed(t,_)->alignOf_intt.ttype|TArray(t,_,_)->alignOf_intt|TPtr_|TBuiltin_va_list_->!M.theMachine.M.alignof_ptr(* For composite types get the maximum alignment of any field inside *)|TComp(c,_)->(* On GCC the zero-width fields do not contribute to the alignment. *)letrecdropZeros(afterbitfield:bool)=function|f::restwhenf.fbitfield=Some0&¬afterbitfield->dropZerosafterbitfieldrest|f::rest->f::dropZeros(f.fbitfield<>None)rest|[]->[]inletfields=dropZerosfalsec.cfieldsinList.fold_left(funsofarf->(* Bitfields with zero width do not contribute to the alignment in
GCC *)iff.fbitfield=Some0thensofarelsemaxsofar(alignOfFieldf))1fields(* These are some error cases *)|TFun_->!M.theMachine.M.alignof_fun|TVoid_ast->raise(SizeOfError("void",t))inmatchfilterAttributes"aligned"(typeAttrst)with[]->(* no __aligned__ attribute, so get the default alignment *)alignOfType()|_when!ignoreAlignmentAttrs->ignore(warn"ignoring recursive align attributes on %a"(!pd_type)t);alignOfType()|(Attr(_,[a])asat)::rest->beginifrest<>[]thenignore(warn"ignoring duplicate align attributes on %a"(!pd_type)t);matchintOfAttrparamawithSomen->n|None->ignore(warn"alignment attribute \"%a\" not understood on %a"(!pd_attr)at(!pd_type)t);alignOfType()end|Attr(_,[])::rest->(* aligned with no arg means a power of two at least as large as
any alignment on the system.*)ifrest<>[]thenignore(warn"ignoring duplicate align attributes on %a"(!pd_type)t);!M.theMachine.M.alignof_aligned|at::_->ignore(warn"alignment attribute \"%a\" not understood on %a"(!pd_attr)at(!pd_type)t);alignOfType()(* alignment of a possibly-packed struct field. *)andalignOfField(fi:fieldinfo)=letfieldIsPacked=hasAttribute"packed"fi.fattr||hasAttribute"packed"fi.fcomp.cattriniffieldIsPackedthen1elsealignOf_intfi.ftypeandintOfAttrparam(a:attrparam):intoption=letrecdoita:int=matchawithAInt(n)->n|ABinOp(Shiftlt,a1,a2)->(doita1)lsl(doita2)|ABinOp(Div,a1,a2)->(doita1)/(doita2)|ASizeOf(t)->letbs=bitsSizeOftinbs/8|AAlignOf(t)->alignOf_intt|_->raise(SizeOfError("",voidType))in(* Use ignoreAlignmentAttrs here to prevent stack overflow if a buggy
program does something like
struct s {...} __attribute__((aligned(sizeof(struct s))))
This is too conservative, but it's often enough.
*)assert(not!ignoreAlignmentAttrs);ignoreAlignmentAttrs:=true;tryletn=doitainignoreAlignmentAttrs:=false;SomenwithSizeOfError_->(* Can't compile *)ignoreAlignmentAttrs:=false;None(* GCC version *)(* Does not use the sofar.oaPrevBitPack *)andoffsetOfFieldAcc_GCC(fi:fieldinfo)(sofar:offsetAcc):offsetAcc=(* field type *)letftype=unrollTypefi.ftypeinletftypeAlign=8*alignOfFieldfiinletftypeBits=bitsSizeOfftypeinmatchftype,fi.fbitfieldwith(* A width of 0 means that we must end the current packing. It seems that
GCC pads only up to the alignment boundary for the type of this field.
*)|_,Some0->letfirstFree=addTrailingsofar.oaFirstFreeftypeAlignin{oaFirstFree=firstFree;oaLastFieldStart=firstFree;oaLastFieldWidth=0;oaPrevBitPack=None}(* A bitfield cannot span more alignment boundaries of its type than the
type itself *)|_,Somewdthiswhen(sofar.oaFirstFree+wdthis+ftypeAlign-1)/ftypeAlign-sofar.oaFirstFree/ftypeAlign>ftypeBits/ftypeAlign->letstart=addTrailingsofar.oaFirstFreeftypeAlignin{oaFirstFree=start+wdthis;oaLastFieldStart=start;oaLastFieldWidth=wdthis;oaPrevBitPack=None}(* Try a simple method. Just put the field down *)|_,Somewdthis->{oaFirstFree=sofar.oaFirstFree+wdthis;oaLastFieldStart=sofar.oaFirstFree;oaLastFieldWidth=wdthis;oaPrevBitPack=None}(* Non-bitfield *)|_,None->(* Align this field *)letnewStart=addTrailingsofar.oaFirstFreeftypeAlignin{oaFirstFree=newStart+ftypeBits;oaLastFieldStart=newStart;oaLastFieldWidth=ftypeBits;oaPrevBitPack=None;}andoffsetOfFieldAcc~(fi:fieldinfo)~(sofar:offsetAcc):offsetAcc=offsetOfFieldAcc_GCCfisofar(* The size of a type, in bits. If a struct or array, then trailing padding is
added *)andbitsSizeOft=ifnot!initCIL_calledthenE.s(E.error"You did not call Cil.initCIL before using the CIL library");matchtwith|TInt(ik,_)->8*(bytesSizeOfIntik)|TFloat(FDouble,_)->8*!M.theMachine.M.sizeof_double|TFloat(FLongDouble,_)->8*!M.theMachine.M.sizeof_longdouble|TFloat(FFloat128,_)->8*!M.theMachine.M.sizeof_float128|TFloat(FFloat16,_)->8*!M.theMachine.M.sizeof_float16|TFloat(FFloat,_)->8*!M.theMachine.M.sizeof_float|TFloat(FComplexDouble,_)->8*!M.theMachine.M.sizeof_doublecomplex|TFloat(FComplexLongDouble,_)->8*!M.theMachine.M.sizeof_longdoublecomplex|TFloat(FComplexFloat128,_)->8*!M.theMachine.M.sizeof_float128complex|TFloat(FComplexFloat16,_)->8*!M.theMachine.M.sizeof_float16complex|TFloat(FComplexFloat,_)->8*!M.theMachine.M.sizeof_floatcomplex|TEnum(ei,_)->bitsSizeOf(TInt(ei.ekind,[]))|TPtr_->8*!M.theMachine.M.sizeof_ptr|TBuiltin_va_list_->8*!M.theMachine.M.sizeof_ptr|TNamed(t,_)->bitsSizeOft.ttype|TComp(comp,_)whencomp.cfields==[]->beginifnotcomp.cdefinedthenraise(SizeOfError("abstract type",t))(*abstract type*)else0end|TComp(comp,_)whencomp.cstruct->(* Struct *)(* Go and get the last offset *)letstartAcc={oaFirstFree=0;oaLastFieldStart=0;oaLastFieldWidth=0;oaPrevBitPack=None;}inletlastoff=List.fold_left(funaccfi->offsetOfFieldAcc~fi~sofar:acc)startAcccomp.cfieldsin(* Drop e.g. the align attribute from t. For this purpose,
consider only the attributes on comp itself.*)letstructAlign=8*alignOf_int(TComp(comp,[]))inaddTrailinglastoff.oaFirstFreestructAlign|TComp(comp,_)->(* when not comp.cstruct *)(* Get the maximum of all fields *)letstartAcc={oaFirstFree=0;oaLastFieldStart=0;oaLastFieldWidth=0;oaPrevBitPack=None;}inletmax=List.fold_left(funaccfi->letlastoff=offsetOfFieldAcc~fi~sofar:startAcciniflastoff.oaFirstFree>accthenlastoff.oaFirstFreeelseacc)0comp.cfieldsin(* Add trailing by simulating adding an extra field *)addTrailingmax(8*alignOf_intt)|TArray(bt,Somelen,_)->beginmatchconstFoldtruelenwithConst(CInt(l,lk,_))->letsz=mul_cilint(mkCilintIklkl)(cilint_of_int(bitsSizeOfbt))in(* Check for overflow.
There are other places in these cil.ml that overflow can occur,
but this multiplication is the most likely to be a problem. *)ifnot(is_int_cilintsz)thenraise(SizeOfError("Array is so long that its size can't be "^"represented with an OCaml int.",t))elseaddTrailing(int_of_cilintsz)(8*alignOf_intt)|_->raise(SizeOfError("array non-constant length",t))end|TVoid_->8*!M.theMachine.M.sizeof_void|TFun_->(* On GCC the size of a function is defined *)8*!M.theMachine.M.sizeof_fun|TArray(_,None,_)->(* it seems that on GCC the size of such an
array is 0 *)0andaddTrailingnrbitsroundto=(nrbits+roundto-1)land(lnot(roundto-1))andsizeOft=tryinteger((bitsSizeOft)lsr3)withSizeOfError_->SizeOf(t)andbitsOffset(baset:typ)(off:offset):int*int=letrecloopOff(baset:typ)(width:int)(start:int)=functionNoOffset->start,width|Index(e,off)->beginletei=matchgetIntegerewithSomei->cilint_to_inti|None->raise(SizeOfError("index not constant",baset))inletbt=matchunrollTypebasetwithTArray(bt,_,_)->bt|_->E.s(E.bug"bitsOffset: Index on a non-array")inletbitsbt=bitsSizeOfbtinloopOffbtbitsbt(start+ei*bitsbt)offend|Field(f,off)whennotf.fcomp.cstruct->(* All union fields start at offset 0 *)loopOfff.ftype(bitsSizeOff.ftype)startoff|Field(f,off)->(* Construct a list of fields preceding and including this one *)letprevflds=letrecloop=function[]->E.s(E.bug"bitsOffset: Cannot find field %s in %s\n"f.fnamef.fcomp.cname)|fi'::_whenfi'==f->[fi']|fi'::rest->fi'::looprestinloopf.fcomp.cfieldsinletlastoff=List.fold_left(funaccfi'->offsetOfFieldAcc~fi:fi'~sofar:acc){oaFirstFree=0;(* Start at 0 because each struct is done
separately *)oaLastFieldStart=0;oaLastFieldWidth=0;oaPrevBitPack=None}prevfldsin(* ignore (E.log "Field %s of %s: start=%d, lastFieldStart=%d\n"
f.fname f.fcomp.cname start lastoff.oaLastFieldStart); *)loopOfff.ftypelastoff.oaLastFieldWidth(start+lastoff.oaLastFieldStart)offinloopOffbaset(bitsSizeOfbaset)0off(** Do constant folding on an expression. If the first argument is true then
will also compute compiler-dependent expressions such as sizeof.
See also {!constFoldVisitor}, which will run constFold on all
expressions in a given AST node.*)andconstFold(machdep:bool)(e:exp):exp=matchewithBinOp(bop,e1,e2,tres)->constFoldBinOpmachdepbope1e2tres|UnOp(unop,e1,tres)->begintrylettk=matchunrollTypetreswith|TInt(ik,_)->ik|TEnum(ei,_)->ei.ekind|_->raiseNot_found(* probably a float *)inmatchconstFoldmachdepe1with|Const(CInt(i,ik,s))->beginletic=mkCilintIkikiinmatchunopwithNeg->const_if_not_overflow(UnOp(Neg,Const(CInt(i,ik,s)),tres))tk(neg_cilintic)|BNot->const_if_not_overflow(UnOp(BNot,Const(CInt(i,ik,s)),tres))tk(lognot_cilintic)|LNot->ifis_zero_cilinticthenoneelsezeroend|e1c->UnOp(unop,e1c,tres)withNot_found->eend(* Characters are integers *)|Const(CChrc)->Const(charConstToIntc)|Const(CEnum(v,_,_))->constFoldmachdepv|SizeOftwhenmachdep->begintryletbs=bitsSizeOftinkinteger!kindOfSizeOf(bs/8)withSizeOfError_->eend|SizeOfEewhenmachdep->constFoldmachdep(SizeOf(typeOfe))|SizeOfStrswhenmachdep->kinteger!kindOfSizeOf(1+String.lengths)|AlignOftwhenmachdep->kinteger!kindOfSizeOf(alignOf_intt)|AlignOfEewhenmachdep->begin(* The alignment of an expression is not always the alignment of its
type. I know that for strings this is not true *)matchewithConst(CStr_)->kinteger!kindOfSizeOf!M.theMachine.M.alignof_str(* For an array, it is the alignment of the array ! *)|_->constFoldmachdep(AlignOf(typeOfe))end|CastE(it,AddrOf(Mem(CastE(TPtr(bt,_),z)),off))whenmachdep&&isZeroz->begintryletstart,width=bitsOffsetbtoffinifstartmod8<>0thenE.s(error"Using offset of bitfield");constFoldmachdep(CastE(it,(kinteger!kindOfSizeOf(start/8))))withSizeOfError_->eend|CastE(t,e)->beginmatchconstFoldmachdepe,unrollTypetwith(* Might truncate silently *)|Const(CInt(i,k,_)),TInt(nk,a)(* It's okay to drop a cast to const.
If the cast has any other attributes, leave the cast alone. *)when(dropAttributes["const";"pconst"]a)=[]->leti',_=truncateCilintnk(mkCilintIkki)inConst(CInt(i',nk,None))|e',_->CastE(t,e')end|Lvallv->Lval(constFoldLvalmachdeplv)|AddrOflv->AddrOf(constFoldLvalmachdeplv)|StartOflv->StartOf(constFoldLvalmachdeplv)|_->eandconstFoldLvalmachdep(host,offset)=letnewhost=matchhostwith|Meme->Mem(constFoldmachdepe)|Var_->hostinletrecconstFoldOffsetmachdep=function|NoOffset->NoOffset|Field(fi,offset)->Field(fi,constFoldOffsetmachdepoffset)|Index(exp,offset)->Index(constFoldmachdepexp,constFoldOffsetmachdepoffset)in(newhost,constFoldOffsetmachdepoffset)andconstFoldBinOp(machdep:bool)bope1e2tres=lete1'=constFoldmachdepe1inlete2'=constFoldmachdepe2inifisIntegralTypetresthenbeginletnewe=lettk=matchunrollTypetreswithTInt(ik,_)->ik|TEnum(ei,_)->ei.ekind|_->E.s(bug"constFoldBinOp")inletcollapse0()=kintegertk0inletcollapsee=e(*mkCast e tres*)inletshiftInBoundsi2=(* We only try to fold shifts if the second arg is positive and
less than the size of the type of the first argument.
Otherwise, the semantics are processor-dependent, so let the
compiler sort it out. *)ifmachdepthentrycompare_cilinti2zero_cilint>=0&&compare_cilinti2(cilint_of_int(bitsSizeOf(typeOfe1')))<0withSizeOfError_->falseelsefalseinletno_ov=const_if_not_overflow(BinOp(bop,e1',e2',tres))tkin(* Assume that the necessary promotions have been done *)matchbop,getIntegere1',getIntegere2'with|PlusA,Somei1,Somei2->no_ov(add_cilinti1i2)|PlusA,Somez,_whenis_zero_cilintz->collapsee2'|PlusA,_,Somezwhenis_zero_cilintz->collapsee1'|MinusA,Somei1,Somei2->no_ov(sub_cilinti1i2)|MinusA,_,Somezwhenis_zero_cilintz->collapsee1'|Mult,Somei1,Somei2->no_ov(mul_cilinti1i2)|Mult,Somez,_whenis_zero_cilintz->collapse0()|Mult,_,Somezwhenis_zero_cilintz->collapse0()|Mult,Someo,_whencompare_cilintoone_cilint=0->collapsee2'|Mult,_,Someowhencompare_cilintoone_cilint=0->collapsee1'|Div,Somei1,Somei2->begintryno_ov(div0_cilinti1i2)withDivision_by_zero->BinOp(bop,e1',e2',tres)end|Div,_,Someowhencompare_cilintoone_cilint=0->collapsee1'|Mod,Somei1,Somei2->begintryno_ov(rem_cilinti1i2)withDivision_by_zero->BinOp(bop,e1',e2',tres)end|Mod,_,Someowhencompare_cilintoone_cilint=0->collapse0()|BAnd,Somei1,Somei2->kintegerCilinttk(logand_cilinti1i2)|BAnd,Somez,_whenis_zero_cilintz->collapse0()|BAnd,_,Somezwhenis_zero_cilintz->collapse0()|BOr,Somei1,Somei2->kintegerCilinttk(logor_cilinti1i2)|BOr,Somez,_whenis_zero_cilintz->collapsee2'|BOr,_,Somezwhenis_zero_cilintz->collapsee1'|BXor,Somei1,Somei2->kintegerCilinttk(logxor_cilinti1i2)|BXor,Somez,_whenis_zero_cilintz->collapsee2'|BXor,_,Somezwhenis_zero_cilintz->collapsee1'(* C99 6.5.7 (4) *)|Shiftlt,Somei1,Somei2whenshiftInBoundsi2&¬@@isSignedtk->kintegerCilinttk(shift_left_cilinti1(int_of_cilinti2))|Shiftlt,Somei1,Somei2whencompare_cilinti1zero_cilint>=0&&shiftInBoundsi2->(* i1 has signed type and is non-negative *)const_if_not_overflow(BinOp(bop,e1',e2',tres))tk(shift_left_cilinti1(int_of_cilinti2))|Shiftlt,Somez,_whenis_zero_cilintz->collapse0()|Shiftlt,_,Somezwhenis_zero_cilintz&¬@@isSignedtk->collapsee1'|Shiftrt,Somei1,Somei2whenshiftInBoundsi2->kintegerCilinttk(shift_right_cilinti1(int_of_cilinti2))|Shiftrt,Somez,_whenis_zero_cilintz->collapse0()|Shiftrt,_,Somezwhenis_zero_cilintz->collapsee1'|Eq,Somei1,Somei2->ifcompare_cilinti1i2=0thenoneelsezero|Ne,Somei1,Somei2->ifcompare_cilinti1i2<>0thenoneelsezero|Le,Somei1,Somei2->ifcompare_cilinti1i2<=0thenoneelsezero|Ge,Somei1,Somei2->ifcompare_cilinti1i2>=0thenoneelsezero|Lt,Somei1,Somei2->ifcompare_cilinti1i2<0thenoneelsezero|Gt,Somei1,Somei2->ifcompare_cilinti1i2>0thenoneelsezero|LAnd,Somei1,_when!removeBranchingOnConstants->ifis_zero_cilinti1thencollapse0()elsecollapsee2'|LAnd,_,Somei2when!removeBranchingOnConstants->ifis_zero_cilinti2thencollapse0()elsecollapsee1'|LOr,Somei1,_when!removeBranchingOnConstants->ifis_zero_cilinti1thencollapsee2'elseone|LOr,_,Somei2when!removeBranchingOnConstants->ifis_zero_cilinti2thencollapsee1'elseone|_->BinOp(bop,e1',e2',tres)inifdebugConstFoldthenignore(E.log"Folded %a to %a\n"(!pd_exp)(BinOp(bop,e1',e2',tres))(!pd_exp)newe);neweendelseBinOp(bop,e1',e2',tres)letisArrayTypet=matchunrollTypetwithTArray_->true|_->false(** 6.3.2.3 subsection 3
An integer constant expr with value 0, or such an expr cast to void *, is called a null pointer constant. *)letisNullPtrConstante=letrecisNullPtrConstant=function|CastE(TPtr(TVoid[],[]),e)->isNullPtrConstante(* no qualifiers allowed on void or ptr *)|e->isZeroeinisNullPtrConstant(constFoldtruee)letrecisConstant=function|Const_->true|UnOp(_,e,_)->isConstante|BinOp(_,e1,e2,_)->isConstante1&&isConstante2|Question(e1,e2,e3,_)->isConstante1&&isConstante2&&isConstante3|Lval(Varvi,NoOffset)->(vi.vglob&&isArrayTypevi.vtype||isFunctionTypevi.vtype)|Lval_->false|Reale->isConstante|Image->isConstante|SizeOf_|SizeOfE_|SizeOfStr_|AlignOf_|AlignOfE_->true|CastE(_,e)->isConstante|AddrOf(Varvi,off)|StartOf(Varvi,off)->vi.vglob&&isConstantOffsetoff|AddrOf(Meme,off)|StartOf(Meme,off)->isConstante&&isConstantOffsetoff|AddrOfLabel_->trueandisConstantOffset=functionNoOffset->true|Field(fi,off)->isConstantOffsetoff|Index(e,off)->isConstante&&isConstantOffsetoffletparseInt(str:string):exp=lethasSuffixstrsuff=letl=String.lengthstrinletlsuff=String.lengthsuffinl>=lsuff&&suff=String.uppercase_ascii(String.substr(l-lsuff)lsuff)inletl=String.lengthstrin(* See if it is octal or hex *)letoctalhex=(l>=1&&String.getstr0='0')in(* The length of the suffix and a list of possible kinds. See ISO
6.4.4.1 *)lethasSuffix=hasSuffixstrinletsuffixlen,kinds=(* 128bit constants are only supported if long long is also 128bit, so we can parse those as long long *)ifhasSuffix"ULL"||hasSuffix"LLU"then3,[IULongLong]elseifhasSuffix"LL"then2,ifoctalhexthen[ILongLong;IULongLong]else[ILongLong]elseifhasSuffix"UL"||hasSuffix"LU"then2,[IULong;IULongLong]elseifhasSuffix"L"then1,ifoctalhexthen[ILong;IULong;ILongLong;IULongLong]else[ILong;ILongLong]elseifhasSuffix"U"then1,[IUInt;IULong;IULongLong]else0,ifoctalhexthen[IInt;IUInt;ILong;IULong;ILongLong;IULongLong]elseifnot(c99Mode())then[IInt;ILong;IULong;ILongLong;IULongLong]else[IInt;ILong;ILongLong](* c99mode only affects parsing of decimal integer constants without suffix
a) on machines where long and long long do not have the same size
(e.g. 32 Bit machines, 64 Bit Windows, not 64 Bit MacOS or (most? all?) 64 Bit Linux:
giving constants that are bigger than max long type long long in c99mode vs. unsigned long
if c99mode is off.
b) for constants bigger than long long producing a "Unimplemented: Cannot represent the integer"
warning in C99 mode vs. unsigned long long if c99mode is off. *)inletstart,base=ifoctalhexthenifl>=2&&(letc=String.getstr1inc='x'||c='X')then2,16else1,8else0,10inlett=String.substrstart(String.lengthstr-start-suffixlen)in(* Normal Z.of_string does not work here as 0 is not recognized as the prefix for octal here *)leti=Z.of_string_basebasetintry(* Construct an integer of the first kinds that fits. i must be POSITIVE *)letik=List.find(funik->fitsInIntiki)kindsinkintegerCilintStringiki(Somestr)withNot_found->E.s(E.unimp"Cannot represent the integer %s\n"(string_of_cilinti))letd_unop()u=matchuwithNeg->text"-"|BNot->text"~"|LNot->text"!"letd_binop()b=matchbwithPlusA|PlusPI|IndexPI->text"+"|MinusA|MinusPP|MinusPI->text"-"|Mult->text"*"|Div->text"/"|Mod->text"%"|Shiftlt->text"<<"|Shiftrt->text">>"|Lt->text"<"|Gt->text">"|Le->text"<="|Ge->text">="|Eq->text"=="|Ne->text"!="|BAnd->text"&"|BXor->text"^"|BOr->text"|"|LAnd->text"&&"|LOr->text"||"letinvalidStmt=mkStmt(Instr[])(** Construct a hash with the builtins *)letbuiltinFunctions:(string,typ*typlist*bool)H.t=H.create49(* Initialize the builtin functions after the machine has been initialized. *)letinitGccBuiltins():unit=ifnot!initCIL_calledthenE.s(bug"Call initCIL before initGccBuiltins");ifH.lengthbuiltinFunctions<>0thenE.s(bug"builtins already initialized.");leth=builtinFunctionsin(* See if we have builtin_va_list *)lethasbva=!M.theMachine.M.__builtin_va_listinletulongLongType=TInt(IULongLong,[])inletfloatType=TFloat(FFloat,[])inletlongDoubleType=TFloat(FLongDouble,[])inletvoidConstPtrType=TPtr(TVoid[Attr("const",[]);Attr("pconst",[])],[])inletsizeType=!typeOfSizeOfinletv4sfType=TFloat(FFloat,[Attr("__vector_size__",[AInt16])])inH.addh"__builtin___fprintf_chk"(intType,[voidPtrType;intType;charConstPtrType],true)(* first argument is really FILE*, not void*, but we don't want to build in the definition for FILE *);H.addh"__builtin___memcpy_chk"(voidPtrType,[voidPtrType;voidConstPtrType;sizeType;sizeType],false);H.addh"__builtin___memmove_chk"(voidPtrType,[voidPtrType;voidConstPtrType;sizeType;sizeType],false);H.addh"__builtin___mempcpy_chk"(voidPtrType,[voidPtrType;voidConstPtrType;sizeType;sizeType],false);H.addh"__builtin___memset_chk"(voidPtrType,[voidPtrType;intType;sizeType;sizeType],false);H.addh"__builtin___printf_chk"(intType,[intType;charConstPtrType],true);H.addh"__builtin___snprintf_chk"(intType,[charPtrType;sizeType;intType;sizeType;charConstPtrType],true);H.addh"__builtin___sprintf_chk"(intType,[charPtrType;intType;sizeType;charConstPtrType],true);H.addh"__builtin___stpcpy_chk"(charPtrType,[charPtrType;charConstPtrType;sizeType],false);H.addh"__builtin___strcat_chk"(charPtrType,[charPtrType;charConstPtrType;sizeType],false);H.addh"__builtin___strcpy_chk"(charPtrType,[charPtrType;charConstPtrType;sizeType],false);H.addh"__builtin___strncat_chk"(charPtrType,[charPtrType;charConstPtrType;sizeType;sizeType],false);H.addh"__builtin___strncpy_chk"(charPtrType,[charPtrType;charConstPtrType;sizeType;sizeType],false);H.addh"__builtin___vfprintf_chk"(intType,[voidPtrType;intType;charConstPtrType;TBuiltin_va_list[]],false)(* first argument is really FILE*, not void*, but we don't want to build in the definition for FILE *);H.addh"__builtin___vprintf_chk"(intType,[intType;charConstPtrType;TBuiltin_va_list[]],false);H.addh"__builtin___vsnprintf_chk"(intType,[charPtrType;sizeType;intType;sizeType;charConstPtrType;TBuiltin_va_list[]],false);H.addh"__builtin___vsprintf_chk"(intType,[charPtrType;intType;sizeType;charConstPtrType;TBuiltin_va_list[]],false);H.addh"__builtin_acos"(doubleType,[doubleType],false);H.addh"__builtin_acosf"(floatType,[floatType],false);H.addh"__builtin_acosl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_alloca"(voidPtrType,[sizeType],false);H.addh"__builtin_asin"(doubleType,[doubleType],false);H.addh"__builtin_asinf"(floatType,[floatType],false);H.addh"__builtin_asinl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_atan"(doubleType,[doubleType],false);H.addh"__builtin_atanf"(floatType,[floatType],false);H.addh"__builtin_atanl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_atan2"(doubleType,[doubleType;doubleType],false);H.addh"__builtin_atan2f"(floatType,[floatType;floatType],false);H.addh"__builtin_atan2l"(longDoubleType,[longDoubleType;longDoubleType],false);letaddSwapsizeInBits=tryassert(sizeInBitsmod8=0);letsizeInBytes=sizeInBits/8inletsizedIntType=TInt(intKindForSizesizeInBytesfalse,[])inletname=Printf.sprintf"__builtin_bswap%d"sizeInBitsinH.addhname(sizedIntType,[sizedIntType],false)withNot_found->()inaddSwap16;addSwap32;addSwap64;H.addh"__builtin_ceil"(doubleType,[doubleType],false);H.addh"__builtin_ceilf"(floatType,[floatType],false);H.addh"__builtin_ceill"(longDoubleType,[longDoubleType],false);H.addh"__builtin_cos"(doubleType,[doubleType],false);H.addh"__builtin_cosf"(floatType,[floatType],false);H.addh"__builtin_cosl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_cosh"(doubleType,[doubleType],false);H.addh"__builtin_coshf"(floatType,[floatType],false);H.addh"__builtin_coshl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_clz"(intType,[uintType],false);H.addh"__builtin_clzl"(intType,[ulongType],false);H.addh"__builtin_clzll"(intType,[ulongLongType],false);H.addh"__builtin_constant_p"(intType,[intType],false);H.addh"__builtin_ctz"(intType,[uintType],false);H.addh"__builtin_ctzl"(intType,[ulongType],false);H.addh"__builtin_ctzll"(intType,[ulongLongType],false);H.addh"__builtin_exp"(doubleType,[doubleType],false);H.addh"__builtin_expf"(floatType,[floatType],false);H.addh"__builtin_expl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_expect"(longType,[longType;longType],false);H.addh"__builtin_trap"(voidType,[],false);H.addh"__builtin_unreachable"(voidType,[],false);H.addh"__builtin_fabs"(doubleType,[doubleType],false);H.addh"__builtin_fabsf"(floatType,[floatType],false);H.addh"__builtin_fabsl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_ffs"(intType,[uintType],false);H.addh"__builtin_ffsl"(intType,[ulongType],false);H.addh"__builtin_ffsll"(intType,[ulongLongType],false);H.addh"__builtin_frame_address"(voidPtrType,[uintType],false);H.addh"__builtin_floor"(doubleType,[doubleType],false);H.addh"__builtin_floorf"(floatType,[floatType],false);H.addh"__builtin_floorl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_huge_val"(doubleType,[],false);H.addh"__builtin_huge_valf"(floatType,[],false);H.addh"__builtin_huge_vall"(longDoubleType,[],false);H.addh"__builtin_inf"(doubleType,[],false);H.addh"__builtin_inff"(floatType,[],false);H.addh"__builtin_infl"(longDoubleType,[],false);H.addh"__builtin_memcpy"(voidPtrType,[voidPtrType;voidConstPtrType;sizeType],false);H.addh"__builtin_memchr"(voidPtrType,[voidConstPtrType;intType;ulongType],false);H.addh"__builtin_mempcpy"(voidPtrType,[voidPtrType;voidConstPtrType;sizeType],false);H.addh"__builtin_memset"(voidPtrType,[voidPtrType;intType;intType],false);H.addh"__builtin_bcopy"(voidType,[voidConstPtrType;voidPtrType;sizeType],false);H.addh"__builtin_bzero"(voidType,[voidPtrType;sizeType],false);H.addh"__builtin_fmod"(doubleType,[doubleType],false);H.addh"__builtin_fmodf"(floatType,[floatType],false);H.addh"__builtin_fmodl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_frexp"(doubleType,[doubleType;intPtrType],false);H.addh"__builtin_frexpf"(floatType,[floatType;intPtrType],false);H.addh"__builtin_frexpl"(longDoubleType,[longDoubleType;intPtrType],false);H.addh"__builtin_ldexp"(doubleType,[doubleType;intType],false);H.addh"__builtin_ldexpf"(floatType,[floatType;intType],false);H.addh"__builtin_ldexpl"(longDoubleType,[longDoubleType;intType],false);H.addh"__builtin_log"(doubleType,[doubleType],false);H.addh"__builtin_logf"(floatType,[floatType],false);H.addh"__builtin_logl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_log10"(doubleType,[doubleType],false);H.addh"__builtin_log10f"(floatType,[floatType],false);H.addh"__builtin_log10l"(longDoubleType,[longDoubleType],false);H.addh"__builtin_modff"(floatType,[floatType;TPtr(floatType,[])],false);H.addh"__builtin_modfl"(longDoubleType,[longDoubleType;TPtr(longDoubleType,[])],false);H.addh"__builtin_nan"(doubleType,[charConstPtrType],false);H.addh"__builtin_nanf"(floatType,[charConstPtrType],false);H.addh"__builtin_nanl"(longDoubleType,[charConstPtrType],false);H.addh"__builtin_nans"(doubleType,[charConstPtrType],false);H.addh"__builtin_nansf"(floatType,[charConstPtrType],false);H.addh"__builtin_nansl"(longDoubleType,[charConstPtrType],false);H.addh"__builtin_next_arg"((ifhasbvathenTBuiltin_va_list[]elsevoidPtrType),[],false)(* When we parse builtin_next_arg we drop the argument *);H.addh"__builtin_object_size"(sizeType,[voidPtrType;intType],false);H.addh"__builtin_parity"(intType,[uintType],false);H.addh"__builtin_parityl"(intType,[ulongType],false);H.addh"__builtin_parityll"(intType,[ulongLongType],false);H.addh"__builtin_popcount"(intType,[uintType],false);H.addh"__builtin_popcountl"(intType,[ulongType],false);H.addh"__builtin_popcountll"(intType,[ulongLongType],false);H.addh"__builtin_powi"(doubleType,[doubleType;intType],false);H.addh"__builtin_powif"(floatType,[floatType;intType],false);H.addh"__builtin_powil"(longDoubleType,[longDoubleType;intType],false);H.addh"__builtin_prefetch"(voidType,[voidConstPtrType],true);H.addh"__builtin_return"(voidType,[voidConstPtrType],false);H.addh"__builtin_return_address"(voidPtrType,[uintType],false);H.addh"__builtin_extract_return_addr"(voidPtrType,[voidPtrType],false);H.addh"__builtin_frob_return_address"(voidPtrType,[voidPtrType],false);H.addh"__builtin_sin"(doubleType,[doubleType],false);H.addh"__builtin_sinf"(floatType,[floatType],false);H.addh"__builtin_sinl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_sinh"(doubleType,[doubleType],false);H.addh"__builtin_sinhf"(floatType,[floatType],false);H.addh"__builtin_sinhl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_sqrt"(doubleType,[doubleType],false);H.addh"__builtin_sqrtf"(floatType,[floatType],false);H.addh"__builtin_sqrtl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_stpcpy"(charPtrType,[charPtrType;charConstPtrType],false);H.addh"__builtin_strcat"(charPtrType,[charPtrType;charConstPtrType],false);H.addh"__builtin_strchr"(charPtrType,[charPtrType;intType],false);H.addh"__builtin_strcmp"(intType,[charConstPtrType;charConstPtrType],false);H.addh"__builtin_strcpy"(charPtrType,[charPtrType;charConstPtrType],false);H.addh"__builtin_strlen"(sizeType,[charConstPtrType],false);H.addh"__builtin_strcspn"(sizeType,[charConstPtrType;charConstPtrType],false);H.addh"__builtin_strncat"(charPtrType,[charPtrType;charConstPtrType;sizeType],false);H.addh"__builtin_strncmp"(intType,[charConstPtrType;charConstPtrType;sizeType],false);H.addh"__builtin_strncpy"(charPtrType,[charPtrType;charConstPtrType;sizeType],false);H.addh"__builtin_strspn"(sizeType,[charConstPtrType;charConstPtrType],false);H.addh"__builtin_strpbrk"(charPtrType,[charConstPtrType;charConstPtrType],false);(* When we parse builtin_types_compatible_p, we change its interface *)H.addh"__builtin_types_compatible_p"(intType,[!typeOfSizeOf;(* Sizeof the type *)!typeOfSizeOf(* Sizeof the type *)],false);H.addh"__builtin_tan"(doubleType,[doubleType],false);H.addh"__builtin_tanf"(floatType,[floatType],false);H.addh"__builtin_tanl"(longDoubleType,[longDoubleType],false);H.addh"__builtin_tanh"(doubleType,[doubleType],false);H.addh"__builtin_tanhf"(floatType,[floatType],false);H.addh"__builtin_tanhl"(longDoubleType,[longDoubleType],false);(* MMX Builtins *)H.addh"__builtin_ia32_addps"(v4sfType,[v4sfType;v4sfType],false);H.addh"__builtin_ia32_subps"(v4sfType,[v4sfType;v4sfType],false);H.addh"__builtin_ia32_mulps"(v4sfType,[v4sfType;v4sfType],false);H.addh"__builtin_ia32_unpckhps"(v4sfType,[v4sfType;v4sfType],false);H.addh"__builtin_ia32_unpcklps"(v4sfType,[v4sfType;v4sfType],false);H.addh"__builtin_ia32_maxps"(v4sfType,[v4sfType;v4sfType],false);(* tgmath in newer versions of GCC *)H.addh"__builtin_tgmath"(TVoid[Attr("overloaded",[])],[],true);(* Atomic Builtins
These builtins have an overloaded return type, hence the "magic" void type
with __overloaded__ attribute, used to infer return type from parameters in
cabs2cil.ml.
For the same reason, we do not specify the type of the parameters. *)H.addh"__sync_fetch_and_add"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_fetch_and_sub"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_fetch_and_or"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_fetch_and_and"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_fetch_and_xor"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_fetch_and_nand"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_add_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_sub_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_or_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_and_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_xor_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_nand_and_fetch"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_bool_compare_and_swap"(TInt(IBool,[]),[],true);H.addh"__sync_val_compare_and_swap"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_synchronize"(voidType,[],true);H.addh"__sync_lock_test_and_set"(TVoid[Attr("overloaded",[])],[],true);H.addh"__sync_lock_release"(voidType,[],true);(* __atomic builtins for various bit widths
Most __atomic functions are offered for various bit widths, using
a different suffix for each concrete bit width: "_1", "_2", and
so on up to "_16". Each of these functions also exists in a form
with no bit width specified, and occasionally with a bit width
suffix of "_n".
Note that these __atomic functions are not really va_arg, but we
set the va_arg flag nonetheless because it prevents CIL from
trying to check the type of parameters against the prototype.
*)letaddAtomicForWidthsbaseName?n~none~num()=(* ?n gives the return type to be used with the "_n" suffix, if any *)(* ~none gives the return type to be used with no suffix *)(* ~num gives the return type to be used with the "_1" through "_16" suffixes *)letaddWithSuffixsuffixreturnType=letidentifier="__atomic_"^baseName^suffixinH.addhidentifier(returnType,[],true)inList.iterbeginfunbitWidth->letsuffix="_"^(string_of_intbitWidth)inaddWithSuffixsuffixnumend[1;2;4;8;16];addWithSuffix""none;matchnwith|None->()|Sometyp->addWithSuffix"_n"typinletanyType=TVoid[Attr("overloaded",[])]in(* binary operations combined with a fetch of either the old or new value *)List.iterbeginfunoperation->addAtomicForWidths("fetch_"^operation)~none:anyType~num:anyType();addAtomicForWidths(operation^"_fetch")~none:anyType~num:anyType()end["add";"and";"nand";"or";"sub";"xor"];(* other atomic operations provided at various bit widths *)addAtomicForWidths"compare_exchange"~none:boolType~n:boolType~num:boolType();addAtomicForWidths"exchange"~none:voidType~n:anyType~num:anyType();addAtomicForWidths"load"~none:voidType~n:anyType~num:anyType();addAtomicForWidths"store"~none:voidType~n:voidType~num:voidType();(* Some atomic builtins actually have a decent, C-compatible type *)H.addh"__atomic_test_and_set"(boolType,[voidPtrType;intType],false);H.addh"__atomic_clear"(voidType,[boolPtrType;intType],false);H.addh"__atomic_thread_fence"(voidType,[intType],false);H.addh"__atomic_signal_fence"(voidType,[intType],false);H.addh"__atomic_always_lock_free"(boolType,[sizeType;voidPtrType],false);H.addh"__atomic_is_lock_free"(boolType,[sizeType;voidPtrType],false);H.addh"__atomic_feraiseexcept"(voidType,[intType],false);ifhasbvathenbeginH.addh"__builtin_va_end"(voidType,[TBuiltin_va_list[]],false);H.addh"__builtin_varargs_start"(voidType,[TBuiltin_va_list[]],false);(* When we parse builtin_{va,stdarg}_start, we drop the second argument *)H.addh"__builtin_va_start"(voidType,[TBuiltin_va_list[]],false);H.addh"__builtin_stdarg_start"(voidType,[TBuiltin_va_list[];],false);(* When we parse builtin_va_arg we change its interface *)H.addh"__builtin_va_arg"(voidType,[TBuiltin_va_list[];!typeOfSizeOf;(* Sizeof the type *)voidPtrType;(* Ptr to res *)],false);H.addh"__builtin_va_copy"(voidType,[TBuiltin_va_list[];TBuiltin_va_list[]],false);end;H.addh"__builtin_apply_args"(voidPtrType,[],false);letfnPtr=TPtr(TFun(voidType,None,false,[]),[])inH.addh"__builtin_apply"(voidPtrType,[fnPtr;voidPtrType;sizeType],false);H.addh"__builtin_va_arg_pack"(intType,[],false);H.addh"__builtin_va_arg_pack_len"(intType,[],false);()(** This is used as the location of the prototypes of builtin functions. *)letbuiltinLoc:location={line=1;file="<compiler builtins>";byte=0;column=0;endLine=-1;endByte=-1;endColumn=-1;synthetic=true;}letpTypeSig:(typ->typsig)ref=ref(fun_->E.s(E.bug"pTypeSig not initialized"))(** A printer interface for CIL trees. Create instantiations of
this type by specializing the class {!defaultCilPrinter}. *)classtypecilPrinter=objectmethodsetCurrentFormals:varinfolist->unitmethodsetPrintInstrTerminator:string->unitmethodgetPrintInstrTerminator:unit->stringmethodpVDecl:unit->varinfo->doc(** Invoked for each variable declaration. Note that variable
declarations are all the [GVar], [GVarDecl], [GFun], all the [varinfo]
in formals of function types, and the formals and locals for function
definitions. *)methodpVar:varinfo->doc(** Invoked on each variable use. *)methodpLval:unit->lval->doc(** Invoked on each lvalue occurrence *)methodpOffset:doc->offset->doc(** Invoked on each offset occurrence. The second argument is the base. *)methodpInstr:unit->instr->doc(** Invoked on each instruction occurrence. *)methodpStmt:unit->stmt->doc(** Control-flow statement. This is used by
{!printGlobal} and by {!dumpGlobal}. *)methoddStmt:out_channel->int->stmt->unit(** Dump a control-flow statement to a file with a given indentation. This is used by
{!dumpGlobal}. *)methoddBlock:out_channel->int->block->unit(** Dump a control-flow block to a file with a given indentation. This is
used by {!dumpGlobal}. *)methodpBlock:unit->block->Pretty.doc(** Print a block. *)methodpGlobal:unit->global->doc(** Global (vars, types, etc.). This can be slow and is used only by
{!printGlobal} but by {!dumpGlobal} for everything else except
[GVar] and [GFun]. *)methoddGlobal:out_channel->global->unit(** Dump a global to a file. This is used by {!dumpGlobal}. *)methodpFieldDecl:unit->fieldinfo->doc(** A field declaration *)methodpType:docoption->unit->typ->doc(* Use of some type in some declaration. The first argument is used to print
the declared element, or is None if we are just printing a type with no
name being declared. Note that for structure/union and enumeration types
the definition of the composite type is not visited. Use [vglob] to
visit it. *)methodpAttr:attribute->doc*bool(** Attribute. Also return an indication whether this attribute must be
printed inside the __attribute__ list or not. *)methodpAttrParam:unit->attrparam->doc(** Attribute parameter *)methodpAttrs:unit->attributes->doc(** Attribute lists *)methodpLabel:unit->label->doc(** Label *)methodpLineDirective:?forcefile:bool->location->Pretty.doc(** Print a line-number. This is assumed to come always on an empty line.
If the forcefile argument is present and is true then the file name
will be printed always. Otherwise the file name is printed only if it
is different from the last time time this function is called. The last
file name is stored in a private field inside the cilPrinter object. *)methodpStmtKind:stmt->unit->stmtkind->Pretty.doc(** Print a statement kind. The code to be printed is given in the
{!stmtkind} argument. The initial {!stmt} argument
records the statement which follows the one being printed;
{!defaultCilPrinterClass} uses this information to prettify
statement printing in certain special cases. *)methodpExp:unit->exp->doc(** Print expressions *)methodpInit:unit->init->doc(** Print initializers. This can be slow and is used by
{!printGlobal} but not by {!dumpGlobal}. *)methoddInit:out_channel->int->init->unit(** Dump a global to a file with a given indentation. This is used by
{!dumpGlobal}. *)endclassdefaultCilPrinterClass:cilPrinter=object(self)valmutablecurrentFormals:varinfolist=[]methodprivategetLastNamedArgument(s:string):exp=matchList.revcurrentFormalswithf::_->Lval(varf)|[]->E.s(bug"Cannot find the last named argument when printing call to %s\n"s)methodprivatesetCurrentFormals(fms:varinfolist)=currentFormals<-fms(*** VARIABLES ***)(* variable use *)methodpVar(v:varinfo)=textv.vname(* variable declaration *)methodpVDecl()(v:varinfo)=(* First the storage modifiers *)text(ifv.vinlinethen"__inline "else"")++d_storage()v.vstorage++(self#pType(Some(textv.vname))()v.vtype)++text" "++self#pAttrs()v.vattr(*** L-VALUES ***)methodpLval()(lv:lval)=(* lval (base is 1st field) *)matchlvwithVarvi,o->self#pOffset(self#pVarvi)o|Meme,Field(fi,o)->self#pOffset((self#pExpPrecarrowLevel()e)++text("->"^fi.fname))o|Meme,NoOffset->text"*"++self#pExpPrecderefStarLevel()e|Meme,o->self#pOffset(text"(*"++self#pExpPrecderefStarLevel()e++text")")o(** Offsets **)methodpOffset(base:doc)=function|NoOffset->base|Field(fi,o)->self#pOffset(base++text"."++textfi.fname)o|Index(e,o)->self#pOffset(base++text"["++self#pExp()e++text"]")omethodprivatepLvalPrec(contextprec:int)()lv=ifgetParenthLevel(Lval(lv))>=contextprecthentext"("++self#pLval()lv++text")"elseself#pLval()lv(*** EXPRESSIONS ***)methodpExp()(e:exp):doc=letlevel=getParenthLeveleinmatchewithConst(c)->d_const()c|Lval(l)->self#pLval()l|UnOp(u,e1,_)->(d_unop()u)++chr' '++(self#pExpPreclevel()e1)|BinOp(b,e1,e2,_)->align++(self#pExpPreclevel()e1)++chr' '++(d_binop()b)++chr' '++(self#pExpPreclevel()e2)++unalign|Question(e1,e2,e3,_)->(self#pExpPreclevel()e1)++text" ? "++(self#pExpPreclevel()e2)++text" : "++(self#pExpPreclevel()e3)|CastE(t,e)->text"("++self#pTypeNone()t++text")"++self#pExpPreclevel()e|SizeOf(t)->text"sizeof("++self#pTypeNone()t++chr')'|SizeOfE(Lval(Varfv,NoOffset))whenfv.vname="__builtin_va_arg_pack"&&(not!printCilAsIs)->text"__builtin_va_arg_pack()"|SizeOfE(e)->text"sizeof("++self#pExp()e++chr')'|Image->text"__imag__("++self#pExp()e++chr')'|Reale->text"__real__("++self#pExp()e++chr')'|SizeOfStrs->text"sizeof("++d_const()(CStr(s,No_encoding))++chr')'|AlignOf(t)->text"__alignof__("++self#pTypeNone()t++chr')'|AlignOfE(e)->text"__alignof__("++self#pExp()e++chr')'|AddrOf(lv)->text"& "++(self#pLvalPrecaddrOfLevel()lv)|AddrOfLabel(sref)->begin(* Grab one of the labels *)letrecpickLabel=function[]->None|Label(l,_,_)::_->Somel|_::rest->pickLabelrestinmatchpickLabel!sref.labelswithSomelbl->text("&& "^lbl)|None->ignore(error"Cannot find label for target of address of label");text"&& __invalid_label"end|StartOf(lv)->self#pLval()lv(* Print an expression, given the precedence of the context in which it
appears. *)methodprivatepExpPrec(contextprec:int)()(e:exp)=letthisLevel=getParenthLeveleinletneedParens=ifthisLevel>=contextprecthentrueelseifcontextprec==bitwiseLevelthen(* quiet down some GCC warnings *)thisLevel==additiveLevel||thisLevel==comparativeLevelelsefalseinifneedParensthenchr'('++self#pExp()e++chr')'elseself#pExp()emethodpInit()=functionSingleInite->self#pExp()e|CompoundInit(t,initl)->(* We do not print the type of the Compound *)(*
let dinit e = d_init () e in
dprintf "{@[%a@]}"
(docList ~sep:(chr ',' ++ break) dinit) initl
*)letprintDesignator=(* Print only for union when we do not initialize the first field *)matchunrollTypet,initlwithTComp(ci,_),[(Field(f,NoOffset),_)]->ifnot(ci.cstruct)&&ci.cfields!=[]&&(List.hdci.cfields)!=fthentrueelsefalse|_->falseinletd_oneInit=functionField(f,NoOffset),i->(ifprintDesignatorthentext("."^f.fname^" = ")elsenil)++self#pInit()i|Index(e,NoOffset),i->(ifprintDesignatorthentext"["++self#pExp()e++text"] = "elsenil)++self#pInit()i|_->E.s(unimp"Trying to print malformed initializer")inchr'{'++(align++((docList~sep:(chr','++break)d_oneInit)()initl)++unalign)++chr'}'(*
| ArrayInit (_, _, il) ->
chr '{' ++ (align
++ ((docList (chr ',' ++ break) (self#pInit ())) () il)
++ unalign)
++ chr '}'
*)(* dump initializers to a file. *)methoddInit(out:out_channel)(ind:int)(i:init)=(* Dump an array *)letdumpArray(bt:typ)(il:'alist)(getelem:'a->init)=letonALine=(* How many elements on a line *)matchunrollTypebtwithTComp_|TArray_->1|_->4inletrecoutputElements(isfirst:bool)(room_on_line:int)=function[]->output_stringout"}"|(i:'a)::rest->ifnotisfirstthenoutput_stringout", ";letnew_room_on_line=ifroom_on_line==0thenbeginoutput_stringout"\n";output_stringout(String.makeind' ');onALine-1endelseroom_on_line-1inself#dInitout(ind+2)(getelemi);outputElementsfalsenew_room_on_linerestinoutput_stringout"{ ";outputElementstrueonALineilinmatchiwithSingleInite->fprintout~width:!lineLength(indentind(self#pExp()e))|CompoundInit(t,initl)->beginmatchunrollTypetwithTArray(bt,_,_)->dumpArraybtinitl(fun(_,i)->i)|_->(* Now a structure or a union *)fprintout~width:!lineLength(indentind(self#pInit()i))end(*
| ArrayInit (bt, len, initl) -> begin
(* If the base type does not contain structs then use the pInit
match unrollType bt with
TComp _ | TArray _ ->
dumpArray bt initl (fun x -> x)
| _ -> *)
fprint out !lineLength (indent ind (self#pInit () i))
end
*)(** What terminator to print after an instruction. sometimes we want to
print sequences of instructions separated by comma *)valmutableprintInstrTerminator=";"methodprivatesetPrintInstrTerminator(term:string)=printInstrTerminator<-termmethodprivategetPrintInstrTerminator()=printInstrTerminator(*** INSTRUCTIONS ****)methodpInstr()(i:instr)=(* imperative instruction *)matchiwith|Set(lv,e,l,el)->begin(* Be nice to some special cases *)matchewithBinOp((PlusA|PlusPI|IndexPI),Lval(lv'),Const(CInt(one,_,_)),_)whenUtil.equalslvlv'&&compare_cilintoneone_cilint=0&¬!printCilAsIs->self#pLineDirectivel++self#pLvalPrecindexLevel()lv++text(" ++"^printInstrTerminator)|BinOp((MinusA|MinusPI),Lval(lv'),Const(CInt(one,_,_)),_)whenUtil.equalslvlv'&&compare_cilintoneone_cilint=0&¬!printCilAsIs->self#pLineDirectivel++self#pLvalPrecindexLevel()lv++text(" --"^printInstrTerminator)|BinOp((PlusA|PlusPI|IndexPI),Lval(lv'),Const(CInt(mone,_,_)),_)whenUtil.equalslvlv'&&compare_cilintmonemone_cilint=0&¬!printCilAsIs->self#pLineDirectivel++self#pLvalPrecindexLevel()lv++text(" --"^printInstrTerminator)|BinOp((PlusA|PlusPI|IndexPI|MinusA|MinusPP|MinusPI|BAnd|BOr|BXor|Mult|Div|Mod|Shiftlt|Shiftrt)asbop,Lval(lv'),e,_)whenUtil.equalslvlv'&¬!printCilAsIs->self#pLineDirectivel++self#pLval()lv++text" "++d_binop()bop++text"= "++self#pExp()e++textprintInstrTerminator|_->self#pLineDirectivel++self#pLval()lv++text" = "++self#pExp()e++textprintInstrTerminatorend|VarDecl(v,l)->self#pLineDirectivel++self#pVDecl()v++(matchv.vinit.initwith|None->text";"|Somei->text" = "++self#pInit()i++text";")(* In cabs2cil we have turned the call to builtin_va_arg into a
three-argument call: the last argument is the address of the
destination *)|Call(None,Lval(Varvi,NoOffset),[dest;SizeOft;adest],l,el)whenvi.vname="__builtin_va_arg"&¬!printCilAsIs->letdestlv=matchstripCastsadestwithAddrOfdestlv->destlv(* If this fails, it's likely that an extension interfered
with the AddrOf *)|_->E.s(E.bug"%a: Encountered unexpected call to %s with dest %a\n"d_loclvi.vnameself#pExpadest)inself#pLineDirectivel++self#pLval()destlv++text" = "(* Now the function name *)++text"__builtin_va_arg"++text"("++(align(* Now the arguments *)++self#pExp()dest++chr','++break++self#pTypeNone()t++unalign)++text(")"^printInstrTerminator)(* In cabs2cil we have dropped the last argument in the call to
__builtin_va_start and __builtin_stdarg_start. *)|Call(None,Lval(Varvi,NoOffset),[marker],l,el)when((vi.vname="__builtin_stdarg_start"||vi.vname="__builtin_va_start")&¬!printCilAsIs)->ifcurrentFormals<>[]thenbeginletlast=self#getLastNamedArgumentvi.vnameinself#pInstr()(Call(None,Lval(Varvi,NoOffset),[marker;last],l,el))endelsebegin(* We can't print this call because someone called pInstr outside
of a pFunDecl, so we don't know what the formals of the current
function are. Just put in a placeholder for now; this isn't
valid C. *)self#pLineDirectivel++dprintf"%s(%a, /* last named argument of the function calling %s */)"vi.vnameself#pExpmarkervi.vname++textprintInstrTerminatorend(* In cabs2cil we have dropped the last argument in the call to
__builtin_next_arg. *)|Call(res,Lval(Varvi,NoOffset),[],l,el)whenvi.vname="__builtin_next_arg"&¬!printCilAsIs->beginletlast=self#getLastNamedArgumentvi.vnameinself#pInstr()(Call(res,Lval(Varvi,NoOffset),[last],l,el))end(* In cparser we have turned the call to
__builtin_types_compatible_p(t1, t2) into
__builtin_types_compatible_p(sizeof t1, sizeof t2), so that we can
represent the types as expressions.
Remove the sizeofs when printing. *)|Call(dest,Lval(Varvi,NoOffset),[SizeOft1;SizeOft2],l,el)whenvi.vname="__builtin_types_compatible_p"&¬!printCilAsIs->self#pLineDirectivel(* Print the destination *)++(matchdestwithNone->nil|Somelv->self#pLval()lv++text" = ")(* Now the call itself *)++dprintf"%s(%a, %a)"vi.vname(self#pTypeNone)t1(self#pTypeNone)t2++textprintInstrTerminator|Call(_,Lval(Varvi,NoOffset),_,l,el)whenvi.vname="__builtin_types_compatible_p"&¬!printCilAsIs->E.s(bug"__builtin_types_compatible_p: cabs2cil should have added sizeof to the arguments.")|Call(dest,e,args,l,el)->letrecpatchTypeNotVLAt=matchtwith|TPtr(t,args)->TPtr(patchTypeNotVLAt,args)|TArray(t,None,args)->TArray(patchTypeNotVLAt,None,args)|TArray(t,Someexp,args)whenisConstantexp->TArray(patchTypeNotVLAt,Someexp,args)|TArray(t,Someexp,args)->TArray(patchTypeNotVLAt,None,args)|_->tinletpatchArgNotUseVLACastexp=matchexpwith|CastE(t,e)->CastE(patchTypeNotVLAt,e)|e->einself#pLineDirectivel++(matchdestwithNone->nil|Somelv->self#pLval()lv++text" = "++(* Maybe we need to print a cast *)(letdestt=typeOfLvallvinmatchunrollType(typeOfe)withTFun(rt,_,_,_)whennot(Util.equals(!pTypeSigrt)(!pTypeSigdestt))->text"("++self#pTypeNone()destt++text")"|_->nil))(* Now the function name *)++(leted=self#pExp()einmatchewithLval(Var_,_)->ed|_->text"("++ed++text")")++text"("++(align(* Now the arguments *)++(docList~sep:(chr','++break)(funx->self#pExp()(patchArgNotUseVLACastx))()args)(* here we would need to remove casts to array types that are not ok *)++unalign)++text(")"^printInstrTerminator)|Asm(attrs,tmpls,outs,ins,clobs,l)->self#pLineDirectivel++text("__asm__ ")++self#pAttrs()attrs++text" ("++(align++(docList~sep:line(funx->text("\""^escape_stringx^"\""))()tmpls)++(ifouts=[]&&ins=[]&&clobs=[]thenchr':'else(text": "++(docList~sep:(chr','++break)(fun(idopt,c,lv)->text(matchidoptwithNone->""|Someid->"["^id^"] ")++text("\""^escape_stringc^"\" (")++self#pLval()lv++text")")()outs)))++(ifins=[]&&clobs=[]thennilelse(text": "++(docList~sep:(chr','++break)(fun(idopt,c,e)->text(matchidoptwithNone->""|Someid->"["^id^"] ")++text("\""^escape_stringc^"\" (")++self#pExp()e++text")")()ins)))++(ifclobs=[]thennilelse(text": "++(docList~sep:(chr','++break)(func->text("\""^escape_stringc^"\""))()clobs)))++unalign)++text(")"^printInstrTerminator)(**** STATEMENTS ****)methodpStmt()(s:stmt)=(* control-flow statement *)self#pStmtNextinvalidStmt()smethoddStmt(out:out_channel)(ind:int)(s:stmt):unit=fprintout~width:!lineLength(indentind(self#pStmt()s))methoddBlock(out:out_channel)(ind:int)(b:block):unit=fprintout~width:!lineLength(indentind(align++self#pBlock()b))methodprivatepStmtNext(next:stmt)()(s:stmt)=(* print the labels *)letlabels=((docList~sep:line(funl->self#pLabel()l))()s.labels)inifs.skind=Instr[]&&s.labels<>[]then(* If the labels are non-empty and the statement is empty, print a semicolon *)labels++text";"elseletpre=ifs.labels<>[]then(matchs.skindwith|Instr(VarDecl(_)::_)->text";"(* first instruction is VarDecl, insert semicolon *)|_->nil)++lineelsenil(* no labels, no new line needed *)inlabels++pre++self#pStmtKindnext()s.skindmethodprivatepLabel()=functionLabel(s,_,true)->text(s^": ")|Label(s,_,false)->text(s^": /* CIL Label */ ")|Case(e,_,_)->text"case "++self#pExp()e++text": "|CaseRange(e1,e2,_,_)->text"case "++self#pExp()e1++text" ... "++self#pExp()e2++text": "|Default_->text"default: "(* The pBlock will put the unalign itself *)methodpBlock()(blk:block)=letrecdofirst()=function[]->nil|[x]->self#pStmtNextinvalidStmt()x|x::rest->dorestnilxrestanddorestaccprev=function[]->acc++(self#pStmtNextinvalidStmt()prev)|x::rest->dorest(acc++(self#pStmtNextx()prev)++line)xrestin(* Let the host of the block decide on the alignment. The d_block will
pop the alignment as well *)text"{"++(ifblk.battrs<>[]thenself#pAttrsGentrueblk.battrselsenil)++line++(dofirst()blk.bstmts)++unalign++line++text"}"(* Store here the name of the last file printed in a line number. This is
private to the object *)valmutablelastFileName=""valmutablelastLineNumber=-1(* Make sure that you only call self#pLineDirective on an empty line *)methodpLineDirective?(forcefile=false)l=currentLoc:=l;match!lineDirectiveStylewith|None->nil|Some_whenl.line<=0->nil(* Do not print lineComment if the same line as above *)|SomeLineCommentSparsewhenl.line=lastLineNumber->nil|Somestyle->letdirective=matchstylewith|LineComment|LineCommentSparse->text"//#line "|LinePreprocessorOutput->chr'#'|LinePreprocessorInput->text"#line"inlastLineNumber<-l.line;letfilename=ifforcefile||l.file<>lastFileNamethenbeginlastFileName<-l.file;text" \""++textl.file++text"\""endelsenilinleftflush++directive++chr' '++numl.line++filename++linemethodprivatepIfConditionThenlocconditionthenBlock=self#pLineDirectiveloc++text"if"++(align++text" ("++self#pExp()condition++text") "++self#pBlock()thenBlock)methodprivatepStmtKind(next:stmt)()=functionReturn(None,l,el)->self#pLineDirectivel++text"return;"|Return(Somee,l,el)->self#pLineDirectivel++text"return ("++self#pExp()e++text");"|Goto(sref,l)->begin(* Grab one of the labels *)letrecpickLabel=function[]->None|Label(l,_,_)::_->Somel|_::rest->pickLabelrestinmatchpickLabel!sref.labelswithSomelbl->self#pLineDirectivel++text("goto "^lbl^";")|None->ignore(error"Cannot find label for target of goto");text"goto __invalid_label;"end|ComputedGoto(e,l)->self#pLineDirectivel++text"goto *("++self#pExp()e++text");"|Breakl->self#pLineDirectivel++text"break;"|Continuel->self#pLineDirectivel++text"continue;"|Instril->align++(docList~sep:line(funi->self#pInstr()i)()il)++unalign|If(be,t,{bstmts=[];battrs=[]},l,el)whennot!printCilAsIs->self#pIfConditionThenlbet|If(be,t,{bstmts=[{skind=Goto(gref,_);labels=[];_}];battrs=[]},l,el)when!gref==next&¬!printCilAsIs->self#pIfConditionThenlbet|If(be,{bstmts=[];battrs=[]},e,l,el)whennot!printCilAsIs->self#pIfConditionThenl(UnOp(LNot,be,intType))e|If(be,{bstmts=[{skind=Goto(gref,_);labels=[];_}];battrs=[]},e,l,el)when!gref==next&¬!printCilAsIs->self#pIfConditionThenl(UnOp(LNot,be,intType))e|If(be,t,e,l,el)->self#pIfConditionThenlbet++(matchewith{bstmts=[{skind=If_;_}aselsif];battrs=[]}->text" else"++line(* Don't indent else-ifs *)++self#pStmtNextnext()elsif|_->text" "(* sm: indent next code 2 spaces (was 4) *)++align++text"else "++self#pBlock()e)|Switch(e,b,_,l,el)->self#pLineDirectivel++(align++text"switch ("++self#pExp()e++text") "++self#pBlock()b)|Loop(b,l,el,_,_)->begin(* Maybe the first thing is a conditional. Turn it into a WHILE *)tryletterm,bodystmts=letrecskipEmpty=function[]->[]|{skind=Instr[];labels=[];_}::rest->skipEmptyrest|x->xin(* Bill McCloskey: Do not remove the If if it has labels *)matchskipEmptyb.bstmtswith{skind=If(e,tb,fb,_,_);labels=[];_}::restwhennot!printCilAsIs->beginmatchskipEmptytb.bstmts,skipEmptyfb.bstmtswith[],{skind=Break_;labels=[];_}::_->e,rest|{skind=Break_;labels=[];_}::_,[]->UnOp(LNot,e,intType),rest|_->raiseNot_foundend|_->raiseNot_foundinself#pLineDirectivel++text"wh"++(align++text"ile ("++self#pExp()term++text") "++self#pBlock(){bstmts=bodystmts;battrs=b.battrs})withNot_found->self#pLineDirectivel++text"wh"++(align++text"ile (1) "++self#pBlock()b)end|Blockb->align++self#pBlock()b(*** GLOBALS ***)methodpGlobal()(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 print 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)++text" {"++line++(docList~sep:(chr','++line)(fun(n,attrs,i,loc)->textn++self#pAttrs()attrs++text" = "++self#pExp()i)()enum.eitems)++unalign++line++text"} "++self#pAttrs()enum.eattr++text";\n"|GEnumTagDecl(enum,l)->(* This is a declaration of a tag *)self#pLineDirectivel++text"enum "++textenum.ename++chr' '++self#pAttrs()enum.eattr++text";\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 *)letsu=ifcomp.cstructthen"struct "else"union "inself#pLineDirectivel++textsu++textcomp.cname++chr' '++self#pAttrs()comp.cattr++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)->ifnot!printCilAsIs&&H.membuiltinFunctionsvi.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"elsenilmethoddGlobal(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:!lineLength(proto++(self#pLineDirective~forcefile:truel));(* Temporarily remove the function attributes *)fdec.svar.vattr<-[];fprintout~width:!lineLength(self#pFunDecl()fdec);fdec.svar.vattr<-oldattr;output_stringout"\n"|GVar(vi,{init=Somei},l)->beginfprintout~width:!lineLength(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:!lineLength(self#pGlobal()g)methodpFieldDecl()fi=(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=self#pVDecl()f.svar++line++text"{ "++(align(* locals. *)++line++(docList~sep:line(funvi->matchvi.vinit.initwith|None->self#pVDecl()vi++text";"|Somei->self#pVDecl()vi++text" = "++self#pInit()i++text";")()(List.filter(funv->notv.vhasdeclinstruction)f.slocals))++line++line(* the body *)++((* remember the declaration *)currentFormals<-f.sformals;letbody=self#pBlock()f.sbodyincurrentFormals<-[];body))++line++text"}"(***** PRINTING DECLARATIONS and TYPES ****)methodpType(nameOpt:docoption)(* Whether we are declaring a name or
we are just printing a type *)()(t:typ)=(* use of some type *)letname=matchnameOptwithNone->nil|Somed->dinletprintAttributes(a:attributes)=letpa=self#pAttrs()ainmatchnameOptwith|Nonewhennot!print_CIL_Input->(* Cannot print the attributes in this case because gcc does not
like them here, except if we are printing for CIL. *)ifpa=nilthennilelsetext"/*"++pa++text"*/"|_->painmatchtwithTVoida->text"void"++self#pAttrs()a++text" "++name|TInt(ikind,a)->d_ikind()ikind++self#pAttrs()a++text" "++name|TFloat(fkind,a)->d_fkind()fkind++self#pAttrs()a++text" "++name|TComp(comp,a)->(* A reference to a struct *)letsu=ifcomp.cstructthen"struct"else"union"intext(su^" "^comp.cname^" ")++self#pAttrs()a++name|TEnum(enum,a)->text("enum "^enum.ename^" ")++self#pAttrs()a++name|TPtr(bt,a)->(* Parenthesize the ( * attr name) if a pointer to a function or an
array. *)let(paren:docoption),(bt':typ)=matchbtwith|TFun_|TArray_->Some(text"("),bt|_->None,btinletname'=text"*"++printAttributesa++nameinletname''=(* Put the parenthesis *)matchparenwithSomep->p++name'++text")"|_->name'inself#pType(Somename'')()bt'|TArray(elemt,lo,a)->(* ignore the const attribute for arrays *)leta'=dropAttributes["pconst"]ainletname'=ifa'==[]thennameelseifnameOpt==NonethenprintAttributesa'elsetext"("++printAttributesa'++name++text")"inself#pType(Some(name'++text"["++(matchlowithNone->nil|Somee->self#pExp()e)++text"]"))()elemt|TFun(restyp,args,isvararg,a)->letname'=ifa==[]thennameelseifnameOpt==NonethenprintAttributesaelsetext"("++printAttributesa++name++text")"inself#pType(Some(name'++text"("++(align++(ifargs=Some[]&&isvarargthentext"..."else(ifargs=Nonethennilelseifargs=Some[]thentext"void"elseletpArg(aname,atype,aattr)=(self#pType(Some(textaname))()atype)++text" "++self#pAttrs()aattrin(docList~sep:(chr','++break)pArg)()(argsToListargs))++(ifisvarargthenbreak++text", ..."elsenil))++unalign)++text")"))()restyp|TNamed(t,a)->textt.tname++self#pAttrs()a++text" "++name|TBuiltin_va_lista->text"__builtin_va_list"++self#pAttrs()a++text" "++name(**** PRINTING ATTRIBUTES *********)methodpAttrs()(a:attributes)=self#pAttrsGenfalsea(* Print one attribute. Return also an indication whether this attribute
should be printed inside the __attribute__ list *)methodpAttr(Attr(an,args):attribute):doc*bool=(* Recognize and take care of some known cases *)matchan,argswith"atomic",[]->text"_Atomic",false|"const",[]->nil,false(* don't print const directly, because of split local declarations *)|"pconst",[]->text"const",false(* pconst means print const *)(* Put the aconst inside the attribute list *)|"complex",[]when(c99Mode())->text"_Complex",false|"complex",[]->text"__complex__",false|"aconst",[]->text"__const__",true|"thread",[]->text"__thread",false|"volatile",[]->text"volatile",false|"restrict",[]->text"__restrict",false|"missingproto",[]->text"/* missing proto */",false|"asm",args->text"__asm__("++docList(self#pAttrParam())()args++text")",false(* we suppress printing mode(__si__) because it triggers an *)(* internal compiler error in all current gcc versions *)(* sm: I've now encountered a problem with mode(__hi__)... *)(* I don't know what's going on, but let's try disabling all "mode"..*)|"mode",[ACons(tag,[])]->text"/* mode("++texttag++text") */",false(* sm: also suppress "format" because we seem to print it in *)(* a way gcc does not like *)|"format",_->text"/* format attribute */",false(* sm: here's another one I don't want to see gcc warnings about.. *)|"mayPointToStack",_whennot!print_CIL_Input(* [matth: may be inside another comment.]
-> text "/*mayPointToStack*/", false
*)->text"",false|"arraylen",[a]->(* text "/*[" ++ self#pAttrParam () a ++ text "]*/" *)nil,false|_->(* This is the default case *)(* Add underscores to the name *)letan'="__"^an^"__"inifargs=[]thentextan',trueelsetext(an'^"(")++(docList(self#pAttrParam())()args)++text")",truemethodprivatepAttrPrec(contextprec:int)()(a:attrparam)=letthisLevel=getParenthLevelAttrParamainletneedParens=ifthisLevel>=contextprecthentrueelseifcontextprec==bitwiseLevelthen(* quiet down some GCC warnings *)thisLevel==additiveLevel||thisLevel==comparativeLevelelsefalseinifneedParensthenchr'('++self#pAttrParam()a++chr')'elseself#pAttrParam()amethodpAttrParam()a=letlevel=getParenthLevelAttrParamainmatchawith|AIntn->numn|AStrs->text("\""^escape_strings^"\"")|ACons(s,[])->texts|ACons(s,al)->text(s^"(")++(docList(self#pAttrParam())()al)++text")"|ASizeOfEa->text"sizeof("++self#pAttrParam()a++text")"|ASizeOft->text"sizeof("++self#pTypeNone()t++text")"|ASizeOfSts->text"sizeof(<typsig>)"|AAlignOfEa->text"__alignof__("++self#pAttrParam()a++text")"|AAlignOft->text"__alignof__("++self#pTypeNone()t++text")"|AAlignOfSts->text"__alignof__(<typsig>)"|AUnOp(u,a1)->(d_unop()u)++chr' '++(self#pAttrPreclevel()a1)|ABinOp(b,a1,a2)->align++text"("++(self#pAttrPreclevel()a1)++text") "++(d_binop()b)++break++text" ("++(self#pAttrPreclevel()a2)++text") "++unalign|ADot(ap,s)->(self#pAttrParam()ap)++text("."^s)|AStara1->text"(*"++(self#pAttrPrecderefStarLevel()a1)++text")"|AAddrOfa1->text"& "++(self#pAttrPrecaddrOfLevel()a1)|AIndex(a1,a2)->self#pAttrParam()a1++text"["++self#pAttrParam()a2++text"]"|AQuestion(a1,a2,a3)->self#pAttrParam()a1++text" ? "++self#pAttrParam()a2++text" : "++self#pAttrParam()a3|AAssign(a1,a2)->self#pAttrParam()a1++text"="++self#pAttrParam()a2(* A general way of printing lists of attributes *)methodprivatepAttrsGen(block:bool)(a:attributes)=(* Scan all the attributes and separate those that must be printed inside
the __attribute__ list *)letrecloop(in__attr__:doclist)=function[]->beginmatchin__attr__with[]->nil|_::_->(* sm: added 'forgcc' calls to not comment things out
if CIL is the consumer; this is to address a case
Daniel ran into where blockattribute(nobox) was being
dropped by the merger
*)(ifblockthentext(" "^(forgcc"/*")^" __blockattribute__(")elsetext"__attribute__((")++(docList~sep:(chr','++break)(funa->a))()in__attr__++text")"++(ifblockthentext(forgcc"*/")elsetext")")end|x::rest->letdx,ina=self#pAttrxinifinathenloop(dx::in__attr__)restelseifdx=nilthenloopin__attr__restelsedx++text" "++loopin__attr__restinletres=loop[]ainifres=nilthenreselsetext" "++res++text" "end(* class defaultCilPrinterClass *)letdefaultCilPrinter=newdefaultCilPrinterClass(* Top-level printing functions *)letprintType(pp:cilPrinter)()(t:typ):doc=pp#pTypeNone()tletprintExp(pp:cilPrinter)()(e:exp):doc=pp#pExp()eletprintLval(pp:cilPrinter)()(lv:lval):doc=pp#pLval()lvletprintGlobal(pp:cilPrinter)()(g:global):doc=pp#pGlobal()gletdumpGlobal(pp:cilPrinter)(out:out_channel)(g:global):unit=pp#dGlobaloutgletprintAttr(pp:cilPrinter)()(a:attribute):doc=letad,_=pp#pAttrainadletprintAttrs(pp:cilPrinter)()(a:attributes):doc=pp#pAttrs()aletprintInstr(pp:cilPrinter)()(i:instr):doc=pp#pInstr()iletprintStmt(pp:cilPrinter)()(s:stmt):doc=pp#pStmt()sletprintBlock(pp:cilPrinter)()(b:block):doc=(* We must add the alignment ourselves, because pBlock will pop it *)align++pp#pBlock()bletdumpStmt(pp:cilPrinter)(out:out_channel)(ind:int)(s:stmt):unit=pp#dStmtoutindsletdumpBlock(pp:cilPrinter)(out:out_channel)(ind:int)(b:block):unit=pp#dBlockoutindbletprintInit(pp:cilPrinter)()(i:init):doc=pp#pInit()iletdumpInit(pp:cilPrinter)(out:out_channel)(ind:int)(i:init):unit=pp#dInitoutindi(* Now define some short cuts *)letd_exp()e=printExpdefaultCilPrinter()elet_=pd_exp:=d_expletd_lval()lv=printLvaldefaultCilPrinter()lvletd_offsetbase()off=defaultCilPrinter#pOffsetbaseoffletd_init()i=printInitdefaultCilPrinter()iletd_type()t=printTypedefaultCilPrinter()tlet_=pd_type:=d_typeletd_global()g=printGlobaldefaultCilPrinter()gletd_attrlist()a=printAttrsdefaultCilPrinter()aletd_attr()a=printAttrdefaultCilPrinter()alet_=pd_attr:=d_attrletd_attrparam()e=defaultCilPrinter#pAttrParam()eletd_label()l=defaultCilPrinter#pLabel()lletd_stmt()s=printStmtdefaultCilPrinter()sletd_block()b=printBlockdefaultCilPrinter()bletd_instr()i=printInstrdefaultCilPrinter()iletd_shortglobal()=functionGPragma(Attr(an,_),_)->dprintf"#pragma %s"an|GType(ti,_)->dprintf"typedef %s"ti.tname|GVarDecl(vi,_)->dprintf"declaration of %s"vi.vname|GVar(vi,_,_)->dprintf"definition of %s"vi.vname|GCompTag(ci,_)->dprintf"definition of %s"(compFullNameci)|GCompTagDecl(ci,_)->dprintf"declaration of %s"(compFullNameci)|GEnumTag(ei,_)->dprintf"definition of enum %s"ei.ename|GEnumTagDecl(ei,_)->dprintf"declaration of enum %s"ei.ename|GFun(fd,_)->dprintf"definition of %s"fd.svar.vname|GText_->text"GText"|GAsm_->text"GAsm"(* sm: given an ordinary CIL object printer, yield one which
behaves the same, except it never prints #line directives
(this is useful for debugging printfs) *)letdn_obj(func:unit->'a->doc):(unit->'a->doc)=begin(* construct the closure to return *)lettheFunc()(obj:'a):doc=beginletprevStyle=!lineDirectiveStyleinlineDirectiveStyle:=None;letret=(func()obj)in(* call underlying printer *)lineDirectiveStyle:=prevStyle;retendintheFuncend(* now define shortcuts for the non-location-printing versions,
with the naming prefix "dn_" *)letdn_exp=(dn_objd_exp)letdn_lval=(dn_objd_lval)(* dn_offset is missing because it has a different interface *)letdn_init=(dn_objd_init)letdn_type=(dn_objd_type)letdn_global=(dn_objd_global)letdn_attrlist=(dn_objd_attrlist)letdn_attr=(dn_objd_attr)letdn_attrparam=(dn_objd_attrparam)letdn_stmt=(dn_objd_stmt)letdn_instr=(dn_objd_instr)(* Now define a cilPlainPrinter *)classplainCilPrinterClass=(* We keep track of the composite types that we have done to avoid
recursion *)letdonecomps:(int,unit)H.t=H.create13inobject(self)inheritdefaultCilPrinterClass(*** PLAIN TYPES ***)method!pType(dn:docoption)()(t:typ)=matchdnwithNone->self#pOnlyType()t|Somed->d++text" : "++self#pOnlyType()tmethodprivatepOnlyType()=functionTVoida->dprintf"TVoid(@[%a@])"self#pAttrsa|TInt(ikind,a)->dprintf"TInt(@[%a,@?%a@])"d_ikindikindself#pAttrsa|TFloat(fkind,a)->dprintf"TFloat(@[%a,@?%a@])"d_fkindfkindself#pAttrsa|TNamed(t,a)->dprintf"TNamed(@[%s,@?%a,@?%a@])"t.tnameself#pOnlyTypet.ttypeself#pAttrsa|TPtr(t,a)->dprintf"TPtr(@[%a,@?%a@])"self#pOnlyTypetself#pAttrsa|TArray(t,l,a)->letdl=matchlwithNone->text"None"|Somel->dprintf"Some(@[%a@])"self#pExplindprintf"TArray(@[%a,@?%a,@?%a@])"self#pOnlyTypetinsertdlself#pAttrsa|TEnum(enum,a)->dprintf"Enum(%s,@[%a@])"enum.enameself#pAttrsa|TFun(tr,args,isva,a)->dprintf"TFun(@[%a,@?%a%s,@?%a@])"self#pOnlyTypetrinsert(ifargs=Nonethentext"None"else(docList~sep:(chr','++break)(fun(an,at,aa)->dprintf"%s: %a"anself#pOnlyTypeat))()(argsToListargs))(ifisvathen"..."else"")self#pAttrsa|TComp(comp,a)->ifH.memdonecompscomp.ckeythendprintf"TCompLoop(%s %s, _, %a)"(ifcomp.cstructthen"struct"else"union")comp.cnameself#pAttrscomp.cattrelsebeginH.adddonecompscomp.ckey();(* Add it before we do the fields *)letdoc=dprintf"TComp(@[%s %s,@?%a,@?%a,@?%a@])"(ifcomp.cstructthen"struct"else"union")comp.cname(docList~sep:(chr','++break)(funf->dprintf"%s : %a"f.fnameself#pOnlyTypef.ftype))comp.cfieldsself#pAttrscomp.cattrself#pAttrsainH.removedonecompscomp.ckey;(* Remove it after we do the fields, so printer doesn't have global state *)docend|TBuiltin_va_lista->dprintf"TBuiltin_va_list(%a)"self#pAttrsa(* Some plain pretty-printers. Unlike the above these expose all the
details of the internal representation *)method!pExp()=functionConst(c)->letd_plainconst()c=matchcwithCInt(i,ik,so)->letfmt=ifisSignedikthen"%d"else"%x"indprintf"Int(%s,%a,%s)"(Z.formatfmti)d_ikindik(matchsowithSomes->s|_->"None")|CStr(s,enc)->letenc_string=matchencwithNo_encoding->"_"|Utf8->"UTF8"intext("CStr(\""^escape_strings^"\","^enc_string^")")|CWStr(s,_)->dprintf"CWStr(%a)"d_constc|CChr(c)->text("CChr('"^escape_charc^"')")|CReal(f,fk,so)->dprintf"CReal(%f, %a, %s)"fd_fkindfk(matchsowithSomes->s|_->"None")|CEnum(_,s,_)->textsintext"Const("++d_plainconst()c++text")"|Lval(lv)->text"Lval("++(align++self#pLval()lv++unalign)++text")"|CastE(t,e)->dprintf"CastE(@[%a,@?%a@])"self#pOnlyTypetself#pExpe|UnOp(u,e1,_)->dprintf"UnOp(@[%a,@?%a@])"d_unopuself#pExpe1|BinOp(b,e1,e2,_)->letd_plainbinop()b=matchbwithPlusA->text"PlusA"|PlusPI->text"PlusPI"|IndexPI->text"IndexPI"|MinusA->text"MinusA"|MinusPP->text"MinusPP"|MinusPI->text"MinusPI"|_->d_binop()bindprintf"%a(@[%a,@?%a@])"d_plainbinopbself#pExpe1self#pExpe2|Question(e1,e2,e3,_)->dprintf"Question(@[%a,@?%a,@?%a@])"self#pExpe1self#pExpe2self#pExpe3|SizeOf(t)->text"sizeof("++self#pTypeNone()t++chr')'|SizeOfE(e)->text"sizeofE("++self#pExp()e++chr')'|SizeOfStr(s)->text"sizeofStr("++d_const()(CStr(s,No_encoding))++chr')'|AlignOf(t)->text"__alignof__("++self#pTypeNone()t++chr')'|AlignOfE(e)->text"__alignof__("++self#pExp()e++chr')'|Image->text"__imag__("++self#pExp()e++chr')'|Reale->text"__real__("++self#pExp()e++chr')'|StartOflv->dprintf"StartOf(%a)"self#pLvallv|AddrOf(lv)->dprintf"AddrOf(%a)"self#pLvallv|AddrOfLabel(sref)->dprintf"AddrOfLabel(%a)"self#pStmt!srefmethodprivated_plainoffset()=functionNoOffset->text"NoOffset"|Field(fi,o)->dprintf"Field(@[%s:%a,@?%a@])"fi.fnameself#pOnlyTypefi.ftypeself#d_plainoffseto|Index(e,o)->dprintf"Index(@[%a,@?%a@])"self#pExpeself#d_plainoffsetomethod!pInit()=functionSingleInite->dprintf"SI(%a)"d_expe|CompoundInit(t,initl)->letd_plainoneinit(o,i)=self#d_plainoffset()o++text" = "++self#pInit()iindprintf"CI(@[%a,@?%a@])"self#pOnlyTypet(docList~sep:(chr','++break)d_plainoneinit)initl(*
| ArrayInit (t, len, initl) ->
let idx = ref (- 1) in
let d_plainoneinit i =
incr idx;
text "[" ++ num !idx ++ text "] = " ++ self#pInit () i
in
dprintf "AI(@[%a,%d,@?%a@])" self#pOnlyType t len
(docList ~sep:(chr ',' ++ break) d_plainoneinit) initl
*)method!pLval()(lv:lval)=matchlvwith|Varvi,o->dprintf"Var(@[%s,@?%a@])"vi.vnameself#d_plainoffseto|Meme,o->dprintf"Mem(@[%a,@?%a@])"self#pExpeself#d_plainoffsetoendletplainCilPrinter=newplainCilPrinterClass(* And now some shortcuts *)letd_plainexp()e=plainCilPrinter#pExp()eletd_plaintype()t=plainCilPrinter#pTypeNone()tletd_plaininit()i=plainCilPrinter#pInit()iletd_plainlval()l=plainCilPrinter#pLval()lclasstypedescriptiveCilPrinter=objectinheritcilPrintermethodstartTemps:unit->unitmethodstopTemps:unit->unitmethodpTemps:unit->Pretty.docendclassdescriptiveCilPrinterClass(enable:bool):descriptiveCilPrinter=object(self)(** Like defaultCilPrinterClass, but instead of temporary variable
names it prints the description that was provided when the temp was
created. This is usually better for messages that are printed for end
users, although you may want the temporary names for debugging.
The boolean here enables descriptive printing. Usually use true
here, but you can set enable to false to make this class behave
like defaultCilPrinterClass. This allows subclasses to turn the
feature off. *)inheritdefaultCilPrinterClassassupervalmutabletemps:(varinfo*string*doc)list=[]valmutableuseTemps:bool=falsemethodstartTemps():unit=temps<-[];useTemps<-truemethodstopTemps():unit=temps<-[];useTemps<-falsemethodpTemps():doc=iftemps=[]thennilelsetext"\nWhere:\n "++docList~sep:(text"\n ")(fun(_,s,d)->dprintf"%s = %a"sinsertd)()(List.revtemps)methodprivatepVarDescriptive(vi:varinfo):doc=ifvi.vdescr<>nilthenbeginifvi.vdescrpure||notuseTempsthenvi.vdescrelsebegintrylet_,name,_=List.find(fun(vi',_,_)->vi==vi')tempsintextnamewithNot_found->letname="tmp"^string_of_int(List.lengthtemps)intemps<-(vi,name,vi.vdescr)::temps;textnameendendelsesuper#pVarvi(* Only substitute temp vars that appear in expressions.
(Other occurrences of lvalues are the left-hand sides of assignments,
but we shouldn't substitute there since "foo(a,b) = foo(a,b)"
would make no sense to the user.) *)method!pExp()(e:exp):doc=ifenablethenmatchewithLval(Varvi,o)|StartOf(Varvi,o)->self#pOffset(self#pVarDescriptivevi)o|AddrOf(Varvi,o)->(* No parens needed, since offsets have higher precedence than & *)text"& "++self#pOffset(self#pVarDescriptivevi)o|_->super#pExp()eelsesuper#pExp()eendletdescriptiveCilPrinter:descriptiveCilPrinter=((newdescriptiveCilPrinterClasstrue):>descriptiveCilPrinter)letdd_exp=descriptiveCilPrinter#pExpletdd_lval=descriptiveCilPrinter#pLval(* zra: this allows pretty printers not in cil.ml to
be exposed to cilmain.ml *)letprinterForMaincil=refdefaultCilPrinterletrecd_typsig()=functionTSArray(ts,eo,al)->dprintf"TSArray(@[%a,@?%a,@?%a@])"d_typsigtsinsert(text(matcheowithNone->"None"|Somee->"Some "^string_of_cilinte))d_attrlistal|TSPtr(ts,al)->dprintf"TSPtr(@[%a,@?%a@])"d_typsigtsd_attrlistal|TSComp(iss,name,al)->dprintf"TSComp(@[%s %s,@?%a@])"(ifissthen"struct"else"union")named_attrlistal|TSFun(rt,args,isva,al)->dprintf"TSFun(@[%a,@?%a,%B,@?%a@])"d_typsigrtinsert(matchargswith|None->text"None"|Someargs->docList~sep:(chr','++break)(d_typsig())()args)isvad_attrlistal|TSEnum(n,al)->dprintf"TSEnum(@[%s,@?%a@])"nd_attrlistal|TSBaset->dprintf"TSBase(%a)"d_typetletnewVID()=lett=!nextGlobalVIDinincrnextGlobalVID;t(* Make a varinfo. Used mostly as a helper function below *)letmakeVarinfoglobalname?inittyp=(* Strip const from type for locals *)letvi={vname=name;vid=newVID();vglob=global;vtype=ifglobalthentypelsetypeRemoveAttributes["pconst"]typ;vdecl=lu;vinit={init=init};vinline=false;vattr=[];vstorage=NoStorage;vaddrof=false;vreferenced=false;vdescr=nil;vdescrpure=true;vhasdeclinstruction=false;}inviletcopyVarinfo(vi:varinfo)(newname:string):varinfo=letvi'={viwithvname=newname;vid=newVID()}invi'letmakeLocalfdecnametypinit=(* a helper function *)fdec.smaxid<-1+fdec.smaxid;letvi=makeVarinfofalsename?init:inittypinvi(* Make a local variable and add it to a function *)letmakeLocalVarfdec?(insert=true)name?inittyp=letvi=makeLocalfdecnametypinitinifinsertthenfdec.slocals<-fdec.slocals@[vi];viletmakeTempVarfdec?(insert=true)?(name="__cil_tmp")?(descr=nil)?(descrpure=true)typ:varinfo=letrecfindUniqueName():string=letn=name^(string_of_int(1+fdec.smaxid))in(* Is this check a performance problem? We could bring the old
unchecked makeTempVar back as a separate function that assumes
the prefix name does not occur in the original program. *)if(List.exists(funvi->vi.vname=n)fdec.slocals)||(List.exists(funvi->vi.vname=n)fdec.sformals)thenbeginfdec.smaxid<-1+fdec.smaxid;findUniqueName()endelseninletname=findUniqueName()inletvi=makeLocalVarfdec~insertnametypinvi.vdescr<-descr;vi.vdescrpure<-descrpure;vi(* Set the formals and re-create the function name based on the information*)letsetFormals(f:fundec)(forms:varinfolist)=f.sformals<-forms;(* Set the formals *)matchunrollTypef.svar.vtypewithTFun(rt,_,isva,fa)->f.svar.vtype<-TFun(rt,Some(Util.list_map(funa->(a.vname,a.vtype,a.vattr))forms),isva,fa)|_->E.s(E.bug"Set formals. %s does not have function type\n"f.svar.vname)(* Set the types of arguments and results as given by the function type
passed as the second argument *)letsetFunctionType(f:fundec)(t:typ)=matchunrollTypetwithTFun(rt,Someargs,va,a)->ifList.lengthf.sformals<>List.lengthargsthenE.s(E.bug"setFunctionType: number of arguments differs from the number of formals");(* Change the function type. *)f.svar.vtype<-t;(* Change the sformals and we know that indirectly we'll change the
function type *)List.iter2(fun(an,at,aa)f->f.vtype<-at;f.vattr<-aa)argsf.sformals|_->E.s(E.bug"setFunctionType: not a function type")(* Set the types of arguments and results as given by the function type
passed as the second argument *)letsetFunctionTypeMakeFormals(f:fundec)(t:typ)=matchunrollTypetwithTFun(rt,Someargs,va,a)->iff.sformals<>[]thenE.s(E.warn"setFunctionTypMakeFormals called on function %s with some formals already"f.svar.vname);(* Change the function type. *)f.svar.vtype<-t;f.sformals<-[];f.sformals<-Util.list_map(fun(n,t,a)->makeLocalfntNone)args;setFunctionTypeft|_->E.s(E.bug"setFunctionTypeMakeFormals: not a function type: %a"d_typet)letsetMaxId(f:fundec)=f.smaxid<-List.lengthf.sformals+List.lengthf.slocals(* Make a formal variable for a function. Insert it in both the sformals
and the type of the function. You can optionally specify where to insert
this one. If where = "^" then it is inserted first. If where = "$" then
it is inserted last. Otherwise where must be the name of a formal after
which to insert this. By default it is inserted at the end. *)letmakeFormalVarfdec?(where="$")nametyp:varinfo=(* Search for the insertion place *)letthenewone=reffdec.svarin(* Just a placeholder *)letmakeit():varinfo=letvi=makeLocalfdecnametypNoneinthenewone:=vi;viinletrecloopFormals=function[]->ifwhere="$"then[makeit()]elseE.s(E.error"makeFormalVar: cannot find insert-after formal %s"where)|f::restwhenf.vname=where->f::makeit()::rest|f::rest->f::loopFormalsrestinletnewformals=ifwhere="^"thenmakeit()::fdec.sformalselseloopFormalsfdec.sformalsinsetFormalsfdecnewformals;!thenewone(* Make a global variable. Your responsibility to make sure that the name
is unique *)letmakeGlobalVarnametyp=letvi=makeVarinfotruenametypinvi(* Make an empty function *)letemptyFunctionname={svar=makeGlobalVarname(TFun(voidType,Some[],false,[]));smaxid=0;slocals=[];sformals=[];sbody=mkBlock[];smaxstmtid=None;sallstmts=[];}(* A dummy function declaration handy for initialization *)letdummyFunDec=emptyFunction"@dummy"letdummyFile={globals=[];fileName="<dummy>";globinit=None;globinitcalled=false;}(***** Load and store files as unmarshalled Ocaml binary data. ****)typesavedFile={savedFile:file;savedNextVID:int;savedNextCompinfoKey:int}letsaveBinaryFileChannel(cil_file:file)(outchan:out_channel)=letsave={savedFile=cil_file;savedNextVID=!nextGlobalVID;savedNextCompinfoKey=!nextCompinfoKey}inMarshal.to_channeloutchansave[]letsaveBinaryFile(cil_file:file)(filename:string)=letoutchan=open_out_binfilenameinsaveBinaryFileChannelcil_fileoutchan;close_outoutchan(** Read a {!file} in binary form from the filesystem. The first
argument is the name of a file previously created by
{!saveBinaryFile}. Because this also reads some global state,
this should be called before any other CIL code is parsed or generated. *)letloadBinaryFile(filename:string):file=letinchan=open_in_binfilenameinletloaded:savedFile=(Marshal.from_channelinchan:savedFile)inclose_ininchan;(* nextGlobalVID = 11 because CIL initialises many dummy variables *)if!nextGlobalVID!=11||!nextCompinfoKey!=1thenbegin(* In this case, we should change all of the varinfo and compinfo
keys in loaded.savedFile to prevent conflicts. But since that hasn't
been implemented yet, just print a warning. If you do implement this,
please send it to the CIL maintainers. *)ignore(E.warn"You are possibly loading a binary file after another file has been loaded. This isn't currently supported, so varinfo and compinfo id numbers may conflict.")end;nextGlobalVID:=maxloaded.savedNextVID!nextGlobalVID;nextCompinfoKey:=maxloaded.savedNextCompinfoKey!nextCompinfoKey;loaded.savedFile(* Take the name of a file and make a valid symbol name out of it. There are
a few characters that are not valid in symbols *)letmakeValidSymbolName(s:string)=letb=Bytes.copy(Bytes.of_strings)in(* So that we can update in place *)letl=String.lengthsinfori=0tol-1doletc=String.getsiinletisinvalid=matchcwith'-'|'.'->true|_->falseinifisinvalidthenBytes.setbi'_';done;Bytes.to_stringbletrecaddOffset(toadd:offset)(off:offset):offset=matchoffwithNoOffset->toadd|Field(fid',offset)->Field(fid',addOffsettoaddoffset)|Index(e,offset)->Index(e,addOffsettoaddoffset)(* Add an offset at the end of an lv *)letaddOffsetLvaltoadd(b,off):lval=b,addOffsettoaddoffletrecremoveOffset(off:offset):offset*offset=matchoffwithNoOffset->NoOffset,NoOffset|Field(f,NoOffset)->NoOffset,off|Index(i,NoOffset)->NoOffset,off|Field(f,restoff)->letoff',last=removeOffsetrestoffinField(f,off'),last|Index(i,restoff)->letoff',last=removeOffsetrestoffinIndex(i,off'),lastletremoveOffsetLval((b,off):lval):lval*offset=letoff',last=removeOffsetoffin(b,off'),last(*** Define the visiting engine ****)(* visit all the nodes in a Cil expression *)letdoVisit(vis:cilVisitor)(action:'avisitAction)(children:cilVisitor->'a->'a)(node:'a):'a=matchactionwithSkipChildren->node|ChangeTonode'->node'|DoChildren->childrenvisnode|ChangeDoChildrenPost(node',f)->f(childrenvisnode')(* mapNoCopy is like map but avoid copying the list if the function does not
change the elements. *)letmapNoCopy(f:'a->'a)l=letrecauxaccchanged=function[]->ifchangedthenList.revaccelsel|i::resti->leti'=fiinaux(i'::acc)(changed||i!=i')restiinaux[]falselletmapNoCopyList(f:'a->'alist)l=letrecauxaccchanged=function[]->ifchangedthenList.revaccelsel|i::resti->letil'=fiinlethas_changed=matchil'with[i']wheni'==i->false|_->trueinaux(List.rev_appendil'acc)(changed||has_changed)restiinaux[]falsel(* A visitor for lists *)letdoVisitList(vis:cilVisitor)(action:'alistvisitAction)(children:cilVisitor->'a->'a)(node:'a):'alist=matchactionwithSkipChildren->[node]|ChangeTonodes'->nodes'|DoChildren->[childrenvisnode]|ChangeDoChildrenPost(nodes',f)->f(mapNoCopy(funn->childrenvisn)nodes')letdebugVisit=falseletrecvisitCilExpr(vis:cilVisitor)(e:exp):exp=doVisitvis(vis#vexpre)childrenExpeandchildrenExp(vis:cilVisitor)(e:exp):exp=letvExpe=visitCilExprviseinletvTypt=visitCilTypevistinletvLvallv=visitCilLvalvislvinmatchewith|Const(CEnum(v,s,ei))->letv'=vExpvinifv'!=vthenConst(CEnum(v',s,ei))elsee|Const_->e|SizeOft->lett'=vTyptinift'!=tthenSizeOft'elsee|SizeOfEe1->lete1'=vExpe1inife1'!=e1thenSizeOfEe1'elsee|SizeOfStrs->e|Reale1->lete1'=vExpe1inife1'!=e1thenReale1'elsee|Image1->lete1'=vExpe1inife1'!=e1thenImage1'elsee|AlignOft->lett'=vTyptinift'!=tthenAlignOft'elsee|AlignOfEe1->lete1'=vExpe1inife1'!=e1thenAlignOfEe1'elsee|Lvallv->letlv'=vLvallviniflv'!=lvthenLvallv'elsee|UnOp(uo,e1,t)->lete1'=vExpe1inlett'=vTyptinife1'!=e1||t'!=tthenUnOp(uo,e1',t')elsee|BinOp(bo,e1,e2,t)->lete1'=vExpe1inlete2'=vExpe2inlett'=vTyptinife1'!=e1||e2'!=e2||t'!=tthenBinOp(bo,e1',e2',t')elsee|Question(e1,e2,e3,t)->lete1'=vExpe1inlete2'=vExpe2inlete3'=vExpe3inlett'=vTyptinife1'!=e1||e2'!=e2||e3'!=e3||t'!=tthenQuestion(e1',e2',e3',t')elsee|CastE(t,e1)->lett'=vTyptinlete1'=vExpe1inift'!=t||e1'!=e1thenCastE(t',e1')elsee|AddrOflv->letlv'=vLvallviniflv'!=lvthenAddrOflv'elsee|AddrOfLabel_->e|StartOflv->letlv'=vLvallviniflv'!=lvthenStartOflv'elseeandvisitCilInit(vis:cilVisitor)(forglob:varinfo)(atoff:offset)(i:init):init=letchildrenInit(vis:cilVisitor)(i:init):init=letfExpe=visitCilExprviseinletfTypt=visitCilTypevistinmatchiwith|SingleInite->lete'=fExpeinife'!=ethenSingleInite'elsei|CompoundInit(t,initl)->lett'=fTyptin(* Collect the new initializer list, in reverse. We prefer two
traversals to ensure tail-recursion. *)letnewinitl:(offset*init)listref=ref[]in(* Keep track whether the list has changed *)lethasChanged=reffalseinletdoOneInit((o,i)asoi)=leto'=visitCilInitOffsetvisoin(* use initializer version *)leti'=visitCilInitvisforglob(addOffseto'atoff)iinletnewio=ifo'!=o||i'!=ithenbeginhasChanged:=true;(o',i')endelseoiinnewinitl:=newio::!newinitlinList.iterdoOneInitinitl;letinitl'=if!hasChangedthenList.rev!newinitlelseinitlinift'!=t||initl'!=initlthenCompoundInit(t',initl')elseiindoVisitvis(vis#vinitforglobatoffi)childrenInitiandvisitCilLval(vis:cilVisitor)(lv:lval):lval=doVisitvis(vis#vlvallv)childrenLvallvandchildrenLval(vis:cilVisitor)(lv:lval):lval=(* and visit its subexpressions *)letvExpe=visitCilExprviseinletvOffoff=visitCilOffsetvisoffinmatchlvwithVarv,off->letv'=doVisitvis(vis#vvrblv)(fun_x->x)vinletoff'=vOffoffinifv'!=v||off'!=offthenVarv',off'elselv|Meme,off->lete'=vExpeinletoff'=vOffoffinife'!=e||off'!=offthenMeme',off'elselvandvisitCilOffset(vis:cilVisitor)(off:offset):offset=doVisitvis(vis#voffsoff)childrenOffsetoffandchildrenOffset(vis:cilVisitor)(off:offset):offset=letvOffoff=visitCilOffsetvisoffinmatchoffwithField(f,o)->leto'=vOffoinifo'!=othenField(f,o')elseoff|Index(e,o)->lete'=visitCilExprviseinleto'=vOffoinife'!=e||o'!=othenIndex(e',o')elseoff|NoOffset->off(* sm: for offsets in initializers, the 'startvisit' will be the
vinitoffs method, but we can re-use the childrenOffset from
above since recursive offsets are visited by voffs. (this point
is moot according to cil.mli which claims the offsets in
initializers will never recursively contain offsets)
*)andvisitCilInitOffset(vis:cilVisitor)(off:offset):offset=doVisitvis(vis#vinitoffsoff)childrenOffsetoffandvisitCilInstr(vis:cilVisitor)(i:instr):instrlist=letoldloc=!currentLocincurrentLoc:=(get_instrLoci);assertEmptyQueuevis;letres=doVisitListvis(vis#vinsti)childrenInstriincurrentLoc:=oldloc;(* See if we have accumulated some instructions *)vis#unqueueInstr()@resandchildrenInstr(vis:cilVisitor)(i:instr):instr=letfExpe=visitCilExprviseinletfLvallv=visitCilLvalvislvinmatchiwith|VarDecl(v,l)->i|Set(lv,e,l,el)->letlv'=fLvallvinlete'=fExpeiniflv'!=lv||e'!=ethenSet(lv',e',l,el)elsei|Call(None,f,args,l,el)->letf'=fExpfinletargs'=mapNoCopyfExpargsiniff'!=f||args'!=argsthenCall(None,f',args',l,el)elsei|Call(Somelv,fn,args,l,el)->letlv'=fLvallvinletfn'=fExpfninletargs'=mapNoCopyfExpargsiniflv'!=lv||fn'!=fn||args'!=argsthenCall(Somelv',fn',args',l,el)elsei|Asm(sl,isvol,outs,ins,clobs,l)->letouts'=mapNoCopy(fun((id,s,lv)aspair)->letlv'=fLvallviniflv'!=lvthen(id,s,lv')elsepair)outsinletins'=mapNoCopy(fun((id,s,e)aspair)->lete'=fExpeinife'!=ethen(id,s,e')elsepair)insinifouts'!=outs||ins'!=insthenAsm(sl,isvol,outs',ins',clobs,l)elsei(* visit all nodes in a Cil statement tree in preorder *)andvisitCilStmt(vis:cilVisitor)(s:stmt):stmt=letoldloc=!currentLocincurrentLoc:=(get_stmtLocs.skind);assertEmptyQueuevis;lettoPrepend:instrlistref=ref[]in(* childrenStmt may add to this *)letres=doVisitvis(vis#vstmts)(childrenStmttoPrepend)sin(* Now see if we have saved some instructions *)toPrepend:=!toPrepend@vis#unqueueInstr();(match!toPrependwith[]->()(* Return the same statement *)|_->(* Make our statement contain the instructions to prepend *)res.skind<-Block{battrs=[];bstmts=[mkStmt(Instr!toPrepend);mkStmtres.skind]});currentLoc:=oldloc;resandchildrenStmt(toPrepend:instrlistref):cilVisitor->stmt->stmt=(* this is a hack to avoid currying and reduce GC pressure *)();funviss->letfExpe=(visitCilExprvise)inletfBlockb=visitCilBlockvisbinletfInsti=visitCilInstrvisiin(* Just change the statement kind *)letskind'=matchs.skindwithBreak_|Continue_|Goto_|Return(None,_,_)->s.skind|ComputedGoto(e,l)->lete'=fExpeinife'!=ethenComputedGoto(e',l)elses.skind|Return(Somee,l,el)->lete'=fExpeinife'!=ethenReturn(Somee',l,el)elses.skind|Loop(b,l,el,s1,s2)->letb'=fBlockbinifb'!=bthenLoop(b',l,el,s1,s2)elses.skind|If(e,s1,s2,l,el)->lete'=fExpein(*if e queued any instructions, pop them here and remember them so that
they are inserted before the If stmt, not in the then block. *)toPrepend:=vis#unqueueInstr();lets1'=fBlocks1inlets2'=fBlocks2in(* the stmts in the blocks should have cleaned up after themselves.*)assertEmptyQueuevis;ife'!=e||s1'!=s1||s2'!=s2thenIf(e',s1',s2',l,el)elses.skind|Switch(e,b,stmts,l,el)->lete'=fExpeintoPrepend:=vis#unqueueInstr();(* insert these before the switch *)letb'=fBlockbin(* the stmts in b should have cleaned up after themselves.*)assertEmptyQueuevis;(* Don't do stmts, but we better not change those *)ife'!=e||b'!=bthenSwitch(e',b',stmts,l,el)elses.skind|Instril->letil'=mapNoCopyListfInstilinifil'!=ilthenInstril'elses.skind|Blockb->letb'=fBlockbinifb'!=bthenBlockb'elses.skindinifskind'!=s.skindthens.skind<-skind';(* Visit the labels *)letlabels'=letfLabel=functionCase(e,l,el)aslb->lete'=fExpeinife'!=ethenCase(e',l,el)elselb|CaseRange(e1,e2,l,el)aslb->lete1'=fExpe1inlete2'=fExpe2inife1'!=e1||e2'!=e2thenCaseRange(e1',e2',l,el)elselb|lb->lbinmapNoCopyfLabels.labelsiniflabels'!=s.labelsthens.labels<-labels';sandvisitCilBlock(vis:cilVisitor)(b:block):block=doVisitvis(vis#vblockb)childrenBlockbandchildrenBlock(vis:cilVisitor)(b:block):block=letfStmts=visitCilStmtvissinletstmts'=mapNoCopyfStmtb.bstmtsinifstmts'!=b.bstmtsthen{battrs=b.battrs;bstmts=stmts'}elsebandvisitCilType(vis:cilVisitor)(t:typ):typ=doVisitvis(vis#vtypet)childrenTypetandchildrenType(vis:cilVisitor)(t:typ):typ=(* look for types referred to inside t's definition *)letfTypt=visitCilTypevistinletfAttra=visitCilAttributesvisainmatchtwithTPtr(t1,a)->lett1'=fTypt1inleta'=fAttrainift1'!=t1||a'!=athenTPtr(t1',a')elset|TArray(t1,None,a)->lett1'=fTypt1inleta'=fAttrainift1'!=t1||a'!=athenTArray(t1',None,a')elset|TArray(t1,Somee,a)->lett1'=fTypt1inlete'=visitCilExprviseinleta'=fAttrainift1'!=t1||e'!=e||a'!=athenTArray(t1',Somee',a')elset(* DON'T recurse into the compinfo, this is done in visitCilGlobal.
User can iterate over cinfo.cfields manually, if desired.*)|TComp(cinfo,a)->leta'=fAttrainifa!=a'thenTComp(cinfo,a')elset|TFun(rettype,args,isva,a)->letrettype'=fTyprettypein(* iterate over formals, as variable declarations *)letargslist=argsToListargsinletvisitArg((an,at,aa)asarg)=letat'=fTypatinletaa'=fAttraainifat'!=at||aa'!=aathen(an,at',aa')elsearginletargslist'=mapNoCopyvisitArgargslistinleta'=fAttrainifrettype'!=rettype||argslist'!=argslist||a'!=athenletargs'=ifargslist'==argslistthenargselseSomeargslist'inTFun(rettype',args',isva,a')elset|TNamed(t1,a)->(* Do not go into the type. Will do it at the time of
GType *)leta'=fAttrainifa'!=athenTNamed(t1,a')elset|_->(* other types (TVoid, TInt, TFloat, TEnum, and TBuiltin_va_list)
don't contain nested types, but they do have attributes. *)leta=typeAttrstinleta'=fAttrainifa'!=athensetTypeAttrsta'elset(* for declarations, we visit the types inside; but for uses, *)(* we just visit the varinfo node *)andvisitCilVarDecl(vis:cilVisitor)(v:varinfo):varinfo=doVisitvis(vis#vvdecv)childrenVarDeclvandchildrenVarDecl(vis:cilVisitor)(v:varinfo):varinfo=v.vtype<-visitCilTypevisv.vtype;v.vattr<-visitCilAttributesvisv.vattr;(matchv.vinit.initwithNone->()|Somei->leti'=visitCilInitvisvNoOffsetiinifi'!=ithenv.vinit.init<-Somei');vandvisitCilAttributes(vis:cilVisitor)(al:attributelist):attributelist=letal'=mapNoCopyList(funx->doVisitListvis(vis#vattrx)childrenAttributex)alinifal'!=althen(* Must re-sort *)addAttributesal'[]elsealandchildrenAttribute(vis:cilVisitor)(a:attribute):attribute=letfAttrPa=visitCilAttrParamsvisainmatchawithAttr(n,args)->letargs'=mapNoCopyfAttrPargsinifargs'!=argsthenAttr(n,args')elseaandvisitCilAttrParams(vis:cilVisitor)(a:attrparam):attrparam=doVisitvis(vis#vattrparama)childrenAttrparamaandchildrenAttrparam(vis:cilVisitor)(aa:attrparam):attrparam=letfTypt=visitCilTypevistinletfAttrPa=visitCilAttrParamsvisainmatchaawithAInt_|AStr_->aa|ACons(n,args)->letargs'=mapNoCopyfAttrPargsinifargs'!=argsthenACons(n,args')elseaa|ASizeOft->lett'=fTyptinift'!=tthenASizeOft'elseaa|ASizeOfEe->lete'=fAttrPeinife'!=ethenASizeOfEe'elseaa|AAlignOft->lett'=fTyptinift'!=tthenAAlignOft'elseaa|AAlignOfEe->lete'=fAttrPeinife'!=ethenAAlignOfEe'elseaa|ASizeOfS_|AAlignOfS_->ignore(warn"Visitor inside of a type signature.");aa|AUnOp(uo,e1)->lete1'=fAttrPe1inife1'!=e1thenAUnOp(uo,e1')elseaa|ABinOp(bo,e1,e2)->lete1'=fAttrPe1inlete2'=fAttrPe2inife1'!=e1||e2'!=e2thenABinOp(bo,e1',e2')elseaa|ADot(ap,s)->letap'=fAttrPapinifap'!=apthenADot(ap',s)elseaa|AStarap->letap'=fAttrPapinifap'!=apthenAStarap'elseaa|AAddrOfap->letap'=fAttrPapinifap'!=apthenAAddrOfap'elseaa|AIndex(e1,e2)->lete1'=fAttrPe1inlete2'=fAttrPe2inife1'!=e1||e2'!=e2thenAIndex(e1',e2')elseaa|AQuestion(e1,e2,e3)->lete1'=fAttrPe1inlete2'=fAttrPe2inlete3'=fAttrPe3inife1'!=e1||e2'!=e2||e3'!=e3thenAQuestion(e1',e2',e3')elseaa|AAssign(e1,e2)->lete1'=fAttrPe1inlete2'=fAttrPe2inife1'!=e1||e2'!=e2thenAAssign(e1',e2')elseaaletrecvisitCilFunction(vis:cilVisitor)(f:fundec):fundec=ifdebugVisitthenignore(E.log"Visiting function %s\n"f.svar.vname);assertEmptyQueuevis;letf=doVisitvis(vis#vfuncf)childrenFunctionfinlettoPrepend=vis#unqueueInstr()iniftoPrepend<>[]thenf.sbody.bstmts<-mkStmt(InstrtoPrepend)::f.sbody.bstmts;fandchildrenFunction(vis:cilVisitor)(f:fundec):fundec=letvisitVarDeclvd=visitCilVarDeclvisvdinf.svar<-visitCilVarDeclvisf.svar;(* hit the function name *)(* visit local declarations *)f.slocals<-mapNoCopyvisitVarDeclf.slocals;(* visit the formals *)letnewformals=mapNoCopyvisitVarDeclf.sformalsin(* Make sure the type reflects the formals *)setFormalsfnewformals;(* Remember any new instructions that were generated while visiting
variable declarations. *)lettoPrepend=vis#unqueueInstr()inf.sbody<-visitCilBlockvisf.sbody;(* visit the body *)iftoPrepend<>[]thenf.sbody.bstmts<-mkStmt(InstrtoPrepend)::f.sbody.bstmts;fletrecvisitCilGlobal(vis:cilVisitor)(g:global):globallist=(*(trace "visit" (dprintf "visitCilGlobal\n"));*)letoldloc=!currentLocincurrentLoc:=(get_globalLocg);currentGlobal:=g;letres=doVisitListvis(vis#vglobg)childrenGlobalgincurrentLoc:=oldloc;resandchildrenGlobal(vis:cilVisitor)(g:global):global=matchgwith|GFun(f,l)->letf'=visitCilFunctionvisfiniff'!=fthenGFun(f',l)elseg|GType(t,l)->t.ttype<-visitCilTypevist.ttype;g|GEnumTagDecl_|GCompTagDecl_->g(* Nothing to visit *)|GEnumTag(enum,_)->(* (trace "visit" (dprintf "visiting global enum %s\n" enum.ename)); *)(* Do the values and attributes of the enumerated items *)letitemVisit(name,attrs,exp,loc)=(name,visitCilAttributesvisattrs,visitCilExprvisexp,loc)inenum.eitems<-mapNoCopyitemVisitenum.eitems;enum.eattr<-visitCilAttributesvisenum.eattr;g|GCompTag(comp,_)->(* (trace "visit" (dprintf "visiting global comp %s\n" comp.cname)); *)(* Do the types and attributes of the fields *)letfieldVisit=funfi->fi.ftype<-visitCilTypevisfi.ftype;fi.fattr<-visitCilAttributesvisfi.fattrinList.iterfieldVisitcomp.cfields;comp.cattr<-visitCilAttributesviscomp.cattr;g|GVarDecl(v,l)->letv'=visitCilVarDeclvisvinifv'!=vthenGVarDecl(v',l)elseg|GVar(v,inito,l)->letv'=visitCilVarDeclvisvinifv'!=vthenGVar(v',inito,l)elseg|GPragma(a,l)->beginmatchvisitCilAttributesvis[a]with[a']->ifa'!=athenGPragma(a',l)elseg|_->E.s(E.unimp"visitCilAttributes returns more than one attribute")end|_->g(** A visitor that does constant folding. If "machdep" is true then we do
machine dependent simplification (e.g., sizeof) *)classconstFoldVisitorClass(machdep:bool):cilVisitor=objectinheritnopCilVisitormethod!vinsti=matchiwith(* Skip two functions to which we add Sizeof to the type arguments.
See the comments for these above. *)Call(_,(Lval(Varvi,NoOffset)),_,_,_)when((vi.vname="__builtin_va_arg")||(vi.vname="__builtin_types_compatible_p"))->SkipChildren|_->DoChildrenmethod!vexpr(e:exp)=(* Do it bottom up *)ChangeDoChildrenPost(e,constFoldmachdep)endletconstFoldVisitor(machdep:bool)=newconstFoldVisitorClassmachdep(* Iterate over all globals, including the global initializer *)letiterGlobals(fl:file)(doone:global->unit):unit=letdoone'g=currentLoc:=get_globalLocg;dooneginList.iterdoone'fl.globals;(matchfl.globinitwithNone->()|Someg->doone'(GFun(g,locUnknown)))(* Fold over all globals, including the global initializer *)letfoldGlobals(fl:file)(doone:'a->global->'a)(acc:'a):'a=letdoone'accg=currentLoc:=get_globalLocg;dooneaccginletacc'=List.fold_leftdoone'accfl.globalsin(matchfl.globinitwithNone->acc'|Someg->doone'acc'(GFun(g,locUnknown)))(** Find a function or function prototype with the given name in the file.
If it does not exist, create a prototype with the given type, and return
the new varinfo. This is useful when you need to call a libc function
whose prototype may or may not already exist in the file.
Because the new prototype is added to the start of the file, you shouldn't
refer to any struct or union types in the function type.*)letfindOrCreateFunc(f:file)(name:string)(t:typ):varinfo=letrecsearchglist=matchglistwithGVarDecl(vi,_)::rest|GFun({svar=vi;_},_)::restwhenvi.vname=name->ifnot(isFunctionTypevi.vtype)thenE.s(error("findOrCreateFunc: can't create %s because another "^^"global exists with that name.")name);vi|_::rest->searchrest(* tail recursive *)|[]->(*not found, so create one *)lett'=unrollTypeDeeptinletnew_decl=makeGlobalVarnamet'inf.globals<-GVarDecl(new_decl,locUnknown)::f.globals;new_declinsearchf.globals(* A visitor for the whole file that does not change the globals *)letvisitCilFileSameGlobals(vis:cilVisitor)(f:file):unit=letfGlobg=visitCilGlobalvisginiterGlobalsf(fung->matchfGlobgwith[g']wheng'==g||Util.equalsg'g->()(* Try to do the pointer check first *)|gl->ignore(E.log"You used visitCilFilSameGlobals but the global got changed:\n %a\nchanged to %a\n"d_globalg(docList~sep:line(d_global()))gl);())(* Be careful with visiting the whole file because it might be huge. *)letvisitCilFile(vis:cilVisitor)(f:file):unit=letfGlobg=visitCilGlobalvisgin(* Scan the globals. Make sure this is tail recursive. *)letrecloop(acc:globallist)=function[]->f.globals<-List.revacc|g::restg->loop((List.rev(fGlobg))@acc)restginloop[]f.globals;(* the global initializer *)(matchf.globinitwithNone->()|Someg->f.globinit<-Some(visitCilFunctionvisg))(** Create or fetch the global initializer. Tries to put a call to the
function with the main_name into it *)letgetGlobInit?(main_name="main")(fl:file)=matchfl.globinitwithSomef->f|None->begin(* Sadly, we cannot use the Filename library because it does not like
function names with multiple . in them *)letf=letlen=String.lengthfl.fileNamein(* Find the last path separator and record the first . that we see,
going backwards *)letlastDot=refleninletrecfindLastPathSepi=ifi<0then-1elseletc=String.getfl.fileNameiinifc='/'||c='\\'thenielsebeginifc='.'&&!lastDot=lenthenlastDot:=i;findLastPathSep(i-1)endinletlastPathSep=findLastPathSep(len-1)inletbasenoext=String.subfl.fileName(lastPathSep+1)(!lastDot-lastPathSep-1)inemptyFunction(makeValidSymbolName("__globinit_"^basenoext))infl.globinit<-Somef;(* Now try to add a call to the global initialized at the beginning of
main *)letinserted=reffalseinList.iter(functionGFun(m,lm)whenm.svar.vname=main_name->(* Prepend a prototype to the global initializer *)fl.globals<-GVarDecl(f.svar,lm)::fl.globals;m.sbody.bstmts<-compactStmts(mkStmt(Instr[Call(None,Lval(varf.svar),[],locUnknown,locUnknown)])::m.sbody.bstmts);inserted:=true;if!E.verboseFlagthenignore(E.log"Inserted the globinit\n");fl.globinitcalled<-true;|_->())fl.globals;ifnot!insertedthenignore(E.warn"Cannot find %s to add global initializer %s"main_namef.svar.vname);fend(* Fold over all globals, including the global initializer *)letmapGlobals(fl:file)(doone:global->global):unit=fl.globals<-Util.list_mapdoonefl.globals;(matchfl.globinitwithNone->()|Someg->beginmatchdoone(GFun(g,locUnknown))withGFun(g',_)->fl.globinit<-Someg'|_->E.s(E.bug"mapGlobals: globinit is not a function")end)letdumpFile(pp:cilPrinter)(out:out_channel)(outfile:string)file=printDepth:=99999;(* We don't want ... in the output *)Pretty.fastMode:=true;if!E.verboseFlagthenignore(E.log"printing file %s\n"outfile);letprintx=fprintout~width:78xinprint(text("/* Generated by Goblint-CIL v. "^cilVersion^" */\n"^(* sm: I want to easily tell whether the generated output
is with print_CIL_Input or not *)"/* print_CIL_Input is "^(if!print_CIL_Inputthen"true"else"false")^" */\n\n"));iterGlobalsfile(fung->dumpGlobalppoutg);(* sm: we have to flush the output channel; if we don't then under *)(* some circumstances (I haven't figure out exactly when, but it happens *)(* more often with big inputs), we get a truncated output file *)flushout(******************
******************
******************)(* Convert an expression into an attribute, if possible. Otherwise raise
NotAnAttrParam *)exceptionNotAnAttrParamofexpletrecexpToAttrParam(e:exp):attrparam=matchewithConst(CInt(i,k,_))->leti'=mkCilintIkkiinifnot(is_int_cilinti')thenraise(NotAnAttrParame);AInt(int_of_cilinti')|Lval(Varv,NoOffset)->ACons(v.vname,[])|SizeOft->ASizeOft|SizeOfEe'->ASizeOfE(expToAttrParame')|UnOp(uo,e',_)->AUnOp(uo,expToAttrParame')|BinOp(bo,e1',e2',_)->ABinOp(bo,expToAttrParame1',expToAttrParame2')|_->raise(NotAnAttrParame)(******************** OPTIMIZATIONS *****)letrecpeepHole1(* Process one instruction and possibly replace it *)(doone:instr->instrlistoption)(* Scan a block and recurse inside nested blocks *)(ss:stmtlist):unit=letrecdoInstrList(il:instrlist):instrlist=matchilwith[]->[]|i::rest->beginmatchdooneiwithNone->i::doInstrListrest|Somesl->doInstrList(sl@rest)endinList.iter(funs->matchs.skindwithInstril->s.skind<-Instr(doInstrListil)|If(e,tb,eb,_,_)->peepHole1doonetb.bstmts;peepHole1dooneeb.bstmts|Switch(e,b,_,_,_)->peepHole1dooneb.bstmts|Loop(b,l,el,_,_)->peepHole1dooneb.bstmts|Blockb->peepHole1dooneb.bstmts|Return_|Goto_|ComputedGoto_|Break_|Continue_->())ssletrecpeepHole2(* Process two instructions and possibly replace them both *)(dotwo:instr*instr->instrlistoption)(ss:stmtlist):unit=letrecdoInstrList(il:instrlist):instrlist=matchilwith[]->[]|[i]->[i]|(i1::((i2::rest)asrest2))->beginmatchdotwo(i1,i2)withNone->i1::doInstrListrest2|Somesl->doInstrList(sl@rest)endinList.iter(funs->matchs.skindwithInstril->s.skind<-Instr(doInstrListil)|If(e,tb,eb,_,_)->peepHole2dotwotb.bstmts;peepHole2dotwoeb.bstmts|Switch(e,b,_,_,_)->peepHole2dotwob.bstmts|Loop(b,l,el,_,_)->peepHole2dotwob.bstmts|Blockb->peepHole2dotwob.bstmts|Return_|Goto_|ComputedGoto_|Break_|Continue_->())ss(*** Type signatures ***)(* Helper class for typeSig: replace any types in attributes with typsigs *)classtypeSigVisitor(typeSigConverter:typ->typsig)=objectinheritnopCilVisitormethod!vattrparamap=matchapwith|ASizeOft->ChangeTo(ASizeOfS(typeSigConvertert))|AAlignOft->ChangeTo(AAlignOfS(typeSigConvertert))|_->DoChildrenendlettypeSigAddAttrsa0t=ifa0==[]thentelsematchtwithTSBaset->TSBase(typeAddAttributesa0t)|TSPtr(ts,a)->TSPtr(ts,addAttributesa0a)|TSArray(ts,l,a)->TSArray(ts,l,addAttributesa0a)|TSComp(iss,n,a)->TSComp(iss,n,addAttributesa0a)|TSEnum(n,a)->TSEnum(n,addAttributesa0a)|TSFun(ts,tsargs,isva,a)->TSFun(ts,tsargs,isva,addAttributesa0a)(* Compute a type signature.
Use ~ignoreSign:true to convert all signed integer types to unsigned,
so that signed and unsigned will compare the same. *)letrectypeSigWithAttrs?(ignoreSign=false)doattrt=lettypeSig=typeSigWithAttrs~ignoreSigndoattrinletattrVisitor=newtypeSigVisitortypeSiginletdoattral=visitCilAttributesattrVisitor(doattral)inmatchtwith|TInt(ik,al)->letik'=ifignoreSignthenunsignedVersionOfikelseikinTSBase(TInt(ik',doattral))|TFloat(fk,al)->TSBase(TFloat(fk,doattral))|TVoidal->TSBase(TVoid(doattral))|TEnum(enum,a)->TSEnum(enum.ename,doattra)|TPtr(t,a)->TSPtr(typeSigt,doattra)|TArray(t,l,a)->(* We do not want fancy expressions in array lengths.
So constant fold the lengths *)letl'=matchlwithSomel->beginmatchconstFoldtruelwithConst(CInt(i,_,_))->Somei|e->None(* Returning None for length in a typesig if the length is not a constant (VLA) *)end|None->NoneinTSArray(typeSigt,l',doattra)|TComp(comp,a)->TSComp(comp.cstruct,comp.cname,doattr(addAttributescomp.cattra))|TFun(rt,args,isva,a)->TSFun(typeSigrt,(Util.list_map_opt(fun(_,atype,_)->(typeSigatype))args),isva,doattra)|TNamed(t,a)->typeSigAddAttrs(doattra)(typeSigt.ttype)|TBuiltin_va_listal->TSBase(TBuiltin_va_list(doattral))lettypeSigt=typeSigWithAttrs(funal->al)tlet_=pTypeSig:=typeSig(* Remove the attribute from the top-level of the type signature *)letsetTypeSigAttrs(a:attributelist)=functionTSBaset->TSBase(setTypeAttrsta)|TSPtr(ts,_)->TSPtr(ts,a)|TSArray(ts,l,_)->TSArray(ts,l,a)|TSComp(iss,n,_)->TSComp(iss,n,a)|TSEnum(n,_)->TSEnum(n,a)|TSFun(ts,tsargs,isva,_)->TSFun(ts,tsargs,isva,a)lettypeSigAttrs=functionTSBaset->typeAttrst|TSPtr(ts,a)->a|TSArray(ts,l,a)->a|TSComp(iss,n,a)->a|TSEnum(n,a)->a|TSFun(ts,tsargs,isva,a)->aletdExp:doc->exp=fund->Const(CStr(sprint~width:!lineLengthd,No_encoding))letdInstr:doc->location->instr=fundl->Asm([],[sprint~width:!lineLengthd],[],[],[],l)letdGlobal:doc->location->global=fundl->GAsm(sprint~width:!lineLengthd,l)(* Make an AddrOf. Given an lval of type T will give back an expression of
type ptr(T) *)letmkAddrOf((b,off)aslval):exp=(* Never take the address of a register variable *)(matchlvalwithVarvi,offwhenvi.vstorage=Register->vi.vstorage<-NoStorage|_->());matchlvalwithMeme,NoOffset->e(* Don't do this:
| b, Index(z, NoOffset) when isZero z -> StartOf (b, NoOffset)
&a[0] is not the same as a, e.g. within typeof and sizeof.
Code must be able to handle the results without this anyway... *)|_->AddrOflvalletmkAddrOrStartOf(lv:lval):exp=matchunrollType(typeOfLvallv)withTArray_->StartOflv|_->mkAddrOflv(* Make a Mem, while optimizing AddrOf. The type of the addr must be
TPtr(t) and the type of the resulting lval is t. Note that in CIL the
implicit conversion between a function and a pointer to a function does
not apply. You must do the conversion yourself using AddrOf *)letmkMem~(addr:exp)~(off:offset):lval=letres=matchaddr,offwithAddrOflv,_->addOffsetLvalofflv|StartOflv,_->(* Must be an array *)addOffsetLval(Index(zero,off))lv|_,_->Memaddr,offin(* ignore (E.log "memof : %a:%a\nresult = %a\n"
d_plainexp addr d_plainoffset off d_plainexp res); *)resletsplitFunctionType(ftype:typ):typ*(string*typ*attributes)listoption*bool*attributes=matchunrollTypeftypewithTFun(rt,args,isva,a)->rt,args,isva,a|_->E.s(bug"splitFunctionType invoked on a non function type %a"d_typeftype)letsplitFunctionTypeVI(fvi:varinfo):typ*(string*typ*attributes)listoption*bool*attributes=matchunrollTypefvi.vtypewithTFun(rt,args,isva,a)->rt,args,isva,a|_->E.s(bug"Function %s invoked on a non function type"fvi.vname)letgetCompField(cinfo:compinfo)(fieldName:string):fieldinfo=(List.find(funfi->fi.fname=fieldName)cinfo.cfields)letmkCastT~(e:exp)~(oldt:typ)~(newt:typ)=(* Do not remove old casts because they are conversions !!! *)ifUtil.equals(typeSigoldt)(typeSignewt)thenbegineendelsebegin(* Watch out for constants *)matchnewt,ewith(* Casts to _Bool are special: they behave like "!= 0" ISO C99 6.3.1.2 *)TInt(IBool,[]),Const(CInt(i,_,_))->letv=ifcompareizero_cilint=0thenzero_cilintelseone_cilintinConst(CInt(v,IBool,None))|TInt(newik,[]),Const(CInt(i,_,_))->kintegerCilintnewiki|_->CastE(newt,e)endletmkCast~(e:exp)~(newt:typ)=mkCastT~e:e~oldt:(typeOfe)~newt:newttypeexistsAction=ExistsTrue(* We have found it *)|ExistsFalse(* Stop processing this branch *)|ExistsMaybe(* This node is not what we are
looking for but maybe its
successors are *)letexistsType(f:typ->existsAction)(t:typ):bool=letmemo:(int,unit)H.t=H.create17in(* Memo table *)letrecloopt=matchftwithExistsTrue->true|ExistsFalse->false|ExistsMaybe->(matchtwithTNamed(t',_)->loopt'.ttype|TComp(c,_)->loopCompc|TArray(t',_,_)->loopt'|TPtr(t',_)->loopt'|TFun(rt,args,_,_)->(looprt||List.exists(fun(_,at,_)->loopat)(argsToListargs))|_->false)andloopCompc=ifH.memmemoc.ckeythen(* We are looping, the answer must be false *)falseelsebeginH.addmemoc.ckey();List.exists(funf->loopf.ftype)c.cfieldsendinloopt(* Try to do an increment, with constant folding *)letincrem(e:exp)(i:int)=letet=typeOfeinletbop=ifisPointerTypeetthenPlusPIelsePlusAinconstFoldfalse(BinOp(bop,e,integeri,et))exceptionLenOfArrayletlenOfArray(eo:expoption):int=matcheowithNone->raiseLenOfArray|Somee->beginmatchconstFoldtrueewith|Const(CInt(ni,_,_))whencompare_cilintnizero_cilint>=0->cilint_to_intni|e->raiseLenOfArrayend(*** Make an initializer for zeroe-ing a data type ***)letrecmakeZeroInit(t:typ):init=matchunrollTypetwithTInt(ik,_)->SingleInit(Const(CInt(zero_cilint,ik,None)))|TFloat(fk,_)->SingleInit(Const(CReal(0.0,fk,None)))|TEnum(e,_)->SingleInit(kintegere.ekind0)|TComp(comp,_)ast'whencomp.cstruct->letinits=List.fold_right(funfacc->iff.fname<>missingFieldNamethen(Field(f,NoOffset),makeZeroInitf.ftype)::accelseacc)comp.cfields[]inCompoundInit(t',inits)|TComp(comp,_)whennotcomp.cstruct->letfstfield,rest=matchcomp.cfieldswithf::rest->f,rest|[]->E.s(unimp"Cannot create init for empty union")inletfieldToInit=(* gcc initializes the whole union to zero. So choose the largest
field, and set that to zero. Choose the first field if possible. *)letfieldSizef=trybitsSizeOff.ftypewithSizeOfError_->0inletwidestField,widestFieldWidth=List.fold_left(funaccthisField->letwidestField,widestFieldWidth=accinletthisSize=fieldSizethisFieldinifthisSize>widestFieldWidththenthisField,thisSizeelseacc)(fstfield,fieldSizefstfield)restinwidestFieldinCompoundInit(t,[(Field(fieldToInit,NoOffset),makeZeroInitfieldToInit.ftype)])|TArray(bt,Somelen,_)ast'->letn=matchconstFoldtruelenwithConst(CInt(n,_,_))->cilint_to_intn|_->E.s(E.unimp"Cannot understand length of array")inletinitbt=makeZeroInitbtinletrecloopElemsacci=ifi<0thenaccelseloopElems((Index(integeri,NoOffset),initbt)::acc)(i-1)inCompoundInit(t',loopElems[](n-1))|TArray(bt,None,at)ast'->(* Unsized array, allow it and fill it in later
(see cabs2cil.ml, collectInitializer) *)CompoundInit(t',[])|TPtr_ast->SingleInit(if!insertImplicitCaststhenmkCast~e:zero~newt:telsezero)|x->E.s(unimp"Cannot initialize type: %a"d_typex)(** Fold over the list of initializers in a Compound (not also the nested
ones). [doinit] is called on every present initializer, even if it is of
compound type. The parameters of [doinit] are: the offset in the compound
(this is [Field(f,NoOffset)] or [Index(i,NoOffset)]), the initializer
value, expected type of the initializer value, accumulator. In the case of
arrays there might be missing zero-initializers at the end of the list.
These are scanned only if [implicit] is true. This is much like
[List.fold_left] except we also pass the type of the initializer. *)letfoldLeftCompound~(implicit:bool)~(doinit:offset->init->typ->'a->'a)~(ct:typ)~(initl:(offset*init)list)~(acc:'a)=matchunrollTypectwithTArray(bt,leno,_)->begin(* Scan the existing initializer *)letpart=List.fold_left(funacc(o,i)->doinitoibtacc)accinitlin(* See how many more we have to do *)matchlenowithSomelenewhenimplicit->beginmatchconstFoldtruelenewithConst(CInt(i,_,_))->letlen_array=cilint_to_intiinletlen_init=List.lengthinitliniflen_array>len_initthenletzi=makeZeroInitbtinletrecloopacci=ifi>=len_arraythenaccelseloop(doinit(Index(integeri,NoOffset))zibtacc)(i+1)inlooppart(len_init+1)elsepart|_->E.s(unimp"foldLeftCompound: array with initializer and non-constant length\n")end|_whennotimplicit->part|_->E.s(unimp"foldLeftCompound: TArray with initializer and no length")end|TComp(comp,_)->letgetTypeOffset=functionField(f,NoOffset)->f.ftype|_->E.s(bug"foldLeftCompound: malformed initializer")inList.fold_left(funacc(o,i)->doinitoi(getTypeOffseto)acc)accinitl|_->E.s(E.unimp"Type of Compound is not array or struct or union")letrecisCompleteTypet=matchunrollTypetwith|TArray(t,None,_)->false|TArray(t,Somez,_)whenisZeroz->false|TComp(comp,_)->(* Struct or union *)List.for_all(funfi->isCompleteTypefi.ftype)comp.cfields|_->truemoduleA=Alpha(** Uniquefy the variable names *)letuniqueVarNames(f:file):unit=(* Setup the alpha conversion table for globals *)letgAlphaTable:(string,locationA.alphaTableDataref)H.t=H.create113in(* Keep also track of the global names that we have used. Map them to the
variable ID. We do this only to check that we do not have two globals
with the same name. *)letglobalNames:(string,int)H.t=H.create113in(* Scan the file and add the global names to the table *)iterGlobalsf(functionGVarDecl(vi,l)|GVar(vi,_,l)|GFun({svar=vi;_},l)->(* See if we have used this name already for something else *)(tryletoldid=H.findglobalNamesvi.vnameinifoldid<>vi.vidthenignore(warn"The name %s is used for two distinct globals"vi.vname);(* Here if we have used this name already. Go ahead *)()withNot_found->begin(* Here if this is the first time we define a name *)H.addglobalNamesvi.vnamevi.vid;(* And register it *)A.registerAlphaName~alphaTable:gAlphaTable~undolist:None~lookupname:vi.vname~data:!currentLoc;()end)|_->());(* Now we must scan the function bodies and rename the locals *)iterGlobalsf(functionGFun(fdec,l)->begincurrentLoc:=l;(* Setup an undo list to be able to revert the changes to the
global alpha table *)letundolist=ref[]in(* Process one local variable *)letprocessLocal(v:varinfo)=letnewname,oldloc=A.newAlphaName~alphaTable:gAlphaTable~undolist:(Someundolist)~lookupname:v.vname~data:!currentLociniffalse&&newname<>v.vnamethen(* Disable this warning *)ignore(warn"uniqueVarNames: Changing the name of local %s in %s to %s (due to duplicate at %a)"v.vnamefdec.svar.vnamenewnamed_locoldloc);v.vname<-newnamein(* Do the formals first *)List.iterprocessLocalfdec.sformals;(* Fix the type again *)setFormalsfdecfdec.sformals;(* And now the locals *)List.iterprocessLocalfdec.slocals;(* Undo the changes to the global table *)A.undoAlphaChanges~alphaTable:gAlphaTable~undolist:!undolist;()end|_->());()(* A visitor that makes a deep copy of a function body *)classcopyFunctionVisitor(newname:string)=object(self)inheritnopCilVisitor(* Keep here a maping from locals to their copies *)valmap:(string,varinfo)H.t=H.create113(* Keep here a maping from statements to their copies *)valstmtmap:(int,stmt)H.t=H.create113valsid=ref0(* Will have to assign ids to statements *)(* Keep here a list of statements to be patched *)valpatches:stmtlistref=ref[]valargid=ref0(* This is the main function *)method!vfunc(f:fundec):fundecvisitAction=(* We need a map from the old locals/formals to the new ones *)H.clearmap;argid:=0;(* Make a copy of the fundec. *)letf'={fwithsvar=f.svar}inletpatchfunction(f':fundec)=(* Change the name. Only this late to allow the visitor to copy the
svar *)f'.svar.vname<-newname;letfindStmt(i:int)=tryH.findstmtmapiwithNot_found->E.s(bug"Cannot find the copy of stmt#%d"i)inletpatchstmt(s:stmt)=matchs.skindwithGoto(sr,l)->(* Make a copy of the reference *)letsr'=ref(findStmt!sr.sid)ins.skind<-Goto(sr',l)|Switch(e,body,cases,l,el)->s.skind<-Switch(e,body,Util.list_map(funcs->findStmtcs.sid)cases,l,el)|_->()inList.iterpatchstmt!patches;f'inpatches:=[];sid:=0;H.clearstmtmap;ChangeDoChildrenPost(f',patchfunction)(* We must create a new varinfo for each declaration. Memoize to
maintain sharing *)method!vvdec(v:varinfo)=(* Some varinfo have empty names. Give them some name *)ifv.vname=""thenbeginv.vname<-"arg"^string_of_int!argid;incrargidend;tryChangeTo(H.findmapv.vname)withNot_found->beginletv'={vwithvid=newVID()}inH.addmapv.vnamev';ChangeDoChildrenPost(v',funx->x)end(* We must replace references to local variables *)method!vvrbl(v:varinfo)=ifv.vglobthenSkipChildrenelsetryChangeTo(H.findmapv.vname)withNot_found->E.s(bug"Cannot find the new copy of local variable %s"v.vname)(* Replace statements. *)method!vstmt(s:stmt):stmtvisitAction=s.sid<-!sid;incrsid;lets'={swithsid=s.sid}inH.addstmtmaps.sids';(* Remember where we copied this *)(* if we have a Goto or a Switch remember them to fixup at end *)(matchs'.skindwith(Goto_|Switch_)->patches:=s'::!patches|_->());(* Do the children *)ChangeDoChildrenPost(s',funx->x)(* Copy blocks since they are mutable *)method!vblock(b:block)=ChangeDoChildrenPost({bwithbstmts=b.bstmts},funx->x)method!vglob_=E.s(bug"copyFunction should not be used on globals")end(* We need a function that copies a CIL function. *)letcopyFunction(f:fundec)(newname:string):fundec=visitCilFunction(newcopyFunctionVisitor(newname))f(********* Compute the CFG ********)letsid_counter=ref0letnew_sid()=letid=!sid_counterinincrsid_counter;idletstatements:stmtlistref=ref[](* Clear all info about the CFG in statements *)classclear:cilVisitor=objectinheritnopCilVisitormethod!vstmts=begins.sid<-!sid_counter;incrsid_counter;statements:=s::!statements;s.succs<-[];s.preds<-[];DoChildrenendmethod!vexpr_=SkipChildrenmethod!vtype_=SkipChildrenmethod!vinst_=SkipChildrenendletlinksourcedest=beginifnot(List.memdestsource.succs)thensource.succs<-dest::source.succs;ifnot(List.memsourcedest.preds)thendest.preds<-source::dest.predsendlettrylinksourcedest_option=matchdest_optionwithNone->()|Some(dest)->linksourcedest(** Compute the successors and predecessors of a block, given a fallthrough *)letrecsuccpred_blockbfallthroughrlabels=letrechandlesl=matchslwith[]->()|[a]->succpred_stmtafallthroughrlabels|hd::((next::_)astl)->succpred_stmthd(Somenext)rlabels;handletlinhandleb.bstmtsandsuccpred_stmtsfallthroughrlabels=s.fallthrough<-fallthrough;matchs.skindwithInstr_->trylinksfallthrough|Return_->()|Goto(dest,l)->links!dest|ComputedGoto(e,l)->List.iter(links)rlabels|Break_|Continue_|Switch_->failwith"computeCFGInfo: cannot be called on functions with break, continue or switch statements. Use prepareCFG first to remove them."|If(e1,b1,b2,l,el)->(matchb1.bstmtswith[]->trylinksfallthrough|hd::tl->(linkshd;succpred_blockb1fallthroughrlabels));(matchb2.bstmtswith[]->trylinksfallthrough|hd::tl->(linkshd;succpred_blockb2fallthroughrlabels))|Loop(b,l,el,_,_)->beginmatchb.bstmtswith[]->failwith"computeCFGInfo: empty loop"|hd::tl->linkshd;succpred_blockb(Some(hd))rlabelsend|Block(b)->beginmatchb.bstmtswith[]->trylinksfallthrough|hd::tl->linkshd;succpred_blockbfallthroughrlabelsendletcaseRangeFold(l:labellist)=letrecfoldacc=function|((Case_|Default_|Label_)asx)::xs->fold(x::acc)xs|CaseRange(el,eh,loc,eloc)::xs->letil,ih,ik=matchconstFoldtrueel,constFoldtrueehwithConst(CInt(il,ilk,_)),Const(CInt(ih,ihk,_))->mkCilintIkilkil,mkCilintIkihkih,commonIntKindilkihk|_->E.s(error"Cannot understand the constants in case range (%a and %a)"d_expeld_expeh)inifcompare_cilintilih>0thenE.s(error"Empty case range");letrecmkAll(i:cilint)acc=ifcompare_cilintiih>0thenaccelsemkAll(add_cilintione_cilint)(Case(kintegerCilintiki,loc,eloc)::acc)infold(mkAllilacc)xs|[]->List.revaccinfold[]l(* [weimer] Sun May 5 12:25:24 PDT 2002
This code was pulled from ext/switch.ml because it looks like we really
want it to be part of CIL.
Here is the magic handling to
(1) replace switch statements with if/goto
(2) remove "break"
(3) remove "default"
(4) remove "continue"
*)(* This alphaTable is used to prevent collision of label names when
transforming switch statements and loops. It uses a *unit*
alphaTableData ref because there isn't any information we need to
carry around. *)letlabelAlphaTable:(string,unitA.alphaTableDataref)H.t=H.create11(* Compute a fresh label name, call populateLabelAlphaTable with the appropriate fd before *)letfreshLabel(base:string)=fst(A.newAlphaName~alphaTable:labelAlphaTable~undolist:None~lookupname:base~data:())letrecxform_switch_stmtsbreak_destcont_dest=beginletsuffixe=matchgetIntegerewith|Somevalue->ifcompare_cilintvaluezero_cilint<0then"neg_"^string_of_cilint(neg_cilintvalue)elsestring_of_cilintvalue|None->"exp"ins.labels<-Util.list_map(funlab->matchlabwithLabel_->lab|Case(e,l,el)->letstr=Printf.sprintf"case_%s"(suffixe)inLabel(freshLabelstr,l,false)|CaseRange(e1,e2,l,el)->letstr=Printf.sprintf"caserange_%s_%s"(suffixe1)(suffixe2)inLabel(freshLabelstr,l,false)|Default(l,el)->Label(freshLabel"switch_default",l,false))s.labels;matchs.skindwith|Instr_|Return_|Goto_|ComputedGoto_->()|Break(l)->begintrys.skind<-Goto(break_dest(),l)withe->ignore(error"prepareCFG: break: %a@!"d_stmts);raiseeend|Continue(l)->begintrys.skind<-Goto(cont_dest(),l)withe->ignore(error"prepareCFG: continue: %a@!"d_stmts);raiseeend|If(e,b1,b2,l,el)->xform_switch_blockb1break_destcont_dest;xform_switch_blockb2break_destcont_dest|Switch(e,b,sl,l,el)->(* change
switch (se) {
case 0: s0 ;
case 1: s1 ; break;
...
}
into:
if (se == 0) goto label_0;
if (se == 1) goto label_1;
...
goto label_default; // If there is a [Default]
goto label_break; // If there is no [Default]
label_0: s0;
label_1: s1; goto label_break;
...
label_break: ; // break_stmt
The default case, if present, must be used only if *all*
non-default cases fail [ISO/IEC 9899:1999, �6.8.4.2, �5]. As
a result, we test all cases first, and hit 'default' only if
no case matches. However, we do not reorder the switch's
body, so fall-through still works as expected.
*)letbreak_stmt=mkStmt(Instr[])inbreak_stmt.labels<-[Label(freshLabel"switch_break",l,false)];(* To be changed into goto default if there if a [Default] *)letgoto_break=mkStmt(Goto(refbreak_stmt,l))in(* Return a list of [If] statements, equivalent to the cases of [stmt].
Use a single [If] and || operators if useLogicalOperators is true.
If [stmt] is a [Default], update goto label_break into goto
label_default.
*)letxform_choicestmt=letcases=List.filter(functionLabel_->false|_->true)stmt.labelsintry(* is this the default case? *)matchList.find(functionDefault_->true|_->false)caseswith|Default(dl,del)->(* We found a [Default], update the fallthrough goto *)goto_break.skind<-Goto(refstmt,dl);[]|_->E.s(bug"Unexpected pattern-matching failure")withNot_found->(* this is a list of specific cases *)matchcaseswith|((Case(_,cl,cel)|CaseRange(_,_,cl,cel))aslab)::lab_tl->(* assume that integer promotion and type conversion of cases is
performed by cabs2cil. *)letcomp_case_rangee1e2=BinOp(Ge,e,e1,intType),BinOp(Le,e,e2,intType)inletmake_complab=beginmatchlabwith|Case(exp,_,_)->BinOp(Eq,e,exp,intType)|CaseRange(e1,e2,_,_)when!useLogicalOperators->letc1,c2=comp_case_rangee1e2inBinOp(LAnd,c1,c2,intType)|_->E.s(bug"Unexpected pattern-matching failure")endinletmake_or_from_cases()=List.fold_left(funpredlabel->BinOp(LOr,pred,make_complabel,intType))(make_complab)lab_tlinletmake_if_stmtpredclcel=letthen_block=mkBlock[mkStmt(Goto(refstmt,cl))]inletelse_block=mkBlock[]inmkStmt(If(pred,then_block,else_block,cl,cel))inletmake_double_if_stmt(pred1,pred2)clcel=letthen_block=mkBlock[make_if_stmtpred2clcel]inletelse_block=mkBlock[]inmkStmt(If(pred1,then_block,else_block,cl,cel))inif!useLogicalOperatorsthen[make_if_stmt(make_or_from_cases())clcel]elseList.map(function|Case_aslab->make_if_stmt(make_complab)clcel|CaseRange(e1,e2,_,_)->make_double_if_stmt(comp_case_rangee1e2)clcel|_->E.s(bug"Unexpected pattern-matching failure"))cases|Default_::_|Label_::_->E.s(bug"Unexpected pattern-matching failure")|[]->E.s(bug"Block missing 'case' and 'default' in switch statement")inb.bstmts<-(List.flatten(List.mapxform_choicesl))@[goto_break]@b.bstmts@[break_stmt];s.skind<-Blockb;xform_switch_blockb(fun()->refbreak_stmt)cont_dest|Loop(b,l,el,_,_)->letbreak_stmt=mkStmt(Instr[])inbreak_stmt.labels<-[Label(freshLabel"while_break",l,false)];letcont_stmt=mkStmt(Instr[])incont_stmt.labels<-[Label(freshLabel"while_continue",l,false)];b.bstmts<-cont_stmt::b.bstmts;letthis_stmt=mkStmt(Loop(b,l,el,Some(cont_stmt),Some(break_stmt)))inletbreak_dest()=refbreak_stmtinletcont_dest()=refcont_stmtinxform_switch_blockbbreak_destcont_dest;break_stmt.succs<-s.succs;letnew_block=mkBlock[this_stmt;break_stmt]ins.skind<-Blocknew_block|Block(b)->xform_switch_blockbbreak_destcont_destendandxform_switch_blockbbreak_destcont_dest=tryletreclink_succssl=matchslwith|[]->()|hd::tl->(ifhd.succs=[]thenhd.succs<-tl);link_succstlinlink_succsb.bstmts;List.iter(funstmt->xform_switch_stmtstmtbreak_destcont_dest)b.bstmts;withe->List.iter(funstmt->ignore(warn"prepareCFG: %a@!"d_stmtstmt))b.bstmts;raisee(* Enter all the labels in a function into an alpha renaming table to
prevent duplicate labels when transforming loops and switch
statements. *)classregisterLabelsVisitor:cilVisitor=objectinheritnopCilVisitormethod!vstmt{labels=labels;_}=beginList.iter(functionLabel(name,_,_)->A.registerAlphaName~alphaTable:labelAlphaTable~undolist:None~lookupname:name~data:()|_->())labels;DoChildrenendmethod!vexpr_=SkipChildrenmethod!vtype_=SkipChildrenmethod!vinst_=SkipChildrenend(* Find all labels-as-value in a function to use them as successors of computed
gotos. Duplicated in src/ext/cfg.ml. *)classaddrOfLabelFinderslr=object(self)inheritnopCilVisitormethod!vexpre=matchewith|AddrOfLabelsref->slr:=!sref::(!slr);SkipChildren|_->DoChildrenendletfindAddrOfLabelStmts(b:block):stmtlist=letslr=ref[]inletvis=newaddrOfLabelFinderslrinignore(visitCilBlockvisb);!slr(* Clears the labelAlphaTable and populates it with all label names appearing in fd *)letpopulateLabelAlphaTable(fd:fundec):unit=H.clearlabelAlphaTable;ignore(visitCilFunction(newregisterLabelsVisitor)fd)(* prepare a function for computeCFGInfo by removing break, continue,
default and switch statements/labels and replacing them with Ifs and
Gotos. *)letprepareCFG(fd:fundec):unit=(* Labels are local to a function, so start with a clean slate by
clearing labelAlphaTable. Then register all labels. *)populateLabelAlphaTablefd;xform_switch_blockfd.sbody(fun()->failwith"prepareCFG: break with no enclosing loop")(fun()->failwith"prepareCFG: continue with no enclosing loop")(* make the cfg and return a list of statements *)letcomputeCFGInfo(f:fundec)(global_numbering:bool):unit=ifnotglobal_numberingthensid_counter:=0;statements:=[];letclear_it=newclearinignore(visitCilBlockclear_itf.sbody);f.smaxstmtid<-Some(!sid_counter);letrlabels=findAddrOfLabelStmtsf.sbodyinsuccpred_blockf.sbodyNonerlabels;letres=List.rev!statementsinstatements:=[];f.sallstmts<-res;()letinitCIL()=ifnot!initCIL_calledthenbegin(* Set the machine *)beginmatch!envMachinewithSomemachine->M.theMachine:=machine|None->M.theMachine:=M.gccend;(* Find the right ikind given the size *)letfindIkindSz(unsigned:bool)(sz:int):ikind=tryintKindForSizeszunsignedwithNot_found->E.s(E.unimp"initCIL: cannot find the right ikind for size %d\n"sz)in(* Find the right ikind given the name *)letfindIkindName(name:string):ikind=(* Test the most common sizes first *)ifname="int"thenIIntelseifname="unsigned int"thenIUIntelseifname="long"thenILongelseifname="unsigned long"thenIULongelseifname="long long"thenILongLongelseifname="unsigned long long"thenIULongLongelseifname="short"thenIShortelseifname="unsigned short"thenIUShortelseifname="char"thenICharelseifname="unsigned char"thenIUCharelseifname="__int128"thenIInt128elseifname="unsigned __int128"thenIUInt128elseE.s(E.unimp"initCIL: cannot find the right ikind for type %s\n"name)inupointType:=TInt(findIkindSztrue!M.theMachine.M.sizeof_ptr,[]);ptrdiffType:=TInt(findIkindSzfalse!M.theMachine.M.sizeof_ptr,[]);kindOfSizeOf:=findIkindName!M.theMachine.M.size_t;typeOfSizeOf:=TInt(!kindOfSizeOf,[]);wcharKind:=findIkindName!M.theMachine.M.wchar_t;wcharType:=TInt(!wcharKind,[]);char_is_unsigned:=!M.theMachine.M.char_is_unsigned;little_endian:=!M.theMachine.M.little_endian;underscore_name:=!M.theMachine.M.underscore_name;(* nextGlobalVID := 1; *)(* nextCompinfoKey := 1; *)initCIL_called:=true;initGccBuiltins()end(* We want to bring all type declarations before the data declarations. This
is needed for code of the following form:
int f(); // Prototype without arguments
typedef int FOO;
int f(FOO x) { ... }
In CIL the prototype also lists the type of the argument as being FOO,
which is undefined.
There is one catch with this scheme. If the type contains an array whose
length refers to variables then those variables must be declared before
the type *)letpullTypesForward=true(* Scan a type and collect the variables that are referred *)classgetVarsInGlobalClass(pacc:varinfolistref)=objectinheritnopCilVisitormethod!vvrbl(vi:varinfo)=pacc:=vi::!pacc;SkipChildrenmethod!vglob=functionGType_|GCompTag_->DoChildren|_->SkipChildrenendletgetVarsInGlobal(g:global):varinfolist=letpacc:varinfolistref=ref[]inletv:cilVisitor=newgetVarsInGlobalClasspaccinignore(visitCilGlobalvg);!paccletpushGlobal(g:global)~(types:globallistref)~(variables:globallistref)=ifnotpullTypesForwardthenvariables:=g::!variableselsebegin(* Collect a list of variables that are referred from the type. Return
Some if the global should go with the types and None if it should go
to the variables. *)letvarsintype:(varinfolist*location)option=matchgwithGType(_,l)|GCompTag(_,l)->Some(getVarsInGlobalg,l)|GEnumTag(_,l)|GPragma(Attr("pack",_),l)|GCompTagDecl(_,l)|GEnumTagDecl(_,l)->Some([],l)(* Move the warning pragmas early
| GPragma(Attr(s, _), l) when hasPrefix "warning" s -> Some ([], l)
*)|_->None(* Does not go with the types *)inmatchvarsintypewithNone->variables:=g::!variables|Some(vl,loc)->types:=(* insert declarations for referred variables ('vl'), before
the type definition 'g' itself *)g::(List.fold_left(funaccv->GVarDecl(v,loc)::acc)!typesvl)endtypeformatArg=Feofexp|Feoofexpoption(** For array lengths *)|Fuofunop|Fbofbinop|Fkofikind|FEofexplist(** For arguments in a function call *)|Ffof(string*typ*attributes)(** For a formal argument *)|FFof(string*typ*attributes)list(* For formal argument lists *)|Fvaofbool(** For the ellipsis in a function type *)|Fvofvarinfo|Floflval|Flooflvaloption(** For the result of a function call *)|Foofoffset|Fcofcompinfo|Fiofinstr|FIofinstrlist|Ftoftyp|Fdofint|Fgofstring|Fsofstmt|FSofstmtlist|FAofattributes|Fpofattrparam|FPofattrparamlist|FXofstringletd_formatarg()=functionFee->dprintf"Fe(%a)"d_expe|FeoNone->dprintf"Feo(None)"|Feo(Somee)->dprintf"Feo(%a)"d_expe|FE_->dprintf"FE()"|Fkik->dprintf"Fk()"|Fvab->dprintf"Fva(%b)"b|Ff(an,_,_)->dprintf"Ff(%s)"an|FF_->dprintf"FF(...)"|FA_->dprintf"FA(...)"|Fuuo->dprintf"Fu()"|Fbbo->dprintf"Fb()"|Fvv->dprintf"Fv(%s)"v.vname|Fll->dprintf"Fl(%a)"d_lvall|FloNone->dprintf"Flo(None)"|Flo(Somel)->dprintf"Flo(%a)"d_lvall|Foo->dprintf"Fo"|Fcci->dprintf"Fc(%s)"ci.cname|Fii->dprintf"Fi(...)"|FIi->dprintf"FI(...)"|Ftt->dprintf"Ft(%a)"d_typet|Fdn->dprintf"Fd(%d)"n|Fgs->dprintf"Fg(%s)"s|Fp_->dprintf"Fp(...)"|FPn->dprintf"FP(...)"|Fs_->dprintf"FS"|FS_->dprintf"FS"|FX_->dprintf"FX()"(* ------------------------------------------------------------------------- *)(* DEPRECATED FUNCTIONS *)(* These will eventually go away *)(* ------------------------------------------------------------------------- *)(** Deprecated. For compatibility with older programs, these are
aliases for {!builtinFunctions} *)letgccBuiltins=builtinFunctions