2 % (c) The University of Glasgow 2000
4 \section[ByteCodeGen]{Generate bytecode from Core}
7 module ByteCodeGen ( byteCodeGen ) where
9 #include "HsVersions.h"
16 import Name ( Name, getName )
17 import Id ( Id, idType, isDataConId_maybe )
18 import OrdList ( OrdList, consOL, snocOL, appOL, unitOL,
19 nilOL, toOL, concatOL, fromOL )
20 import FiniteMap ( FiniteMap, addListToFM, listToFM,
21 addToFM, lookupFM, fmToList )
23 import Literal ( Literal(..) )
24 import PrimRep ( PrimRep(..) )
25 import CoreFVs ( freeVars )
26 import Type ( typePrimRep )
27 import DataCon ( DataCon, dataConTag, fIRST_TAG )
28 import Util ( zipEqual, zipWith4Equal )
29 import VarSet ( VarSet, varSetElems )
36 byteCodeGen :: [CoreBind] -> [BCO Name]
38 = let flatBinds = concatMap getBind binds
39 getBind (NonRec bndr rhs) = [(bndr, freeVars rhs)]
40 getBind (Rec binds) = [(bndr, freeVars rhs) | (bndr,rhs) <- binds]
42 snd (initBc [] (mapBc schemeR flatBinds))
49 -- Messing with the stack
51 | PUSH_L Int{-size-} Int{-offset-}
53 -- | PUSH_ALTS Name{-labels the alt BCO; derived from case binder-}
55 | SLIDE Int{-this many-} Int{-down by this much-}
56 -- To do with the heap
58 | MKAP Int{-place ptr to heap this far down stack-} Int{-# words-}
61 -- Casery (in French: caseage)
62 | CASE_PTR [(Discr, BCInstrList)]
63 | CASE_INT [(Discr, BCInstrList)]
64 | CASE_FLOAT [(Discr, BCInstrList)]
65 | CASE_DOUBLE [(Discr, BCInstrList)]
66 -- To Infinity And Beyond
69 instance Outputable BCInstr where
70 ppr (ARGCHECK n) = text "ARGCHECK" <+> int n
71 ppr (PUSH_L sz offset) = text "PUSH_L " <+> int sz <+> int offset
72 ppr (PUSH_G nm) = text "PUSH_G " <+> ppr nm
73 ppr (PUSH_I i) = text "PUSH_I " <+> integer i
74 ppr (SLIDE n d) = text "SLIDE " <+> int n <+> int d
75 ppr (ALLOC sz) = text "ALLOC " <+> int sz
76 ppr (MKAP offset sz) = text "MKAP " <+> int offset <+> int sz
77 ppr (UNPACK sz) = text "UNPACK " <+> int sz
78 ppr (PACK dcon sz) = text "PACK " <+> ppr dcon <+> ppr sz
79 ppr (CASE_PTR altcode) = text "CASE_P" $$ nest 3 (pprAltCode altcode)
80 ppr (CASE_INT altcode) = text "CASE_P" $$ nest 3 (pprAltCode altcode)
81 ppr (CASE_FLOAT altcode) = text "CASE_P" $$ nest 3 (pprAltCode altcode)
82 ppr (CASE_DOUBLE altcode) = text "CASE_P" $$ nest 3 (pprAltCode altcode)
83 ppr ENTER = text "ENTER"
85 pprAltCode discrs_n_codes
86 = vcat (map f discrs_n_codes)
87 where f (discr, code) = ppr discr <> colon <+> vcat (map ppr (fromOL code))
90 type BCInstrList = OrdList BCInstr
92 data BCO a = BCO a BCInstrList
94 instance Outputable a => Outputable (BCO a) where
96 = (text "BCO" <+> ppr name <> colon)
97 $$ nest 6 (vcat (map ppr (fromOL instrs)))
102 type Sequel = Int -- back off to this depth before ENTER
104 -- Maps Ids to the offset from the stack _base_ so we don't have
105 -- to mess with it after each push/pop.
106 type BCEnv = FiniteMap Id Int -- To find vars on the stack
108 lookupBCEnv :: BCEnv -> Id -> Int
110 = case lookupFM env nm of
111 Nothing -> pprPanic "lookupBCEnv"
112 (ppr nm $$ char ' ' $$ vcat (map ppr (fmToList env)))
115 lookupBCEnv_maybe :: BCEnv -> Id -> Maybe Int
116 lookupBCEnv_maybe = lookupFM
119 -- Describes case alts
127 instance Outputable Discr where
128 ppr (DiscrI i) = int i
129 ppr (DiscrF r) = rational r
130 ppr (DiscrD r) = rational r
131 ppr (DiscrP i) = int i
132 ppr NoDiscr = text "DEF"
135 -- Hmm. This isn't really right (ie on Alpha, idSizeW Double -> 2)
136 -- There must be an Officially Approved way to do this somewhere.
139 = let pr = typePrimRep (idType nm)
140 in case pr of IntRep -> 2
144 other -> pprPanic "ByteCodeGen.idSizeW" (ppr pr)
148 -- Compile code for the right hand side of a let binding.
149 -- Park the resulting BCO in the monad. Also requires the
150 -- variable to which this value was bound, so as to give the
151 -- resulting BCO a name.
152 schemeR :: (Id, AnnExpr Id VarSet) -> BcM ()
153 schemeR (nm, rhs) = schemeR_wrk nm (collect [] rhs)
155 collect xs (_, AnnLam x e) = collect (x:xs) e
156 collect xs not_lambda = (reverse xs, not_lambda)
158 schemeR_wrk nm (args, body)
160 all_args = varSetElems fvs ++ args
161 szsw_args = map idSizeW all_args
162 szw_args = sum szsw_args
163 p_init = listToFM (zip all_args (scanl (+) 0 szsw_args))
164 argcheck = if null args then nilOL else unitOL (ARGCHECK szw_args)
166 schemeE szw_args 0 p_init body `thenBc` \ body_code ->
167 emitBc (BCO (getName nm) (appOL argcheck body_code))
170 -- Compile code to apply the given expression to the remaining args
171 -- on the stack, returning a HNF.
172 schemeE :: Int -> Sequel -> BCEnv -> AnnExpr Id VarSet -> BcM BCInstrList
174 -- Delegate tail-calls to schemeT.
175 schemeE d s p (fvs, AnnApp f a) = returnBc (schemeT d s 0 p (fvs, AnnApp f a))
176 schemeE d s p (fvs, AnnVar v) = returnBc (schemeT d s 0 p (fvs, AnnVar v))
178 schemeE d s p (fvs, AnnLet binds b)
179 = let (xs,rhss) = case binds of AnnNonRec x rhs -> ([x],[rhs])
180 AnnRec xs_n_rhss -> unzip xs_n_rhss
182 mapBc schemeR (zip xs rhss) `thenBc_`
184 fvss = map (varSetElems.fst) rhss
185 sizes = map (\rhs_fvs -> 1 + sum (map idSizeW rhs_fvs)) fvss
186 p' = addListToFM p (zipE xs [d .. d+n-1])
188 infos = zipE4 fvss sizes xs [n, n-1 .. 1]
189 zipE = zipEqual "schemeE"
190 zipE4 = zipWith4Equal "schemeE" (\a b c d -> (a,b,c,d))
192 -- ToDo: don't build thunks for things with no free variables
193 buildThunk (fvs, size, id, off)
194 = case unzip (map (pushAtom d' p . AnnVar) (reverse fvs)) of
195 (push_codes, pushed_szsw)
196 -> ASSERT(sum pushed_szsw == size - 1)
197 (toOL push_codes `snocOL` PUSH_G (getName id)
198 `appOL` unitOL (MKAP off size))
200 thunkCode = concatOL (map buildThunk infos)
201 allocCode = toOL (map ALLOC sizes)
203 schemeE d' s p' b `thenBc` \ bodyCode ->
204 mapBc schemeR (zip xs rhss) `thenBc` \_ ->
205 returnBc (allocCode `appOL` thunkCode `appOL` bodyCode)
208 schemeE d s p (fvs, AnnCase scrut bndr alts)
210 -- Top of stack is the return itbl, as usual.
211 -- underneath it is the pointer to the alt_code BCO.
212 -- When an alt is entered, it assumes the returned value is
213 -- on top of the itbl.
216 -- Env and depth in which to compile the alts, not including
217 -- any vars bound by the alts themselves
218 d' = d + ret_frame_sizeW + idSizeW bndr
219 p' = addToFM p bndr d'
221 (case_instr, isAlgCase)
222 = case typePrimRep (idType bndr) of
223 IntRep -> (CASE_INT, False)
224 FloatRep -> (CASE_FLOAT, False)
225 DoubleRep -> (CASE_DOUBLE, False)
226 PtrRep -> (CASE_PTR, True)
227 other -> pprPanic "ByteCodeGen.schemeE" (ppr other)
229 -- make the code for an alt
230 codeAlt (discr, binds, rhs)
232 = let binds_szsw = map idSizeW binds
233 binds_szw = sum binds_szsw
234 p'' = addListToFM p' (zip binds (scanl (+) d' binds_szsw))
236 in schemeE d'' s p'' rhs `thenBc` \ rhs_code ->
237 returnBc (UNPACK binds_szw `consOL` rhs_code)
239 = ASSERT(null binds) schemeE d' s p' rhs
241 discr (DEFAULT, binds, rhs) = NoDiscr
242 discr (DataAlt dc, binds, rhs) = DiscrP (dataConTag dc - fIRST_TAG)
243 discr (LitAlt l, binds, rhs)
244 = case l of MachInt i -> DiscrI (fromInteger i)
245 MachFloat r -> DiscrF r
246 MachDouble r -> DiscrD r
248 discrs = map discr alts
250 mapBc codeAlt alts `thenBc` \ alt_codes ->
252 alt_code = case_instr (zip discrs alt_codes)
253 alt_bco_name = getName bndr
254 alt_bco = BCO alt_bco_name (unitOL alt_code)
256 schemeE (d + ret_frame_sizeW)
257 (d + ret_frame_sizeW) p scrut `thenBc` \ scrut_code ->
259 emitBc alt_bco `thenBc_`
260 returnBc (PUSH_G alt_bco_name `consOL` scrut_code)
263 -- Compile code to do a tail call. Doesn't need to be monadic.
264 schemeT :: Int -> Sequel -> Int -> BCEnv -> AnnExpr Id VarSet -> BCInstrList
266 schemeT d s narg_words p (_, AnnApp f a)
267 = let (push, arg_words) = pushAtom d p (snd a)
269 `consOL` schemeT (d+arg_words) s (narg_words+arg_words) p f
271 schemeT d s narg_words p (_, AnnVar f)
272 | Just con <- isDataConId_maybe f
273 = PACK con narg_words `consOL` SLIDE 1 (d-s-1) `consOL` unitOL ENTER
275 = let (push, arg_words) = pushAtom d p (AnnVar f)
277 `consOL` SLIDE (narg_words+arg_words) (d - s - narg_words)
278 `consOL` unitOL ENTER
281 -- Push an atom onto the stack, returning suitable code & number of
283 pushAtom d p (AnnVar v)
284 = case lookupBCEnv_maybe p v of
285 Just offset -> (PUSH_L sz offset, sz)
286 Nothing -> ASSERT(sz == 1) (PUSH_G nm, 1)
291 pushAtom d p (AnnLit lit)
293 MachInt i -> (PUSH_I i, 2)
296 The bytecode generator's monad.
299 type BcM_State = [BCO Name] -- accumulates completed BCOs
301 type BcM result = BcM_State -> (result, BcM_State)
303 mkBcM_State :: [BCO Name] -> BcM_State
306 initBc :: BcM_State -> BcM a -> (a, BcM_State)
307 initBc init_st m = case m init_st of { (r,st) -> (r,st) }
309 thenBc :: BcM a -> (a -> BcM b) -> BcM b
311 = case expr st of { (result, st') -> cont result st' }
313 thenBc_ :: BcM a -> BcM b -> BcM b
315 = case expr st of { (result, st') -> cont st' }
317 returnBc :: a -> BcM a
318 returnBc result st = (result, st)
320 mapBc :: (a -> BcM b) -> [a] -> BcM [b]
321 mapBc f [] = returnBc []
323 = f x `thenBc` \ r ->
324 mapBc f xs `thenBc` \ rs ->
327 emitBc :: BCO Name -> BcM ()