12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409 |
- ; Complete source for Twobit and Sparc assembler in one file.
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; See 'twobit-benchmark', at end.
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Completely fundamental pathname manipulation.
- ; This takes zero or more directory components and a file name and
- ; constructs a filename relative to the current directory.
- (define (make-relative-filename . components)
- (define (construct l)
- (if (null? (cdr l))
- l
- (cons (car l)
- (cons "/" (construct (cdr l))))))
- (if (null? (cdr components))
- (car components)
- (apply string-append (construct components))))
- ; This takes one or more directory components and constructs a
- ; directory name with proper termination (a crock -- we can finess
- ; this later).
- (define (pathname-append . components)
- (define (construct l)
- (cond ((null? (cdr l))
- l)
- ((string=? (car l) "")
- (construct (cdr l)))
- ((char=? #\/ (string-ref (car l) (- (string-length (car l)) 1)))
- (cons (car l) (construct (cdr l))))
- (else
- (cons (car l)
- (cons "/" (construct (cdr l)))))))
- (let ((n (if (null? (cdr components))
- (car components)
- (apply string-append (construct components)))))
- (if (not (char=? #\/ (string-ref n (- (string-length n) 1))))
- (string-append n "/")
- n)))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Nbuild parameters for SPARC Larceny.
- (define (make-nbuild-parameter dir source? verbose? hostdir hostname)
- (let ((parameters
- `((compiler . ,(pathname-append dir "Compiler"))
- (util . ,(pathname-append dir "Util"))
- (build . ,(pathname-append dir "Rts" "Build"))
- (source . ,(pathname-append dir "Lib"))
- (common-source . ,(pathname-append dir "Lib" "Common"))
- (repl-source . ,(pathname-append dir "Repl"))
- (interp-source . ,(pathname-append dir "Eval"))
- (machine-source . ,(pathname-append dir "Lib" "Sparc"))
- (common-asm . ,(pathname-append dir "Asm" "Common"))
- (sparc-asm . ,(pathname-append dir "Asm" "Sparc"))
- (target-machine . SPARC)
- (endianness . big)
- (word-size . 32)
- (always-source? . ,source?)
- (verbose-load? . ,verbose?)
- (compatibility . ,(pathname-append dir "Compat" hostdir))
- (host-system . ,hostname)
- )))
- (lambda (key)
- (let ((probe (assq key parameters)))
- (if probe
- (cdr probe)
- #f)))))
- (define nbuild-parameter
- (make-nbuild-parameter "" #f #f "Larceny" "Larceny"))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Useful list functions.
- ;
- ; Notes:
- ; * Reduce, reduce-right, fold-right, fold-left are compatible with MIT Scheme.
- ; * Make-list is compatible with MIT Scheme and Chez Scheme.
- ; * These are not (yet) compatible with Shivers's proposed list functions.
- ; * remq, remv, remove, remq!, remv!, remov!, every?, and some? are in the
- ; basic library.
- ; Destructively remove all associations whose key matches `key' from `alist'.
- (define (aremq! key alist)
- (cond ((null? alist) alist)
- ((eq? key (caar alist))
- (aremq! key (cdr alist)))
- (else
- (set-cdr! alist (aremq! key (cdr alist)))
- alist)))
- (define (aremv! key alist)
- (cond ((null? alist) alist)
- ((eqv? key (caar alist))
- (aremv! key (cdr alist)))
- (else
- (set-cdr! alist (aremv! key (cdr alist)))
- alist)))
- (define (aremove! key alist)
- (cond ((null? alist) alist)
- ((equal? key (caar alist))
- (aremove! key (cdr alist)))
- (else
- (set-cdr! alist (aremove! key (cdr alist)))
- alist)))
- ; Return a list of elements of `list' selected by the predicate.
- (define (filter select? list)
- (cond ((null? list) list)
- ((select? (car list))
- (cons (car list) (filter select? (cdr list))))
- (else
- (filter select? (cdr list)))))
- ; Return the first element of `list' selected by the predicate.
- (define (find selected? list)
- (cond ((null? list) #f)
- ((selected? (car list)) (car list))
- (else (find selected? (cdr list)))))
- ; Return a list with all duplicates (according to predicate) removed.
- (define (remove-duplicates list same?)
- (define (member? x list)
- (cond ((null? list) #f)
- ((same? x (car list)) #t)
- (else (member? x (cdr list)))))
- (cond ((null? list) list)
- ((member? (car list) (cdr list))
- (remove-duplicates (cdr list) same?))
- (else
- (cons (car list) (remove-duplicates (cdr list) same?)))))
- ; Return the least element of `list' according to some total order.
- (define (least less? list)
- (reduce (lambda (a b) (if (less? a b) a b)) #f list))
- ; Return the greatest element of `list' according to some total order.
- (define (greatest greater? list)
- (reduce (lambda (a b) (if (greater? a b) a b)) #f list))
-
- ; (mappend p l) = (apply append (map p l))
- (define (mappend proc l)
- (apply append (map proc l)))
- ; (make-list n) => (a1 ... an) for some ai
- ; (make-list n x) => (a1 ... an) where ai = x
- (define (make-list nelem . rest)
- (let ((val (if (null? rest) #f (car rest))))
- (define (loop n l)
- (if (zero? n)
- l
- (loop (- n 1) (cons val l))))
- (loop nelem '())))
- ; (reduce p x ()) => x
- ; (reduce p x (a)) => a
- ; (reduce p x (a b ...)) => (p (p a b) ...))
- (define (reduce proc initial l)
- (define (loop val l)
- (if (null? l)
- val
- (loop (proc val (car l)) (cdr l))))
- (cond ((null? l) initial)
- ((null? (cdr l)) (car l))
- (else (loop (car l) (cdr l)))))
- ; (reduce-right p x ()) => x
- ; (reduce-right p x (a)) => a
- ; (reduce-right p x (a b ...)) => (p a (p b ...))
- (define (reduce-right proc initial l)
- (define (loop l)
- (if (null? (cdr l))
- (car l)
- (proc (car l) (loop (cdr l)))))
- (cond ((null? l) initial)
- ((null? (cdr l)) (car l))
- (else (loop l))))
- ; (fold-left p x (a b ...)) => (p (p (p x a) b) ...)
- (define (fold-left proc initial l)
- (if (null? l)
- initial
- (fold-left proc (proc initial (car l)) (cdr l))))
- ; (fold-right p x (a b ...)) => (p a (p b (p ... x)))
- (define (fold-right proc initial l)
- (if (null? l)
- initial
- (proc (car l) (fold-right proc initial (cdr l)))))
- ; (iota n) => (0 1 2 ... n-1)
- (define (iota n)
- (let loop ((n (- n 1)) (r '()))
- (let ((r (cons n r)))
- (if (= n 0)
- r
- (loop (- n 1) r)))))
- ; (list-head (a1 ... an) m) => (a1 ... am) for m <= n
- (define (list-head l n)
- (if (zero? n)
- '()
- (cons (car l) (list-head (cdr l) (- n 1)))))
-
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Larceny -- compatibility library for Twobit running under Larceny.
- (define ($$trace x) #t)
- (define host-system 'larceny)
- ; Temporary?
- (define (.check! flag exn . args)
- (if (not flag)
- (apply error "Runtime check exception: " exn args)))
- ; The compatibility library loads Auxlib if compat:initialize is called
- ; without arguments. Compat:load will load fasl files when appropriate.
- (define (compat:initialize . rest)
- (if (null? rest)
- (let ((dir (nbuild-parameter 'compatibility)))
- (compat:load (string-append dir "compat2.sch"))
- (compat:load (string-append dir "../../Auxlib/list.sch"))
- (compat:load (string-append dir "../../Auxlib/pp.sch")))))
- (define (with-optimization level thunk)
- (thunk))
- ; Calls thunk1, and if thunk1 causes an error to be signaled, calls thunk2.
- (define (call-with-error-control thunk1 thunk2)
- (let ((eh (error-handler)))
- (error-handler (lambda args
- (error-handler eh)
- (thunk2)
- (apply eh args)))
- (thunk1)
- (error-handler eh)))
- (define (larc-new-extension fn ext)
- (let* ((l (string-length fn))
- (x (let loop ((i (- l 1)))
- (cond ((< i 0) #f)
- ((char=? (string-ref fn i) #\.) (+ i 1))
- (else (loop (- i 1)))))))
- (if (not x)
- (string-append fn "." ext)
- (string-append (substring fn 0 x) ext))))
- (define (compat:load filename)
- (define (loadit fn)
- (if (nbuild-parameter 'verbose-load?)
- (format #t "~a~%" fn))
- (load fn))
- (if (nbuild-parameter 'always-source?)
- (loadit filename)
- (let ((fn (larc-new-extension filename "fasl")))
- (if (and (file-exists? fn)
- (compat:file-newer? fn filename))
- (loadit fn)
- (loadit filename)))))
- (define (compat:file-newer? a b)
- (let* ((ta (file-modification-time a))
- (tb (file-modification-time b))
- (limit (vector-length ta)))
- (let loop ((i 0))
- (cond ((= i limit)
- #f)
- ((= (vector-ref ta i) (vector-ref tb i))
- (loop (+ i 1)))
- (else
- (> (vector-ref ta i) (vector-ref tb i)))))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Larceny -- second part of compatibility code
- ; This file ought to be compiled, but doesn't have to be.
- ;
- ; 12 April 1999
- (define host-system 'larceny) ; Don't remove this!
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; A well-defined sorting procedure.
- (define compat:sort (lambda (list less?) (sort list less?)))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; Well-defined character codes.
- ; Returns the UCS-2 code for a character.
- (define compat:char->integer char->integer)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; Input and output
- (define (write-lop item port)
- (lowlevel-write item port)
- (newline port)
- (newline port))
- (define write-fasl-datum lowlevel-write)
- ; The power of self-hosting ;-)
- (define (misc->bytevector x)
- (let ((bv (bytevector-like-copy x)))
- (typetag-set! bv $tag.bytevector-typetag)
- bv))
- (define string->bytevector misc->bytevector)
- (define bignum->bytevector misc->bytevector)
- (define (flonum->bytevector x)
- (clear-first-word (misc->bytevector x)))
- (define (compnum->bytevector x)
- (clear-first-word (misc->bytevector x)))
- ; Clears garbage word of compnum/flonum; makes regression testing much
- ; easier.
- (define (clear-first-word bv)
- (bytevector-like-set! bv 0 0)
- (bytevector-like-set! bv 1 0)
- (bytevector-like-set! bv 2 0)
- (bytevector-like-set! bv 3 0)
- bv)
- (define (list->bytevector l)
- (let ((b (make-bytevector (length l))))
- (do ((i 0 (+ i 1))
- (l l (cdr l)))
- ((null? l) b)
- (bytevector-set! b i (car l)))))
- (define bytevector-word-ref
- (let ((two^8 (expt 2 8))
- (two^16 (expt 2 16))
- (two^24 (expt 2 24)))
- (lambda (bv i)
- (+ (* (bytevector-ref bv i) two^24)
- (* (bytevector-ref bv (+ i 1)) two^16)
- (* (bytevector-ref bv (+ i 2)) two^8)
- (bytevector-ref bv (+ i 3))))))
- (define (twobit-format fmt . rest)
- (let ((out (open-output-string)))
- (apply format out fmt rest)
- (get-output-string out)))
- ; This needs to be a random number in both a weaker and stronger sense
- ; than `random': it doesn't need to be a truly random number, so a sequence
- ; of calls can return a non-random sequence, but if two processes generate
- ; two sequences, then those sequences should not be the same.
- ;
- ; Gross, huh?
- (define (an-arbitrary-number)
- (system "echo \\\"`date`\\\" > a-random-number")
- (let ((x (string-hash (call-with-input-file "a-random-number" read))))
- (delete-file "a-random-number")
- x))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; Miscellaneous
- (define cerror error)
- ; eof
- ; Copyright 1991 Wiliam Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Sets represented as lists.
- ;
- ; 5 April 1999.
- (define (empty-set) '())
- (define (empty-set? x) (null? x))
- (define (make-set x)
- (define (loop x y)
- (cond ((null? x) y)
- ((member (car x) y) (loop (cdr x) y))
- (else (loop (cdr x) (cons (car x) y)))))
- (loop x '()))
- (define (set-equal? x y)
- (and (subset? x y) (subset? y x)))
- (define (subset? x y)
- (every? (lambda (x) (member x y))
- x))
- ; To get around MacScheme's limit on the number of arguments.
- (define apply-union)
- (define union
- (letrec ((union2
- (lambda (x y)
- (cond ((null? x) y)
- ((member (car x) y)
- (union2 (cdr x) y))
- (else (union2 (cdr x) (cons (car x) y)))))))
-
- (set! apply-union
- (lambda (sets)
- (do ((sets sets (cdr sets))
- (result '() (union2 (car sets) result)))
- ((null? sets)
- result))))
-
- (lambda args
- (cond ((null? args) '())
- ((null? (cdr args)) (car args))
- ((null? (cddr args)) (union2 (car args) (cadr args)))
- (else (union2 (union2 (car args)
- (cadr args))
- (apply union (cddr args))))))))
- (define intersection
- (letrec ((intersection2
- (lambda (x y)
- (cond ((null? x) '())
- ((member (car x) y)
- (cons (car x) (intersection2 (cdr x) y)))
- (else (intersection2 (cdr x) y))))))
- (lambda args
- (cond ((null? args) '())
- ((null? (cdr args)) (car args))
- ((null? (cddr args)) (intersection2 (car args) (cadr args)))
- (else (intersection2 (intersection2 (car args)
- (cadr args))
- (apply intersection (cddr args))))))))
- (define (difference x y)
- (cond ((null? x) '())
- ((member (car x) y)
- (difference (cdr x) y))
- (else (cons (car x) (difference (cdr x) y)))))
- ; Reasonably portable hashing on EQ?, EQV?, EQUAL?.
- ; Requires bignums, SYMBOL-HASH.
- ;
- ; Given any Scheme object, returns a non-negative exact integer
- ; less than 2^24.
- (define object-hash (lambda (x) 0)) ; hash on EQ?, EQV?
- (define equal-hash (lambda (x) 0)) ; hash on EQUAL?
- (let ((n 16777216)
- (n-1 16777215)
- (adj:fixnum 9000000)
- (adj:negative 8000000)
- (adj:large 7900000)
- (adj:ratnum 7800000)
- (adj:complex 7700000)
- (adj:flonum 7000000)
- (adj:compnum 6900000)
- (adj:char 6111000)
- (adj:string 5022200)
- (adj:vector 4003330)
- (adj:misc 3000444)
- (adj:pair 2555000)
- (adj:proc 2321001)
- (adj:iport 2321002)
- (adj:oport 2321003)
- (adj:weird 2321004)
- (budget0 32))
-
- (define (combine hash adjustment)
- (modulo (+ hash hash hash adjustment) 16777216))
-
- (define (hash-on-equal x budget)
- (if (> budget 0)
- (cond ((string? x)
- (string-hash x))
- ((pair? x)
- (let ((budget (quotient budget 2)))
- (combine (hash-on-equal (car x) budget)
- (hash-on-equal (cdr x) budget))))
- ((vector? x)
- (let ((n (vector-length x))
- (budget (quotient budget 4)))
- (if (> n 0)
- (combine
- (combine (hash-on-equal (vector-ref x 0) budget)
- (hash-on-equal (vector-ref x (- n 1)) budget))
- (hash-on-equal (vector-ref x (quotient n 2))
- (+ budget budget)))
- adj:vector)))
- (else
- (object-hash x)))
- adj:weird))
-
- (set! object-hash
- (lambda (x)
- (cond ((symbol? x)
- (symbol-hash x))
- ((number? x)
- (if (exact? x)
- (cond ((integer? x)
- (cond ((negative? x)
- (combine (object-hash (- x)) adj:negative))
- ((< x n)
- (combine x adj:fixnum))
- (else
- (combine (modulo x n) adj:large))))
- ((rational? x)
- (combine (combine (object-hash (numerator x))
- adj:ratnum)
- (object-hash (denominator x))))
- ((real? x)
- adj:weird)
- ((complex? x)
- (combine (combine (object-hash (real-part x))
- adj:complex)
- (object-hash (imag-part x))))
- (else
- adj:weird))
- (cond (#t
- ; We can't really do anything with inexact numbers
- ; unless infinities and NaNs behave reasonably.
- adj:flonum)
- ((rational? x)
- (combine
- (combine (object-hash
- (inexact->exact (numerator x)))
- adj:flonum)
- (object-hash (inexact->exact (denominator x)))))
- ((real? x)
- adj:weird)
- ((complex? x)
- (combine (combine (object-hash (real-part x))
- adj:compnum)
- (object-hash (imag-part x))))
- (else adj:weird))))
- ((char? x)
- (combine (char->integer x) adj:char))
- ((string? x)
- (combine (string-length x) adj:string))
- ((vector? x)
- (combine (vector-length x) adj:vector))
- ((eq? x #t)
- (combine 1 adj:misc))
- ((eq? x #f)
- (combine 2 adj:misc))
- ((null? x)
- (combine 3 adj:misc))
- ((pair? x)
- adj:pair)
- ((procedure? x)
- adj:proc)
- ((input-port? x)
- adj:iport)
- ((output-port? x)
- adj:oport)
- (else
- adj:weird))))
-
- (set! equal-hash
- (lambda (x)
- (hash-on-equal x budget0)))); Hash tables.
- ; Requires CALL-WITHOUT-INTERRUPTS.
- ; This code should be thread-safe provided VECTOR-REF is atomic.
- ;
- ; (make-hashtable <hash-function> <bucket-searcher> <size>)
- ;
- ; Returns a newly allocated mutable hash table
- ; using <hash-function> as the hash function
- ; and <bucket-searcher>, e.g. ASSQ, ASSV, ASSOC, to search a bucket
- ; with <size> buckets at first, expanding the number of buckets as needed.
- ; The <hash-function> must accept a key and return a non-negative exact
- ; integer.
- ;
- ; (make-hashtable <hash-function> <bucket-searcher>)
- ;
- ; Equivalent to (make-hashtable <hash-function> <bucket-searcher> n)
- ; for some value of n chosen by the implementation.
- ;
- ; (make-hashtable <hash-function>)
- ;
- ; Equivalent to (make-hashtable <hash-function> assv).
- ;
- ; (make-hashtable)
- ;
- ; Equivalent to (make-hashtable object-hash assv).
- ;
- ; (hashtable-contains? <hashtable> <key>)
- ;
- ; Returns true iff the <hashtable> contains an entry for <key>.
- ;
- ; (hashtable-fetch <hashtable> <key> <flag>)
- ;
- ; Returns the value associated with <key> in the <hashtable> if the
- ; <hashtable> contains <key>; otherwise returns <flag>.
- ;
- ; (hashtable-get <hashtable> <key>)
- ;
- ; Equivalent to (hashtable-fetch <hashtable> <key> #f)
- ;
- ; (hashtable-put! <hashtable> <key> <value>)
- ;
- ; Changes the <hashtable> to associate <key> with <value>, replacing
- ; any existing association for <key>.
- ;
- ; (hashtable-remove! <hashtable> <key>)
- ;
- ; Removes any association for <key> within the <hashtable>.
- ;
- ; (hashtable-clear! <hashtable>)
- ;
- ; Removes all associations from the <hashtable>.
- ;
- ; (hashtable-size <hashtable>)
- ;
- ; Returns the number of keys contained within the <hashtable>.
- ;
- ; (hashtable-for-each <procedure> <hashtable>)
- ;
- ; The <procedure> must accept two arguments, a key and the value
- ; associated with that key. Calls the <procedure> once for each
- ; key-value association. The order of these calls is indeterminate.
- ;
- ; (hashtable-map <procedure> <hashtable>)
- ;
- ; The <procedure> must accept two arguments, a key and the value
- ; associated with that key. Calls the <procedure> once for each
- ; key-value association, and returns a list of the results. The
- ; order of the calls is indeterminate.
- ;
- ; (hashtable-copy <hashtable>)
- ;
- ; Returns a copy of the <hashtable>.
- ; These global variables are assigned new values later.
- (define make-hashtable (lambda args '*))
- (define hashtable-contains? (lambda (ht key) #f))
- (define hashtable-fetch (lambda (ht key flag) flag))
- (define hashtable-get (lambda (ht key) (hashtable-fetch ht key #f)))
- (define hashtable-put! (lambda (ht key val) '*))
- (define hashtable-remove! (lambda (ht key) '*))
- (define hashtable-clear! (lambda (ht) '*))
- (define hashtable-size (lambda (ht) 0))
- (define hashtable-for-each (lambda (ht proc) '*))
- (define hashtable-map (lambda (ht proc) '()))
- (define hashtable-copy (lambda (ht) ht))
- ; Implementation.
- ; A hashtable is represented as a vector of the form
- ;
- ; #(("HASHTABLE") <count> <hasher> <searcher> <buckets>)
- ;
- ; where <count> is the number of associations within the hashtable,
- ; <hasher> is the hash function, <searcher> is the bucket searcher,
- ; and <buckets> is a vector of buckets.
- ;
- ; The <hasher> and <searcher> fields are constant, but
- ; the <count> and <buckets> fields are mutable.
- ;
- ; For thread-safe operation, the mutators must modify both
- ; as an atomic operation. Other operations do not require
- ; critical sections provided VECTOR-REF is an atomic operation
- ; and the operation does not modify the hashtable, does not
- ; reference the <count> field, and fetches the <buckets>
- ; field exactly once.
- (let ((doc (list "HASHTABLE"))
- (count (lambda (ht) (vector-ref ht 1)))
- (count! (lambda (ht n) (vector-set! ht 1 n)))
- (hasher (lambda (ht) (vector-ref ht 2)))
- (searcher (lambda (ht) (vector-ref ht 3)))
- (buckets (lambda (ht) (vector-ref ht 4)))
- (buckets! (lambda (ht v) (vector-set! ht 4 v)))
- (defaultn 10))
- (let ((hashtable? (lambda (ht)
- (and (vector? ht)
- (= 5 (vector-length ht))
- (eq? doc (vector-ref ht 0)))))
- (hashtable-error (lambda (x)
- (display "ERROR: Bad hash table: ")
- (newline)
- (write x)
- (newline))))
-
- ; Internal operations.
-
- (define (make-ht hashfun searcher size)
- (vector doc 0 hashfun searcher (make-vector size '())))
-
- ; Substitute x for the first occurrence of y within the list z.
- ; y is known to occur within z.
-
- (define (substitute1 x y z)
- (cond ((eq? y (car z))
- (cons x (cdr z)))
- (else
- (cons (car z)
- (substitute1 x y (cdr z))))))
-
- ; Remove the first occurrence of x from y.
- ; x is known to occur within y.
-
- (define (remq1 x y)
- (cond ((eq? x (car y))
- (cdr y))
- (else
- (cons (car y)
- (remq1 x (cdr y))))))
-
- (define (resize ht0)
- (call-without-interrupts
- (lambda ()
- (let ((ht (make-ht (hasher ht0)
- (searcher ht0)
- (+ 1 (* 2 (count ht0))))))
- (ht-for-each (lambda (key val)
- (put! ht key val))
- ht0)
- (buckets! ht0 (buckets ht))))))
-
- ; Returns the contents of the hashtable as a vector of pairs.
-
- (define (contents ht)
- (let* ((v (buckets ht))
- (n (vector-length v))
- (z (make-vector (count ht) '())))
- (define (loop i bucket j)
- (if (null? bucket)
- (if (= i n)
- (if (= j (vector-length z))
- z
- (begin (display "BUG in hashtable")
- (newline)
- '#()))
- (loop (+ i 1)
- (vector-ref v i)
- j))
- (let ((entry (car bucket)))
- (vector-set! z j (cons (car entry) (cdr entry)))
- (loop i
- (cdr bucket)
- (+ j 1)))))
- (loop 0 '() 0)))
-
- (define (contains? ht key)
- (if (hashtable? ht)
- (let* ((v (buckets ht))
- (n (vector-length v))
- (h (modulo ((hasher ht) key) n))
- (b (vector-ref v h)))
- (if ((searcher ht) key b)
- #t
- #f))
- (hashtable-error ht)))
-
- (define (fetch ht key flag)
- (if (hashtable? ht)
- (let* ((v (buckets ht))
- (n (vector-length v))
- (h (modulo ((hasher ht) key) n))
- (b (vector-ref v h))
- (probe ((searcher ht) key b)))
- (if probe
- (cdr probe)
- flag))
- (hashtable-error ht)))
-
- (define (put! ht key val)
- (if (hashtable? ht)
- (call-without-interrupts
- (lambda ()
- (let* ((v (buckets ht))
- (n (vector-length v))
- (h (modulo ((hasher ht) key) n))
- (b (vector-ref v h))
- (probe ((searcher ht) key b)))
- (if probe
- ; Using SET-CDR! on the probe would make it necessary
- ; to synchronize the CONTENTS routine.
- (vector-set! v h (substitute1 (cons key val) probe b))
- (begin (count! ht (+ (count ht) 1))
- (vector-set! v h (cons (cons key val) b))
- (if (> (count ht) n)
- (resize ht)))))
- #f))
- (hashtable-error ht)))
-
- (define (remove! ht key)
- (if (hashtable? ht)
- (call-without-interrupts
- (lambda ()
- (let* ((v (buckets ht))
- (n (vector-length v))
- (h (modulo ((hasher ht) key) n))
- (b (vector-ref v h))
- (probe ((searcher ht) key b)))
- (if probe
- (begin (count! ht (- (count ht) 1))
- (vector-set! v h (remq1 probe b))
- (if (< (* 2 (+ defaultn (count ht))) n)
- (resize ht))))
- #f)))
- (hashtable-error ht)))
-
- (define (clear! ht)
- (if (hashtable? ht)
- (call-without-interrupts
- (lambda ()
- (begin (count! ht 0)
- (buckets! ht (make-vector defaultn '()))
- #f)))
- (hashtable-error ht)))
-
- (define (size ht)
- (if (hashtable? ht)
- (count ht)
- (hashtable-error ht)))
-
- ; This code must be written so that the procedure can modify the
- ; hashtable without breaking any invariants.
-
- (define (ht-for-each f ht)
- (if (hashtable? ht)
- (let* ((v (contents ht))
- (n (vector-length v)))
- (do ((j 0 (+ j 1)))
- ((= j n))
- (let ((x (vector-ref v j)))
- (f (car x) (cdr x)))))
- (hashtable-error ht)))
-
- (define (ht-map f ht)
- (if (hashtable? ht)
- (let* ((v (contents ht))
- (n (vector-length v)))
- (do ((j 0 (+ j 1))
- (results '() (let ((x (vector-ref v j)))
- (cons (f (car x) (cdr x))
- results))))
- ((= j n)
- (reverse results))))
- (hashtable-error ht)))
-
- (define (ht-copy ht)
- (if (hashtable? ht)
- (let* ((newtable (make-hashtable (hasher ht) (searcher ht) 0))
- (v (buckets ht))
- (n (vector-length v))
- (newvector (make-vector n '())))
- (count! newtable (count ht))
- (buckets! newtable newvector)
- (do ((i 0 (+ i 1)))
- ((= i n))
- (vector-set! newvector i (append (vector-ref v i) '())))
- newtable)
- (hashtable-error ht)))
-
- ; External entry points.
-
- (set! make-hashtable
- (lambda args
- (let* ((hashfun (if (null? args) object-hash (car args)))
- (searcher (if (or (null? args) (null? (cdr args)))
- assv
- (cadr args)))
- (size (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
- defaultn
- (caddr args))))
- (make-ht hashfun searcher size))))
-
- (set! hashtable-contains? (lambda (ht key) (contains? ht key)))
- (set! hashtable-fetch (lambda (ht key flag) (fetch ht key flag)))
- (set! hashtable-get (lambda (ht key) (fetch ht key #f)))
- (set! hashtable-put! (lambda (ht key val) (put! ht key val)))
- (set! hashtable-remove! (lambda (ht key) (remove! ht key)))
- (set! hashtable-clear! (lambda (ht) (clear! ht)))
- (set! hashtable-size (lambda (ht) (size ht)))
- (set! hashtable-for-each (lambda (ht proc) (ht-for-each ht proc)))
- (set! hashtable-map (lambda (ht proc) (ht-map ht proc)))
- (set! hashtable-copy (lambda (ht) (ht-copy ht)))
- #f))
- ; Hash trees: a functional data structure analogous to hash tables.
- ;
- ; (make-hashtree <hash-function> <bucket-searcher>)
- ;
- ; Returns a newly allocated mutable hash table
- ; using <hash-function> as the hash function
- ; and <bucket-searcher>, e.g. ASSQ, ASSV, ASSOC, to search a bucket.
- ; The <hash-function> must accept a key and return a non-negative exact
- ; integer.
- ;
- ; (make-hashtree <hash-function>)
- ;
- ; Equivalent to (make-hashtree <hash-function> assv).
- ;
- ; (make-hashtree)
- ;
- ; Equivalent to (make-hashtree object-hash assv).
- ;
- ; (hashtree-contains? <hashtree> <key>)
- ;
- ; Returns true iff the <hashtree> contains an entry for <key>.
- ;
- ; (hashtree-fetch <hashtree> <key> <flag>)
- ;
- ; Returns the value associated with <key> in the <hashtree> if the
- ; <hashtree> contains <key>; otherwise returns <flag>.
- ;
- ; (hashtree-get <hashtree> <key>)
- ;
- ; Equivalent to (hashtree-fetch <hashtree> <key> #f)
- ;
- ; (hashtree-put <hashtree> <key> <value>)
- ;
- ; Returns a new hashtree that is like <hashtree> except that
- ; <key> is associated with <value>.
- ;
- ; (hashtree-remove <hashtree> <key>)
- ;
- ; Returns a new hashtree that is like <hashtree> except that
- ; <key> is not associated with any value.
- ;
- ; (hashtree-size <hashtree>)
- ;
- ; Returns the number of keys contained within the <hashtree>.
- ;
- ; (hashtree-for-each <procedure> <hashtree>)
- ;
- ; The <procedure> must accept two arguments, a key and the value
- ; associated with that key. Calls the <procedure> once for each
- ; key-value association. The order of these calls is indeterminate.
- ;
- ; (hashtree-map <procedure> <hashtree>)
- ;
- ; The <procedure> must accept two arguments, a key and the value
- ; associated with that key. Calls the <procedure> once for each
- ; key-value association, and returns a list of the results. The
- ; order of the calls is indeterminate.
- ; These global variables are assigned new values later.
- (define make-hashtree (lambda args '*))
- (define hashtree-contains? (lambda (ht key) #f))
- (define hashtree-fetch (lambda (ht key flag) flag))
- (define hashtree-get (lambda (ht key) (hashtree-fetch ht key #f)))
- (define hashtree-put (lambda (ht key val) '*))
- (define hashtree-remove (lambda (ht key) '*))
- (define hashtree-size (lambda (ht) 0))
- (define hashtree-for-each (lambda (ht proc) '*))
- (define hashtree-map (lambda (ht proc) '()))
- ; Implementation.
- ; A hashtree is represented as a vector of the form
- ;
- ; #(("hashtree") <count> <hasher> <searcher> <buckets>)
- ;
- ; where <count> is the number of associations within the hashtree,
- ; <hasher> is the hash function, <searcher> is the bucket searcher,
- ; and <buckets> is generated by the following grammar:
- ;
- ; <buckets> ::= ()
- ; | (<fixnum> <associations> <buckets> <buckets>)
- ; <alist> ::= (<associations>)
- ; <associations> ::=
- ; | <association> <associations>
- ; <association> ::= (<key> . <value>)
- ;
- ; If <buckets> is of the form (n alist buckets1 buckets2),
- ; then n is the hash code of all keys in alist, all keys in buckets1
- ; have a hash code less than n, and all keys in buckets2 have a hash
- ; code greater than n.
- (let ((doc (list "hashtree"))
- (count (lambda (ht) (vector-ref ht 1)))
- (hasher (lambda (ht) (vector-ref ht 2)))
- (searcher (lambda (ht) (vector-ref ht 3)))
- (buckets (lambda (ht) (vector-ref ht 4)))
-
- (make-empty-buckets (lambda () '()))
-
- (make-buckets
- (lambda (h alist buckets1 buckets2)
- (list h alist buckets1 buckets2)))
-
- (buckets-empty? (lambda (buckets) (null? buckets)))
-
- (buckets-n (lambda (buckets) (car buckets)))
- (buckets-alist (lambda (buckets) (cadr buckets)))
- (buckets-left (lambda (buckets) (caddr buckets)))
- (buckets-right (lambda (buckets) (cadddr buckets))))
-
- (let ((hashtree? (lambda (ht)
- (and (vector? ht)
- (= 5 (vector-length ht))
- (eq? doc (vector-ref ht 0)))))
- (hashtree-error (lambda (x)
- (display "ERROR: Bad hash tree: ")
- (newline)
- (write x)
- (newline))))
-
- ; Internal operations.
-
- (define (make-ht count hashfun searcher buckets)
- (vector doc count hashfun searcher buckets))
-
- ; Substitute x for the first occurrence of y within the list z.
- ; y is known to occur within z.
-
- (define (substitute1 x y z)
- (cond ((eq? y (car z))
- (cons x (cdr z)))
- (else
- (cons (car z)
- (substitute1 x y (cdr z))))))
-
- ; Remove the first occurrence of x from y.
- ; x is known to occur within y.
-
- (define (remq1 x y)
- (cond ((eq? x (car y))
- (cdr y))
- (else
- (cons (car y)
- (remq1 x (cdr y))))))
-
- ; Returns the contents of the hashtree as a list of pairs.
-
- (define (contents ht)
- (let* ((t (buckets ht)))
-
- (define (contents t alist)
- (if (buckets-empty? t)
- alist
- (contents (buckets-left t)
- (contents (buckets-right t)
- (append-reverse (buckets-alist t)
- alist)))))
-
- (define (append-reverse x y)
- (if (null? x)
- y
- (append-reverse (cdr x)
- (cons (car x) y))))
-
- ; Creating a new hashtree from a list that is almost sorted
- ; in hash code order would create an extremely unbalanced
- ; hashtree, so this routine randomizes the order a bit.
-
- (define (randomize1 alist alist1 alist2 alist3)
- (if (null? alist)
- (randomize-combine alist1 alist2 alist3)
- (randomize2 (cdr alist)
- (cons (car alist) alist1)
- alist2
- alist3)))
-
- (define (randomize2 alist alist1 alist2 alist3)
- (if (null? alist)
- (randomize-combine alist1 alist2 alist3)
- (randomize3 (cdr alist)
- alist1
- (cons (car alist) alist2)
- alist3)))
-
- (define (randomize3 alist alist1 alist2 alist3)
- (if (null? alist)
- (randomize-combine alist1 alist2 alist3)
- (randomize1 (cdr alist)
- alist1
- alist2
- (cons (car alist) alist3))))
-
- (define (randomize-combine alist1 alist2 alist3)
- (cond ((null? alist2)
- alist1)
- ((null? alist3)
- (append-reverse alist2 alist1))
- (else
- (append-reverse
- (randomize1 alist3 '() '() '())
- (append-reverse
- (randomize1 alist1 '() '() '())
- (randomize1 alist2 '() '() '()))))))
-
- (randomize1 (contents t '()) '() '() '())))
-
- (define (contains? ht key)
- (if (hashtree? ht)
- (let* ((t (buckets ht))
- (h ((hasher ht) key)))
- (if ((searcher ht) key (find-bucket t h))
- #t
- #f))
- (hashtree-error ht)))
-
- (define (fetch ht key flag)
- (if (hashtree? ht)
- (let* ((t (buckets ht))
- (h ((hasher ht) key))
- (probe ((searcher ht) key (find-bucket t h))))
- (if probe
- (cdr probe)
- flag))
- (hashtree-error ht)))
-
- ; Given a <buckets> t and a hash code h, returns the alist for h.
-
- (define (find-bucket t h)
- (if (buckets-empty? t)
- '()
- (let ((n (buckets-n t)))
- (cond ((< h n)
- (find-bucket (buckets-left t) h))
- ((< n h)
- (find-bucket (buckets-right t) h))
- (else
- (buckets-alist t))))))
-
- (define (put ht key val)
- (if (hashtree? ht)
- (let ((t (buckets ht))
- (h ((hasher ht) key))
- (association (cons key val))
- (c (count ht)))
- (define (put t h)
- (if (buckets-empty? t)
- (begin (set! c (+ c 1))
- (make-buckets h (list association) t t))
- (let ((n (buckets-n t))
- (alist (buckets-alist t))
- (left (buckets-left t))
- (right (buckets-right t)))
- (cond ((< h n)
- (make-buckets n
- alist
- (put (buckets-left t) h)
- right))
- ((< n h)
- (make-buckets n
- alist
- left
- (put (buckets-right t) h)))
- (else
- (let ((probe ((searcher ht) key alist)))
- (if probe
- (make-buckets n
- (substitute1 association
- probe
- alist)
- left
- right)
- (begin
- (set! c (+ c 1))
- (make-buckets n
- (cons association alist)
- left
- right)))))))))
- (let ((buckets (put t h)))
- (make-ht c (hasher ht) (searcher ht) buckets)))
- (hashtree-error ht)))
-
- (define (remove ht key)
- (if (hashtree? ht)
- (let ((t (buckets ht))
- (h ((hasher ht) key))
- (c (count ht)))
- (define (remove t h)
- (if (buckets-empty? t)
- t
- (let ((n (buckets-n t))
- (alist (buckets-alist t))
- (left (buckets-left t))
- (right (buckets-right t)))
- (cond ((< h n)
- (make-buckets n
- alist
- (remove left h)
- right))
- ((< n h)
- (make-buckets n
- alist
- left
- (remove right h)))
- (else
- (let ((probe ((searcher ht) key alist)))
- (if probe
- (begin (set! c (- c 1))
- (make-buckets n
- (remq1 probe alist)
- left
- right))
- t)))))))
- (let ((buckets (remove t h)))
- (make-ht c (hasher ht) (searcher ht) buckets)))
- (hashtree-error ht)))
-
- (define (size ht)
- (if (hashtree? ht)
- (count ht)
- (hashtree-error ht)))
-
- (define (ht-for-each f ht)
- (if (hashtree? ht)
- (for-each (lambda (association)
- (f (car association)
- (cdr association)))
- (contents ht))
- (hashtree-error ht)))
-
- (define (ht-map f ht)
- (if (hashtree? ht)
- (map (lambda (association)
- (f (car association)
- (cdr association)))
- (contents ht))
- (hashtree-error ht)))
-
- ; External entry points.
-
- (set! make-hashtree
- (lambda args
- (let* ((hashfun (if (null? args) object-hash (car args)))
- (searcher (if (or (null? args) (null? (cdr args)))
- assv
- (cadr args))))
- (make-ht 0 hashfun searcher (make-empty-buckets)))))
-
- (set! hashtree-contains? (lambda (ht key) (contains? ht key)))
- (set! hashtree-fetch (lambda (ht key flag) (fetch ht key flag)))
- (set! hashtree-get (lambda (ht key) (fetch ht key #f)))
- (set! hashtree-put (lambda (ht key val) (put ht key val)))
- (set! hashtree-remove (lambda (ht key) (remove ht key)))
- (set! hashtree-size (lambda (ht) (size ht)))
- (set! hashtree-for-each (lambda (ht proc) (ht-for-each ht proc)))
- (set! hashtree-map (lambda (ht proc) (ht-map ht proc)))
- #f))
- ; Copyright 1994 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 24 April 1999
- ;
- ; Compiler switches needed by Twobit.
- (define make-twobit-flag)
- (define display-twobit-flag)
- (define make-twobit-flag
- (lambda (name)
- (define (twobit-warning)
- (display "Error: incorrect arguments to ")
- (write name)
- (newline)
- (reset))
- (define (display-flag state)
- (display (if state " + " " - "))
- (display name)
- (display " is ")
- (display (if state "on" "off"))
- (newline))
- (let ((state #t))
- (lambda args
- (cond ((null? args) state)
- ((and (null? (cdr args))
- (boolean? (car args)))
- (set! state (car args))
- state)
- ((and (null? (cdr args))
- (eq? (car args) 'display))
- (display-flag state))
- (else (twobit-warning)))))))
- (define (display-twobit-flag flag)
- (flag 'display))
-
- ; Debugging and convenience.
- (define issue-warnings
- (make-twobit-flag 'issue-warnings))
- (define include-source-code
- (make-twobit-flag 'include-source-code))
- (define include-variable-names
- (make-twobit-flag 'include-variable-names))
- (define include-procedure-names
- (make-twobit-flag 'include-procedure-names))
- ; Space efficiency.
- ; This switch isn't fully implemented yet. If it is true, then
- ; Twobit will generate flat closures and will go to some trouble
- ; to zero stale registers and stack slots.
- ; Don't turn this switch off unless space is more important than speed.
- (define avoid-space-leaks
- (make-twobit-flag 'avoid-space-leaks))
- ; Major optimizations.
- (define integrate-usual-procedures
- (make-twobit-flag 'integrate-usual-procedures))
- (define control-optimization
- (make-twobit-flag 'control-optimization))
- (define parallel-assignment-optimization
- (make-twobit-flag 'parallel-assignment-optimization))
- (define lambda-optimization
- (make-twobit-flag 'lambda-optimization))
- (define benchmark-mode
- (make-twobit-flag 'benchmark-mode))
- (define benchmark-block-mode
- (make-twobit-flag 'benchmark-block-mode))
- (define global-optimization
- (make-twobit-flag 'global-optimization))
- (define interprocedural-inlining
- (make-twobit-flag 'interprocedural-inlining))
- (define interprocedural-constant-propagation
- (make-twobit-flag 'interprocedural-constant-propagation))
- (define common-subexpression-elimination
- (make-twobit-flag 'common-subexpression-elimination))
- (define representation-inference
- (make-twobit-flag 'representation-inference))
- (define local-optimization
- (make-twobit-flag 'local-optimization))
- ; For backwards compatibility, until I can change the code.
- (define (ignore-space-leaks . args)
- (if (null? args)
- (not (avoid-space-leaks))
- (avoid-space-leaks (not (car args)))))
- (define lambda-optimizations lambda-optimization)
- (define local-optimizations local-optimization)
- (define (set-compiler-flags! how)
- (case how
- ((no-optimization)
- (set-compiler-flags! 'standard)
- (avoid-space-leaks #t)
- (integrate-usual-procedures #f)
- (control-optimization #f)
- (parallel-assignment-optimization #f)
- (lambda-optimization #f)
- (benchmark-mode #f)
- (benchmark-block-mode #f)
- (global-optimization #f)
- (interprocedural-inlining #f)
- (interprocedural-constant-propagation #f)
- (common-subexpression-elimination #f)
- (representation-inference #f)
- (local-optimization #f))
- ((standard)
- (issue-warnings #t)
- (include-source-code #f)
- (include-procedure-names #t)
- (include-variable-names #t)
- (avoid-space-leaks #f)
- (runtime-safety-checking #t)
- (integrate-usual-procedures #f)
- (control-optimization #t)
- (parallel-assignment-optimization #t)
- (lambda-optimization #t)
- (benchmark-mode #f)
- (benchmark-block-mode #f)
- (global-optimization #t)
- (interprocedural-inlining #t)
- (interprocedural-constant-propagation #t)
- (common-subexpression-elimination #t)
- (representation-inference #t)
- (local-optimization #t))
- ((fast-safe)
- (let ((bbmode (benchmark-block-mode)))
- (set-compiler-flags! 'standard)
- (integrate-usual-procedures #t)
- (benchmark-mode #t)
- (benchmark-block-mode bbmode)))
- ((fast-unsafe)
- (set-compiler-flags! 'fast-safe)
- (runtime-safety-checking #f))
- (else
- (error "set-compiler-flags!: unknown mode " how))))
- (define (display-twobit-flags which)
- (case which
- ((debugging)
- (display-twobit-flag issue-warnings)
- (display-twobit-flag include-procedure-names)
- (display-twobit-flag include-variable-names)
- (display-twobit-flag include-source-code))
- ((safety)
- (display-twobit-flag avoid-space-leaks))
- ((optimization)
- (display-twobit-flag integrate-usual-procedures)
- (display-twobit-flag control-optimization)
- (display-twobit-flag parallel-assignment-optimization)
- (display-twobit-flag lambda-optimization)
- (display-twobit-flag benchmark-mode)
- (display-twobit-flag benchmark-block-mode)
- (display-twobit-flag global-optimization)
- (if (global-optimization)
- (begin (display " ")
- (display-twobit-flag interprocedural-inlining)
- (display " ")
- (display-twobit-flag interprocedural-constant-propagation)
- (display " ")
- (display-twobit-flag common-subexpression-elimination)
- (display " ")
- (display-twobit-flag representation-inference)))
- (display-twobit-flag local-optimization))
- (else
- ; The switch might mean something to the assembler, but not to Twobit
- #t)))
- ; eof
- ; Copyright 1991 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 14 April 1999 / wdc
- ($$trace "pass1.aux")
- ;***************************************************************
- ;
- ; Each definition in this section should be overridden by an assignment
- ; in a target-specific file.
- ;
- ; If a lambda expression has more than @maxargs-with-rest-arg@ required
- ; arguments followed by a rest argument, then the macro expander will
- ; rewrite the lambda expression as a lambda expression with only one
- ; argument (a rest argument) whose body is a LET that binds the arguments
- ; of the original lambda expression.
- (define @maxargs-with-rest-arg@
- 1000000) ; infinity
- (define (prim-entry name) #f) ; no integrable procedures
- (define (prim-arity name) 0) ; all of which take 0 arguments
- (define (prim-opcodename name) name) ; and go by their source names
- ; End of definitions to be overridden by target-specific assignments.
- ;
- ;***************************************************************
- ; Miscellaneous routines.
- (define (m-warn msg . more)
- (if (issue-warnings)
- (begin
- (display "WARNING from macro expander:")
- (newline)
- (display msg)
- (newline)
- (for-each (lambda (x) (write x) (newline))
- more))))
- (define (m-error msg . more)
- (display "ERROR detected during macro expansion:")
- (newline)
- (display msg)
- (newline)
- (for-each (lambda (x) (write x) (newline))
- more)
- (m-quit (make-constant #f)))
- (define (m-bug msg . more)
- (display "BUG in macro expander: ")
- (newline)
- (display msg)
- (newline)
- (for-each (lambda (x) (write x) (newline))
- more)
- (m-quit (make-constant #f)))
- ; Given a <formals>, returns a list of bound variables.
- '
- (define (make-null-terminated x)
- (cond ((null? x) '())
- ((pair? x)
- (cons (car x) (make-null-terminated (cdr x))))
- (else (list x))))
- ; Returns the length of the given list, or -1 if the argument
- ; is not a list. Does not check for circular lists.
- (define (safe-length x)
- (define (loop x n)
- (cond ((null? x) n)
- ((pair? x) (loop (cdr x) (+ n 1)))
- (else -1)))
- (loop x 0))
- ; Given a unary predicate and a list, returns a list of those
- ; elements for which the predicate is true.
- (define (filter1 p x)
- (cond ((null? x) '())
- ((p (car x)) (cons (car x) (filter1 p (cdr x))))
- (else (filter1 p (cdr x)))))
- ; Given a unary predicate and a list, returns #t if the
- ; predicate is true of every element of the list.
- (define (every1? p x)
- (cond ((null? x) #t)
- ((p (car x)) (every1? p (cdr x)))
- (else #f)))
- ; Binary union of two sets represented as lists, using equal?.
- (define (union2 x y)
- (cond ((null? x) y)
- ((member (car x) y)
- (union2 (cdr x) y))
- (else (union2 (cdr x) (cons (car x) y)))))
- ; Given an association list, copies the association pairs.
- (define (copy-alist alist)
- (map (lambda (x) (cons (car x) (cdr x)))
- alist))
- ; Removes a value from a list. May destroy the list.
- '
- (define remq!
- (letrec ((loop (lambda (x y prev)
- (cond ((null? y) #t)
- ((eq? x (car y))
- (set-cdr! prev (cdr y))
- (loop x (cdr prev) prev))
- (else
- (loop x (cdr y) y))))))
- (lambda (x y)
- (cond ((null? y) '())
- ((eq? x (car y))
- (remq! x (cdr y)))
- (else
- (loop x (cdr y) y)
- y)))))
- ; Procedure-specific source code transformations.
- ; The transformer is passed a source code expression and a predicate
- ; and returns one of:
- ;
- ; the original source code expression
- ; a new source code expression to use in place of the original
- ; #f to indicate that the procedure is being called
- ; with an incorrect number of arguments or
- ; with an incorrect operand
- ;
- ; The original source code expression is guaranteed to be a list whose
- ; car is the name associated with the transformer.
- ; The predicate takes an identifier (a symbol) and returns true iff
- ; that identifier is bound to something other than its global binding.
- ;
- ; Since the procedures and their transformations are target-specific,
- ; they are defined in another file, in the Target subdirectory.
- ; FIXME:
- ; I think this is now used in only one place, in simplify-if.
- (define (integrable? name)
- (and (integrate-usual-procedures)
- (prim-entry name)))
- ; MAKE-READABLE strips the referencing information
- ; and replaces (begin I) by I.
- ; If the optional argument is true, then it also reconstructs LET.
- (define (make-readable exp . rest)
- (let ((fancy? (and (not (null? rest))
- (car rest))))
- (define (make-readable exp)
- (case (car exp)
- ((quote) (make-readable-quote exp))
- ((lambda) `(lambda ,(lambda.args exp)
- ,@(map (lambda (def)
- `(define ,(def.lhs def)
- ,(make-readable (def.rhs def))))
- (lambda.defs exp))
- ,(make-readable (lambda.body exp))))
- ((set!) `(set! ,(assignment.lhs exp)
- ,(make-readable (assignment.rhs exp))))
- ((if) `(if ,(make-readable (if.test exp))
- ,(make-readable (if.then exp))
- ,(make-readable (if.else exp))))
- ((begin) (if (variable? exp)
- (variable.name exp)
- `(begin ,@(map make-readable (begin.exprs exp)))))
- (else (make-readable-call exp))))
- (define (make-readable-quote exp)
- (let ((x (constant.value exp)))
- (if (and fancy?
- (or (boolean? x)
- (number? x)
- (char? x)
- (string? x)))
- x
- exp)))
- (define (make-readable-call exp)
- (let ((proc (call.proc exp)))
- (if (and fancy?
- (lambda? proc)
- (list? (lambda.args proc)))
- ;(make-readable-let* exp '() '() '())
- (make-readable-let exp)
- `(,(make-readable (call.proc exp))
- ,@(map make-readable (call.args exp))))))
- (define (make-readable-let exp)
- (let* ((L (call.proc exp))
- (formals (lambda.args L))
- (args (map make-readable (call.args exp)))
- (body (make-readable (lambda.body L))))
- (if (and (null? (lambda.defs L))
- (= (length args) 1)
- (pair? body)
- (or (and (eq? (car body) 'let)
- (= (length (cadr body)) 1))
- (eq? (car body) 'let*)))
- `(let* ((,(car formals) ,(car args))
- ,@(cadr body))
- ,@(cddr body))
- `(let ,(map list
- (lambda.args L)
- args)
- ,@(map (lambda (def)
- `(define ,(def.lhs def)
- ,(make-readable (def.rhs def))))
- (lambda.defs L))
- ,body))))
- (define (make-readable-let* exp vars inits defs)
- (if (and (null? defs)
- (call? exp)
- (lambda? (call.proc exp))
- (= 1 (length (lambda.args (call.proc exp)))))
- (let ((proc (call.proc exp))
- (arg (car (call.args exp))))
- (if (and (call? arg)
- (lambda? (call.proc arg))
- (= 1 (length (lambda.args (call.proc arg))))
- (null? (lambda.defs (call.proc arg))))
- (make-readable-let*
- (make-call proc (list (lambda.body (call.proc arg))))
- (cons (car (lambda.args (call.proc arg))) vars)
- (cons (make-readable (car (call.args arg))) inits)
- '())
- (make-readable-let* (lambda.body proc)
- (cons (car (lambda.args proc)) vars)
- (cons (make-readable (car (call.args exp)))
- inits)
- (map (lambda (def)
- `(define ,(def.lhs def)
- ,(make-readable (def.rhs def))))
- (reverse (lambda.defs proc))))))
- (cond ((or (not (null? vars))
- (not (null? defs)))
- `(let* ,(map list
- (reverse vars)
- (reverse inits))
- ,@defs
- ,(make-readable exp)))
- ((and (call? exp)
- (lambda? (call.proc exp)))
- (let ((proc (call.proc exp)))
- `(let ,(map list
- (lambda.args proc)
- (map make-readable (call.args exp)))
- ,@(map (lambda (def)
- `(define ,(def.lhs def)
- ,(make-readable (def.rhs def))))
- (lambda.defs proc))
- ,(make-readable (lambda.body proc)))))
- (else
- (make-readable exp)))))
- (make-readable exp)))
- ; For testing.
- ; MAKE-UNREADABLE does the reverse.
- ; It assumes there are no internal definitions.
- (define (make-unreadable exp)
- (cond ((symbol? exp) (list 'begin exp))
- ((pair? exp)
- (case (car exp)
- ((quote) exp)
- ((lambda) (list 'lambda
- (cadr exp)
- '(begin)
- (list '() '() '() '())
- (make-unreadable (cons 'begin (cddr exp)))))
- ((set!) (list 'set! (cadr exp) (make-unreadable (caddr exp))))
- ((if) (list 'if
- (make-unreadable (cadr exp))
- (make-unreadable (caddr exp))
- (if (= (length exp) 3)
- '(unspecified)
- (make-unreadable (cadddr exp)))))
- ((begin) (if (= (length exp) 2)
- (make-unreadable (cadr exp))
- (cons 'begin (map make-unreadable (cdr exp)))))
- (else (map make-unreadable exp))))
- (else (list 'quote exp))))
- ; Copyright 1991 William D Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 12 April 1999.
- ;
- ; Procedures for fetching and clobbering parts of expressions.
- ($$trace "pass2.aux")
- (define (constant? exp) (eq? (car exp) 'quote))
- (define (variable? exp)
- (and (eq? (car exp) 'begin)
- (null? (cddr exp))))
- (define (lambda? exp) (eq? (car exp) 'lambda))
- (define (call? exp) (pair? (car exp)))
- (define (assignment? exp) (eq? (car exp) 'set!))
- (define (conditional? exp) (eq? (car exp) 'if))
- (define (begin? exp)
- (and (eq? (car exp) 'begin)
- (not (null? (cddr exp)))))
- (define (make-constant value) (list 'quote value))
- (define (make-variable name) (list 'begin name))
- (define (make-lambda formals defs R F G decls doc body)
- (list 'lambda
- formals
- (cons 'begin defs)
- (list 'quote (list R F G decls doc))
- body))
- (define (make-call proc args) (cons proc (append args '())))
- (define (make-assignment lhs rhs) (list 'set! lhs rhs))
- (define (make-conditional e0 e1 e2) (list 'if e0 e1 e2))
- (define (make-begin exprs)
- (if (null? (cdr exprs))
- (car exprs)
- (cons 'begin (append exprs '()))))
- (define (make-definition lhs rhs) (list 'define lhs rhs))
- (define (constant.value exp) (cadr exp))
- (define (variable.name exp) (cadr exp))
- (define (lambda.args exp) (cadr exp))
- (define (lambda.defs exp) (cdr (caddr exp)))
- (define (lambda.R exp) (car (cadr (cadddr exp))))
- (define (lambda.F exp) (cadr (cadr (cadddr exp))))
- (define (lambda.G exp) (caddr (cadr (cadddr exp))))
- (define (lambda.decls exp) (cadddr (cadr (cadddr exp))))
- (define (lambda.doc exp) (car (cddddr (cadr (cadddr exp)))))
- (define (lambda.body exp) (car (cddddr exp)))
- (define (call.proc exp) (car exp))
- (define (call.args exp) (cdr exp))
- (define (assignment.lhs exp) (cadr exp))
- (define (assignment.rhs exp) (caddr exp))
- (define (if.test exp) (cadr exp))
- (define (if.then exp) (caddr exp))
- (define (if.else exp) (cadddr exp))
- (define (begin.exprs exp) (cdr exp))
- (define (def.lhs exp) (cadr exp))
- (define (def.rhs exp) (caddr exp))
- (define (variable-set! exp newexp)
- (set-car! exp (car newexp))
- (set-cdr! exp (append (cdr newexp) '())))
- (define (lambda.args-set! exp args) (set-car! (cdr exp) args))
- (define (lambda.defs-set! exp defs) (set-cdr! (caddr exp) defs))
- (define (lambda.R-set! exp R) (set-car! (cadr (cadddr exp)) R))
- (define (lambda.F-set! exp F) (set-car! (cdr (cadr (cadddr exp))) F))
- (define (lambda.G-set! exp G) (set-car! (cddr (cadr (cadddr exp))) G))
- (define (lambda.decls-set! exp decls) (set-car! (cdddr (cadr (cadddr exp))) decls))
- (define (lambda.doc-set! exp doc) (set-car! (cddddr (cadr (cadddr exp))) doc))
- (define (lambda.body-set! exp exp0) (set-car! (cddddr exp) exp0))
- (define (call.proc-set! exp exp0) (set-car! exp exp0))
- (define (call.args-set! exp exprs) (set-cdr! exp exprs))
- (define (assignment.rhs-set! exp exp0) (set-car! (cddr exp) exp0))
- (define (if.test-set! exp exp0) (set-car! (cdr exp) exp0))
- (define (if.then-set! exp exp0) (set-car! (cddr exp) exp0))
- (define (if.else-set! exp exp0) (set-car! (cdddr exp) exp0))
- (define (begin.exprs-set! exp exprs) (set-cdr! exp exprs))
- (define expression-set! variable-set!) ; used only by pass 3
- ; FIXME: This duplicates information in Lib/procinfo.sch.
- (define (make-doc name arity formals source-code filename filepos)
- (vector name source-code arity filename filepos formals))
- (define (doc.name d) (vector-ref d 0))
- (define (doc.code d) (vector-ref d 1))
- (define (doc.arity d) (vector-ref d 2))
- (define (doc.file d) (vector-ref d 3))
- (define (doc.filepos d) (vector-ref d 4))
- (define (doc.formals d) (vector-ref d 5))
- (define (doc.name-set! d x) (if d (vector-set! d 0 x)))
- (define (doc.code-set! d x) (if d (vector-set! d 1 x)))
- (define (doc.arity-set! d x) (if d (vector-set! d 2 x)))
- (define (doc.file-set! d x) (if d (vector-set! d 3 x)))
- (define (doc.filepos-set! d x) (if d (vector-set! d 4 x)))
- (define (doc.formals-set! d x) (if d (vector-set! d 5 x)))
- (define (doc-copy d) (list->vector (vector->list d)))
- (define (ignored? name) (eq? name name:IGNORED))
- ; Fairly harmless bug: rest arguments aren't getting flagged.
- (define (flag-as-ignored name L)
- (define (loop name formals)
- (cond ((null? formals)
- ;(pass2-error p2error:violation-of-invariant name formals)
- #t)
- ((symbol? formals) #t)
- ((eq? name (car formals))
- (set-car! formals name:IGNORED)
- (if (not (local? (lambda.R L) name:IGNORED))
- (lambda.R-set! L
- (cons (make-R-entry name:IGNORED '() '() '())
- (lambda.R L)))))
- (else (loop name (cdr formals)))))
- (loop name (lambda.args L)))
- (define (make-null-terminated formals)
- (cond ((null? formals) '())
- ((symbol? formals) (list formals))
- (else (cons (car formals)
- (make-null-terminated (cdr formals))))))
- (define (list-head x n)
- (cond ((zero? n) '())
- (else (cons (car x) (list-head (cdr x) (- n 1))))))
- (define (remq x y)
- (cond ((null? y) '())
- ((eq? x (car y)) (remq x (cdr y)))
- (else (cons (car y) (remq x (cdr y))))))
- (define (make-call-to-LIST args)
- (cond ((null? args) (make-constant '()))
- ((null? (cdr args))
- (make-call (make-variable name:CONS)
- (list (car args) (make-constant '()))))
- (else (make-call (make-variable name:LIST) args))))
- (define (pass2-error i . etc)
- (apply cerror (cons (vector-ref pass2-error-messages i) etc)))
- (define pass2-error-messages
- '#("System error: violation of an invariant in pass 2"
- "Wrong number of arguments to known procedure"))
- (define p2error:violation-of-invariant 0)
- (define p2error:wna 1)
- ; Procedures for fetching referencing information from R-tables.
- (define (make-R-entry name refs assigns calls)
- (list name refs assigns calls))
- (define (R-entry.name x) (car x))
- (define (R-entry.references x) (cadr x))
- (define (R-entry.assignments x) (caddr x))
- (define (R-entry.calls x) (cadddr x))
- (define (R-entry.references-set! x refs) (set-car! (cdr x) refs))
- (define (R-entry.assignments-set! x assignments) (set-car! (cddr x) assignments))
- (define (R-entry.calls-set! x calls) (set-car! (cdddr x) calls))
- (define (local? R I)
- (assq I R))
- (define (R-entry R I)
- (assq I R))
- (define (R-lookup R I)
- (or (assq I R)
- (pass2-error p2error:violation-of-invariant R I)))
- (define (references R I)
- (cadr (R-lookup R I)))
- (define (assignments R I)
- (caddr (R-lookup R I)))
- (define (calls R I)
- (cadddr (R-lookup R I)))
- (define (references-set! R I X)
- (set-car! (cdr (R-lookup R I)) X))
- (define (assignments-set! R I X)
- (set-car! (cddr (R-lookup R I)) X))
- (define (calls-set! R I X)
- (set-car! (cdddr (R-lookup R I)) X))
- ; A notepad is a vector of the form #(L0 (L1 ...) (L2 ...) (I ...)),
- ; where the components are:
- ; element 0: a parent lambda expression (or #f if there is no enclosing
- ; parent, or we want to pretend that there isn't).
- ; element 1: a list of lambda expressions that the parent lambda
- ; expression encloses immediately.
- ; element 2: a subset of that list that does not escape.
- ; element 3: a list of free variables.
- (define (make-notepad L)
- (vector L '() '() '()))
- (define (notepad.parent np) (vector-ref np 0))
- (define (notepad.lambdas np) (vector-ref np 1))
- (define (notepad.nonescaping np) (vector-ref np 2))
- (define (notepad.vars np) (vector-ref np 3))
- (define (notepad.lambdas-set! np x) (vector-set! np 1 x))
- (define (notepad.nonescaping-set! np x) (vector-set! np 2 x))
- (define (notepad.vars-set! np x) (vector-set! np 3 x))
- (define (notepad-lambda-add! np L)
- (notepad.lambdas-set! np (cons L (notepad.lambdas np))))
- (define (notepad-nonescaping-add! np L)
- (notepad.nonescaping-set! np (cons L (notepad.nonescaping np))))
- (define (notepad-var-add! np I)
- (let ((vars (notepad.vars np)))
- (if (not (memq I vars))
- (notepad.vars-set! np (cons I vars)))))
- ; Given a notepad, returns the list of variables that are closed
- ; over by some nested lambda expression that escapes.
- (define (notepad-captured-variables np)
- (let ((nonescaping (notepad.nonescaping np)))
- (apply-union
- (map (lambda (L)
- (if (memq L nonescaping)
- (lambda.G L)
- (lambda.F L)))
- (notepad.lambdas np)))))
- ; Given a notepad, returns a list of free variables computed
- ; as the union of the immediate free variables with the free
- ; variables of nested lambda expressions.
- (define (notepad-free-variables np)
- (do ((lambdas (notepad.lambdas np) (cdr lambdas))
- (fv (notepad.vars np)
- (let ((L (car lambdas)))
- (union (difference (lambda.F L)
- (make-null-terminated (lambda.args L)))
- fv))))
- ((null? lambdas) fv)))
- ; Copyright 1992 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 13 December 1998
- ; Implementation-dependent parameters and preferences that determine
- ; how identifiers are represented in the output of the macro expander.
- ;
- ; The basic problem is that there are no reserved words, so the
- ; syntactic keywords of core Scheme that are used to express the
- ; output need to be represented by data that cannot appear in the
- ; input. This file defines those data.
- ($$trace "prefs")
- ; FIXME: The following definitions are currently ignored.
- ; The following definitions assume that identifiers of mixed case
- ; cannot appear in the input.
- (define begin1 (string->symbol "Begin"))
- (define define1 (string->symbol "Define"))
- (define quote1 (string->symbol "Quote"))
- (define lambda1 (string->symbol "Lambda"))
- (define if1 (string->symbol "If"))
- (define set!1 (string->symbol "Set!"))
- ; The following defines an implementation-dependent expression
- ; that evaluates to an undefined (not unspecified!) value, for
- ; use in expanding the (define x) syntax.
- (define undefined1 (list (string->symbol "Undefined")))
- ; End of FIXME.
- ; A variable is renamed by suffixing a vertical bar followed by a unique
- ; integer. In IEEE and R4RS Scheme, a vertical bar cannot appear as part
- ; of an identifier, but presumably this is enforced by the reader and not
- ; by the compiler. Any other character that cannot appear as part of an
- ; identifier may be used instead of the vertical bar.
- (define renaming-prefix-character #\.)
- (define renaming-suffix-character #\|)
- (define renaming-prefix (string renaming-prefix-character))
- (define renaming-suffix (string renaming-suffix-character))
- ; Patches for Twobit. Here temporarily.
- (define (make-toplevel-definition id exp)
- (if (lambda? exp)
- (doc.name-set! (lambda.doc exp) id))
- (make-begin
- (list (make-assignment id exp)
- (make-constant id))))
-
- (define (make-undefined)
- (make-call (make-variable 'undefined) '()))
- (define (make-unspecified)
- (make-call (make-variable 'unspecified) '()))
- ; Copyright 1992 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 9 December 1998
- ; Syntactic environments.
- ;
- ; A syntactic environment maps identifiers to denotations,
- ; where a denotation is one of
- ;
- ; (special <special>)
- ; (macro <rules> <env>)
- ; (inline <rules> <env>)
- ; (identifier <id> <references> <assignments> <calls>)
- ;
- ; and where <special> is one of
- ;
- ; quote
- ; lambda
- ; if
- ; set!
- ; begin
- ; define
- ; define-syntax
- ; let-syntax
- ; letrec-syntax
- ; syntax-rules
- ;
- ; and where <rules> is a compiled <transformer spec> (see R4RS),
- ; <env> is a syntactic environment, and <id> is an identifier.
- ;
- ; An inline denotation is like a macro denotation, except that it
- ; is not an error when none of the rules match the use. Inline
- ; denotations are created by DEFINE-INLINE.
- ; The standard syntactic environment should not include any
- ; identifier denotations; space leaks will result if it does.
- ($$trace "syntaxenv")
- (define standard-syntactic-environment
- `((quote . (special quote))
- (lambda . (special lambda))
- (if . (special if))
- (set! . (special set!))
- (begin . (special begin))
- (define . (special define))
- (define-inline . (special define-inline))
- (define-syntax . (special define-syntax))
- (let-syntax . (special let-syntax))
- (letrec-syntax . (special letrec-syntax))
- (syntax-rules . (special syntax-rules))
- ))
- ; Unforgeable synonyms for lambda and set!, used to expand definitions.
- (define lambda0 (string->symbol " lambda "))
- (define set!0 (string->symbol " set! "))
- (define (syntactic-copy env)
- (copy-alist env))
- (define (make-basic-syntactic-environment)
- (cons (cons lambda0
- (cdr (assq 'lambda standard-syntactic-environment)))
- (cons (cons set!0
- (cdr (assq 'set! standard-syntactic-environment)))
- (syntactic-copy standard-syntactic-environment))))
- ; The global-syntactic-environment will always be a nonempty
- ; association list since there is no way to remove the entry
- ; for lambda0. That entry is used as a header by destructive
- ; operations.
- (define global-syntactic-environment
- (make-basic-syntactic-environment))
- (define (global-syntactic-environment-set! env)
- (set-cdr! global-syntactic-environment env)
- #t)
- (define (syntactic-bind-globally! id denotation)
- (if (and (identifier-denotation? denotation)
- (eq? id (identifier-name denotation)))
- (letrec ((remove-bindings-for-id
- (lambda (bindings)
- (cond ((null? bindings) '())
- ((eq? (caar bindings) id)
- (remove-bindings-for-id (cdr bindings)))
- (else (cons (car bindings)
- (remove-bindings-for-id (cdr bindings))))))))
- (global-syntactic-environment-set!
- (remove-bindings-for-id (cdr global-syntactic-environment))))
- (let ((x (assq id global-syntactic-environment)))
- (if x
- (begin (set-cdr! x denotation) #t)
- (global-syntactic-environment-set!
- (cons (cons id denotation)
- (cdr global-syntactic-environment)))))))
- (define (syntactic-divert env1 env2)
- (append env2 env1))
- (define (syntactic-extend env ids denotations)
- (syntactic-divert env (map cons ids denotations)))
- (define (syntactic-lookup env id)
- (let ((entry (assq id env)))
- (if entry
- (cdr entry)
- (make-identifier-denotation id))))
- (define (syntactic-assign! env id denotation)
- (let ((entry (assq id env)))
- (if entry
- (set-cdr! entry denotation)
- (m-bug "Bug detected in syntactic-assign!" env id denotation))))
- ; Denotations.
- (define denotation-class car)
- (define (special-denotation? denotation)
- (eq? (denotation-class denotation) 'special))
- (define (macro-denotation? denotation)
- (eq? (denotation-class denotation) 'macro))
- (define (inline-denotation? denotation)
- (eq? (denotation-class denotation) 'inline))
- (define (identifier-denotation? denotation)
- (eq? (denotation-class denotation) 'identifier))
- (define (make-macro-denotation rules env)
- (list 'macro rules env))
- (define (make-inline-denotation id rules env)
- (list 'inline rules env id))
- (define (make-identifier-denotation id)
- (list 'identifier id '() '() '()))
- (define macro-rules cadr)
- (define macro-env caddr)
- (define inline-rules macro-rules)
- (define inline-env macro-env)
- (define inline-name cadddr)
- (define identifier-name cadr)
- (define identifier-R-entry cdr)
- (define (same-denotation? d1 d2)
- (or (eq? d1 d2)
- (and (identifier-denotation? d1)
- (identifier-denotation? d2)
- (eq? (identifier-name d1)
- (identifier-name d2)))))
- (define denotation-of-quote
- (syntactic-lookup standard-syntactic-environment 'quote))
- (define denotation-of-lambda
- (syntactic-lookup standard-syntactic-environment 'lambda))
- (define denotation-of-if
- (syntactic-lookup standard-syntactic-environment 'if))
- (define denotation-of-set!
- (syntactic-lookup standard-syntactic-environment 'set!))
- (define denotation-of-begin
- (syntactic-lookup standard-syntactic-environment 'begin))
- (define denotation-of-define
- (syntactic-lookup standard-syntactic-environment 'define))
- (define denotation-of-define-inline
- (syntactic-lookup standard-syntactic-environment 'define-inline))
- (define denotation-of-define-syntax
- (syntactic-lookup standard-syntactic-environment 'define-syntax))
- (define denotation-of-let-syntax
- (syntactic-lookup standard-syntactic-environment 'let-syntax))
- (define denotation-of-letrec-syntax
- (syntactic-lookup standard-syntactic-environment 'letrec-syntax))
- (define denotation-of-syntax-rules
- (syntactic-lookup standard-syntactic-environment 'syntax-rules))
- (define denotation-of-...
- (syntactic-lookup standard-syntactic-environment '...))
- (define denotation-of-transformer
- (syntactic-lookup standard-syntactic-environment 'transformer))
- ; Given a syntactic environment env to be extended, an alist returned
- ; by rename-vars, and a syntactic environment env2, extends env by
- ; binding the fresh identifiers to the denotations of the original
- ; identifiers in env2.
- (define (syntactic-alias env alist env2)
- (syntactic-divert
- env
- (map (lambda (name-pair)
- (let ((old-name (car name-pair))
- (new-name (cdr name-pair)))
- (cons new-name
- (syntactic-lookup env2 old-name))))
- alist)))
- ; Given a syntactic environment and an alist returned by rename-vars,
- ; extends the environment by binding the old identifiers to the fresh
- ; identifiers.
- ; For Twobit, it also binds the fresh identifiers to their denotations.
- ; This is ok so long as the fresh identifiers are not legal Scheme
- ; identifiers.
- (define (syntactic-rename env alist)
- (if (null? alist)
- env
- (let* ((old (caar alist))
- (new (cdar alist))
- (denotation (make-identifier-denotation new)))
- (syntactic-rename
- (cons (cons old denotation)
- (cons (cons new denotation)
- env))
- (cdr alist)))))
- ; Renaming of variables.
- (define renaming-counter 0)
- (define (make-rename-procedure)
- (set! renaming-counter (+ renaming-counter 1))
- (let ((suffix (string-append renaming-suffix (number->string renaming-counter))))
- (lambda (sym)
- (if (symbol? sym)
- (let ((s (symbol->string sym)))
- (if (and (positive? (string-length s))
- (char=? (string-ref s 0) renaming-prefix-character))
- (string->symbol (string-append s suffix))
- (string->symbol (string-append renaming-prefix s suffix))))
- (m-warn "Illegal use of rename procedure" 'ok:FIXME sym)))))
- ; Given a datum, strips the suffixes from any symbols that appear within
- ; the datum, trying not to copy any more of the datum than necessary.
- (define (m-strip x)
- (define (original-symbol x)
- (define (loop sym s i n)
- (cond ((= i n) sym)
- ((char=? (string-ref s i)
- renaming-suffix-character)
- (string->symbol (substring s 1 i)))
- (else
- (loop sym s (+ i 1) n))))
- (let ((s (symbol->string x)))
- (if (and (positive? (string-length s))
- (char=? (string-ref s 0) renaming-prefix-character))
- (loop x s 0 (string-length s))
- x)))
- (cond ((symbol? x)
- (original-symbol x))
- ((pair? x)
- (let ((a (m-strip (car x)))
- (b (m-strip (cdr x))))
- (if (and (eq? a (car x))
- (eq? b (cdr x)))
- x
- (cons a b))))
- ((vector? x)
- (let* ((v (vector->list x))
- (v2 (map m-strip v)))
- (if (equal? v v2)
- x
- (list->vector v2))))
- (else x)))
- ; Given a list of identifiers, or a formal parameter "list",
- ; returns an alist that associates each identifier with a fresh identifier.
- (define (rename-vars original-vars)
- (let ((rename (make-rename-procedure)))
- (define (loop vars newvars)
- (cond ((null? vars) (reverse newvars))
- ((pair? vars)
- (let ((var (car vars)))
- (if (symbol? var)
- (loop (cdr vars)
- (cons (cons var (rename var))
- newvars))
- (m-error "Illegal variable" var))))
- ((symbol? vars)
- (loop (list vars) newvars))
- (else (m-error "Malformed parameter list" original-vars))))
- (loop original-vars '())))
- ; Given a <formals> and an alist returned by rename-vars that contains
- ; a new name for each formal identifier in <formals>, renames the
- ; formal identifiers.
- (define (rename-formals formals alist)
- (cond ((null? formals) '())
- ((pair? formals)
- (cons (cdr (assq (car formals) alist))
- (rename-formals (cdr formals) alist)))
- (else (cdr (assq formals alist)))))
- ; Copyright 1992 William Clinger
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful purpose, and to redistribute this software
- ; is granted subject to the restriction that all copies made of this
- ; software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 23 November 1998
- ; Compiler for a <transformer spec>.
- ;
- ; References:
- ;
- ; The Revised^4 Report on the Algorithmic Language Scheme.
- ; Clinger and Rees [editors]. To appear in Lisp Pointers.
- ; Also available as a technical report from U of Oregon,
- ; MIT AI Lab, and Cornell.
- ;
- ; Macros That Work. Clinger and Rees. POPL '91.
- ;
- ; The input is a <transformer spec> and a syntactic environment.
- ; Syntactic environments are described in another file.
- ;
- ; The supported syntax differs from the R4RS in that vectors are
- ; allowed as patterns and as templates and are not allowed as
- ; pattern or template data.
- ;
- ; <transformer spec> --> (syntax-rules <literals> <rules>)
- ; <rules> --> () | (<rule> . <rules>)
- ; <rule> --> (<pattern> <template>)
- ; <pattern> --> <pattern_var> ; a <symbol> not in <literals>
- ; | <symbol> ; a <symbol> in <literals>
- ; | ()
- ; | (<pattern> . <pattern>)
- ; | (<ellipsis_pattern>)
- ; | #(<pattern>*) ; extends R4RS
- ; | #(<pattern>* <ellipsis_pattern>) ; extends R4RS
- ; | <pattern_datum>
- ; <template> --> <pattern_var>
- ; | <symbol>
- ; | ()
- ; | (<template2> . <template2>)
- ; | #(<template>*) ; extends R4RS
- ; | <pattern_datum>
- ; <template2> --> <template> | <ellipsis_template>
- ; <pattern_datum> --> <string> ; no <vector>
- ; | <character>
- ; | <boolean>
- ; | <number>
- ; <ellipsis_pattern> --> <pattern> ...
- ; <ellipsis_template> --> <template> ...
- ; <pattern_var> --> <symbol> ; not in <literals>
- ; <literals> --> () | (<symbol> . <literals>)
- ;
- ; Definitions.
- ;
- ; scope of an ellipsis
- ;
- ; Within a pattern or template, the scope of an ellipsis
- ; (...) is the pattern or template that appears to its left.
- ;
- ; rank of a pattern variable
- ;
- ; The rank of a pattern variable is the number of ellipses
- ; within whose scope it appears in the pattern.
- ;
- ; rank of a subtemplate
- ;
- ; The rank of a subtemplate is the number of ellipses within
- ; whose scope it appears in the template.
- ;
- ; template rank of an occurrence of a pattern variable
- ;
- ; The template rank of an occurrence of a pattern variable
- ; within a template is the rank of that occurrence, viewed
- ; as a subtemplate.
- ;
- ; variables bound by a pattern
- ;
- ; The variables bound by a pattern are the pattern variables
- ; that appear within it.
- ;
- ; referenced variables of a subtemplate
- ;
- ; The referenced variables of a subtemplate are the pattern
- ; variables that appear within it.
- ;
- ; variables opened by an ellipsis template
- ;
- ; The variables opened by an ellipsis template are the
- ; referenced pattern variables whose rank is greater than
- ; the rank of the ellipsis template.
- ;
- ;
- ; Restrictions.
- ;
- ; No pattern variable appears more than once within a pattern.
- ;
- ; For every occurrence of a pattern variable within a template,
- ; the template rank of the occurrence must be greater than or
- ; equal to the pattern variable's rank.
- ;
- ; Every ellipsis template must open at least one variable.
- ;
- ; For every ellipsis template, the variables opened by an
- ; ellipsis template must all be bound to sequences of the
- ; same length.
- ;
- ;
- ; The compiled form of a <rule> is
- ;
- ; <rule> --> (<pattern> <template> <inserted>)
- ; <pattern> --> <pattern_var>
- ; | <symbol>
- ; | ()
- ; | (<pattern> . <pattern>)
- ; | <ellipsis_pattern>
- ; | #(<pattern>)
- ; | <pattern_datum>
- ; <template> --> <pattern_var>
- ; | <symbol>
- ; | ()
- ; | (<template2> . <template2>)
- ; | #(<pattern>)
- ; | <pattern_datum>
- ; <template2> --> <template> | <ellipsis_template>
- ; <pattern_datum> --> <string>
- ; | <character>
- ; | <boolean>
- ; | <number>
- ; <pattern_var> --> #(<V> <symbol> <rank>)
- ; <ellipsis_pattern> --> #(<E> <pattern> <pattern_vars>)
- ; <ellipsis_template> --> #(<E> <template> <pattern_vars>)
- ; <inserted> --> () | (<symbol> . <inserted>)
- ; <pattern_vars> --> () | (<pattern_var> . <pattern_vars>)
- ; <rank> --> <exact non-negative integer>
- ;
- ; where <V> and <E> are unforgeable values.
- ; The pattern variables associated with an ellipsis pattern
- ; are the variables bound by the pattern, and the pattern
- ; variables associated with an ellipsis template are the
- ; variables opened by the ellipsis template.
- ;
- ;
- ; What's wrong with the above?
- ; If the template contains a big chunk that contains no pattern variables
- ; or inserted identifiers, then the big chunk will be copied unnecessarily.
- ; That shouldn't matter very often.
- ($$trace "syntaxrules")
- (define pattern-variable-flag (list 'v))
- (define ellipsis-pattern-flag (list 'e))
- (define ellipsis-template-flag ellipsis-pattern-flag)
- (define (make-patternvar v rank)
- (vector pattern-variable-flag v rank))
- (define (make-ellipsis-pattern P vars)
- (vector ellipsis-pattern-flag P vars))
- (define (make-ellipsis-template T vars)
- (vector ellipsis-template-flag T vars))
- (define (patternvar? x)
- (and (vector? x)
- (= (vector-length x) 3)
- (eq? (vector-ref x 0) pattern-variable-flag)))
- (define (ellipsis-pattern? x)
- (and (vector? x)
- (= (vector-length x) 3)
- (eq? (vector-ref x 0) ellipsis-pattern-flag)))
- (define (ellipsis-template? x)
- (and (vector? x)
- (= (vector-length x) 3)
- (eq? (vector-ref x 0) ellipsis-template-flag)))
- (define (patternvar-name V) (vector-ref V 1))
- (define (patternvar-rank V) (vector-ref V 2))
- (define (ellipsis-pattern P) (vector-ref P 1))
- (define (ellipsis-pattern-vars P) (vector-ref P 2))
- (define (ellipsis-template T) (vector-ref T 1))
- (define (ellipsis-template-vars T) (vector-ref T 2))
- (define (pattern-variable v vars)
- (cond ((null? vars) #f)
- ((eq? v (patternvar-name (car vars)))
- (car vars))
- (else (pattern-variable v (cdr vars)))))
- ; Given a <transformer spec> and a syntactic environment,
- ; returns a macro denotation.
- ;
- ; A macro denotation is of the form
- ;
- ; (macro (<rule> ...) env)
- ;
- ; where each <rule> has been compiled as described above.
- (define (m-compile-transformer-spec spec env)
- (if (and (> (safe-length spec) 1)
- (eq? (syntactic-lookup env (car spec))
- denotation-of-syntax-rules))
- (let ((literals (cadr spec))
- (rules (cddr spec)))
- (if (or (not (list? literals))
- (not (every1? (lambda (rule)
- (and (= (safe-length rule) 2)
- (pair? (car rule))))
- rules)))
- (m-error "Malformed syntax-rules" spec))
- (list 'macro
- (map (lambda (rule)
- (m-compile-rule rule literals env))
- rules)
- env))
- (m-error "Malformed syntax-rules" spec)))
- (define (m-compile-rule rule literals env)
- (m-compile-pattern (cdr (car rule))
- literals
- env
- (lambda (compiled-rule patternvars)
- ; FIXME
- ; should check uniqueness of pattern variables here
- (cons compiled-rule
- (m-compile-template
- (cadr rule)
- patternvars
- env)))))
- (define (m-compile-pattern P literals env k)
- (define (loop P vars rank k)
- (cond ((symbol? P)
- (if (memq P literals)
- (k P vars)
- (let ((var (make-patternvar P rank)))
- (k var (cons var vars)))))
- ((null? P) (k '() vars))
- ((pair? P)
- (if (and (pair? (cdr P))
- (symbol? (cadr P))
- (same-denotation? (syntactic-lookup env (cadr P))
- denotation-of-...))
- (if (null? (cddr P))
- (loop (car P)
- '()
- (+ rank 1)
- (lambda (P vars1)
- (k (make-ellipsis-pattern P vars1)
- (union2 vars1 vars))))
- (m-error "Malformed pattern" P))
- (loop (car P)
- vars
- rank
- (lambda (P1 vars)
- (loop (cdr P)
- vars
- rank
- (lambda (P2 vars)
- (k (cons P1 P2) vars)))))))
- ((vector? P)
- (loop (vector->list P)
- vars
- rank
- (lambda (P vars)
- (k (vector P) vars))))
- (else (k P vars))))
- (loop P '() 0 k))
- (define (m-compile-template T vars env)
-
- (define (loop T inserted referenced rank escaped? k)
- (cond ((symbol? T)
- (let ((x (pattern-variable T vars)))
- (if x
- (if (>= rank (patternvar-rank x))
- (k x inserted (cons x referenced))
- (m-error
- "Too few ellipses follow pattern variable in template"
- (patternvar-name x)))
- (k T (cons T inserted) referenced))))
- ((null? T) (k '() inserted referenced))
- ((pair? T)
- (cond ((and (not escaped?)
- (symbol? (car T))
- (same-denotation? (syntactic-lookup env (car T))
- denotation-of-...)
- (pair? (cdr T))
- (null? (cddr T)))
- (loop (cadr T) inserted referenced rank #t k))
- ((and (not escaped?)
- (pair? (cdr T))
- (symbol? (cadr T))
- (same-denotation? (syntactic-lookup env (cadr T))
- denotation-of-...))
- (loop1 T inserted referenced rank escaped? k))
- (else
- (loop (car T)
- inserted
- referenced
- rank
- escaped?
- (lambda (T1 inserted referenced)
- (loop (cdr T)
- inserted
- referenced
- rank
- escaped?
- (lambda (T2 inserted referenced)
- (k (cons T1 T2) inserted referenced))))))))
- ((vector? T)
- (loop (vector->list T)
- inserted
- referenced
- rank
- escaped?
- (lambda (T inserted referenced)
- (k (vector T) inserted referenced))))
- (else (k T inserted referenced))))
-
- (define (loop1 T inserted referenced rank escaped? k)
- (loop (car T)
- inserted
- '()
- (+ rank 1)
- escaped?
- (lambda (T1 inserted referenced1)
- (loop (cddr T)
- inserted
- (append referenced1 referenced)
- rank
- escaped?
- (lambda (T2 inserted referenced)
- (k (cons (make-ellipsis-template
- T1
- (filter1 (lambda (var)
- (> (patternvar-rank var)
- rank))
- referenced1))
- T2)
- inserted
- referenced))))))
-
- (loop T
- '()
- '()
- 0
- #f
- (lambda (T inserted referenced)
- (list T inserted))))
- ; The pattern matcher.
- ;
- ; Given an input, a pattern, and two syntactic environments,
- ; returns a pattern variable environment (represented as an alist)
- ; if the input matches the pattern, otherwise returns #f.
- (define empty-pattern-variable-environment
- (list (make-patternvar (string->symbol "") 0)))
- (define (m-match F P env-def env-use)
-
- (define (match F P answer rank)
- (cond ((null? P)
- (and (null? F) answer))
- ((pair? P)
- (and (pair? F)
- (let ((answer (match (car F) (car P) answer rank)))
- (and answer (match (cdr F) (cdr P) answer rank)))))
- ((symbol? P)
- (and (symbol? F)
- (same-denotation? (syntactic-lookup env-def P)
- (syntactic-lookup env-use F))
- answer))
- ((patternvar? P)
- (cons (cons P F) answer))
- ((ellipsis-pattern? P)
- (match1 F P answer (+ rank 1)))
- ((vector? P)
- (and (vector? F)
- (match (vector->list F) (vector-ref P 0) answer rank)))
- (else (and (equal? F P) answer))))
-
- (define (match1 F P answer rank)
- (cond ((not (list? F)) #f)
- ((null? F)
- (append (map (lambda (var) (cons var '()))
- (ellipsis-pattern-vars P))
- answer))
- (else
- (let* ((P1 (ellipsis-pattern P))
- (answers (map (lambda (F) (match F P1 answer rank))
- F)))
- (if (every1? (lambda (answer) answer) answers)
- (append (map (lambda (var)
- (cons var
- (map (lambda (answer)
- (cdr (assq var answer)))
- answers)))
- (ellipsis-pattern-vars P))
- answer)
- #f)))))
-
- (match F P empty-pattern-variable-environment 0))
- (define (m-rewrite T alist)
-
- (define (rewrite T alist rank)
- (cond ((null? T) '())
- ((pair? T)
- ((if (ellipsis-pattern? (car T))
- append
- cons)
- (rewrite (car T) alist rank)
- (rewrite (cdr T) alist rank)))
- ((symbol? T) (cdr (assq T alist)))
- ((patternvar? T) (cdr (assq T alist)))
- ((ellipsis-template? T)
- (rewrite1 T alist (+ rank 1)))
- ((vector? T)
- (list->vector (rewrite (vector-ref T 0) alist rank)))
- (else T)))
-
- (define (rewrite1 T alist rank)
- (let* ((T1 (ellipsis-template T))
- (vars (ellipsis-template-vars T))
- (rows (map (lambda (var) (cdr (assq var alist)))
- vars)))
- (map (lambda (alist) (rewrite T1 alist rank))
- (make-columns vars rows alist))))
-
- (define (make-columns vars rows alist)
- (define (loop rows)
- (if (null? (car rows))
- '()
- (cons (append (map (lambda (var row)
- (cons var (car row)))
- vars
- rows)
- alist)
- (loop (map cdr rows)))))
- (if (or (null? (cdr rows))
- (apply = (map length rows)))
- (loop rows)
- (m-error "Use of macro is not consistent with definition"
- vars
- rows)))
-
- (rewrite T alist 0))
- ; Given a use of a macro, the syntactic environment of the use,
- ; a continuation that expects a transcribed expression and
- ; a new environment in which to continue expansion, and a boolean
- ; that is true if this transcription is for an inline procedure,
- ; does the right thing.
- (define (m-transcribe0 exp env-use k inline?)
- (let* ((m (syntactic-lookup env-use (car exp)))
- (rules (macro-rules m))
- (env-def (macro-env m))
- (F (cdr exp)))
- (define (loop rules)
- (if (null? rules)
- (if inline?
- (k exp env-use)
- (m-error "Use of macro does not match definition" exp))
- (let* ((rule (car rules))
- (pattern (car rule))
- (alist (m-match F pattern env-def env-use)))
- (if alist
- (let* ((template (cadr rule))
- (inserted (caddr rule))
- (alist2 (rename-vars inserted))
- (newexp (m-rewrite template (append alist2 alist))))
- (k newexp
- (syntactic-alias env-use alist2 env-def)))
- (loop (cdr rules))))))
- (if (procedure? rules)
- (m-transcribe-low-level exp env-use k rules env-def)
- (loop rules))))
- (define (m-transcribe exp env-use k)
- (m-transcribe0 exp env-use k #f))
- (define (m-transcribe-inline exp env-use k)
- (m-transcribe0 exp env-use k #t))
- ; Copyright 1998 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Low-level macro facility based on explicit renaming. See
- ; William D Clinger. Hygienic macros through explicit renaming.
- ; In Lisp Pointers IV(4), 25-28, December 1991.
- ($$trace "lowlevel")
- (define (m-transcribe-low-level exp env-use k transformer env-def)
- (let ((rename0 (make-rename-procedure))
- (renamed '())
- (ok #t))
- (define (lookup sym)
- (let loop ((alist renamed))
- (cond ((null? alist)
- (syntactic-lookup env-use sym))
- ((eq? sym (cdr (car alist)))
- (syntactic-lookup env-def (car (car alist))))
- (else
- (loop (cdr alist))))))
- (let ((rename
- (lambda (sym)
- (if ok
- (let ((probe (assq sym renamed)))
- (if probe
- (cdr probe)
- (let ((sym2 (rename0 sym)))
- (set! renamed (cons (cons sym sym2) renamed))
- sym2)))
- (m-error "Illegal use of a rename procedure" sym))))
- (compare
- (lambda (sym1 sym2)
- (same-denotation? (lookup sym1) (lookup sym2)))))
- (let ((exp2 (transformer exp rename compare)))
- (set! ok #f)
- (k exp2
- (syntactic-alias env-use renamed env-def))))))
- (define identifier? symbol?)
- (define (identifier->symbol id)
- (m-strip id))
- ; Copyright 1992 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 22 April 1999
- ($$trace "expand")
- ; This procedure sets the default scope of global macro definitions.
- (define define-syntax-scope
- (let ((flag 'letrec))
- (lambda args
- (cond ((null? args) flag)
- ((not (null? (cdr args)))
- (apply m-warn
- "Too many arguments passed to define-syntax-scope"
- args))
- ((memq (car args) '(letrec letrec* let*))
- (set! flag (car args)))
- (else (m-warn "Unrecognized argument to define-syntax-scope"
- (car args)))))))
- ; The main entry point.
- ; The outermost lambda allows known procedures to be lifted outside
- ; all local variables.
- (define (macro-expand def-or-exp)
- (call-with-current-continuation
- (lambda (k)
- (set! m-quit k)
- (set! renaming-counter 0)
- (make-call
- (make-lambda '() ; formals
- '() ; definitions
- '() ; R
- '() ; F
- '() ; G
- '() ; declarations
- #f ; documentation
- (desugar-definitions def-or-exp
- global-syntactic-environment
- make-toplevel-definition))
- '()))))
- (define (desugar-definitions exp env make-toplevel-definition)
- (letrec
-
- ((define-loop
- (lambda (exp rest first env)
- (cond ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-begin)
- (pair? (cdr exp)))
- (define-loop (cadr exp) (append (cddr exp) rest) first env))
- ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-define))
- (let ((exp (desugar-define exp env)))
- (cond ((and (null? first) (null? rest))
- exp)
- ((null? rest)
- (make-begin (reverse (cons exp first))))
- (else (define-loop (car rest)
- (cdr rest)
- (cons exp first)
- env)))))
- ((and (pair? exp)
- (symbol? (car exp))
- (or (eq? (syntactic-lookup env (car exp))
- denotation-of-define-syntax)
- (eq? (syntactic-lookup env (car exp))
- denotation-of-define-inline))
- (null? first))
- (define-syntax-loop exp rest env))
- ((and (pair? exp)
- (symbol? (car exp))
- (macro-denotation? (syntactic-lookup env (car exp))))
- (m-transcribe exp
- env
- (lambda (exp env)
- (define-loop exp rest first env))))
- ((and (null? first) (null? rest))
- (m-expand exp env))
- ((null? rest)
- (make-begin (reverse (cons (m-expand exp env) first))))
- (else (make-begin
- (append (reverse first)
- (map (lambda (exp) (m-expand exp env))
- (cons exp rest))))))))
-
- (define-syntax-loop
- (lambda (exp rest env)
- (cond ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-begin)
- (pair? (cdr exp)))
- (define-syntax-loop (cadr exp) (append (cddr exp) rest) env))
- ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-define-syntax))
- (if (pair? (cdr exp))
- (redefinition (cadr exp)))
- (if (null? rest)
- (m-define-syntax exp env)
- (begin (m-define-syntax exp env)
- (define-syntax-loop (car rest) (cdr rest) env))))
- ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-define-inline))
- (if (pair? (cdr exp))
- (redefinition (cadr exp)))
- (if (null? rest)
- (m-define-inline exp env)
- (begin (m-define-inline exp env)
- (define-syntax-loop (car rest) (cdr rest) env))))
- ((and (pair? exp)
- (symbol? (car exp))
- (macro-denotation? (syntactic-lookup env (car exp))))
- (m-transcribe exp
- env
- (lambda (exp env)
- (define-syntax-loop exp rest env))))
- ((and (pair? exp)
- (symbol? (car exp))
- (eq? (syntactic-lookup env (car exp))
- denotation-of-define))
- (define-loop exp rest '() env))
- ((null? rest)
- (m-expand exp env))
- (else (make-begin
- (map (lambda (exp) (m-expand exp env))
- (cons exp rest)))))))
-
- (desugar-define
- (lambda (exp env)
- (cond
- ((null? (cdr exp)) (m-error "Malformed definition" exp))
- ; (define foo) syntax is transformed into (define foo (undefined)).
- ((null? (cddr exp))
- (let ((id (cadr exp)))
- (if (or (null? pass1-block-inlines)
- (not (memq id pass1-block-inlines)))
- (begin
- (redefinition id)
- (syntactic-bind-globally! id (make-identifier-denotation id))))
- (make-toplevel-definition id (make-undefined))))
- ((pair? (cadr exp))
- (desugar-define
- (let* ((def (car exp))
- (pattern (cadr exp))
- (f (car pattern))
- (args (cdr pattern))
- (body (cddr exp)))
- (if (and (symbol? (car (cadr exp)))
- (benchmark-mode)
- (list? (cadr exp)))
- `(,def ,f
- (,lambda0 ,args
- ((,lambda0 (,f)
- (,set!0 ,f (,lambda0 ,args ,@body))
- ,pattern)
- 0)))
- `(,def ,f (,lambda0 ,args ,@body))))
- env))
- ((> (length exp) 3) (m-error "Malformed definition" exp))
- (else (let ((id (cadr exp)))
- (if (or (null? pass1-block-inlines)
- (not (memq id pass1-block-inlines)))
- (begin
- (redefinition id)
- (syntactic-bind-globally! id (make-identifier-denotation id))))
- (make-toplevel-definition id (m-expand (caddr exp) env)))))))
-
- (redefinition
- (lambda (id)
- (if (symbol? id)
- (if (not (identifier-denotation?
- (syntactic-lookup global-syntactic-environment id)))
- (if (issue-warnings)
- (m-warn "Redefining " id)))
- (m-error "Malformed variable or keyword" id)))))
-
- ; body of letrec
-
- (define-loop exp '() '() env)))
- ; Given an expression and a syntactic environment,
- ; returns an expression in core Scheme.
- (define (m-expand exp env)
- (cond ((not (pair? exp))
- (m-atom exp env))
- ((not (symbol? (car exp)))
- (m-application exp env))
- (else
- (let ((keyword (syntactic-lookup env (car exp))))
- (case (denotation-class keyword)
- ((special)
- (cond
- ((eq? keyword denotation-of-quote) (m-quote exp))
- ((eq? keyword denotation-of-lambda) (m-lambda exp env))
- ((eq? keyword denotation-of-if) (m-if exp env))
- ((eq? keyword denotation-of-set!) (m-set exp env))
- ((eq? keyword denotation-of-begin) (m-begin exp env))
- ((eq? keyword denotation-of-let-syntax)
- (m-let-syntax exp env))
- ((eq? keyword denotation-of-letrec-syntax)
- (m-letrec-syntax exp env))
- ((or (eq? keyword denotation-of-define)
- (eq? keyword denotation-of-define-syntax)
- (eq? keyword denotation-of-define-inline))
- (m-error "Definition out of context" exp))
- (else (m-bug "Bug detected in m-expand" exp env))))
- ((macro) (m-macro exp env))
- ((inline) (m-inline exp env))
- ((identifier) (m-application exp env))
- (else (m-bug "Bug detected in m-expand" exp env)))))))
- (define (m-atom exp env)
- (cond ((not (symbol? exp))
- ; Here exp ought to be a boolean, number, character, or string.
- ; I'll warn about other things but treat them as if quoted.
- ;
- ; I'm turning off some of the warnings because notably procedures
- ; and #!unspecified can occur in loaded files and it's a major
- ; pain if a warning is printed for each. --lars
- (if (and (not (boolean? exp))
- (not (number? exp))
- (not (char? exp))
- (not (string? exp))
- (not (procedure? exp))
- (not (eq? exp (unspecified))))
- (m-warn "Malformed constant -- should be quoted" exp))
- (make-constant exp))
- (else (let ((denotation (syntactic-lookup env exp)))
- (case (denotation-class denotation)
- ((special macro)
- (m-warn "Syntactic keyword used as a variable" exp)
- ; Syntactic keywords used as variables are treated as #t.
- (make-constant #t))
- ((inline)
- (make-variable (inline-name denotation)))
- ((identifier)
- (let ((var (make-variable (identifier-name denotation)))
- (R-entry (identifier-R-entry denotation)))
- (R-entry.references-set!
- R-entry
- (cons var (R-entry.references R-entry)))
- var))
- (else (m-bug "Bug detected by m-atom" exp env)))))))
- (define (m-quote exp)
- (if (and (pair? (cdr exp))
- (null? (cddr exp)))
- (make-constant (m-strip (cadr exp)))
- (m-error "Malformed quoted constant" exp)))
- (define (m-lambda exp env)
- (if (> (safe-length exp) 2)
-
- (let* ((formals (cadr exp))
- (alist (rename-vars formals))
- (env (syntactic-rename env alist))
- (body (cddr exp)))
-
- (do ((alist alist (cdr alist)))
- ((null? alist))
- (if (assq (caar alist) (cdr alist))
- (m-error "Malformed parameter list" formals)))
-
- ; To simplify the run-time system, there's a limit on how many
- ; fixed arguments can be followed by a rest argument.
- ; That limit is removed here.
- ; Bug: documentation slot isn't right when this happens.
- ; Bug: this generates extremely inefficient code.
-
- (if (and (not (list? formals))
- (> (length alist) @maxargs-with-rest-arg@))
- (let ((TEMP (car (rename-vars '(temp)))))
- (m-lambda
- `(,lambda0 ,TEMP
- ((,lambda0 ,(map car alist)
- ,@(cddr exp))
- ,@(do ((actuals '() (cons (list name:CAR path)
- actuals))
- (path TEMP (list name:CDR path))
- (formals formals (cdr formals)))
- ((symbol? formals)
- (append (reverse actuals) (list path))))))
- env))
- (make-lambda (rename-formals formals alist)
- '() ; no definitions yet
- (map (lambda (entry)
- (cdr (syntactic-lookup env (cdr entry))))
- alist) ; R
- '() ; F
- '() ; G
- '() ; decls
- (make-doc #f
- (if (list? formals)
- (length alist)
- (exact->inexact (- (length alist) 1)))
- (if (include-variable-names)
- formals
- #f)
- (if (include-source-code)
- exp
- #f)
- source-file-name
- source-file-position)
- (m-body body env))))
-
- (m-error "Malformed lambda expression" exp)))
- (define (m-body body env)
- (define (loop body env defs)
- (if (null? body)
- (m-error "Empty body"))
- (let ((exp (car body)))
- (if (and (pair? exp)
- (symbol? (car exp)))
- (let ((denotation (syntactic-lookup env (car exp))))
- (case (denotation-class denotation)
- ((special)
- (cond ((eq? denotation denotation-of-begin)
- (loop (append (cdr exp) (cdr body)) env defs))
- ((eq? denotation denotation-of-define)
- (loop (cdr body) env (cons exp defs)))
- (else (finalize-body body env defs))))
- ((macro)
- (m-transcribe exp
- env
- (lambda (exp env)
- (loop (cons exp (cdr body))
- env
- defs))))
- ((inline identifier)
- (finalize-body body env defs))
- (else (m-bug "Bug detected in m-body" body env))))
- (finalize-body body env defs))))
- (loop body env '()))
- (define (finalize-body body env defs)
- (if (null? defs)
- (let ((body (map (lambda (exp) (m-expand exp env))
- body)))
- (if (null? (cdr body))
- (car body)
- (make-begin body)))
- (let ()
- (define (sort-defs defs)
- (let* ((augmented
- (map (lambda (def)
- (let ((rhs (cadr def)))
- (if (not (pair? rhs))
- (cons 'trivial def)
- (let ((denotation
- (syntactic-lookup env (car rhs))))
- (cond ((eq? denotation
- denotation-of-lambda)
- (cons 'procedure def))
- ((eq? denotation
- denotation-of-quote)
- (cons 'trivial def))
- (else
- (cons 'miscellaneous def)))))))
- defs))
- (sorted (twobit-sort (lambda (x y)
- (or (eq? (car x) 'procedure)
- (eq? (car y) 'miscellaneous)))
- augmented)))
- (map cdr sorted)))
- (define (desugar-definition def)
- (if (> (safe-length def) 2)
- (cond ((pair? (cadr def))
- (desugar-definition
- `(,(car def)
- ,(car (cadr def))
- (,lambda0
- ,(cdr (cadr def))
- ,@(cddr def)))))
- ((and (= (length def) 3)
- (symbol? (cadr def)))
- (cdr def))
- (else (m-error "Malformed definition" def)))
- (m-error "Malformed definition" def)))
- (define (expand-letrec bindings body)
- (make-call
- (m-expand
- `(,lambda0 ,(map car bindings)
- ,@(map (lambda (binding)
- `(,set!0 ,(car binding)
- ,(cadr binding)))
- bindings)
- ,@body)
- env)
- (map (lambda (binding) (make-unspecified)) bindings)))
- (expand-letrec (sort-defs (map desugar-definition
- (reverse defs)))
- body))))
- (define (m-if exp env)
- (let ((n (safe-length exp)))
- (if (or (= n 3) (= n 4))
- (make-conditional (m-expand (cadr exp) env)
- (m-expand (caddr exp) env)
- (if (= n 3)
- (make-unspecified)
- (m-expand (cadddr exp) env)))
- (m-error "Malformed if expression" exp))))
- (define (m-set exp env)
- (if (= (safe-length exp) 3)
- (let ((lhs (m-expand (cadr exp) env))
- (rhs (m-expand (caddr exp) env)))
- (if (variable? lhs)
- (let* ((x (variable.name lhs))
- (assignment (make-assignment x rhs))
- (denotation (syntactic-lookup env x)))
- (if (identifier-denotation? denotation)
- (let ((R-entry (identifier-R-entry denotation)))
- (R-entry.references-set!
- R-entry
- (remq lhs (R-entry.references R-entry)))
- (R-entry.assignments-set!
- R-entry
- (cons assignment (R-entry.assignments R-entry)))))
- (if (and (lambda? rhs)
- (include-procedure-names))
- (let ((doc (lambda.doc rhs)))
- (doc.name-set! doc x)))
- (if pass1-block-compiling?
- (set! pass1-block-assignments
- (cons x pass1-block-assignments)))
- assignment)
- (m-error "Malformed assignment" exp)))
- (m-error "Malformed assignment" exp)))
- (define (m-begin exp env)
- (cond ((> (safe-length exp) 1)
- (make-begin (map (lambda (exp) (m-expand exp env)) (cdr exp))))
- ((= (safe-length exp) 1)
- (m-warn "Non-standard begin expression" exp)
- (make-unspecified))
- (else
- (m-error "Malformed begin expression" exp))))
- (define (m-application exp env)
- (if (> (safe-length exp) 0)
- (let* ((proc (m-expand (car exp) env))
- (args (map (lambda (exp) (m-expand exp env))
- (cdr exp)))
- (call (make-call proc args)))
- (if (variable? proc)
- (let* ((procname (variable.name proc))
- (entry
- (and (not (null? args))
- (constant? (car args))
- (integrate-usual-procedures)
- (every1? constant? args)
- (let ((entry (constant-folding-entry procname)))
- (and entry
- (let ((predicates
- (constant-folding-predicates entry)))
- (and (= (length args)
- (length predicates))
- (let loop ((args args)
- (predicates predicates))
- (cond ((null? args) entry)
- (((car predicates)
- (constant.value (car args)))
- (loop (cdr args)
- (cdr predicates)))
- (else #f))))))))))
- (if entry
- (make-constant (apply (constant-folding-folder entry)
- (map constant.value args)))
- (let ((denotation (syntactic-lookup env procname)))
- (if (identifier-denotation? denotation)
- (let ((R-entry (identifier-R-entry denotation)))
- (R-entry.calls-set!
- R-entry
- (cons call (R-entry.calls R-entry)))))
- call)))
- call))
- (m-error "Malformed application" exp)))
- ; The environment argument should always be global here.
- (define (m-define-inline exp env)
- (cond ((and (= (safe-length exp) 3)
- (symbol? (cadr exp)))
- (let ((name (cadr exp)))
- (m-define-syntax1 name
- (caddr exp)
- env
- (define-syntax-scope))
- (let ((denotation
- (syntactic-lookup global-syntactic-environment name)))
- (syntactic-bind-globally!
- name
- (make-inline-denotation name
- (macro-rules denotation)
- (macro-env denotation))))
- (make-constant name)))
- (else
- (m-error "Malformed define-inline" exp))))
- ; The environment argument should always be global here.
- (define (m-define-syntax exp env)
- (cond ((and (= (safe-length exp) 3)
- (symbol? (cadr exp)))
- (m-define-syntax1 (cadr exp)
- (caddr exp)
- env
- (define-syntax-scope)))
- ((and (= (safe-length exp) 4)
- (symbol? (cadr exp))
- ; FIXME: should use denotations here
- (memq (caddr exp) '(letrec letrec* let*)))
- (m-define-syntax1 (cadr exp)
- (cadddr exp)
- env
- (caddr exp)))
- (else (m-error "Malformed define-syntax" exp))))
- (define (m-define-syntax1 keyword spec env scope)
- (if (and (pair? spec)
- (symbol? (car spec)))
- (let* ((transformer-keyword (car spec))
- (denotation (syntactic-lookup env transformer-keyword)))
- (cond ((eq? denotation denotation-of-syntax-rules)
- (case scope
- ((letrec) (m-define-syntax-letrec keyword spec env))
- ((letrec*) (m-define-syntax-letrec* keyword spec env))
- ((let*) (m-define-syntax-let* keyword spec env))
- (else (m-bug "Weird scope" scope))))
- ((same-denotation? denotation denotation-of-transformer)
- ; FIXME: no error checking here
- (syntactic-bind-globally!
- keyword
- (make-macro-denotation (eval (cadr spec)) env)))
- (else
- (m-error "Malformed syntax transformer" spec))))
- (m-error "Malformed syntax transformer" spec))
- (make-constant keyword))
- (define (m-define-syntax-letrec keyword spec env)
- (syntactic-bind-globally!
- keyword
- (m-compile-transformer-spec spec env)))
- (define (m-define-syntax-letrec* keyword spec env)
- (let* ((env (syntactic-extend (syntactic-copy env)
- (list keyword)
- '((fake denotation))))
- (transformer (m-compile-transformer-spec spec env)))
- (syntactic-assign! env keyword transformer)
- (syntactic-bind-globally! keyword transformer)))
- (define (m-define-syntax-let* keyword spec env)
- (syntactic-bind-globally!
- keyword
- (m-compile-transformer-spec spec (syntactic-copy env))))
- (define (m-let-syntax exp env)
- (if (and (> (safe-length exp) 2)
- (every1? (lambda (binding)
- (and (pair? binding)
- (symbol? (car binding))
- (pair? (cdr binding))
- (null? (cddr binding))))
- (cadr exp)))
- (m-body (cddr exp)
- (syntactic-extend env
- (map car (cadr exp))
- (map (lambda (spec)
- (m-compile-transformer-spec
- spec
- env))
- (map cadr (cadr exp)))))
- (m-error "Malformed let-syntax" exp)))
- (define (m-letrec-syntax exp env)
- (if (and (> (safe-length exp) 2)
- (every1? (lambda (binding)
- (and (pair? binding)
- (symbol? (car binding))
- (pair? (cdr binding))
- (null? (cddr binding))))
- (cadr exp)))
- (let ((env (syntactic-extend env
- (map car (cadr exp))
- (map (lambda (id)
- '(fake denotation))
- (cadr exp)))))
- (for-each (lambda (id spec)
- (syntactic-assign!
- env
- id
- (m-compile-transformer-spec spec env)))
- (map car (cadr exp))
- (map cadr (cadr exp)))
- (m-body (cddr exp) env))
- (m-error "Malformed let-syntax" exp)))
- (define (m-macro exp env)
- (m-transcribe exp
- env
- (lambda (exp env)
- (m-expand exp env))))
- (define (m-inline exp env)
- (if (integrate-usual-procedures)
- (m-transcribe-inline exp
- env
- (lambda (newexp env)
- (if (eq? exp newexp)
- (m-application exp env)
- (m-expand newexp env))))
- (m-application exp env)))
- (define m-quit ; assigned by macro-expand
- (lambda (v) v))
- ; To do:
- ; Clean up alist hacking et cetera.
- ; Declarations.
- ; Integrable procedures.
- ; New semantics for body of LET-SYNTAX and LETREC-SYNTAX.
- ; Copyright 1992 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 5 April 1999.
- ($$trace "usual")
- ; The usual macros, adapted from Jonathan's Version 2 implementation.
- ; DEFINE is handled primitively, since top-level DEFINE has a side
- ; effect on the global syntactic environment, and internal definitions
- ; have to be handled specially anyway.
- ;
- ; Some extensions are noted, as are some optimizations.
- ;
- ; The LETREC* scope rule is used here to protect these macros against
- ; redefinition of LAMBDA etc. The scope rule is changed to LETREC at
- ; the end of this file.
- (define-syntax-scope 'letrec*)
- (for-each (lambda (form)
- (macro-expand form))
- '(
- ; Named LET is defined later, after LETREC has been defined.
- (define-syntax let
- (syntax-rules ()
- ((let ((?name ?val) ...) ?body ?body1 ...)
- ((lambda (?name ...) ?body ?body1 ...) ?val ...))))
- (define-syntax let*
- (syntax-rules ()
- ((let* () ?body ?body1 ...)
- (let () ?body ?body1 ...))
- ((let* ((?name1 ?val1) (?name ?val) ...) ?body ?body1 ...)
- (let ((?name1 ?val1)) (let* ((?name ?val) ...) ?body ?body1 ...)))))
- ; Internal definitions have to be handled specially anyway,
- ; so we might as well rely on them here.
- (define-syntax letrec
- (syntax-rules (lambda quote)
- ((letrec ((?name ?val) ...) ?body ?body2 ...)
- ((lambda ()
- (define ?name ?val) ...
- ?body ?body2 ...)))))
- ; This definition of named LET extends the prior definition of LET.
- ; The first rule is non-circular, thanks to the LET* scope that is
- ; specified for this use of DEFINE-SYNTAX.
- (define-syntax let let*
- (syntax-rules ()
- ((let (?bindings ...) . ?body)
- (let (?bindings ...) . ?body))
- ((let ?tag ((?name ?val) ...) ?body ?body1 ...)
- (let ((?name ?val) ...)
- (letrec ((?tag (lambda (?name ...) ?body ?body1 ...)))
- (?tag ?name ...))))))
- (define-syntax and
- (syntax-rules ()
- ((and) #t)
- ((and ?e) ?e)
- ((and ?e1 ?e2 ?e3 ...)
- (if ?e1 (and ?e2 ?e3 ...) #f))))
- (define-syntax or
- (syntax-rules ()
- ((or) #f)
- ((or ?e) ?e)
- ((or ?e1 ?e2 ?e3 ...)
- (let ((temp ?e1))
- (if temp temp (or ?e2 ?e3 ...))))))
- (define-syntax cond
- (syntax-rules (else =>)
- ((cond (else ?result ?result2 ...))
- (begin ?result ?result2 ...))
-
- ((cond (?test => ?result))
- (let ((temp ?test))
- (if temp (?result temp))))
-
- ((cond (?test)) ?test)
-
- ((cond (?test ?result ?result2 ...))
- (if ?test (begin ?result ?result2 ...)))
-
- ((cond (?test => ?result) ?clause ?clause2 ...)
- (let ((temp ?test))
- (if temp (?result temp) (cond ?clause ?clause2 ...))))
-
- ((cond (?test) ?clause ?clause2 ...)
- (or ?test (cond ?clause ?clause2 ...)))
-
- ((cond (?test ?result ?result2 ...)
- ?clause ?clause2 ...)
- (if ?test
- (begin ?result ?result2 ...)
- (cond ?clause ?clause2 ...)))))
- ; The R4RS says a <step> may be omitted.
- ; That's a good excuse for a macro-defining macro that uses LETREC-SYNTAX
- ; and the ... escape.
- (define-syntax do
- (syntax-rules ()
- ((do (?bindings0 ...) (?test) ?body0 ...)
- (do (?bindings0 ...) (?test (if #f #f)) ?body0 ...))
- ((do (?bindings0 ...) ?clause0 ?body0 ...)
- (letrec-syntax
- ((do-aux
- (... (syntax-rules ()
- ((do-aux () ((?name ?init ?step) ...) ?clause ?body ...)
- (letrec ((loop (lambda (?name ...)
- (cond ?clause
- (else
- (begin #t ?body ...)
- (loop ?step ...))))))
- (loop ?init ...)))
- ((do-aux ((?name ?init ?step) ?todo ...)
- (?bindings ...)
- ?clause
- ?body ...)
- (do-aux (?todo ...)
- (?bindings ... (?name ?init ?step))
- ?clause
- ?body ...))
- ((do-aux ((?name ?init) ?todo ...)
- (?bindings ...)
- ?clause
- ?body ...)
- (do-aux (?todo ...)
- (?bindings ... (?name ?init ?name))
- ?clause
- ?body ...))))))
- (do-aux (?bindings0 ...) () ?clause0 ?body0 ...)))))
- (define-syntax delay
- (syntax-rules ()
- ((delay ?e) (.make-promise (lambda () ?e)))))
- ; Another use of LETREC-SYNTAX and the escape extension.
- (define-syntax case
- (syntax-rules (else)
- ((case ?e1 (else ?body ?body2 ...))
- (begin ?e1 ?body ?body2 ...))
- ((case ?e1 (?z ?body ?body2 ...))
- (if (memv ?e1 '?z) (begin ?body ?body2 ...)))
- ((case ?e1 ?clause1 ?clause2 ?clause3 ...)
- (letrec-syntax
- ((case-aux
- (... (syntax-rules (else)
- ((case-aux ?temp (else ?body ?body2 ...))
- (begin ?body ?body2 ...))
- ((case-aux ?temp ((?z ...) ?body ?body2 ...))
- (if (memv ?temp '(?z ...)) (begin ?body ?body2 ...)))
- ((case-aux ?temp ((?z ...) ?body ?body2 ...) ?c1 ?c2 ...)
- (if (memv ?temp '(?z ...))
- (begin ?body ?body2 ...)
- (case-aux ?temp ?c1 ?c2 ...)))
- ; a popular extension
- ((case-aux ?temp (?z ?body ...) ?c1 ...)
- (case-aux ?temp ((?z) ?body ...) ?c1 ...))))))
- (let ((temp ?e1))
- (case-aux temp ?clause1 ?clause2 ?clause3 ...))))))
- ; A complete implementation of quasiquote, obtained by translating
- ; Jonathan Rees's implementation that was posted to RRRS-AUTHORS
- ; on 22 December 1986.
- ; Unfortunately, the use of LETREC scope means that it is vulnerable
- ; to top-level redefinitions of QUOTE etc. That could be fixed, but
- ; it has hair enough already.
- (begin
-
- (define-syntax .finalize-quasiquote letrec
- (syntax-rules (quote unquote unquote-splicing)
- ((.finalize-quasiquote quote ?arg ?return)
- (.interpret-continuation ?return (quote ?arg)))
- ((.finalize-quasiquote unquote ?arg ?return)
- (.interpret-continuation ?return ?arg))
- ((.finalize-quasiquote unquote-splicing ?arg ?return)
- (syntax-error ",@ in illegal context" ?arg))
- ((.finalize-quasiquote ?mode ?arg ?return)
- (.interpret-continuation ?return (?mode . ?arg)))))
-
- ; The first two "arguments" to .descend-quasiquote and to
- ; .descend-quasiquote-pair are always identical.
-
- (define-syntax .descend-quasiquote letrec
- (syntax-rules (quasiquote unquote unquote-splicing)
- ((.descend-quasiquote `?y ?x ?level ?return)
- (.descend-quasiquote-pair ?x ?x (?level) ?return))
- ((.descend-quasiquote ,?y ?x () ?return)
- (.interpret-continuation ?return unquote ?y))
- ((.descend-quasiquote ,?y ?x (?level) ?return)
- (.descend-quasiquote-pair ?x ?x ?level ?return))
- ((.descend-quasiquote ,@?y ?x () ?return)
- (.interpret-continuation ?return unquote-splicing ?y))
- ((.descend-quasiquote ,@?y ?x (?level) ?return)
- (.descend-quasiquote-pair ?x ?x ?level ?return))
- ((.descend-quasiquote (?y . ?z) ?x ?level ?return)
- (.descend-quasiquote-pair ?x ?x ?level ?return))
- ((.descend-quasiquote #(?y ...) ?x ?level ?return)
- (.descend-quasiquote-vector ?x ?x ?level ?return))
- ((.descend-quasiquote ?y ?x ?level ?return)
- (.interpret-continuation ?return quote ?x))))
-
- (define-syntax .descend-quasiquote-pair letrec
- (syntax-rules (quote unquote unquote-splicing)
- ((.descend-quasiquote-pair (?carx . ?cdrx) ?x ?level ?return)
- (.descend-quasiquote ?carx ?carx ?level (1 ?cdrx ?x ?level ?return)))))
-
- (define-syntax .descend-quasiquote-vector letrec
- (syntax-rules (quote)
- ((.descend-quasiquote-vector #(?y ...) ?x ?level ?return)
- (.descend-quasiquote (?y ...) (?y ...) ?level (6 ?x ?return)))))
-
- ; Representations for continuations used here.
- ; Continuation types 0, 1, 2, and 6 take a mode and an expression.
- ; Continuation types -1, 3, 4, 5, and 7 take just an expression.
- ;
- ; (-1)
- ; means no continuation
- ; (0)
- ; means to call .finalize-quasiquote with no further continuation
- ; (1 ?cdrx ?x ?level ?return)
- ; means a return from the call to .descend-quasiquote from
- ; .descend-quasiquote-pair
- ; (2 ?car-mode ?car-arg ?x ?return)
- ; means a return from the second call to .descend-quasiquote in
- ; in Jonathan's code for .descend-quasiquote-pair
- ; (3 ?car-arg ?return)
- ; means take the result and return an append of ?car-arg with it
- ; (4 ?cdr-mode ?cdr-arg ?return)
- ; means take the result and call .finalize-quasiquote on ?cdr-mode
- ; and ?cdr-arg with a continuation of type 5
- ; (5 ?car-result ?return)
- ; means take the result and return a cons of ?car-result onto it
- ; (6 ?x ?return)
- ; means a return from the call to .descend-quasiquote from
- ; .descend-quasiquote-vector
- ; (7 ?return)
- ; means take the result and return a call of list->vector on it
-
- (define-syntax .interpret-continuation letrec
- (syntax-rules (quote unquote unquote-splicing)
- ((.interpret-continuation (-1) ?e) ?e)
- ((.interpret-continuation (0) ?mode ?arg)
- (.finalize-quasiquote ?mode ?arg (-1)))
- ((.interpret-continuation (1 ?cdrx ?x ?level ?return) ?car-mode ?car-arg)
- (.descend-quasiquote ?cdrx
- ?cdrx
- ?level
- (2 ?car-mode ?car-arg ?x ?return)))
- ((.interpret-continuation (2 quote ?car-arg ?x ?return) quote ?cdr-arg)
- (.interpret-continuation ?return quote ?x))
- ((.interpret-continuation (2 unquote-splicing ?car-arg ?x ?return) quote ())
- (.interpret-continuation ?return unquote ?car-arg))
- ((.interpret-continuation (2 unquote-splicing ?car-arg ?x ?return)
- ?cdr-mode ?cdr-arg)
- (.finalize-quasiquote ?cdr-mode ?cdr-arg (3 ?car-arg ?return)))
- ((.interpret-continuation (2 ?car-mode ?car-arg ?x ?return)
- ?cdr-mode ?cdr-arg)
- (.finalize-quasiquote ?car-mode ?car-arg (4 ?cdr-mode ?cdr-arg ?return)))
-
- ((.interpret-continuation (3 ?car-arg ?return) ?e)
- (.interpret-continuation ?return append (?car-arg ?e)))
- ((.interpret-continuation (4 ?cdr-mode ?cdr-arg ?return) ?e1)
- (.finalize-quasiquote ?cdr-mode ?cdr-arg (5 ?e1 ?return)))
- ((.interpret-continuation (5 ?e1 ?return) ?e2)
- (.interpret-continuation ?return .cons (?e1 ?e2)))
- ((.interpret-continuation (6 ?x ?return) quote ?arg)
- (.interpret-continuation ?return quote ?x))
- ((.interpret-continuation (6 ?x ?return) ?mode ?arg)
- (.finalize-quasiquote ?mode ?arg (7 ?return)))
- ((.interpret-continuation (7 ?return) ?e)
- (.interpret-continuation ?return .list->vector (?e)))))
-
- (define-syntax quasiquote letrec
- (syntax-rules ()
- ((quasiquote ?x)
- (.descend-quasiquote ?x ?x () (0)))))
- )
- (define-syntax let*-syntax
- (syntax-rules ()
- ((let*-syntax () ?body)
- (let-syntax () ?body))
- ((let*-syntax ((?name1 ?val1) (?name ?val) ...) ?body)
- (let-syntax ((?name1 ?val1)) (let*-syntax ((?name ?val) ...) ?body)))))
- ))
- (define-syntax-scope 'letrec)
- (define standard-syntactic-environment
- (syntactic-copy global-syntactic-environment))
- (define (make-standard-syntactic-environment)
- (syntactic-copy standard-syntactic-environment))
- ; Copyright 1998 William Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 25 April 1999
- ;
- ; Given an expression in the subset of Scheme used as an intermediate language
- ; by Twobit, returns a newly allocated copy of the expression in which the
- ; local variables have been renamed and the referencing information has been
- ; recomputed.
- (define (copy-exp exp)
-
- (define special-names (cons name:IGNORED argument-registers))
-
- (define original-names (make-hashtable symbol-hash assq))
-
- (define renaming-counter 0)
-
- (define (rename-vars vars)
- (let ((rename (make-rename-procedure)))
- (map (lambda (var)
- (cond ((memq var special-names)
- var)
- ((hashtable-get original-names var)
- (rename var))
- (else
- (hashtable-put! original-names var #t)
- var)))
- vars)))
-
- (define (rename-formals formals newnames)
- (cond ((null? formals) '())
- ((symbol? formals) (car newnames))
- ((memq (car formals) special-names)
- (cons (car formals)
- (rename-formals (cdr formals)
- (cdr newnames))))
- (else (cons (car newnames)
- (rename-formals (cdr formals)
- (cdr newnames))))))
-
- ; Environments that map symbols to arbitrary information.
- ; This data type is mutable, and uses the shallow binding technique.
-
- (define (make-env) (make-hashtable symbol-hash assq))
-
- (define (env-bind! env sym info)
- (let ((stack (hashtable-get env sym)))
- (hashtable-put! env sym (cons info stack))))
-
- (define (env-unbind! env sym)
- (let ((stack (hashtable-get env sym)))
- (hashtable-put! env sym (cdr stack))))
-
- (define (env-lookup env sym default)
- (let ((stack (hashtable-get env sym)))
- (if stack
- (car stack)
- default)))
-
- (define (env-bind-multiple! env symbols infos)
- (for-each (lambda (sym info) (env-bind! env sym info))
- symbols
- infos))
-
- (define (env-unbind-multiple! env symbols)
- (for-each (lambda (sym) (env-unbind! env sym))
- symbols))
-
- ;
-
- (define (lexical-lookup R-table name)
- (assq name R-table))
-
- (define (copy exp env notepad R-table)
- (cond ((constant? exp) exp)
- ((lambda? exp)
- (let* ((bvl (make-null-terminated (lambda.args exp)))
- (newnames (rename-vars bvl))
- (procnames (map def.lhs (lambda.defs exp)))
- (newprocnames (rename-vars procnames))
- (refinfo (map (lambda (var)
- (make-R-entry var '() '() '()))
- (append newnames newprocnames)))
- (newexp
- (make-lambda
- (rename-formals (lambda.args exp) newnames)
- '()
- refinfo
- '()
- '()
- (lambda.decls exp)
- (lambda.doc exp)
- (lambda.body exp))))
- (env-bind-multiple! env procnames newprocnames)
- (env-bind-multiple! env bvl newnames)
- (for-each (lambda (entry)
- (env-bind! R-table (R-entry.name entry) entry))
- refinfo)
- (notepad-lambda-add! notepad newexp)
- (let ((newnotepad (make-notepad notepad)))
- (for-each (lambda (name rhs)
- (lambda.defs-set!
- newexp
- (cons (make-definition
- name
- (copy rhs env newnotepad R-table))
- (lambda.defs newexp))))
- (reverse newprocnames)
- (map def.rhs
- (reverse (lambda.defs exp))))
- (lambda.body-set!
- newexp
- (copy (lambda.body exp) env newnotepad R-table))
- (lambda.F-set! newexp (notepad-free-variables newnotepad))
- (lambda.G-set! newexp (notepad-captured-variables newnotepad)))
- (env-unbind-multiple! env procnames)
- (env-unbind-multiple! env bvl)
- (for-each (lambda (entry)
- (env-unbind! R-table (R-entry.name entry)))
- refinfo)
- newexp))
- ((assignment? exp)
- (let* ((oldname (assignment.lhs exp))
- (name (env-lookup env oldname oldname))
- (varinfo (env-lookup R-table name #f))
- (newexp
- (make-assignment name
- (copy (assignment.rhs exp) env notepad R-table))))
- (notepad-var-add! notepad name)
- (if varinfo
- (R-entry.assignments-set!
- varinfo
- (cons newexp (R-entry.assignments varinfo))))
- newexp))
- ((conditional? exp)
- (make-conditional (copy (if.test exp) env notepad R-table)
- (copy (if.then exp) env notepad R-table)
- (copy (if.else exp) env notepad R-table)))
- ((begin? exp)
- (make-begin (map (lambda (exp) (copy exp env notepad R-table))
- (begin.exprs exp))))
- ((variable? exp)
- (let* ((oldname (variable.name exp))
- (name (env-lookup env oldname oldname))
- (varinfo (env-lookup R-table name #f))
- (newexp (make-variable name)))
- (notepad-var-add! notepad name)
- (if varinfo
- (R-entry.references-set!
- varinfo
- (cons newexp (R-entry.references varinfo))))
- newexp))
- ((call? exp)
- (let ((newexp (make-call (copy (call.proc exp) env notepad R-table)
- (map (lambda (exp)
- (copy exp env notepad R-table))
- (call.args exp)))))
- (if (variable? (call.proc newexp))
- (let ((varinfo
- (env-lookup R-table
- (variable.name
- (call.proc newexp))
- #f)))
- (if varinfo
- (R-entry.calls-set!
- varinfo
- (cons newexp (R-entry.calls varinfo))))))
- (if (lambda? (call.proc newexp))
- (notepad-nonescaping-add! notepad (call.proc newexp)))
- newexp))
- (else ???)))
-
- (copy exp (make-env) (make-notepad #f) (make-env)))
- ; For debugging.
- ; Given an expression, traverses the expression to confirm
- ; that the referencing invariants are correct.
- (define (check-referencing-invariants exp . flags)
-
- (let ((check-free-variables? (memq 'free flags))
- (check-referencing? (memq 'reference flags))
- (first-violation? #t))
-
- ; env is the list of enclosing lambda expressions,
- ; beginning with the innermost.
-
- (define (check exp env)
- (cond ((constant? exp) (return exp #t))
- ((lambda? exp)
- (let ((env (cons exp env)))
- (return exp
- (and (every? (lambda (exp)
- (check exp env))
- (map def.rhs (lambda.defs exp)))
- (check (lambda.body exp) env)
- (if (and check-free-variables?
- (not (null? env)))
- (subset? (difference
- (lambda.F exp)
- (make-null-terminated
- (lambda.args exp)))
- (lambda.F (car env)))
- #t)
- (if check-referencing?
- (let ((env (cons exp env))
- (R (lambda.R exp)))
- (every? (lambda (formal)
- (or (ignored? formal)
- (R-entry R formal)))
- (make-null-terminated
- (lambda.args exp))))
- #t)))))
- ((variable? exp)
- (return exp
- (and (if (and check-free-variables?
- (not (null? env)))
- (memq (variable.name exp)
- (lambda.F (car env)))
- #t)
- (if check-referencing?
- (let ((Rinfo (lookup env (variable.name exp))))
- (if Rinfo
- (memq exp (R-entry.references Rinfo))
- #t))
- #t))))
- ((assignment? exp)
- (return exp
- (and (check (assignment.rhs exp) env)
- (if (and check-free-variables?
- (not (null? env)))
- (memq (assignment.lhs exp)
- (lambda.F (car env)))
- #t)
- (if check-referencing?
- (let ((Rinfo (lookup env (assignment.lhs exp))))
- (if Rinfo
- (memq exp (R-entry.assignments Rinfo))
- #t))
- #t))))
- ((conditional? exp)
- (return exp
- (and (check (if.test exp) env)
- (check (if.then exp) env)
- (check (if.else exp) env))))
- ((begin? exp)
- (return exp
- (every? (lambda (exp) (check exp env))
- (begin.exprs exp))))
- ((call? exp)
- (return exp
- (and (check (call.proc exp) env)
- (every? (lambda (exp) (check exp env))
- (call.args exp))
- (if (and check-referencing?
- (variable? (call.proc exp)))
- (let ((Rinfo (lookup env
- (variable.name
- (call.proc exp)))))
- (if Rinfo
- (memq exp (R-entry.calls Rinfo))
- #t))
- #t))))
- (else ???)))
-
- (define (return exp flag)
- (cond (flag
- #t)
- (first-violation?
- (set! first-violation? #f)
- (display "Violation of referencing invariants")
- (newline)
- (pretty-print (make-readable exp))
- #f)
- (else (pretty-print (make-readable exp))
- #f)))
-
- (define (lookup env I)
- (if (null? env)
- #f
- (let ((Rinfo (R-entry (lambda.R (car env)) I)))
- (or Rinfo
- (lookup (cdr env) I)))))
-
- (if (null? flags)
- (begin (set! check-free-variables? #t)
- (set! check-referencing? #t)))
-
- (check exp '())))
- ; Calculating the free variable information for an expression
- ; as output by pass 2. This should be faster than computing both
- ; the free variables and the referencing information.
- (define (compute-free-variables! exp)
-
- (define empty-set (make-set '()))
-
- (define (singleton x) (list x))
-
- (define (union2 x y) (union x y))
- (define (union3 x y z) (union x y z))
-
- (define (set->list set) set)
-
- (define (free exp)
- (cond ((constant? exp) empty-set)
- ((lambda? exp)
- (let* ((defs (lambda.defs exp))
- (formals (make-set
- (make-null-terminated (lambda.args exp))))
- (defined (make-set (map def.lhs defs)))
- (Fdefs
- (apply-union
- (map (lambda (def)
- (free (def.rhs def)))
- defs)))
- (Fbody (free (lambda.body exp)))
- (F (union2 Fdefs Fbody)))
- (lambda.F-set! exp (set->list F))
- (lambda.G-set! exp (set->list F))
- (difference F (union2 formals defined))))
- ((assignment? exp)
- (union2 (make-set (list (assignment.lhs exp)))
- (free (assignment.rhs exp))))
- ((conditional? exp)
- (union3 (free (if.test exp))
- (free (if.then exp))
- (free (if.else exp))))
- ((begin? exp)
- (apply-union
- (map (lambda (exp) (free exp))
- (begin.exprs exp))))
- ((variable? exp)
- (singleton (variable.name exp)))
- ((call? exp)
- (union2 (free (call.proc exp))
- (apply-union
- (map (lambda (exp) (free exp))
- (call.args exp)))))
- (else ???)))
-
- (free exp))
- ; As above, but representing sets as hashtrees.
- ; This is commented out because it is much slower than the implementation
- ; above. Because the set of free variables is represented as a list
- ; within a lambda expression, this implementation must convert the
- ; representation for every lambda expression, which is quite expensive
- ; for A-normal form.
- (begin
- '
- (define (compute-free-variables! exp)
-
- (define empty-set (make-hashtree symbol-hash assq))
-
- (define (singleton x)
- (hashtree-put empty-set x #t))
-
- (define (make-set values)
- (if (null? values)
- empty-set
- (hashtree-put (make-set (cdr values))
- (car values)
- #t)))
-
- (define (union2 x y)
- (hashtree-for-each (lambda (key val)
- (set! x (hashtree-put x key #t)))
- y)
- x)
-
- (define (union3 x y z)
- (union2 (union2 x y) z))
-
- (define (apply-union sets)
- (cond ((null? sets)
- (make-set '()))
- ((null? (cdr sets))
- (car sets))
- (else
- (union2 (car sets)
- (apply-union (cdr sets))))))
-
- (define (difference x y)
- (hashtree-for-each (lambda (key val)
- (set! x (hashtree-remove x key)))
- y)
- x)
-
- (define (set->list set)
- (hashtree-map (lambda (sym val) sym) set))
-
- (define (free exp)
- (cond ((constant? exp) empty-set)
- ((lambda? exp)
- (let* ((defs (lambda.defs exp))
- (formals (make-set
- (make-null-terminated (lambda.args exp))))
- (defined (make-set (map def.lhs defs)))
- (Fdefs
- (apply-union
- (map (lambda (def)
- (free (def.rhs def)))
- defs)))
- (Fbody (free (lambda.body exp)))
- (F (union2 Fdefs Fbody)))
- (lambda.F-set! exp (set->list F))
- (lambda.G-set! exp (set->list F))
- (difference F (union2 formals defined))))
- ((assignment? exp)
- (union2 (make-set (list (assignment.lhs exp)))
- (free (assignment.rhs exp))))
- ((conditional? exp)
- (union3 (free (if.test exp))
- (free (if.then exp))
- (free (if.else exp))))
- ((begin? exp)
- (apply-union
- (map (lambda (exp) (free exp))
- (begin.exprs exp))))
- ((variable? exp)
- (singleton (variable.name exp)))
- ((call? exp)
- (union2 (free (call.proc exp))
- (apply-union
- (map (lambda (exp) (free exp))
- (call.args exp)))))
- (else ???)))
-
- (hashtree-map (lambda (sym val) sym)
- (free exp)))
- #t); Copyright 1991 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 24 April 1999
- ;
- ; First pass of the Twobit compiler:
- ; macro expansion, syntax checking, alpha conversion,
- ; preliminary annotation.
- ;
- ; The input to this pass is a Scheme definition or expression.
- ; The output is an expression in the subset of Scheme described
- ; by the following grammar, where the output satisfies certain
- ; additional invariants described below.
- ;
- ; "X ..." means zero or more occurrences of X.
- ;
- ; L --> (lambda (I_1 ...)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>)
- ; E)
- ; | (lambda (I_1 ... . I_rest)
- ; (begin D ...)
- ; (quote (R F <decls> <doc>))
- ; E)
- ; D --> (define I L)
- ; E --> (quote K) ; constants
- ; | (begin I) ; variable references
- ; | L ; lambda expressions
- ; | (E0 E1 ...) ; calls
- ; | (set! I E) ; assignments
- ; | (if E0 E1 E2) ; conditionals
- ; | (begin E0 E1 E2 ...) ; sequential expressions
- ; I --> <identifier>
- ;
- ; R --> ((I <references> <assignments> <calls>) ...)
- ; F --> (I ...)
- ; G --> (I ...)
- ;
- ; Invariants that hold for the output:
- ; * There are no internal definitions.
- ; * No identifier containing an upper case letter is bound anywhere.
- ; (Change the "name:..." variables if upper case is preferred.)
- ; * No identifier is bound in more than one place.
- ; * Each R contains one entry for every identifier bound in the
- ; formal argument list and the internal definition list that
- ; precede it. Each entry contains a list of pointers to all
- ; references to the identifier, a list of pointers to all
- ; assignments to the identifier, and a list of pointers to all
- ; calls to the identifier.
- ; * Except for constants, the expression does not share structure
- ; with the original input or itself, except that the references
- ; and assignments in R are guaranteed to share structure with
- ; the expression. Thus the expression may be side effected, and
- ; side effects to references or assignments obtained through R
- ; are guaranteed to change the references or assignments pointed
- ; to by R.
- ; * F and G are garbage.
- ($$trace "pass1")
- (define source-file-name #f)
- (define source-file-position #f)
- (define pass1-block-compiling? #f)
- (define pass1-block-assignments '())
- (define pass1-block-inlines '())
- (define (pass1 def-or-exp . rest)
- (set! source-file-name #f)
- (set! source-file-position #f)
- (set! pass1-block-compiling? #f)
- (set! pass1-block-assignments '())
- (set! pass1-block-inlines '())
- (if (not (null? rest))
- (begin (set! source-file-name (car rest))
- (if (not (null? (cdr rest)))
- (set! source-file-position (cadr rest)))))
- (set! renaming-counter 0)
- (macro-expand def-or-exp))
- ; Compiles a whole sequence of top-level forms on the assumption
- ; that no variable that is defined by a form in the sequence is
- ; ever defined or assigned outside of the sequence.
- ;
- ; This is a crock in three parts:
- ;
- ; 1. Macro-expand each form and record assignments.
- ; 2. Find the top-level variables that are defined but not
- ; assigned, give them local names, generate a DEFINE-INLINE
- ; for each of the top-level procedures, and macro-expand
- ; each form again.
- ; 3. Wrap the whole mess in an appropriate LET and recompute
- ; the referencing information by copying it.
- ;
- ; Note that macros get expanded twice, and that all DEFINE-SYNTAX
- ; macros are considered local to the forms.
- ; FIXME: Need to turn off warning messages.
- (define (pass1-block forms . rest)
-
- (define (part1)
- (set! pass1-block-compiling? #t)
- (set! pass1-block-assignments '())
- (set! pass1-block-inlines '())
- (set! renaming-counter 0)
- (let ((env0 (syntactic-copy global-syntactic-environment))
- (bmode (benchmark-mode))
- (wmode (issue-warnings))
- (defined '()))
- (define (make-toplevel-definition id exp)
- (cond ((memq id defined)
- (set! pass1-block-assignments
- (cons id pass1-block-assignments)))
- ((or (constant? exp)
- (and (lambda? exp)
- (list? (lambda.args exp))))
- (set! defined (cons id defined))))
- (make-begin
- (list (make-assignment id exp)
- (make-constant id))))
- (benchmark-mode #f)
- (issue-warnings #f)
- (for-each (lambda (form)
- (desugar-definitions form
- global-syntactic-environment
- make-toplevel-definition))
- forms)
- (set! global-syntactic-environment env0)
- (benchmark-mode bmode)
- (issue-warnings wmode)
- (part2 (filter (lambda (id)
- (not (memq id pass1-block-assignments)))
- (reverse defined)))))
-
- (define (part2 defined)
- (set! pass1-block-compiling? #f)
- (set! pass1-block-assignments '())
- (set! pass1-block-inlines '())
- (set! renaming-counter 0)
- (let* ((rename (make-rename-procedure))
- (alist (map (lambda (id)
- (cons id (rename id)))
- defined))
- (definitions0 '()) ; for constants
- (definitions1 '())) ; for lambda expressions
- (define (make-toplevel-definition id exp)
- (if (lambda? exp)
- (doc.name-set! (lambda.doc exp) id))
- (let ((probe (assq id alist)))
- (if probe
- (let ((id1 (cdr probe)))
- (cond ((constant? exp)
- (set! definitions0
- (cons (make-assignment id exp)
- definitions0))
- (make-constant id))
- ((lambda? exp)
- (set! definitions1
- (cons (make-assignment id1 exp)
- definitions1))
- (make-assignment
- id
- (make-lambda (lambda.args exp)
- '() ; no definitions
- '() ; R
- '() ; F
- '() ; G
- '() ; decls
- (lambda.doc exp)
- (make-call
- (make-variable id1)
- (map make-variable
- (lambda.args exp))))))
- (else
- (m-error "Inconsistent macro expansion"
- (make-readable exp)))))
- (make-assignment id exp))))
- (let ((env0 (syntactic-copy global-syntactic-environment))
- (bmode (benchmark-mode))
- (wmode (issue-warnings)))
- (issue-warnings #f)
- (for-each (lambda (pair)
- (let ((id0 (car pair))
- (id1 (cdr pair)))
- (syntactic-bind-globally!
- id0
- (make-inline-denotation
- id0
- (lambda (exp rename compare)
- ; Deliberately non-hygienic!
- (cons id1 (cdr exp)))
- global-syntactic-environment))
- (set! pass1-block-inlines
- (cons id0 pass1-block-inlines))))
- alist)
- (benchmark-mode #f)
- (issue-warnings wmode)
- (let ((forms
- (do ((forms forms (cdr forms))
- (newforms '()
- (cons (desugar-definitions
- (car forms)
- global-syntactic-environment
- make-toplevel-definition)
- newforms)))
- ((null? forms)
- (reverse newforms)))))
- (benchmark-mode bmode)
- (set! global-syntactic-environment env0)
- (part3 alist definitions0 definitions1 forms)))))
-
- (define (part3 alist definitions0 definitions1 forms)
- (set! pass1-block-compiling? #f)
- (set! pass1-block-assignments '())
- (set! pass1-block-inlines '())
- (let* ((constnames0 (map assignment.lhs definitions0))
- (constnames1 (map (lambda (id0)
- (cdr (assq id0 alist)))
- constnames0))
- (procnames1 (map assignment.lhs definitions1)))
- (copy-exp
- (make-call
- (make-lambda
- constnames1
- '() ; no definitions
- '() ; R
- '() ; F
- '() ; G
- '() ; decls
- #f ; doc
- (make-begin
- (list
- (make-begin
- (cons (make-constant #f)
- (reverse
- (map (lambda (id)
- (make-assignment id (make-variable (cdr (assq id alist)))))
- constnames0))))
- (make-call
- (make-lambda
- constnames0
- '() ; no definitions
- '() ; R
- '() ; F
- '() ; G
- '() ; decls
- #f ; doc
- (make-call
- (make-lambda
- (map assignment.lhs definitions1)
- '() ; no definitions
- '() ; R
- '() ; F
- '() ; G
- '() ; decls
- #f ; doc
- (make-begin (cons (make-constant #f)
- (append definitions1 forms))))
- (map (lambda (ignored) (make-unspecified))
- definitions1)))
- (map make-variable constnames1))
- )))
- (map assignment.rhs definitions0)))))
-
- (set! source-file-name #f)
- (set! source-file-position #f)
- (if (not (null? rest))
- (begin (set! source-file-name (car rest))
- (if (not (null? (cdr rest)))
- (set! source-file-position (cadr rest)))))
- (part1))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 7 June 1999.
- ;
- ; Support for intraprocedural value numbering:
- ; set of available expressions
- ; miscellaneous
- ;
- ; The set of available expressions is represented as a
- ; mutable abstract data type Available with these operations:
- ;
- ; make-available-table: -> Available
- ; copy-available-table: Available -> Available
- ; available-expression: Available x Expr -> (symbol + {#f})
- ; available-variable: Available x symbol -> Expr
- ; available-extend!: Available x symbol x Expr x Killer ->
- ; available-kill!: Available x Killer ->
- ;
- ; where Expr is of the form
- ;
- ; Expr --> W
- ; | (W_0 W_1 ...)
- ;
- ; W --> (quote K)
- ; | (begin I)
- ;
- ; and Killer is a fixnum, as defined later in this file.
- ;
- ; (make-available-table)
- ; returns an empty table of available expressions.
- ; (copy-available-table available)
- ; copies the given table.
- ; (available-expression available E)
- ; returns the name of E if it is available in the table, else #f.
- ; (available-variable available T)
- ; returns a constant or variable to use in place of T, else #f.
- ; (available-extend! available T E K)
- ; adds the binding (T E) to the table, with Killer K.
- ; If E is a variable and this binding is never killed, then copy
- ; propagation will replace uses of T by uses of E; otherwise
- ; commoning will replace uses of E by uses of T, until the
- ; binding is killed.
- ; (available-kill! available K)
- ; removes all bindings whose Killer intersects K.
- ;
- ; (available-extend! available T E K) is very fast if the previous
- ; operation on the table was (available-expression available E).
- ; Implementation.
- ;
- ; Quick and dirty.
- ; The available expressions are represented as a vector of 2 association
- ; lists. The first list is used for common subexpression elimination,
- ; and the second is used for copy and constant propagation.
- ;
- ; Each element of the first list is a binding of
- ; a symbol T to an expression E, with killer K,
- ; represented by the list (E T K).
- ;
- ; Each element of the second list is a binding of
- ; a symbol T to an expression E, with killer K,
- ; represented by the list (T E K).
- ; The expression E will be a constant or variable.
- (define (make-available-table)
- (vector '() '()))
- (define (copy-available-table available)
- (vector (vector-ref available 0)
- (vector-ref available 1)))
- (define (available-expression available E)
- (let ((binding (assoc E (vector-ref available 0))))
- (if binding
- (cadr binding)
- #f)))
- (define (available-variable available T)
- (let ((binding (assq T (vector-ref available 1))))
- (if binding
- (cadr binding)
- #f)))
- (define (available-extend! available T E K)
- (cond ((constant? E)
- (vector-set! available
- 1
- (cons (list T E K)
- (vector-ref available 1))))
- ((and (variable? E)
- (eq? K available:killer:none))
- (vector-set! available
- 1
- (cons (list T E K)
- (vector-ref available 1))))
- (else
- (vector-set! available
- 0
- (cons (list E T K)
- (vector-ref available 0))))))
- (define (available-kill! available K)
- (vector-set! available
- 0
- (filter (lambda (binding)
- (zero?
- (logand K
- (caddr binding))))
- (vector-ref available 0)))
- (vector-set! available
- 1
- (filter (lambda (binding)
- (zero?
- (logand K
- (caddr binding))))
- (vector-ref available 1))))
- (define (available-intersect! available0 available1 available2)
- (vector-set! available0
- 0
- (intersection (vector-ref available1 0)
- (vector-ref available2 0)))
- (vector-set! available0
- 1
- (intersection (vector-ref available1 1)
- (vector-ref available2 1))))
- ; The Killer concrete data type, represented as a fixnum.
- ;
- ; The set of side effects that can kill an available expression
- ; are a subset of
- ;
- ; assignments to global variables
- ; uses of SET-CAR!
- ; uses of SET-CDR!
- ; uses of STRING-SET!
- ; uses of VECTOR-SET!
- ;
- ; This list is not complete. If we were trying to perform common
- ; subexpression elimination on calls to PEEK-CHAR, for example,
- ; then those calls would be killed by reads.
- (define available:killer:globals 2)
- (define available:killer:car 4)
- (define available:killer:cdr 8)
- (define available:killer:string 16) ; also bytevectors etc
- (define available:killer:vector 32) ; also structures etc
- (define available:killer:cell 64)
- (define available:killer:io 128)
- (define available:killer:none 0) ; none of the above
- (define available:killer:all 1022) ; all of the above
- (define available:killer:immortal 0) ; never killed
- (define available:killer:dead 1023) ; never available
- (define (available:killer-combine k1 k2)
- (logior k1 k2))
- ; Miscellaneous.
- ; A simple lambda expression has no internal definitions at its head
- ; and no declarations aside from A-normal form.
- (define (simple-lambda? L)
- (and (null? (lambda.defs L))
- (every? (lambda (decl)
- (eq? decl A-normal-form-declaration))
- (lambda.decls L))))
- ; A real call is a call whose procedure expression is
- ; neither a lambda expression nor a primop.
- (define (real-call? E)
- (and (call? E)
- (let ((proc (call.proc E)))
- (and (not (lambda? proc))
- (or (not (variable? proc))
- (let ((f (variable.name proc)))
- (or (not (integrate-usual-procedures))
- (not (prim-entry f)))))))))
- (define (prim-call E)
- (and (call? E)
- (let ((proc (call.proc E)))
- (and (variable? proc)
- (integrate-usual-procedures)
- (prim-entry (variable.name proc))))))
- (define (no-side-effects? E)
- (or (constant? E)
- (variable? E)
- (lambda? E)
- (and (conditional? E)
- (no-side-effects? (if.test E))
- (no-side-effects? (if.then E))
- (no-side-effects? (if.else E)))
- (and (call? E)
- (let ((proc (call.proc E)))
- (and (variable? proc)
- (integrate-usual-procedures)
- (let ((entry (prim-entry (variable.name proc))))
- (and entry
- (not (eq? available:killer:dead
- (prim-lives-until entry))))))))))
- ; Given a local variable, the expression within its scope, and
- ; a list of local variables that are known to be used only once,
- ; returns #t if the variable is used only once.
- ;
- ; The purpose of this routine is to recognize temporaries that
- ; may once have had two or more uses because of CSE, but now have
- ; only one use because of further CSE followed by dead code elimination.
- (define (temporary-used-once? T E used-once)
- (cond ((call? E)
- (let ((proc (call.proc E))
- (args (call.args E)))
- (or (and (lambda? proc)
- (not (memq T (lambda.F proc)))
- (and (pair? args)
- (null? (cdr args))
- (temporary-used-once? T (car args) used-once)))
- (do ((exprs (cons proc (call.args E))
- (cdr exprs))
- (n 0
- (let ((exp (car exprs)))
- (cond ((constant? exp)
- n)
- ((variable? exp)
- (if (eq? T (variable.name exp))
- (+ n 1)
- n))
- (else
- ; Terminate the loop and return #f.
- 2)))))
- ((or (null? exprs)
- (> n 1))
- (= n 1))))))
- (else
- (memq T used-once))))
- ; Register bindings.
- (define (make-regbinding lhs rhs use)
- (list lhs rhs use))
- (define (regbinding.lhs x) (car x))
- (define (regbinding.rhs x) (cadr x))
- (define (regbinding.use x) (caddr x))
- ; Given a list of register bindings, an expression E and its free variables F,
- ; returns two values:
- ; E with the register bindings wrapped around it
- ; the free variables of the wrapped expression
- (define (wrap-with-register-bindings regbindings E F)
- (if (null? regbindings)
- (values E F)
- (let* ((regbinding (car regbindings))
- (R (regbinding.lhs regbinding))
- (x (regbinding.rhs regbinding)))
- (wrap-with-register-bindings
- (cdr regbindings)
- (make-call (make-lambda (list R)
- '()
- '()
- F
- F
- (list A-normal-form-declaration)
- #f
- E)
- (list (make-variable x)))
- (union (list x)
- (difference F (list R)))))))
- ; Returns two values:
- ; the subset of regbindings that have x as their right hand side
- ; the rest of regbindings
- (define (register-bindings regbindings x)
- (define (loop regbindings to-x others)
- (cond ((null? regbindings)
- (values to-x others))
- ((eq? x (regbinding.rhs (car regbindings)))
- (loop (cdr regbindings)
- (cons (car regbindings) to-x)
- others))
- (else
- (loop (cdr regbindings)
- to-x
- (cons (car regbindings) others)))))
- (loop regbindings '() '()))
- ; This procedure is called when the compiler can tell that an assertion
- ; is never true.
- (define (declaration-error E)
- (if (issue-warnings)
- (begin (display "WARNING: Assertion is false: ")
- (write (make-readable E #t))
- (newline))))
- ; Representations, which form a subtype hierarchy.
- ;
- ; <rep> ::= <fixnum> | (<fixnum> <datum> ...)
- ;
- ; (<rep> <datum> ...) is a subtype of <rep>, but the non-fixnum
- ; representations are otherwise interpreted by arbitrary code.
- (define *nreps* 0)
- (define *rep-encodings* '())
- (define *rep-decodings* '())
- (define *rep-subtypes* '())
- (define *rep-joins* (make-bytevector 0))
- (define *rep-meets* (make-bytevector 0))
- (define *rep-joins-special* '#())
- (define *rep-meets-special* '#())
- (define (representation-error msg . stuff)
- (apply error
- (if (string? msg)
- (string-append "Bug in flow analysis: " msg)
- msg)
- stuff))
- (define (symbol->rep sym)
- (let ((probe (assq sym *rep-encodings*)))
- (if probe
- (cdr probe)
- (let ((rep *nreps*))
- (set! *nreps* (+ *nreps* 1))
- (if (> *nreps* 255)
- (representation-error "Too many representation types"))
- (set! *rep-encodings*
- (cons (cons sym rep)
- *rep-encodings*))
- (set! *rep-decodings*
- (cons (cons rep sym)
- *rep-decodings*))
- rep))))
- (define (rep->symbol rep)
- (if (pair? rep)
- (cons (rep->symbol (car rep)) (cdr rep))
- (let ((probe (assv rep *rep-decodings*)))
- (if probe
- (cdr probe)
- 'unknown))))
- (define (representation-table table)
- (map (lambda (row)
- (map (lambda (x)
- (if (list? x)
- (map symbol->rep x)
- x))
- row))
- table))
- ; DEFINE-SUBTYPE is how representation types are defined.
- (define (define-subtype sym1 sym2)
- (let* ((rep2 (symbol->rep sym2))
- (rep1 (symbol->rep sym1)))
- (set! *rep-subtypes*
- (cons (cons rep1 rep2)
- *rep-subtypes*))
- sym1))
- ; COMPUTE-TYPE-STRUCTURE! must be called before DEFINE-INTERSECTION.
- (define (define-intersection sym1 sym2 sym3)
- (let ((rep1 (symbol->rep sym1))
- (rep2 (symbol->rep sym2))
- (rep3 (symbol->rep sym3)))
- (representation-aset! *rep-meets* rep1 rep2 rep3)
- (representation-aset! *rep-meets* rep2 rep1 rep3)))
- ;
- (define (representation-aref bv i j)
- (bytevector-ref bv (+ (* *nreps* i) j)))
- (define (representation-aset! bv i j x)
- (bytevector-set! bv (+ (* *nreps* i) j) x))
- (define (compute-unions!)
-
- ; Always define a bottom element.
-
- (for-each (lambda (sym)
- (define-subtype 'bottom sym))
- (map car *rep-encodings*))
-
- (let* ((debugging? #f)
- (n *nreps*)
- (n^2 (* n n))
- (matrix (make-bytevector n^2)))
-
- ; This code assumes there will always be a top element.
-
- (define (lub rep1 rep2 subtype?)
- (do ((i 0 (+ i 1))
- (bounds '()
- (if (and (subtype? rep1 i)
- (subtype? rep2 i))
- (cons i bounds)
- bounds)))
- ((= i n)
- (car (twobit-sort subtype? bounds)))))
-
- (define (join i j)
- (lub i j (lambda (rep1 rep2)
- (= 1 (representation-aref matrix rep1 rep2)))))
-
- (define (compute-transitive-closure!)
- (let ((changed? #f))
- (define (loop)
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((k 0 (+ k 1)))
- ((= k n))
- (do ((j 0 (+ j 1))
- (sum 0
- (logior sum
- (logand
- (representation-aref matrix i j)
- (representation-aref matrix j k)))))
- ((= j n)
- (if (> sum 0)
- (let ((x (representation-aref matrix i k)))
- (if (zero? x)
- (begin
- (set! changed? #t)
- (representation-aset! matrix i k 1)))))))))
- (if changed?
- (begin (set! changed? #f)
- (loop))))
- (loop)))
-
- (define (compute-joins!)
- (let ((default (lambda (x y)
- (error "Compiler bug: special meet or join" x y))))
- (set! *rep-joins-special* (make-vector n default))
- (set! *rep-meets-special* (make-vector n default)))
- (set! *rep-joins* (make-bytevector n^2))
- (set! *rep-meets* (make-bytevector n^2))
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((j 0 (+ j 1)))
- ((= j n))
- (representation-aset! *rep-joins*
- i
- j
- (join i j)))))
-
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((j 0 (+ j 1)))
- ((= j n))
- (representation-aset! matrix i j 0))
- (representation-aset! matrix i i 1))
- (for-each (lambda (subtype)
- (let ((rep1 (car subtype))
- (rep2 (cdr subtype)))
- (representation-aset! matrix rep1 rep2 1)))
- *rep-subtypes*)
- (compute-transitive-closure!)
- (if debugging?
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((j 0 (+ j 1)))
- ((= j n))
- (write-char #\space)
- (write (representation-aref matrix i j)))
- (newline)))
- (compute-joins!)
- (set! *rep-subtypes* '())))
- ; Intersections are not dual to unions because a conservative analysis
- ; must always err on the side of the larger subtype.
- ; COMPUTE-UNIONS! must be called before COMPUTE-INTERSECTIONS!.
- (define (compute-intersections!)
- (let ((n *nreps*))
-
- (define (meet i j)
- (let ((k (representation-union i j)))
- (if (= i k)
- j
- i)))
-
- (do ((i 0 (+ i 1)))
- ((= i n))
- (do ((j 0 (+ j 1)))
- ((= j n))
- (representation-aset! *rep-meets*
- i
- j
- (meet i j))))))
- (define (compute-type-structure!)
- (compute-unions!)
- (compute-intersections!))
- (define (representation-subtype? rep1 rep2)
- (equal? rep2 (representation-union rep1 rep2)))
- (define (representation-union rep1 rep2)
- (if (fixnum? rep1)
- (if (fixnum? rep2)
- (representation-aref *rep-joins* rep1 rep2)
- (representation-union rep1 (car rep2)))
- (if (fixnum? rep2)
- (representation-union (car rep1) rep2)
- (let ((r1 (car rep1))
- (r2 (car rep2)))
- (if (= r1 r2)
- ((vector-ref *rep-joins-special* r1) rep1 rep2)
- (representation-union r1 r2))))))
- (define (representation-intersection rep1 rep2)
- (if (fixnum? rep1)
- (if (fixnum? rep2)
- (representation-aref *rep-meets* rep1 rep2)
- (representation-intersection rep1 (car rep2)))
- (if (fixnum? rep2)
- (representation-intersection (car rep1) rep2)
- (let ((r1 (car rep1))
- (r2 (car rep2)))
- (if (= r1 r2)
- ((vector-ref *rep-meets-special* r1) rep1 rep2)
- (representation-intersection r1 r2))))))
- ; For debugging.
- (define (display-unions-and-intersections)
- (let* ((column-width 10)
- (columns/row (quotient 80 column-width)))
-
- (define (display-symbol sym)
- (let* ((s (symbol->string sym))
- (n (string-length s)))
- (if (< n column-width)
- (begin (display s)
- (display (make-string (- column-width n) #\space)))
- (begin (display (substring s 0 (- column-width 1)))
- (write-char #\space)))))
-
- ; Display columns i to n.
-
- (define (display-matrix f i n)
- (display (make-string column-width #\space))
- (do ((i i (+ i 1)))
- ((= i n))
- (display-symbol (rep->symbol i)))
- (newline)
- (newline)
- (do ((k 0 (+ k 1)))
- ((= k *nreps*))
- (display-symbol (rep->symbol k))
- (do ((i i (+ i 1)))
- ((= i n))
- (display-symbol (rep->symbol (f k i))))
- (newline))
- (newline)
- (newline))
-
- (display "Unions:")
- (newline)
- (newline)
-
- (do ((i 0 (+ i columns/row)))
- ((>= i *nreps*))
- (display-matrix representation-union
- i
- (min *nreps* (+ i columns/row))))
-
- (display "Intersections:")
- (newline)
- (newline)
-
- (do ((i 0 (+ i columns/row)))
- ((>= i *nreps*))
- (display-matrix representation-intersection
- i
- (min *nreps* (+ i columns/row))))))
- ; Operations that can be specialized.
- ;
- ; Format: (<name> (<arg-rep> ...) <specific-name>)
- (define (rep-specific? f rs)
- (rep-match f rs rep-specific caddr))
- ; Operations whose result has some specific representation.
- ;
- ; Format: (<name> (<arg-rep> ...) (<result-rep>))
- (define (rep-result? f rs)
- (rep-match f rs rep-result caaddr))
- ; Unary predicates that give information about representation.
- ;
- ; Format: (<name> <rep-if-true> <rep-if-false>)
- (define (rep-if-true f rs)
- (rep-match f rs rep-informing caddr))
- (define (rep-if-false f rs)
- (rep-match f rs rep-informing cadddr))
- ; Given the name of an integrable primitive,
- ; the representations of its arguments,
- ; a representation table, and a selector function
- ; finds the most type-specific row of the table that matches both
- ; the name of the primitive and the representations of its arguments,
- ; and returns the result of applying the selector to that row.
- ; If no row matches, then REP-MATCH returns #f.
- ;
- ; FIXME: This should be more efficient, and should prefer the most
- ; specific matches.
- (define (rep-match f rs table selector)
- (let ((n (length rs)))
- (let loop ((entries table))
- (cond ((null? entries)
- #f)
- ((eq? f (car (car entries)))
- (let ((rs0 (cadr (car entries))))
- (if (and (= n (length rs0))
- (every? (lambda (r1+r2)
- (let ((r1 (car r1+r2))
- (r2 (cdr r1+r2)))
- (representation-subtype? r1 r2)))
- (map cons rs rs0)))
- (selector (car entries))
- (loop (cdr entries)))))
- (else
- (loop (cdr entries)))))))
- ; Abstract interpretation with respect to types and constraints.
- ; Returns a representation type.
- (define (aeval E types constraints)
- (cond ((call? E)
- (let ((proc (call.proc E)))
- (if (variable? proc)
- (let* ((op (variable.name proc))
- (argtypes (map (lambda (E)
- (aeval E types constraints))
- (call.args E)))
- (type (rep-result? op argtypes)))
- (if type
- type
- rep:object))
- rep:object)))
- ((variable? E)
- (representation-typeof (variable.name E) types constraints))
- ((constant? E)
- (representation-of-value (constant.value E)))
- (else
- rep:object)))
- ; If x has representation type t0 in the hash table,
- ; and some further constraints
- ;
- ; x = (op y1 ... yn)
- ; x : t1
- ; ...
- ; x : tk
- ;
- ; then
- ;
- ; typeof (x) = op (typeof (y1), ..., typeof (yn))
- ; & t0 & t1 & ... & tk
- ;
- ; where & means intersection and op is the abstraction of op.
- ;
- ; Also if T : true and T = E then E may give information about
- ; the types of other variables. Similarly for T : false.
- (define (representation-typeof name types constraints)
- (let ((t0 (hashtable-fetch types name rep:object))
- (cs (hashtable-fetch (constraints.table constraints) name '())))
- (define (loop type cs)
- (if (null? cs)
- type
- (let* ((c (car cs))
- (cs (cdr cs))
- (E (constraint.rhs c)))
- (cond ((constant? E)
- (loop (representation-intersection type
- (constant.value E))
- cs))
- ((call? E)
- (loop (representation-intersection
- type (aeval E types constraints))
- cs))
- (else
- (loop type cs))))))
- (loop t0 cs)))
- ; Constraints.
- ;
- ; The constraints used by this analysis consist of type constraints
- ; together with the available expressions used for commoning.
- ;
- ; (T E K) T = E until killed by an effect in K
- ; (T '<rep> K) T : <rep> until killed by an effect in K
- (define (make-constraint T E K)
- (list T E K))
- (define (constraint.lhs c)
- (car c))
- (define (constraint.rhs c)
- (cadr c))
- (define (constraint.killer c)
- (caddr c))
- (define (make-type-constraint T type K)
- (make-constraint T
- (make-constant type)
- K))
- ; If the new constraint is of the form T = E until killed by K,
- ; then there shouldn't be any prior constraints.
- ;
- ; Otherwise the new constraint is of the form T : t until killed by K.
- ; Suppose the prior constraints are
- ; T = E until killed by K
- ; T : t1 until killed by K1
- ; ...
- ; T : tn until killed by Kn
- ;
- ; If there exists i such that ti is a subtype of t and Ki a subset of K,
- ; then the new constraint adds no new information and should be ignored.
- ; Otherwise compute t' = t1 & ... & tn and K' = K1 | ... | Kn, where
- ; & indicates intersection and | indicates union.
- ; If K = K' then add the new constraint T : t' until killed by K;
- ; otherwise add two new constraints:
- ; T : t' until killed by K'
- ; T : t until killed by K
- (define (constraints-add! types constraints new)
- (let* ((debugging? #f)
- (T (constraint.lhs new))
- (E (constraint.rhs new))
- (K (constraint.killer new))
- (cs (constraints-for-variable constraints T)))
-
- (define (loop type K cs newcs)
- (if (null? cs)
- (cons (make-type-constraint T type K) newcs)
- (let* ((c2 (car cs))
- (cs (cdr cs))
- (E2 (constraint.rhs c2))
- (K2 (constraint.killer c2)))
- (if (constant? E2)
- (let* ((type2 (constant.value E2))
- (type3 (representation-intersection type type2)))
- (cond ((eq? type2 type3)
- (if (= K2 (logand K K2))
- (append newcs cs)
- (loop (representation-intersection type type2)
- (available:killer-combine K K2)
- cs
- (cons c2 newcs))))
- ((representation-subtype? type type3)
- (if (= K (logand K K2))
- (loop type K cs newcs)
- (loop type K cs (cons c2 newcs))))
- (else
- (loop type3
- (available:killer-combine K K2)
- cs
- (cons c2 newcs)))))
- (let* ((op (variable.name (call.proc E2)))
- (args (call.args E2))
- (argtypes (map (lambda (exp)
- (aeval exp types constraints))
- args)))
- (cond ((representation-subtype? type rep:true)
- (let ((reps (rep-if-true op argtypes)))
- (if reps
- (record-new-reps! args argtypes reps K2))))
- ((representation-subtype? type rep:false)
- (let ((reps (rep-if-false op argtypes)))
- (if reps
- (record-new-reps! args argtypes reps K2)))))
- (loop type K cs (cons c2 newcs)))))))
-
- (define (record-new-reps! args argtypes reps K2)
- (if debugging?
- (begin (write (list (map make-readable args)
- (map rep->symbol argtypes)
- (map rep->symbol reps)))
- (newline)))
- (for-each (lambda (arg type0 type1)
- (if (not (representation-subtype? type0 type1))
- (if (variable? arg)
- (let ((name (variable.name arg)))
- ; FIXME: In this context, a variable
- ; should always be local so the hashtable
- ; operation isn't necessary.
- (if (hashtable-get types name)
- (constraints-add!
- types
- constraints
- (make-type-constraint
- name
- type1
- (available:killer-combine K K2)))
- (cerror
- "Compiler bug: unexpected global: "
- name))))))
- args argtypes reps))
-
- (if (not (zero? K))
- (constraints-add-killedby! constraints T K))
-
- (let* ((table (constraints.table constraints))
- (cs (hashtable-fetch table T '())))
- (cond ((constant? E)
- ; It's a type constraint.
- (let ((type (constant.value E)))
- (if debugging?
- (begin (display T)
- (display " : ")
- (display (rep->symbol type))
- (newline)))
- (let ((cs (loop type K cs '())))
- (hashtable-put! table T cs)
- constraints)))
- (else
- (if debugging?
- (begin (display T)
- (display " = ")
- (display (make-readable E #t))
- (newline)))
- (if (not (null? cs))
- (begin
- (display "Compiler bug: ")
- (write T)
- (display " has unexpectedly nonempty constraints")
- (newline)))
- (hashtable-put! table T (list (list T E K)))
- constraints)))))
- ; Sets of constraints.
- ;
- ; The set of constraints is represented as (<hashtable> <killedby>),
- ; where <hashtable> is a hashtable mapping variables to lists of
- ; constraints as above, and <killedby> is a vector mapping basic killers
- ; to lists of variables that need to be examined for constraints that
- ; are killed by that basic killer.
- (define number-of-basic-killers
- (do ((i 0 (+ i 1))
- (k 1 (+ k k)))
- ((> k available:killer:dead)
- i)))
- (define (constraints.table constraints) (car constraints))
- (define (constraints.killed constraints) (cadr constraints))
- (define (make-constraints-table)
- (list (make-hashtable symbol-hash assq)
- (make-vector number-of-basic-killers '())))
- (define (copy-constraints-table constraints)
- (list (hashtable-copy (constraints.table constraints))
- (list->vector (vector->list (constraints.killed constraints)))))
- (define (constraints-for-variable constraints T)
- (hashtable-fetch (constraints.table constraints) T '()))
- (define (constraints-add-killedby! constraints T K0)
- (if (not (zero? K0))
- (let ((v (constraints.killed constraints)))
- (do ((i 0 (+ i 1))
- (k 1 (+ k k)))
- ((= i number-of-basic-killers))
- (if (not (zero? (logand k K0)))
- (vector-set! v i (cons T (vector-ref v i))))))))
- (define (constraints-kill! constraints K)
- (if (not (zero? K))
- (let ((table (constraints.table constraints))
- (killed (constraints.killed constraints)))
- (define (examine! T)
- (let ((cs (filter (lambda (c)
- (zero? (logand (constraint.killer c) K)))
- (hashtable-fetch table T '()))))
- (if (null? cs)
- (hashtable-remove! table T)
- (hashtable-put! table T cs))))
- (do ((i 0 (+ i 1))
- (j 1 (+ j j)))
- ((= i number-of-basic-killers))
- (if (not (zero? (logand j K)))
- (begin (for-each examine! (vector-ref killed i))
- (vector-set! killed i '())))))))
- (define (constraints-intersect! constraints0 constraints1 constraints2)
- (let ((table0 (constraints.table constraints0))
- (table1 (constraints.table constraints1))
- (table2 (constraints.table constraints2)))
- (if (eq? table0 table1)
- ; FIXME: Which is more efficient: to update the killed vector,
- ; or not to update it? Both are safe.
- (hashtable-for-each (lambda (T cs)
- (if (not (null? cs))
- (hashtable-put!
- table0
- T
- (cs-intersect
- (hashtable-fetch table2 T '())
- cs))))
- table1)
- ; This case shouldn't ever happen, so it can be slow.
- (begin
- (constraints-intersect! constraints0 constraints0 constraints1)
- (constraints-intersect! constraints0 constraints0 constraints2)))))
- (define (cs-intersect cs1 cs2)
- (define (loop cs init rep Krep)
- (if (null? cs)
- (values init rep Krep)
- (let* ((c (car cs))
- (cs (cdr cs))
- (E2 (constraint.rhs c))
- (K2 (constraint.killer c)))
- (cond ((constant? E2)
- (loop cs
- init
- (representation-intersection rep (constant.value E2))
- (available:killer-combine Krep K2)))
- ((call? E2)
- (if init
- (begin (display "Compiler bug in cs-intersect")
- (break))
- (loop cs c rep Krep)))
- (else
- (error "Compiler bug in cs-intersect"))))))
- (call-with-values
- (lambda ()
- (loop cs1 #f rep:object available:killer:none))
- (lambda (c1 rep1 Krep1)
- (call-with-values
- (lambda ()
- (loop cs2 #f rep:object available:killer:none))
- (lambda (c2 rep2 Krep2)
- (let ((c (if (equal? c1 c2) c1 #f))
- (rep (representation-union rep1 rep2))
- (Krep (available:killer-combine Krep1 Krep2)))
- (if (eq? rep rep:object)
- (if c (list c) '())
- (let ((T (constraint.lhs (car cs1))))
- (if c
- (list c (make-type-constraint T rep Krep))
- (list (make-type-constraint T rep Krep)))))))))))
- ; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
- (define $gc.ephemeral 0)
- (define $gc.tenuring 1)
- (define $gc.full 2)
- (define $mstat.wallocated-hi 0)
- (define $mstat.wallocated-lo 1)
- (define $mstat.wcollected-hi 2)
- (define $mstat.wcollected-lo 3)
- (define $mstat.wcopied-hi 4)
- (define $mstat.wcopied-lo 5)
- (define $mstat.gctime 6)
- (define $mstat.wlive 7)
- (define $mstat.gc-last-gen 8)
- (define $mstat.gc-last-type 9)
- (define $mstat.generations 10)
- (define $mstat.g-gc-count 0)
- (define $mstat.g-prom-count 1)
- (define $mstat.g-gctime 2)
- (define $mstat.g-wlive 3)
- (define $mstat.g-np-youngp 4)
- (define $mstat.g-np-oldp 5)
- (define $mstat.g-np-j 6)
- (define $mstat.g-np-k 7)
- (define $mstat.g-alloc 8)
- (define $mstat.g-target 9)
- (define $mstat.g-promtime 10)
- (define $mstat.remsets 11)
- (define $mstat.r-apool 0)
- (define $mstat.r-upool 1)
- (define $mstat.r-ahash 2)
- (define $mstat.r-uhash 3)
- (define $mstat.r-hrec-hi 4)
- (define $mstat.r-hrec-lo 5)
- (define $mstat.r-hrem-hi 6)
- (define $mstat.r-hrem-lo 7)
- (define $mstat.r-hscan-hi 8)
- (define $mstat.r-hscan-lo 9)
- (define $mstat.r-wscan-hi 10)
- (define $mstat.r-wscan-lo 11)
- (define $mstat.r-ssbrec-hi 12)
- (define $mstat.r-ssbrec-lo 13)
- (define $mstat.r-np-p 14)
- (define $mstat.fflushed-hi 12)
- (define $mstat.fflushed-lo 13)
- (define $mstat.wflushed-hi 14)
- (define $mstat.wflushed-lo 15)
- (define $mstat.stk-created 16)
- (define $mstat.frestored-hi 17)
- (define $mstat.frestored-lo 18)
- (define $mstat.words-heap 19)
- (define $mstat.words-remset 20)
- (define $mstat.words-rts 21)
- (define $mstat.swb-assign 22)
- (define $mstat.swb-lhs-ok 23)
- (define $mstat.swb-rhs-const 24)
- (define $mstat.swb-not-xgen 25)
- (define $mstat.swb-trans 26)
- (define $mstat.rtime 27)
- (define $mstat.stime 28)
- (define $mstat.utime 29)
- (define $mstat.minfaults 30)
- (define $mstat.majfaults 31)
- (define $mstat.np-remsetp 32)
- (define $mstat.max-heap 33)
- (define $mstat.promtime 34)
- (define $mstat.wmoved-hi 35)
- (define $mstat.wmoved-lo 36)
- (define $mstat.vsize 37)
- (define $g.reg0 12)
- (define $r.reg8 44)
- (define $r.reg9 48)
- (define $r.reg10 52)
- (define $r.reg11 56)
- (define $r.reg12 60)
- (define $r.reg13 64)
- (define $r.reg14 68)
- (define $r.reg15 72)
- (define $r.reg16 76)
- (define $r.reg17 80)
- (define $r.reg18 84)
- (define $r.reg19 88)
- (define $r.reg20 92)
- (define $r.reg21 96)
- (define $r.reg22 100)
- (define $r.reg23 104)
- (define $r.reg24 108)
- (define $r.reg25 112)
- (define $r.reg26 116)
- (define $r.reg27 120)
- (define $r.reg28 124)
- (define $r.reg29 128)
- (define $r.reg30 132)
- (define $r.reg31 136)
- (define $g.stkbot 180)
- (define $g.gccnt 420)
- (define $m.alloc 1024)
- (define $m.alloci 1032)
- (define $m.gc 1040)
- (define $m.addtrans 1048)
- (define $m.stkoflow 1056)
- (define $m.stkuflow 1072)
- (define $m.creg 1080)
- (define $m.creg-set! 1088)
- (define $m.add 1096)
- (define $m.subtract 1104)
- (define $m.multiply 1112)
- (define $m.quotient 1120)
- (define $m.remainder 1128)
- (define $m.divide 1136)
- (define $m.modulo 1144)
- (define $m.negate 1152)
- (define $m.numeq 1160)
- (define $m.numlt 1168)
- (define $m.numle 1176)
- (define $m.numgt 1184)
- (define $m.numge 1192)
- (define $m.zerop 1200)
- (define $m.complexp 1208)
- (define $m.realp 1216)
- (define $m.rationalp 1224)
- (define $m.integerp 1232)
- (define $m.exactp 1240)
- (define $m.inexactp 1248)
- (define $m.exact->inexact 1256)
- (define $m.inexact->exact 1264)
- (define $m.make-rectangular 1272)
- (define $m.real-part 1280)
- (define $m.imag-part 1288)
- (define $m.sqrt 1296)
- (define $m.round 1304)
- (define $m.truncate 1312)
- (define $m.apply 1320)
- (define $m.varargs 1328)
- (define $m.typetag 1336)
- (define $m.typetag-set 1344)
- (define $m.break 1352)
- (define $m.eqv 1360)
- (define $m.partial-list->vector 1368)
- (define $m.timer-exception 1376)
- (define $m.exception 1384)
- (define $m.singlestep 1392)
- (define $m.syscall 1400)
- (define $m.bvlcmp 1408)
- (define $m.enable-interrupts 1416)
- (define $m.disable-interrupts 1424)
- (define $m.alloc-bv 1432)
- (define $m.global-ex 1440)
- (define $m.invoke-ex 1448)
- (define $m.global-invoke-ex 1456)
- (define $m.argc-ex 1464)
- ; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
- (define $r.g0 0)
- (define $r.g1 1)
- (define $r.g2 2)
- (define $r.g3 3)
- (define $r.g4 4)
- (define $r.g5 5)
- (define $r.g6 6)
- (define $r.g7 7)
- (define $r.o0 8)
- (define $r.o1 9)
- (define $r.o2 10)
- (define $r.o3 11)
- (define $r.o4 12)
- (define $r.o5 13)
- (define $r.o6 14)
- (define $r.o7 15)
- (define $r.l0 16)
- (define $r.l1 17)
- (define $r.l2 18)
- (define $r.l3 19)
- (define $r.l4 20)
- (define $r.l5 21)
- (define $r.l6 22)
- (define $r.l7 23)
- (define $r.i0 24)
- (define $r.i1 25)
- (define $r.i2 26)
- (define $r.i3 27)
- (define $r.i4 28)
- (define $r.i5 29)
- (define $r.i6 30)
- (define $r.i7 31)
- (define $r.result $r.o0)
- (define $r.argreg2 $r.o1)
- (define $r.argreg3 $r.o2)
- (define $r.stkp $r.o3)
- (define $r.stklim $r.i0)
- (define $r.tmp1 $r.o4)
- (define $r.tmp2 $r.o5)
- (define $r.tmp0 $r.g1)
- (define $r.e-top $r.i0)
- (define $r.e-limit $r.o3)
- (define $r.timer $r.i4)
- (define $r.millicode $r.i7)
- (define $r.globals $r.i7)
- (define $r.reg0 $r.l0)
- (define $r.reg1 $r.l1)
- (define $r.reg2 $r.l2)
- (define $r.reg3 $r.l3)
- (define $r.reg4 $r.l4)
- (define $r.reg5 $r.l5)
- (define $r.reg6 $r.l6)
- (define $r.reg7 $r.l7)
- ; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
- (define $ex.car 0)
- (define $ex.cdr 1)
- (define $ex.setcar 2)
- (define $ex.setcdr 3)
- (define $ex.add 10)
- (define $ex.sub 11)
- (define $ex.mul 12)
- (define $ex.div 13)
- (define $ex.lessp 14)
- (define $ex.lesseqp 15)
- (define $ex.equalp 16)
- (define $ex.greatereqp 17)
- (define $ex.greaterp 18)
- (define $ex.quotient 19)
- (define $ex.remainder 20)
- (define $ex.modulo 21)
- (define $ex.logior 22)
- (define $ex.logand 23)
- (define $ex.logxor 24)
- (define $ex.lognot 25)
- (define $ex.lsh 26)
- (define $ex.rsha 27)
- (define $ex.rshl 28)
- (define $ex.e2i 29)
- (define $ex.i2e 30)
- (define $ex.exactp 31)
- (define $ex.inexactp 32)
- (define $ex.round 33)
- (define $ex.trunc 34)
- (define $ex.zerop 35)
- (define $ex.neg 36)
- (define $ex.abs 37)
- (define $ex.realpart 38)
- (define $ex.imagpart 39)
- (define $ex.vref 40)
- (define $ex.vset 41)
- (define $ex.vlen 42)
- (define $ex.pref 50)
- (define $ex.pset 51)
- (define $ex.plen 52)
- (define $ex.sref 60)
- (define $ex.sset 61)
- (define $ex.slen 62)
- (define $ex.bvref 70)
- (define $ex.bvset 71)
- (define $ex.bvlen 72)
- (define $ex.bvlref 80)
- (define $ex.bvlset 81)
- (define $ex.bvllen 82)
- (define $ex.vlref 90)
- (define $ex.vlset 91)
- (define $ex.vllen 92)
- (define $ex.typetag 100)
- (define $ex.typetagset 101)
- (define $ex.apply 102)
- (define $ex.argc 103)
- (define $ex.vargc 104)
- (define $ex.nonproc 105)
- (define $ex.undef-global 106)
- (define $ex.dump 107)
- (define $ex.dumpfail 108)
- (define $ex.timer 109)
- (define $ex.unsupported 110)
- (define $ex.int2char 111)
- (define $ex.char2int 112)
- (define $ex.mkbvl 113)
- (define $ex.mkvl 114)
- (define $ex.char<? 115)
- (define $ex.char<=? 116)
- (define $ex.char=? 117)
- (define $ex.char>? 118)
- (define $ex.char>=? 119)
- (define $ex.bvfill 120)
- (define $ex.enable-interrupts 121)
- (define $ex.keyboard-interrupt 122)
- (define $ex.arithmetic-exception 123)
- (define $ex.global-invoke 124)
- (define $ex.fx+ 140)
- (define $ex.fx- 141)
- (define $ex.fx-- 142)
- (define $ex.fx= 143)
- (define $ex.fx< 144)
- (define $ex.fx<= 145)
- (define $ex.fx> 146)
- (define $ex.fx>= 147)
- (define $ex.fxpositive? 148)
- (define $ex.fxnegative? 149)
- (define $ex.fxzero? 150)
- (define $ex.fx* 151)
- ; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
- (define $tag.tagmask 7)
- (define $tag.pair-tag 1)
- (define $tag.vector-tag 3)
- (define $tag.bytevector-tag 5)
- (define $tag.procedure-tag 7)
- (define $imm.vector-header 162)
- (define $imm.bytevector-header 194)
- (define $imm.procedure-header 254)
- (define $imm.true 6)
- (define $imm.false 2)
- (define $imm.null 10)
- (define $imm.unspecified 278)
- (define $imm.eof 534)
- (define $imm.undefined 790)
- (define $imm.character 38)
- (define $tag.vector-typetag 0)
- (define $tag.rectnum-typetag 4)
- (define $tag.ratnum-typetag 8)
- (define $tag.symbol-typetag 12)
- (define $tag.port-typetag 16)
- (define $tag.structure-typetag 20)
- (define $tag.bytevector-typetag 0)
- (define $tag.string-typetag 4)
- (define $tag.flonum-typetag 8)
- (define $tag.compnum-typetag 12)
- (define $tag.bignum-typetag 16)
- (define $hdr.port 178)
- (define $hdr.struct 182)
- (define $p.codevector -3)
- (define $p.constvector 1)
- (define $p.linkoffset 5)
- (define $p.reg0 5)
- (define $p.codeoffset -1)
- ; Copyright 1991 William Clinger
- ;
- ; Relatively target-independent information for Twobit's backend.
- ;
- ; 24 April 1999 / wdc
- ;
- ; Most of the definitions in this file can be extended or overridden by
- ; target-specific definitions.
- (define twobit-sort
- (lambda (less? list) (compat:sort list less?)))
- (define renaming-prefix ".")
- ; The prefix used for cells introduced by the compiler.
- (define cell-prefix (string-append renaming-prefix "CELL:"))
- ; Names of global procedures that cannot be redefined or assigned
- ; by ordinary code.
- ; The expansion of quasiquote uses .cons and .list directly, so these
- ; should not be changed willy-nilly.
- ; Others may be used directly by a DEFINE-INLINE.
- (define name:CHECK! '.check!)
- (define name:CONS '.cons)
- (define name:LIST '.list)
- (define name:MAKE-CELL '.make-cell)
- (define name:CELL-REF '.cell-ref)
- (define name:CELL-SET! '.cell-set!)
- (define name:IGNORED (string->symbol "IGNORED"))
- (define name:CAR '.car)
- (define name:CDR '.cdr)
- ;(begin (eval `(define ,name:CONS cons))
- ; (eval `(define ,name:LIST list))
- ; (eval `(define ,name:MAKE-CELL list))
- ; (eval `(define ,name:CELL-REF car))
- ; (eval `(define ,name:CELL-SET! set-car!)))
- ; If (INTEGRATE-USUAL-PROCEDURES) is true, then control optimization
- ; recognizes calls to these procedures.
- (define name:NOT 'not)
- (define name:MEMQ 'memq)
- (define name:MEMV 'memv)
- ; If (INTEGRATE-USUAL-PROCEDURES) is true, then control optimization
- ; recognizes calls to these procedures and also creates calls to them.
- (define name:EQ? 'eq?)
- (define name:EQV? 'eqv?)
- ; Control optimization creates calls to these procedures,
- ; which do not need to check their arguments.
- (define name:FIXNUM? 'fixnum?)
- (define name:CHAR? 'char?)
- (define name:SYMBOL? 'symbol?)
- (define name:FX< '<:fix:fix)
- (define name:FX- 'fx-) ; non-checking version
- (define name:CHAR->INTEGER 'char->integer) ; non-checking version
- (define name:VECTOR-REF 'vector-ref:trusted)
- ; Constant folding.
- ; Prototype, will probably change in the future.
- (define (constant-folding-entry name)
- (assq name $usual-constant-folding-procedures$))
- (define constant-folding-predicates cadr)
- (define constant-folding-folder caddr)
- (define $usual-constant-folding-procedures$
- (let ((always? (lambda (x) #t))
- (charcode? (lambda (n)
- (and (number? n)
- (exact? n)
- (<= 0 n)
- (< n 128))))
- (ratnum? (lambda (n)
- (and (number? n)
- (exact? n)
- (rational? n))))
- ; smallint? is defined later.
- (smallint? (lambda (n) (smallint? n))))
- `(
- ; This makes some assumptions about the host system.
-
- (integer->char (,charcode?) ,integer->char)
- (char->integer (,char?) ,char->integer)
- (zero? (,ratnum?) ,zero?)
- (< (,ratnum? ,ratnum?) ,<)
- (<= (,ratnum? ,ratnum?) ,<=)
- (= (,ratnum? ,ratnum?) ,=)
- (>= (,ratnum? ,ratnum?) ,>=)
- (> (,ratnum? ,ratnum?) ,>)
- (+ (,ratnum? ,ratnum?) ,+)
- (- (,ratnum? ,ratnum?) ,-)
- (* (,ratnum? ,ratnum?) ,*)
- (-- (,ratnum?) ,(lambda (x) (- 0 x)))
- (eq? (,always? ,always?) ,eq?)
- (eqv? (,always? ,always?) ,eqv?)
- (equal? (,always? ,always?) ,equal?)
- (memq (,always? ,list?) ,memq)
- (memv (,always? ,list?) ,memv)
- (member (,always? ,list?) ,member)
- (assq (,always? ,list?) ,assq)
- (assv (,always? ,list?) ,assv)
- (assoc (,always? ,list?) ,assoc)
- (length (,list?) ,length)
- (fixnum? (,smallint?) ,smallint?)
- (=:fix:fix (,smallint? ,smallint?) ,=)
- (<:fix:fix (,smallint? ,smallint?) ,<)
- (<=:fix:fix (,smallint? ,smallint?) ,<=)
- (>:fix:fix (,smallint? ,smallint?) ,>)
- (>=:fix:fix (,smallint? ,smallint?) ,>=)
- )))
- (begin '
- (define (.check! flag exn . args)
- (if (not flag)
- (apply error "Runtime check exception: " exn args)))
- #t)
- ; Order matters. If f and g are both inlined, and the definition of g
- ; uses f, then f should be defined before g.
- (for-each pass1
- `(
- (define-inline car
- (syntax-rules ()
- ((car x0)
- (let ((x x0))
- (.check! (pair? x) ,$ex.car x)
- (car:pair x)))))
-
- (define-inline cdr
- (syntax-rules ()
- ((car x0)
- (let ((x x0))
- (.check! (pair? x) ,$ex.cdr x)
- (cdr:pair x)))))
- (define-inline vector-length
- (syntax-rules ()
- ((vector-length v0)
- (let ((v v0))
- (.check! (vector? v) ,$ex.vlen v)
- (vector-length:vec v)))))
-
- (define-inline vector-ref
- (syntax-rules ()
- ((vector-ref v0 i0)
- (let ((v v0)
- (i i0))
- (.check! (fixnum? i) ,$ex.vref v i)
- (.check! (vector? v) ,$ex.vref v i)
- (.check! (<:fix:fix i (vector-length:vec v)) ,$ex.vref v i)
- (.check! (>=:fix:fix i 0) ,$ex.vref v i)
- (vector-ref:trusted v i)))))
-
- (define-inline vector-set!
- (syntax-rules ()
- ((vector-set! v0 i0 x0)
- (let ((v v0)
- (i i0)
- (x x0))
- (.check! (fixnum? i) ,$ex.vset v i x)
- (.check! (vector? v) ,$ex.vset v i x)
- (.check! (<:fix:fix i (vector-length:vec v)) ,$ex.vset v i x)
- (.check! (>=:fix:fix i 0) ,$ex.vset v i x)
- (vector-set!:trusted v i x)))))
-
- ; This transformation must make sure the entire list is freshly
- ; allocated when an argument to LIST returns more than once.
- (define-inline list
- (syntax-rules ()
- ((list)
- '())
- ((list ?e)
- (cons ?e '()))
- ((list ?e1 ?e2 ...)
- (let* ((t1 ?e1)
- (t2 (list ?e2 ...)))
- (cons t1 t2)))))
- ; This transformation must make sure the entire list is freshly
- ; allocated when an argument to VECTOR returns more than once.
- (define-inline vector
- (syntax-rules ()
- ((vector)
- '#())
- ((vector ?e)
- (make-vector 1 ?e))
- ((vector ?e1 ?e2 ...)
- (letrec-syntax
- ((vector-aux1
- (... (syntax-rules ()
- ((vector-aux1 () ?n ?exps ?indexes ?temps)
- (vector-aux2 ?n ?exps ?indexes ?temps))
- ((vector-aux1 (?exp1 ?exp2 ...) ?n ?exps ?indexes ?temps)
- (vector-aux1 (?exp2 ...)
- (+ ?n 1)
- (?exp1 . ?exps)
- (?n . ?indexes)
- (t . ?temps))))))
- (vector-aux2
- (... (syntax-rules ()
- ((vector-aux2 ?n (?exp1 ?exp2 ...) (?n1 ?n2 ...) (?t1 ?t2 ...))
- (let* ((?t1 ?exp1)
- (?t2 ?exp2)
- ...
- (v (make-vector ?n ?t1)))
- (vector-set! v ?n2 ?t2)
- ...
- v))))))
- (vector-aux1 (?e1 ?e2 ...) 0 () () ())))))
- (define-inline cadddr
- (syntax-rules ()
- ((cadddr ?e)
- (car (cdr (cdr (cdr ?e)))))))
- (define-inline cddddr
- (syntax-rules ()
- ((cddddr ?e)
- (cdr (cdr (cdr (cdr ?e)))))))
- (define-inline cdddr
- (syntax-rules ()
- ((cdddr ?e)
- (cdr (cdr (cdr ?e))))))
- (define-inline caddr
- (syntax-rules ()
- ((caddr ?e)
- (car (cdr (cdr ?e))))))
- (define-inline cddr
- (syntax-rules ()
- ((cddr ?e)
- (cdr (cdr ?e)))))
- (define-inline cdar
- (syntax-rules ()
- ((cdar ?e)
- (cdr (car ?e)))))
- (define-inline cadr
- (syntax-rules ()
- ((cadr ?e)
- (car (cdr ?e)))))
- (define-inline caar
- (syntax-rules ()
- ((caar ?e)
- (car (car ?e)))))
- (define-inline make-vector
- (syntax-rules ()
- ((make-vector ?n)
- (make-vector ?n '()))))
- (define-inline make-string
- (syntax-rules ()
- ((make-string ?n)
- (make-string ?n #\space))))
- (define-inline =
- (syntax-rules ()
- ((= ?e1 ?e2 ?e3 ?e4 ...)
- (let ((t ?e2))
- (and (= ?e1 t)
- (= t ?e3 ?e4 ...))))))
- (define-inline <
- (syntax-rules ()
- ((< ?e1 ?e2 ?e3 ?e4 ...)
- (let ((t ?e2))
- (and (< ?e1 t)
- (< t ?e3 ?e4 ...))))))
- (define-inline >
- (syntax-rules ()
- ((> ?e1 ?e2 ?e3 ?e4 ...)
- (let ((t ?e2))
- (and (> ?e1 t)
- (> t ?e3 ?e4 ...))))))
- (define-inline <=
- (syntax-rules ()
- ((<= ?e1 ?e2 ?e3 ?e4 ...)
- (let ((t ?e2))
- (and (<= ?e1 t)
- (<= t ?e3 ?e4 ...))))))
- (define-inline >=
- (syntax-rules ()
- ((>= ?e1 ?e2 ?e3 ?e4 ...)
- (let ((t ?e2))
- (and (>= ?e1 t)
- (>= t ?e3 ?e4 ...))))))
- (define-inline +
- (syntax-rules ()
- ((+)
- 0)
- ((+ ?e)
- ?e)
- ((+ ?e1 ?e2 ?e3 ?e4 ...)
- (+ (+ ?e1 ?e2) ?e3 ?e4 ...))))
- (define-inline *
- (syntax-rules ()
- ((*)
- 1)
- ((* ?e)
- ?e)
- ((* ?e1 ?e2 ?e3 ?e4 ...)
- (* (* ?e1 ?e2) ?e3 ?e4 ...))))
- (define-inline -
- (syntax-rules ()
- ((- ?e)
- (- 0 ?e))
- ((- ?e1 ?e2 ?e3 ?e4 ...)
- (- (- ?e1 ?e2) ?e3 ?e4 ...))))
- (define-inline /
- (syntax-rules ()
- ((/ ?e)
- (/ 1 ?e))
- ((/ ?e1 ?e2 ?e3 ?e4 ...)
- (/ (/ ?e1 ?e2) ?e3 ?e4 ...))))
- (define-inline abs
- (syntax-rules ()
- ((abs ?z)
- (let ((temp ?z))
- (if (< temp 0)
- (-- temp)
- temp)))))
- (define-inline negative?
- (syntax-rules ()
- ((negative? ?x)
- (< ?x 0))))
- (define-inline positive?
- (syntax-rules ()
- ((positive? ?x)
- (> ?x 0))))
- (define-inline eqv?
- (transformer
- (lambda (exp rename compare)
- (let ((arg1 (cadr exp))
- (arg2 (caddr exp)))
- (define (constant? exp)
- (or (boolean? exp)
- (char? exp)
- (and (pair? exp)
- (= (length exp) 2)
- (identifier? (car exp))
- (compare (car exp) (rename 'quote))
- (symbol? (cadr exp)))))
- (if (or (constant? arg1)
- (constant? arg2))
- (cons (rename 'eq?) (cdr exp))
- exp)))))
- (define-inline memq
- (syntax-rules (quote)
- ((memq ?expr '(?datum ...))
- (letrec-syntax
- ((memq0
- (... (syntax-rules (quote)
- ((memq0 '?xx '(?d ...))
- (let ((t1 '(?d ...)))
- (memq1 '?xx t1 (?d ...))))
- ((memq0 ?e '(?d ...))
- (let ((t0 ?e)
- (t1 '(?d ...)))
- (memq1 t0 t1 (?d ...)))))))
- (memq1
- (... (syntax-rules ()
- ((memq1 ?t0 ?t1 ())
- #f)
- ((memq1 ?t0 ?t1 (?d1 ?d2 ...))
- (if (eq? ?t0 '?d1)
- ?t1
- (let ((?t1 (cdr ?t1)))
- (memq1 ?t0 ?t1 (?d2 ...)))))))))
- (memq0 ?expr '(?datum ...))))))
- (define-inline memv
- (transformer
- (lambda (exp rename compare)
- (let ((arg1 (cadr exp))
- (arg2 (caddr exp)))
- (if (or (boolean? arg1)
- (fixnum? arg1)
- (char? arg1)
- (and (pair? arg1)
- (= (length arg1) 2)
- (identifier? (car arg1))
- (compare (car arg1) (rename 'quote))
- (symbol? (cadr arg1)))
- (and (pair? arg2)
- (= (length arg2) 2)
- (identifier? (car arg2))
- (compare (car arg2) (rename 'quote))
- (every1? (lambda (x)
- (or (boolean? x)
- (fixnum? x)
- (char? x)
- (symbol? x)))
- (cadr arg2))))
- (cons (rename 'memq) (cdr exp))
- exp)))))
- (define-inline assv
- (transformer
- (lambda (exp rename compare)
- (let ((arg1 (cadr exp))
- (arg2 (caddr exp)))
- (if (or (boolean? arg1)
- (char? arg1)
- (and (pair? arg1)
- (= (length arg1) 2)
- (identifier? (car arg1))
- (compare (car arg1) (rename 'quote))
- (symbol? (cadr arg1)))
- (and (pair? arg2)
- (= (length arg2) 2)
- (identifier? (car arg2))
- (compare (car arg2) (rename 'quote))
- (every1? (lambda (y)
- (and (pair? y)
- (let ((x (car y)))
- (or (boolean? x)
- (char? x)
- (symbol? x)))))
- (cadr arg2))))
- (cons (rename 'assq) (cdr exp))
- exp)))))
- (define-inline map
- (syntax-rules (lambda)
- ((map ?proc ?exp1 ?exp2 ...)
- (letrec-syntax
- ((loop
- (... (syntax-rules (lambda)
- ((loop 1 () (?y1 ?y2 ...) ?f ?exprs)
- (loop 2 (?y1 ?y2 ...) ?f ?exprs))
- ((loop 1 (?a1 ?a2 ...) (?y2 ...) ?f ?exprs)
- (loop 1 (?a2 ...) (y1 ?y2 ...) ?f ?exprs))
-
- ((loop 2 ?ys (lambda ?formals ?body) ?exprs)
- (loop 3 ?ys (lambda ?formals ?body) ?exprs))
- ((loop 2 ?ys (?f1 . ?f2) ?exprs)
- (let ((f (?f1 . ?f2)))
- (loop 3 ?ys f ?exprs)))
- ; ?f must be a constant or variable.
- ((loop 2 ?ys ?f ?exprs)
- (loop 3 ?ys ?f ?exprs))
-
- ((loop 3 (?y1 ?y2 ...) ?f (?e1 ?e2 ...))
- (do ((?y1 ?e1 (cdr ?y1))
- (?y2 ?e2 (cdr ?y2))
- ...
- (results '() (cons (?f (car ?y1) (car ?y2) ...)
- results)))
- ((or (null? ?y1) (null? ?y2) ...)
- (reverse results))))))))
-
- (loop 1 (?exp1 ?exp2 ...) () ?proc (?exp1 ?exp2 ...))))))
- (define-inline for-each
- (syntax-rules (lambda)
- ((for-each ?proc ?exp1 ?exp2 ...)
- (letrec-syntax
- ((loop
- (... (syntax-rules (lambda)
- ((loop 1 () (?y1 ?y2 ...) ?f ?exprs)
- (loop 2 (?y1 ?y2 ...) ?f ?exprs))
- ((loop 1 (?a1 ?a2 ...) (?y2 ...) ?f ?exprs)
- (loop 1 (?a2 ...) (y1 ?y2 ...) ?f ?exprs))
-
- ((loop 2 ?ys (lambda ?formals ?body) ?exprs)
- (loop 3 ?ys (lambda ?formals ?body) ?exprs))
- ((loop 2 ?ys (?f1 . ?f2) ?exprs)
- (let ((f (?f1 . ?f2)))
- (loop 3 ?ys f ?exprs)))
- ; ?f must be a constant or variable.
- ((loop 2 ?ys ?f ?exprs)
- (loop 3 ?ys ?f ?exprs))
-
- ((loop 3 (?y1 ?y2 ...) ?f (?e1 ?e2 ...))
- (do ((?y1 ?e1 (cdr ?y1))
- (?y2 ?e2 (cdr ?y2))
- ...)
- ((or (null? ?y1) (null? ?y2) ...)
- (if #f #f))
- (?f (car ?y1) (car ?y2) ...)))))))
-
- (loop 1 (?exp1 ?exp2 ...) () ?proc (?exp1 ?exp2 ...))))))
- ))
- (define extended-syntactic-environment
- (syntactic-copy global-syntactic-environment))
- (define (make-extended-syntactic-environment)
- (syntactic-copy extended-syntactic-environment))
- ; MacScheme machine assembly instructions.
- (define instruction.op car)
- (define instruction.arg1 cadr)
- (define instruction.arg2 caddr)
- (define instruction.arg3 cadddr)
- ; Opcode table.
- (define *mnemonic-names* '()) ; For readify-lap
- (begin
- '
- (define *last-reserved-mnemonic* 32767) ; For consistency check
- '
- (define make-mnemonic
- (let ((count 0))
- (lambda (name)
- (set! count (+ count 1))
- (if (= count *last-reserved-mnemonic*)
- (error "Error in make-mnemonic: conflict: " name))
- (set! *mnemonic-names* (cons (cons count name) *mnemonic-names*))
- count)))
- '
- (define (reserved-mnemonic name value)
- (if (and (> value 0) (< value *last-reserved-mnemonic*))
- (set! *last-reserved-mnemonic* value))
- (set! *mnemonic-names* (cons (cons value name) *mnemonic-names*))
- value)
- #t)
- (define make-mnemonic
- (let ((count 0))
- (lambda (name)
- (set! count (+ count 1))
- (set! *mnemonic-names* (cons (cons count name) *mnemonic-names*))
- count)))
- (define (reserved-mnemonic name ignored)
- (make-mnemonic name))
- (define $.linearize (reserved-mnemonic '.linearize -1)) ; unused?
- (define $.label (reserved-mnemonic '.label 63))
- (define $.proc (reserved-mnemonic '.proc 62)) ; proc entry point
- (define $.cont (reserved-mnemonic '.cont 61)) ; return point
- (define $.align (reserved-mnemonic '.align 60)) ; align code stream
- (define $.asm (reserved-mnemonic '.asm 59)) ; in-line native code
- (define $.proc-doc ; internal def proc info
- (reserved-mnemonic '.proc-doc 58))
- (define $.end ; end of code vector
- (reserved-mnemonic '.end 57)) ; (asm internal)
- (define $.singlestep ; insert singlestep point
- (reserved-mnemonic '.singlestep 56)) ; (asm internal)
- (define $.entry (reserved-mnemonic '.entry 55)) ; procedure entry point
- ; (asm internal)
- (define $op1 (make-mnemonic 'op1)) ; op prim
- (define $op2 (make-mnemonic 'op2)) ; op2 prim,k
- (define $op3 (make-mnemonic 'op3)) ; op3 prim,k1,k2
- (define $op2imm (make-mnemonic 'op2imm)) ; op2imm prim,x
- (define $const (make-mnemonic 'const)) ; const x
- (define $global (make-mnemonic 'global)) ; global x
- (define $setglbl (make-mnemonic 'setglbl)) ; setglbl x
- (define $lexical (make-mnemonic 'lexical)) ; lexical m,n
- (define $setlex (make-mnemonic 'setlex)) ; setlex m,n
- (define $stack (make-mnemonic 'stack)) ; stack n
- (define $setstk (make-mnemonic 'setstk)) ; setstk n
- (define $load (make-mnemonic 'load)) ; load k,n
- (define $store (make-mnemonic 'store)) ; store k,n
- (define $reg (make-mnemonic 'reg)) ; reg k
- (define $setreg (make-mnemonic 'setreg)) ; setreg k
- (define $movereg (make-mnemonic 'movereg)) ; movereg k1,k2
- (define $lambda (make-mnemonic 'lambda)) ; lambda x,n,doc
- (define $lexes (make-mnemonic 'lexes)) ; lexes n,doc
- (define $args= (make-mnemonic 'args=)) ; args= k
- (define $args>= (make-mnemonic 'args>=)) ; args>= k
- (define $invoke (make-mnemonic 'invoke)) ; invoke k
- (define $save (make-mnemonic 'save)) ; save L,k
- (define $setrtn (make-mnemonic 'setrtn)) ; setrtn L
- (define $restore (make-mnemonic 'restore)) ; restore n ; deprecated
- (define $pop (make-mnemonic 'pop)) ; pop k
- (define $popstk (make-mnemonic 'popstk)) ; popstk ; for students
- (define $return (make-mnemonic 'return)) ; return
- (define $mvrtn (make-mnemonic 'mvrtn)) ; mvrtn ; NYI
- (define $apply (make-mnemonic 'apply)) ; apply
- (define $nop (make-mnemonic 'nop)) ; nop
- (define $jump (make-mnemonic 'jump)) ; jump m,o
- (define $skip (make-mnemonic 'skip)) ; skip L ; forward
- (define $branch (make-mnemonic 'branch)) ; branch L
- (define $branchf (make-mnemonic 'branchf)) ; branchf L
- (define $check (make-mnemonic 'check)) ; check k1,k2,k3,L
- (define $trap (make-mnemonic 'trap)) ; trap k1,k2,k3,exn
- ; A peephole optimizer may define more instructions in some
- ; target-specific file.
- ; eof
- ; Copyright 1991 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Larceny -- target-specific information for Twobit's SPARC backend.
- ;
- ; 11 June 1999 / wdc
- ; The maximum number of fixed arguments that may be followed by a rest
- ; argument. This limitation is removed by the macro expander.
- (define @maxargs-with-rest-arg@ 30)
- ; The number of MacScheme machine registers.
- ; (They do not necessarily correspond to hardware registers.)
- (define *nregs* 32)
- (define *lastreg* (- *nregs* 1))
- (define *fullregs* (quotient *nregs* 2))
- ; The number of argument registers that are represented by hardware
- ; registers.
- (define *nhwregs* 8)
- ; Variable names that indicate register targets.
- (define *regnames*
- (do ((alist '() (cons (cons (string->symbol
- (string-append ".REG" (number->string r)))
- r)
- alist))
- (r (- *nhwregs* 1) (- r 1)))
- ((<= r 0)
- alist)))
- ; A non-inclusive upper bound for the instruction encodings.
- (define *number-of-mnemonics* 72)
- ; Integrable procedures and procedure-specific source code transformations.
- ; Every integrable procedure that takes a varying number of arguments must
- ; supply a transformation procedure to map calls into the fixed arity
- ; required by the MacScheme machine instructions.
- ; The table of integrable procedures.
- ; Each entry is a list of the following items:
- ;
- ; procedure name
- ; arity (or -1 for special primops like .check!)
- ; procedure name to be used by the disassembler
- ; predicate for immediate operands (or #f)
- ; primop code in the MacScheme machine (not used by Larceny)
- ; the effects that kill this primop's result
- ; the effects of this primop that kill available expressions
- (define (prim-entry name)
- (assq name $usual-integrable-procedures$))
- (define prim-arity cadr)
- (define prim-opcodename caddr)
- (define prim-immediate? cadddr)
- (define (prim-primcode entry)
- (car (cddddr entry)))
- ; This predicate returns #t iff its argument will be represented
- ; as a fixnum on the target machine.
- (define smallint?
- (let* ((least (- (expt 2 29)))
- (greatest (- (- least) 1)))
- (lambda (x)
- (and (number? x)
- (exact? x)
- (integer? x)
- (<= least x greatest)))))
- (define (sparc-imm? x)
- (and (fixnum? x)
- (<= -1024 x 1023)))
- (define (sparc-eq-imm? x)
- (or (sparc-imm? x)
- (eq? x #t)
- (eq? x #f)
- (eq? x '())))
- (define (valid-typetag? x)
- (and (fixnum? x)
- (<= 0 x 7)))
- (define (fixnum-primitives) #t)
- (define (flonum-primitives) #t)
- ; The table of primitives has been extended with
- ; kill information used for commoning.
- (define (prim-lives-until entry)
- (list-ref entry 5))
- (define (prim-kills entry)
- (list-ref entry 6))
- (define $usual-integrable-procedures$
- (let ((:globals available:killer:globals)
- (:car available:killer:car)
- (:cdr available:killer:cdr)
- (:string available:killer:string)
- (:vector available:killer:vector)
- (:cell available:killer:cell)
- (:io available:killer:io)
- (:none available:killer:none) ; none of the above
- (:all available:killer:all) ; all of the above
- (:immortal available:killer:immortal) ; never killed
- (:dead available:killer:dead) ; never available
- )
- ; external arity internal immediate ignored killed kills
- ; name name predicate by what
- ; kind of
- ; effect
- `((break 0 break #f 3 ,:dead ,:all)
- (creg 0 creg #f 7 ,:dead ,:all)
- (unspecified 0 unspecified #f -1 ,:dead ,:none)
- (undefined 0 undefined #f 8 ,:dead ,:none)
- (eof-object 0 eof-object #f -1 ,:dead ,:none)
- (enable-interrupts 1 enable-interrupts #f -1 ,:dead ,:all)
- (disable-interrupts 0 disable-interrupts #f -1 ,:dead ,:all)
- (typetag 1 typetag #f #x11 ,:dead ,:none)
- (not 1 not #f #x18 ,:immortal ,:none)
- (null? 1 null? #f #x19 ,:immortal ,:none)
- (pair? 1 pair? #f #x1a ,:immortal ,:none)
- (eof-object? 1 eof-object? #f -1 ,:immortal ,:none)
- (port? 1 port? #f -1 ,:dead ,:none)
- (structure? 1 structure? #f -1 ,:dead ,:none)
- (car 1 car #f #x1b ,:car ,:none)
- (,name:CAR 1 car #f #x1b ,:car ,:none)
- (cdr 1 cdr #f #x1c ,:cdr ,:none)
- (,name:CDR 1 cdr #f #x1c ,:cdr ,:none)
- (symbol? 1 symbol? #f #x1f ,:immortal ,:none)
- (number? 1 complex? #f #x20 ,:immortal ,:none)
- (complex? 1 complex? #f #x20 ,:immortal ,:none)
- (real? 1 rational? #f #x21 ,:immortal ,:none)
- (rational? 1 rational? #f #x21 ,:immortal ,:none)
- (integer? 1 integer? #f #x22 ,:immortal ,:none)
- (fixnum? 1 fixnum? #f #x23 ,:immortal ,:none)
- (flonum? 1 flonum? #f -1 ,:immortal ,:none)
- (compnum? 1 compnum? #f -1 ,:immortal ,:none)
- (exact? 1 exact? #f #x24 ,:immortal ,:none)
- (inexact? 1 inexact? #f #x25 ,:immortal ,:none)
- (exact->inexact 1 exact->inexact #f #x26 ,:immortal ,:none)
- (inexact->exact 1 inexact->exact #f #x27 ,:immortal ,:none)
- (round 1 round #f #x28 ,:immortal ,:none)
- (truncate 1 truncate #f #x29 ,:immortal ,:none)
- (zero? 1 zero? #f #x2c ,:immortal ,:none)
- (-- 1 -- #f #x2d ,:immortal ,:none)
- (lognot 1 lognot #f #x2f ,:immortal ,:none)
- (real-part 1 real-part #f #x3e ,:immortal ,:none)
- (imag-part 1 imag-part #f #x3f ,:immortal ,:none)
- (char? 1 char? #f #x40 ,:immortal ,:none)
- (char->integer 1 char->integer #f #x41 ,:immortal ,:none)
- (integer->char 1 integer->char #f #x42 ,:immortal ,:none)
- (string? 1 string? #f #x50 ,:immortal ,:none)
- (string-length 1 string-length #f #x51 ,:immortal ,:none)
- (vector? 1 vector? #f #x52 ,:immortal ,:none)
- (vector-length 1 vector-length #f #x53 ,:immortal ,:none)
- (bytevector? 1 bytevector? #f #x54 ,:immortal ,:none)
- (bytevector-length 1 bytevector-length #f #x55 ,:immortal ,:none)
- (bytevector-fill! 2 bytevector-fill! #f -1 ,:dead ,:string)
- (make-bytevector 1 make-bytevector #f #x56 ,:dead ,:none)
- (procedure? 1 procedure? #f #x58 ,:immortal ,:none)
- (procedure-length 1 procedure-length #f #x59 ,:dead ,:none)
- (make-procedure 1 make-procedure #f #x5a ,:dead ,:none)
- (creg-set! 1 creg-set! #f #x71 ,:dead ,:none)
- (,name:MAKE-CELL 1 make-cell #f #x7e ,:dead ,:none)
- (,name:CELL-REF 1 cell-ref #f #x7f ,:cell ,:none)
- (,name:CELL-SET! 2 cell-set! #f #xdf ,:dead ,:cell)
- (typetag-set! 2 typetag-set! ,valid-typetag? #xa0 ,:dead ,:all)
- (eq? 2 eq? ,sparc-eq-imm? #xa1 ,:immortal ,:none)
- (eqv? 2 eqv? #f #xa2 ,:immortal ,:none)
- (cons 2 cons #f #xa8 ,:dead ,:none)
- (,name:CONS 2 cons #f #xa8 ,:dead ,:none)
- (set-car! 2 set-car! #f #xa9 ,:dead ,:car)
- (set-cdr! 2 set-cdr! #f #xaa ,:dead ,:cdr)
- (+ 2 + ,sparc-imm? #xb0 ,:immortal ,:none)
- (- 2 - ,sparc-imm? #xb1 ,:immortal ,:none)
- (* 2 * ,sparc-imm? #xb2 ,:immortal ,:none)
- (/ 2 / #f #xb3 ,:immortal ,:none)
- (quotient 2 quotient #f #xb4 ,:immortal ,:none)
- (< 2 < ,sparc-imm? #xb5 ,:immortal ,:none)
- (<= 2 <= ,sparc-imm? #xb6 ,:immortal ,:none)
- (= 2 = ,sparc-imm? #xb7 ,:immortal ,:none)
- (> 2 > ,sparc-imm? #xb8 ,:immortal ,:none)
- (>= 2 >= ,sparc-imm? #xb9 ,:immortal ,:none)
- (logand 2 logand #f #xc0 ,:immortal ,:none)
- (logior 2 logior #f #xc1 ,:immortal ,:none)
- (logxor 2 logxor #f #xc2 ,:immortal ,:none)
- (lsh 2 lsh #f #xc3 ,:immortal ,:none)
- (rsha 2 rsha #f -1 ,:immortal ,:none)
- (rshl 2 rshl #f -1 ,:immortal ,:none)
- (rot 2 rot #f #xc4 ,:immortal ,:none)
- (make-string 2 make-string #f -1 ,:dead ,:none)
- (string-ref 2 string-ref ,sparc-imm? #xd1 ,:string ,:none)
- (string-set! 3 string-set! ,sparc-imm? -1 ,:dead ,:string)
- (make-vector 2 make-vector #f #xd2 ,:dead ,:none)
- (vector-ref 2 vector-ref ,sparc-imm? #xd3 ,:vector ,:none)
- (bytevector-ref 2 bytevector-ref ,sparc-imm? #xd5 ,:string ,:none)
- (procedure-ref 2 procedure-ref #f #xd7 ,:dead ,:none)
- (char<? 2 char<? ,char? #xe0 ,:immortal ,:none)
- (char<=? 2 char<=? ,char? #xe1 ,:immortal ,:none)
- (char=? 2 char=? ,char? #xe2 ,:immortal ,:none)
- (char>? 2 char>? ,char? #xe3 ,:immortal ,:none)
- (char>=? 2 char>=? ,char? #xe4 ,:immortal ,:none)
-
- (sys$partial-list->vector 2 sys$partial-list->vector #f -1 ,:dead ,:all)
- (vector-set! 3 vector-set! #f #xf1 ,:dead ,:vector)
- (bytevector-set! 3 bytevector-set! #f #xf2 ,:dead ,:string)
- (procedure-set! 3 procedure-set! #f #xf3 ,:dead ,:all)
- (bytevector-like? 1 bytevector-like? #f -1 ,:immortal ,:none)
- (vector-like? 1 vector-like? #f -1 ,:immortal ,:none)
- (bytevector-like-ref 2 bytevector-like-ref #f -1 ,:string ,:none)
- (bytevector-like-set! 3 bytevector-like-set! #f -1 ,:dead ,:string)
- (sys$bvlcmp 2 sys$bvlcmp #f -1 ,:dead ,:all)
- (vector-like-ref 2 vector-like-ref #f -1 ,:vector ,:none)
- (vector-like-set! 3 vector-like-set! #f -1 ,:dead ,:vector)
- (vector-like-length 1 vector-like-length #f -1 ,:immortal ,:none)
- (bytevector-like-length 1 bytevector-like-length #f -1 ,:immortal ,:none)
- (remainder 2 remainder #f -1 ,:immortal ,:none)
- (sys$read-char 1 sys$read-char #f -1 ,:dead ,:io)
- (gc-counter 0 gc-counter #f -1 ,:dead ,:none)
- ,@(if (fixnum-primitives)
- `((most-positive-fixnum
- 0 most-positive-fixnum
- #f -1 ,:immortal ,:none)
- (most-negative-fixnum
- 0 most-negative-fixnum
- #f -1 ,:immortal ,:none)
- (fx+ 2 fx+ ,sparc-imm? -1 ,:immortal ,:none)
- (fx- 2 fx- ,sparc-imm? -1 ,:immortal ,:none)
- (fx-- 1 fx-- #f -1 ,:immortal ,:none)
- (fx* 2 fx* #f -1 ,:immortal ,:none)
- (fx= 2 fx= ,sparc-imm? -1 ,:immortal ,:none)
- (fx< 2 fx< ,sparc-imm? -1 ,:immortal ,:none)
- (fx<= 2 fx<= ,sparc-imm? -1 ,:immortal ,:none)
- (fx> 2 fx> ,sparc-imm? -1 ,:immortal ,:none)
- (fx>= 2 fx>= ,sparc-imm? -1 ,:immortal ,:none)
- (fxzero? 1 fxzero? #f -1 ,:immortal ,:none)
- (fxpositive? 1 fxpositive? #f -1 ,:immortal ,:none)
- (fxnegative? 1 fxnegative? #f -1 ,:immortal ,:none))
- '())
- ,@(if (flonum-primitives)
- `((fl+ 2 + #f -1 ,:immortal ,:none)
- (fl- 2 - #f -1 ,:immortal ,:none)
- (fl-- 1 -- #f -1 ,:immortal ,:none)
- (fl* 2 * #f -1 ,:immortal ,:none)
- (fl= 2 = #f -1 ,:immortal ,:none)
- (fl< 2 < #f -1 ,:immortal ,:none)
- (fl<= 2 <= #f -1 ,:immortal ,:none)
- (fl> 2 > #f -1 ,:immortal ,:none)
- (fl>= 2 >= #f -1 ,:immortal ,:none))
- '())
- ; Added for CSE, representation analysis.
- (,name:CHECK! -1 check! #f -1 ,:dead ,:none)
- (vector-length:vec 1 vector-length:vec #f -1 ,:immortal ,:none)
- (vector-ref:trusted 2 vector-ref:trusted ,sparc-imm? -1 ,:vector ,:none)
- (vector-set!:trusted 3 vector-set!:trusted #f -1 ,:dead ,:vector)
- (car:pair 1 car:pair #f -1 ,:car ,:none)
- (cdr:pair 1 cdr:pair #f -1 ,:cdr ,:none)
- (=:fix:fix 2 =:fix:fix ,sparc-imm? -1 ,:immortal ,:none)
- (<:fix:fix 2 <:fix:fix ,sparc-imm? -1 ,:immortal ,:none)
- (<=:fix:fix 2 <=:fix:fix ,sparc-imm? -1 ,:immortal ,:none)
- (>=:fix:fix 2 >=:fix:fix ,sparc-imm? -1 ,:immortal ,:none)
- (>:fix:fix 2 >:fix:fix ,sparc-imm? -1 ,:immortal ,:none)
-
- ; Not yet implemented.
- (+:idx:idx 2 +:idx:idx #f -1 ,:immortal ,:none)
- (+:fix:fix 2 +:idx:idx #f -1 ,:immortal ,:none)
- (+:exi:exi 2 +:idx:idx #f -1 ,:immortal ,:none)
- (+:flo:flo 2 +:idx:idx #f -1 ,:immortal ,:none)
- (=:flo:flo 2 =:flo:flo #f -1 ,:immortal ,:none)
- (=:obj:flo 2 =:obj:flo #f -1 ,:immortal ,:none)
- (=:flo:obj 2 =:flo:obj #f -1 ,:immortal ,:none)
- )))
- ; Not used by the Sparc assembler; for information only.
- (define $immediate-primops$
- '((typetag-set! #x80)
- (eq? #x81)
- (+ #x82)
- (- #x83)
- (< #x84)
- (<= #x85)
- (= #x86)
- (> #x87)
- (>= #x88)
- (char<? #x89)
- (char<=? #x8a)
- (char=? #x8b)
- (char>? #x8c)
- (char>=? #x8d)
- (string-ref #x90)
- (vector-ref #x91)
- (bytevector-ref #x92)
- (bytevector-like-ref -1)
- (vector-like-ref -1)
- (fx+ -1)
- (fx- -1)
- (fx-- -1)
- (fx= -1)
- (fx< -1)
- (fx<= -1)
- (fx> -1)
- (fx>= -1)))
- ; Operations introduced by peephole optimizer.
- (define $reg/op1/branchf ; reg/op1/branchf prim,k1,L
- (make-mnemonic 'reg/op1/branchf))
- (define $reg/op2/branchf ; reg/op2/branchf prim,k1,k2,L
- (make-mnemonic 'reg/op2/branchf))
- (define $reg/op2imm/branchf ; reg/op2imm/branchf prim,k1,x,L
- (make-mnemonic 'reg/op2imm/branchf))
- (define $reg/op1/check ; reg/op1/check prim,k1,k2,k3,k4,exn
- (make-mnemonic 'reg/op1/check))
- (define $reg/op2/check ; reg/op2/check prim,k1,k2,k3,k4,k5,exn
- (make-mnemonic 'reg/op2/check))
- (define $reg/op2imm/check ; reg/op2imm/check prim,k1,x,k2,k3,k4,exn
- (make-mnemonic 'reg/op2imm/check))
- (define $reg/op1/setreg ; reg/op1/setreg prim,k1,kr
- (make-mnemonic 'reg/op1/setreg))
- (define $reg/op2/setreg ; reg/op2/setreg prim,k1,k2,kr
- (make-mnemonic 'reg/op2/setreg))
- (define $reg/op2imm/setreg ; reg/op2imm/setreg prim,k1,x,kr
- (make-mnemonic 'reg/op2imm/setreg))
- (define $reg/branchf ; reg/branchf k, L
- (make-mnemonic 'reg/branchf))
- (define $reg/return ; reg/return k
- (make-mnemonic 'reg/return))
- (define $reg/setglbl ; reg/setglbl k,x
- (make-mnemonic 'reg/setglbl))
- (define $reg/op3 ; reg/op3 prim,k1,k2,k3
- (make-mnemonic 'reg/op3))
- (define $const/setreg ; const/setreg const,k
- (make-mnemonic 'const/setreg))
- (define $const/return ; const/return const
- (make-mnemonic 'const/return))
- (define $global/setreg ; global/setreg x,k
- (make-mnemonic 'global/setreg))
- (define $setrtn/branch ; setrtn/branch L,doc
- (make-mnemonic 'setrtn/branch))
- (define $setrtn/invoke ; setrtn/invoke L
- (make-mnemonic 'setrtn/invoke))
- (define $global/invoke ; global/invoke global,n
- (make-mnemonic 'global/invoke))
- ; misc
- (define $cons 'cons)
- (define $car:pair 'car)
- (define $cdr:pair 'cdr)
- ; eof
- ; Target-specific representations.
- ;
- ; A few of these representation types must be specified for every target:
- ; rep:object
- ; rep:procedure
- ; rep:true
- ; rep:false
- ; rep:bottom
- (define-subtype 'true 'object) ; values that count as true
- (define-subtype 'eqtype 'object) ; can use EQ? instead of EQV?
- (define-subtype 'nonpointer 'eqtype) ; can omit write barrier
- (define-subtype 'eqtype1 'eqtype) ; eqtypes excluding #f
- (define-subtype 'boolean 'nonpointer)
- (define-subtype 'truth 'eqtype1) ; { #t }
- (define-subtype 'truth 'boolean)
- (define-subtype 'false 'boolean) ; { #f }
- (define-subtype 'eqtype1 'true)
- (define-subtype 'procedure 'true)
- (define-subtype 'vector 'true)
- (define-subtype 'bytevector 'true)
- (define-subtype 'string 'true)
- (define-subtype 'pair 'true)
- (define-subtype 'emptylist 'eqtype1)
- (define-subtype 'emptylist 'nonpointer)
- (define-subtype 'symbol 'eqtype1)
- (define-subtype 'char 'eqtype1)
- (define-subtype 'char 'nonpointer)
- (define-subtype 'number 'true)
- (define-subtype 'inexact 'number)
- (define-subtype 'flonum 'inexact)
- (define-subtype 'integer 'number)
- (define-subtype 'exact 'number)
- (define-subtype 'exactint 'integer)
- (define-subtype 'exactint 'exact)
- (define-subtype 'fixnum 'exactint)
- (define-subtype '!fixnum 'fixnum) ; 0 <= n
- (define-subtype 'fixnum! 'fixnum) ; n <= largest index
- (define-subtype 'index '!fixnum)
- (define-subtype 'index 'fixnum!)
- (define-subtype 'zero 'index)
- (define-subtype 'fixnum 'eqtype1)
- (define-subtype 'fixnum 'nonpointer)
- (compute-type-structure!)
- ; If the intersection of rep1 and rep2 is known precisely,
- ; but neither is a subtype of the other, then their intersection
- ; should be declared explicitly.
- ; Otherwise a conservative approximation will be used.
- (define-intersection 'true 'eqtype 'eqtype1)
- (define-intersection 'true 'boolean 'truth)
- (define-intersection 'exact 'integer 'exactint)
- (define-intersection '!fixnum 'fixnum! 'index)
- ;(display-unions-and-intersections)
- ; Parameters.
- (define rep:min_fixnum (- (expt 2 29)))
- (define rep:max_fixnum (- (expt 2 29) 1))
- (define rep:max_index (- (expt 2 24) 1))
- ; The representations we'll recognize for now.
- (define rep:object (symbol->rep 'object))
- (define rep:true (symbol->rep 'true))
- (define rep:truth (symbol->rep 'truth))
- (define rep:false (symbol->rep 'false))
- (define rep:boolean (symbol->rep 'boolean))
- (define rep:pair (symbol->rep 'pair))
- (define rep:symbol (symbol->rep 'symbol))
- (define rep:number (symbol->rep 'number))
- (define rep:zero (symbol->rep 'zero))
- (define rep:index (symbol->rep 'index))
- (define rep:fixnum (symbol->rep 'fixnum))
- (define rep:exactint (symbol->rep 'exactint))
- (define rep:flonum (symbol->rep 'flonum))
- (define rep:exact (symbol->rep 'exact))
- (define rep:inexact (symbol->rep 'inexact))
- (define rep:integer (symbol->rep 'integer))
- ;(define rep:real (symbol->rep 'real))
- (define rep:char (symbol->rep 'char))
- (define rep:string (symbol->rep 'string))
- (define rep:vector (symbol->rep 'vector))
- (define rep:procedure (symbol->rep 'procedure))
- (define rep:bottom (symbol->rep 'bottom))
- ; Given the value of a quoted constant, return its representation.
- (define (representation-of-value x)
- (cond ((boolean? x)
- (if x
- rep:truth
- rep:false))
- ((pair? x)
- rep:pair)
- ((symbol? x)
- rep:symbol)
- ((number? x)
- (cond ((and (exact? x)
- (integer? x))
- (cond ((zero? x)
- rep:zero)
- ((<= 0 x rep:max_index)
- rep:index)
- ((<= rep:min_fixnum
- x
- rep:max_fixnum)
- rep:fixnum)
- (else
- rep:exactint)))
- ((and (inexact? x)
- (real? x))
- rep:flonum)
- (else
- ; We're not tracking other numbers yet.
- rep:number)))
- ((char? x)
- rep:char)
- ((string? x)
- rep:string)
- ((vector? x)
- rep:vector)
- ; Everything counts as true except for #f.
- (else
- rep:true)))
- ; Tables that express the representation-specific operations,
- ; and the information about representations that are implied
- ; by certain operations.
- ; FIXME: Currently way incomplete, but good enough for testing.
- (define rep-specific
-
- (representation-table
-
- ; When the procedure in the first column is called with
- ; arguments described in the middle column, then the procedure
- ; in the last column can be called instead.
-
- '(
- ;(+ (index index) +:idx:idx)
- ;(+ (fixnum fixnum) +:fix:fix)
- ;(- (index index) -:idx:idx)
- ;(- (fixnum fixnum) -:fix:fix)
-
- (= (fixnum fixnum) =:fix:fix)
- (< (fixnum fixnum) <:fix:fix)
- (<= (fixnum fixnum) <=:fix:fix)
- (> (fixnum fixnum) >:fix:fix)
- (>= (fixnum fixnum) >=:fix:fix)
-
- ;(+ (flonum flonum) +:flo:flo)
- ;(- (flonum flonum) -:flo:flo)
- ;(= (flonum flonum) =:flo:flo)
- ;(< (flonum flonum) <:flo:flo)
- ;(<= (flonum flonum) <=:flo:flo)
- ;(> (flonum flonum) >:flo:flo)
- ;(>= (flonum flonum) >=:flo:flo)
-
- ;(vector-set!:trusted (vector fixnum nonpointer) vector-set!:trusted:imm)
- )))
- (define rep-result
-
- (representation-table
-
- ; When the procedure in the first column is called with
- ; arguments described in the middle column, then the result
- ; is described by the last column.
-
- '((fixnum? (fixnum) (truth))
- (vector? (vector) (truth))
- (<= (zero !fixnum) (truth))
- (>= (!fixnum zero) (truth))
- (<=:fix:fix (zero !fixnum) (truth))
- (>=:fix:fix (!fixnum zero) (truth))
-
- (+ (index index) (!fixnum))
- (+ (fixnum fixnum) (exactint))
- (- (index index) (fixnum!))
- (- (fixnum fixnum) (exactint))
-
- (+ (flonum flonum) (flonum))
- (- (flonum flonum) (flonum))
-
- ;(+:idx:idx (index index) (!fixnum))
- ;(-:idx:idx (index index) (fixnum!))
- ;(+:fix:fix (index index) (exactint))
- ;(+:fix:fix (fixnum fixnum) (exactint))
- ;(-:idx:idx (index index) (fixnum))
- ;(-:fix:fix (fixnum fixnum) (exactint))
-
- (make-vector (object object) (vector))
- (vector-length:vec (vector) (index))
- (cons (object object) (pair))
-
- ; Is it really all that useful to know that the result
- ; of these comparisons is a boolean?
-
- (= (number number) (boolean))
- (< (number number) (boolean))
- (<= (number number) (boolean))
- (> (number number) (boolean))
- (>= (number number) (boolean))
-
- (=:fix:fix (fixnum fixnum) (boolean))
- (<:fix:fix (fixnum fixnum) (boolean))
- (<=:fix:fix (fixnum fixnum) (boolean))
- (>:fix:fix (fixnum fixnum) (boolean))
- (>=:fix:fix (fixnum fixnum) (boolean))
- )))
- (define rep-informing
-
- (representation-table
-
- ; When the predicate in the first column is called in the test position
- ; of a conditional expression, on arguments described by the second
- ; column, then the arguments are described by the third column if the
- ; predicate returns true, and by the fourth column if the predicate
- ; returns false.
-
- '(
- (fixnum? (object) (fixnum) (object))
- (flonum? (object) (flonum) (object))
- (vector? (object) (vector) (object))
- (pair? (object) (pair) (object))
-
- (= (exactint index) (index index) (exactint index))
- (= (index exactint) (index index) (index exactint))
- (= (exactint !fixnum) (!fixnum !fixnum) (exactint !fixnum))
- (= (!fixnum exactint) (!fixnum !fixnum) (!fixnum exactint))
- (= (exactint fixnum!) (fixnum! fixnum!) (exactint fixnum!))
- (= (fixnum! exactint) (fixnum! fixnum!) (fixnum! exactint))
-
- (< (!fixnum fixnum!) (index index) (!fixnum fixnum!))
- (< (fixnum fixnum!) (fixnum! fixnum!) (fixnum fixnum!))
- (< (!fixnum fixnum) (!fixnum !fixnum) (!fixnum fixnum))
- (< (fixnum! !fixnum) (fixnum! !fixnum) (index index))
-
- (<= (!fixnum fixnum!) (index index) (!fixnum fixnum!))
- (<= (fixnum! !fixnum) (fixnum! !fixnum) (index index))
- (<= (fixnum fixnum!) (fixnum! fixnum!) (fixnum fixnum!))
- (<= (!fixnum fixnum) (!fixnum !fixnum) (!fixnum fixnum))
-
- (> (!fixnum fixnum!) (!fixnum fixnum!) (index index))
- (> (fixnum! !fixnum) (index index) (fixnum! !fixnum))
- (> (fixnum fixnum!) (fixnum fixnum!) (fixnum! fixnum!))
- (> (!fixnum fixnum) (!fixnum fixnum) (!fixnum !fixnum))
-
- (>= (!fixnum fixnum!) (!fixnum fixnum!) (index index))
- (>= (fixnum! !fixnum) (index index) (fixnum! !fixnum))
- (>= (fixnum fixnum!) (fixnum fixnum!) (fixnum! fixnum!))
- (>= (!fixnum fixnum) (!fixnum fixnum) (!fixnum !fixnum))
-
- (=:fix:fix (exactint index) (index index) (exactint index))
- (=:fix:fix (index exactint) (index index) (index exactint))
- (=:fix:fix (exactint !fixnum) (!fixnum !fixnum) (exactint !fixnum))
- (=:fix:fix (!fixnum exactint) (!fixnum !fixnum) (!fixnum exactint))
- (=:fix:fix (exactint fixnum!) (fixnum! fixnum!) (exactint fixnum!))
- (=:fix:fix (fixnum! exactint) (fixnum! fixnum!) (fixnum! exactint))
-
- (<:fix:fix (!fixnum fixnum!) (index index) (!fixnum fixnum!))
- (<:fix:fix (fixnum! !fixnum) (fixnum! !fixnum) (index index))
- (<:fix:fix (fixnum fixnum!) (fixnum! fixnum!) (fixnum fixnum!))
- (<:fix:fix (!fixnum fixnum) (!fixnum !fixnum) (!fixnum fixnum))
-
- (<=:fix:fix (!fixnum fixnum!) (index index) (!fixnum fixnum!))
- (<=:fix:fix (fixnum! !fixnum) (fixnum! !fixnum) (index index))
- (<=:fix:fix (fixnum fixnum!) (fixnum! fixnum!) (fixnum fixnum!))
- (<=:fix:fix (!fixnum fixnum) (!fixnum !fixnum) (!fixnum fixnum))
-
- (>:fix:fix (!fixnum fixnum!) (!fixnum fixnum!) (index index))
- (>:fix:fix (fixnum! !fixnum) (index index) (fixnum! !fixnum))
- (>:fix:fix (fixnum fixnum!) (fixnum fixnum!) (fixnum! fixnum!))
- (>:fix:fix (!fixnum fixnum) (!fixnum fixnum) (!fixnum !fixnum))
-
- (>=:fix:fix (!fixnum fixnum!) (!fixnum fixnum!) (index index))
- (>=:fix:fix (fixnum! !fixnum) (index index) (fixnum! !fixnum))
- (>=:fix:fix (fixnum fixnum!) (fixnum fixnum!) (fixnum! fixnum!))
- (>=:fix:fix (!fixnum fixnum) (!fixnum fixnum) (!fixnum !fixnum))
- )))
- ; Copyright 1991 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 25 April 1999.
- ;
- ; Second pass of the Twobit compiler:
- ; single assignment analysis, local source transformations,
- ; assignment elimination, and lambda lifting.
- ; The code for assignment elimination and lambda lifting
- ; are in a separate file.
- ;
- ; This pass operates as a source-to-source transformation on
- ; expressions written in the subset of Scheme described by the
- ; following grammar, where the input and output expressions
- ; satisfy certain additional invariants described below.
- ;
- ; "X ..." means zero or more occurrences of X.
- ;
- ; L --> (lambda (I_1 ...)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>)
- ; E)
- ; | (lambda (I_1 ... . I_rest)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>))
- ; E)
- ; D --> (define I L)
- ; E --> (quote K) ; constants
- ; | (begin I) ; variable references
- ; | L ; lambda expressions
- ; | (E0 E1 ...) ; calls
- ; | (set! I E) ; assignments
- ; | (if E0 E1 E2) ; conditionals
- ; | (begin E0 E1 E2 ...) ; sequential expressions
- ; I --> <identifier>
- ;
- ; R --> ((I <references> <assignments> <calls>) ...)
- ; F --> (I ...)
- ; G --> (I ...)
- ;
- ; Invariants that hold for the input only:
- ; * There are no internal definitions.
- ; * No identifier containing an upper case letter is bound anywhere.
- ; (Change the "name:..." variables if upper case is preferred.)
- ; * No identifier is bound in more than one place.
- ; * Each R contains one entry for every identifier bound in the
- ; formal argument list and the internal definition list that
- ; precede it. Each entry contains a list of pointers to all
- ; references to the identifier, a list of pointers to all
- ; assignments to the identifier, and a list of pointers to all
- ; calls to the identifier.
- ; * Except for constants, the expression does not share structure
- ; with the original input or itself, except that the references
- ; and assignments in R are guaranteed to share structure with
- ; the expression. Thus the expression may be side effected, and
- ; side effects to references or assignments obtained through R
- ; are guaranteed to change the references or assignments pointed
- ; to by R.
- ;
- ; Invariants that hold for the output only:
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * Each R contains one entry for every identifier bound in the
- ; formal argument list and the internal definition list that
- ; precede it. Each entry contains a list of pointers to all
- ; references to the identifier, a list of pointers to all
- ; assignments to the identifier, and a list of pointers to all
- ; calls to the identifier.
- ; * For each lambda expression, the associated F is a list of all
- ; the identifiers that occur free in the body of that lambda
- ; expression, and possibly a few extra identifiers that were
- ; once free but have been removed by optimization.
- ; * For each lambda expression, the associated G is a subset of F
- ; that contains every identifier that occurs free within some
- ; inner lambda expression that escapes, and possibly a few that
- ; don't. (Assignment-elimination does not calculate G exactly.)
- ; * Variables named IGNORED are neither referenced nor assigned.
- ; * Except for constants, the expression does not share structure
- ; with the original input or itself, except that the references
- ; and assignments in R are guaranteed to share structure with
- ; the expression. Thus the expression may be side effected, and
- ; side effects to references or assignments obtained through R
- ; are guaranteed to change the references or assignments pointed
- ; to by R.
- (define (pass2 exp)
- (simplify exp (make-notepad #f)))
- ; Given an expression and a "notepad" data structure that conveys
- ; inherited attributes, performs the appropriate optimizations and
- ; destructively modifies the notepad to record various attributes
- ; that it synthesizes while traversing the expression. In particular,
- ; any nested lambda expressions and any variable references will be
- ; noted in the notepad.
- (define (simplify exp notepad)
- (case (car exp)
- ((quote) exp)
- ((lambda) (simplify-lambda exp notepad))
- ((set!) (simplify-assignment exp notepad))
- ((if) (simplify-conditional exp notepad))
- ((begin) (if (variable? exp)
- (begin (notepad-var-add! notepad (variable.name exp))
- exp)
- (simplify-sequential exp notepad)))
- (else (simplify-call exp notepad))))
- ; Most optimization occurs here.
- ; The right hand sides of internal definitions are simplified,
- ; as is the body.
- ; Internal definitions of enclosed lambda expressions may
- ; then be lifted to this one.
- ; Single assignment analysis creates internal definitions.
- ; Single assignment elimination converts single assignments
- ; to bindings where possible, and renames arguments whose value
- ; is ignored.
- ; Assignment elimination then replaces all remaining assigned
- ; variables by heap-allocated cells.
- (define (simplify-lambda exp notepad)
- (notepad-lambda-add! notepad exp)
- (let ((defs (lambda.defs exp))
- (body (lambda.body exp))
- (newnotepad (make-notepad exp)))
- (for-each (lambda (def)
- (simplify-lambda (def.rhs def) newnotepad))
- defs)
- (lambda.body-set! exp (simplify body newnotepad))
- (lambda.F-set! exp (notepad-free-variables newnotepad))
- (lambda.G-set! exp (notepad-captured-variables newnotepad))
- (single-assignment-analysis exp newnotepad)
- (let ((known-lambdas (notepad.nonescaping newnotepad)))
- (for-each (lambda (L)
- (if (memq L known-lambdas)
- (lambda-lifting L exp)
- (lambda-lifting L L)))
- (notepad.lambdas newnotepad))))
- (single-assignment-elimination exp notepad)
- (assignment-elimination exp)
- (if (not (notepad.parent notepad))
- ; This is an outermost lambda expression.
- (lambda-lifting exp exp))
- exp)
- ; SIMPLIFY-ASSIGNMENT performs this transformation:
- ;
- ; (set! I (begin ... E))
- ; -> (begin ... (set! I E))
- (define (simplify-assignment exp notepad)
- (notepad-var-add! notepad (assignment.lhs exp))
- (let ((rhs (simplify (assignment.rhs exp) notepad)))
- (cond ((begin? rhs)
- (let ((exprs (reverse (begin.exprs rhs))))
- (assignment.rhs-set! exp (car exprs))
- (post-simplify-begin
- (make-begin (reverse (cons exp (cdr exprs))))
- notepad)))
- (else (assignment.rhs-set! exp rhs) exp))))
- (define (simplify-sequential exp notepad)
- (let ((exprs (map (lambda (exp) (simplify exp notepad))
- (begin.exprs exp))))
- (begin.exprs-set! exp exprs)
- (post-simplify-begin exp notepad)))
- ; Given (BEGIN E0 E1 E2 ...) where the E_i are simplified expressions,
- ; flattens any nested BEGINs and removes trivial expressions that
- ; don't appear in the last position. The second argument is used only
- ; if a lambda expression is removed.
- ; This procedure is careful to return E instead of (BEGIN E).
- ; Fairly harmless bug: a variable reference removed by this procedure
- ; may remain on the notepad when it shouldn't.
- (define (post-simplify-begin exp notepad)
- (let ((unspecified-expression (make-unspecified)))
- ; (flatten exprs '()) returns the flattened exprs in reverse order.
- (define (flatten exprs flattened)
- (cond ((null? exprs) flattened)
- ((begin? (car exprs))
- (flatten (cdr exprs)
- (flatten (begin.exprs (car exprs)) flattened)))
- (else (flatten (cdr exprs) (cons (car exprs) flattened)))))
- (define (filter exprs filtered)
- (if (null? exprs)
- filtered
- (let ((exp (car exprs)))
- (cond ((constant? exp) (filter (cdr exprs) filtered))
- ((variable? exp) (filter (cdr exprs) filtered))
- ((lambda? exp)
- (notepad.lambdas-set!
- notepad
- (remq exp (notepad.lambdas notepad)))
- (filter (cdr exprs) filtered))
- ((equal? exp unspecified-expression)
- (filter (cdr exprs) filtered))
- (else (filter (cdr exprs) (cons exp filtered)))))))
- (let ((exprs (flatten (begin.exprs exp) '())))
- (begin.exprs-set! exp (filter (cdr exprs) (list (car exprs))))
- (if (null? (cdr (begin.exprs exp)))
- (car (begin.exprs exp))
- exp))))
- ; SIMPLIFY-CALL performs this transformation:
- ;
- ; (... (begin ... E) ...)
- ; -> (begin ... (... E ...))
- ;
- ; It also takes care of LET transformations.
- (define (simplify-call exp notepad)
- (define (loop args newargs exprs)
- (cond ((null? args)
- (finish newargs exprs))
- ((begin? (car args))
- (let ((newexprs (reverse (begin.exprs (car args)))))
- (loop (cdr args)
- (cons (car newexprs) newargs)
- (append (cdr newexprs) exprs))))
- (else (loop (cdr args) (cons (car args) newargs) exprs))))
- (define (finish newargs exprs)
- (call.args-set! exp (reverse newargs))
- (let* ((newexp
- (if (lambda? (call.proc exp))
- (simplify-let exp notepad)
- (begin
- (call.proc-set! exp
- (simplify (call.proc exp) notepad))
- exp)))
- (newexp
- (if (and (call? newexp)
- (variable? (call.proc newexp)))
- (let* ((procname (variable.name (call.proc newexp)))
- (args (call.args newexp))
- (entry
- (and (not (null? args))
- (constant? (car args))
- (integrate-usual-procedures)
- (every? constant? args)
- (let ((entry (constant-folding-entry procname)))
- (and entry
- (let ((predicates
- (constant-folding-predicates entry)))
- (and (= (length args)
- (length predicates))
- (let loop ((args args)
- (predicates predicates))
- (cond ((null? args) entry)
- (((car predicates)
- (constant.value
- (car args)))
- (loop (cdr args)
- (cdr predicates)))
- (else #f))))))))))
- (if entry
- (make-constant (apply (constant-folding-folder entry)
- (map constant.value args)))
- newexp))
- newexp)))
- (cond ((and (call? newexp)
- (begin? (call.proc newexp)))
- (let ((exprs0 (reverse (begin.exprs (call.proc newexp)))))
- (call.proc-set! newexp (car exprs0))
- (post-simplify-begin
- (make-begin (reverse
- (cons newexp
- (append (cdr exprs0) exprs))))
- notepad)))
- ((null? exprs)
- newexp)
- (else
- (post-simplify-begin
- (make-begin (reverse (cons newexp exprs)))
- notepad)))))
- (call.args-set! exp (map (lambda (arg) (simplify arg notepad))
- (call.args exp)))
- (loop (call.args exp) '() '()))
- ; SIMPLIFY-LET performs these transformations:
- ;
- ; ((lambda (I_1 ... I_k . I_rest) ---) E1 ... Ek Ek+1 ...)
- ; -> ((lambda (I_1 ... I_k I_rest) ---) E1 ... Ek (LIST Ek+1 ...))
- ;
- ; ((lambda (I1 I2 ...) (begin D ...) (quote ...) E) L ...)
- ; -> ((lambda (I2 ...) (begin (define I1 L) D ...) (quote ...) E) ...)
- ;
- ; provided I1 is not assigned and each reference to I1 is in call position.
- ;
- ; ((lambda (I1)
- ; (begin)
- ; (quote ((I1 ((begin I1)) () ())))
- ; (begin I1))
- ; E1)
- ;
- ; -> E1
- ;
- ; ((lambda (I1)
- ; (begin)
- ; (quote ((I1 ((begin I1)) () ())))
- ; (if (begin I1) E2 E3))
- ; E1)
- ;
- ; -> (if E1 E2 E3)
- ;
- ; (Together with SIMPLIFY-CONDITIONAL, this cleans up the output of the OR
- ; macro and enables certain control optimizations.)
- ;
- ; ((lambda (I1 I2 ...)
- ; (begin D ...)
- ; (quote (... (I <references> () <calls>) ...) ...)
- ; E)
- ; K ...)
- ; -> ((lambda (I2 ...)
- ; (begin D' ...)
- ; (quote (... ...) ...)
- ; E')
- ; ...)
- ;
- ; where D' ... and E' ... are obtained from D ... and E ...
- ; by replacing all references to I1 by K. This transformation
- ; applies if K is a constant that can be duplicated without changing
- ; its EQV? behavior.
- ;
- ; ((lambda () (begin) (quote ...) E)) -> E
- ;
- ; ((lambda (IGNORED I2 ...) ---) E1 E2 ...)
- ; -> (begin E1 ((lambda (I2 ...) ---) E2 ...))
- ;
- ; (Single assignment analysis, performed by the simplifier for lambda
- ; expressions, detects unused arguments and replaces them in the argument
- ; list by the special identifier IGNORED.)
- (define (simplify-let exp notepad)
- (define proc (call.proc exp))
-
- ; Loop1 operates before simplification of the lambda body.
-
- (define (loop1 formals actuals processed-formals processed-actuals)
- (cond ((null? formals)
- (if (not (null? actuals))
- (pass2-error p2error:wna exp))
- (return1 processed-formals processed-actuals))
- ((symbol? formals)
- (return1 (cons formals processed-formals)
- (cons (make-call-to-LIST actuals) processed-actuals)))
- ((null? actuals)
- (pass2-error p2error:wna exp)
- (return1 processed-formals
- processed-actuals))
- ((and (lambda? (car actuals))
- (let ((Rinfo (R-lookup (lambda.R proc) (car formals))))
- (and (null? (R-entry.assignments Rinfo))
- (= (length (R-entry.references Rinfo))
- (length (R-entry.calls Rinfo))))))
- (let ((I (car formals))
- (L (car actuals)))
- (notepad-nonescaping-add! notepad L)
- (lambda.defs-set! proc
- (cons (make-definition I L)
- (lambda.defs proc)))
- (standardize-known-calls L
- (R-entry.calls
- (R-lookup (lambda.R proc) I)))
- (lambda.F-set! proc (union (lambda.F proc)
- (free-variables L)))
- (lambda.G-set! proc (union (lambda.G proc) (lambda.G L))))
- (loop1 (cdr formals)
- (cdr actuals)
- processed-formals
- processed-actuals))
- ((and (constant? (car actuals))
- (let ((x (constant.value (car actuals))))
- (or (boolean? x)
- (number? x)
- (symbol? x)
- (char? x))))
- (let* ((I (car formals))
- (Rinfo (R-lookup (lambda.R proc) I)))
- (if (null? (R-entry.assignments Rinfo))
- (begin
- (for-each (lambda (ref)
- (variable-set! ref (car actuals)))
- (R-entry.references Rinfo))
- (lambda.R-set! proc (remq Rinfo (lambda.R proc)))
- (lambda.F-set! proc (remq I (lambda.F proc)))
- (lambda.G-set! proc (remq I (lambda.G proc)))
- (loop1 (cdr formals)
- (cdr actuals)
- processed-formals
- processed-actuals))
- (loop1 (cdr formals)
- (cdr actuals)
- (cons (car formals) processed-formals)
- (cons (car actuals) processed-actuals)))))
- (else (if (null? actuals)
- (pass2-error p2error:wna exp))
- (loop1 (cdr formals)
- (cdr actuals)
- (cons (car formals) processed-formals)
- (cons (car actuals) processed-actuals)))))
-
- (define (return1 rev-formals rev-actuals)
- (let ((formals (reverse rev-formals))
- (actuals (reverse rev-actuals)))
- (lambda.args-set! proc formals)
- (if (and (not (null? formals))
- (null? (cdr formals))
- (let* ((x (car formals))
- (R (lambda.R proc))
- (refs (references R x)))
- (and (= 1 (length refs))
- (null? (assignments R x)))))
- (let ((x (car formals))
- (body (lambda.body proc)))
- (cond ((and (variable? body)
- (eq? x (variable.name body)))
- (simplify (car actuals) notepad))
- ((and (conditional? body)
- (let ((B0 (if.test body)))
- (variable? B0)
- (eq? x (variable.name B0))))
- (if.test-set! body (car actuals))
- (simplify body notepad))
- (else
- (return1-finish formals actuals))))
- (return1-finish formals actuals))))
-
- (define (return1-finish formals actuals)
- (simplify-lambda proc notepad)
- (loop2 formals actuals '() '() '()))
-
- ; Loop2 operates after simplification of the lambda body.
-
- (define (loop2 formals actuals processed-formals processed-actuals for-effect)
- (cond ((null? formals)
- (return2 processed-formals processed-actuals for-effect))
- ((ignored? (car formals))
- (loop2 (cdr formals)
- (cdr actuals)
- processed-formals
- processed-actuals
- (cons (car actuals) for-effect)))
- (else (loop2 (cdr formals)
- (cdr actuals)
- (cons (car formals) processed-formals)
- (cons (car actuals) processed-actuals)
- for-effect))))
-
- (define (return2 rev-formals rev-actuals rev-for-effect)
- (let ((formals (reverse rev-formals))
- (actuals (reverse rev-actuals))
- (for-effect (reverse rev-for-effect)))
- (lambda.args-set! proc formals)
- (call.args-set! exp actuals)
- (let ((exp (if (and (null? actuals)
- (or (null? (lambda.defs proc))
- (and (notepad.parent notepad)
- (POLICY:LIFT? proc
- (notepad.parent notepad)
- (map (lambda (def) '())
- (lambda.defs proc))))))
- (begin (for-each (lambda (I)
- (notepad-var-add! notepad I))
- (lambda.F proc))
- (if (not (null? (lambda.defs proc)))
- (let ((parent (notepad.parent notepad))
- (defs (lambda.defs proc))
- (R (lambda.R proc)))
- (lambda.defs-set!
- parent
- (append defs (lambda.defs parent)))
- (lambda.defs-set! proc '())
- (lambda.R-set!
- parent
- (append (map (lambda (def)
- (R-lookup R (def.lhs def)))
- defs)
- (lambda.R parent)))))
- (lambda.body proc))
- exp)))
- (if (null? for-effect)
- exp
- (post-simplify-begin (make-begin (append for-effect (list exp)))
- notepad)))))
-
- (notepad-nonescaping-add! notepad proc)
- (loop1 (lambda.args proc) (call.args exp) '() '()))
- ; Single assignment analysis performs the transformation
- ;
- ; (lambda (... I ...)
- ; (begin D ...)
- ; (quote (... (I <references> ((set! I L)) <calls>) ...) ...)
- ; (begin (set! I L) E1 ...))
- ; -> (lambda (... IGNORED ...)
- ; (begin (define I L) D ...)
- ; (quote (... (I <references> () <calls>) ...) ...)
- ; (begin E1 ...))
- ;
- ; For best results, pass 1 should sort internal definitions and LETRECs so
- ; that procedure definitions/bindings come first.
- ;
- ; This procedure operates by side effect.
- (define (single-assignment-analysis L notepad)
- (let ((formals (lambda.args L))
- (defs (lambda.defs L))
- (R (lambda.R L))
- (body (lambda.body L)))
- (define (finish! exprs escapees)
- (begin.exprs-set! body
- (append (reverse escapees)
- exprs))
- (lambda.body-set! L (post-simplify-begin body '())))
- (if (begin? body)
- (let loop ((exprs (begin.exprs body))
- (escapees '()))
- (let ((first (car exprs)))
- (if (and (assignment? first)
- (not (null? (cdr exprs))))
- (let ((I (assignment.lhs first))
- (rhs (assignment.rhs first)))
- (if (and (lambda? rhs)
- (local? R I)
- (= 1 (length (assignments R I))))
- (if (= (length (calls R I))
- (length (references R I)))
- (begin (notepad-nonescaping-add! notepad rhs)
- (flag-as-ignored I L)
- (lambda.defs-set! L
- (cons (make-definition I rhs)
- (lambda.defs L)))
- (assignments-set! R I '())
- (standardize-known-calls
- rhs
- (R-entry.calls (R-lookup R I)))
- (loop (cdr exprs) escapees))
- (loop (cdr exprs)
- (cons (car exprs) escapees)))
- (finish! exprs escapees)))
- (finish! exprs escapees)))))))
- (define (standardize-known-calls L calls)
- (let ((formals (lambda.args L)))
- (cond ((not (list? formals))
- (let* ((newformals (make-null-terminated formals))
- (n (- (length newformals) 1)))
- (lambda.args-set! L newformals)
- (for-each (lambda (call)
- (if (>= (length (call.args call)) n)
- (call.args-set!
- call
- (append (list-head (call.args call) n)
- (list
- (make-call-to-LIST
- (list-tail (call.args call) n)))))
- (pass2-error p2error:wna call)))
- calls)))
- (else (let ((n (length formals)))
- (for-each (lambda (call)
- (if (not (= (length (call.args call)) n))
- (pass2-error p2error:wna call)))
- calls))))))
- ; Copyright 1991 William D Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 13 November 1998
- ;
- ; Second pass of the Twobit compiler, part 2:
- ; single assignment elimination, assignment elimination,
- ; and lambda lifting.
- ;
- ; See part 1 for further documentation.
- ; Single assignment elimination performs the transformation
- ;
- ; (lambda (... I1 ... In ...)
- ; (begin D ...)
- ; (begin (set! I1 E1)
- ; ...
- ; (set! In En)
- ; E ...))
- ; -> (lambda (... IGNORED ... IGNORED ...)
- ; (let* ((I1 E1) ... (In En))
- ; (begin D ...)
- ; (begin E ...)))
- ;
- ; provided for each k:
- ;
- ; 1. Ik does not occur in E1, ..., Ek.
- ; 2. Either E1 through Ek contain no procedure calls
- ; or Ik is not referenced by an escaping lambda expression.
- ; 3. Ik is assigned only once.
- ;
- ; I doubt whether the third condition is really necessary, but
- ; dropping it would involve a more complex calculation of the
- ; revised referencing information.
- ;
- ; A more precise description of the transformation:
- ;
- ; (lambda (... I1 ... In ...)
- ; (begin (define F1 L1) ...)
- ; (quote (... (I1 <references> ((set! I1 E1)) <calls>) ...
- ; (In <references> ((set! In En)) <calls>)
- ; (F1 <references> () <calls>) ...) ...)
- ; (begin (set! I1 E1) ... (set! In En) E ...))
- ; -> (lambda (... IGNORED ... IGNORED ...)
- ; (begin)
- ; (quote (...) ...)
- ; ((lambda (I1)
- ; (begin)
- ; (quote ((I1 <references> () <calls>)) ...)
- ; ...
- ; ((lambda (In)
- ; (begin (define F1 L1) ...)
- ; (quote (... (In <references> () <calls>)
- ; (F1 <references> () <calls>) ...) ...)
- ; (begin E ...))
- ; En)
- ; ...)
- ; E1))
- ;
- ; For best results, pass 1 should sort internal definitions and LETRECs
- ; so that procedure definitions/bindings come first, followed by
- ; definitions/bindings whose right hand side contains no calls,
- ; followed by definitions/bindings of variables that do not escape,
- ; followed by all other definitions/bindings.
- ;
- ; Pass 1 can't tell which variables escape, however. Pass 2 can't tell
- ; which variables escape either until all enclosed lambda expressions
- ; have been simplified and the first transformation above has been
- ; performed. That is why single assignment analysis precedes single
- ; assignment elimination. As implemented here, an assignment that does
- ; not satisfy the conditions above will prevent the transformation from
- ; being applied to any subsequent assignments.
- ;
- ; This procedure operates by side effect.
- (define (single-assignment-elimination L notepad)
-
- (if (begin? (lambda.body L))
-
- (let* ((formals (make-null-terminated (lambda.args L)))
- (defined (map def.lhs (lambda.defs L)))
- (escaping (intersection formals
- (notepad-captured-variables notepad)))
- (R (lambda.R L)))
-
- ; Given:
- ; exprs that remain in the body;
- ; assigns that will be replaced by let* variables;
- ; call-has-occurred?, a boolean;
- ; free variables of the assigns;
- ; Performs the transformation described above.
-
- (define (loop exprs assigns call-has-occurred? free)
- (cond ((null? (cdr exprs))
- (return exprs assigns))
- ((assignment? (car exprs))
- (let ((I1 (assignment.lhs (car exprs)))
- (E1 (assignment.rhs (car exprs))))
- (if (and (memq I1 formals)
- (= (length (assignments R I1)) 1)
- (not (and call-has-occurred?
- (memq I1 escaping))))
- (let* ((free-in-E1 (free-variables E1))
- (newfree (union free-in-E1 free)))
- (if (or (memq I1 newfree)
- (not
- (empty-set?
- (intersection free-in-E1 defined))))
- (return exprs assigns)
- (loop (cdr exprs)
- (cons (car exprs) assigns)
- (or call-has-occurred?
- (might-return-twice? E1))
- newfree)))
- (return exprs assigns))))
- (else (return exprs assigns))))
-
- (define (return exprs assigns)
- (if (not (null? assigns))
- (let ((I (assignment.lhs (car assigns)))
- (E (assignment.rhs (car assigns)))
- (defs (lambda.defs L))
- (F (lambda.F L))
- (G (lambda.G L)))
- (flag-as-ignored I L)
- (assignments-set! R I '())
- (let ((L2 (make-lambda (list I)
- defs
- (cons (R-entry R I)
- (map (lambda (def)
- (R-entry R (def.lhs def)))
- defs))
- F
- G
- (lambda.decls L)
- (lambda.doc L)
- (make-begin exprs))))
- (lambda.defs-set! L '())
- (for-each (lambda (entry)
- (lambda.R-set! L (remq entry R)))
- (lambda.R L2))
- (return-loop (cdr assigns) (make-call L2 (list E)))))))
-
- (define (return-loop assigns body)
- (if (null? assigns)
- (let ((L3 (call.proc body)))
- (lambda.body-set! L body)
- (lambda-lifting L3 L))
- (let* ((I (assignment.lhs (car assigns)))
- (E (assignment.rhs (car assigns)))
- (L3 (call.proc body))
- (F (remq I (lambda.F L3)))
- (G (remq I (lambda.G L3))))
- (flag-as-ignored I L)
- (assignments-set! R I '())
- (let ((L2 (make-lambda (list I)
- '()
- (list (R-entry R I))
- F
- G
- (lambda.decls L)
- (lambda.doc L)
- body)))
- (lambda.R-set! L (remq (R-entry R I) R))
- (lambda-lifting L3 L2)
- (return-loop (cdr assigns) (make-call L2 (list E)))))))
-
- (loop (begin.exprs (lambda.body L)) '() #f '())))
-
- L)
- ; Temporary definitions.
- (define (free-variables exp)
- (case (car exp)
- ((quote) '())
- ((lambda) (difference (lambda.F exp)
- (make-null-terminated (lambda.args exp))))
- ((set!) (union (list (assignment.lhs exp))
- (free-variables (assignment.rhs exp))))
- ((if) (union (free-variables (if.test exp))
- (free-variables (if.then exp))
- (free-variables (if.else exp))))
- ((begin) (if (variable? exp)
- (list (variable.name exp))
- (apply union (map free-variables (begin.exprs exp)))))
- (else (apply union (map free-variables exp)))))
- (define (might-return-twice? exp)
- (case (car exp)
- ((quote) #f)
- ((lambda) #f)
- ((set!) (might-return-twice? (assignment.rhs exp)))
- ((if) (or (might-return-twice? (if.test exp))
- (might-return-twice? (if.then exp))
- (might-return-twice? (if.else exp))))
- ((begin) (if (variable? exp)
- #f
- (some? might-return-twice? (begin.exprs exp))))
- (else #t)))
- ; Assignment elimination replaces variables that appear on the left
- ; hand side of an assignment by data structures. This is necessary
- ; to avoid some nasty complications with lambda lifting.
- ;
- ; This procedure operates by side effect.
- (define (assignment-elimination L)
- (let ((R (lambda.R L)))
-
- ; Given a list of entries, return those for assigned variables.
-
- (define (loop entries assigned)
- (cond ((null? entries)
- (if (not (null? assigned))
- (eliminate assigned)))
- ((not (null? (R-entry.assignments (car entries))))
- (loop (cdr entries) (cons (car entries) assigned)))
- ((null? (R-entry.references (car entries)))
- (flag-as-ignored (R-entry.name (car entries)) L)
- (loop (cdr entries) assigned))
- (else (loop (cdr entries) assigned))))
-
- ; Given a list of entries for assigned variables I1 ...,
- ; remove the assignments by replacing the body by a LET of the form
- ; ((LAMBDA (V1 ...) ...) (MAKE-CELL I1) ...), by replacing references
- ; by calls to CELL-REF, and by replacing assignments by calls to
- ; CELL-SET!.
-
- (define (eliminate assigned)
- (let* ((oldnames (map R-entry.name assigned))
- (newnames (map generate-new-name oldnames)))
- (let ((augmented-entries (map list newnames assigned))
- (renaming-alist (map cons oldnames newnames))
- (defs (lambda.defs L)))
- (for-each cellify! augmented-entries)
- (for-each (lambda (def)
- (do ((free (lambda.F (def.rhs def)) (cdr free)))
- ((null? free))
- (let ((z (assq (car free) renaming-alist)))
- (if z
- (set-car! free (cdr z))))))
- defs)
- (let ((newbody
- (make-call
- (make-lambda (map car augmented-entries)
- defs
- (union (map (lambda (def)
- (R-entry R (def.lhs def)))
- defs)
- (map new-reference-info augmented-entries))
- (union (list name:CELL-REF name:CELL-SET!)
- newnames
- (difference (lambda.F L) oldnames))
- (union (list name:CELL-REF name:CELL-SET!)
- newnames
- (difference (lambda.G L) oldnames))
- (lambda.decls L)
- (lambda.doc L)
- (lambda.body L))
- (map (lambda (name)
- (make-call (make-variable name:MAKE-CELL)
- (list (make-variable name))))
- (map R-entry.name assigned)))))
- (lambda.F-set! L (union (list name:MAKE-CELL name:CELL-REF name:CELL-SET!)
- (difference (lambda.F L)
- (map def.lhs (lambda.defs L)))))
- (lambda.defs-set! L '())
- (for-each update-old-reference-info!
- (map (lambda (arg)
- (car (call.args arg)))
- (call.args newbody)))
- (lambda.body-set! L newbody)
- (lambda-lifting (call.proc newbody) L)))))
-
- (define (generate-new-name name)
- (string->symbol (string-append cell-prefix (symbol->string name))))
-
- ; In addition to replacing references and assignments involving the
- ; old variable by calls to CELL-REF and CELL-SET! on the new, CELLIFY!
- ; uses the old entry to collect the referencing information for the
- ; new variable.
-
- (define (cellify! augmented-entry)
- (let ((newname (car augmented-entry))
- (entry (cadr augmented-entry)))
- (do ((refs (R-entry.references entry)
- (cdr refs)))
- ((null? refs))
- (let* ((reference (car refs))
- (newref (make-variable newname)))
- (set-car! reference (make-variable name:CELL-REF))
- (set-car! (cdr reference) newref)
- (set-car! refs newref)))
- (do ((assigns (R-entry.assignments entry)
- (cdr assigns)))
- ((null? assigns))
- (let* ((assignment (car assigns))
- (newref (make-variable newname)))
- (set-car! assignment (make-variable name:CELL-SET!))
- (set-car! (cdr assignment) newref)
- (R-entry.references-set! entry
- (cons newref
- (R-entry.references entry)))))
- (R-entry.assignments-set! entry '())))
-
- ; This procedure creates a brand new entry for a new variable, extracting
- ; the references stored in the old entry by CELLIFY!.
-
- (define (new-reference-info augmented-entry)
- (make-R-entry (car augmented-entry)
- (R-entry.references (cadr augmented-entry))
- '()
- '()))
-
- ; This procedure updates the old entry to reflect the fact that it is
- ; now referenced once and never assigned.
-
- (define (update-old-reference-info! ref)
- (references-set! R (variable.name ref) (list ref))
- (assignments-set! R (variable.name ref) '())
- (calls-set! R (variable.name ref) '()))
-
- (loop R '())))
- ; Lambda lifting raises internal definitions to outer scopes to avoid
- ; having to choose between creating a closure or losing tail recursion.
- ; If L is not #f, then L2 is a lambda expression nested within L.
- ; Any internal definitions that occur within L2 may be lifted to L
- ; by adding extra arguments to the defined procedure and to all calls to it.
- ; Lambda lifting is not a clear win, because the extra arguments could
- ; easily become more expensive than creating a closure and referring
- ; to the non-local arguments through the closure. The heuristics used
- ; to decide whether to lift a group of internal definitions are isolated
- ; within the POLICY:LIFT? procedure.
- ; L2 can be the same as L, so the order of side effects is critical.
- (define (lambda-lifting L2 L)
-
- ; The call to sort is optional. It gets the added arguments into
- ; the same order they appear in the formals list, which is an
- ; advantage for register targeting.
-
- (define (lift L2 L args-to-add)
- (let ((formals (make-null-terminated (lambda.args L2))))
- (do ((defs (lambda.defs L2) (cdr defs))
- (args-to-add args-to-add (cdr args-to-add)))
- ((null? defs))
- (let* ((def (car defs))
- (entry (R-lookup (lambda.R L2) (def.lhs def)))
- (calls (R-entry.calls entry))
- (added (twobit-sort (lambda (x y)
- (let ((xx (memq x formals))
- (yy (memq y formals)))
- (if (and xx yy)
- (> (length xx) (length yy))
- #t)))
- (car args-to-add)))
- (L3 (def.rhs def)))
- ; The flow equation guarantees that these added arguments
- ; will occur free by the time this round of lifting is done.
- (lambda.F-set! L3 (union added (lambda.F L3)))
- (lambda.args-set! L3 (append added (lambda.args L3)))
- (for-each (lambda (call)
- (let ((newargs (map make-variable added)))
- ; The referencing information is made obsolete here!
- (call.args-set! call
- (append newargs (call.args call)))))
- calls)
- (lambda.R-set! L2 (remq entry (lambda.R L2)))
- (lambda.R-set! L (cons entry (lambda.R L)))
- ))
- (if (not (eq? L2 L))
- (begin
- (lambda.defs-set! L (append (lambda.defs L2) (lambda.defs L)))
- (lambda.defs-set! L2 '())))))
-
- (if L
- (if (not (null? (lambda.defs L2)))
- (let ((args-to-add (compute-added-arguments
- (lambda.defs L2)
- (make-null-terminated (lambda.args L2)))))
- (if (POLICY:LIFT? L2 L args-to-add)
- (lift L2 L args-to-add))))))
- ; Given a list of definitions ((define f1 ...) ...) and a set of formals
- ; N over which the definitions may be lifted, returns a list of the
- ; subsets of N that need to be added to each procedure definition
- ; as new arguments.
- ;
- ; Algorithm: Let F_i be the variables that occur free in the body of
- ; the lambda expression associated with f_i. Construct the call graph.
- ; Solve the flow equations
- ;
- ; A_i = (F_i /\ N) \/ (\/ {A_j | A_i calls A_j})
- ;
- ; where /\ is intersection and \/ is union.
- (define (compute-added-arguments defs formals)
- (let ((procs (map def.lhs defs))
- (freevars (map lambda.F (map def.rhs defs))))
- (let ((callgraph (map (lambda (names)
- (map (lambda (name)
- (position name procs))
- (intersection names procs)))
- freevars))
- (added_0 (map (lambda (names)
- (intersection names formals))
- freevars)))
- (vector->list
- (compute-fixedpoint
- (make-vector (length procs) '())
- (list->vector (map (lambda (term0 indexes)
- (lambda (approximations)
- (union term0
- (apply union
- (map (lambda (i)
- (vector-ref approximations i))
- indexes)))))
- added_0
- callgraph))
- set-equal?)))))
- (define (position x l)
- (cond ((eq? x (car l)) 0)
- (else (+ 1 (position x (cdr l))))))
- ; Given a vector of starting approximations,
- ; a vector of functions that compute a next approximation
- ; as a function of the vector of approximations,
- ; and an equality predicate,
- ; returns a vector of fixed points.
- (define (compute-fixedpoint v functions equiv?)
- (define (loop i flag)
- (if (negative? i)
- (if flag
- (loop (- (vector-length v) 1) #f)
- v)
- (let ((next_i ((vector-ref functions i) v)))
- (if (equiv? next_i (vector-ref v i))
- (loop (- i 1) flag)
- (begin (vector-set! v i next_i)
- (loop (- i 1) #t))))))
- (loop (- (vector-length v) 1) #f))
- ; Given a lambda expression L2, its parent lambda expression
- ; L (which may be the same as L2, or #f), and a list of the
- ; lists of arguments that would need to be added to known
- ; local procedures, returns #t iff lambda lifting should be done.
- ;
- ; Here are some heuristics:
- ;
- ; Don't lift if it means adding too many arguments.
- ; Don't lift large groups of definitions.
- ; In questionable cases it is better to lift to an outer
- ; lambda expression that already contains internal
- ; definitions than to one that doesn't.
- ; It is better not to lift if the body contains a lambda
- ; expression that has to be closed anyway.
- (define (POLICY:LIFT? L2 L args-to-add)
- (and (lambda-optimizations)
- (not (lambda? (lambda.body L2)))
- (every? (lambda (addlist)
- (< (length addlist) 6))
- args-to-add)))
- ; Copyright 1991 William D Clinger (for SIMPLIFY-CONDITIONAL)
- ; Copyright 1999 William D Clinger (for everything else)
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 11 April 1999.
- ;
- ; Some source transformations on IF expressions:
- ;
- ; (if '#f E1 E2) E2
- ; (if 'K E1 E2) E1 K != #f
- ; (if (if B0 '#f '#f) E1 E2) (begin B0 E2)
- ; (if (if B0 '#f 'K ) E1 E2) (if B0 E2 E1) K != #f
- ; (if (if B0 'K '#f) E1 E2) (if B0 E1 E2) K != #f
- ; (if (if B0 'K1 'K2) E1 E2) (begin B0 E1) K1, K2 != #f
- ; (if (if B0 (if B1 #t #f) B2) E1 E2) (if (if B0 B1 B2) E1 E2)
- ; (if (if B0 B1 (if B2 #t #f)) E1 E2) (if (if B0 B1 B2) E1 E2)
- ; (if (if X X B0 ) E1 E2) (if (if X #t B0) E1 E2) X a variable
- ; (if (if X B0 X ) E1 E2) (if (if X B0 #f) E1 E2) X a variable
- ; (if ((lambda (X) (if ((lambda (X)
- ; (if X X B2)) B0) (if X #t (if B2 #t #f))) B0)
- ; E1 E2) E1 E2)
- ; (if (begin ... B0) E1 E2) (begin ... (if B0 E1 E2))
- ; (if (not E0) E1 E2) (if E0 E2 E1) not is integrable
- ;
- ; FIXME: Three of the transformations above are intended to clean up
- ; the output of the OR macro. It isn't yet clear how well this works.
- (define (simplify-conditional exp notepad)
- (define (coercion-to-boolean? exp)
- (and (conditional? exp)
- (let ((E1 (if.then exp))
- (E2 (if.else exp)))
- (and (constant? E1)
- (eq? #t (constant.value E1))
- (constant? E2)
- (eq? #f (constant.value E2))))))
- (if (not (control-optimization))
- (begin (if.test-set! exp (simplify (if.test exp) notepad))
- (if.then-set! exp (simplify (if.then exp) notepad))
- (if.else-set! exp (simplify (if.else exp) notepad))
- exp)
- (let* ((test (if.test exp)))
- (if (and (call? test)
- (lambda? (call.proc test))
- (let* ((L (call.proc test))
- (body (lambda.body L)))
- (and (conditional? body)
- (let ((R (lambda.R L))
- (B0 (if.test body))
- (B1 (if.then body)))
- (and (variable? B0)
- (variable? B1)
- (let ((x (variable.name B0)))
- (and (eq? x (variable.name B1))
- (local? R x)
- (= 1 (length R))
- (= 1 (length (call.args test))))))))))
- (let* ((L (call.proc test))
- (R (lambda.R L))
- (body (lambda.body L))
- (ref (if.then body))
- (x (variable.name ref))
- (entry (R-entry R x)))
- (if.then-set! body (make-constant #t))
- (if.else-set! body
- (make-conditional (if.else body)
- (make-constant #t)
- (make-constant #f)))
- (R-entry.references-set! entry
- (remq ref
- (R-entry.references entry)))
- (simplify-conditional exp notepad))
- (let loop ((test (simplify (if.test exp) notepad)))
- (if.test-set! exp test)
- (cond ((constant? test)
- (simplify (if (constant.value test)
- (if.then exp)
- (if.else exp))
- notepad))
- ((and (conditional? test)
- (constant? (if.then test))
- (constant? (if.else test)))
- (cond ((and (constant.value (if.then test))
- (constant.value (if.else test)))
- (post-simplify-begin
- (make-begin (list (if.test test)
- (simplify (if.then exp)
- notepad)))
- notepad))
- ((and (not (constant.value (if.then test)))
- (not (constant.value (if.else test))))
- (post-simplify-begin
- (make-begin (list (if.test test)
- (simplify (if.else exp)
- notepad)))
- notepad))
- (else (if (not (constant.value (if.then test)))
- (let ((temp (if.then exp)))
- (if.then-set! exp (if.else exp))
- (if.else-set! exp temp)))
- (if.test-set! exp (if.test test))
- (loop (if.test exp)))))
- ((and (conditional? test)
- (or (coercion-to-boolean? (if.then test))
- (coercion-to-boolean? (if.else test))))
- (if (coercion-to-boolean? (if.then test))
- (if.then-set! test (if.test (if.then test)))
- (if.else-set! test (if.test (if.else test))))
- (loop test))
- ((and (conditional? test)
- (variable? (if.test test))
- (let ((x (variable.name (if.test test))))
- (or (and (variable? (if.then test))
- (eq? x (variable.name (if.then test)))
- 1)
- (and (variable? (if.else test))
- (eq? x (variable.name (if.else test)))
- 2))))
- =>
- (lambda (n)
- (case n
- ((1) (if.then-set! test (make-constant #t)))
- ((2) (if.else-set! test (make-constant #f))))
- (loop test)))
- ((begin? test)
- (let ((exprs (reverse (begin.exprs test))))
- (if.test-set! exp (car exprs))
- (post-simplify-begin
- (make-begin (reverse (cons (loop (car exprs))
- (cdr exprs))))
- notepad)))
- ((and (call? test)
- (variable? (call.proc test))
- (eq? (variable.name (call.proc test)) name:NOT)
- (integrable? name:NOT)
- (integrate-usual-procedures)
- (= (length (call.args test)) 1))
- (let ((temp (if.then exp)))
- (if.then-set! exp (if.else exp))
- (if.else-set! exp temp))
- (loop (car (call.args test))))
- (else
- (simplify-case exp notepad))))))))
- ; Given a conditional expression whose test has been simplified,
- ; simplifies the then and else parts while applying optimizations
- ; for CASE expressions.
- ; Precondition: (control-optimization) is true.
- (define (simplify-case exp notepad)
- (let ((E0 (if.test exp)))
- (if (and (call? E0)
- (variable? (call.proc E0))
- (let ((name (variable.name (call.proc E0))))
- ; FIXME: Should ensure that the name is integrable,
- ; but MEMQ and MEMV probably aren't according to the
- ; INTEGRABLE? predicate.
- (or (eq? name name:EQ?)
- (eq? name name:EQV?)
- (eq? name name:MEMQ)
- (eq? name name:MEMV)))
- (integrate-usual-procedures)
- (= (length (call.args E0)) 2)
- (variable? (car (call.args E0)))
- (constant? (cadr (call.args E0))))
- (simplify-case-clauses (variable.name (car (call.args E0)))
- exp
- notepad)
- (begin (if.then-set! exp (simplify (if.then exp) notepad))
- (if.else-set! exp (simplify (if.else exp) notepad))
- exp))))
- ; Code generation for case expressions.
- ;
- ; A case expression turns into a conditional expression
- ; of the form
- ;
- ; CASE{I} ::= E | (if (PRED I K) E CASE{I})
- ; PRED ::= memv | memq | eqv? | eq?
- ;
- ; The memq and eq? predicates are used when the constant
- ; is a (list of) boolean, fixnum, char, empty list, or symbol.
- ; The constants will almost always be of these types.
- ;
- ; The first step is to remove duplicated constants and to
- ; collect all the case clauses, sorting them into the following
- ; categories based on their simplified list of constants:
- ; constants are fixnums
- ; constants are characters
- ; constants are symbols
- ; constants are of mixed or other type
- ; After duplicated constants have been removed, the predicates
- ; for these clauses can be tested in any order.
- ; Given the name of an arbitrary variable, an expression that
- ; has not yet been simplified or can safely be simplified again,
- ; and a notepad, returns the expression after simplification.
- ; If the expression is equivalent to a case expression that dispatches
- ; on the given variable, then case-optimization will be applied.
- (define (simplify-case-clauses var0 E notepad)
-
- (define notepad2 (make-notepad (notepad.parent notepad)))
-
- (define (collect-clauses E fix chr sym other constants)
- (if (not (conditional? E))
- (analyze (simplify E notepad2)
- fix chr sym other constants)
- (let ((test (simplify (if.test E) notepad2))
- (code (simplify (if.then E) notepad2)))
- (if.test-set! E test)
- (if.then-set! E code)
- (if (not (call? test))
- (finish E fix chr sym other constants)
- (let ((proc (call.proc test))
- (args (call.args test)))
- (if (not (and (variable? proc)
- (let ((name (variable.name proc)))
- ; FIXME: See note above.
- (or (eq? name name:EQ?)
- (eq? name name:EQV?)
- (eq? name name:MEMQ)
- (eq? name name:MEMV)))
- (= (length args) 2)
- (variable? (car args))
- (eq? (variable.name (car args)) var0)
- (constant? (cadr args))))
- (finish E fix chr sym other constants)
- (let ((pred (variable.name proc))
- (datum (constant.value (cadr args))))
- ; FIXME
- (if (or (and (or (eq? pred name:MEMV)
- (eq? pred name:MEMQ))
- (not (list? datum)))
- (and (eq? pred name:EQ?)
- (not (eqv-is-ok? datum)))
- (and (eq? pred name:MEMQ)
- (not (every? (lambda (datum)
- (eqv-is-ok? datum))
- datum))))
- (finish E fix chr sym other constants)
- (call-with-values
- (lambda ()
- (remove-duplicates (if (or (eq? pred name:EQV?)
- (eq? pred name:EQ?))
- (list datum)
- datum)
- constants))
- (lambda (data constants)
- (let ((clause (list data code))
- (E2 (if.else E)))
- (cond ((every? smallint? data)
- (collect-clauses E2
- (cons clause fix)
- chr
- sym
- other
- constants))
- ((every? char? data)
- (collect-clauses E2
- fix
- (cons clause chr)
- sym
- other
- constants))
- ((every? symbol? data)
- (collect-clauses E2
- fix
- chr
- (cons clause sym)
- other
- constants))
- (else
- (collect-clauses E2
- fix
- chr
- sym
- (cons clause other)
- constants))))))))))))))
-
- (define (remove-duplicates data set)
- (let loop ((originals data)
- (data '())
- (set set))
- (if (null? originals)
- (values data set)
- (let ((x (car originals))
- (originals (cdr originals)))
- (if (memv x set)
- (loop originals data set)
- (loop originals (cons x data) (cons x set)))))))
-
- (define (finish E fix chr sym other constants)
- (if.else-set! E (simplify (if.else E) notepad2))
- (analyze E fix chr sym other constants))
-
- (define (analyze default fix chr sym other constants)
- (notepad-var-add! notepad2 var0)
- (for-each (lambda (L)
- (notepad-lambda-add! notepad L))
- (notepad.lambdas notepad2))
- (for-each (lambda (L)
- (notepad-nonescaping-add! notepad L))
- (notepad.nonescaping notepad2))
- (for-each (lambda (var)
- (notepad-var-add! notepad var))
- (append (list name:FIXNUM?
- name:CHAR?
- name:SYMBOL?
- name:FX<
- name:FX-
- name:CHAR->INTEGER
- name:VECTOR-REF)
- (notepad.vars notepad2)))
- (analyze-clauses (notepad.vars notepad2)
- var0
- default
- (reverse fix)
- (reverse chr)
- (reverse sym)
- (reverse other)
- constants))
-
- (collect-clauses E '() '() '() '() '()))
- ; Returns true if EQ? and EQV? behave the same on x.
- (define (eqv-is-ok? x)
- (or (smallint? x)
- (char? x)
- (symbol? x)
- (boolean? x)))
- ; Returns true if EQ? and EQV? behave the same on x.
- (define (eq-is-ok? x)
- (eqv-is-ok? x))
- ; Any case expression that dispatches on a variable var0 and whose
- ; constants are disjoint can be compiled as
- ;
- ; (let ((n (cond ((eq? var0 'K1) ...) ; miscellaneous constants
- ; ...
- ; ((fixnum? var0)
- ; <dispatch-on-fixnum>)
- ; ((char? var0)
- ; <dispatch-on-char>)
- ; ((symbol? var0)
- ; <dispatch-on-symbols>)
- ; (else 0))))
- ; <dispatch-on-case-number>)
- ;
- ; where the <dispatch-on-case-number> uses binary search within
- ; the interval [0, p+1), where p is the number of non-default cases.
- ;
- ; On the SPARC, sequential search is faster if there are fewer than
- ; 8 constants, and sequential search uses less than half the space
- ; if there are fewer than 10 constants. Most target machines should
- ; similar, so I'm hard-wiring this constant.
- ; FIXME: The hardwired constant is annoying.
- (define (analyze-clauses F var0 default fix chr sym other constants)
- (cond ((or (and (null? fix)
- (null? chr))
- (< (length constants) 12))
- (implement-clauses-by-sequential-search var0
- default
- (append fix chr sym other)))
- (else
- (implement-clauses F var0 default fix chr sym other constants))))
- ; Implements the general technique described above.
- (define (implement-clauses F var0 default fix chr sym other constants)
- (let* ((name:n ((make-rename-procedure) 'n))
- ; Referencing information is destroyed by pass 2.
- (entry (make-R-entry name:n '() '() '()))
- (F (union (make-set (list name:n)) F))
- (L (make-lambda
- (list name:n)
- '()
- '() ; entry
- F
- '()
- '()
- #f
- (implement-case-dispatch
- name:n
- (cons default
- (map cadr
- ; The order here must match the order
- ; used by IMPLEMENT-DISPATCH.
- (append other fix chr sym)))))))
- (make-call L
- (list (implement-dispatch 0
- var0
- (map car other)
- (map car fix)
- (map car chr)
- (map car sym))))))
- (define (implement-case-dispatch var0 exprs)
- (implement-intervals var0
- (map (lambda (n code)
- (list n (+ n 1) code))
- (iota (length exprs))
- exprs)))
- ; Given the number of prior clauses,
- ; the variable on which to dispatch,
- ; a list of constant lists for mixed or miscellaneous clauses,
- ; a list of constant lists for the fixnum clauses,
- ; a list of constant lists for the character clauses, and
- ; a list of constant lists for the symbol clauses,
- ; returns code that computes the index of the selected clause.
- ; The mixed/miscellaneous clauses must be tested first because
- ; Twobit's SMALLINT? predicate might not be true of all fixnums
- ; on the target machine, which means that Twobit might classify
- ; some fixnums as miscellaneous.
- (define (implement-dispatch prior var0 other fix chr sym)
- (cond ((not (null? other))
- (implement-dispatch-other
- (implement-dispatch (+ prior (length other))
- var0 fix chr sym '())
- prior var other))
- ((not (null? fix))
- (make-conditional (make-call (make-variable name:FIXNUM?)
- (list (make-variable var0)))
- (implement-dispatch-fixnum prior var0 fix)
- (implement-dispatch (+ prior (length fix))
- var0 '() chr sym other)))
- ((not (null? chr))
- (make-conditional (make-call (make-variable name:CHAR?)
- (list (make-variable var0)))
- (implement-dispatch-char prior var0 chr)
- (implement-dispatch (+ prior (length chr))
- var0 fix '() sym other)))
- ((not (null? sym))
- (make-conditional (make-call (make-variable name:SYMBOL?)
- (list (make-variable var0)))
- (implement-dispatch-symbol prior var0 sym)
- (implement-dispatch (+ prior (length sym))
- var0 fix chr '() other)))
- (else
- (make-constant 0))))
- ; The value of var0 will be known to be a fixnum.
- ; Can use table lookup, binary search, or sequential search.
- ; FIXME: Never uses sequential search, which is best when
- ; there are only a few constants, with gaps between them.
- (define (implement-dispatch-fixnum prior var0 lists)
-
- (define (calculate-intervals n lists)
- (define (loop n lists intervals)
- (if (null? lists)
- (twobit-sort (lambda (interval1 interval2)
- (< (car interval1) (car interval2)))
- intervals)
- (let ((constants (twobit-sort < (car lists))))
- (loop (+ n 1)
- (cdr lists)
- (append (extract-intervals n constants)
- intervals)))))
- (loop n lists '()))
-
- (define (extract-intervals n constants)
- (if (null? constants)
- '()
- (let ((k0 (car constants)))
- (do ((constants (cdr constants) (cdr constants))
- (k1 (+ k0 1) (+ k1 1)))
- ((or (null? constants)
- (not (= k1 (car constants))))
- (cons (list k0 k1 (make-constant n))
- (extract-intervals n constants)))))))
-
- (define (complete-intervals intervals)
- (cond ((null? intervals)
- intervals)
- ((null? (cdr intervals))
- intervals)
- (else
- (let* ((i1 (car intervals))
- (i2 (cadr intervals))
- (end1 (cadr i1))
- (start2 (car i2))
- (intervals (complete-intervals (cdr intervals))))
- (if (= end1 start2)
- (cons i1 intervals)
- (cons i1
- (cons (list end1 start2 (make-constant 0))
- intervals)))))))
-
- (let* ((intervals (complete-intervals
- (calculate-intervals (+ prior 1) lists)))
- (lo (car (car intervals)))
- (hi (car (car (reverse intervals))))
- (p (length intervals)))
- (make-conditional
- (make-call (make-variable name:FX<)
- (list (make-variable var0)
- (make-constant lo)))
- (make-constant 0)
- (make-conditional
- (make-call (make-variable name:FX<)
- (list (make-variable var0)
- (make-constant (+ hi 1))))
- ; The static cost of table lookup is about hi - lo words.
- ; The static cost of binary search is about 5 SPARC instructions
- ; per interval.
- (if (< (- hi lo) (* 5 p))
- (implement-table-lookup var0 (+ prior 1) lists lo hi)
- (implement-intervals var0 intervals))
- (make-constant 0)))))
- (define (implement-dispatch-char prior var0 lists)
- (let* ((lists (map (lambda (constants)
- (map compat:char->integer constants))
- lists))
- (name:n ((make-rename-procedure) 'n))
- ; Referencing information is destroyed by pass 2.
- ;(entry (make-R-entry name:n '() '() '()))
- (F (list name:n name:EQ? name:FX< name:FX- name:VECTOR-REF))
- (L (make-lambda
- (list name:n)
- '()
- '() ; entry
- F
- '()
- '()
- #f
- (implement-dispatch-fixnum prior name:n lists))))
- (make-call L
- (make-call (make-variable name:CHAR->INTEGER)
- (list (make-variable var0))))))
- (define (implement-dispatch-symbol prior var0 lists)
- (implement-dispatch-other (make-constant 0) prior var0 lists))
- (define (implement-dispatch-other default prior var0 lists)
- (if (null? lists)
- default
- (let* ((constants (car lists))
- (lists (cdr lists))
- (n (+ prior 1)))
- (make-conditional (make-call-to-memv var0 constants)
- (make-constant n)
- (implement-dispatch-other default n var0 lists)))))
- (define (make-call-to-memv var0 constants)
- (cond ((null? constants)
- (make-constant #f))
- ((null? (cdr constants))
- (make-call-to-eqv var0 (car constants)))
- (else
- (make-conditional (make-call-to-eqv var0 (car constants))
- (make-constant #t)
- (make-call-to-memv var0 (cdr constants))))))
- (define (make-call-to-eqv var0 constant)
- (make-call (make-variable
- (if (eq-is-ok? constant)
- name:EQ?
- name:EQV?))
- (list (make-variable var0)
- (make-constant constant))))
- ; Given a variable whose value is known to be a fixnum,
- ; the clause index for the first fixnum clause,
- ; an ordered list of lists of constants for fixnum-only clauses,
- ; and the least and greatest constants in those lists,
- ; returns code for a table lookup.
- (define (implement-table-lookup var0 index lists lo hi)
- (let ((v (make-vector (+ 1 (- hi lo)) 0)))
- (do ((index index (+ index 1))
- (lists lists (cdr lists)))
- ((null? lists))
- (for-each (lambda (k)
- (vector-set! v (- k lo) index))
- (car lists)))
- (make-call (make-variable name:VECTOR-REF)
- (list (make-constant v)
- (make-call (make-variable name:FX-)
- (list (make-variable var0)
- (make-constant lo)))))))
- ; Given a variable whose value is known to lie within the
- ; half-open interval [m0, mk), and an ordered complete
- ; list of intervals of the form
- ;
- ; ((m0 m1 code0)
- ; (m1 m2 code1)
- ; ...
- ; (m{k-1} mk code{k-1})
- ; )
- ;
- ; returns an expression that finds the unique i such that
- ; var0 lies within [mi, m{i+1}), and then executes code{i}.
- (define (implement-intervals var0 intervals)
- (if (null? (cdr intervals))
- (caddr (car intervals))
- (let ((n (quotient (length intervals) 2)))
- (do ((n n (- n 1))
- (intervals1 '() (cons (car intervals2) intervals1))
- (intervals2 intervals (cdr intervals2)))
- ((zero? n)
- (let ((intervals1 (reverse intervals1))
- (m (car (car intervals2))))
- (make-conditional (make-call (make-variable name:FX<)
- (list
- (make-variable var0)
- (make-constant m)))
- (implement-intervals var0 intervals1)
- (implement-intervals var0 intervals2))))))))
- ; The brute force approach.
- ; Given the variable on which the dispatch is being performed, and
- ; actual (simplified) code for the default clause and
- ; for all other clauses,
- ; returns code to perform the dispatch by sequential search.
- (define *memq-threshold* 20)
- (define *memv-threshold* 4)
- (define (implement-clauses-by-sequential-search var0 default clauses)
- (if (null? clauses)
- default
- (let* ((case1 (car clauses))
- (clauses (cdr clauses))
- (constants1 (car case1))
- (code1 (cadr case1)))
- (make-conditional (make-call-to-memv var0 constants1)
- code1
- (implement-clauses-by-sequential-search
- var0 default clauses)))))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 13 April 1999.
- ;
- ; The tail and non-tail call graphs of known and unknown procedures.
- ;
- ; Given an expression E returned by pass 2 of Twobit,
- ; returns a list of the following form:
- ;
- ; ((#t L () <tailcalls> <nontailcalls> <size> #f)
- ; (<name> L <vars> <tailcalls> <nontailcalls> <size> #f)
- ; ...)
- ;
- ; where
- ;
- ; Each L is a lambda expression that occurs within E
- ; as either an escaping lambda expression or as a known
- ; procedure. If L is a known procedure, then <name> is
- ; its name; otherwise <name> is #f.
- ;
- ; <vars> is a list of the non-global variables within whose
- ; scope L occurs.
- ;
- ; <tailcalls> is a complete list of names of known local procedures
- ; that L calls tail-recursively, disregarding calls from other known
- ; procedures or escaping lambda expressions that occur within L.
- ;
- ; <nontailcalls> is a complete list of names of known local procedures
- ; that L calls non-tail-recursively, disregarding calls from other
- ; known procedures or escaping lambda expressions that occur within L.
- ;
- ; <size> is a measure of the size of L, including known procedures
- ; and escaping lambda expressions that occur within L.
- (define (callgraphnode.name x) (car x))
- (define (callgraphnode.code x) (cadr x))
- (define (callgraphnode.vars x) (caddr x))
- (define (callgraphnode.tailcalls x) (cadddr x))
- (define (callgraphnode.nontailcalls x) (car (cddddr x)))
- (define (callgraphnode.size x) (cadr (cddddr x)))
- (define (callgraphnode.info x) (caddr (cddddr x)))
- (define (callgraphnode.size! x v) (set-car! (cdr (cddddr x)) v) #f)
- (define (callgraphnode.info! x v) (set-car! (cddr (cddddr x)) v) #f)
- (define (callgraph exp)
-
- ; Returns (union (list x) z).
-
- (define (adjoin x z)
- (if (memq x z)
- z
- (cons x z)))
-
- (let ((result '()))
-
- ; Given a <name> as described above, a lambda expression, a list
- ; of variables that are in scope, and a list of names of known
- ; local procedure that are in scope, computes an entry for L and
- ; entries for any nested known procedures or escaping lambda
- ; expressions, and adds them to the result.
-
- (define (add-vertex! name L vars known)
-
- (let ((tailcalls '())
- (nontailcalls '())
- (size 0))
-
- ; Given an expression, a list of variables that are in scope,
- ; a list of names of known local procedures that are in scope,
- ; and a boolean indicating whether the expression occurs in a
- ; tail context, adds any tail or non-tail calls to known
- ; procedures that occur within the expression to the list
- ; variables declared above.
-
- (define (graph! exp vars known tail?)
- (set! size (+ size 1))
- (case (car exp)
-
- ((quote) #f)
-
- ((lambda) (add-vertex! #f exp vars known)
- (set! size
- (+ size
- (callgraphnode.size (car result)))))
-
- ((set!) (graph! (assignment.rhs exp) vars known #f))
-
- ((if) (graph! (if.test exp) vars known #f)
- (graph! (if.then exp) vars known tail?)
- (graph! (if.else exp) vars known tail?))
-
- ((begin) (if (not (variable? exp))
- (do ((exprs (begin.exprs exp) (cdr exprs)))
- ((null? (cdr exprs))
- (graph! (car exprs) vars known tail?))
- (graph! (car exprs) vars known #f))))
-
- (else (let ((proc (call.proc exp)))
- (cond ((variable? proc)
- (let ((name (variable.name proc)))
- (if (memq name known)
- (if tail?
- (set! tailcalls
- (adjoin name tailcalls))
- (set! nontailcalls
- (adjoin name nontailcalls))))))
- ((lambda? proc)
- (graph-lambda! proc vars known tail?))
- (else
- (graph! proc vars known #f)))
- (for-each (lambda (exp)
- (graph! exp vars known #f))
- (call.args exp))))))
-
- (define (graph-lambda! L vars known tail?)
- (let* ((defs (lambda.defs L))
- (newknown (map def.lhs defs))
- (vars (append newknown
- (make-null-terminated
- (lambda.args L))
- vars))
- (known (append newknown known)))
- (for-each (lambda (def)
- (add-vertex! (def.lhs def)
- (def.rhs def)
- vars
- known)
- (set! size
- (+ size
- (callgraphnode.size (car result)))))
- defs)
- (graph! (lambda.body L) vars known tail?)))
-
- (graph-lambda! L vars known #t)
-
- (set! result
- (cons (list name L vars tailcalls nontailcalls size #f)
- result))))
-
- (add-vertex! #t
- (make-lambda '() '() '() '() '() '() '() exp)
- '()
- '())
- result))
- ; Displays the callgraph, for debugging.
- (define (view-callgraph g)
- (for-each (lambda (entry)
- (let ((name (callgraphnode.name entry))
- (exp (callgraphnode.code entry))
- (vars (callgraphnode.vars entry))
- (tail (callgraphnode.tailcalls entry))
- (nt (callgraphnode.nontailcalls entry))
- (size (callgraphnode.size entry)))
- (cond ((symbol? name)
- (write name))
- (name
- (display "TOP LEVEL EXPRESSION"))
- (else
- (display "ESCAPING LAMBDA EXPRESSION")))
- (display ":")
- (newline)
- (display "Size: ")
- (write size)
- (newline)
- ;(newline)
- ;(display "Variables in scope: ")
- ;(write vars)
- ;(newline)
- (display "Tail calls: ")
- (write tail)
- (newline)
- (display "Non-tail calls: ")
- (write nt)
- (newline)
- ;(newline)
- ;(pretty-print (make-readable exp))
- ;(newline)
- ;(newline)
- (newline)))
- g))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 14 April 1999.
- ;
- ; Inlining of known local procedures.
- ;
- ; First find the known and escaping procedures and compute the call graph.
- ;
- ; If a known local procedure is not called at all, then delete its code.
- ;
- ; If a known local procedure is called exactly once,
- ; then inline its code at the call site and delete the
- ; known local procedure. Change the size of the code
- ; at the call site by adding the size of the inlined code.
- ;
- ; Divide the remaining known and escaping procedures into categories:
- ; 1. makes no calls to known local procedures
- ; 2. known procedures that call known procedures;
- ; within this category, try to sort so that procedures do not
- ; call procedures that come later in the sequence; or sort by
- ; number of calls and/or size
- ; 3. escaping procedures that call known procedures
- ;
- ; Approve each procedure in category 1 for inlining if its code size
- ; is less than some threshold.
- ;
- ; For each procedure in categories 2 and 3, traverse its code, inlining
- ; where it seems like a good idea. The compiler should be more aggressive
- ; about inlining non-tail calls than tail calls because:
- ;
- ; Inlining a non-tail call can eliminate a stack frame
- ; or expose the inlined code to loop optimizations.
- ;
- ; The main reason for inlining a tail call is to enable
- ; intraprocedural optimizations or to unroll a loop.
- ;
- ; After inlining has been performed on a known local procedure,
- ; then approve it for inlining if its size is less than some threshold.
- ;
- ; FIXME:
- ; This strategy avoids infinite unrolling, but it also avoids finite
- ; unrolling of loops.
- ; Parameters to control inlining.
- ; These can be tuned later.
- (define *tail-threshold* 10)
- (define *nontail-threshold* 20)
- (define *multiplier* 300)
- ; Given a callgraph, performs inlining of known local procedures
- ; by side effect. The original expression must then be copied to
- ; reinstate Twobit's invariants.
- ; FIXME: This code doesn't yet do the right thing with known local
- ; procedures that aren't called or are called in exactly one place.
- (define (inline-using-callgraph! g)
- (let ((known (make-hashtable))
- (category2 '())
- (category3 '()))
- (for-each (lambda (node)
- (let ((name (callgraphnode.name node))
- (tcalls (callgraphnode.tailcalls node))
- (ncalls (callgraphnode.nontailcalls node)))
- (if (symbol? name)
- (hashtable-put! known name node))
- (if (and (null? tcalls)
- (null? ncalls))
- (if (< (callgraphnode.size node)
- *nontail-threshold*)
- (callgraphnode.info! node #t))
- (if (symbol? name)
- (set! category2 (cons node category2))
- (set! category3 (cons node category3))))))
- g)
- (set! category2 (twobit-sort (lambda (x y)
- (< (callgraphnode.size x)
- (callgraphnode.size y)))
- category2))
- (for-each (lambda (node)
- (inline-node! node known))
- category2)
- (for-each (lambda (node)
- (inline-node! node known))
- category3)
- ; FIXME:
- ; Inlining destroys the callgraph, so maybe this cleanup is useless.
- (hashtable-for-each (lambda (name node) (callgraphnode.info! node #f))
- known)))
- ; Given a node of the callgraph and a hash table of nodes for
- ; known local procedures, performs inlining by side effect.
- (define (inline-node! node known)
- (let* ((debugging? #f)
- (name (callgraphnode.name node))
- (exp (callgraphnode.code node))
- (size0 (callgraphnode.size node))
- (budget (quotient (* (- *multiplier* 100) size0) 100))
- (tail-threshold *tail-threshold*)
- (nontail-threshold *nontail-threshold*))
-
- ; Given an expression,
- ; a boolean indicating whether the expression is in a tail context,
- ; a list of procedures that should not be inlined,
- ; and a size budget,
- ; performs inlining by side effect and returns the unused budget.
-
- (define (inline exp tail? budget)
- (if (positive? budget)
-
- (case (car exp)
-
- ((quote lambda)
- budget)
-
- ((set!)
- (inline (assignment.rhs exp) #f budget))
-
- ((if)
- (let* ((budget (inline (if.test exp) #f budget))
- (budget (inline (if.then exp) tail? budget))
- (budget (inline (if.else exp) tail? budget)))
- budget))
-
- ((begin)
- (if (variable? exp)
- budget
- (do ((exprs (begin.exprs exp) (cdr exprs))
- (budget budget
- (inline (car exprs) #f budget)))
- ((null? (cdr exprs))
- (inline (car exprs) tail? budget)))))
-
- (else
- (let ((budget (do ((exprs (call.args exp) (cdr exprs))
- (budget budget
- (inline (car exprs) #f budget)))
- ((null? exprs)
- budget))))
- (let ((proc (call.proc exp)))
- (cond ((variable? proc)
- (let* ((procname (variable.name proc))
- (procnode (hashtable-get known procname)))
- (if procnode
- (let ((size (callgraphnode.size procnode))
- (info (callgraphnode.info procnode)))
- (if (and info
- (<= size budget)
- (<= size
- (if tail?
- tail-threshold
- nontail-threshold)))
- (begin
- (if debugging?
- (begin
- (display " Inlining ")
- (write (variable.name proc))
- (newline)))
- (call.proc-set!
- exp
- (copy-exp
- (callgraphnode.code procnode)))
- (callgraphnode.size!
- node
- (+ (callgraphnode.size node) size))
- (- budget size))
- (begin
- (if (and #f debugging?)
- (begin
- (display " Declining to inline ")
- (write (variable.name proc))
- (newline)))
- budget)))
- budget)))
- ((lambda? proc)
- (inline (lambda.body proc) tail? budget))
- (else
- (inline proc #f budget)))))))
- -1))
-
- (if (and #f debugging?)
- (begin
- (display "Processing ")
- (write name)
- (newline)))
-
- (let ((budget (inline (if (lambda? exp)
- (lambda.body exp)
- exp)
- #t
- budget)))
- (if (and (negative? budget)
- debugging?)
- ; This shouldn't happen very often.
- (begin (display "Ran out of inlining budget for ")
- (write (callgraphnode.name node))
- (newline)))
- (if (<= (callgraphnode.size node) nontail-threshold)
- (callgraphnode.info! node #t))
- #f)))
- ; For testing.
- (define (test-inlining test0)
- (begin (define exp0 (begin (display "Compiling...")
- (newline)
- (pass2 (pass1 test0))))
- (define g0 (begin (display "Computing call graph...")
- (newline)
- (callgraph exp0))))
- (display "Inlining...")
- (newline)
- (inline-using-callgraph! g0)
- (pretty-print (make-readable (copy-exp exp0))))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 14 April 1999.
- ;
- ; Interprocedural constant propagation and folding.
- ;
- ; Constant propagation must converge before constant folding can be
- ; performed. Constant folding creates more constants that can be
- ; propagated, so these two optimizations must be iterated, but it
- ; is safe to stop at any time.
- ;
- ; Abstract interpretation for constant folding.
- ;
- ; The abstract values are
- ; bottom (represented here by #f)
- ; constants (represented by quoted literals)
- ; top (represented here by #t)
- ;
- ; Let [[ E ]] be the abstract interpretation of E over that domain
- ; of abstract values, with respect to some arbitrary set of abstract
- ; values for local variables.
- ;
- ; If a is a global variable or a formal parameter of an escaping
- ; lambda expression, then [[ a ]] = #t.
- ;
- ; If x is the ith formal parameter of a known local procedure f,
- ; then [[ x ]] = \join_{(f E1 ... En)} [[ Ei ]].
- ;
- ; [[ K ]] = K
- ; [[ L ]] = #t
- ; [[ (begin E1 ... En) ]] = [[ En ]]
- ; [[ (set! I E) ]] = #f
- ;
- ; If [[ E0 ]] = #t, then [[ (if E0 E1 E2) ]] = [[ E1 ]] \join [[ E2 ]]
- ; else if [[ E0 ]] = K, then [[ (if E0 E1 E2) ]] = [[ E1 ]]
- ; or [[ (if E0 E1 E2) ]] = [[ E2 ]]
- ; depending upon K
- ; else [[ (if E0 E1 E2) ]] = #f
- ;
- ; If f is a known local procedure with body E,
- ; then [[ (f E1 ... En) ]] = [[ E ]]
- ;
- ; If g is a foldable integrable procedure, then:
- ; if there is some i for which [[ Ei ]] = #t,
- ; then [[ (g E1 ... En) ]] = #t
- ; else if [[ E1 ]] = K1, ..., [[ En ]] = Kn,
- ; then [[ (g E1 ... En) ]] = (g K1 ... Kn)
- ; else [[ (g E1 ... En) ]] = #f
- ;
- ; Symbolic representations of abstract values.
- ; (Can be thought of as mappings from abstract environments to
- ; abstract values.)
- ;
- ; <symbolic> ::= #t | ( <expressions> )
- ; <expressions> ::= <empty> | <expression> <expressions>
- ; Parameter to limit constant propagation and folding.
- ; This parameter can be tuned later.
- (define *constant-propagation-limit* 5)
- ; Given an expression as output by pass 2, performs constant
- ; propagation and folding.
- (define (constant-propagation exp)
- (define (constant-propagation exp i)
- (if (< i *constant-propagation-limit*)
- (begin
- ;(display "Performing constant propagation and folding...")
- ;(newline)
- (let* ((g (callgraph exp))
- (L (callgraphnode.code (car g)))
- (variables (constant-propagation-using-callgraph g))
- (changed? (constant-folding! L variables)))
- (if changed?
- (constant-propagation (lambda.body L) (+ i 1))
- (lambda.body L))))))
- (constant-propagation exp 0))
- ; Given a callgraph, returns a hashtable of abstract values for
- ; all local variables.
- (define (constant-propagation-using-callgraph g)
- (let ((debugging? #f)
- (folding? (integrate-usual-procedures))
- (known (make-hashtable))
- (variables (make-hashtable))
- (counter 0))
-
- ; Computes joins of abstract values.
-
- (define (join x y)
- (cond ((boolean? x)
- (if x #t y))
- ((boolean? y)
- (join y x))
- ((equal? x y)
- x)
- (else #t)))
-
- ; Given a <symbolic> and a vector of abstract values,
- ; evaluates the <symbolic> and returns its abstract value.
-
- (define (aeval rep env)
- (cond ((eq? rep #t)
- #t)
- ((null? rep)
- #f)
- ((null? (cdr rep))
- (aeval1 (car rep) env))
- (else
- (join (aeval1 (car rep) env)
- (aeval (cdr rep) env)))))
-
- (define (aeval1 exp env)
-
- (case (car exp)
-
- ((quote)
- exp)
-
- ((lambda)
- #t)
-
- ((set!)
- #f)
-
- ((begin)
- (if (variable? exp)
- (let* ((name (variable.name exp))
- (i (hashtable-get variables name)))
- (if i
- (vector-ref env i)
- #t))
- (aeval1-error)))
-
- ((if)
- (let* ((val0 (aeval1 (if.test exp) env))
- (val1 (aeval1 (if.then exp) env))
- (val2 (aeval1 (if.else exp) env)))
- (cond ((eq? val0 #t)
- (join val1 val2))
- ((pair? val0)
- (if (constant.value val0)
- val1
- val2))
- (else
- #f))))
-
- (else
- (do ((exprs (reverse (call.args exp)) (cdr exprs))
- (vals '() (cons (aeval1 (car exprs) env) vals)))
- ((null? exprs)
- (let ((proc (call.proc exp)))
- (cond ((variable? proc)
- (let* ((procname (variable.name proc))
- (procnode (hashtable-get known procname))
- (entry (if folding?
- (constant-folding-entry procname)
- #f)))
- (cond (procnode
- (vector-ref env
- (hashtable-get variables
- procname)))
- (entry
- ; FIXME: No constant folding
- #t)
- (else (aeval1-error)))))
- (else
- (aeval1-error)))))))))
-
- (define (aeval1-error)
- (error "Compiler bug: constant propagation (aeval1)"))
-
- ; Combines two <symbolic>s.
-
- (define (combine-symbolic rep1 rep2)
- (cond ((eq? rep1 #t) #t)
- ((eq? rep2 #t) #t)
- (else
- (append rep1 rep2))))
-
- ; Given an expression, returns a <symbolic> that represents
- ; a list of expressions whose abstract values can be joined
- ; to obtain the abstract value of the given expression.
- ; As a side effect, enters local variables into variables.
-
- (define (collect! exp)
-
- (case (car exp)
-
- ((quote)
- (list exp))
-
- ((lambda)
- #t)
-
- ((set!)
- (collect! (assignment.rhs exp))
- '())
-
- ((begin)
- (if (variable? exp)
- (list exp)
- (do ((exprs (begin.exprs exp) (cdr exprs)))
- ((null? (cdr exprs))
- (collect! (car exprs)))
- (collect! (car exprs)))))
-
- ((if)
- (collect! (if.test exp))
- (collect! (if.then exp))
- (collect! (if.else exp))
- #t)
-
- (else
- (do ((exprs (reverse (call.args exp)) (cdr exprs))
- (reps '() (cons (collect! (car exprs)) reps)))
- ((null? exprs)
- (let ((proc (call.proc exp)))
- (define (put-args! args reps)
- (cond ((pair? args)
- (let ((v (car args))
- (rep (car reps)))
- (hashtable-put! variables v rep)
- (put-args! (cdr args) (cdr reps))))
- ((symbol? args)
- (hashtable-put! variables args #t))
- (else #f)))
- (cond ((variable? proc)
- (let* ((procname (variable.name proc))
- (procnode (hashtable-get known procname))
- (entry (if folding?
- (constant-folding-entry procname)
- #f)))
- (cond (procnode
- (for-each (lambda (v rep)
- (hashtable-put!
- variables
- v
- (combine-symbolic
- rep (hashtable-get variables v))))
- (lambda.args
- (callgraphnode.code procnode))
- reps)
- (list (make-variable procname)))
- (entry
- ; FIXME: No constant folding
- #t)
- (else #t))))
- ((lambda? proc)
- (put-args! (lambda.args proc) reps)
- (collect! (lambda.body proc)))
- (else
- (collect! proc)
- #t))))))))
-
- (for-each (lambda (node)
- (let* ((name (callgraphnode.name node))
- (code (callgraphnode.code node))
- (known? (symbol? name))
- (rep (if known? '() #t)))
- (if known?
- (hashtable-put! known name node))
- (if (lambda? code)
- (for-each (lambda (var)
- (hashtable-put! variables var rep))
- (make-null-terminated (lambda.args code))))))
- g)
-
- (for-each (lambda (node)
- (let ((name (callgraphnode.name node))
- (code (callgraphnode.code node)))
- (cond ((symbol? name)
- (hashtable-put! variables
- name
- (collect! (lambda.body code))))
- (else
- (collect! (lambda.body code))))))
- g)
-
- (if (and #f debugging?)
- (begin
- (hashtable-for-each (lambda (v rep)
- (write v)
- (display ": ")
- (write rep)
- (newline))
- variables)
-
- (display "----------------------------------------")
- (newline)))
-
- ;(trace aeval aeval1)
-
- (let* ((n (hashtable-size variables))
- (vars (hashtable-map (lambda (v rep) v) variables))
- (reps (map (lambda (v) (hashtable-get variables v)) vars))
- (init (make-vector n #f))
- (next (make-vector n)))
- (do ((i 0 (+ i 1))
- (vars vars (cdr vars))
- (reps reps (cdr reps)))
- ((= i n))
- (hashtable-put! variables (car vars) i)
- (vector-set! next
- i
- (let ((rep (car reps)))
- (lambda (env)
- (aeval rep env)))))
- (compute-fixedpoint init next equal?)
- (for-each (lambda (v)
- (let* ((i (hashtable-get variables v))
- (aval (vector-ref init i)))
- (hashtable-put! variables v aval)
- (if (and debugging?
- (not (eq? aval #t)))
- (begin (write v)
- (display ": ")
- (write aval)
- (newline)))))
- vars)
- variables)))
- ; Given a lambda expression, performs constant propagation, folding,
- ; and simplifications by side effect, using the abstract values in the
- ; hash table of variables.
- ; Returns #t if any new constants were created by constant folding,
- ; otherwise returns #f.
- (define (constant-folding! L variables)
- (let ((debugging? #f)
- (msg1 " Propagating constant value for ")
- (msg2 " Folding: ")
- (msg3 " ==> ")
- (folding? (integrate-usual-procedures))
- (changed? #f))
-
- ; Given a known lambda expression L, its original formal parameters,
- ; and a list of all calls to L, deletes arguments that are now
- ; ignored because of constant propagation.
-
- (define (delete-ignored-args! L formals0 calls)
- (let ((formals1 (lambda.args L)))
- (for-each (lambda (call)
- (do ((formals0 formals0 (cdr formals0))
- (formals1 formals1 (cdr formals1))
- (args (call.args call)
- (cdr args))
- (newargs '()
- (if (and (eq? (car formals1) name:IGNORED)
- (pair?
- (hashtable-get variables
- (car formals0))))
- newargs
- (cons (car args) newargs))))
- ((null? formals0)
- (call.args-set! call (reverse newargs)))))
- calls)
- (do ((formals0 formals0 (cdr formals0))
- (formals1 formals1 (cdr formals1))
- (formals2 '()
- (if (and (not (eq? (car formals0)
- (car formals1)))
- (eq? (car formals1) name:IGNORED)
- (pair?
- (hashtable-get variables
- (car formals0))))
- formals2
- (cons (car formals1) formals2))))
- ((null? formals0)
- (lambda.args-set! L (reverse formals2))))))
-
- (define (fold! exp)
-
- (case (car exp)
-
- ((quote) exp)
-
- ((lambda)
- (let ((Rinfo (lambda.R exp))
- (known (map def.lhs (lambda.defs exp))))
- (for-each (lambda (entry)
- (let* ((v (R-entry.name entry))
- (aval (hashtable-fetch variables v #t)))
- (if (and (pair? aval)
- (not (memq v known)))
- (let ((x (constant.value aval)))
- (if (or (boolean? x)
- (null? x)
- (symbol? x)
- (number? x)
- (char? x)
- (and (vector? x)
- (zero? (vector-length x))))
- (let ((refs (R-entry.references entry)))
- (for-each (lambda (ref)
- (variable-set! ref aval))
- refs)
- ; Do not try to use Rinfo in place of
- ; (lambda.R exp) below!
- (lambda.R-set!
- exp
- (remq entry (lambda.R exp)))
- (flag-as-ignored v exp)
- (if debugging?
- (begin (display msg1)
- (write v)
- (display ": ")
- (write aval)
- (newline)))))))))
- Rinfo)
- (for-each (lambda (def)
- (let* ((name (def.lhs def))
- (rhs (def.rhs def))
- (entry (R-lookup Rinfo name))
- (calls (R-entry.calls entry)))
- (if (null? calls)
- (begin (lambda.defs-set!
- exp
- (remq def (lambda.defs exp)))
- ; Do not try to use Rinfo in place of
- ; (lambda.R exp) below!
- (lambda.R-set!
- exp
- (remq entry (lambda.R exp))))
- (let* ((formals0 (append (lambda.args rhs) '()))
- (L (fold! rhs))
- (formals1 (lambda.args L)))
- (if (not (equal? formals0 formals1))
- (delete-ignored-args! L formals0 calls))))))
- (lambda.defs exp))
- (lambda.body-set!
- exp
- (fold! (lambda.body exp)))
- exp))
-
- ((set!)
- (assignment.rhs-set! exp (fold! (assignment.rhs exp)))
- exp)
-
- ((begin)
- (if (variable? exp)
- exp
- (post-simplify-begin (make-begin (map fold! (begin.exprs exp)))
- (make-notepad #f))))
-
- ((if)
- (let ((exp0 (fold! (if.test exp)))
- (exp1 (fold! (if.then exp)))
- (exp2 (fold! (if.else exp))))
- (if (constant? exp0)
- (let ((newexp (if (constant.value exp0)
- exp1
- exp2)))
- (if debugging?
- (begin (display msg2)
- (write (make-readable exp))
- (display msg3)
- (write (make-readable newexp))
- (newline)))
- (set! changed? #t)
- newexp)
- (make-conditional exp0 exp1 exp2))))
-
- (else
- (let ((args (map fold! (call.args exp)))
- (proc (fold! (call.proc exp))))
- (cond ((and folding?
- (variable? proc)
- (every? constant? args)
- (let ((entry
- (constant-folding-entry (variable.name proc))))
- (and entry
- (let ((preds
- (constant-folding-predicates entry)))
- (and (= (length args) (length preds))
- (every?
- (lambda (x) x)
- (map (lambda (f v) (f v))
- (constant-folding-predicates entry)
- (map constant.value args))))))))
- (set! changed? #t)
- (let ((result
- (make-constant
- (apply (constant-folding-folder
- (constant-folding-entry
- (variable.name proc)))
- (map constant.value args)))))
- (if debugging?
- (begin (display msg2)
- (write (make-readable (make-call proc args)))
- (display msg3)
- (write result)
- (newline)))
- result))
- ((and (lambda? proc)
- (list? (lambda.args proc)))
- ; FIXME: Folding should be done even if there is
- ; a rest argument.
- (let loop ((formals (reverse (lambda.args proc)))
- (actuals (reverse args))
- (processed-formals '())
- (processed-actuals '())
- (for-effect '()))
- (cond ((null? formals)
- (lambda.args-set! proc processed-formals)
- (call.args-set! exp processed-actuals)
- (let ((call (if (and (null? processed-formals)
- (null? (lambda.defs proc)))
- (lambda.body proc)
- exp)))
- (if (null? for-effect)
- call
- (post-simplify-begin
- (make-begin
- (reverse (cons call for-effect)))
- (make-notepad #f)))))
- ((ignored? (car formals))
- (loop (cdr formals)
- (cdr actuals)
- processed-formals
- processed-actuals
- (cons (car actuals) for-effect)))
- (else
- (loop (cdr formals)
- (cdr actuals)
- (cons (car formals) processed-formals)
- (cons (car actuals) processed-actuals)
- for-effect)))))
- (else
- (call.proc-set! exp proc)
- (call.args-set! exp args)
- exp))))))
-
- (fold! L)
- changed?))
- ; Copyright 1998 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 7 June 1999.
- ;
- ; Conversion to A-normal form, with heuristics for
- ; choosing a good order of evaluation.
- ;
- ; This pass operates as a source-to-source transformation on
- ; expressions written in the subset of Scheme described by the
- ; following grammar, where the input and output expressions
- ; satisfy certain additional invariants described below.
- ;
- ; "X ..." means zero or more occurrences of X.
- ;
- ; L --> (lambda (I_1 ...)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>)
- ; E)
- ; | (lambda (I_1 ... . I_rest)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>))
- ; E)
- ; D --> (define I L)
- ; E --> (quote K) ; constants
- ; | (begin I) ; variable references
- ; | L ; lambda expressions
- ; | (E0 E1 ...) ; calls
- ; | (set! I E) ; assignments
- ; | (if E0 E1 E2) ; conditionals
- ; | (begin E0 E1 E2 ...) ; sequential expressions
- ; I --> <identifier>
- ;
- ; R --> ((I <references> <assignments> <calls>) ...)
- ; F --> (I ...)
- ; G --> (I ...)
- ;
- ; Invariants that hold for the input only:
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * For each lambda expression, the associated F is a list of all
- ; the identifiers that occur free in the body of that lambda
- ; expression, and possibly a few extra identifiers that were
- ; once free but have been removed by optimization.
- ; * For each lambda expression, the associated G is a subset of F
- ; that contains every identifier that occurs free within some
- ; inner lambda expression that escapes, and possibly a few that
- ; don't. (Assignment-elimination does not calculate G exactly.)
- ; * Variables named IGNORED are neither referenced nor assigned.
- ;
- ; Invariants that hold for the output only:
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * R, F, and G are garbage.
- ; * There are no sequential expressions.
- ; * The output is an expression E with syntax
- ;
- ; E --> A
- ; | (L)
- ; | (L A)
- ;
- ; A --> W
- ; | L
- ; | (W_0 W_1 ...)
- ; | (set! I W)
- ; | (if W E1 E2)
- ;
- ; W --> (quote K)
- ; | (begin I)
- ;
- ; In other words:
- ; An expression is a LET* such that the rhs of every binding is
- ; a conditional with the test already evaluated, or
- ; an expression that can be evaluated in one step
- ; (treating function calls as a single step)
- ;
- ; A-normal form corresponds to the control flow graph for a lambda
- ; expression.
- ; Algorithm: repeated use of these rules:
- ;
- ; (E0 E1 ...) ((lambda (T0 T1 ...) (T0 T1 ...))
- ; E0 E1 ...)
- ; (set! I E) ((lambda (T) (set! I T)) E)
- ; (if E0 E1 E2) ((lambda (T) (if T E1 E2)) E0)
- ; (begin E0 E1 E2 ...) ((lambda (T) (begin E1 E2 ...)) E0)
- ;
- ; ((lambda (I1 I2 I3 ...) E) ((lambda (I1)
- ; E1 E2 E3) ((lambda (I2 I3 ...) E)
- ; E2 E3))
- ; E1)
- ;
- ; ((lambda (I2) E) ((lambda (I1)
- ; ((lambda (I1) E2) ((lambda (I2) E)
- ; E1)) E2)
- ; E1)
- ;
- ; In other words:
- ; Introduce a temporary name for every expression except:
- ; tail expressions
- ; the alternatives of a non-tail conditional
- ; Convert every LET into a LET*.
- ; Get rid of LET* on the right hand side of a binding.
- ; Given an expression E in the representation output by pass 2,
- ; returns an A-normal form for E in that representation.
- ; Except for quoted values, the A-normal form does not share
- ; mutable structure with the original expression E.
- ;
- ; KNOWN BUG:
- ;
- ; If you call A-normal on a form that has already been converted
- ; to A-normal form, then the same temporaries will be generated
- ; twice. An optional argument lets you specify a different prefix
- ; for temporaries the second time around. Example:
- ;
- ; (A-normal-form (A-normal-form E ".T")
- ; ".U")
- ; This is the declaration that is used to indicate A-normal form.
- (define A-normal-form-declaration (list 'anf))
- (define (A-normal-form E . rest)
-
- (define (A-normal-form E)
- (anf-make-let* (anf E '() '())))
-
- ; New temporaries.
-
- (define temp-counter 0)
-
- (define temp-prefix
- (if (or (null? rest)
- (not (string? (car rest))))
- (string-append renaming-prefix "T")
- (car rest)))
-
- (define (newtemp)
- (set! temp-counter (+ temp-counter 1))
- (string->symbol
- (string-append temp-prefix
- (number->string temp-counter))))
-
- ; Given an expression E as output by pass 2,
- ; a list of surrounding LET* bindings,
- ; and an ordered list of likely register variables,
- ; return a non-empty list of LET* bindings
- ; whose first binding associates a dummy variable
- ; with an A-expression giving the value for E.
-
- (define (anf E bindings regvars)
- (case (car E)
- ((quote) (anf-bind-dummy E bindings))
- ((begin) (if (variable? E)
- (anf-bind-dummy E bindings)
- (anf-sequential E bindings regvars)))
- ((lambda) (anf-lambda E bindings regvars))
- ((set!) (anf-assignment E bindings regvars))
- ((if) (anf-conditional E bindings regvars))
- (else (anf-call E bindings regvars))))
-
- (define anf:dummy (string->symbol "RESULT"))
-
- (define (anf-bind-dummy E bindings)
- (cons (list anf:dummy E)
- bindings))
-
- ; Unlike anf-bind-dummy, anf-bind-name and anf-bind convert
- ; their expression argument to A-normal form.
- ; Don't change anf-bind to call anf-bind-name, because that
- ; would name the temporaries in an aesthetically bad order.
-
- (define (anf-bind-name name E bindings regvars)
- (let ((bindings (anf E bindings regvars)))
- (cons (list name (cadr (car bindings)))
- (cdr bindings))))
-
- (define (anf-bind E bindings regvars)
- (let ((bindings (anf E bindings regvars)))
- (cons (list (newtemp) (cadr (car bindings)))
- (cdr bindings))))
-
- (define (anf-result bindings)
- (make-variable (car (car bindings))))
-
- (define (anf-make-let* bindings)
- (define (loop bindings body)
- (if (null? bindings)
- body
- (let ((T1 (car (car bindings)))
- (E1 (cadr (car bindings))))
- (loop (cdr bindings)
- (make-call (make-lambda (list T1)
- '()
- '()
- '()
- '()
- (list A-normal-form-declaration)
- '()
- body)
- (list E1))))))
- (loop (cdr bindings)
- (cadr (car bindings))))
-
- (define (anf-sequential E bindings regvars)
- (do ((bindings bindings
- (anf-bind (car exprs) bindings regvars))
- (exprs (begin.exprs E)
- (cdr exprs)))
- ((null? (cdr exprs))
- (anf (car exprs) bindings regvars))))
-
- ; Heuristic: the formal parameters of an escaping lambda or
- ; known local procedure are kept in REG1, REG2, et cetera.
-
- (define (anf-lambda L bindings regvars)
- (anf-bind-dummy
- (make-lambda (lambda.args L)
- (map (lambda (def)
- (make-definition
- (def.lhs def)
- (A-normal-form (def.rhs def))))
- (lambda.defs L))
- '()
- '()
- '()
- (cons A-normal-form-declaration
- (lambda.decls L))
- (lambda.doc L)
- (anf-make-let*
- (anf (lambda.body L)
- '()
- (make-null-terminated (lambda.args L)))))
- bindings))
-
- (define (anf-assignment E bindings regvars)
- (let ((I (assignment.lhs E))
- (E1 (assignment.rhs E)))
- (if (variable? E1)
- (anf-bind-dummy E bindings)
- (let* ((bindings (anf-bind E1 bindings regvars))
- (T1 (anf-result bindings)))
- (anf-bind-dummy (make-assignment I T1) bindings)))))
-
- (define (anf-conditional E bindings regvars)
- (let ((E0 (if.test E))
- (E1 (if.then E))
- (E2 (if.else E)))
- (if (variable? E0)
- (let ((E1 (anf-make-let* (anf E1 '() regvars)))
- (E2 (anf-make-let* (anf E2 '() regvars))))
- (anf-bind-dummy
- (make-conditional E0 E1 E2)
- bindings))
- (let* ((bindings (anf-bind E0 bindings regvars))
- (E1 (anf-make-let* (anf E1 '() regvars)))
- (E2 (anf-make-let* (anf E2 '() regvars))))
- (anf-bind-dummy
- (make-conditional (anf-result bindings) E1 E2)
- bindings)))))
-
- (define (anf-call E bindings regvars)
- (let* ((proc (call.proc E))
- (args (call.args E)))
-
- ; Evaluates the exprs and returns both a list of bindings and
- ; a list of the temporaries that name the results of the exprs.
- ; If rename-always? is true, then temporaries are generated even
- ; for constants and temporaries.
-
- (define (loop exprs bindings names rename-always?)
- (if (null? exprs)
- (values bindings (reverse names))
- (let ((E (car exprs)))
- (if (or rename-always?
- (not (or (constant? E)
- (variable? E))))
- (let* ((bindings
- (anf-bind (car exprs) bindings regvars)))
- (loop (cdr exprs)
- bindings
- (cons (anf-result bindings) names)
- rename-always?))
- (loop (cdr exprs)
- bindings
- (cons E names)
- rename-always?)))))
-
- ; Evaluates the exprs, binding them to the vars, and returns
- ; a list of bindings.
- ;
- ; Although LET variables are likely to be kept in registers,
- ; trying to guess which register will be allocated is likely
- ; to do more harm than good.
-
- (define (let-loop exprs bindings regvars vars)
- (if (null? exprs)
- (if (null? (lambda.defs proc))
- (anf (lambda.body proc)
- bindings
- regvars)
- (let ((bindings
- (anf-bind
- (make-lambda '()
- (lambda.defs proc)
- '()
- '()
- '()
- (cons A-normal-form-declaration
- (lambda.decls proc))
- (lambda.doc proc)
- (lambda.body proc))
- bindings
- '())))
- (anf-bind-dummy
- (make-call (anf-result bindings) '())
- bindings)))
- (let-loop (cdr exprs)
- (anf-bind-name (car vars)
- (car exprs)
- bindings
- regvars)
- regvars
- (cdr vars))))
-
- (cond ((lambda? proc)
- (let ((formals (lambda.args proc)))
- (if (list? formals)
- (let* ((pi (anf-order-of-evaluation args regvars #f))
- (exprs (permute args pi))
- (names (permute (lambda.args proc) pi)))
- (let-loop (reverse exprs) bindings regvars (reverse names)))
- (anf-call (normalize-let E) bindings regvars))))
-
- ((not (variable? proc))
- (let ((pi (anf-order-of-evaluation args regvars #f)))
- (call-with-values
- (lambda () (loop (permute args pi) bindings '() #t))
- (lambda (bindings names)
- (let ((bindings (anf-bind proc bindings regvars)))
- (anf-bind-dummy
- (make-call (anf-result bindings)
- (unpermute names pi))
- bindings))))))
-
- ((and (integrate-usual-procedures)
- (prim-entry (variable.name proc)))
- (let ((pi (anf-order-of-evaluation args regvars #t)))
- (call-with-values
- (lambda () (loop (permute args pi) bindings '() #t))
- (lambda (bindings names)
- (anf-bind-dummy
- (make-call proc (unpermute names pi))
- bindings)))))
-
- ((memq (variable.name proc) regvars)
- (let* ((exprs (cons proc args))
- (pi (anf-order-of-evaluation
- exprs
- (cons name:IGNORED regvars)
- #f)))
- (call-with-values
- (lambda () (loop (permute exprs pi) bindings '() #t))
- (lambda (bindings names)
- (let ((names (unpermute names pi)))
- (anf-bind-dummy
- (make-call (car names) (cdr names))
- bindings))))))
-
- (else
- (let ((pi (anf-order-of-evaluation args regvars #f)))
- (call-with-values
- (lambda () (loop (permute args pi) bindings '() #t))
- (lambda (bindings names)
- (anf-bind-dummy
- (make-call proc (unpermute names pi))
- bindings))))))))
-
- ; Given a list of expressions, a list of likely register contents,
- ; and a switch telling whether these are arguments for a primop
- ; or something else (such as the arguments for a real call),
- ; try to choose a good order in which to evaluate the expressions.
- ;
- ; Heuristic: If none of the expressions is a call to a non-primop,
- ; then parallel assignment optimization gives a good order if the
- ; regvars are right, and should do no worse than a random order if
- ; the regvars are wrong.
- ;
- ; Heuristic: If the expressions are arguments to a primop, and
- ; none are a call to a non-primop, then the register contents
- ; are irrelevant, and the first argument should be evaluated last.
- ;
- ; Heuristic: If one or more of the expressions is a call to a
- ; non-primop, then the following should be a good order:
- ;
- ; expressions that are neither a constant, variable, or a call
- ; calls to non-primops
- ; constants and variables
-
- (define (anf-order-of-evaluation exprs regvars for-primop?)
- (define (ordering targets exprs alist)
- (let ((para
- (parallel-assignment targets alist exprs)))
- (or para
- ; Evaluate left to right until a parallel assignment is found.
- (cons (car targets)
- (ordering (cdr targets)
- (cdr exprs)
- alist)))))
- (if (parallel-assignment-optimization)
- (cond ((null? exprs) '())
- ((null? (cdr exprs)) '(0))
- (else
- (let* ((contains-call? #f)
- (vexprs (list->vector exprs))
- (vindexes (list->vector
- (iota (vector-length vexprs))))
- (contains-call? #f)
- (categories
- (list->vector
- (map (lambda (E)
- (cond ((constant? E)
- 2)
- ((variable? E)
- 2)
- ((complicated? E)
- (set! contains-call? #t)
- 1)
- (else
- 0)))
- exprs))))
- (cond (contains-call?
- (twobit-sort (lambda (i j)
- (< (vector-ref categories i)
- (vector-ref categories j)))
- (iota (length exprs))))
- (for-primop?
- (reverse (iota (length exprs))))
- (else
- (let ((targets (iota (length exprs))))
- (define (pairup regvars targets)
- (if (or (null? targets)
- (null? regvars))
- '()
- (cons (cons (car regvars)
- (car targets))
- (pairup (cdr regvars)
- (cdr targets)))))
- (ordering targets
- exprs
- (pairup regvars targets))))))))
- (iota (length exprs))))
-
- (define (permute things pi)
- (let ((v (list->vector things)))
- (map (lambda (i) (vector-ref v i))
- pi)))
-
- (define (unpermute things pi)
- (let* ((v0 (list->vector things))
- (v1 (make-vector (vector-length v0))))
- (do ((pi pi (cdr pi))
- (k 0 (+ k 1)))
- ((null? pi)
- (vector->list v1))
- (vector-set! v1 (car pi) (vector-ref v0 k)))))
-
- ; Given a call whose procedure is a lambda expression that has
- ; a rest argument, return a genuine let expression.
-
- (define (normalize-let-error exp)
- (if (issue-warnings)
- (begin (display "WARNING from compiler: ")
- (display "Wrong number of arguments ")
- (display "to lambda expression")
- (newline)
- (pretty-print (make-readable exp) #t)
- (newline))))
-
- (define (normalize-let exp)
- (let* ((L (call.proc exp)))
- (let loop ((formals (lambda.args L))
- (args (call.args exp))
- (newformals '())
- (newargs '()))
- (cond ((null? formals)
- (if (null? args)
- (begin (lambda.args-set! L (reverse newformals))
- (call.args-set! exp (reverse newargs)))
- (begin (normalize-let-error exp)
- (loop (list (newtemp))
- args
- newformals
- newargs))))
- ((pair? formals)
- (if (pair? args)
- (loop (cdr formals)
- (cdr args)
- (cons (car formals) newformals)
- (cons (car args) newargs))
- (begin (normalize-let-error exp)
- (loop formals
- (cons (make-constant 0)
- args)
- newformals
- newargs))))
- (else
- (loop (list formals)
- (list (make-call-to-list args))
- newformals
- newargs))))))
-
- ; For heuristic use only.
- ; An expression is complicated unless it can probably be evaluated
- ; without saving and restoring any registers, even if it occurs in
- ; a non-tail position.
-
- (define (complicated? exp)
- ; Let's not spend all day on this.
- (let ((budget 10))
- (define (complicated? exp)
- (set! budget (- budget 1))
- (if (zero? budget)
- #t
- (case (car exp)
- ((quote) #f)
- ((lambda) #f)
- ((set!) (complicated? (assignment.rhs exp)))
- ((if) (or (complicated? (if.test exp))
- (complicated? (if.then exp))
- (complicated? (if.else exp))))
- ((begin) (if (variable? exp)
- #f
- (some? complicated?
- (begin.exprs exp))))
- (else (let ((proc (call.proc exp)))
- (if (and (variable? proc)
- (integrate-usual-procedures)
- (prim-entry (variable.name proc)))
- (some? complicated?
- (call.args exp))
- #t))))))
- (complicated? exp)))
-
- (A-normal-form E))
- (define (post-simplify-anf L0 T1 E0 E1 free regbindings L2)
-
- (define (return-normally)
- (values (make-call L0 (list E1))
- free
- regbindings))
-
- (return-normally))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 7 June 1999.
- ;
- ; Intraprocedural common subexpression elimination, constant propagation,
- ; copy propagation, dead code elimination, and register targeting.
- ;
- ; (intraprocedural-commoning E 'commoning)
- ;
- ; Given an A-normal form E (alpha-converted, with correct free
- ; variables and referencing information), returns an optimized
- ; A-normal form with correct free variables but incorrect referencing
- ; information.
- ;
- ; (intraprocedural-commoning E 'target-registers)
- ;
- ; Given an A-normal form E (alpha-converted, with correct free
- ; variables and referencing information), returns an A-normal form
- ; with correct free variables but incorrect referencing information,
- ; and in which MacScheme machine register names are used as temporary
- ; variables. The result is alpha-converted except for register names.
- ;
- ; (intraprocedural-commoning E 'commoning 'target-registers)
- ; (intraprocedural-commoning E)
- ;
- ; Given an A-normal form as described above, returns an optimized
- ; form in which register names are used as temporary variables.
- ; Semantics of .check!:
- ;
- ; (.check! b exn x ...) faults with code exn and arguments x ...
- ; if b is #f.
- ; The list of argument registers.
- ; This can't go in pass3commoning.aux.sch because that file must be
- ; loaded before the target-specific file that defines *nregs*.
- (define argument-registers
- (do ((n (- *nregs* 2) (- n 1))
- (regs '()
- (cons (string->symbol
- (string-append ".REG" (number->string n)))
- regs)))
- ((zero? n)
- regs)))
- (define (intraprocedural-commoning E . flags)
-
- (define target-registers? (or (null? flags) (memq 'target-registers flags)))
- (define commoning? (or (null? flags) (memq 'commoning flags)))
-
- (define debugging? #f)
-
- (call-with-current-continuation
- (lambda (return)
-
- (define (error . stuff)
- (display "Bug detected during intraprocedural optimization")
- (newline)
- (for-each (lambda (s)
- (display s) (newline))
- stuff)
- (return (make-constant #f)))
-
- ; Given an expression, an environment, the available expressions,
- ; and an ordered list of likely register variables (used heuristically),
- ; returns the transformed expression and its set of free variables.
-
- (define (scan-body E env available regvars)
-
- ; The local variables are those that are bound by a LET within
- ; this procedure. The formals of a lambda expression and the
- ; known local procedures are counted as non-global, not local,
- ; because there is no let-binding for a formal that can be
- ; renamed during register targeting.
- ; For each local variable, we keep track of how many times it
- ; is referenced. This information is not accurate until we
- ; are backing out of the recursion, and does not have to be.
-
- (define local-variables (make-hashtable symbol-hash assq))
-
- (define (local-variable? sym)
- (hashtable-get local-variables sym))
-
- (define (local-variable-not-used? sym)
- (= 0 (hashtable-fetch local-variables sym -1)))
-
- (define (local-variable-used-once? sym)
- (= 1 (hashtable-fetch local-variables sym 0)))
-
- (define (record-local-variable! sym)
- (hashtable-put! local-variables sym 0))
-
- (define (used-local-variable! sym)
- (adjust-local-variable! sym 1))
-
- (define (adjust-local-variable! sym n)
- (let ((m (hashtable-get local-variables sym)))
- (if debugging?
- (if (and m (> m 0))
- (begin (write (list sym (+ m n)))
- (newline))))
- (if m
- (hashtable-put! local-variables
- sym
- (+ m n)))))
-
- (define (closed-over-local-variable! sym)
- ; Set its reference count to infinity so it won't be optimized away.
- ; FIXME: One million isn't infinity.
- (hashtable-put! local-variables sym 1000000))
-
- (define (used-variable! sym)
- (used-local-variable! sym))
-
- (define (abandon-expression! E)
- (cond ((variable? E)
- (adjust-local-variable! (variable.name E) -1))
- ((conditional? E)
- (abandon-expression! (if.test E))
- (abandon-expression! (if.then E))
- (abandon-expression! (if.else E)))
- ((call? E)
- (for-each (lambda (exp)
- (if (variable? exp)
- (let ((name (variable.name exp)))
- (if (local-variable? name)
- (adjust-local-variable! name -1)))))
- (cons (call.proc E)
- (call.args E))))))
-
- ; Environments are represented as hashtrees.
-
- (define (make-empty-environment)
- (make-hashtree symbol-hash assq))
-
- (define (environment-extend env sym)
- (hashtree-put env sym #t))
-
- (define (environment-extend* env symbols)
- (if (null? symbols)
- env
- (environment-extend* (hashtree-put env (car symbols) #t)
- (cdr symbols))))
-
- (define (environment-lookup env sym)
- (hashtree-get env sym))
-
- (define (global? x)
- (cond ((local-variable? x)
- #f)
- ((environment-lookup env x)
- #f)
- (else
- #t)))
-
- ;
-
- (define (available-add! available T E)
- (cond ((constant? E)
- (available-extend! available T E available:killer:immortal))
- ((variable? E)
- (available-extend! available
- T
- E
- (if (global? (variable.name E))
- available:killer:globals
- available:killer:immortal)))
- (else
- (let ((entry (prim-call E)))
- (if entry
- (let ((killer (prim-lives-until entry)))
- (if (not (eq? killer available:killer:dead))
- (do ((args (call.args E) (cdr args))
- (k killer
- (let ((arg (car args)))
- (if (and (variable? arg)
- (global? (variable.name arg)))
- available:killer:globals
- k))))
- ((null? args)
- (available-extend!
- available
- T
- E
- (logior killer k)))))))))))
-
- ; Given an expression E,
- ; an environment containing all variables that are in scope,
- ; and a table of available expressions,
- ; returns multiple values:
- ; the transformed E
- ; the free variables of E
- ; the register bindings to be inserted; each binding has the form
- ; (R x (begin R)), where (begin R) is a reference to R.
- ;
- ; Side effects E.
-
- (define (scan E env available)
- (if (not (call? E))
- (scan-rhs E env available)
- (let ((proc (call.proc E)))
- (if (not (lambda? proc))
- (scan-rhs E env available)
- (let ((vars (lambda.args proc)))
- (cond ((null? vars)
- (scan-let0 E env available))
- ((null? (cdr vars))
- (scan-binding E env available))
- (else
- (error (make-readable E)))))))))
-
- ; E has the form of (let ((T1 E1)) E0).
-
- (define (scan-binding E env available)
- (let* ((L (call.proc E))
- (T1 (car (lambda.args L)))
- (E1 (car (call.args E)))
- (E0 (lambda.body L)))
- (record-local-variable! T1)
- (call-with-values
- (lambda () (scan-rhs E1 env available))
- (lambda (E1 F1 regbindings1)
- (available-add! available T1 E1)
- (let* ((env (let ((formals
- (make-null-terminated (lambda.args L))))
- (environment-extend*
- (environment-extend* env formals)
- (map def.lhs (lambda.defs L)))))
- (Fdefs (scan-defs L env available)))
- (call-with-values
- (lambda () (scan E0 env available))
- (lambda (E0 F0 regbindings0)
- (lambda.body-set! L E0)
- (if target-registers?
- (scan-binding-phase2
- L T1 E0 E1 F0 F1 Fdefs regbindings0 regbindings1)
- (scan-binding-phase3
- L E0 E1 (union F0 Fdefs)
- F1 regbindings0 regbindings1)))))))))
-
- ; Given the lambda expression for a let expression that binds
- ; a single variable T1, the transformed body E0 and right hand side E1,
- ; their sets of free variables F0 and F1, the set of free variables
- ; for the internal definitions of L, and the sets of register
- ; bindings that need to be wrapped around E0 and E1, returns the
- ; transformed let expression, its free variables, and register
- ; bindings.
- ;
- ; This phase is concerned exclusively with register bindings,
- ; and is bypassed unless the target-registers flag is specified.
-
- (define (scan-binding-phase2
- L T1 E0 E1 F0 F1 Fdefs regbindings0 regbindings1)
-
- ; T1 can't be a register because we haven't
- ; yet inserted register bindings that high up.
-
- ; Classify the register bindings that need to wrapped around E0:
- ; 1. those that have T1 as their rhs
- ; 2. those whose lhs is a register that is likely to hold
- ; a variable that occurs free in E1
- ; 3. all others
-
- (define (phase2a)
- (do ((rvars regvars (cdr rvars))
- (regs argument-registers (cdr regs))
- (regs1 '() (if (memq (car rvars) F1)
- (cons (car regs) regs1)
- regs1)))
- ((or (null? rvars)
- (null? regs))
- ; regs1 is the set of registers that are live for E1
-
- (let loop ((regbindings regbindings0)
- (rb1 '())
- (rb2 '())
- (rb3 '()))
- (if (null? regbindings)
- (phase2b rb1 rb2 rb3)
- (let* ((binding (car regbindings))
- (regbindings (cdr regbindings))
- (lhs (regbinding.lhs binding))
- (rhs (regbinding.rhs binding)))
- (cond ((eq? rhs T1)
- (loop regbindings
- (cons binding rb1)
- rb2
- rb3))
- ((memq lhs regs1)
- (loop regbindings
- rb1
- (cons binding rb2)
- rb3))
- (else
- (loop regbindings
- rb1
- rb2
- (cons binding rb3))))))))))
-
- ; Determine which categories of register bindings should be
- ; wrapped around E0.
- ; Always wrap the register bindings in category 2.
- ; If E1 is a conditional or a real call, then wrap category 3.
- ; If T1 might be used more than once, then wrap category 1.
-
- (define (phase2b rb1 rb2 rb3)
- (if (or (conditional? E1)
- (real-call? E1))
- (phase2c (append rb2 rb3) rb1 '())
- (phase2c rb2 rb1 rb3)))
-
- (define (phase2c towrap rb1 regbindings0)
- (cond ((and (not (null? rb1))
- (local-variable-used-once? T1))
- (phase2d towrap rb1 regbindings0))
- (else
- (phase2e (append rb1 towrap) regbindings0))))
-
- ; T1 is used only once, and there is a register binding (R T1).
- ; Change T1 to R.
-
- (define (phase2d towrap regbindings-T1 regbindings0)
- (if (not (null? (cdr regbindings-T1)))
- (error "incorrect number of uses" T1))
- (let* ((regbinding (car regbindings-T1))
- (R (regbinding.lhs regbinding)))
- (lambda.args-set! L (list R))
- (phase2e towrap regbindings0)))
-
- ; Wrap the selected register bindings around E0.
-
- (define (phase2e towrap regbindings0)
- (call-with-values
- (lambda ()
- (wrap-with-register-bindings towrap E0 F0))
- (lambda (E0 F0)
- (let ((F (union Fdefs F0)))
- (scan-binding-phase3
- L E0 E1 F F1 regbindings0 regbindings1)))))
-
- (phase2a))
-
- ; This phase, with arguments as above, constructs the result.
-
- (define (scan-binding-phase3 L E0 E1 F F1 regbindings0 regbindings1)
- (let* ((args (lambda.args L))
- (T1 (car args))
- (free (union F1 (difference F args)))
- (simple-let? (simple-lambda? L))
- (regbindings
-
- ; At least one of regbindings0 and regbindings1
- ; is the empty list.
-
- (cond ((null? regbindings0)
- regbindings1)
- ((null? regbindings1)
- regbindings0)
- (else
- (error 'scan-binding 'regbindings)))))
- (lambda.body-set! L E0)
- (lambda.F-set! L F)
- (lambda.G-set! L F)
- (cond ((and simple-let?
- (not (memq T1 F))
- (no-side-effects? E1))
- (abandon-expression! E1)
- (values E0 F regbindings0))
- ((and target-registers?
- simple-let?
- (local-variable-used-once? T1))
- (post-simplify-anf L T1 E0 E1 free regbindings #f))
- (else
- (values (make-call L (list E1))
- free
- regbindings)))))
-
- (define (scan-let0 E env available)
- (let ((L (call.proc E)))
- (if (simple-lambda? L)
- (scan (lambda.body L) env available)
- (let ((T1 (make-variable name:IGNORED)))
- (lambda.args-set! L (list T1))
- (call-with-values
- (lambda () (scan (make-call L (list (make-constant 0)))
- env
- available))
- (lambda (E F regbindings)
- (lambda.args-set! L '())
- (values (make-call L '())
- F
- regbindings)))))))
-
- ; Optimizes the internal definitions of L and returns their
- ; free variables.
-
- (define (scan-defs L env available)
- (let loop ((defs (lambda.defs L))
- (newdefs '())
- (Fdefs '()))
- (if (null? defs)
- (begin (lambda.defs-set! L (reverse newdefs))
- Fdefs)
- (let ((def (car defs)))
- (call-with-values
- (lambda ()
- (let* ((Ldef (def.rhs def))
- (Lformals (make-null-terminated (lambda.args Ldef)))
- (Lenv (environment-extend*
- (environment-extend* env Lformals)
- (map def.lhs (lambda.defs Ldef)))))
- (scan Ldef Lenv available)))
- (lambda (rhs Frhs empty)
- (if (not (null? empty))
- (error 'scan-binding 'def))
- (loop (cdr defs)
- (cons (make-definition (def.lhs def) rhs)
- newdefs)
- (union Frhs Fdefs))))))))
-
- ; Given the right-hand side of a let-binding, an environment,
- ; and a table of available expressions, returns the transformed
- ; expression, its free variables, and the register bindings that
- ; need to be wrapped around it.
-
- (define (scan-rhs E env available)
-
- (cond
- ((constant? E)
- (values E (empty-set) '()))
-
- ((variable? E)
- (let* ((name (variable.name E))
- (Enew (and commoning?
- (if (global? name)
- (let ((T (available-expression
- available E)))
- (if T
- (make-variable T)
- #f))
- (available-variable available name)))))
- (if Enew
- (scan-rhs Enew env available)
- (begin (used-variable! name)
- (values E (list name) '())))))
-
- ((lambda? E)
- (let* ((formals (make-null-terminated (lambda.args E)))
- (env (environment-extend*
- (environment-extend* env formals)
- (map def.lhs (lambda.defs E))))
- (Fdefs (scan-defs E env available)))
- (call-with-values
- (lambda ()
- (let ((available (copy-available-table available)))
- (available-kill! available available:killer:all)
- (scan-body (lambda.body E)
- env
- available
- formals)))
- (lambda (E0 F0 regbindings0)
- (call-with-values
- (lambda ()
- (wrap-with-register-bindings regbindings0 E0 F0))
- (lambda (E0 F0)
- (lambda.body-set! E E0)
- (let ((F (union Fdefs F0)))
- (for-each (lambda (x)
- (closed-over-local-variable! x))
- F)
- (lambda.F-set! E F)
- (lambda.G-set! E F)
- (values E
- (difference F
- (make-null-terminated
- (lambda.args E)))
- '()))))))))
-
- ((conditional? E)
- (let ((E0 (if.test E))
- (E1 (if.then E))
- (E2 (if.else E)))
- (if (constant? E0)
- ; FIXME: E1 and E2 might not be a legal rhs,
- ; so we can't just return the simplified E1 or E2.
- (let ((E1 (if (constant.value E0) E1 E2)))
- (call-with-values
- (lambda () (scan E1 env available))
- (lambda (E1 F1 regbindings1)
- (cond ((or (not (call? E1))
- (not (lambda? (call.proc E1))))
- (values E1 F1 regbindings1))
- (else
- ; FIXME: Must return a valid rhs.
- (values (make-conditional
- (make-constant #t)
- E1
- (make-constant 0))
- F1
- regbindings1))))))
- (call-with-values
- (lambda () (scan E0 env available))
- (lambda (E0 F0 regbindings0)
- (if (not (null? regbindings0))
- (error 'scan-rhs 'if))
- (if (not (eq? E0 (if.test E)))
- (scan-rhs (make-conditional E0 E1 E2)
- env available)
- (let ((available1
- (copy-available-table available))
- (available2
- (copy-available-table available)))
- (if (variable? E0)
- (let ((T0 (variable.name E0)))
- (available-add!
- available2 T0 (make-constant #f)))
- (error (make-readable E #t)))
- (call-with-values
- (lambda () (scan E1 env available1))
- (lambda (E1 F1 regbindings1)
- (call-with-values
- (lambda ()
- (wrap-with-register-bindings
- regbindings1 E1 F1))
- (lambda (E1 F1)
- (call-with-values
- (lambda () (scan E2 env available2))
- (lambda (E2 F2 regbindings2)
- (call-with-values
- (lambda ()
- (wrap-with-register-bindings
- regbindings2 E2 F2))
- (lambda (E2 F2)
- (let ((E (make-conditional
- E0 E1 E2))
- (F (union F0 F1 F2)))
- (available-intersect!
- available
- available1
- available2)
- (values E F '())))))))))))))))))
-
-
- ((assignment? E)
- (call-with-values
- (lambda () (scan-rhs (assignment.rhs E) env available))
- (lambda (E1 F1 regbindings1)
- (if (not (null? regbindings1))
- (error 'scan-rhs 'set!))
- (available-kill! available available:killer:globals)
- (values (make-assignment (assignment.lhs E) E1)
- (union (list (assignment.lhs E)) F1)
- '()))))
-
- ((begin? E)
- ; Shouldn't occur in A-normal form.
- (error 'scan-rhs 'begin))
-
- ((real-call? E)
- (let* ((E0 (call.proc E))
- (args (call.args E))
- (regcontents (append regvars
- (map (lambda (x) #f) args))))
- (let loop ((args args)
- (regs argument-registers)
- (regcontents regcontents)
- (newargs '())
- (regbindings '())
- (F (if (variable? E0)
- (let ((f (variable.name E0)))
- (used-variable! f)
- (list f))
- (empty-set))))
- (cond ((null? args)
- (available-kill! available available:killer:all)
- (values (make-call E0 (reverse newargs))
- F
- regbindings))
- ((null? regs)
- (let ((arg (car args)))
- (loop (cdr args)
- '()
- (cdr regcontents)
- (cons arg newargs)
- regbindings
- (if (variable? arg)
- (let ((name (variable.name arg)))
- (used-variable! name)
- (union (list name) F))
- F))))
- ((and commoning?
- (variable? (car args))
- (available-variable
- available
- (variable.name (car args))))
- (let* ((name (variable.name (car args)))
- (Enew (available-variable available name)))
- (loop (cons Enew (cdr args))
- regs regcontents newargs regbindings F)))
- ((and target-registers?
- (variable? (car args))
- (let ((x (variable.name (car args))))
- ; We haven't yet recorded this use.
- (or (local-variable-not-used? x)
- (and (memq x regvars)
- (not (eq? x (car regcontents)))))))
- (let* ((x (variable.name (car args)))
- (R (car regs))
- (newarg (make-variable R)))
- (used-variable! x)
- (loop (cdr args)
- (cdr regs)
- (cdr regcontents)
- (cons newarg newargs)
- (cons (make-regbinding R x newarg)
- regbindings)
- (union (list R) F))))
- (else
- (let ((E1 (car args)))
- (loop (cdr args)
- (cdr regs)
- (cdr regcontents)
- (cons E1 newargs)
- regbindings
- (if (variable? E1)
- (let ((name (variable.name E1)))
- (used-variable! name)
- (union (list name) F))
- F))))))))
-
- ((call? E)
- ; Must be a call to a primop.
- (let* ((E0 (call.proc E))
- (f0 (variable.name E0)))
- (let loop ((args (call.args E))
- (newargs '())
- (F (list f0)))
- (cond ((null? args)
- (let* ((E (make-call E0 (reverse newargs)))
- (T (and commoning?
- (available-expression
- available E))))
- (if T
- (begin (abandon-expression! E)
- (scan-rhs (make-variable T) env available))
- (begin
- (available-kill!
- available
- (prim-kills (prim-entry f0)))
- (cond ((eq? f0 name:check!)
- (let ((x (car (call.args E))))
- (cond ((not (runtime-safety-checking))
- (abandon-expression! E)
- ;(values x '() '())
- (scan-rhs x env available))
- ((variable? x)
- (available-add!
- available
- (variable.name x)
- (make-constant #t))
- (values E F '()))
- ((constant.value x)
- (abandon-expression! E)
- (values x '() '()))
- (else
- (declaration-error E)
- (values E F '())))))
- (else
- (values E F '())))))))
- ((variable? (car args))
- (let* ((E1 (car args))
- (x (variable.name E1))
- (Enew
- (and commoning?
- (available-variable available x))))
- (if Enew
- ; All of the arguments are constants or
- ; variables, so if the variable is replaced
- ; here it will be replaced throughout the call.
- (loop (cons Enew (cdr args))
- newargs
- (remq x F))
- (begin
- (used-variable! x)
- (loop (cdr args)
- (cons (car args) newargs)
- (union (list x) F))))))
- (else
- (loop (cdr args)
- (cons (car args) newargs)
- F))))))
-
- (else
- (error 'scan-rhs (make-readable E)))))
-
- (call-with-values
- (lambda () (scan E env available))
- (lambda (E F regbindings)
- (call-with-values
- (lambda () (wrap-with-register-bindings regbindings E F))
- (lambda (E F)
- (values E F '()))))))
-
- (call-with-values
- (lambda ()
- (scan-body E
- (make-hashtree symbol-hash assq)
- (make-available-table)
- '()))
- (lambda (E F regbindings)
- (if (not (null? regbindings))
- (error 'scan-body))
- E)))))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 16 June 1999.
- ;
- ; Intraprocedural representation inference.
- (define (representation-analysis exp)
- (let* ((debugging? #f)
- (integrate-usual? (integrate-usual-procedures))
- (known (make-hashtable symbol-hash assq))
- (types (make-hashtable symbol-hash assq))
- (g (callgraph exp))
- (schedule (list (callgraphnode.code (car g))))
- (changed? #f)
- (mutate? #f))
-
- ; known is a hashtable that maps the name of a known local procedure
- ; to a list of the form (tv1 ... tvN), where tv1, ..., tvN
- ; are type variables that stand for the representation types of its
- ; arguments. The type variable that stands for the representation
- ; type of the result of the procedure has the same name as the
- ; procedure itself.
-
- ; types is a hashtable that maps local variables and the names
- ; of known local procedures to an approximation of their
- ; representation type.
- ; For a known local procedure, the representation type is for the
- ; result of the procedure, not the procedure itself.
-
- ; schedule is a stack of work that needs to be done.
- ; Each entry in the stack is either an escaping lambda expression
- ; or the name of a known local procedure.
-
- (define (schedule! job)
- (if (not (memq job schedule))
- (begin (set! schedule (cons job schedule))
- (if (not (symbol? job))
- (callgraphnode.info! (lookup-node job) #t)))))
-
- ; Schedules a known local procedure.
-
- (define (schedule-known-procedure! name)
- ; Mark every known procedure that can actually be called.
- (callgraphnode.info! (assq name g) #t)
- (schedule! name))
-
- ; Schedule all code that calls the given known local procedure.
-
- (define (schedule-callers! name)
- (for-each (lambda (node)
- (if (and (callgraphnode.info node)
- (or (memq name (callgraphnode.tailcalls node))
- (memq name (callgraphnode.nontailcalls node))))
- (let ((caller (callgraphnode.name node)))
- (if caller
- (schedule! caller)
- (schedule! (callgraphnode.code node))))))
- g))
-
- ; Schedules local procedures of a lambda expression.
-
- (define (schedule-local-procedures! L)
- (for-each (lambda (def)
- (let ((name (def.lhs def)))
- (if (known-procedure-is-callable? name)
- (schedule! name))))
- (lambda.defs L)))
-
- ; Returns true iff the given known procedure is known to be callable.
-
- (define (known-procedure-is-callable? name)
- (callgraphnode.info (assq name g)))
-
- ; Sets CHANGED? to #t and returns #t if the type variable's
- ; approximation has changed; otherwise returns #f.
-
- (define (update-typevar! tv type)
- (let* ((type0 (hashtable-get types tv))
- (type0 (or type0
- (begin (hashtable-put! types tv rep:bottom)
- rep:bottom)))
- (type1 (representation-union type0 type)))
- (if (eq? type0 type1)
- #f
- (begin (hashtable-put! types tv type1)
- (set! changed? #t)
- (if (and debugging? mutate?)
- (begin (display "******** Changing type of ")
- (display tv)
- (display " from ")
- (display (rep->symbol type0))
- (display " to ")
- (display (rep->symbol type1))
- (newline)))
- #t))))
-
- ; GIven the name of a known local procedure, returns its code.
-
- (define (lookup-code name)
- (callgraphnode.code (assq name g)))
-
- ; Given a lambda expression, either escaping or the code for
- ; a known local procedure, returns its node in the call graph.
-
- (define (lookup-node L)
- (let loop ((g g))
- (cond ((null? g)
- (error "Unknown lambda expression" (make-readable L #t)))
- ((eq? L (callgraphnode.code (car g)))
- (car g))
- (else
- (loop (cdr g))))))
-
- ; Given: a type variable, expression, and a set of constraints.
- ; Side effects:
- ; Update the representation types of all variables that are
- ; bound within the expression.
- ; Update the representation types of all arguments to known
- ; local procedures that are called within the expression.
- ; If the representation type of an argument to a known local
- ; procedure changes, then schedule that procedure's code
- ; for analysis.
- ; Update the constraint set to reflect the constraints that
- ; hold following execution of the expression.
- ; If mutate? is true, then transform the expression to rely
- ; on the representation types that have been inferred.
- ; Return: type of the expression under the current assumptions
- ; and constraints.
-
- (define (analyze exp constraints)
-
- (if (and #f debugging?)
- (begin (display "Analyzing: ")
- (newline)
- (pretty-print (make-readable exp #t))
- (newline)))
-
- (case (car exp)
-
- ((quote)
- (representation-of-value (constant.value exp)))
-
- ((begin)
- (let* ((name (variable.name exp)))
- (representation-typeof name types constraints)))
-
- ((lambda)
- (schedule! exp)
- rep:procedure)
-
- ((set!)
- (analyze (assignment.rhs exp) constraints)
- (constraints-kill! constraints available:killer:globals)
- rep:object)
-
- ((if)
- (let* ((E0 (if.test exp))
- (E1 (if.then exp))
- (E2 (if.else exp))
- (type0 (analyze E0 constraints)))
- (if mutate?
- (cond ((representation-subtype? type0 rep:true)
- (if.test-set! exp (make-constant #t)))
- ((representation-subtype? type0 rep:false)
- (if.test-set! exp (make-constant #f)))))
- (cond ((representation-subtype? type0 rep:true)
- (analyze E1 constraints))
- ((representation-subtype? type0 rep:false)
- (analyze E2 constraints))
- ((variable? E0)
- (let* ((T0 (variable.name E0))
- (ignored (analyze E0 constraints))
- (constraints1 (copy-constraints-table constraints))
- (constraints2 (copy-constraints-table constraints)))
- (constraints-add! types
- constraints1
- (make-type-constraint
- T0 rep:true available:killer:immortal))
- (constraints-add! types
- constraints2
- (make-type-constraint
- T0 rep:false available:killer:immortal))
- (let* ((type1 (analyze E1 constraints1))
- (type2 (analyze E2 constraints2))
- (type (representation-union type1 type2)))
- (constraints-intersect! constraints
- constraints1
- constraints2)
- type)))
- (else
- (representation-error "Bad ANF" (make-readable exp #t))))))
-
- (else
- (let ((proc (call.proc exp))
- (args (call.args exp)))
- (cond ((lambda? proc)
- (cond ((null? args)
- (analyze-let0 exp constraints))
- ((null? (cdr args))
- (analyze-let1 exp constraints))
- (else
- (error "Compiler bug: pass3rep"))))
- ((variable? proc)
- (let* ((procname (variable.name proc)))
- (cond ((hashtable-get known procname)
- =>
- (lambda (vars)
- (analyze-known-call exp constraints vars)))
- (integrate-usual?
- (let ((entry (prim-entry procname)))
- (if entry
- (analyze-primop-call exp constraints entry)
- (analyze-unknown-call exp constraints))))
- (else
- (analyze-unknown-call exp constraints)))))
- (else
- (analyze-unknown-call exp constraints)))))))
-
- (define (analyze-let0 exp constraints)
- (let ((proc (call.proc exp)))
- (schedule-local-procedures! proc)
- (if (null? (lambda.args proc))
- (analyze (lambda.body exp) constraints)
- (analyze-unknown-call exp constraints))))
-
- (define (analyze-let1 exp constraints)
- (let* ((proc (call.proc exp))
- (vars (lambda.args proc)))
- (schedule-local-procedures! proc)
- (if (and (pair? vars)
- (null? (cdr vars)))
- (let* ((T1 (car vars))
- (E1 (car (call.args exp))))
- (if (and integrate-usual? (call? E1))
- (let ((proc (call.proc E1))
- (args (call.args E1)))
- (if (variable? proc)
- (let* ((op (variable.name proc))
- (entry (prim-entry op))
- (K1 (if entry
- (prim-lives-until entry)
- available:killer:dead)))
- (if (not (= K1 available:killer:dead))
- ; Must copy the call to avoid problems
- ; with side effects when mutate? is true.
- (constraints-add!
- types
- constraints
- (make-constraint T1
- (make-call proc args)
- K1)))))))
- (update-typevar! T1 (analyze E1 constraints))
- (analyze (lambda.body proc) constraints))
- (analyze-unknown-call exp constraints))))
-
- (define (analyze-primop-call exp constraints entry)
- (let* ((op (prim-opcodename entry))
- (args (call.args exp))
- (argtypes (map (lambda (arg) (analyze arg constraints))
- args))
- (type (rep-result? op argtypes)))
- (constraints-kill! constraints (prim-kills entry))
- (cond ((and (eq? op 'check!)
- (variable? (car args)))
- (let ((varname (variable.name (car args))))
- (if (and mutate?
- (representation-subtype? (car argtypes) rep:true))
- (call.args-set! exp
- (cons (make-constant #t) (cdr args))))
- (constraints-add! types
- constraints
- (make-type-constraint
- varname
- rep:true
- available:killer:immortal))))
- ((and mutate? (rep-specific? op argtypes))
- =>
- (lambda (newop)
- (call.proc-set! exp (make-variable newop)))))
- (or type rep:object)))
-
- (define (analyze-known-call exp constraints vars)
- (let* ((procname (variable.name (call.proc exp)))
- (args (call.args exp))
- (argtypes (map (lambda (arg) (analyze arg constraints))
- args)))
- (if (not (known-procedure-is-callable? procname))
- (schedule-known-procedure! procname))
- (for-each (lambda (var type)
- (if (update-typevar! var type)
- (schedule-known-procedure! procname)))
- vars
- argtypes)
- ; FIXME: We aren't analyzing the effects of known local procedures.
- (constraints-kill! constraints available:killer:all)
- (hashtable-get types procname)))
-
- (define (analyze-unknown-call exp constraints)
- (analyze (call.proc exp) constraints)
- (for-each (lambda (arg) (analyze arg constraints))
- (call.args exp))
- (constraints-kill! constraints available:killer:all)
- rep:object)
-
- (define (analyze-known-local-procedure name)
- (if debugging?
- (begin (display "Analyzing ")
- (display name)
- (newline)))
- (let ((L (lookup-code name))
- (constraints (make-constraints-table)))
- (schedule-local-procedures! L)
- (let ((type (analyze (lambda.body L) constraints)))
- (if (update-typevar! name type)
- (schedule-callers! name))
- type)))
-
- (define (analyze-unknown-lambda L)
- (if debugging?
- (begin (display "Analyzing escaping lambda expression")
- (newline)))
- (schedule-local-procedures! L)
- (let ((vars (make-null-terminated (lambda.args L))))
- (for-each (lambda (var)
- (hashtable-put! types var rep:object))
- vars)
- (analyze (lambda.body L)
- (make-constraints-table))))
-
- ; For debugging.
-
- (define (display-types)
- (hashtable-for-each (lambda (f vars)
- (write f)
- (display " : returns ")
- (write (rep->symbol (hashtable-get types f)))
- (newline)
- (for-each (lambda (x)
- (display " ")
- (write x)
- (display ": ")
- (write (rep->symbol
- (hashtable-get types x)))
- (newline))
- vars))
- known))
-
- (define (display-all-types)
- (let* ((vars (hashtable-map (lambda (x type) x) types))
- (vars (twobit-sort (lambda (var1 var2)
- (string<=? (symbol->string var1)
- (symbol->string var2)))
- vars)))
- (for-each (lambda (x)
- (write x)
- (display ": ")
- (write (rep->symbol
- (hashtable-get types x)))
- (newline))
- vars)))
- '
- (if debugging?
- (begin (pretty-print (make-readable (car schedule) #t))
- (newline)))
- (if debugging?
- (view-callgraph g))
-
- (for-each (lambda (node)
- (let* ((name (callgraphnode.name node))
- (code (callgraphnode.code node))
- (vars (make-null-terminated (lambda.args code)))
- (known? (symbol? name))
- (rep (if known? rep:bottom rep:object)))
- (callgraphnode.info! node #f)
- (if known?
- (begin (hashtable-put! known name vars)
- (hashtable-put! types name rep)))
- (for-each (lambda (var)
- (hashtable-put! types var rep))
- vars)))
- g)
-
- (let loop ()
- (cond ((not (null? schedule))
- (let ((job (car schedule)))
- (set! schedule (cdr schedule))
- (if (symbol? job)
- (analyze-known-local-procedure job)
- (analyze-unknown-lambda job))
- (loop)))
- (changed?
- (set! changed? #f)
- (set! schedule (list (callgraphnode.code (car g))))
- (if debugging?
- (begin (display-all-types) (newline)))
- (loop))))
-
- (if debugging?
- (display-types))
-
- (set! mutate? #t)
-
- ; We don't want to analyze known procedures that are never called.
-
- (set! schedule
- (cons (callgraphnode.code (car g))
- (map callgraphnode.name
- (filter (lambda (node)
- (let* ((name (callgraphnode.name node))
- (known? (symbol? name))
- (marked?
- (known-procedure-is-callable? name)))
- (callgraphnode.info! node #f)
- (and known? marked?)))
- g))))
- (let loop ()
- (if (not (null? schedule))
- (let ((job (car schedule)))
- (set! schedule (cdr schedule))
- (if (symbol? job)
- (analyze-known-local-procedure job)
- (analyze-unknown-lambda job))
- (loop))))
-
- (if changed?
- (error "Compiler bug in representation inference"))
-
- (if debugging?
- (pretty-print (make-readable (callgraphnode.code (car g)) #t)))
-
- exp))
- ; Copyright 1999 William D Clinger.
- ;
- ; Permission to copy this software, in whole or in part, to use this
- ; software for any lawful noncommercial purpose, and to redistribute
- ; this software is granted subject to the restriction that all copies
- ; made of this software must include this copyright notice in full.
- ;
- ; I also request that you send me a copy of any improvements that you
- ; make to this software so that they may be incorporated within it to
- ; the benefit of the Scheme community.
- ;
- ; 11 June 1999.
- ;
- ; The third "pass" of the Twobit compiler actually consists of several
- ; passes, which are related by the common theme of flow analysis:
- ; interprocedural inlining of known local procedures
- ; interprocedural constant propagation and folding
- ; intraprocedural commoning, copy propagation, and dead code elimination
- ; representation inference (not yet implemented)
- ; register targeting
- ;
- ; This pass operates as source-to-source transformations on
- ; expressions written in the subset of Scheme described by the
- ; following grammar:
- ;
- ; "X ..." means zero or more occurrences of X.
- ;
- ; L --> (lambda (I_1 ...)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>)
- ; E)
- ; | (lambda (I_1 ... . I_rest)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>))
- ; E)
- ; D --> (define I L)
- ; E --> (quote K) ; constants
- ; | (begin I) ; variable references
- ; | L ; lambda expressions
- ; | (E0 E1 ...) ; calls
- ; | (set! I E) ; assignments
- ; | (if E0 E1 E2) ; conditionals
- ; | (begin E0 E1 E2 ...) ; sequential expressions
- ; I --> <identifier>
- ;
- ; R --> ((I <references> <assignments> <calls>) ...)
- ; F --> (I ...)
- ; G --> (I ...)
- ;
- ; Invariants that hold for the input only:
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * R, F, and G are garbage.
- ; * Variables named IGNORED are neither referenced nor assigned.
- ; * The expression does not share structure with the original input,
- ; but might share structure with itself.
- ;
- ; Invariants that hold for the output only:
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * R is garbage.
- ; * For each lambda expression, the associated F is a list of all
- ; the identifiers that occur free in the body of that lambda
- ; expression, and possibly a few extra identifiers that were
- ; once free but have been removed by optimization.
- ; * If a lambda expression is declared to be in A-normal form (see
- ; pass3anormal.sch), then it really is in A-normal form.
- ;
- ; The phases of pass 3 interact with the referencing information R
- ; and the free variables F as follows:
- ;
- ; Inlining ignores R, ignores F, destroys R, destroys F.
- ; Constant propagation uses R, ignores F, preserves R, preserves F.
- ; Conversion to ANF ignores R, ignores F, destroys R, destroys F.
- ; Commoning ignores R, ignores F, destroys R, computes F.
- ; Register targeting ignores R, ignores F, destroys R, computes F.
- (define (pass3 exp)
-
- (define (phase1 exp)
- (if (interprocedural-inlining)
- (let ((g (callgraph exp)))
- (inline-using-callgraph! g)
- exp)
- exp))
-
- (define (phase2 exp)
- (if (interprocedural-constant-propagation)
- (constant-propagation (copy-exp exp))
- exp))
-
- (define (phase3 exp)
- (if (common-subexpression-elimination)
- (let* ((exp (if (interprocedural-constant-propagation)
- exp
- ; alpha-conversion
- (copy-exp exp)))
- (exp (a-normal-form exp)))
- (if (representation-inference)
- (intraprocedural-commoning exp 'commoning)
- (intraprocedural-commoning exp)))
- exp))
-
- (define (phase4 exp)
- (if (representation-inference)
- (let ((exp (cond ((common-subexpression-elimination)
- exp)
- ((interprocedural-constant-propagation)
- (a-normal-form exp))
- (else
- ; alpha-conversion
- (a-normal-form (copy-exp exp))))))
- (intraprocedural-commoning
- (representation-analysis exp)))
- exp))
-
- (define (finish exp)
- (if (and (not (interprocedural-constant-propagation))
- (not (common-subexpression-elimination)))
- (begin (compute-free-variables! exp)
- exp)
- ;(make-begin (list (make-constant 'anf) exp))))
- exp))
-
- (define (verify exp)
- (check-referencing-invariants exp 'free)
- exp)
-
- (if (global-optimization)
- (verify (finish (phase4 (phase3 (phase2 (phase1 exp))))))
- (begin (compute-free-variables! exp)
- (verify exp))))
- ; Copyright 1991 Lightship Software, Incorporated.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 4 June 1999
- ; Implements the following abstract data types.
- ;
- ; labels
- ; (init-labels)
- ; (make-label)
- ; cg-label-counter
- ;
- ; assembly streams
- ; (make-assembly-stream)
- ; (assembly-stream-code as)
- ; (gen! as . instruction)
- ; (gen-instruction! as instruction)
- ; (gen-save! as frame)
- ; (gen-restore! as frame)
- ; (gen-pop! as frame)
- ; (gen-setstk! as frame v)
- ; (gen-store! as frame r v)
- ; (gen-load! as frame r v)
- ; (gen-stack! as frame v)
- ;
- ; temporaries
- ; (init-temps)
- ; (newtemp)
- ; (newtemps)
- ; newtemp-counter
- ;
- ; register environments
- ; (cgreg-initial)
- ; (cgreg-copy regs)
- ; (cgreg-tos regs)
- ; (cgreg-liveregs regs)
- ; (cgreg-live regs r)
- ; (cgreg-vars regs)
- ; (cgreg-bind! regs r v)
- ; (cgreg-bindregs! regs vars)
- ; (cgreg-rename! regs alist)
- ; (cgreg-release! regs r)
- ; (cgreg-clear! regs)
- ; (cgreg-lookup regs var)
- ; (cgreg-lookup-reg regs r)
- ; (cgreg-join! regs1 regs2)
- ;
- ; stack frame environments
- ; (cgframe-initial)
- ; (cgframe-size-cell frame)
- ; (cgframe-size frame)
- ; (cgframe-copy frame)
- ; (cgframe-join! frame1 frame2)
- ; (cgframe-update-stale! frame)
- ; (cgframe-used! frame)
- ; (cgframe-bind! frame n v instruction)
- ; (cgframe-touch! frame v)
- ; (cgframe-rename! frame alist)
- ; (cgframe-release! frame v)
- ; (cgframe-lookup frame v)
- ; (cgframe-spilled? frame v)
- ;
- ; environments
- ; (entry.name entry)
- ; (entry.kind entry)
- ; (entry.rib entry)
- ; (entry.offset entry)
- ; (entry.label entry)
- ; (entry.regnum entry)
- ; (entry.arity entry)
- ; (entry.op entry)
- ; (entry.imm entry)
- ; (cgenv-initial)
- ; (cgenv-lookup env id)
- ; (cgenv-extend env vars procs)
- ; (cgenv-bindprocs env procs)
- ; (var-lookup var regs frame env)
- ; Labels.
- (define (init-labels)
- (set! cg-label-counter 1000))
- (define (make-label)
- (set! cg-label-counter (+ cg-label-counter 1))
- cg-label-counter)
- (define cg-label-counter 1000)
- ; an assembly stream into which instructions should be emitted
- ; an expression
- ; the desired target register ('result, a register number, or '#f)
- ; a register environment [cgreg]
- ; a stack-frame environment [cgframe]
- ; contains size of frame, current top of frame
- ; a compile-time environment [cgenv]
- ; a flag indicating whether the expression is in tail position
- ; Assembly streams, into which instructions are emitted by side effect.
- ; Represented as a list of two things:
- ;
- ; Assembly code, represented as a pair whose car is a nonempty list
- ; whose cdr is a possibly empty list of MacScheme machine assembly
- ; instructions, and whose cdr is the last pair of the car.
- ;
- ; Any Scheme object that the code generator wants to associate with
- ; this code.
- (define (make-assembly-stream)
- (let ((code (list (list 0))))
- (set-cdr! code (car code))
- (list code #f)))
- (define (assembly-stream-code output)
- (if (local-optimizations)
- (filter-basic-blocks (cdar (car output)))
- (cdar (car output))))
- (define (assembly-stream-info output)
- (cadr output))
- (define (assembly-stream-info! output x)
- (set-car! (cdr output) x)
- #f)
- (define (gen-instruction! output instruction)
- (let ((pair (list instruction))
- (code (car output)))
- (set-cdr! (cdr code) pair)
- (set-cdr! code pair)
- output))
- ;
- (define (gen! output . instruction)
- (gen-instruction! output instruction))
- (define (gen-save! output frame t0)
- (let ((size (cgframe-size-cell frame)))
- (gen-instruction! output (cons $save size))
- (gen-store! output frame 0 t0)
- (cgframe:stale-set! frame '())))
- (define (gen-restore! output frame)
- (let ((size (cgframe-size-cell frame)))
- (gen-instruction! output (cons $restore size))))
- (define (gen-pop! output frame)
- (let ((size (cgframe-size-cell frame)))
- (gen-instruction! output (cons $pop size))))
- (define (gen-setstk! output frame tempname)
- (let ((instruction (list $nop $setstk -1)))
- (cgframe-bind! frame tempname instruction)
- (gen-instruction! output instruction)))
- (define (gen-store! output frame r tempname)
- (let ((instruction (list $nop $store r -1)))
- (cgframe-bind! frame tempname instruction)
- (gen-instruction! output instruction)))
- (define (gen-load! output frame r tempname)
- (cgframe-touch! frame tempname)
- (let ((n (entry.slotnum (cgframe-lookup frame tempname))))
- (gen! output $load r n)))
- (define (gen-stack! output frame tempname)
- (cgframe-touch! frame tempname)
- (let ((n (entry.slotnum (cgframe-lookup frame tempname))))
- (gen! output $stack n)))
- ; Returns a temporary name.
- ; Temporaries are compared using EQ?, so the use of small
- ; exact integers as temporary names is implementation-dependent.
- (define (init-temps)
- (set! newtemp-counter 5000))
- (define (newtemp)
- (set! newtemp-counter
- (+ newtemp-counter 1))
- newtemp-counter)
- (define newtemp-counter 5000)
- (define (newtemps n)
- (if (zero? n)
- '()
- (cons (newtemp)
- (newtemps (- n 1)))))
- ; New representation of
- ; Register environments.
- ; Represented as a list of three items:
- ; an exact integer, one more than the highest index of a live register
- ; a mutable vector with *nregs* elements of the form
- ; #f (the register is dead)
- ; #t (the register is live)
- ; v (the register contains variable v)
- ; t (the register contains temporary variable t)
- ; a mutable vector of booleans: true if the register might be stale
- (define (cgreg-makeregs n v1 v2) (list n v1 v2))
- (define (cgreg-liveregs regs)
- (car regs))
- (define (cgreg-contents regs)
- (cadr regs))
- (define (cgreg-stale regs)
- (caddr regs))
- (define (cgreg-liveregs-set! regs n)
- (set-car! regs n)
- regs)
- (define (cgreg-initial)
- (let ((v1 (make-vector *nregs* #f))
- (v2 (make-vector *nregs* #f)))
- (cgreg-makeregs 0 v1 v2)))
- (define (cgreg-copy regs)
- (let* ((newregs (cgreg-initial))
- (v1a (cgreg-contents regs))
- (v2a (cgreg-stale regs))
- (v1 (cgreg-contents newregs))
- (v2 (cgreg-stale newregs))
- (n (vector-length v1a)))
- (cgreg-liveregs-set! newregs (cgreg-liveregs regs))
- (do ((i 0 (+ i 1)))
- ((= i n)
- newregs)
- (vector-set! v1 i (vector-ref v1a i))
- (vector-set! v2 i (vector-ref v2a i)))))
- (define (cgreg-tos regs)
- (- (cgreg-liveregs regs) 1))
- (define (cgreg-live regs r)
- (if (eq? r 'result)
- (cgreg-tos regs)
- (max r (cgreg-tos regs))))
- (define (cgreg-vars regs)
- (let ((m (cgreg-liveregs regs))
- (v (cgreg-contents regs)))
- (do ((i (- m 1) (- i 1))
- (vars '()
- (cons (vector-ref v i)
- vars)))
- ((< i 0)
- vars))))
- (define (cgreg-bind! regs r t)
- (let ((m (cgreg-liveregs regs))
- (v (cgreg-contents regs)))
- (vector-set! v r t)
- (if (>= r m)
- (cgreg-liveregs-set! regs (+ r 1)))))
- (define (cgreg-bindregs! regs vars)
- (do ((m (cgreg-liveregs regs) (+ m 1))
- (v (cgreg-contents regs))
- (vars vars (cdr vars)))
- ((null? vars)
- (cgreg-liveregs-set! regs m)
- regs)
- (vector-set! v m (car vars))))
- (define (cgreg-rename! regs alist)
- (do ((i (- (cgreg-liveregs regs) 1) (- i 1))
- (v (cgreg-contents regs)))
- ((negative? i))
- (let ((var (vector-ref v i)))
- (if var
- (let ((probe (assv var alist)))
- (if probe
- (vector-set! v i (cdr probe))))))))
- (define (cgreg-release! regs r)
- (let ((m (cgreg-liveregs regs))
- (v (cgreg-contents regs)))
- (vector-set! v r #f)
- (vector-set! (cgreg-stale regs) r #t)
- (if (= r (- m 1))
- (do ((m r (- m 1)))
- ((or (negative? m)
- (vector-ref v m))
- (cgreg-liveregs-set! regs (+ m 1)))))))
- (define (cgreg-release-except! regs vars)
- (do ((i (- (cgreg-liveregs regs) 1) (- i 1))
- (v (cgreg-contents regs)))
- ((negative? i))
- (let ((var (vector-ref v i)))
- (if (and var (not (memq var vars)))
- (cgreg-release! regs i)))))
- (define (cgreg-clear! regs)
- (let ((m (cgreg-liveregs regs))
- (v1 (cgreg-contents regs))
- (v2 (cgreg-stale regs)))
- (do ((r 0 (+ r 1)))
- ((= r m)
- (cgreg-liveregs-set! regs 0))
- (vector-set! v1 r #f)
- (vector-set! v2 r #t))))
- (define (cgreg-lookup regs var)
- (let ((m (cgreg-liveregs regs))
- (v (cgreg-contents regs)))
- (define (loop i)
- (cond ((< i 0)
- #f)
- ((eq? var (vector-ref v i))
- (list var 'register i '(object)))
- (else
- (loop (- i 1)))))
- (loop (- m 1))))
- (define (cgreg-lookup-reg regs r)
- (let ((m (cgreg-liveregs regs))
- (v (cgreg-contents regs)))
- (if (<= m r)
- #f
- (vector-ref v r))))
- (define (cgreg-join! regs1 regs2)
- (let ((m1 (cgreg-liveregs regs1))
- (m2 (cgreg-liveregs regs2))
- (v1 (cgreg-contents regs1))
- (v2 (cgreg-contents regs2))
- (stale1 (cgreg-stale regs1)))
- (do ((i (- (max m1 m2) 1) (- i 1)))
- ((< i 0)
- (cgreg-liveregs-set! regs1 (min m1 m2)))
- (let ((x1 (vector-ref v1 i))
- (x2 (vector-ref v2 i)))
- (cond ((eq? x1 x2)
- #t)
- ((not x1)
- (if x2
- (vector-set! stale1 i #t)))
- (else
- (vector-set! v1 i #f)
- (vector-set! stale1 i #t)))))))
- ; New representation of
- ; Stack-frame environments.
- ; Represented as a three-element list.
- ;
- ; Its car is a list whose car is a list of slot entries, each
- ; of the form
- ; (v n instruction stale)
- ; where
- ; v is the name of a variable or temporary,
- ; n is #f or a slot number,
- ; instruction is a possibly phantom store or setstk instruction
- ; that stores v into slot n, and
- ; stale is a list of stale slot entries, each of the form
- ; (#t . n)
- ; or (#f . -1)
- ; where slot n had been allocated, initialized, and released
- ; before the store or setstk instruction was generated.
- ; Slot entries are updated by side effect.
- ;
- ; Its cadr is the list of currently stale slots.
- ;
- ; Its caddr is a list of variables that are free in the continuation,
- ; or #f if that information is unknown.
- ; This information allows a direct-style code generator to know when
- ; a slot becomes stale.
- ;
- ; Its cadddr is the size of the stack frame, which can be
- ; increased but not decreased. The cdddr of the stack frame
- ; environment is shared with the save instruction that
- ; created the frame. What a horrible crock!
- ; This stuff is private to the implementation of stack-frame
- ; environments.
- (define cgframe:slots car)
- (define cgframe:stale cadr)
- (define cgframe:livevars caddr)
- (define cgframe:slot.name car)
- (define cgframe:slot.offset cadr)
- (define cgframe:slot.instruction caddr)
- (define cgframe:slot.stale cadddr)
- (define cgframe:slots-set! set-car!)
- (define (cgframe:stale-set! frame stale)
- (set-car! (cdr frame) stale))
- (define (cgframe:livevars-set! frame vars)
- (set-car! (cddr frame) vars))
- (define cgframe:slot.name-set! set-car!)
- (define (cgframe:slot.offset-set! entry n)
- (let ((instruction (caddr entry)))
- (if (or (not (eq? #f (cadr entry)))
- (not (eq? $nop (car instruction))))
- (error "Compiler bug: cgframe" entry)
- (begin
- (set-car! (cdr entry) n)
- (set-car! instruction (cadr instruction))
- (set-cdr! instruction (cddr instruction))
- (if (eq? $setstk (car instruction))
- (set-car! (cdr instruction) n)
- (set-car! (cddr instruction) n))))))
- ; Reserves a slot offset that was unused where the instruction
- ; of the slot entry was generated, and returns that offset.
- (define (cgframe:unused-slot frame entry)
- (let* ((stale (cgframe:slot.stale entry))
- (probe (assq #t stale)))
- (if probe
- (let ((n (cdr probe)))
- (if (zero? n)
- (cgframe-used! frame))
- (set-car! probe #f)
- n)
- (let* ((cell (cgframe-size-cell frame))
- (n (+ 1 (car cell))))
- (set-car! cell n)
- (if (zero? n)
- (cgframe:unused-slot frame entry)
- n)))))
- ; Public entry points.
- ; The runtime system requires slot 0 of a frame to contain
- ; a closure whose code pointer contains the return address
- ; of the frame.
- ; To prevent slot 0 from being used for some other purpose,
- ; we rely on a complex trick: Slot 0 is initially stale.
- ; Gen-save! generates a store instruction for register 0,
- ; with slot 0 as the only stale slot for that instruction;
- ; then gen-save! clears the frame's set of stale slots, which
- ; prevents other store instructions from using slot 0.
- (define (cgframe-initial)
- (list '()
- (list (cons #t 0))
- '#f
- -1))
- (define cgframe-livevars cgframe:livevars)
- (define cgframe-livevars-set! cgframe:livevars-set!)
- (define (cgframe-size-cell frame)
- (cdddr frame))
- (define (cgframe-size frame)
- (car (cgframe-size-cell frame)))
- (define (cgframe-used! frame)
- (if (negative? (cgframe-size frame))
- (set-car! (cgframe-size-cell frame) 0)))
- ; Called only by gen-store!, gen-setstk!
- (define (cgframe-bind! frame var instruction)
- (cgframe:slots-set! frame
- (cons (list var #f instruction (cgframe:stale frame))
- (cgframe:slots frame))))
- ; Called only by gen-load!, gen-stack!
- (define (cgframe-touch! frame var)
- (let ((entry (assq var (cgframe:slots frame))))
- (if entry
- (let ((n (cgframe:slot.offset entry)))
- (if (eq? #f n)
- (let ((n (cgframe:unused-slot frame entry)))
- (cgframe:slot.offset-set! entry n))))
- (error "Compiler bug: cgframe-touch!" frame var))))
- (define (cgframe-rename! frame alist)
- (for-each (lambda (entry)
- (let ((probe (assq (cgframe:slot.name entry) alist)))
- (if probe
- (cgframe:slot.name-set! entry (cdr probe)))))
- (cgframe:slots frame)))
- (define (cgframe-release! frame var)
- (let* ((slots (cgframe:slots frame))
- (entry (assq var slots)))
- (if entry
- (begin (cgframe:slots-set! frame (remq entry slots))
- (let ((n (cgframe:slot.offset entry)))
- (if (and (not (eq? #f n))
- (not (zero? n)))
- (cgframe:stale-set!
- frame
- (cons (cons #t n)
- (cgframe:stale frame)))))))))
- (define (cgframe-release-except! frame vars)
- (let loop ((slots (reverse (cgframe:slots frame)))
- (newslots '())
- (stale (cgframe:stale frame)))
- (if (null? slots)
- (begin (cgframe:slots-set! frame newslots)
- (cgframe:stale-set! frame stale))
- (let ((slot (car slots)))
- (if (memq (cgframe:slot.name slot) vars)
- (loop (cdr slots)
- (cons slot newslots)
- stale)
- (let ((n (cgframe:slot.offset slot)))
- (cond ((eq? n #f)
- (loop (cdr slots)
- newslots
- stale))
- ((zero? n)
- (loop (cdr slots)
- (cons slot newslots)
- stale))
- (else
- (loop (cdr slots)
- newslots
- (cons (cons #t n) stale))))))))))
- (define (cgframe-lookup frame var)
- (let ((entry (assq var (cgframe:slots frame))))
- (if entry
- (let ((n (cgframe:slot.offset entry)))
- (if (eq? #f n)
- (cgframe-touch! frame var))
- (list var 'frame (cgframe:slot.offset entry) '(object)))
- #f)))
- (define (cgframe-spilled? frame var)
- (let ((entry (assq var (cgframe:slots frame))))
- (if entry
- (let ((n (cgframe:slot.offset entry)))
- (not (eq? #f n)))
- #f)))
- ; For a conditional expression, the then and else parts must be
- ; evaluated using separate copies of the frame environment,
- ; and those copies must be resolved at the join point. The
- ; nature of the resolution depends upon whether the conditional
- ; expression is in a tail position.
- ;
- ; Critical invariant:
- ; Any store instructions that are generated within either arm of the
- ; conditional involve variables and temporaries that are local to the
- ; conditional.
- ;
- ; If the conditional expression is in a tail position, then a slot
- ; that is stale after the test can be allocated independently by the
- ; two arms of the conditional. If the conditional expression is in a
- ; non-tail position, then the slot can be allocated independently
- ; provided it is not a candidate destination for any previous emitted
- ; store instruction.
- (define (cgframe-copy frame)
- (cons (car frame)
- (cons (cadr frame)
- (cons (caddr frame)
- (cdddr frame)))))
- (define (cgframe-update-stale! frame)
- (let* ((n (cgframe-size frame))
- (v (make-vector (+ 1 n) #t))
- (stale (cgframe:stale frame)))
- (for-each (lambda (x)
- (if (car x)
- (let ((i (cdr x)))
- (if (<= i n)
- (vector-set! v i #f)))))
- stale)
- (for-each (lambda (slot)
- (let ((offset (cgframe:slot.offset slot)))
- (if offset
- (vector-set! v offset #f)
- (for-each (lambda (stale)
- (if (car stale)
- (let ((i (cdr stale)))
- (if (< i n)
- (vector-set! v i #f)))))
- (cgframe:slot.stale slot)))))
- (cgframe:slots frame))
- (do ((i n (- i 1))
- (stale (filter car stale)
- (if (vector-ref v i)
- (cons (cons #t i) stale)
- stale)))
- ((<= i 0)
- (cgframe:stale-set! frame stale)))))
- (define (cgframe-join! frame1 frame2)
- (let* ((slots1 (cgframe:slots frame1))
- (slots2 (cgframe:slots frame2))
- (slots (intersection slots1 slots2))
- (deadslots (append (difference slots1 slots)
- (difference slots2 slots)))
- (deadoffsets (make-set
- (filter (lambda (x) (not (eq? x #f)))
- (map cgframe:slot.offset deadslots))))
- (stale1 (cgframe:stale frame1))
- (stale2 (cgframe:stale frame2))
- (stale (intersection stale1 stale2))
- (stale (append (map (lambda (n) (cons #t n))
- deadoffsets)
- stale)))
- (cgframe:slots-set! frame1 slots)
- (cgframe:stale-set! frame1 stale)))
- ; Environments.
- ;
- ; Each identifier has one of the following kinds of entry.
- ;
- ; (<name> register <number> (object))
- ; (<name> frame <slot> (object))
- ; (<name> lexical <rib> <offset> (object))
- ; (<name> procedure <rib> <label> (object))
- ; (<name> integrable <arity> <op> <imm> (object))
- ; (<name> global (object))
- ;
- ; Implementation.
- ;
- ; An environment is represented as a list of the form
- ;
- ; ((<entry> ...) ; lexical rib
- ; ...)
- ;
- ; where each <entry> has one of the forms
- ;
- ; (<name> lexical <offset> (object))
- ; (<name> procedure <rib> <label> (object))
- ; (<name> integrable <arity> <op> <imm> (object))
- (define entry.name car)
- (define entry.kind cadr)
- (define entry.rib caddr)
- (define entry.offset cadddr)
- (define entry.label cadddr)
- (define entry.regnum caddr)
- (define entry.slotnum caddr)
- (define entry.arity caddr)
- (define entry.op cadddr)
- (define (entry.imm entry) (car (cddddr entry)))
- (define (cgenv-initial integrable)
- (list (map (lambda (x)
- (list (car x)
- 'integrable
- (cadr x)
- (caddr x)
- (cadddr x)
- '(object)))
- integrable)))
- (define (cgenv-lookup env id)
- (define (loop ribs m)
- (if (null? ribs)
- (cons id '(global (object)))
- (let ((x (assq id (car ribs))))
- (if x
- (case (cadr x)
- ((lexical)
- (cons id
- (cons (cadr x)
- (cons m (cddr x)))))
- ((procedure)
- (cons id
- (cons (cadr x)
- (cons m (cddr x)))))
- ((integrable)
- (if (integrate-usual-procedures)
- x
- (loop '() m)))
- (else ???))
- (loop (cdr ribs) (+ m 1))))))
- (loop env 0))
- (define (cgenv-extend env vars procs)
- (cons (do ((n 0 (+ n 1))
- (vars vars (cdr vars))
- (rib (map (lambda (id)
- (list id 'procedure (make-label) '(object)))
- procs)
- (cons (list (car vars) 'lexical n '(object)) rib)))
- ((null? vars) rib))
- env))
- (define (cgenv-bindprocs env procs)
- (cons (append (map (lambda (id)
- (list id 'procedure (make-label) '(object)))
- procs)
- (car env))
- (cdr env)))
- (define (var-lookup var regs frame env)
- (or (cgreg-lookup regs var)
- (cgframe-lookup frame var)
- (cgenv-lookup env var)))
- ; Compositions.
- (define compile
- (lambda (x)
- (pass4 (pass3 (pass2 (pass1 x))) $usual-integrable-procedures$)))
- (define compile-block
- (lambda (x)
- (pass4 (pass3 (pass2 (pass1-block x))) $usual-integrable-procedures$)))
- ; For testing.
- (define foo
- (lambda (x)
- (pretty-print (compile x))))
- ; Find the smallest number of registers such that
- ; adding more registers does not affect the code
- ; generated for x (from 4 to 32 registers).
- (define (minregs x)
- (define (defregs R)
- (set! *nregs* R)
- (set! *lastreg* (- *nregs* 1))
- (set! *fullregs* (quotient *nregs* 2)))
- (defregs 32)
- (let ((code (assemble (compile x))))
- (define (binary-search m1 m2)
- (if (= (+ m1 1) m2)
- m2
- (let ((midpt (quotient (+ m1 m2) 2)))
- (defregs midpt)
- (if (equal? code (assemble (compile x)))
- (binary-search m1 midpt)
- (binary-search midpt m2)))))
- (defregs 4)
- (let ((newcode (assemble (compile x))))
- (if (equal? code newcode)
- 4
- (binary-search 4 32)))))
- ; Minimums:
- ; browse 10
- ; triangle 5
- ; traverse 10
- ; destruct 6
- ; puzzle 8,8,10,7
- ; tak 6
- ; fft 28 (changing the named lets to macros didn't matter)
- ; Copyright 1991 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 7 June 1999.
- ;
- ; Fourth pass of the Twobit compiler:
- ; code generation for the MacScheme machine.
- ;
- ; This pass operates on input expressions described by the
- ; following grammar and the invariants that follow it.
- ;
- ; "X ..." means zero or more occurrences of X.
- ;
- ; L --> (lambda (I_1 ...)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>)
- ; E)
- ; | (lambda (I_1 ... . I_rest)
- ; (begin D ...)
- ; (quote (R F G <decls> <doc>))
- ; E)
- ; D --> (define I L)
- ; E --> (quote K) ; constants
- ; | (begin I) ; variable references
- ; | L ; lambda expressions
- ; | (E0 E1 ...) ; calls
- ; | (set! I E) ; assignments
- ; | (if E0 E1 E2) ; conditionals
- ; | (begin E0 E1 E2 ...) ; sequential expressions
- ; I --> <identifier>
- ;
- ; R --> ((I <references> <assignments> <calls>) ...)
- ; F --> (I ...)
- ; G --> (I ...)
- ;
- ; Invariants that hold for the input
- ; * There are no assignments except to global variables.
- ; * If I is declared by an internal definition, then the right hand
- ; side of the internal definition is a lambda expression and I
- ; is referenced only in the procedure position of a call.
- ; * Every procedure defined by an internal definition takes a
- ; fixed number of arguments.
- ; * Every call to a procedure defined by an internal definition
- ; passes the correct number of arguments.
- ; * For each lambda expression, the associated F is a list of all
- ; the identifiers that occur free in the body of that lambda
- ; expression, and possibly a few extra identifiers that were
- ; once free but have been removed by optimization.
- ; * For each lambda expression, the associated G is a subset of F
- ; that contains every identifier that occurs free within some
- ; inner lambda expression that escapes, and possibly a few that
- ; don't. (Assignment-elimination does not calculate G exactly.)
- ; * Variables named IGNORED are neither referenced nor assigned.
- ; * Any lambda expression that is declared to be in A-normal form
- ; really is in A-normal form.
- ;
- ;
- ; Stack frames are created by "save" instructions.
- ; A save instruction is generated
- ;
- ; * at the beginning of each lambda body
- ; * at the beginning of the code for each arm of a conditional,
- ; provided:
- ; the conditional is in a tail position
- ; the frames that were allocated by the save instructions
- ; that dominate the arms of the conditional have not been
- ; used (those save instructions will be eliminated during
- ; assembly)
- ;
- ; The operand of a save instruction, and of its matching pop instructions,
- ; increases automatically as frame slots are allocated.
- ;
- ; The code generated to return from a procedure is
- ;
- ; pop n
- ; return
- ;
- ; The code generated for a tail call is
- ;
- ; pop n
- ; invoke ...
- ;
- ; Invariant: When the code generator reserves an argument register
- ; to hold a value, that value is named, and is stored into the current
- ; stack frame. These store instructions are eliminated during assembly
- ; unless there is a matching load instruction. If all of the instructions
- ; that store into a stack frame are eliminated, then the stack frame
- ; itself is eliminated.
- ; Exception: An argument register may be used without naming or storing
- ; its value provided the register is not in use and no expressions are
- ; evaluated while it contains the unnamed and unstored value.
- (define (pass4 exp integrable)
- (init-labels)
- (init-temps)
- (let ((output (make-assembly-stream))
- (frame (cgframe-initial))
- (regs (cgreg-initial))
- (t0 (newtemp)))
- (assembly-stream-info! output (make-hashtable equal-hash assoc))
- (cgreg-bind! regs 0 t0)
- (gen-save! output frame t0)
- (cg0 output
- exp
- 'result
- regs
- frame
- (cgenv-initial integrable)
- #t)
- (pass4-code output)))
- (define (pass4-code output)
- (hashtable-for-each (lambda (situation label)
- (cg-trap output situation label))
- (assembly-stream-info output))
- (assembly-stream-code output))
- ; Given:
- ; an assembly stream into which instructions should be emitted
- ; an expression
- ; the target register
- ; ('result, a register number, or '#f; tail position implies 'result)
- ; a register environment [cgreg]
- ; a stack-frame environment [cgframe]
- ; a compile-time environment [cgenv]
- ; a flag indicating whether the expression is in tail position
- ; Returns:
- ; the target register ('result or a register number)
- ; Side effects:
- ; may change the register and stack-frame environments
- ; may increase the size of the stack frame, which changes previously
- ; emitted instructions
- ; writes instructions to the assembly stream
- (define (cg0 output exp target regs frame env tail?)
- (case (car exp)
- ((quote) (gen! output $const (constant.value exp))
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- ((lambda) (cg-lambda output exp regs frame env)
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- ((set!) (cg0 output (assignment.rhs exp) 'result regs frame env #f)
- (cg-assignment-result output exp target regs frame env tail?))
- ((if) (cg-if output exp target regs frame env tail?))
- ((begin) (if (variable? exp)
- (cg-variable output exp target regs frame env tail?)
- (cg-sequential output exp target regs frame env tail?)))
- (else (cg-call output exp target regs frame env tail?))))
- ; Lambda expressions that evaluate to closures.
- ; This is hard because the MacScheme machine's lambda instruction
- ; closes over the values that are in argument registers 0 through r
- ; (where r can be larger than *nregs*).
- ; The set of free variables is calculated and then sorted to minimize
- ; register shuffling.
- ;
- ; Returns: nothing.
- (define (cg-lambda output exp regs frame env)
- (let* ((args (lambda.args exp))
- (vars (make-null-terminated args))
- (free (difference (lambda.F exp) vars))
- (free (cg-sort-vars free regs frame env))
- (newenv (cgenv-extend env (cons #t free) '()))
- (newoutput (make-assembly-stream)))
- (assembly-stream-info! newoutput (make-hashtable equal-hash assoc))
- (gen! newoutput $.proc)
- (if (list? args)
- (gen! newoutput $args= (length args))
- (gen! newoutput $args>= (- (length vars) 1)))
- (cg-known-lambda newoutput exp newenv)
- (cg-eval-vars output free regs frame env)
- ; FIXME
- '
- (if (not (ignore-space-leaks))
- ; FIXME: Is this the right constant?
- (begin (gen! output $const #f)
- (gen! output $setreg 0)))
- (gen! output
- $lambda
- (pass4-code newoutput)
- (length free)
- (lambda.doc exp))
- ; FIXME
- '
- (if (not (ignore-space-leaks))
- ; FIXME: This load forces a stack frame to be allocated.
- (gen-load! output frame 0 (cgreg-lookup-reg regs 0)))))
- ; Given a list of free variables, filters out the ones that
- ; need to be copied into a closure, and sorts them into an order
- ; that reduces register shuffling. Returns a sorted version of
- ; the list in which the first element (element 0) should go
- ; into register 1, the second into register 2, and so on.
- (define (cg-sort-vars free regs frame env)
- (let* ((free (filter (lambda (var)
- (case (entry.kind
- (var-lookup var regs frame env))
- ((register frame)
- #t)
- ((lexical)
- (not (ignore-space-leaks)))
- (else #f)))
- free))
- (n (length free))
- (m (min n (- *nregs* 1)))
- (vec (make-vector m #f)))
- (define (loop1 free free-notregister)
- (if (null? free)
- (loop2 0 free-notregister)
- (let* ((var (car free))
- (entry (cgreg-lookup regs var)))
- (if entry
- (let ((r (entry.regnum entry)))
- (if (<= r n)
- (begin (vector-set! vec (- r 1) var)
- (loop1 (cdr free)
- free-notregister))
- (loop1 (cdr free)
- (cons var free-notregister))))
- (loop1 (cdr free)
- (cons var free-notregister))))))
- (define (loop2 i free)
- (cond ((null? free)
- (vector->list vec))
- ((= i m)
- (append (vector->list vec) free))
- ((vector-ref vec i)
- (loop2 (+ i 1) free))
- (else
- (vector-set! vec i (car free))
- (loop2 (+ i 1) (cdr free)))))
- (loop1 free '())))
- ; Fetches the given list of free variables into the corresponding
- ; registers in preparation for a $lambda or $lexes instruction.
- (define (cg-eval-vars output free regs frame env)
- (let ((n (length free))
- (R-1 (- *nregs* 1)))
- (if (>= n R-1)
- (begin (gen! output $const '())
- (gen! output $setreg R-1)
- (cgreg-release! regs R-1)))
- (do ((r n (- r 1))
- (vars (reverse free) (cdr vars)))
- ((zero? r))
- (let* ((v (car vars))
- (entry (var-lookup v regs frame env)))
- (case (entry.kind entry)
- ((register)
- (let ((r1 (entry.regnum entry)))
- (if (not (eqv? r r1))
- (if (< r R-1)
- (begin (gen! output $movereg r1 r)
- (cgreg-bind! regs r v))
- (gen! output $reg r1 v)))))
- ((frame)
- (if (< r R-1)
- (begin (gen-load! output frame r v)
- (cgreg-bind! regs r v))
- (gen-stack! output frame v)))
- ((lexical)
- (gen! output $lexical
- (entry.rib entry)
- (entry.offset entry)
- v)
- (if (< r R-1)
- (begin (gen! output $setreg r)
- (cgreg-bind! regs r v)
- (gen-store! output frame r v))))
- (else
- (error "Bug in cg-close-lambda")))
- (if (>= r R-1)
- (begin (gen! output $op2 $cons R-1)
- (gen! output $setreg R-1)))))))
- ; Lambda expressions that appear on the rhs of a definition are
- ; compiled here. They don't need an args= instruction at their head.
- ;
- ; Returns: nothing.
- (define (cg-known-lambda output exp env)
- (let* ((vars (make-null-terminated (lambda.args exp)))
- (regs (cgreg-initial))
- (frame (cgframe-initial))
- (t0 (newtemp)))
- (if (member A-normal-form-declaration (lambda.decls exp))
- (cgframe-livevars-set! frame '()))
- (cgreg-bind! regs 0 t0)
- (gen-save! output frame t0)
- (do ((r 1 (+ r 1))
- (vars vars (cdr vars)))
- ((or (null? vars)
- (= r *lastreg*))
- (if (not (null? vars))
- (begin (gen! output $movereg *lastreg* 1)
- (cgreg-release! regs 1)
- (do ((vars vars (cdr vars)))
- ((null? vars))
- (gen! output $reg 1)
- (gen! output $op1 $car:pair)
- (gen-setstk! output frame (car vars))
- (gen! output $reg 1)
- (gen! output $op1 $cdr:pair)
- (gen! output $setreg 1)))))
- (cgreg-bind! regs r (car vars))
- (gen-store! output frame r (car vars)))
- (cg-body output
- exp
- 'result
- regs
- frame
- env
- #t)))
- ; Compiles a let or lambda body.
- ; The arguments of the lambda expression L are already in
- ; registers or the stack frame, as specified by regs and frame.
- ;
- ; The problem here is that the free variables of an internal
- ; definition must be in a heap-allocated environment, so any
- ; such variables in registers must be copied to the heap.
- ;
- ; Returns: destination register.
- (define (cg-body output L target regs frame env tail?)
- (let* ((exp (lambda.body L))
- (defs (lambda.defs L))
- (free (apply-union
- (map (lambda (def)
- (let ((L (def.rhs def)))
- (difference (lambda.F L)
- (lambda.args L))))
- defs))))
- (cond ((or (null? defs) (constant? exp) (variable? exp))
- (cg0 output exp target regs frame env tail?))
- ((lambda? exp)
- (let* ((free (cg-sort-vars
- (union free
- (difference
- (lambda.F exp)
- (make-null-terminated (lambda.args exp))))
- regs frame env))
- (newenv1 (cgenv-extend env
- (cons #t free)
- (map def.lhs defs)))
- (args (lambda.args exp))
- (vars (make-null-terminated args))
- (newoutput (make-assembly-stream)))
- (assembly-stream-info! newoutput (make-hashtable equal-hash assoc))
- (gen! newoutput $.proc)
- (if (list? args)
- (gen! newoutput $args= (length args))
- (gen! newoutput $args>= (- (length vars) 1)))
- (cg-known-lambda newoutput exp newenv1)
- (cg-defs newoutput defs newenv1)
- (cg-eval-vars output free regs frame env)
- (gen! output
- $lambda
- (pass4-code newoutput)
- (length free)
- (lambda.doc exp))
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target))))
- ((every? (lambda (def)
- (every? (lambda (v)
- (case (entry.kind
- (var-lookup v regs frame env))
- ((register frame) #f)
- (else #t)))
- (let ((Ldef (def.rhs def)))
- (difference (lambda.F Ldef)
- (lambda.args Ldef)))))
- defs)
- (let* ((newenv (cgenv-bindprocs env (map def.lhs defs)))
- (L (make-label))
- (r (cg0 output exp target regs frame newenv tail?)))
- (if (not tail?)
- (gen! output $skip L (cgreg-live regs r)))
- (cg-defs output defs newenv)
- (if (not tail?)
- (gen! output $.label L))
- r))
- (else
- (let ((free (cg-sort-vars free regs frame env)))
- (cg-eval-vars output free regs frame env)
- ; FIXME: Have to restore it too!
- '
- (if (not (ignore-space-leaks))
- ; FIXME: Is this constant the right one?
- (begin (gen! output $const #f)
- (gen! output $setreg 0)))
- (let ((t0 (cgreg-lookup-reg regs 0))
- (t1 (newtemp))
- (newenv (cgenv-extend env
- (cons #t free)
- (map def.lhs defs)))
- (L (make-label)))
- (gen! output $lexes (length free) free)
- (gen! output $setreg 0)
- (cgreg-bind! regs 0 t1)
- (if tail?
- (begin (cgframe-release! frame t0)
- (gen-store! output frame 0 t1)
- (cg0 output exp 'result regs frame newenv #t)
- (cg-defs output defs newenv)
- 'result)
- (begin (gen-store! output frame 0 t1)
- (cg0 output exp 'result regs frame newenv #f)
- (gen! output $skip L (cgreg-tos regs))
- (cg-defs output defs newenv)
- (gen! output $.label L)
- (gen-load! output frame 0 t0)
- (cgreg-bind! regs 0 t0)
- (cgframe-release! frame t1)
- (cg-move output frame regs 'result target)))))))))
- (define (cg-defs output defs env)
- (for-each (lambda (def)
- (gen! output $.align 4)
- (gen! output $.label
- (entry.label
- (cgenv-lookup env (def.lhs def))))
- (gen! output $.proc)
- (gen! output $.proc-doc (lambda.doc (def.rhs def)))
- (cg-known-lambda output
- (def.rhs def)
- env))
- defs))
- ; The right hand side has already been evaluated into the result register.
- (define (cg-assignment-result output exp target regs frame env tail?)
- (gen! output $setglbl (assignment.lhs exp))
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- (define (cg-if output exp target regs frame env tail?)
- ; The test can be a constant, because it is awkward
- ; to remove constant tests from an A-normal form.
- (if (constant? (if.test exp))
- (cg0 output
- (if (constant.value (if.test exp))
- (if.then exp)
- (if.else exp))
- target regs frame env tail?)
- (begin
- (cg0 output (if.test exp) 'result regs frame env #f)
- (cg-if-result output exp target regs frame env tail?))))
- ; The test expression has already been evaluated into the result register.
- (define (cg-if-result output exp target regs frame env tail?)
- (let ((L1 (make-label))
- (L2 (make-label)))
- (gen! output $branchf L1 (cgreg-tos regs))
- (let* ((regs2 (cgreg-copy regs))
- (frame1 (if (and tail?
- (negative? (cgframe-size frame)))
- (cgframe-initial)
- frame))
- (frame2 (if (eq? frame frame1)
- (cgframe-copy frame1)
- (cgframe-initial)))
- (t0 (cgreg-lookup-reg regs 0)))
- (if (not (eq? frame frame1))
- (let ((live (cgframe-livevars frame)))
- (cgframe-livevars-set! frame1 live)
- (cgframe-livevars-set! frame2 live)
- (gen-save! output frame1 t0)
- (cg-saveregs output regs frame1)))
- (let ((r (cg0 output (if.then exp) target regs frame1 env tail?)))
- (if (not tail?)
- (gen! output $skip L2 (cgreg-live regs r)))
- (gen! output $.label L1)
- (if (not (eq? frame frame1))
- (begin (gen-save! output frame2 t0)
- (cg-saveregs output regs2 frame2))
- (cgframe-update-stale! frame2))
- (cg0 output (if.else exp) r regs2 frame2 env tail?)
- (if (not tail?)
- (begin (gen! output $.label L2)
- (cgreg-join! regs regs2)
- (cgframe-join! frame1 frame2)))
- (if (and (not target)
- (not (eq? r 'result))
- (not (cgreg-lookup-reg regs r)))
- (cg-move output frame regs r 'result)
- r)))))
- (define (cg-variable output exp target regs frame env tail?)
- (define (return id)
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (if (and target
- (not (eq? 'result target)))
- (begin (gen! output $setreg target)
- (cgreg-bind! regs target id)
- (gen-store! output frame target id)
- target)
- 'result)))
- ; Same as return, but doesn't emit a store instruction.
- (define (return-nostore id)
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (if (and target
- (not (eq? 'result target)))
- (begin (gen! output $setreg target)
- (cgreg-bind! regs target id)
- target)
- 'result)))
- (let* ((id (variable.name exp))
- (entry (var-lookup id regs frame env)))
- (case (entry.kind entry)
- ((global integrable)
- (gen! output $global id)
- (return (newtemp)))
- ((lexical)
- (let ((m (entry.rib entry))
- (n (entry.offset entry)))
- (gen! output $lexical m n id)
- (if (or (zero? m)
- (negative? (cgframe-size frame)))
- (return-nostore id)
- (return id))))
- ((procedure) (error "Bug in cg-variable" exp))
- ((register)
- (let ((r (entry.regnum entry)))
- (if (or tail?
- (and target (not (eqv? target r))))
- (begin (gen! output $reg (entry.regnum entry) id)
- (return-nostore id))
- r)))
- ((frame)
- (cond ((eq? target 'result)
- (gen-stack! output frame id)
- (return id))
- (target
- ; Must be non-tail.
- (gen-load! output frame target id)
- (cgreg-bind! regs target id)
- target)
- (else
- ; Must be non-tail.
- (let ((r (choose-register regs frame)))
- (gen-load! output frame r id)
- (cgreg-bind! regs r id)
- r))))
- (else (error "Bug in cg-variable" exp)))))
- (define (cg-sequential output exp target regs frame env tail?)
- (cg-sequential-loop output (begin.exprs exp) target regs frame env tail?))
- (define (cg-sequential-loop output exprs target regs frame env tail?)
- (cond ((null? exprs)
- (gen! output $const unspecified)
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- ((null? (cdr exprs))
- (cg0 output (car exprs) target regs frame env tail?))
- (else (cg0 output (car exprs) #f regs frame env #f)
- (cg-sequential-loop output
- (cdr exprs)
- target regs frame env tail?))))
- (define (cg-saveregs output regs frame)
- (do ((i 1 (+ i 1))
- (vars (cdr (cgreg-vars regs)) (cdr vars)))
- ((null? vars))
- (let ((t (car vars)))
- (if t
- (gen-store! output frame i t)))))
- (define (cg-move output frame regs src dst)
- (define (bind dst)
- (let ((temp (newtemp)))
- (cgreg-bind! regs dst temp)
- (gen-store! output frame dst temp)
- dst))
- (cond ((not dst)
- src)
- ((eqv? src dst)
- dst)
- ((eq? dst 'result)
- (gen! output $reg src)
- dst)
- ((eq? src 'result)
- (gen! output $setreg dst)
- (bind dst))
- ((and (not (zero? src))
- (not (zero? dst)))
- (gen! output $movereg src dst)
- (bind dst))
- (else
- (gen! output $reg src)
- (gen! output $setreg dst)
- (bind dst))))
- ; On-the-fly register allocator.
- ; Tries to allocate:
- ; a hardware register that isn't being used
- ; a hardware register whose contents have already been spilled
- ; a software register that isn't being used, unless a stack
- ; frame has already been created, in which case it is better to use
- ; a hardware register that is in use and hasn't yet been spilled
- ;
- ; All else equal, it is better to allocate a higher-numbered register
- ; because the lower-numbered registers are targets when arguments
- ; are being evaluated.
- ;
- ; Invariant: Every register that is returned by this allocator
- ; is either not in use or has been spilled.
- (define (choose-register regs frame)
- (car (choose-registers regs frame 1)))
- (define (choose-registers regs frame n)
-
- ; Find unused hardware registers.
- (define (loop1 i n good)
- (cond ((zero? n)
- good)
- ((zero? i)
- (if (negative? (cgframe-size frame))
- (hardcase)
- (loop2 (- *nhwregs* 1) n good)))
- (else
- (if (cgreg-lookup-reg regs i)
- (loop1 (- i 1) n good)
- (loop1 (- i 1)
- (- n 1)
- (cons i good))))))
-
- ; Find already spilled hardware registers.
- (define (loop2 i n good)
- (cond ((zero? n)
- good)
- ((zero? i)
- (hardcase))
- (else
- (let ((t (cgreg-lookup-reg regs i)))
- (if (and t (cgframe-spilled? frame t))
- (loop2 (- i 1)
- (- n 1)
- (cons i good))
- (loop2 (- i 1) n good))))))
-
- ; This is ridiculous.
- ; Fortunately the correctness of the compiler is independent
- ; of the predicate used for this sort.
-
- (define (hardcase)
- (let* ((frame-exists? (not (negative? (cgframe-size frame))))
- (stufftosort
- (map (lambda (r)
- (let* ((t (cgreg-lookup-reg regs r))
- (spilled?
- (and t
- (cgframe-spilled? frame t))))
- (list r t spilled?)))
- (cdr (iota *nregs*))))
- (registers
- (twobit-sort
- (lambda (x1 x2)
- (let ((r1 (car x1))
- (r2 (car x2))
- (t1 (cadr x1))
- (t2 (cadr x2)))
- (cond ((< r1 *nhwregs*)
- (cond ((not t1) #t)
- ((< r2 *nhwregs*)
- (cond ((not t2) #f)
- ((caddr x1) #t)
- ((caddr x2) #f)
- (else #t)))
- (frame-exists? #t)
- (t2 #t)
- (else #f)))
- ((< r2 *nhwregs*)
- (cond (frame-exists? #f)
- (t1 #f)
- (t2 #t)
- (else #f)))
- (t1
- (if (and (caddr x1)
- t2
- (not (caddr x2)))
- #t
- #f))
- (else #t))))
- stufftosort)))
- ; FIXME: What was this for?
- '
- (for-each (lambda (register)
- (let ((t (cadr register))
- (spilled? (caddr register)))
- (if (and t (not spilled?))
- (cgframe-touch! frame t))))
- registers)
- (do ((sorted (map car registers) (cdr sorted))
- (rs '() (cons (car sorted) rs))
- (n n (- n 1)))
- ((zero? n)
- (reverse rs)))))
-
- (if (< n *nregs*)
- (loop1 (- *nhwregs* 1) n '())
- (error (string-append "Compiler bug: can't allocate "
- (number->string n)
- " registers on this target."))))
- ; Copyright 1991 William Clinger
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 21 May 1999.
- ; Procedure calls.
- (define (cg-call output exp target regs frame env tail?)
- (let ((proc (call.proc exp)))
- (cond ((and (lambda? proc)
- (list? (lambda.args proc)))
- (cg-let output exp target regs frame env tail?))
- ((not (variable? proc))
- (cg-unknown-call output exp target regs frame env tail?))
- (else (let ((entry
- (var-lookup (variable.name proc) regs frame env)))
- (case (entry.kind entry)
- ((global lexical frame register)
- (cg-unknown-call output
- exp
- target regs frame env tail?))
- ((integrable)
- (cg-integrable-call output
- exp
- target regs frame env tail?))
- ((procedure)
- (cg-known-call output
- exp
- target regs frame env tail?))
- (else (error "Bug in cg-call" exp))))))))
- (define (cg-unknown-call output exp target regs frame env tail?)
- (let* ((proc (call.proc exp))
- (args (call.args exp))
- (n (length args))
- (L (make-label)))
- (cond ((>= (+ n 1) *lastreg*)
- (cg-big-call output exp target regs frame env tail?))
- (else
- (let ((r0 (cgreg-lookup-reg regs 0)))
- (if (variable? proc)
- (let ((entry (cgreg-lookup regs (variable.name proc))))
- (if (and entry
- (<= (entry.regnum entry) n))
- (begin (cg-arguments output
- (iota1 (+ n 1))
- (append args (list proc))
- regs frame env)
- (gen! output $reg (+ n 1)))
- (begin (cg-arguments output
- (iota1 n)
- args
- regs frame env)
- (cg0 output proc 'result regs frame env #f)))
- (if tail?
- (gen-pop! output frame)
- (begin (cgframe-used! frame)
- (gen! output $setrtn L)))
- (gen! output $invoke n))
- (begin (cg-arguments output
- (iota1 (+ n 1))
- (append args (list proc))
- regs frame env)
- (gen! output $reg (+ n 1))
- (if tail?
- (gen-pop! output frame)
- (begin (cgframe-used! frame)
- (gen! output $setrtn L)))
- (gen! output $invoke n)))
- (if tail?
- 'result
- (begin (gen! output $.align 4)
- (gen! output $.label L)
- (gen! output $.cont)
- (cgreg-clear! regs)
- (cgreg-bind! regs 0 r0)
- (gen-load! output frame 0 r0)
- (cg-move output frame regs 'result target))))))))
- (define (cg-known-call output exp target regs frame env tail?)
- (let* ((args (call.args exp))
- (n (length args))
- (L (make-label)))
- (cond ((>= (+ n 1) *lastreg*)
- (cg-big-call output exp target regs frame env tail?))
- (else
- (let ((r0 (cgreg-lookup-reg regs 0)))
- (cg-arguments output (iota1 n) args regs frame env)
- (if tail?
- (gen-pop! output frame)
- (begin (cgframe-used! frame)
- (gen! output $setrtn L)))
- (let* ((entry (cgenv-lookup env (variable.name (call.proc exp))))
- (label (entry.label entry))
- (m (entry.rib entry)))
- (if (zero? m)
- (gen! output $branch label n)
- (gen! output $jump m label n)))
- (if tail?
- 'result
- (begin (gen! output $.align 4)
- (gen! output $.label L)
- (gen! output $.cont)
- (cgreg-clear! regs)
- (cgreg-bind! regs 0 r0)
- (gen-load! output frame 0 r0)
- (cg-move output frame regs 'result target))))))))
- ; Any call can be compiled as follows, even if there are no free registers.
- ;
- ; Let T0, T1, ..., Tn be newly allocated stack temporaries.
- ;
- ; <arg0>
- ; setstk T0
- ; <arg1> -|
- ; setstk T1 |
- ; ... |- evaluate args into stack frame
- ; <argn> |
- ; setstk Tn -|
- ; const ()
- ; setreg R-1
- ; stack Tn -|
- ; op2 cons,R-1 |
- ; setreg R-1 |
- ; ... |- cons up overflow args
- ; stack T_{R-1} |
- ; op2 cons,R-1 |
- ; setreg R-1 -|
- ; stack T_{R-2} -|
- ; setreg R-2 |
- ; ... |- pop remaining args into registers
- ; stack T1 |
- ; setreg 1 -|
- ; stack T0
- ; invoke n
- (define (cg-big-call output exp target regs frame env tail?)
- (let* ((proc (call.proc exp))
- (args (call.args exp))
- (n (length args))
- (argslots (newtemps n))
- (procslot (newtemp))
- (r0 (cgreg-lookup-reg regs 0))
- (R-1 (- *nregs* 1))
- (entry (if (variable? proc)
- (let ((entry
- (var-lookup (variable.name proc)
- regs frame env)))
- (if (eq? (entry.kind entry) 'procedure)
- entry
- #f))
- #f))
- (L (make-label)))
- (if (not entry)
- (begin
- (cg0 output proc 'result regs frame env #f)
- (gen-setstk! output frame procslot)))
- (for-each (lambda (arg argslot)
- (cg0 output arg 'result regs frame env #f)
- (gen-setstk! output frame argslot))
- args
- argslots)
- (cgreg-clear! regs)
- (gen! output $const '())
- (gen! output $setreg R-1)
- (do ((i n (- i 1))
- (slots (reverse argslots) (cdr slots)))
- ((zero? i))
- (if (< i R-1)
- (gen-load! output frame i (car slots))
- (begin (gen-stack! output frame (car slots))
- (gen! output $op2 $cons R-1)
- (gen! output $setreg R-1))))
- (if (not entry)
- (gen-stack! output frame procslot))
- (if tail?
- (gen-pop! output frame)
- (begin (cgframe-used! frame)
- (gen! output $setrtn L)))
- (if entry
- (let ((label (entry.label entry))
- (m (entry.rib entry)))
- (if (zero? m)
- (gen! output $branch label n)
- (gen! output $jump m label n)))
- (gen! output $invoke n))
- (if tail?
- 'result
- (begin (gen! output $.align 4)
- (gen! output $.label L)
- (gen! output $.cont)
- (cgreg-clear! regs) ; redundant, see above
- (cgreg-bind! regs 0 r0)
- (gen-load! output frame 0 r0)
- (cg-move output frame regs 'result target)))))
- (define (cg-integrable-call output exp target regs frame env tail?)
- (let ((args (call.args exp))
- (entry (var-lookup (variable.name (call.proc exp)) regs frame env)))
- (if (= (entry.arity entry) (length args))
- (begin (case (entry.arity entry)
- ((0) (gen! output $op1 (entry.op entry)))
- ((1) (cg0 output (car args) 'result regs frame env #f)
- (gen! output $op1 (entry.op entry)))
- ((2) (cg-integrable-call2 output
- entry
- args
- regs frame env))
- ((3) (cg-integrable-call3 output
- entry
- args
- regs frame env))
- (else (error "Bug detected by cg-integrable-call"
- (make-readable exp))))
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- (if (negative? (entry.arity entry))
- (cg-special output exp target regs frame env tail?)
- (error "Wrong number of arguments to integrable procedure"
- (make-readable exp))))))
- (define (cg-integrable-call2 output entry args regs frame env)
- (let ((op (entry.op entry)))
- (if (and (entry.imm entry)
- (constant? (cadr args))
- ((entry.imm entry) (constant.value (cadr args))))
- (begin (cg0 output (car args) 'result regs frame env #f)
- (gen! output $op2imm
- op
- (constant.value (cadr args))))
- (let* ((reg2 (cg0 output (cadr args) #f regs frame env #f))
- (r2 (choose-register regs frame))
- (t2 (if (eq? reg2 'result)
- (let ((t2 (newtemp)))
- (gen! output $setreg r2)
- (cgreg-bind! regs r2 t2)
- (gen-store! output frame r2 t2)
- t2)
- (cgreg-lookup-reg regs reg2))))
- (cg0 output (car args) 'result regs frame env #f)
- (let* ((r2 (or (let ((entry (cgreg-lookup regs t2)))
- (if entry
- (entry.regnum entry)
- #f))
- (let ((r2 (choose-register regs frame)))
- (cgreg-bind! regs r2 t2)
- (gen-load! output frame r2 t2)
- r2))))
- (gen! output $op2 (entry.op entry) r2)
- (if (eq? reg2 'result)
- (begin (cgreg-release! regs r2)
- (cgframe-release! frame t2)))))))
- 'result)
- (define (cg-integrable-call3 output entry args regs frame env)
- (let* ((reg2 (cg0 output (cadr args) #f regs frame env #f))
- (r2 (choose-register regs frame))
- (t2 (if (eq? reg2 'result)
- (let ((t2 (newtemp)))
- (gen! output $setreg r2)
- (cgreg-bind! regs r2 t2)
- (gen-store! output frame r2 t2)
- t2)
- (cgreg-lookup-reg regs reg2)))
- (reg3 (cg0 output (caddr args) #f regs frame env #f))
- (spillregs (choose-registers regs frame 2))
- (t3 (if (eq? reg3 'result)
- (let ((t3 (newtemp))
- (r3 (if (eq? t2 (cgreg-lookup-reg
- regs (car spillregs)))
- (cadr spillregs)
- (car spillregs))))
- (gen! output $setreg r3)
- (cgreg-bind! regs r3 t3)
- (gen-store! output frame r3 t3)
- t3)
- (cgreg-lookup-reg regs reg3))))
- (cg0 output (car args) 'result regs frame env #f)
- (let* ((spillregs (choose-registers regs frame 2))
- (r2 (or (let ((entry (cgreg-lookup regs t2)))
- (if entry
- (entry.regnum entry)
- #f))
- (let ((r2 (car spillregs)))
- (cgreg-bind! regs r2 t2)
- (gen-load! output frame r2 t2)
- r2)))
- (r3 (or (let ((entry (cgreg-lookup regs t3)))
- (if entry
- (entry.regnum entry)
- #f))
- (let ((r3 (if (eq? r2 (car spillregs))
- (cadr spillregs)
- (car spillregs))))
- (cgreg-bind! regs r3 t3)
- (gen-load! output frame r3 t3)
- r3))))
- (gen! output $op3 (entry.op entry) r2 r3)
- (if (eq? reg2 'result)
- (begin (cgreg-release! regs r2)
- (cgframe-release! frame t2)))
- (if (eq? reg3 'result)
- (begin (cgreg-release! regs r3)
- (cgframe-release! frame t3)))))
- 'result)
- ; Given a short list of expressions that can be evaluated in any order,
- ; evaluates the first into the result register and the others into any
- ; register, and returns an ordered list of the registers that contain
- ; the arguments that follow the first.
- ; The number of expressions must be less than the number of argument
- ; registers.
- (define (cg-primop-args output args regs frame env)
-
- ; Given a list of expressions to evaluate, a list of variables
- ; and temporary names for arguments that have already been
- ; evaluated, in reverse order, and a mask of booleans that
- ; indicate which temporaries should be released before returning,
- ; returns the correct result.
-
- (define (eval-loop args temps mask)
- (if (null? args)
- (eval-first-into-result temps mask)
- (let ((reg (cg0 output (car args) #f regs frame env #f)))
- (if (eq? reg 'result)
- (let* ((r (choose-register regs frame))
- (t (newtemp)))
- (gen! output $setreg r)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t)
- (eval-loop (cdr args)
- (cons t temps)
- (cons #t mask)))
- (eval-loop (cdr args)
- (cons (cgreg-lookup-reg regs reg) temps)
- (cons #f mask))))))
-
- (define (eval-first-into-result temps mask)
- (cg0 output (car args) 'result regs frame env #f)
- (finish-loop (choose-registers regs frame (length temps))
- temps
- mask
- '()))
-
- ; Given a sufficient number of disjoint registers, a list of
- ; variable and temporary names that may need to be loaded into
- ; registers, a mask of booleans that indicates which temporaries
- ; should be released, and a list of registers in forward order,
- ; returns the correct result.
-
- (define (finish-loop disjoint temps mask registers)
- (if (null? temps)
- registers
- (let* ((t (car temps))
- (entry (cgreg-lookup regs t)))
- (if entry
- (let ((r (entry.regnum entry)))
- (if (car mask)
- (begin (cgreg-release! regs r)
- (cgframe-release! frame t)))
- (finish-loop disjoint
- (cdr temps)
- (cdr mask)
- (cons r registers)))
- (let ((r (car disjoint)))
- (if (memv r registers)
- (finish-loop (cdr disjoint) temps mask registers)
- (begin (gen-load! output frame r t)
- (cgreg-bind! regs r t)
- (if (car mask)
- (begin (cgreg-release! regs r)
- (cgframe-release! frame t)))
- (finish-loop disjoint
- (cdr temps)
- (cdr mask)
- (cons r registers)))))))))
-
- (if (< (length args) *nregs*)
- (eval-loop (cdr args) '() '())
- (error "Bug detected by cg-primop-args" args)))
- ; Parallel assignment.
- ; Given a list of target registers, a list of expressions, and a
- ; compile-time environment, generates code to evaluate the expressions
- ; into the registers.
- ;
- ; Argument evaluation proceeds as follows:
- ;
- ; 1. Evaluate all but one of the complicated arguments.
- ; 2. Evaluate remaining arguments.
- ; 3. Load spilled arguments from stack.
- (define (cg-arguments output targets args regs frame env)
-
- ; Sorts the args and their targets into complicated and
- ; uncomplicated args and targets.
- ; Then it calls evalargs.
-
- (define (sortargs targets args targets1 args1 targets2 args2)
- (if (null? args)
- (evalargs targets1 args1 targets2 args2)
- (let ((target (car targets))
- (arg (car args))
- (targets (cdr targets))
- (args (cdr args)))
- (if (complicated? arg env)
- (sortargs targets
- args
- (cons target targets1)
- (cons arg args1)
- targets2
- args2)
- (sortargs targets
- args
- targets1
- args1
- (cons target targets2)
- (cons arg args2))))))
-
- ; Given the complicated args1 and their targets1,
- ; and the uncomplicated args2 and their targets2,
- ; evaluates all the arguments into their target registers.
-
- (define (evalargs targets1 args1 targets2 args2)
- (let* ((temps1 (newtemps (length targets1)))
- (temps2 (newtemps (length targets2))))
- (if (not (null? args1))
- (for-each (lambda (arg temp)
- (cg0 output arg 'result regs frame env #f)
- (gen-setstk! output frame temp))
- (cdr args1)
- (cdr temps1)))
- (if (not (null? args1))
- (evalargs0 (cons (car targets1) targets2)
- (cons (car args1) args2)
- (cons (car temps1) temps2))
- (evalargs0 targets2 args2 temps2))
- (for-each (lambda (r t)
- (let ((temp (cgreg-lookup-reg regs r)))
- (if (not (eq? temp t))
- (let ((entry (var-lookup t regs frame env)))
- (case (entry.kind entry)
- ((register)
- (gen! output $movereg (entry.regnum entry) r))
- ((frame)
- (gen-load! output frame r t)))
- (cgreg-bind! regs r t)))
- (cgframe-release! frame t)))
- (append targets1 targets2)
- (append temps1 temps2))))
-
- (define (evalargs0 targets args temps)
- (if (not (null? targets))
- (let ((para (let* ((regvars (map (lambda (reg)
- (cgreg-lookup-reg regs reg))
- targets)))
- (parallel-assignment targets
- (map cons regvars targets)
- args))))
- (if para
- (let ((targets para)
- (args (cg-permute args targets para))
- (temps (cg-permute temps targets para)))
- (for-each (lambda (arg r t)
- (cg0 output arg r regs frame env #f)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t))
- args
- para
- temps))
- (let ((r (choose-register regs frame))
- (t (car temps)))
- (cg0 output (car args) r regs frame env #f)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t)
- (evalargs0 (cdr targets)
- (cdr args)
- (cdr temps)))))))
-
- (if (parallel-assignment-optimization)
- (sortargs (reverse targets) (reverse args) '() '() '() '())
- (cg-evalargs output targets args regs frame env)))
- ; Left-to-right evaluation of arguments directly into targets.
- (define (cg-evalargs output targets args regs frame env)
- (let ((temps (newtemps (length targets))))
- (for-each (lambda (arg r t)
- (cg0 output arg r regs frame env #f)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t))
- args
- targets
- temps)
- (for-each (lambda (r t)
- (let ((temp (cgreg-lookup-reg regs r)))
- (if (not (eq? temp t))
- (begin (gen-load! output frame r t)
- (cgreg-bind! regs r t)))
- (cgframe-release! frame t)))
- targets
- temps)))
- ; For heuristic use only.
- ; An expression is complicated unless it can probably be evaluated
- ; without saving and restoring any registers, even if it occurs in
- ; a non-tail position.
- (define (complicated? exp env)
- (case (car exp)
- ((quote) #f)
- ((lambda) #t)
- ((set!) (complicated? (assignment.rhs exp) env))
- ((if) (or (complicated? (if.test exp) env)
- (complicated? (if.then exp) env)
- (complicated? (if.else exp) env)))
- ((begin) (if (variable? exp)
- #f
- (some? (lambda (exp)
- (complicated? exp env))
- (begin.exprs exp))))
- (else (let ((proc (call.proc exp)))
- (if (and (variable? proc)
- (let ((entry
- (cgenv-lookup env (variable.name proc))))
- (eq? (entry.kind entry) 'integrable)))
- (some? (lambda (exp)
- (complicated? exp env))
- (call.args exp))
- #t)))))
- ; Returns a permutation of the src list, permuted the same way the
- ; key list was permuted to obtain newkey.
- (define (cg-permute src key newkey)
- (let ((alist (map cons key (iota (length key)))))
- (do ((newkey newkey (cdr newkey))
- (dest '()
- (cons (list-ref src (cdr (assq (car newkey) alist)))
- dest)))
- ((null? newkey) (reverse dest)))))
- ; Given a list of register numbers,
- ; an association list with entries of the form (name . regnum) giving
- ; the variable names by which those registers are known in code,
- ; and a list of expressions giving new values for those registers,
- ; returns an ordering of the register assignments that implements a
- ; parallel assignment if one can be found, otherwise returns #f.
- (define parallel-assignment
- (lambda (regnums alist exps)
- (if (null? regnums)
- #t
- (let ((x (toposort (dependency-graph regnums alist exps))))
- (if x (reverse x) #f)))))
- (define dependency-graph
- (lambda (regnums alist exps)
- (let ((names (map car alist)))
- (do ((regnums regnums (cdr regnums))
- (exps exps (cdr exps))
- (l '() (cons (cons (car regnums)
- (map (lambda (var) (cdr (assq var alist)))
- (intersection (freevariables (car exps))
- names)))
- l)))
- ((null? regnums) l)))))
- ; Given a nonempty graph represented as a list of the form
- ; ((node1 . <list of nodes that node1 is less than or equal to>)
- ; (node2 . <list of nodes that node2 is less than or equal to>)
- ; ...)
- ; returns a topological sort of the nodes if one can be found,
- ; otherwise returns #f.
- (define toposort
- (lambda (graph)
- (cond ((null? (cdr graph)) (list (caar graph)))
- (else (toposort2 graph '())))))
- (define toposort2
- (lambda (totry tried)
- (cond ((null? totry) #f)
- ((or (null? (cdr (car totry)))
- (and (null? (cddr (car totry)))
- (eq? (cadr (car totry))
- (car (car totry)))))
- (if (and (null? (cdr totry)) (null? tried))
- (list (caar totry))
- (let* ((node (caar totry))
- (x (toposort2 (map (lambda (y)
- (cons (car y) (remove node (cdr y))))
- (append (cdr totry) tried))
- '())))
- (if x
- (cons node x)
- #f))))
- (else (toposort2 (cdr totry) (cons (car totry) tried))))))
- (define iota (lambda (n) (iota2 n '())))
- (define iota1 (lambda (n) (cdr (iota2 (+ n 1) '()))))
- (define iota2
- (lambda (n l)
- (if (zero? n)
- l
- (let ((n (- n 1)))
- (iota2 n (cons n l))))))
- (define (freevariables exp)
- (freevars2 exp '()))
- (define (freevars2 exp env)
- (cond ((symbol? exp)
- (if (memq exp env) '() (list exp)))
- ((not (pair? exp)) '())
- (else (let ((keyword (car exp)))
- (cond ((eq? keyword 'quote) '())
- ((eq? keyword 'lambda)
- (let ((env (append (make-null-terminated (cadr exp))
- env)))
- (apply-union
- (map (lambda (x) (freevars2 x env))
- (cddr exp)))))
- ((memq keyword '(if set! begin))
- (apply-union
- (map (lambda (x) (freevars2 x env))
- (cdr exp))))
- (else (apply-union
- (map (lambda (x) (freevars2 x env))
- exp))))))))
- ; Copyright 1991 William Clinger (cg-let and cg-let-body)
- ; Copyright 1999 William Clinger (everything else)
- ;
- ; 10 June 1999.
- ; Generates code for a let expression.
- (define (cg-let output exp target regs frame env tail?)
- (let* ((proc (call.proc exp))
- (vars (lambda.args proc))
- (n (length vars))
- (free (lambda.F proc))
- (live (cgframe-livevars frame)))
- (if (and (null? (lambda.defs proc))
- (= n 1))
- (cg-let1 output exp target regs frame env tail?)
- (let* ((args (call.args exp))
- (temps (newtemps n))
- (alist (map cons temps vars)))
- (for-each (lambda (arg t)
- (let ((r (choose-register regs frame)))
- (cg0 output arg r regs frame env #f)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t)))
- args
- temps)
- (cgreg-rename! regs alist)
- (cgframe-rename! frame alist)
- (cg-let-release! free live regs frame tail?)
- (cg-let-body output proc target regs frame env tail?)))))
- ; Given the free variables of a let body, and the variables that are
- ; live after the let expression, and the usual regs, frame, and tail?
- ; arguments, releases any registers and frame slots that don't need
- ; to be preserved across the body of the let.
- (define (cg-let-release! free live regs frame tail?)
- ; The tail case is easy because there are no live temporaries,
- ; and there are no free variables in the context.
- ; The non-tail case assumes A-normal form.
- (cond (tail?
- (let ((keepers (cons (cgreg-lookup-reg regs 0) free)))
- (cgreg-release-except! regs keepers)
- (cgframe-release-except! frame keepers)))
- (live
- (let ((keepers (cons (cgreg-lookup-reg regs 0)
- (union live free))))
- (cgreg-release-except! regs keepers)
- (cgframe-release-except! frame keepers)))))
- ; Generates code for the body of a let.
- (define (cg-let-body output L target regs frame env tail?)
- (let ((vars (lambda.args L))
- (free (lambda.F L))
- (live (cgframe-livevars frame)))
- (let ((r (cg-body output L target regs frame env tail?)))
- (for-each (lambda (v)
- (let ((entry (cgreg-lookup regs v)))
- (if entry
- (cgreg-release! regs (entry.regnum entry)))
- (cgframe-release! frame v)))
- vars)
- (if (and (not target)
- (not (eq? r 'result))
- (not (cgreg-lookup-reg regs r)))
- (cg-move output frame regs r 'result)
- r))))
- ; Generates code for a let expression that binds exactly one variable
- ; and has no internal definitions. These let expressions are very
- ; common in A-normal form, and there are many special cases with
- ; respect to register allocation and order of evaluation.
- (define (cg-let1 output exp target regs frame env tail?)
- (let* ((proc (call.proc exp))
- (v (car (lambda.args proc)))
- (arg (car (call.args exp)))
- (free (lambda.F proc))
- (live (cgframe-livevars frame))
- (body (lambda.body proc)))
-
- (define (evaluate-into-register r)
- (cg0 output arg r regs frame env #f)
- (cgreg-bind! regs r v)
- (gen-store! output frame r v)
- r)
-
- (define (release-registers!)
- (cgframe-livevars-set! frame live)
- (cg-let-release! free live regs frame tail?))
-
- (define (finish)
- (release-registers!)
- (cg-let-body output proc target regs frame env tail?))
-
- (if live
- (cgframe-livevars-set! frame (union live free)))
-
- (cond ((assq v *regnames*)
- (evaluate-into-register (cdr (assq v *regnames*)))
- (finish))
- ((not (memq v free))
- (cg0 output arg #f regs frame env #f)
- (finish))
- (live
- (cg0 output arg 'result regs frame env #f)
- (release-registers!)
- (cg-let1-result output exp target regs frame env tail?))
- (else
- (evaluate-into-register (choose-register regs frame))
- (finish)))))
- ; Given a let expression that binds one variable whose value has already
- ; been evaluated into the result register, generates code for the rest
- ; of the let expression.
- ; The main difficulty is an unfortunate interaction between A-normal
- ; form and the MacScheme machine architecture: We don't want to move
- ; a value from the result register into a general register if it has
- ; only one use and can remain in the result register until that use.
- (define (cg-let1-result output exp target regs frame env tail?)
- (let* ((proc (call.proc exp))
- (v (car (lambda.args proc)))
- (free (lambda.F proc))
- (live (cgframe-livevars frame))
- (body (lambda.body proc))
- (pattern (cg-let-used-once v body)))
-
- (define (move-to-register r)
- (gen! output $setreg r)
- (cgreg-bind! regs r v)
- (gen-store! output frame r v)
- r)
-
- (define (release-registers!)
- (cgframe-livevars-set! frame live)
- (cg-let-release! free live regs frame tail?))
-
- ; FIXME: The live variables must be correct in the frame.
-
- (case pattern
- ((if)
- (cg-if-result output body target regs frame env tail?))
- ((let-if)
- (if live
- (cgframe-livevars-set! frame (union live free)))
- (cg-if-result output
- (car (call.args body))
- 'result regs frame env #f)
- (release-registers!)
- (cg-let1-result output body target regs frame env tail?))
- ((set!)
- (cg-assignment-result output
- body target regs frame env tail?))
- ((let-set!)
- (cg-assignment-result output
- (car (call.args body))
- 'result regs frame env #f)
- (cg-let1-result output body target regs frame env tail?))
- ((primop)
- (cg-primop-result output body target regs frame env tail?))
- ((let-primop)
- (cg-primop-result output
- (car (call.args body))
- 'result regs frame env #f)
- (cg-let1-result output body target regs frame env tail?))
- ; FIXME
- ((_called)
- (cg-call-result output body target regs frame env tail?))
- ; FIXME
- ((_let-called)
- (cg-call-result output
- (car (call.args body))
- 'result regs frame env #f)
- (cg-let1-result output body target regs frame env tail?))
- (else
- ; FIXME: The first case was handled by cg-let1.
- (cond ((assq v *regnames*)
- (move-to-register (cdr (assq v *regnames*))))
- ((memq v free)
- (move-to-register (choose-register regs frame))))
- (cg-let-body output proc target regs frame env tail?)))))
- ; Given a call to a primop whose first argument has already been
- ; evaluated into the result register and whose remaining arguments
- ; consist of constants and variable references, generates code for
- ; the call.
- (define (cg-primop-result output exp target regs frame env tail?)
- (let ((args (call.args exp))
- (entry (var-lookup (variable.name (call.proc exp)) regs frame env)))
- (if (= (entry.arity entry) (length args))
- (begin (case (entry.arity entry)
- ((0) (gen! output $op1 (entry.op entry)))
- ((1) (gen! output $op1 (entry.op entry)))
- ((2) (cg-primop2-result! output entry args regs frame env))
- ((3) (let ((rs (cg-result-args output args regs frame env)))
- (gen! output
- $op3 (entry.op entry) (car rs) (cadr rs))))
- (else (error "Bug detected by cg-primop-result"
- (make-readable exp))))
- (if tail?
- (begin (gen-pop! output frame)
- (gen! output $return)
- 'result)
- (cg-move output frame regs 'result target)))
- (if (negative? (entry.arity entry))
- (cg-special-result output exp target regs frame env tail?)
- (error "Wrong number of arguments to integrable procedure"
- (make-readable exp))))))
- (define (cg-primop2-result! output entry args regs frame env)
- (let ((op (entry.op entry))
- (arg2 (cadr args)))
- (if (and (constant? arg2)
- (entry.imm entry)
- ((entry.imm entry) (constant.value arg2)))
- (gen! output $op2imm op (constant.value arg2))
- (let ((rs (cg-result-args output args regs frame env)))
- (gen! output $op2 op (car rs))))))
- ; Given a short list of constants and variable references to be evaluated
- ; into arbitrary general registers, evaluates them into registers without
- ; disturbing the result register and returns a list of the registers into
- ; which they are evaluated. Before returning, any registers that were
- ; allocated by this routine are released.
- (define (cg-result-args output args regs frame env)
-
- ; Given a list of unevaluated arguments,
- ; a longer list of disjoint general registers,
- ; the register that holds the first evaluated argument,
- ; a list of registers in reverse order that hold other arguments,
- ; and a list of registers to be released afterwards,
- ; generates code to evaluate the arguments,
- ; deallocates any registers that were evaluated to hold the arguments,
- ; and returns the list of registers that contain the arguments.
-
- (define (loop args registers rr rs temps)
- (if (null? args)
- (begin (if (not (eq? rr 'result))
- (gen! output $reg rr))
- (for-each (lambda (r) (cgreg-release! regs r))
- temps)
- (reverse rs))
- (let ((arg (car args)))
- (cond ((constant? arg)
- (let ((r (car registers)))
- (gen! output $const/setreg (constant.value arg) r)
- (cgreg-bind! regs r #t)
- (loop (cdr args)
- (cdr registers)
- rr
- (cons r rs)
- (cons r temps))))
- ((variable? arg)
- (let* ((id (variable.name arg))
- (entry (var-lookup id regs frame env)))
- (case (entry.kind entry)
- ((global integrable)
- (if (eq? rr 'result)
- (save-result! args registers rr rs temps)
- (let ((r (car registers)))
- (gen! output $global id)
- (gen! output $setreg r)
- (cgreg-bind! regs r id)
- (loop (cdr args)
- (cdr registers)
- rr
- (cons r rs)
- (cons r temps)))))
- ((lexical)
- (if (eq? rr 'result)
- (save-result! args registers rr rs temps)
- (let ((m (entry.rib entry))
- (n (entry.offset entry))
- (r (car registers)))
- (gen! output $lexical m n id)
- (gen! output $setreg r)
- (cgreg-bind! regs r id)
- (loop (cdr args)
- (cdr registers)
- rr
- (cons r rs)
- (cons r temps)))))
- ((procedure) (error "Bug in cg-variable" arg))
- ((register)
- (let ((r (entry.regnum entry)))
- (loop (cdr args)
- registers
- rr
- (cons r rs)
- temps)))
- ((frame)
- (let ((r (car registers)))
- (gen-load! output frame r id)
- (cgreg-bind! regs r id)
- (loop (cdr args)
- (cdr registers)
- rr
- (cons r rs)
- (cons r temps))))
- (else (error "Bug in cg-result-args" arg)))))
- (else
- (error "Bug in cg-result-args"))))))
-
- (define (save-result! args registers rr rs temps)
- (let ((r (car registers)))
- (gen! output $setreg r)
- (loop args
- (cdr registers)
- r
- rs
- temps)))
-
- (loop (cdr args)
- (choose-registers regs frame (length args))
- 'result '() '()))
- ; Given a local variable T1 and an expression in A-normal form,
- ; cg-let-used-once returns a symbol if the local variable is used
- ; exactly once in the expression and the expression matches one of
- ; the patterns below. Otherwise returns #f. The symbol that is
- ; returned is the name of the pattern that is matched.
- ;
- ; pattern symbol returned
- ;
- ; (if T1 ... ...) if
- ;
- ; (<primop> T1 ...) primop
- ;
- ; (T1 ...) called
- ;
- ; (set! ... T1) set!
- ;
- ; (let ((T2 (if T1 ... ...))) let-if
- ; E3)
- ;
- ; (let ((T2 (<primop> T1 ...))) let-primop
- ; E3)
- ;
- ; (let ((T2 (T1 ...))) let-called
- ; E3)
- ;
- ; (let ((T2 (set! ... T1))) let-set!
- ; E3)
- ;
- ; This implementation sometimes returns #f incorrectly, but it always
- ; returns an answer in constant time (assuming A-normal form).
- (define (cg-let-used-once T1 exp)
- (define budget 20)
- (define (cg-let-used-once T1 exp)
- (define (used? T1 exp)
- (set! budget (- budget 1))
- (cond ((negative? budget) #t)
- ((constant? exp) #f)
- ((variable? exp)
- (eq? T1 (variable.name exp)))
- ((lambda? exp)
- (memq T1 (lambda.F exp)))
- ((assignment? exp)
- (used? T1 (assignment.rhs exp)))
- ((call? exp)
- (or (used? T1 (call.proc exp))
- (used-in-args? T1 (call.args exp))))
- ((conditional? exp)
- (or (used? T1 (if.test exp))
- (used? T1 (if.then exp))
- (used? T1 (if.else exp))))
- (else #t)))
- (define (used-in-args? T1 args)
- (if (null? args)
- #f
- (or (used? T1 (car args))
- (used-in-args? T1 (cdr args)))))
- (set! budget (- budget 1))
- (cond ((negative? budget) #f)
- ((call? exp)
- (let ((proc (call.proc exp))
- (args (call.args exp)))
- (cond ((variable? proc)
- (let ((f (variable.name proc)))
- (cond ((eq? f T1)
- (and (not (used-in-args? T1 args))
- 'called))
- ((and (integrable? f)
- (not (null? args))
- (variable? (car args))
- (eq? T1 (variable.name (car args))))
- (and (not (used-in-args? T1 (cdr args)))
- 'primop))
- (else #f))))
- ((lambda? proc)
- (and (not (memq T1 (lambda.F proc)))
- (not (null? args))
- (null? (cdr args))
- (case (cg-let-used-once T1 (car args))
- ((if) 'let-if)
- ((primop) 'let-primop)
- ((called) 'let-called)
- ((set!) 'let-set!)
- (else #f))))
- (else #f))))
- ((conditional? exp)
- (let ((E0 (if.test exp)))
- (and (variable? E0)
- (eq? T1 (variable.name E0))
- (not (used? T1 (if.then exp)))
- (not (used? T1 (if.else exp)))
- 'if)))
- ((assignment? exp)
- (let ((rhs (assignment.rhs exp)))
- (and (variable? rhs)
- (eq? T1 (variable.name rhs))
- 'set!)))
- (else #f)))
- (cg-let-used-once T1 exp))
- ; Given the name of a let-body pattern, an expression that matches that
- ; pattern, and an expression to be substituted for the let variable,
- ; returns the transformed expression.
- ; FIXME: No longer used.
- (define (cg-let-transform pattern exp E1)
- (case pattern
- ((if)
- (make-conditional E1 (if.then exp) (if.else exp)))
- ((primop)
- (make-call (call.proc exp)
- (cons E1 (cdr (call.args exp)))))
- ((called)
- (make-call E1 (call.args exp)))
- ((set!)
- (make-assignment (assignment.lhs exp) E1))
- ((let-if let-primop let-called let-set!)
- (make-call (call.proc exp)
- (list (cg-let-transform (case pattern
- ((let-if) 'if)
- ((let-primop) 'primop)
- ((let-called) 'called)
- ((let-set!) 'set!))
- (car (call.args exp))
- E1))))
- (else
- (error "Unrecognized pattern in cg-let-transform" pattern)))); Copyright 1999 William Clinger
- ;
- ; Code for special primitives, used to generate runtime safety checks,
- ; efficient code for call-with-values, and other weird things.
- ;
- ; 4 June 1999.
- (define (cg-special output exp target regs frame env tail?)
- (let ((name (variable.name (call.proc exp))))
- (cond ((eq? name name:CHECK!)
- (if (runtime-safety-checking)
- (cg-check output exp target regs frame env tail?)))
- (else
- (error "Compiler bug: cg-special" (make-readable exp))))))
- (define (cg-special-result output exp target regs frame env tail?)
- (let ((name (variable.name (call.proc exp))))
- (cond ((eq? name name:CHECK!)
- (if (runtime-safety-checking)
- (cg-check-result output exp target regs frame env tail?)))
- (else
- (error "Compiler bug: cg-special" (make-readable exp))))))
- (define (cg-check output exp target regs frame env tail?)
- (cg0 output (car (call.args exp)) 'result regs frame env #f)
- (cg-check-result output exp target regs frame env tail?))
- (define (cg-check-result output exp target regs frame env tail?)
- (let* ((args (call.args exp))
- (nargs (length args))
- (valexps (cddr args)))
- (if (and (<= 2 nargs 5)
- (constant? (cadr args))
- (every? (lambda (exp)
- (or (constant? exp)
- (variable? exp)))
- valexps))
- (let* ((exn (constant.value (cadr args)))
- (vars (filter variable? valexps))
- (rs (cg-result-args output
- (cons (car args) vars)
- regs frame env)))
-
- ; Construct the trap situation:
- ; the exception number followed by an ordered list of
- ; register numbers and constant expressions.
-
- (let loop ((registers rs)
- (exps valexps)
- (operands '()))
- (cond ((null? exps)
- (let* ((situation (cons exn (reverse operands)))
- (ht (assembly-stream-info output))
- (L1 (or (hashtable-get ht situation)
- (let ((L1 (make-label)))
- (hashtable-put! ht situation L1)
- L1))))
- (define (translate r)
- (if (number? r) r 0))
- (case (length operands)
- ((0) (gen! output $check 0 0 0 L1))
- ((1) (gen! output $check
- (translate (car operands))
- 0 0 L1))
- ((2) (gen! output $check
- (translate (car operands))
- (translate (cadr operands))
- 0 L1))
- ((3) (gen! output $check
- (translate (car operands))
- (translate (cadr operands))
- (translate (caddr operands))
- L1)))))
- ((constant? (car exps))
- (loop registers
- (cdr exps)
- (cons (car exps) operands)))
- (else
- (loop (cdr registers)
- (cdr exps)
- (cons (car registers) operands))))))
- (error "Compiler bug: runtime check" (make-readable exp)))))
- ; Given an assembly stream and the description of a trap as recorded
- ; by cg-check above, generates a non-continuable trap at that label for
- ; that trap, passing the operands to the exception handler.
- (define (cg-trap output situation L1)
- (let* ((exn (car situation))
- (operands (cdr situation)))
- (gen! output $.label L1)
- (let ((liveregs (filter number? operands)))
- (define (loop operands registers r)
- (cond ((null? operands)
- (case (length registers)
- ((0) (gen! output $trap 0 0 0 exn))
- ((1) (gen! output $trap (car registers) 0 0 exn))
- ((2) (gen! output $trap
- (car registers)
- (cadr registers)
- 0
- exn))
- ((3) (gen! output $trap
- (car registers)
- (cadr registers)
- (caddr registers)
- exn))
- (else "Compiler bug: trap")))
- ((number? (car operands))
- (loop (cdr operands)
- (cons (car operands) registers)
- r))
- ((memv r liveregs)
- (loop operands registers (+ r 1)))
- (else
- (gen! output $const (constant.value (car operands)))
- (gen! output $setreg r)
- (loop (cdr operands)
- (cons r registers)
- (+ r 1)))))
- (loop (reverse operands) '() 1))))
- ; Given a short list of expressions that can be evaluated in any order,
- ; evaluates the first into the result register and the others into any
- ; register, and returns an ordered list of the registers that contain
- ; the arguments that follow the first.
- ; The number of expressions must be less than the number of argument
- ; registers.
- ; FIXME: No longer used.
- (define (cg-check-args output args regs frame env)
-
- ; Given a list of expressions to evaluate, a list of variables
- ; and temporary names for arguments that have already been
- ; evaluated, in reverse order, and a mask of booleans that
- ; indicate which temporaries should be released before returning,
- ; returns the correct result.
-
- (define (eval-loop args temps mask)
- (if (null? args)
- (eval-first-into-result temps mask)
- (let ((reg (cg0 output (car args) #f regs frame env #f)))
- (if (eq? reg 'result)
- (let* ((r (choose-register regs frame))
- (t (newtemp)))
- (gen! output $setreg r)
- (cgreg-bind! regs r t)
- (gen-store! output frame r t)
- (eval-loop (cdr args)
- (cons t temps)
- (cons #t mask)))
- (eval-loop (cdr args)
- (cons (cgreg-lookup-reg regs reg) temps)
- (cons #f mask))))))
-
- (define (eval-first-into-result temps mask)
- (cg0 output (car args) 'result regs frame env #f)
- (finish-loop (choose-registers regs frame (length temps))
- temps
- mask
- '()))
-
- ; Given a sufficient number of disjoint registers, a list of
- ; variable and temporary names that may need to be loaded into
- ; registers, a mask of booleans that indicates which temporaries
- ; should be released, and a list of registers in forward order,
- ; returns the correct result.
-
- (define (finish-loop disjoint temps mask registers)
- (if (null? temps)
- registers
- (let* ((t (car temps))
- (entry (cgreg-lookup regs t)))
- (if entry
- (let ((r (entry.regnum entry)))
- (if (car mask)
- (begin (cgreg-release! regs r)
- (cgframe-release! frame t)))
- (finish-loop disjoint
- (cdr temps)
- (cdr mask)
- (cons r registers)))
- (let ((r (car disjoint)))
- (if (memv r registers)
- (finish-loop (cdr disjoint) temps mask registers)
- (begin (gen-load! output frame r t)
- (cgreg-bind! regs r t)
- (if (car mask)
- (begin (cgreg-release! regs r)
- (cgframe-release! frame t)))
- (finish-loop disjoint
- (cdr temps)
- (cdr mask)
- (cons r registers)))))))))
-
- (if (< (length args) *nregs*)
- (eval-loop (cdr args) '() '())
- (error "Bug detected by cg-primop-args" args)))
- ; Copyright 1998 William Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 5 June 1999.
- ;
- ; Local optimizations for MacScheme machine assembly code.
- ;
- ; Branch tensioning.
- ; Suppress nop instructions.
- ; Suppress save, restore, and pop instructions whose operand is -1.
- ; Suppress redundant stores.
- ; Suppress definitions (primarily loads) of dead registers.
- ;
- ; Note: Twobit never generates a locally redundant load or store,
- ; so this code must be tested with a different code generator.
- ;
- ; To perform these optimizations, the basic block must be traversed
- ; both forwards and backwards.
- ; The forward traversal keeps track of registers that were defined
- ; by a load.
- ; The backward traversal keeps track of live registers.
- (define filter-basic-blocks
-
- (let* ((suppression-message
- "Local optimization detected a useless instruction.")
-
- ; Each instruction is mapping to an encoding of the actions
- ; to be performed when it is encountered during the forward
- ; or backward traversal.
-
- (forward:normal 0)
- (forward:nop 1)
- (forward:ends-block 2)
- (forward:interesting 3)
- (forward:kills-all-registers 4)
- (forward:nop-if-arg1-is-negative 5)
-
- (backward:normal 0)
- (backward:ends-block 1)
- (backward:begins-block 2)
- (backward:uses-arg1 4)
- (backward:uses-arg2 8)
- (backward:uses-arg3 16)
- (backward:kills-arg1 32)
- (backward:kills-arg2 64)
- (backward:uses-many 128)
-
- ; largest mnemonic + 1
-
- (dispatch-table-size *number-of-mnemonics*)
-
- ; Dispatch table for the forwards traversal.
-
- (forward-table (make-bytevector dispatch-table-size))
-
- ; Dispatch table for the backwards traversal.
-
- (backward-table (make-bytevector dispatch-table-size)))
-
- (do ((i 0 (+ i 1)))
- ((= i dispatch-table-size))
- (bytevector-set! forward-table i forward:normal)
- (bytevector-set! backward-table i backward:normal))
-
- (bytevector-set! forward-table $nop forward:nop)
-
- (bytevector-set! forward-table $invoke forward:ends-block)
- (bytevector-set! forward-table $return forward:ends-block)
- (bytevector-set! forward-table $skip forward:ends-block)
- (bytevector-set! forward-table $branch forward:ends-block)
- (bytevector-set! forward-table $branchf forward:ends-block)
- (bytevector-set! forward-table $jump forward:ends-block)
- (bytevector-set! forward-table $.align forward:ends-block)
- (bytevector-set! forward-table $.proc forward:ends-block)
- (bytevector-set! forward-table $.cont forward:ends-block)
- (bytevector-set! forward-table $.label forward:ends-block)
-
- (bytevector-set! forward-table $store forward:interesting)
- (bytevector-set! forward-table $load forward:interesting)
- (bytevector-set! forward-table $setstk forward:interesting)
- (bytevector-set! forward-table $setreg forward:interesting)
- (bytevector-set! forward-table $movereg forward:interesting)
- (bytevector-set! forward-table $const/setreg
- forward:interesting)
-
- (bytevector-set! forward-table $args>= forward:kills-all-registers)
- (bytevector-set! forward-table $popstk forward:kills-all-registers)
-
- ; These instructions also kill all registers.
-
- (bytevector-set! forward-table $save forward:nop-if-arg1-is-negative)
- (bytevector-set! forward-table $restore forward:nop-if-arg1-is-negative)
- (bytevector-set! forward-table $pop forward:nop-if-arg1-is-negative)
-
- (bytevector-set! backward-table $invoke backward:ends-block)
- (bytevector-set! backward-table $return backward:ends-block)
- (bytevector-set! backward-table $skip backward:ends-block)
- (bytevector-set! backward-table $branch backward:ends-block)
- (bytevector-set! backward-table $branchf backward:ends-block)
-
- (bytevector-set! backward-table $jump backward:begins-block) ; [sic]
- (bytevector-set! backward-table $.align backward:begins-block)
- (bytevector-set! backward-table $.proc backward:begins-block)
- (bytevector-set! backward-table $.cont backward:begins-block)
- (bytevector-set! backward-table $.label backward:begins-block)
-
- (bytevector-set! backward-table $op2 backward:uses-arg2)
- (bytevector-set! backward-table $op3 (logior backward:uses-arg2
- backward:uses-arg3))
- (bytevector-set! backward-table $check (logior
- backward:uses-arg1
- (logior backward:uses-arg2
- backward:uses-arg3)))
- (bytevector-set! backward-table $trap (logior
- backward:uses-arg1
- (logior backward:uses-arg2
- backward:uses-arg3)))
- (bytevector-set! backward-table $store backward:uses-arg1)
- (bytevector-set! backward-table $reg backward:uses-arg1)
- (bytevector-set! backward-table $load backward:kills-arg1)
- (bytevector-set! backward-table $setreg backward:kills-arg1)
- (bytevector-set! backward-table $movereg (logior backward:uses-arg1
- backward:kills-arg2))
- (bytevector-set! backward-table $const/setreg
- backward:kills-arg2)
- (bytevector-set! backward-table $lambda backward:uses-many)
- (bytevector-set! backward-table $lexes backward:uses-many)
- (bytevector-set! backward-table $args>= backward:uses-many)
-
- (lambda (instructions)
-
- (let* ((*nregs* *nregs*) ; locals might be faster than globals
-
- ; During the forwards traversal:
- ; (vector-ref registers i) = #f
- ; means the content of register i is unknown
- ; (vector-ref registers i) = j
- ; means register was defined by load i,j
- ;
- ; During the backwards traversal:
- ; (vector-ref registers i) = #f means register i is dead
- ; (vector-ref registers i) = #t means register i is live
-
- (registers (make-vector *nregs* #f))
-
- ; During the forwards traversal, the label of a block that
- ; falls through into another block or consists of a skip
- ; to another block is mapped to another label.
- ; This mapping is implemented by a hash table.
- ; Before the backwards traversal, the transitive closure
- ; is computed. The graph has no cycles, and the maximum
- ; out-degree is 1, so this is easy.
-
- (label-table (make-hashtable (lambda (n) n) assv)))
-
- (define (compute-transitive-closure!)
- (define (lookup x)
- (let ((y (hashtable-get label-table x)))
- (if y
- (lookup y)
- x)))
- (hashtable-for-each (lambda (x y)
- (hashtable-put! label-table x (lookup y)))
- label-table))
-
- ; Don't use this procedure until the preceding procedure
- ; has been called.
-
- (define (lookup-label x)
- (hashtable-fetch label-table x x))
-
- (define (vector-fill! v x)
- (subvector-fill! v 0 (vector-length v) x))
-
- (define (subvector-fill! v i j x)
- (if (< i j)
- (begin (vector-set! v i x)
- (subvector-fill! v (+ i 1) j x))))
-
- (define (kill-stack! j)
- (do ((i 0 (+ i 1)))
- ((= i *nregs*))
- (let ((x (vector-ref registers i)))
- (if (and x (= x j))
- (vector-set! registers i #f)))))
-
- ; Dispatch procedure for the forwards traversal.
-
- (define (forwards instructions filtered)
- (if (null? instructions)
- (begin (vector-fill! registers #f)
- (vector-set! registers 0 #t)
- (compute-transitive-closure!)
- (backwards0 filtered '()))
- (let* ((instruction (car instructions))
- (instructions (cdr instructions))
- (op (instruction.op instruction))
- (flags (bytevector-ref forward-table op)))
- (cond ((eqv? flags forward:normal)
- (forwards instructions (cons instruction filtered)))
- ((eqv? flags forward:nop)
- (forwards instructions filtered))
- ((eqv? flags forward:nop-if-arg1-is-negative)
- (if (negative? (instruction.arg1 instruction))
- (forwards instructions filtered)
- (begin (vector-fill! registers #f)
- (forwards instructions
- (cons instruction filtered)))))
- ((eqv? flags forward:kills-all-registers)
- (vector-fill! registers #f)
- (forwards instructions
- (cons instruction filtered)))
- ((eqv? flags forward:ends-block)
- (vector-fill! registers #f)
- (if (eqv? op $.label)
- (forwards-label instruction
- instructions
- filtered)
- (forwards instructions
- (cons instruction filtered))))
- ((eqv? flags forward:interesting)
- (cond ((eqv? op $setreg)
- (vector-set! registers
- (instruction.arg1 instruction)
- #f)
- (forwards instructions
- (cons instruction filtered)))
- ((eqv? op $const/setreg)
- (vector-set! registers
- (instruction.arg2 instruction)
- #f)
- (forwards instructions
- (cons instruction filtered)))
- ((eqv? op $movereg)
- (vector-set! registers
- (instruction.arg2 instruction)
- #f)
- (forwards instructions
- (cons instruction filtered)))
- ((eqv? op $setstk)
- (kill-stack! (instruction.arg1 instruction))
- (forwards instructions
- (cons instruction filtered)))
- ((eqv? op $load)
- (let ((i (instruction.arg1 instruction))
- (j (instruction.arg2 instruction)))
- (if (eqv? (vector-ref registers i) j)
- ; Suppress redundant load.
- ; Should never happen with Twobit.
- (suppress-forwards instruction
- instructions
- filtered)
- (begin (vector-set! registers i j)
- (forwards instructions
- (cons instruction
- filtered))))))
- ((eqv? op $store)
- (let ((i (instruction.arg1 instruction))
- (j (instruction.arg2 instruction)))
- (if (eqv? (vector-ref registers i) j)
- ; Suppress redundant store.
- ; Should never happen with Twobit.
- (suppress-forwards instruction
- instructions
- filtered)
- (begin (kill-stack! j)
- (forwards instructions
- (cons instruction
- filtered))))))
- (else
- (local-optimization-error op))))
- (else
- (local-optimization-error op))))))
-
- ; Enters labels into a table for branch tensioning.
-
- (define (forwards-label instruction1 instructions filtered)
- (let ((label1 (instruction.arg1 instruction1)))
- (if (null? instructions)
- ; This is ok provided the label is unreachable.
- (forwards instructions (cdr filtered))
- (let loop ((instructions instructions)
- (filtered (cons instruction1 filtered)))
- (let* ((instruction (car instructions))
- (op (instruction.op instruction))
- (flags (bytevector-ref forward-table op)))
- (cond ((eqv? flags forward:nop)
- (loop (cdr instructions) filtered))
- ((and (eqv? flags forward:nop-if-arg1-is-negative)
- (negative? (instruction.arg1 instruction)))
- (loop (cdr instructions) filtered))
- ((eqv? op $.label)
- (let ((label2 (instruction.arg1 instruction)))
- (hashtable-put! label-table label1 label2)
- (forwards-label instruction
- (cdr instructions)
- (cdr filtered))))
- ((eqv? op $skip)
- (let ((label2 (instruction.arg1 instruction)))
- (hashtable-put! label-table label1 label2)
- ; We can't get rid of the skip instruction
- ; because control might fall into this block,
- ; but we can get rid of the label.
- (forwards instructions (cdr filtered))))
- (else
- (forwards instructions filtered))))))))
-
- ; Dispatch procedure for the backwards traversal.
-
- (define (backwards instructions filtered)
- (if (null? instructions)
- filtered
- (let* ((instruction (car instructions))
- (instructions (cdr instructions))
- (op (instruction.op instruction))
- (flags (bytevector-ref backward-table op)))
- (cond ((eqv? flags backward:normal)
- (backwards instructions (cons instruction filtered)))
- ((eqv? flags backward:ends-block)
- (backwards0 (cons instruction instructions)
- filtered))
- ((eqv? flags backward:begins-block)
- (backwards0 instructions
- (cons instruction filtered)))
- ((eqv? flags backward:uses-many)
- (cond ((or (eqv? op $lambda)
- (eqv? op $lexes))
- (let ((live
- (if (eqv? op $lexes)
- (instruction.arg1 instruction)
- (instruction.arg2 instruction))))
- (subvector-fill! registers
- 0
- (min *nregs* (+ 1 live))
- #t)
- (backwards instructions
- (cons instruction filtered))))
- ((eqv? op $args>=)
- (vector-fill! registers #t)
- (backwards instructions
- (cons instruction filtered)))
- (else
- (local-optimization-error op))))
- ((and (eqv? (logand flags backward:kills-arg1)
- backward:kills-arg1)
- (not (vector-ref registers
- (instruction.arg1 instruction))))
- ; Suppress initialization of dead register.
- (suppress-backwards instruction
- instructions
- filtered))
- ((and (eqv? (logand flags backward:kills-arg2)
- backward:kills-arg2)
- (not (vector-ref registers
- (instruction.arg2 instruction))))
- ; Suppress initialization of dead register.
- (suppress-backwards instruction
- instructions
- filtered))
- ((and (eqv? op $movereg)
- (= (instruction.arg1 instruction)
- (instruction.arg2 instruction)))
- (backwards instructions filtered))
- (else
- (let ((filtered (cons instruction filtered)))
- (if (eqv? (logand flags backward:kills-arg1)
- backward:kills-arg1)
- (vector-set! registers
- (instruction.arg1 instruction)
- #f))
- (if (eqv? (logand flags backward:kills-arg2)
- backward:kills-arg2)
- (vector-set! registers
- (instruction.arg2 instruction)
- #f))
- (if (eqv? (logand flags backward:uses-arg1)
- backward:uses-arg1)
- (vector-set! registers
- (instruction.arg1 instruction)
- #t))
- (if (eqv? (logand flags backward:uses-arg2)
- backward:uses-arg2)
- (vector-set! registers
- (instruction.arg2 instruction)
- #t))
- (if (eqv? (logand flags backward:uses-arg3)
- backward:uses-arg3)
- (vector-set! registers
- (instruction.arg3 instruction)
- #t))
- (backwards instructions filtered)))))))
-
- ; Given a list of instructions in reverse order, whose first
- ; element is the last instruction of a basic block,
- ; and a filtered list of instructions in forward order,
- ; returns a filtered list of instructions in the correct order.
-
- (define (backwards0 instructions filtered)
- (if (null? instructions)
- filtered
- (let* ((instruction (car instructions))
- (mnemonic (instruction.op instruction)))
- (cond ((or (eqv? mnemonic $.label)
- (eqv? mnemonic $.proc)
- (eqv? mnemonic $.cont)
- (eqv? mnemonic $.align))
- (backwards0 (cdr instructions)
- (cons instruction filtered)))
- ; all registers are dead at a $return
- ((eqv? mnemonic $return)
- (vector-fill! registers #f)
- (vector-set! registers 0 #t)
- (backwards (cdr instructions)
- (cons instruction filtered)))
- ; all but the argument registers are dead at an $invoke
- ((eqv? mnemonic $invoke)
- (let ((n+1 (min *nregs*
- (+ (instruction.arg1 instruction) 1))))
- (subvector-fill! registers 0 n+1 #t)
- (subvector-fill! registers n+1 *nregs* #f)
- (backwards (cdr instructions)
- (cons instruction filtered))))
- ; the compiler says which registers are live at the
- ; target of $skip, $branch, $branchf, or $jump
- ((or (eqv? mnemonic $skip)
- (eqv? mnemonic $branch))
- (let* ((live (instruction.arg2 instruction))
- (n+1 (min *nregs* (+ live 1))))
- (subvector-fill! registers 0 n+1 #t)
- (subvector-fill! registers n+1 *nregs* #f)
- (let ((instruction
- ; FIXME
- (list mnemonic
- (lookup-label
- (instruction.arg1 instruction))
- live)))
- (backwards (cdr instructions)
- (cons instruction filtered)))))
- ((eqv? mnemonic $jump)
- (let ((n+1 (min *nregs*
- (+ (instruction.arg3 instruction) 1))))
- (subvector-fill! registers 0 n+1 #t)
- (subvector-fill! registers n+1 *nregs* #f)
- (backwards (cdr instructions)
- (cons instruction filtered))))
- ; the live registers at the target of a $branchf must be
- ; combined with the live registers at the $branchf
- ((eqv? mnemonic $branchf)
- (let* ((live (instruction.arg2 instruction))
- (n+1 (min *nregs* (+ live 1))))
- (subvector-fill! registers 0 n+1 #t)
- (let ((instruction
- ; FIXME
- (list mnemonic
- (lookup-label
- (instruction.arg1 instruction))
- live)))
- (backwards (cdr instructions)
- (cons instruction filtered)))))
- (else (backwards instructions filtered))))))
-
- (define (suppress-forwards instruction instructions filtered)
- (if (issue-warnings)
- '(begin (display suppression-message)
- (newline)))
- (forwards instructions filtered))
-
- (define (suppress-backwards instruction instructions filtered)
- (if (issue-warnings)
- '(begin (display suppression-message)
- (newline)))
- (backwards instructions filtered))
-
- (define (local-optimization-error op)
- (error "Compiler bug: local optimization" op))
-
- (vector-fill! registers #f)
- (forwards instructions '())))))
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; 28 April 1999
- ;
- ; compile313 -- compilation parameters and driver procedures.
- ; File types -- these may differ between operating systems.
- (define *scheme-file-types* '(".sch" ".scm"))
- (define *lap-file-type* ".lap")
- (define *mal-file-type* ".mal")
- (define *lop-file-type* ".lop")
- (define *fasl-file-type* ".fasl")
- ; Compile and assemble a scheme source file and produce a fastload file.
- (define (compile-file infilename . rest)
- (define (doit)
- (let ((outfilename
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type infilename
- *scheme-file-types*
- *fasl-file-type*)))
- (user
- (assembly-user-data)))
- (if (and (not (integrate-usual-procedures))
- (issue-warnings))
- (begin
- (display "WARNING from compiler: ")
- (display "integrate-usual-procedures is turned off")
- (newline)
- (display "Performance is likely to be poor.")
- (newline)))
- (if (benchmark-block-mode)
- (process-file-block infilename
- outfilename
- dump-fasl-segment-to-port
- (lambda (forms)
- (assemble (compile-block forms) user)))
- (process-file infilename
- outfilename
- dump-fasl-segment-to-port
- (lambda (expr)
- (assemble (compile expr) user))))
- (unspecified)))
- (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
- (error "Compile-file not supported on this target architecture.")
- (doit)))
- ; Assemble a MAL or LOP file and produce a FASL file.
- (define (assemble-file infilename . rest)
- (define (doit)
- (let ((outfilename
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type infilename
- (list *lap-file-type* *mal-file-type*)
- *fasl-file-type*)))
- (malfile?
- (file-type=? infilename *mal-file-type*))
- (user
- (assembly-user-data)))
- (process-file infilename
- outfilename
- dump-fasl-segment-to-port
- (lambda (x) (assemble (if malfile? (eval x) x) user)))
- (unspecified)))
-
- (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
- (error "Assemble-file not supported on this target architecture.")
- (doit)))
- ; Compile and assemble a single expression; return the LOP segment.
- (define compile-expression
- (let ()
-
- (define (compile-expression expr env)
- (let ((syntax-env
- (case (environment-tag env)
- ((0 1) (make-standard-syntactic-environment))
- ((2) global-syntactic-environment)
- (else
- (error "Invalid environment for compile-expression: " env)
- #t))))
- (let ((current-env global-syntactic-environment))
- (dynamic-wind
- (lambda ()
- (set! global-syntactic-environment syntax-env))
- (lambda ()
- (assemble (compile expr)))
- (lambda ()
- (set! global-syntactic-environment current-env))))))
-
- compile-expression))
- (define macro-expand-expression
- (let ()
-
- (define (macro-expand-expression expr env)
- (let ((syntax-env
- (case (environment-tag env)
- ((0 1) (make-standard-syntactic-environment))
- ((2) global-syntactic-environment)
- (else
- (error "Invalid environment for compile-expression: " env)
- #t))))
- (let ((current-env global-syntactic-environment))
- (dynamic-wind
- (lambda ()
- (set! global-syntactic-environment syntax-env))
- (lambda ()
- (make-readable
- (macro-expand expr)))
- (lambda ()
- (set! global-syntactic-environment current-env))))))
-
- macro-expand-expression))
- ; Compile a scheme source file to a LAP file.
- (define (compile313 infilename . rest)
- (let ((outfilename
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type infilename
- *scheme-file-types*
- *lap-file-type*)))
- (write-lap
- (lambda (item port)
- (write item port)
- (newline port)
- (newline port))))
- (if (benchmark-block-mode)
- (process-file-block infilename outfilename write-lap compile-block)
- (process-file infilename outfilename write-lap compile))
- (unspecified)))
- ; Assemble a LAP or MAL file to a LOP file.
- (define (assemble313 file . rest)
- (let ((outputfile
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type file
- (list *lap-file-type* *mal-file-type*)
- *lop-file-type*)))
- (malfile?
- (file-type=? file *mal-file-type*))
- (user
- (assembly-user-data)))
- (process-file file
- outputfile
- write-lop
- (lambda (x) (assemble (if malfile? (eval x) x) user)))
- (unspecified)))
- ; Compile and assemble a Scheme source file to a LOP file.
- (define (compile-and-assemble313 input-file . rest)
- (let ((output-file
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type input-file
- *scheme-file-types*
- *lop-file-type*)))
- (user
- (assembly-user-data)))
- (if (benchmark-block-mode)
- (process-file-block input-file
- output-file
- write-lop
- (lambda (x) (assemble (compile-block x) user)))
- (process-file input-file
- output-file
- write-lop
- (lambda (x) (assemble (compile x) user))))
- (unspecified)))
- ; Convert a LOP file to a FASL file.
- (define (make-fasl infilename . rest)
- (define (doit)
- (let ((outfilename
- (if (not (null? rest))
- (car rest)
- (rewrite-file-type infilename
- *lop-file-type*
- *fasl-file-type*))))
- (process-file infilename
- outfilename
- dump-fasl-segment-to-port
- (lambda (x) x))
- (unspecified)))
- (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
- (error "Make-fasl not supported on this target architecture.")
- (doit)))
- ; Disassemble a procedure's code vector.
- (define (disassemble item . rest)
- (let ((output-port (if (null? rest)
- (current-output-port)
- (car rest))))
- (disassemble-item item #f output-port)
- (unspecified)))
- ; The item can be either a procedure or a pair (assumed to be a segment).
- (define (disassemble-item item segment-no port)
-
- (define (print . rest)
- (for-each (lambda (x) (display x port)) rest)
- (newline port))
-
- (define (print-constvector cv)
- (do ((i 0 (+ i 1)))
- ((= i (vector-length cv)))
- (print "------------------------------------------")
- (print "Constant vector element # " i)
- (case (car (vector-ref cv i))
- ((codevector)
- (print "Code vector")
- (print-instructions (disassemble-codevector
- (cadr (vector-ref cv i)))
- port))
- ((constantvector)
- (print "Constant vector")
- (print-constvector (cadr (vector-ref cv i))))
- ((global)
- (print "Global: " (cadr (vector-ref cv i))))
- ((data)
- (print "Data: " (cadr (vector-ref cv i)))))))
-
- (define (print-segment segment)
- (print "Segment # " segment-no)
- (print-instructions (disassemble-codevector (car segment)) port)
- (print-constvector (cdr segment))
- (print "========================================"))
-
- (cond ((procedure? item)
- (print-instructions (disassemble-codevector (procedure-ref item 0))
- port))
- ((and (pair? item)
- (bytevector? (car item))
- (vector? (cdr item)))
- (print-segment item))
- (else
- (error "disassemble-item: " item " is not disassemblable."))))
- ; Disassemble a ".lop" or ".fasl" file; dump output to screen or
- ; other (optional) file.
- (define (disassemble-file file . rest)
-
- (define (doit input-port output-port)
- (display "; From " output-port)
- (display file output-port)
- (newline output-port)
- (do ((segment-no 0 (+ segment-no 1))
- (segment (read input-port) (read input-port)))
- ((eof-object? segment))
- (disassemble-item segment segment-no output-port)))
- ; disassemble313
- (call-with-input-file
- file
- (lambda (input-port)
- (if (null? rest)
- (doit input-port (current-output-port))
- (begin
- (delete-file (car rest))
- (call-with-output-file
- (car rest)
- (lambda (output-port) (doit input-port output-port)))))))
- (unspecified))
- ; Display and manipulate the compiler switches.
- (define (compiler-switches . rest)
- (define (slow-code)
- (set-compiler-flags! 'no-optimization)
- (set-assembler-flags! 'no-optimization))
- (define (standard-code)
- (set-compiler-flags! 'standard)
- (set-assembler-flags! 'standard))
- (define (fast-safe-code)
- (set-compiler-flags! 'fast-safe)
- (set-assembler-flags! 'fast-safe))
- (define (fast-unsafe-code)
- (set-compiler-flags! 'fast-unsafe)
- (set-assembler-flags! 'fast-unsafe))
- (cond ((null? rest)
- (display "Debugging:")
- (newline)
- (display-twobit-flags 'debugging)
- (display-assembler-flags 'debugging)
- (newline)
- (display "Safety:")
- (newline)
- (display-twobit-flags 'safety)
- (display-assembler-flags 'safety)
- (newline)
- (display "Speed:")
- (newline)
- (display-twobit-flags 'optimization)
- (display-assembler-flags 'optimization)
- (if #f #f))
- ((null? (cdr rest))
- (case (car rest)
- ((0 slow) (slow-code))
- ((1 standard) (standard-code))
- ((2 fast-safe) (fast-safe-code))
- ((3 fast-unsafe) (fast-unsafe-code))
- ((default
- factory-settings) (fast-safe-code)
- (include-source-code #t)
- (benchmark-mode #f)
- (benchmark-block-mode #f)
- (common-subexpression-elimination #f)
- (representation-inference #f))
- (else
- (error "Unrecognized flag " (car rest) " to compiler-switches.")))
- (unspecified))
- (else
- (error "Too many arguments to compiler-switches."))))
- ; Read and process one file, producing another.
- ; Preserves the global syntactic environment.
- (define (process-file infilename outfilename writer processer)
- (define (doit)
- (delete-file outfilename)
- (call-with-output-file
- outfilename
- (lambda (outport)
- (call-with-input-file
- infilename
- (lambda (inport)
- (let loop ((x (read inport)))
- (if (eof-object? x)
- #t
- (begin (writer (processer x) outport)
- (loop (read inport))))))))))
- (let ((current-syntactic-environment
- (syntactic-copy global-syntactic-environment)))
- (dynamic-wind
- (lambda () #t)
- (lambda () (doit))
- (lambda ()
- (set! global-syntactic-environment
- current-syntactic-environment)))))
- ; Same as above, but passes a list of the entire file's contents
- ; to the processer.
- ; FIXME: Both versions of PROCESS-FILE always delete the output file.
- ; Shouldn't it be left alone if the input file can't be opened?
- (define (process-file-block infilename outfilename writer processer)
- (define (doit)
- (delete-file outfilename)
- (call-with-output-file
- outfilename
- (lambda (outport)
- (call-with-input-file
- infilename
- (lambda (inport)
- (do ((x (read inport) (read inport))
- (forms '() (cons x forms)))
- ((eof-object? x)
- (writer (processer (reverse forms)) outport))))))))
- (let ((current-syntactic-environment
- (syntactic-copy global-syntactic-environment)))
- (dynamic-wind
- (lambda () #t)
- (lambda () (doit))
- (lambda ()
- (set! global-syntactic-environment
- current-syntactic-environment)))))
- ; Given a file name with some type, produce another with some other type.
- (define (rewrite-file-type filename matches new)
- (if (not (pair? matches))
- (rewrite-file-type filename (list matches) new)
- (let ((j (string-length filename)))
- (let loop ((m matches))
- (cond ((null? m)
- (string-append filename new))
- (else
- (let* ((n (car m))
- (l (string-length n)))
- (if (file-type=? filename n)
- (string-append (substring filename 0 (- j l)) new)
- (loop (cdr m))))))))))
- (define (file-type=? file-name type-name)
- (let ((fl (string-length file-name))
- (tl (string-length type-name)))
- (and (>= fl tl)
- (string-ci=? type-name
- (substring file-name (- fl tl) fl)))))
- ; eof
- ; Copyright 1998 William Clinger.
- ;
- ; $Id: twobit-smaller.sch,v 1.1 1999/09/09 22:13:42 lth Exp $
- ;
- ; Procedures that make .LAP structures human-readable
- (define (readify-lap code)
- (map (lambda (x)
- (let ((iname (cdr (assv (car x) *mnemonic-names*))))
- (if (not (= (car x) $lambda))
- (cons iname (cdr x))
- (list iname (readify-lap (cadr x)) (caddr x)))))
- code))
- (define (readify-file f . o)
- (define (doit)
- (let ((i (open-input-file f)))
- (let loop ((x (read i)))
- (if (not (eof-object? x))
- (begin (pretty-print (readify-lap x))
- (loop (read i)))))))
- (if (null? o)
- (doit)
- (begin (delete-file (car o))
- (with-output-to-file (car o) doit))))
- ; eof
- ; ----------------------------------------------------------------------
- (define (twobit-benchmark . rest)
- (let ((k (if (null? rest) 1 (car rest))))
- (compiler-switches 'fast-safe)
- (benchmark-block-mode #t)
- (run-benchmark
- "twobit"
- k
- (lambda () (compile-file "twobit-input.sch"))
- (lambda (result)
- #t))))
- ; eof
|