PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/guile/2.0/language/tree-il
Viewing File: /usr/share/guile/2.0/language/tree-il/compile-glil.scm
;;; TREE-IL -> GLIL compiler ;; Copyright (C) 2001,2008,2009,2010,2011,2012,2013,2014 Free Software Foundation, Inc. ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;; Code: (define-module (language tree-il compile-glil) #:use-module (system base syntax) #:use-module (system base pmatch) #:use-module (system base message) #:use-module (ice-9 receive) #:use-module (language glil) #:use-module (system vm instruction) #:use-module (language tree-il) #:use-module (language tree-il optimize) #:use-module (language tree-il canonicalize) #:use-module (language tree-il analyze) #:use-module ((srfi srfi-1) #:select (filter-map)) #:export (compile-glil)) ;; allocation: ;; sym -> {lambda -> address} ;; lambda -> (labels . free-locs) ;; lambda-case -> (gensym . nlocs) ;; ;; address ::= (local? boxed? . index) ;; labels ::= ((sym . lambda) ...) ;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...) ;; free variable addresses are relative to parent proc. (define *comp-module* (make-fluid)) (define %warning-passes `((unused-variable . ,unused-variable-analysis) (unused-toplevel . ,unused-toplevel-analysis) (unbound-variable . ,unbound-variable-analysis) (arity-mismatch . ,arity-analysis) (format . ,format-analysis))) (define (compile-glil x e opts) (define warnings (or (and=> (memq #:warnings opts) cadr) '())) ;; Go through the warning passes. (let ((analyses (filter-map (lambda (kind) (assoc-ref %warning-passes kind)) warnings))) (analyze-tree analyses x e)) (let* ((x (make-lambda (tree-il-src x) '() (make-lambda-case #f '() #f #f #f '() '() x #f))) (x (optimize! x e opts)) (x (canonicalize! x)) (allocation (analyze-lexicals x))) (with-fluids ((*comp-module* e)) (values (flatten-lambda x #f allocation) e e)))) (define *primcall-ops* (make-hash-table)) (for-each (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x))) '(((eq? . 2) . eq?) ((eqv? . 2) . eqv?) ((equal? . 2) . equal?) ((= . 2) . ee?) ((< . 2) . lt?) ((> . 2) . gt?) ((<= . 2) . le?) ((>= . 2) . ge?) ((+ . 2) . add) ((- . 2) . sub) ((1+ . 1) . add1) ((1- . 1) . sub1) ((* . 2) . mul) ((/ . 2) . div) ((quotient . 2) . quo) ((remainder . 2) . rem) ((modulo . 2) . mod) ((ash . 2) . ash) ((logand . 2) . logand) ((logior . 2) . logior) ((logxor . 2) . logxor) ((not . 1) . not) ((pair? . 1) . pair?) ((cons . 2) . cons) ((car . 1) . car) ((cdr . 1) . cdr) ((set-car! . 2) . set-car!) ((set-cdr! . 2) . set-cdr!) ((null? . 1) . null?) ((list? . 1) . list?) ((symbol? . 1) . symbol?) ((vector? . 1) . vector?) (list . list) (vector . vector) ((class-of . 1) . class-of) ((vector-ref . 2) . vector-ref) ((vector-set! . 3) . vector-set) ((variable-ref . 1) . variable-ref) ;; nb, *not* variable-set! -- the args are switched ((variable-bound? . 1) . variable-bound?) ((struct? . 1) . struct?) ((struct-vtable . 1) . struct-vtable) ((struct-ref . 2) . struct-ref) ((struct-set! . 3) . struct-set) (make-struct/no-tail . make-struct) ;; hack for javascript ((return . 1) . return) ;; hack for lua (return/values . return/values) ((bytevector-u8-ref . 2) . bv-u8-ref) ((bytevector-u8-set! . 3) . bv-u8-set) ((bytevector-s8-ref . 2) . bv-s8-ref) ((bytevector-s8-set! . 3) . bv-s8-set) ((bytevector-u16-ref . 3) . bv-u16-ref) ((bytevector-u16-set! . 4) . bv-u16-set) ((bytevector-u16-native-ref . 2) . bv-u16-native-ref) ((bytevector-u16-native-set! . 3) . bv-u16-native-set) ((bytevector-s16-ref . 3) . bv-s16-ref) ((bytevector-s16-set! . 4) . bv-s16-set) ((bytevector-s16-native-ref . 2) . bv-s16-native-ref) ((bytevector-s16-native-set! . 3) . bv-s16-native-set) ((bytevector-u32-ref . 3) . bv-u32-ref) ((bytevector-u32-set! . 4) . bv-u32-set) ((bytevector-u32-native-ref . 2) . bv-u32-native-ref) ((bytevector-u32-native-set! . 3) . bv-u32-native-set) ((bytevector-s32-ref . 3) . bv-s32-ref) ((bytevector-s32-set! . 4) . bv-s32-set) ((bytevector-s32-native-ref . 2) . bv-s32-native-ref) ((bytevector-s32-native-set! . 3) . bv-s32-native-set) ((bytevector-u64-ref . 3) . bv-u64-ref) ((bytevector-u64-set! . 4) . bv-u64-set) ((bytevector-u64-native-ref . 2) . bv-u64-native-ref) ((bytevector-u64-native-set! . 3) . bv-u64-native-set) ((bytevector-s64-ref . 3) . bv-s64-ref) ((bytevector-s64-set! . 4) . bv-s64-set) ((bytevector-s64-native-ref . 2) . bv-s64-native-ref) ((bytevector-s64-native-set! . 3) . bv-s64-native-set) ((bytevector-ieee-single-ref . 3) . bv-f32-ref) ((bytevector-ieee-single-set! . 4) . bv-f32-set) ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref) ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set) ((bytevector-ieee-double-ref . 3) . bv-f64-ref) ((bytevector-ieee-double-set! . 4) . bv-f64-set) ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref) ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set))) (define (make-label) (gensym ":L")) (define (vars->bind-list ids vars allocation proc) (map (lambda (id v) (pmatch (hashq-ref (hashq-ref allocation v) proc) ((#t ,boxed? . ,n) (list id boxed? n)) (,x (error "bad var list element" id v x)))) ids vars)) (define (emit-bindings src ids vars allocation proc emit-code) (emit-code src (make-glil-bind (vars->bind-list ids vars allocation proc)))) (define (with-output-to-code proc) (let ((out '())) (define (emit-code src x) (set! out (cons x out)) (if src (set! out (cons (make-glil-source src) out)))) (proc emit-code) (reverse out))) (define (flatten-lambda x self-label allocation) (record-case x ((<lambda> src meta body) (make-glil-program meta (with-output-to-code (lambda (emit-code) ;; write source info for proc (if src (emit-code #f (make-glil-source src))) ;; compile the body, yo (flatten-lambda-case body allocation x self-label (car (hashq-ref allocation x)) emit-code))))))) (define (flatten-lambda-case lcase allocation self self-label fix-labels emit-code) (define (emit-label label) (emit-code #f (make-glil-label label))) (define (emit-branch src inst label) (emit-code src (make-glil-branch inst label))) ;; RA: "return address"; #f unless we're in a non-tail fix with labels ;; MVRA: "multiple-values return address"; #f unless we're in a let-values (let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f)) (define (comp-tail tree) (comp tree context RA MVRA)) (define (comp-push tree) (comp tree 'push #f #f)) (define (comp-drop tree) (comp tree 'drop #f #f)) (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA)) (define (comp-fix tree RA) (comp tree context RA MVRA)) ;; A couple of helpers. Note that if we are in tail context, we ;; won't have an RA. (define (maybe-emit-return) (if RA (emit-branch #f 'br RA) (if (eq? context 'tail) (emit-code #f (make-glil-call 'return 1))))) ;; After lexical binding forms in non-tail context, call this ;; function to clear stack slots, allowing their previous values to ;; be collected. (define (clear-stack-slots context syms) (case context ((push drop) (for-each (lambda (v) (and=> ;; Can be #f if the var is labels-allocated. (hashq-ref allocation v) (lambda (h) (pmatch (hashq-ref h self) ((#t _ . ,n) (emit-code #f (make-glil-void)) (emit-code #f (make-glil-lexical #t #f 'set n))) (,loc (error "bad let var allocation" x loc)))))) syms)))) (record-case x ((<void>) (case context ((push vals tail) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((<const> src exp) (case context ((push vals tail) (emit-code src (make-glil-const exp)))) (maybe-emit-return)) ;; FIXME: should represent sequence as exps tail ((<sequence> exps) (let lp ((exps exps)) (if (null? (cdr exps)) (comp-tail (car exps)) (begin (comp-drop (car exps)) (lp (cdr exps)))))) ((<application> src proc args) ;; FIXME: need a better pattern-matcher here (cond ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) '@apply) (>= (length args) 1)) (let ((proc (car args)) (args (cdr args))) (cond ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values) (not (eq? context 'push)) (not (eq? context 'vals))) ;; tail: (lambda () (apply values '(1 2))) ;; drop: (lambda () (apply values '(1 2)) 3) ;; push: (lambda () (list (apply values '(10 12)) 1)) (case context ((drop) (for-each comp-drop args) (maybe-emit-return)) ((tail) (for-each comp-push args) (emit-code src (make-glil-call 'return/values* (length args)))))) (else (case context ((tail) (comp-push proc) (for-each comp-push args) (emit-code src (make-glil-call 'tail-apply (1+ (length args))))) ((push) (emit-code src (make-glil-call 'new-frame 0)) (comp-push proc) (for-each comp-push args) (emit-code src (make-glil-call 'apply (1+ (length args)))) (maybe-emit-return)) ((vals) (comp-vals (make-application src (make-primitive-ref #f 'apply) (cons proc args)) MVRA) (maybe-emit-return)) ((drop) ;; Well, shit. The proc might return any number of ;; values (including 0), since it's in a drop context, ;; yet apply does not create a MV continuation. So we ;; mv-call out to our trampoline instead. (comp-drop (make-application src (make-primitive-ref #f 'apply) (cons proc args))) (maybe-emit-return))))))) ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)) ;; tail: (lambda () (values '(1 2))) ;; drop: (lambda () (values '(1 2)) 3) ;; push: (lambda () (list (values '(10 12)) 1)) ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...) (case context ((drop) (for-each comp-drop args) (maybe-emit-return)) ((push) (case (length args) ((0) ;; FIXME: This is surely an error. We need to add a ;; values-mismatch warning pass. (emit-code src (make-glil-call 'new-frame 0)) (comp-push proc) (emit-code src (make-glil-call 'call 0)) (maybe-emit-return)) (else ;; Taking advantage of unspecified order of evaluation of ;; arguments. (for-each comp-drop (cdr args)) (comp-push (car args)) (maybe-emit-return)))) ((vals) (for-each comp-push args) (emit-code #f (make-glil-const (length args))) (emit-branch src 'br MVRA)) ((tail) (for-each comp-push args) (emit-code src (let ((len (length args))) (if (= len 1) (make-glil-call 'return 1) (make-glil-call 'return/values len))))))) ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) '@call-with-values) (= (length args) 2)) ;; CONSUMER ;; PRODUCER ;; (mv-call MV) ;; ([tail]-call 1) ;; goto POST ;; MV: [tail-]call/nargs ;; POST: (maybe-drop) (case context ((vals) ;; Fall back. (comp-vals (make-application src (make-primitive-ref #f 'call-with-values) args) MVRA) (maybe-emit-return)) (else (let ((MV (make-label)) (POST (make-label)) (producer (car args)) (consumer (cadr args))) (if (not (eq? context 'tail)) (emit-code src (make-glil-call 'new-frame 0))) (comp-push consumer) (emit-code src (make-glil-call 'new-frame 0)) (comp-push producer) (emit-code src (make-glil-mv-call 0 MV)) (case context ((tail) (emit-code src (make-glil-call 'tail-call 1))) (else (emit-code src (make-glil-call 'call 1)) (emit-branch #f 'br POST))) (emit-label MV) (case context ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0))) (else (emit-code src (make-glil-call 'call/nargs 0)) (emit-label POST) (if (eq? context 'drop) (emit-code #f (make-glil-call 'drop 1))) (maybe-emit-return))))))) ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) '@call-with-current-continuation) (= (length args) 1)) (case context ((tail) (comp-push (car args)) (emit-code src (make-glil-call 'tail-call/cc 1))) ((vals) (comp-vals (make-application src (make-primitive-ref #f 'call-with-current-continuation) args) MVRA) (maybe-emit-return)) ((push) (comp-push (car args)) (emit-code src (make-glil-call 'call/cc 1)) (maybe-emit-return)) ((drop) ;; Crap. Just like `apply' in drop context. (comp-drop (make-application src (make-primitive-ref #f 'call-with-current-continuation) args)) (maybe-emit-return)))) ;; A hack for variable-set, the opcode for which takes its args ;; reversed, relative to the variable-set! function ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'variable-set!) (= (length args) 2)) (comp-push (cadr args)) (comp-push (car args)) (emit-code src (make-glil-call 'variable-set 2)) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((and (primitive-ref? proc) (or (hash-ref *primcall-ops* (cons (primitive-ref-name proc) (length args))) (hash-ref *primcall-ops* (primitive-ref-name proc)))) => (lambda (op) (for-each comp-push args) (emit-code src (make-glil-call op (length args))) (case (instruction-pushes op) ((0) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((1) (case context ((drop) (emit-code #f (make-glil-call 'drop 1)))) (maybe-emit-return)) ((-1) ;; A control instruction, like return/values. Here we ;; just have to hope that the author of the tree-il ;; knew what they were doing. *unspecified*) (else (error "bad primitive op: too many pushes" op (instruction-pushes op)))))) ;; call to the same lambda-case in tail position ((and (lexical-ref? proc) self-label (eq? (lexical-ref-gensym proc) self-label) (eq? context 'tail) (not (lambda-case-kw lcase)) (not (lambda-case-rest lcase)) (= (length args) (+ (length (lambda-case-req lcase)) (or (and=> (lambda-case-opt lcase) length) 0)))) (for-each comp-push args) (for-each (lambda (sym) (pmatch (hashq-ref (hashq-ref allocation sym) self) ((#t #f . ,index) ; unboxed (emit-code #f (make-glil-lexical #t #f 'set index))) ((#t #t . ,index) ; boxed ;; new box (emit-code #f (make-glil-lexical #t #t 'box index))) (,x (error "bad lambda-case arg allocation" x)))) (reverse (lambda-case-gensyms lcase))) (emit-branch src 'br (car (hashq-ref allocation lcase)))) ;; lambda, the ultimate goto ((and (lexical-ref? proc) (assq (lexical-ref-gensym proc) fix-labels)) ;; like the self-tail-call case, though we can handle "drop" ;; contexts too. first, evaluate new values, pushing them on ;; the stack (for-each comp-push args) ;; find the specific case, rename args, and goto the case label (let lp ((lcase (lambda-body (assq-ref fix-labels (lexical-ref-gensym proc))))) (cond ((and (lambda-case? lcase) (not (lambda-case-kw lcase)) (not (lambda-case-opt lcase)) (not (lambda-case-rest lcase)) (= (length args) (length (lambda-case-req lcase)))) ;; we have a case that matches the args; rename variables ;; and goto the case label (for-each (lambda (sym) (pmatch (hashq-ref (hashq-ref allocation sym) self) ((#t #f . ,index) ; unboxed (emit-code #f (make-glil-lexical #t #f 'set index))) ((#t #t . ,index) ; boxed (emit-code #f (make-glil-lexical #t #t 'box index))) (,x (error "bad lambda-case arg allocation" x)))) (reverse (lambda-case-gensyms lcase))) (emit-branch src 'br (car (hashq-ref allocation lcase)))) ((lambda-case? lcase) ;; no match, try next case (lp (lambda-case-alternate lcase))) (else ;; no cases left. we can't really handle this currently. ;; ideally we would push on a new frame, then do a "local ;; call" -- which doesn't require consing up a program ;; object. but for now error, as this sort of case should ;; preclude label allocation. (error "couldn't find matching case for label call" x))))) (else (if (not (eq? context 'tail)) (emit-code src (make-glil-call 'new-frame 0))) (comp-push proc) (for-each comp-push args) (let ((len (length args))) (case context ((tail) (if (<= len #xff) (emit-code src (make-glil-call 'tail-call len)) (begin (comp-push (make-const #f len)) (emit-code src (make-glil-call 'tail-call/nargs 0))))) ((push) (if (<= len #xff) (emit-code src (make-glil-call 'call len)) (begin (comp-push (make-const #f len)) (emit-code src (make-glil-call 'call/nargs 0)))) (maybe-emit-return)) ;; FIXME: mv-call doesn't have a /nargs variant, so it is ;; limited to 255 args. Can work around it with a ;; trampoline and tail-call/nargs, but it's not so nice. ((vals) (emit-code src (make-glil-mv-call len MVRA)) (maybe-emit-return)) ((drop) (let ((MV (make-label)) (POST (make-label))) (emit-code src (make-glil-mv-call len MV)) (emit-code #f (make-glil-call 'drop 1)) (emit-branch #f 'br (or RA POST)) (emit-label MV) (emit-code #f (make-glil-mv-bind 0 #f)) (if RA (emit-branch #f 'br RA) (emit-label POST))))))))) ((<conditional> src test consequent alternate) ;; TEST ;; (br-if-not L1) ;; consequent ;; (br L2) ;; L1: alternate ;; L2: (let ((L1 (make-label)) (L2 (make-label))) ;; need a pattern matcher (record-case test ((<application> proc args) (record-case proc ((<primitive-ref> name) (let ((len (length args))) (cond ((and (eq? name 'eq?) (= len 2)) (comp-push (car args)) (comp-push (cadr args)) (emit-branch src 'br-if-not-eq L1)) ((and (eq? name 'null?) (= len 1)) (comp-push (car args)) (emit-branch src 'br-if-not-null L1)) ((and (eq? name 'not) (= len 1)) (let ((app (car args))) (record-case app ((<application> proc args) (let ((len (length args))) (record-case proc ((<primitive-ref> name) (cond ((and (eq? name 'eq?) (= len 2)) (comp-push (car args)) (comp-push (cadr args)) (emit-branch src 'br-if-eq L1)) ((and (eq? name 'null?) (= len 1)) (comp-push (car args)) (emit-branch src 'br-if-null L1)) (else (comp-push app) (emit-branch src 'br-if L1)))) (else (comp-push app) (emit-branch src 'br-if L1))))) (else (comp-push app) (emit-branch src 'br-if L1))))) (else (comp-push test) (emit-branch src 'br-if-not L1))))) (else (comp-push test) (emit-branch src 'br-if-not L1)))) (else (comp-push test) (emit-branch src 'br-if-not L1))) (comp-tail consequent) ;; if there is an RA, comp-tail will cause a jump to it -- just ;; have to clean up here if there is no RA. (if (and (not RA) (not (eq? context 'tail))) (emit-branch #f 'br L2)) (emit-label L1) (comp-tail alternate) (if (and (not RA) (not (eq? context 'tail))) (emit-label L2)))) ((<primitive-ref> src name) (cond ((eq? (module-variable (fluid-ref *comp-module*) name) (module-variable the-root-module name)) (case context ((tail push vals) (emit-code src (make-glil-toplevel 'ref name)))) (maybe-emit-return)) ((module-variable the-root-module name) (case context ((tail push vals) (emit-code src (make-glil-module 'ref '(guile) name #f)))) (maybe-emit-return)) (else (case context ((tail push vals) (emit-code src (make-glil-module 'ref (module-name (fluid-ref *comp-module*)) name #f)))) (maybe-emit-return)))) ((<lexical-ref> src gensym) (case context ((push vals tail) (pmatch (hashq-ref (hashq-ref allocation gensym) self) ((,local? ,boxed? . ,index) (emit-code src (make-glil-lexical local? boxed? 'ref index))) (,loc (error "bad lexical allocation" x loc))))) (maybe-emit-return)) ((<lexical-set> src gensym exp) (comp-push exp) (pmatch (hashq-ref (hashq-ref allocation gensym) self) ((,local? ,boxed? . ,index) (emit-code src (make-glil-lexical local? boxed? 'set index))) (,loc (error "bad lexical allocation" x loc))) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((<module-ref> src mod name public?) (emit-code src (make-glil-module 'ref mod name public?)) (case context ((drop) (emit-code #f (make-glil-call 'drop 1)))) (maybe-emit-return)) ((<module-set> src mod name public? exp) (comp-push exp) (emit-code src (make-glil-module 'set mod name public?)) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((<toplevel-ref> src name) (emit-code src (make-glil-toplevel 'ref name)) (case context ((drop) (emit-code #f (make-glil-call 'drop 1)))) (maybe-emit-return)) ((<toplevel-set> src name exp) (comp-push exp) (emit-code src (make-glil-toplevel 'set name)) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((<toplevel-define> src name exp) (comp-push exp) (emit-code src (make-glil-toplevel 'define name)) (case context ((tail push vals) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ((<lambda>) (let ((free-locs (cdr (hashq-ref allocation x)))) (case context ((push vals tail) (emit-code #f (flatten-lambda x #f allocation)) (if (not (null? free-locs)) (begin (for-each (lambda (loc) (pmatch loc ((,local? ,boxed? . ,n) (emit-code #f (make-glil-lexical local? #f 'ref n))) (else (error "bad lambda free var allocation" x loc)))) free-locs) (emit-code #f (make-glil-call 'make-closure (length free-locs)))))))) (maybe-emit-return)) ((<lambda-case> src req opt rest kw inits gensyms alternate body) ;; o/~ feature on top of feature o/~ ;; req := (name ...) ;; opt := (name ...) | #f ;; rest := name | #f ;; kw: (allow-other-keys? (keyword name var) ...) | #f ;; gensyms: (sym ...) ;; init: tree-il in context of gensyms ;; gensyms map to named arguments in the following order: ;; required, optional (positional), rest, keyword. (let* ((nreq (length req)) (nopt (if opt (length opt) 0)) (rest-idx (and rest (+ nreq nopt))) (opt-names (or opt '())) (allow-other-keys? (if kw (car kw) #f)) (kw-indices (map (lambda (x) (pmatch x ((,key ,name ,var) (cons key (list-index gensyms var))) (else (error "bad kwarg" x)))) (if kw (cdr kw) '()))) (nargs (apply max (+ nreq nopt (if rest 1 0)) (map 1+ (map cdr kw-indices)))) (nlocs (cdr (hashq-ref allocation x))) (alternate-label (and alternate (make-label)))) (or (= nargs (length gensyms) (+ nreq (length inits) (if rest 1 0))) (error "lambda-case gensyms don't correspond to args" req opt rest kw inits gensyms nreq nopt kw-indices nargs)) ;; the prelude, to check args & reset the stack pointer, ;; allowing room for locals (emit-code src (cond (kw (make-glil-kw-prelude nreq nopt rest-idx kw-indices allow-other-keys? nlocs alternate-label)) ((or rest opt) (make-glil-opt-prelude nreq nopt rest-idx nlocs alternate-label)) (#t (make-glil-std-prelude nreq nlocs alternate-label)))) ;; box args if necessary (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #t . ,n) (emit-code #f (make-glil-lexical #t #f 'ref n)) (emit-code #f (make-glil-lexical #t #t 'box n))))) gensyms) ;; write bindings info (if (not (null? gensyms)) (emit-bindings #f (let lp ((kw (if kw (cdr kw) '())) (names (append (reverse opt-names) (reverse req))) (gensyms (list-tail gensyms (+ nreq nopt (if rest 1 0))))) (pmatch kw (() ;; fixme: check that gensyms is empty (reverse (if rest (cons rest names) names))) (((,key ,name ,var) . ,kw) (if (memq var gensyms) (lp kw (cons name names) (delq var gensyms)) (lp kw names gensyms))) (,kw (error "bad keywords, yo" kw)))) gensyms allocation self emit-code)) ;; init optional/kw args (let lp ((inits inits) (n nreq) (gensyms (list-tail gensyms nreq))) (cond ((null? inits)) ; done ((and rest-idx (= n rest-idx)) (lp inits (1+ n) (cdr gensyms))) (#t (pmatch (hashq-ref (hashq-ref allocation (car gensyms)) self) ((#t ,boxed? . ,n*) (guard (= n* n)) (let ((L (make-label))) (emit-code #f (make-glil-lexical #t boxed? 'bound? n)) (emit-code #f (make-glil-branch 'br-if L)) (comp-push (car inits)) (emit-code #f (make-glil-lexical #t boxed? 'set n)) (emit-label L) (lp (cdr inits) (1+ n) (cdr gensyms)))) (#t (error "bad arg allocation" (car gensyms) inits)))))) ;; post-prelude case label for label calls (emit-label (car (hashq-ref allocation x))) (comp-tail body) (if (not (null? gensyms)) (emit-code #f (make-glil-unbind))) (if alternate-label (begin (emit-label alternate-label) (flatten-lambda-case alternate allocation self self-label fix-labels emit-code))))) ((<let> src names gensyms vals body) (for-each comp-push vals) (emit-bindings src names gensyms allocation self emit-code) (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #f . ,n) (emit-code src (make-glil-lexical #t #f 'set n))) ((#t #t . ,n) (emit-code src (make-glil-lexical #t #t 'box n))) (,loc (error "bad let var allocation" x loc)))) (reverse gensyms)) (comp-tail body) (clear-stack-slots context gensyms) (emit-code #f (make-glil-unbind))) ((<letrec> src in-order? names gensyms vals body) ;; First prepare heap storage slots. (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #t . ,n) (emit-code src (make-glil-lexical #t #t 'empty-box n))) (,loc (error "bad letrec var allocation" x loc)))) gensyms) ;; Even though the slots are empty, the bindings are valid. (emit-bindings src names gensyms allocation self emit-code) (cond (in-order? ;; For letrec*, bind values in order. (for-each (lambda (name v val) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #t . ,n) (comp-push val) (emit-code src (make-glil-lexical #t #t 'set n))) (,loc (error "bad letrec var allocation" x loc)))) names gensyms vals)) (else ;; But for letrec, eval all values, then bind. (for-each comp-push vals) (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #t . ,n) (emit-code src (make-glil-lexical #t #t 'set n))) (,loc (error "bad letrec var allocation" x loc)))) (reverse gensyms)))) (comp-tail body) (clear-stack-slots context gensyms) (emit-code #f (make-glil-unbind))) ((<fix> src names gensyms vals body) ;; The ideal here is to just render the lambda bodies inline, and ;; wire the code together with gotos. We can do that if ;; analyze-lexicals has determined that a given var has "label" ;; allocation -- which is the case if it is in `fix-labels'. ;; ;; But even for closures that we can't inline, we can do some ;; tricks to avoid heap-allocation for the binding itself. Since ;; we know the vals are lambdas, we can set them to their local ;; var slots first, then capture their bindings, mutating them in ;; place. (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label)))) (for-each (lambda (x v) (cond ((hashq-ref allocation x) ;; allocating a closure (emit-code #f (flatten-lambda x v allocation)) (let ((free-locs (cdr (hashq-ref allocation x)))) (if (not (null? free-locs)) ;; Need to make-closure first, so we have a fresh closure on ;; the heap, but with a temporary free values. (begin (for-each (lambda (loc) (emit-code #f (make-glil-const #f))) free-locs) (emit-code #f (make-glil-call 'make-closure (length free-locs)))))) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #f . ,n) (emit-code src (make-glil-lexical #t #f 'set n))) (,loc (error "bad fix var allocation" x loc)))) (else ;; labels allocation: emit label & body, but jump over it (let ((POST (make-label))) (emit-branch #f 'br POST) (let lp ((lcase (lambda-body x))) (if lcase (record-case lcase ((<lambda-case> src req gensyms body alternate) (emit-label (car (hashq-ref allocation lcase))) ;; FIXME: opt & kw args in the bindings (emit-bindings #f req gensyms allocation self emit-code) (if src (emit-code #f (make-glil-source src))) (comp-fix body (or RA new-RA)) (emit-code #f (make-glil-unbind)) (lp alternate))) (emit-label POST))))))) vals gensyms) ;; Emit bindings metadata for closures (let ((binds (let lp ((out '()) (gensyms gensyms) (names names)) (cond ((null? gensyms) (reverse! out)) ((assq (car gensyms) fix-labels) (lp out (cdr gensyms) (cdr names))) (else (lp (acons (car gensyms) (car names) out) (cdr gensyms) (cdr names))))))) (emit-bindings src (map cdr binds) (map car binds) allocation self emit-code)) ;; Now go back and fix up the bindings for closures. (for-each (lambda (x v) (let ((free-locs (if (hashq-ref allocation x) (cdr (hashq-ref allocation x)) ;; can hit this latter case for labels allocation '()))) (if (not (null? free-locs)) (begin (for-each (lambda (loc) (pmatch loc ((,local? ,boxed? . ,n) (emit-code #f (make-glil-lexical local? #f 'ref n))) (else (error "bad free var allocation" x loc)))) free-locs) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #f . ,n) (emit-code #f (make-glil-lexical #t #f 'fix n))) (,loc (error "bad fix var allocation" x loc))))))) vals gensyms) (comp-tail body) (if new-RA (emit-label new-RA)) (clear-stack-slots context gensyms) (emit-code #f (make-glil-unbind)))) ((<let-values> src exp body) (record-case body ((<lambda-case> req opt kw rest gensyms body alternate) (if (or opt kw alternate) (error "unexpected lambda-case in let-values" x)) (let ((MV (make-label))) (comp-vals exp MV) (emit-code #f (make-glil-const 1)) (emit-label MV) (emit-code src (make-glil-mv-bind (vars->bind-list (append req (if rest (list rest) '())) gensyms allocation self) (and rest #t))) (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #f . ,n) (emit-code src (make-glil-lexical #t #f 'set n))) ((#t #t . ,n) (emit-code src (make-glil-lexical #t #t 'box n))) (,loc (error "bad let-values var allocation" x loc)))) (reverse gensyms)) (comp-tail body) (clear-stack-slots context gensyms) (emit-code #f (make-glil-unbind)))))) ;; much trickier than i thought this would be, at first, due to the need ;; to have body's return value(s) on the stack while the unwinder runs, ;; then proceed with returning or dropping or what-have-you, interacting ;; with RA and MVRA. What have you, I say. ((<dynwind> src body winder unwinder) (comp-push winder) (comp-push unwinder) (comp-drop (make-application src winder '())) (emit-code #f (make-glil-call 'wind 2)) (case context ((tail) (let ((MV (make-label))) (comp-vals body MV) ;; one value: unwind... (emit-code #f (make-glil-call 'unwind 0)) (comp-drop (make-application src unwinder '())) ;; ...and return the val (emit-code #f (make-glil-call 'return 1)) (emit-label MV) ;; multiple values: unwind... (emit-code #f (make-glil-call 'unwind 0)) (comp-drop (make-application src unwinder '())) ;; and return the values. (emit-code #f (make-glil-call 'return/nvalues 1)))) ((push) ;; we only want one value. so ask for one value (comp-push body) ;; and unwind, leaving the val on the stack (emit-code #f (make-glil-call 'unwind 0)) (comp-drop (make-application src unwinder '()))) ((vals) (let ((MV (make-label))) (comp-vals body MV) ;; one value: push 1 and fall through to MV case (emit-code #f (make-glil-const 1)) (emit-label MV) ;; multiple values: unwind... (emit-code #f (make-glil-call 'unwind 0)) (comp-drop (make-application src unwinder '())) ;; and goto the MVRA. (emit-branch #f 'br MVRA))) ((drop) ;; compile body, discarding values. then unwind... (comp-drop body) (emit-code #f (make-glil-call 'unwind 0)) (comp-drop (make-application src unwinder '())) ;; and fall through, or goto RA if there is one. (if RA (emit-branch #f 'br RA))))) ((<dynlet> src fluids vals body) (for-each comp-push fluids) (for-each comp-push vals) (emit-code #f (make-glil-call 'wind-fluids (length fluids))) (case context ((tail) (let ((MV (make-label))) ;; NB: in tail case, it is possible to preserve asymptotic tail ;; recursion, via merging unwind-fluids structures -- but we'd need ;; to compile in the body twice (once in tail context, assuming the ;; caller unwinds, and once with this trampoline thing, unwinding ;; ourselves). (comp-vals body MV) ;; one value: unwind and return (emit-code #f (make-glil-call 'unwind-fluids 0)) (emit-code #f (make-glil-call 'return 1)) (emit-label MV) ;; multiple values: unwind and return values (emit-code #f (make-glil-call 'unwind-fluids 0)) (emit-code #f (make-glil-call 'return/nvalues 1)))) ((push) (comp-push body) (emit-code #f (make-glil-call 'unwind-fluids 0))) ((vals) (let ((MV (make-label))) (comp-vals body MV) ;; one value: push 1 and fall through to MV case (emit-code #f (make-glil-const 1)) (emit-label MV) ;; multiple values: unwind and goto MVRA (emit-code #f (make-glil-call 'unwind-fluids 0)) (emit-branch #f 'br MVRA))) ((drop) ;; compile body, discarding values. then unwind... (comp-drop body) (emit-code #f (make-glil-call 'unwind-fluids 0)) ;; and fall through, or goto RA if there is one. (if RA (emit-branch #f 'br RA))))) ((<dynref> src fluid) (case context ((drop) (comp-drop fluid)) ((push vals tail) (comp-push fluid) (emit-code #f (make-glil-call 'fluid-ref 1)))) (maybe-emit-return)) ((<dynset> src fluid exp) (comp-push fluid) (comp-push exp) (emit-code #f (make-glil-call 'fluid-set 2)) (case context ((push vals tail) (emit-code #f (make-glil-void)))) (maybe-emit-return)) ;; What's the deal here? The deal is that we are compiling the start of a ;; delimited continuation. We try to avoid heap allocation in the normal ;; case; so the body is an expression, not a thunk, and we try to render ;; the handler inline. Also we did some analysis, in analyze.scm, so that ;; if the continuation isn't referenced, we don't reify it. This makes it ;; possible to implement catch and throw with delimited continuations, ;; without any overhead. ((<prompt> src tag body handler) (let ((H (make-label)) (POST (make-label)) (escape-only? (hashq-ref allocation x))) ;; First, set up the prompt. (comp-push tag) (emit-code src (make-glil-prompt H escape-only?)) ;; Then we compile the body, with its normal return path, unwinding ;; before proceeding. (case context ((tail) (let ((MV (make-label))) (comp-vals body MV) ;; one value: unwind and return (emit-code #f (make-glil-call 'unwind 0)) (emit-code #f (make-glil-call 'return 1)) ;; multiple values: unwind and return (emit-label MV) (emit-code #f (make-glil-call 'unwind 0)) (emit-code #f (make-glil-call 'return/nvalues 1)))) ((push) ;; we only want one value. so ask for one value, unwind, and jump to ;; post (comp-push body) (emit-code #f (make-glil-call 'unwind 0)) (emit-branch #f 'br (or RA POST))) ((vals) (let ((MV (make-label))) (comp-vals body MV) ;; one value: push 1 and fall through to MV case (emit-code #f (make-glil-const 1)) ;; multiple values: unwind and goto MVRA (emit-label MV) (emit-code #f (make-glil-call 'unwind 0)) (emit-branch #f 'br MVRA))) ((drop) ;; compile body, discarding values, then unwind & fall through. (comp-drop body) (emit-code #f (make-glil-call 'unwind 0)) (emit-branch #f 'br (or RA POST)))) (emit-label H) ;; Now the handler. The stack is now made up of the continuation, and ;; then the args to the continuation (pushed separately), and then the ;; number of args, including the continuation. (record-case handler ((<lambda-case> req opt kw rest gensyms body alternate) (if (or opt kw alternate) (error "unexpected lambda-case in prompt" x)) (emit-code src (make-glil-mv-bind (vars->bind-list (append req (if rest (list rest) '())) gensyms allocation self) (and rest #t))) (for-each (lambda (v) (pmatch (hashq-ref (hashq-ref allocation v) self) ((#t #f . ,n) (emit-code src (make-glil-lexical #t #f 'set n))) ((#t #t . ,n) (emit-code src (make-glil-lexical #t #t 'box n))) (,loc (error "bad prompt handler arg allocation" x loc)))) (reverse gensyms)) (comp-tail body) (emit-code #f (make-glil-unbind)))) (if (and (not RA) (or (eq? context 'push) (eq? context 'drop))) (emit-label POST)))) ((<abort> src tag args tail) (comp-push tag) (for-each comp-push args) (comp-push tail) (emit-code src (make-glil-call 'abort (length args))) ;; so, the abort can actually return. if it does, the values will be on ;; the stack, then the MV marker, just as in an MV context. (case context ((tail) ;; Return values. (emit-code #f (make-glil-call 'return/nvalues 1))) ((drop) ;; Drop all values and goto RA, or otherwise fall through. (emit-code #f (make-glil-mv-bind 0 #f)) (if RA (emit-branch #f 'br RA))) ((push) ;; Truncate to one value. (emit-code #f (make-glil-mv-bind 1 #f))) ((vals) ;; Go to MVRA. (emit-branch #f 'br MVRA)))))))