123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799 |
- ; Complete source for Twobit and Sparc assembler in one file.
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; See 'twobit-benchmark', at end.
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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.sch,v 1.3 1999/08/23 19:14:26 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
- ; Copyright 1991 Lightship Software, Incorporated.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Target-independent part of the assembler.
- ;
- ; This is a simple, table-driven, one-pass assembler.
- ; Part of it assumes a big-endian target machine.
- ;
- ; The input to this pass is a list of symbolic MacScheme machine
- ; instructions and pseudo-instructions. Each symbolic MacScheme
- ; machine instruction or pseudo-instruction is a list whose car
- ; is a small non-negative fixnum that acts as the mnemonic for the
- ; instruction. The rest of the list is interpreted as indicated
- ; by the mnemonic.
- ;
- ; The output is a pair consisting of machine code (a bytevector or
- ; string) and a constant vector.
- ;
- ; This assembler is table-driven, and may be customized to emit
- ; machine code for different target machines. The table consists
- ; of a vector of procedures indexed by mnemonics. Each procedure
- ; in the table should take two arguments: an assembly structure
- ; and a source instruction. The procedure should just assemble
- ; the instruction using the operations defined below.
- ;
- ; The table and target can be changed by redefining the following
- ; five procedures.
- (define (assembly-table) (error "No assembly table defined."))
- (define (assembly-start as) #t)
- (define (assembly-end as segment) segment)
- (define (assembly-user-data) #f)
- ; The main entry point.
- (define (assemble source . rest)
- (let* ((user (if (null? rest) (assembly-user-data) (car rest)))
- (as (make-assembly-structure source (assembly-table) user)))
- (assembly-start as)
- (assemble1 as
- (lambda (as)
- (let ((segment (assemble-pasteup as)))
- (assemble-finalize! as)
- (assembly-end as segment)))
- #f)))
- ; The following procedures are to be called by table routines.
- ;
- ; The assembly source for nested lambda expressions should be
- ; assembled by calling this procedure. This allows an inner
- ; lambda to refer to labels defined by outer lambdas.
- ;
- ; We delay the assembly of the nested lambda until after the outer lambda
- ; has been finalized so that all labels in the outer lambda are known
- ; to the inner lambda.
- ;
- ; The continuation procedure k is called to backpatch the constant
- ; vector of the outer lambda after the inner lambda has been
- ; finalized. This is necessary because of the delayed evaluation: the
- ; outer lambda holds code and constants for the inner lambda in its
- ; constant vector.
- (define (assemble-nested-lambda as source doc k . rest)
- (let* ((user (if (null? rest) #f (car rest)))
- (nested-as (make-assembly-structure source (as-table as) user)))
- (as-parent! nested-as as)
- (as-nested! as (cons (lambda ()
- (assemble1 nested-as
- (lambda (nested-as)
- (let ((segment
- (assemble-pasteup nested-as)))
- (assemble-finalize! nested-as)
- (k nested-as segment)))
- doc))
- (as-nested as)))))
- (define operand0 car) ; the mnemonic
- (define operand1 cadr)
- (define operand2 caddr)
- (define operand3 cadddr)
- (define (operand4 i) (car (cddddr i)))
- ; Emits the bits contained in the bytevector bv.
- (define (emit! as bv)
- (as-code! as (cons bv (as-code as)))
- (as-lc! as (+ (as-lc as) (bytevector-length bv))))
- ; Emits the characters contained in the string s as code (for C generation).
- (define (emit-string! as s)
- (as-code! as (cons s (as-code as)))
- (as-lc! as (+ (as-lc as) (string-length s))))
- ; Given any Scheme object that may legally be quoted, returns an
- ; index into the constant vector for that constant.
- (define (emit-constant as x)
- (do ((i 0 (+ i 1))
- (y (as-constants as) (cdr y)))
- ((or (null? y) (equal? x (car y)))
- (if (null? y)
- (as-constants! as (append! (as-constants as) (list x))))
- i)))
- (define (emit-datum as x)
- (emit-constant as (list 'data x)))
- (define (emit-global as x)
- (emit-constant as (list 'global x)))
- (define (emit-codevector as x)
- (emit-constants as (list 'codevector x)))
- (define (emit-constantvector as x)
- (emit-constants as (list 'constantvector x)))
- ; Set-constant changes the datum stored, without affecting the tag.
- ; It can operate on the list form because the pair stored in the list
- ; is shared between the list and any vector created from the list.
- (define (set-constant! as n datum)
- (let ((pair (list-ref (as-constants as) n)))
- (set-car! (cdr pair) datum)))
- ; Guarantees that the constants will not share structure
- ; with any others, and will occupy consecutive positions
- ; in the constant vector. Returns the index of the first
- ; constant.
- (define (emit-constants as x . rest)
- (let* ((constants (as-constants as))
- (i (length constants)))
- (as-constants! as (append! constants (cons x rest)))
- i))
- ; Defines the given label using the current location counter.
- (define (emit-label! as L)
- (set-cdr! L (as-lc as)))
- ; Adds the integer n to the size code bytes beginning at the
- ; given byte offset from the current value of the location counter.
- (define (emit-fixup! as offset size n)
- (as-fixups! as (cons (list (+ offset (as-lc as)) size n)
- (as-fixups as))))
- ; Adds the value of the label L to the size code bytes beginning
- ; at the given byte offset from the current location counter.
- (define (emit-fixup-label! as offset size L)
- (as-fixups! as (cons (list (+ offset (as-lc as)) size (list L))
- (as-fixups as))))
- ; Allows the procedure proc of two arguments (code vector and current
- ; location counter) to modify the code vector at will, at fixup time.
- (define (emit-fixup-proc! as proc)
- (as-fixups! as (cons (list (as-lc as) 0 proc)
- (as-fixups as))))
- ; Labels.
- ; The current value of the location counter.
- (define (here as) (as-lc as))
- ; Given a MAL label (a number), create an assembler label.
- (define (make-asm-label as label)
- (let ((probe (find-label as label)))
- (if probe
- probe
- (let ((l (cons label #f)))
- (as-labels! as (cons l (as-labels as)))
- l))))
- ; This can use hashed lookup.
- (define (find-label as L)
- (define (lookup-label-loop x labels parent)
- (let ((entry (assq x labels)))
- (cond (entry)
- ((not parent) #f)
- (else
- (lookup-label-loop x (as-labels parent) (as-parent parent))))))
-
- (lookup-label-loop L (as-labels as) (as-parent as)))
- ; Create a new assembler label, distinguishable from a MAL label.
- (define new-label
- (let ((n 0))
- (lambda ()
- (set! n (- n 1))
- (cons n #f))))
- ; Given a value name (a number), return the label value or #f.
- (define (label-value as L) (cdr L))
- ; For peephole optimization.
- (define (next-instruction as)
- (let ((source (as-source as)))
- (if (null? source)
- '(-1)
- (car source))))
- (define (consume-next-instruction! as)
- (as-source! as (cdr (as-source as))))
- (define (push-instruction as instruction)
- (as-source! as (cons instruction (as-source as))))
- ; For use by the machine assembler: assoc lists connected to as structure.
- (define (assembler-value as key)
- (let ((probe (assq key (as-values as))))
- (if probe
- (cdr probe)
- #f)))
- (define (assembler-value! as key value)
- (let ((probe (assq key (as-values as))))
- (if probe
- (set-cdr! probe value)
- (as-values! as (cons (cons key value) (as-values as))))))
- ; For documentation.
- ;
- ; The value must be a documentation structure (a vector).
- (define (add-documentation as doc)
- (let* ((existing-constants (cadr (car (as-constants as))))
- (new-constants
- (twobit-sort (lambda (a b)
- (< (car a) (car b)))
- (cond ((not existing-constants)
- (list (cons (here as) doc)))
- ((pair? existing-constants)
- (cons (cons (here as) doc)
- existing-constants))
- (else
- (list (cons (here as) doc)
- (cons 0 existing-constants)))))))
- (set-car! (cdar (as-constants as)) new-constants)))
- ; This is called when a value is too large to be handled by the assembler.
- ; Info is a string, expr an assembler expression, and val the resulting
- ; value. The default behavior is to signal an error.
- (define (asm-value-too-large as info expr val)
- (if (as-retry as)
- ((as-retry as))
- (asm-error info ": Value too large: " expr " = " val)))
- ; The implementations of asm-error and disasm-error depend on the host
- ; system. Sigh.
- (define (asm-error msg . rest)
- (cond ((eq? host-system 'chez)
- (error 'assembler "~a" (list msg rest)))
- (else
- (apply error msg rest))))
- (define (disasm-error msg . rest)
- (cond ((eq? host-system 'chez)
- (error 'disassembler "~a" (list msg rest)))
- (else
- (apply error msg rest))))
- ; The remaining procedures in this file are local to the assembler.
- ; An assembly structure is a vector consisting of
- ;
- ; table (a table of assembly routines)
- ; source (a list of symbolic instructions)
- ; lc (location counter; an integer)
- ; code (a list of bytevectors)
- ; constants (a list)
- ; labels (an alist of labels and values)
- ; fixups (an alist of locations, sizes, and labels or fixnums)
- ; nested (a list of assembly procedures for nested lambdas)
- ; values (an assoc list)
- ; parent (an assembly structure or #f)
- ; retry (a thunk or #f)
- ; user-data (anything)
- ;
- ; In fixups, labels are of the form (<L>) to distinguish them from fixnums.
- (define (label? x) (and (pair? x) (fixnum? (car x))))
- (define label.ident car)
- (define (make-assembly-structure source table user-data)
- (vector table
- source
- 0
- '()
- '()
- '()
- '()
- '()
- '()
- #f
- #f
- user-data))
- (define (as-reset! as source)
- (as-source! as source)
- (as-lc! as 0)
- (as-code! as '())
- (as-constants! as '())
- (as-labels! as '())
- (as-fixups! as '())
- (as-nested! as '())
- (as-values! as '())
- (as-retry! as #f))
- (define (as-table as) (vector-ref as 0))
- (define (as-source as) (vector-ref as 1))
- (define (as-lc as) (vector-ref as 2))
- (define (as-code as) (vector-ref as 3))
- (define (as-constants as) (vector-ref as 4))
- (define (as-labels as) (vector-ref as 5))
- (define (as-fixups as) (vector-ref as 6))
- (define (as-nested as) (vector-ref as 7))
- (define (as-values as) (vector-ref as 8))
- (define (as-parent as) (vector-ref as 9))
- (define (as-retry as) (vector-ref as 10))
- (define (as-user as) (vector-ref as 11))
- (define (as-source! as x) (vector-set! as 1 x))
- (define (as-lc! as x) (vector-set! as 2 x))
- (define (as-code! as x) (vector-set! as 3 x))
- (define (as-constants! as x) (vector-set! as 4 x))
- (define (as-labels! as x) (vector-set! as 5 x))
- (define (as-fixups! as x) (vector-set! as 6 x))
- (define (as-nested! as x) (vector-set! as 7 x))
- (define (as-values! as x) (vector-set! as 8 x))
- (define (as-parent! as x) (vector-set! as 9 x))
- (define (as-retry! as x) (vector-set! as 10 x))
- (define (as-user! as x) (vector-set! as 11 x))
- ; The guts of the assembler.
- (define (assemble1 as finalize doc)
- (let ((assembly-table (as-table as))
- (peep? (peephole-optimization))
- (step? (single-stepping))
- (step-instr (list $.singlestep))
- (end-instr (list $.end)))
- (define (loop)
- (let ((source (as-source as)))
- (if (null? source)
- (begin ((vector-ref assembly-table $.end) end-instr as)
- (finalize as))
- (begin (if step?
- ((vector-ref assembly-table $.singlestep)
- step-instr
- as))
- (if peep?
- (let peeploop ((src1 source))
- (peep as)
- (let ((src2 (as-source as)))
- (if (not (eq? src1 src2))
- (peeploop src2)))))
- (let ((source (as-source as)))
- (as-source! as (cdr source))
- ((vector-ref assembly-table (caar source))
- (car source)
- as)
- (loop))))))
- (define (doit)
- (emit-datum as doc)
- (loop))
- (let* ((source (as-source as))
- (r (call-with-current-continuation
- (lambda (k)
- (as-retry! as (lambda () (k 'retry)))
- (doit)))))
- (if (eq? r 'retry)
- (let ((old (short-effective-addresses)))
- (as-reset! as source)
- (dynamic-wind
- (lambda ()
- (short-effective-addresses #f))
- doit
- (lambda ()
- (short-effective-addresses old))))
- r))))
- (define (assemble-pasteup as)
- (define (pasteup-code)
- (let ((code (make-bytevector (as-lc as)))
- (constants (list->vector (as-constants as))))
-
- ; The bytevectors: byte 0 is most significant.
- (define (paste-code! bvs i)
- (if (not (null? bvs))
- (let* ((bv (car bvs))
- (n (bytevector-length bv)))
- (do ((i i (- i 1))
- (j (- n 1) (- j 1))) ; (j 0 (+ j 1))
- ((< j 0) ; (= j n)
- (paste-code! (cdr bvs) i))
- (bytevector-set! code i (bytevector-ref bv j))))))
-
- (paste-code! (as-code as) (- (as-lc as) 1))
- (as-code! as (list code))
- (cons code constants)))
- (define (pasteup-strings)
- (let ((code (make-string (as-lc as)))
- (constants (list->vector (as-constants as))))
- (define (paste-code! strs i)
- (if (not (null? strs))
- (let* ((s (car strs))
- (n (string-length s)))
- (do ((i i (- i 1))
- (j (- n 1) (- j 1))) ; (j 0 (+ j 1))
- ((< j 0) ; (= j n)
- (paste-code! (cdr strs) i))
- (string-set! code i (string-ref s j))))))
- (paste-code! (as-code as) (- (as-lc as) 1))
- (as-code! as (list code))
- (cons code constants)))
- (if (bytevector? (car (as-code as)))
- (pasteup-code)
- (pasteup-strings)))
- (define (assemble-finalize! as)
- (let ((code (car (as-code as))))
- (define (apply-fixups! fixups)
- (if (not (null? fixups))
- (let* ((fixup (car fixups))
- (i (car fixup))
- (size (cadr fixup))
- (adjustment (caddr fixup)) ; may be procedure
- (n (if (label? adjustment)
- (lookup-label adjustment)
- adjustment)))
- (case size
- ((0) (fixup-proc code i n))
- ((1) (fixup1 code i n))
- ((2) (fixup2 code i n))
- ((3) (fixup3 code i n))
- ((4) (fixup4 code i n))
- (else ???))
- (apply-fixups! (cdr fixups)))))
- (define (lookup-label L)
- (or (label-value as (label.ident L))
- (asm-error "Assembler error -- undefined label " L)))
- (apply-fixups! (reverse! (as-fixups as)))
- (for-each (lambda (nested-as-proc)
- (nested-as-proc))
- (as-nested as))))
- ; These fixup routines assume a big-endian target machine.
- (define (fixup1 code i n)
- (bytevector-set! code i (+ n (bytevector-ref code i))))
- (define (fixup2 code i n)
- (let* ((x (+ (* 256 (bytevector-ref code i))
- (bytevector-ref code (+ i 1))))
- (y (+ x n))
- (y0 (modulo y 256))
- (y1 (modulo (quotient (- y y0) 256) 256)))
- (bytevector-set! code i y1)
- (bytevector-set! code (+ i 1) y0)))
- (define (fixup3 code i n)
- (let* ((x (+ (* 65536 (bytevector-ref code i))
- (* 256 (bytevector-ref code (+ i 1)))
- (bytevector-ref code (+ i 2))))
- (y (+ x n))
- (y0 (modulo y 256))
- (y1 (modulo (quotient (- y y0) 256) 256))
- (y2 (modulo (quotient (- y (* 256 y1) y0) 256) 256)))
- (bytevector-set! code i y2)
- (bytevector-set! code (+ i 1) y1)
- (bytevector-set! code (+ i 2) y0)))
- (define (fixup4 code i n)
- (let* ((x (+ (* 16777216 (bytevector-ref code i))
- (* 65536 (bytevector-ref code (+ i 1)))
- (* 256 (bytevector-ref code (+ i 2)))
- (bytevector-ref code (+ i 3))))
- (y (+ x n))
- (y0 (modulo y 256))
- (y1 (modulo (quotient (- y y0) 256) 256))
- (y2 (modulo (quotient (- y (* 256 y1) y0) 256) 256))
- (y3 (modulo (quotient (- y (* 65536 y2)
- (* 256 y1)
- y0)
- 256)
- 256)))
- (bytevector-set! code i y3)
- (bytevector-set! code (+ i 1) y2)
- (bytevector-set! code (+ i 2) y1)
- (bytevector-set! code (+ i 3) y0)))
- (define (fixup-proc code i p)
- (p code i))
- ; For testing.
- (define (view-segment segment)
- (define (display-bytevector bv)
- (let ((n (bytevector-length bv)))
- (do ((i 0 (+ i 1)))
- ((= i n))
- (if (zero? (remainder i 4))
- (write-char #\space))
- (if (zero? (remainder i 8))
- (write-char #\space))
- (if (zero? (remainder i 32))
- (newline))
- (let ((byte (bytevector-ref bv i)))
- (write-char
- (string-ref (number->string (quotient byte 16) 16) 0))
- (write-char
- (string-ref (number->string (remainder byte 16) 16) 0))))))
- (if (and (pair? segment)
- (bytevector? (car segment))
- (vector? (cdr segment)))
- (begin (display-bytevector (car segment))
- (newline)
- (write (cdr segment))
- (newline)
- (do ((constants (vector->list (cdr segment))
- (cdr constants)))
- ((or (null? constants)
- (null? (cdr constants))))
- (if (and (bytevector? (car constants))
- (vector? (cadr constants)))
- (view-segment (cons (car constants)
- (cadr constants))))))))
- ; emit is a procedure that takes an as and emits instructions into it.
- (define (test-asm emit)
- (let ((as (make-assembly-structure #f #f #f)))
- (emit as)
- (let ((segment (assemble-pasteup as)))
- (assemble-finalize! as)
- (disassemble segment))))
- (define (compile&assemble x)
- (view-segment (assemble (compile x))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Common assembler -- miscellaneous utility procedures.
- ; Given any Scheme object, return its printable representation as a string.
- ; This code is largely portable (see comments).
- (define (format-object x)
- (define (format-list x)
- (define (loop x)
- (cond ((null? x)
- '(")"))
- ((null? (cdr x))
- (list (format-object (car x)) ")"))
- (else
- (cons (format-object (car x))
- (cons " "
- (loop (cdr x)))))))
- (apply string-append (cons "(" (loop x))))
- (define (format-improper-list x)
- (define (loop x)
- (if (pair? (cdr x))
- (cons (format-object (car x))
- (cons " "
- (loop (cdr x))))
- (list (format-object (car x))
- " . "
- (format-object (cdr x))
- ")")))
- (apply string-append (cons "(" (loop x))))
- (cond ((null? x) "()")
- ((not x) "#f")
- ((eq? x #t) "#t")
- ((symbol? x) (symbol->string x))
- ((number? x) (number->string x))
- ((char? x) (string x))
- ((string? x) x)
- ((procedure? x) "#<procedure>")
- ((bytevector? x) "#<bytevector>") ; Larceny
- ((eof-object? x) "#<eof>")
- ((port? x) "#<port>")
- ((eq? x (unspecified)) "#!unspecified") ; Larceny
- ((eq? x (undefined)) "#!undefined") ; Larceny
- ((vector? x)
- (string-append "#" (format-list (vector->list x))))
- ((list? x)
- (format-list x))
- ((pair? x)
- (format-improper-list x))
- (else "#<weird>")))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Larceny assembler -- 32-bit big-endian utility procedures.
- ;
- ; 32-bit numbers are represented as 4-byte bytevectors where byte 3
- ; is the least significant and byte 0 is the most significant.
- ;
- ; Logically, the 'big' end is on the left and the 'little' end
- ; is on the right, so a left shift shifts towards the 'big' end.
- ;
- ; Performance: poor, for good reasons. See asmutil32.sch.
- ; Identifies the code loaded.
- (define asm:endianness 'big)
- ; Given four bytes, create a length-4 bytevector.
- ; N1 is the most significant byte, n4 the least significant.
- (define (asm:bv n1 n2 n3 n4)
- (let ((bv (make-bytevector 4)))
- (bytevector-set! bv 0 n1)
- (bytevector-set! bv 1 n2)
- (bytevector-set! bv 2 n3)
- (bytevector-set! bv 3 n4)
- bv))
- ; Given a length-4 bytevector, convert it to an integer.
- (define (asm:bv->int bv)
- (let ((i (+ (* (+ (* (+ (* (bytevector-ref bv 0) 256)
- (bytevector-ref bv 1))
- 256)
- (bytevector-ref bv 2))
- 256)
- (bytevector-ref bv 3))))
- (if (> (bytevector-ref bv 0) 127)
- (- i)
- i)))
- ; Shift the bits of m left by n bits, shifting in zeroes at the right end.
- ; Returns a length-4 bytevector.
- ;
- ; M may be an exact integer or a length-4 bytevector.
- ; N must be an exact nonnegative integer; it's interpreted modulo 33.
- (define (asm:lsh m n)
- (if (not (bytevector? m))
- (asm:lsh (asm:int->bv m) n)
- (let ((m (bytevector-copy m))
- (n (remainder n 33)))
- (if (>= n 8)
- (let ((k (quotient n 8)))
- (do ((i 0 (+ i 1)))
- ((= (+ i k) 4)
- (do ((i i (+ i 1)))
- ((= i 4))
- (bytevector-set! m i 0)))
- (bytevector-set! m i (bytevector-ref m (+ i k))))))
- (let* ((d0 (bytevector-ref m 0))
- (d1 (bytevector-ref m 1))
- (d2 (bytevector-ref m 2))
- (d3 (bytevector-ref m 3))
- (n (remainder n 8))
- (n- (- 8 n)))
- (asm:bv (logand (logior (lsh d0 n) (rshl d1 n-)) 255)
- (logand (logior (lsh d1 n) (rshl d2 n-)) 255)
- (logand (logior (lsh d2 n) (rshl d3 n-)) 255)
- (logand (lsh d3 n) 255))))))
- ; Shift the bits of m right by n bits, shifting in zeroes at the high end.
- ; Returns a length-4 bytevector.
- ;
- ; M may be an exact integer or a length-4 bytevector.
- ; N must be an exact nonnegative integer; it's interpreted modulo 33.
- (define (asm:rshl m n)
- (if (not (bytevector? m))
- (asm:rshl (asm:int->bv m) n)
- (let ((m (bytevector-copy m))
- (n (remainder n 33)))
- (if (>= n 8)
- (let ((k (quotient n 8)))
- (do ((i 3 (- i 1)))
- ((< (- i k) 0)
- (do ((i i (- i 1)))
- ((< i 0))
- (bytevector-set! m i 0)))
- (bytevector-set! m i (bytevector-ref m (- i k))))))
- (let* ((d0 (bytevector-ref m 0))
- (d1 (bytevector-ref m 1))
- (d2 (bytevector-ref m 2))
- (d3 (bytevector-ref m 3))
- (n (remainder n 8))
- (n- (- 8 n)))
- (asm:bv (rshl d0 n)
- (logand (logior (rshl d1 n) (lsh d0 n-)) 255)
- (logand (logior (rshl d2 n) (lsh d1 n-)) 255)
- (logand (logior (rshl d3 n) (lsh d2 n-)) 255))))))
- ; Shift the bits of m right by n bits, shifting in the sign bit at the
- ; high end. Returns a length-4 bytevector.
- ;
- ; M may be an exact integer or a length-4 bytevector.
- ; N must be an exact nonnegative integer; it's interpreted modulo 33.
- (define asm:rsha
- (let ((ones (asm:bv #xff #xff #xff #xff)))
- (lambda (m n)
- (let* ((m (if (bytevector? m) m (asm:int->bv m)))
- (n (remainder n 33))
- (h (rshl (bytevector-ref m 0) 7))
- (k (asm:rshl m n)))
- ; (format #t "~a ~a ~a~%" h (bytevector-ref m 0) n)
- ; (prnx (asm:lsh ones (- 32 n))) (newline)
- (if (zero? h)
- k
- (asm:logior k (asm:lsh ones (- 32 n))))))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Larceny assembler -- 32-bit endianness-independent utility procedures.
- ;
- ; 32-bit numbers are represented as 4-byte bytevectors where the
- ; exact layout depends on whether the little-endian or big-endian
- ; module has been loaded. One of them must be loaded prior to loading
- ; this module.
- ;
- ; Logically, the 'big' end is on the left and the 'little' end
- ; is on the right, so a left shift shifts towards the big end.
- ;
- ; Generally, performance is not a major issue in this module. The
- ; assemblers should use more specialized code for truly good performance.
- ; These procedures are mainly suitable for one-time construction of
- ; instruction templates, and during development.
- ;
- ; Endian-ness specific operations are in asmutil32be.sch and asmutil32le.sch:
- ;
- ; (asm:bv n0 n1 n2 n3) ; Construct bytevector
- ; (asm:bv->int bv) ; Convert bytevector to integer
- ; (asm:lsh m k) ; Shift left logical k bits
- ; (asm:rshl m k) ; Shift right logical k bits
- ; (asm:rsha m k) ; Shirt right arithmetic k bits
- ; Convert an integer to a length-4 bytevector using two's complement
- ; representation for negative numbers.
- ; Returns length-4 bytevector.
- ;
- ; The procedure handles numbers in the range -2^31..2^32-1 [sic].
- ; It is an error for the number to be outside this range.
- ;
- ; FIXME: quotient/remainder may be slow; we could have special fixnum
- ; case that uses shifts (that could be in-lined as macro). It could
- ; work for negative numbers too.
- ; FIXME: should probably check that the number is within range.
- (define asm:int->bv
- (let ((two^32 (expt 2 32)))
- (lambda (m)
- (let* ((m (if (< m 0) (+ two^32 m) m))
- (b0 (remainder m 256))
- (m (quotient m 256))
- (b1 (remainder m 256))
- (m (quotient m 256))
- (b2 (remainder m 256))
- (m (quotient m 256))
- (b3 (remainder m 256)))
- (asm:bv b3 b2 b1 b0)))))
- ; `Or' the bits of multiple operands together.
- ; Each operand may be an exact integer or a length-4 bytevector.
- ; Returns a length-4 bytevector.
- (define (asm:logior . ops)
- (let ((r (asm:bv 0 0 0 0)))
- (do ((ops ops (cdr ops)))
- ((null? ops) r)
- (let* ((op (car ops))
- (op (if (bytevector? op) op (asm:int->bv op))))
- (bytevector-set! r 0 (logior (bytevector-ref r 0)
- (bytevector-ref op 0)))
- (bytevector-set! r 1 (logior (bytevector-ref r 1)
- (bytevector-ref op 1)))
- (bytevector-set! r 2 (logior (bytevector-ref r 2)
- (bytevector-ref op 2)))
- (bytevector-set! r 3 (logior (bytevector-ref r 3)
- (bytevector-ref op 3)))))))
- ; `And' the bits of two operands together.
- ; Either may be an exact integer or length-4 bytevector.
- ; Returns length-4 bytevector.
- (define (asm:logand op1 op2)
- (let ((op1 (if (bytevector? op1) op1 (asm:int->bv op1)))
- (op2 (if (bytevector? op2) op2 (asm:int->bv op2)))
- (bv (make-bytevector 4)))
- (bytevector-set! bv 0 (logand (bytevector-ref op1 0)
- (bytevector-ref op2 0)))
- (bytevector-set! bv 1 (logand (bytevector-ref op1 1)
- (bytevector-ref op2 1)))
- (bytevector-set! bv 2 (logand (bytevector-ref op1 2)
- (bytevector-ref op2 2)))
- (bytevector-set! bv 3 (logand (bytevector-ref op1 3)
- (bytevector-ref op2 3)))
- bv))
- ; Extract the n low-order bits of m.
- ; m may be an exact integer or a length-4 bytevector.
- ; n must be an exact nonnegative integer, interpreted modulo 32.
- ; Returns length-4 bytevector.
- ;
- ; Does not depend on endian-ness.
- (define asm:lobits
- (let ((v (make-vector 33)))
- (do ((i 0 (+ i 1)))
- ((= i 33))
- (vector-set! v i (asm:int->bv (- (expt 2 i) 1))))
- (lambda (m n)
- (asm:logand m (vector-ref v (remainder n 33))))))
- ; Extract the n high-order bits of m.
- ; m may be an exact integer or a length-4 bytevector.
- ; n must be an exact nonnegative integer, interpreted modulo 33.
- ; Returns length-4 bytevector with the high-order bits of m at low end.
- ;
- ; Does not depend on endian-ness.
- (define (asm:hibits m n)
- (asm:rshl m (- 32 (remainder n 33))))
- ; Test that the given number (not! bytevector) m fits in an n-bit
- ; signed slot.
- ;
- ; Does not depend on endian-ness.
- (define asm:fits?
- (let ((v (make-vector 33)))
- (do ((i 0 (+ i 1)))
- ((= i 33))
- (vector-set! v i (expt 2 i)))
- (lambda (m n)
- (<= (- (vector-ref v (- n 1))) m (- (vector-ref v (- n 1)) 1)))))
- ; Test that the given number (not! bytevector) m fits in an n-bit
- ; unsigned slot.
- ;
- ; Does not depend on endian-ness.
- (define asm:fits-unsigned?
- (let ((v (make-vector 33)))
- (do ((i 0 (+ i 1)))
- ((= i 33))
- (vector-set! v i (expt 2 i)))
- (lambda (m n)
- (<= 0 m (- (vector-ref v n) 1)))))
- ; Add two operands (numbers or bytevectors).
- ;
- ; Does not depend on endian-ness.
- (define (asm:add a b)
- (asm:int->bv (+ (if (bytevector? a) (asm:bv->int a) a)
- (if (bytevector? b) (asm:bv->int b) b))))
- ; Given an unsigned 32-bit number, return it as a signed number
- ; as appropriate.
- ;
- ; Does not depend on endian-ness.
- (define (asm:signed n)
- (if (< n 2147483647)
- n
- (- n 4294967296)))
- (define (asm:print-bv bv)
- (define hex "0123456789abcdef")
- (define (pdig k)
- (display (string-ref hex (quotient k 16)))
- (display (string-ref hex (remainder k 16)))
- (display " "))
-
- (if (eq? asm:endianness 'little)
- (do ((i 3 (- i 1)))
- ((< i 0))
- (pdig (bytevector-ref bv i)))
- (do ((i 0 (+ i 1)))
- ((= i 4))
- (pdig (bytevector-ref bv i)))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Procedure that writes fastload segment.
- ;
- ; The procedure 'dump-fasl-segment-to-port' takes a segment and an output
- ; port as arguments and dumps the segment in fastload format on that port.
- ; The port must be a binary (untranslated) port.
- ;
- ; A fastload segment looks like a Scheme expression, and in fact,
- ; fastload files can mix compiled and uncompiled expressions. A compiled
- ; expression (as created by dump-fasl-segment-to-port) is a list with
- ; a literal procedure in the operator position and no arguments.
- ;
- ; A literal procedure is a three-element list prefixed by #^P. The three
- ; elements are code (a bytevector), constants (a regular vector), and
- ; R0/static link slot (always #f).
- ;
- ; A bytevector is a string prefixed by #^B. The string may contain
- ; control characters; \ and " must be quoted as usual.
- ;
- ; A global variable reference in the constant vector is a symbol prefixed
- ; by #^G. On reading, the reference is replaced by (a pointer to) the
- ; actual cell.
- ;
- ; This code is highly bummed. The procedure write-bytevector-like has the
- ; same meaning as display, but in Larceny, the former is currently much
- ; faster than the latter.
- (define (dump-fasl-segment-to-port segment outp . rest)
- (let* ((omit-code? (not (null? rest)))
- (controllify
- (lambda (char)
- (integer->char (- (char->integer char) (char->integer #\@)))))
- (CTRLP (controllify #\P))
- (CTRLB (controllify #\B))
- (CTRLG (controllify #\G))
- (DOUBLEQUOTE (char->integer #\"))
- (BACKSLASH (char->integer #\\))
- (len 1024))
- (define buffer (make-string len #\&))
- (define ptr 0)
- (define (flush)
- (if (< ptr len)
- (write-bytevector-like (substring buffer 0 ptr) outp)
- (write-bytevector-like buffer outp))
- (set! ptr 0))
- (define (putc c)
- (if (= ptr len) (flush))
- (string-set! buffer ptr c)
- (set! ptr (+ ptr 1)))
- (define (putb b)
- (if (= ptr len) (flush))
- (string-set! buffer ptr (integer->char b))
- (set! ptr (+ ptr 1)))
- (define (puts s)
- (let ((ls (string-length s)))
- (if (>= (+ ptr ls) len)
- (begin (flush)
- (write-bytevector-like s outp))
- (do ((i (- ls 1) (- i 1))
- (p (+ ptr ls -1) (- p 1)))
- ((< i 0)
- (set! ptr (+ ptr ls)))
- (string-set! buffer p (string-ref s i))))))
- (define (putd d)
- (flush)
- (write-fasl-datum d outp))
- (define (dump-codevec bv)
- (if omit-code?
- (puts "#f")
- (begin
- (putc #\#)
- (putc CTRLB)
- (putc #\")
- (let ((limit (bytevector-length bv)))
- (do ((i 0 (+ i 1)))
- ((= i limit) (putc #\")
- (putc #\newline))
- (let ((c (bytevector-ref bv i)))
- (cond ((= c DOUBLEQUOTE) (putc #\\))
- ((= c BACKSLASH) (putc #\\)))
- (putb c)))))))
- (define (dump-constvec cv)
- (puts "#(")
- (for-each (lambda (const)
- (putc #\space)
- (case (car const)
- ((data)
- (putd (cadr const)))
- ((constantvector)
- (dump-constvec (cadr const)))
- ((codevector)
- (dump-codevec (cadr const)))
- ((global)
- (putc #\#)
- (putc CTRLG)
- (putd (cadr const)))
- ((bits)
- (error "BITS attribute is not supported in fasl files."))
- (else
- (error "Faulty .lop file."))))
- (vector->list cv))
- (puts ")")
- (putc #\newline))
- (define (dump-fasl-segment segment)
- (if (not omit-code?) (putc #\())
- (putc #\#)
- (putc CTRLP)
- (putc #\()
- (dump-codevec (car segment))
- (putc #\space)
- (dump-constvec (cdr segment))
- (puts " #f)")
- (if (not omit-code?) (putc #\)))
- (putc #\newline))
- (dump-fasl-segment segment)
- (flush)))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Bootstrap heap dumper.
- ;
- ; Usage: (build-heap-image outputfile inputfile-list)
- ;
- ; Each input file is a sequence of segments, the structure of which
- ; depends on the target architecture, but at least segment.code and
- ; segment.constants exist as accessors.
- ;
- ; The code is a bytevector. The constant vector contains tagged
- ; entries (represented using length-2 lists), where the tags are
- ; `data', `codevector', `constantvector', `global', or `bits'.
- ;
- ; `build-heap-image' reads its file arguments into the heap, creates
- ; thunks from the segments, and creates a list of the thunks. It also
- ; creates a list of all symbols present in the loaded files. Finally,
- ; it generates an initialization procedure (the LAP of which is hardcoded
- ; into this file; see below). A pointer to this procedure is installed
- ; in the SCHEME_ENTRY root pointer; hence, this procedure (a thunk, as
- ; it were) is called when the heap image is loaded.
- ;
- ; The initialization procedure calls each procedure in the thunk list in
- ; order. It then invokes the procedure `go', which takes one argument:
- ; the list of symbols. Typically, `go' will initialize the symbol table
- ; and other system tables and then call `main', but this is by no means
- ; required.
- ;
- ; The Scheme assembler must be co-resident, since it is used by
- ; `build-heap-image' procedure to assemble the final startup code. This
- ; could be avoided by pre-assembling the code and patching it here, but
- ; the way it is now, this procedure is entirely portable -- no target
- ; dependencies.
- ;
- ; The code is structured to allow most procedures to be overridden for
- ; target architectures with more complex needs (notably the C backend).
- (define generate-global-symbols
- (make-twobit-flag 'generate-global-symbols))
- (generate-global-symbols #t)
- (define heap.version-number 9) ; Heap version number
- (define heap.root-names ; Roots in heap version 9
- '(result argreg2 argreg3
- reg0 reg1 reg2 reg3 reg3 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12
- reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg20 reg21 reg22 reg23
- reg24 reg25 reg26 reg27 reg28 reg29 reg30 reg31
- cont startup callouts schcall-arg4 alloci-tmp))
-
- (define (build-heap-image output-file input-files)
- (define tmp-file "HEAPDATA.dat")
- (define (process-input-files heap)
- (let loop ((files input-files) (inits '()))
- (cond ((null? files)
- (heap.thunks! heap (apply append inits)))
- (else
- (let ((filename (car files)))
- (display "Loading ")
- (display filename)
- (newline)
- (loop (cdr files)
- (append inits (list (dump-file! heap filename)))))))))
- (delete-file tmp-file)
- (let ((heap (make-heap #f (open-output-file tmp-file))))
- (before-all-files heap output-file input-files)
- (process-input-files heap)
- (heap.set-root! heap
- 'startup
- (dump-startup-procedure! heap))
- (heap.set-root! heap
- 'callouts
- (dump-global! heap 'millicode-support))
- (write-header heap output-file)
- (after-all-files heap output-file input-files)
- (close-output-port (heap.output-port heap))
- (append-file-shell-command tmp-file output-file)
- (load-map heap)
- (unspecified)))
- (define (before-all-files heap output-file-name input-file-names) #t)
- (define (after-all-files heap output-file-name input-file-names) #t)
- ; Public
- ;
- ; A 'heap' is a data structure with the following public fields; none
- ; of them are constant unless so annotated:
- ;
- ; version a fixnum (constant) - heap type version number
- ; roots an assoc list that maps root names to values
- ; top an exact nonnegative integer: the address of the
- ; next byte to be emitted
- ; symbol-table a symbol table abstract data type
- ; extra any value - a client-extension field
- ; output-port an output port (for the data stream)
- ; thunks a list of codevector addresses
- ;
- ; Bytes are emitted with the heap.byte! and heap.word! procedures,
- ; which emit a byte and a 4-byte word respectively. These update
- ; the top field.
- (define (make-heap extra output-port)
- (vector heap.version-number ; version
- '() ; roots
- 0 ; top
- (make-heap-symbol-table) ; symtab
- extra ; extra
- output-port ; output port
- '() ; thunks
- ))
- (define (heap.version h) (vector-ref h 0))
- (define (heap.roots h) (vector-ref h 1))
- (define (heap.top h) (vector-ref h 2))
- (define (heap.symbol-table h) (vector-ref h 3))
- (define (heap.extra h) (vector-ref h 4))
- (define (heap.output-port h) (vector-ref h 5))
- (define (heap.thunks h) (vector-ref h 6))
- (define (heap.roots! h x) (vector-set! h 1 x))
- (define (heap.top! h x) (vector-set! h 2 x))
- (define (heap.thunks! h x) (vector-set! h 6 x))
- ; Symbol table.
- ;
- ; The symbol table maps names to symbol structures, and a symbol
- ; structure contains information about that symbol.
- ;
- ; The structure has four fields:
- ; name a symbol - the print name
- ; symloc a fixnum or null - if fixnum, the location in the
- ; heap of the symbol structure.
- ; valloc a fixnum or null - if fixnum, the location in the
- ; heap of the global variable cell that has this
- ; symbol for its name.
- ; valno a fixnum or null - if fixnum, the serial number of
- ; the global variable cell (largely obsolete).
- ;
- ; Note therefore that the symbol table maintains information about
- ; whether the symbol is used as a symbol (in a datum), as a global
- ; variable, or both.
- (define (make-heap-symbol-table)
- (vector '() 0))
- (define (symtab.symbols st) (vector-ref st 0))
- (define (symtab.cell-no st) (vector-ref st 1))
- (define (symtab.symbols! st x) (vector-set! st 0 x))
- (define (symtab.cell-no! st x) (vector-set! st 1 x))
- (define (make-symcell name)
- (vector name '() '() '()))
- (define (symcell.name sc) (vector-ref sc 0)) ; name
- (define (symcell.symloc sc) (vector-ref sc 1)) ; symbol location (if any)
- (define (symcell.valloc sc) (vector-ref sc 2)) ; value cell location (ditto)
- (define (symcell.valno sc) (vector-ref sc 3)) ; value cell number (ditto)
- (define (symcell.symloc! sc x) (vector-set! sc 1 x))
- (define (symcell.valloc! sc x) (vector-set! sc 2 x))
- (define (symcell.valno! sc x) (vector-set! sc 3 x))
- ; Find a symcell in the table, or make a new one if there's none.
- (define (symbol-cell h name)
- (let ((symtab (heap.symbol-table h)))
- (let loop ((symbols (symtab.symbols symtab)))
- (cond ((null? symbols)
- (let ((new-sym (make-symcell name)))
- (symtab.symbols! symtab (cons new-sym
- (symtab.symbols symtab)))
- new-sym))
- ((eq? name (symcell.name (car symbols)))
- (car symbols))
- (else
- (loop (cdr symbols)))))))
- ; Fundamental data emitters
- (define twofiftysix^3 (* 256 256 256))
- (define twofiftysix^2 (* 256 256))
- (define twofiftysix 256)
- (define (heap.word-be! h w)
- (heap.byte! h (quotient w twofiftysix^3))
- (heap.byte! h (quotient (remainder w twofiftysix^3) twofiftysix^2))
- (heap.byte! h (quotient (remainder w twofiftysix^2) twofiftysix))
- (heap.byte! h (remainder w twofiftysix)))
- (define (heap.word-el! h w)
- (heap.byte! h (remainder w twofiftysix))
- (heap.byte! h (quotient (remainder w twofiftysix^2) twofiftysix))
- (heap.byte! h (quotient (remainder w twofiftysix^3) twofiftysix^2))
- (heap.byte! h (quotient w twofiftysix^3)))
- (define heap.word! heap.word-be!)
- (define (dumpheap.set-endianness! which)
- (case which
- ((big) (set! heap.word! heap.word-be!))
- ((little) (set! heap.word! heap.word-el!))
- (else ???)))
- (define (heap.byte! h b)
- (write-char (integer->char b) (heap.output-port h))
- (heap.top! h (+ 1 (heap.top h))))
- ; Useful abstractions and constants.
- (define (heap.header-word! h immediate length)
- (heap.word! h (+ (* length 256) immediate)))
- (define (heap.adjust! h)
- (let ((p (heap.top h)))
- (let loop ((i (- (* 8 (quotient (+ p 7) 8)) p)))
- (if (zero? i)
- '()
- (begin (heap.byte! h 0)
- (loop (- i 1)))))))
-
- (define heap.largest-fixnum (- (expt 2 29) 1))
- (define heap.smallest-fixnum (- (expt 2 29)))
- (define (heap.set-root! h name value)
- (heap.roots! h (cons (cons name value) (heap.roots h))))
- ;;; The segment.* procedures may be overridden by custom code.
- (define segment.code car)
- (define segment.constants cdr)
- ;;; The dump-*! procedures may be overridden by custom code.
- ; Load a LOP file into the heap, create a thunk in the heap to hold the
- ; code and constant vector, and return the list of thunk addresses in
- ; the order dumped.
- (define (dump-file! h filename)
- (before-dump-file h filename)
- (call-with-input-file filename
- (lambda (in)
- (do ((segment (read in) (read in))
- (thunks '() (cons (dump-segment! h segment) thunks)))
- ((eof-object? segment)
- (after-dump-file h filename)
- (reverse thunks))))))
- (define (before-dump-file h filename) #t)
- (define (after-dump-file h filename) #t)
- ; Dump a segment and return the heap address of the resulting thunk.
- (define (dump-segment! h segment)
- (let* ((the-code (dump-codevector! h (segment.code segment)))
- (the-consts (dump-constantvector! h (segment.constants segment))))
- (dump-thunk! h the-code the-consts)))
- (define (dump-tagged-item! h item)
- (case (car item)
- ((codevector)
- (dump-codevector! h (cadr item)))
- ((constantvector)
- (dump-constantvector! h (cadr item)))
- ((data)
- (dump-datum! h (cadr item)))
- ((global)
- (dump-global! h (cadr item)))
- ((bits)
- (cadr item))
- (else
- (error 'dump-tagged-item! "Unknown item ~a" item))))
- (define (dump-datum! h datum)
- (define (fixnum? x)
- (and (integer? x)
- (exact? x)
- (<= heap.smallest-fixnum x heap.largest-fixnum)))
- (define (bignum? x)
- (and (integer? x)
- (exact? x)
- (or (> x heap.largest-fixnum)
- (< x heap.smallest-fixnum))))
- (define (ratnum? x)
- (and (rational? x) (exact? x) (not (integer? x))))
- (define (flonum? x)
- (and (real? x) (inexact? x)))
- (define (compnum? x)
- (and (complex? x) (inexact? x) (not (real? x))))
- (define (rectnum? x)
- (and (complex? x) (exact? x) (not (real? x))))
- (cond ((fixnum? datum)
- (dump-fixnum! h datum))
- ((bignum? datum)
- (dump-bignum! h datum))
- ((ratnum? datum)
- (dump-ratnum! h datum))
- ((flonum? datum)
- (dump-flonum! h datum))
- ((compnum? datum)
- (dump-compnum! h datum))
- ((rectnum? datum)
- (dump-rectnum! h datum))
- ((char? datum)
- (dump-char! h datum))
- ((null? datum)
- $imm.null)
- ((eq? datum #t)
- $imm.true)
- ((eq? datum #f)
- $imm.false)
- ((equal? datum (unspecified))
- $imm.unspecified)
- ((equal? datum (undefined))
- $imm.undefined)
- ((vector? datum)
- (dump-vector! h datum $tag.vector-typetag))
- ((bytevector? datum)
- (dump-bytevector! h datum $tag.bytevector-typetag))
- ((pair? datum)
- (dump-pair! h datum))
- ((string? datum)
- (dump-string! h datum))
- ((symbol? datum)
- (dump-symbol! h datum))
- (else
- (error 'dump-datum! "Unsupported type of datum ~a" datum))))
- ; Returns the two's complement representation as a positive number.
- (define (dump-fixnum! h f)
- (if (negative? f)
- (- #x100000000 (* (abs f) 4))
- (* 4 f)))
- (define (dump-char! h c)
- (+ (* (char->integer c) twofiftysix^2) $imm.character))
- (define (dump-bignum! h b)
- (dump-bytevector! h (bignum->bytevector b) $tag.bignum-typetag))
- (define (dump-ratnum! h r)
- (dump-vector! h
- (vector (numerator r) (denominator r))
- $tag.ratnum-typetag))
- (define (dump-flonum! h f)
- (dump-bytevector! h (flonum->bytevector f) $tag.flonum-typetag))
- (define (dump-compnum! h c)
- (dump-bytevector! h (compnum->bytevector c) $tag.compnum-typetag))
- (define (dump-rectnum! h r)
- (dump-vector! h
- (vector (real-part r) (imag-part r))
- $tag.rectnum-typetag))
- (define (dump-string! h s)
- (dump-bytevector! h (string->bytevector s) $tag.string-typetag))
- (define (dump-pair! h p)
- (let ((the-car (dump-datum! h (car p)))
- (the-cdr (dump-datum! h (cdr p))))
- (let ((base (heap.top h)))
- (heap.word! h the-car)
- (heap.word! h the-cdr)
- (+ base $tag.pair-tag))))
- (define (dump-bytevector! h bv variation)
- (let ((base (heap.top h))
- (l (bytevector-length bv)))
- (heap.header-word! h (+ $imm.bytevector-header variation) l)
- (let loop ((i 0))
- (if (< i l)
- (begin (heap.byte! h (bytevector-ref bv i))
- (loop (+ i 1)))
- (begin (heap.adjust! h)
- (+ base $tag.bytevector-tag))))))
- (define (dump-vector! h v variation)
- (dump-vector-like! h v dump-datum! variation))
- (define (dump-vector-like! h cv recur! variation)
- (let* ((l (vector-length cv))
- (v (make-vector l '())))
- (let loop ((i 0))
- (if (< i l)
- (begin (vector-set! v i (recur! h (vector-ref cv i)))
- (loop (+ i 1)))
- (let ((base (heap.top h)))
- (heap.header-word! h (+ $imm.vector-header variation) (* l 4))
- (let loop ((i 0))
- (if (< i l)
- (begin (heap.word! h (vector-ref v i))
- (loop (+ i 1)))
- (begin (heap.adjust! h)
- (+ base $tag.vector-tag)))))))))
- (define (dump-codevector! h cv)
- (dump-bytevector! h cv $tag.bytevector-typetag))
- (define (dump-constantvector! h cv)
- (dump-vector-like! h cv dump-tagged-item! $tag.vector-typetag))
- (define (dump-symbol! h s)
- (let ((x (symbol-cell h s)))
- (if (null? (symcell.symloc x))
- (symcell.symloc! x (create-symbol! h s)))
- (symcell.symloc x)))
- (define (dump-global! h g)
- (let ((x (symbol-cell h g)))
- (if (null? (symcell.valloc x))
- (let ((cell (create-cell! h g)))
- (symcell.valloc! x (car cell))
- (symcell.valno! x (cdr cell))))
- (symcell.valloc x)))
- (define (dump-thunk! h code constants)
- (let ((base (heap.top h)))
- (heap.header-word! h $imm.procedure-header 8)
- (heap.word! h code)
- (heap.word! h constants)
- (heap.adjust! h)
- (+ base $tag.procedure-tag)))
- ; The car's are all heap pointers, so they should not be messed with.
- ; The cdr must be dumped, and then the pair.
- (define (dump-list-spine! h l)
- (if (null? l)
- $imm.null
- (let ((the-car (car l))
- (the-cdr (dump-list-spine! h (cdr l))))
- (let ((base (heap.top h)))
- (heap.word! h the-car)
- (heap.word! h the-cdr)
- (+ base $tag.pair-tag)))))
- (define (dump-startup-procedure! h)
- (let ((thunks (dump-list-spine! h (heap.thunks h)))
- (symbols (dump-list-spine! h (symbol-locations h))))
- (dump-segment! h (construct-startup-procedure symbols thunks))))
- ; The initialization procedure. The lists are magically patched into
- ; the constant vector after the procedure has been assembled but before
- ; it is dumped into the heap. See below.
- ;
- ; (define (init-proc argv)
- ; (let loop ((l <list-of-thunks>))
- ; (if (null? l)
- ; (go <list-of-symbols> argv)
- ; (begin ((car l))
- ; (loop (cdr l))))))
- (define init-proc
- `((,$.proc)
- (,$args= 1)
- (,$reg 1) ; argv into
- (,$setreg 2) ; register 2
- (,$const (thunks)) ; dummy list of thunks.
- (,$setreg 1)
- (,$.label 0)
- (,$reg 1)
- (,$op1 null?) ; (null? l)
- (,$branchf 2)
- (,$const (symbols)) ; dummy list of symbols
- (,$setreg 1)
- (,$global go)
- ;(,$op1 break)
- (,$invoke 2) ; (go <list of symbols> argv)
- (,$.label 2)
- (,$save 2)
- (,$store 0 0)
- (,$store 1 1)
- (,$store 2 2)
- (,$setrtn 3)
- (,$reg 1)
- (,$op1 car)
- (,$invoke 0) ; ((car l))
- (,$.label 3)
- (,$.cont)
- (,$restore 2)
- (,$pop 2)
- (,$reg 1)
- (,$op1 cdr)
- (,$setreg 1)
- (,$branch 0))) ; (loop (cdr l))
- ;;; Non-overridable code beyond this point
- ; Stuff a new symbol into the heap, return its location.
- (define (create-symbol! h s)
- (dump-vector-like!
- h
- (vector `(bits ,(dump-string! h (symbol->string s)))
- '(data 0)
- '(data ()))
- dump-tagged-item!
- $tag.symbol-typetag))
- ; Stuff a value cell into the heap, return a pair of its location
- ; and its cell number.
- (define (create-cell! h s)
- (let* ((symtab (heap.symbol-table h))
- (n (symtab.cell-no symtab))
- (p (dump-pair! h (cons (undefined)
- (if (generate-global-symbols)
- s
- n)))))
- (symtab.cell-no! symtab (+ n 1))
- (cons p n)))
- (define (construct-startup-procedure symbol-list-addr init-list-addr)
- ; Given some value which might appear in the constant vector,
- ; replace the entries matching that value with a new value.
- (define (patch-constant-vector! v old new)
- (let loop ((i (- (vector-length v) 1)))
- (if (>= i 0)
- (begin (if (equal? (vector-ref v i) old)
- (vector-set! v i new))
- (loop (- i 1))))))
- ; Assemble the startup thunk, patch it, and return it.
- (display "Assembling final procedure") (newline)
- (let ((e (single-stepping)))
- (single-stepping #f)
- (let ((segment (assemble init-proc)))
- (single-stepping e)
- (patch-constant-vector! (segment.constants segment)
- '(data (thunks))
- `(bits ,init-list-addr))
- (patch-constant-vector! (segment.constants segment)
- '(data (symbols))
- `(bits ,symbol-list-addr))
- segment)))
- ; Return a list of symbol locations for symbols in the heap, in order.
- (define (symbol-locations h)
- (let loop ((symbols (symtab.symbols (heap.symbol-table h))) (res '()))
- (cond ((null? symbols)
- (reverse res))
- ((not (null? (symcell.symloc (car symbols))))
- (loop (cdr symbols)
- (cons (symcell.symloc (car symbols)) res)))
- (else
- (loop (cdr symbols) res)))))
- ; Return list of variable name to cell number mappings for global vars.
- (define (load-map h)
- (let loop ((symbols (symtab.symbols (heap.symbol-table h))) (res '()))
- (cond ((null? symbols)
- (reverse res))
- ((not (null? (symcell.valloc (car symbols))))
- (loop (cdr symbols)
- (cons (cons (symcell.name (car symbols))
- (symcell.valno (car symbols)))
- res)))
- (else
- (loop (cdr symbols) res)))))
- (define (write-header h output-file)
- (delete-file output-file)
- (call-with-output-file output-file
- (lambda (out)
- (define (write-word w)
- (display (integer->char (quotient w twofiftysix^3)) out)
- (display (integer->char (quotient (remainder w twofiftysix^3)
- twofiftysix^2))
- out)
- (display (integer->char (quotient (remainder w twofiftysix^2)
- twofiftysix))
- out)
- (display (integer->char (remainder w twofiftysix)) out))
- (define (write-roots)
- (let ((assigned-roots (heap.roots h)))
- (for-each (lambda (root-name)
- (let ((probe (assq root-name assigned-roots)))
- (if probe
- (write-word (cdr probe))
- (write-word $imm.false))))
- heap.root-names)))
- (write-word heap.version-number)
- (write-roots)
- (write-word (quotient (heap.top h) 4)))))
- ; This is a gross hack that happens to work very well.
- (define (append-file-shell-command file-to-append file-to-append-to)
- (define (message)
- (display "You must execute the command") (newline)
- (display " cat ") (display file-to-append)
- (display " >> ") (display file-to-append-to) (newline)
- (display "to create the final heap image.") (newline))
- (case host-system
- ((chez larceny)
- (display "Creating final image in \"")
- (display file-to-append-to) (display "\"...") (newline)
- (if (zero? (system (string-append "cat " file-to-append " >> "
- file-to-append-to)))
- (delete-file file-to-append)
- (begin (display "Failed to create image!")
- (newline))))
- (else
- (message))))
- ; eof
- ; Copyright 1991 Lightship Software, Incorporated.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 11 June 1999 / wdc
- ;
- ; Asm/Sparc/pass5p2.sch -- Sparc machine assembler, top level
- ; Overrides the procedure of the same name in Asm/Common/pass5p1.sch.
- (define (assembly-table) $sparc-assembly-table$)
- ; Controls listing of instructions during assembly.
- (define listify? #f)
- ; Table of assembler procedures.
- (define $sparc-assembly-table$
- (make-vector
- *number-of-mnemonics*
- (lambda (instruction as)
- (asm-error "Unrecognized mnemonic " instruction))))
- (define (define-instruction i proc)
- (vector-set! $sparc-assembly-table$ i proc)
- #t)
- (define (list-instruction name instruction)
- (if listify?
- (begin (display list-indentation)
- (display " ")
- (display name)
- (display (make-string (max (- 12 (string-length name)) 1)
- #\space))
- (if (not (null? (cdr instruction)))
- (begin (write (cadr instruction))
- (do ((operands (cddr instruction)
- (cdr operands)))
- ((null? operands))
- (write-char #\,)
- (write (car operands)))))
- (newline)
- (flush-output-port))))
- (define (list-label instruction)
- (if listify?
- (begin (display list-indentation)
- (write-char #\L)
- (write (cadr instruction))
- (newline))))
- (define (list-lambda-start instruction)
- (list-instruction "lambda" (list $lambda '* (operand2 instruction)))
- (set! list-indentation (string-append list-indentation "| ")))
- (define (list-lambda-end)
- (set! list-indentation
- (substring list-indentation
- 0
- (- (string-length list-indentation) 4))))
- (define list-indentation "")
- ; Utilities
- ; Pseudo-instructions.
- (define-instruction $.label
- (lambda (instruction as)
- (list-label instruction)
- (sparc.label as (make-asm-label as (operand1 instruction)))))
- (define-instruction $.proc
- (lambda (instruction as)
- (list-instruction ".proc" instruction)
- #t))
- (define-instruction $.proc-doc
- (lambda (instruction as)
- (list-instruction ".proc-doc" instruction)
- (add-documentation as (operand1 instruction))
- #t))
- (define-instruction $.cont
- (lambda (instruction as)
- (list-instruction ".cont" instruction)
- #t))
- (define-instruction $.align
- (lambda (instruction as)
- (list-instruction ".align" instruction)
- #t))
- (define-instruction $.end
- (lambda (instruction as)
- #t))
- (define-instruction $.singlestep
- (lambda (instruction as)
- (let ((instr (car (as-source as))))
-
- (define (special?)
- (let ((op (operand0 instr)))
- (or (= op $.label)
- (= op $.proc)
- (= op $.cont)
- (= op $.align)
- (and (= op $load) (= 0 (operand1 instr))))))
-
- (define (readify-instr)
- (if (= (operand0 instr) $lambda)
- (list 'lambda '(...) (caddr instr) (cadddr instr))
- (car (readify-lap (list instr)))))
-
- (if (not (special?))
- (let ((repr (format-object (readify-instr)))
- (funky? (= (operand0 instr) $restore)))
- (let ((o (emit-datum as repr)))
- (emit-singlestep-instr! as funky? 0 o)))))))
- ; Instructions.
- (define-instruction $op1
- (lambda (instruction as)
- (list-instruction "op1" instruction)
- (emit-primop.1arg! as (operand1 instruction))))
- (define-instruction $op2
- (lambda (instruction as)
- (list-instruction "op2" instruction)
- (emit-primop.2arg! as
- (operand1 instruction)
- (regname (operand2 instruction)))))
- (define-instruction $op3
- (lambda (instruction as)
- (list-instruction "op3" instruction)
- (emit-primop.3arg! as
- (operand1 instruction)
- (regname (operand2 instruction))
- (regname (operand3 instruction)))))
- (define-instruction $op2imm
- (lambda (instruction as)
- (list-instruction "op2imm" instruction)
- (let ((op (case (operand1 instruction)
- ((+) 'internal:+/imm)
- ((-) 'internal:-/imm)
- ((fx+) 'internal:fx+/imm)
- ((fx-) 'internal:fx-/imm)
- ((fx=) 'internal:fx=/imm)
- ((fx<) 'internal:fx</imm)
- ((fx<=) 'internal:fx<=/imm)
- ((fx>) 'internal:fx>/imm)
- ((fx>=) 'internal:fx>=/imm)
- ((=:fix:fix) 'internal:=:fix:fix/imm)
- ((<:fix:fix) 'internal:<:fix:fix/imm)
- ((<=:fix:fix) 'internal:<=:fix:fix/imm)
- ((>:fix:fix) 'internal:>:fix:fix/imm)
- ((>=:fix:fix) 'internal:>=:fix:fix/imm)
- (else #f))))
- (if op
- (emit-primop.4arg! as op $r.result (operand2 instruction) $r.result)
- (begin
- (emit-constant->register as (operand2 instruction) $r.argreg2)
- (emit-primop.2arg! as
- (operand1 instruction)
- $r.argreg2))))))
- (define-instruction $const
- (lambda (instruction as)
- (list-instruction "const" instruction)
- (emit-constant->register as (operand1 instruction) $r.result)))
- (define-instruction $global
- (lambda (instruction as)
- (list-instruction "global" instruction)
- (emit-global->register! as
- (emit-global as (operand1 instruction))
- $r.result)))
- (define-instruction $setglbl
- (lambda (instruction as)
- (list-instruction "setglbl" instruction)
- (emit-register->global! as
- $r.result
- (emit-global as (operand1 instruction)))))
- ; FIXME: A problem is that the listing is messed up because of the delayed
- ; assembly; somehow we should fix this by putting an identifying label
- ; in the listing and emitting this label later, with the code.
- (define-instruction $lambda
- (lambda (instruction as)
- (let ((code-offset #f)
- (const-offset #f))
- (list-lambda-start instruction)
- (assemble-nested-lambda as
- (operand1 instruction)
- (operand3 instruction) ; documentation
- (lambda (nested-as segment)
- (set-constant! as code-offset (car segment))
- (set-constant! as const-offset (cdr segment))))
- (list-lambda-end)
- (set! code-offset (emit-codevector as 0))
- (set! const-offset (emit-constantvector as 0))
- (emit-lambda! as
- code-offset
- const-offset
- (operand2 instruction)))))
- (define-instruction $lexes
- (lambda (instruction as)
- (list-instruction "lexes" instruction)
- (emit-lexes! as (operand1 instruction))))
- (define-instruction $args=
- (lambda (instruction as)
- (list-instruction "args=" instruction)
- (emit-args=! as (operand1 instruction))))
- (define-instruction $args>=
- (lambda (instruction as)
- (list-instruction "args>=" instruction)
- (emit-args>=! as (operand1 instruction))))
- (define-instruction $invoke
- (lambda (instruction as)
- (list-instruction "invoke" instruction)
- (emit-invoke as (operand1 instruction) #f $m.invoke-ex)))
- (define-instruction $restore
- (lambda (instruction as)
- (if (not (negative? (operand1 instruction)))
- (begin
- (list-instruction "restore" instruction)
- (emit-restore! as (operand1 instruction))))))
- (define-instruction $pop
- (lambda (instruction as)
- (if (not (negative? (operand1 instruction)))
- (begin
- (list-instruction "pop" instruction)
- (let ((next (next-instruction as)))
- (if (and (peephole-optimization)
- (eqv? $return (operand0 next)))
- (begin (list-instruction "return" next)
- (consume-next-instruction! as)
- (emit-pop! as (operand1 instruction) #t))
- (emit-pop! as (operand1 instruction) #f)))))))
- (define-instruction $stack
- (lambda (instruction as)
- (list-instruction "stack" instruction)
- (emit-load! as (operand1 instruction) $r.result)))
- (define-instruction $setstk
- (lambda (instruction as)
- (list-instruction "setstk" instruction)
- (emit-store! as $r.result (operand1 instruction))))
- (define-instruction $load
- (lambda (instruction as)
- (list-instruction "load" instruction)
- (emit-load! as (operand2 instruction) (regname (operand1 instruction)))))
- (define-instruction $store
- (lambda (instruction as)
- (list-instruction "store" instruction)
- (emit-store! as (regname (operand1 instruction)) (operand2 instruction))))
- (define-instruction $lexical
- (lambda (instruction as)
- (list-instruction "lexical" instruction)
- (emit-lexical! as (operand1 instruction) (operand2 instruction))))
- (define-instruction $setlex
- (lambda (instruction as)
- (list-instruction "setlex" instruction)
- (emit-setlex! as (operand1 instruction) (operand2 instruction))))
- (define-instruction $reg
- (lambda (instruction as)
- (list-instruction "reg" instruction)
- (emit-register->register! as (regname (operand1 instruction)) $r.result)))
- (define-instruction $setreg
- (lambda (instruction as)
- (list-instruction "setreg" instruction)
- (emit-register->register! as $r.result (regname (operand1 instruction)))))
- (define-instruction $movereg
- (lambda (instruction as)
- (list-instruction "movereg" instruction)
- (emit-register->register! as
- (regname (operand1 instruction))
- (regname (operand2 instruction)))))
- (define-instruction $return
- (lambda (instruction as)
- (list-instruction "return" instruction)
- (emit-return! as)))
- (define-instruction $reg/return
- (lambda (instruction as)
- (list-instruction "reg/return" instruction)
- (emit-return-reg! as (regname (operand1 instruction)))))
- (define-instruction $const/return
- (lambda (instruction as)
- (list-instruction "const/return" instruction)
- (emit-return-const! as (operand1 instruction))))
- (define-instruction $nop
- (lambda (instruction as)
- (list-instruction "nop" instruction)))
- (define-instruction $save
- (lambda (instruction as)
- (if (not (negative? (operand1 instruction)))
- (begin
- (list-instruction "save" instruction)
- (let* ((n (operand1 instruction))
- (v (make-vector (+ n 1) #t)))
- (emit-save0! as n)
- (if (peephole-optimization)
- (let loop ((instruction (next-instruction as)))
- (if (eqv? $store (operand0 instruction))
- (begin (list-instruction "store" instruction)
- (emit-store! as
- (regname (operand1 instruction))
- (operand2 instruction))
- (consume-next-instruction! as)
- (vector-set! v (operand2 instruction) #f)
- (loop (next-instruction as))))))
- (emit-save1! as v))))))
- (define-instruction $setrtn
- (lambda (instruction as)
- (list-instruction "setrtn" instruction)
- (emit-setrtn! as (make-asm-label as (operand1 instruction)))))
- (define-instruction $apply
- (lambda (instruction as)
- (list-instruction "apply" instruction)
- (emit-apply! as
- (regname (operand1 instruction))
- (regname (operand2 instruction)))))
- (define-instruction $jump
- (lambda (instruction as)
- (list-instruction "jump" instruction)
- (emit-jump! as
- (operand1 instruction)
- (make-asm-label as (operand2 instruction)))))
- (define-instruction $skip
- (lambda (instruction as)
- (list-instruction "skip" instruction)
- (emit-branch! as #f (make-asm-label as (operand1 instruction)))))
- (define-instruction $branch
- (lambda (instruction as)
- (list-instruction "branch" instruction)
- (emit-branch! as #t (make-asm-label as (operand1 instruction)))))
- (define-instruction $branchf
- (lambda (instruction as)
- (list-instruction "branchf" instruction)
- (emit-branchf! as (make-asm-label as (operand1 instruction)))))
- (define-instruction $check
- (lambda (instruction as)
- (list-instruction "check" instruction)
- (if (not (unsafe-code))
- (emit-check! as $r.result
- (make-asm-label as (operand4 instruction))
- (list (regname (operand1 instruction))
- (regname (operand2 instruction))
- (regname (operand3 instruction)))))))
- (define-instruction $trap
- (lambda (instruction as)
- (list-instruction "trap" instruction)
- (emit-trap! as
- (regname (operand1 instruction))
- (regname (operand2 instruction))
- (regname (operand3 instruction))
- (operand4 instruction))))
- (define-instruction $const/setreg
- (lambda (instruction as)
- (list-instruction "const/setreg" instruction)
- (let ((x (operand1 instruction))
- (r (operand2 instruction)))
- (if (hwreg? r)
- (emit-constant->register as x (regname r))
- (begin (emit-constant->register as x $r.tmp0)
- (emit-register->register! as $r.tmp0 (regname r)))))))
- ; Operations introduced by the peephole optimizer.
- (define (peep-regname r)
- (if (eq? r 'RESULT) $r.result (regname r)))
- (define-instruction $reg/op1/branchf
- (lambda (instruction as)
- (list-instruction "reg/op1/branchf" instruction)
- (emit-primop.3arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (make-asm-label as (operand3 instruction)))))
- (define-instruction $reg/op2/branchf
- (lambda (instruction as)
- (list-instruction "reg/op2/branchf" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (peep-regname (operand3 instruction))
- (make-asm-label as (operand4 instruction)))))
- (define-instruction $reg/op2imm/branchf
- (lambda (instruction as)
- (list-instruction "reg/op2imm/branchf" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (operand3 instruction)
- (make-asm-label as (operand4 instruction)))))
- ; These three are like the corresponding branchf sequences except that
- ; there is a strong prediction that the branch will not be taken.
- (define-instruction $reg/op1/check
- (lambda (instruction as)
- (list-instruction "reg/op1/check" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (make-asm-label as (operand3 instruction))
- (map peep-regname (operand4 instruction)))))
- (define-instruction $reg/op2/check
- (lambda (instruction as)
- (list-instruction "reg/op2/check" instruction)
- (emit-primop.5arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (peep-regname (operand3 instruction))
- (make-asm-label as (operand4 instruction))
- (map peep-regname (operand5 instruction)))))
- (define-instruction $reg/op2imm/check
- (lambda (instruction as)
- (list-instruction "reg/op2imm/check" instruction)
- (emit-primop.5arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (operand3 instruction)
- (make-asm-label as (operand4 instruction))
- (map peep-regname (operand5 instruction)))))
- ;
- (define-instruction $reg/op1/setreg
- (lambda (instruction as)
- (list-instruction "reg/op1/setreg" instruction)
- (emit-primop.3arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (peep-regname (operand3 instruction)))))
- (define-instruction $reg/op2/setreg
- (lambda (instruction as)
- (list-instruction "reg/op2/setreg" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (peep-regname (operand3 instruction))
- (peep-regname (operand4 instruction)))))
- (define-instruction $reg/op2imm/setreg
- (lambda (instruction as)
- (list-instruction "reg/op2imm/setreg" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (operand3 instruction)
- (peep-regname (operand4 instruction)))))
- (define-instruction $reg/op3
- (lambda (instruction as)
- (list-instruction "reg/op3" instruction)
- (emit-primop.4arg! as
- (operand1 instruction)
- (peep-regname (operand2 instruction))
- (peep-regname (operand3 instruction))
- (peep-regname (operand4 instruction)))))
- (define-instruction $reg/branchf
- (lambda (instruction as)
- (list-instruction "reg/branchf" instruction)
- (emit-branchfreg! as
- (regname (operand1 instruction))
- (make-asm-label as (operand2 instruction)))))
- (define-instruction $setrtn/branch
- (lambda (instruction as)
- (list-instruction "setrtn/branch" instruction)
- (emit-branch-with-setrtn! as (make-asm-label as (operand1 instruction)))))
- (define-instruction $setrtn/invoke
- (lambda (instruction as)
- (list-instruction "setrtn/invoke" instruction)
- (emit-invoke as (operand1 instruction) #t $m.invoke-ex)))
- (define-instruction $global/setreg
- (lambda (instruction as)
- (list-instruction "global/setreg" instruction)
- (emit-global->register! as
- (emit-global as (operand1 instruction))
- (regname (operand2 instruction)))))
- (define-instruction $global/invoke
- (lambda (instruction as)
- (list-instruction "global/invoke" instruction)
- (emit-load-global as
- (emit-global as (operand1 instruction))
- $r.result
- #f)
- (emit-invoke as (operand2 instruction) #f $m.global-invoke-ex)))
- (define-instruction $reg/setglbl
- (lambda (instruction as)
- (list-instruction "reg/setglbl" instruction)
- (emit-register->global! as
- (regname (operand1 instruction))
- (emit-global as (operand2 instruction)))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 9 May 1999.
- ;
- ; Asm/Sparc/peepopt.sch -- MAL peephole optimizer, for the SPARC assembler.
- ;
- ; The procedure `peep' is called on the as structure before every
- ; instruction is assembled. It may replace the prefix of the instruction
- ; stream by some other instruction sequence.
- ;
- ; Invariant: if the peephole optimizer doesn't change anything, then
- ;
- ; (let ((x (as-source as)))
- ; (peep as)
- ; (eq? x (as-source as))) => #t
- ;
- ; Note this still isn't right -- it should be integrated with pass5p2 --
- ; but it's a step in the right direction.
- (define *peephole-table* (make-vector *number-of-mnemonics* #f))
- (define (define-peephole n p)
- (vector-set! *peephole-table* n p)
- (unspecified))
- (define (peep as)
- (let ((t0 (as-source as)))
- (if (not (null? t0))
- (let ((i1 (car t0)))
- (let ((p (vector-ref *peephole-table* (car i1))))
- (if p
- (let* ((t1 (if (null? t0) t0 (cdr t0)))
- (i2 (if (null? t1) '(-1 0 0 0) (car t1)))
- (t2 (if (null? t1) t1 (cdr t1)))
- (i3 (if (null? t2) '(-1 0 0 0) (car t2)))
- (t3 (if (null? t2) t2 (cdr t2))))
- (p as i1 i2 i3 t1 t2 t3))))))))
- (define-peephole $reg
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $return)
- (reg-return as i1 i2 t2))
- ((= (car i2) $setglbl)
- (reg-setglbl as i1 i2 t2))
- ((= (car i2) $op1)
- (cond ((= (car i3) $setreg)
- (reg-op1-setreg as i1 i2 i3 t2 t3))
- ((= (car i3) $branchf)
- (reg-op1-branchf as i1 i2 i3 t3))
- ((= (car i3) $check)
- (reg-op1-check as i1 i2 i3 t3))
- (else
- (reg-op1 as i1 i2 t2))))
- ((= (car i2) $op2)
- (cond ((= (car i3) $setreg)
- (reg-op2-setreg as i1 i2 i3 t2 t3))
- ((= (car i3) $branchf)
- (reg-op2-branchf as i1 i2 i3 t3))
- ((= (car i3) $check)
- (reg-op2-check as i1 i2 i3 t3))
- (else
- (reg-op2 as i1 i2 t2))))
- ((= (car i2) $op2imm)
- (cond ((= (car i3) $setreg)
- (reg-op2imm-setreg as i1 i2 i3 t2 t3))
- ((= (car i3) $branchf)
- (reg-op2imm-branchf as i1 i2 i3 t3))
- ((= (car i3) $check)
- (reg-op2imm-check as i1 i2 i3 t3))
- (else
- (reg-op2imm as i1 i2 t2))))
- ((= (car i2) $op3)
- (reg-op3 as i1 i2 t2))
- ((= (car i2) $setreg)
- (reg-setreg as i1 i2 t2))
- ((= (car i2) $branchf)
- (reg-branchf as i1 i2 t2)))))
- (define-peephole $op1
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $branchf)
- (op1-branchf as i1 i2 t2))
- ((= (car i2) $setreg)
- (op1-setreg as i1 i2 t2))
- ((= (car i2) $check)
- (op1-check as i1 i2 t2)))))
- (define-peephole $op2
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $branchf)
- (op2-branchf as i1 i2 t2))
- ((= (car i2) $setreg)
- (op2-setreg as i1 i2 t2))
- ((= (car i2) $check)
- (op2-check as i1 i2 t2)))))
- (define-peephole $op2imm
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $branchf)
- (op2imm-branchf as i1 i2 t2))
- ((= (car i2) $setreg)
- (op2imm-setreg as i1 i2 t2))
- ((= (car i2) $check)
- (op2imm-check as i1 i2 t2)))))
- (define-peephole $const
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $setreg)
- (const-setreg as i1 i2 t2))
- ((= (car i2) $op2)
- (const-op2 as i1 i2 t2))
- ((= (car i2) $return)
- (const-return as i1 i2 t2)))))
- (define-peephole $setrtn
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $branch)
- (cond ((= (car i3) $.align)
- (if (not (null? t3))
- (let ((i4 (car t3))
- (t4 (cdr t3)))
- (cond ((= (car i4) $.label)
- (setrtn-branch as i1 i2 i3 i4 t4))))))))
- ((= (car i2) $invoke)
- (cond ((= (car i3) $.align)
- (if (not (null? t3))
- (let ((i4 (car t3))
- (t4 (cdr t3)))
- (cond ((= (car i4) $.label)
- (setrtn-invoke as i1 i2 i3 i4 t4)))))))))))
- (define-peephole $branch
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $.align)
- (cond ((= (car i3) $.label)
- (branch-and-label as i1 i2 i3 t3)))))))
- (define-peephole $global
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $setreg)
- (global-setreg as i1 i2 t2))
- ((= (car i2) $invoke)
- (global-invoke as i1 i2 t2))
- ((= (car i2) $setrtn)
- (cond ((= (car i3) $invoke)
- (global-setrtn-invoke as i1 i2 i3 t3)))))))
- (define-peephole $reg/op1/check
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $reg)
- (cond ((= (car i3) $op1)
- (if (not (null? t3))
- (let ((i4 (car t3))
- (t4 (cdr t3)))
- (cond ((= (car i4) $setreg)
- (reg/op1/check-reg-op1-setreg
- as i1 i2 i3 i4 t4)))))))))))
- (define-peephole $reg/op2/check
- (lambda (as i1 i2 i3 t1 t2 t3)
- (cond ((= (car i2) $reg)
- (cond ((= (car i3) $op2imm)
- (if (not (null? t3))
- (let ((i4 (car t3))
- (t4 (cdr t3)))
- (cond ((= (car i4) $check)
- (reg/op2/check-reg-op2imm-check
- as i1 i2 i3 i4 t4)))))))))))
- ; Worker procedures.
- (define (reg-return as i:reg i:return tail)
- (let ((rs (operand1 i:reg)))
- (if (hwreg? rs)
- (as-source! as (cons (list $reg/return rs) tail)))))
- (define (reg-op1-setreg as i:reg i:op1 i:setreg tail-1 tail)
- (let ((rs (operand1 i:reg))
- (rd (operand1 i:setreg))
- (op (operand1 i:op1)))
- (if (hwreg? rs)
- (if (hwreg? rd)
- (peep-reg/op1/setreg as op rs rd tail)
- (peep-reg/op1/setreg as op rs 'RESULT tail-1)))))
- (define (reg-op1 as i:reg i:op1 tail)
- (let ((rs (operand1 i:reg))
- (op (operand1 i:op1)))
- (if (hwreg? rs)
- (peep-reg/op1/setreg as op rs 'RESULT tail))))
- (define (op1-setreg as i:op1 i:setreg tail)
- (let ((op (operand1 i:op1))
- (rd (operand1 i:setreg)))
- (if (hwreg? rd)
- (peep-reg/op1/setreg as op 'RESULT rd tail))))
- (define (peep-reg/op1/setreg as op rs rd tail)
- (let ((op (case op
- ((car) 'internal:car)
- ((cdr) 'internal:cdr)
- ((car:pair) 'internal:car:pair)
- ((cdr:pair) 'internal:cdr:pair)
- ((cell-ref) 'internal:cell-ref)
- ((vector-length) 'internal:vector-length)
- ((vector-length:vec) 'internal:vector-length:vec)
- ((string-length) 'internal:string-length)
- ((--) 'internal:--)
- ((fx--) 'internal:fx--)
- ((fxpositive?) 'internal:fxpositive?)
- ((fxnegative?) 'internal:fxnegative?)
- ((fxzero?) 'internal:fxzero?)
- (else #f))))
- (if op
- (as-source! as (cons (list $reg/op1/setreg op rs rd) tail)))))
- (define (reg-op2-setreg as i:reg i:op2 i:setreg tail-1 tail)
- (let ((rs1 (operand1 i:reg))
- (rs2 (operand2 i:op2))
- (op (operand1 i:op2))
- (rd (operand1 i:setreg)))
- (if (hwreg? rs1)
- (if (hwreg? rd)
- (peep-reg/op2/setreg as op rs1 rs2 rd tail)
- (peep-reg/op2/setreg as op rs1 rs2 'RESULT tail-1)))))
- (define (reg-op2 as i:reg i:op2 tail)
- (let ((rs1 (operand1 i:reg))
- (rs2 (operand2 i:op2))
- (op (operand1 i:op2)))
- (if (hwreg? rs1)
- (peep-reg/op2/setreg as op rs1 rs2 'RESULT tail))))
- (define (op2-setreg as i:op2 i:setreg tail)
- (let ((op (operand1 i:op2))
- (rs2 (operand2 i:op2))
- (rd (operand1 i:setreg)))
- (if (hwreg? rd)
- (peep-reg/op2/setreg as op 'RESULT rs2 rd tail))))
- (define (peep-reg/op2/setreg as op rs1 rs2 rd tail)
- (let ((op (case op
- ((+) 'internal:+)
- ((-) 'internal:-)
- ((fx+) 'internal:fx+)
- ((fx-) 'internal:fx-)
- ((fx=) 'internal:fx=)
- ((fx>) 'internal:fx>)
- ((fx>=) 'internal:fx>=)
- ((fx<) 'internal:fx<)
- ((fx<=) 'internal:fx<=)
- ((eq?) 'internal:eq?)
- ((cons) 'internal:cons)
- ((vector-ref) 'internal:vector-ref)
- ((vector-ref:trusted) 'internal:vector-ref:trusted)
- ((string-ref) 'internal:string-ref)
- ((set-car!) 'internal:set-car!)
- ((set-cdr!) 'internal:set-cdr!)
- ((cell-set!) 'internal:cell-set!)
- (else #f))))
- (if op
- (as-source! as (cons (list $reg/op2/setreg op rs1 rs2 rd) tail)))))
- (define (reg-op2imm-setreg as i:reg i:op2imm i:setreg tail-1 tail)
- (let ((rs (operand1 i:reg))
- (imm (operand2 i:op2imm))
- (op (operand1 i:op2imm))
- (rd (operand1 i:setreg)))
- (if (hwreg? rs)
- (if (hwreg? rd)
- (peep-reg/op2imm/setreg as op rs imm rd tail)
- (peep-reg/op2imm/setreg as op rs imm 'RESULT tail-1)))))
- (define (reg-op2imm as i:reg i:op2imm tail)
- (let ((rs (operand1 i:reg))
- (imm (operand2 i:op2imm))
- (op (operand1 i:op2imm)))
- (if (hwreg? rs)
- (peep-reg/op2imm/setreg as op rs imm 'RESULT tail))))
- (define (op2imm-setreg as i:op2imm i:setreg tail)
- (let ((op (operand1 i:op2imm))
- (imm (operand2 i:op2imm))
- (rd (operand1 i:setreg)))
- (if (hwreg? rd)
- (peep-reg/op2imm/setreg as op 'RESULT imm rd tail))))
- (define (peep-reg/op2imm/setreg as op rs imm rd tail)
- (let ((op (case op
- ((+) 'internal:+/imm)
- ((-) 'internal:-/imm)
- ((fx+) 'internal:fx+/imm)
- ((fx-) 'internal:fx-/imm)
- ((fx=) 'internal:fx=/imm)
- ((fx<) 'internal:fx</imm)
- ((fx<=) 'internal:fx<=/imm)
- ((fx>) 'internal:fx>/imm)
- ((fx>=) 'internal:fx>=/imm)
- ((eq?) 'internal:eq?/imm)
- ((vector-ref) 'internal:vector-ref/imm)
- ((string-ref) 'internal:string-ref/imm)
- (else #f))))
- (if op
- (as-source! as (cons (list $reg/op2imm/setreg op rs imm rd) tail)))))
- (define (reg-op1-branchf as i:reg i:op1 i:branchf tail)
- (let ((rs (operand1 i:reg))
- (op (operand1 i:op1))
- (L (operand1 i:branchf)))
- (if (hwreg? rs)
- (peep-reg/op1/branchf as op rs L tail))))
- (define (op1-branchf as i:op1 i:branchf tail)
- (let ((op (operand1 i:op1))
- (L (operand1 i:branchf)))
- (peep-reg/op1/branchf as op 'RESULT L tail)))
- (define (peep-reg/op1/branchf as op rs L tail)
- (let ((op (case op
- ((null?) 'internal:branchf-null?)
- ((pair?) 'internal:branchf-pair?)
- ((zero?) 'internal:branchf-zero?)
- ((eof-object?) 'internal:branchf-eof-object?)
- ((fixnum?) 'internal:branchf-fixnum?)
- ((char?) 'internal:branchf-char?)
- ((fxzero?) 'internal:branchf-fxzero?)
- ((fxnegative?) 'internal:branchf-fxnegative?)
- ((fxpositive?) 'internal:branchf-fxpositive?)
- (else #f))))
- (if op
- (as-source! as (cons (list $reg/op1/branchf op rs L) tail)))))
- (define (reg-op2-branchf as i:reg i:op2 i:branchf tail)
- (let ((rs1 (operand1 i:reg))
- (rs2 (operand2 i:op2))
- (op (operand1 i:op2))
- (L (operand1 i:branchf)))
- (if (hwreg? rs1)
- (peep-reg/op2/branchf as op rs1 rs2 L tail))))
- (define (op2-branchf as i:op2 i:branchf tail)
- (let ((op (operand1 i:op2))
- (rs2 (operand2 i:op2))
- (L (operand1 i:branchf)))
- (peep-reg/op2/branchf as op 'RESULT rs2 L tail)))
- (define (peep-reg/op2/branchf as op rs1 rs2 L tail)
- (let ((op (case op
- ((<) 'internal:branchf-<)
- ((>) 'internal:branchf->)
- ((>=) 'internal:branchf->=)
- ((<=) 'internal:branchf-<=)
- ((=) 'internal:branchf-=)
- ((eq?) 'internal:branchf-eq?)
- ((char=?) 'internal:branchf-char=?)
- ((char>=?) 'internal:branchf-char>=?)
- ((char>?) 'internal:branchf-char>?)
- ((char<=?) 'internal:branchf-char<=?)
- ((char<?) 'internal:branchf-char<?)
- ((fx=) 'internal:branchf-fx=)
- ((fx>) 'internal:branchf-fx>)
- ((fx>=) 'internal:branchf-fx>=)
- ((fx<) 'internal:branchf-fx<)
- ((fx<=) 'internal:branchf-fx<=)
- (else #f))))
- (if op
- (as-source! as
- (cons (list $reg/op2/branchf op rs1 rs2 L)
- tail)))))
- (define (reg-op2imm-branchf as i:reg i:op2imm i:branchf tail)
- (let ((rs (operand1 i:reg))
- (imm (operand2 i:op2imm))
- (op (operand1 i:op2imm))
- (L (operand1 i:branchf)))
- (if (hwreg? rs)
- (peep-reg/op2imm/branchf as op rs imm L tail))))
- (define (op2imm-branchf as i:op2imm i:branchf tail)
- (let ((op (operand1 i:op2imm))
- (imm (operand2 i:op2imm))
- (L (operand1 i:branchf)))
- (peep-reg/op2imm/branchf as op 'RESULT imm L tail)))
- (define (peep-reg/op2imm/branchf as op rs imm L tail)
- (let ((op (case op
- ((<) 'internal:branchf-</imm)
- ((>) 'internal:branchf->/imm)
- ((>=) 'internal:branchf->=/imm)
- ((<=) 'internal:branchf-<=/imm)
- ((=) 'internal:branchf-=/imm)
- ((eq?) 'internal:branchf-eq?/imm)
- ((char=?) 'internal:branchf-char=?/imm)
- ((char>=?) 'internal:branchf-char>=?/imm)
- ((char>?) 'internal:branchf-char>?/imm)
- ((char<=?) 'internal:branchf-char<=?/imm)
- ((char<?) 'internal:branchf-char<?/imm)
- ((fx=) 'internal:branchf-fx=/imm)
- ((fx>) 'internal:branchf-fx>/imm)
- ((fx>=) 'internal:branchf-fx>=/imm)
- ((fx<) 'internal:branchf-fx</imm)
- ((fx<=) 'internal:branchf-fx<=/imm)
- (else #f))))
- (if op
- (as-source! as
- (cons (list $reg/op2imm/branchf op rs imm L)
- tail)))))
- ; Check optimization.
- (define (reg-op1-check as i:reg i:op1 i:check tail)
- (let ((rs (operand1 i:reg))
- (op (operand1 i:op1)))
- (if (hwreg? rs)
- (peep-reg/op1/check as
- op
- rs
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail))))
- (define (op1-check as i:op1 i:check tail)
- (let ((op (operand1 i:op1)))
- (peep-reg/op1/check as
- op
- 'RESULT
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail)))
- (define (peep-reg/op1/check as op rs L1 liveregs tail)
- (let ((op (case op
- ((fixnum?) 'internal:check-fixnum?)
- ((pair?) 'internal:check-pair?)
- ((vector?) 'internal:check-vector?)
- (else #f))))
- (if op
- (as-source! as
- (cons (list $reg/op1/check op rs L1 liveregs)
- tail)))))
- (define (reg-op2-check as i:reg i:op2 i:check tail)
- (let ((rs1 (operand1 i:reg))
- (rs2 (operand2 i:op2))
- (op (operand1 i:op2)))
- (if (hwreg? rs1)
- (peep-reg/op2/check as
- op
- rs1
- rs2
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail))))
- (define (op2-check as i:op2 i:check tail)
- (let ((rs2 (operand2 i:op2))
- (op (operand1 i:op2)))
- (peep-reg/op2/check as
- op
- 'RESULT
- rs2
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail)))
- (define (peep-reg/op2/check as op rs1 rs2 L1 liveregs tail)
- (let ((op (case op
- ((<:fix:fix) 'internal:check-<:fix:fix)
- ((<=:fix:fix) 'internal:check-<=:fix:fix)
- ((>=:fix:fix) 'internal:check->=:fix:fix)
- (else #f))))
- (if op
- (as-source! as
- (cons (list $reg/op2/check op rs1 rs2 L1 liveregs)
- tail)))))
- (define (reg-op2imm-check as i:reg i:op2imm i:check tail)
- (let ((rs1 (operand1 i:reg))
- (op (operand1 i:op2imm))
- (imm (operand2 i:op2imm)))
- (if (hwreg? rs1)
- (peep-reg/op2imm/check as
- op
- rs1
- imm
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail))))
- (define (op2imm-check as i:op2imm i:check tail)
- (let ((op (operand1 i:op2imm))
- (imm (operand2 i:op2imm)))
- (peep-reg/op2imm/check as
- op
- 'RESULT
- imm
- (operand4 i:check)
- (list (operand1 i:check)
- (operand2 i:check)
- (operand3 i:check))
- tail)))
- (define (peep-reg/op2imm/check as op rs1 imm L1 liveregs tail)
- (let ((op (case op
- ((<:fix:fix) 'internal:check-<:fix:fix/imm)
- ((<=:fix:fix) 'internal:check-<=:fix:fix/imm)
- ((>=:fix:fix) 'internal:check->=:fix:fix/imm)
- (else #f))))
- (if op
- (as-source! as
- (cons (list $reg/op2imm/check op rs1 imm L1 liveregs)
- tail)))))
- (define (reg/op1/check-reg-op1-setreg as i:ro1check i:reg i:op1 i:setreg tail)
- (let ((o1 (operand1 i:ro1check))
- (r1 (operand2 i:ro1check))
- (r2 (operand1 i:reg))
- (o2 (operand1 i:op1))
- (r3 (operand1 i:setreg)))
- (if (and (eq? o1 'internal:check-vector?)
- (eq? r1 r2)
- (eq? o2 'vector-length:vec)
- (hwreg? r1)
- (hwreg? r3))
- (as-source! as
- (cons (list $reg/op2/check
- 'internal:check-vector?/vector-length:vec
- r1
- r3
- (operand3 i:ro1check)
- (operand4 i:ro1check))
- tail)))))
- ; Range checks of the form 0 <= i < n can be performed by a single check.
- ; This peephole optimization recognizes
- ; reg rs1
- ; op2 <:fix:fix,rs2
- ; check r1,r2,r3,L
- ; reg rs1 ; must match earlier reg
- ; op2imm >=:fix:fix,0
- ; check r1,r2,r3,L ; label must match earlier check
- (define (reg/op2/check-reg-op2imm-check
- as i:ro2check i:reg i:op2imm i:check tail)
- (let ((o1 (operand1 i:ro2check))
- (rs1 (operand2 i:ro2check))
- (rs2 (operand3 i:ro2check))
- (L1 (operand4 i:ro2check))
- (live (operand5 i:ro2check))
- (rs3 (operand1 i:reg))
- (o2 (operand1 i:op2imm))
- (x (operand2 i:op2imm))
- (L2 (operand4 i:check)))
- (if (and (eq? o1 'internal:check-<:fix:fix)
- (eq? o2 '>=:fix:fix)
- (eq? rs1 rs3)
- (eq? x 0)
- (eq? L1 L2))
- (as-source! as
- (cons (list $reg/op2/check 'internal:check-range
- rs1 rs2 L1 live)
- tail)))))
- ; End of check optimization.
- (define (reg-op3 as i:reg i:op3 tail)
- (let ((rs1 (operand1 i:reg))
- (rs2 (operand2 i:op3))
- (rs3 (operand3 i:op3))
- (op (operand1 i:op3)))
- (if (hwreg? rs1)
- (let ((op (case op
- ((vector-set!) 'internal:vector-set!)
- ((string-set!) 'internal:string-set!)
- (else #f))))
- (if op
- (as-source! as (cons (list $reg/op3 op rs1 rs2 rs3) tail)))))))
- ; Reg-setreg is not restricted to hardware registers, as $movereg is
- ; a standard instruction.
- (define (reg-setreg as i:reg i:setreg tail)
- (let ((rs (operand1 i:reg))
- (rd (operand1 i:setreg)))
- (if (= rs rd)
- (as-source! as tail)
- (as-source! as (cons (list $movereg rs rd) tail)))))
- (define (reg-branchf as i:reg i:branchf tail)
- (let ((rs (operand1 i:reg))
- (L (operand1 i:branchf)))
- (if (hwreg? rs)
- (as-source! as (cons (list $reg/branchf rs L) tail)))))
- (define (const-setreg as i:const i:setreg tail)
- (let ((c (operand1 i:const))
- (rd (operand1 i:setreg)))
- (if (hwreg? rd)
- (as-source! as (cons (list $const/setreg c rd) tail)))))
- ; Make-vector on vectors of known short length.
- (define (const-op2 as i:const i:op2 tail)
- (let ((vn '#(make-vector:0 make-vector:1 make-vector:2 make-vector:3
- make-vector:4 make-vector:5 make-vector:6 make-vector:7
- make-vector:8 make-vector:9))
- (c (operand1 i:const))
- (op (operand1 i:op2))
- (r (operand2 i:op2)))
- (if (and (eq? op 'make-vector)
- (fixnum? c)
- (<= 0 c 9))
- (as-source! as (cons (list $op2 (vector-ref vn c) r) tail)))))
- ; Constants that can be synthesized in a single instruction can be
- ; moved into RESULT in the delay slot of the return instruction.
- (define (const-return as i:const i:return tail)
- (let ((c (operand1 i:const)))
- (if (or (and (number? c) (immediate-int? c))
- (null? c)
- (boolean? c))
- (as-source! as (cons (list $const/return c) tail)))))
- ; This allows the use of hardware 'call' instructions.
- ; (setrtn Lx)
- ; (branch Ly k)
- ; (.align k) Ignored on SPARC
- ; (.label Lx)
- ; => (setrtn/branch Ly k)
- ; (.label Lx)
- (define (setrtn-branch as i:setrtn i:branch i:align i:label tail)
- (let ((return-label (operand1 i:setrtn))
- (branch-ops (cdr i:branch))
- (label (operand1 i:label)))
- (if (= return-label label)
- (as-source! as (cons (cons $setrtn/branch branch-ops)
- (cons i:label
- tail))))))
- ; Ditto for 'invoke'.
- ;
- ; Disabled because it does _not_ pay off on the SPARC currently --
- ; probably, the dependency created between 'jmpl' and 'st' is not
- ; handled well on the test machine (an Ultrasparc). Might work
- ; better if the return address were to be kept in a register always.
- (define (setrtn-invoke as i:setrtn i:invoke i:align i:label tail)
- (let ((return-label (operand1 i:setrtn))
- (invoke-ops (operand1 i:invoke))
- (label (operand1 i:label)))
- (if (and #f ; DISABLED
- (= return-label label))
- (as-source! as (cons (cons $setrtn/invoke invoke-ops)
- (cons i:label
- tail))))))
- ; Gets rid of spurious branch-to-next-instruction
- ; (branch Lx k)
- ; (.align y)
- ; (.label Lx)
- ; => (.align y)
- ; (.label Lx)
- (define (branch-and-label as i:branch i:align i:label tail)
- (let ((branch-label (operand1 i:branch))
- (label (operand1 i:label)))
- (if (= branch-label label)
- (as-source! as (cons i:align (cons i:label tail))))))
- (define (global-setreg as i:global i:setreg tail)
- (let ((global (operand1 i:global))
- (rd (operand1 i:setreg)))
- (if (hwreg? rd)
- (as-source! as (cons (list $global/setreg global rd) tail)))))
- ; Obscure guard: unsafe-code = #t implies that global/invoke will not
- ; check the value of the global variable, yet unsafe-code and
- ; catch-undefined-globals are supposed to be independent.
- (define (global-invoke as i:global i:invoke tail)
- (let ((global (operand1 i:global))
- (argc (operand1 i:invoke)))
- (if (not (and (unsafe-code) (catch-undefined-globals)))
- (as-source! as (cons (list $global/invoke global argc) tail)))))
- ; Obscure guard: see comment for previous procedure.
- ; FIXME! This implementation is temporary until setrtn-invoke is enabled.
- (define (global-setrtn-invoke as i:global i:setrtn i:invoke tail)
- (let ((global (operand1 i:global))
- (argc (operand1 i:invoke)))
- (if (not (and (unsafe-code) (catch-undefined-globals)))
- (as-source! as (cons i:setrtn
- (cons (list $global/invoke global argc)
- tail))))))
- (define (reg-setglbl as i:reg i:setglbl tail)
- (let ((rs (operand1 i:reg))
- (global (operand1 i:setglbl)))
- (if (hwreg? rs)
- (as-source! as (cons (list $reg/setglbl rs global) tail)))))
- ; Test code
- (define (peeptest istream)
- (let ((as (make-assembly-structure istream)))
- (let loop ((l '()))
- (if (null? (as-source as))
- (reverse l)
- (begin (peep as)
- (let ((a (car (as-source as))))
- (as-source! as (cdr (as-source as)))
- (loop (cons a l))))))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; SPARC assembler machine parameters & utility procedures.
- ;
- ; 13 May 1999 / wdc
- ; Round up to nearest 8.
- (define (roundup8 n)
- (* (quotient (+ n 7) 8) 8))
- ; Given an integer code for a register, return its register label.
- ; This register label is the register number for a h.w. register and the
- ; offsets from GLOBALS[ r0 ] for a s.w. register.
- (define regname
- (let ((v (vector $r.reg0 $r.reg1 $r.reg2 $r.reg3 $r.reg4 $r.reg5
- $r.reg6 $r.reg7 $r.reg8 $r.reg9 $r.reg10 $r.reg11
- $r.reg12 $r.reg13 $r.reg14 $r.reg15 $r.reg16 $r.reg17
- $r.reg18 $r.reg19 $r.reg20 $r.reg21 $r.reg22 $r.reg23
- $r.reg24 $r.reg25 $r.reg26 $r.reg27 $r.reg28 $r.reg29
- $r.reg30 $r.reg31)))
- (lambda (r)
- (vector-ref v r))))
- ; Is a general-purpose register mapped to a hardware register?
- ; This is fragile! FIXME.
- (define (hardware-mapped? r)
- (or (and (>= r $r.reg0) (<= r $r.reg7))
- (= r $r.argreg2)
- (= r $r.argreg3)
- (= r $r.result)
- (= r $r.g0)
- (= r $r.tmp0)
- (= r $r.tmp1)
- (= r $r.tmp2)))
- ; Used by peephole optimizer
- (define (hwreg? x)
- (<= 0 x 7))
- (define (immediate-int? x)
- (and (exact? x)
- (integer? x)
- (<= -1024 x 1023)))
- ; Given an exact integer, can it be represented as a fixnum?
- (define fixnum-range?
- (let ((-two^29 (- (expt 2 29)))
- (two^29-1 (- (expt 2 29) 1)))
- (lambda (x)
- (<= -two^29 x two^29-1))))
- ; Does the integer x fit in the immediate field of an instruction?
- (define (immediate-literal? x)
- (<= -4096 x 4095))
- ; Return the offset in the %GLOBALS table of the given memory-mapped
- ; register. A memory-mapped register is represented by an integer which
- ; is its offet, so just return the value.
- (define (swreg-global-offset r) r)
- ; Return a bit representation of a character constant.
- (define (char->immediate c)
- (+ (* (char->integer c) 65536) $imm.character))
- ; Convert an integer to a fixnum.
- (define (thefixnum x) (* x 4))
- ; The offset of data slot 'n' within a procedure structure, not adjusting
- ; for tag. The proc is a header followed by code, const, and then data.
- (define (procedure-slot-offset n)
- (+ 12 (* n 4)))
- ; Src is a register, hwreg is a hardware register. If src is a
- ; hardware register, return src. Otherwise, emit an instruction to load
- ; src into hwreg and return hwreg.
- (define (force-hwreg! as src hwreg)
- (if (hardware-mapped? src)
- src
- (emit-load-reg! as src hwreg)))
- ; Given an arbitrary constant opd, generate code to load it into a
- ; register r.
- (define (emit-constant->register as opd r)
- (cond ((and (integer? opd) (exact? opd))
- (if (fixnum-range? opd)
- (emit-immediate->register! as (thefixnum opd) r)
- (emit-const->register! as (emit-datum as opd) r)))
- ((boolean? opd)
- (emit-immediate->register! as
- (if (eq? opd #t)
- $imm.true
- $imm.false)
- r))
- ((equal? opd (eof-object))
- (emit-immediate->register! as $imm.eof r))
- ((equal? opd (unspecified))
- (emit-immediate->register! as $imm.unspecified r))
- ((equal? opd (undefined))
- (emit-immediate->register! as $imm.undefined r))
- ((null? opd)
- (emit-immediate->register! as $imm.null r))
- ((char? opd)
- (emit-immediate->register! as (char->immediate opd) r))
- (else
- (emit-const->register! as (emit-datum as opd) r))))
- ; Stuff a bitpattern or symbolic expression into a register.
- ; (CONST, for immediate constants.)
- ;
- ; FIXME(?): if this had access to eval-expr (currently hidden inside the
- ; sparc assembler) it could attempt to evaluate symbolic expressions,
- ; thereby selecting better code sequences when possible.
- (define (emit-immediate->register! as i r)
- (let ((dest (if (not (hardware-mapped? r)) $r.tmp0 r)))
- (cond ((and (number? i) (immediate-literal? i))
- (sparc.set as i dest))
- ((and (number? i) (zero? (remainder (abs i) 1024)))
- (sparc.sethi as `(hi ,i) dest))
- (else
- (sparc.sethi as `(hi ,i) dest)
- (sparc.ori as dest `(lo ,i) dest)))
- (if (not (hardware-mapped? r))
- (emit-store-reg! as r dest))))
- ; Reference the constants vector and put the constant reference in a register.
- ; `offset' is an integer offset into the constants vector (a constant) for
- ; the current procedure.
- ; Destroys $r.tmp0 and $r.tmp1, but either can be the destination register.
- ; (CONST, for structured constants, GLOBAL, SETGLBL, LAMBDA).
- (define (emit-const->register! as offset r)
- (let ((cvlabel (+ 4 (- (* offset 4) $tag.vector-tag))))
- (cond ((hardware-mapped? r)
- (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
- (if (asm:fits? cvlabel 13)
- (sparc.ldi as $r.tmp0 cvlabel r)
- (begin (sparc.sethi as `(hi ,cvlabel) $r.tmp1)
- (sparc.addr as $r.tmp0 $r.tmp1 $r.tmp0)
- (sparc.ldi as $r.tmp0 `(lo ,cvlabel) r))))
- (else
- (emit-const->register! as offset $r.tmp0)
- (emit-store-reg! as $r.tmp0 r)))))
- ; Emit single instruction to load sw-mapped reg into another reg, and return
- ; the destination reg.
- (define (emit-load-reg! as from to)
- (if (or (hardware-mapped? from) (not (hardware-mapped? to)))
- (asm-error "emit-load-reg: " from to)
- (begin (sparc.ldi as $r.globals (swreg-global-offset from) to)
- to)))
- (define (emit-store-reg! as from to)
- (if (or (not (hardware-mapped? from)) (hardware-mapped? to))
- (asm-error "emit-store-reg: " from to)
- (begin (sparc.sti as from (swreg-global-offset to) $r.globals)
- to)))
- ; Generic move-reg-to-HW-reg
- (define (emit-move2hwreg! as from to)
- (if (hardware-mapped? from)
- (sparc.move as from to)
- (emit-load-reg! as from to))
- to)
- ; Evaluation of condition code for value or control.
- ;
- ; branchf.a is an annulled conditional branch that tests the condition codes
- ; and branches if some condition is false.
- ; rd is #f or a hardware register.
- ; target is #f or a label.
- ; Exactly one of rd and target must be #f.
- ;
- ; (Why isn't this split into two separate procedures? Because dozens of
- ; this procedure's callers have the value/control duality, and it saves
- ; space to put the test here instead of putting it in each caller.)
- (define (emit-evaluate-cc! as branchf.a rd target)
- (if target
- (begin (branchf.a as target)
- (sparc.slot as))
- (let ((target (new-label)))
- (branchf.a as target)
- (sparc.set as $imm.false rd)
- (sparc.set as $imm.true rd)
- (sparc.label as target))))
- ; Code for runtime safety checking.
- (define (emit-check! as rs0 L1 liveregs)
- (sparc.cmpi as rs0 $imm.false)
- (emit-checkcc! as sparc.be L1 liveregs))
- ; FIXME: This should call the exception handler for non-continuable exceptions.
- (define (emit-trap! as rs1 rs2 rs3 exn)
- (if (not (= rs3 $r.reg0))
- (emit-move2hwreg! as rs3 $r.argreg3))
- (if (not (= rs2 $r.reg0))
- (emit-move2hwreg! as rs2 $r.argreg2))
- (if (not (= rs1 $r.reg0))
- (emit-move2hwreg! as rs1 $r.result))
- (millicode-call/numarg-in-reg as $m.exception (thefixnum exn) $r.tmp0))
- ; Given:
- ; an annulled conditional branch that branches
- ; if the check is ok
- ; a non-annulled conditional branch that branches
- ; if the check is not ok
- ; #f, or a procedure that takes an assembly segment as
- ; argument and emits an instruction that goes into
- ; the delay slot of either branch
- ; three registers whose contents should be passed to the
- ; exception handler if the check is not ok
- ; the exception code
- ; Emits code to call the millicode exception routine with
- ; the given exception code if the condition is false.
- ;
- ; FIXME: The nop can often be replaced by the instruction that
- ; follows it.
- (begin
- '
- (define (emit-checkcc-and-fill-slot!
- as branch-ok.a branch-bad slot-filler L1)
- (let* ((situation (list exn rs1 rs2 rs3))
- (L1 (exception-label as situation)))
- (if L1
- (begin (branch-bad as L1)
- (if slot-filler
- (slot-filler as)
- (sparc.nop as)))
- (let* ((L1 (new-label))
- (L2 (new-label)))
- (exception-label-set! as situation L1)
- (branch-ok.a as L2)
- (if slot-filler
- (slot-filler as)
- (sparc.slot as))
- (sparc.label as L1)
- (cond ((= rs3 $r.reg0)
- #f)
- ((hardware-mapped? $r.argreg3)
- (emit-move2hwreg! as rs3 $r.argreg3))
- ((hardware-mapped? rs3)
- (emit-store-reg! as rs3 $r.argreg3))
- (else
- (emit-move2hwreg! as rs3 $r.tmp0)
- (emit-store-reg! as $r.tmp0 $r.argreg3)))
- (if (not (= rs2 $r.reg0))
- (emit-move2hwreg! as rs2 $r.argreg2))
- (if (not (= rs1 $r.reg0))
- (emit-move2hwreg! as rs1 $r.result))
- ; FIXME: This should be a non-continuable exception.
- (sparc.jmpli as $r.millicode $m.exception $r.o7)
- (emit-immediate->register! as (thefixnum exn) $r.tmp0)
- (sparc.label as L2)))))
- #f
- )
- (define (emit-checkcc! as branch-bad L1 liveregs)
- (branch-bad as L1)
- (apply sparc.slot2 as liveregs))
- ; Generation of millicode calls for non-continuable exceptions.
- (begin
- '
- ; To create only one millicode call per code segment per non-continuable
- ; exception situation, we use the "as-user" feature of assembly segments.
- ; Could use a hash table here.
- (define (exception-label as situation)
- (let ((user-data (as-user as)))
- (if user-data
- (let ((exception-labels (assq 'exception-labels user-data)))
- (if exception-labels
- (let ((probe (assoc situation (cdr exception-labels))))
- (if probe
- (cdr probe)
- #f))
- #f))
- #f)))
- '
- (define (exception-label-set! as situation label)
- (let ((user-data (as-user as)))
- (if user-data
- (let ((exception-labels (assq 'exception-labels user-data)))
- (if exception-labels
- (let ((probe (assoc situation (cdr exception-labels))))
- (if probe
- (error "COMPILER BUG: Exception situation defined twice")
- (set-cdr! exception-labels
- (cons (cons situation label)
- (cdr exception-labels)))))
- (begin (as-user! as
- (cons (list 'exception-labels)
- user-data))
- (exception-label-set! as situation label))))
- (begin (as-user! as '())
- (exception-label-set! as situation label)))))
- #f
- )
- ; Millicode calling
- (define (millicode-call/0arg as mproc)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (sparc.nop as))
- (define (millicode-call/1arg as mproc r)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (emit-move2hwreg! as r $r.argreg2))
- (define (millicode-call/1arg-in-result as mproc r)
- (millicode-call/1arg-in-reg as mproc r $r.result))
- (define (millicode-call/1arg-in-reg as mproc rs rd)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (emit-move2hwreg! as rs rd))
- (define (millicode-call/numarg-in-result as mproc num)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (sparc.set as num $r.result))
- (define (millicode-call/numarg-in-reg as mproc num reg)
- (if (not (hardware-mapped? reg))
- (asm-error "millicode-call/numarg-in-reg requires HW register: " reg))
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (sparc.set as num reg))
- (define (millicode-call/2arg as mproc r1 r2)
- (emit-move2hwreg! as r1 $r.argreg2)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (emit-move2hwreg! as r2 $r.argreg3))
- ; NOTE: Don't use TMP0 since TMP0 is sometimes a millicode argument
- ; register (for example to m_exception).
- ;
- ; NOTE: Don't use sparc.set rather than sethi/ori; we need to know that
- ; two instructions get generated.
- ;
- ; FIXME: Should calculate the value if possible to get better precision
- ; and to avoid generating a fixup. See emit-return-address! in gen-msi.sch.
- (define (millicode-call/ret as mproc label)
- (cond ((short-effective-addresses)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (sparc.addi as $r.o7 `(- ,label (- ,(here as) 4) 8) $r.o7))
- (else
- (let ((val `(- ,label (+ ,(here as) 8) 8)))
- (sparc.sethi as `(hi ,val) $r.tmp1)
- (sparc.ori as $r.tmp1 `(lo ,val) $r.tmp1)
- (sparc.jmpli as $r.millicode mproc $r.o7)
- (sparc.addr as $r.o7 $r.tmp1 $r.o7)))))
- (define (check-timer as DESTINATION RETRY)
- (sparc.subicc as $r.timer 1 $r.timer)
- (sparc.bne.a as DESTINATION)
- (sparc.slot as)
- (millicode-call/ret as $m.timer-exception RETRY))
- ; When the destination and retry labels are the same, and follow the
- ; timer check immediately, then this code saves two static instructions.
- (define (check-timer0 as)
- (sparc.subicc as $r.timer 1 $r.timer)
- (sparc.bne.a as (+ (here as) 16))
- (sparc.slot as)
- (sparc.jmpli as $r.millicode $m.timer-exception $r.o7)
- (sparc.nop as))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 9 May 1999 / wdc
- ;
- ; SPARC machine assembler.
- ;
- ; The procedure `sparc-instruction' takes an instruction class keyword and
- ; some operands and returns an assembler procedure for the instruction
- ; denoted by the class and the operands.
- ;
- ; All assembler procedures for SPARC mnemonics are defined in sparcasm2.sch.
- ;
- ; The SPARC has 32-bit, big-endian words. All instructions are 1 word.
- ; This assembler currently accepts a subset of the SPARC v8 instruction set.
- ;
- ; Each assembler procedure takes an `as' assembly structure (see
- ; Asm/Common/pass5p1.sch) and operands relevant to the instruction, and
- ; side-effects the assembly structure by emitting bits for the instruction
- ; and any necessary fixups. There are separate instruction mnemonics and
- ; assembler procedures for instructions which in the SPARC instruction set
- ; are normally considered the "same". For example, the `add' instruction is
- ; split into two operations here: `sparc.addr' takes a register as operand2,
- ; and `sparc.addi' takes an immediate. We could remove this restriction
- ; by using objects with identity rather than numbers for registers, but it
- ; does not seem to be an important problem.
- ;
- ; Operands that denote values (addresses, immediates, offsets) may be
- ; expressed using symbolic expressions. These expressions must conform
- ; to the following grammar:
- ;
- ; <expr> --> ( <number> . <obj> ) ; label
- ; | <number> ; literal value (exact integer)
- ; | (+ <expr> ... ) ; sum
- ; | (- <expr> ... ) ; difference
- ; | (hi <expr>) ; high 22 bits
- ; | (lo <expr>) ; low 10 bits
- ;
- ; Each assembler procedure will check that its value operand(s) fit in
- ; their instruction fields. It is a fatal error for an operand not
- ; to fit, and the assembler calls `asm-error' to signal this error.
- ; However, in some cases the assembler will instead call the error
- ; procedure `asm-value-too-large', which allows the higher-level assembler
- ; to retry the assembly with different settings (typically, by splitting
- ; a jump instruction into an offset calculation and a jump).
- ;
- ; Note: the idiom that is seen in this file,
- ; (emit-fixup-proc! as (lambda (b l) (fixup b l)))
- ; when `fixup' is a local procedure, avoids allocation of the closure
- ; except in the cases where the fixup is in fact needed, for gains in
- ; speed and reduction in allocation. (Ask me if you want numbers.)
- ;
- ; If FILL-DELAY-SLOTS returns true, then this assembler supports two
- ; distinct mechanisms for filling branch delay slots.
- ;
- ; An annulled conditional branch or an un-annulled unconditional branch
- ; may be followed by the strange instruction SPARC.SLOT, which turns into
- ; a nop in the delay slot that may be replaced by copying the instruction
- ; at the target of the branch into the delay slot and increasing the branch
- ; offset by 4.
- ;
- ; An un-annulled conditional branch whose target depends upon a known set
- ; of general registers, and does not depend upon the condition codes, may
- ; be followed by the strange instruction SPARC.SLOT2, which takes any
- ; number of registers as operands. This strange instruction turns into
- ; nothing at all if the following instruction has no side effects except
- ; to the condition codes and/or to a destination register that is distinct
- ; from the specified registers plus the stack pointer and %o7; otherwise
- ; the SPARC.SLOT2 instruction becomes a nop in the delay slot. The
- ; implementation of this uses a buffer that must be cleared when a label
- ; is emitted or when the current offset is obtained.
- (define sparc-instruction)
- (let ((original-emit-label! emit-label!)
- (original-here here))
- (set! emit-label!
- (lambda (as L)
- (assembler-value! as 'slot2-info #f)
- (original-emit-label! as L)))
- (set! here
- (lambda (as)
- (assembler-value! as 'slot2-info #f)
- (original-here as)))
- 'emit-label!)
- (let ((emit! (lambda (as bits)
- (assembler-value! as 'slot2-info #f)
- (emit! as bits)))
- (emit-fixup-proc! (lambda (as proc)
- (assembler-value! as 'slot2-info #f)
- (emit-fixup-proc! as proc)))
- (goes-in-delay-slot2? (lambda (as rd)
- (let ((regs (assembler-value as 'slot2-info)))
- (and regs
- (fill-delay-slots)
- (not (= rd $r.stkp))
- (not (= rd $r.o7))
- (not (memv rd regs)))))))
-
- (define ibit (asm:bv 0 0 #x20 0)) ; immediate bit: 2^13
- (define abit (asm:bv #x20 0 0 0)) ; annul bit: 2^29
- (define zero (asm:bv 0 0 0 0)) ; all zero bits
-
- (define two^32 (expt 2 32))
-
- ; Constant expression evaluation. If the expression cannot be
- ; evaluated, eval-expr returns #f, otherwise a number.
- ; The symbol table lookup must fail by returning #f.
-
- (define (eval-expr as e)
-
- (define (complement x)
- (modulo (+ two^32 x) two^32))
-
- (define (hibits e)
- (cond ((not e) e)
- ((< e 0)
- (complement (quotient (complement e) 1024)))
- (else
- (quotient e 1024))))
-
- (define (lobits e)
- (cond ((not e) e)
- ((< e 0)
- (remainder (complement e) 1024))
- (else
- (remainder e 1024))))
-
- (define (evaluate e)
- (cond ((integer? e) e)
- ((label? e) (label-value as e))
- ((eq? 'hi (car e)) (hibits (evaluate (cadr e))))
- ((eq? 'lo (car e)) (lobits (evaluate (cadr e))))
- ((eq? '+ (car e))
- (let loop ((e (cdr e)) (s 0))
- (if (null? e) s
- (let ((op (evaluate (car e))))
- (if (not op) op
- (loop (cdr e) (+ s op)))))))
- ((eq? '- (car e))
- (let loop ((e (cdr e)) (d #f))
- (if (null? e) d
- (let ((op (evaluate (car e))))
- (if (not op) op
- (loop (cdr e) (if d (- d op) op)))))))
- (else
- (signal-error 'badexpr e))))
-
- (evaluate e))
-
- ; Common error handling.
-
- (define (signal-error code . rest)
- (define msg "SPARC assembler: ")
- (case code
- ((badexpr)
- (asm-error msg "invalid expression " (car rest)))
- ((toolarge)
- (asm-error msg "value too large in " (car rest) ": "
- (cadr rest) " = " (caddr rest)))
- ((fixup)
- (asm-error msg "fixup failed in " (car rest) " for " (cadr rest)))
- ((unaligned)
- (asm-error msg "unaligned target in " (car rest) ": " (cadr rest)))
- (else
- (error "Invalid error code in assembler: " code))))
-
- ; The following procedures construct instructions by depositing field
- ; values directly into bytevectors; the location parameter in the dep-*!
- ; procedures is the address in the bytevector of the most significant byte.
-
- (define (copy! bv k bits)
- (bytevector-set! bv k (bytevector-ref bits 0))
- (bytevector-set! bv (+ k 1) (bytevector-ref bits 1))
- (bytevector-set! bv (+ k 2) (bytevector-ref bits 2))
- (bytevector-set! bv (+ k 3) (bytevector-ref bits 3))
- bv)
-
- (define (copy bits)
- (let ((bv (make-bytevector 4)))
- (bytevector-set! bv 0 (bytevector-ref bits 0))
- (bytevector-set! bv 1 (bytevector-ref bits 1))
- (bytevector-set! bv 2 (bytevector-ref bits 2))
- (bytevector-set! bv 3 (bytevector-ref bits 3))
- bv))
-
- (define (copy-instr bv from to)
- (bytevector-set! bv to (bytevector-ref bv from))
- (bytevector-set! bv (+ to 1) (bytevector-ref bv (+ from 1)))
- (bytevector-set! bv (+ to 2) (bytevector-ref bv (+ from 2)))
- (bytevector-set! bv (+ to 3) (bytevector-ref bv (+ from 3))))
-
- (define (dep-rs1! bits k rs1)
- (bytevector-set! bits (+ k 1)
- (logior (bytevector-ref bits (+ k 1))
- (rshl rs1 2)))
- (bytevector-set! bits (+ k 2)
- (logior (bytevector-ref bits (+ k 2))
- (lsh (logand rs1 3) 6))))
-
- (define (dep-rs2! bits k rs2)
- (bytevector-set! bits (+ k 3)
- (logior (bytevector-ref bits (+ k 3)) rs2)))
-
- (define (dep-rd! bits k rd)
- (bytevector-set! bits k
- (logior (bytevector-ref bits k) (lsh rd 1))))
-
- (define (dep-imm! bits k imm)
- (cond ((fixnum? imm)
- (bytevector-set! bits (+ k 3) (logand imm 255))
- (bytevector-set! bits (+ k 2)
- (logior (bytevector-ref bits (+ k 2))
- (logand (rsha imm 8) 31))))
- ((bytevector? imm)
- (bytevector-set! bits (+ k 3) (bytevector-ref imm 0))
- (bytevector-set! bits (+ k 2)
- (logior (bytevector-ref bits (+ k 2))
- (logand (bytevector-ref imm 1)
- 31))))
- (else
- (dep-imm! bits k (asm:int->bv imm)))))
-
- (define (dep-branch-offset! bits k offs)
- (cond ((fixnum? offs)
- (if (not (= (logand offs 3) 0))
- (signal-error 'unaligned "branch" offs))
- (dep-imm22! bits k (rsha offs 2)))
- ((bytevector? offs)
- (if (not (= (logand (bytevector-ref offs 3) 3) 0))
- (signal-error 'unaligned "branch" (asm:bv->int offs)))
- (dep-imm22! bits k (asm:rsha offs 2)))
- (else
- (dep-branch-offset! bits k (asm:int->bv offs)))))
-
- (define (dep-imm22! bits k imm)
- (cond ((fixnum? imm)
- (bytevector-set! bits (+ k 3) (logand imm 255))
- (bytevector-set! bits (+ k 2)
- (logand (rsha imm 8) 255))
- (bytevector-set! bits (+ k 1)
- (logior (bytevector-ref bits (+ k 1))
- (logand (rsha imm 16) 63))))
- ((bytevector? imm)
- (bytevector-set! bits (+ k 3) (bytevector-ref imm 3))
- (bytevector-set! bits (+ k 2) (bytevector-ref imm 2))
- (bytevector-set! bits (+ k 1)
- (logior (bytevector-ref bits (+ k 1))
- (logand (bytevector-ref imm 1)
- 63))))
- (else
- (dep-imm22! bits k (asm:int->bv imm)))))
-
- (define (dep-call-offset! bits k offs)
- (cond ((fixnum? offs)
- (if (not (= (logand offs 3) 0))
- (signal-error 'unaligned "call" offs))
- (bytevector-set! bits (+ k 3) (logand (rsha offs 2) 255))
- (bytevector-set! bits (+ k 2) (logand (rsha offs 10) 255))
- (bytevector-set! bits (+ k 1) (logand (rsha offs 18) 255))
- (bytevector-set! bits k (logior (bytevector-ref bits k)
- (logand (rsha offs 26) 63))))
- ((bytevector? offs)
- (if (not (= (logand (bytevector-ref offs 3) 3) 0))
- (signal-error 'unaligned "call" (asm:bv->int offs)))
- (let ((offs (asm:rsha offs 2)))
- (bytevector-set! bits (+ k 3) (bytevector-ref offs 3))
- (bytevector-set! bits (+ k 2) (bytevector-ref offs 2))
- (bytevector-set! bits (+ k 1) (bytevector-ref offs 1))
- (bytevector-set! bits k (logior (bytevector-ref bits k)
- (logand (bytevector-ref offs 0)
- 63)))))
- (else
- (dep-call-offset! bits k (asm:int->bv offs)))))
-
- ; Add 1 to an instruction (to bump a branch offset by 4).
- ; FIXME: should check for field overflow.
-
- (define (add1 bv loc)
- (let* ((r0 (+ (bytevector-ref bv (+ loc 3)) 1))
- (d0 (logand r0 255))
- (c0 (rshl r0 8)))
- (bytevector-set! bv (+ loc 3) d0)
- (let* ((r1 (+ (bytevector-ref bv (+ loc 2)) c0))
- (d1 (logand r1 255))
- (c1 (rshl r1 8)))
- (bytevector-set! bv (+ loc 2) d1)
- (let* ((r2 (+ (bytevector-ref bv (+ loc 1)) c1))
- (d2 (logand r2 255)))
- (bytevector-set! bv (+ loc 1) d2)))))
-
- ; For delay slot filling -- uses the assembler value scratchpad in
- ; the as structure. Delay slot filling is discussed in the comments
- ; for `branch' and `class-slot', below.
-
- (define (remember-branch-target as obj)
- (assembler-value! as 'branch-target obj))
-
- (define (recover-branch-target as)
- (assembler-value as 'branch-target))
-
- ; Mark the instruction at the current address as not being eligible
- ; for being lifted into a branch delay slot.
- ;
- ; FIXME: should perhaps be a hash table; see BOOT-STATUS file for details.
-
- (define (not-a-delay-slot-instruction as)
- (assembler-value! as 'not-dsi
- (cons (here as)
- (or (assembler-value as 'not-dsi) '()))))
-
- (define (is-a-delay-slot-instruction? as bv addr)
- (and (not (memv addr (or (assembler-value as 'not-dsi) '())))
- (< addr (bytevector-length bv))))
-
- ; SETHI, etc.
-
- (define (class-sethi bits)
- (let ((bits (asm:lsh bits 22)))
- (lambda (as val rd)
-
- (define (fixup bv loc)
- (dep-imm22! bv loc
- (or (eval-expr as val)
- (signal-error 'fixup "sethi" val))))
-
- (define (fixup2 bv loc)
- (copy! bv loc bits)
- (dep-rd! bv loc rd)
- (fixup bv loc))
-
- (if (goes-in-delay-slot2? as rd)
- (emit-fixup-proc! as
- (lambda (b l)
- (fixup2 b (- l 4))))
-
- (let ((bits (copy bits))
- (e (eval-expr as val)))
- (if e
- (dep-imm22! bits 0 e)
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (dep-rd! bits 0 rd)
- (emit! as bits))))))
-
- ; NOP is a peculiar sethi
-
- (define (class-nop i)
- (let ((instr (class-sethi i)))
- (lambda (as)
- (instr as 0 $r.g0))))
-
-
- ; Branches
-
- (define (class00b i) (branch #b010 i zero)) ; Un-annulled IU branches.
- (define (class00a i) (branch #b010 i abit)) ; Annulled IU branches.
- (define (classf00b i) (branch #b110 i zero)) ; Un-annulled FP branches.
- (define (classf00a i) (branch #b110 i abit)) ; Annulled FP branches.
-
- ; The `type' parameter is #b010 for IU branches, #b110 for FP branches.
- ; The `bits' parameter is the bits for the cond field.
- ; The `annul' parameter is either `zero' or `abit' (see top of file).
- ;
- ; Annuled branches require special treatement for delay slot
- ; filling based on the `slot' pseudo-instruction.
- ;
- ; Strategy: when a branch with the annul bit set is assembled, remember
- ; its target in a one-element cache in the AS structure. When a slot
- ; instruction is found (it has its own class) then the cached
- ; value (possibly a delayed expression) is gotten, and a fixup for the
- ; slot is registered. When the fixup is later evaluated, the branch
- ; target instruction can be found, examined, and evaluated.
- ;
- ; The cached value is always valid when the slot instruction is assembled,
- ; because a slot instruction is always directly preceded by an annulled
- ; branch (which will always set the cache).
-
- (define (branch type bits annul)
- ; The delay slot should be filled if this is an annulled branch
- ; or an unconditional branch.
- (let ((fill-delay-slot? (or (not (eq? annul zero))
- (eq? bits #b1000)))
- (bits (asm:logior (asm:lsh bits 25) (asm:lsh type 22) annul)))
- (lambda (as target0)
- (let ((target `(- ,target0 ,(here as))))
-
- (define (expr)
- (let ((e (eval-expr as target)))
- (cond ((not e)
- e)
- ((not (zero? (logand e 3)))
- (signal-error 'unaligned "branch" target0))
- ((asm:fits? e 24)
- e)
- (else
- (asm-value-too-large as "branch" target e)))))
-
- (define (fixup bv loc)
- (let ((e (expr)))
- (if e
- (dep-branch-offset! bv loc e)
- (signal-error 'fixup "branch" target0))))
-
- (if fill-delay-slot?
- (remember-branch-target as target0)
- (remember-branch-target as #f)) ; Clears the cache.
- (not-a-delay-slot-instruction as)
- (let ((bits (copy bits))
- (e (expr)))
- (if e
- (dep-branch-offset! bits 0 e)
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (emit! as bits))))))
-
- ; Branch delay slot pseudo-instruction.
- ;
- ; Get the branch target expression from the cache in the AS structure,
- ; and if it is not #f, register a fixup procedure for the delay slot that
- ; will copy the target instruction to the slot and add 4 to the branch
- ; offset (unless that will overflow the offset or the instruction at the
- ; target is not suitable for lifting).
- ;
- ; It's important that this fixup run _after_ any fixups for the branch
- ; instruction itself!
-
- (define (class-slot)
- (let ((nop-instr (class-nop #b100)))
- (lambda (as)
-
- ; The branch target is the expression denoting the target location.
-
- (define branch-target (recover-branch-target as))
-
- (define (fixup bv loc)
- (let ((bt (or (eval-expr as branch-target)
- (asm-error "Branch fixup: can't happen: "
- branch-target))))
- (if (is-a-delay-slot-instruction? as bv bt)
- (begin
- (copy-instr bv bt loc)
- (add1 bv (- loc 4))))))
-
- (if (and branch-target (fill-delay-slots))
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (nop-instr as))))
-
- ; Branch delay slot pseudo-instruction 2.
- ;
- ; Emit a nop, but record the information that will allow this nop to be
- ; replaced by a sufficiently harmless ALU instruction.
-
- (define (class-slot2)
- (let ((nop-instr (class-nop #b100)))
- (lambda (as . regs)
- (nop-instr as)
- (assembler-value! as 'slot2-info regs))))
-
- ; ALU stuff, register operand, rdy, wryr. Also: jump.
-
- (define (class10r bits . extra)
- (cond ((and (not (null? extra)) (eq? (car extra) 'rdy))
- (let ((op (class10r bits)))
- (lambda (as rd)
- (op as 0 0 rd))))
- ((and (not (null? extra)) (eq? (car extra) 'wry))
- (let ((op (class10r bits)))
- (lambda (as rs)
- (op as rs 0 0))))
- (else
- (let ((bits (asm:logior (asm:lsh #b10 30) (asm:lsh bits 19)))
- (jump? (and (not (null? extra)) (eq? (car extra) 'jump))))
- (lambda (as rs1 rs2 rd)
- (let ((bits (copy bits)))
- (dep-rs1! bits 0 rs1)
- (dep-rs2! bits 0 rs2)
- (dep-rd! bits 0 rd)
- (cond (jump?
- (not-a-delay-slot-instruction as)
- (emit! as bits))
- ((goes-in-delay-slot2? as rd)
- (emit-fixup-proc!
- as
- (lambda (bv loc)
- (copy! bv (- loc 4) bits))))
- (else
- (emit! as bits)))))))))
-
-
- ; ALU stuff, immediate operand, wryi. Also: jump.
-
- (define (class10i bits . extra)
- (if (and (not (null? extra)) (eq? (car extra) 'wry))
- (let ((op (class10i bits)))
- (lambda (as src)
- (op as 0 src 0)))
- (let ((bits (asm:logior (asm:lsh #b10 30) (asm:lsh bits 19) ibit))
- (jump? (and (not (null? extra)) (eq? (car extra) 'jump))))
- (lambda (as rs1 e rd)
-
- (define (expr)
- (let ((imm (eval-expr as e)))
- (cond ((not imm)
- imm)
- ((asm:fits? imm 13)
- imm)
- (jump?
- (asm-value-too-large as "`jmpli'" e imm))
- (else
- (asm-value-too-large as "ALU instruction" e imm)))))
-
- (define (fixup bv loc)
- (let ((e (expr)))
- (if e
- (dep-imm! bv loc e)
- (signal-error 'fixup "ALU instruction" e))))
-
- (let ((bits (copy bits))
- (e (expr)))
- (if e
- (dep-imm! bits 0 e)
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (dep-rs1! bits 0 rs1)
- (dep-rd! bits 0 rd)
- (cond (jump?
- (not-a-delay-slot-instruction as)
- (emit! as bits))
- ((goes-in-delay-slot2? as rd)
- (emit-fixup-proc!
- as
- (lambda (bv loc)
- (copy! bv (- loc 4) bits))))
- (else
- (emit! as bits))))))))
-
- ; Memory stuff, register operand.
-
- (define (class11r bits)
- (let ((bits (asm:logior (asm:lsh #b11 30) (asm:lsh bits 19))))
- (lambda (as rs1 rs2 rd)
- (let ((bits (copy bits)))
- (dep-rs1! bits 0 rs1)
- (dep-rs2! bits 0 rs2)
- (dep-rd! bits 0 rd)
- (emit! as bits)))))
-
- ; Memory stuff, immediate operand.
-
- (define (class11i bits)
- (let ((bits (asm:logior (asm:lsh #b11 30) (asm:lsh bits 19) ibit)))
- (lambda (as rs1 e rd)
-
- (define (expr)
- (let ((imm (eval-expr as e)))
- (cond ((not imm) imm)
- ((asm:fits? imm 13) imm)
- (else
- (signal-error 'toolarge "Memory instruction" e imm)))))
-
- (define (fixup bv loc)
- (let ((e (expr)))
- (if e
- (dep-imm! bv loc e)
- (signal-error 'fixup "Memory instruction" e))))
-
- (let ((bits (copy bits))
- (e (expr)))
- (dep-rs1! bits 0 rs1)
- (dep-rd! bits 0 rd)
- (if e
- (dep-imm! bits 0 e)
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (emit! as bits)))))
-
- ; For store instructions. The syntax is (st a b c) meaning m[ b+c ] <- a.
- ; However, on the Sparc, the destination (rd) field is the source of
- ; a store, so we transform the instruction into (st c b a) and pass it
- ; to the real store procedure.
-
- (define (class11sr bits)
- (let ((store-instr (class11r bits)))
- (lambda (as a b c)
- (store-instr as c b a))))
-
- (define (class11si bits)
- (let ((store-instr (class11i bits)))
- (lambda (as a b c)
- (store-instr as c b a))))
-
- ; Call is a class all by itself.
-
- (define (class-call)
- (let ((code (asm:lsh #b01 30)))
- (lambda (as target0)
- (let ((target `(- ,target0 ,(here as))))
-
- (define (fixup bv loc)
- (let ((e (eval-expr as target)))
- (if e
- (dep-call-offset! bv loc e)
- (signal-error 'fixup "call" target0))))
-
- (let ((bits (copy code))
- (e (eval-expr as target)))
- (not-a-delay-slot-instruction as)
- (if e
- (dep-call-offset! bits 0 e)
- (emit-fixup-proc! as (lambda (b l) (fixup b l))))
- (emit! as bits))))))
-
- (define (class-label)
- (lambda (as label)
- (emit-label! as label)))
-
- ; FP operation, don't set CC.
-
- (define (class-fpop1 i) (fpop #b110100 i))
-
- ; FP operation, set CC
-
- (define (class-fpop2 i) (fpop #b110101 i))
-
- (define (fpop type opf)
- (let ((bits (asm:logior (asm:lsh #b10 30)
- (asm:lsh type 19)
- (asm:lsh opf 5))))
- (lambda (as rs1 rs2 rd)
- (let ((bits (copy bits)))
- (dep-rs1! bits 0 rs1)
- (dep-rs2! bits 0 rs2)
- (dep-rd! bits 0 rd)
- (emit! as bits)))))
-
- (set! sparc-instruction
- (lambda (kwd . ops)
- (case kwd
- ((i11) (apply class11i ops))
- ((r11) (apply class11r ops))
- ((si11) (apply class11si ops))
- ((sr11) (apply class11sr ops))
- ((sethi) (apply class-sethi ops))
- ((r10) (apply class10r ops))
- ((i10) (apply class10i ops))
- ((b00) (apply class00b ops))
- ((a00) (apply class00a ops))
- ((call) (apply class-call ops))
- ((label) (apply class-label ops))
- ((nop) (apply class-nop ops))
- ((slot) (apply class-slot ops))
- ((slot2) (apply class-slot2 ops))
- ((fb00) (apply classf00b ops))
- ((fa00) (apply classf00a ops))
- ((fp) (apply class-fpop1 ops))
- ((fpcc) (apply class-fpop2 ops))
- (else
- (asm-error "sparc-instruction: unrecognized class: " kwd)))))
- 'sparc-instruction)
- ; eof
- ; Instruction mnemonics
- (define sparc.lddi (sparc-instruction 'i11 #b000011))
- (define sparc.lddr (sparc-instruction 'r11 #b000011))
- (define sparc.ldi (sparc-instruction 'i11 #b000000))
- (define sparc.ldr (sparc-instruction 'r11 #b000000))
- (define sparc.ldhi (sparc-instruction 'i11 #b000010))
- (define sparc.ldhr (sparc-instruction 'r11 #b000010))
- (define sparc.ldbi (sparc-instruction 'i11 #b000001))
- (define sparc.ldbr (sparc-instruction 'r11 #b000001))
- (define sparc.lddfi (sparc-instruction 'i11 #b100011))
- (define sparc.lddfr (sparc-instruction 'r11 #b100011))
- (define sparc.stdi (sparc-instruction 'si11 #b000111))
- (define sparc.stdr (sparc-instruction 'sr11 #b000111))
- (define sparc.sti (sparc-instruction 'si11 #b000100))
- (define sparc.str (sparc-instruction 'sr11 #b000100))
- (define sparc.sthi (sparc-instruction 'si11 #b000110))
- (define sparc.sthr (sparc-instruction 'sr11 #b000110))
- (define sparc.stbi (sparc-instruction 'si11 #b000101))
- (define sparc.stbr (sparc-instruction 'sr11 #b000101))
- (define sparc.stdfi (sparc-instruction 'si11 #b100111))
- (define sparc.stdfr (sparc-instruction 'sr11 #b100111))
- (define sparc.sethi (sparc-instruction 'sethi #b100))
- (define sparc.andr (sparc-instruction 'r10 #b000001))
- (define sparc.andrcc (sparc-instruction 'r10 #b010001))
- (define sparc.andi (sparc-instruction 'i10 #b000001))
- (define sparc.andicc (sparc-instruction 'i10 #b010001))
- (define sparc.orr (sparc-instruction 'r10 #b000010))
- (define sparc.orrcc (sparc-instruction 'r10 #b010010))
- (define sparc.ori (sparc-instruction 'i10 #b000010))
- (define sparc.oricc (sparc-instruction 'i10 #b010010))
- (define sparc.xorr (sparc-instruction 'r10 #b000011))
- (define sparc.xorrcc (sparc-instruction 'r10 #b010011))
- (define sparc.xori (sparc-instruction 'i10 #b000011))
- (define sparc.xoricc (sparc-instruction 'i10 #b010011))
- (define sparc.sllr (sparc-instruction 'r10 #b100101))
- (define sparc.slli (sparc-instruction 'i10 #b100101))
- (define sparc.srlr (sparc-instruction 'r10 #b100110))
- (define sparc.srli (sparc-instruction 'i10 #b100110))
- (define sparc.srar (sparc-instruction 'r10 #b100111))
- (define sparc.srai (sparc-instruction 'i10 #b100111))
- (define sparc.addr (sparc-instruction 'r10 #b000000))
- (define sparc.addrcc (sparc-instruction 'r10 #b010000))
- (define sparc.addi (sparc-instruction 'i10 #b000000))
- (define sparc.addicc (sparc-instruction 'i10 #b010000))
- (define sparc.taddrcc (sparc-instruction 'r10 #b100000))
- (define sparc.taddicc (sparc-instruction 'i10 #b100000))
- (define sparc.subr (sparc-instruction 'r10 #b000100))
- (define sparc.subrcc (sparc-instruction 'r10 #b010100))
- (define sparc.subi (sparc-instruction 'i10 #b000100))
- (define sparc.subicc (sparc-instruction 'i10 #b010100))
- (define sparc.tsubrcc (sparc-instruction 'r10 #b100001))
- (define sparc.tsubicc (sparc-instruction 'i10 #b100001))
- (define sparc.smulr (sparc-instruction 'r10 #b001011))
- (define sparc.smulrcc (sparc-instruction 'r10 #b011011))
- (define sparc.smuli (sparc-instruction 'i10 #b001011))
- (define sparc.smulicc (sparc-instruction 'i10 #b011011))
- (define sparc.sdivr (sparc-instruction 'r10 #b001111))
- (define sparc.sdivrcc (sparc-instruction 'r10 #b011111))
- (define sparc.sdivi (sparc-instruction 'i10 #b001111))
- (define sparc.sdivicc (sparc-instruction 'i10 #b011111))
- (define sparc.b (sparc-instruction 'b00 #b1000))
- (define sparc.b.a (sparc-instruction 'a00 #b1000))
- (define sparc.bne (sparc-instruction 'b00 #b1001))
- (define sparc.bne.a (sparc-instruction 'a00 #b1001))
- (define sparc.be (sparc-instruction 'b00 #b0001))
- (define sparc.be.a (sparc-instruction 'a00 #b0001))
- (define sparc.bg (sparc-instruction 'b00 #b1010))
- (define sparc.bg.a (sparc-instruction 'a00 #b1010))
- (define sparc.ble (sparc-instruction 'b00 #b0010))
- (define sparc.ble.a (sparc-instruction 'a00 #b0010))
- (define sparc.bge (sparc-instruction 'b00 #b1011))
- (define sparc.bge.a (sparc-instruction 'a00 #b1011))
- (define sparc.bl (sparc-instruction 'b00 #b0011))
- (define sparc.bl.a (sparc-instruction 'a00 #b0011))
- (define sparc.bgu (sparc-instruction 'b00 #b1100))
- (define sparc.bgu.a (sparc-instruction 'a00 #b1100))
- (define sparc.bleu (sparc-instruction 'b00 #b0100))
- (define sparc.bleu.a (sparc-instruction 'a00 #b0100))
- (define sparc.bcc (sparc-instruction 'b00 #b1101))
- (define sparc.bcc.a (sparc-instruction 'a00 #b1101))
- (define sparc.bcs (sparc-instruction 'b00 #b0101))
- (define sparc.bcs.a (sparc-instruction 'a00 #b0101))
- (define sparc.bpos (sparc-instruction 'b00 #b1110))
- (define sparc.bpos.a (sparc-instruction 'a00 #b1110))
- (define sparc.bneg (sparc-instruction 'b00 #b0110))
- (define sparc.bneg.a (sparc-instruction 'a00 #b0110))
- (define sparc.bvc (sparc-instruction 'b00 #b1111))
- (define sparc.bvc.a (sparc-instruction 'a00 #b1111))
- (define sparc.bvs (sparc-instruction 'b00 #b0111))
- (define sparc.bvs.a (sparc-instruction 'a00 #b0111))
- (define sparc.call (sparc-instruction 'call))
- (define sparc.jmplr (sparc-instruction 'r10 #b111000 'jump))
- (define sparc.jmpli (sparc-instruction 'i10 #b111000 'jump))
- (define sparc.nop (sparc-instruction 'nop #b100))
- (define sparc.ornr (sparc-instruction 'r10 #b000110))
- (define sparc.orni (sparc-instruction 'i10 #b000110))
- (define sparc.ornrcc (sparc-instruction 'r10 #b010110))
- (define sparc.ornicc (sparc-instruction 'i10 #b010110))
- (define sparc.andni (sparc-instruction 'i10 #b000101))
- (define sparc.andnr (sparc-instruction 'r10 #b000101))
- (define sparc.andnicc (sparc-instruction 'i10 #b010101))
- (define sparc.andnrcc (sparc-instruction 'r10 #b010101))
- (define sparc.rdy (sparc-instruction 'r10 #b101000 'rdy))
- (define sparc.wryr (sparc-instruction 'r10 #b110000 'wry))
- (define sparc.wryi (sparc-instruction 'i10 #b110000 'wry))
- (define sparc.fb (sparc-instruction 'fb00 #b1000))
- (define sparc.fb.a (sparc-instruction 'fa00 #b1000))
- (define sparc.fbn (sparc-instruction 'fb00 #b0000))
- (define sparc.fbn.a (sparc-instruction 'fa00 #b0000))
- (define sparc.fbu (sparc-instruction 'fb00 #b0111))
- (define sparc.fbu.a (sparc-instruction 'fa00 #b0111))
- (define sparc.fbg (sparc-instruction 'fb00 #b0110))
- (define sparc.fbg.a (sparc-instruction 'fa00 #b0110))
- (define sparc.fbug (sparc-instruction 'fb00 #b0101))
- (define sparc.fbug.a (sparc-instruction 'fa00 #b0101))
- (define sparc.fbl (sparc-instruction 'fb00 #b0100))
- (define sparc.fbl.a (sparc-instruction 'fa00 #b0100))
- (define sparc.fbul (sparc-instruction 'fb00 #b0011))
- (define sparc.fbul.a (sparc-instruction 'fa00 #b0011))
- (define sparc.fblg (sparc-instruction 'fb00 #b0010))
- (define sparc.fblg.a (sparc-instruction 'fa00 #b0010))
- (define sparc.fbne (sparc-instruction 'fb00 #b0001))
- (define sparc.fbne.a (sparc-instruction 'fa00 #b0001))
- (define sparc.fbe (sparc-instruction 'fb00 #b1001))
- (define sparc.fbe.a (sparc-instruction 'fa00 #b1001))
- (define sparc.fbue (sparc-instruction 'fb00 #b1010))
- (define sparc.fbue.a (sparc-instruction 'fa00 #b1010))
- (define sparc.fbge (sparc-instruction 'fb00 #b1011))
- (define sparc.fbge.a (sparc-instruction 'fa00 #b1011))
- (define sparc.fbuge (sparc-instruction 'fb00 #b1100))
- (define sparc.fbuge.a (sparc-instruction 'fa00 #b1100))
- (define sparc.fble (sparc-instruction 'fb00 #b1101))
- (define sparc.fble.a (sparc-instruction 'fa00 #b1101))
- (define sparc.fbule (sparc-instruction 'fb00 #b1110))
- (define sparc.fbule.a (sparc-instruction 'fa00 #b1110))
- (define sparc.fbo (sparc-instruction 'fb00 #b1111))
- (define sparc.fbo.a (sparc-instruction 'fa00 #b1111))
- (define sparc.faddd (sparc-instruction 'fp #b001000010))
- (define sparc.fsubd (sparc-instruction 'fp #b001000110))
- (define sparc.fmuld (sparc-instruction 'fp #b001001010))
- (define sparc.fdivd (sparc-instruction 'fp #b001001110))
- (define sparc%fnegs (sparc-instruction 'fp #b000000101)) ; See below
- (define sparc%fmovs (sparc-instruction 'fp #b000000001)) ; See below
- (define sparc%fabss (sparc-instruction 'fp #b000001001)) ; See below
- (define sparc%fcmpdcc (sparc-instruction 'fpcc #b001010010)) ; See below
- ; Strange instructions.
- (define sparc.slot (sparc-instruction 'slot))
- (define sparc.slot2 (sparc-instruction 'slot2))
- (define sparc.label (sparc-instruction 'label))
- ; Aliases.
- (define sparc.bnz sparc.bne)
- (define sparc.bnz.a sparc.bne.a)
- (define sparc.bz sparc.be)
- (define sparc.bz.a sparc.be.a)
- (define sparc.bgeu sparc.bcc)
- (define sparc.bgeu.a sparc.bcc.a)
- (define sparc.blu sparc.bcs)
- (define sparc.blu.a sparc.bcs.a)
- ; Abstractions.
- (define (sparc.cmpr as r1 r2) (sparc.subrcc as r1 r2 $r.g0))
- (define (sparc.cmpi as r imm) (sparc.subicc as r imm $r.g0))
- (define (sparc.move as rs rd) (sparc.orr as $r.g0 rs rd))
- (define (sparc.set as imm rd) (sparc.ori as $r.g0 imm rd))
- (define (sparc.btsti as rs imm) (sparc.andicc as rs imm $r.g0))
- (define (sparc.clr as rd) (sparc.move as $r.g0 rd))
- (define (sparc.deccc as rs . rest)
- (let ((k (cond ((null? rest) 1)
- ((null? (cdr rest)) (car rest))
- (else (asm-error "sparc.deccc: too many operands: " rest)))))
- (sparc.subicc as rs k rs)))
- ; Floating-point abstractions
- ;
- ; For fmovd, fnegd, and fabsd, we must synthesize the instruction from
- ; fmovs, fnegs, and fabss -- SPARC V8 has only the latter. (SPARC V9 add
- ; the former.)
- (define (sparc.fmovd as rs rd)
- (sparc%fmovs as rs 0 rd)
- (sparc%fmovs as (+ rs 1) 0 (+ rd 1)))
- (define (sparc.fnegd as rs rd)
- (sparc%fnegs as rs 0 rd)
- (if (not (= rs rd))
- (sparc%fmovs as (+ rs 1) 0 (+ rd 1))))
- (define (sparc.fabsd as rs rd)
- (sparc%fabss as rs 0 rd)
- (if (not (= rs rd))
- (sparc%fmovs as (+ rs 1) 0 (+ rd 1))))
- (define (sparc.fcmpd as rs1 rs2)
- (sparc%fcmpdcc as rs1 rs2 0))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; Asm/Sparc/gen-msi.sch -- SPARC assembler code emitters for
- ; core MacScheme instructions
- ;
- ; 9 May 1999 / wdc
- ; SETGLBL
- ;
- ; RS must be a hardware register.
- ;
- ; A global cell is a pair, where the car holds the value.
- (define (emit-register->global! as rs offset)
- (cond ((= rs $r.result)
- (sparc.move as $r.result $r.argreg2)
- (emit-const->register! as offset $r.result)
- (if (write-barrier)
- (sparc.jmpli as $r.millicode $m.addtrans $r.o7))
- (sparc.sti as $r.argreg2 (- $tag.pair-tag) $r.result))
- (else
- (emit-const->register! as offset $r.result)
- (sparc.sti as rs (- $tag.pair-tag) $r.result)
- (if (write-barrier)
- (millicode-call/1arg as $m.addtrans rs)))))
- ; GLOBAL
- ;
- ; A global cell is a pair, where the car holds the value.
- ; If (catch-undefined-globals) is true, then code will be emitted to
- ; check whether the global is #!undefined when loaded. If it is,
- ; an exception will be taken, with the global in question in $r.result.
- (define (emit-global->register! as offset r)
- (emit-load-global as offset r (catch-undefined-globals)))
- ; This leaves the cell in ARGREG2. That fact is utilized by global/invoke
- ; to signal an appropriate error message.
- (define (emit-load-global as offset r check?)
-
- (define (emit-undef-check! as r)
- (if check?
- (let ((GLOBAL-OK (new-label)))
- (sparc.cmpi as r $imm.undefined)
- (sparc.bne.a as GLOBAL-OK)
- (sparc.slot as)
- (millicode-call/0arg as $m.global-ex) ; Cell in ARGREG2.
- (sparc.label as GLOBAL-OK))))
- (emit-const->register! as offset $r.argreg2) ; Load cell.
- (if (hardware-mapped? r)
- (begin (sparc.ldi as $r.argreg2 (- $tag.pair-tag) r)
- (emit-undef-check! as r))
- (begin (sparc.ldi as $r.argreg2 (- $tag.pair-tag) $r.tmp0)
- (emit-store-reg! as $r.tmp0 r)
- (emit-undef-check! as $r.tmp0))))
- ; MOVEREG
- (define (emit-register->register! as from to)
- (if (not (= from to))
- (cond ((and (hardware-mapped? from) (hardware-mapped? to))
- (sparc.move as from to))
- ((hardware-mapped? from)
- (emit-store-reg! as from to))
- ((hardware-mapped? to)
- (emit-load-reg! as from to))
- (else
- (emit-load-reg! as from $r.tmp0)
- (emit-store-reg! as $r.tmp0 to)))))
- ; ARGS=
- (define (emit-args=! as n)
- (if (not (unsafe-code))
- (let ((L2 (new-label)))
- (sparc.cmpi as $r.result (thefixnum n)) ; FIXME: limit 1023 args
- (sparc.be.a as L2)
- (sparc.slot as)
- (millicode-call/numarg-in-reg as $m.argc-ex (thefixnum n) $r.argreg2)
- (sparc.label as L2))))
- ; ARGS>=
- ;
- ; The cases for 0 and 1 rest arguments are handled in-line; all other
- ; cases, including too few, are handled in millicode (really: a C call-out).
- ;
- ; The fast path only applies when we don't have to mess with the last
- ; register, hence the test.
- (define (emit-args>=! as n)
- (let ((L0 (new-label))
- (L99 (new-label))
- (L98 (new-label)))
- (if (< n (- *lastreg* 1))
- (let ((dest (regname (+ n 1))))
- (sparc.cmpi as $r.result (thefixnum n)) ; n args
- (if (hardware-mapped? dest)
- (begin
- (sparc.be.a as L99)
- (sparc.set as $imm.null dest))
- (begin
- (sparc.set as $imm.null $r.tmp0)
- (sparc.be.a as L99)
- (sparc.sti as $r.tmp0 (swreg-global-offset dest) $r.globals)))
- (sparc.cmpi as $r.result (thefixnum (+ n 1))) ; n+1 args
- (sparc.bne.a as L98)
- (sparc.nop as)
- (millicode-call/numarg-in-result as $m.alloc 8)
- (let ((src1 (force-hwreg! as dest $r.tmp1)))
- (sparc.set as $imm.null $r.tmp0)
- (sparc.sti as src1 0 $r.result)
- (sparc.sti as $r.tmp0 4 $r.result)
- (sparc.addi as $r.result $tag.pair-tag $r.result)
- (sparc.b as L99)
- (if (hardware-mapped? dest)
- (sparc.move as $r.result dest)
- (sparc.sti as $r.result (swreg-global-offset dest)
- $r.globals)))))
- ; General case
- (sparc.label as L98)
- (sparc.move as $r.reg0 $r.argreg3) ; FIXME in Sparc/mcode.s
- (millicode-call/numarg-in-reg as $m.varargs (thefixnum n) $r.argreg2)
- (sparc.label as L99)))
- ; INVOKE
- ; SETRTN/INVOKE
- ;
- ; Bummed. Can still do better when the procedure to call is in a general
- ; register (avoids the redundant move to RESULT preceding INVOKE).
- ;
- ; Note we must set up the argument count even in unsafe mode, because we
- ; may be calling code that was not compiled unsafe.
- (define (emit-invoke as n setrtn? mc-exception)
- (let ((START (new-label))
- (TIMER-OK (new-label))
- (PROC-OK (new-label)))
- (cond ((not (unsafe-code))
- (sparc.label as START)
- (sparc.subicc as $r.timer 1 $r.timer)
- (sparc.bne as TIMER-OK)
- (sparc.andi as $r.result $tag.tagmask $r.tmp0)
- (millicode-call/ret as $m.timer-exception START)
- (sparc.label as TIMER-OK)
- (sparc.cmpi as $r.tmp0 $tag.procedure-tag)
- (sparc.be.a as PROC-OK)
- (sparc.ldi as $r.result $p.codevector $r.tmp0)
- (millicode-call/ret as mc-exception START)
- (sparc.label as PROC-OK))
- (else
- (sparc.label as START)
- (sparc.subicc as $r.timer 1 $r.timer)
- (sparc.bne.a as TIMER-OK)
- (sparc.ldi as $r.result $p.codevector $r.tmp0)
- (millicode-call/ret as $m.timer-exception START)
- (sparc.label as TIMER-OK)))
- (sparc.move as $r.result $r.reg0)
- ;; FIXME: limit 1023 args
- (cond (setrtn?
- (sparc.set as (thefixnum n) $r.result)
- (sparc.jmpli as $r.tmp0 $p.codeoffset $r.o7)
- (sparc.sti as $r.o7 4 $r.stkp))
- (else
- (sparc.jmpli as $r.tmp0 $p.codeoffset $r.g0)
- (sparc.set as (thefixnum n) $r.result)))))
- ; SAVE -- for new compiler
- ;
- ; Create stack frame. To avoid confusing the garbage collector, the
- ; slots must be initialized to something definite unless they will
- ; immediately be initialized by a MacScheme machine store instruction.
- ; The creation is done by emit-save0!, and the initialization is done
- ; by emit-save1!.
- (define (emit-save0! as n)
- (let* ((L1 (new-label))
- (L0 (new-label))
- (framesize (+ 8 (* (+ n 1) 4)))
- (realsize (roundup8 (+ framesize 4))))
- (sparc.label as L0)
- (sparc.subi as $r.stkp realsize $r.stkp)
- (sparc.cmpr as $r.stklim $r.stkp)
- (sparc.ble.a as L1)
- (sparc.set as framesize $r.tmp0)
- (sparc.addi as $r.stkp realsize $r.stkp)
- (millicode-call/ret as $m.stkoflow L0)
- (sparc.label as L1)
- ; initialize size and return fields of stack frame
- (sparc.sti as $r.tmp0 0 $r.stkp)
- (sparc.sti as $r.g0 4 $r.stkp)))
- ; Given a vector v of booleans, initializes slot i of the stack frame
- ; if and only if (vector-ref v i).
- (define (emit-save1! as v)
- (let ((n (vector-length v)))
- (let loop ((i 0) (offset 12))
- (cond ((= i n)
- #t)
- ((vector-ref v i)
- (sparc.sti as $r.g0 offset $r.stkp)
- (loop (+ i 1) (+ offset 4)))
- (else
- (loop (+ i 1) (+ offset 4)))))))
- ; RESTORE
- ;
- ; Restore registers from stack frame
- ; FIXME: Use ldd/std here; see comments for emit-save!, above.
- ; We pop only actual registers.
- (define (emit-restore! as n)
- (let ((n (min n 31)))
- (do ((i 0 (+ i 1))
- (offset 12 (+ offset 4)))
- ((> i n))
- (let ((r (regname i)))
- (if (hardware-mapped? r)
- (sparc.ldi as $r.stkp offset r)
- (begin (sparc.ldi as $r.stkp offset $r.tmp0)
- (emit-store-reg! as $r.tmp0 r)))))))
- ; POP -- for new compiler
- ;
- ; Pop frame.
- ; If returning?, then emit the return as well and put the pop
- ; in its delay slot.
- (define (emit-pop! as n returning?)
- (let* ((framesize (+ 8 (* (+ n 1) 4)))
- (realsize (roundup8 (+ framesize 4))))
- (if returning?
- (begin (sparc.ldi as $r.stkp (+ realsize 4) $r.o7)
- (sparc.jmpli as $r.o7 8 $r.g0)
- (sparc.addi as $r.stkp realsize $r.stkp))
- (sparc.addi as $r.stkp realsize $r.stkp))))
- ; SETRTN
- ;
- ; Change the return address in the stack frame.
- (define (emit-setrtn! as label)
- (emit-return-address! as label)
- (sparc.sti as $r.o7 4 $r.stkp))
- ; APPLY
- ;
- ; `apply' falls into millicode.
- ;
- ; The timer check is performed here because it is not very easy for the
- ; millicode to do this.
- (define (emit-apply! as r1 r2)
- (let ((L0 (new-label)))
- (check-timer0 as)
- (sparc.label as L0)
- (emit-move2hwreg! as r1 $r.argreg2)
- (emit-move2hwreg! as r2 $r.argreg3)
- (millicode-call/0arg as $m.apply)))
- ; LOAD
- (define (emit-load! as slot dest-reg)
- (if (hardware-mapped? dest-reg)
- (sparc.ldi as $r.stkp (+ 12 (* slot 4)) dest-reg)
- (begin (sparc.ldi as $r.stkp (+ 12 (* slot 4)) $r.tmp0)
- (emit-store-reg! as $r.tmp0 dest-reg))))
- ; STORE
- (define (emit-store! as k n)
- (if (hardware-mapped? k)
- (sparc.sti as k (+ 12 (* n 4)) $r.stkp)
- (begin (emit-load-reg! as k $r.tmp0)
- (sparc.sti as $r.tmp0 (+ 12 (* n 4)) $r.stkp))))
- ; LEXICAL
- (define (emit-lexical! as m n)
- (let ((base (emit-follow-chain! as m)))
- (sparc.ldi as base (- (procedure-slot-offset n) $tag.procedure-tag)
- $r.result)))
- ; SETLEX
- ; FIXME: should allow an in-line barrier
- (define (emit-setlex! as m n)
- (let ((base (emit-follow-chain! as m)))
- (sparc.sti as $r.result (- (procedure-slot-offset n) $tag.procedure-tag)
- base)
- (if (write-barrier)
- (begin
- (sparc.move as $r.result $r.argreg2)
- (millicode-call/1arg-in-result as $m.addtrans base)))))
- ; Follow static links.
- ;
- ; By using and leaving the result in ARGREG3 rather than in RESULT,
- ; we save a temporary register.
- (define (emit-follow-chain! as m)
- (let loop ((q m))
- (cond ((not (zero? q))
- (sparc.ldi as
- (if (= q m) $r.reg0 $r.argreg3)
- $p.linkoffset
- $r.argreg3)
- (loop (- q 1)))
- ((zero? m)
- $r.reg0)
- (else
- $r.argreg3))))
- ; RETURN
- (define (emit-return! as)
- (sparc.ldi as $r.stkp 4 $r.o7)
- (sparc.jmpli as $r.o7 8 $r.g0)
- (sparc.nop as))
- ; RETURN-REG k
- (define (emit-return-reg! as r)
- (sparc.ldi as $r.stkp 4 $r.o7)
- (sparc.jmpli as $r.o7 8 $r.g0)
- (sparc.move as r $r.result))
- ; RETURN-CONST k
- ;
- ; The constant c must be synthesizable in a single instruction.
- (define (emit-return-const! as c)
- (sparc.ldi as $r.stkp 4 $r.o7)
- (sparc.jmpli as $r.o7 8 $r.g0)
- (emit-constant->register as c $r.result))
- ; MVRTN
- (define (emit-mvrtn! as)
- (asm-error "multiple-value return has not been implemented (yet)."))
- ; LEXES
- (define (emit-lexes! as n-slots)
- (emit-alloc-proc! as n-slots)
- (sparc.ldi as $r.reg0 $p.codevector $r.tmp0)
- (sparc.ldi as $r.reg0 $p.constvector $r.tmp1)
- (sparc.sti as $r.tmp0 $p.codevector $r.result)
- (sparc.sti as $r.tmp1 $p.constvector $r.result)
- (emit-init-proc-slots! as n-slots))
- ; LAMBDA
- (define (emit-lambda! as code-offs0 const-offs0 n-slots)
- (let* ((code-offs (+ 4 (- (* 4 code-offs0) $tag.vector-tag)))
- (const-offs (+ 4 (- (* 4 const-offs0) $tag.vector-tag)))
- (fits? (asm:fits? const-offs 13)))
- (emit-alloc-proc! as n-slots)
- (if fits?
- (begin (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
- (sparc.ldi as $r.tmp0 code-offs $r.tmp1))
- (emit-const->register! as code-offs0 $r.tmp1))
- (sparc.sti as $r.tmp1 $p.codevector $r.result)
- (if fits?
- (begin (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
- (sparc.ldi as $r.tmp0 const-offs $r.tmp1))
- (emit-const->register! as const-offs0 $r.tmp1))
- (sparc.sti as $r.tmp1 $p.constvector $r.result)
- (emit-init-proc-slots! as n-slots)))
-
- ; Allocate procedure with room for n register slots; return tagged pointer.
- (define emit-alloc-proc!
- (let ((two^12 (expt 2 12)))
- (lambda (as n)
- (millicode-call/numarg-in-result as $m.alloc (* (+ n 4) 4))
- (let ((header (+ (* (* (+ n 3) 4) 256) $imm.procedure-header)))
- (emit-immediate->register! as header $r.tmp0)
- (sparc.sti as $r.tmp0 0 $r.result)
- (sparc.addi as $r.result $tag.procedure-tag $r.result)))))
- ; Initialize data slots in procedure from current registers as specified for
- ; `lamba' and `lexes'. If there are more data slots than registers, then
- ; we must generate code to cdr down the list in the last register to obtain
- ; the rest of the data. The list is expected to have at least the minimal
- ; length.
- ;
- ; The tagged pointer to the procedure is in $r.result.
- (define (emit-init-proc-slots! as n)
- (define (save-registers lo hi offset)
- (do ((lo lo (+ lo 1))
- (offset offset (+ offset 4)))
- ((> lo hi))
- (let ((r (force-hwreg! as (regname lo) $r.tmp0)))
- (sparc.sti as r offset $r.result))))
- (define (save-list lo hi offset)
- (emit-load-reg! as $r.reg31 $r.tmp0)
- (do ((lo lo (+ lo 1))
- (offset offset (+ offset 4)))
- ((> lo hi))
- (sparc.ldi as $r.tmp0 (- $tag.pair-tag) $r.tmp1)
- (sparc.sti as $r.tmp1 offset $r.result)
- (if (< lo hi)
- (begin
- (sparc.ldi as $r.tmp0 (+ (- $tag.pair-tag) 4) $r.tmp0)))))
-
- (cond ((< n *lastreg*)
- (save-registers 0 n $p.reg0))
- (else
- (save-registers 0 (- *lastreg* 1) $p.reg0)
- (save-list *lastreg* n (+ $p.reg0 (* *lastreg* 4))))))
- ; BRANCH
- (define (emit-branch! as check-timer? label)
- (if check-timer?
- (check-timer as label label)
- (begin (sparc.b as label)
- (sparc.slot as))))
- ; BRANCHF
- (define (emit-branchf! as label)
- (emit-branchfreg! as $r.result label))
- ; BRANCHFREG -- introduced by peephole optimization.
- (define (emit-branchfreg! as hwreg label)
- (sparc.cmpi as hwreg $imm.false)
- (sparc.be.a as label)
- (sparc.slot as))
- ; BRANCH-WITH-SETRTN -- introduced by peephole optimization
- (define (emit-branch-with-setrtn! as label)
- (check-timer0 as)
- (sparc.call as label)
- (sparc.sti as $r.o7 4 $r.stkp))
- ; JUMP
- ;
- ; Given the finalization order (outer is finalized before inner is assembled)
- ; the label value will always be available when a jump is assembled. The
- ; only exception is when m = 0, but does this ever happen? This code handles
- ; the case anyway.
- (define (emit-jump! as m label)
- (let* ((r (emit-follow-chain! as m))
- (labelv (label-value as label))
- (v (if (number? labelv)
- (+ labelv $p.codeoffset)
- (list '+ label $p.codeoffset))))
- (sparc.ldi as r $p.codevector $r.tmp0)
- (if (and (number? v) (immediate-literal? v))
- (sparc.jmpli as $r.tmp0 v $r.g0)
- (begin (emit-immediate->register! as v $r.tmp1)
- (sparc.jmplr as $r.tmp0 $r.tmp1 $r.g0)))
- (sparc.move as r $r.reg0)))
- ; .SINGLESTEP
- ;
- ; Single step: jump to millicode; pass index of documentation string in
- ; %TMP0. Some instructions execute when reg0 is not a valid pointer to
- ; the current procedure (because this is just after returning); in this
- ; case we restore reg0 from the stack location given by 'funkyloc'.
- (define (emit-singlestep-instr! as funky? funkyloc cvlabel)
- (if funky?
- (sparc.ldi as $r.stkp (+ (thefixnum funkyloc) 12) $r.reg0))
- (millicode-call/numarg-in-reg as $m.singlestep
- (thefixnum cvlabel)
- $r.argreg2))
- ; Emit the effective address of a label-8 into %o7.
- ;
- ; There are multiple ways to do this. If the call causes an expensive
- ; bubble in the pipeline it is probably much less expensive to grub
- ; the code vector address out of the procedure in REG0 and calculate it
- ; that way. FIXME: We need to benchmark these options.
- ;
- ; In general the point is moot as the common-case sequence
- ; setrtn L1
- ; invoke n
- ; L1:
- ; should be peephole-optimized into the obvious fast code.
- (define (emit-return-address! as label)
- (let* ((loc (here as))
- (lloc (label-value as label)))
- (define (emit-short val)
- (sparc.call as (+ loc 8))
- (sparc.addi as $r.o7 val $r.o7))
- (define (emit-long val)
- ; Don't use sparc.set: we need to know that two instructions get
- ; generated.
- (sparc.sethi as `(hi ,val) $r.tmp0)
- (sparc.ori as $r.tmp0 `(lo ,val) $r.tmp0)
- (sparc.call as (+ loc 16))
- (sparc.addr as $r.o7 $r.tmp0 $r.o7))
- (cond (lloc
- (let ((target-rel-addr (- lloc loc 8)))
- (if (immediate-literal? target-rel-addr)
- (emit-short target-rel-addr)
- (emit-long (- target-rel-addr 8)))))
- ((short-effective-addresses)
- (emit-short `(- ,label ,loc 8)))
- (else
- (emit-long `(- ,label ,loc 16))))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 22 April 1999 / wdc
- ;
- ; SPARC code generation macros for primitives, part 1:
- ; primitives defined in Compiler/sparc.imp.sch.
- ; These extend Asm/Common/pass5p1.sch.
- (define (operand5 instruction)
- (car (cddddr (cdr instruction))))
- (define (operand6 instruction)
- (cadr (cddddr (cdr instruction))))
- (define (operand7 instruction)
- (caddr (cddddr (cdr instruction))))
- ; Primop emitters.
- (define (emit-primop.1arg! as op)
- ((find-primop op) as))
- (define (emit-primop.2arg! as op r)
- ((find-primop op) as r))
- (define (emit-primop.3arg! as a1 a2 a3)
- ((find-primop a1) as a2 a3))
- (define (emit-primop.4arg! as a1 a2 a3 a4)
- ((find-primop a1) as a2 a3 a4))
- (define (emit-primop.5arg! as a1 a2 a3 a4 a5)
- ((find-primop a1) as a2 a3 a4 a5))
- (define (emit-primop.6arg! as a1 a2 a3 a4 a5 a6)
- ((find-primop a1) as a2 a3 a4 a5 a6))
- (define (emit-primop.7arg! as a1 a2 a3 a4 a5 a6 a7)
- ((find-primop a1) as a2 a3 a4 a5 a6 a7))
- ; Hash table of primops
- (define primop-vector (make-vector 256 '()))
- (define (define-primop name proc)
- (let ((h (logand (symbol-hash name) 255)))
- (vector-set! primop-vector h (cons (cons name proc)
- (vector-ref primop-vector h)))
- name))
- (define (find-primop name)
- (let ((h (logand (symbol-hash name) 255)))
- (cdr (assq name (vector-ref primop-vector h)))))
- (define (for-each-primop proc)
- (do ((i 0 (+ i 1)))
- ((= i (vector-length primop-vector)))
- (for-each (lambda (p)
- (proc (cdr p)))
- (vector-ref primop-vector i))))
- ; Primops
- (define-primop 'unspecified
- (lambda (as)
- (emit-immediate->register! as $imm.unspecified $r.result)))
- (define-primop 'undefined
- (lambda (as)
- (emit-immediate->register! as $imm.undefined $r.result)))
- (define-primop 'eof-object
- (lambda (as)
- (emit-immediate->register! as $imm.eof $r.result)))
- (define-primop 'enable-interrupts
- (lambda (as)
- (millicode-call/0arg as $m.enable-interrupts)))
- (define-primop 'disable-interrupts
- (lambda (as)
- (millicode-call/0arg as $m.disable-interrupts)))
- (define-primop 'gc-counter
- (lambda (as)
- (sparc.ldi as $r.globals $g.gccnt $r.result)))
- (define-primop 'zero?
- (lambda (as)
- (emit-cmp-primop! as sparc.be.a $m.zerop $r.g0)))
- (define-primop '=
- (lambda (as r)
- (emit-cmp-primop! as sparc.be.a $m.numeq r)))
- (define-primop '<
- (lambda (as r)
- (emit-cmp-primop! as sparc.bl.a $m.numlt r)))
- (define-primop '<=
- (lambda (as r)
- (emit-cmp-primop! as sparc.ble.a $m.numle r)))
- (define-primop '>
- (lambda (as r)
- (emit-cmp-primop! as sparc.bg.a $m.numgt r)))
- (define-primop '>=
- (lambda (as r)
- (emit-cmp-primop! as sparc.bge.a $m.numge r)))
- (define-primop 'complex?
- (lambda (as)
- (millicode-call/0arg as $m.complexp)))
- (define-primop 'real?
- (lambda (as)
- (millicode-call/0arg as $m.realp)))
- (define-primop 'rational?
- (lambda (as)
- (millicode-call/0arg as $m.rationalp)))
- (define-primop 'integer?
- (lambda (as)
- (millicode-call/0arg as $m.integerp)))
- (define-primop 'exact?
- (lambda (as)
- (millicode-call/0arg as $m.exactp)))
- (define-primop 'inexact?
- (lambda (as)
- (millicode-call/0arg as $m.inexactp)))
- (define-primop 'fixnum?
- (lambda (as)
- (sparc.btsti as $r.result 3)
- (emit-set-boolean! as)))
- (define-primop '+
- (lambda (as r)
- (emit-primop.4arg! as 'internal:+ $r.result r $r.result)))
- (define-primop '-
- (lambda (as r)
- (emit-primop.4arg! as 'internal:- $r.result r $r.result)))
- (define-primop '*
- (lambda (as rs2)
- (emit-multiply-code as rs2 #f)))
- (define (emit-multiply-code as rs2 fixnum-arithmetic?)
- (if (and (unsafe-code) fixnum-arithmetic?)
- (begin
- (sparc.srai as $r.result 2 $r.tmp0)
- (sparc.smulr as $r.tmp0 rs2 $r.result))
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2))
- (Lstart (new-label))
- (Ltagok (new-label))
- (Loflo (new-label))
- (Ldone (new-label)))
- (sparc.label as Lstart)
- (sparc.orr as $r.result rs2 $r.tmp0)
- (sparc.btsti as $r.tmp0 3)
- (sparc.be.a as Ltagok)
- (sparc.srai as $r.result 2 $r.tmp0)
- (sparc.label as Loflo)
- (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
- (if (not fixnum-arithmetic?)
- (begin
- (millicode-call/ret as $m.multiply Ldone))
- (begin
- (sparc.set as (thefixnum $ex.fx*) $r.tmp0)
- (millicode-call/ret as $m.exception Lstart)))
- (sparc.label as Ltagok)
- (sparc.smulr as $r.tmp0 rs2 $r.tmp0)
- (sparc.rdy as $r.tmp1)
- (sparc.srai as $r.tmp0 31 $r.tmp2)
- (sparc.cmpr as $r.tmp1 $r.tmp2)
- (sparc.bne.a as Loflo)
- (sparc.slot as)
- (sparc.move as $r.tmp0 $r.result)
- (sparc.label as Ldone))))
- (define-primop '/
- (lambda (as r)
- (millicode-call/1arg as $m.divide r)))
- (define-primop 'quotient
- (lambda (as r)
- (millicode-call/1arg as $m.quotient r)))
- (define-primop 'remainder
- (lambda (as r)
- (millicode-call/1arg as $m.remainder r)))
- (define-primop '--
- (lambda (as)
- (emit-negate as $r.result $r.result)))
- (define-primop 'round
- (lambda (as)
- (millicode-call/0arg as $m.round)))
- (define-primop 'truncate
- (lambda (as)
- (millicode-call/0arg as $m.truncate)))
- (define-primop 'lognot
- (lambda (as)
- (if (not (unsafe-code))
- (emit-assert-fixnum! as $r.result $ex.lognot))
- (sparc.ornr as $r.g0 $r.result $r.result) ; argument order matters
- (sparc.xori as $r.result 3 $r.result)))
- (define-primop 'logand
- (lambda (as x)
- (logical-op as $r.result x $r.result sparc.andr $ex.logand)))
- (define-primop 'logior
- (lambda (as x)
- (logical-op as $r.result x $r.result sparc.orr $ex.logior)))
- (define-primop 'logxor
- (lambda (as x)
- (logical-op as $r.result x $r.result sparc.xorr $ex.logxor)))
- ; Fixnum shifts.
- ;
- ; Only positive shifts are meaningful.
- ; FIXME: These are incompatible with MacScheme and MIT Scheme.
- ; FIXME: need to return to start of sequence after fault.
- (define-primop 'lsh
- (lambda (as x)
- (emit-shift-operation as $ex.lsh $r.result x $r.result)))
- (define-primop 'rshl
- (lambda (as x)
- (emit-shift-operation as $ex.rshl $r.result x $r.result)))
- (define-primop 'rsha
- (lambda (as x)
- (emit-shift-operation as $ex.rsha $r.result x $r.result)))
- ; fixnums only.
- ; FIXME: for symmetry with shifts there should be rotl and rotr (?)
- ; or perhaps rot should only ever rotate one way.
- ; FIXME: implement.
- (define-primop 'rot
- (lambda (as x)
- (asm-error "Sparcasm: ROT primop is not implemented.")))
- (define-primop 'null?
- (lambda (as)
- (sparc.cmpi as $r.result $imm.null)
- (emit-set-boolean! as)))
- (define-primop 'pair?
- (lambda (as)
- (emit-single-tagcheck->bool! as $tag.pair-tag)))
- (define-primop 'eof-object?
- (lambda (as)
- (sparc.cmpi as $r.result $imm.eof)
- (emit-set-boolean! as)))
- ; Tests the specific representation, not 'flonum or compnum with 0i'.
- (define-primop 'flonum?
- (lambda (as)
- (emit-double-tagcheck->bool! as $tag.bytevector-tag
- (+ $imm.bytevector-header
- $tag.flonum-typetag))))
- (define-primop 'compnum?
- (lambda (as)
- (emit-double-tagcheck->bool! as $tag.bytevector-tag
- (+ $imm.bytevector-header
- $tag.compnum-typetag))))
- (define-primop 'symbol?
- (lambda (as)
- (emit-double-tagcheck->bool! as $tag.vector-tag
- (+ $imm.vector-header
- $tag.symbol-typetag))))
- (define-primop 'port?
- (lambda (as)
- (emit-double-tagcheck->bool! as $tag.vector-tag
- (+ $imm.vector-header
- $tag.port-typetag))))
- (define-primop 'structure?
- (lambda (as)
- (emit-double-tagcheck->bool! as $tag.vector-tag
- (+ $imm.vector-header
- $tag.structure-typetag))))
- (define-primop 'char?
- (lambda (as)
- (sparc.andi as $r.result #xFF $r.tmp0)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (emit-set-boolean! as)))
- (define-primop 'string?
- (lambda (as)
- (emit-double-tagcheck->bool! as
- $tag.bytevector-tag
- (+ $imm.bytevector-header
- $tag.string-typetag))))
- (define-primop 'bytevector?
- (lambda (as)
- (emit-double-tagcheck->bool! as
- $tag.bytevector-tag
- (+ $imm.bytevector-header
- $tag.bytevector-typetag))))
- (define-primop 'bytevector-like?
- (lambda (as)
- (emit-single-tagcheck->bool! as $tag.bytevector-tag)))
- (define-primop 'vector?
- (lambda (as)
- (emit-double-tagcheck->bool! as
- $tag.vector-tag
- (+ $imm.vector-header
- $tag.vector-typetag))))
- (define-primop 'vector-like?
- (lambda (as)
- (emit-single-tagcheck->bool! as $tag.vector-tag)))
- (define-primop 'procedure?
- (lambda (as)
- (emit-single-tagcheck->bool! as $tag.procedure-tag)))
- (define-primop 'cons
- (lambda (as r)
- (emit-primop.4arg! as 'internal:cons $r.result r $r.result)))
- (define-primop 'car
- (lambda (as)
- (emit-primop.3arg! as 'internal:car $r.result $r.result)))
- (define-primop 'cdr
- (lambda (as)
- (emit-primop.3arg! as 'internal:cdr $r.result $r.result)))
- (define-primop 'car:pair
- (lambda (as)
- (sparc.ldi as $r.result (- $tag.pair-tag) $r.result)))
- (define-primop 'cdr:pair
- (lambda (as)
- (sparc.ldi as $r.result (- 4 $tag.pair-tag) $r.result)))
- (define-primop 'set-car!
- (lambda (as x)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as $tag.pair-tag $ex.car #f))
- (emit-setcar/setcdr! as $r.result x 0)))
- (define-primop 'set-cdr!
- (lambda (as x)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as $tag.pair-tag $ex.cdr #f))
- (emit-setcar/setcdr! as $r.result x 4)))
- ; Cells are internal data structures, represented using pairs.
- ; No error checking is done on cell references.
- (define-primop 'make-cell
- (lambda (as)
- (emit-primop.4arg! as 'internal:cons $r.result $r.g0 $r.result)))
- (define-primop 'cell-ref
- (lambda (as)
- (emit-primop.3arg! as 'internal:cell-ref $r.result $r.result)))
- (define-primop 'cell-set!
- (lambda (as r)
- (emit-setcar/setcdr! as $r.result r 0)))
- (define-primop 'syscall
- (lambda (as)
- (millicode-call/0arg as $m.syscall)))
- (define-primop 'break
- (lambda (as)
- (millicode-call/0arg as $m.break)))
- (define-primop 'creg
- (lambda (as)
- (millicode-call/0arg as $m.creg)))
- (define-primop 'creg-set!
- (lambda (as)
- (millicode-call/0arg as $m.creg-set!)))
- (define-primop 'typetag
- (lambda (as)
- (millicode-call/0arg as $m.typetag)))
- (define-primop 'typetag-set!
- (lambda (as r)
- (millicode-call/1arg as $m.typetag-set r)))
- (define-primop 'exact->inexact
- (lambda (as)
- (millicode-call/0arg as $m.exact->inexact)))
- (define-primop 'inexact->exact
- (lambda (as)
- (millicode-call/0arg as $m.inexact->exact)))
- (define-primop 'real-part
- (lambda (as)
- (millicode-call/0arg as $m.real-part)))
- (define-primop 'imag-part
- (lambda (as)
- (millicode-call/0arg as $m.imag-part)))
- (define-primop 'char->integer
- (lambda (as)
- (if (not (unsafe-code))
- (emit-assert-char! as $ex.char2int #f))
- (sparc.srli as $r.result 14 $r.result)))
- (define-primop 'integer->char
- (lambda (as)
- (if (not (unsafe-code))
- (emit-assert-fixnum! as $r.result $ex.int2char))
- (sparc.andi as $r.result #x3FF $r.result)
- (sparc.slli as $r.result 14 $r.result)
- (sparc.ori as $r.result $imm.character $r.result)))
- (define-primop 'not
- (lambda (as)
- (sparc.cmpi as $r.result $imm.false)
- (emit-set-boolean! as)))
- (define-primop 'eq?
- (lambda (as x)
- (emit-primop.4arg! as 'internal:eq? $r.result x $r.result)))
- (define-primop 'eqv?
- (lambda (as x)
- (let ((tmp (force-hwreg! as x $r.tmp0))
- (L1 (new-label)))
- (sparc.cmpr as $r.result tmp)
- (sparc.be.a as L1)
- (sparc.set as $imm.true $r.result)
- (millicode-call/1arg as $m.eqv tmp)
- (sparc.label as L1))))
- (define-primop 'make-bytevector
- (lambda (as)
- (if (not (unsafe-code))
- (emit-assert-positive-fixnum! as $r.result $ex.mkbvl))
- (emit-allocate-bytevector as
- (+ $imm.bytevector-header
- $tag.bytevector-typetag)
- #f)
- (sparc.addi as $r.result $tag.bytevector-tag $r.result)))
- (define-primop 'bytevector-fill!
- (lambda (as rs2)
- (let* ((fault (emit-double-tagcheck-assert! as
- $tag.bytevector-tag
- (+ $imm.bytevector-header
- $tag.bytevector-typetag)
- $ex.bvfill
- rs2))
- (rs2 (force-hwreg! as rs2 $r.argreg2)))
- (sparc.btsti as rs2 3)
- (sparc.bne as fault)
- (sparc.srai as rs2 2 $r.tmp2)
- (sparc.ldi as $r.result (- $tag.bytevector-tag) $r.tmp0)
- (sparc.addi as $r.result (- 4 $tag.bytevector-tag) $r.tmp1)
- (sparc.srai as $r.tmp0 8 $r.tmp0)
- (emit-bytevector-fill as $r.tmp0 $r.tmp1 $r.tmp2))))
- (define-primop 'bytevector-length
- (lambda (as)
- (emit-get-length! as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.bytevector-typetag)
- $ex.bvlen
- $r.result
- $r.result)))
- (define-primop 'bytevector-like-length
- (lambda (as)
- (emit-get-length! as
- $tag.bytevector-tag
- #f
- $ex.bvllen
- $r.result
- $r.result)))
- (define-primop 'bytevector-ref
- (lambda (as r)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert!
- as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.bytevector-typetag)
- $ex.bvref
- r)
- #f)))
- (emit-bytevector-like-ref! as $r.result r $r.result fault #f #t))))
- (define-primop 'bytevector-like-ref
- (lambda (as r)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.bytevector-tag
- $ex.bvlref
- r)
- #f)))
- (emit-bytevector-like-ref! as $r.result r $r.result fault #f #f))))
- (define-primop 'bytevector-set!
- (lambda (as r1 r2)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert!
- as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.bytevector-typetag)
- $ex.bvset
- r1)
- #f)))
- (emit-bytevector-like-set! as r1 r2 fault #t))))
- (define-primop 'bytevector-like-set!
- (lambda (as r1 r2)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.bytevector-tag
- $ex.bvlset
- r1)
- #f)))
- (emit-bytevector-like-set! as r1 r2 fault #f))))
- (define-primop 'sys$bvlcmp
- (lambda (as x)
- (millicode-call/1arg as $m.bvlcmp x)))
- ; Strings
- ; RESULT must have nonnegative fixnum.
- ; RS2 must have character.
- (define-primop 'make-string
- (lambda (as rs2)
- (let ((FAULT (new-label))
- (START (new-label)))
- (sparc.label as START)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
- (if (not (unsafe-code))
- (let ((L1 (new-label))
- (L2 (new-label)))
- (sparc.tsubrcc as $r.result $r.g0 $r.g0)
- (sparc.bvc.a as L1)
- (sparc.andi as rs2 255 $r.tmp0)
- (sparc.label as FAULT)
- (if (not (= rs2 $r.argreg2))
- (sparc.move as rs2 $r.argreg2))
- (sparc.set as (thefixnum $ex.mkbvl) $r.tmp0) ; Wrong code.
- (millicode-call/ret as $m.exception START)
- (sparc.label as L1)
- (sparc.bl as FAULT)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (sparc.bne as FAULT)
- (sparc.move as $r.result $r.argreg3))
- (begin
- (sparc.move as $r.result $r.argreg3)))
- (emit-allocate-bytevector as
- (+ $imm.bytevector-header
- $tag.string-typetag)
- $r.argreg3)
- (sparc.srai as rs2 16 $r.tmp1)
- (sparc.addi as $r.result 4 $r.result)
- (sparc.srai as $r.argreg3 2 $r.tmp0)
- (emit-bytevector-fill as $r.tmp0 $r.result $r.tmp1)
- (sparc.addi as $r.result (- $tag.bytevector-tag 4) $r.result)))))
- (define-primop 'string-length
- (lambda (as)
- (emit-primop.3arg! as 'internal:string-length $r.result $r.result)))
- (define-primop 'string-ref
- (lambda (as r)
- (emit-primop.4arg! as 'internal:string-ref $r.result r $r.result)))
- (define-primop 'string-set!
- (lambda (as r1 r2)
- (emit-string-set! as $r.result r1 r2)))
- (define-primop 'sys$partial-list->vector
- (lambda (as r)
- (millicode-call/1arg as $m.partial-list->vector r)))
- (define-primop 'make-procedure
- (lambda (as)
- (emit-make-vector-like! as
- '()
- $imm.procedure-header
- $tag.procedure-tag)))
- (define-primop 'make-vector
- (lambda (as r)
- (emit-make-vector-like! as
- r
- (+ $imm.vector-header $tag.vector-typetag)
- $tag.vector-tag)))
- (define-primop 'make-vector:0
- (lambda (as r) (make-vector-n as 0 r)))
- (define-primop 'make-vector:1
- (lambda (as r) (make-vector-n as 1 r)))
- (define-primop 'make-vector:2
- (lambda (as r) (make-vector-n as 2 r)))
- (define-primop 'make-vector:3
- (lambda (as r) (make-vector-n as 3 r)))
- (define-primop 'make-vector:4
- (lambda (as r) (make-vector-n as 4 r)))
- (define-primop 'make-vector:5
- (lambda (as r) (make-vector-n as 5 r)))
- (define-primop 'make-vector:6
- (lambda (as r) (make-vector-n as 6 r)))
- (define-primop 'make-vector:7
- (lambda (as r) (make-vector-n as 7 r)))
- (define-primop 'make-vector:8
- (lambda (as r) (make-vector-n as 8 r)))
- (define-primop 'make-vector:9
- (lambda (as r) (make-vector-n as 9 r)))
- (define-primop 'vector-length
- (lambda (as)
- (emit-primop.3arg! as 'internal:vector-length $r.result $r.result)))
- (define-primop 'vector-like-length
- (lambda (as)
- (emit-get-length! as $tag.vector-tag #f $ex.vllen $r.result $r.result)))
- (define-primop 'vector-length:vec
- (lambda (as)
- (emit-get-length-trusted! as $tag.vector-tag $r.result $r.result)))
- (define-primop 'procedure-length
- (lambda (as)
- (emit-get-length! as $tag.procedure-tag #f $ex.plen $r.result $r.result)))
- (define-primop 'vector-ref
- (lambda (as r)
- (emit-primop.4arg! as 'internal:vector-ref $r.result r $r.result)))
- (define-primop 'vector-like-ref
- (lambda (as r)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.vector-tag
- $ex.vlref
- r)
- #f)))
- (emit-vector-like-ref!
- as $r.result r $r.result fault $tag.vector-tag #f))))
- (define-primop 'vector-ref:trusted
- (lambda (as rs2)
- (emit-vector-like-ref-trusted!
- as $r.result rs2 $r.result $tag.vector-tag)))
- (define-primop 'procedure-ref
- (lambda (as r)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.procedure-tag
- $ex.pref
- r)
- #f)))
- (emit-vector-like-ref!
- as $r.result r $r.result fault $tag.procedure-tag #f))))
- (define-primop 'vector-set!
- (lambda (as r1 r2)
- (emit-primop.4arg! as 'internal:vector-set! $r.result r1 r2)))
- (define-primop 'vector-like-set!
- (lambda (as r1 r2)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.vector-tag
- $ex.vlset
- r1)
- #f)))
- (emit-vector-like-set! as $r.result r1 r2 fault $tag.vector-tag #f))))
- (define-primop 'vector-set!:trusted
- (lambda (as rs2 rs3)
- (emit-vector-like-set-trusted! as $r.result rs2 rs3 $tag.vector-tag)))
- (define-primop 'procedure-set!
- (lambda (as r1 r2)
- (let ((fault (if (not (unsafe-code))
- (emit-single-tagcheck-assert! as
- $tag.procedure-tag
- $ex.pset
- r1)
- #f)))
- (emit-vector-like-set! as $r.result r1 r2 fault $tag.procedure-tag #f))))
- (define-primop 'char<?
- (lambda (as x)
- (emit-char-cmp as x sparc.bl.a $ex.char<?)))
- (define-primop 'char<=?
- (lambda (as x)
- (emit-char-cmp as x sparc.ble.a $ex.char<=?)))
- (define-primop 'char=?
- (lambda (as x)
- (emit-char-cmp as x sparc.be.a $ex.char=?)))
- (define-primop 'char>?
- (lambda (as x)
- (emit-char-cmp as x sparc.bg.a $ex.char>?)))
- (define-primop 'char>=?
- (lambda (as x)
- (emit-char-cmp as x sparc.bge.a $ex.char>=?)))
- ; Experimental (for performance).
- ; This makes massive assumptions about the layout of the port structure:
- ; A port is a vector-like where
- ; #0 = port.input?
- ; #4 = port.buffer
- ; #7 = port.rd-lim
- ; #8 = port.rd-ptr
- ; See Lib/iosys.sch for more information.
- (define-primop 'sys$read-char
- (lambda (as)
- (let ((Lfinish (new-label))
- (Lend (new-label)))
- (if (not (unsafe-code))
- (begin
- (sparc.andi as $r.result $tag.tagmask $r.tmp0) ; mask argument tag
- (sparc.cmpi as $r.tmp0 $tag.vector-tag); vector-like?
- (sparc.bne as Lfinish) ; skip if not vector-like
- (sparc.nop as)
- (sparc.ldbi as $r.RESULT 0 $r.tmp1))) ; header byte
- (sparc.ldi as $r.RESULT 1 $r.tmp2) ; port.input? or garbage
- (if (not (unsafe-code))
- (begin
- (sparc.cmpi as $r.tmp1 $hdr.port) ; port?
- (sparc.bne as Lfinish))) ; skip if not port
- (sparc.cmpi as $r.tmp2 $imm.false) ; [slot] input port?
- (sparc.be as Lfinish) ; skip if not active port
- (sparc.ldi as $r.RESULT (+ 1 32) $r.tmp1) ; [slot] port.rd-ptr
- (sparc.ldi as $r.RESULT (+ 1 28) $r.tmp2) ; port.rd-lim
- (sparc.ldi as $r.RESULT (+ 1 16) $r.tmp0) ; port.buffer
- (sparc.cmpr as $r.tmp1 $r.tmp2) ; rd-ptr < rd-lim?
- (sparc.bge as Lfinish) ; skip if rd-ptr >= rd-lim
- (sparc.subi as $r.tmp0 1 $r.tmp0) ; [slot] addr of string@0
- (sparc.srai as $r.tmp1 2 $r.tmp2) ; rd-ptr as native int
- (sparc.ldbr as $r.tmp0 $r.tmp2 $r.tmp2) ; get byte from string
- (sparc.addi as $r.tmp1 4 $r.tmp1) ; bump rd-ptr
- (sparc.sti as $r.tmp1 (+ 1 32) $r.RESULT) ; store rd-ptr in port
- (sparc.slli as $r.tmp2 16 $r.tmp2) ; convert to char #1
- (sparc.b as Lend)
- (sparc.ori as $r.tmp2 $imm.character $r.RESULT) ; [slot] convert to char
- (sparc.label as Lfinish)
- (sparc.set as $imm.false $r.RESULT) ; failed
- (sparc.label as Lend))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 9 May 1999 / wdc
- ;
- ; SPARC code generation macros for primitives, part 2:
- ; primitives introduced by peephole optimization.
- (define-primop 'internal:car
- (lambda (as src1 dest)
- (internal-primop-invariant2 'internal:car src1 dest)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert-reg! as
- $tag.pair-tag src1 #f $ex.car))
- (sparc.ldi as src1 (- $tag.pair-tag) dest)))
- (define-primop 'internal:cdr
- (lambda (as src1 dest)
- (internal-primop-invariant2 'internal:cdr src1 dest)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert-reg! as
- $tag.pair-tag src1 #f $ex.cdr))
- (sparc.ldi as src1 (- 4 $tag.pair-tag) dest)))
- (define-primop 'internal:cell-ref
- (lambda (as src1 dest)
- (internal-primop-invariant2 'internal:cell-ref src1 dest)
- (sparc.ldi as src1 (- $tag.pair-tag) dest)))
- (define-primop 'internal:set-car!
- (lambda (as rs1 rs2 dest-ignored)
- (internal-primop-invariant2 'internal:set-car! rs1 dest-ignored)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert-reg! as $tag.pair-tag rs1 rs2 $ex.car))
- (emit-setcar/setcdr! as rs1 rs2 0)))
- (define-primop 'internal:set-cdr!
- (lambda (as rs1 rs2 dest-ignored)
- (internal-primop-invariant2 'internal:set-cdr! rs1 dest-ignored)
- (if (not (unsafe-code))
- (emit-single-tagcheck-assert-reg! as $tag.pair-tag rs1 rs2 $ex.cdr))
- (emit-setcar/setcdr! as rs1 rs2 4)))
- (define-primop 'internal:cell-set!
- (lambda (as rs1 rs2 dest-ignored)
- (internal-primop-invariant2 'internal:cell-set! rs1 dest-ignored)
- (emit-setcar/setcdr! as rs1 rs2 0)))
- ; CONS
- ;
- ; One instruction reduced here translates into about 2.5KB reduction in the
- ; size of the basic heap image. :-)
- ;
- ; In the out-of-line case, if rd != RESULT then a garbage value is left
- ; in RESULT, but it always looks like a fixnum, so it's OK.
- (define-primop 'internal:cons
- (lambda (as rs1 rs2 rd)
- (if (inline-allocation)
- (let ((ENOUGH-MEMORY (new-label))
- (START (new-label)))
- (sparc.label as START)
- (sparc.addi as $r.e-top 8 $r.e-top)
- (sparc.cmpr as $r.e-top $r.e-limit)
- (sparc.ble.a as ENOUGH-MEMORY)
- (sparc.sti as rs1 -8 $r.e-top)
- (millicode-call/ret as $m.gc START)
- (sparc.label as ENOUGH-MEMORY)
- (sparc.sti as (force-hwreg! as rs2 $r.tmp0) -4 $r.e-top)
- (sparc.subi as $r.e-top (- 8 $tag.pair-tag) rd))
- (begin
- (if (= rs1 $r.result)
- (sparc.move as $r.result $r.argreg2))
- (millicode-call/numarg-in-result as $m.alloc 8)
- (if (= rs1 $r.result)
- (sparc.sti as $r.argreg2 0 $r.result)
- (sparc.sti as rs1 0 $r.result))
- (sparc.sti as (force-hwreg! as rs2 $r.tmp1) 4 $r.result)
- (sparc.addi as $r.result $tag.pair-tag rd)))))
- (define-primop 'internal:car:pair
- (lambda (as src1 dest)
- (internal-primop-invariant2 'internal:car src1 dest)
- (sparc.ldi as src1 (- $tag.pair-tag) dest)))
- (define-primop 'internal:cdr:pair
- (lambda (as src1 dest)
- (internal-primop-invariant2 'internal:cdr src1 dest)
- (sparc.ldi as src1 (- 4 $tag.pair-tag) dest)))
- ; Vector operations.
- (define-primop 'internal:vector-length
- (lambda (as rs rd)
- (internal-primop-invariant2 'internal:vector-length rs rd)
- (emit-get-length! as
- $tag.vector-tag
- (+ $imm.vector-header $tag.vector-typetag)
- $ex.vlen
- rs
- rd)))
- (define-primop 'internal:vector-ref
- (lambda (as rs1 rs2 rd)
- (internal-primop-invariant2 'internal:vector-ref rs1 rd)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert-reg/reg!
- as
- $tag.vector-tag
- (+ $imm.vector-header $tag.vector-typetag)
- rs1
- rs2
- $ex.vref))))
- (emit-vector-like-ref! as rs1 rs2 rd fault $tag.vector-tag #t))))
- (define-primop 'internal:vector-ref/imm
- (lambda (as rs1 imm rd)
- (internal-primop-invariant2 'internal:vector-ref/imm rs1 rd)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert-reg/imm!
- as
- $tag.vector-tag
- (+ $imm.vector-header $tag.vector-typetag)
- rs1
- imm
- $ex.vref))))
- (emit-vector-like-ref/imm! as rs1 imm rd fault $tag.vector-tag #t))))
- (define-primop 'internal:vector-set!
- (lambda (as rs1 rs2 rs3)
- (internal-primop-invariant1 'internal:vector-set! rs1)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert-reg/reg!
- as
- $tag.vector-tag
- (+ $imm.vector-header $tag.vector-typetag)
- rs1
- rs2
- $ex.vset))))
- (emit-vector-like-set! as rs1 rs2 rs3 fault $tag.vector-tag #t))))
- (define-primop 'internal:vector-length:vec
- (lambda (as rs1 dst)
- (internal-primop-invariant2 'internal:vector-length:vec rs1 dst)
- (emit-get-length-trusted! as $tag.vector-tag rs1 dst)))
- (define-primop 'internal:vector-ref:trusted
- (lambda (as rs1 rs2 dst)
- (emit-vector-like-ref-trusted! as rs1 rs2 dst $tag.vector-tag)))
- (define-primop 'internal:vector-set!:trusted
- (lambda (as rs1 rs2 rs3)
- (emit-vector-like-ref-trusted! as rs1 rs2 rs3 $tag.vector-tag)))
- ; Strings.
- (define-primop 'internal:string-length
- (lambda (as rs rd)
- (internal-primop-invariant2 'internal:string-length rs rd)
- (emit-get-length! as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.string-typetag)
- $ex.slen
- rs
- rd)))
- (define-primop 'internal:string-ref
- (lambda (as rs1 rs2 rd)
- (internal-primop-invariant2 'internal:string-ref rs1 rd)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert-reg/reg!
- as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.string-typetag)
- rs1
- rs2
- $ex.sref))))
- (emit-bytevector-like-ref! as rs1 rs2 rd fault #t #t))))
- (define-primop 'internal:string-ref/imm
- (lambda (as rs1 imm rd)
- (internal-primop-invariant2 'internal:string-ref/imm rs1 rd)
- (let ((fault (if (not (unsafe-code))
- (emit-double-tagcheck-assert-reg/imm!
- as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.string-typetag)
- rs1
- imm
- $ex.sref))))
- (emit-bytevector-like-ref/imm! as rs1 imm rd fault #t #t))))
- (define-primop 'internal:string-set!
- (lambda (as rs1 rs2 rs3)
- (internal-primop-invariant1 'internal:string-set! rs1)
- (emit-string-set! as rs1 rs2 rs3)))
- (define-primop 'internal:+
- (lambda (as src1 src2 dest)
- (internal-primop-invariant2 'internal:+ src1 dest)
- (emit-arith-primop! as sparc.taddrcc sparc.subr $m.add src1 src2 dest #t)))
- (define-primop 'internal:+/imm
- (lambda (as src1 imm dest)
- (internal-primop-invariant2 'internal:+/imm src1 dest)
- (emit-arith-primop! as sparc.taddicc sparc.subi $m.add src1 imm dest #f)))
- (define-primop 'internal:-
- (lambda (as src1 src2 dest)
- (internal-primop-invariant2 'internal:- src1 dest)
- (emit-arith-primop! as sparc.tsubrcc sparc.addr $m.subtract
- src1 src2 dest #t)))
- (define-primop 'internal:-/imm
- (lambda (as src1 imm dest)
- (internal-primop-invariant2 'internal:-/imm src1 dest)
- (emit-arith-primop! as sparc.tsubicc sparc.addi $m.subtract
- src1 imm dest #f)))
- (define-primop 'internal:--
- (lambda (as rs rd)
- (internal-primop-invariant2 'internal:-- rs rd)
- (emit-negate as rs rd)))
- (define-primop 'internal:branchf-null?
- (lambda (as reg label)
- (internal-primop-invariant1 'internal:branchf-null? reg)
- (sparc.cmpi as reg $imm.null)
- (sparc.bne.a as label)
- (sparc.slot as)))
- (define-primop 'internal:branchf-pair?
- (lambda (as reg label)
- (internal-primop-invariant1 'internal:branchf-pair? reg)
- (sparc.andi as reg $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 $tag.pair-tag)
- (sparc.bne.a as label)
- (sparc.slot as)))
- (define-primop 'internal:branchf-zero?
- (lambda (as reg label)
- (internal-primop-invariant1 'internal:brancf-zero? reg)
- (emit-bcmp-primop! as sparc.bne.a reg $r.g0 label $m.zerop #t)))
- (define-primop 'internal:branchf-eof-object?
- (lambda (as rs label)
- (internal-primop-invariant1 'internal:branchf-eof-object? rs)
- (sparc.cmpi as rs $imm.eof)
- (sparc.bne.a as label)
- (sparc.slot as)))
- (define-primop 'internal:branchf-fixnum?
- (lambda (as rs label)
- (internal-primop-invariant1 'internal:branchf-fixnum? rs)
- (sparc.btsti as rs 3)
- (sparc.bne.a as label)
- (sparc.slot as)))
- (define-primop 'internal:branchf-char?
- (lambda (as rs label)
- (internal-primop-invariant1 'internal:branchf-char? rs)
- (sparc.andi as rs 255 $r.tmp0)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (sparc.bne.a as label)
- (sparc.slot as)))
- (define-primop 'internal:branchf-=
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-= src1)
- (emit-bcmp-primop! as sparc.bne.a src1 src2 label $m.numeq #t)))
- (define-primop 'internal:branchf-<
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-< src1)
- (emit-bcmp-primop! as sparc.bge.a src1 src2 label $m.numlt #t)))
- (define-primop 'internal:branchf-<=
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-<= src1)
- (emit-bcmp-primop! as sparc.bg.a src1 src2 label $m.numle #t)))
- (define-primop 'internal:branchf->
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-> src1)
- (emit-bcmp-primop! as sparc.ble.a src1 src2 label $m.numgt #t)))
- (define-primop 'internal:branchf->=
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf->= src1)
- (emit-bcmp-primop! as sparc.bl.a src1 src2 label $m.numge #t)))
- (define-primop 'internal:branchf-=/imm
- (lambda (as src1 imm label)
- (internal-primop-invariant1 'internal:branchf-=/imm src1)
- (emit-bcmp-primop! as sparc.bne.a src1 imm label $m.numeq #f)))
- (define-primop 'internal:branchf-</imm
- (lambda (as src1 imm label)
- (internal-primop-invariant1 'internal:branchf-</imm src1)
- (emit-bcmp-primop! as sparc.bge.a src1 imm label $m.numlt #f)))
- (define-primop 'internal:branchf-<=/imm
- (lambda (as src1 imm label)
- (internal-primop-invariant1 'internal:branchf-<=/imm src1)
- (emit-bcmp-primop! as sparc.bg.a src1 imm label $m.numle #f)))
- (define-primop 'internal:branchf->/imm
- (lambda (as src1 imm label)
- (internal-primop-invariant1 'internal:branchf->/imm src1)
- (emit-bcmp-primop! as sparc.ble.a src1 imm label $m.numgt #f)))
- (define-primop 'internal:branchf->=/imm
- (lambda (as src1 imm label)
- (internal-primop-invariant1 'internal:branchf->=/imm src1)
- (emit-bcmp-primop! as sparc.bl.a src1 imm label $m.numge #f)))
- (define-primop 'internal:branchf-char=?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-char=? src1)
- (emit-char-bcmp-primop! as sparc.bne.a src1 src2 label $ex.char=?)))
- (define-primop 'internal:branchf-char<=?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-char<=? src1)
- (emit-char-bcmp-primop! as sparc.bg.a src1 src2 label $ex.char<=?)))
- (define-primop 'internal:branchf-char<?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-char<? src1)
- (emit-char-bcmp-primop! as sparc.bge.a src1 src2 label $ex.char<?)))
- (define-primop 'internal:branchf-char>=?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-char>=? src1)
- (emit-char-bcmp-primop! as sparc.bl.a src1 src2 label $ex.char>=?)))
- (define-primop 'internal:branchf-char>?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-char>=? src1)
- (emit-char-bcmp-primop! as sparc.ble.a src1 src2 label $ex.char>?)))
- (define-primop 'internal:branchf-char=?/imm
- (lambda (as src imm label)
- (internal-primop-invariant1 'internal:branchf-char=?/imm src)
- (emit-char-bcmp-primop! as sparc.bne.a src imm label $ex.char=?)))
- (define-primop 'internal:branchf-char>=?/imm
- (lambda (as src imm label)
- (internal-primop-invariant1 'internal:branchf-char>=?/imm src)
- (emit-char-bcmp-primop! as sparc.bl.a src imm label $ex.char>=?)))
- (define-primop 'internal:branchf-char>?/imm
- (lambda (as src imm label)
- (internal-primop-invariant1 'internal:branchf-char>?/imm src)
- (emit-char-bcmp-primop! as sparc.ble.a src imm label $ex.char>?)))
- (define-primop 'internal:branchf-char<=?/imm
- (lambda (as src imm label)
- (internal-primop-invariant1 'internal:branchf-char<=?/imm src)
- (emit-char-bcmp-primop! as sparc.bg.a src imm label $ex.char<=?)))
- (define-primop 'internal:branchf-char<?/imm
- (lambda (as src imm label)
- (internal-primop-invariant1 'internal:branchf-char<?/imm src)
- (emit-char-bcmp-primop! as sparc.bge.a src imm label $ex.char<?)))
- (define-primop 'internal:eq?
- (lambda (as src1 src2 dest)
- (internal-primop-invariant2 'internal:eq? src1 dest)
- (let ((tmp (force-hwreg! as src2 $r.tmp0)))
- (sparc.cmpr as src1 tmp)
- (emit-set-boolean-reg! as dest))))
- (define-primop 'internal:eq?/imm
- (lambda (as rs imm rd)
- (internal-primop-invariant2 'internal:eq?/imm rs rd)
- (cond ((fixnum? imm) (sparc.cmpi as rs (thefixnum imm)))
- ((eq? imm #t) (sparc.cmpi as rs $imm.true))
- ((eq? imm #f) (sparc.cmpi as rs $imm.false))
- ((null? imm) (sparc.cmpi as rs $imm.null))
- (else ???))
- (emit-set-boolean-reg! as rd)))
- (define-primop 'internal:branchf-eq?
- (lambda (as src1 src2 label)
- (internal-primop-invariant1 'internal:branchf-eq? src1)
- (let ((src2 (force-hwreg! as src2 $r.tmp0)))
- (sparc.cmpr as src1 src2)
- (sparc.bne.a as label)
- (sparc.slot as))))
- (define-primop 'internal:branchf-eq?/imm
- (lambda (as rs imm label)
- (internal-primop-invariant1 'internal:branchf-eq?/imm rs)
- (cond ((fixnum? imm) (sparc.cmpi as rs (thefixnum imm)))
- ((eq? imm #t) (sparc.cmpi as rs $imm.true))
- ((eq? imm #f) (sparc.cmpi as rs $imm.false))
- ((null? imm) (sparc.cmpi as rs $imm.null))
- (else ???))
- (sparc.bne.a as label)
- (sparc.slot as)))
- ; Unary predicates followed by a check.
- (define-primop 'internal:check-fixnum?
- (lambda (as src L1 liveregs)
- (sparc.btsti as src 3)
- (emit-checkcc! as sparc.bne L1 liveregs)))
- (define-primop 'internal:check-pair?
- (lambda (as src L1 liveregs)
- (sparc.andi as src $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 $tag.pair-tag)
- (emit-checkcc! as sparc.bne L1 liveregs)))
- (define-primop 'internal:check-vector?
- (lambda (as src L1 liveregs)
- (sparc.andi as src $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 $tag.vector-tag)
- (sparc.bne as L1)
- (sparc.nop as)
- (sparc.ldi as src (- $tag.vector-tag) $r.tmp0)
- (sparc.andi as $r.tmp0 255 $r.tmp1)
- (sparc.cmpi as $r.tmp1 $imm.vector-header)
- (emit-checkcc! as sparc.bne L1 liveregs)))
- (define-primop 'internal:check-vector?/vector-length:vec
- (lambda (as src dst L1 liveregs)
- (sparc.andi as src $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 $tag.vector-tag)
- (sparc.bne as L1)
- (sparc.nop as)
- (sparc.ldi as src (- $tag.vector-tag) $r.tmp0)
- (sparc.andi as $r.tmp0 255 $r.tmp1)
- (sparc.cmpi as $r.tmp1 $imm.vector-header)
- (sparc.bne as L1)
- (apply sparc.slot2 as liveregs)
- (sparc.srli as $r.tmp0 8 dst)))
- (define (internal-primop-invariant2 name a b)
- (if (not (and (hardware-mapped? a) (hardware-mapped? b)))
- (asm-error "SPARC assembler internal invariant violated by " name
- " on operands " a " and " b)))
- (define (internal-primop-invariant1 name a)
- (if (not (hardware-mapped? a))
- (asm-error "SPARC assembler internal invariant violated by " name
- " on operand " a)))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; SPARC code generation macros for primitives, part 3a:
- ; helper procedures for scalars.
- ; LOGAND, LOGIOR, LOGXOR: logical operations on fixnums.
- ;
- ; Input: Registers rs1 and rs2, both of which can be general registers.
- ; In addition, rs1 can be RESULT, and rs2 can be ARGREG2.
- ; Output: Register dest, which can be a general register or RESULT.
- (define (logical-op as rs1 rs2 dest op excode)
- (define (fail rs1 rs2 L0)
- (if (not (= rs1 $r.result)) (sparc.move as rs1 $r.result))
- (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
- (sparc.set as (thefixnum excode) $r.tmp0)
- (millicode-call/ret as $m.exception L0))
- (let ((L0 (new-label))
- (L1 (new-label)))
- (sparc.label as L0)
- (let ((rs1 (force-hwreg! as rs1 $r.result))
- (rs2 (force-hwreg! as rs2 $r.argreg2))
- (u (unsafe-code))
- (d (hardware-mapped? dest)))
- (cond ((and u d)
- (op as rs1 rs2 dest))
- ((and u (not d))
- (op as rs1 rs2 $r.tmp0)
- (emit-store-reg! as $r.tmp0 dest))
- ((and (not u) d)
- (sparc.orr as rs1 rs2 $r.tmp0)
- (sparc.btsti as $r.tmp0 3)
- (sparc.bz.a as L1)
- (op as rs1 rs2 dest)
- (fail rs1 rs2 L0)
- (sparc.label as L1))
- (else
- (sparc.orr as rs1 rs2 $r.tmp0)
- (sparc.btsti as $r.tmp0 3)
- (sparc.bz.a as L1)
- (op as rs1 rs2 $r.tmp0)
- (fail rs1 rs2 L0)
- (sparc.label as L1)
- (emit-store-reg! as $r.tmp0 dest))))))
- ; LSH, RSHA, RSHL: Bitwise shifts on fixnums.
- ;
- ; Notes for future contemplation:
- ; - The semantics do not match those of MIT Scheme or MacScheme: only
- ; positive shifts are allowed.
- ; - The names do not match the fixnum-specific procedures of Chez Scheme
- ; that have the same semantics: fxsll, fxsra, fxsrl.
- ; - This code checks that the second argument is in range; if it did
- ; not, then we could get a MOD for free. Probably too hardware-dependent
- ; to worry about.
- ; - The range 0..31 for the shift count is curious given that the fixnum
- ; is 30-bit.
- (define (emit-shift-operation as exn rs1 rs2 rd)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
- (if (not (unsafe-code))
- (let ((L0 (new-label))
- (FAULT (new-label))
- (START (new-label)))
- (sparc.label as START)
- (sparc.btsti as rs1 3) ; RS1 fixnum?
- (sparc.be.a as L0)
- (sparc.andi as rs2 #x7c $r.g0) ; RS2 fixnum and 0 <= RS2 < 32?
- (sparc.label as FAULT)
- (if (not (= rs1 $r.result))
- (sparc.move as rs1 $r.result))
- (if (not (= rs2 $r.argreg2))
- (emit-move2hwreg! as rs2 $r.argreg2))
- (sparc.set as (thefixnum exn) $r.tmp0)
- (millicode-call/ret as $m.exception START)
- (sparc.label as L0)
- (sparc.bne as FAULT)
- (sparc.srai as rs2 2 $r.tmp1))
- (begin
- (sparc.srai as rs2 2 $r.tmp1)))
- (cond ((= exn $ex.lsh)
- (sparc.sllr as rs1 $r.tmp1 rd))
- ((= exn $ex.rshl)
- (sparc.srlr as rs1 $r.tmp1 rd)
- (sparc.andni as rd 3 rd))
- ((= exn $ex.rsha)
- (sparc.srar as rs1 $r.tmp1 rd)
- (sparc.andni as rd 3 rd))
- (else ???))))
- ; Set result on condition code.
- ;
- ; The processor's zero bit has been affected by a previous instruction.
- ; If the bit is set, store #t in RESULT, otherwise store #f in RESULT.
- (define (emit-set-boolean! as)
- (emit-set-boolean-reg! as $r.result))
- ; Set on condition code.
- ;
- ; The processor's zero bit has been affected by a previous instruction.
- ; If the bit is set, store #t in the processor register 'dest', otherwise
- ; store #f in 'dest'.
- (define (emit-set-boolean-reg! as dest)
- (let ((L1 (new-label)))
- (sparc.set as $imm.true dest)
- (sparc.bne.a as L1)
- (sparc.set as $imm.false dest)
- (sparc.label as L1)))
- ; Representation predicate.
- (define (emit-single-tagcheck->bool! as tag)
- (sparc.andi as $r.result $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 tag)
- (emit-set-boolean! as))
- (define (emit-single-tagcheck-assert! as tag1 excode reg2)
- (emit-single-tagcheck-assert-reg! as tag1 $r.result reg2 excode))
- (define (emit-single-tagcheck-assert-reg! as tag1 reg reg2 excode)
- (let ((L0 (new-label))
- (L1 (new-label))
- (FAULT (new-label)))
- (sparc.label as L0)
- (sparc.andi as reg $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 tag1)
- (fault-if-ne as excode #f #f reg reg2 L0)))
- ; Assert that a machine register has a fixnum in it.
- ; Returns the label of the fault code.
- (define (emit-assert-fixnum! as reg excode)
- (let ((L0 (new-label))
- (L1 (new-label))
- (FAULT (new-label)))
- (sparc.label as L0)
- (sparc.btsti as reg 3)
- (fault-if-ne as excode #f #f reg #f L0)))
- ; Assert that RESULT has a character in it.
- ; Returns the label of the fault code.
- (define (emit-assert-char! as excode fault-label)
- (let ((L0 (new-label))
- (L1 (new-label))
- (FAULT (new-label)))
- (sparc.label as L0)
- (sparc.andi as $r.result #xFF $r.tmp0)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (fault-if-ne as excode #f fault-label #f #f L0)))
- ; Generate code for fault handling if the zero flag is not set.
- ; - excode is the nativeint exception code.
- ; - cont-label, if not #f, is the label to go to if there is no fault.
- ; - fault-label, if not #f, is the label of an existing fault handler.
- ; - reg1, if not #f, is the number of a register which must be
- ; moved into RESULT before the fault handler is called.
- ; - reg2, if not #f, is the number of a register which must be moved
- ; into ARGREG2 before the fault handler is called.
- ; - ret-label, if not #f, is the return address to be set up before calling
- ; the fault handler.
- ;
- ; Ret-label and fault-label cannot simultaneously be non-#f; in this case
- ; the ret-label is ignored (since the existing fault handler most likely
- ; sets up the return in the desired manner).
- (define (fault-if-ne as excode cont-label fault-label reg1 reg2 ret-label)
- (if fault-label
- (begin
- (if (and reg2 (not (= reg2 $r.argreg2)))
- (emit-move2hwreg! as reg2 $r.argreg2))
- (sparc.bne as fault-label)
- (if (and reg1 (not (= reg1 $r.result)))
- (sparc.move as reg1 $r.result)
- (sparc.nop as))
- fault-label)
- (let ((FAULT (new-label))
- (L1 (new-label)))
- (sparc.be.a as (or cont-label L1))
- (sparc.slot as)
- (sparc.label as FAULT)
- (if (and reg1 (not (= reg1 $r.result)))
- (sparc.move as reg1 $r.result))
- (if (and reg2 (not (= reg2 $r.argreg2)))
- (emit-move2hwreg! as reg2 $r.argreg2))
- (sparc.set as (thefixnum excode) $r.tmp0)
- (millicode-call/ret as $m.exception (or ret-label L1))
- (if (or (not cont-label) (not ret-label))
- (sparc.label as L1))
- FAULT)))
- ; This is more expensive than what is good for it (5 cycles in the usual case),
- ; but there does not seem to be a better way.
- (define (emit-assert-positive-fixnum! as reg excode)
- (let ((L1 (new-label))
- (L2 (new-label))
- (L3 (new-label)))
- (sparc.label as L2)
- (sparc.tsubrcc as reg $r.g0 $r.g0)
- (sparc.bvc as L1)
- (sparc.nop as)
- (sparc.label as L3)
- (if (not (= reg $r.result))
- (sparc.move as reg $r.result))
- (sparc.set as (thefixnum excode) $r.tmp0)
- (millicode-call/ret as $m.exception l2)
- (sparc.label as L1)
- (sparc.bl as L3)
- (sparc.nop as)
- L3))
- ; Arithmetic comparison with boolean result.
- (define (emit-cmp-primop! as branch_t.a generic r)
- (let ((Ltagok (new-label))
- (Lcont (new-label))
- (r (force-hwreg! as r $r.argreg2)))
- (sparc.tsubrcc as $r.result r $r.g0)
- (sparc.bvc.a as Ltagok)
- (sparc.set as $imm.false $r.result)
- (if (not (= r $r.argreg2))
- (sparc.move as r $r.argreg2))
- (millicode-call/ret as generic Lcont)
- (sparc.label as Ltagok)
- (branch_t.a as Lcont)
- (sparc.set as $imm.true $r.result)
- (sparc.label as Lcont)))
- ; Arithmetic comparison and branch.
- ;
- ; This code does not use the chained branch trick (DCTI) that was documented
- ; in the Sparc v8 manual and deprecated in the v9 manual. This code executes
- ; _much_ faster on the Ultra than the code using DCTI, even though it executes
- ; the same instructions.
- ;
- ; Parameters and preconditions.
- ; Src1 is a general register, RESULT, ARGREG2, or ARGREG3.
- ; Src2 is a general register, RESULT, ARGREG2, ARGREG3, or an immediate.
- ; Src2 is an immediate iff src2isreg = #f.
- ; Branch_f.a is a branch on condition code that branches if the condition
- ; is not true.
- ; Generic is the millicode table offset of the generic procedure.
- (define (emit-bcmp-primop! as branch_f.a src1 src2 Lfalse generic src2isreg)
- (let ((Ltagok (new-label))
- (Ltrue (new-label))
- (op2 (if src2isreg
- (force-hwreg! as src2 $r.tmp1)
- (thefixnum src2)))
- (sub (if src2isreg sparc.tsubrcc sparc.tsubicc))
- (mov (if src2isreg sparc.move sparc.set)))
- (sub as src1 op2 $r.g0)
- (sparc.bvc.a as Ltagok)
- (sparc.slot as)
- ; Not both fixnums.
- ; Must move src1 to result if src1 is not result.
- ; Must move src2 to argreg2 if src2 is not argreg2.
- (let ((move-res (not (= src1 $r.result)))
- (move-arg2 (or (not src2isreg) (not (= op2 $r.argreg2)))))
- (if (and move-arg2 move-res)
- (mov as op2 $r.argreg2))
- (sparc.jmpli as $r.millicode generic $r.o7)
- (cond (move-res (sparc.move as src1 $r.result))
- (move-arg2 (mov as op2 $r.argreg2))
- (else (sparc.nop as)))
- (sparc.cmpi as $r.result $imm.false)
- (sparc.bne.a as Ltrue)
- (sparc.slot as)
- (sparc.b as Lfalse)
- (sparc.slot as))
- (sparc.label as Ltagok)
- (branch_f.a as Lfalse)
- (sparc.slot as)
- (sparc.label as Ltrue)))
- ; Generic arithmetic for + and -.
- ; Some rules:
- ; We have two HW registers src1 and dest.
- ; If src2isreg is #t then src2 may be a HW reg or a SW reg
- ; If src2isreg is #f then src2 is an immediate fixnum, not shifted.
- ; Src1 and dest may be RESULT, but src2 may not.
- ; Src2 may be ARGREG2, the others may not.
- ;
- ; FIXME! This is incomprehensible.
- ; New code below.
- '(define (emit-arith-primop! as op invop generic src1 src2 dest src2isreg)
- (let ((L1 (new-label))
- (op2 (if src2isreg
- (force-hwreg! as src2 $r.tmp1)
- (thefixnum src2))))
- (if (and src2isreg (= op2 dest))
- (begin (op as src1 op2 $r.tmp0)
- (sparc.bvc.a as L1)
- (sparc.move as $r.tmp0 dest))
- (begin (op as src1 op2 dest)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (invop as dest op2 dest)))
- (let ((n (+ (if (not (= src1 $r.result)) 1 0)
- (if (or (not src2isreg) (not (= op2 $r.argreg2))) 1 0)))
- (mov2 (if src2isreg sparc.move sparc.set)))
- (if (= n 2)
- (mov2 as op2 $r.argreg2))
- (sparc.jmpli as $r.millicode generic $r.o7)
- (cond ((= n 0) (sparc.nop as))
- ((= n 1) (mov2 as op2 $r.argreg2))
- (else (sparc.move as src1 $r.result)))
- ; Generic arithmetic leaves stuff in RESULT, must move to dest if
- ; dest is not RESULT.
- (if (not (= dest $r.result))
- (sparc.move as $r.result dest))
- (sparc.label as L1))))
- ; Comprehensible, but longer.
- ;
- ; Important to be careful not to clobber arguments, and not to leave garbage
- ; in rd, if millicode is called.
- ;
- ; op is the appropriate operation.
- ; invop is the appropriate inverse operation.
- ; RS1 can be any general hw register or RESULT.
- ; RS2/IMM can be any general register or ARGREG2 (op2isreg=#t), or
- ; an immediate (op2isreg=#f)
- ; RD can be any general hw register or RESULT.
- ;
- ; FIXME: split this into two procedures.
- (define (emit-arith-primop! as op invop generic rs1 rs2/imm rd op2isreg)
- (let ((L1 (new-label)))
- (if op2isreg
- (let ((rs2 (force-hwreg! as rs2/imm $r.argreg2)))
- (cond ((or (= rs1 rs2 rd)
- (and (= rs2 rd)
- (= generic $m.subtract)))
- (op as rs1 rs2 $r.tmp0)
- (sparc.bvc.a as L1)
- (sparc.move as $r.tmp0 rd))
- ((= rs1 rd)
- (op as rs1 rs2 rs1)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (invop as rs1 rs2 rs1))
- ((= rs2 rd)
- (op as rs1 rs2 rs2)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (invop as rs2 rs1 rs2))
- (else
- (op as rs1 rs2 rd)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (if (and (not (= rd $r.result)) (not (= rd $r.argreg2)))
- (sparc.clr as rd))))
- (cond ((and (= rs1 $r.result) (= rs2 $r.argreg2))
- ;; Could peephole the INVOP or CLR into the slot here.
- (millicode-call/0arg as generic))
- ((= rs1 $r.result)
- (millicode-call/1arg as generic rs2))
- ((= rs2 $r.argreg2)
- (millicode-call/1arg-in-result as generic rs1))
- (else
- (sparc.move as rs2 $r.argreg2)
- (millicode-call/1arg-in-result as generic rs1))))
- (let ((imm (thefixnum rs2/imm)))
- (op as rs1 imm rd)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (invop as rd imm rd)
- (if (not (= rs1 $r.result))
- (sparc.move as rs1 $r.result))
- (millicode-call/numarg-in-reg as generic imm $r.argreg2)))
- (if (not (= rd $r.result))
- (sparc.move as $r.result rd))
- (sparc.label as L1)))
- ; Important to be careful not to leave garbage in rd if millicode is called.
- (define (emit-negate as rs rd)
- (let ((L1 (new-label)))
- (cond ((= rs rd)
- (sparc.tsubrcc as $r.g0 rs rs)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (if (= rs $r.result)
- (begin
- (sparc.jmpli as $r.millicode $m.negate $r.o7)
- (sparc.subr as $r.g0 $r.result $r.result))
- (begin
- (sparc.subr as $r.g0 rs rs)
- (sparc.jmpli as $r.millicode $m.negate $r.o7)
- (sparc.move as rs $r.result))))
- (else
- (sparc.tsubrcc as $r.g0 rs rd)
- (sparc.bvc.a as L1)
- (sparc.slot as)
- (cond ((= rs $r.result)
- (sparc.jmpli as $r.millicode $m.negate $r.o7)
- (sparc.clr as rd))
- ((= rd $r.result)
- (sparc.jmpli as $r.millicode $m.negate $r.o7)
- (sparc.move as rs $r.result))
- (else
- (sparc.clr as rd)
- (sparc.jmpli as $r.millicode $m.negate $r.o7)
- (sparc.move as rs $r.result)))))
- (if (not (= rd $r.result))
- (sparc.move as $r.result rd))
- (sparc.label as L1)))
- ; Character comparison.
- ; r is a register or a character constant.
- (define (emit-char-cmp as r btrue.a excode)
- (emit-charcmp! as (lambda ()
- (let ((l2 (new-label)))
- (sparc.set as $imm.false $r.result)
- (btrue.a as L2)
- (sparc.set as $imm.true $r.result)
- (sparc.label as L2)))
- $r.result
- r
- excode))
-
- ; op1 is a hw register
- ; op2 is a register or a character constant
- (define (emit-char-bcmp-primop! as bfalse.a op1 op2 L0 excode)
- (emit-charcmp! as (lambda ()
- (bfalse.a as L0)
- (sparc.slot as))
- op1
- op2
- excode))
- ; We check the tags of both by xoring them and seeing if the low byte is 0.
- ; If so, then we can subtract one from the other (tag and all) and check the
- ; condition codes.
- ;
- ; The branch-on-true instruction must have the annull bit set. (???)
- ;
- ; op1 is a hw register
- ; op2 is a register or a character constant.
- (define (emit-charcmp! as tail op1 op2 excode)
- (let ((op2 (if (char? op2)
- op2
- (force-hwreg! as op2 $r.argreg2))))
- (cond ((not (unsafe-code))
- (let ((L0 (new-label))
- (L1 (new-label))
- (FAULT (new-label)))
- (sparc.label as L0)
- (cond ((char? op2)
- (sparc.xori as op1 $imm.character $r.tmp0)
- (sparc.btsti as $r.tmp0 #xFF)
- (sparc.srli as op1 16 $r.tmp0)
- (sparc.be.a as L1)
- (sparc.cmpi as $r.tmp0 (char->integer op2)))
- (else
- (sparc.andi as op1 #xFF $r.tmp0)
- (sparc.andi as op2 #xFF $r.tmp1)
- (sparc.cmpr as $r.tmp0 $r.tmp1)
- (sparc.bne as FAULT)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (sparc.be.a as L1)
- (sparc.cmpr as op1 op2)))
- (sparc.label as FAULT)
- (if (not (eqv? op1 $r.result))
- (sparc.move as op1 $r.result))
- (cond ((char? op2)
- (emit-immediate->register! as
- (char->immediate op2)
- $r.argreg2))
- ((not (eqv? op2 $r.argreg2))
- (sparc.move as op2 $r.argreg2)))
- (sparc.set as (thefixnum excode) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1)))
- ((not (char? op2))
- (sparc.cmpr as op1 op2))
- (else
- (sparc.srli as op1 16 $r.tmp0)
- (sparc.cmpi as $r.tmp0 (char->integer op2))))
- (tail)))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; SPARC code generation macros for primitives, part 3b:
- ; helper procedures for data structures.
- ; SET-CAR!, SET-CDR!, CELL-SET!
- ;
- ; Input: RS1: a hardware register; has pair pointer (tag check must be
- ; performed by the caller).
- ; RS2: any register; has value to store.
- ; Output: None.
- ;
- ; Having rs1 != RESULT is pretty silly with the current write barrier
- ; but will be less silly with the new barrier.
- (define (emit-setcar/setcdr! as rs1 rs2 offs)
- (cond ((and (write-barrier) (hardware-mapped? rs2))
- (sparc.sti as rs2 (- offs $tag.pair-tag) rs1)
- (if (not (= rs1 $r.result))
- (sparc.move as rs1 $r.result))
- (millicode-call/1arg as $m.addtrans rs2))
- ((write-barrier)
- (emit-move2hwreg! as rs2 $r.argreg2)
- (sparc.sti as $r.argreg2 (- offs $tag.pair-tag) rs1)
- (millicode-call/1arg-in-result as $m.addtrans rs1))
- ((hardware-mapped? rs2)
- (sparc.sti as rs2 (- offs $tag.pair-tag) rs1))
- (else
- (emit-move2hwreg! as rs2 $r.argreg2)
- (sparc.sti as $r.argreg2 (- offs $tag.pair-tag) rs1))))
- ; Representation predicate.
- ;
- ; RESULT has an object. If the tag of RESULT is 'tag1' and the
- ; header byte of the object is 'tag2' then set RESULT to #t, else
- ; set it to #f.
- (define (emit-double-tagcheck->bool! as tag1 tag2)
- (let ((L1 (new-label)))
- (sparc.andi as $r.result $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 tag1)
- (sparc.bne.a as L1)
- (sparc.set as $imm.false $r.result)
- (sparc.ldbi as $r.result (+ (- tag1) 3) $r.tmp0)
- (sparc.set as $imm.true $r.result)
- (sparc.cmpi as $r.tmp0 tag2)
- (sparc.bne.a as L1)
- (sparc.set as $imm.false $r.result)
- (sparc.label as L1)))
- ; Check structure tag.
- ;
- ; RS1 has an object. If the tag of RS1 is not 'tag1', or if the tag is
- ; 'tag1' but the header byte of the object header is not 'tag2', then an
- ; exception with code 'excode' is signaled. The exception call is set
- ; up to return to the first instruction of the emitted code.
- ;
- ; If RS1 is not RESULT then it is moved to RESULT before the exception
- ; is signaled.
- ;
- ; If RS2/IMM is not #f, then it is a register or immediate that is moved
- ; to ARGREG2 before the exception is signaled; it is an immediate iff
- ; imm? = #t.
- ;
- ; RS1 must be a hardware register.
- ; RS2/IMM is a general register, ARGREG2, an immediate, or #f.
- ; RS3 is a general register, ARGREG3, or #f.
- ;
- ; The procedure returns the label of the fault address. If the execution
- ; falls off the end of the emitted instruction sequence, then the following
- ; are true:
- ; - the tag of the object in RS1 was 'tag1' and its header byte was 'tag2'
- ; - the object header word is in TMP0.
- (define (double-tagcheck-assert as tag1 tag2 rs1 rs2/imm rs3 excode imm?)
- (let ((L0 (new-label))
- (L1 (new-label))
- (FAULT (new-label)))
- (sparc.label as L0)
- (sparc.andi as rs1 $tag.tagmask $r.tmp0)
- (sparc.cmpi as $r.tmp0 tag1)
- (sparc.be.a as L1)
- (sparc.ldi as rs1 (- tag1) $r.tmp0)
- (sparc.label as FAULT)
- (if (not (= rs1 $r.result))
- (sparc.move as rs1 $r.result))
- (if rs2/imm
- (cond (imm?
- (sparc.set as (thefixnum rs2/imm) $r.argreg2))
- ((= rs2/imm $r.argreg2))
- (else
- (emit-move2hwreg! as rs2/imm $r.argreg2))))
- (if (and rs3 (not (= rs3 $r.argreg3)))
- (emit-move2hwreg! as rs3 $r.argreg3))
- (sparc.set as (thefixnum excode) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1)
- (sparc.andi as $r.tmp0 255 $r.tmp1)
- (sparc.cmpi as $r.tmp1 tag2)
- (sparc.bne.a as FAULT)
- (sparc.slot as)
- FAULT))
- (define (emit-double-tagcheck-assert! as tag1 tag2 excode reg2)
- (double-tagcheck-assert as tag1 tag2 $r.result reg2 #f excode #f))
- (define (emit-double-tagcheck-assert-reg/reg! as tag1 tag2 rs1 rs2 excode)
- (double-tagcheck-assert as tag1 tag2 rs1 rs2 #f excode #f))
-
- (define (emit-double-tagcheck-assert-reg/imm! as tag1 tag2 rs1 imm excode)
- (double-tagcheck-assert as tag1 tag2 rs1 imm #f excode #t))
-
- ; Get the length of a vector or bytevector structure, with tag checking
- ; included.
- ;
- ; Input: RS and RD are both hardware registers.
- (define (emit-get-length! as tag1 tag2 excode rs rd)
- (if (not (unsafe-code))
- (if tag2
- (emit-double-tagcheck-assert-reg/reg! as tag1 tag2 rs rd excode)
- (emit-single-tagcheck-assert-reg! as tag1 rs rd excode)))
- (emit-get-length-trusted! as tag1 rs rd))
- ; Get the length of a vector or bytevector structure, without tag checking.
- ;
- ; Input: RS and RD are both hardware registers.
- (define (emit-get-length-trusted! as tag1 rs rd)
- (sparc.ldi as rs (- tag1) $r.tmp0)
- (sparc.srli as $r.tmp0 8 rd)
- (if (= tag1 $tag.bytevector-tag)
- (sparc.slli as rd 2 rd)))
- ; Allocate a bytevector, leave untagged pointer in RESULT.
- (define (emit-allocate-bytevector as hdr preserved-result)
- ; Preserve the length field, then calculate the number of words
- ; to allocate. The value `28' is an adjustment of 3 (for rounding
- ; up) plus another 4 bytes for the header, all represented as a fixnum.
- (if (not preserved-result)
- (sparc.move as $r.result $r.argreg2))
- (sparc.addi as $r.result 28 $r.result)
- (sparc.andi as $r.result (asm:signed #xFFFFFFF0) $r.result)
- ; Allocate space
- (sparc.jmpli as $r.millicode $m.alloc-bv $r.o7)
- (sparc.srai as $r.result 2 $r.result)
-
- ; Setup the header.
- (if (not preserved-result)
- (sparc.slli as $r.argreg2 6 $r.tmp0)
- (sparc.slli as preserved-result 6 $r.tmp0))
- (sparc.addi as $r.tmp0 hdr $r.tmp0)
- (sparc.sti as $r.tmp0 0 $r.result))
- ; Given a nativeint count, a pointer to the first element of a
- ; bytevector-like structure, and a byte value, fill the bytevector
- ; with the byte value.
- (define (emit-bytevector-fill as r-bytecount r-pointer r-value)
- (let ((L2 (new-label))
- (L1 (new-label)))
- (sparc.label as L2)
- (sparc.deccc as r-bytecount)
- (sparc.bge.a as L2)
- (sparc.stbr as r-value r-bytecount r-pointer)
- (sparc.label as L1)))
- ; BYTEVECTOR-REF, BYTEVECTOR-LIKE-REF, STRING-REF.
- ;
- ; The pointer in RS1 is known to be bytevector-like. RS2 is the fixnum
- ; index into the structure. Get the RS2'th element and place it in RD.
- ;
- ; RS1 and RD are hardware registers.
- ; RS2 is a general register or ARGREG2.
- ; 'fault' is defined iff (unsafe-code) = #f
- ; header is in TMP0 iff (unsafe-code) = #f and 'header-loaded?' = #t
- ; if 'charize?' is #t then store result as char, otherwise as fixnum.
- (define (emit-bytevector-like-ref! as rs1 rs2 rd fault charize? header-loaded?)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
- (if (not (unsafe-code))
- (begin
- ; check that index is fixnum
- (sparc.btsti as rs2 3)
- (sparc.bne as fault)
- (if (not header-loaded?)
- (sparc.ldi as rs1 (- $tag.bytevector-tag) $r.tmp0))
- ; check length
- (sparc.srai as rs2 2 $r.tmp1)
- (sparc.srli as $r.tmp0 8 $r.tmp0)
- (sparc.cmpr as $r.tmp0 $r.tmp1)
- (sparc.bleu as fault)
- ; No NOP or SLOT -- the SUBI below goes into the slot.
- )
- (begin
- (sparc.srai as rs2 2 $r.tmp1)))
- ; Pointer is in RS1.
- ; Shifted index is in TMP1.
- (sparc.addi as rs1 (- 4 $tag.bytevector-tag) $r.tmp0)
- (sparc.ldbr as $r.tmp0 $r.tmp1 $r.tmp0)
- (if (not charize?)
- (sparc.slli as $r.tmp0 2 rd)
- (begin (sparc.slli as $r.tmp0 16 rd)
- (sparc.ori as rd $imm.character rd)))))
- ; As above, but RS2 is replaced by an immediate, IMM.
- ;
- ; The immediate, represented as a fixnum, is guaranteed fit in the
- ; instruction's immediate field.
- (define (emit-bytevector-like-ref/imm! as rs1 imm rd fault charize?
- header-loaded?)
- (if (not (unsafe-code))
- (begin
- (if (not header-loaded?)
- (sparc.ldi as rs1 (- $tag.bytevector-tag) $r.tmp0))
- ; Range check.
- (sparc.srli as $r.tmp0 8 $r.tmp0)
- (sparc.cmpi as $r.tmp0 imm)
- (sparc.bleu.a as fault)
- (sparc.slot as)))
- ; Pointer is in RS1.
- (let ((adjusted-offset (+ (- 4 $tag.bytevector-tag) imm)))
- (if (immediate-literal? adjusted-offset)
- (begin
- (sparc.ldbi as rs1 adjusted-offset $r.tmp0))
- (begin
- (sparc.addi as rs1 (- 4 $tag.bytevector-tag) $r.tmp0)
- (sparc.ldbr as $r.tmp0 imm $r.tmp0)))
- (if (not charize?)
- (sparc.slli as $r.tmp0 2 rd)
- (begin (sparc.slli as $r.tmp0 16 rd)
- (sparc.ori as rd $imm.character rd)))))
- ; BYTEVECTOR-SET!, BYTEVECTOR-LIKE-SET!
- ;
- ; Input: RESULT -- a pointer to a bytevector-like structure.
- ; TMP0 -- the header iff (unsafe-code) = #f and header-loaded? = #t
- ; IDX -- a register that holds the second argument
- ; BYTE -- a register that holds the third argument
- ; Output: Nothing.
- ;
- ; 'Fault' is the address of the error code iff (unsafe-code) = #f
- ;
- ; FIXME:
- ; - Argument values passed to error handler appear to be bogus
- ; (error message is very strange).
- ; - There's no check that the value actually fits in a byte.
- ; - Uses ARGREG3 and and TMP2.
- (define (emit-bytevector-like-set! as idx byte fault header-loaded?)
- (let ((r1 (force-hwreg! as idx $r.tmp1))
- (r2 (force-hwreg! as byte $r.argreg3)))
- (if (not (unsafe-code))
- (begin
- (if (not header-loaded?)
- (sparc.ldi as $r.result (- $tag.bytevector-tag) $r.tmp0))
- ; Both index and byte must be fixnums.
- ; Can't use tsubcc because the computation may really overflow.
- (sparc.orr as r1 r2 $r.tmp2)
- (sparc.btsti as $r.tmp2 3)
- (sparc.bnz as fault)
- ; No NOP -- next instruction is OK in slot.
- ; Index must be in range.
- (sparc.srli as $r.tmp0 8 $r.tmp0) ; limit - in slot
- (sparc.srai as r1 2 $r.tmp1) ; index
- (sparc.cmpr as $r.tmp1 $r.tmp0)
- (sparc.bgeu as fault)
- ; No NOP -- next instruction is OK in slot.
- )
- (begin
- (sparc.srai as r1 2 $r.tmp1)))
- (sparc.srli as r2 2 $r.tmp0)
- ; Using ARGREG2 as the destination is OK because the resulting pointer
- ; value always looks like a fixnum. By doing so, we avoid needing TMP2.
- (sparc.addi as $r.result (- 4 $tag.bytevector-tag) $r.argreg2)
- (sparc.stbr as $r.tmp0 $r.tmp1 $r.argreg2)))
- ; STRING-SET!
- (define (emit-string-set! as rs1 rs2 rs3)
- (let* ((rs2 (force-hwreg! as rs2 $r.argreg2))
- (rs3 (force-hwreg! as rs3 $r.argreg3))
- (FAULT (if (not (unsafe-code))
- (double-tagcheck-assert
- as
- $tag.bytevector-tag
- (+ $imm.bytevector-header $tag.string-typetag)
- rs1 rs2 rs3
- $ex.sset
- #f))))
- ; Header is in TMP0; TMP1 and TMP2 are free.
- (if (not (unsafe-code))
- (begin
- ; RS2 must be a fixnum.
- (sparc.btsti as rs2 3)
- (sparc.bne as FAULT)
- ; Index (in RS2) must be valid; header is in tmp0.
- (sparc.srli as $r.tmp0 8 $r.tmp0) ; limit
- (sparc.srai as rs2 2 $r.tmp1) ; index
- (sparc.cmpr as $r.tmp1 $r.tmp0)
- (sparc.bgeu as FAULT)
- ; RS3 must be a character.
- (sparc.andi as rs3 #xFF $r.tmp0)
- (sparc.cmpi as $r.tmp0 $imm.character)
- (sparc.bne as FAULT)
- ; No NOP -- the SRLI below goes in the slot
- )
- (begin
- (sparc.srai as rs2 2 $r.tmp1)))
- ; tmp1 has nativeint index.
- ; rs3/argreg3 has character.
- ; tmp0 is garbage.
- (sparc.subi as $r.tmp1 (- $tag.bytevector-tag 4) $r.tmp1)
- (sparc.srli as rs3 16 $r.tmp0)
- (sparc.stbr as $r.tmp0 rs1 $r.tmp1)))
- ; VECTORS and PROCEDURES
- ; Allocate short vectors of known length; faster than the general case.
- ; FIXME: can also allocate in-line.
- (define (make-vector-n as length r)
- (sparc.jmpli as $r.millicode $m.alloc $r.o7)
- (sparc.set as (thefixnum (+ length 1)) $r.result)
- (emit-immediate->register! as (+ (* 256 (thefixnum length))
- $imm.vector-header
- $tag.vector-typetag)
- $r.tmp0)
- (sparc.sti as $r.tmp0 0 $r.result)
- (let ((dest (force-hwreg! as r $r.argreg2)))
- (do ((i 0 (+ i 1)))
- ((= i length))
- (sparc.sti as dest (* (+ i 1) 4) $r.result)))
- (sparc.addi as $r.result $tag.vector-tag $r.result))
- ; emit-make-vector-like! assumes argreg3 is not destroyed by alloci.
- ; FIXME: bug: $ex.mkvl is not right if the operation is make-procedure
- ; or make-vector.
- (define (emit-make-vector-like! as r hdr ptrtag)
- (let ((FAULT (emit-assert-positive-fixnum! as $r.result $ex.mkvl)))
- (sparc.move as $r.result $r.argreg3)
- (sparc.addi as $r.result 4 $r.result)
- (sparc.jmpli as $r.millicode $m.alloci $r.o7)
- (if (null? r)
- (sparc.set as $imm.null $r.argreg2)
- (emit-move2hwreg! as r $r.argreg2))
- (sparc.slli as $r.argreg3 8 $r.tmp0)
- (sparc.addi as $r.tmp0 hdr $r.tmp0)
- (sparc.sti as $r.tmp0 0 $r.result)
- (sparc.addi as $r.result ptrtag $r.result)))
- ; VECTOR-REF, VECTOR-LIKE-REF, PROCEDURE-REF
- ;
- ; FAULT is valid iff (unsafe-code) = #f
- ; Header is in TMP0 iff (unsafe-code) = #f and header-loaded? = #t.
- (define (emit-vector-like-ref! as rs1 rs2 rd FAULT tag header-loaded?)
- (let ((index (force-hwreg! as rs2 $r.argreg2)))
- (if (not (unsafe-code))
- (begin
- (if (not header-loaded?)
- (sparc.ldi as rs1 (- tag) $r.tmp0))
- ; Index must be fixnum.
- (sparc.btsti as index 3)
- (sparc.bne as FAULT)
- ; Index must be within bounds.
- (sparc.srai as $r.tmp0 8 $r.tmp0)
- (sparc.cmpr as $r.tmp0 index)
- (sparc.bleu as FAULT)
- ; No NOP; the following instruction is valid in the slot.
- ))
- (emit-vector-like-ref-trusted! as rs1 index rd tag)))
- (define (emit-vector-like-ref-trusted! as rs1 rs2 rd tag)
- (let ((index (force-hwreg! as rs2 $r.argreg2)))
- (sparc.addi as rs1 (- 4 tag) $r.tmp0)
- (sparc.ldr as $r.tmp0 index rd)))
- ; VECTOR-REF/IMM, VECTOR-LIKE-REF/IMM, PROCEDURE-REF/IMM
- ;
- ; 'rs1' is a hardware register containing a vectorish pointer (to a
- ; vector-like or procedure).
- ; 'imm' is a fixnum s.t. (immediate-literal? imm) => #t.
- ; 'rd' is a hardware register.
- ; 'FAULT' is the label of the error code iff (unsafe-code) => #f
- ; 'tag' is the tag of the pointer in rs1.
- ; 'header-loaded?' is #t iff the structure header word is in $r.tmp0.
- (define (emit-vector-like-ref/imm! as rs1 imm rd FAULT tag header-loaded?)
- (if (not (unsafe-code))
- (begin
- (if (not header-loaded?) (sparc.ldi as rs1 (- tag) $r.tmp0))
- ; Check bounds.
- (sparc.srai as $r.tmp0 10 $r.tmp0)
- (sparc.cmpi as $r.tmp0 imm)
- (sparc.bleu as FAULT)
- (sparc.nop as)))
- (emit-vector-like-ref/imm-trusted! as rs1 imm rd tag))
- ; 'rs1' is a hardware register containing a vectorish pointer (to a
- ; vector-like or procedure).
- ; 'imm' is a fixnum s.t. (immediate-literal? imm) => #t.
- ; 'rd' is a hardware register.
- ; 'tag' is the tag of the pointer in rs1.
- (define (emit-vector-like-ref/imm-trusted! as rs1 imm rd tag)
- (let* ((offset (* imm 4)) ; words->bytes
- (adjusted-offset (+ (- 4 tag) offset)))
- (if (immediate-literal? adjusted-offset)
- (begin
- (sparc.ldi as rs1 adjusted-offset rd))
- (begin
- (sparc.addi as rs1 (- 4 tag) $r.tmp0)
- (sparc.ldi as $r.tmp0 offset rd)))))
- ; VECTOR-SET!, VECTOR-LIKE-SET!, PROCEDURE-SET!
- ;
- ; It is assumed that the pointer in RESULT is valid. We must check the index
- ; in register x for validity and then perform the side effect (by calling
- ; millicode). The tag is the pointer tag to be adjusted for.
- ;
- ; The use of vector-set is ok even if it is a procedure.
- ; fault is valid iff (unsafe-code) = #f
- ; header is in tmp0 iff (unsafe-code) = #f and header-loaded? = #t
- (define (emit-vector-like-set! as rs1 rs2 rs3 fault tag header-loaded?)
- (let ((rs2 (force-hwreg! as rs2 $r.tmp1))
- (rs3 (force-hwreg! as rs3 $r.argreg2)))
- (if (not (unsafe-code))
- (begin
- (if (not header-loaded?)
- (sparc.ldi as $r.result (- tag) $r.tmp0))
- (sparc.btsti as rs2 3)
- (sparc.bne as fault)
- (sparc.srai as $r.tmp0 8 $r.tmp0)
- (sparc.cmpr as $r.tmp0 rs2)
- (sparc.bleu as fault)))
- (emit-vector-like-set-trusted! as rs1 rs2 rs3 tag)))
- ; rs1 must be a hardware register.
- ; tag is the pointer tag to be adjusted for.
- (define (emit-vector-like-set-trusted! as rs1 rs2 rs3 tag)
- (let ((rs2 (force-hwreg! as rs2 $r.tmp1))
- (rs3 (force-hwreg! as rs3 $r.argreg2)))
- ;; The ADDR can go in the delay slot of a preceding BLEU.
- (sparc.addr as rs1 rs2 $r.tmp0)
- (cond ((not (write-barrier))
- (sparc.sti as rs3 (- 4 tag) $r.tmp0))
- ((= rs1 $r.result)
- (cond ((= rs3 $r.argreg2)
- (sparc.jmpli as $r.millicode $m.addtrans $r.o7)
- (sparc.sti as rs3 (- 4 tag) $r.tmp0))
- (else
- (sparc.sti as rs3 (- 4 tag) $r.tmp0)
- (millicode-call/1arg as $m.addtrans rs3))))
- (else
- (cond ((= rs3 $r.argreg2)
- (sparc.sti as rs3 (- 4 tag) $r.tmp0)
- (millicode-call/1arg-in-result as $m.addtrans rs1))
- (else
- (sparc.sti as rs3 (- 4 tag) $r.tmp0)
- (sparc.move as rs1 $r.result)
- (millicode-call/1arg as $m.addtrans rs3)))))))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; 9 May 1999 / wdc
- ;
- ; SPARC code generation macros for primitives, part 3:
- ; fixnum-specific operations.
- ;
- ; Constraints for all the primops.
- ;
- ; RS1 is a general hardware register or RESULT.
- ; RS2 is a general register or ARGREG2.
- ; IMM is an exact integer in the range -1024 .. 1023.
- ; RD is a general hardware register or RESULT.
- ; FIXME
- ; Missing fxquotient, fxremainder
- ; When new pass1 in place:
- ; Must add code to pass1 to allow n-ary calls to be rewritten as binary
- ; Must add compiler macro for fxabs.
- ; most-negative-fixnum, most-positive-fixnum.
- (define-primop 'most-negative-fixnum
- (lambda (as)
- (emit-immediate->register! as (asm:signed #x80000000) $r.result)))
- (define-primop 'most-positive-fixnum
- (lambda (as)
- (emit-immediate->register! as (asm:signed #x7FFFFFFC) $r.result)))
- ; fx+, fx- w/o immediates
- (define-primop 'fx+
- (lambda (as rs2)
- (emit-fixnum-arithmetic as sparc.taddrcc sparc.addr $r.result rs2 $r.result
- $ex.fx+)))
- (define-primop 'internal:fx+
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-arithmetic as sparc.taddrcc sparc.addr rs1 rs2 rd $ex.fx+)))
- (define-primop 'fx-
- (lambda (as rs2)
- (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr $r.result rs2 $r.result
- $ex.fx-)))
- (define-primop 'internal:fx-
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr rs1 rs2 rd $ex.fx-)))
- (define-primop 'fx--
- (lambda (as)
- (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr
- $r.g0 $r.result $r.result $ex.fx--)))
- (define-primop 'internal:fx--
- (lambda (as rs rd)
- (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr $r.g0 rs rd $ex.fx--)))
- (define (emit-fixnum-arithmetic as op-check op-nocheck rs1 rs2 rd exn)
- (if (unsafe-code)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
- (op-nocheck as rs1 rs2 rd))
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2))
- (L0 (new-label))
- (L1 (new-label)))
- (sparc.label as L0)
- (op-check as rs1 rs2 $r.tmp0)
- (sparc.bvc.a as L1)
- (sparc.move as $r.tmp0 rd)
- (if (not (= exn $ex.fx--))
- (begin
- (if (not (= rs1 $r.result)) (sparc.move as rs1 $r.result))
- (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2)))
- (begin
- (if (not (= rs2 $r.result)) (sparc.move as rs2 $r.result))))
- (sparc.set as (thefixnum exn) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1))))
- ; fx* w/o immediate
- (define-primop 'fx*
- (lambda (as rs2)
- (emit-multiply-code as rs2 #t)))
- ; fx+, fx- w/immediates
- (define-primop 'internal:fx+/imm
- (lambda (as rs imm rd)
- (emit-fixnum-arithmetic/imm as sparc.taddicc sparc.addi
- rs imm rd $ex.fx+)))
- (define-primop 'internal:fx-/imm
- (lambda (as rs imm rd)
- (emit-fixnum-arithmetic/imm as sparc.tsubicc sparc.subi
- rs imm rd $ex.fx-)))
- (define (emit-fixnum-arithmetic/imm as op-check op-nocheck rs imm rd exn)
- (if (unsafe-code)
- (op-nocheck as rs (thefixnum imm) rd)
- (let ((L0 (new-label))
- (L1 (new-label)))
- (sparc.label as L0)
- (op-check as rs (thefixnum imm) $r.tmp0)
- (sparc.bvc.a as L1)
- (sparc.move as $r.tmp0 rd)
- (if (not (= rs $r.result)) (sparc.move as rs $r.result))
- (sparc.set as (thefixnum imm) $r.argreg2)
- (sparc.set as (thefixnum exn) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1))))
- ; fx=, fx<, fx<=, fx>, fx>=, fxpositive?, fxnegative?, fxzero? w/o immediates
- (define-primop 'fx=
- (lambda (as rs2)
- (emit-fixnum-compare as sparc.bne.a $r.result rs2 $r.result $ex.fx= #f)))
- (define-primop 'fx<
- (lambda (as rs2)
- (emit-fixnum-compare as sparc.bge.a $r.result rs2 $r.result $ex.fx< #f)))
- (define-primop 'fx<=
- (lambda (as rs2)
- (emit-fixnum-compare as sparc.bg.a $r.result rs2 $r.result $ex.fx<= #f)))
- (define-primop 'fx>
- (lambda (as rs2)
- (emit-fixnum-compare as sparc.ble.a $r.result rs2 $r.result $ex.fx> #f)))
- (define-primop 'fx>=
- (lambda (as rs2)
- (emit-fixnum-compare as sparc.bl.a $r.result rs2 $r.result $ex.fx>= #f)))
- (define-primop 'internal:fx=
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare as sparc.bne.a rs1 rs2 rd $ex.fx= #f)))
- (define-primop 'internal:fx<
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare as sparc.bge.a rs1 rs2 rd $ex.fx< #f)))
- (define-primop 'internal:fx<=
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare as sparc.bg.a rs1 rs2 rd $ex.fx<= #f)))
- (define-primop 'internal:fx>
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare as sparc.ble.a rs1 rs2 rd $ex.fx> #f)))
- (define-primop 'internal:fx>=
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare as sparc.bl.a rs1 rs2 rd $ex.fx>= #f)))
- ; Use '/imm' code for these because the generated code is better.
- (define-primop 'fxpositive?
- (lambda (as)
- (emit-fixnum-compare/imm as sparc.ble.a $r.result 0 $r.result
- $ex.fxpositive? #f)))
- (define-primop 'fxnegative?
- (lambda (as)
- (emit-fixnum-compare/imm as sparc.bge.a $r.result 0 $r.result
- $ex.fxnegative? #f)))
- (define-primop 'fxzero?
- (lambda (as)
- (emit-fixnum-compare/imm as sparc.bne.a $r.result 0 $r.result
- $ex.fxzero? #f)))
- (define-primop 'internal:fxpositive?
- (lambda (as rs rd)
- (emit-fixnum-compare/imm as sparc.ble.a rs 0 rd $ex.fxpositive? #f)))
- (define-primop 'internal:fxnegative?
- (lambda (as rs rd)
- (emit-fixnum-compare/imm as sparc.bge.a rs 0 rd $ex.fxnegative? #f)))
- (define-primop 'internal:fxzero?
- (lambda (as rs rd)
- (emit-fixnum-compare/imm as sparc.bne.a rs 0 rd $ex.fxzero? #f)))
- ; fx=, fx<, fx<=, fx>, fx>= w/immediates
- (define-primop 'internal:fx=/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm as sparc.bne.a rs imm rd $ex.fx= #f)))
- (define-primop 'internal:fx</imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm as sparc.bge.a rs imm rd $ex.fx< #f)))
- (define-primop 'internal:fx<=/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm as sparc.bg.a rs imm rd $ex.fx<= #f)))
- (define-primop 'internal:fx>/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm as sparc.ble.a rs imm rd $ex.fx> #f)))
- (define-primop 'internal:fx>=/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm as sparc.bl.a rs imm rd $ex.fx>= #f)))
- ; fx=, fx<, fx<=, fx>, fx>=, fxpositive?, fxnegative?, fxzero? w/o immediates
- ; for control.
- (define-primop 'internal:branchf-fx=
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare as sparc.bne.a rs1 rs2 #f $ex.fx= L)))
- (define-primop 'internal:branchf-fx<
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare as sparc.bge.a rs1 rs2 #f $ex.fx< L)))
- (define-primop 'internal:branchf-fx<=
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare as sparc.bg.a rs1 rs2 #f $ex.fx<= L)))
- (define-primop 'internal:branchf-fx>
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare as sparc.ble.a rs1 rs2 #f $ex.fx> L)))
- (define-primop 'internal:branchf-fx>=
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare as sparc.bl.a rs1 rs2 #f $ex.fx>= L)))
- (define-primop 'internal:branchf-fxpositive?
- (lambda (as rs1 L)
- (emit-fixnum-compare/imm as sparc.ble.a rs1 0 #f $ex.fxpositive? L)))
- (define-primop 'internal:branchf-fxnegative?
- (lambda (as rs1 L)
- (emit-fixnum-compare/imm as sparc.bge.a rs1 0 #f $ex.fxnegative? L)))
- (define-primop 'internal:branchf-fxzero?
- (lambda (as rs1 L)
- (emit-fixnum-compare/imm as sparc.bne.a rs1 0 #f $ex.fxzero? L)))
- ; fx=, fx<, fx<=, fx>, fx>= w/immediates for control.
- (define-primop 'internal:branchf-fx=/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm as sparc.bne.a rs imm #f $ex.fx= L)))
- (define-primop 'internal:branchf-fx</imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm as sparc.bge.a rs imm #f $ex.fx< L)))
- (define-primop 'internal:branchf-fx<=/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm as sparc.bg.a rs imm #f $ex.fx<= L)))
- (define-primop 'internal:branchf-fx>/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm as sparc.ble.a rs imm #f $ex.fx> L)))
- (define-primop 'internal:branchf-fx>=/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm as sparc.bl.a rs imm #f $ex.fx>= L)))
- ; Trusted fixnum comparisons.
- (define-primop '=:fix:fix
- (lambda (as rs2)
- (emit-fixnum-compare-trusted as sparc.bne.a $r.result rs2 $r.result #f)))
- (define-primop '<:fix:fix
- (lambda (as rs2)
- (emit-fixnum-compare-trusted as sparc.bge.a $r.result rs2 $r.result #f)))
- (define-primop '<=:fix:fix
- (lambda (as rs2)
- (emit-fixnum-compare-trusted as sparc.bg.a $r.result rs2 $r.result #f)))
- (define-primop '>:fix:fix
- (lambda (as rs2)
- (emit-fixnum-compare-trusted as sparc.ble.a $r.result rs2 $r.result #f)))
- (define-primop '>=:fix:fix
- (lambda (as rs2)
- (emit-fixnum-compare-trusted as sparc.bl.a $r.result rs2 $r.result #f)))
- (define-primop 'internal:=:fix:fix
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare-trusted as sparc.bne.a rs1 rs2 rd #f)))
- (define-primop 'internal:<:fix:fix
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare-trusted as sparc.bge.a rs1 rs2 rd #f)))
- (define-primop 'internal:<=:fix:fix
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare-trusted as sparc.bg.a rs1 rs2 rd #f)))
- (define-primop 'internal:>:fix:fix
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare-trusted as sparc.ble.a rs1 rs2 rd #f)))
- (define-primop 'internal:>=:fix:fix
- (lambda (as rs1 rs2 rd)
- (emit-fixnum-compare-trusted as sparc.bl.a rs1 rs2 rd #f)))
- ; With immediates.
- (define-primop 'internal:=:fix:fix/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm-trusted as sparc.bne.a rs imm rd #f)))
- (define-primop 'internal:<:fix:fix/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm-trusted as sparc.bge.a rs imm rd #f)))
- (define-primop 'internal:<=:fix:fix/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm-trusted as sparc.bg.a rs imm rd #f)))
- (define-primop 'internal:>:fix:fix/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm-trusted as sparc.ble.a rs imm rd #f)))
- (define-primop 'internal:>=:fix:fix/imm
- (lambda (as rs imm rd)
- (emit-fixnum-compare/imm-trusted as sparc.bl.a rs imm rd #f)))
- ; Without immediates, for control.
- (define-primop 'internal:branchf-=:fix:fix
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare-trusted as sparc.bne.a rs1 rs2 #f L)))
- (define-primop 'internal:branchf-<:fix:fix
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare-trusted as sparc.bge.a rs1 rs2 #f L)))
- (define-primop 'internal:branchf-<=:fix:fix
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare-trusted as sparc.bg.a rs1 rs2 #f L)))
- (define-primop 'internal:branchf->:fix:fix
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare-trusted as sparc.ble.a rs1 rs2 #f L)))
- (define-primop 'internal:branchf->=:fix:fix
- (lambda (as rs1 rs2 L)
- (emit-fixnum-compare-trusted as sparc.bl.a rs1 rs2 #f L)))
- ; With immediates, for control.
- (define-primop 'internal:branchf-=:fix:fix/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm-trusted as sparc.bne.a rs imm #f L)))
- (define-primop 'internal:branchf-<:fix:fix/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm-trusted as sparc.bge.a rs imm #f L)))
- (define-primop 'internal:branchf-<=:fix:fix/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm-trusted as sparc.bg.a rs imm #f L)))
- (define-primop 'internal:branchf->:fix:fix/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm-trusted as sparc.ble.a rs imm #f L)))
- (define-primop 'internal:branchf->=:fix:fix/imm
- (lambda (as rs imm L)
- (emit-fixnum-compare/imm-trusted as sparc.bl.a rs imm #f L)))
- ; Range check: 0 <= src1 < src2
- (define-primop 'internal:check-range
- (lambda (as src1 src2 L1 livregs)
- (let ((src2 (force-hwreg! as src2 $r.argreg2)))
- (emit-fixnum-compare-check
- as src2 src1 sparc.bleu L1 livregs))))
- ; Trusted fixnum comparisons followed by a check.
- (define-primop 'internal:check-=:fix:fix
- (lambda (as src1 src2 L1 liveregs)
- (emit-fixnum-compare-check
- as src1 src2 sparc.bne L1 liveregs)))
- (define-primop 'internal:check-<:fix:fix
- (lambda (as src1 src2 L1 liveregs)
- (emit-fixnum-compare-check
- as src1 src2 sparc.bge L1 liveregs)))
- (define-primop 'internal:check-<=:fix:fix
- (lambda (as src1 src2 L1 liveregs)
- (emit-fixnum-compare-check
- as src1 src2 sparc.bg L1 liveregs)))
- (define-primop 'internal:check->:fix:fix
- (lambda (as src1 src2 L1 liveregs)
- (emit-fixnum-compare-check
- as src1 src2 sparc.ble L1 liveregs)))
- (define-primop 'internal:check->=:fix:fix
- (lambda (as src1 src2 L1 liveregs)
- (emit-fixnum-compare-check
- as src1 src2 sparc.bl L1 liveregs)))
- (define-primop 'internal:check-=:fix:fix/imm
- (lambda (as src1 imm L1 liveregs)
- (emit-fixnum-compare/imm-check
- as src1 imm sparc.bne L1 liveregs)))
- (define-primop 'internal:check-<:fix:fix/imm
- (lambda (as src1 imm L1 liveregs)
- (emit-fixnum-compare/imm-check
- as src1 imm sparc.bge L1 liveregs)))
- (define-primop 'internal:check-<=:fix:fix/imm
- (lambda (as src1 imm L1 liveregs)
- (emit-fixnum-compare/imm-check
- as src1 imm sparc.bg L1 liveregs)))
- (define-primop 'internal:check->:fix:fix/imm
- (lambda (as src1 imm L1 liveregs)
- (emit-fixnum-compare/imm-check
- as src1 imm sparc.ble L1 liveregs)))
- (define-primop 'internal:check->=:fix:fix/imm
- (lambda (as src1 imm L1 liveregs)
- (emit-fixnum-compare/imm-check
- as src1 imm sparc.bl L1 liveregs)))
- ; Below, 'target' is a label or #f. If #f, RD must be a general hardware
- ; register or RESULT, and a boolean result is generated in RD.
- (define (emit-fixnum-compare as branchf.a rs1 rs2 rd exn target)
- (if (unsafe-code)
- (emit-fixnum-compare-trusted as branchf.a rs1 rs2 rd target)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2))
- (L0 (new-label))
- (L1 (new-label)))
- (sparc.label as L0)
- (sparc.orr as rs1 rs2 $r.tmp0)
- (sparc.btsti as $r.tmp0 3)
- (sparc.be.a as L1)
- (sparc.cmpr as rs1 rs2)
- (if (not (= rs1 $r.result)) (sparc.move as rs1 $r.result))
- (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
- (sparc.set as (thefixnum exn) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1)
- (emit-evaluate-cc! as branchf.a rd target))))
- ; Below, 'target' is a label or #f. If #f, RD must be a general hardware
- ; register or RESULT, and a boolean result is generated in RD.
- (define (emit-fixnum-compare-trusted as branchf.a rs1 rs2 rd target)
- (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
- (sparc.cmpr as rs1 rs2)
- (emit-evaluate-cc! as branchf.a rd target)))
- ; rs must be a hardware register.
- (define (emit-fixnum-compare/imm as branchf.a rs imm rd exn target)
- (if (unsafe-code)
- (emit-fixnum-compare/imm-trusted as branchf.a rs imm rd target)
- (let ((L0 (new-label))
- (L1 (new-label)))
- (sparc.label as L0)
- (sparc.btsti as rs 3)
- (sparc.be.a as L1)
- (sparc.cmpi as rs (thefixnum imm))
- (if (not (= rs $r.result)) (sparc.move as rs $r.result))
- (sparc.set as (thefixnum imm) $r.argreg2)
- (sparc.set as (thefixnum exn) $r.tmp0)
- (millicode-call/ret as $m.exception L0)
- (sparc.label as L1)))
- (emit-evaluate-cc! as branchf.a rd target))
- ; rs must be a hardware register.
- (define (emit-fixnum-compare/imm-trusted as branchf.a rs imm rd target)
- (sparc.cmpi as rs (thefixnum imm))
- (emit-evaluate-cc! as branchf.a rd target))
- ; Range checks.
- (define (emit-fixnum-compare-check
- as src1 src2 branch-bad L1 liveregs)
- (internal-primop-invariant1 'emit-fixnum-compare-check src1)
- (let ((src2 (force-hwreg! as src2 $r.argreg2)))
- (sparc.cmpr as src1 src2)
- (emit-checkcc! as branch-bad L1 liveregs)))
- (define (emit-fixnum-compare/imm-check
- as src1 imm branch-bad L1 liveregs)
- (internal-primop-invariant1 'emit-fixnum-compare/imm-check src1)
- (sparc.cmpi as src1 imm)
- (emit-checkcc! as branch-bad L1 liveregs))
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; SPARC machine assembler flags.
- ;
- ; 12 April 1999
- ; INTERNAL!
- (define short-effective-addresses
- (make-twobit-flag 'short-effective-addresses))
- (define runtime-safety-checking
- (make-twobit-flag 'runtime-safety-checking))
- (define catch-undefined-globals
- (make-twobit-flag 'catch-undefined-globals))
- (define inline-allocation
- (make-twobit-flag 'inline-allocation))
-
- ;(define inline-assignment
- ; (make-twobit-flag 'inline-assignment))
- (define write-barrier
- (make-twobit-flag 'write-barrier))
- (define peephole-optimization
- (make-twobit-flag 'peephole-optimization))
- (define single-stepping
- (make-twobit-flag 'single-stepping))
- (define fill-delay-slots
- (make-twobit-flag 'fill-delay-slots))
- ; For backward compatibility.
- ;(define unsafe-code
- ; (make-twobit-flag 'unsafe-code))
- (define (unsafe-code . args)
- (if (null? args)
- (not (runtime-safety-checking))
- (runtime-safety-checking (not (car args)))))
- (define (display-assembler-flags which)
- (case which
- ((debugging)
- (display-twobit-flag single-stepping))
- ((safety)
- (display-twobit-flag write-barrier)
- ;(display-twobit-flag unsafe-code)
- (display-twobit-flag runtime-safety-checking)
- (if (runtime-safety-checking)
- (begin (display " ")
- (display-twobit-flag catch-undefined-globals))))
- ((optimization)
- (display-twobit-flag peephole-optimization)
- (display-twobit-flag inline-allocation)
- ; (display-twobit-flag inline-assignment)
- (display-twobit-flag fill-delay-slots))
- (else #t)))
- (define (set-assembler-flags! mode)
- (case mode
- ((no-optimization)
- (set-assembler-flags! 'standard)
- (peephole-optimization #f)
- (fill-delay-slots #f))
- ((standard)
- (short-effective-addresses #t)
- (catch-undefined-globals #t)
- (inline-allocation #f)
- ; (inline-assignment #f)
- (peephole-optimization #t)
- (runtime-safety-checking #t)
- (write-barrier #t)
- (single-stepping #f)
- (fill-delay-slots #t))
- ((fast-safe default)
- (set-assembler-flags! 'standard)
- ; (inline-assignment #t)
- (inline-allocation #t))
- ((fast-unsafe)
- (set-assembler-flags! 'fast-safe)
- (catch-undefined-globals #f)
- (runtime-safety-checking #f))
- (else
- (error "set-assembler-flags!: unknown mode " mode))))
- (set-assembler-flags! 'default)
- ; eof
- ; Copyright 1998 Lars T Hansen.
- ;
- ; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
- ;
- ; SPARC disassembler.
- ;
- ; (disassemble-instruction instruction address)
- ; => decoded-instruction
- ;
- ; (disassemble-codevector codevector)
- ; => decoded-instruction-list
- ;
- ; (print-instructions decoded-instruction-list)
- ; => unspecified
- ; Also takes an optional port and optionally the symbol "native-names".
- ;
- ; (format-instruction decoded-instruction address larceny-names?)
- ; => string
- ;
- ; A `decoded-instruction' is a list where the car is a mnemonic and
- ; the operands are appropriate for that mnemonic.
- ;
- ; A `mnemonic' is an exact nonnegative integer. It encodes the name of
- ; the instruction as well as its attributes (operand pattern and instruction
- ; type). See below for specific operations on mnemonics.
- (define (disassemble-codevector cv)
- (define (loop addr ilist)
- (if (< addr 0)
- ilist
- (loop (- addr 4)
- (cons (disassemble-instruction (bytevector-word-ref cv addr)
- addr)
- ilist))))
- (loop (- (bytevector-length cv) 4) '()))
- (define disassemble-instruction) ; Defined below.
- ; Mnemonics
- (define *asm-annul* 1)
- (define *asm-immed* 2)
- (define *asm-store* 4)
- (define *asm-load* 8)
- (define *asm-branch* 16)
- (define *asm-freg* 32)
- (define *asm-fpop* 64)
- (define *asm-no-op2* 128)
- (define *asm-no-op3* 256)
- (define *asm-bits*
- `((a . ,*asm-annul*) (i . ,*asm-immed*) (s . ,*asm-store*)
- (l . ,*asm-load*) (b . ,*asm-branch*) (f . ,*asm-freg*)
- (fpop . ,*asm-fpop*) (no-op2 . ,*asm-no-op2*) (no-op3 . ,*asm-no-op3*)))
- (define *asm-mnemonic-table* '())
- (define mnemonic
- (let ((n 0))
- (lambda (name . rest)
- (let* ((probe (assq name *asm-mnemonic-table*))
- (code (* 1024
- (if probe
- (cdr probe)
- (let ((code n))
- (set! n (+ n 1))
- (set! *asm-mnemonic-table*
- (cons (cons name code)
- *asm-mnemonic-table*))
- code)))))
- (for-each (lambda (x)
- (set! code (+ code (cdr (assq x *asm-bits*)))))
- rest)
- code))))
- (define (mnemonic:name mnemonic)
- (let ((mnemonic (quotient mnemonic 1024)))
- (let loop ((t *asm-mnemonic-table*))
- (cond ((null? t) #f)
- ((= (cdar t) mnemonic) (caar t))
- (else (loop (cdr t)))))))
- (define (mnemonic=? m name)
- (= (quotient m 1024) (quotient (mnemonic name) 1024)))
- (define (mnemonic:test bit)
- (lambda (mnemonic)
- (not (zero? (logand mnemonic bit)))))
- (define (mnemonic:test-not bit)
- (lambda (mnemonic)
- (zero? (logand mnemonic bit))))
- (define mnemonic:annul? (mnemonic:test *asm-annul*))
- (define mnemonic:immediate? (mnemonic:test *asm-immed*))
- (define mnemonic:store? (mnemonic:test *asm-store*))
- (define mnemonic:load? (mnemonic:test *asm-load*))
- (define mnemonic:branch? (mnemonic:test *asm-branch*))
- (define mnemonic:freg? (mnemonic:test *asm-freg*))
- (define mnemonic:fpop? (mnemonic:test *asm-fpop*))
- (define mnemonic:op2? (mnemonic:test-not *asm-no-op2*))
- (define mnemonic:op3? (mnemonic:test-not *asm-no-op3*))
- ; Instruction disassembler.
- (let ()
- ;; Useful constants
- (define two^3 (expt 2 3))
- (define two^5 (expt 2 5))
- (define two^6 (expt 2 6))
- (define two^8 (expt 2 8))
- (define two^9 (expt 2 9))
- (define two^12 (expt 2 12))
- (define two^13 (expt 2 13))
- (define two^14 (expt 2 14))
- (define two^16 (expt 2 16))
- (define two^19 (expt 2 19))
- (define two^21 (expt 2 21))
- (define two^22 (expt 2 22))
- (define two^24 (expt 2 24))
- (define two^25 (expt 2 25))
- (define two^29 (expt 2 29))
- (define two^30 (expt 2 30))
- (define two^32 (expt 2 32))
- ;; Class 0 has branches and weirdness, like sethi and nop.
- ;; We dispatch first on the op2 field and then on the op3 field.
- (define class00
- (let ((b-table
- (vector (mnemonic 'bn 'b)
- (mnemonic 'be 'b)
- (mnemonic 'ble 'b)
- (mnemonic 'bl 'b)
- (mnemonic 'bleu 'b)
- (mnemonic 'bcs 'b)
- (mnemonic 'bneg 'b)
- (mnemonic 'bvs 'b)
- (mnemonic 'ba 'b)
- (mnemonic 'bne 'b)
- (mnemonic 'bg 'b)
- (mnemonic 'bge 'b)
- (mnemonic 'bgu 'b)
- (mnemonic 'bcc 'b)
- (mnemonic 'bpos 'b)
- (mnemonic 'bvc 'b)
- (mnemonic 'bn 'a 'b)
- (mnemonic 'be 'a 'b)
- (mnemonic 'ble 'a 'b)
- (mnemonic 'bl 'a 'b)
- (mnemonic 'bleu 'a 'b)
- (mnemonic 'bcs 'a 'b)
- (mnemonic 'bneg 'a 'b)
- (mnemonic 'bvs 'a 'b)
- (mnemonic 'ba 'a 'b)
- (mnemonic 'bne 'a 'b)
- (mnemonic 'bg 'a 'b)
- (mnemonic 'bge 'a 'b)
- (mnemonic 'bgu 'a 'b)
- (mnemonic 'bcc 'a 'b)
- (mnemonic 'bpos 'a 'b)
- (mnemonic 'bvc 'a 'b)))
- (fb-table
- (vector (mnemonic 'fbn 'b)
- (mnemonic 'fbne 'b)
- (mnemonic 'fblg 'b)
- (mnemonic 'fbul 'b)
- (mnemonic 'fbl 'b)
- (mnemonic 'fbug 'b)
- (mnemonic 'fbg 'b)
- (mnemonic 'fbu 'b)
- (mnemonic 'fba 'b)
- (mnemonic 'fbe 'b)
- (mnemonic 'fbue 'b)
- (mnemonic 'fbge 'b)
- (mnemonic 'fbuge 'b)
- (mnemonic 'fble 'b)
- (mnemonic 'fbule 'b)
- (mnemonic 'fbo 'b)
- (mnemonic 'fbn 'a 'b)
- (mnemonic 'fbne 'a 'b)
- (mnemonic 'fblg 'a 'b)
- (mnemonic 'fbul 'a 'b)
- (mnemonic 'fbl 'a 'b)
- (mnemonic 'fbug 'a 'b)
- (mnemonic 'fbg 'a 'b)
- (mnemonic 'fbu 'a 'b)
- (mnemonic 'fba 'a 'b)
- (mnemonic 'fbe 'a 'b)
- (mnemonic 'fbue 'a 'b)
- (mnemonic 'fbge 'a 'b)
- (mnemonic 'fbuge 'a 'b)
- (mnemonic 'fble 'a 'b)
- (mnemonic 'fbule 'a 'b)
- (mnemonic 'fbo 'a 'b)))
- (nop (mnemonic 'nop))
- (sethi (mnemonic 'sethi)))
- (lambda (ip instr)
- (let ((op2 (op2field instr)))
- (cond ((= op2 #b100)
- (if (zero? (rdfield instr))
- `(,nop)
- `(,sethi ,(imm22field instr) ,(rdfield instr))))
- ((= op2 #b010)
- `(,(vector-ref b-table (rdfield instr))
- ,(* 4 (imm22field instr))))
- ((= op2 #b110)
- `(,(vector-ref fb-table (rdfield instr))
- ,(* 4 (imm22field instr))))
- (else
- (disasm-error "Can't disassemble " (number->string instr 16)
- " at ip=" ip
- " with op2=" op2)))))))
- ;; Class 1 is the call instruction; there's no choice.
- (define (class01 ip instr)
- `(,(mnemonic 'call) ,(* 4 (imm30field instr))))
- ;; Class 2 is for the ALU. Dispatch on op3 field.
- (define class10
- (let ((op3-table
- `#((,(mnemonic 'add) ,(mnemonic 'add 'i))
- (,(mnemonic 'and) ,(mnemonic 'and 'i))
- (,(mnemonic 'or) ,(mnemonic 'or 'i))
- (,(mnemonic 'xor) ,(mnemonic 'xor 'i))
- (,(mnemonic 'sub) ,(mnemonic 'sub 'i))
- (,(mnemonic 'andn) ,(mnemonic 'andn 'i))
- (,(mnemonic 'orn) ,(mnemonic 'orn 'i))
- (,(mnemonic 'xnor) ,(mnemonic 'xnor 'i))
- (0 0)
- (0 0)
- (0 0) ; 10
- (,(mnemonic 'smul) ,(mnemonic 'smul 'i))
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'sdiv) ,(mnemonic 'sdiv 'i))
- (,(mnemonic 'addcc) ,(mnemonic 'addcc 'i))
- (,(mnemonic 'andcc) ,(mnemonic 'andcc 'i))
- (,(mnemonic 'orcc) ,(mnemonic 'orcc 'i))
- (,(mnemonic 'xorcc) ,(mnemonic 'xorcc 'i))
- (,(mnemonic 'subcc) ,(mnemonic 'subcc 'i)) ; 20
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'smulcc) ,(mnemonic 'smulcc 'i))
- (0 0)
- (0 0)
- (0 0) ; 30
- (,(mnemonic 'sdivcc) ,(mnemonic 'sdivcc 'i))
- (,(mnemonic 'taddcc) ,(mnemonic 'taddcc 'i))
- (,(mnemonic 'tsubcc) ,(mnemonic 'tsubcc 'i))
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'sll) ,(mnemonic 'sll 'i))
- (,(mnemonic 'srl) ,(mnemonic 'srl 'i))
- (,(mnemonic 'sra) ,(mnemonic 'sra 'i))
- (,(mnemonic 'rd) 0) ; 40
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'wr) ,(mnemonic 'wr 'i))
- (0 0)
- (0 0) ; 50
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'jmpl) ,(mnemonic 'jmpl 'i))
- (0 0)
- (0 0)
- (0 0)
- (,(mnemonic 'save) ,(mnemonic 'save 'i)) ; 60
- (,(mnemonic 'restore) ,(mnemonic 'restore 'i))
- (0 0)
- (0 0))))
- (lambda (ip instr)
- (let ((op3 (op3field instr)))
- (if (or (= op3 #b110100) (= op3 #b110101))
- (fpop-instruction ip instr)
- (nice-instruction op3-table ip instr))))))
- ;; Class 3 is memory stuff.
- (define class11
- (let ((op3-table
- `#((,(mnemonic 'ld 'l) ,(mnemonic 'ld 'i 'l))
- (,(mnemonic 'ldb 'l) ,(mnemonic 'ldb 'i 'l))
- (,(mnemonic 'ldh 'l) ,(mnemonic 'ldh 'i 'l))
- (,(mnemonic 'ldd 'l) ,(mnemonic 'ldd 'i 'l))
- (,(mnemonic 'st 's) ,(mnemonic 'st 'i 's))
- (,(mnemonic 'stb 's) ,(mnemonic 'stb 'i 's))
- (,(mnemonic 'sth 's) ,(mnemonic 'sth 'i 's))
- (,(mnemonic 'std 's) ,(mnemonic 'std 'i 's))
- (0 0)
- (0 0)
- (0 0) ; 10
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0) ; 20
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0) ; 30
- (0 0)
- (,(mnemonic 'ldf 'f 'l) ,(mnemonic 'ldf 'i 'f 'l))
- (0 0)
- (0 0)
- (,(mnemonic 'lddf 'f 'l) ,(mnemonic 'lddf 'i 'f 'l))
- (,(mnemonic 'stf 'f 's) ,(mnemonic 'stf 'i 'f 's))
- (0 0)
- (0 0)
- (,(mnemonic 'stdf 'f 's) ,(mnemonic 'stdf 'i 'f 's))
- (0 0) ; 40
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0) ; 50
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0)
- (0 0) ; 60
- (0 0)
- (0 0)
- (0 0))))
- (lambda (ip instr)
- (nice-instruction op3-table ip instr))))
- ;; For classes 2 and 3
- (define (nice-instruction op3-table ip instr)
- (let* ((op3 (op3field instr))
- (imm (ifield instr))
- (rd (rdfield instr))
- (rs1 (rs1field instr))
- (src2 (if (zero? imm)
- (rs2field instr)
- (imm13field instr))))
- (let ((op ((if (zero? imm) car cadr) (vector-ref op3-table op3))))
- `(,op ,rs1 ,src2 ,rd))))
- ;; Floating-point operate instructions
- (define (fpop-instruction ip instr)
- (let ((rd (rdfield instr))
- (rs1 (rs1field instr))
- (rs2 (rs2field instr))
- (fpop (fpop-field instr)))
- `(,(cdr (assv fpop fpop-names)) ,rs1 ,rs2 ,rd)))
- (define fpop-names
- `((#b000000001 . ,(mnemonic 'fmovs 'fpop 'no-op2))
- (#b000000101 . ,(mnemonic 'fnegs 'fpop 'no-op2))
- (#b000001001 . ,(mnemonic 'fabss 'fpop 'no-op2))
- (#b001000010 . ,(mnemonic 'faddd 'fpop))
- (#b001000110 . ,(mnemonic 'fsubd 'fpop))
- (#b001001010 . ,(mnemonic 'fmuld 'fpop))
- (#b001001110 . ,(mnemonic 'fdivd 'fpop))
- (#b001010010 . ,(mnemonic 'fcmpd 'fpop 'no-op3))))
-
- ;; The following procedures pick apart an instruction
- (define (op2field instr)
- (remainder (quotient instr two^22) two^3))
- (define (op3field instr)
- (remainder (quotient instr two^19) two^6))
- (define (ifield instr)
- (remainder (quotient instr two^13) 2))
- (define (rs2field instr)
- (remainder instr two^5))
- (define (rs1field instr)
- (remainder (quotient instr two^14) two^5))
- (define (rdfield instr)
- (remainder (quotient instr two^25) two^5))
- (define (imm13field instr)
- (let ((x (remainder instr two^13)))
- (if (not (zero? (quotient x two^12)))
- (- x two^13)
- x)))
-
- (define (imm22field instr)
- (let ((x (remainder instr two^22)))
- (if (not (zero? (quotient x two^21)))
- (- x two^22)
- x)))
- (define (imm30field instr)
- (let ((x (remainder instr two^30)))
- (if (not (zero? (quotient x two^29)))
- (- x two^30)
- x)))
- (define (fpop-field instr)
- (remainder (quotient instr two^5) two^9))
- (set! disassemble-instruction
- (let ((class-table (vector class00 class01 class10 class11)))
- (lambda (instr addr)
- ((vector-ref class-table (quotient instr two^30)) addr instr))))
- 'disassemble-instruction)
- ; Instruction printer
- ;
- ; It assumes that the first instruction comes from address 0, and prints
- ; addresses (and relative addresses) based on that assumption.
- ;
- ; If the optional symbol native-names is supplied, then SPARC register
- ; names is used, and millicode calls are not annotated with millicode names.
- (define (print-instructions ilist . rest)
- (define port (current-output-port))
- (define larceny-names? #t)
- (define (print-ilist ilist a)
- (if (null? ilist)
- '()
- (begin (display (format-instruction (car ilist) a larceny-names?)
- port)
- (newline port)
- (print-ilist (cdr ilist) (+ a 4)))))
-
- (do ((rest rest (cdr rest)))
- ((null? rest))
- (cond ((port? (car rest))
- (set! port (car rest)))
- ((eq? (car rest) 'native-names)
- (set! larceny-names? #f))))
-
- (print-ilist ilist 0))
- (define format-instruction) ; Defined below
- (define *format-instructions-pretty* #t)
- ; Instruction formatter.
- (let ()
- (define use-larceny-registers #t)
- (define sparc-register-table
- (vector "%g0" "%g1" "%g2" "%g3" "%g4" "%g5" "%g6" "%g7"
- "%o0" "%o1" "%o2" "%o3" "%o4" "%o5" "%o6" "%o7"
- "%l0" "%l1" "%l2" "%l3" "%l4" "%l5" "%l6" "%l7"
- "%i0" "%i1" "%i2" "%i3" "%i4" "%i5" "%i6" "%i7"))
- (define larceny-register-table
- (make-vector 32 #f))
- (define (larceny-register-name reg . rest)
- (if (null? rest)
- (or (and use-larceny-registers
- (vector-ref larceny-register-table reg))
- (vector-ref sparc-register-table reg))
- (vector-set! larceny-register-table reg (car rest))))
- (define millicode-procs '())
- (define (float-register-name reg)
- (string-append "%f" (number->string reg)))
-
- (define op car)
- (define op1 cadr)
- (define op2 caddr)
- (define op3 cadddr)
- (define tabstring (string #\tab))
- (define (heximm n)
- (if (>= n 16)
- (string-append tabstring "! 0x" (number->string n 16))
- ""))
- (define (millicode-name offset . rest)
- (if (null? rest)
- (let ((probe (assv offset millicode-procs)))
- (if probe
- (cdr probe)
- "[unknown]"))
- (set! millicode-procs
- (cons (cons offset (car rest)) millicode-procs))))
- (define (millicode-call offset)
- (string-append tabstring "! " (millicode-name offset)))
- (define (plus/minus n)
- (cond ((< n 0)
- (string-append " - " (number->string (abs n))))
- ((and (= n 0) *format-instructions-pretty*) "")
- (else
- (string-append " + " (number->string n)))))
- (define (srcreg instr extractor)
- (if (mnemonic:freg? (op instr))
- (float-register-name (extractor instr))
- (larceny-register-name (extractor instr))))
-
- (define (sethi instr)
- (string-append (number->string (* (op1 instr) 1024)) ", "
- (larceny-register-name (op2 instr))
- (heximm (* (op1 instr) 1024))))
- (define (rrr instr)
- (string-append (larceny-register-name (op1 instr)) ", "
- (larceny-register-name (op2 instr)) ", "
- (larceny-register-name (op3 instr))))
- (define (rir instr)
- (string-append (larceny-register-name (op1 instr)) ", "
- (number->string (op2 instr)) ", "
- (larceny-register-name (op3 instr))
- (heximm (op2 instr))))
- (define (sir instr)
- (string-append (srcreg instr op3) ", [ "
- (larceny-register-name (op1 instr))
- (plus/minus (op2 instr)) " ]"))
- (define (srr instr)
- (string-append (srcreg instr op3) ", [ "
- (larceny-register-name (op1 instr)) "+"
- (larceny-register-name (op2 instr)) " ]"))
-
- (define (lir instr)
- (string-append "[ " (larceny-register-name (op1 instr))
- (plus/minus (op2 instr)) " ], "
- (srcreg instr op3)))
- (define (lrr instr)
- (string-append "[ " (larceny-register-name (op1 instr)) "+"
- (larceny-register-name (op2 instr)) " ], "
- (srcreg instr op3)))
- (define (bimm instr addr)
- (string-append "#" (number->string (+ (op1 instr) addr))))
- (define (jmpli instr)
- (string-append (larceny-register-name (op1 instr))
- (plus/minus (op2 instr)) ", "
- (larceny-register-name (op3 instr))
- (if (and (= (op1 instr) $r.globals)
- use-larceny-registers)
- (millicode-call (op2 instr))
- (heximm (op2 instr)))))
- (define (jmplr instr)
- (string-append (larceny-register-name (op1 instr)) "+"
- (larceny-register-name (op2 instr)) ", "
- (larceny-register-name (op3 instr))))
- (define (call instr addr)
- (string-append "#" (number->string (+ (op1 instr) addr))))
- (define (rd instr)
- (string-append "%y, " (srcreg instr op3)))
- (define (wr instr imm?)
- (if imm?
- (string-append (larceny-register-name (op1 instr)) ", "
- (number->string (op2 instr)) ", %y"
- (larceny-register-name (op3 instr)))
- (string-append (larceny-register-name (op1 instr)) ", "
- (larceny-register-name (op2 instr)) ", %y")))
- (define (fpop instr op2-used? op3-used?)
- (string-append (float-register-name (op1 instr)) ", "
- (cond ((and op2-used? op3-used?)
- (string-append
- (float-register-name (op2 instr)) ", "
- (float-register-name (op3 instr))))
- (op2-used?
- (float-register-name (op2 instr)))
- (else
- (float-register-name (op3 instr))))))
- ;; If we want to handle instruction aliases (clr, mov, etc) then
- ;; the structure of this procedure must change, because as it is,
- ;; the printing of the name is independent of the operand values.
- (define (format-instr i a larceny-names?)
- (set! use-larceny-registers larceny-names?)
- (let ((m (car i)))
- (string-append (number->string a)
- tabstring
- (symbol->string (mnemonic:name m))
- (if (mnemonic:annul? m) ",a" "")
- tabstring
- (cond ((mnemonic:store? m)
- (if (mnemonic:immediate? m) (sir i) (srr i)))
- ((mnemonic:load? m)
- (if (mnemonic:immediate? m) (lir i) (lrr i)))
- ((mnemonic:fpop? m)
- (fpop i (mnemonic:op2? m) (mnemonic:op3? m)))
- ((mnemonic:branch? m) (bimm i a))
- ((mnemonic=? m 'sethi) (sethi i))
- ((mnemonic=? m 'nop) "")
- ((mnemonic=? m 'jmpl)
- (if (mnemonic:immediate? m) (jmpli i) (jmplr i)))
- ((mnemonic=? m 'call) (call i a))
- ((mnemonic=? m 'rd) (rd i))
- ((mnemonic=? m 'wr) (wr i (mnemonic:immediate? m)))
- ((mnemonic:immediate? m) (rir i))
- (else (rrr i))))))
- (larceny-register-name $r.tmp0 "%tmp0")
- (larceny-register-name $r.result "%result")
- (larceny-register-name $r.argreg2 "%argreg2")
- (larceny-register-name $r.argreg3 "%argreg3")
- (larceny-register-name $r.tmp1 "%tmp1")
- (larceny-register-name $r.tmp2 "%tmp2")
- (larceny-register-name $r.reg0 "%r0")
- (larceny-register-name $r.reg1 "%r1")
- (larceny-register-name $r.reg2 "%r2")
- (larceny-register-name $r.reg3 "%r3")
- (larceny-register-name $r.reg4 "%r4")
- (larceny-register-name $r.reg5 "%r5")
- (larceny-register-name $r.reg6 "%r6")
- (larceny-register-name $r.reg7 "%r7")
- (larceny-register-name $r.e-top "%etop")
- (larceny-register-name $r.e-limit "%elim")
- (larceny-register-name $r.timer "%timer")
- (larceny-register-name $r.millicode "%millicode")
- (larceny-register-name $r.globals "%globals")
- (larceny-register-name $r.stkp "%stkp") ; note: after elim
- (millicode-name $m.alloc "alloc")
- (millicode-name $m.alloci "alloci")
- (millicode-name $m.gc "gc")
- (millicode-name $m.addtrans "addtrans")
- (millicode-name $m.stkoflow "stkoflow")
- (millicode-name $m.stkuflow "stkuflow")
- (millicode-name $m.creg "creg")
- (millicode-name $m.creg-set! "creg-set!")
- (millicode-name $m.add "+")
- (millicode-name $m.subtract "- (binary)")
- (millicode-name $m.multiply "*")
- (millicode-name $m.quotient "quotient")
- (millicode-name $m.remainder "remainder")
- (millicode-name $m.divide "/")
- (millicode-name $m.modulo "modulo")
- (millicode-name $m.negate "- (unary)")
- (millicode-name $m.numeq "=")
- (millicode-name $m.numlt "<")
- (millicode-name $m.numle "<=")
- (millicode-name $m.numgt ">")
- (millicode-name $m.numge ">=")
- (millicode-name $m.zerop "zero?")
- (millicode-name $m.complexp "complex?")
- (millicode-name $m.realp "real?")
- (millicode-name $m.rationalp "rational?")
- (millicode-name $m.integerp "integer?")
- (millicode-name $m.exactp "exact?")
- (millicode-name $m.inexactp "inexact?")
- (millicode-name $m.exact->inexact "exact->inexact")
- (millicode-name $m.inexact->exact "inexact->exact")
- (millicode-name $m.make-rectangular "make-rectangular")
- (millicode-name $m.real-part "real-part")
- (millicode-name $m.imag-part "imag-part")
- (millicode-name $m.sqrt "sqrt")
- (millicode-name $m.round "round")
- (millicode-name $m.truncate "truncate")
- (millicode-name $m.apply "apply")
- (millicode-name $m.varargs "varargs")
- (millicode-name $m.typetag "typetag")
- (millicode-name $m.typetag-set "typetag-set")
- (millicode-name $m.break "break")
- (millicode-name $m.eqv "eqv?")
- (millicode-name $m.partial-list->vector "partial-list->vector")
- (millicode-name $m.timer-exception "timer-exception")
- (millicode-name $m.exception "exception")
- (millicode-name $m.singlestep "singlestep")
- (millicode-name $m.syscall "syscall")
- (millicode-name $m.bvlcmp "bvlcmp")
- (millicode-name $m.enable-interrupts "enable-interrupts")
- (millicode-name $m.disable-interrupts "disable-interrupts")
- (millicode-name $m.alloc-bv "alloc-bv")
- (millicode-name $m.global-ex "global-exception")
- (millicode-name $m.invoke-ex "invoke-exception")
- (millicode-name $m.global-invoke-ex "global-invoke-exception")
- (millicode-name $m.argc-ex "argc-exception")
- (set! format-instruction format-instr)
- 'format-instruction)
- ; eof
- ; ----------------------------------------------------------------------
- (define (twobit-benchmark type . rest)
- (let ((k (if (null? rest) 1 (car rest))))
- (run-benchmark
- "twobit"
- k
- (lambda ()
- (case type
- ((long)
- (compiler-switches 'fast-safe)
- (benchmark-block-mode #f)
- (compile-file "benchmarks/twobit-input-long.sch"))
- ((short)
- (compiler-switches 'fast-safe)
- (benchmark-block-mode #t)
- (compile-file "benchmarks/twobit-input-short.sch"))
- (else
- (error "Benchmark type must be `long' or `short': " type))))
- (lambda (result)
- #t))))
- ; eof
|