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, naturalMergeSortLe )
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]
41 final_state = runBc (BcM_State [] 0)
42 (mapBc schemeR flatBinds `thenBc_` returnBc ())
45 BcM_State bcos final_ctr -> bcos
55 -- Messing with the stack
57 | PUSH_L Int{-size-} Int{-offset-}
60 | SLIDE Int{-this many-} Int{-down by this much-}
61 -- To do with the heap
63 | MKAP Int{-place ptr to heap this far down stack-} Int{-# words-}
66 -- For doing case trees
68 | TESTLT_I Int LocalLabel
69 | TESTEQ_I Int LocalLabel
70 | TESTLT_F Float LocalLabel
71 | TESTEQ_F Float LocalLabel
72 | TESTLT_D Double LocalLabel
73 | TESTEQ_D Double LocalLabel
74 | TESTLT_P Int LocalLabel
75 | TESTEQ_P Int LocalLabel
77 -- To Infinity And Beyond
80 instance Outputable BCInstr where
81 ppr (ARGCHECK n) = text "ARGCHECK" <+> int n
82 ppr (PUSH_L sz offset) = text "PUSH_L " <+> int sz <+> int offset
83 ppr (PUSH_G nm) = text "PUSH_G " <+> ppr nm
84 ppr (PUSH_I i) = text "PUSH_I " <+> integer i
85 ppr (SLIDE n d) = text "SLIDE " <+> int n <+> int d
86 ppr (ALLOC sz) = text "ALLOC " <+> int sz
87 ppr (MKAP offset sz) = text "MKAP " <+> int offset <+> int sz
88 ppr (UNPACK sz) = text "UNPACK " <+> int sz
89 ppr (PACK dcon sz) = text "PACK " <+> ppr dcon <+> ppr sz
90 ppr ENTER = text "ENTER"
92 pprAltCode discrs_n_codes
93 = vcat (map f discrs_n_codes)
94 where f (discr, code) = ppr discr <> colon <+> vcat (map ppr (fromOL code))
97 type BCInstrList = OrdList BCInstr
99 data BCO a = BCO a BCInstrList
101 instance Outputable a => Outputable (BCO a) where
102 ppr (BCO name instrs)
103 = (text "BCO" <+> ppr name <> colon)
104 $$ nest 6 (vcat (map ppr (fromOL instrs)))
109 type Sequel = Int -- back off to this depth before ENTER
111 -- Maps Ids to the offset from the stack _base_ so we don't have
112 -- to mess with it after each push/pop.
113 type BCEnv = FiniteMap Id Int -- To find vars on the stack
115 lookupBCEnv :: BCEnv -> Id -> Int
117 = case lookupFM env nm of
118 Nothing -> pprPanic "lookupBCEnv"
119 (ppr nm $$ char ' ' $$ vcat (map ppr (fmToList env)))
122 lookupBCEnv_maybe :: BCEnv -> Id -> Maybe Int
123 lookupBCEnv_maybe = lookupFM
126 -- Describes case alts
134 instance Outputable Discr where
135 ppr (DiscrI i) = int i
136 ppr (DiscrF f) = text (show f)
137 ppr (DiscrD d) = text (show d)
138 ppr (DiscrP i) = int i
139 ppr NoDiscr = text "DEF"
142 -- Hmm. This isn't really right (ie on Alpha, idSizeW Double -> 2)
143 -- There must be an Officially Approved way to do this somewhere.
146 = let pr = typePrimRep (idType nm)
147 in case pr of IntRep -> 2
151 other -> pprPanic "ByteCodeGen.idSizeW" (ppr pr)
155 -- Compile code for the right hand side of a let binding.
156 -- Park the resulting BCO in the monad. Also requires the
157 -- variable to which this value was bound, so as to give the
158 -- resulting BCO a name.
159 schemeR :: (Id, AnnExpr Id VarSet) -> BcM ()
160 schemeR (nm, rhs) = schemeR_wrk nm (collect [] rhs)
162 collect xs (_, AnnLam x e) = collect (x:xs) e
163 collect xs not_lambda = (reverse xs, not_lambda)
165 schemeR_wrk nm (args, body)
167 all_args = varSetElems fvs ++ args
168 szsw_args = map idSizeW all_args
169 szw_args = sum szsw_args
170 p_init = listToFM (zip all_args (scanl (+) 0 szsw_args))
171 argcheck = if null args then nilOL else unitOL (ARGCHECK szw_args)
173 schemeE szw_args 0 p_init body `thenBc` \ body_code ->
174 emitBc (BCO (getName nm) (appOL argcheck body_code))
177 -- Compile code to apply the given expression to the remaining args
178 -- on the stack, returning a HNF.
179 schemeE :: Int -> Sequel -> BCEnv -> AnnExpr Id VarSet -> BcM BCInstrList
181 -- Delegate tail-calls to schemeT.
182 schemeE d s p (fvs, AnnApp f a) = returnBc (schemeT d s 0 p (fvs, AnnApp f a))
183 schemeE d s p (fvs, AnnVar v) = returnBc (schemeT d s 0 p (fvs, AnnVar v))
185 schemeE d s p (fvs, AnnLet binds b)
186 = let (xs,rhss) = case binds of AnnNonRec x rhs -> ([x],[rhs])
187 AnnRec xs_n_rhss -> unzip xs_n_rhss
189 mapBc schemeR (zip xs rhss) `thenBc_`
191 fvss = map (varSetElems.fst) rhss
192 sizes = map (\rhs_fvs -> 1 + sum (map idSizeW rhs_fvs)) fvss
193 p' = addListToFM p (zipE xs [d .. d+n-1])
195 infos = zipE4 fvss sizes xs [n, n-1 .. 1]
196 zipE = zipEqual "schemeE"
197 zipE4 = zipWith4Equal "schemeE" (\a b c d -> (a,b,c,d))
199 -- ToDo: don't build thunks for things with no free variables
200 buildThunk (fvs, size, id, off)
201 = case unzip (map (pushAtom d' p . AnnVar) (reverse fvs)) of
202 (push_codes, pushed_szsw)
203 -> ASSERT(sum pushed_szsw == size - 1)
204 (toOL push_codes `snocOL` PUSH_G (getName id)
205 `appOL` unitOL (MKAP off size))
207 thunkCode = concatOL (map buildThunk infos)
208 allocCode = toOL (map ALLOC sizes)
210 schemeE d' s p' b `thenBc` \ bodyCode ->
211 mapBc schemeR (zip xs rhss) `thenBc` \_ ->
212 returnBc (allocCode `appOL` thunkCode `appOL` bodyCode)
215 schemeE d s p (fvs, AnnCase scrut bndr alts)
217 -- Top of stack is the return itbl, as usual.
218 -- underneath it is the pointer to the alt_code BCO.
219 -- When an alt is entered, it assumes the returned value is
220 -- on top of the itbl.
223 -- Env and depth in which to compile the alts, not including
224 -- any vars bound by the alts themselves
225 d' = d + ret_frame_sizeW + idSizeW bndr
226 p' = addToFM p bndr d'
229 = case typePrimRep (idType bndr) of
230 IntRep -> False ; FloatRep -> False ; DoubleRep -> False
232 other -> pprPanic "ByteCodeGen.schemeE" (ppr other)
234 -- given an alt, return a discr and code for it.
235 codeAlt alt@(discr, binds, rhs)
237 = let binds_szsw = map idSizeW binds
238 binds_szw = sum binds_szsw
239 p'' = addListToFM p' (zip binds (scanl (+) d' binds_szsw))
241 in schemeE d'' s p'' rhs `thenBc` \ rhs_code ->
242 returnBc (my_discr alt, UNPACK binds_szw `consOL` rhs_code)
245 schemeE d' s p' rhs `thenBc` \ rhs_code ->
246 returnBc (my_discr alt, rhs_code)
248 my_discr (DEFAULT, binds, rhs) = NoDiscr
249 my_discr (DataAlt dc, binds, rhs) = DiscrP (dataConTag dc - fIRST_TAG)
250 my_discr (LitAlt l, binds, rhs)
251 = case l of MachInt i -> DiscrI (fromInteger i)
252 MachFloat r -> DiscrF (fromRational r)
253 MachDouble r -> DiscrD (fromRational r)
256 mapBc codeAlt alts `thenBc` \ alt_stuff ->
257 mkMultiBranch alt_stuff `thenBc` \ alt_final ->
259 alt_bco_name = getName bndr
260 alt_bco = BCO alt_bco_name alt_final
262 schemeE (d + ret_frame_sizeW)
263 (d + ret_frame_sizeW) p scrut `thenBc` \ scrut_code ->
265 emitBc alt_bco `thenBc_`
266 returnBc (PUSH_G alt_bco_name `consOL` scrut_code)
269 -- Given a bunch of alts code and their discrs, do the donkey work
270 -- of making a multiway branch using a switch tree.
271 -- What a load of hassle!
272 mkMultiBranch :: [(Discr, BCInstrList)] -> BcM BCInstrList
273 mkMultiBranch raw_ways
274 = let d_way = filter (isNoDiscr.fst) raw_ways
275 notd_ways = naturalMergeSortLe
276 (\w1 w2 -> leAlt (fst w1) (fst w2))
277 (filter (not.isNoDiscr.fst) raw_ways)
279 mkTree :: [(Discr, BCInstrList)] -> Discr -> Discr -> BcM BCInstrList
280 mkTree [] range_lo range_hi = returnBc the_default
282 mkTree [val] range_lo range_hi
283 | range_lo `eqAlt` range_hi
286 = getLabelBc `thenBc` \ label_neq ->
287 returnBc (mkTestEQ (fst val) label_neq
289 `appOL` unitOL (LABEL label_neq)
290 `appOL` the_default))
292 mkTree vals range_lo range_hi
293 = let n = length vals `div` 2
294 vals_lo = take n vals
295 vals_hi = drop n vals
296 v_mid = fst (head vals_hi)
298 getLabelBc `thenBc` \ label_geq ->
299 mkTree vals_lo range_lo (dec v_mid) `thenBc` \ code_lo ->
300 mkTree vals_hi v_mid range_hi `thenBc` \ code_hi ->
301 returnBc (mkTestLT v_mid label_geq
303 `appOL` unitOL (LABEL label_geq)
307 = case d_way of [] -> unitOL CASEFAIL
310 -- None of these will be needed if there are no non-default alts
311 (mkTestLT, mkTestEQ, init_lo, init_hi)
313 = panic "mkMultiBranch: awesome foursome"
315 = case fst (head notd_ways) of {
316 DiscrI _ -> ( \(DiscrI i) fail_label -> TESTLT_I i fail_label,
317 \(DiscrI i) fail_label -> TESTEQ_I i fail_label,
320 DiscrF _ -> ( \(DiscrF f) fail_label -> TESTLT_F f fail_label,
321 \(DiscrF f) fail_label -> TESTEQ_F f fail_label,
324 DiscrD _ -> ( \(DiscrD d) fail_label -> TESTLT_D d fail_label,
325 \(DiscrD d) fail_label -> TESTEQ_D d fail_label,
328 DiscrP _ -> ( \(DiscrP i) fail_label -> TESTLT_P i fail_label,
329 \(DiscrP i) fail_label -> TESTEQ_P i fail_label,
334 (DiscrI i1) `eqAlt` (DiscrI i2) = i1 == i2
335 (DiscrF f1) `eqAlt` (DiscrF f2) = f1 == f2
336 (DiscrD d1) `eqAlt` (DiscrD d2) = d1 == d2
337 (DiscrP i1) `eqAlt` (DiscrP i2) = i1 == i2
338 NoDiscr `eqAlt` NoDiscr = True
341 (DiscrI i1) `leAlt` (DiscrI i2) = i1 <= i2
342 (DiscrF f1) `leAlt` (DiscrF f2) = f1 <= f2
343 (DiscrD d1) `leAlt` (DiscrD d2) = d1 <= d2
344 (DiscrP i1) `leAlt` (DiscrP i2) = i1 <= i2
345 NoDiscr `leAlt` NoDiscr = True
348 isNoDiscr NoDiscr = True
351 dec (DiscrI i) = DiscrI (i-1)
352 dec (DiscrP i) = DiscrP (i-1)
353 dec other = other -- not really right, but if you
354 -- do cases on floating values, you'll get what you deserve
356 -- same snotty comment applies to the following
364 mkTree notd_ways init_lo init_hi
367 -- Compile code to do a tail call. Doesn't need to be monadic.
368 schemeT :: Int -> Sequel -> Int -> BCEnv -> AnnExpr Id VarSet -> BCInstrList
370 schemeT d s narg_words p (_, AnnApp f a)
371 = let (push, arg_words) = pushAtom d p (snd a)
373 `consOL` schemeT (d+arg_words) s (narg_words+arg_words) p f
375 schemeT d s narg_words p (_, AnnVar f)
376 | Just con <- isDataConId_maybe f
377 = PACK con narg_words `consOL` SLIDE 1 (d-s-1) `consOL` unitOL ENTER
379 = let (push, arg_words) = pushAtom d p (AnnVar f)
381 `consOL` SLIDE (narg_words+arg_words) (d - s - narg_words)
382 `consOL` unitOL ENTER
385 -- Push an atom onto the stack, returning suitable code & number of
387 pushAtom d p (AnnVar v)
388 = case lookupBCEnv_maybe p v of
389 Just offset -> (PUSH_L sz offset, sz)
390 Nothing -> ASSERT(sz == 1) (PUSH_G nm, 1)
395 pushAtom d p (AnnLit lit)
397 MachInt i -> (PUSH_I i, 2)
400 The bytecode generator's monad.
404 = BcM_State { bcos :: [BCO Name], -- accumulates completed BCOs
405 nextlabel :: Int } -- for generating local labels
407 type BcM result = BcM_State -> (result, BcM_State)
409 mkBcM_State :: [BCO Name] -> Int -> BcM_State
410 mkBcM_State = BcM_State
412 runBc :: BcM_State -> BcM () -> BcM_State
413 runBc init_st m = case m init_st of { (r,st) -> st }
415 thenBc :: BcM a -> (a -> BcM b) -> BcM b
417 = case expr st of { (result, st') -> cont result st' }
419 thenBc_ :: BcM a -> BcM b -> BcM b
421 = case expr st of { (result, st') -> cont st' }
423 returnBc :: a -> BcM a
424 returnBc result st = (result, st)
426 mapBc :: (a -> BcM b) -> [a] -> BcM [b]
427 mapBc f [] = returnBc []
429 = f x `thenBc` \ r ->
430 mapBc f xs `thenBc` \ rs ->
433 emitBc :: BCO Name -> BcM ()
435 = ((), st{bcos = bco : bcos st})
437 getLabelBc :: BcM Int
439 = (nextlabel st, st{nextlabel = 1 + nextlabel st})